Merge "Renderscript samples. Resolving name conflict in model viewer. Adding fov projection matrix function. Adding helper methods for blending."
diff --git a/Android.mk b/Android.mk
index 4fab976..a73767b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -372,7 +372,7 @@
-since ./frameworks/base/api/7.xml 7 \
-since ./frameworks/base/api/8.xml 8 \
-since ./frameworks/base/api/current.xml HC \
- -error 101 -error 102 -warning 103 -error 104 -error 106 -error 108 \
+ -error 101 -error 102 -warning 103 -error 104 -error 106 -error 108 -warning 113 \
-overview $(LOCAL_PATH)/core/java/overview.html
framework_docs_LOCAL_ADDITIONAL_JAVA_DIR:=$(call intermediates-dir-for,JAVA_LIBRARIES,framework)
diff --git a/api/current.xml b/api/current.xml
index 9c35a53..d935c38 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -8919,6 +8919,17 @@
visibility="public"
>
</field>
+<field name="splitMotionEvents"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843567"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="src"
type="int"
transient="false"
@@ -16641,6 +16652,39 @@
visibility="public"
>
</field>
+<field name="Theme_Holo"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973974"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="Theme_Holo_NoTitleBar"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973976"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="Theme_Holo_NoTitleBar_Fullscreen"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973977"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="Theme_InputMethod"
type="int"
transient="false"
@@ -16663,6 +16707,39 @@
visibility="public"
>
</field>
+<field name="Theme_Light_Holo"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973975"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="Theme_Light_Holo_NoTitleBar"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973978"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="Theme_Light_Holo_NoTitleBar_Fullscreen"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973979"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="Theme_Light_NoTitleBar"
type="int"
transient="false"
@@ -19952,63 +20029,7 @@
>
<parameter name="duration" type="long">
</parameter>
-<parameter name="keyframes" type="android.animation.Keyframe...">
-</parameter>
-</constructor>
-<constructor name="Animator"
- type="android.animation.Animator"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="duration" type="long">
-</parameter>
-<parameter name="valueFrom" type="float">
-</parameter>
-<parameter name="valueTo" type="float">
-</parameter>
-</constructor>
-<constructor name="Animator"
- type="android.animation.Animator"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="duration" type="long">
-</parameter>
-<parameter name="valueFrom" type="int">
-</parameter>
-<parameter name="valueTo" type="int">
-</parameter>
-</constructor>
-<constructor name="Animator"
- type="android.animation.Animator"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="duration" type="long">
-</parameter>
-<parameter name="valueFrom" type="double">
-</parameter>
-<parameter name="valueTo" type="double">
-</parameter>
-</constructor>
-<constructor name="Animator"
- type="android.animation.Animator"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="duration" type="long">
-</parameter>
-<parameter name="valueFrom" type="java.lang.Object">
-</parameter>
-<parameter name="valueTo" type="java.lang.Object">
+<parameter name="values" type="T...">
</parameter>
</constructor>
<method name="addUpdateListener"
@@ -20035,6 +20056,19 @@
visibility="public"
>
</method>
+<method name="getAnimatedValue"
+ return="java.lang.Object"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="propertyName" type="java.lang.String">
+</parameter>
+</method>
<method name="getCurrentPlayTime"
return="long"
abstract="false"
@@ -20090,28 +20124,6 @@
visibility="public"
>
</method>
-<method name="getValueFrom"
- return="java.lang.Object"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getValueTo"
- return="java.lang.Object"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
<method name="isRunning"
return="boolean"
abstract="false"
@@ -20238,7 +20250,7 @@
<parameter name="startDelay" type="long">
</parameter>
</method>
-<method name="setValueFrom"
+<method name="setValues"
return="void"
abstract="false"
native="false"
@@ -20248,10 +20260,10 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="valueFrom" type="java.lang.Object">
+<parameter name="values" type="android.animation.PropertyValuesHolder...">
</parameter>
</method>
-<method name="setValueTo"
+<method name="setValues"
return="void"
abstract="false"
native="false"
@@ -20261,7 +20273,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="valueTo" type="java.lang.Object">
+<parameter name="values" type="T...">
</parameter>
</method>
<field name="INFINITE"
@@ -20594,15 +20606,13 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="duration" type="int">
+<parameter name="duration" type="long">
</parameter>
<parameter name="target" type="java.lang.Object">
</parameter>
<parameter name="propertyName" type="java.lang.String">
</parameter>
-<parameter name="valueFrom" type="float">
-</parameter>
-<parameter name="valueTo" type="float">
+<parameter name="values" type="T...">
</parameter>
</constructor>
<constructor name="PropertyAnimator"
@@ -20612,131 +20622,90 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="duration" type="int">
+<parameter name="duration" type="long">
</parameter>
<parameter name="target" type="java.lang.Object">
</parameter>
-<parameter name="propertyName" type="java.lang.String">
-</parameter>
-<parameter name="valueTo" type="float">
+<parameter name="values" type="android.animation.PropertyValuesHolder...">
</parameter>
</constructor>
-<constructor name="PropertyAnimator"
- type="android.animation.PropertyAnimator"
+<method name="getPropertyName"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
-<parameter name="duration" type="int">
-</parameter>
-<parameter name="target" type="java.lang.Object">
-</parameter>
-<parameter name="propertyName" type="java.lang.String">
-</parameter>
-<parameter name="valueFrom" type="int">
-</parameter>
-<parameter name="valueTo" type="int">
-</parameter>
-</constructor>
-<constructor name="PropertyAnimator"
- type="android.animation.PropertyAnimator"
+</method>
+<method name="getTarget"
+ return="java.lang.Object"
+ abstract="false"
+ native="false"
+ synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
-<parameter name="duration" type="int">
-</parameter>
-<parameter name="target" type="java.lang.Object">
-</parameter>
-<parameter name="propertyName" type="java.lang.String">
-</parameter>
-<parameter name="valueTo" type="int">
-</parameter>
-</constructor>
-<constructor name="PropertyAnimator"
- type="android.animation.PropertyAnimator"
+</method>
+<method name="setPropertyName"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
-<parameter name="duration" type="int">
-</parameter>
-<parameter name="target" type="java.lang.Object">
-</parameter>
<parameter name="propertyName" type="java.lang.String">
</parameter>
-<parameter name="valueFrom" type="double">
-</parameter>
-<parameter name="valueTo" type="double">
-</parameter>
-</constructor>
-<constructor name="PropertyAnimator"
- type="android.animation.PropertyAnimator"
+</method>
+<method name="setTarget"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
-<parameter name="duration" type="int">
-</parameter>
<parameter name="target" type="java.lang.Object">
</parameter>
-<parameter name="propertyName" type="java.lang.String">
-</parameter>
-<parameter name="valueTo" type="double">
-</parameter>
-</constructor>
-<constructor name="PropertyAnimator"
- type="android.animation.PropertyAnimator"
+</method>
+</class>
+<class name="PropertyValuesHolder"
+ extends="java.lang.Object"
+ abstract="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
-<parameter name="duration" type="int">
-</parameter>
-<parameter name="target" type="java.lang.Object">
-</parameter>
-<parameter name="propertyName" type="java.lang.String">
-</parameter>
-<parameter name="valueFrom" type="java.lang.Object">
-</parameter>
-<parameter name="valueTo" type="java.lang.Object">
-</parameter>
-</constructor>
-<constructor name="PropertyAnimator"
- type="android.animation.PropertyAnimator"
+<constructor name="PropertyValuesHolder"
+ type="android.animation.PropertyValuesHolder"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
-<parameter name="duration" type="int">
-</parameter>
-<parameter name="target" type="java.lang.Object">
-</parameter>
-<parameter name="propertyName" type="java.lang.String">
-</parameter>
-<parameter name="valueTo" type="java.lang.Object">
+<parameter name="values" type="T...">
</parameter>
</constructor>
-<constructor name="PropertyAnimator"
- type="android.animation.PropertyAnimator"
+<constructor name="PropertyValuesHolder"
+ type="android.animation.PropertyValuesHolder"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
-<parameter name="duration" type="int">
-</parameter>
-<parameter name="target" type="java.lang.Object">
-</parameter>
<parameter name="propertyName" type="java.lang.String">
</parameter>
-<parameter name="keyframes" type="android.animation.Keyframe...">
+<parameter name="values" type="T...">
</parameter>
</constructor>
<method name="getGetter"
@@ -20772,8 +20741,8 @@
visibility="public"
>
</method>
-<method name="getTarget"
- return="java.lang.Object"
+<method name="setEvaluator"
+ return="void"
abstract="false"
native="false"
synchronized="false"
@@ -20782,6 +20751,8 @@
deprecated="not deprecated"
visibility="public"
>
+<parameter name="evaluator" type="android.animation.TypeEvaluator">
+</parameter>
</method>
<method name="setGetter"
return="void"
@@ -20822,7 +20793,7 @@
<parameter name="setter" type="java.lang.reflect.Method">
</parameter>
</method>
-<method name="setTarget"
+<method name="setValues"
return="void"
abstract="false"
native="false"
@@ -20832,7 +20803,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="target" type="java.lang.Object">
+<parameter name="values" type="T...">
</parameter>
</method>
</class>
@@ -27498,6 +27469,17 @@
visibility="public"
>
</method>
+<method name="getShowsDialog"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getTheme"
return="int"
abstract="false"
@@ -27561,6 +27543,19 @@
<parameter name="cancelable" type="boolean">
</parameter>
</method>
+<method name="setShowsDialog"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="showsDialog" type="boolean">
+</parameter>
+</method>
<method name="setStyle"
return="void"
abstract="false"
@@ -27583,7 +27578,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="activity" type="android.app.Activity">
@@ -27592,6 +27587,21 @@
</parameter>
</method>
<method name="show"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="manager" type="android.app.FragmentManager">
+</parameter>
+<parameter name="tag" type="java.lang.String">
+</parameter>
+</method>
+<method name="show"
return="int"
abstract="false"
native="false"
@@ -27601,8 +27611,6 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="activity" type="android.app.Activity">
-</parameter>
<parameter name="transaction" type="android.app.FragmentTransaction">
</parameter>
<parameter name="tag" type="java.lang.String">
@@ -199400,6 +199408,17 @@
visibility="protected"
>
</method>
+<method name="isMotionEventSplittingEnabled"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="measureChild"
return="void"
abstract="false"
@@ -199863,6 +199882,19 @@
<parameter name="animationListener" type="android.view.animation.Animation.AnimationListener">
</parameter>
</method>
+<method name="setMotionEventSplittingEnabled"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="split" type="boolean">
+</parameter>
+</method>
<method name="setOnHierarchyChangeListener"
return="void"
abstract="false"
@@ -225186,6 +225218,19 @@
<parameter name="modal" type="boolean">
</parameter>
</method>
+<method name="setOnDismissListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.widget.PopupWindow.OnDismissListener">
+</parameter>
+</method>
<method name="setOnItemClickListener"
return="void"
abstract="false"
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index fb60fdf..0f343ff 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -626,7 +626,7 @@
" [-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...]\n" +
" [--esn <EXTRA_KEY> ...]\n" +
" [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]\n" +
- " [-e|--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]\n" +
+ " [--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]\n" +
" [-n <COMPONENT>] [-f <FLAGS>]\n" +
" [--grant-read-uri-permission] [--grant-write-uri-permission]\n" +
" [--debug-log-resolution]\n" +
diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c
index 02c9cbc..8f3642c 100644
--- a/cmds/dumpstate/dumpstate.c
+++ b/cmds/dumpstate/dumpstate.c
@@ -39,6 +39,8 @@
static char cmdline_buf[16384] = "(unknown)";
static const char *dump_traces_path = NULL;
+static char screenshot_path[PATH_MAX] = "";
+
/* dumps the current system state to stdout */
static void dumpstate() {
time_t now = time(NULL);
@@ -76,6 +78,12 @@
dump_file("SLAB INFO", "/proc/slabinfo");
dump_file("ZONEINFO", "/proc/zoneinfo");
+ if (screenshot_path[0]) {
+ LOGI("taking screenshot\n");
+ run_command(NULL, 5, "su", "root", "screenshot", screenshot_path, NULL);
+ LOGI("wrote screenshot: %s\n", screenshot_path);
+ }
+
run_command("SYSTEM LOG", 20, "logcat", "-v", "time", "-d", "*:v", NULL);
/* show the traces we collected in main(), if that was done */
@@ -167,14 +175,15 @@
}
static void usage() {
- fprintf(stderr, "usage: dumpstate [-b file] [-d] [-e file] [-o file] [-s] "
- "[-z]\n"
- " -b: play sound file instead of vibrate, at beginning of job\n"
- " -d: append date to filename (requires -o)\n"
- " -e: play sound file instead of vibrate, at end of job\n"
+ fprintf(stderr, "usage: dumpstate [-b soundfile] [-e soundfile] [-o file [-d] [-p] [-z]] [-s]\n"
" -o: write to file (instead of stdout)\n"
+ " -d: append date to filename (requires -o)\n"
+ " -z: gzip output (requires -o)\n"
+ " -p: capture screenshot to filename.png (requires -o)\n"
" -s: write output to control socket (for init)\n"
- " -z: gzip output (requires -o)\n");
+ " -b: play sound file instead of vibrate, at beginning of job\n"
+ " -e: play sound file instead of vibrate, at end of job\n"
+ );
}
int main(int argc, char *argv[]) {
@@ -184,6 +193,7 @@
char* begin_sound = 0;
char* end_sound = 0;
int use_socket = 0;
+ int do_fb = 0;
LOGI("begin\n");
@@ -199,7 +209,7 @@
dump_traces_path = dump_vm_traces();
int c;
- while ((c = getopt(argc, argv, "b:de:ho:svz")) != -1) {
+ while ((c = getopt(argc, argv, "b:de:ho:svzp")) != -1) {
switch (c) {
case 'b': begin_sound = optarg; break;
case 'd': do_add_date = 1; break;
@@ -208,6 +218,7 @@
case 's': use_socket = 1; break;
case 'v': break; // compatibility no-op
case 'z': do_compress = 6; break;
+ case 'p': do_fb = 1; break;
case '?': printf("\n");
case 'h':
usage();
@@ -244,6 +255,10 @@
strftime(date, sizeof(date), "-%Y-%m-%d-%H-%M-%S", localtime(&now));
strlcat(path, date, sizeof(path));
}
+ if (do_fb) {
+ strlcpy(screenshot_path, path, sizeof(screenshot_path));
+ strlcat(screenshot_path, ".png", sizeof(screenshot_path));
+ }
strlcat(path, ".txt", sizeof(path));
if (do_compress) strlcat(path, ".gz", sizeof(path));
strlcpy(tmp_path, path, sizeof(tmp_path));
diff --git a/cmds/screenshot/Android.mk b/cmds/screenshot/Android.mk
new file mode 100644
index 0000000..99c7aeb
--- /dev/null
+++ b/cmds/screenshot/Android.mk
@@ -0,0 +1,16 @@
+ifneq ($(TARGET_SIMULATOR),true)
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := screenshot.c
+
+LOCAL_MODULE := screenshot
+
+LOCAL_SHARED_LIBRARIES := libcutils libz
+LOCAL_STATIC_LIBRARIES := libpng
+LOCAL_C_INCLUDES += external/zlib
+
+include $(BUILD_EXECUTABLE)
+
+endif
diff --git a/cmds/screenshot/screenshot.c b/cmds/screenshot/screenshot.c
new file mode 100644
index 0000000..46e6507
--- /dev/null
+++ b/cmds/screenshot/screenshot.c
@@ -0,0 +1,118 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <linux/fb.h>
+
+#include <zlib.h>
+#include <libpng/png.h>
+
+#include "private/android_filesystem_config.h"
+
+#define LOG_TAG "screenshot"
+#include <utils/Log.h>
+
+void take_screenshot(FILE *fb_in, FILE *fb_out) {
+ int fb;
+ char imgbuf[0x10000];
+ struct fb_var_screeninfo vinfo;
+ png_structp png;
+ png_infop info;
+ unsigned int r,c,rowlen;
+ unsigned int bytespp,offset;
+
+ fb = fileno(fb_in);
+ if(fb < 0) {
+ LOGE("failed to open framebuffer\n");
+ return;
+ }
+ fb_in = fdopen(fb, "r");
+
+ if(ioctl(fb, FBIOGET_VSCREENINFO, &vinfo) < 0) {
+ LOGE("failed to get framebuffer info\n");
+ return;
+ }
+ fcntl(fb, F_SETFD, FD_CLOEXEC);
+
+ png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (png == NULL) {
+ LOGE("failed png_create_write_struct\n");
+ fclose(fb_in);
+ return;
+ }
+
+ png_init_io(png, fb_out);
+ info = png_create_info_struct(png);
+ if (info == NULL) {
+ LOGE("failed png_create_info_struct\n");
+ png_destroy_write_struct(&png, NULL);
+ fclose(fb_in);
+ return;
+ }
+ if (setjmp(png_jmpbuf(png))) {
+ LOGE("failed png setjmp\n");
+ png_destroy_write_struct(&png, NULL);
+ fclose(fb_in);
+ return;
+ }
+
+ bytespp = vinfo.bits_per_pixel / 8;
+ png_set_IHDR(png, info,
+ vinfo.xres, vinfo.yres, vinfo.bits_per_pixel / 4,
+ PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+ png_write_info(png, info);
+
+ rowlen=vinfo.xres * bytespp;
+ if (rowlen > sizeof(imgbuf)) {
+ LOGE("crazy rowlen: %d\n", rowlen);
+ png_destroy_write_struct(&png, NULL);
+ fclose(fb_in);
+ return;
+ }
+
+ offset = vinfo.xoffset * bytespp + vinfo.xres * vinfo.yoffset * bytespp;
+ fseek(fb_in, offset, SEEK_SET);
+
+ for(r=0; r<vinfo.yres; r++) {
+ int len = fread(imgbuf, 1, rowlen, fb_in);
+ if (len <= 0) break;
+ png_write_row(png, (png_bytep)imgbuf);
+ }
+
+ png_write_end(png, info);
+ fclose(fb_in);
+ png_destroy_write_struct(&png, NULL);
+}
+
+int main(int argc, char**argv) {
+ FILE *png = NULL;
+ FILE *fb_in = NULL;
+ if (argc < 2) {
+ fprintf(stderr, "usage: screenshot filename.png\n");
+ exit(1);
+ }
+ fb_in = fopen("/dev/graphics/fb0", "r");
+ if (!fb_in) {
+ fprintf(stderr, "error: could not read framebuffer\n");
+ exit(1);
+ }
+
+ /* switch to non-root user and group */
+ gid_t groups[] = { AID_LOG, AID_SDCARD_RW };
+ setgroups(sizeof(groups)/sizeof(groups[0]), groups);
+ setuid(AID_SHELL);
+
+ png = fopen(argv[1], "w");
+ if (!png) {
+ fprintf(stderr, "error: writing file %s: %s\n", argv[1], strerror(errno));
+ exit(1);
+ }
+
+ take_screenshot(fb_in, png);
+
+ exit(0);
+}
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index cd6531b..951d104 100755
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -26,6 +26,7 @@
import android.view.animation.Interpolator;
import java.util.ArrayList;
+import java.util.HashMap;
/**
* This class provides a simple timing engine for running animations
@@ -39,7 +40,7 @@
* out of an animation. This behavior can be changed by calling
* {@link Animator#setInterpolator(Interpolator)}.</p>
*/
-public class Animator extends Animatable {
+public class Animator<T> extends Animatable {
/**
* Internal constants
@@ -164,12 +165,6 @@
// How long the animation should last in ms
private long mDuration;
- // The value that the animation should start from, set in the constructor
- private Object mValueFrom;
-
- // The value that the animation should animate to, set in the constructor
- private Object mValueTo;
-
// The amount of time in ms to delay starting the animation after start() is called
private long mStartDelay = 0;
@@ -195,29 +190,22 @@
private Interpolator mInterpolator = sDefaultInterpolator;
/**
- * The type evaluator used to calculate the animated values. This evaluator is determined
- * automatically based on the type of the start/end objects passed into the constructor,
- * but the system only knows about the primitive types int, double, and float. Any other
- * type will need to set the evaluator to a custom evaluator for that type.
- */
- private TypeEvaluator mEvaluator;
-
- /**
* The set of listeners to be sent events through the life of an animation.
*/
private ArrayList<AnimatorUpdateListener> mUpdateListeners = null;
/**
- * The current value calculated by the animation. The value is calculated in animateFraction(),
- * prior to calling the setter (if set) and sending out the onAnimationUpdate() callback
- * to the update listeners.
+ * The property/value sets being animated.
*/
- private Object mAnimatedValue = null;
+ HashMap<String, PropertyValuesHolder> mValues;
/**
- * The set of keyframes (time/value pairs) that define this animation.
+ * This value is used in the simple/common case of animating just one value; the user
+ * may call getAnimatedValue(), which should return the value of the first (and only)
+ * ProeprtyValuesHolder animated value, which is looked up using this string.
*/
- private KeyframeSet mKeyframeSet = null;
+ String mFirstPropertyName;
+
/**
* The type of the values, as determined by the valueFrom/valueTo properties.
@@ -267,144 +255,120 @@
int valueType = a.getInt(com.android.internal.R.styleable.Animator_valueType,
VALUE_TYPE_FLOAT);
+ Object valueFrom = null;
+ Object valueTo = null;
+
switch (valueType) {
case VALUE_TYPE_FLOAT:
- mValueFrom = a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f);
- mValueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f);
+ valueFrom = a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f);
+ valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f);
mValueType = float.class;
break;
case VALUE_TYPE_INT:
- mValueFrom = a.getInt(com.android.internal.R.styleable.Animator_valueFrom, 0);
- mValueTo = a.getInt(com.android.internal.R.styleable.Animator_valueTo, 0);
+ valueFrom = a.getInt(com.android.internal.R.styleable.Animator_valueFrom, 0);
+ valueTo = a.getInt(com.android.internal.R.styleable.Animator_valueTo, 0);
mValueType = int.class;
break;
case VALUE_TYPE_DOUBLE:
- mValueFrom = (double)
+ valueFrom = (double)
a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f);
- mValueTo = (double)
+ valueTo = (double)
a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f);
mValueType = double.class;
break;
case VALUE_TYPE_COLOR:
- mValueFrom = a.getInt(com.android.internal.R.styleable.Animator_valueFrom, 0);
- mValueTo = a.getInt(com.android.internal.R.styleable.Animator_valueTo, 0);
- mEvaluator = new RGBEvaluator();
+ valueFrom = a.getInt(com.android.internal.R.styleable.Animator_valueFrom, 0);
+ valueTo = a.getInt(com.android.internal.R.styleable.Animator_valueTo, 0);
+ setEvaluator(new RGBEvaluator());
mValueType = int.class;
break;
case VALUE_TYPE_CUSTOM:
// TODO: How to get an 'Object' value?
- mValueFrom = a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f);
- mValueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f);
+ valueFrom = a.getFloat(com.android.internal.R.styleable.Animator_valueFrom, 0f);
+ valueTo = a.getFloat(com.android.internal.R.styleable.Animator_valueTo, 0f);
mValueType = Object.class;
break;
}
+ mValues = new HashMap<String, PropertyValuesHolder>(1);
+ mFirstPropertyName = "";
+ PropertyValuesHolder valuesHolder = new PropertyValuesHolder(mFirstPropertyName,
+ valueFrom, valueTo);
+ mValues.put(mFirstPropertyName, valuesHolder);
+
mRepeatCount = a.getInt(com.android.internal.R.styleable.Animator_repeatCount, mRepeatCount);
mRepeatMode = a.getInt(com.android.internal.R.styleable.Animator_repeatMode, RESTART);
a.recycle();
}
- private Animator(long duration, Object valueFrom, Object valueTo, Class valueType) {
+
+ /**
+ * Constructs an Animator object with the specified duration and set of
+ * values. If the values are a set of PropertyValuesHolder objects, then these objects
+ * define the potentially multiple properties being animated and the values the properties are
+ * animated between. Otherwise, the values define a single set of values animated between.
+ *
+ * @param duration The length of the animation, in milliseconds.
+ * @param values The set of values to animate between. If these values are not
+ * PropertyValuesHolder objects, then there should be more than one value, since the values
+ * determine the interval to animate between.
+ */
+ public Animator(long duration, T...values) {
mDuration = duration;
- mValueFrom = valueFrom;
- mValueTo= valueTo;
- this.mValueType = valueType;
+ if (values.length > 0) {
+ setValues(values);
+ }
+ }
+
+ public void setValues(PropertyValuesHolder... values) {
+ int numValues = values.length;
+ mValues = new HashMap<String, PropertyValuesHolder>(numValues);
+ for (int i = 0; i < numValues; ++i) {
+ PropertyValuesHolder valuesHolder = (PropertyValuesHolder) values[i];
+ mValues.put(valuesHolder.getPropertyName(), valuesHolder);
+ }
+ if (numValues > 0 && values[0] != null) {
+ mFirstPropertyName = ((PropertyValuesHolder) values[0]).getPropertyName();
+ } else {
+ mFirstPropertyName = "";
+ }
}
/**
- * This constructor takes a set of {@link Keyframe} objects that define the values
- * for the animation, along with the times at which those values will hold true during
- * the animation.
+ * Sets the values to animate between for this animation. If <code>values</code> is
+ * a set of PropertyValuesHolder objects, these objects will become the set of properties
+ * animated and the values that those properties are animated between. Otherwise, this method
+ * will set only one set of values for the Animator. Also, if the values are not
+ * PropertyValuesHolder objects and if there are already multiple sets of
+ * values defined for this Animator via
+ * more than one PropertyValuesHolder objects, this method will set the values for
+ * the first of those objects.
*
- * @param duration The length of the animation, in milliseconds.
- * @param keyframes The set of keyframes that define the time/value pairs for the animation.
+ * @param values The set of values to animate between.
*/
- public Animator(long duration, Keyframe...keyframes) {
- mDuration = duration;
- mKeyframeSet = new KeyframeSet(keyframes);
- mValueType = keyframes[0].getType();
- }
-
- /**
- * A constructor that takes <code>float</code> values.
- *
- * @param duration The length of the animation, in milliseconds.
- * @param valueFrom The initial value of the property when the animation begins.
- * @param valueTo The value to which the property will animate.
- */
- public Animator(long duration, float valueFrom, float valueTo) {
- this(duration, valueFrom, valueTo, float.class);
- }
-
- /**
- * A constructor that takes <code>int</code> values.
- *
- * @param duration The length of the animation, in milliseconds.
- * @param valueFrom The initial value of the property when the animation begins.
- * @param valueTo The value to which the property will animate.
- */
- public Animator(long duration, int valueFrom, int valueTo) {
- this(duration, valueFrom, valueTo, int.class);
- }
-
- /**
- * A constructor that takes <code>double</code> values.
- *
- * @param duration The length of the animation, in milliseconds.
- * @param valueFrom The initial value of the property when the animation begins.
- * @param valueTo The value to which the property will animate.
- */
- public Animator(long duration, double valueFrom, double valueTo) {
- this(duration, valueFrom, valueTo, double.class);
- }
-
- /**
- * A constructor that takes <code>Object</code> values.
- *
- * @param duration The length of the animation, in milliseconds.
- * @param valueFrom The initial value of the property when the animation begins.
- * @param valueTo The value to which the property will animate.
- */
- public Animator(long duration, Object valueFrom, Object valueTo) {
- this(duration, valueFrom, valueTo,
- (valueFrom != null) ? valueFrom.getClass() : valueTo.getClass());
- }
-
- /**
- * Internal constructor that takes a single <code>float</code> value.
- * This constructor is called by PropertyAnimator.
- *
- * @param duration The length of the animation, in milliseconds.
- * @param valueFrom The initial value of the property when the animation begins.
- * @param valueTo The value to which the property will animate.
- */
- Animator(long duration, float valueTo) {
- this(duration, null, valueTo, float.class);
- }
-
- /**
- * Internal constructor that takes a single <code>int</code> value.
- * This constructor is called by PropertyAnimator.
- *
- * @param duration The length of the animation, in milliseconds.
- * @param valueFrom The initial value of the property when the animation begins.
- * @param valueTo The value to which the property will animate.
- */
- Animator(long duration, int valueTo) {
- this(duration, null, valueTo, int.class);
- }
-
- /**
- * Internal constructor that takes a single <code>double</code> value.
- * This constructor is called by PropertyAnimator.
- *
- * @param duration The length of the animation, in milliseconds.
- * @param valueFrom The initial value of the property when the animation begins.
- * @param valueTo The value to which the property will animate.
- */
- Animator(long duration, double valueTo) {
- this(duration, null, valueTo, double.class);
+ public void setValues(T... values) {
+ if (values[0] instanceof PropertyValuesHolder) {
+ int numValues = values.length;
+ mValues = new HashMap<String, PropertyValuesHolder>(numValues);
+ for (int i = 0; i < numValues; ++i) {
+ PropertyValuesHolder valuesHolder = (PropertyValuesHolder) values[i];
+ mValues.put(valuesHolder.getPropertyName(), valuesHolder);
+ }
+ if (numValues > 0 && values[0] != null) {
+ mFirstPropertyName = ((PropertyValuesHolder) values[0]).getPropertyName();
+ } else {
+ mFirstPropertyName = "";
+ }
+ } else {
+ if (mValues == null) {
+ setValues(new PropertyValuesHolder[]{
+ new PropertyValuesHolder("", (Object[])values)});
+ } else {
+ PropertyValuesHolder valuesHolder = mValues.get(mFirstPropertyName);
+ valuesHolder.setValues(values);
+ }
+ }
}
/**
@@ -418,9 +382,8 @@
* that internal mechanisms for the animation are set up correctly.</p>
*/
void initAnimation() {
- if (mEvaluator == null) {
- mEvaluator = (mValueType == int.class) ? sIntEvaluator :
- (mValueType == double.class) ? sDoubleEvaluator : sFloatEvaluator;
+ for (PropertyValuesHolder pvHolder: mValues.values()) {
+ pvHolder.init();
}
mCurrentIteration = 0;
mInitialized = true;
@@ -599,67 +562,6 @@
}
/**
- * Gets the set of keyframes that define this animation.
- *
- * @return KeyframeSet The set of keyframes for this animation.
- */
- KeyframeSet getKeyframes() {
- return mKeyframeSet;
- }
-
- /**
- * Gets the value that this animation will start from.
- *
- * @return Object The starting value for the animation.
- */
- public Object getValueFrom() {
- if (mKeyframeSet != null) {
- return mKeyframeSet.mKeyframes.get(0).getValue();
- }
- return mValueFrom;
- }
-
- /**
- * Sets the value that this animation will start from.
- */
- public void setValueFrom(Object valueFrom) {
- if (mKeyframeSet != null) {
- Keyframe kf = mKeyframeSet.mKeyframes.get(0);
- kf.setValue(valueFrom);
- } else {
- mValueFrom = valueFrom;
- }
- }
-
- /**
- * Gets the value that this animation will animate to.
- *
- * @return Object The ending value for the animation.
- */
- public Object getValueTo() {
- if (mKeyframeSet != null) {
- int numKeyframes = mKeyframeSet.mKeyframes.size();
- return mKeyframeSet.mKeyframes.get(numKeyframes - 1).getValue();
- }
- return mValueTo;
- }
-
- /**
- * Sets the value that this animation will animate to.
- *
- * @return Object The ending value for the animation.
- */
- public void setValueTo(Object valueTo) {
- if (mKeyframeSet != null) {
- int numKeyframes = mKeyframeSet.mKeyframes.size();
- Keyframe kf = mKeyframeSet.mKeyframes.get(numKeyframes - 1);
- kf.setValue(valueTo);
- } else {
- mValueTo = valueTo;
- }
- }
-
- /**
* The amount of time, in milliseconds, between each frame of the animation. This is a
* requested time that the animation will attempt to honor, but the actual delay between
* frames may be different, depending on system load and capabilities. This is a static
@@ -673,17 +575,33 @@
}
/**
- * The most recent value calculated by this <code>Animator</code> for the property
- * being animated. This value is only sensible while the animation is running. The main
+ * The most recent value calculated by this <code>Animator</code> when there is just one
+ * property being animated. This value is only sensible while the animation is running. The main
* purpose for this read-only property is to retrieve the value from the <code>Animator</code>
* during a call to {@link AnimatorUpdateListener#onAnimationUpdate(Animator)}, which
* is called during each animation frame, immediately after the value is calculated.
*
* @return animatedValue The value most recently calculated by this <code>Animator</code> for
- * the property specified in the constructor.
+ * the single property being animated. If there are several properties being animated
+ * (specified by several PropertyValuesHolder objects in the constructor), this function
+ * returns the animated value for the first of those objects.
*/
public Object getAnimatedValue() {
- return mAnimatedValue;
+ return getAnimatedValue(mFirstPropertyName);
+ }
+
+ /**
+ * The most recent value calculated by this <code>Animator</code> for <code>propertyName</code>.
+ * The main purpose for this read-only property is to retrieve the value from the
+ * <code>Animator</code> during a call to
+ * {@link AnimatorUpdateListener#onAnimationUpdate(Animator)}, which
+ * is called during each animation frame, immediately after the value is calculated.
+ *
+ * @return animatedValue The value most recently calculated for the named property
+ * by this <code>Animator</code>.
+ */
+ public Object getAnimatedValue(String propertyName) {
+ return mValues.get(mFirstPropertyName).getAnimatedValue();
}
/**
@@ -781,14 +699,26 @@
* For example, when running an animation on color values, the {@link RGBEvaluator}
* should be used to get correct RGB color interpolation.
*
+ * <p>If this Animator has only one set of values being animated between, this evaluator
+ * will be used for that set. If there are several sets of values being animated, which is
+ * the case if PropertyValuesHOlder objects were set on the Animator, then the evaluator
+ * is assigned just to the first PropertyValuesHolder object.</p>
+ *
* @param value the evaluator to be used this animation
*/
public void setEvaluator(TypeEvaluator value) {
- if (value != null) {
- mEvaluator = value;
+ if (value != null && mValues != null) {
+ mValues.get(mFirstPropertyName).setEvaluator(value);
}
}
+ /**
+ * Start the animation playing. This version of start() takes a boolean flag that indicates
+ * whether the animation should play in reverse. The flag is usually false, but may be set
+ * to true if called from the reverse() method/
+ *
+ * @param playBackwards Whether the Animator should start playing in reverse.
+ */
private void start(boolean playBackwards) {
mPlayingBackwards = playBackwards;
mPlayingState = STOPPED;
@@ -998,10 +928,8 @@
*/
void animateValue(float fraction) {
fraction = mInterpolator.getInterpolation(fraction);
- if (mKeyframeSet != null) {
- mAnimatedValue = mKeyframeSet.getValue(fraction, mEvaluator);
- } else {
- mAnimatedValue = mEvaluator.evaluate(fraction, mValueFrom, mValueTo);
+ for (PropertyValuesHolder valuesHolder : mValues.values()) {
+ valuesHolder.calculateValue(fraction);
}
if (mUpdateListeners != null) {
int numListeners = mUpdateListeners.size();
diff --git a/core/java/android/animation/DoubleEvaluator.java b/core/java/android/animation/DoubleEvaluator.java
index 86e3f22..e46eb37 100644
--- a/core/java/android/animation/DoubleEvaluator.java
+++ b/core/java/android/animation/DoubleEvaluator.java
@@ -36,7 +36,7 @@
* <code>fraction</code> parameter.
*/
public Object evaluate(float fraction, Object startValue, Object endValue) {
- double startDouble = (Double) startValue;
- return startDouble + fraction * ((Double) endValue - startDouble);
+ double startDouble = ((Number) startValue).doubleValue();
+ return startDouble + fraction * (((Number) endValue).doubleValue() - startDouble);
}
}
\ No newline at end of file
diff --git a/core/java/android/animation/FloatEvaluator.java b/core/java/android/animation/FloatEvaluator.java
index 29a6f71..9e2054d 100644
--- a/core/java/android/animation/FloatEvaluator.java
+++ b/core/java/android/animation/FloatEvaluator.java
@@ -36,7 +36,7 @@
* <code>fraction</code> parameter.
*/
public Object evaluate(float fraction, Object startValue, Object endValue) {
- float startFloat = (Float) startValue;
- return startFloat + fraction * ((Float) endValue - startFloat);
+ float startFloat = ((Number) startValue).floatValue();
+ return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
}
}
\ No newline at end of file
diff --git a/core/java/android/animation/IntEvaluator.java b/core/java/android/animation/IntEvaluator.java
index 7a2911a..7288927 100644
--- a/core/java/android/animation/IntEvaluator.java
+++ b/core/java/android/animation/IntEvaluator.java
@@ -36,7 +36,7 @@
* <code>fraction</code> parameter.
*/
public Object evaluate(float fraction, Object startValue, Object endValue) {
- int startInt = (Integer) startValue;
- return (int) (startInt + fraction * ((Integer) endValue - startInt));
+ int startInt = ((Number) startValue).intValue();
+ return (int) (startInt + fraction * (((Number) endValue).intValue() - startInt));
}
}
\ No newline at end of file
diff --git a/core/java/android/animation/PropertyAnimator.java b/core/java/android/animation/PropertyAnimator.java
index eac9798..9366a71 100644
--- a/core/java/android/animation/PropertyAnimator.java
+++ b/core/java/android/animation/PropertyAnimator.java
@@ -33,35 +33,13 @@
* are then determined internally and the animation will call these functions as necessary to
* animate the property.
*/
-public final class PropertyAnimator extends Animator {
+public final class PropertyAnimator<T> extends Animator<T> {
// The target object on which the property exists, set in the constructor
private Object mTarget;
private String mPropertyName;
- private Method mGetter = null;
-
- // The property setter that is assigned internally, based on the propertyName passed into
- // the constructor
- private Method mSetter;
-
- // These maps hold all property entries for a particular class. This map
- // is used to speed up property/setter/getter lookups for a given class/property
- // combination. No need to use reflection on the combination more than once.
- private static final HashMap<Object, HashMap<String, Method>> sSetterPropertyMap =
- new HashMap<Object, HashMap<String, Method>>();
- private static final HashMap<Object, HashMap<String, Method>> sGetterPropertyMap =
- new HashMap<Object, HashMap<String, Method>>();
-
- // This lock is used to ensure that only one thread is accessing the property maps
- // at a time.
- private ReentrantReadWriteLock propertyMapLock = new ReentrantReadWriteLock();
-
- // Used to pass single value to varargs parameter in setter invocation
- private Object[] mTmpValueArray = new Object[1];
-
-
/**
* Sets the name of the property that will be animated. This name is used to derive
* a setter function that will be called to set animated values.
@@ -75,9 +53,19 @@
* <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to
* the setter function will fail.</p>
*
+ * <p>If this PropertyAnimator has been set up to animate several properties together,
+ * using more than one PropertyValuesHolder objects, then setting the propertyName simply
+ * sets the propertyName in the first of those PropertyValuesHolder objects.</p>
+ *
* @param propertyName The name of the property being animated.
*/
public void setPropertyName(String propertyName) {
+ if (mValues != null) {
+ // should always be the case
+ PropertyValuesHolder valuesHolder = mValues.get(mFirstPropertyName);
+ valuesHolder.setPropertyName(propertyName);
+ mFirstPropertyName = propertyName;
+ }
mPropertyName = propertyName;
}
@@ -94,67 +82,6 @@
}
/**
- * Sets the <code>Method</code> that is called with the animated values calculated
- * during the animation. Setting the setter method is an alternative to supplying a
- * {@link #setPropertyName(String) propertyName} from which the method is derived. This
- * approach is more direct, and is especially useful when a function must be called that does
- * not correspond to the convention of <code>setName()</code>. For example, if a function
- * called <code>offset()</code> is to be called with the animated values, there is no way
- * to tell <code>PropertyAnimator</code> how to call that function simply through a property
- * name, so a setter method should be supplied instead.
- *
- * <p>Note that the setter function must take the same parameter type as the
- * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to
- * the setter function will fail.</p>
- *
- * @param setter The setter method that should be called with the animated values.
- */
- public void setSetter(Method setter) {
- mSetter = setter;
- }
-
- /**
- * Gets the <code>Method</code> that is called with the animated values calculated
- * during the animation.
- */
- public Method getSetter() {
- return mSetter;
- }
-
- /**
- * Sets the <code>Method</code> that is called to get unsupplied <code>valueFrom</code> or
- * <code>valueTo</code> properties. Setting the getter method is an alternative to supplying a
- * {@link #setPropertyName(String) propertyName} from which the method is derived. This
- * approach is more direct, and is especially useful when a function must be called that does
- * not correspond to the convention of <code>setName()</code>. For example, if a function
- * called <code>offset()</code> is to be called to get an initial value, there is no way
- * to tell <code>PropertyAnimator</code> how to call that function simply through a property
- * name, so a getter method should be supplied instead.
- *
- * <p>Note that the getter method is only called whether supplied here or derived
- * from the property name, if one of <code>valueFrom</code> or <code>valueTo</code> are
- * null. If both of those values are non-null, then there is no need to get one of the
- * values and the getter is not called.
- *
- * <p>Note that the getter function must return the same parameter type as the
- * <code>valueFrom</code> and <code>valueTo</code> properties (whichever of them are
- * non-null), otherwise the call to the getter function will fail.</p>
- *
- * @param getter The getter method that should be called to get initial animation values.
- */
- public void setGetter(Method getter) {
- mGetter = getter;
- }
-
- /**
- * Gets the <code>Method</code> that is called to get unsupplied <code>valueFrom</code> or
- * <code>valueTo</code> properties.
- */
- public Method getGetter() {
- return mGetter;
- }
-
- /**
* Creates a new animation whose parameters come from the specified context and
* attributes set.
*
@@ -167,7 +94,8 @@
TypedArray a = context.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.PropertyAnimator);
- mPropertyName = a.getString(com.android.internal.R.styleable.PropertyAnimator_propertyName);
+ setPropertyName(a.getString(
+ com.android.internal.R.styleable.PropertyAnimator_propertyName));
a.recycle();
@@ -203,224 +131,42 @@
}
/**
- * A constructor that takes <code>float</code> values. When this constructor
- * is called, the system expects to find a setter for <code>propertyName</code> on
- * the target object that takes a <code>float</code> value.
+ * A constructor that takes a single property name and set of values. This constructor is
+ * used in the simple case of animating a single property.
*
* @param duration The length of the animation, in milliseconds.
* @param target The object whose property is to be animated. This object should
- * have a public function on it called <code>setName()</code>, where <code>name</code> is
- * the name of the property passed in as the <code>propertyName</code> parameter.
- * @param propertyName The name of the property on the <code>target</code> object
- * that will be animated. Given this name, the constructor will search for a
- * setter on the target object with the name <code>setPropertyName</code>. For example,
- * if the constructor is called with <code>propertyName = "foo"</code>, then the
- * target object should have a setter function with the name <code>setFoo()</code>.
- * @param valueFrom The initial value of the property when the animation begins.
- * @param valueTo The value to which the property will animate.
+ * have a public method on it called <code>setName()</code>, where <code>name</code> is
+ * the value of the <code>propertyName</code> parameter.
+ * @param propertyName The name of the property being animated.
+ * @param values The set of values to animate between. If there is only one value, it
+ * is assumed to be the final value being animated to, and the initial value will be
+ * derived on the fly.
*/
- public PropertyAnimator(int duration, Object target, String propertyName,
- float valueFrom, float valueTo) {
- super(duration, valueFrom, valueTo);
+ public PropertyAnimator(long duration, Object target, String propertyName, T...values) {
+ super(duration, (T[]) values);
mTarget = target;
- mPropertyName = propertyName;
+ setPropertyName(propertyName);
}
/**
- * A constructor that takes a single <code>float</code> value, which is the value that the
- * target object will animate to. When this constructor
- * is called, the system expects to find a setter for <code>propertyName</code> on
- * the target object that takes a value of the same type as the <code>Object</code>s. The
- * system also expects to find a similar getter function with which to derive the starting
- * value for the animation.
+ * A constructor that takes <code>PropertyValueHolder</code> values. This constructor should
+ * be used when animating several properties at once with the same PropertyAnimator, since
+ * PropertyValuesHolder allows you to associate a set of animation values with a property
+ * name.
*
* @param duration The length of the animation, in milliseconds.
* @param target The object whose property is to be animated. This object should
- * have a public function on it called <code>setName()</code>, where <code>name</code> is
- * the name of the property passed in as the <code>propertyName</code> parameter.
- * @param propertyName The name of the property on the <code>target</code> object
- * that will be animated. Given this name, the constructor will search for a
- * setter on the target object with the name <code>setPropertyName</code>. For example,
- * if the constructor is called with <code>propertyName = "foo"</code>, then the
- * target object should have a setter function with the name <code>setFoo()</code>.
- * @param valueTo The value to which the property will animate.
+ * have public methods on it called <code>setName()</code>, where <code>name</code> is
+ * the name of the property passed in as the <code>propertyName</code> parameter for
+ * each of the PropertyValuesHolder objects.
+ * @param values The PropertyValuesHolder objects which hold each the property name and values
+ * to animate that property between.
*/
- public PropertyAnimator(int duration, Object target, String propertyName, float valueTo) {
- super(duration, valueTo);
+ public PropertyAnimator(long duration, Object target, PropertyValuesHolder...values) {
+ super(duration);
+ setValues(values);
mTarget = target;
- mPropertyName = propertyName;
- }
-
- /**
- * A constructor that takes <code>int</code> values. When this constructor
- * is called, the system expects to find a setter for <code>propertyName</code> on
- * the target object that takes a <code>int</code> value.
- *
- * @param duration The length of the animation, in milliseconds.
- * @param target The object whose property is to be animated. This object should
- * have a public function on it called <code>setName()</code>, where <code>name</code> is
- * the name of the property passed in as the <code>propertyName</code> parameter.
- * @param propertyName The name of the property on the <code>target</code> object
- * that will be animated. Given this name, the constructor will search for a
- * setter on the target object with the name <code>setPropertyName</code>. For example,
- * if the constructor is called with <code>propertyName = "foo"</code>, then the
- * target object should have a setter function with the name <code>setFoo()</code>.
- * @param valueFrom The initial value of the property when the animation begins.
- * @param valueTo The value to which the property will animate.
- */
- public PropertyAnimator(int duration, Object target, String propertyName,
- int valueFrom, int valueTo) {
- super(duration, valueFrom, valueTo);
- mTarget = target;
- mPropertyName = propertyName;
- }
-
- /**
- * A constructor that takes a single <code>int</code> value, which is the value that the
- * target object will animate to. When this constructor
- * is called, the system expects to find a setter for <code>propertyName</code> on
- * the target object that takes a value of the same type as the <code>Object</code>s. The
- * system also expects to find a similar getter function with which to derive the starting
- * value for the animation.
- *
- * @param duration The length of the animation, in milliseconds.
- * @param target The object whose property is to be animated. This object should
- * have a public function on it called <code>setName()</code>, where <code>name</code> is
- * the name of the property passed in as the <code>propertyName</code> parameter.
- * @param propertyName The name of the property on the <code>target</code> object
- * that will be animated. Given this name, the constructor will search for a
- * setter on the target object with the name <code>setPropertyName</code>. For example,
- * if the constructor is called with <code>propertyName = "foo"</code>, then the
- * target object should have a setter function with the name <code>setFoo()</code>.
- * @param valueTo The value to which the property will animate.
- */
- public PropertyAnimator(int duration, Object target, String propertyName, int valueTo) {
- super(duration, valueTo);
- mTarget = target;
- mPropertyName = propertyName;
- }
-
- /**
- * A constructor that takes <code>double</code> values. When this constructor
- * is called, the system expects to find a setter for <code>propertyName</code> on
- * the target object that takes a <code>double</code> value.
- *
- * @param duration The length of the animation, in milliseconds.
- * @param target The object whose property is to be animated. This object should
- * have a public function on it called <code>setName()</code>, where <code>name</code> is
- * the name of the property passed in as the <code>propertyName</code> parameter.
- * @param propertyName The name of the property on the <code>target</code> object
- * that will be animated. Given this name, the constructor will search for a
- * setter on the target object with the name <code>setPropertyName</code>. For example,
- * if the constructor is called with <code>propertyName = "foo"</code>, then the
- * target object should have a setter function with the name <code>setFoo()</code>.
- * @param valueFrom The initial value of the property when the animation begins.
- * @param valueTo The value to which the property will animate.
- */
- public PropertyAnimator(int duration, Object target, String propertyName,
- double valueFrom, double valueTo) {
- super(duration, valueFrom, valueTo);
- mTarget = target;
- mPropertyName = propertyName;
- }
-
- /**
- * A constructor that takes a single <code>double</code> value, which is the value that the
- * target object will animate to. When this constructor
- * is called, the system expects to find a setter for <code>propertyName</code> on
- * the target object that takes a value of the same type as the <code>Object</code>s. The
- * system also expects to find a similar getter function with which to derive the starting
- * value for the animation.
- *
- * @param duration The length of the animation, in milliseconds.
- * @param target The object whose property is to be animated. This object should
- * have a public function on it called <code>setName()</code>, where <code>name</code> is
- * the name of the property passed in as the <code>propertyName</code> parameter.
- * @param propertyName The name of the property on the <code>target</code> object
- * that will be animated. Given this name, the constructor will search for a
- * setter on the target object with the name <code>setPropertyName</code>. For example,
- * if the constructor is called with <code>propertyName = "foo"</code>, then the
- * target object should have a setter function with the name <code>setFoo()</code>.
- * @param valueTo The value to which the property will animate.
- */
- public PropertyAnimator(int duration, Object target, String propertyName, double valueTo) {
- super(duration, valueTo);
- mTarget = target;
- mPropertyName = propertyName;
- }
-
- /**
- * A constructor that takes <code>Object</code> values. When this constructor
- * is called, the system expects to find a setter for <code>propertyName</code> on
- * the target object that takes a value of the same type as the <code>Object</code>s.
- *
- * @param duration The length of the animation, in milliseconds.
- * @param target The object whose property is to be animated. This object should
- * have a public function on it called <code>setName()</code>, where <code>name</code> is
- * the name of the property passed in as the <code>propertyName</code> parameter.
- * @param propertyName The name of the property on the <code>target</code> object
- * that will be animated. Given this name, the constructor will search for a
- * setter on the target object with the name <code>setPropertyName</code>. For example,
- * if the constructor is called with <code>propertyName = "foo"</code>, then the
- * target object should have a setter function with the name <code>setFoo()</code>.
- * @param valueFrom The initial value of the property when the animation begins.
- * @param valueTo The value to which the property will animate.
- */
- public PropertyAnimator(int duration, Object target, String propertyName,
- Object valueFrom, Object valueTo) {
- super(duration, valueFrom, valueTo);
- mTarget = target;
- mPropertyName = propertyName;
- }
-
- /**
- * A constructor that takes a single <code>Object</code> value, which is the value that the
- * target object will animate to. When this constructor
- * is called, the system expects to find a setter for <code>propertyName</code> on
- * the target object that takes a value of the same type as the <code>Object</code>s. The
- * system also expects to find a similar getter function with which to derive the starting
- * value for the animation.
- *
- * @param duration The length of the animation, in milliseconds.
- * @param target The object whose property is to be animated. This object should
- * have a public function on it called <code>setName()</code>, where <code>name</code> is
- * the name of the property passed in as the <code>propertyName</code> parameter.
- * @param propertyName The name of the property on the <code>target</code> object
- * that will be animated. Given this name, the constructor will search for a
- * setter on the target object with the name <code>setPropertyName</code>. For example,
- * if the constructor is called with <code>propertyName = "foo"</code>, then the
- * target object should have a setter function with the name <code>setFoo()</code>.
- * @param valueTo The value to which the property will animate.
- */
- public PropertyAnimator(int duration, Object target, String propertyName, Object valueTo) {
- this(duration, target, propertyName, null, valueTo);
- }
-
- /**
- * A constructor that takes <code>Keyframe</code>s. When this constructor
- * is called, the system expects to find a setter for <code>propertyName</code> on
- * the target object that takes a value of the same type as that returned from
- * {@link Keyframe#getType()}.
- * .
- *
- * @param duration The length of the animation, in milliseconds.
- * @param target The object whose property is to be animated. This object should
- * have a public function on it called <code>setName()</code>, where <code>name</code> is
- * the name of the property passed in as the <code>propertyName</code> parameter.
- * @param propertyName The name of the property on the <code>target</code> object
- * that will be animated. Given this name, the constructor will search for a
- * setter on the target object with the name <code>setPropertyName</code>. For example,
- * if the constructor is called with <code>propertyName = "foo"</code>, then the
- * target object should have a setter function with the name <code>setFoo()</code>.
- * @param keyframes The set of keyframes that define the times and values for the animation.
- * These keyframes should be ordered in increasing time value, should have a starting
- * keyframe with a fraction of 0 and and ending keyframe with a fraction of 1.
- */
- public PropertyAnimator(int duration, Object target, String propertyName,
- Keyframe...keyframes) {
- super(duration, keyframes);
- mTarget = target;
- mPropertyName = propertyName;
}
/**
@@ -438,64 +184,8 @@
@Override
void initAnimation() {
super.initAnimation();
- if (mSetter == null) {
- try {
- // Have to lock property map prior to reading it, to guard against
- // another thread putting something in there after we've checked it
- // but before we've added an entry to it
- propertyMapLock.writeLock().lock();
- HashMap<String, Method> propertyMap = sSetterPropertyMap.get(mTarget);
- if (propertyMap != null) {
- mSetter = propertyMap.get(mPropertyName);
- }
- if (mSetter == null) {
- mSetter = getPropertyFunction("set", mValueType);
- if (propertyMap == null) {
- propertyMap = new HashMap<String, Method>();
- sSetterPropertyMap.put(mTarget, propertyMap);
- }
- propertyMap.put(mPropertyName, mSetter);
- }
- } finally {
- propertyMapLock.writeLock().unlock();
- }
- }
- if (getKeyframes() == null && (getValueFrom() == null || getValueTo() == null)) {
- // Need to set up the getter if not set by the user, then call it
- // to get the initial values
- if (mGetter == null) {
- try {
- propertyMapLock.writeLock().lock();
- HashMap<String, Method> propertyMap = sGetterPropertyMap.get(mTarget);
- if (propertyMap != null) {
- mGetter = propertyMap.get(mPropertyName);
- }
- if (mGetter == null) {
- mGetter = getPropertyFunction("get", null);
- if (propertyMap == null) {
- propertyMap = new HashMap<String, Method>();
- sGetterPropertyMap.put(mTarget, propertyMap);
- }
- propertyMap.put(mPropertyName, mGetter);
- }
- } finally {
- propertyMapLock.writeLock().unlock();
- }
- }
- try {
- if (getValueFrom() == null) {
- setValueFrom(mGetter.invoke(mTarget));
- }
- if (getValueTo() == null) {
- setValueTo(mGetter.invoke(mTarget));
- }
- } catch (IllegalArgumentException e) {
- Log.e("PropertyAnimator", e.toString());
- } catch (IllegalAccessException e) {
- Log.e("PropertyAnimator", e.toString());
- } catch (InvocationTargetException e) {
- Log.e("PropertyAnimator", e.toString());
- }
+ for (PropertyValuesHolder valuesHolder : mValues.values()) {
+ valuesHolder.setupSetterAndGetter(mTarget);
}
}
@@ -533,23 +223,8 @@
@Override
void animateValue(float fraction) {
super.animateValue(fraction);
- if (mSetter != null) {
- try {
- mTmpValueArray[0] = getAnimatedValue();
- mSetter.invoke(mTarget, mTmpValueArray);
- } catch (InvocationTargetException e) {
- Log.e("PropertyAnimator", e.toString());
- } catch (IllegalAccessException e) {
- Log.e("PropertyAnimator", e.toString());
- }
+ for (PropertyValuesHolder valuesHolder : mValues.values()) {
+ valuesHolder.setAnimatedValue(mTarget);
}
}
-
- @Override
- public String toString() {
- return "Animator: target: " + this.mTarget + "\n" +
- " property: " + mPropertyName + "\n" +
- " from: " + getValueFrom() + "\n" +
- " to: " + getValueTo();
- }
}
diff --git a/core/java/android/animation/PropertyValuesHolder.java b/core/java/android/animation/PropertyValuesHolder.java
new file mode 100644
index 0000000..05e0bc1
--- /dev/null
+++ b/core/java/android/animation/PropertyValuesHolder.java
@@ -0,0 +1,514 @@
+/*
+ * 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.animation;
+
+import android.util.Log;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ */
+public class PropertyValuesHolder<T> {
+
+ /**
+ * The name of the property associated with the values. This need not be a real property,
+ * unless this object is being used with PropertyAnimator. But this is the name by which
+ * aniamted values are looked up with getAnimatedValue(String) in Animator.
+ */
+ private String mPropertyName;
+
+ /**
+ * The setter function, if needed. PropertyAnimator hands off this functionality to
+ * PropertyValuesHolder, since it holds all of the per-property information. This
+ * property can be manually set via setSetter(). Otherwise, it is automatically
+ * derived when the animation starts in setupSetterAndGetter() if using PropertyAnimator.
+ */
+ private Method mSetter = null;
+
+ /**
+ * The getter function, if needed. PropertyAnimator hands off this functionality to
+ * PropertyValuesHolder, since it holds all of the per-property information. This
+ * property can be manually set via setSetter(). Otherwise, it is automatically
+ * derived when the animation starts in setupSetterAndGetter() if using PropertyAnimator.
+ * The getter is only derived and used if one of the values is null.
+ */
+ private Method mGetter = null;
+
+ /**
+ * The type of values supplied. This information is used both in deriving the setter/getter
+ * functions and in deriving the type of TypeEvaluator.
+ */
+ private Class mValueType;
+
+ /**
+ * The set of keyframes (time/value pairs) that define this animation.
+ */
+ private KeyframeSet mKeyframeSet = null;
+
+
+ // type evaluators for the three primitive types handled by this implementation
+ private static final TypeEvaluator sIntEvaluator = new IntEvaluator();
+ private static final TypeEvaluator sFloatEvaluator = new FloatEvaluator();
+ private static final TypeEvaluator sDoubleEvaluator = new DoubleEvaluator();
+
+ // We try several different types when searching for appropriate setter/getter functions.
+ // The caller may have supplied values in a type that does not match the setter/getter
+ // functions (such as the integers 0 and 1 to represent floating point values for alpha).
+ // Also, the use of generics in constructors means that we end up with the Object versions
+ // of primitive types (Float vs. float). But most likely, the setter/getter functions
+ // will take primitive types instead.
+ // So we supply an ordered array of other types to try before giving up.
+ private static Class[] FLOAT_VARIANTS = {float.class, Float.class, double.class, int.class,
+ Double.class, Integer.class};
+ private static Class[] INTEGER_VARIANTS = {int.class, Integer.class, float.class, double.class,
+ Float.class, Double.class};
+ private static Class[] DOUBLE_VARIANTS = {double.class, Double.class, float.class, int.class,
+ Float.class, Integer.class};
+
+ // These maps hold all property entries for a particular class. This map
+ // is used to speed up property/setter/getter lookups for a given class/property
+ // combination. No need to use reflection on the combination more than once.
+ private static final HashMap<Class, HashMap<String, Method>> sSetterPropertyMap =
+ new HashMap<Class, HashMap<String, Method>>();
+ private static final HashMap<Class, HashMap<String, Method>> sGetterPropertyMap =
+ new HashMap<Class, HashMap<String, Method>>();
+
+ // This lock is used to ensure that only one thread is accessing the property maps
+ // at a time.
+ private ReentrantReadWriteLock propertyMapLock = new ReentrantReadWriteLock();
+
+ // Used to pass single value to varargs parameter in setter invocation
+ private Object[] mTmpValueArray = new Object[1];
+
+ /**
+ * The type evaluator used to calculate the animated values. This evaluator is determined
+ * automatically based on the type of the start/end objects passed into the constructor,
+ * but the system only knows about the primitive types int, double, and float. Any other
+ * type will need to set the evaluator to a custom evaluator for that type.
+ */
+ private TypeEvaluator mEvaluator;
+
+ /**
+ * The value most recently calculated by calculateValue(). This is set during
+ * that function and might be retrieved later either by Animator.animatedValue() or
+ * by the property-setting logic in PropertyAnimator.animatedValue().
+ */
+ private Object mAnimatedValue;
+
+ /**
+ * Constructs a PropertyValuesHolder object with just a set of values. This constructor
+ * is typically not used when animating objects with PropertyAnimator, because that
+ * object needs distinct and meaningful property names. Simpler animations of one
+ * set of values using Animator may use this constructor, however, because no
+ * distinguishing name is needed.
+ * @param values The set of values to animate between. If there is only one value, it
+ * is assumed to be the final value being animated to, and the initial value will be
+ * derived on the fly.
+ */
+ public PropertyValuesHolder(T...values) {
+ this(null, values);
+ }
+
+ /**
+ * Constructs a PropertyValuesHolder object with the specified property name and set of
+ * values. These values can be of any type, but the type should be consistent so that
+ * an appropriate {@link android.animation.TypeEvaluator} can be found that matches
+ * the common type.
+ * <p>If there is only one value, it is assumed to be the end value of an animation,
+ * and an initial value will be derived, if possible, by calling a getter function
+ * on the object. Also, if any value is null, the value will be filled in when the animation
+ * starts in the same way. This mechanism of automatically getting null values only works
+ * if the PropertyValuesHolder object is used in conjunction
+ * {@link android.animation.PropertyAnimator}, and with a getter function either
+ * derived automatically from <code>propertyName</code> or set explicitly via
+ * {@link #setGetter(java.lang.reflect.Method)}, since otherwise PropertyValuesHolder has
+ * no way of determining what the value should be.
+ * @param propertyName The name of the property associated with this set of values. This
+ * can be the actual property name to be used when using a PropertyAnimator object, or
+ * just a name used to get animated values, such as if this object is used with an
+ * Animator object.
+ * @param values The set of values to animate between.
+ */
+ public PropertyValuesHolder(String propertyName, T... values) {
+ mPropertyName = propertyName;
+ setValues(values);
+ }
+
+ /**
+ * Sets the values being animated between.
+ * If there is only one value, it is assumed to be the end value of an animation,
+ * and an initial value will be derived, if possible, by calling a getter function
+ * on the object. Also, if any value is null, the value will be filled in when the animation
+ * starts in the same way. This mechanism of automatically getting null values only works
+ * if the PropertyValuesHolder object is used in conjunction
+ * {@link android.animation.PropertyAnimator}, and with a getter function either
+ * derived automatically from <code>propertyName</code> or set explicitly via
+ * {@link #setGetter(java.lang.reflect.Method)}, since otherwise PropertyValuesHolder has
+ * no way of determining what the value should be.
+ * @param values The set of values to animate between.
+ */
+ public void setValues(T... values) {
+ int numKeyframes = values.length;
+ for (int i = 0; i < numKeyframes; ++i) {
+ if (values[i] != null) {
+ Class thisValueType = values[i].getClass();
+ if (mValueType == null) {
+ mValueType = thisValueType;
+ } else {
+ if (thisValueType != mValueType) {
+ if (mValueType == Integer.class &&
+ (thisValueType == Float.class || thisValueType == Double.class)) {
+ mValueType = thisValueType;
+ } else if (mValueType == Float.class && thisValueType == Double.class) {
+ mValueType = thisValueType;
+ }
+ }
+ }
+ }
+ }
+ Keyframe keyframes[] = new Keyframe[Math.max(numKeyframes,2)];
+ if (mValueType.equals(Keyframe.class)) {
+ mValueType = ((Keyframe)values[0]).getType();
+ for (int i = 0; i < numKeyframes; ++i) {
+ keyframes[i] = (Keyframe)values[i];
+ }
+ } else {
+ if (numKeyframes == 1) {
+ keyframes[0] = new Keyframe(0f, null);
+ keyframes[1] = new Keyframe(1f, values[0]);
+ } else {
+ keyframes[0] = new Keyframe(0f, values[0]);
+ for (int i = 1; i < numKeyframes; ++i) {
+ if (values[i] != null && (values[i].getClass() != mValueType)) {
+
+ }
+ keyframes[i] = new Keyframe((float) i / (numKeyframes - 1), values[i]);
+ }
+ }
+ }
+ mKeyframeSet = new KeyframeSet(keyframes);
+ }
+
+
+
+ /**
+ * Determine the setter or getter function using the JavaBeans convention of setFoo or
+ * getFoo for a property named 'foo'. This function figures out what the name of the
+ * function should be and uses reflection to find the Method with that name on the
+ * target object.
+ *
+ * @param targetClass The class to search for the method
+ * @param prefix "set" or "get", depending on whether we need a setter or getter.
+ * @param valueType The type of the parameter (in the case of a setter). This type
+ * is derived from the values set on this PropertyValuesHolder. This type is used as
+ * a first guess at the parameter type, but we check for methods with several different
+ * types to avoid problems with slight mis-matches between supplied values and actual
+ * value types used on the setter.
+ * @return Method the method associated with mPropertyName.
+ */
+ private Method getPropertyFunction(Class targetClass, String prefix, Class valueType) {
+ // TODO: faster implementation...
+ Method returnVal = null;
+ String firstLetter = mPropertyName.substring(0, 1);
+ String theRest = mPropertyName.substring(1);
+ firstLetter = firstLetter.toUpperCase();
+ String methodName = prefix + firstLetter + theRest;
+ Class args[] = null;
+ if (valueType == null) {
+ try {
+ returnVal = targetClass.getMethod(methodName, args);
+ } catch (NoSuchMethodException e) {
+ Log.e("PropertyValuesHolder",
+ "Couldn't find no-arg method for property " + mPropertyName + ": " + e);
+ }
+ } else {
+ args = new Class[1];
+ Class typeVariants[];
+ if (mValueType.equals(Float.class)) {
+ typeVariants = FLOAT_VARIANTS;
+ } else if (mValueType.equals(Integer.class)) {
+ typeVariants = INTEGER_VARIANTS;
+ } else if (mValueType.equals(Double.class)) {
+ typeVariants = DOUBLE_VARIANTS;
+ } else {
+ typeVariants = new Class[1];
+ typeVariants[0] = mValueType;
+ }
+ for (Class typeVariant : typeVariants) {
+ args[0] = typeVariant;
+ try {
+ returnVal = targetClass.getMethod(methodName, args);
+ return returnVal;
+ } catch (NoSuchMethodException e) {
+ // Swallow the error and keep trying other variants
+ }
+ }
+ }
+ // If we got here, then no appropriate function was found
+ Log.e("PropertyValuesHolder",
+ "Couldn't find setter/getter for property " + mPropertyName +
+ "with value type "+ mValueType);
+ return returnVal;
+ }
+
+
+ /**
+ * Returns the setter or getter requested. This utility function checks whether the
+ * requested method exists in the propertyMapMap cache. If not, it calls another
+ * utility function to request the Method from the targetClass directly.
+ * @param targetClass The Class on which the requested method should exist.
+ * @param propertyMapMap The cache of setters/getters derived so far.
+ * @param prefix "set" or "get", for the setter or getter.
+ * @param valueType The type of parameter passed into the method (null for getter).
+ * @return Method the method associated with mPropertyName.
+ */
+ private Method setupSetterOrGetter(Class targetClass,
+ HashMap<Class, HashMap<String, Method>> propertyMapMap,
+ String prefix, Class valueType) {
+ Method setterOrGetter = null;
+ try {
+ // Have to lock property map prior to reading it, to guard against
+ // another thread putting something in there after we've checked it
+ // but before we've added an entry to it
+ // TODO: can we store the setter/getter per Class instead of per Object?
+ propertyMapLock.writeLock().lock();
+ HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass);
+ if (propertyMap != null) {
+ setterOrGetter = propertyMap.get(mPropertyName);
+ }
+ if (setterOrGetter == null) {
+ setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);
+ if (propertyMap == null) {
+ propertyMap = new HashMap<String, Method>();
+ propertyMapMap.put(targetClass, propertyMap);
+ }
+ propertyMap.put(mPropertyName, setterOrGetter);
+ }
+ } finally {
+ propertyMapLock.writeLock().unlock();
+ }
+ return setterOrGetter;
+ }
+
+ /**
+ * Utility function to get the setter from targetClass
+ * @param targetClass The Class on which the requested method should exist.
+ */
+ private void setupSetter(Class targetClass) {
+ mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", mValueType);
+ }
+
+ /**
+ * Utility function to get the getter from targetClass
+ */
+ private void setupGetter(Class targetClass) {
+ mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get", null);
+ }
+
+ /**
+ * Internal function (called from PropertyAnimator) to set up the setter and getter
+ * prior to running the animation. If the setter has not been manually set for this
+ * object, it will be derived automatically given the property name, target object, and
+ * types of values supplied. If no getter has been set, it will be supplied iff any of the
+ * supplied values was null. If there is a null value, then the getter (supplied or derived)
+ * will be called to set those null values to the current value of the property
+ * on the target object.
+ * @param target The object on which the setter (and possibly getter) exist.
+ */
+ void setupSetterAndGetter(Object target) {
+ Class targetClass = target.getClass();
+ if (mSetter == null) {
+ setupSetter(targetClass);
+ }
+ for (Keyframe kf : mKeyframeSet.mKeyframes) {
+ if (kf.getValue() == null) {
+ if (mGetter == null) {
+ setupGetter(targetClass);
+ }
+ try {
+ kf.setValue((T) mGetter.invoke(target));
+ } catch (InvocationTargetException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ } catch (IllegalAccessException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ }
+ }
+ }
+ }
+
+ /**
+ * Internal function to set the value on the target object, using the setter set up
+ * earlier on this PropertyValuesHolder object. This function is called by PropertyAnimator
+ * to handle turning the value calculated by Animator into a value set on the object
+ * according to the name of the property.
+ * @param target The target object on which the value is set
+ */
+ void setAnimatedValue(Object target) {
+ if (mSetter != null) {
+ try {
+ mTmpValueArray[0] = mAnimatedValue;
+ mSetter.invoke(target, mTmpValueArray);
+ } catch (InvocationTargetException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ } catch (IllegalAccessException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ }
+ }
+ }
+
+ /**
+ * Internal function, called by Animator, to set up the TypeEvaluator that will be used
+ * to calculate animated values.
+ */
+ void init() {
+ if (mEvaluator == null) {
+ mEvaluator = (mValueType == int.class) ? sIntEvaluator :
+ (mValueType == double.class) ? sDoubleEvaluator : sFloatEvaluator;
+ }
+ }
+
+ /**
+ * The TypeEvaluator will the automatically determined based on the type of values
+ * supplied to PropertyValuesHolder. The evaluator can be manually set, however, if so
+ * desired. This may be important in cases where either the type of the values supplied
+ * do not match the way that they should be interpolated between, or if the values
+ * are of a custom type or one not currently understood by the animation system. Currently,
+ * only values of type float, double, and int (and their Object equivalents, Float, Double,
+ * and Integer) are correctly interpolated; all other types require setting a TypeEvaluator.
+ * @param evaluator
+ */
+ public void setEvaluator(TypeEvaluator evaluator) {
+ mEvaluator = evaluator;
+ }
+
+ /**
+ * Function used to calculate the value according to the evaluator set up for
+ * this PropertyValuesHolder object. This function is called by Animator.animateValue().
+ *
+ * @param fraction The elapsed, interpolated fraction of the animation.
+ * @return The calculated value at this point in the animation.
+ */
+ Object calculateValue(float fraction) {
+ mAnimatedValue = mKeyframeSet.getValue(fraction, mEvaluator);
+ return mAnimatedValue;
+ }
+
+ /**
+ * Sets the <code>Method</code> that is called with the animated values calculated
+ * during the animation. Setting the setter method is an alternative to supplying a
+ * {@link #setPropertyName(String) propertyName} from which the method is derived. This
+ * approach is more direct, and is especially useful when a function must be called that does
+ * not correspond to the convention of <code>setName()</code>. For example, if a function
+ * called <code>offset()</code> is to be called with the animated values, there is no way
+ * to tell <code>PropertyAnimator</code> how to call that function simply through a property
+ * name, so a setter method should be supplied instead.
+ *
+ * <p>Note that the setter function must take the same parameter type as the
+ * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to
+ * the setter function will fail.</p>
+ *
+ * @param setter The setter method that should be called with the animated values.
+ */
+ public void setSetter(Method setter) {
+ mSetter = setter;
+ }
+
+ /**
+ * Gets the <code>Method</code> that is called with the animated values calculated
+ * during the animation.
+ */
+ public Method getSetter() {
+ return mSetter;
+ }
+
+ /**
+ * Sets the <code>Method</code> that is called to get unsupplied <code>valueFrom</code> or
+ * <code>valueTo</code> properties. Setting the getter method is an alternative to supplying a
+ * {@link #setPropertyName(String) propertyName} from which the method is derived. This
+ * approach is more direct, and is especially useful when a function must be called that does
+ * not correspond to the convention of <code>setName()</code>. For example, if a function
+ * called <code>offset()</code> is to be called to get an initial value, there is no way
+ * to tell <code>PropertyAnimator</code> how to call that function simply through a property
+ * name, so a getter method should be supplied instead.
+ *
+ * <p>Note that the getter method is only called whether supplied here or derived
+ * from the property name, if one of <code>valueFrom</code> or <code>valueTo</code> are
+ * null. If both of those values are non-null, then there is no need to get one of the
+ * values and the getter is not called.
+ *
+ * <p>Note that the getter function must return the same parameter type as the
+ * <code>valueFrom</code> and <code>valueTo</code> properties (whichever of them are
+ * non-null), otherwise the call to the getter function will fail.</p>
+ *
+ * @param getter The getter method that should be called to get initial animation values.
+ */
+ public void setGetter(Method getter) {
+ mGetter = getter;
+ }
+
+ /**
+ * Gets the <code>Method</code> that is called to get unsupplied <code>valueFrom</code> or
+ * <code>valueTo</code> properties.
+ */
+ public Method getGetter() {
+ return mGetter;
+ }
+
+ /**
+ * Sets the name of the property that will be animated. This name is used to derive
+ * a setter function that will be called to set animated values.
+ * For example, a property name of <code>foo</code> will result
+ * in a call to the function <code>setFoo()</code> on the target object. If either
+ * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
+ * also be derived and called.
+ *
+ * <p>Note that the setter function derived from this property name
+ * must take the same parameter type as the
+ * <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to
+ * the setter function will fail.</p>
+ *
+ * @param propertyName The name of the property being animated.
+ */
+ public void setPropertyName(String propertyName) {
+ mPropertyName = propertyName;
+ }
+
+ /**
+ * Gets the name of the property that will be animated. This name will be used to derive
+ * a setter function that will be called to set animated values.
+ * For example, a property name of <code>foo</code> will result
+ * in a call to the function <code>setFoo()</code> on the target object. If either
+ * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
+ * also be derived and called.
+ */
+ public String getPropertyName() {
+ return mPropertyName;
+ }
+
+ /**
+ * Internal function, called by Animator and PropertyAnimator, to retrieve the value
+ * most recently calculated in calculateValue().
+ * @return
+ */
+ Object getAnimatedValue() {
+ return mAnimatedValue;
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 1c8c73d..d8e249e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3343,10 +3343,6 @@
Slog.e(TAG, "Failed to find provider info for " + name);
return null;
}
- if (holder.permissionFailure != null) {
- throw new SecurityException("Permission " + holder.permissionFailure
- + " required for provider " + name);
- }
IContentProvider prov = installProvider(context, holder.provider,
holder.info, true);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 2786372..e34bad2 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2882,6 +2882,7 @@
boolean returnValue;
boolean hasListeners;
+ boolean changesMade = false;
List<String> keysModified = null;
Set<OnSharedPreferenceChangeListener> listeners = null;
@@ -2895,17 +2896,31 @@
synchronized (this) {
if (mClear) {
- mMap.clear();
+ if (!mMap.isEmpty()) {
+ changesMade = true;
+ mMap.clear();
+ }
mClear = false;
}
for (Entry<String, Object> e : mModified.entrySet()) {
String k = e.getKey();
Object v = e.getValue();
- if (v == this) {
- mMap.remove(k);
+ if (v == this) { // magic value for a removal mutation
+ if (mMap.containsKey(k)) {
+ mMap.remove(k);
+ changesMade = true;
+ }
} else {
- mMap.put(k, v);
+ boolean isSame = false;
+ if (mMap.containsKey(k)) {
+ Object existingValue = mMap.get(k);
+ isSame = existingValue != null && existingValue.equals(v);
+ }
+ if (!isSame) {
+ mMap.put(k, v);
+ changesMade = true;
+ }
}
if (hasListeners) {
@@ -2916,7 +2931,7 @@
mModified.clear();
}
- returnValue = writeFileLocked();
+ returnValue = writeFileLocked(changesMade);
}
if (hasListeners) {
@@ -2961,9 +2976,16 @@
return str;
}
- private boolean writeFileLocked() {
+ private boolean writeFileLocked(boolean changesMade) {
// Rename the current file so it may be used as a backup during the next read
if (mFile.exists()) {
+ if (!changesMade) {
+ // If the file already exists, but no changes were
+ // made to the underlying map, it's wasteful to
+ // re-write the file. Return as if we wrote it
+ // out.
+ return true;
+ }
if (!mBackupFile.exists()) {
if (!mFile.renameTo(mBackupFile)) {
Log.e(TAG, "Couldn't rename file " + mFile
diff --git a/core/java/android/app/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java
index f780e1d..8ba480d 100644
--- a/core/java/android/app/DatePickerDialog.java
+++ b/core/java/android/app/DatePickerDialog.java
@@ -34,6 +34,9 @@
/**
* A simple dialog containing an {@link android.widget.DatePicker}.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-datepicker.html">Date Picker
+ * tutorial</a>.</p>
*/
public class DatePickerDialog extends AlertDialog implements OnClickListener,
OnDateChangedListener {
diff --git a/core/java/android/app/DialogFragment.java b/core/java/android/app/DialogFragment.java
index 50e7421..e8dfac9 100644
--- a/core/java/android/app/DialogFragment.java
+++ b/core/java/android/app/DialogFragment.java
@@ -36,6 +36,102 @@
* content of the dialog. Alternatively, they can override
* {@link #onCreateDialog(Bundle)} to create an entirely custom dialog, such
* as an AlertDialog, with its own content.
+ *
+ * <p>Topics covered here:
+ * <ol>
+ * <li><a href="#Lifecycle">Lifecycle</a>
+ * <li><a href="#BasicDialog">Basic Dialog</a>
+ * <li><a href="#AlertDialog">Alert Dialog</a>
+ * <li><a href="#DialogOrEmbed">Selecting Between Dialog or Embedding</a>
+ * </ol>
+ *
+ * <a name="Lifecycle"></a>
+ * <h3>Lifecycle</h3>
+ *
+ * <p>DialogFragment does various things to keep the fragment's lifecycle
+ * driving it, instead of the Dialog. Note that dialogs are generally
+ * autonomous entities -- they are their own window, receiving their own
+ * input events, and often deciding on their own when to disappear (by
+ * receiving a back key event or the user clicking on a button).
+ *
+ * <p>DialogFragment needs to ensure that what is happening with the Fragment
+ * and Dialog states remains consistent. To do this, it watches for dismiss
+ * events from the dialog and takes are of removing its own state when they
+ * happen. This means you should use {@link #show(FragmentManager, String)}
+ * or {@link #show(FragmentTransaction, String)} to add an instance of
+ * DialogFragment to your UI, as these keep track of how DialogFragment should
+ * remove itself when the dialog is dismissed.
+ *
+ * <a name="BasicDialog"></a>
+ * <h3>Basic Dialog</h3>
+ *
+ * <p>The simplest use of DialogFragment is as a floating container for the
+ * fragment's view hierarchy. A simple implementation may look like this:
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentDialog.java
+ * dialog}
+ *
+ * <p>An example showDialog() method on the Activity could be:
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentDialog.java
+ * add_dialog}
+ *
+ * <p>This removes any currently shown dialog, creates a new DialogFragment
+ * with an argument, and shows it as a new state on the back stack. When the
+ * transaction is popped, the current DialogFragment and its Dialog will be
+ * destroyed, and the previous one (if any) re-shown. Note that in this case
+ * DialogFragment will take care of popping the transaction of the Dialog
+ * is dismissed separately from it.
+ *
+ * <a name="AlertDialog"></a>
+ * <h3>Alert Dialog</h3>
+ *
+ * <p>Instead of (or in addition to) implementing {@link #onCreateView} to
+ * generate the view hierarchy inside of a dialog, you may implement
+ * {@link #onCreateDialog(Bundle)} to create your own custom Dialog object.
+ *
+ * <p>This is most useful for creating an {@link AlertDialog}, allowing you
+ * to display standard alerts to the user that are managed by a fragment.
+ * A simple example implementation of this is:
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentAlertDialog.java
+ * dialog}
+ *
+ * <p>The activity creating this fragment may have the following methods to
+ * show the dialog and receive results from it:
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentAlertDialog.java
+ * activity}
+ *
+ * <p>Note that in this case the fragment is not placed on the back stack, it
+ * is just added as an indefinitely running fragment. Because dialogs normally
+ * are modal, this will still operate as a back stack, since the dialog will
+ * capture user input until it is dismissed. When it is dismissed, DialogFragment
+ * will take care of removing itself from its fragment manager.
+ *
+ * <a name="DialogOrEmbed"></a>
+ * <h3>Selecting Between Dialog or Embedding</h3>
+ *
+ * <p>A DialogFragment can still optionally be used as a normal fragment, if
+ * desired. This is useful if you have a fragment that in some cases should
+ * be shown as a dialog and others embedded in a larger UI. This behavior
+ * will normally be automatically selected for you based on how you are using
+ * the fragment, but can be customized with {@link #setShowsDialog(boolean)}.
+ *
+ * <p>For example, here is a simple dialog fragment:
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentDialogOrActivity.java
+ * dialog}
+ *
+ * <p>An instance of this fragment can be created and shown as a dialog:
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentDialogOrActivity.java
+ * show_dialog}
+ *
+ * <p>It can also be added as content in a view hierarchy:
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentDialogOrActivity.java
+ * embed}
*/
public class DialogFragment extends Fragment
implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
@@ -70,11 +166,13 @@
private static final String SAVED_STYLE = "android:style";
private static final String SAVED_THEME = "android:theme";
private static final String SAVED_CANCELABLE = "android:cancelable";
+ private static final String SAVED_SHOWS_DIALOG = "android:showsDialog";
private static final String SAVED_BACK_STACK_ID = "android:backStackId";
int mStyle = STYLE_NORMAL;
int mTheme = 0;
boolean mCancelable = true;
+ boolean mShowsDialog = true;
int mBackStackId = -1;
Dialog mDialog;
@@ -109,16 +207,9 @@
}
/**
- * Display the dialog, adding the fragment to the given activity. This
- * is a convenience for explicitly creating a transaction, adding the
- * fragment to it with the given tag, and committing it. This does
- * <em>not</em> add the transaction to the back stack. When the fragment
- * is dismissed, a new transaction will be executed to remove it from
- * the activity.
- * @param activity The activity this fragment will be added to.
- * @param tag The tag for this fragment, as per
- * {@link FragmentTransaction#add(Fragment, String) FragmentTransaction.add}.
+ * @deprecated Please use {@link #show(FragmentManager, String)}.
*/
+ @Deprecated
public void show(Activity activity, String tag) {
FragmentTransaction ft = activity.openFragmentTransaction();
ft.add(this, tag);
@@ -126,16 +217,32 @@
}
/**
- * Display the dialog, adding the fragment to the given activity using
- * an existing transaction and then committing the transaction.
- * @param activity The activity this fragment will be added to.
+ * Display the dialog, adding the fragment to the given FragmentManager. This
+ * is a convenience for explicitly creating a transaction, adding the
+ * fragment to it with the given tag, and committing it. This does
+ * <em>not</em> add the transaction to the back stack. When the fragment
+ * is dismissed, a new transaction will be executed to remove it from
+ * the activity.
+ * @param manager The FragmentManager this fragment will be added to.
+ * @param tag The tag for this fragment, as per
+ * {@link FragmentTransaction#add(Fragment, String) FragmentTransaction.add}.
+ */
+ public void show(FragmentManager manager, String tag) {
+ FragmentTransaction ft = manager.openTransaction();
+ ft.add(this, tag);
+ ft.commit();
+ }
+
+ /**
+ * Display the dialog, adding the fragment using an existing transaction
+ * and then committing the transaction.
* @param transaction An existing transaction in which to add the fragment.
* @param tag The tag for this fragment, as per
* {@link FragmentTransaction#add(Fragment, String) FragmentTransaction.add}.
* @return Returns the identifier of the committed transaction, as per
* {@link FragmentTransaction#commit() FragmentTransaction.commit()}.
*/
- public int show(Activity activity, FragmentTransaction transaction, String tag) {
+ public int show(FragmentTransaction transaction, String tag) {
transaction.add(this, tag);
mRemoved = false;
mBackStackId = transaction.commit();
@@ -173,23 +280,67 @@
return mTheme;
}
+ /**
+ * Control whether the shown Dialog is cancelable. Use this instead of
+ * directly calling {@link Dialog#setCancelable(boolean)
+ * Dialog.setCancelable(boolean)}, because DialogFragment needs to change
+ * its behavior based on this.
+ *
+ * @param cancelable If true, the dialog is cancelable. The default
+ * is true.
+ */
public void setCancelable(boolean cancelable) {
mCancelable = cancelable;
if (mDialog != null) mDialog.setCancelable(cancelable);
}
+ /**
+ * Return the current value of {@link #setCancelable(boolean)}.
+ */
public boolean getCancelable() {
return mCancelable;
}
+ /**
+ * Controls whether this fragment should be shown in a dialog. If not
+ * set, no Dialog will be created in {@link #onActivityCreated(Bundle)},
+ * and the fragment's view hierarchy will thus not be added to it. This
+ * allows you to instead use it as a normal fragment (embedded inside of
+ * its activity).
+ *
+ * <p>This is normally set for you based on whether the fragment is
+ * associated with a container view ID passed to
+ * {@link FragmentTransaction#add(int, Fragment) FragmentTransaction.add(int, Fragment)}.
+ * If the fragment was added with a container, setShowsDialog will be
+ * initialized to false; otherwise, it will be true.
+ *
+ * @param showsDialog If true, the fragment will be displayed in a Dialog.
+ * If false, no Dialog will be created and the fragment's view hierarchly
+ * left undisturbed.
+ */
+ public void setShowsDialog(boolean showsDialog) {
+ mShowsDialog = showsDialog;
+ }
+
+ /**
+ * Return the current value of {@link #setShowsDialog(boolean)}.
+ */
+ public boolean getShowsDialog() {
+ return mShowsDialog;
+ }
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
+ mShowsDialog = mContainerId == 0;
+
if (savedInstanceState != null) {
- mStyle = savedInstanceState.getInt(SAVED_STYLE, mStyle);
- mTheme = savedInstanceState.getInt(SAVED_THEME, mTheme);
- mCancelable = savedInstanceState.getBoolean(SAVED_CANCELABLE, mCancelable);
- mBackStackId = savedInstanceState.getInt(SAVED_BACK_STACK_ID, mBackStackId);
+ mStyle = savedInstanceState.getInt(SAVED_STYLE, STYLE_NORMAL);
+ mTheme = savedInstanceState.getInt(SAVED_THEME, 0);
+ mCancelable = savedInstanceState.getBoolean(SAVED_CANCELABLE, true);
+ mShowsDialog = savedInstanceState.getBoolean(SAVED_SHOWS_DIALOG, mShowsDialog);
+ mBackStackId = savedInstanceState.getInt(SAVED_BACK_STACK_ID, -1);
}
}
@@ -209,6 +360,11 @@
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
+
+ if (!mShowsDialog) {
+ return;
+ }
+
mDialog = onCreateDialog(savedInstanceState);
mDestroyed = false;
switch (mStyle) {
@@ -258,10 +414,21 @@
outState.putBundle(SAVED_DIALOG_STATE_TAG, dialogState);
}
}
- outState.putInt(SAVED_STYLE, mStyle);
- outState.putInt(SAVED_THEME, mTheme);
- outState.putBoolean(SAVED_CANCELABLE, mCancelable);
- outState.putInt(SAVED_BACK_STACK_ID, mBackStackId);
+ if (mStyle != STYLE_NORMAL) {
+ outState.putInt(SAVED_STYLE, mStyle);
+ }
+ if (mTheme != 0) {
+ outState.putInt(SAVED_THEME, mTheme);
+ }
+ if (!mCancelable) {
+ outState.putBoolean(SAVED_CANCELABLE, mCancelable);
+ }
+ if (!mShowsDialog) {
+ outState.putBoolean(SAVED_SHOWS_DIALOG, mShowsDialog);
+ }
+ if (mBackStackId != -1) {
+ outState.putInt(SAVED_BACK_STACK_ID, mBackStackId);
+ }
}
@Override
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index b10a8a8..56cf399 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -849,6 +849,25 @@
mCalled = true;
}
+ /**
+ * Called to ask the fragment to save its current dynamic state, so it
+ * can later be reconstructed in a new instance of its process is
+ * restarted. If a new instance of the fragment later needs to be
+ * created, the data you place in the Bundle here will be available
+ * in the Bundle given to {@link #onCreate(Bundle)},
+ * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}, and
+ * {@link #onActivityCreated(Bundle)}.
+ *
+ * <p>This corresponds to {@link Activity#onSaveInstanceState(Bundle)
+ * Activity.onnSaveInstanceState(Bundle)} and most of the discussion there
+ * applies here as well. Note however: <em>this method may be called
+ * at any time before {@link #onDestroy()}</em>. There are many situations
+ * where a fragment may be mostly torn down (such as when placed on the
+ * back stack with no UI showing), but its state will not be saved until
+ * its owning activity actually needs to save its state.
+ *
+ * @param outState Bundle in which to place your saved state.
+ */
public void onSaveInstanceState(Bundle outState) {
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 0d35ba4..5514582 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -330,28 +330,19 @@
/** Information you can retrieve about a particular application. */
public static class ContentProviderHolder implements Parcelable {
public final ProviderInfo info;
- public final String permissionFailure;
public IContentProvider provider;
public boolean noReleaseNeeded;
public ContentProviderHolder(ProviderInfo _info) {
info = _info;
- permissionFailure = null;
}
- public ContentProviderHolder(ProviderInfo _info,
- String _permissionFailure) {
- info = _info;
- permissionFailure = _permissionFailure;
- }
-
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
info.writeToParcel(dest, 0);
- dest.writeString(permissionFailure);
if (provider != null) {
dest.writeStrongBinder(provider.asBinder());
} else {
@@ -373,7 +364,6 @@
private ContentProviderHolder(Parcel source) {
info = ProviderInfo.CREATOR.createFromParcel(source);
- permissionFailure = source.readString();
provider = ContentProviderNative.asInterface(
source.readStrongBinder());
noReleaseNeeded = source.readInt() != 0;
diff --git a/core/java/android/app/TimePickerDialog.java b/core/java/android/app/TimePickerDialog.java
index a04b9e9..521d41c 100644
--- a/core/java/android/app/TimePickerDialog.java
+++ b/core/java/android/app/TimePickerDialog.java
@@ -32,6 +32,9 @@
/**
* A dialog that prompts the user for the time of day using a {@link TimePicker}.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-timepicker.html">Time Picker
+ * tutorial</a>.</p>
*/
public class TimePickerDialog extends AlertDialog implements OnClickListener,
OnTimeChangedListener {
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 22bce05..88e0123 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -91,6 +91,7 @@
private String mReadPermission;
private String mWritePermission;
private PathPermission[] mPathPermissions;
+ private boolean mExported;
private Transport mTransport = new Transport();
@@ -274,9 +275,9 @@
final Context context = getContext();
final String rperm = getReadPermission();
final int pid = Binder.getCallingPid();
- if (rperm == null
+ if (mExported && (rperm == null
|| context.checkPermission(rperm, pid, uid)
- == PackageManager.PERMISSION_GRANTED) {
+ == PackageManager.PERMISSION_GRANTED)) {
return;
}
@@ -320,9 +321,9 @@
final Context context = getContext();
final String wperm = getWritePermission();
final int pid = Binder.getCallingPid();
- if (wperm == null
+ if (mExported && (wperm == null
|| context.checkPermission(wperm, pid, uid)
- == PackageManager.PERMISSION_GRANTED) {
+ == PackageManager.PERMISSION_GRANTED)) {
return true;
}
@@ -972,6 +973,7 @@
setReadPermission(info.readPermission);
setWritePermission(info.writePermission);
setPathPermissions(info.pathPermissions);
+ mExported = info.exported;
}
ContentProvider.this.onCreate();
}
diff --git a/core/java/android/content/res/ObbInfo.java b/core/java/android/content/res/ObbInfo.java
index b18d784..838c5ff 100644
--- a/core/java/android/content/res/ObbInfo.java
+++ b/core/java/android/content/res/ObbInfo.java
@@ -25,6 +25,9 @@
* @hide
*/
public class ObbInfo implements Parcelable {
+ /** Flag noting that this OBB is an overlay patch for a base OBB. */
+ public static final int OBB_OVERLAY = 1 << 0;
+
/**
* The name of the package to which the OBB file belongs.
*/
@@ -35,13 +38,26 @@
*/
public int version;
+ /**
+ * The flags relating to the OBB.
+ */
+ public int flags;
+
public ObbInfo() {
}
public String toString() {
- return "ObbInfo{"
- + Integer.toHexString(System.identityHashCode(this))
- + " packageName=" + packageName + ",version=" + version + "}";
+ StringBuilder sb = new StringBuilder();
+ sb.append("ObbInfo{");
+ sb.append(Integer.toHexString(System.identityHashCode(this)));
+ sb.append(" packageName=");
+ sb.append(packageName);
+ sb.append(",version=");
+ sb.append(version);
+ sb.append(",flags=");
+ sb.append(flags);
+ sb.append('}');
+ return sb.toString();
}
public int describeContents() {
@@ -51,6 +67,7 @@
public void writeToParcel(Parcel dest, int parcelableFlags) {
dest.writeString(packageName);
dest.writeInt(version);
+ dest.writeInt(flags);
}
public static final Parcelable.Creator<ObbInfo> CREATOR
@@ -67,5 +84,6 @@
private ObbInfo(Parcel source) {
packageName = source.readString();
version = source.readInt();
+ flags = source.readInt();
}
}
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index b0c149d..37fdeb6 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -708,9 +708,7 @@
outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID];
outValue.changingConfigurations = data[index+AssetManager.STYLE_CHANGING_CONFIGURATIONS];
outValue.density = data[index+AssetManager.STYLE_DENSITY];
- if (type == TypedValue.TYPE_STRING) {
- outValue.string = loadStringValueAt(index);
- }
+ outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null;
return true;
}
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index f6d237a..d2c3eaa 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -1525,7 +1525,22 @@
* Typically the atmospheric pressure is read from a
* {@link Sensor#TYPE_PRESSURE} sensor. The pressure at sea level must be
* known, usually it can be retrieved from airport databases in the
- * vicinity.
+ * vicinity. If unknown, you can use {@link #PRESSURE_STANDARD_ATMOSPHERE}
+ * as an approximation, but absolute altitudes won't be accurate.
+ * </p>
+ * <p>
+ * To calculate altitude differences, you must calculate the difference
+ * between the altitudes at both points. If you don't know the altitude
+ * as sea level, you can use {@link #PRESSURE_STANDARD_ATMOSPHERE} instead,
+ * which will give good results considering the range of pressure typically
+ * involved.
+ * </p>
+ * <p>
+ * <code><ul>
+ * float altitude_difference =
+ * getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, pressure_at_point2)
+ * - getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, pressure_at_point1);
+ * </ul></code>
* </p>
*
* @param p0 pressure at sea level
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 7c9effa..4a0296b 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -295,6 +295,8 @@
* file matches a package ID that is owned by the calling program's UID.
* That is, shared UID applications can obtain access to any other
* application's OBB that shares its UID.
+ * <p>
+ * STOPSHIP document more; discuss lack of guarantees of security
*
* @param filename the path to the OBB file
* @param key decryption key
@@ -319,6 +321,8 @@
* file matches a package ID that is owned by the calling program's UID.
* That is, shared UID applications can obtain access to any other
* application's OBB that shares its UID.
+ * <p>
+ * STOPSHIP document more; discuss lack of guarantees of security
*
* @param filename path to the OBB file
* @param force whether to kill any programs using this in order to unmount
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 4556995..276aa84 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -201,8 +201,8 @@
* </p>
* <p>
* The most important use case for Directories is search. A Directory provider is
- * expected to support at least {@link Contacts#CONTENT_FILTER_URI
- * Contacts#CONTENT_FILTER_URI}. If a Directory provider wants to participate
+ * expected to support at least {@link ContactsContract.Contacts#CONTENT_FILTER_URI
+ * Contacts.CONTENT_FILTER_URI}. If a Directory provider wants to participate
* in email and phone lookup functionalities, it should also implement
* {@link CommonDataKinds.Email#CONTENT_FILTER_URI CommonDataKinds.Email.CONTENT_FILTER_URI}
* and
@@ -225,7 +225,7 @@
* <li>The URI authority is replaced with the corresponding {@link #DIRECTORY_AUTHORITY}.</li>
* <li>The {@code accountName=} and {@code accountType=} parameters are added or
* replaced using the corresponding {@link #ACCOUNT_TYPE} and {@link #ACCOUNT_NAME} values.</li>
- * <li>If the URI is missing a {@link ContactsContract#REQUESTING_PACKAGE_PARAM_KEY}
+ * <li>If the URI is missing a ContactsContract.REQUESTING_PACKAGE_PARAM_KEY
* parameter, this parameter is added.</li>
* </ul>
* </p>
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index ab78aeb..1808656 100644
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -1256,12 +1256,32 @@
return mTetheringOn;
}
- public synchronized void setBluetoothTethering(boolean value, String uuid, String bridge) {
+ private BroadcastReceiver mTetheringReceiver = null;
+
+ public synchronized void setBluetoothTethering(boolean value,
+ final String uuid, final String bridge) {
mTetheringOn = value;
if (!value) {
disconnectPan();
}
- setBluetoothTetheringNative(value, uuid, bridge);
+
+ if (getBluetoothState() != BluetoothAdapter.STATE_ON && mTetheringOn) {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+ mTetheringReceiver = new BroadcastReceiver() {
+ @Override
+ public synchronized void onReceive(Context context, Intent intent) {
+ if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF)
+ == BluetoothAdapter.STATE_ON) {
+ setBluetoothTethering(true, uuid, bridge);
+ mContext.unregisterReceiver(mTetheringReceiver);
+ }
+ }
+ };
+ mContext.registerReceiver(mTetheringReceiver, filter);
+ } else {
+ setBluetoothTetheringNative(value, uuid, bridge);
+ }
}
public synchronized int getPanDeviceState(BluetoothDevice device) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f31a248..c13bb8c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -16,7 +16,6 @@
package android.view;
-import android.graphics.Camera;
import com.android.internal.R;
import com.android.internal.view.menu.MenuBuilder;
@@ -25,6 +24,7 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
+import android.graphics.Camera;
import android.graphics.Canvas;
import android.graphics.Interpolator;
import android.graphics.LinearGradient;
@@ -69,7 +69,6 @@
import android.view.inputmethod.InputMethodManager;
import android.widget.ScrollBarDrawable;
-import java.lang.ref.SoftReference;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
@@ -5419,37 +5418,6 @@
}
/**
- * This method detects whether the given event is inside the view and, if so,
- * handles it via the dispatchEvent(MotionEvent) method.
- *
- * @param ev The event that is being dispatched.
- * @param parentX The x location of the event in the parent's coordinates.
- * @param parentY The y location of the event in the parent's coordinates.
- * @return true if the event was inside this view, false otherwise.
- */
- boolean dispatchTouchEvent(MotionEvent ev, float parentX, float parentY) {
- float localX = parentX - mLeft;
- float localY = parentY - mTop;
- if (!hasIdentityMatrix() && mAttachInfo != null) {
- // non-identity matrix: transform the point into the view's coordinates
- final float[] localXY = mAttachInfo.mTmpTransformLocation;
- localXY[0] = localX;
- localXY[1] = localY;
- getInverseMatrix().mapPoints(localXY);
- localX = localXY[0];
- localY = localXY[1];
- }
- if (localX >= 0 && localY >= 0 && localX < (mRight - mLeft) && localY < (mBottom - mTop)) {
- // It would be safer to clone the event here but we don't for performance.
- // There are many subtle interactions in touch event dispatch; change at your own risk.
- mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
- ev.setLocation(localX, localY);
- return dispatchTouchEvent(ev);
- }
- return false;
- }
-
- /**
* Utility method to determine whether the given point, in local coordinates,
* is inside the view, where the area of the view is expanded by the slop factor.
* This method is called while processing touch-move events to determine if the event
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index e2f9c15..e2e3333 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -43,6 +43,7 @@
import android.view.animation.Transformation;
import java.util.ArrayList;
+import java.util.Arrays;
/**
* <p>
@@ -108,6 +109,9 @@
// Target of Motion events
private View mMotionTarget;
+ // Targets of MotionEvents in split mode
+ private SplitMotionTargets mSplitMotionTargets;
+
// Layout animation
private LayoutAnimationController mLayoutAnimationController;
private Animation.AnimationListener mAnimationListener;
@@ -242,6 +246,11 @@
protected static final int FLAG_DISALLOW_INTERCEPT = 0x80000;
/**
+ * When set, this ViewGroup will split MotionEvents to multiple child Views when appropriate.
+ */
+ private static final int FLAG_SPLIT_MOTION_EVENTS = 0x100000;
+
+ /**
* Indicates which types of drawing caches are to be kept in memory.
* This field should be made private, so it is hidden from the SDK.
* {@hide}
@@ -362,6 +371,9 @@
case R.styleable.ViewGroup_descendantFocusability:
setDescendantFocusability(DESCENDANT_FOCUSABILITY_FLAGS[a.getInt(attr, 0)]);
break;
+ case R.styleable.ViewGroup_splitMotionEvents:
+ setMotionEventSplittingEnabled(a.getBoolean(attr, false));
+ break;
}
}
@@ -843,6 +855,10 @@
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
+ if ((mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) == FLAG_SPLIT_MOTION_EVENTS) {
+ return dispatchSplitTouchEvent(ev);
+ }
+
final int action = ev.getAction();
final float xf = ev.getX();
final float yf = ev.getY();
@@ -872,7 +888,9 @@
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
|| child.getAnimation() != null) {
- if (child.dispatchTouchEvent(ev, scrolledXFloat, scrolledYFloat)) {
+ // Single dispatch always picks its target based on the initial down
+ // event's position - index 0
+ if (dispatchTouchEventIfInView(child, ev, 0)) {
mMotionTarget = child;
return true;
}
@@ -953,6 +971,276 @@
}
/**
+ * This method detects whether the pointer location at <code>pointerIndex</code> within
+ * <code>ev</code> is inside the specified view. If so, the transformed event is dispatched to
+ * <code>child</code>.
+ *
+ * @param child View to hit test against
+ * @param ev MotionEvent to test
+ * @param pointerIndex Index of the pointer within <code>ev</code> to test
+ * @return <code>false</code> if the hit test failed, or the result of
+ * <code>child.dispatchTouchEvent</code>
+ */
+ private boolean dispatchTouchEventIfInView(View child, MotionEvent ev, int pointerIndex) {
+ final float x = ev.getX(pointerIndex);
+ final float y = ev.getY(pointerIndex);
+ final float scrolledX = x + mScrollX;
+ final float scrolledY = y + mScrollY;
+ float localX = scrolledX - child.mLeft;
+ float localY = scrolledY - child.mTop;
+ if (!child.hasIdentityMatrix() && mAttachInfo != null) {
+ // non-identity matrix: transform the point into the view's coordinates
+ final float[] localXY = mAttachInfo.mTmpTransformLocation;
+ localXY[0] = localX;
+ localXY[1] = localY;
+ child.getInverseMatrix().mapPoints(localXY);
+ localX = localXY[0];
+ localY = localXY[1];
+ }
+ if (localX >= 0 && localY >= 0 && localX < (child.mRight - child.mLeft) &&
+ localY < (child.mBottom - child.mTop)) {
+ // It would be safer to clone the event here but we don't for performance.
+ // There are many subtle interactions in touch event dispatch; change at your own risk.
+ child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
+ ev.offsetLocation(localX - x, localY - y);
+ return child.dispatchTouchEvent(ev);
+ }
+ return false;
+ }
+
+ private boolean dispatchSplitTouchEvent(MotionEvent ev) {
+ final SplitMotionTargets targets = mSplitMotionTargets;
+ final int action = ev.getAction();
+ final int maskedAction = ev.getActionMasked();
+ float xf = ev.getX();
+ float yf = ev.getY();
+ float scrolledXFloat = xf + mScrollX;
+ float scrolledYFloat = yf + mScrollY;
+
+ boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
+
+ if (maskedAction == MotionEvent.ACTION_DOWN ||
+ maskedAction == MotionEvent.ACTION_POINTER_DOWN) {
+ final int actionIndex = ev.getActionIndex();
+ final int actionId = ev.getPointerId(actionIndex);
+
+ // Clear out any current target for this ID.
+ // XXX: We should probably send an ACTION_UP to the current
+ // target if present.
+ targets.removeById(actionId);
+
+ // If we're disallowing intercept or if we're allowing and we didn't
+ // intercept
+ if (disallowIntercept || !onInterceptTouchEvent(ev)) {
+ // reset this event's action (just to protect ourselves)
+ ev.setAction(action);
+ // We know we want to dispatch the event down, try to find a child
+ // who can handle it, start with the front-most child.
+ final View[] children = mChildren;
+ final int count = mChildrenCount;
+ for (int i = count - 1; i >= 0; i--) {
+ final View child = children[i];
+ if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
+ || child.getAnimation() != null) {
+ final MotionEvent childEvent = targets.filterMotionEventForChild(ev, child);
+ if (childEvent != null) {
+ try {
+ final int childActionIndex = childEvent.findPointerIndex(actionId);
+ if (dispatchTouchEventIfInView(child, childEvent,
+ childActionIndex)) {
+ targets.add(actionId, child);
+
+ return true;
+ }
+ } finally {
+ childEvent.recycle();
+ }
+ }
+ }
+ }
+
+ // Didn't find a new target. Do we have a "primary" target to send to?
+ final View primaryTarget = targets.getPrimaryTarget();
+ if (primaryTarget != null) {
+ final MotionEvent childEvent =
+ targets.filterMotionEventForChild(ev, primaryTarget);
+ if (childEvent != null) {
+ try {
+ // Calculate the offset point into the target's local coordinates
+ float xc = scrolledXFloat - (float) primaryTarget.mLeft;
+ float yc = scrolledYFloat - (float) primaryTarget.mTop;
+ if (!primaryTarget.hasIdentityMatrix() && mAttachInfo != null) {
+ // non-identity matrix: transform the point into the view's
+ // coordinates
+ final float[] localXY = mAttachInfo.mTmpTransformLocation;
+ localXY[0] = xc;
+ localXY[1] = yc;
+ primaryTarget.getInverseMatrix().mapPoints(localXY);
+ xc = localXY[0];
+ yc = localXY[1];
+ }
+ childEvent.setLocation(xc, yc);
+ if (primaryTarget.dispatchTouchEvent(childEvent)) {
+ targets.add(actionId, primaryTarget);
+ return true;
+ }
+ } finally {
+ childEvent.recycle();
+ }
+ }
+ }
+ }
+ }
+
+ boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
+ (action == MotionEvent.ACTION_CANCEL);
+
+ if (isUpOrCancel) {
+ // Note, we've already copied the previous state to our local
+ // variable, so this takes effect on the next event
+ mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
+ }
+
+ if (targets.isEmpty()) {
+ // We don't have any targets, this means we're handling the
+ // event as a regular view.
+ ev.setLocation(xf, yf);
+ if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
+ ev.setAction(MotionEvent.ACTION_CANCEL);
+ mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
+ }
+ return super.dispatchTouchEvent(ev);
+ }
+
+ // if we have targets, see if we're allowed to and want to intercept their
+ // events
+ int uniqueTargetCount = targets.getUniqueTargetCount();
+ if (!disallowIntercept && onInterceptTouchEvent(ev)) {
+ mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
+
+ for (int uniqueIndex = 0; uniqueIndex < uniqueTargetCount; uniqueIndex++) {
+ final View target = targets.getUniqueTargetAt(uniqueIndex);
+
+ // Calculate the offset point into the target's local coordinates
+ float xc = scrolledXFloat - (float) target.mLeft;
+ float yc = scrolledYFloat - (float) target.mTop;
+ if (!target.hasIdentityMatrix() && mAttachInfo != null) {
+ // non-identity matrix: transform the point into the view's coordinates
+ final float[] localXY = mAttachInfo.mTmpTransformLocation;
+ localXY[0] = xc;
+ localXY[1] = yc;
+ target.getInverseMatrix().mapPoints(localXY);
+ xc = localXY[0];
+ yc = localXY[1];
+ }
+
+ ev.setAction(MotionEvent.ACTION_CANCEL);
+ ev.setLocation(xc, yc);
+ if (!target.dispatchTouchEvent(ev)) {
+ // target didn't handle ACTION_CANCEL. not much we can do
+ // but they should have.
+ }
+ }
+ targets.clear();
+ // Don't dispatch this event to our own view, because we already
+ // saw it when intercepting; we just want to give the following
+ // event to the normal onTouchEvent().
+ return true;
+ }
+
+ boolean handled = false;
+ for (int uniqueIndex = 0; uniqueIndex < uniqueTargetCount; uniqueIndex++) {
+ final View target = targets.getUniqueTargetAt(uniqueIndex);
+
+ final MotionEvent targetEvent = targets.filterMotionEventForChild(ev, target);
+ if (targetEvent == null) {
+ continue;
+ }
+
+ try {
+ // Calculate the offset point into the target's local coordinates
+ xf = targetEvent.getX();
+ yf = targetEvent.getY();
+ scrolledXFloat = xf + mScrollX;
+ scrolledYFloat = yf + mScrollY;
+ float xc = scrolledXFloat - (float) target.mLeft;
+ float yc = scrolledYFloat - (float) target.mTop;
+ if (!target.hasIdentityMatrix() && mAttachInfo != null) {
+ // non-identity matrix: transform the point into the view's coordinates
+ final float[] localXY = mAttachInfo.mTmpTransformLocation;
+ localXY[0] = xc;
+ localXY[1] = yc;
+ target.getInverseMatrix().mapPoints(localXY);
+ xc = localXY[0];
+ yc = localXY[1];
+ }
+
+ // finally offset the event to the target's coordinate system and
+ // dispatch the event.
+ targetEvent.setLocation(xc, yc);
+
+ if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
+ targetEvent.setAction(MotionEvent.ACTION_CANCEL);
+ target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
+ targets.removeView(target);
+ uniqueIndex--;
+ uniqueTargetCount--;
+ }
+
+ handled |= target.dispatchTouchEvent(targetEvent);
+ } finally {
+ targetEvent.recycle();
+ }
+ }
+
+ if (maskedAction == MotionEvent.ACTION_POINTER_UP) {
+ final int removeId = ev.getPointerId(ev.getActionIndex());
+ targets.removeById(removeId);
+ }
+
+ if (isUpOrCancel) {
+ targets.clear();
+ }
+
+ return handled;
+ }
+
+ /**
+ * Enable or disable the splitting of MotionEvents to multiple children during touch event
+ * dispatch. This behavior is disabled by default.
+ *
+ * <p>When this option is enabled MotionEvents may be split and dispatched to different child
+ * views depending on where each pointer initially went down. This allows for user interactions
+ * such as scrolling two panes of content independently, chording of buttons, and performing
+ * independent gestures on different pieces of content.
+ *
+ * @param split <code>true</code> to allow MotionEvents to be split and dispatched to multiple
+ * child views. <code>false</code> to only allow one child view to be the target of
+ * any MotionEvent received by this ViewGroup.
+ */
+ public void setMotionEventSplittingEnabled(boolean split) {
+ // TODO Applications really shouldn't change this setting mid-touch event,
+ // but perhaps this should handle that case and send ACTION_CANCELs to any child views
+ // with gestures in progress when this is changed.
+ if (split) {
+ if ((mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) == 0) {
+ mSplitMotionTargets = new SplitMotionTargets();
+ }
+ mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
+ } else {
+ mGroupFlags &= ~FLAG_SPLIT_MOTION_EVENTS;
+ mSplitMotionTargets = null;
+ }
+ }
+
+ /**
+ * @return true if MotionEvents dispatched to this ViewGroup can be split to multiple children.
+ */
+ public boolean isMotionEventSplittingEnabled() {
+ return (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) == FLAG_SPLIT_MOTION_EVENTS;
+ }
+
+ /**
* {@inheritDoc}
*/
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
@@ -3812,4 +4100,263 @@
bottomMargin = bottom;
}
}
+
+ private static class SplitMotionTargets {
+ private SparseArray<View> mTargets;
+ private View[] mUniqueTargets;
+ private int mUniqueTargetCount;
+ private long mDownTime;
+ private MotionEvent.PointerCoords[] mPointerCoords;
+ private int[] mPointerIds;
+
+ private static final int INITIAL_UNIQUE_MOTION_TARGETS_SIZE = 5;
+ private static final int INITIAL_BUCKET_SIZE = 5;
+
+ public SplitMotionTargets() {
+ mTargets = new SparseArray<View>();
+ mUniqueTargets = new View[INITIAL_UNIQUE_MOTION_TARGETS_SIZE];
+ mPointerIds = new int[INITIAL_BUCKET_SIZE];
+ mPointerCoords = new MotionEvent.PointerCoords[INITIAL_BUCKET_SIZE];
+ for (int i = 0; i < INITIAL_BUCKET_SIZE; i++) {
+ mPointerCoords[i] = new MotionEvent.PointerCoords();
+ }
+ }
+
+ public void clear() {
+ mTargets.clear();
+ Arrays.fill(mUniqueTargets, null);
+ mUniqueTargetCount = 0;
+ }
+
+ public void add(int pointerId, View target) {
+ mTargets.put(pointerId, target);
+
+ final int uniqueCount = mUniqueTargetCount;
+ boolean addUnique = true;
+ for (int i = 0; i < uniqueCount; i++) {
+ if (mUniqueTargets[i] == target) {
+ addUnique = false;
+ }
+ }
+ if (addUnique) {
+ if (mUniqueTargets == null) {
+ mUniqueTargets = new View[INITIAL_UNIQUE_MOTION_TARGETS_SIZE];
+ }
+ if (mUniqueTargets.length == uniqueCount) {
+ View[] newTargets =
+ new View[uniqueCount + INITIAL_UNIQUE_MOTION_TARGETS_SIZE];
+ System.arraycopy(mUniqueTargets, 0, newTargets, 0, uniqueCount);
+ mUniqueTargets = newTargets;
+ }
+ mUniqueTargets[uniqueCount] = target;
+ mUniqueTargetCount++;
+ }
+ }
+
+ public int getIdCount() {
+ return mTargets.size();
+ }
+
+ public int getUniqueTargetCount() {
+ return mUniqueTargetCount;
+ }
+
+ public View getUniqueTargetAt(int index) {
+ return mUniqueTargets[index];
+ }
+
+ public View get(int id) {
+ return mTargets.get(id);
+ }
+
+ public int indexOfId(int id) {
+ return mTargets.indexOfKey(id);
+ }
+
+ public int indexOfTarget(View target) {
+ return mTargets.indexOfValue(target);
+ }
+
+ public int idAt(int index) {
+ return mTargets.keyAt(index);
+ }
+
+ public View targetAt(int index) {
+ return mTargets.valueAt(index);
+ }
+
+ public View getPrimaryTarget() {
+ if (!isEmpty()) {
+ return mUniqueTargets[0];
+ }
+ return null;
+ }
+
+ public boolean hasTarget(View target) {
+ final View[] unique = mUniqueTargets;
+ final int uniqueCount = mUniqueTargetCount;
+ for (int i = 0; i < uniqueCount; i++) {
+ if (unique[i] == target) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean isEmpty() {
+ return mUniqueTargetCount == 0;
+ }
+
+ public void removeById(int id) {
+ final int index = mTargets.indexOfKey(id);
+ removeAt(index);
+ }
+
+ public void removeView(View view) {
+ int i = 0;
+ while (i < mTargets.size()) {
+ if (mTargets.valueAt(i) == view) {
+ mTargets.removeAt(i);
+ } else {
+ i++;
+ }
+ }
+ removeUnique(view);
+ }
+
+ public void removeAt(int index) {
+ if (index < 0 || index >= mTargets.size()) {
+ return;
+ }
+
+ final View removeView = mTargets.valueAt(index);
+ mTargets.removeAt(index);
+ if (mTargets.indexOfValue(removeView) < 0) {
+ removeUnique(removeView);
+ }
+ }
+
+ private void removeUnique(View removeView) {
+ View[] unique = mUniqueTargets;
+ int uniqueCount = mUniqueTargetCount;
+ for (int i = 0; i < uniqueCount; i++) {
+ if (unique[i] == removeView) {
+ unique[i] = unique[--uniqueCount];
+ unique[uniqueCount] = null;
+ break;
+ }
+ }
+
+ mUniqueTargetCount = uniqueCount;
+ }
+
+ /**
+ * Return a new (obtain()ed) MotionEvent containing only data for pointers that should
+ * be dispatched to child. Don't forget to recycle it!
+ */
+ public MotionEvent filterMotionEventForChild(MotionEvent ev, View child) {
+ int action = ev.getAction();
+ final int maskedAction = action & MotionEvent.ACTION_MASK;
+
+ // Only send pointer up events if this child was the target. Drop it otherwise.
+ if (maskedAction == MotionEvent.ACTION_POINTER_UP &&
+ get(ev.getPointerId(ev.getActionIndex())) != child) {
+ return null;
+ }
+
+ int pointerCount = 0;
+ final int idCount = getIdCount();
+ for (int i = 0; i < idCount; i++) {
+ if (targetAt(i) == child) {
+ pointerCount++;
+ }
+ }
+
+ int actionId = -1;
+ boolean needsNewIndex = false; // True if we should fill in the action's masked index
+
+ // If we have a down event, it wasn't counted above.
+ if (maskedAction == MotionEvent.ACTION_DOWN) {
+ pointerCount++;
+ actionId = ev.getPointerId(0);
+ mDownTime = ev.getDownTime();
+ } else if (maskedAction == MotionEvent.ACTION_POINTER_DOWN) {
+ pointerCount++;
+
+ actionId = ev.getPointerId(ev.getActionIndex());
+
+ if (indexOfTarget(child) < 0) {
+ // The new action should be ACTION_DOWN if this child isn't currently getting
+ // any events.
+ action = MotionEvent.ACTION_DOWN;
+ } else {
+ // Fill in the index portion of the action later.
+ needsNewIndex = true;
+ }
+ } else if (maskedAction == MotionEvent.ACTION_POINTER_UP) {
+ actionId = ev.getPointerId(ev.getActionIndex());
+ if (pointerCount == 1) {
+ // The new action should be ACTION_UP if there's only one pointer left for
+ // this target.
+ action = MotionEvent.ACTION_UP;
+ } else {
+ // Fill in the index portion of the action later.
+ needsNewIndex = true;
+ }
+ }
+
+ if (pointerCount == 0) {
+ return null;
+ }
+
+ // Fill the buckets with pointer data!
+ final int eventPointerCount = ev.getPointerCount();
+ int bucketIndex = 0;
+ int newActionIndex = -1;
+ for (int evp = 0; evp < eventPointerCount; evp++) {
+ final int id = ev.getPointerId(evp);
+
+ // Add this pointer to the bucket if it is new or targeted at child
+ if (id == actionId || get(id) == child) {
+ // Expand scratch arrays if needed
+ if (mPointerCoords.length <= bucketIndex) {
+ int[] pointerIds = new int[pointerCount];
+ MotionEvent.PointerCoords[] pointerCoords =
+ new MotionEvent.PointerCoords[pointerCount];
+ for (int i = mPointerCoords.length; i < pointerCoords.length; i++) {
+ pointerCoords[i] = new MotionEvent.PointerCoords();
+ }
+
+ System.arraycopy(mPointerCoords, 0,
+ pointerCoords, 0, mPointerCoords.length);
+ System.arraycopy(mPointerIds, 0, pointerIds, 0, mPointerIds.length);
+
+ mPointerCoords = pointerCoords;
+ mPointerIds = pointerIds;
+ }
+
+ mPointerIds[bucketIndex] = id;
+ ev.getPointerCoords(evp, mPointerCoords[bucketIndex]);
+
+ if (needsNewIndex && id == actionId) {
+ newActionIndex = bucketIndex;
+ }
+
+ bucketIndex++;
+ }
+ }
+
+ // Encode the new action index if we have one
+ if (newActionIndex >= 0) {
+ action = (action & MotionEvent.ACTION_MASK) |
+ (newActionIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
+ }
+
+ MotionEvent result = MotionEvent.obtain(mDownTime, ev.getEventTime(),
+ action, pointerCount, mPointerIds, mPointerCoords, ev.getMetaState(),
+ ev.getXPrecision(), ev.getYPrecision(), ev.getDeviceId(), ev.getEdgeFlags(),
+ ev.getSource());
+ return result;
+ }
+ }
}
diff --git a/core/java/android/view/WindowOrientationListener.java b/core/java/android/view/WindowOrientationListener.java
index fed55dc..55d11bb 100755
--- a/core/java/android/view/WindowOrientationListener.java
+++ b/core/java/android/view/WindowOrientationListener.java
@@ -103,11 +103,11 @@
}
}
- public int getCurrentRotation() {
+ public int getCurrentRotation(int lastRotation) {
if (mEnabled) {
- return mSensorEventListener.getCurrentRotation();
+ return mSensorEventListener.getCurrentRotation(lastRotation);
}
- return -1;
+ return lastRotation;
}
/**
@@ -153,9 +153,15 @@
private static final int ROTATION_270 = 2;
// Mapping our internal aliases into actual Surface rotation values
- private static final int[] SURFACE_ROTATIONS = new int[] {
+ private static final int[] INTERNAL_TO_SURFACE_ROTATION = new int[] {
Surface.ROTATION_0, Surface.ROTATION_90, Surface.ROTATION_270};
+ // Mapping Surface rotation values to internal aliases.
+ // We have no constant for Surface.ROTATION_180. That should never happen, but if it
+ // does, we'll arbitrarily choose a mapping.
+ private static final int[] SURFACE_TO_INTERNAL_ROTATION = new int[] {
+ ROTATION_0, ROTATION_90, ROTATION_90, ROTATION_270};
+
// Threshold ranges of orientation angle to transition into other orientation states.
// The first list is for transitions from ROTATION_0, the next for ROTATION_90, etc.
// ROTATE_TO defines the orientation each threshold range transitions to, and must be kept
@@ -243,8 +249,12 @@
return (float) SAMPLING_PERIOD_MS / (timeConstantMs + SAMPLING_PERIOD_MS);
}
- int getCurrentRotation() {
- return SURFACE_ROTATIONS[mRotation];
+ int getCurrentRotation(int lastRotation) {
+ if (mTiltDistrust > 0) {
+ // we really don't know the current orientation, so trust what's currently displayed
+ mRotation = SURFACE_TO_INTERNAL_ROTATION[lastRotation];
+ }
+ return INTERNAL_TO_SURFACE_ROTATION[mRotation];
}
private void calculateNewRotation(float orientation, float tiltAngle) {
@@ -267,7 +277,7 @@
if (localLOGV) Log.i(TAG, " new rotation = " + rotation);
mRotation = rotation;
- mOrientationListener.onOrientationChanged(getCurrentRotation());
+ mOrientationListener.onOrientationChanged(INTERNAL_TO_SURFACE_ROTATION[mRotation]);
}
private float lowpassFilter(float newValue, float oldValue, float alpha) {
@@ -306,7 +316,8 @@
mTiltAngle = lowpassFilter(newTiltAngle, mTiltAngle, alpha);
float absoluteTilt = Math.abs(mTiltAngle);
- if (checkFullyTilted(absoluteTilt)) {
+ checkFullyTilted(absoluteTilt);
+ if (mTiltDistrust > 0) {
return; // when fully tilted, ignore orientation entirely
}
@@ -347,11 +358,9 @@
* get un-tilted.
*
* @param absoluteTilt the absolute value of the current tilt angle
- * @return true if the phone is fully tilted
*/
- private boolean checkFullyTilted(float absoluteTilt) {
- boolean fullyTilted = absoluteTilt > MAX_TILT;
- if (fullyTilted) {
+ private void checkFullyTilted(float absoluteTilt) {
+ if (absoluteTilt > MAX_TILT) {
if (mRotation == ROTATION_0) {
mOrientationAngle = 0;
} else if (mRotation == ROTATION_90) {
@@ -366,7 +375,6 @@
} else if (mTiltDistrust > 0) {
mTiltDistrust--;
}
- return fullyTilted;
}
/**
@@ -389,8 +397,8 @@
*/
private void filterOrientation(float absoluteTilt, float orientationAngle) {
float alpha = DEFAULT_LOWPASS_ALPHA;
- if (mTiltDistrust > 0 || mAccelerationDistrust > 1) {
- // when fully tilted, or under more than a transient acceleration, distrust heavily
+ if (mAccelerationDistrust > 1) {
+ // when under more than a transient acceleration, distrust heavily
alpha = ACCELERATING_LOWPASS_ALPHA;
} else if (absoluteTilt > PARTIAL_TILT || mAccelerationDistrust == 1) {
// when tilted partway, or under transient acceleration, distrust lightly
diff --git a/core/java/android/webkit/DeviceOrientationManager.java b/core/java/android/webkit/DeviceOrientationManager.java
index f65dccf..aac2f43 100644
--- a/core/java/android/webkit/DeviceOrientationManager.java
+++ b/core/java/android/webkit/DeviceOrientationManager.java
@@ -17,8 +17,8 @@
package android.webkit;
/**
- * This class is simply a container for the methods used to configure WebKit's
- * mock DeviceOrientationClient for use in LayoutTests.
+ * This class is simply a container for the methods used to implement DeviceOrientation,
+ * including the mock DeviceOrientationClient for use in LayoutTests.
*
* This could be part of WebViewCore, but have moved it to its own class to
* avoid bloat there.
@@ -26,6 +26,7 @@
*/
public final class DeviceOrientationManager {
private WebViewCore mWebViewCore;
+ private DeviceOrientationService mService;
public DeviceOrientationManager(WebViewCore webViewCore) {
mWebViewCore = webViewCore;
@@ -50,9 +51,19 @@
canProvideGamma, gamma);
}
+ public void onOrientationChange(Double alpha, Double beta, Double gamma) {
+ nativeOnOrientationChange(mWebViewCore,
+ alpha != null, alpha != null ? alpha.doubleValue() : 0.0,
+ beta != null, beta != null ? beta.doubleValue() : 0.0,
+ gamma != null, gamma != null ? gamma.doubleValue() : 0.0);
+ }
+
// Native functions
private static native void nativeUseMock(WebViewCore webViewCore);
private static native void nativeSetMockOrientation(WebViewCore webViewCore,
boolean canProvideAlpha, double alpha, boolean canProvideBeta, double beta,
boolean canProvideGamma, double gamma);
+ private static native void nativeOnOrientationChange(WebViewCore webViewCore,
+ boolean canProvideAlpha, double alpha, boolean canProvideBeta, double beta,
+ boolean canProvideGamma, double gamma);
}
diff --git a/core/java/android/webkit/DeviceOrientationService.java b/core/java/android/webkit/DeviceOrientationService.java
new file mode 100755
index 0000000..07d3d2f
--- /dev/null
+++ b/core/java/android/webkit/DeviceOrientationService.java
@@ -0,0 +1,81 @@
+/*
+ * 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.webkit;
+
+import android.os.Handler;
+import android.webkit.DeviceOrientationManager;
+import java.lang.Runnable;
+
+
+final class DeviceOrientationService {
+ private DeviceOrientationManager mManager;
+ private boolean mIsRunning;
+ private Handler mHandler;
+
+ public DeviceOrientationService(DeviceOrientationManager manager) {
+ mManager = manager;
+ assert(mManager != null);
+ }
+
+ public void start() {
+ mIsRunning = true;
+ registerForSensors();
+ }
+
+ public void stop() {
+ mIsRunning = false;
+ unregisterFromSensors();
+ }
+
+ public void suspend() {
+ if (mIsRunning) {
+ unregisterFromSensors();
+ }
+ }
+
+ public void resume() {
+ if (mIsRunning) {
+ registerForSensors();
+ }
+ }
+
+ private void sendErrorEvent() {
+ assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
+ if (mHandler == null) {
+ mHandler = new Handler();
+ }
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
+ if (mIsRunning) {
+ mManager.onOrientationChange(null, null, null);
+ }
+ }
+ });
+ }
+
+ private void registerForSensors() {
+ // Send the error event for now.
+ // FIXME: Implement.
+ sendErrorEvent();
+ }
+
+ private void unregisterFromSensors() {
+ // FIXME: Implement.
+ }
+}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index a2628ec..4823407 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -114,6 +114,9 @@
* href="{@docRoot}guide/topics/manifest/manifest-element.html">{@code <manifest>}</a>
* element.</p>
*
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-webview.html">Web View
+ * tutorial</a>.</p>
+ *
* <h3>Basic usage</h3>
*
* <p>By default, a WebView provides no browser-like widgets, does not
@@ -1275,6 +1278,7 @@
outState.putBundle("certificate",
SslCertificate.saveState(mCertificate));
}
+ outState.putBoolean("privateBrowsingEnabled", isPrivateBrowsingEnabled());
return list;
}
@@ -1437,6 +1441,10 @@
// Update the copy to have the correct index.
returnList.setCurrentIndex(index);
}
+ // Restore private browsing setting.
+ if (inState.getBoolean("privateBrowsingEnabled")) {
+ getSettings().setPrivateBrowsingEnabled(true);
+ }
// Remove all pending messages because we are restoring previous
// state.
mWebViewCore.removeMessages();
@@ -1699,18 +1707,7 @@
getSettings().setPrivateBrowsingEnabled(true);
if (!wasPrivateBrowsingEnabled) {
- StringBuilder data = new StringBuilder(1024);
- try {
- InputStreamReader file = new InputStreamReader(mContext.getResources().openRawResource(com.android.internal.R.raw.incognito_mode_start_page));
- int size;
- char[] buffer = new char[1024];
- while ((size = file.read(buffer)) != -1) {
- data.append(buffer, 0, size);
- }
- } catch (IOException e) {
- // This should never happen since this is a static resource.
- }
- loadDataWithBaseURL(null, data.toString(), "text/html", "utf-8", null);
+ loadUrl("browser:incognito");
}
}
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 4bb65e1..860edf2 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -34,6 +34,7 @@
import android.view.SurfaceView;
import android.view.View;
import android.webkit.DeviceOrientationManager;
+import android.webkit.DeviceOrientationService;
import java.util.ArrayList;
import java.util.Collection;
@@ -118,6 +119,7 @@
private int mWebkitScrollY = 0;
private DeviceOrientationManager mDeviceOrientationManager = new DeviceOrientationManager(this);
+ private DeviceOrientationService mDeviceOrientationService;
// The thread name used to identify the WebCore thread and for use in
// debugging other classes that require operation within the WebCore thread.
@@ -2500,6 +2502,13 @@
canProvideGamma, gamma);
}
+ protected DeviceOrientationService getDeviceOrientationService() {
+ if (mDeviceOrientationService == null) {
+ mDeviceOrientationService = new DeviceOrientationService(mDeviceOrientationManager);
+ }
+ return mDeviceOrientationService;
+ }
+
private native void nativePause();
private native void nativeResume();
private native void nativeFreeMemory();
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index 34aef99..e07befa 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -75,6 +75,9 @@
* }
* </pre>
*
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-autocomplete.html">Auto Complete
+ * tutorial</a>.</p>
+ *
* @attr ref android.R.styleable#AutoCompleteTextView_completionHint
* @attr ref android.R.styleable#AutoCompleteTextView_completionThreshold
* @attr ref android.R.styleable#AutoCompleteTextView_completionHintView
diff --git a/core/java/android/widget/Button.java b/core/java/android/widget/Button.java
index 5e692d4..176233e 100644
--- a/core/java/android/widget/Button.java
+++ b/core/java/android/widget/Button.java
@@ -48,6 +48,9 @@
* }
* </pre>
*
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-formstuff.html">Form Stuff
+ * tutorial</a>.</p>
+ *
* <p><strong>XML attributes</strong></p>
* <p>
* See {@link android.R.styleable#Button Button Attributes},
diff --git a/core/java/android/widget/CheckBox.java b/core/java/android/widget/CheckBox.java
index ff63a24..b89c2a9 100644
--- a/core/java/android/widget/CheckBox.java
+++ b/core/java/android/widget/CheckBox.java
@@ -41,6 +41,9 @@
* }
* }
* </pre>
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-formstuff.html">Form Stuff
+ * tutorial</a>.</p>
*
* <p><strong>XML attributes</strong></p>
* <p>
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 1fc23ab..8aed454 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -37,6 +37,9 @@
/**
* A view for selecting a month / year / day based on a calendar like layout.
*
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-datepicker.html">Date Picker
+ * tutorial</a>.</p>
+ *
* For a dialog using this view, see {@link android.app.DatePickerDialog}.
*/
@Widget
diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java
index 1532db1..0da68a4 100644
--- a/core/java/android/widget/EditText.java
+++ b/core/java/android/widget/EditText.java
@@ -35,6 +35,9 @@
/**
* EditText is a thin veneer over TextView that configures itself
* to be editable.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-formstuff.html">Form Stuff
+ * tutorial</a>.</p>
* <p>
* <b>XML attributes</b>
* <p>
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index e445180..559a5fe 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -32,8 +32,8 @@
/**
* FrameLayout is designed to block out an area on the screen to display
- * a single item. You can add multiple children to a FrameLayout, but all
- * children are pegged to the top left of the screen.
+ * a single item. You can add multiple children to a FrameLayout and control their
+ * position within the FrameLayout using {@link android.widget.FrameLayout.LayoutParams#gravity}.
* Children are drawn in a stack, with the most recently added child on top.
* The size of the frame layout is the size of its largest child (plus padding), visible
* or not (if the FrameLayout's parent permits). Views that are GONE are used for sizing
@@ -440,6 +440,8 @@
* Per-child layout information for layouts that support margins.
* See {@link android.R.styleable#FrameLayout_Layout FrameLayout Layout Attributes}
* for a list of all child view attributes that this class supports.
+ *
+ * @attr ref android.R.styleable#FrameLayout_Layout_layout_gravity
*/
public static class LayoutParams extends MarginLayoutParams {
/**
@@ -447,6 +449,8 @@
* are associated.
*
* @see android.view.Gravity
+ *
+ * @attr ref android.R.styleable#FrameLayout_Layout_layout_gravity
*/
public int gravity = -1;
diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java
index c47292f..9789658 100644
--- a/core/java/android/widget/Gallery.java
+++ b/core/java/android/widget/Gallery.java
@@ -46,6 +46,9 @@
* <p>
* Views given to the Gallery should use {@link Gallery.LayoutParams} as their
* layout parameters type.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-gallery.html">Gallery
+ * tutorial</a>.</p>
*
* @attr ref android.R.styleable#Gallery_animationDuration
* @attr ref android.R.styleable#Gallery_spacing
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index ea767f6..dffe685 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -34,6 +34,9 @@
/**
* A view that shows items in two-dimensional scrolling grid. The items in the
* grid come from the {@link ListAdapter} associated with this view.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-gridview.html">Grid
+ * View tutorial</a>.</p>
*/
@RemoteView
public class GridView extends AbsListView {
@@ -1551,9 +1554,9 @@
int nextPage = -1;
if (direction == FOCUS_UP) {
- nextPage = Math.max(0, mSelectedPosition - getChildCount() - 1);
+ nextPage = Math.max(0, mSelectedPosition - getChildCount());
} else if (direction == FOCUS_DOWN) {
- nextPage = Math.min(mItemCount - 1, mSelectedPosition + getChildCount() - 1);
+ nextPage = Math.min(mItemCount - 1, mSelectedPosition + getChildCount());
}
if (nextPage >= 0) {
diff --git a/core/java/android/widget/ImageButton.java b/core/java/android/widget/ImageButton.java
index 5c05170..12a68db 100644
--- a/core/java/android/widget/ImageButton.java
+++ b/core/java/android/widget/ImageButton.java
@@ -62,6 +62,9 @@
* it will only be applied after {@code android:state_pressed} and {@code
* android:state_focused} have both evaluated false.</p>
*
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-formstuff.html">Form Stuff
+ * tutorial</a>.</p>
+ *
* <p><strong>XML attributes</strong></p>
* <p>
* See {@link android.R.styleable#ImageView Button Attributes},
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index 53187bf..1e5489a 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -37,6 +37,9 @@
* {@link android.widget.LinearLayout.LayoutParams LinearLayout.LayoutParams}.
* The default orientation is horizontal.
*
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-linearlayout.html">Linear Layout
+ * tutorial</a>.</p>
+ *
* <p>
* Also see {@link LinearLayout.LayoutParams android.widget.LinearLayout.LayoutParams}
* for layout attributes </p>
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index 35e0603..12ff292 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -604,6 +604,15 @@
mDropDownList = null;
}
+ /**
+ * Set a listener to receive a callback when the popup is dismissed.
+ *
+ * @param listener Listener that will be notified when the popup is dismissed.
+ */
+ public void setOnDismissListener(PopupWindow.OnDismissListener listener) {
+ mPopup.setOnDismissListener(listener);
+ }
+
private void removePromptView() {
if (mPromptView != null) {
final ViewParent parent = mPromptView.getParent();
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index de7157b..f9bdc43 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -57,6 +57,9 @@
* A view that shows items in a vertically scrolling list. The items
* come from the {@link ListAdapter} associated with this view.
*
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-listview.html">List View
+ * tutorial</a>.</p>
+ *
* @attr ref android.R.styleable#ListView_entries
* @attr ref android.R.styleable#ListView_divider
* @attr ref android.R.styleable#ListView_dividerHeight
diff --git a/core/java/android/widget/RadioButton.java b/core/java/android/widget/RadioButton.java
index 14ec8c6..ebbe1cd 100644
--- a/core/java/android/widget/RadioButton.java
+++ b/core/java/android/widget/RadioButton.java
@@ -34,6 +34,9 @@
* a radio group, checking one radio button unchecks all the others.</p>
* </p>
*
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-formstuff.html">Form Stuff
+ * tutorial</a>.</p>
+ *
* <p><strong>XML attributes</strong></p>
* <p>
* See {@link android.R.styleable#CompoundButton CompoundButton Attributes},
diff --git a/core/java/android/widget/RatingBar.java b/core/java/android/widget/RatingBar.java
index 1800c5a..28499d0 100644
--- a/core/java/android/widget/RatingBar.java
+++ b/core/java/android/widget/RatingBar.java
@@ -41,6 +41,9 @@
* <p>
* The secondary progress should not be modified by the client as it is used
* internally as the background for a fractionally filled star.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-formstuff.html">Form Stuff
+ * tutorial</a>.</p>
*
* @attr ref android.R.styleable#RatingBar_numStars
* @attr ref android.R.styleable#RatingBar_rating
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index 64cda49..a47359f 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -54,6 +54,9 @@
* {@link #ALIGN_PARENT_BOTTOM}.
* </p>
*
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-relativelayout.html">Relative
+ * Layout tutorial</a>.</p>
+ *
* <p>
* Also see {@link android.widget.RelativeLayout.LayoutParams RelativeLayout.LayoutParams} for
* layout attributes
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index 60e8568..b534c34 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -33,6 +33,9 @@
* A view that displays one child at a time and lets the user pick among them.
* The items in the Spinner come from the {@link Adapter} associated with
* this view.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-spinner.html">Spinner
+ * tutorial</a>.</p>
*
* @attr ref android.R.styleable#Spinner_prompt
*/
diff --git a/core/java/android/widget/TabHost.java b/core/java/android/widget/TabHost.java
index 02cd6a8..f720cee 100644
--- a/core/java/android/widget/TabHost.java
+++ b/core/java/android/widget/TabHost.java
@@ -40,6 +40,9 @@
* user clicks to select a specific tab, and a FrameLayout object that displays the contents of that
* page. The individual elements are typically controlled using this container object, rather than
* setting values on the child elements themselves.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-tabwidget.html">Tab Layout
+ * tutorial</a>.</p>
*/
public class TabHost extends FrameLayout implements ViewTreeObserver.OnTouchModeChangeListener {
diff --git a/core/java/android/widget/TabWidget.java b/core/java/android/widget/TabWidget.java
index 4e1b585..afae7ef 100644
--- a/core/java/android/widget/TabWidget.java
+++ b/core/java/android/widget/TabWidget.java
@@ -40,6 +40,9 @@
* handler, and manage callbacks. You might call this object to iterate the list
* of tabs, or to tweak the layout of the tab list, but most methods should be
* called on the containing TabHost object.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-tabwidget.html">Tab Layout
+ * tutorial</a>.</p>
*
* @attr ref android.R.styleable#TabWidget_divider
* @attr ref android.R.styleable#TabWidget_tabStripEnabled
diff --git a/core/java/android/widget/TableLayout.java b/core/java/android/widget/TableLayout.java
index 73760ac..7f26e28 100644
--- a/core/java/android/widget/TableLayout.java
+++ b/core/java/android/widget/TableLayout.java
@@ -67,6 +67,9 @@
* <p>Although the typical child of a TableLayout is a TableRow, you can
* actually use any View subclass as a direct child of TableLayout. The View
* will be displayed as a single row that spans all the table columns.</p>
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-tablelayout.html">Table
+ * Layout tutorial</a>.</p>
*/
public class TableLayout extends LinearLayout {
private int[] mMaxWidths;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 0510701..25c5b24 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6584,7 +6584,7 @@
onEndBatchEdit();
hideInsertionPointCursorController();
- stopSelectionActionMode();
+ terminateSelectionActionMode();
}
startStopMarquee(focused);
@@ -6680,7 +6680,7 @@
return;
} else {
// Tapping outside stops selection mode, if any
- finishSelectionActionMode();
+ stopSelectionActionMode();
}
}
@@ -6782,9 +6782,9 @@
mSelectionModifierCursorController = new SelectionModifierCursorController();
}
} else {
- mSelectionModifierCursorController = null;
// Stop selection mode if the controller becomes unavailable.
- finishSelectionActionMode();
+ stopSelectionActionMode();
+ mSelectionModifierCursorController = null;
}
}
@@ -7029,7 +7029,7 @@
}
private boolean canSelectAll() {
- return canSelectText();
+ return canSelectText() && mText.length() != 0;
}
private boolean canSelectText() {
@@ -7037,7 +7037,6 @@
// If you change this condition, make sure prepareCursorController is called anywhere
// the value of this condition might be changed.
return (mText instanceof Spannable &&
- mText.length() != 0 &&
mMovement != null &&
mMovement.canSelectArbitrarily());
}
@@ -7388,13 +7387,13 @@
}
/**
- * Same as {@link #finishSelectionActionMode()}, except that there is no cursor controller
+ * Same as {@link #stopSelectionActionMode()}, except that there is no cursor controller
* fade out animation. Needed since the drawable and their alpha values are shared by all
* TextViews. Switching from one TextView to another would fade the cursor controllers in the
* new one otherwise.
*/
- private void stopSelectionActionMode() {
- finishSelectionActionMode();
+ private void terminateSelectionActionMode() {
+ stopSelectionActionMode();
if (mSelectionModifierCursorController != null) {
SelectionModifierCursorController selectionModifierCursorController =
(SelectionModifierCursorController) mSelectionModifierCursorController;
@@ -7402,7 +7401,7 @@
}
}
- private void finishSelectionActionMode() {
+ private void stopSelectionActionMode() {
if (mSelectionActionMode != null) {
mSelectionActionMode.finish();
}
@@ -7544,7 +7543,7 @@
}
}
}
- finishSelectionActionMode();
+ stopSelectionActionMode();
}
return true;
@@ -7553,13 +7552,13 @@
clipboard.setPrimaryClip(new ClippedData(null, null,
new ClippedData.Item(mTransformed.subSequence(min, max))));
((Editable) mText).delete(min, max);
- finishSelectionActionMode();
+ stopSelectionActionMode();
return true;
case ID_COPY:
clipboard.setPrimaryClip(new ClippedData(null, null,
new ClippedData.Item(mTransformed.subSequence(min, max))));
- finishSelectionActionMode();
+ stopSelectionActionMode();
return true;
}
@@ -8058,7 +8057,7 @@
private void hideControllers() {
hideInsertionPointCursorController();
- finishSelectionActionMode();
+ stopSelectionActionMode();
}
/**
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index caed308..e61fac3 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -45,6 +45,9 @@
* Under AM/PM mode, the user can hit 'a', 'A", 'p' or 'P' to pick.
*
* For a dialog using this view, see {@link android.app.TimePickerDialog}.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-timepicker.html">Time Picker
+ * tutorial</a>.</p>
*/
@Widget
public class TimePicker extends FrameLayout {
diff --git a/core/java/android/widget/ToggleButton.java b/core/java/android/widget/ToggleButton.java
index dc791e3..3b680e8 100644
--- a/core/java/android/widget/ToggleButton.java
+++ b/core/java/android/widget/ToggleButton.java
@@ -26,6 +26,9 @@
/**
* Displays checked/unchecked states as a button
* with a "light" indicator and by default accompanied with the text "ON" or "OFF".
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-formstuff.html">Form Stuff
+ * tutorial</a>.</p>
*
* @attr ref android.R.styleable#ToggleButton_textOn
* @attr ref android.R.styleable#ToggleButton_textOff
diff --git a/core/java/com/android/internal/util/HierarchicalStateMachine.java b/core/java/com/android/internal/util/HierarchicalStateMachine.java
index 7138b5c..d38c03b 100644
--- a/core/java/com/android/internal/util/HierarchicalStateMachine.java
+++ b/core/java/com/android/internal/util/HierarchicalStateMachine.java
@@ -137,7 +137,7 @@
}
class State1 extends HierarchicalState {
- @Override public boolean processMessage(Message message) {
+ \@Override public boolean processMessage(Message message) {
Log.d(TAG, "Hello World");
return HANDLED;
}
@@ -257,10 +257,10 @@
}
class P1 extends HierarchicalState {
- @Override public void enter() {
+ \@Override public void enter() {
Log.d(TAG, "mP1.enter");
}
- @Override public boolean processMessage(Message message) {
+ \@Override public boolean processMessage(Message message) {
boolean retVal;
Log.d(TAG, "mP1.processMessage what=" + message.what);
switch(message.what) {
@@ -278,16 +278,16 @@
}
return retVal;
}
- @Override public void exit() {
+ \@Override public void exit() {
Log.d(TAG, "mP1.exit");
}
}
class S1 extends HierarchicalState {
- @Override public void enter() {
+ \@Override public void enter() {
Log.d(TAG, "mS1.enter");
}
- @Override public boolean processMessage(Message message) {
+ \@Override public boolean processMessage(Message message) {
Log.d(TAG, "S1.processMessage what=" + message.what);
if (message.what == CMD_1) {
// Transition to ourself to show that enter/exit is called
@@ -298,16 +298,16 @@
return NOT_HANDLED;
}
}
- @Override public void exit() {
+ \@Override public void exit() {
Log.d(TAG, "mS1.exit");
}
}
class S2 extends HierarchicalState {
- @Override public void enter() {
+ \@Override public void enter() {
Log.d(TAG, "mS2.enter");
}
- @Override public boolean processMessage(Message message) {
+ \@Override public boolean processMessage(Message message) {
boolean retVal;
Log.d(TAG, "mS2.processMessage what=" + message.what);
switch(message.what) {
@@ -326,17 +326,17 @@
}
return retVal;
}
- @Override public void exit() {
+ \@Override public void exit() {
Log.d(TAG, "mS2.exit");
}
}
class P2 extends HierarchicalState {
- @Override public void enter() {
+ \@Override public void enter() {
Log.d(TAG, "mP2.enter");
sendMessage(obtainMessage(CMD_5));
}
- @Override public boolean processMessage(Message message) {
+ \@Override public boolean processMessage(Message message) {
Log.d(TAG, "P2.processMessage what=" + message.what);
switch(message.what) {
case(CMD_3):
@@ -349,12 +349,12 @@
}
return HANDLED;
}
- @Override public void exit() {
+ \@Override public void exit() {
Log.d(TAG, "mP2.exit");
}
}
- @Override
+ \@Override
void halting() {
Log.d(TAG, "halting");
synchronized (this) {
diff --git a/core/java/com/android/internal/view/menu/ActionMenuView.java b/core/java/com/android/internal/view/menu/ActionMenuView.java
index cb5f179..e064e2c 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuView.java
@@ -25,7 +25,6 @@
import android.widget.ImageButton;
import android.widget.LinearLayout;
-import java.lang.ref.WeakReference;
import java.util.ArrayList;
/**
@@ -41,7 +40,13 @@
private int mMaxItems;
private boolean mReserveOverflow;
private OverflowMenuButton mOverflowButton;
- private WeakReference<MenuPopupHelper> mOverflowPopup;
+ private MenuPopupHelper mOverflowPopup;
+
+ private Runnable mShowOverflow = new Runnable() {
+ public void run() {
+ showOverflowMenu();
+ }
+ };
public ActionMenuView(Context context) {
this(context, null);
@@ -56,18 +61,40 @@
com.android.internal.R.styleable.Theme_actionButtonPadding, 0);
mItemMargin = mItemPadding / 2;
a.recycle();
-
+
+ // Measure for initial configuration
+ mMaxItems = measureMaxActionButtons();
+
+ // TODO There has to be a better way to indicate that we don't have a hard menu key.
+ final int screen = getResources().getConfiguration().screenLayout;
+ mReserveOverflow = (screen & Configuration.SCREENLAYOUT_SIZE_MASK) ==
+ Configuration.SCREENLAYOUT_SIZE_XLARGE;
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ final int screen = newConfig.screenLayout;
+ mReserveOverflow = (screen & Configuration.SCREENLAYOUT_SIZE_MASK) ==
+ Configuration.SCREENLAYOUT_SIZE_XLARGE;
+ mMaxItems = measureMaxActionButtons();
+ if (mMenu != null) {
+ mMenu.setMaxActionItems(mMaxItems);
+ updateChildren(false);
+ }
+
+ if (mOverflowPopup != null && mOverflowPopup.isShowing()) {
+ mOverflowPopup.dismiss();
+ post(mShowOverflow);
+ }
+ }
+
+ private int measureMaxActionButtons() {
final Resources res = getResources();
final int size = res.getDimensionPixelSize(com.android.internal.R.dimen.action_icon_size);
final int spaceAvailable = res.getDisplayMetrics().widthPixels / 2;
final int itemSpace = size + mItemPadding;
- mMaxItems = spaceAvailable / (itemSpace > 0 ? itemSpace : 1);
-
- // TODO There has to be a better way to indicate that we don't have a hard menu key.
- final int screen = res.getConfiguration().screenLayout;
- mReserveOverflow = (screen & Configuration.SCREENLAYOUT_SIZE_MASK) ==
- Configuration.SCREENLAYOUT_SIZE_XLARGE;
+ return spaceAvailable / (itemSpace > 0 ? itemSpace : 1);
}
public boolean isOverflowReserved() {
@@ -156,14 +183,14 @@
popup.show();
}
});
- mOverflowPopup = new WeakReference<MenuPopupHelper>(popup);
+ mOverflowPopup = popup;
return true;
}
return false;
}
public boolean isOverflowMenuShowing() {
- MenuPopupHelper popup = mOverflowPopup != null ? mOverflowPopup.get() : null;
+ MenuPopupHelper popup = mOverflowPopup;
if (popup != null) {
return popup.isShowing();
}
@@ -171,7 +198,7 @@
}
public boolean hideOverflowMenu() {
- MenuPopupHelper popup = mOverflowPopup != null ? mOverflowPopup.get() : null;
+ MenuPopupHelper popup = mOverflowPopup;
if (popup != null) {
popup.dismiss();
return true;
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index f52c93c..a12a4d6 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -26,6 +26,7 @@
import android.view.View.MeasureSpec;
import android.widget.AdapterView;
import android.widget.ListPopupWindow;
+import android.widget.PopupWindow;
import java.lang.ref.WeakReference;
@@ -42,6 +43,12 @@
private WeakReference<View> mAnchorView;
private boolean mOverflowOnly;
+ private PopupWindow.OnDismissListener mDismissListener = new PopupWindow.OnDismissListener() {
+ public void onDismiss() {
+ mPopup = null;
+ }
+ };
+
public MenuPopupHelper(Context context, MenuBuilder menu) {
this(context, menu, null, false);
}
@@ -69,6 +76,7 @@
mPopup = new ListPopupWindow(mContext, null, 0,
com.android.internal.R.style.Widget_Spinner);
mPopup.setOnItemClickListener(this);
+ mPopup.setOnDismissListener(mDismissListener);
final MenuAdapter adapter = mOverflowOnly ?
mMenu.getOverflowMenuAdapter(MenuBuilder.TYPE_POPUP) :
@@ -95,7 +103,6 @@
if (isShowing()) {
mPopup.dismiss();
}
- mPopup = null;
}
public boolean isShowing() {
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index 067d022..b9e4e46 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -221,6 +221,14 @@
return false;
}
+ public void postShowOverflowMenu() {
+ post(new Runnable() {
+ public void run() {
+ showOverflowMenu();
+ }
+ });
+ }
+
public boolean hideOverflowMenu() {
if (mMenuView != null) {
return mMenuView.hideOverflowMenu();
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 21b2e3b..254d9a4 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -218,6 +218,7 @@
}
pr->setDitherImage(ditherImage);
bitmap->setPixelRef(pr)->unref();
+ pr->isOpaque(bitmap);
return pr;
}
@@ -464,10 +465,8 @@
jobject options) { // BitmapFactory$Options
SkStream* stream;
Asset* asset = reinterpret_cast<Asset*>(native_asset);
- // assets can always be rebuilt, so force this
- bool forcePurgeable = true;
-
- if (forcePurgeable || optionsPurgeable(env, options)) {
+ bool forcePurgeable = optionsPurgeable(env, options);
+ if (forcePurgeable) {
// if we could "ref/reopen" the asset, we may not need to copy it here
// and we could assume optionsShareable, since assets are always RO
stream = copyAssetToStream(asset);
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 204bb74..578de6f 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -518,35 +518,48 @@
///////////////////////////////////////////////////////////////////////////////
JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env, bool reportSizeToVM)
- : fEnv(env), fReportSizeToVM(reportSizeToVM) {}
+ : fReportSizeToVM(reportSizeToVM) {
+ if (env->GetJavaVM(&fVM) != JNI_OK) {
+ SkDebugf("------ [%p] env->GetJavaVM failed\n", env);
+ sk_throw();
+ }
+}
bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
- return GraphicsJNI::setJavaPixelRef(fEnv, bitmap, ctable, fReportSizeToVM);
+ JNIEnv* env = vm2env(fVM);
+ return GraphicsJNI::setJavaPixelRef(env, bitmap, ctable, fReportSizeToVM);
}
////////////////////////////////////////////////////////////////////////////////
JavaMemoryUsageReporter::JavaMemoryUsageReporter(JNIEnv* env)
- : fEnv(env), fTotalSize(0) {}
+ : fTotalSize(0) {
+ if (env->GetJavaVM(&fVM) != JNI_OK) {
+ SkDebugf("------ [%p] env->GetJavaVM failed\n", env);
+ sk_throw();
+ }
+}
JavaMemoryUsageReporter::~JavaMemoryUsageReporter() {
+ JNIEnv* env = vm2env(fVM);
jlong jtotalSize = fTotalSize;
- fEnv->CallVoidMethod(gVMRuntime_singleton,
+ env->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,
+ JNIEnv* env = vm2env(fVM);
+ bool r = env->CallBooleanMethod(gVMRuntime_singleton,
gVMRuntime_trackExternalAllocationMethodID,
jsize);
- if (GraphicsJNI::hasException(fEnv)) {
+ if (GraphicsJNI::hasException(env)) {
return false;
}
if (!r) {
LOGE("VM won't let us allocate %zd bytes\n", memorySize);
- doThrowOOME(fEnv, "bitmap size exceeds VM budget");
+ doThrowOOME(env, "bitmap size exceeds VM budget");
return false;
}
fTotalSize += memorySize;
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 8d6528b..1a43a3e 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -80,7 +80,7 @@
virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable);
private:
- JNIEnv* fEnv;
+ JavaVM* fVM;
bool fReportSizeToVM;
};
@@ -92,7 +92,7 @@
virtual bool reportMemory(size_t memorySize);
private:
- JNIEnv* fEnv;
+ JavaVM* fVM;
size_t fTotalSize;
};
diff --git a/core/jni/android_content_res_ObbScanner.cpp b/core/jni/android_content_res_ObbScanner.cpp
index 1239274..62c89fc 100644
--- a/core/jni/android_content_res_ObbScanner.cpp
+++ b/core/jni/android_content_res_ObbScanner.cpp
@@ -31,6 +31,7 @@
jfieldID packageName;
jfieldID version;
+ jfieldID flags;
} gObbInfoClassInfo;
static jboolean android_content_res_ObbScanner_getObbInfo(JNIEnv* env, jobject clazz, jstring file,
@@ -85,6 +86,8 @@
"packageName", "Ljava/lang/String;");
GET_FIELD_ID(gObbInfoClassInfo.version, gObbInfoClassInfo.clazz,
"version", "I");
+ GET_FIELD_ID(gObbInfoClassInfo.flags, gObbInfoClassInfo.clazz,
+ "flags", "I");
return AndroidRuntime::registerNativeMethods(env, "android/content/res/ObbScanner", gMethods,
NELEM(gMethods));
diff --git a/core/res/res/raw/incognito_mode_start_page.html b/core/res/assets/webkit/incognito_mode_start_page.html
similarity index 100%
rename from core/res/res/raw/incognito_mode_start_page.html
rename to core/res/assets/webkit/incognito_mode_start_page.html
diff --git a/core/res/res/drawable-hdpi/textfield_default_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_default_holo_dark.9.png
new file mode 100644
index 0000000..c67d04d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_default_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_default_holo_light.9.png
new file mode 100644
index 0000000..1292ebf
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_disabled_holo_dark.9.png
new file mode 100644
index 0000000..a39982a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_disabled_holo_light.9.png
new file mode 100644
index 0000000..1f224b9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_disabled_selected_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_disabled_selected_holo_dark.9.png
new file mode 100644
index 0000000..66f18cc
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_disabled_selected_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_disabled_selected_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_disabled_selected_holo_light.9.png
new file mode 100644
index 0000000..2664384
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_disabled_selected_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_pressed_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_pressed_holo_dark.9.png
new file mode 100644
index 0000000..bbf53c5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_pressed_holo_light.9.png
new file mode 100644
index 0000000..ce49b8d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_selected_holo_dark.9.png b/core/res/res/drawable-hdpi/textfield_selected_holo_dark.9.png
new file mode 100644
index 0000000..bbf53c5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_selected_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_selected_holo_light.9.png b/core/res/res/drawable-hdpi/textfield_selected_holo_light.9.png
new file mode 100644
index 0000000..ce49b8d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_selected_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_default_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_default_holo_dark.9.png
new file mode 100644
index 0000000..a9a2cc6
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_default_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_default_holo_light.9.png
new file mode 100644
index 0000000..36003cf
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_disabled_holo_dark.9.png
new file mode 100644
index 0000000..cec82dd
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_disabled_holo_light.9.png
new file mode 100644
index 0000000..8a9b11f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_disabled_selected_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_disabled_selected_holo_dark.9.png
new file mode 100644
index 0000000..545924c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_disabled_selected_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_disabled_selected_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_disabled_selected_holo_light.9.png
new file mode 100644
index 0000000..53ee26c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_disabled_selected_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_pressed_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_pressed_holo_dark.9.png
new file mode 100644
index 0000000..0e7c24d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_pressed_holo_light.9.png
new file mode 100644
index 0000000..fee09a6
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_selected_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_selected_holo_dark.9.png
new file mode 100644
index 0000000..0e7c24d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_selected_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_selected_holo_light.9.png b/core/res/res/drawable-mdpi/textfield_selected_holo_light.9.png
new file mode 100644
index 0000000..fee09a6
--- /dev/null
+++ b/core/res/res/drawable-mdpi/textfield_selected_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable/edit_text_holo_dark.xml b/core/res/res/drawable/edit_text_holo_dark.xml
new file mode 100644
index 0000000..b7d24ff
--- /dev/null
+++ b/core/res/res/drawable/edit_text_holo_dark.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_window_focused="false" android:state_enabled="true"
+ android:drawable="@drawable/textfield_default_holo_dark" />
+ <item android:state_window_focused="false" android:state_enabled="false"
+ android:drawable="@drawable/textfield_disabled_holo_dark" />
+ <item android:state_pressed="true" android:drawable="@drawable/textfield_pressed_holo_dark" />
+ <item android:state_enabled="true" android:state_focused="true" android:drawable="@drawable/textfield_selected_holo_dark" />
+ <item android:state_enabled="true" android:drawable="@drawable/textfield_default_holo_dark" />
+ <item android:state_focused="true" android:drawable="@drawable/textfield_disabled_selected_holo_dark" />
+ <item android:drawable="@drawable/textfield_disabled_holo_dark" />
+</selector>
+
diff --git a/core/res/res/drawable/edit_text_holo_light.xml b/core/res/res/drawable/edit_text_holo_light.xml
new file mode 100644
index 0000000..dae39e3
--- /dev/null
+++ b/core/res/res/drawable/edit_text_holo_light.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_window_focused="false" android:state_enabled="true"
+ android:drawable="@drawable/textfield_default_holo_light" />
+ <item android:state_window_focused="false" android:state_enabled="false"
+ android:drawable="@drawable/textfield_disabled_holo_light" />
+ <item android:state_pressed="true" android:drawable="@drawable/textfield_pressed_holo_light" />
+ <item android:state_enabled="true" android:state_focused="true" android:drawable="@drawable/textfield_selected_holo_light" />
+ <item android:state_enabled="true" android:drawable="@drawable/textfield_default_holo_light" />
+ <item android:state_focused="true" android:drawable="@drawable/textfield_disabled_selected_holo_light" />
+ <item android:drawable="@drawable/textfield_disabled_holo_light" />
+</selector>
+
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 4fb3039..fe8a816 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -124,10 +124,14 @@
<!-- Text color, typeface, size, and style for system search result subtitle. Defaults to primary inverse text color. -->
<attr name="textAppearanceSearchResultSubtitle" format="reference" />
-
<!-- Text color, typeface, size, and style for the text inside of a button. -->
<attr name="textAppearanceButton" format="reference" />
+ <!-- EditText text foreground color. -->
+ <attr name="editTextColor" format="reference|color" />
+ <!-- EditText background drawable. -->
+ <attr name="editTextBackground" format="reference" />
+
<!-- A styled string, specifying the style to be used for showing
inline candidate text when composing with an input method. The
text itself will be ignored, but the style spans will be applied
@@ -1449,6 +1453,17 @@
<enum name="blocksDescendants" value="2" />
</attr>
+ <!-- Sets whether this ViewGroup should split MotionEvents
+ to separate child views during touch event dispatch.
+ If false (default), touch events will be dispatched to
+ the child view where the first pointer went down until
+ the last pointer goes up.
+ If true, touch events may be dispatched to multiple children.
+ MotionEvents for each pointer will be dispatched to the child
+ view where the initial ACTION_DOWN event happened.
+ See {@link android.view.ViewGroup#setMotionEventSplittingEnabled(boolean)}
+ for more information. -->
+ <attr name="splitMotionEvents" format="boolean" />
</declare-styleable>
<!-- A {@link android.view.ViewStub} lets you lazily include other XML layouts
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 3a0c651..30c5184 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -39,7 +39,7 @@
<color name="background_dark">#ff101010</color>
<color name="bright_foreground_dark">#ffefefef</color>
<color name="bright_foreground_dark_disabled">#80ffffff</color>
- <color name="bright_foreground_dark_inverse">@android:color/background_dark</color>
+ <color name="bright_foreground_dark_inverse">@android:color/dim_foreground_dark</color>
<color name="dim_foreground_dark">#bebebe</color>
<color name="dim_foreground_dark_disabled">#80bebebe</color>
<color name="dim_foreground_dark_inverse">#323232</color>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 6aca361..ea5c158 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1324,6 +1324,7 @@
<public type="attr" name="imeSubtypeLocale" />
<public type="attr" name="imeSubtypeMode" />
<public type="attr" name="imeSubtypeExtraValue" />
+ <public type="attr" name="splitMotionEvents" />
<public type="anim" name="animator_fade_in" />
<public type="anim" name="animator_fade_out" />
@@ -1346,5 +1347,18 @@
the base class. -->
<public type="layout" name="list_content" />
+ <!-- A dark holographic theme. -->
+ <public type="style" name="Theme.Holo" />
+ <!-- A light holographic theme. -->
+ <public type="style" name="Theme.Light.Holo" />
+ <!-- Variant of the holographic (dark) theme with no title bar -->
+ <public type="style" name="Theme.Holo.NoTitleBar" />
+ <!-- Variant of the holographic (dark) theme that has no title bar and fills the entire screen -->
+ <public type="style" name="Theme.Holo.NoTitleBar.Fullscreen" />
+ <!-- Variant of the holographic light theme with no title bar -->
+ <public type="style" name="Theme.Light.Holo.NoTitleBar" />
+ <!-- Variant of the holographic light theme that has no title bar and fills the entire screen -->
+ <public type="style" name="Theme.Light.Holo.NoTitleBar.Fullscreen" />
+
<public type="string" name="selectTextMode" id="0x01040030" />
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a107048..e38a228 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1887,16 +1887,16 @@
<!-- Item on EditText context menu. This action is used to copy a URL from the edit field into the clipboard. -->
<string name="copyUrl">Copy URL</string>
- <!-- Item on EditText context menu. Added only when the context menu is not empty, it enable selection context mode. -->
+ <!-- Item on EditText context menu. Added only when the context menu is not empty, it enable selection context mode. [CHAR LIMIT=20] -->
<string name="selectTextMode">Select text...</string>
- <!-- Text selection contextual mode title, displayed in the CAB.. -->
+ <!-- Text selection contextual mode title, displayed in the CAB. [CHAR LIMIT=20] -->
<string name="textSelectionCABTitle">Text selection</string>
<!-- EditText context menu -->
<string name="inputMethod">Input method</string>
- <!-- Title for EditText context menu -->
+ <!-- Title for EditText context menu [CHAR LIMIT=20] -->
<string name="editTextMenuTitle">Text actions</string>
<!-- If the device is getting low on internal storage, a notification is shown to the user. This is the title of that notification. -->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 4294c22..37b66d3 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -425,9 +425,9 @@
<item name="android:focusable">true</item>
<item name="android:focusableInTouchMode">true</item>
<item name="android:clickable">true</item>
- <item name="android:background">@android:drawable/edit_text</item>
+ <item name="android:background">?android:attr/editTextBackground</item>
<item name="android:textAppearance">?android:attr/textAppearanceMediumInverse</item>
- <item name="android:textColor">@android:color/primary_text_light</item>
+ <item name="android:textColor">?android:attr/editTextColor</item>
<item name="android:gravity">center_vertical</item>
</style>
@@ -659,14 +659,10 @@
<style name="TextAppearance.DialogWindowTitle">
<item name="android:textSize">18sp</item>
- <item name="android:textStyle">normal</item>
- <item name="android:textColor">?textColorPrimary</item>
</style>
<style name="TextAppearance.Large">
<item name="android:textSize">22sp</item>
- <item name="android:textStyle">normal</item>
- <item name="android:textColor">?textColorPrimary</item>
</style>
<style name="TextAppearance.Large.Inverse">
@@ -678,8 +674,6 @@
<style name="TextAppearance.Medium">
<item name="android:textSize">18sp</item>
- <item name="android:textStyle">normal</item>
- <item name="android:textColor">?textColorPrimary</item>
</style>
<style name="TextAppearance.Medium.Inverse">
@@ -691,7 +685,6 @@
<style name="TextAppearance.Small">
<item name="android:textSize">14sp</item>
- <item name="android:textStyle">normal</item>
<item name="android:textColor">?textColorSecondary</item>
</style>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 348a7de51..c78705b 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -69,6 +69,9 @@
<item name="textAppearanceButton">@android:style/TextAppearance.Widget.Button</item>
+ <item name="editTextColor">?android:attr/textColorPrimaryInverse</item>
+ <item name="editTextBackground">@android:drawable/edit_text</item>
+
<item name="candidatesTextStyleSpans">@android:string/candidates_style</item>
<item name="textCheckMark">@android:drawable/indicator_check_mark_dark</item>
@@ -257,6 +260,7 @@
<item name="textColorLink">@android:color/link_text_light</item>
<item name="textColorLinkInverse">@android:color/link_text_dark</item>
+ <item name="editTextColor">?android:attr/textColorPrimary</item>
<item name="popupWindowStyle">@android:style/Widget.PopupWindow</item>
<item name="textCheckMark">@android:drawable/indicator_check_mark_light</item>
@@ -570,4 +574,38 @@
<style name="Theme.NoTitleBar.OverlayActionModes">
<item name="android:windowActionModeOverlay">true</item>
</style>
+
+ <!-- New Honeycomb holographic theme. Dark version -->
+ <style name="Theme.Holo">
+ <item name="editTextBackground">@android:drawable/edit_text_holo_dark</item>
+ <item name="editTextColor">?android:attr/textColorPrimary</item>
+ </style>
+
+ <!-- New Honeycomb holographic theme. Light version -->
+ <style name="Theme.Light.Holo">
+ <item name="editTextBackground">@android:drawable/edit_text_holo_light</item>
+ </style>
+
+ <!-- Variant of the holo (dark) theme with no title bar -->
+ <style name="Theme.Holo.NoTitleBar">
+ <item name="android:windowNoTitle">true</item>
+ </style>
+
+ <!-- Variant of the holo (dark) theme that has no title bar and fills the entire screen -->
+ <style name="Theme.Holo.NoTitleBar.Fullscreen">
+ <item name="android:windowFullscreen">true</item>
+ <item name="android:windowContentOverlay">@null</item>
+ </style>
+
+ <!-- Variant of the holo light theme with no title bar -->
+ <style name="Theme.Light.Holo.NoTitleBar">
+ <item name="android:windowNoTitle">true</item>
+ </style>
+
+ <!-- Variant of the holo light theme that has no title bar and fills the entire screen -->
+ <style name="Theme.Light.Holo.NoTitleBar.Fullscreen">
+ <item name="android:windowFullscreen">true</item>
+ <item name="android:windowContentOverlay">@null</item>
+ </style>
+
</resources>
diff --git a/docs/html/resources/tutorials/views/hello-formstuff.jd b/docs/html/resources/tutorials/views/hello-formstuff.jd
index 3dd5f21..b9f6c16 100644
--- a/docs/html/resources/tutorials/views/hello-formstuff.jd
+++ b/docs/html/resources/tutorials/views/hello-formstuff.jd
@@ -32,9 +32,19 @@
}
</pre>
+<p>Now select which kind of form widget you'd like to create:</p>
+<ul>
+ <li><a href="#CustomButton">Custom Button</a></li>
+ <li><a href="#EditText">Edit Text</a></li>
+ <li><a href="#Checkbox">Checkbox</a></li>
+ <li><a href="#RadioButtons">Radio Buttons</a></li>
+ <li><a href="#ToggleButton">Toggle Button</a></li>
+ <li><a href="#RatingBar">Rating Bar</a></li>
+</ul>
-<h2>Custom Button</h2>
+
+<h2 id="CustomButton">Custom Button</h2>
<p>In this section, you will create a button with a custom image instead of text, using the {@link
android.widget.Button} widget and an XML file that defines three different images to use for the
@@ -111,7 +121,8 @@
</ol>
-<h2>EditText</h2>
+
+<h2 id="EditText">Edit Text</h2>
<p>In this section, you will create a text field for user input, using the {@link
android.widget.EditText} widget. Once text has been entered into the field, the "Enter" key will
@@ -158,7 +169,8 @@
</ol>
-<h2>CheckBox</h2>
+
+<h2 id="Checkbox">Checkbox</h2>
<p>In this section, you will create a checkbox for selecting items, using the {@link
android.widget.CheckBox} widget. When the checkbox is pressed, a toast message will
@@ -209,7 +221,8 @@
android.widget.CompoundButton#toggle()} method.</p>
-<h2>RadioButton</h2>
+
+<h2 id="RadioButtons">Radio Buttons</h2>
<p>In this section, you will create two mutually-exclusive radio buttons (enabling one disables
the other), using the {@link android.widget.RadioGroup} and {@link android.widget.RadioButton}
@@ -274,7 +287,8 @@
android.widget.CompoundButton#toggle()} method.</p>
-<h2>ToggleButton</h2>
+
+<h2 id="ToggleButton">Toggle Button</h2>
<p>In this section, you'll create a button used specifically for toggling between two
states, using the {@link android.widget.ToggleButton} widget. This widget is an excellent
@@ -330,7 +344,7 @@
-<h2>RatingBar</h2>
+<h2 id="RatingBar">Rating Bar</h2>
<p>In this section, you'll create a widget that allows the user to provide a rating,
with the {@link android.widget.RatingBar} widget.</p>
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index d2845cf..5dbbd70 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -26,7 +26,6 @@
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,
@@ -39,7 +38,7 @@
* the same result from the decoder as if null were passed.
*/
public Options() {
- inDither = true;
+ inDither = false;
inScaled = true;
}
@@ -70,8 +69,8 @@
* system's screen depth, and characteristics of the original image such
* as if it has per-pixel alpha (requiring a config that also does).
*
- * The configuration is set to {@link android.graphics.Bitmap.Config#ARGB_8888}
- * by default.
+ * Image are loaded with the {@link Bitmap.Config#ARGB_8888} config by
+ * default.
*/
public Bitmap.Config inPreferredConfig = Bitmap.Config.ARGB_8888;
@@ -455,10 +454,8 @@
// into is.read(...) This number is not related to the value passed
// to mark(...) above.
byte [] tempStorage = null;
- if (opts != null)
- tempStorage = opts.inTempStorage;
- if (tempStorage == null)
- tempStorage = new byte[16 * 1024];
+ if (opts != null) tempStorage = opts.inTempStorage;
+ if (tempStorage == null) tempStorage = new byte[16 * 1024];
bm = nativeDecodeStream(is, tempStorage, outPadding, opts);
}
@@ -477,8 +474,7 @@
bm.setDensity(density);
final int targetDensity = opts.inTargetDensity;
- if (targetDensity == 0 || density == targetDensity
- || density == opts.inScreenDensity) {
+ if (targetDensity == 0 || density == targetDensity || density == opts.inScreenDensity) {
return bm;
}
@@ -655,8 +651,7 @@
// 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];
+ byte [] tempStorage = new byte[16 * 1024];
return nativeCreateLargeBitmap(is, tempStorage, isShareable);
}
}
@@ -677,8 +672,8 @@
* @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 {
+ public static LargeBitmap createLargeBitmap(String pathName, boolean isShareable)
+ throws IOException {
LargeBitmap bm = null;
InputStream stream = null;
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index 3624678..b2e8dd9 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -37,6 +37,7 @@
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
+#include "android_runtime/android_view_Surface.h"
#include <RenderScript.h>
#include <RenderScriptEnv.h>
@@ -184,9 +185,7 @@
if (wnd == NULL) {
} else {
- jclass surface_class = _env->FindClass("android/view/Surface");
- jfieldID surfaceFieldID = _env->GetFieldID(surface_class, ANDROID_VIEW_SURFACE_JNI_ID, "I");
- window = (Surface*)_env->GetIntField(wnd, surfaceFieldID);
+ window = (Surface*) android_Surface_getNativeWindow(_env, wnd).get();
}
rsContextSetSurface(con, width, height, window);
diff --git a/include/media/stagefright/AMRWriter.h b/include/media/stagefright/AMRWriter.h
index 813dd43..aa965e1 100644
--- a/include/media/stagefright/AMRWriter.h
+++ b/include/media/stagefright/AMRWriter.h
@@ -37,8 +37,8 @@
virtual status_t addSource(const sp<MediaSource> &source);
virtual bool reachedEOS();
virtual status_t start(MetaData *params = NULL);
- virtual void stop();
- virtual void pause();
+ virtual status_t stop();
+ virtual status_t pause();
protected:
virtual ~AMRWriter();
@@ -57,7 +57,7 @@
int64_t mEstimatedDurationUs;
static void *ThreadWrapper(void *);
- void threadFunc();
+ status_t threadFunc();
bool exceedsFileSizeLimit();
bool exceedsFileDurationLimit();
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index be96935..de82b38 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -37,9 +37,9 @@
virtual status_t addSource(const sp<MediaSource> &source);
virtual status_t start(MetaData *param = NULL);
+ virtual status_t stop();
+ virtual status_t pause();
virtual bool reachedEOS();
- virtual void stop();
- virtual void pause();
void beginBox(const char *fourcc);
void writeInt8(int8_t x);
diff --git a/include/media/stagefright/MediaWriter.h b/include/media/stagefright/MediaWriter.h
index 8d3a9df..151bf16 100644
--- a/include/media/stagefright/MediaWriter.h
+++ b/include/media/stagefright/MediaWriter.h
@@ -35,8 +35,9 @@
virtual status_t addSource(const sp<MediaSource> &source) = 0;
virtual bool reachedEOS() = 0;
virtual status_t start(MetaData *params = NULL) = 0;
- virtual void stop() = 0;
- virtual void pause() = 0;
+ virtual status_t stop() = 0;
+ virtual status_t pause() = 0;
+
virtual void setMaxFileSize(int64_t bytes) { mMaxFileSizeLimitBytes = bytes; }
virtual void setMaxFileDuration(int64_t durationUs) { mMaxFileDurationLimitUs = durationUs; }
virtual void setListener(const sp<IMediaRecorderClient>& listener) {
diff --git a/include/media/stagefright/YUVCanvas.h b/include/media/stagefright/YUVCanvas.h
index 5e17046..ff70923 100644
--- a/include/media/stagefright/YUVCanvas.h
+++ b/include/media/stagefright/YUVCanvas.h
@@ -55,6 +55,18 @@
int32_t destStartX, int32_t destStartY,
const YUVImage &srcImage);
+ // Downsamples the srcImage into the canvas' target image (mYUVImage)
+ // The downsampling copies pixels from the source image starting at
+ // (srcOffsetX, srcOffsetY) to the target image, starting at (0, 0).
+ // For each X increment in the target image, skipX pixels are skipped
+ // in the source image.
+ // Similarly for each Y increment in the target image, skipY pixels
+ // are skipped in the source image.
+ void downsample(
+ int32_t srcOffsetX, int32_t srcOffsetY,
+ int32_t skipX, int32_t skipY,
+ const YUVImage &srcImage);
+
private:
YUVImage& mYUVImage;
diff --git a/include/media/stagefright/YUVImage.h b/include/media/stagefright/YUVImage.h
index afeac3f..4e98618 100644
--- a/include/media/stagefright/YUVImage.h
+++ b/include/media/stagefright/YUVImage.h
@@ -67,18 +67,22 @@
// memory.
static size_t bufferSize(YUVFormat yuvFormat, int32_t width, int32_t height);
- int32_t width() {return mWidth;}
- int32_t height() {return mHeight;}
+ int32_t width() const {return mWidth;}
+ int32_t height() const {return mHeight;}
+
+ // Returns true if pixel is the range [0, width-1] x [0, height-1]
+ // and false otherwise.
+ bool validPixel(int32_t x, int32_t y) const;
// Get the pixel YUV value at pixel (x,y).
// Note that the range of x is [0, width-1] and the range of y is [0, height-1].
- // Returns true if get was succesful and false otherwise.
+ // Returns true if get was successful and false otherwise.
bool getPixelValue(int32_t x, int32_t y,
uint8_t *yPtr, uint8_t *uPtr, uint8_t *vPtr) const;
// Set the pixel YUV value at pixel (x,y).
// Note that the range of x is [0, width-1] and the range of y is [0, height-1].
- // Returns true if set was succesful and false otherwise.
+ // Returns true if set was successful and false otherwise.
bool setPixelValue(int32_t x, int32_t y,
uint8_t yValue, uint8_t uValue, uint8_t vValue);
diff --git a/media/libstagefright/mpeg2ts/ABitReader.h b/include/media/stagefright/foundation/ABitReader.h
similarity index 100%
rename from media/libstagefright/mpeg2ts/ABitReader.h
rename to include/media/stagefright/foundation/ABitReader.h
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h
index 2505cb0..aed4fa1 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -159,6 +159,12 @@
virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
int32_t injectorPid, int32_t injectorUid,
Vector<InputTarget>& outTargets) = 0;
+
+ /* Gets the maximum suggested event delivery rate per second.
+ * This value is used to throttle motion event movement actions on a per-device
+ * basis. It is not intended to be a hard limit.
+ */
+ virtual int32_t getMaxEventsPerSecond() = 0;
};
@@ -332,6 +338,8 @@
// Linked list of motion samples associated with this motion event.
MotionSample firstSample;
MotionSample* lastSample;
+
+ uint32_t countSamples() const;
};
// Tracks the progress of dispatching a particular event to a particular connection.
@@ -587,6 +595,17 @@
Condition mInjectionSyncFinishedCondition;
void decrementPendingSyncDispatchesLocked(EventEntry* entry);
+ // Throttling state.
+ struct ThrottleState {
+ nsecs_t minTimeBetweenEvents;
+
+ nsecs_t lastEventTime;
+ int32_t lastDeviceId;
+ uint32_t lastSource;
+
+ uint32_t originalSampleCount; // only collected during debugging
+ } mThrottleState;
+
// Key repeat tracking.
// XXX Move this up to the input reader instead.
struct KeyRepeatState {
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
index 71c6c51..56d2765 100644
--- a/include/ui/InputReader.h
+++ b/include/ui/InputReader.h
@@ -480,10 +480,6 @@
inline void clear() {
fields = 0;
}
-
- inline bool isDirty() {
- return fields != 0;
- }
} mAccumulator;
float mXScale;
@@ -702,7 +698,7 @@
} historyData[AVERAGING_HISTORY_SIZE];
} mAveragingTouchFilter;
- struct JumpTouchFilterState {
+ struct JumpyTouchFilterState {
uint32_t jumpyPointsDropped;
} mJumpyTouchFilter;
@@ -765,10 +761,6 @@
inline void clear() {
fields = 0;
}
-
- inline bool isDirty() {
- return fields != 0;
- }
} mAccumulator;
bool mDown;
@@ -804,7 +796,8 @@
FIELD_ABS_MT_WIDTH_MAJOR = 16,
FIELD_ABS_MT_WIDTH_MINOR = 32,
FIELD_ABS_MT_ORIENTATION = 64,
- FIELD_ABS_MT_TRACKING_ID = 128
+ FIELD_ABS_MT_TRACKING_ID = 128,
+ FIELD_ABS_MT_PRESSURE = 256,
};
uint32_t pointerCount;
@@ -819,6 +812,7 @@
int32_t absMTWidthMinor;
int32_t absMTOrientation;
int32_t absMTTrackingId;
+ int32_t absMTPressure;
inline void clear() {
fields = 0;
@@ -829,10 +823,6 @@
pointerCount = 0;
pointers[0].clear();
}
-
- inline bool isDirty() {
- return pointerCount != 0;
- }
} mAccumulator;
void initialize();
diff --git a/include/utils/ObbFile.h b/include/utils/ObbFile.h
index d2ca82e..5243f50 100644
--- a/include/utils/ObbFile.h
+++ b/include/utils/ObbFile.h
@@ -18,12 +18,16 @@
#define OBBFILE_H_
#include <stdint.h>
+#include <strings.h>
#include <utils/RefBase.h>
#include <utils/String8.h>
namespace android {
+// OBB flags (bit 0)
+#define OBB_OVERLAY (1 << 0)
+
class ObbFile : public RefBase {
protected:
virtual ~ObbFile();
@@ -46,18 +50,38 @@
return mPackageName;
}
- int32_t getVersion() const {
- return mVersion;
- }
-
void setPackageName(String8 packageName) {
mPackageName = packageName;
}
+ int32_t getVersion() const {
+ return mVersion;
+ }
+
void setVersion(int32_t version) {
mVersion = version;
}
+ int32_t getFlags() const {
+ return mFlags;
+ }
+
+ void setFlags(int32_t flags) {
+ mFlags = flags;
+ }
+
+ bool isOverlay() {
+ return (mFlags & OBB_OVERLAY) == OBB_OVERLAY;
+ }
+
+ void setOverlay(bool overlay) {
+ if (overlay) {
+ mFlags |= OBB_OVERLAY;
+ } else {
+ mFlags &= ~OBB_OVERLAY;
+ }
+ }
+
static inline uint32_t get4LE(const unsigned char* buf) {
return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
}
@@ -76,6 +100,9 @@
/* Package version this ObbFile is associated with */
int32_t mVersion;
+ /* Flags for this OBB type. */
+ int32_t mFlags;
+
const char* mFileName;
size_t mFileSize;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 3440687..49d49da 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -575,6 +575,14 @@
return;
}
+ float scaleX = paint->getTextScaleX();
+ bool applyScaleX = scaleX < 0.9999f || scaleX > 1.0001f;
+ if (applyScaleX) {
+ save(0);
+ translate(x - (x * scaleX), 0.0f);
+ scale(scaleX, 1.0f);
+ }
+
float length = -1.0f;
switch (paint->getTextAlign()) {
case SkPaint::kCenter_Align:
@@ -626,6 +634,10 @@
glDisableVertexAttribArray(mCurrentProgram->getAttrib("texCoords"));
drawTextDecorations(text, bytesCount, length, x, y, paint);
+
+ if (applyScaleX) {
+ restore();
+ }
}
void OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) {
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 3f9698d..2e8a8be 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -149,9 +149,11 @@
GL_RGB, GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels());
break;
case SkBitmap::kARGB_8888_Config:
- texture->blend = !bitmap->isOpaque();
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap->rowBytesAsPixels(), texture->height, 0,
GL_RGBA, GL_UNSIGNED_BYTE, bitmap->getPixels());
+ // Do this after calling getPixels() to make sure Skia's deferred
+ // decoding happened
+ texture->blend = !bitmap->isOpaque();
break;
default:
break;
diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp
index a3c34d0..891661d 100644
--- a/libs/ui/EventHub.cpp
+++ b/libs/ui/EventHub.cpp
@@ -581,7 +581,6 @@
if (strcmp(name, test) == 0) {
LOGI("ignoring event id %s driver %s\n", deviceName, test);
close(fd);
- fd = -1;
return -1;
}
}
@@ -813,6 +812,14 @@
device->id, name, propName, keylayoutFilename);
}
+ // If the device isn't recognized as something we handle, don't monitor it.
+ if (device->classes == 0) {
+ LOGV("Dropping device %s %p, id = %d\n", deviceName, device, devid);
+ close(fd);
+ delete device;
+ return -1;
+ }
+
LOGI("New device: path=%s name=%s id=0x%x (of 0x%x) index=%d fd=%d classes=0x%x\n",
deviceName, name, device->id, mNumDevicesById, mFDCount, fd, device->classes);
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index 13030b5..e35050c 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -28,6 +28,9 @@
// Log debug messages about input event injection.
#define DEBUG_INJECTION 0
+// Log debug messages about input event throttling.
+#define DEBUG_THROTTLING 0
+
#include <cutils/log.h>
#include <ui/InputDispatcher.h>
@@ -66,6 +69,15 @@
mKeyRepeatState.lastKeyEntry = NULL;
+ int32_t maxEventsPerSecond = policy->getMaxEventsPerSecond();
+ mThrottleState.minTimeBetweenEvents = 1000000000LL / maxEventsPerSecond;
+ mThrottleState.lastDeviceId = -1;
+
+#if DEBUG_THROTTLING
+ mThrottleState.originalSampleCount = 0;
+ LOGD("Throttling - Max events per second = %d", maxEventsPerSecond);
+#endif
+
mCurrentInputTargetsValid = false;
}
@@ -144,12 +156,61 @@
}
} else {
// Inbound queue has at least one entry.
- // Start processing it but leave it on the queue until later so that the
+ EventEntry* entry = mInboundQueue.head.next;
+
+ // Consider throttling the entry if it is a move event and there are no
+ // other events behind it in the queue. Due to movement batching, additional
+ // samples may be appended to this event by the time the throttling timeout
+ // expires.
+ // TODO Make this smarter and consider throttling per device independently.
+ if (entry->type == EventEntry::TYPE_MOTION) {
+ MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
+ int32_t deviceId = motionEntry->deviceId;
+ uint32_t source = motionEntry->source;
+ if (motionEntry->next == & mInboundQueue.tail
+ && motionEntry->action == AMOTION_EVENT_ACTION_MOVE
+ && deviceId == mThrottleState.lastDeviceId
+ && source == mThrottleState.lastSource) {
+ nsecs_t nextTime = mThrottleState.lastEventTime
+ + mThrottleState.minTimeBetweenEvents;
+ if (currentTime < nextTime) {
+ // Throttle it!
+#if DEBUG_THROTTLING
+ LOGD("Throttling - Delaying motion event for "
+ "device 0x%x, source 0x%08x by up to %0.3fms.",
+ deviceId, source, (nextTime - currentTime) * 0.000001);
+#endif
+ if (nextTime < nextWakeupTime) {
+ nextWakeupTime = nextTime;
+ }
+ if (mThrottleState.originalSampleCount == 0) {
+ mThrottleState.originalSampleCount =
+ motionEntry->countSamples();
+ }
+ goto Throttle;
+ }
+ }
+
+#if DEBUG_THROTTLING
+ if (mThrottleState.originalSampleCount != 0) {
+ uint32_t count = motionEntry->countSamples();
+ LOGD("Throttling - Motion event sample count grew by %d from %d to %d.",
+ count - mThrottleState.originalSampleCount,
+ mThrottleState.originalSampleCount, count);
+ mThrottleState.originalSampleCount = 0;
+ }
+#endif
+
+ mThrottleState.lastEventTime = entry->eventTime < currentTime
+ ? entry->eventTime : currentTime;
+ mThrottleState.lastDeviceId = deviceId;
+ mThrottleState.lastSource = source;
+ }
+
+ // Start processing the entry but leave it on the queue until later so that the
// input reader can keep appending samples onto a motion event between the
// time we started processing it and the time we finally enqueue dispatch
// entries for it.
- EventEntry* entry = mInboundQueue.head.next;
-
switch (entry->type) {
case EventEntry::TYPE_CONFIGURATION_CHANGED: {
ConfigurationChangedEntry* typedEntry =
@@ -179,6 +240,8 @@
mInboundQueue.dequeue(entry);
mAllocator.releaseEventEntry(entry);
skipPoll = true;
+
+ Throttle: ;
}
}
@@ -192,8 +255,8 @@
return;
}
- // Wait for callback or timeout or wake.
- nsecs_t timeout = nanoseconds_to_milliseconds(nextWakeupTime - currentTime);
+ // Wait for callback or timeout or wake. (make sure we round up, not down)
+ nsecs_t timeout = (nextWakeupTime - currentTime + 999999LL) / 1000000LL;
int32_t timeoutMillis = timeout > INT_MAX ? -1 : timeout > 0 ? int32_t(timeout) : 0;
mPollLoop->pollOnce(timeoutMillis);
}
@@ -1708,6 +1771,16 @@
motionEntry->lastSample = sample;
}
+// --- InputDispatcher::MotionEntry ---
+
+uint32_t InputDispatcher::MotionEntry::countSamples() const {
+ uint32_t count = 1;
+ for (MotionSample* sample = firstSample.next; sample != NULL; sample = sample->next) {
+ count += 1;
+ }
+ return count;
+}
+
// --- InputDispatcher::Connection ---
InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel) :
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index 5f5a4ac..6f042ec 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -945,7 +945,6 @@
mAccumulator.fields = Accumulator::FIELD_BTN_MOUSE;
mAccumulator.btnMouse = false;
sync(when);
- mAccumulator.clear();
}
InputMapper::reset();
@@ -958,9 +957,9 @@
case BTN_MOUSE:
mAccumulator.fields |= Accumulator::FIELD_BTN_MOUSE;
mAccumulator.btnMouse = rawEvent->value != 0;
-
+ // Sync now since BTN_MOUSE is not necessarily followed by SYN_REPORT and
+ // we need to ensure that we report the up/down promptly.
sync(rawEvent->when);
- mAccumulator.clear();
break;
}
break;
@@ -981,10 +980,7 @@
case EV_SYN:
switch (rawEvent->scanCode) {
case SYN_REPORT:
- if (mAccumulator.isDirty()) {
- sync(rawEvent->when);
- mAccumulator.clear();
- }
+ sync(rawEvent->when);
break;
}
break;
@@ -992,13 +988,17 @@
}
void TrackballInputMapper::sync(nsecs_t when) {
+ uint32_t fields = mAccumulator.fields;
+ if (fields == 0) {
+ return; // no new state changes, so nothing to do
+ }
+
int motionEventAction;
PointerCoords pointerCoords;
nsecs_t downTime;
{ // acquire lock
AutoMutex _l(mLock);
- uint32_t fields = mAccumulator.fields;
bool downChanged = fields & Accumulator::FIELD_BTN_MOUSE;
if (downChanged) {
@@ -1061,6 +1061,8 @@
} // release lock
applyPolicyAndDispatch(when, motionEventAction, & pointerCoords, downTime);
+
+ mAccumulator.clear();
}
void TrackballInputMapper::applyPolicyAndDispatch(nsecs_t when, int32_t motionEventAction,
@@ -2380,8 +2382,8 @@
mDown = false;
mX = 0;
mY = 0;
- mPressure = 0;
- mSize = 0;
+ mPressure = 1; // default to 1 for devices that don't report pressure
+ mSize = 0; // default to 0 for devices that don't report size
}
void SingleTouchInputMapper::reset() {
@@ -2397,9 +2399,9 @@
case BTN_TOUCH:
mAccumulator.fields |= Accumulator::FIELD_BTN_TOUCH;
mAccumulator.btnTouch = rawEvent->value != 0;
-
- sync(rawEvent->when);
- mAccumulator.clear();
+ // Don't sync immediately. Wait until the next SYN_REPORT since we might
+ // not have received valid position information yet. This logic assumes that
+ // BTN_TOUCH is always followed by SYN_REPORT as part of a complete packet.
break;
}
break;
@@ -2428,10 +2430,7 @@
case EV_SYN:
switch (rawEvent->scanCode) {
case SYN_REPORT:
- if (mAccumulator.isDirty()) {
- sync(rawEvent->when);
- mAccumulator.clear();
- }
+ sync(rawEvent->when);
break;
}
break;
@@ -2439,9 +2438,10 @@
}
void SingleTouchInputMapper::sync(nsecs_t when) {
- /* Update device state */
-
uint32_t fields = mAccumulator.fields;
+ if (fields == 0) {
+ return; // no new state changes, so nothing to do
+ }
if (fields & Accumulator::FIELD_BTN_TOUCH) {
mDown = mAccumulator.btnTouch;
@@ -2472,8 +2472,8 @@
mCurrentTouch.pointers[0].y = mY;
mCurrentTouch.pointers[0].pressure = mPressure;
mCurrentTouch.pointers[0].size = mSize;
- mCurrentTouch.pointers[0].touchMajor = mPressure;
- mCurrentTouch.pointers[0].touchMinor = mPressure;
+ mCurrentTouch.pointers[0].touchMajor = mSize;
+ mCurrentTouch.pointers[0].touchMinor = mSize;
mCurrentTouch.pointers[0].toolMajor = mSize;
mCurrentTouch.pointers[0].toolMinor = mSize;
mCurrentTouch.pointers[0].orientation = 0;
@@ -2482,6 +2482,8 @@
}
syncTouch(when, true);
+
+ mAccumulator.clear();
}
void SingleTouchInputMapper::configureAxes() {
@@ -2494,8 +2496,8 @@
getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_PRESSURE, & mAxes.pressure);
getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_TOOL_WIDTH, & mAxes.size);
- mAxes.touchMajor = mAxes.pressure;
- mAxes.touchMinor = mAxes.pressure;
+ mAxes.touchMajor = mAxes.size;
+ mAxes.touchMinor = mAxes.size;
mAxes.toolMajor = mAxes.size;
mAxes.toolMinor = mAxes.size;
}
@@ -2585,10 +2587,7 @@
}
case SYN_REPORT:
- if (mAccumulator.isDirty()) {
- sync(rawEvent->when);
- mAccumulator.clear();
- }
+ sync(rawEvent->when);
break;
}
break;
@@ -2598,11 +2597,7 @@
void MultiTouchInputMapper::sync(nsecs_t when) {
static const uint32_t REQUIRED_FIELDS =
Accumulator::FIELD_ABS_MT_POSITION_X
- | Accumulator::FIELD_ABS_MT_POSITION_Y
- | Accumulator::FIELD_ABS_MT_TOUCH_MAJOR
- | Accumulator::FIELD_ABS_MT_WIDTH_MAJOR;
-
- /* Update device state */
+ | Accumulator::FIELD_ABS_MT_POSITION_Y;
uint32_t inCount = mAccumulator.pointerCount;
uint32_t outCount = 0;
@@ -2611,53 +2606,76 @@
mCurrentTouch.clear();
for (uint32_t inIndex = 0; inIndex < inCount; inIndex++) {
- uint32_t fields = mAccumulator.pointers[inIndex].fields;
+ const Accumulator::Pointer& inPointer = mAccumulator.pointers[inIndex];
+ uint32_t fields = inPointer.fields;
if ((fields & REQUIRED_FIELDS) != REQUIRED_FIELDS) {
-#if DEBUG_POINTERS
- LOGD("Pointers: Missing required multitouch pointer fields: index=%d, fields=%d",
- inIndex, fields);
- continue;
-#endif
- }
-
- if (mAccumulator.pointers[inIndex].absMTTouchMajor <= 0) {
- // Pointer is not down. Drop it.
+ // Some drivers send empty MT sync packets without X / Y to indicate a pointer up.
+ // Drop this finger.
continue;
}
- mCurrentTouch.pointers[outCount].x = mAccumulator.pointers[inIndex].absMTPositionX;
- mCurrentTouch.pointers[outCount].y = mAccumulator.pointers[inIndex].absMTPositionY;
+ PointerData& outPointer = mCurrentTouch.pointers[outCount];
+ outPointer.x = inPointer.absMTPositionX;
+ outPointer.y = inPointer.absMTPositionY;
- mCurrentTouch.pointers[outCount].touchMajor =
- mAccumulator.pointers[inIndex].absMTTouchMajor;
- mCurrentTouch.pointers[outCount].touchMinor =
- (fields & Accumulator::FIELD_ABS_MT_TOUCH_MINOR) != 0
- ? mAccumulator.pointers[inIndex].absMTTouchMinor
- : mAccumulator.pointers[inIndex].absMTTouchMajor;
+ if (fields & Accumulator::FIELD_ABS_MT_TOUCH_MAJOR) {
+ int32_t value = inPointer.absMTTouchMajor;
+ if (value <= 0) {
+ // Some devices send sync packets with X / Y but with a 0 touch major to indicate
+ // a pointer up. Drop this finger.
+ continue;
+ }
+ outPointer.touchMajor = inPointer.absMTTouchMajor;
+ } else {
+ outPointer.touchMajor = 0;
+ }
- mCurrentTouch.pointers[outCount].toolMajor =
- mAccumulator.pointers[inIndex].absMTWidthMajor;
- mCurrentTouch.pointers[outCount].toolMinor =
- (fields & Accumulator::FIELD_ABS_MT_WIDTH_MINOR) != 0
- ? mAccumulator.pointers[inIndex].absMTWidthMinor
- : mAccumulator.pointers[inIndex].absMTWidthMajor;
+ if (fields & Accumulator::FIELD_ABS_MT_TOUCH_MINOR) {
+ outPointer.touchMinor = inPointer.absMTTouchMinor;
+ } else {
+ outPointer.touchMinor = outPointer.touchMajor;
+ }
- mCurrentTouch.pointers[outCount].orientation =
- (fields & Accumulator::FIELD_ABS_MT_ORIENTATION) != 0
- ? mAccumulator.pointers[inIndex].absMTOrientation : 0;
+ if (fields & Accumulator::FIELD_ABS_MT_WIDTH_MAJOR) {
+ outPointer.toolMajor = inPointer.absMTWidthMajor;
+ } else {
+ outPointer.toolMajor = outPointer.touchMajor;
+ }
- // Derive an approximation of pressure and size.
- // FIXME assignment of pressure may be incorrect, probably better to let
- // pressure = touch / width. Later on we pass width to MotionEvent as a size, which
- // isn't quite right either. Should be using touch for that.
- mCurrentTouch.pointers[outCount].pressure = mAccumulator.pointers[inIndex].absMTTouchMajor;
- mCurrentTouch.pointers[outCount].size = mAccumulator.pointers[inIndex].absMTWidthMajor;
+ if (fields & Accumulator::FIELD_ABS_MT_WIDTH_MINOR) {
+ outPointer.toolMinor = inPointer.absMTWidthMinor;
+ } else {
+ outPointer.toolMinor = outPointer.toolMajor;
+ }
+
+ if (fields & Accumulator::FIELD_ABS_MT_ORIENTATION) {
+ outPointer.orientation = inPointer.absMTOrientation;
+ } else {
+ outPointer.orientation = 0;
+ }
+
+ if (fields & Accumulator::FIELD_ABS_MT_PRESSURE) {
+ outPointer.pressure = inPointer.absMTPressure;
+ } else {
+ // Derive an approximation of pressure.
+ // FIXME Traditionally we have just passed a normalized value based on
+ // ABS_MT_TOUCH_MAJOR as an estimate of pressure but the result is not
+ // very meaningful, particularly on large displays. We should probably let
+ // pressure = touch_major / tool_major but it is unclear whether that will
+ // break applications.
+ outPointer.pressure = outPointer.touchMajor;
+ }
+
+ // Size is an alias for a normalized tool width.
+ // FIXME Normalized tool width doesn't actually make much sense since it literally
+ // means the approaching contact major axis is divided by its full range as
+ // reported by the driver. On a large display this could produce very small values.
+ outPointer.size = outPointer.toolMajor;
if (havePointerIds) {
- if (fields & Accumulator::
- FIELD_ABS_MT_TRACKING_ID) {
- uint32_t id = uint32_t(mAccumulator.pointers[inIndex].absMTTrackingId);
+ if (fields & Accumulator::FIELD_ABS_MT_TRACKING_ID) {
+ uint32_t id = uint32_t(inPointer.absMTTrackingId);
if (id > MAX_POINTER_ID) {
#if DEBUG_POINTERS
@@ -2668,7 +2686,7 @@
havePointerIds = false;
}
else {
- mCurrentTouch.pointers[outCount].id = id;
+ outPointer.id = id;
mCurrentTouch.idToIndex[id] = outCount;
mCurrentTouch.idBits.markBit(id);
}
@@ -2683,6 +2701,8 @@
mCurrentTouch.pointerCount = outCount;
syncTouch(when, havePointerIds);
+
+ mAccumulator.clear();
}
void MultiTouchInputMapper::configureAxes() {
@@ -2697,6 +2717,7 @@
getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MAJOR, & mAxes.toolMajor);
getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MINOR, & mAxes.toolMinor);
getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_ORIENTATION, & mAxes.orientation);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_PRESSURE, & mAxes.pressure);
if (! mAxes.touchMinor.valid) {
mAxes.touchMinor = mAxes.touchMajor;
@@ -2706,7 +2727,10 @@
mAxes.toolMinor = mAxes.toolMajor;
}
- mAxes.pressure = mAxes.touchMajor;
+ if (! mAxes.pressure.valid) {
+ mAxes.pressure = mAxes.touchMajor;
+ }
+
mAxes.size = mAxes.toolMajor;
}
diff --git a/libs/ui/PixelFormat.cpp b/libs/ui/PixelFormat.cpp
index edf1aed..ee186c8 100644
--- a/libs/ui/PixelFormat.cpp
+++ b/libs/ui/PixelFormat.cpp
@@ -63,7 +63,6 @@
info->bitsPerPixel = 16;
goto done;
case HAL_PIXEL_FORMAT_YCrCb_420_SP:
- case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED:
case HAL_PIXEL_FORMAT_YV12:
info->bitsPerPixel = 12;
done:
diff --git a/libs/utils/ObbFile.cpp b/libs/utils/ObbFile.cpp
index adedf0c..e170ab8 100644
--- a/libs/utils/ObbFile.cpp
+++ b/libs/utils/ObbFile.cpp
@@ -29,12 +29,13 @@
#define kFooterTagSize 8 /* last two 32-bit integers */
-#define kFooterMinSize 21 /* 32-bit signature version
- * 32-bit package version
- * 32-bit package name size
- * 1-character package name
- * 32-bit footer size
- * 32-bit footer marker
+#define kFooterMinSize 25 /* 32-bit signature version (4 bytes)
+ * 32-bit package version (4 bytes)
+ * 32-bit flags (4 bytes)
+ * 32-bit package name size (4-bytes)
+ * >=1-character package name (1 byte)
+ * 32-bit footer size (4 bytes)
+ * 32-bit footer marker (4 bytes)
*/
#define kMaxBufSize 32768 /* Maximum file read buffer */
@@ -45,8 +46,9 @@
/* offsets in version 1 of the header */
#define kPackageVersionOffset 4
-#define kPackageNameLenOffset 8
-#define kPackageNameOffset 12
+#define kFlagsOffset 8
+#define kPackageNameLenOffset 12
+#define kPackageNameOffset 16
/*
* TEMP_FAILURE_RETRY is defined by some, but not all, versions of
@@ -78,7 +80,10 @@
namespace android {
ObbFile::ObbFile() :
- mVersion(-1) {
+ mPackageName(""),
+ mVersion(-1),
+ mFlags(0)
+{
}
ObbFile::~ObbFile() {
@@ -199,6 +204,7 @@
}
mVersion = (int32_t) get4LE((unsigned char*)scanBuf + kPackageVersionOffset);
+ mFlags = (int32_t) get4LE((unsigned char*)scanBuf + kFlagsOffset);
uint32_t packageNameLen = get4LE((unsigned char*)scanBuf + kPackageNameLenOffset);
if (packageNameLen <= 0
@@ -268,6 +274,12 @@
return false;
}
+ put4LE(intBuf, mFlags);
+ if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
+ LOGW("couldn't write package version");
+ return false;
+ }
+
size_t packageNameLen = mPackageName.size();
put4LE(intBuf, packageNameLen);
if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
@@ -280,7 +292,7 @@
return false;
}
- put4LE(intBuf, 3*sizeof(uint32_t) + packageNameLen);
+ put4LE(intBuf, kPackageNameOffset + packageNameLen);
if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
LOGW("couldn't write footer size: %s", strerror(errno));
return false;
diff --git a/media/java/android/media/BassBoost.java b/media/java/android/media/BassBoost.java
index 73c1751..476b056 100644
--- a/media/java/android/media/BassBoost.java
+++ b/media/java/android/media/BassBoost.java
@@ -99,7 +99,7 @@
UnsupportedOperationException, RuntimeException {
super(EFFECT_TYPE_BASS_BOOST, EFFECT_TYPE_NULL, priority, audioSession);
- short[] value = new short[1];
+ int[] value = new int[1];
checkStatus(getParameter(PARAM_STRENGTH_SUPPORTED, value));
mStrengthSupported = (value[0] != 0);
}
diff --git a/media/java/android/media/Virtualizer.java b/media/java/android/media/Virtualizer.java
index d03c2a8..b08f36e 100644
--- a/media/java/android/media/Virtualizer.java
+++ b/media/java/android/media/Virtualizer.java
@@ -100,7 +100,7 @@
UnsupportedOperationException, RuntimeException {
super(EFFECT_TYPE_VIRTUALIZER, EFFECT_TYPE_NULL, priority, audioSession);
- short[] value = new short[1];
+ int[] value = new int[1];
checkStatus(getParameter(PARAM_STRENGTH_SUPPORTED, value));
mStrengthSupported = (value[0] != 0);
}
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index a70bdff..bcd646a 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -1626,9 +1626,15 @@
switch (param){
case BASSBOOST_PARAM_STRENGTH_SUPPORTED:
+ if (*pValueSize != sizeof(uint32_t)){
+ LOGV("\tLVM_ERROR : BassBoost_getParameter() invalid pValueSize %d", *pValueSize);
+ return -EINVAL;
+ }
+ *pValueSize = sizeof(uint32_t);
+ break;
case BASSBOOST_PARAM_STRENGTH:
if (*pValueSize != sizeof(int16_t)){
- LOGV("\tLVM_ERROR : BassBoost_getParameter() invalid pValueSize2 %d", *pValueSize);
+ LOGV("\tLVM_ERROR : BassBoost_getParameter() invalid pValueSize %d", *pValueSize);
return -EINVAL;
}
*pValueSize = sizeof(int16_t);
@@ -1736,9 +1742,16 @@
switch (param){
case VIRTUALIZER_PARAM_STRENGTH_SUPPORTED:
+ if (*pValueSize != sizeof(uint32_t)){
+ LOGV("\tLVM_ERROR : Virtualizer_getParameter() invalid pValueSize %d",*pValueSize);
+ return -EINVAL;
+ }
+ *pValueSize = sizeof(uint32_t);
+ break;
+
case VIRTUALIZER_PARAM_STRENGTH:
if (*pValueSize != sizeof(int16_t)){
- LOGV("\tLVM_ERROR : Virtualizer_getParameter() invalid pValueSize2 %d",*pValueSize);
+ LOGV("\tLVM_ERROR : Virtualizer_getParameter() invalid pValueSize %d",*pValueSize);
return -EINVAL;
}
*pValueSize = sizeof(int16_t);
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index a616aae..59a544c 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -1141,8 +1141,9 @@
status_t StagefrightRecorder::stop() {
LOGV("stop");
+ status_t err = OK;
if (mWriter != NULL) {
- mWriter->stop();
+ err = mWriter->stop();
mWriter.clear();
}
@@ -1164,7 +1165,7 @@
mOutputFd = -1;
}
- return OK;
+ return err;
}
status_t StagefrightRecorder::close() {
diff --git a/media/libstagefright/AMRWriter.cpp b/media/libstagefright/AMRWriter.cpp
index c71743e..71d48b3 100644
--- a/media/libstagefright/AMRWriter.cpp
+++ b/media/libstagefright/AMRWriter.cpp
@@ -136,16 +136,17 @@
return OK;
}
-void AMRWriter::pause() {
+status_t AMRWriter::pause() {
if (!mStarted) {
- return;
+ return OK;
}
mPaused = true;
+ return OK;
}
-void AMRWriter::stop() {
+status_t AMRWriter::stop() {
if (!mStarted) {
- return;
+ return OK;
}
mDone = true;
@@ -153,9 +154,17 @@
void *dummy;
pthread_join(mThread, &dummy);
- mSource->stop();
+ status_t err = (status_t) dummy;
+ {
+ status_t status = mSource->stop();
+ if (err == OK &&
+ (status != OK && status != ERROR_END_OF_STREAM)) {
+ err = status;
+ }
+ }
mStarted = false;
+ return err;
}
bool AMRWriter::exceedsFileSizeLimit() {
@@ -174,21 +183,20 @@
// static
void *AMRWriter::ThreadWrapper(void *me) {
- static_cast<AMRWriter *>(me)->threadFunc();
-
- return NULL;
+ return (void *) static_cast<AMRWriter *>(me)->threadFunc();
}
-void AMRWriter::threadFunc() {
+status_t AMRWriter::threadFunc() {
mEstimatedDurationUs = 0;
mEstimatedSizeBytes = 0;
bool stoppedPrematurely = true;
int64_t previousPausedDurationUs = 0;
int64_t maxTimestampUs = 0;
+ status_t err = OK;
while (!mDone) {
MediaBuffer *buffer;
- status_t err = mSource->read(&buffer);
+ err = mSource->read(&buffer);
if (err != OK) {
break;
@@ -260,6 +268,10 @@
fclose(mFile);
mFile = NULL;
mReachedEOS = true;
+ if (err == ERROR_END_OF_STREAM) {
+ return OK;
+ }
+ return err;
}
bool AMRWriter::reachedEOS() {
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 404762f..b5a6327 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -40,6 +40,7 @@
TimedEventQueue.cpp \
Utils.cpp \
WAVExtractor.cpp \
+ avc_utils.cpp \
string.cpp
LOCAL_C_INCLUDES:= \
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index f52ec1a..6bb54bd 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -48,8 +48,8 @@
~Track();
status_t start(MetaData *params);
- void stop();
- void pause();
+ status_t stop();
+ status_t pause();
bool reachedEOS();
int64_t getDurationUs() const;
@@ -144,7 +144,7 @@
int64_t mTrackEveryTimeDurationUs;
static void *ThreadWrapper(void *me);
- void threadEntry();
+ status_t threadEntry();
const uint8_t *parseParamSet(
const uint8_t *data, size_t length, int type, size_t *paramSetLen);
@@ -378,15 +378,20 @@
return OK;
}
-void MPEG4Writer::pause() {
+status_t MPEG4Writer::pause() {
if (mFile == NULL) {
- return;
+ return OK;
}
mPaused = true;
+ status_t err = OK;
for (List<Track *>::iterator it = mTracks.begin();
it != mTracks.end(); ++it) {
- (*it)->pause();
+ status_t status = (*it)->pause();
+ if (status != OK) {
+ err = status;
+ }
}
+ return err;
}
void MPEG4Writer::stopWriterThread() {
@@ -403,15 +408,19 @@
pthread_join(mThread, &dummy);
}
-void MPEG4Writer::stop() {
+status_t MPEG4Writer::stop() {
if (mFile == NULL) {
- return;
+ return OK;
}
+ status_t err = OK;
int64_t maxDurationUs = 0;
for (List<Track *>::iterator it = mTracks.begin();
it != mTracks.end(); ++it) {
- (*it)->stop();
+ status_t status = (*it)->stop();
+ if (err == OK && status != OK) {
+ err = status;
+ }
int64_t durationUs = (*it)->getDurationUs();
if (durationUs > maxDurationUs) {
@@ -421,6 +430,15 @@
stopWriterThread();
+ // Do not write out movie header on error.
+ if (err != OK) {
+ fflush(mFile);
+ fclose(mFile);
+ mFile = NULL;
+ mStarted = false;
+ return err;
+ }
+
// Fix up the size of the 'mdat' chunk.
if (mUse32BitOffset) {
fseeko(mFile, mMdatOffset, SEEK_SET);
@@ -508,6 +526,7 @@
fclose(mFile);
mFile = NULL;
mStarted = false;
+ return err;
}
status_t MPEG4Writer::setInterleaveDuration(uint32_t durationUs) {
@@ -1030,13 +1049,14 @@
return OK;
}
-void MPEG4Writer::Track::pause() {
+status_t MPEG4Writer::Track::pause() {
mPaused = true;
+ return OK;
}
-void MPEG4Writer::Track::stop() {
+status_t MPEG4Writer::Track::stop() {
if (mDone) {
- return;
+ return OK;
}
mDone = true;
@@ -1044,7 +1064,16 @@
void *dummy;
pthread_join(mThread, &dummy);
- mSource->stop();
+ status_t err = (status_t) dummy;
+
+ {
+ status_t status = mSource->stop();
+ if (err == OK && status != OK && status != ERROR_END_OF_STREAM) {
+ err = status;
+ }
+ }
+
+ return err;
}
bool MPEG4Writer::Track::reachedEOS() {
@@ -1055,9 +1084,8 @@
void *MPEG4Writer::Track::ThreadWrapper(void *me) {
Track *track = static_cast<Track *>(me);
- track->threadEntry();
-
- return NULL;
+ status_t err = track->threadEntry();
+ return (void *) err;
}
#include <ctype.h>
@@ -1352,7 +1380,7 @@
return false;
}
-void MPEG4Writer::Track::threadEntry() {
+status_t MPEG4Writer::Track::threadEntry() {
int32_t count = 0;
const int64_t interleaveDurationUs = mOwner->interleaveDuration();
int64_t chunkTimestampUs = 0;
@@ -1595,7 +1623,7 @@
}
if (mSampleSizes.empty()) {
- err = UNKNOWN_ERROR;
+ err = ERROR_MALFORMED;
}
mOwner->trackProgressStatus(this, -1, err);
@@ -1626,6 +1654,10 @@
count, nZeroLengthFrames, mNumSamples, mMaxWriteTimeUs, mIsAudio? "audio": "video");
logStatisticalData(mIsAudio);
+ if (err == ERROR_END_OF_STREAM) {
+ return OK;
+ }
+ return err;
}
void MPEG4Writer::Track::trackProgressStatus(int64_t timeUs, status_t err) {
@@ -1973,7 +2005,6 @@
int32_t samplerate;
bool success = mMeta->findInt32(kKeySampleRate, &samplerate);
CHECK(success);
-
mOwner->writeInt32(samplerate << 16);
if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
mOwner->beginBox("esds");
diff --git a/media/libstagefright/avc_utils.cpp b/media/libstagefright/avc_utils.cpp
new file mode 100644
index 0000000..511ae12
--- /dev/null
+++ b/media/libstagefright/avc_utils.cpp
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+#include "include/avc_utils.h"
+
+#include <media/stagefright/foundation/ABitReader.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+static unsigned parseUE(ABitReader *br) {
+ unsigned numZeroes = 0;
+ while (br->getBits(1) == 0) {
+ ++numZeroes;
+ }
+
+ unsigned x = br->getBits(numZeroes);
+
+ return x + (1u << numZeroes) - 1;
+}
+
+// Determine video dimensions from the sequence parameterset.
+void FindAVCDimensions(
+ const sp<ABuffer> &seqParamSet, int32_t *width, int32_t *height) {
+ ABitReader br(seqParamSet->data() + 1, seqParamSet->size() - 1);
+
+ unsigned profile_idc = br.getBits(8);
+ br.skipBits(16);
+ parseUE(&br); // seq_parameter_set_id
+
+ if (profile_idc == 100 || profile_idc == 110
+ || profile_idc == 122 || profile_idc == 244
+ || profile_idc == 44 || profile_idc == 83 || profile_idc == 86) {
+ unsigned chroma_format_idc = parseUE(&br);
+ if (chroma_format_idc == 3) {
+ br.skipBits(1); // residual_colour_transform_flag
+ }
+ parseUE(&br); // bit_depth_luma_minus8
+ parseUE(&br); // bit_depth_chroma_minus8
+ br.skipBits(1); // qpprime_y_zero_transform_bypass_flag
+ CHECK_EQ(br.getBits(1), 0u); // seq_scaling_matrix_present_flag
+ }
+
+ parseUE(&br); // log2_max_frame_num_minus4
+ unsigned pic_order_cnt_type = parseUE(&br);
+
+ if (pic_order_cnt_type == 0) {
+ parseUE(&br); // log2_max_pic_order_cnt_lsb_minus4
+ } else if (pic_order_cnt_type == 1) {
+ // offset_for_non_ref_pic, offset_for_top_to_bottom_field and
+ // offset_for_ref_frame are technically se(v), but since we are
+ // just skipping over them the midpoint does not matter.
+
+ br.getBits(1); // delta_pic_order_always_zero_flag
+ parseUE(&br); // offset_for_non_ref_pic
+ parseUE(&br); // offset_for_top_to_bottom_field
+
+ unsigned num_ref_frames_in_pic_order_cnt_cycle = parseUE(&br);
+ for (unsigned i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; ++i) {
+ parseUE(&br); // offset_for_ref_frame
+ }
+ }
+
+ parseUE(&br); // num_ref_frames
+ br.getBits(1); // gaps_in_frame_num_value_allowed_flag
+
+ unsigned pic_width_in_mbs_minus1 = parseUE(&br);
+ unsigned pic_height_in_map_units_minus1 = parseUE(&br);
+ unsigned frame_mbs_only_flag = br.getBits(1);
+
+ *width = pic_width_in_mbs_minus1 * 16 + 16;
+
+ *height = (2 - frame_mbs_only_flag)
+ * (pic_height_in_map_units_minus1 * 16 + 16);
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/mpeg2ts/ABitReader.cpp b/media/libstagefright/foundation/ABitReader.cpp
similarity index 100%
rename from media/libstagefright/mpeg2ts/ABitReader.cpp
rename to media/libstagefright/foundation/ABitReader.cpp
diff --git a/media/libstagefright/foundation/Android.mk b/media/libstagefright/foundation/Android.mk
index 35eea7e..f6a8a52 100644
--- a/media/libstagefright/foundation/Android.mk
+++ b/media/libstagefright/foundation/Android.mk
@@ -3,6 +3,7 @@
LOCAL_SRC_FILES:= \
AAtomizer.cpp \
+ ABitReader.cpp \
ABuffer.cpp \
ADebug.cpp \
AHandler.cpp \
diff --git a/media/libstagefright/include/avc_utils.h b/media/libstagefright/include/avc_utils.h
new file mode 100644
index 0000000..cc405b5
--- /dev/null
+++ b/media/libstagefright/include/avc_utils.h
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#ifndef AVC_UTILS_H_
+
+#define AVC_UTILS_H_
+
+#include <media/stagefright/foundation/ABuffer.h>
+
+namespace android {
+
+void FindAVCDimensions(
+ const sp<ABuffer> &seqParamSet, int32_t *width, int32_t *height);
+
+} // namespace android
+
+#endif // AVC_UTILS_H_
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index d05975d..26a0fb3 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -16,9 +16,10 @@
#include "ATSParser.h"
-#include "ABitReader.h"
#include "AnotherPacketSource.h"
+#include "include/avc_utils.h"
+#include <media/stagefright/foundation/ABitReader.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
@@ -473,60 +474,6 @@
}
}
-static unsigned parseUE(ABitReader *br) {
- unsigned numZeroes = 0;
- while (br->getBits(1) == 0) {
- ++numZeroes;
- }
-
- unsigned x = br->getBits(numZeroes);
-
- return x + (1u << numZeroes) - 1;
-}
-
-// Determine video dimensions from the sequence parameterset.
-static void FindDimensions(
- const sp<ABuffer> seqParamSet, int32_t *width, int32_t *height) {
- ABitReader br(seqParamSet->data() + 1, seqParamSet->size() - 1);
-
- unsigned profile_idc = br.getBits(8);
- br.skipBits(16);
- parseUE(&br); // seq_parameter_set_id
-
- if (profile_idc == 100 || profile_idc == 110
- || profile_idc == 122 || profile_idc == 144) {
- TRESPASS();
- }
-
- parseUE(&br); // log2_max_frame_num_minus4
- unsigned pic_order_cnt_type = parseUE(&br);
-
- if (pic_order_cnt_type == 0) {
- parseUE(&br); // log2_max_pic_order_cnt_lsb_minus4
- } else if (pic_order_cnt_type == 1) {
- br.getBits(1); // delta_pic_order_always_zero_flag
- parseUE(&br); // offset_for_non_ref_pic
- parseUE(&br); // offset_for_top_to_bottom_field
-
- unsigned num_ref_frames_in_pic_order_cnt_cycle = parseUE(&br);
- for (unsigned i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; ++i) {
- parseUE(&br); // offset_for_ref_frame
- }
- }
-
- parseUE(&br); // num_ref_frames
- br.getBits(1); // gaps_in_frame_num_value_allowed_flag
-
- unsigned pic_width_in_mbs_minus1 = parseUE(&br);
- unsigned pic_height_in_map_units_minus1 = parseUE(&br);
- unsigned frame_mbs_only_flag = br.getBits(1);
-
- *width = pic_width_in_mbs_minus1 * 16 + 16;
-
- *height = (2 - frame_mbs_only_flag)
- * (pic_height_in_map_units_minus1 * 16 + 16);
-}
-
static sp<ABuffer> MakeAVCCodecSpecificData(
const sp<ABuffer> &buffer, int32_t *width, int32_t *height) {
const uint8_t *data = buffer->data();
@@ -537,7 +484,7 @@
return NULL;
}
- FindDimensions(seqParamSet, width, height);
+ FindAVCDimensions(seqParamSet, width, height);
size_t stopOffset;
sp<ABuffer> picParamSet = FindNAL(data, size, 8, &stopOffset);
diff --git a/media/libstagefright/mpeg2ts/Android.mk b/media/libstagefright/mpeg2ts/Android.mk
index b6772eb..3544b4c 100644
--- a/media/libstagefright/mpeg2ts/Android.mk
+++ b/media/libstagefright/mpeg2ts/Android.mk
@@ -3,7 +3,6 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- ABitReader.cpp \
AnotherPacketSource.cpp \
ATSParser.cpp \
MPEG2TSExtractor.cpp \
diff --git a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
new file mode 100644
index 0000000..7e633d7
--- /dev/null
+++ b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
@@ -0,0 +1,166 @@
+/*
+ * 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.
+ */
+
+#include "AMPEG4ElementaryAssembler.h"
+
+#include "ARTPSource.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/hexdump.h>
+
+#include <stdint.h>
+
+#define BE_VERBOSE 0
+
+namespace android {
+
+// static
+AMPEG4ElementaryAssembler::AMPEG4ElementaryAssembler(const sp<AMessage> ¬ify)
+ : mNotifyMsg(notify),
+ mAccessUnitRTPTime(0),
+ mNextExpectedSeqNoValid(false),
+ mNextExpectedSeqNo(0),
+ mAccessUnitDamaged(false) {
+}
+
+AMPEG4ElementaryAssembler::~AMPEG4ElementaryAssembler() {
+}
+
+ARTPAssembler::AssemblyStatus AMPEG4ElementaryAssembler::addPacket(
+ const sp<ARTPSource> &source) {
+ List<sp<ABuffer> > *queue = source->queue();
+
+ if (queue->empty()) {
+ return NOT_ENOUGH_DATA;
+ }
+
+ if (mNextExpectedSeqNoValid) {
+ List<sp<ABuffer> >::iterator it = queue->begin();
+ while (it != queue->end()) {
+ if ((uint32_t)(*it)->int32Data() >= mNextExpectedSeqNo) {
+ break;
+ }
+
+ it = queue->erase(it);
+ }
+
+ if (queue->empty()) {
+ return NOT_ENOUGH_DATA;
+ }
+ }
+
+ sp<ABuffer> buffer = *queue->begin();
+
+ if (!mNextExpectedSeqNoValid) {
+ mNextExpectedSeqNoValid = true;
+ mNextExpectedSeqNo = (uint32_t)buffer->int32Data();
+ } else if ((uint32_t)buffer->int32Data() != mNextExpectedSeqNo) {
+#if BE_VERBOSE
+ LOG(VERBOSE) << "Not the sequence number I expected";
+#endif
+
+ return WRONG_SEQUENCE_NUMBER;
+ }
+
+ uint32_t rtpTime;
+ CHECK(buffer->meta()->findInt32("rtp-time", (int32_t *)&rtpTime));
+
+ if (mPackets.size() > 0 && rtpTime != mAccessUnitRTPTime) {
+ submitAccessUnit();
+ }
+ mAccessUnitRTPTime = rtpTime;
+
+ mPackets.push_back(buffer);
+ // hexdump(buffer->data(), buffer->size());
+
+ queue->erase(queue->begin());
+ ++mNextExpectedSeqNo;
+
+ return OK;
+}
+
+void AMPEG4ElementaryAssembler::submitAccessUnit() {
+ CHECK(!mPackets.empty());
+
+#if BE_VERBOSE
+ LOG(VERBOSE) << "Access unit complete (" << mPackets.size() << " nal units)";
+#endif
+
+ uint64_t ntpTime;
+ CHECK((*mPackets.begin())->meta()->findInt64(
+ "ntp-time", (int64_t *)&ntpTime));
+
+ size_t totalSize = 0;
+ for (List<sp<ABuffer> >::iterator it = mPackets.begin();
+ it != mPackets.end(); ++it) {
+ totalSize += (*it)->size();
+ }
+
+ sp<ABuffer> accessUnit = new ABuffer(totalSize);
+ size_t offset = 0;
+ for (List<sp<ABuffer> >::iterator it = mPackets.begin();
+ it != mPackets.end(); ++it) {
+ sp<ABuffer> nal = *it;
+ memcpy(accessUnit->data() + offset, nal->data(), nal->size());
+ offset += nal->size();
+ }
+
+ accessUnit->meta()->setInt64("ntp-time", ntpTime);
+
+#if 0
+ printf(mAccessUnitDamaged ? "X" : ".");
+ fflush(stdout);
+#endif
+
+ if (mAccessUnitDamaged) {
+ accessUnit->meta()->setInt32("damaged", true);
+ }
+
+ mPackets.clear();
+ mAccessUnitDamaged = false;
+
+ sp<AMessage> msg = mNotifyMsg->dup();
+ msg->setObject("access-unit", accessUnit);
+ msg->post();
+}
+
+ARTPAssembler::AssemblyStatus AMPEG4ElementaryAssembler::assembleMore(
+ const sp<ARTPSource> &source) {
+ AssemblyStatus status = addPacket(source);
+ if (status == MALFORMED_PACKET) {
+ mAccessUnitDamaged = true;
+ }
+ return status;
+}
+
+void AMPEG4ElementaryAssembler::packetLost() {
+ CHECK(mNextExpectedSeqNoValid);
+ LOG(VERBOSE) << "packetLost (expected " << mNextExpectedSeqNo << ")";
+
+ ++mNextExpectedSeqNo;
+
+ mAccessUnitDamaged = true;
+}
+
+void AMPEG4ElementaryAssembler::onByeReceived() {
+ sp<AMessage> msg = mNotifyMsg->dup();
+ msg->setInt32("eos", true);
+ msg->post();
+}
+
+} // namespace android
diff --git a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.h b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.h
new file mode 100644
index 0000000..1566d00
--- /dev/null
+++ b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.h
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+#ifndef A_MPEG4_ELEM_ASSEMBLER_H_
+
+#define A_MPEG4_ELEM_ASSEMBLER_H_
+
+#include "ARTPAssembler.h"
+
+#include <utils/List.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct ABuffer;
+struct AMessage;
+
+struct AMPEG4ElementaryAssembler : public ARTPAssembler {
+ AMPEG4ElementaryAssembler(const sp<AMessage> ¬ify);
+
+protected:
+ virtual ~AMPEG4ElementaryAssembler();
+
+ virtual AssemblyStatus assembleMore(const sp<ARTPSource> &source);
+ virtual void onByeReceived();
+ virtual void packetLost();
+
+private:
+ sp<AMessage> mNotifyMsg;
+
+ uint32_t mAccessUnitRTPTime;
+ bool mNextExpectedSeqNoValid;
+ uint32_t mNextExpectedSeqNo;
+ bool mAccessUnitDamaged;
+ List<sp<ABuffer> > mPackets;
+
+ AssemblyStatus addPacket(const sp<ARTPSource> &source);
+ void submitAccessUnit();
+
+ DISALLOW_EVIL_CONSTRUCTORS(AMPEG4ElementaryAssembler);
+};
+
+} // namespace android
+
+#endif // A_MPEG4_ELEM_ASSEMBLER_H_
diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp
index 224b4bf..8c56cb7 100644
--- a/media/libstagefright/rtsp/APacketSource.cpp
+++ b/media/libstagefright/rtsp/APacketSource.cpp
@@ -18,6 +18,11 @@
#include "ASessionDescription.h"
+#include "avc_utils.h"
+
+#include <ctype.h>
+
+#include <media/stagefright/foundation/ABitReader.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
@@ -37,6 +42,10 @@
size_t keyLen = strlen(key);
for (;;) {
+ while (isspace(*s)) {
+ ++s;
+ }
+
const char *colonPos = strchr(s, ';');
size_t len =
@@ -90,7 +99,11 @@
return buffer;
}
-static sp<ABuffer> MakeAVCCodecSpecificData(const char *params) {
+static sp<ABuffer> MakeAVCCodecSpecificData(
+ const char *params, int32_t *width, int32_t *height) {
+ *width = 0;
+ *height = 0;
+
AString val;
if (!GetAttribute(params, "profile-level-id", &val)) {
return NULL;
@@ -172,6 +185,11 @@
memcpy(out, nal->data(), nal->size());
out += nal->size();
+
+ if (i == 0) {
+ FindAVCDimensions(nal, width, height);
+ LOG(INFO) << "dimensions " << *width << "x" << *height;
+ }
}
*out++ = numPicParameterSets;
@@ -187,7 +205,7 @@
out += nal->size();
}
- hexdump(csd->data(), csd->size());
+ // hexdump(csd->data(), csd->size());
return csd;
}
@@ -224,7 +242,162 @@
csd->data()[sizeof(kStaticESDS)] = (x >> 8) & 0xff;
csd->data()[sizeof(kStaticESDS) + 1] = x & 0xff;
- hexdump(csd->data(), csd->size());
+ // hexdump(csd->data(), csd->size());
+
+ return csd;
+}
+
+static size_t GetSizeWidth(size_t x) {
+ size_t n = 1;
+ while (x > 127) {
+ ++n;
+ x >>= 7;
+ }
+ return n;
+}
+
+static uint8_t *EncodeSize(uint8_t *dst, size_t x) {
+ while (x > 127) {
+ *dst++ = (x & 0x7f) | 0x80;
+ x >>= 7;
+ }
+ *dst++ = x;
+ return dst;
+}
+
+static bool ExtractDimensionsFromVOLHeader(
+ const sp<ABuffer> &config, int32_t *width, int32_t *height) {
+ *width = 0;
+ *height = 0;
+
+ const uint8_t *ptr = config->data();
+ size_t offset = 0;
+ bool foundVOL = false;
+ while (offset + 3 < config->size()) {
+ if (memcmp("\x00\x00\x01", &ptr[offset], 3)
+ || (ptr[offset + 3] & 0xf0) != 0x20) {
+ ++offset;
+ continue;
+ }
+
+ foundVOL = true;
+ break;
+ }
+
+ if (!foundVOL) {
+ return false;
+ }
+
+ ABitReader br(&ptr[offset + 4], config->size() - offset - 4);
+ br.skipBits(1); // random_accessible_vol
+ unsigned video_object_type_indication = br.getBits(8);
+
+ CHECK_NE(video_object_type_indication,
+ 0x21u /* Fine Granularity Scalable */);
+
+ unsigned video_object_layer_verid;
+ unsigned video_object_layer_priority;
+ if (br.getBits(1)) {
+ video_object_layer_verid = br.getBits(4);
+ video_object_layer_priority = br.getBits(3);
+ }
+ unsigned aspect_ratio_info = br.getBits(4);
+ if (aspect_ratio_info == 0x0f /* extended PAR */) {
+ br.skipBits(8); // par_width
+ br.skipBits(8); // par_height
+ }
+ if (br.getBits(1)) { // vol_control_parameters
+ br.skipBits(2); // chroma_format
+ br.skipBits(1); // low_delay
+ if (br.getBits(1)) { // vbv_parameters
+ TRESPASS();
+ }
+ }
+ unsigned video_object_layer_shape = br.getBits(2);
+ CHECK_EQ(video_object_layer_shape, 0x00u /* rectangular */);
+
+ CHECK(br.getBits(1)); // marker_bit
+ unsigned vop_time_increment_resolution = br.getBits(16);
+ CHECK(br.getBits(1)); // marker_bit
+
+ if (br.getBits(1)) { // fixed_vop_rate
+ // range [0..vop_time_increment_resolution)
+
+ // vop_time_increment_resolution
+ // 2 => 0..1, 1 bit
+ // 3 => 0..2, 2 bits
+ // 4 => 0..3, 2 bits
+ // 5 => 0..4, 3 bits
+ // ...
+
+ CHECK_GT(vop_time_increment_resolution, 0u);
+ --vop_time_increment_resolution;
+
+ unsigned numBits = 0;
+ while (vop_time_increment_resolution > 0) {
+ ++numBits;
+ vop_time_increment_resolution >>= 1;
+ }
+
+ br.skipBits(numBits); // fixed_vop_time_increment
+ }
+
+ CHECK(br.getBits(1)); // marker_bit
+ unsigned video_object_layer_width = br.getBits(13);
+ CHECK(br.getBits(1)); // marker_bit
+ unsigned video_object_layer_height = br.getBits(13);
+ CHECK(br.getBits(1)); // marker_bit
+
+ unsigned interlaced = br.getBits(1);
+
+ *width = video_object_layer_width;
+ *height = video_object_layer_height;
+
+ LOG(INFO) << "VOL dimensions = " << *width << "x" << *height;
+
+ return true;
+}
+
+sp<ABuffer> MakeMPEG4VideoCodecSpecificData(
+ const char *params, int32_t *width, int32_t *height) {
+ *width = 0;
+ *height = 0;
+
+ AString val;
+ CHECK(GetAttribute(params, "config", &val));
+
+ sp<ABuffer> config = decodeHex(val);
+ CHECK(config != NULL);
+
+ if (!ExtractDimensionsFromVOLHeader(config, width, height)) {
+ return NULL;
+ }
+
+ size_t len1 = config->size() + GetSizeWidth(config->size()) + 1;
+ size_t len2 = len1 + GetSizeWidth(len1) + 1 + 13;
+ size_t len3 = len2 + GetSizeWidth(len2) + 1 + 3;
+
+ sp<ABuffer> csd = new ABuffer(len3);
+ uint8_t *dst = csd->data();
+ *dst++ = 0x03;
+ dst = EncodeSize(dst, len2 + 3);
+ *dst++ = 0x00; // ES_ID
+ *dst++ = 0x00;
+ *dst++ = 0x00; // streamDependenceFlag, URL_Flag, OCRstreamFlag
+
+ *dst++ = 0x04;
+ dst = EncodeSize(dst, len1 + 13);
+ *dst++ = 0x01; // Video ISO/IEC 14496-2 Simple Profile
+ for (size_t i = 0; i < 12; ++i) {
+ *dst++ = 0x00;
+ }
+
+ *dst++ = 0x05;
+ dst = EncodeSize(dst, config->size());
+ memcpy(dst, config->data(), config->size());
+ dst += config->size();
+
+ // hexdump(csd->data(), csd->size());
return csd;
}
@@ -253,25 +426,42 @@
mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
int32_t width, height;
- sessionDesc->getDimensions(index, PT, &width, &height);
+ if (!sessionDesc->getDimensions(index, PT, &width, &height)) {
+ width = -1;
+ height = -1;
+ }
- mFormat->setInt32(kKeyWidth, width);
- mFormat->setInt32(kKeyHeight, height);
-
+ int32_t encWidth, encHeight;
sp<ABuffer> codecSpecificData =
- MakeAVCCodecSpecificData(params.c_str());
+ MakeAVCCodecSpecificData(params.c_str(), &encWidth, &encHeight);
if (codecSpecificData != NULL) {
+ if (width < 0) {
+ // If no explicit width/height given in the sdp, use the dimensions
+ // extracted from the first sequence parameter set.
+ width = encWidth;
+ height = encHeight;
+ }
+
mFormat->setData(
kKeyAVCC, 0,
codecSpecificData->data(), codecSpecificData->size());
+ } else if (width < 0) {
+ mInitCheck = ERROR_UNSUPPORTED;
+ return;
}
+
+ mFormat->setInt32(kKeyWidth, width);
+ mFormat->setInt32(kKeyHeight, height);
} else if (!strncmp(desc.c_str(), "H263-2000/", 10)
|| !strncmp(desc.c_str(), "H263-1998/", 10)) {
mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263);
int32_t width, height;
- sessionDesc->getDimensions(index, PT, &width, &height);
+ if (!sessionDesc->getDimensions(index, PT, &width, &height)) {
+ mInitCheck = ERROR_UNSUPPORTED;
+ return;
+ }
mFormat->setInt32(kKeyWidth, width);
mFormat->setInt32(kKeyHeight, height);
@@ -317,6 +507,36 @@
if (sampleRate != 16000 || numChannels != 1) {
mInitCheck = ERROR_UNSUPPORTED;
}
+ } else if (!strncmp(desc.c_str(), "MP4V-ES/", 8)) {
+ mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
+
+ int32_t width, height;
+ if (!sessionDesc->getDimensions(index, PT, &width, &height)) {
+ width = -1;
+ height = -1;
+ }
+
+ int32_t encWidth, encHeight;
+ sp<ABuffer> codecSpecificData =
+ MakeMPEG4VideoCodecSpecificData(
+ params.c_str(), &encWidth, &encHeight);
+
+ if (codecSpecificData != NULL) {
+ mFormat->setData(
+ kKeyESDS, 0,
+ codecSpecificData->data(), codecSpecificData->size());
+
+ if (width < 0) {
+ width = encWidth;
+ height = encHeight;
+ }
+ } else if (width < 0) {
+ mInitCheck = ERROR_UNSUPPORTED;
+ return;
+ }
+
+ mFormat->setInt32(kKeyWidth, width);
+ mFormat->setInt32(kKeyHeight, height);
} else {
mInitCheck = ERROR_UNSUPPORTED;
}
diff --git a/media/libstagefright/rtsp/ARTPConnection.cpp b/media/libstagefright/rtsp/ARTPConnection.cpp
index 469af3e..6816c45 100644
--- a/media/libstagefright/rtsp/ARTPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTPConnection.cpp
@@ -321,6 +321,8 @@
buffer->setRange(0, nbytes);
+ // LOG(INFO) << "received " << buffer->size() << " bytes.";
+
status_t err;
if (receiveRTP) {
err = parseRTP(s, buffer);
diff --git a/media/libstagefright/rtsp/ARTPSource.cpp b/media/libstagefright/rtsp/ARTPSource.cpp
index 225f6e8..775c4ee 100644
--- a/media/libstagefright/rtsp/ARTPSource.cpp
+++ b/media/libstagefright/rtsp/ARTPSource.cpp
@@ -20,6 +20,7 @@
#include "AAVCAssembler.h"
#include "AH263Assembler.h"
#include "AMPEG4AudioAssembler.h"
+#include "AMPEG4ElementaryAssembler.h"
#include "ASessionDescription.h"
#include <media/stagefright/foundation/ABuffer.h>
@@ -63,6 +64,9 @@
mAssembler = new AAMRAssembler(notify, false /* isWide */, params);
} else if (!strncmp(desc.c_str(), "AMR-WB/", 7)) {
mAssembler = new AAMRAssembler(notify, true /* isWide */, params);
+ } else if (!strncmp(desc.c_str(), "MP4V-ES/", 8)) {
+ mAssembler = new AMPEG4ElementaryAssembler(notify);
+ mIssueFIRRequests = true;
} else {
TRESPASS();
}
diff --git a/media/libstagefright/rtsp/ARTPWriter.cpp b/media/libstagefright/rtsp/ARTPWriter.cpp
index d6dd597..d4eed7c 100644
--- a/media/libstagefright/rtsp/ARTPWriter.cpp
+++ b/media/libstagefright/rtsp/ARTPWriter.cpp
@@ -134,10 +134,10 @@
return OK;
}
-void ARTPWriter::stop() {
+status_t ARTPWriter::stop() {
Mutex::Autolock autoLock(mLock);
if (!(mFlags & kFlagStarted)) {
- return;
+ return OK;
}
(new AMessage(kWhatStop, mReflector->id()))->post();
@@ -145,9 +145,11 @@
while (mFlags & kFlagStarted) {
mCondition.wait(mLock);
}
+ return OK;
}
-void ARTPWriter::pause() {
+status_t ARTPWriter::pause() {
+ return OK;
}
static void StripStartcode(MediaBuffer *buffer) {
diff --git a/media/libstagefright/rtsp/ARTPWriter.h b/media/libstagefright/rtsp/ARTPWriter.h
index b1b8b45..fdc8d23 100644
--- a/media/libstagefright/rtsp/ARTPWriter.h
+++ b/media/libstagefright/rtsp/ARTPWriter.h
@@ -40,8 +40,8 @@
virtual status_t addSource(const sp<MediaSource> &source);
virtual bool reachedEOS();
virtual status_t start(MetaData *params);
- virtual void stop();
- virtual void pause();
+ virtual status_t stop();
+ virtual status_t pause();
virtual void onMessageReceived(const sp<AMessage> &msg);
diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp
index 9826990..5345218 100644
--- a/media/libstagefright/rtsp/ARTSPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTSPConnection.cpp
@@ -136,6 +136,20 @@
return true;
}
+static void MakeSocketBlocking(int s, bool blocking) {
+ // Make socket non-blocking.
+ int flags = fcntl(s, F_GETFL, 0);
+ CHECK_NE(flags, -1);
+
+ if (blocking) {
+ flags &= ~O_NONBLOCK;
+ } else {
+ flags |= O_NONBLOCK;
+ }
+
+ CHECK_NE(fcntl(s, F_SETFL, flags), -1);
+}
+
void ARTSPConnection::onConnect(const sp<AMessage> &msg) {
++mConnectionID;
@@ -150,10 +164,7 @@
mSocket = socket(AF_INET, SOCK_STREAM, 0);
- // Make socket non-blocking.
- int flags = fcntl(mSocket, F_GETFL, 0);
- CHECK_NE(flags, -1);
- CHECK_NE(fcntl(mSocket, F_SETFL, flags | O_NONBLOCK), -1);
+ MakeSocketBlocking(mSocket, false);
AString url;
CHECK(msg->findString("url", &url));
@@ -210,7 +221,7 @@
mSocket = -1;
flushPendingRequests();
- }
+ }
sp<AMessage> reply;
CHECK(msg->findMessage("reply", &reply));
@@ -347,7 +358,13 @@
CHECK_GE(res, 0);
if (res == 1) {
- if (!receiveRTSPReponse()) {
+ MakeSocketBlocking(mSocket, true);
+
+ bool success = receiveRTSPReponse();
+
+ MakeSocketBlocking(mSocket, false);
+
+ if (!success) {
// Something horrible, irreparable has happened.
flushPendingRequests();
return;
diff --git a/media/libstagefright/rtsp/ASessionDescription.cpp b/media/libstagefright/rtsp/ASessionDescription.cpp
index 8187e0c..4a8cce8 100644
--- a/media/libstagefright/rtsp/ASessionDescription.cpp
+++ b/media/libstagefright/rtsp/ASessionDescription.cpp
@@ -203,13 +203,18 @@
}
}
-void ASessionDescription::getDimensions(
+bool ASessionDescription::getDimensions(
size_t index, unsigned long PT,
int32_t *width, int32_t *height) const {
+ *width = 0;
+ *height = 0;
+
char key[20];
sprintf(key, "a=framesize:%lu", PT);
AString value;
- CHECK(findAttribute(index, key, &value));
+ if (!findAttribute(index, key, &value)) {
+ return false;
+ }
const char *s = value.c_str();
char *end;
@@ -221,6 +226,8 @@
*height = strtoul(s, &end, 10);
CHECK_GT(end, s);
CHECK_EQ(*end, '\0');
+
+ return true;
}
bool ASessionDescription::getDurationUs(int64_t *durationUs) const {
diff --git a/media/libstagefright/rtsp/ASessionDescription.h b/media/libstagefright/rtsp/ASessionDescription.h
index b26980f..a3fa79e 100644
--- a/media/libstagefright/rtsp/ASessionDescription.h
+++ b/media/libstagefright/rtsp/ASessionDescription.h
@@ -44,7 +44,7 @@
size_t index, unsigned long *PT,
AString *desc, AString *params) const;
- void getDimensions(
+ bool getDimensions(
size_t index, unsigned long PT,
int32_t *width, int32_t *height) const;
diff --git a/media/libstagefright/rtsp/Android.mk b/media/libstagefright/rtsp/Android.mk
index 7f3659f..ed16059 100644
--- a/media/libstagefright/rtsp/Android.mk
+++ b/media/libstagefright/rtsp/Android.mk
@@ -7,6 +7,7 @@
AAVCAssembler.cpp \
AH263Assembler.cpp \
AMPEG4AudioAssembler.cpp \
+ AMPEG4ElementaryAssembler.cpp \
APacketSource.cpp \
ARTPAssembler.cpp \
ARTPConnection.cpp \
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index 8be8914..3e75ffe 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -309,6 +309,16 @@
size_t trackIndex;
CHECK(msg->findSize("track-index", &trackIndex));
+ int32_t eos;
+ if (msg->findInt32("eos", &eos)) {
+ LOG(INFO) << "received BYE on track index " << trackIndex;
+#if 0
+ TrackInfo *track = &mTracks.editItemAt(trackIndex);
+ track->mPacketSource->signalEOS(ERROR_END_OF_STREAM);
+#endif
+ return;
+ }
+
sp<RefBase> obj;
CHECK(msg->findObject("access-unit", &obj));
diff --git a/media/libstagefright/yuv/YUVCanvas.cpp b/media/libstagefright/yuv/YUVCanvas.cpp
index 7ef652d..38aa779 100644
--- a/media/libstagefright/yuv/YUVCanvas.cpp
+++ b/media/libstagefright/yuv/YUVCanvas.cpp
@@ -17,6 +17,7 @@
#define LOG_NDEBUG 0
#define LOG_TAG "YUVCanvas"
+#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/YUVCanvas.h>
#include <media/stagefright/YUVImage.h>
#include <ui/Rect.h>
@@ -74,10 +75,37 @@
uint8_t uValue;
uint8_t vValue;
- srcImage.getPixelValue(srcX, srcY, &yValue, &uValue, & vValue);
+ srcImage.getPixelValue(srcX, srcY, &yValue, &uValue, &vValue);
mYUVImage.setPixelValue(destX, destY, yValue, uValue, vValue);
}
}
}
+void YUVCanvas::downsample(
+ int32_t srcOffsetX, int32_t srcOffsetY,
+ int32_t skipX, int32_t skipY,
+ const YUVImage &srcImage) {
+ // TODO: Add a low pass filter for downsampling.
+
+ // Check that srcImage is big enough to fill mYUVImage.
+ CHECK((srcOffsetX + (mYUVImage.width() - 1) * skipX) < srcImage.width());
+ CHECK((srcOffsetY + (mYUVImage.height() - 1) * skipY) < srcImage.height());
+
+ uint8_t yValue;
+ uint8_t uValue;
+ uint8_t vValue;
+
+ int32_t srcY = srcOffsetY;
+ for (int32_t y = 0; y < mYUVImage.height(); ++y) {
+ int32_t srcX = srcOffsetX;
+ for (int32_t x = 0; x < mYUVImage.width(); ++x) {
+ srcImage.getPixelValue(srcX, srcY, &yValue, &uValue, &vValue);
+ mYUVImage.setPixelValue(x, y, yValue, uValue, vValue);
+
+ srcX += skipX;
+ }
+ srcY += skipY;
+ }
+}
+
} // namespace android
diff --git a/media/libstagefright/yuv/YUVImage.cpp b/media/libstagefright/yuv/YUVImage.cpp
index 73e3297..b712062 100644
--- a/media/libstagefright/yuv/YUVImage.cpp
+++ b/media/libstagefright/yuv/YUVImage.cpp
@@ -155,8 +155,15 @@
return true;
}
+bool YUVImage::validPixel(int32_t x, int32_t y) const {
+ return (x >= 0 && x < mWidth &&
+ y >= 0 && y < mHeight);
+}
+
bool YUVImage::getPixelValue(int32_t x, int32_t y,
uint8_t *yPtr, uint8_t *uPtr, uint8_t *vPtr) const {
+ CHECK(validPixel(x, y));
+
uint8_t *yAddr;
uint8_t *uAddr;
uint8_t *vAddr;
@@ -171,6 +178,8 @@
bool YUVImage::setPixelValue(int32_t x, int32_t y,
uint8_t yValue, uint8_t uValue, uint8_t vValue) {
+ CHECK(validPixel(x, y));
+
uint8_t *yAddr;
uint8_t *uAddr;
uint8_t *vAddr;
diff --git a/opengl/libagl/array.cpp b/opengl/libagl/array.cpp
index 4997dc8..7fbe9b5 100644
--- a/opengl/libagl/array.cpp
+++ b/opengl/libagl/array.cpp
@@ -685,8 +685,8 @@
} while (num);
}
if (count) {
- v0 = c->vc.vBuffer + 2 + num - 2;
- v1 = c->vc.vBuffer + 2 + num - 1;
+ v0 = c->vc.vBuffer + 2 + vcs - 2;
+ v1 = c->vc.vBuffer + 2 + vcs - 1;
if ((winding&2) == 0) {
// for strips copy back the two last compiled vertices
c->vc.vBuffer[0] = *v0;
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
index 8b476c0..53cc398 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -576,41 +576,44 @@
buffer = 0;
// dequeue a new buffer
- nativeWindow->dequeueBuffer(nativeWindow, &buffer);
-
- // TODO: lockBuffer should rather be executed when the very first
- // direct rendering occurs.
- nativeWindow->lockBuffer(nativeWindow, buffer);
-
- // reallocate the depth-buffer if needed
- if ((width != buffer->width) || (height != buffer->height)) {
- // TODO: we probably should reset the swap rect here
- // if the window size has changed
- width = buffer->width;
- height = buffer->height;
- if (depth.data) {
- free(depth.data);
- depth.width = width;
- depth.height = height;
- depth.stride = buffer->stride;
- depth.data = (GGLubyte*)malloc(depth.stride*depth.height*2);
- if (depth.data == 0) {
- setError(EGL_BAD_ALLOC, EGL_FALSE);
- return EGL_FALSE;
+ if (nativeWindow->dequeueBuffer(nativeWindow, &buffer) == NO_ERROR) {
+
+ // TODO: lockBuffer should rather be executed when the very first
+ // direct rendering occurs.
+ nativeWindow->lockBuffer(nativeWindow, buffer);
+
+ // reallocate the depth-buffer if needed
+ if ((width != buffer->width) || (height != buffer->height)) {
+ // TODO: we probably should reset the swap rect here
+ // if the window size has changed
+ width = buffer->width;
+ height = buffer->height;
+ if (depth.data) {
+ free(depth.data);
+ depth.width = width;
+ depth.height = height;
+ depth.stride = buffer->stride;
+ depth.data = (GGLubyte*)malloc(depth.stride*depth.height*2);
+ if (depth.data == 0) {
+ setError(EGL_BAD_ALLOC, EGL_FALSE);
+ return EGL_FALSE;
+ }
}
}
- }
-
- // keep a reference on the buffer
- buffer->common.incRef(&buffer->common);
- // finally pin the buffer down
- if (lock(buffer, GRALLOC_USAGE_SW_READ_OFTEN |
- GRALLOC_USAGE_SW_WRITE_OFTEN, &bits) != NO_ERROR) {
- LOGE("eglSwapBuffers() failed to lock buffer %p (%ux%u)",
- buffer, buffer->width, buffer->height);
- return setError(EGL_BAD_ACCESS, EGL_FALSE);
- // FIXME: we should make sure we're not accessing the buffer anymore
+ // keep a reference on the buffer
+ buffer->common.incRef(&buffer->common);
+
+ // finally pin the buffer down
+ if (lock(buffer, GRALLOC_USAGE_SW_READ_OFTEN |
+ GRALLOC_USAGE_SW_WRITE_OFTEN, &bits) != NO_ERROR) {
+ LOGE("eglSwapBuffers() failed to lock buffer %p (%ux%u)",
+ buffer, buffer->width, buffer->height);
+ return setError(EGL_BAD_ACCESS, EGL_FALSE);
+ // FIXME: we should make sure we're not accessing the buffer anymore
+ }
+ } else {
+ return setError(EGL_BAD_CURRENT_SURFACE, EGL_FALSE);
}
return EGL_TRUE;
diff --git a/packages/SettingsProvider/res/values-rm/strings.xml b/packages/SettingsProvider/res/values-rm/strings.xml
new file mode 100644
index 0000000..861e625
--- /dev/null
+++ b/packages/SettingsProvider/res/values-rm/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2007, 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="4567566098528588863">"Memoria dals parameters"</string>
+</resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 6a5290e..2e95932 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -222,16 +222,11 @@
final String value = c.moveToNext() ? c.getString(0) : null;
if (value == null) {
final SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
- String serial = SystemProperties.get("ro.serialno");
- if (serial != null) {
- try {
- random.setSeed(serial.getBytes("UTF-8"));
- } catch (UnsupportedEncodingException ignore) {
- // stick with default seed
- }
- }
+ String serial = SystemProperties.get("ro.serialno", "");
+ random.setSeed(
+ (serial + System.nanoTime() + new SecureRandom().nextLong()).getBytes());
final String newAndroidIdValue = Long.toHexString(random.nextLong());
- Log.d(TAG, "Generated and saved new ANDROID_ID");
+ Log.d(TAG, "Generated and saved new ANDROID_ID [" + newAndroidIdValue + "]");
final ContentValues values = new ContentValues();
values.put(Settings.NameValueTable.NAME, Settings.Secure.ANDROID_ID);
values.put(Settings.NameValueTable.VALUE, newAndroidIdValue);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
index 022470e..18b9b41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
@@ -54,7 +54,7 @@
import com.android.systemui.R;
public class TabletStatusBarService extends StatusBarService {
- public static final boolean DEBUG = true;
+ public static final boolean DEBUG = false;
public static final String TAG = "TabletStatusBar";
@@ -234,7 +234,26 @@
public void addNotification(IBinder key, StatusBarNotification notification) {
if (DEBUG) Slog.d(TAG, "addNotification(" + key + " -> " + notification + ")");
addNotificationViews(key, notification);
- // tick()
+
+ boolean immersive = false;
+ try {
+ immersive = ActivityManagerNative.getDefault().isTopActivityImmersive();
+ Slog.d(TAG, "Top activity is " + (immersive?"immersive":"not immersive"));
+ } catch (RemoteException ex) {
+ }
+ if (immersive) {
+ // TODO: immersive mode popups for tablet
+ } else if (notification.notification.fullScreenIntent != null) {
+ // not immersive & a full-screen alert should be shown
+ Slog.d(TAG, "Notification has fullScreenIntent and activity is not immersive;"
+ + " sending fullScreenIntent");
+ try {
+ notification.notification.fullScreenIntent.send();
+ } catch (PendingIntent.CanceledException e) {
+ }
+ } else {
+ // tick()
+ }
}
public void updateNotification(IBinder key, StatusBarNotification notification) {
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 2bb4456..2dabf72 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -364,29 +364,32 @@
@Override
public void onConfigurationChanged(Configuration newConfig) {
- PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
- if ((st != null) && (st.menu != null)) {
- final MenuBuilder menuBuilder = (MenuBuilder) st.menu;
+ // Action bars handle their own menu state
+ if (mActionBar == null) {
+ PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
+ if ((st != null) && (st.menu != null)) {
+ final MenuBuilder menuBuilder = (MenuBuilder) st.menu;
- if (st.isOpen) {
- // Freeze state
- final Bundle state = new Bundle();
- menuBuilder.saveHierarchyState(state);
+ if (st.isOpen) {
+ // Freeze state
+ final Bundle state = new Bundle();
+ menuBuilder.saveHierarchyState(state);
- // Remove the menu views since they need to be recreated
- // according to the new configuration
- clearMenuViews(st);
+ // Remove the menu views since they need to be recreated
+ // according to the new configuration
+ clearMenuViews(st);
- // Re-open the same menu
- reopenMenu(false);
+ // Re-open the same menu
+ reopenMenu(false);
- // Restore state
- menuBuilder.restoreHierarchyState(state);
+ // Restore state
+ menuBuilder.restoreHierarchyState(state);
- } else {
- // Clear menu views so on next menu opening, it will use
- // the proper layout
- clearMenuViews(st);
+ } else {
+ // Clear menu views so on next menu opening, it will use
+ // the proper layout
+ clearMenuViews(st);
+ }
}
}
}
@@ -1515,6 +1518,7 @@
static private final String FOCUSED_ID_TAG = "android:focusedViewId";
static private final String VIEWS_TAG = "android:views";
static private final String PANELS_TAG = "android:Panels";
+ static private final String ACTION_BAR_TAG = "android:ActionBar";
/** {@inheritDoc} */
@Override
@@ -1548,6 +1552,10 @@
outState.putSparseParcelableArray(PANELS_TAG, panelStates);
}
+ if (mActionBar != null) {
+ outState.putBoolean(ACTION_BAR_TAG, mActionBar.isOverflowMenuShowing());
+ }
+
return outState;
}
@@ -1582,6 +1590,10 @@
if (panelStates != null) {
restorePanelState(panelStates);
}
+
+ if (mActionBar != null && savedInstanceState.getBoolean(ACTION_BAR_TAG)) {
+ mActionBar.postShowOverflowMenu();
+ }
}
/**
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index a07c385..ecba1fe 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -2096,9 +2096,7 @@
return mDeskDockRotation;
} else {
if (useSensorForOrientationLp(orientation)) {
- // If the user has enabled auto rotation by default, do it.
- int curRotation = mOrientationListener.getCurrentRotation();
- return curRotation >= 0 ? curRotation : lastRotation;
+ return mOrientationListener.getCurrentRotation(lastRotation);
}
return Surface.ROTATION_0;
}
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 6e7633e..ff31470 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1601,7 +1601,7 @@
}
if (mSuspended) {
- sleepTime = idleSleepTime;
+ sleepTime = suspendSleepTimeUs();
}
// sleepTime == 0 means we must write to audio hardware
if (sleepTime == 0) {
@@ -2021,6 +2021,11 @@
return (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000) / 2;
}
+uint32_t AudioFlinger::MixerThread::suspendSleepTimeUs()
+{
+ return (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000);
+}
+
// ----------------------------------------------------------------------------
AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device)
: PlaybackThread(audioFlinger, output, id, device)
@@ -2365,7 +2370,7 @@
}
if (mSuspended) {
- sleepTime = idleSleepTime;
+ sleepTime = suspendSleepTimeUs();
}
// sleepTime == 0 means we must write to audio hardware
if (sleepTime == 0) {
@@ -2486,6 +2491,18 @@
return time;
}
+uint32_t AudioFlinger::DirectOutputThread::suspendSleepTimeUs()
+{
+ uint32_t time;
+ if (AudioSystem::isLinearPCM(mFormat)) {
+ time = (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000);
+ } else {
+ time = 10000;
+ }
+ return time;
+}
+
+
// ----------------------------------------------------------------------------
AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger, AudioFlinger::MixerThread* mainThread, int id)
@@ -2612,7 +2629,7 @@
}
if (mSuspended) {
- sleepTime = idleSleepTime;
+ sleepTime = suspendSleepTimeUs();
}
// sleepTime == 0 means we must write to audio hardware
if (sleepTime == 0) {
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 5520551..51881f0 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -664,6 +664,7 @@
virtual void deleteTrackName_l(int name) = 0;
virtual uint32_t activeSleepTimeUs() = 0;
virtual uint32_t idleSleepTimeUs() = 0;
+ virtual uint32_t suspendSleepTimeUs() = 0;
private:
@@ -724,6 +725,7 @@
virtual void deleteTrackName_l(int name);
virtual uint32_t activeSleepTimeUs();
virtual uint32_t idleSleepTimeUs();
+ virtual uint32_t suspendSleepTimeUs();
AudioMixer* mAudioMixer;
};
@@ -744,6 +746,7 @@
virtual void deleteTrackName_l(int name);
virtual uint32_t activeSleepTimeUs();
virtual uint32_t idleSleepTimeUs();
+ virtual uint32_t suspendSleepTimeUs();
private:
void applyVolume(uint16_t leftVol, uint16_t rightVol, bool ramp);
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index c2c799b..f330d40 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -26,6 +26,7 @@
import android.os.Environment;
import android.os.LocalPowerManager;
import android.os.PowerManager;
+import android.os.SystemProperties;
import android.util.Slog;
import android.util.Xml;
import android.view.InputChannel;
@@ -47,9 +48,6 @@
/*
* Wraps the C++ InputManager and provides its callbacks.
- *
- * XXX Tempted to promote this to a first-class service, ie. InputManagerService, to
- * improve separation of concerns with respect to the window manager.
*/
public class InputManager {
static final String TAG = "InputManager";
@@ -507,5 +505,18 @@
return names.toArray(new String[names.size()]);
}
+
+ @SuppressWarnings("unused")
+ public int getMaxEventsPerSecond() {
+ int result = 0;
+ try {
+ result = Integer.parseInt(SystemProperties.get("windowsmgr.max_events_per_sec"));
+ } catch (NumberFormatException e) {
+ }
+ if (result < 1) {
+ result = 60;
+ }
+ return result;
+ }
}
}
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 59846b9..9f8557f 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -362,11 +362,10 @@
/**
* see {@link android.net.wifi.WifiManager#startScan()}
- * @return {@code true} if the operation succeeds
*/
- public boolean startScan(boolean forceActive) {
+ public void startScan(boolean forceActive) {
enforceChangePermission();
- return mWifiStateMachine.startScan(forceActive);
+ mWifiStateMachine.startScan(forceActive);
}
private void enforceAccessPermission() {
@@ -528,29 +527,26 @@
/**
* see {@link android.net.wifi.WifiManager#disconnect()}
- * @return {@code true} if the operation succeeds
*/
- public boolean disconnect() {
+ public void disconnect() {
enforceChangePermission();
- return mWifiStateMachine.disconnectCommand();
+ mWifiStateMachine.disconnectCommand();
}
/**
* see {@link android.net.wifi.WifiManager#reconnect()}
- * @return {@code true} if the operation succeeds
*/
- public boolean reconnect() {
+ public void reconnect() {
enforceChangePermission();
- return mWifiStateMachine.reconnectCommand();
+ mWifiStateMachine.reconnectCommand();
}
/**
* see {@link android.net.wifi.WifiManager#reassociate()}
- * @return {@code true} if the operation succeeds
*/
- public boolean reassociate() {
+ public void reassociate() {
enforceChangePermission();
- return mWifiStateMachine.reassociateCommand();
+ mWifiStateMachine.reassociateCommand();
}
/**
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 55ec6aa..63746e7b 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -1163,7 +1163,7 @@
} catch (RemoteException e) {
}
} catch (NameNotFoundException e) {
- Log.w(TAG, "Unable to create context for heavy notification", e);
+ Slog.w(TAG, "Unable to create context for heavy notification", e);
}
} break;
case CANCEL_HEAVY_NOTIFICATION_MSG: {
@@ -2368,7 +2368,7 @@
}
if (proc == null) {
- Log.w(TAG, "crashApplication: nothing for uid=" + uid
+ Slog.w(TAG, "crashApplication: nothing for uid=" + uid
+ " initialPid=" + initialPid
+ " packageName=" + packageName);
return;
@@ -4058,6 +4058,9 @@
return false;
}
}
+ if (!pi.exported && pi.applicationInfo.uid != uid) {
+ return false;
+ }
return true;
} catch (RemoteException e) {
return false;
@@ -4206,8 +4209,8 @@
if (perm == null) {
perm = new UriPermission(targetUid, uri);
targetUris.put(uri, perm);
-
}
+
perm.modeFlags |= modeFlags;
if (activity == null) {
perm.globalModeFlags |= modeFlags;
@@ -4228,6 +4231,11 @@
void grantUriPermissionFromIntentLocked(int callingUid,
String targetPkg, Intent intent, ActivityRecord activity) {
+ if (DEBUG_URI_PERMISSION) Slog.v(TAG,
+ "Grant URI perm to " + (intent != null ? intent.getData() : null)
+ + " from " + intent + "; flags=0x"
+ + Integer.toHexString(intent != null ? intent.getFlags() : 0));
+
if (intent == null) {
return;
}
@@ -4906,13 +4914,12 @@
}
private final String checkContentProviderPermissionLocked(
- ProviderInfo cpi, ProcessRecord r, int mode) {
+ ProviderInfo cpi, ProcessRecord r) {
final int callingPid = (r != null) ? r.pid : Binder.getCallingPid();
final int callingUid = (r != null) ? r.info.uid : Binder.getCallingUid();
if (checkComponentPermission(cpi.readPermission, callingPid, callingUid,
cpi.exported ? -1 : cpi.applicationInfo.uid)
- == PackageManager.PERMISSION_GRANTED
- && mode == ParcelFileDescriptor.MODE_READ_ONLY || mode == -1) {
+ == PackageManager.PERMISSION_GRANTED) {
return null;
}
if (checkComponentPermission(cpi.writePermission, callingPid, callingUid,
@@ -4929,8 +4936,7 @@
PathPermission pp = pps[i];
if (checkComponentPermission(pp.getReadPermission(), callingPid, callingUid,
cpi.exported ? -1 : cpi.applicationInfo.uid)
- == PackageManager.PERMISSION_GRANTED
- && mode == ParcelFileDescriptor.MODE_READ_ONLY || mode == -1) {
+ == PackageManager.PERMISSION_GRANTED) {
return null;
}
if (checkComponentPermission(pp.getWritePermission(), callingPid, callingUid,
@@ -4941,6 +4947,15 @@
}
}
+ HashMap<Uri, UriPermission> perms = mGrantedUriPermissions.get(callingUid);
+ if (perms != null) {
+ for (Map.Entry<Uri, UriPermission> uri : perms.entrySet()) {
+ if (uri.getKey().getAuthority().equals(cpi.authority)) {
+ return null;
+ }
+ }
+ }
+
String msg = "Permission Denial: opening provider " + cpi.name
+ " from " + (r != null ? r : "(null)") + " (pid=" + callingPid
+ ", uid=" + callingUid + ") requires "
@@ -4970,10 +4985,9 @@
cpr = mProvidersByName.get(name);
if (cpr != null) {
cpi = cpr.info;
- if (checkContentProviderPermissionLocked(cpi, r, -1) != null) {
- return new ContentProviderHolder(cpi,
- cpi.readPermission != null
- ? cpi.readPermission : cpi.writePermission);
+ String msg;
+ if ((msg=checkContentProviderPermissionLocked(cpi, r)) != null) {
+ throw new SecurityException(msg);
}
if (r != null && cpr.canRunHere(r)) {
@@ -5033,10 +5047,9 @@
return null;
}
- if (checkContentProviderPermissionLocked(cpi, r, -1) != null) {
- return new ContentProviderHolder(cpi,
- cpi.readPermission != null
- ? cpi.readPermission : cpi.writePermission);
+ String msg;
+ if ((msg=checkContentProviderPermissionLocked(cpi, r)) != null) {
+ throw new SecurityException(msg);
}
if (!mSystemReady && !mDidUpdate && !mWaitingUpdate
@@ -6208,7 +6221,7 @@
Binder.restoreCallingIdentity(origId);
}
int res = result.get();
- Log.w(TAG, "handleApplicationStrictModeViolation; res=" + res);
+ Slog.w(TAG, "handleApplicationStrictModeViolation; res=" + res);
}
}
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index ba58b43..3addc0d 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -139,6 +139,7 @@
jmethodID filterJumpyTouchEvents;
jmethodID getVirtualKeyDefinitions;
jmethodID getExcludedDeviceNames;
+ jmethodID getMaxEventsPerSecond;
} gCallbacksClassInfo;
static struct {
@@ -249,6 +250,7 @@
int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
+ virtual int32_t getMaxEventsPerSecond();
private:
struct InputWindow {
@@ -310,6 +312,9 @@
int32_t mFilterTouchEvents;
int32_t mFilterJumpyTouchEvents;
+ // Cached throttling policy.
+ int32_t mMaxEventsPerSecond;
+
// Cached display state. (lock mDisplayLock)
Mutex mDisplayLock;
int32_t mDisplayWidth, mDisplayHeight;
@@ -400,6 +405,7 @@
NativeInputManager::NativeInputManager(jobject callbacksObj) :
mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1),
+ mMaxEventsPerSecond(-1),
mDisplayWidth(-1), mDisplayHeight(-1), mDisplayOrientation(ROTATION_0),
mDispatchEnabled(true), mDispatchFrozen(false), mWindowsReady(true),
mFocusedWindow(NULL), mTouchDown(false), mTouchedWindow(NULL),
@@ -921,6 +927,21 @@
}
}
+int32_t NativeInputManager::getMaxEventsPerSecond() {
+ if (mMaxEventsPerSecond < 0) {
+ JNIEnv* env = jniEnv();
+
+ jint result = env->CallIntMethod(mCallbacksObj,
+ gCallbacksClassInfo.getMaxEventsPerSecond);
+ if (checkAndClearExceptionFromCallback(env, "getMaxEventsPerSecond")) {
+ result = 60;
+ }
+
+ mMaxEventsPerSecond = result;
+ }
+ return mMaxEventsPerSecond;
+}
+
void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowObjArray) {
#if DEBUG_FOCUS
LOGD("setInputWindows");
@@ -2293,6 +2314,9 @@
GET_METHOD_ID(gCallbacksClassInfo.getExcludedDeviceNames, gCallbacksClassInfo.clazz,
"getExcludedDeviceNames", "()[Ljava/lang/String;");
+ GET_METHOD_ID(gCallbacksClassInfo.getMaxEventsPerSecond, gCallbacksClassInfo.clazz,
+ "getMaxEventsPerSecond", "()I");
+
// VirtualKeyDefinition
FIND_CLASS(gVirtualKeyDefinitionClassInfo.clazz,
diff --git a/services/surfaceflinger/TextureManager.cpp b/services/surfaceflinger/TextureManager.cpp
index 0f448e0..76f6159 100644
--- a/services/surfaceflinger/TextureManager.cpp
+++ b/services/surfaceflinger/TextureManager.cpp
@@ -121,7 +121,6 @@
case HAL_PIXEL_FORMAT_YCbCr_422_SP:
case HAL_PIXEL_FORMAT_YCrCb_420_SP:
case HAL_PIXEL_FORMAT_YCbCr_422_I:
- case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED:
return true;
}
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 775dc249..564f13e 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -59,6 +59,16 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+
+ <activity
+ android:name="BitmapsAlphaActivity"
+ android:label="_BitmapsAlpha"
+ android:theme="@android:style/Theme.Translucent.NoTitleBar">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
<activity
android:name="BitmapsRectActivity"
diff --git a/tests/HwAccelerationTest/res/drawable-hdpi/sunset3.png b/tests/HwAccelerationTest/res/drawable-hdpi/sunset3.png
new file mode 100644
index 0000000..6508b27
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable-hdpi/sunset3.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/sunset3.png b/tests/HwAccelerationTest/res/drawable/sunset3.png
new file mode 100644
index 0000000..6508b27
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable/sunset3.png
Binary files differ
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/BitmapsAlphaActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/BitmapsAlphaActivity.java
new file mode 100644
index 0000000..f6fd8fe
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/BitmapsAlphaActivity.java
@@ -0,0 +1,82 @@
+/*
+ * 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.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.FrameLayout;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class BitmapsAlphaActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final BitmapsView view = new BitmapsView(this);
+ final FrameLayout layout = new FrameLayout(this);
+ layout.addView(view, new FrameLayout.LayoutParams(480, 800, Gravity.CENTER));
+ setContentView(layout);
+ }
+
+ static class BitmapsView extends View {
+ private Paint mBitmapPaint;
+ private final Bitmap mBitmap1;
+ private final Bitmap mBitmap2;
+ private Bitmap mBitmap3;
+
+ BitmapsView(Context c) {
+ super(c);
+
+ Log.d("OpenGLRenderer", "Loading sunset1, default options");
+ mBitmap1 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+ Log.d("OpenGLRenderer", "Loading sunset2, default options");
+ mBitmap2 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset2);
+ Log.d("OpenGLRenderer", "Loading sunset3, forcing ARGB-8888");
+ BitmapFactory.Options opts = new BitmapFactory.Options();
+ opts.inPreferredConfig = Bitmap.Config.ARGB_8888;
+ mBitmap3 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset3, opts);
+ Log.d("OpenGLRenderer", " has bitmap alpha? " + mBitmap3.hasAlpha());
+
+ mBitmapPaint = new Paint();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ Log.d("OpenGLRenderer", "================= Draw");
+
+ canvas.translate(120.0f, 50.0f);
+ canvas.drawBitmap(mBitmap1, 0.0f, 0.0f, mBitmapPaint);
+
+ canvas.translate(0.0f, mBitmap1.getHeight());
+ canvas.translate(0.0f, 25.0f);
+ canvas.drawBitmap(mBitmap2, 0.0f, 0.0f, null);
+
+ canvas.translate(0.0f, mBitmap2.getHeight());
+ canvas.translate(0.0f, 25.0f);
+ canvas.drawBitmap(mBitmap3, 0.0f, 0.0f, null);
+ }
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java
index f645446..abe9d5e 100644
--- a/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java
@@ -36,6 +36,7 @@
private final Paint mMediumPaint;
private final Paint mLargePaint;
private final Paint mStrikePaint;
+ private final Paint mScaledPaint;
CustomTextView(Context c) {
super(c);
@@ -43,14 +44,19 @@
mMediumPaint = new Paint();
mMediumPaint.setAntiAlias(true);
mMediumPaint.setColor(0xffff0000);
+
mLargePaint = new Paint();
mLargePaint.setAntiAlias(true);
mLargePaint.setTextSize(36.0f);
+
mStrikePaint = new Paint();
mStrikePaint.setAntiAlias(true);
mStrikePaint.setTextSize(16.0f);
mStrikePaint.setUnderlineText(true);
-
+
+ mScaledPaint = new Paint();
+ mScaledPaint.setAntiAlias(true);
+ mScaledPaint.setTextSize(16.0f);
}
@Override
@@ -86,6 +92,13 @@
mStrikePaint.setStrikeThruText(false);
mStrikePaint.setUnderlineText(true);
+ mScaledPaint.setTextScaleX(0.5f);
+ canvas.drawText("Hello OpenGL renderer!", 500, 200, mScaledPaint);
+ mScaledPaint.setTextScaleX(2.0f);
+ canvas.drawText("Hello OpenGL renderer!", 500, 230, mScaledPaint);
+ mScaledPaint.setTextScaleX(1.0f);
+ canvas.drawText("Hello OpenGL renderer!", 500, 260, mScaledPaint);
+
canvas.save();
canvas.clipRect(150.0f, 220.0f, 450.0f, 320.0f);
canvas.drawText("Hello OpenGL renderer!", 100, 300, mLargePaint);
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 31c1722..498b193 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -2074,12 +2074,13 @@
// tag:attribute pairs that should be checked in layout files.
KeyedVector<String8, NamespaceAttributePair> kLayoutTagAttrPairs;
addTagAttrPair(&kLayoutTagAttrPairs, "view", NULL, "class");
+ addTagAttrPair(&kLayoutTagAttrPairs, "fragment", 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");
+ addTagAttrPair(&kXmlTagAttrPairs, "header", RESOURCES_ANDROID_NAMESPACE, "fragment");
const Vector<sp<AaptDir> >& dirs = assets->resDirs();
const size_t K = dirs.size();
diff --git a/tools/obbtool/Main.cpp b/tools/obbtool/Main.cpp
index 2a9bf04..49e077f 100644
--- a/tools/obbtool/Main.cpp
+++ b/tools/obbtool/Main.cpp
@@ -29,7 +29,7 @@
static int wantUsage = 0;
static int wantVersion = 0;
-#define ADD_OPTS "n:v:f:c:"
+#define ADD_OPTS "n:v:o"
static const struct option longopts[] = {
{"help", no_argument, &wantUsage, 1},
{"version", no_argument, &wantVersion, 1},
@@ -37,8 +37,7 @@
/* Args for "add" */
{"name", required_argument, NULL, 'n'},
{"version", required_argument, NULL, 'v'},
- {"filesystem", required_argument, NULL, 'f'},
- {"crypto", required_argument, NULL, 'c'},
+ {"overlay", optional_argument, NULL, 'o'},
{NULL, 0, NULL, '\0'}
};
@@ -46,8 +45,7 @@
struct package_info_t {
char* packageName;
int packageVersion;
- char* filesystem;
- char* crypto;
+ bool overlay;
};
/*
@@ -77,6 +75,7 @@
obb->setPackageName(String8(info->packageName));
obb->setVersion(info->packageVersion);
+ obb->setOverlay(info->overlay);
if (!obb->writeTo(filename)) {
fprintf(stderr, "ERROR: %s: couldn't write OBB signature: %s\n",
@@ -112,6 +111,8 @@
printf("OBB info for '%s':\n", filename);
printf("Package name: %s\n", obb->getPackageName().string());
printf(" Version: %d\n", obb->getVersion());
+ printf(" Flags: 0x%08x\n", obb->getFlags());
+ printf(" Overlay: %s\n", obb->isOverlay() ? "true" : "false");
}
/*
@@ -143,7 +144,7 @@
case 'n':
package_info.packageName = optarg;
break;
- case 'v':
+ case 'v': {
char *end;
package_info.packageVersion = strtol(optarg, &end, 10);
if (*optarg == '\0' || *end != '\0') {
@@ -152,11 +153,9 @@
goto bail;
}
break;
- case 'f':
- package_info.filesystem = optarg;
- break;
- case 'c':
- package_info.crypto = optarg;
+ }
+ case 'o':
+ package_info.overlay = true;
break;
case '?':
wantUsage = 1;
diff --git a/voip/java/android/net/rtp/AudioGroup.java b/voip/java/android/net/rtp/AudioGroup.java
index dc86082..37cc121 100644
--- a/voip/java/android/net/rtp/AudioGroup.java
+++ b/voip/java/android/net/rtp/AudioGroup.java
@@ -49,27 +49,28 @@
synchronized void add(AudioStream stream, AudioCodec codec, int codecType, int dtmfType) {
if (!mStreams.containsKey(stream)) {
try {
- int id = add(stream.getMode(), stream.dup(),
+ int socket = stream.dup();
+ add(stream.getMode(), socket,
stream.getRemoteAddress().getHostAddress(), stream.getRemotePort(),
codec.name, codec.sampleRate, codec.sampleCount, codecType, dtmfType);
- mStreams.put(stream, id);
+ mStreams.put(stream, socket);
} catch (NullPointerException e) {
throw new IllegalStateException(e);
}
}
}
- private native int add(int mode, int socket, String remoteAddress, int remotePort,
+ private native void add(int mode, int socket, String remoteAddress, int remotePort,
String codecName, int sampleRate, int sampleCount, int codecType, int dtmfType);
synchronized void remove(AudioStream stream) {
- Integer id = mStreams.remove(stream);
- if (id != null) {
- remove(id);
+ Integer socket = mStreams.remove(stream);
+ if (socket != null) {
+ remove(socket);
}
}
- private native void remove(int id);
+ private native void remove(int socket);
/**
* Sends a DTMF digit to every {@link AudioStream} in this group. Currently
diff --git a/voip/jni/rtp/AudioGroup.cpp b/voip/jni/rtp/AudioGroup.cpp
index fc1ed9b..08a8d1c 100644
--- a/voip/jni/rtp/AudioGroup.cpp
+++ b/voip/jni/rtp/AudioGroup.cpp
@@ -367,7 +367,7 @@
MSG_TRUNC | MSG_DONTWAIT);
// Do we need to check SSRC, sequence, and timestamp? They are not
- // reliable but at least they can be used to identity duplicates?
+ // reliable but at least they can be used to identify duplicates?
if (length < 12 || length > (int)sizeof(buffer) ||
(ntohl(*(uint32_t *)buffer) & 0xC07F0000) != mCodecMagic) {
LOGD("stream[%d] malformed packet", mSocket);
@@ -526,70 +526,6 @@
LOGD("group[%d] is dead", mDeviceSocket);
}
-#define FROYO_COMPATIBLE
-#ifdef FROYO_COMPATIBLE
-
-// Copied from AudioRecord.cpp.
-status_t AudioRecord_getMinFrameCount(
- int* frameCount,
- uint32_t sampleRate,
- int format,
- int channelCount)
-{
- size_t size = 0;
- if (AudioSystem::getInputBufferSize(sampleRate, format, channelCount, &size)
- != NO_ERROR) {
- LOGE("AudioSystem could not query the input buffer size.");
- return NO_INIT;
- }
-
- if (size == 0) {
- LOGE("Unsupported configuration: sampleRate %d, format %d, channelCount %d",
- sampleRate, format, channelCount);
- return BAD_VALUE;
- }
-
- // We double the size of input buffer for ping pong use of record buffer.
- size <<= 1;
-
- if (AudioSystem::isLinearPCM(format)) {
- size /= channelCount * (format == AudioSystem::PCM_16_BIT ? 2 : 1);
- }
-
- *frameCount = size;
- return NO_ERROR;
-}
-
-// Copied from AudioTrack.cpp.
-status_t AudioTrack_getMinFrameCount(
- int* frameCount,
- int streamType,
- uint32_t sampleRate)
-{
- int afSampleRate;
- if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) {
- return NO_INIT;
- }
- int afFrameCount;
- if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) {
- return NO_INIT;
- }
- uint32_t afLatency;
- if (AudioSystem::getOutputLatency(&afLatency, streamType) != NO_ERROR) {
- return NO_INIT;
- }
-
- // Ensure that buffer depth covers at least audio hardware latency
- uint32_t minBufCount = afLatency / ((1000 * afFrameCount) / afSampleRate);
- if (minBufCount < 2) minBufCount = 2;
-
- *frameCount = (sampleRate == 0) ? afFrameCount * minBufCount :
- afFrameCount * minBufCount * sampleRate / afSampleRate;
- return NO_ERROR;
-}
-
-#endif
-
bool AudioGroup::set(int sampleRate, int sampleCount)
{
mEventQueue = epoll_create(2);
@@ -603,15 +539,6 @@
// Find out the frame count for AudioTrack and AudioRecord.
int output = 0;
int input = 0;
-#ifdef FROYO_COMPATIBLE
- if (AudioTrack_getMinFrameCount(&output, AudioSystem::VOICE_CALL,
- sampleRate) != NO_ERROR || output <= 0 ||
- AudioRecord_getMinFrameCount(&input, sampleRate,
- AudioSystem::PCM_16_BIT, 1) != NO_ERROR || input <= 0) {
- LOGE("cannot compute frame count");
- return false;
- }
-#else
if (AudioTrack::getMinFrameCount(&output, AudioSystem::VOICE_CALL,
sampleRate) != NO_ERROR || output <= 0 ||
AudioRecord::getMinFrameCount(&input, sampleRate,
@@ -619,7 +546,6 @@
LOGE("cannot compute frame count");
return false;
}
-#endif
LOGD("reported frame count: output %d, input %d", output, input);
output = (output + sampleCount - 1) / sampleCount * sampleCount;
@@ -771,6 +697,10 @@
for (AudioStream *stream = mChain; stream->mNext; stream = stream->mNext) {
AudioStream *target = stream->mNext;
if (target->mSocket == socket) {
+ if (epoll_ctl(mEventQueue, EPOLL_CTL_DEL, socket, NULL)) {
+ LOGE("epoll_ctl: %s", strerror(errno));
+ return false;
+ }
stream->mNext = target->mNext;
LOGD("stream[%d] leaves group[%d]", socket, mDeviceSocket);
delete target;
@@ -874,7 +804,7 @@
static jfieldID gNative;
static jfieldID gMode;
-jint add(JNIEnv *env, jobject thiz, jint mode,
+void add(JNIEnv *env, jobject thiz, jint mode,
jint socket, jstring jRemoteAddress, jint remotePort,
jstring jCodecName, jint sampleRate, jint sampleCount,
jint codecType, jint dtmfType)
@@ -887,7 +817,7 @@
sockaddr_storage remote;
if (parse(env, jRemoteAddress, remotePort, &remote) < 0) {
// Exception already thrown.
- return -1;
+ goto error;
}
if (sampleRate < 0 || sampleCount < 0 || codecType < 0 || codecType > 127) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
@@ -895,12 +825,12 @@
}
if (!jCodecName) {
jniThrowNullPointerException(env, "codecName");
- return -1;
+ goto error;
}
codecName = env->GetStringUTFChars(jCodecName, NULL);
if (!codecName) {
// Exception already thrown.
- return -1;
+ goto error;
}
// Create audio stream.
@@ -909,8 +839,10 @@
codecType, dtmfType)) {
jniThrowException(env, "java/lang/IllegalStateException",
"cannot initialize audio stream");
+ env->ReleaseStringUTFChars(jCodecName, codecName);
goto error;
}
+ env->ReleaseStringUTFChars(jCodecName, codecName);
socket = -1;
// Create audio group.
@@ -934,16 +866,13 @@
// Succeed.
env->SetIntField(thiz, gNative, (int)group);
- env->ReleaseStringUTFChars(jCodecName, codecName);
- return socket;
+ return;
error:
delete group;
delete stream;
close(socket);
env->SetIntField(thiz, gNative, NULL);
- env->ReleaseStringUTFChars(jCodecName, codecName);
- return -1;
}
void remove(JNIEnv *env, jobject thiz, jint socket)
@@ -976,7 +905,7 @@
}
JNINativeMethod gMethods[] = {
- {"add", "(IILjava/lang/String;ILjava/lang/String;IIII)I", (void *)add},
+ {"add", "(IILjava/lang/String;ILjava/lang/String;IIII)V", (void *)add},
{"remove", "(I)V", (void *)remove},
{"setMode", "(I)V", (void *)setMode},
{"sendDtmf", "(I)V", (void *)sendDtmf},
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index cd07f0e..1913fa0 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -40,15 +40,15 @@
boolean pingSupplicant();
- boolean startScan(boolean forceActive);
+ void startScan(boolean forceActive);
List<ScanResult> getScanResults();
- boolean disconnect();
+ void disconnect();
- boolean reconnect();
+ void reconnect();
- boolean reassociate();
+ void reassociate();
WifiInfo getConnectionInfo();
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 9156358..21671f1 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -524,7 +524,8 @@
*/
public boolean disconnect() {
try {
- return mService.disconnect();
+ mService.disconnect();
+ return true;
} catch (RemoteException e) {
return false;
}
@@ -538,7 +539,8 @@
*/
public boolean reconnect() {
try {
- return mService.reconnect();
+ mService.reconnect();
+ return true;
} catch (RemoteException e) {
return false;
}
@@ -552,7 +554,8 @@
*/
public boolean reassociate() {
try {
- return mService.reassociate();
+ mService.reassociate();
+ return true;
} catch (RemoteException e) {
return false;
}
@@ -581,7 +584,8 @@
*/
public boolean startScan() {
try {
- return mService.startScan(false);
+ mService.startScan(false);
+ return true;
} catch (RemoteException e) {
return false;
}
@@ -599,7 +603,8 @@
*/
public boolean startScanActive() {
try {
- return mService.startScan(true);
+ mService.startScan(true);
+ return true;
} catch (RemoteException e) {
return false;
}
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 360b642..19c4eb0 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -495,9 +495,9 @@
/**
* TODO: doc
*/
- public boolean startScan(boolean forceActive) {
- return sendSyncMessage(obtainMessage(CMD_START_SCAN, forceActive ?
- SCAN_ACTIVE : SCAN_PASSIVE, 0)).boolValue;
+ public void startScan(boolean forceActive) {
+ sendMessage(obtainMessage(CMD_START_SCAN, forceActive ?
+ SCAN_ACTIVE : SCAN_PASSIVE, 0));
}
/**
@@ -642,22 +642,22 @@
/**
* Disconnect from Access Point
*/
- public boolean disconnectCommand() {
- return sendSyncMessage(CMD_DISCONNECT).boolValue;
+ public void disconnectCommand() {
+ sendMessage(CMD_DISCONNECT);
}
/**
* Initiate a reconnection to AP
*/
- public boolean reconnectCommand() {
- return sendSyncMessage(CMD_RECONNECT).boolValue;
+ public void reconnectCommand() {
+ sendMessage(CMD_RECONNECT);
}
/**
* Initiate a re-association to AP
*/
- public boolean reassociateCommand() {
- return sendSyncMessage(CMD_REASSOCIATE).boolValue;
+ public void reassociateCommand() {
+ sendMessage(CMD_REASSOCIATE);
}
/**
@@ -1156,6 +1156,23 @@
mScanResults = scanList;
}
+ private String fetchSSID() {
+ String status = WifiNative.statusCommand();
+ if (status == null) {
+ return null;
+ }
+ // extract ssid from a series of "name=value"
+ String[] lines = status.split("\n");
+ for (String line : lines) {
+ String[] prop = line.split(" *= *");
+ if (prop.length < 2) continue;
+ String name = prop[0];
+ String value = prop[1];
+ if (name.equalsIgnoreCase("ssid")) return value;
+ }
+ return null;
+ }
+
private void configureNetworkProperties() {
try {
mNetworkProperties.setInterface(NetworkInterface.getByName(mInterfaceName));
@@ -2056,10 +2073,6 @@
switch (message.what) {
/* Synchronous call returns */
case CMD_PING_SUPPLICANT:
- case CMD_START_SCAN:
- case CMD_DISCONNECT:
- case CMD_RECONNECT:
- case CMD_REASSOCIATE:
case CMD_REMOVE_NETWORK:
case CMD_ENABLE_NETWORK:
case CMD_DISABLE_NETWORK:
@@ -2093,6 +2106,10 @@
case CMD_STOP_DRIVER:
case CMD_START_AP:
case CMD_STOP_AP:
+ case CMD_START_SCAN:
+ case CMD_DISCONNECT:
+ case CMD_RECONNECT:
+ case CMD_REASSOCIATE:
case CMD_RECONFIGURE_IP:
case SUP_CONNECTION_EVENT:
case SUP_DISCONNECTION_EVENT:
@@ -2647,16 +2664,11 @@
case CMD_SET_NUM_ALLOWED_CHANNELS:
case CMD_START_PACKET_FILTERING:
case CMD_STOP_PACKET_FILTERING:
- deferMessage(message);
- break;
- /* Queue the asynchronous version of these commands */
case CMD_START_SCAN:
case CMD_DISCONNECT:
case CMD_REASSOCIATE:
case CMD_RECONNECT:
- if (message.arg2 != SYNCHRONOUS_CALL) {
- deferMessage(message);
- }
+ deferMessage(message);
break;
default:
return NOT_HANDLED;
@@ -2755,6 +2767,7 @@
try {
mBatteryStats.noteWifiStopped();
} catch (RemoteException ignore) { }
+ mScanResults = null;
}
}
@@ -2781,16 +2794,11 @@
case CMD_SET_NUM_ALLOWED_CHANNELS:
case CMD_START_PACKET_FILTERING:
case CMD_STOP_PACKET_FILTERING:
- deferMessage(message);
- break;
- /* Queue the asynchronous version of these commands */
case CMD_START_SCAN:
case CMD_DISCONNECT:
case CMD_REASSOCIATE:
case CMD_RECONNECT:
- if (message.arg2 != SYNCHRONOUS_CALL) {
- deferMessage(message);
- }
+ deferMessage(message);
break;
default:
return NOT_HANDLED;
@@ -2836,15 +2844,7 @@
}
break;
case CMD_START_SCAN:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.scanCommand(
- message.arg1 == SCAN_ACTIVE);
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
- }
+ WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
break;
/* Ignore */
case CMD_DISCONNECT:
@@ -2882,11 +2882,6 @@
mSupplicantStateTracker.handleEvent(stateChangeResult);
break;
case CMD_START_SCAN:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = true;
- notifyOnMsgObject(message);
- }
/* We need to set scan type in completed state */
Message newMsg = obtainMessage();
newMsg.copyFrom(message);
@@ -2894,34 +2889,13 @@
break;
/* Do a redundant disconnect without transition */
case CMD_DISCONNECT:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.disconnectCommand();
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.disconnectCommand();
- }
+ WifiNative.disconnectCommand();
break;
case CMD_RECONNECT:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.reconnectCommand();
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.reconnectCommand();
- }
+ WifiNative.reconnectCommand();
break;
case CMD_REASSOCIATE:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.reassociateCommand();
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.reassociateCommand();
- }
+ WifiNative.reassociateCommand();
break;
case CMD_CONNECT_NETWORK:
int netId = message.arg1;
@@ -2967,6 +2941,8 @@
Log.d(TAG,"Network connection established");
stateChangeResult = (StateChangeResult) message.obj;
+ //TODO: make supplicant modification to push this in events
+ mWifiInfo.setSSID(fetchSSID());
mWifiInfo.setBSSID(mLastBssid = stateChangeResult.BSSID);
mWifiInfo.setNetworkId(stateChangeResult.networkId);
mLastNetworkId = stateChangeResult.networkId;
@@ -3125,14 +3101,7 @@
transitionTo(mDisconnectingState);
break;
case CMD_DISCONNECT:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- SyncParams syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.disconnectCommand();
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.disconnectCommand();
- }
+ WifiNative.disconnectCommand();
transitionTo(mDisconnectingState);
break;
/* Ignore */
@@ -3187,14 +3156,7 @@
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
switch (message.what) {
case CMD_DISCONNECT:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- SyncParams syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.disconnectCommand();
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.disconnectCommand();
- }
+ WifiNative.disconnectCommand();
transitionTo(mDisconnectingState);
break;
case CMD_RECONFIGURE_IP:
@@ -3447,8 +3409,6 @@
setDetailedState(WifiInfo.getDetailedStateOf(supState));
mWifiInfo.setSupplicantState(supState);
mWifiInfo.setNetworkId(stateChangeResult.networkId);
- //TODO: Modify WifiMonitor to report SSID on events
- //mWifiInfo.setSSID()
return HANDLED;
}
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index a5f62c1..147e2dc 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -245,4 +245,4 @@
}
}
-}
\ No newline at end of file
+}