Merge "Move softap config handling to WifiConfigStore" into honeycomb-mr2
diff --git a/api/current.xml b/api/current.xml
index e669a99..393e542 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -31483,6 +31483,25 @@
<parameter name="exit" type="int">
</parameter>
</method>
+<method name="setCustomAnimations"
+ return="android.app.FragmentTransaction"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="enter" type="int">
+</parameter>
+<parameter name="exit" type="int">
+</parameter>
+<parameter name="popEnter" type="int">
+</parameter>
+<parameter name="popExit" type="int">
+</parameter>
+</method>
<method name="setTransition"
return="android.app.FragmentTransaction"
abstract="true"
@@ -95449,6 +95468,17 @@
visibility="public"
>
</method>
+<method name="getRawDescriptors"
+ return="byte[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getSerial"
return="java.lang.String"
abstract="false"
diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java
index 09e3d76..e5a7980 100644
--- a/core/java/android/app/BackStackRecord.java
+++ b/core/java/android/app/BackStackRecord.java
@@ -43,7 +43,7 @@
if (op.removed != null) numRemoved += op.removed.size();
op = op.next;
}
- mOps = new int[bse.mNumOp*5 + numRemoved];
+ mOps = new int[bse.mNumOp*7 + numRemoved];
if (!bse.mAddToBackStack) {
throw new IllegalStateException("Not on back stack");
@@ -56,6 +56,8 @@
mOps[pos++] = op.fragment.mIndex;
mOps[pos++] = op.enterAnim;
mOps[pos++] = op.exitAnim;
+ mOps[pos++] = op.popEnterAnim;
+ mOps[pos++] = op.popExitAnim;
if (op.removed != null) {
final int N = op.removed.size();
mOps[pos++] = N;
@@ -101,6 +103,8 @@
op.fragment = f;
op.enterAnim = mOps[pos++];
op.exitAnim = mOps[pos++];
+ op.popEnterAnim = mOps[pos++];
+ op.popExitAnim = mOps[pos++];
final int N = mOps[pos++];
if (N > 0) {
op.removed = new ArrayList<Fragment>(N);
@@ -179,6 +183,8 @@
Fragment fragment;
int enterAnim;
int exitAnim;
+ int popEnterAnim;
+ int popExitAnim;
ArrayList<Fragment> removed;
}
@@ -187,6 +193,8 @@
int mNumOp;
int mEnterAnim;
int mExitAnim;
+ int mPopEnterAnim;
+ int mPopExitAnim;
int mTransition;
int mTransitionStyle;
boolean mAddToBackStack;
@@ -243,6 +251,11 @@
writer.print(prefix); writer.print("enterAnim="); writer.print(op.enterAnim);
writer.print(" exitAnim="); writer.println(op.exitAnim);
}
+ if (op.popEnterAnim != 0 || op.popExitAnim != 0) {
+ writer.print(prefix);
+ writer.print("popEnterAnim="); writer.print(op.popEnterAnim);
+ writer.print(" popExitAnim="); writer.println(op.popExitAnim);
+ }
if (op.removed != null && op.removed.size() > 0) {
for (int i=0; i<op.removed.size(); i++) {
writer.print(innerPrefix);
@@ -301,6 +314,8 @@
}
op.enterAnim = mEnterAnim;
op.exitAnim = mExitAnim;
+ op.popEnterAnim = mPopEnterAnim;
+ op.popExitAnim = mPopExitAnim;
mNumOp++;
}
@@ -430,8 +445,15 @@
}
public FragmentTransaction setCustomAnimations(int enter, int exit) {
+ return setCustomAnimations(enter, exit, 0, 0);
+ }
+
+ public FragmentTransaction setCustomAnimations(int enter, int exit,
+ int popEnter, int popExit) {
mEnterAnim = enter;
mExitAnim = exit;
+ mPopEnterAnim = popEnter;
+ mPopExitAnim = popExit;
return this;
}
@@ -631,6 +653,7 @@
switch (op.cmd) {
case OP_ADD: {
Fragment f = op.fragment;
+ f.mNextAnim = op.popExitAnim;
f.mImmediateActivity = null;
mManager.removeFragment(f,
FragmentManagerImpl.reverseTransit(mTransition),
@@ -638,6 +661,7 @@
} break;
case OP_REPLACE: {
Fragment f = op.fragment;
+ f.mNextAnim = op.popExitAnim;
f.mImmediateActivity = null;
mManager.removeFragment(f,
FragmentManagerImpl.reverseTransit(mTransition),
@@ -645,6 +669,7 @@
if (op.removed != null) {
for (int i=0; i<op.removed.size(); i++) {
Fragment old = op.removed.get(i);
+ old.mNextAnim = op.popEnterAnim;
f.mImmediateActivity = mManager.mActivity;
mManager.addFragment(old, false);
}
@@ -652,16 +677,19 @@
} break;
case OP_REMOVE: {
Fragment f = op.fragment;
+ f.mNextAnim = op.popEnterAnim;
f.mImmediateActivity = mManager.mActivity;
mManager.addFragment(f, false);
} break;
case OP_HIDE: {
Fragment f = op.fragment;
+ f.mNextAnim = op.popEnterAnim;
mManager.showFragment(f,
FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
} break;
case OP_SHOW: {
Fragment f = op.fragment;
+ f.mNextAnim = op.popExitAnim;
mManager.hideFragment(f,
FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
} break;
diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java
index 15b873b..c1f3cd6 100644
--- a/core/java/android/app/FragmentTransaction.java
+++ b/core/java/android/app/FragmentTransaction.java
@@ -141,10 +141,20 @@
/**
* Set specific animation resources to run for the fragments that are
- * entering and exiting in this transaction.
+ * entering and exiting in this transaction. These animations will not be
+ * played when popping the back stack.
*/
public abstract FragmentTransaction setCustomAnimations(int enter, int exit);
-
+
+ /**
+ * Set specific animation resources to run for the fragments that are
+ * entering and exiting in this transaction. The <code>popEnter</code>
+ * and <code>popExit</code> animations will be played for enter/exit
+ * operations specifically when popping the back stack.
+ */
+ public abstract FragmentTransaction setCustomAnimations(int enter, int exit,
+ int popEnter, int popExit);
+
/**
* Select a standard transition animation for this transaction. May be
* one of {@link #TRANSIT_NONE}, {@link #TRANSIT_FRAGMENT_OPEN},
diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java
index a153c0b..b536490 100644
--- a/core/java/android/hardware/usb/UsbDeviceConnection.java
+++ b/core/java/android/hardware/usb/UsbDeviceConnection.java
@@ -69,6 +69,17 @@
}
/**
+ * Returns the raw USB descriptors for the device.
+ * This can be used to access descriptors not supported directly
+ * via the higher level APIs.
+ *
+ * @return raw USB descriptors
+ */
+ public byte[] getRawDescriptors() {
+ return native_get_desc();
+ }
+
+ /**
* Claims exclusive access to a {@link android.hardware.usb.UsbInterface}.
* This must be done before sending or receiving data on any
* {@link android.hardware.usb.UsbEndpoint}s belonging to the interface.
@@ -160,6 +171,7 @@
private native boolean native_open(String deviceName, FileDescriptor pfd);
private native void native_close();
private native int native_get_fd();
+ private native byte[] native_get_desc();
private native boolean native_claim_interface(int interfaceID, boolean force);
private native boolean native_release_interface(int interfaceID);
private native int native_control_request(int requestType, int request, int value,
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index d79f6c8..d68e6fb 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -34,10 +34,10 @@
private final boolean mRemovable;
private final boolean mEmulated;
private final int mMtpReserveSpace;
+ private int mStorageId;
public StorageVolume(String path, String description,
- boolean removable, boolean emulated,
- int mtpReserveSpace) {
+ boolean removable, boolean emulated, int mtpReserveSpace) {
mPath = path;
mDescription = description;
mRemovable = removable;
@@ -45,6 +45,17 @@
mMtpReserveSpace = mtpReserveSpace;
}
+ // for parcelling only
+ private StorageVolume(String path, String description,
+ boolean removable, boolean emulated, int mtpReserveSpace, int storageId) {
+ mPath = path;
+ mDescription = description;
+ mRemovable = removable;
+ mEmulated = emulated;
+ mMtpReserveSpace = mtpReserveSpace;
+ mStorageId = storageId;
+ }
+
/**
* Returns the mount path for the volume.
*
@@ -82,6 +93,25 @@
}
/**
+ * Returns the MTP storage ID for the volume.
+ * this is also used for the storage_id column in the media provider.
+ *
+ * @return MTP storage ID
+ */
+ public int getStorageId() {
+ return mStorageId;
+ }
+
+ /**
+ * Do not call this unless you are MountService
+ */
+ public void setStorageId(int index) {
+ // storage ID is 0x00010001 for primary storage,
+ // then 0x00020001, 0x00030001, etc. for secondary storages
+ mStorageId = ((index + 1) << 16) + 1;
+ }
+
+ /**
* Number of megabytes of space to leave unallocated by MTP.
* MTP will subtract this value from the free space it reports back
* to the host via GetStorageInfo, and will not allow new files to
@@ -123,9 +153,11 @@
String description = in.readString();
int removable = in.readInt();
int emulated = in.readInt();
+ int storageId = in.readInt();
int mtpReserveSpace = in.readInt();
return new StorageVolume(path, description,
- removable == 1, emulated == 1, mtpReserveSpace);
+ removable == 1, emulated == 1,
+ mtpReserveSpace, storageId);
}
public StorageVolume[] newArray(int size) {
@@ -142,6 +174,7 @@
parcel.writeString(mDescription);
parcel.writeInt(mRemovable ? 1 : 0);
parcel.writeInt(mEmulated ? 1 : 0);
+ parcel.writeInt(mStorageId);
parcel.writeInt(mMtpReserveSpace);
}
}
diff --git a/core/java/com/android/internal/view/menu/ActionMenuView.java b/core/java/com/android/internal/view/menu/ActionMenuView.java
index b49fdc5..5da5e44 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuView.java
@@ -104,9 +104,7 @@
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- final int screen = newConfig.screenLayout;
- mReserveOverflow = (screen & Configuration.SCREENLAYOUT_SIZE_MASK) ==
- Configuration.SCREENLAYOUT_SIZE_XLARGE;
+ mReserveOverflow = newConfig.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE);
mMaxItems = getMaxActionButtons();
mWidthLimit = getResources().getDisplayMetrics().widthPixels / 2;
if (mMenu != null) {
diff --git a/core/jni/android_hardware_UsbDeviceConnection.cpp b/core/jni/android_hardware_UsbDeviceConnection.cpp
index ec36a38..b5d6b91 100644
--- a/core/jni/android_hardware_UsbDeviceConnection.cpp
+++ b/core/jni/android_hardware_UsbDeviceConnection.cpp
@@ -83,6 +83,27 @@
return usb_device_get_fd(device);
}
+static jbyteArray
+android_hardware_UsbDeviceConnection_get_desc(JNIEnv *env, jobject thiz)
+{
+ char buffer[16384];
+ int fd = android_hardware_UsbDeviceConnection_get_fd(env, thiz);
+ if (fd < 0) return NULL;
+ lseek(fd, 0, SEEK_SET);
+ int length = read(fd, buffer, sizeof(buffer));
+ if (length < 0) return NULL;
+
+ jbyteArray ret = env->NewByteArray(length);
+ if (ret) {
+ jbyte* bytes = (jbyte*)env->GetPrimitiveArrayCritical(ret, 0);
+ if (bytes) {
+ memcpy(bytes, buffer, length);
+ env->ReleasePrimitiveArrayCritical(ret, bytes, 0);
+ }
+ }
+ return ret;
+}
+
static jboolean
android_hardware_UsbDeviceConnection_claim_interface(JNIEnv *env, jobject thiz,
int interfaceID, jboolean force)
@@ -211,6 +232,7 @@
(void *)android_hardware_UsbDeviceConnection_open},
{"native_close", "()V", (void *)android_hardware_UsbDeviceConnection_close},
{"native_get_fd", "()I", (void *)android_hardware_UsbDeviceConnection_get_fd},
+ {"native_get_desc", "()[B", (void *)android_hardware_UsbDeviceConnection_get_desc},
{"native_claim_interface", "(IZ)Z",(void *)android_hardware_UsbDeviceConnection_claim_interface},
{"native_release_interface","(I)Z", (void *)android_hardware_UsbDeviceConnection_release_interface},
{"native_control_request", "(IIII[BII)I",
diff --git a/core/res/res/drawable-hdpi/dialog_bottom_holo_dark.9.png b/core/res/res/drawable-hdpi/dialog_bottom_holo_dark.9.png
index 6c6252e..32c2c97 100644
--- a/core/res/res/drawable-hdpi/dialog_bottom_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/dialog_bottom_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_bottom_holo_light.9.png b/core/res/res/drawable-hdpi/dialog_bottom_holo_light.9.png
index 175c750..f1cba06 100644
--- a/core/res/res/drawable-hdpi/dialog_bottom_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/dialog_bottom_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_full_holo_dark.9.png b/core/res/res/drawable-hdpi/dialog_full_holo_dark.9.png
index 6e9abe65..08b163a 100644
--- a/core/res/res/drawable-hdpi/dialog_full_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/dialog_full_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_full_holo_light.9.png b/core/res/res/drawable-hdpi/dialog_full_holo_light.9.png
index f96d09e..77ec017 100644
--- a/core/res/res/drawable-hdpi/dialog_full_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/dialog_full_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_middle_holo_dark.9.png b/core/res/res/drawable-hdpi/dialog_middle_holo_dark.9.png
index 1f11f44..029f186 100644
--- a/core/res/res/drawable-hdpi/dialog_middle_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/dialog_middle_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_middle_holo_light.9.png b/core/res/res/drawable-hdpi/dialog_middle_holo_light.9.png
index 2e376cd..abaea2d 100644
--- a/core/res/res/drawable-hdpi/dialog_middle_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/dialog_middle_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_top_holo_dark.9.png b/core/res/res/drawable-hdpi/dialog_top_holo_dark.9.png
index 73d56b7..acbd7cf 100644
--- a/core/res/res/drawable-hdpi/dialog_top_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/dialog_top_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_top_holo_light.9.png b/core/res/res/drawable-hdpi/dialog_top_holo_light.9.png
index 869decf..8982396 100644
--- a/core/res/res/drawable-hdpi/dialog_top_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/dialog_top_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/toast_frame.9.png b/core/res/res/drawable-hdpi/toast_frame.9.png
index 736683e..08b163a 100644
--- a/core/res/res/drawable-hdpi/toast_frame.9.png
+++ b/core/res/res/drawable-hdpi/toast_frame.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_bottom_holo_dark.9.png b/core/res/res/drawable-mdpi/dialog_bottom_holo_dark.9.png
index 9471615..cc66804 100644
--- a/core/res/res/drawable-mdpi/dialog_bottom_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/dialog_bottom_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_bottom_holo_light.9.png b/core/res/res/drawable-mdpi/dialog_bottom_holo_light.9.png
index 0502b93..bc734c8 100644
--- a/core/res/res/drawable-mdpi/dialog_bottom_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/dialog_bottom_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_full_holo_dark.9.png b/core/res/res/drawable-mdpi/dialog_full_holo_dark.9.png
index f364b2e..8603e93 100644
--- a/core/res/res/drawable-mdpi/dialog_full_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/dialog_full_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_full_holo_light.9.png b/core/res/res/drawable-mdpi/dialog_full_holo_light.9.png
index 91c2076..65a318c 100644
--- a/core/res/res/drawable-mdpi/dialog_full_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/dialog_full_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_middle_holo_dark.9.png b/core/res/res/drawable-mdpi/dialog_middle_holo_dark.9.png
index 92788c9..e39a472 100644
--- a/core/res/res/drawable-mdpi/dialog_middle_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/dialog_middle_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_middle_holo_light.9.png b/core/res/res/drawable-mdpi/dialog_middle_holo_light.9.png
index 74b66f8..68f9e57 100644
--- a/core/res/res/drawable-mdpi/dialog_middle_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/dialog_middle_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_top_holo_dark.9.png b/core/res/res/drawable-mdpi/dialog_top_holo_dark.9.png
index f25cfb6..32c49f2 100644
--- a/core/res/res/drawable-mdpi/dialog_top_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/dialog_top_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_top_holo_light.9.png b/core/res/res/drawable-mdpi/dialog_top_holo_light.9.png
index ff3ff06..60aa8cb 100644
--- a/core/res/res/drawable-mdpi/dialog_top_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/dialog_top_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/toast_frame.9.png b/core/res/res/drawable-mdpi/toast_frame.9.png
index 1b06b7c8..8603e93 100755
--- a/core/res/res/drawable-mdpi/toast_frame.9.png
+++ b/core/res/res/drawable-mdpi/toast_frame.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/dialog_bottom_holo_dark.9.png b/core/res/res/drawable-xhdpi/dialog_bottom_holo_dark.9.png
new file mode 100644
index 0000000..b33e117
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/dialog_bottom_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/dialog_bottom_holo_light.9.png b/core/res/res/drawable-xhdpi/dialog_bottom_holo_light.9.png
new file mode 100644
index 0000000..d84d427
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/dialog_bottom_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/dialog_full_holo_dark.9.png b/core/res/res/drawable-xhdpi/dialog_full_holo_dark.9.png
new file mode 100644
index 0000000..9c0ff47
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/dialog_full_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/dialog_full_holo_light.9.png b/core/res/res/drawable-xhdpi/dialog_full_holo_light.9.png
new file mode 100644
index 0000000..331a4f2
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/dialog_full_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/dialog_middle_holo_dark.9.png b/core/res/res/drawable-xhdpi/dialog_middle_holo_dark.9.png
new file mode 100644
index 0000000..cdc887d
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/dialog_middle_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/dialog_middle_holo_light.9.png b/core/res/res/drawable-xhdpi/dialog_middle_holo_light.9.png
new file mode 100644
index 0000000..55c60b8
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/dialog_middle_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/dialog_top_holo_dark.9.png b/core/res/res/drawable-xhdpi/dialog_top_holo_dark.9.png
new file mode 100644
index 0000000..600efb3
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/dialog_top_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/dialog_top_holo_light.9.png b/core/res/res/drawable-xhdpi/dialog_top_holo_light.9.png
new file mode 100644
index 0000000..d33d033
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/dialog_top_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/toast_frame.9.png b/core/res/res/drawable-xhdpi/toast_frame.9.png
new file mode 100644
index 0000000..9c0ff47
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/toast_frame.9.png
Binary files differ
diff --git a/core/res/res/layout-w720dp/action_bar_home.xml b/core/res/res/layout-w720dp/action_bar_home.xml
new file mode 100644
index 0000000..ece1a34
--- /dev/null
+++ b/core/res/res/layout-w720dp/action_bar_home.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+
+<view xmlns:android="http://schemas.android.com/apk/res/android"
+ class="com.android.internal.widget.ActionBarView$HomeView"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:background="?android:attr/selectableItemBackground" >
+ <ImageView android:id="@android:id/up"
+ android:src="?android:attr/homeAsUpIndicator"
+ android:layout_gravity="center_vertical|left"
+ android:visibility="gone"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="-12dip" />
+ <ImageView android:id="@android:id/home"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="16dip"
+ android:paddingRight="16dip"
+ android:layout_gravity="center"
+ android:scaleType="center" />
+</view>
diff --git a/core/res/res/layout/action_bar_home.xml b/core/res/res/layout/action_bar_home.xml
index c82f91d..a45432a 100644
--- a/core/res/res/layout/action_bar_home.xml
+++ b/core/res/res/layout/action_bar_home.xml
@@ -25,12 +25,12 @@
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginRight="-12dip" />
+ android:layout_marginRight="-4dip" />
<ImageView android:id="@android:id/home"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:paddingLeft="16dip"
- android:paddingRight="16dip"
+ android:paddingLeft="8dip"
+ android:paddingRight="8dip"
android:layout_gravity="center"
android:scaleType="center" />
</view>
diff --git a/core/res/res/layout/alert_dialog_holo.xml b/core/res/res/layout/alert_dialog_holo.xml
index 57eb9c7..8ee91ca 100644
--- a/core/res/res/layout/alert_dialog_holo.xml
+++ b/core/res/res/layout/alert_dialog_holo.xml
@@ -22,6 +22,8 @@
android:id="@+id/parentPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_marginLeft="8dip"
+ android:layout_marginRight="8dip"
android:orientation="vertical">
<LinearLayout android:id="@+id/topPanel"
@@ -42,7 +44,7 @@
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical|left"
- android:minHeight="60dip"
+ android:minHeight="@dimen/alert_dialog_title_height"
android:layout_marginLeft="32dip"
android:layout_marginRight="32dip">
<ImageView android:id="@+id/icon"
@@ -107,7 +109,7 @@
<LinearLayout android:id="@+id/buttonPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="54dip"
+ android:minHeight="@dimen/alert_dialog_button_bar_height"
android:orientation="vertical"
android:divider="?android:attr/dividerHorizontal"
android:showDividers="beginning"
@@ -132,6 +134,7 @@
android:layout_weight="1"
android:maxLines="2"
style="?android:attr/buttonBarButtonStyle"
+ android:minHeight="@dimen/alert_dialog_button_bar_height"
android:layout_height="wrap_content" />
<Button android:id="@+id/button3"
android:layout_width="0dip"
@@ -139,12 +142,14 @@
android:layout_weight="1"
android:maxLines="2"
style="?android:attr/buttonBarButtonStyle"
+ android:minHeight="@dimen/alert_dialog_button_bar_height"
android:layout_height="wrap_content" />
<Button android:id="@+id/button2"
android:layout_width="0dip"
android:layout_gravity="right"
android:layout_weight="1"
android:maxLines="2"
+ android:minHeight="@dimen/alert_dialog_button_bar_height"
style="?android:attr/buttonBarButtonStyle"
android:layout_height="wrap_content" />
<LinearLayout android:id="@+id/rightSpacer"
diff --git a/core/res/res/values-h720dp/dimens.xml b/core/res/res/values-h720dp/dimens.xml
new file mode 100644
index 0000000..7f43946
--- /dev/null
+++ b/core/res/res/values-h720dp/dimens.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2011, 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>
+ <!-- Dialog title height -->
+ <dimen name="alert_dialog_title_height">54dip</dimen>
+ <!-- Dialog button bar height -->
+ <dimen name="alert_dialog_button_bar_height">54dip</dimen>
+</resources>
diff --git a/core/res/res/values-large/strings.xml b/core/res/res/values-large/strings.xml
new file mode 100644
index 0000000..e998b9a
--- /dev/null
+++ b/core/res/res/values-large/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2011, 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:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Do not translate. WebView User Agent targeted content -->
+ <string name="web_user_agent_target_content" translatable="false"></string>
+
+</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 8a590cd..05840a4 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -76,4 +76,9 @@
<!-- Minimum width of the search view text entry area. -->
<dimen name="search_view_text_min_width">160dip</dimen>
+
+ <!-- Dialog title height -->
+ <dimen name="alert_dialog_title_height">48dip</dimen>
+ <!-- Dialog button bar height -->
+ <dimen name="alert_dialog_button_bar_height">48dip</dimen>
</resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 5d054bd..08a3024 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1434,6 +1434,10 @@
</style>
<style name="Holo.ButtonBar" parent="ButtonBar">
+ <item name="android:paddingTop">0dip</item>
+ <item name="android:paddingLeft">0dip</item>
+ <item name="android:paddingRight">0dip</item>
+ <item name="android:paddingBottom">0dip</item>
<item name="divider">?android:attr/dividerVertical</item>
<item name="showDividers">middle</item>
<item name="dividerPadding">8dip</item>
diff --git a/docs/html/guide/appendix/install-location.jd b/docs/html/guide/appendix/install-location.jd
index 7f96809..617f4fc 100644
--- a/docs/html/guide/appendix/install-location.jd
+++ b/docs/html/guide/appendix/install-location.jd
@@ -171,6 +171,11 @@
<dd>The system delivers the {@link android.content.Intent#ACTION_BOOT_COMPLETED} broadcast
before the external storage is mounted to the device. If your application is installed on the
external storage, it can never receive this broadcast.</dd>
+ <dt>Copy Protection</dt>
+ <dd>Your application cannot be installed to a device's SD card if it uses Android Market's
+ Copy Protection feature. However, if you use Android Market's
+ <a href="{@docRoot}guide/publishing/licensing.html">Application Licensing</a> instead, your
+ application <em>can</em> be installed to internal or external storage, including SD cards.</dd>
</dl>
<p>If your application uses any of the features listed above, you <strong>should not</strong> allow
diff --git a/docs/html/guide/publishing/licensing.jd b/docs/html/guide/publishing/licensing.jd
index a9b182e..d89a8ca 100644
--- a/docs/html/guide/publishing/licensing.jd
+++ b/docs/html/guide/publishing/licensing.jd
@@ -263,15 +263,15 @@
you won't be able to upload a new version that uses licensing.</li>
</ul>
-<h4>Replacement for copy protection</h4>
+<h4>Replacement for Copy Protection</h4>
<p>Android Market Licensing is a flexible, secure mechanism for controlling
-access to your applications. It effectively replaces the copy-protection
+access to your applications. It effectively replaces the Copy Protection
mechanism offered on Android Market and gives you wider distribution
potential for your applications. </p>
<ul>
-<li>A limitation of the legacy copy-protection mechanism on Android Market is
+<li>A limitation of the legacy Copy Protection mechanism on Android Market is
that applications using it can be installed only on compatible devices that
provide a secure internal storage environment. For example, a copy-protected
application cannot be downloaded from Market to a device that provides root
diff --git a/docs/html/guide/topics/appwidgets/index.jd b/docs/html/guide/topics/appwidgets/index.jd
index 89306a2..e589292 100644
--- a/docs/html/guide/topics/appwidgets/index.jd
+++ b/docs/html/guide/topics/appwidgets/index.jd
@@ -7,7 +7,8 @@
<ul>
<li>App Widgets provide users access to some of your application features
directly from the Home screen (without the need to launch an activity)</li>
- <li>App Widgets are backed by a special kind of broadcast receiver that handles the App
+ <li>App Widgets are backed by a special kind of broadcast receiver that
+handles the App
Widget lifecycle</li>
</ul>
@@ -19,15 +20,28 @@
<li><a href="#CreatingLayout">Creating the App Widget Layout</a></li>
<li><a href="#AppWidgetProvider">Using the AppWidgetProvider Class</a>
<ol>
- <li><a href="#ProviderBroadcasts">Receiving App Widget broadcast Intents</a></li>
+ <li><a href="#ProviderBroadcasts">Receiving App Widget broadcast
+Intents</a></li>
</ol>
</li>
- <li><a href="#Configuring">Creating an App Widget Configuration Activity</a>
+ <li><a href="#Configuring">Creating an App Widget Configuration
+Activity</a>
<ol>
- <li><a href="#UpdatingFromTheConfiguration">Updating the App Widget from
+ <li><a href="#UpdatingFromTheConfiguration">Updating the App Widget
+from
the configuration Activity</a></li>
</ol>
</li>
+ <li><a href="#preview">Setting a Preview Image</a></li>
+ <li><a href="#collections">Using App Widgets with Collections</a>
+ <ol>
+ <li><a href="#collection_sample">Sample application</a></li>
+ <li><a href="#implementing_collections">Implementing app widgets with
+collections
+</a></li>
+ <li><a href="#fresh">Keeping Collection Data Fresh</a></li>
+ </ol>
+ </li>
</ol>
<h2>Key classes</h2>
@@ -39,25 +53,34 @@
<h2>See also</h2>
<ol>
- <li><a href="{@docRoot}guide/practices/ui_guidelines/widget_design.html">App Widget Design
+ <li><a
+href="{@docRoot}guide/practices/ui_guidelines/widget_design.html">App Widget
+Design
Guidelines</a></li>
- <li><a href="http://android-developers.blogspot.com/2009/04/introducing-home-screen-widgets-and.html">Introducing
+ <li><a
+href="http://android-developers.blogspot.com/2009/04/introducing-home-screen-
+widgets-and.html">Introducing
home screen widgets and the AppWidget framework »</a></li>
</ol>
</div>
</div>
-<p>App Widgets are miniature application views that can be embedded in other applications
-(such as the Home screen) and receive periodic updates. These views are referred
+<p>App Widgets are miniature application views that can be embedded in other
+applications
+(such as the Home screen) and receive periodic updates. These views are
+referred
to as Widgets in the user interface,
-and you can publish one with an App Widget provider. An application component that is
-able to hold other App Widgets is called an App Widget host. The screenshot below shows
+and you can publish one with an App Widget provider. An application component
+that is
+able to hold other App Widgets is called an App Widget host. The screenshot
+below shows
the Music App Widget.</p>
<img src="{@docRoot}images/appwidget.png" alt="" />
-<p>This document describes how to publish an App Widget using an App Widget provider.</p>
+<p>This document describes how to publish an App Widget using an App Widget
+provider.</p>
<h2 id="Basics">The Basics</h2>
@@ -66,18 +89,23 @@
<dl>
<dt>{@link android.appwidget.AppWidgetProviderInfo} object</dt>
- <dd>Describes the metadata for an App Widget, such as the App Widget's layout, update frequency,
+ <dd>Describes the metadata for an App Widget, such as the App Widget's layout,
+update frequency,
and the AppWidgetProvider class. This should be defined in XML.</dd>
<dt>{@link android.appwidget.AppWidgetProvider} class implementation</dt>
- <dd>Defines the basic methods that allow you to programmatically interface with the App Widget,
- based on broadcast events. Through it, you will receive broadcasts when the App Widget is updated,
+ <dd>Defines the basic methods that allow you to programmatically interface
+with the App Widget,
+ based on broadcast events. Through it, you will receive broadcasts when the
+App Widget is updated,
enabled, disabled and deleted.</dd>
<dt>View layout</dt>
<dd>Defines the initial layout for the App Widget, defined in XML.</dd>
</dl>
-<p>Additionally, you can implement an App Widget configuration Activity. This is an optional
-{@link android.app.Activity} that launches when the user adds your App Widget and allows him or her
+<p>Additionally, you can implement an App Widget configuration Activity. This is
+an optional
+{@link android.app.Activity} that launches when the user adds your App Widget
+and allows him or her
to modify App Widget settings at create-time.</p>
<p>The following sections describe how to setup each of these components.</p>
@@ -85,7 +113,8 @@
<h2 id="Manifest">Declaring an App Widget in the Manifest</h2>
-<p>First, declare the {@link android.appwidget.AppWidgetProvider} class in your application's
+<p>First, declare the {@link android.appwidget.AppWidgetProvider} class in your
+application's
<code>AndroidManifest.xml</code> file. For example:</p>
<pre>
@@ -98,24 +127,32 @@
</receiver>
</pre>
-<p>The <code><receiver></code> element requires the <code>android:name</code>
+<p>The <code><receiver></code> element requires the
+<code>android:name</code>
attribute, which specifies the {@link android.appwidget.AppWidgetProvider} used
by the App Widget.</p>
-<p>The <code><intent-filter></code> element must include an <code><action></code>
+<p>The <code><intent-filter></code> element must include an
+<code><action></code>
element with the <code>android:name</code> attribute. This attribute specifies
that the {@link android.appwidget.AppWidgetProvider} accepts the {@link
-android.appwidget.AppWidgetManager#ACTION_APPWIDGET_UPDATE ACTION_APPWIDGET_UPDATE} broadcast.
-This is the only broadcast that you must explicitly declare. The {@link android.appwidget.AppWidgetManager}
-automatically sends all other App Widget broadcasts to the AppWidgetProvider as necessary.</p>
+android.appwidget.AppWidgetManager#ACTION_APPWIDGET_UPDATE
+ACTION_APPWIDGET_UPDATE} broadcast.
+This is the only broadcast that you must explicitly declare. The {@link
+android.appwidget.AppWidgetManager}
+automatically sends all other App Widget broadcasts to the AppWidgetProvider as
+necessary.</p>
<p>The <code><meta-data></code> element specifies the
{@link android.appwidget.AppWidgetProviderInfo} resource and requires the
following attributes:</p>
<ul>
- <li><code>android:name</code> - Specifies the metadata name. Use <code>android.appwidget.provider</code>
- to identify the data as the {@link android.appwidget.AppWidgetProviderInfo} descriptor.</li>
- <li><code>android:resource</code> - Specifies the {@link android.appwidget.AppWidgetProviderInfo}
+ <li><code>android:name</code> - Specifies the metadata name. Use
+<code>android.appwidget.provider</code>
+ to identify the data as the {@link android.appwidget.AppWidgetProviderInfo}
+descriptor.</li>
+ <li><code>android:resource</code> - Specifies the {@link
+android.appwidget.AppWidgetProviderInfo}
resource location.</li>
</ul>
@@ -123,10 +160,13 @@
<h2 id="MetaData">Adding the AppWidgetProviderInfo Metadata</h2>
<p>The {@link android.appwidget.AppWidgetProviderInfo} defines the essential
-qualities of an App Widget, such as its minimum layout dimensions, its initial layout resource,
-how often to update the App Widget, and (optionally) a configuration Activity to launch at create-time.
+qualities of an App Widget, such as its minimum layout dimensions, its initial
+layout resource,
+how often to update the App Widget, and (optionally) a configuration Activity to
+launch at create-time.
Define the AppWidgetProviderInfo object in an XML resource using a single
-<code><appwidget-provider></code> element and save it in the project's <code>res/xml/</code>
+<code><appwidget-provider></code> element and save it in the project's
+<code>res/xml/</code>
folder.</p>
<p>For example:</p>
@@ -136,71 +176,131 @@
android:minWidth="294dp"
android:minHeight="72dp"
android:updatePeriodMillis="86400000"
+ android:previewImage="@drawable/preview"
android:initialLayout="@layout/example_appwidget"
- android:configure="com.example.android.ExampleAppWidgetConfigure" >
+ android:configure="com.example.android.ExampleAppWidgetConfigure"
+ android:resizeMode="horizontal|vertical">
</appwidget-provider>
</pre>
<p>Here's a summary of the <code><appwidget-provider></code> attributes:</p>
<ul>
- <li>The values for the <code>minWidth</code> and <code>minHeight</code> attributes specify the minimum
- area required by the App Widget's layout.
- <p>The default Home screen positions App Widgets in its window based on a grid of
- cells that have a defined height and width. If the values for an App Widget's minimum width
+ <li>The values for the <code>minWidth</code> and <code>minHeight</code>
+attributes specify the minimum
+ area required by the App Widget's layout.
+ <p>The default Home screen positions App Widgets in its window based on a
+grid of
+ cells that have a defined height and width. If the values for an App
+Widget's minimum width
or height don't match the dimensions of the cells,
then the App Widget dimensions round <em>up</em> to the nearest cell size.
- (See the <a href="{@docRoot}guide/practices/ui_guidelines/widget_design.html">App Widget Design
+ (See the <a
+href="{@docRoot}guide/practices/ui_guidelines/widget_design.html">App Widget
+Design
Guidelines</a> for more information on the Home screen cell sizes.)</p>
- <p>Because the Home screen's layout orientation (and thus, the cell sizes) can change,
- as a rule of thumb, you should assume the worst-case cell size of 74 pixels for the height
- <em>and</em> width of a cell. However, you must subtract 2 from the final dimension to account
- for any integer rounding errors that occur in the pixel count. To find your minimum width
+ <p>Because the Home screen's layout orientation (and thus, the cell sizes)
+can change,
+ as a rule of thumb, you should assume the worst-case cell size of 74 pixels
+for the height
+ <em>and</em> width of a cell. However, you must subtract 2 from the final
+dimension to account
+ for any integer rounding errors that occur in the pixel count. To find your
+minimum width
and height in density-independent pixels (dp), use this formula:<br/>
<code>(number of cells * 74) - 2</code><br/>
- Following this formula, you should use 72 dp for a height of one cell, 294 dp and for a width of four cells.</p>
+ Following this formula, you should use 72 dp for a height of one cell, 294
+dp and for a width of four cells.</p>
+<p class="note"><strong>Note:</strong> To make your app widget portable across
+devices, your app widget's minimum size should never be larger than 4 x 4 cells.
+See the <a
+href="{@docRoot}guide/practices/ui_guidelines/widget_design.htmll#sizes">App
+Widget Design Guidelines</a> for more discussion of Home screen cell sizes.</p>
</li>
- <li>The <code>updatePeriodMillis</code> attribute defines how often the App Widget framework should
- request an update from the {@link android.appwidget.AppWidgetProvider} by calling the
- {@link android.appwidget.AppWidgetProvider#onUpdate(Context,AppWidgetManager,int[])
- onUpdate()} method. The actual update is not guaranteed to occur exactly on time with this value
- and we suggest updating as infrequently as possible—perhaps no more than once an hour to
- conserve the battery. You might also allow the user to adjust the frequency in a
- configuration—some people might want a stock ticker to update every 15 minutes, or maybe
- only four times a day.
- <p class="note"><strong>Note:</strong> If the device is asleep when it is time for an update
- (as defined by <code>updatePeriodMillis</code>), then the device will wake up in order
- to perform the update. If you don't update more than once per hour, this probably won't
- cause significant problems for the battery life. If, however, you need to update more
- frequently and/or you do not need to update while the device is asleep, then you can instead
- perform updates based on an alarm that will not wake the device. To do so, set an alarm with
- an Intent that your AppWidgetProvider receives, using the {@link android.app.AlarmManager}.
- Set the alarm type to either {@link android.app.AlarmManager#ELAPSED_REALTIME} or
+ <li>The <code>updatePeriodMillis</code> attribute defines how often the App
+Widget framework should request an update from the {@link
+android.appwidget.AppWidgetProvider} by calling the
+{@link android.appwidget.AppWidgetProvider#onUpdate(android.content.Context,android.appwidget.AppWidgetManager,int[]) onUpdate()}
+callback method. The actual update
+is not guaranteed to occur exactly on time with this value and we suggest
+updating as infrequently as possible—perhaps no more than once an hour to
+conserve the battery. You might also allow the user to adjust the frequency in a
+configuration—some people might want a stock ticker to update every 15
+minutes, or maybe only four times a day.
+ <p class="note"><strong>Note:</strong> If the device is asleep when it
+is time for an update
+ (as defined by <code>updatePeriodMillis</code>), then the device will
+wake up in order
+ to perform the update. If you don't update more than once per hour, this
+probably won't
+ cause significant problems for the battery life. If, however, you need
+to update more
+ frequently and/or you do not need to update while the device is asleep,
+then you can instead
+ perform updates based on an alarm that will not wake the device. To do
+so, set an alarm with
+ an Intent that your AppWidgetProvider receives, using the {@link
+android.app.AlarmManager}.
+ Set the alarm type to either {@link
+android.app.AlarmManager#ELAPSED_REALTIME} or
{@link android.app.AlarmManager#RTC}, which will only
- deliver the alarm when the device is awake. Then set <code>updatePeriodMillis</code> to
+ deliver the alarm when the device is awake. Then set
+<code>updatePeriodMillis</code> to
zero (<code>"0"</code>).</p>
</li>
- <li>The <code>initialLayout</code> attribute points to the layout resource that defines the
+ <li>The <code>initialLayout</code> attribute points to the layout resource
+that defines the
App Widget layout.</li>
- <li>The <code>configure</code> attribute defines the {@link android.app.Activity} to launch when
- the user adds the App Widget, in order for him or her to configure App Widget properties. This is optional
- (read <a href="#Configuring">Creating an App Widget Configuration Activity</a> below).</li>
-</ul>
+ <li>The <code>configure</code> attribute defines the {@link
+android.app.Activity} to launch when
+ the user adds the App Widget, in order for him or her to configure App
+Widget properties. This is optional
+ (read <a href="#Configuring">Creating an App Widget Configuration
+Activity</a> below).</li>
+
+ <li>The <code>previewImage</code> attribute specifies a preview of what the
+app widget will look like after it's configured, which the user sees when
+selecting the app widget. If not supplied, the user instead sees your
+application's launcher icon. This field corresponds to the
+<code>android:previewImage</code> attribute in the <code><receiver></code>
+element in the <code>AndroidManifest.xml</code> file. For more discussion of
+using <code>previewImage</code>, see <a href="#preview">Setting a Preview
+Image</a>. Introduced in Android 3.0.</li>
-<p>See the {@link android.appwidget.AppWidgetProviderInfo} class for more information on the
+ <li>The <code>autoAdvanceViewId</code> attribute specifies the view ID of the
+app widget subview that should be auto-advanced by the widget's host. Introduced in Android 3.0.</li>
+
+<li>The <code>resizeMode</code> attribute specifies the rules by which a widget
+can be resized. You use this attribute to make homescreen widgets
+resizeable—horizontally, vertically, or on both axes. Users touch-hold a
+widget to show its resize handles, then drag the horizontal and/or vertical
+handles to change the size on the layout grid. Values for the
+<code>resizeMode</code> attribute include "horizontal", "vertical", and "none".
+To declare a widget as resizeable horizontally and vertically, supply the value
+"horizontal|vertical". Introduced in Android 3.1.</li> </ul>
+
+<p>See the {@link android.appwidget.AppWidgetProviderInfo} class for more
+information on the
attributes accepted by the <code><appwidget-provider></code> element.</p>
<h2 id="CreatingLayout">Creating the App Widget Layout</h2>
-<p>You must define an initial layout for your App Widget in XML and save it in the project's
-<code>res/layout/</code> directory. You can design your App Widget using the View objects listed
-below, but before you begin designing your App Widget, please read and understand the
-<a href="{@docRoot}guide/practices/ui_guidelines/widget_design.html">App Widget Design
+<p>You must define an initial layout for your App Widget in XML and save it in
+the project's
+<code>res/layout/</code> directory. You can design your App Widget using the
+View objects listed
+below, but before you begin designing your App Widget, please read and
+understand the
+<a href="{@docRoot}guide/practices/ui_guidelines/widget_design.html">App Widget
+Design
Guidelines</a>.</p>
<p>Creating the App Widget layout is simple if you're
-familiar with <a href="{@docRoot}guide/topics/ui/declaring-layout.html">Declaring Layout in XML</a>.
-However, you must be aware that App Widget layouts are based on {@link android.widget.RemoteViews},
+familiar with <a
+href="{@docRoot}guide/topics/ui/declaring-layout.html">Declaring Layout in
+XML</a>.
+However, you must be aware that App Widget layouts are based on {@link
+android.widget.RemoteViews},
which do not support every kind of layout or view widget.</p>
<p>A RemoteViews object (and, consequently, an App Widget) can support the
@@ -221,6 +321,7 @@
<li>{@link android.widget.ImageView}</li>
<li>{@link android.widget.ProgressBar}</li>
<li>{@link android.widget.TextView}</li>
+ <li>{@link android.widget.ViewFlipper}</li>
</ul>
<p>Descendants of these classes are not supported.</p>
@@ -230,66 +331,90 @@
<div class="sidebox-wrapper">
<div class="sidebox">
- <p>You must declare your AppWidgetProvider class implementation as a broadcast receiver
+ <p>You must declare your AppWidgetProvider class implementation as a
+broadcast receiver
using the <code><receiver></code> element in the AndroidManifest (see
<a href="#Manifest">Declaring an App Widget in the Manifest</a> above).</p>
</div>
</div>
-<p>The {@link android.appwidget.AppWidgetProvider} class extends BroadcastReceiver as a convenience
-class to handle the App Widget broadcasts. The AppWidgetProvider receives only the event broadcasts that
-are relevant to the App Widget, such as when the App Widget is updated, deleted, enabled, and disabled.
-When these broadcast events occur, the AppWidgetProvider receives the following method calls:</p>
+<p>The {@link android.appwidget.AppWidgetProvider} class extends
+BroadcastReceiver as a convenience
+class to handle the App Widget broadcasts. The AppWidgetProvider receives only
+the event broadcasts that
+are relevant to the App Widget, such as when the App Widget is updated, deleted,
+enabled, and disabled.
+When these broadcast events occur, the AppWidgetProvider receives the following
+method calls:</p>
<dl>
- <dt>{@link android.appwidget.AppWidgetProvider#onUpdate(Context,AppWidgetManager,int[])}</dt>
- <dd>This is called to update the App Widget at intervals defined by the <code>updatePeriodMillis</code>
+ <dt>
+ {@link android.appwidget.AppWidgetProvider#onUpdate(android.content.Context,android.appwidget.AppWidgetManager,int[]) onUpdate()}
+</dt>
+ <dd>This is called to update the App Widget at intervals defined by the
+<code>updatePeriodMillis</code>
attribute in the AppWidgetProviderInfo (see <a href="#MetaData">Adding the
AppWidgetProviderInfo Metadata</a> above). This method is also called
when the user adds the App Widget, so it should perform the essential setup,
such as define event handlers for Views and start a temporary
- {@link android.app.Service}, if necessary. However, if you have declared a configuration
- Activity, <strong>this method is not called</strong> when the user adds the App Widget,
+ {@link android.app.Service}, if necessary. However, if you have declared a
+configuration
+ Activity, <strong>this method is not called</strong> when the user adds the
+App Widget,
but is called for the subsequent updates. It is the responsibility of the
- configuration Activity to perform the first update when configuration is done.
- (See <a href="#Configuring">Creating an App Widget Configuration Activity</a> below.)</dd>
+ configuration Activity to perform the first update when configuration is
+done.
+ (See <a href="#Configuring">Creating an App Widget Configuration
+Activity</a> below.)</dd>
<dt>{@link android.appwidget.AppWidgetProvider#onDeleted(Context,int[])}</dt>
- <dd>This is called every time an App Widget is deleted from the App Widget host.</dd>
+ <dd>This is called every time an App Widget is deleted from the App Widget
+host.</dd>
<dt>{@link android.appwidget.AppWidgetProvider#onEnabled(Context)}</dt>
- <dd>This is called when an instance the App Widget is created for the first time. For example, if the user
+ <dd>This is called when an instance the App Widget is created for the first
+time. For example, if the user
adds two instances of your App Widget, this is only called the first time.
- If you need to open a new database or perform other setup that only needs to occur once
+ If you need to open a new database or perform other setup that only needs to
+occur once
for all App Widget instances, then this is a good place to do it.</dd>
<dt>{@link android.appwidget.AppWidgetProvider#onDisabled(Context)}</dt>
- <dd>This is called when the last instance of your App Widget is deleted from the App Widget host.
+ <dd>This is called when the last instance of your App Widget is deleted from
+the App Widget host.
This is where you should clean up any work done in
{@link android.appwidget.AppWidgetProvider#onEnabled(Context)},
such as delete a temporary database.</dd>
<dt>{@link android.appwidget.AppWidgetProvider#onReceive(Context,Intent)}</dt>
- <dd>This is called for every broadcast and before each of the above callback methods.
- You normally don't need to implement this method because the default AppWidgetProvider
+ <dd>This is called for every broadcast and before each of the above callback
+methods.
+ You normally don't need to implement this method because the default
+AppWidgetProvider
implementation filters all App Widget broadcasts and calls the above
methods as appropriate.</dd>
</dl>
-<p class="warning"><strong>Note:</strong> In Android 1.5, there is a known issue in which the
-<code>onDeleted()</code> method will not be called when it should be. To work around this issue,
-you can implement {@link android.appwidget.AppWidgetProvider#onReceive(Context,Intent)
+<p class="warning"><strong>Note:</strong> In Android 1.5, there is a known issue
+in which the
+<code>onDeleted()</code> method will not be called when it should be. To work
+around this issue,
+you can implement {@link
+android.appwidget.AppWidgetProvider#onReceive(Context,Intent)
onReceive()} as described in this
-<a href="http://groups.google.com/group/android-developers/msg/e405ca19df2170e2">Group post</a>
+<a
+href="http://groups.google.com/group/android-developers/msg/e405ca19df2170e2">
+Group post</a>
to receive the <code>onDeleted()</code> callback.
</p>
<p>The most important AppWidgetProvider callback is
-{@link android.appwidget.AppWidgetProvider#onUpdate(Context,AppWidgetManager,int[])
-onUpdated()} because it is called when each App Widget is added to a host (unless you use
-a configuration Activity). If your App Widget accepts any
-user interaction events, then you need to register the event handlers in this callback.
-If your App Widget doesn't create temporary
-files or databases, or perform other work that requires clean-up, then
-{@link android.appwidget.AppWidgetProvider#onUpdate(Context,AppWidgetManager,int[])
-onUpdated()} may be the only callback method you need to define. For example, if you want an App Widget
-with a button that launches an Activity when clicked, you could use the following
+{@link android.appwidget.AppWidgetProvider#onUpdate(android.content.Context, android.appwidget.AppWidgetManager, int[]) onUpdate()}
+because it is called when
+each App Widget is added to a host (unless you use a configuration Activity). If
+your App Widget accepts any user interaction events, then you need to register
+the event handlers in this callback. If your App Widget doesn't create temporary
+files or databases, or perform other work that requires clean-up, then
+{@link android.appwidget.AppWidgetProvider#onUpdate(android.content.Context, android.appwidget.AppWidgetManager, int[]) onUpdate()}
+may be the only callback
+method you need to define. For example, if you want an App Widget with a button
+that launches an Activity when clicked, you could use the following
implementation of AppWidgetProvider:</p>
<pre>
@@ -306,11 +431,12 @@
Intent intent = new Intent(context, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
- // Get the layout for the App Widget and attach an on-click listener to the button
+ // Get the layout for the App Widget and attach an on-click listener
+ // to the button
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider_layout);
views.setOnClickPendingIntent(R.id.button, pendingIntent);
- // Tell the AppWidgetManager to perform an update on the current App Widget
+ // Tell the AppWidgetManager to perform an update on the current app widget
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
@@ -318,43 +444,51 @@
</pre>
<p>This AppWidgetProvider defines only the
-{@link android.appwidget.AppWidgetProvider#onUpdate(Context,AppWidgetManager,int[])
-onUpdated()} method for the purpose
-of defining a {@link android.app.PendingIntent} that launches an {@link android.app.Activity}
-and attaching it to the App Widget's button
-with {@link android.widget.RemoteViews#setOnClickPendingIntent(int,PendingIntent)}.
-Notice that it includes a loop that iterates through each entry in <code>appWidgetIds</code>, which
-is an array of IDs that identify each App Widget created by this provider.
-In this way, if the user creates more than one instance of the App Widget, then they are
-all updated simultaneously. However, only one <code>updatePeriodMillis</code> schedule will be
-managed for all instances of the App Widget. For example, if the update schedule is defined
-to be every two hours, and a second instance
-of the App Widget is added one hour after the first one, then they will both be updated
-on the period defined by the first one and the second update period will be ignored
-(they'll both be updated every two hours, not every hour).</p>
+{@link
+android.appwidget.AppWidgetProvider#onUpdate(android.content.Context, android.appwidget.AppWidgetManager, int[]) onUpdate()}
+method for the purpose of
+defining a {@link android.app.PendingIntent} that launches an {@link
+android.app.Activity} and attaching it to the App Widget's button with {@link
+android.widget.RemoteViews#setOnClickPendingIntent(int,PendingIntent)}. Notice
+that it includes a loop that iterates through each entry in
+<code>appWidgetIds</code>, which is an array of IDs that identify each App
+Widget created by this provider. In this way, if the user creates more than one
+instance of the App Widget, then they are all updated simultaneously. However,
+only one <code>updatePeriodMillis</code> schedule will be managed for all
+instances of the App Widget. For example, if the update schedule is defined to
+be every two hours, and a second instance of the App Widget is added one hour
+after the first one, then they will both be updated on the period defined by the
+first one and the second update period will be ignored (they'll both be updated
+every two hours, not every hour).</p>
-<p class="note"><strong>Note:</strong> Because {@link android.appwidget.AppWidgetProvider} is an
-extension of {@link android.content.BroadcastReceiver}, your process is not guaranteed to keep
-running after the callback methods return (see {@link android.content.BroadcastReceiver} for
-information about the broadcast lifecycle). If your App Widget setup process can take several
-seconds (perhaps while performing web requests) and you require that your process continues,
-consider starting a {@link android.app.Service}
-in the {@link android.appwidget.AppWidgetProvider#onUpdate(Context,AppWidgetManager,int[])
-onUpdated()} method. From within the Service, you can perform your own updates to the App Widget
-without worrying about the AppWidgetProvider closing down due to an
-<a href="{@docRoot}guide/practices/design/responsiveness.html">Application Not Responding</a>
-(ANR) error. See the
-<a href="http://code.google.com/p/wiktionary-android/source/browse/trunk/Wiktionary/src/com/example/android/wiktionary/WordWidget.java">Wiktionary
-sample's AppWidgetProvider</a> for an example of an App Widget running a {@link android.app.Service}.</p>
+<p class="note"><strong>Note:</strong> Because {@link
+android.appwidget.AppWidgetProvider} is an extension of {@link
+android.content.BroadcastReceiver}, your process is not guaranteed to keep
+running after the callback methods return (see {@link
+android.content.BroadcastReceiver} for information about the broadcast
+lifecycle). If your App Widget setup process can take several seconds (perhaps
+while performing web requests) and you require that your process continues,
+consider starting a {@link android.app.Service} in the
+{@link android.appwidget.AppWidgetProvider#onUpdate(Context,AppWidgetManager,int[]) onUpdate()}
+method. From within the Service, you can perform your own updates
+to the App Widget without worrying about the AppWidgetProvider closing down due
+to an <a href="{@docRoot}guide/practices/design/responsiveness.html">Application
+Not Responding</a> (ANR) error. See the <a
+href="http://code.google.com/p/wiktionary-android/source/browse/trunk/Wiktionary
+/src/com/example/android/wiktionary/WordWidget.java">Wiktionary sample's
+AppWidgetProvider</a> for an example of an App Widget running a {@link
+android.app.Service}.</p>
<p>Also see the <a
-href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/appwidget/ExampleAppWidgetProvider.html">
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/
+appwidget/ExampleAppWidgetProvider.html">
ExampleAppWidgetProvider.java</a> sample class.</p>
<h3 id="ProviderBroadcasts">Receiving App Widget broadcast Intents</h3>
-<p>{@link android.appwidget.AppWidgetProvider} is just a convenience class. If you would like
+<p>{@link android.appwidget.AppWidgetProvider} is just a convenience class. If
+you would like
to receive the App Widget broadcasts directly, you can implement your own
{@link android.content.BroadcastReceiver} or override the
{@link android.appwidget.AppWidgetProvider#onReceive(Context,Intent)} callback.
@@ -370,28 +504,36 @@
<h2 id="Configuring">Creating an App Widget Configuration Activity</h2>
-<p>If you would like the user to configure settings when he or she adds a new App Widget,
-you can create an App Widget configuration Activity. This {@link android.app.Activity}
-will be automatically launched by the App Widget host and allows the user to configure
-available settings for the App Widget at create-time, such as the App Widget color, size,
+<p>If you would like the user to configure settings when he or she adds a new
+App Widget,
+you can create an App Widget configuration Activity. This {@link
+android.app.Activity}
+will be automatically launched by the App Widget host and allows the user to
+configure
+available settings for the App Widget at create-time, such as the App Widget
+color, size,
update period or other functionality settings.</p>
-<p>The configuration Activity should be declared as a normal Activity in the Android manifest file.
+<p>The configuration Activity should be declared as a normal Activity in the
+Android manifest file.
However, it will be launched by the App Widget host with the {@link
-android.appwidget.AppWidgetManager#ACTION_APPWIDGET_CONFIGURE ACTION_APPWIDGET_CONFIGURE} action,
+android.appwidget.AppWidgetManager#ACTION_APPWIDGET_CONFIGURE
+ACTION_APPWIDGET_CONFIGURE} action,
so the Activity needs to accept this Intent. For example:</p>
<pre>
<activity android:name=".ExampleAppWidgetConfigure">
<intent-filter>
- <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
+ <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
</intent-filter>
</activity>
</pre>
-<p>Also, the Activity must be declared in the AppWidgetProviderInfo XML file, with the
+<p>Also, the Activity must be declared in the AppWidgetProviderInfo XML file,
+with the
<code>android:configure</code> attribute (see <a href="#MetaData">Adding
-the AppWidgetProviderInfo Metadata</a> above). For example, the configuration Activity
+the AppWidgetProviderInfo Metadata</a> above). For example, the configuration
+Activity
can be declared like this:</p>
<pre>
@@ -402,32 +544,45 @@
</appwidget-provider>
</pre>
-<p>Notice that the Activity is declared with a fully-qualified namespace, because
+<p>Notice that the Activity is declared with a fully-qualified namespace,
+because
it will be referenced from outside your package scope.</p>
-<p>That's all you need to get started with a configuration Activity. Now all you need is the actual
-Activity. There are, however, two important things to remember when you implement the Activity:</p>
+<p>That's all you need to get started with a configuration Activity. Now all you
+need is the actual
+Activity. There are, however, two important things to remember when you
+implement the Activity:</p>
<ul>
- <li>The App Widget host calls the configuration Activity and the configuration Activity should always
+ <li>The App Widget host calls the configuration Activity and the configuration
+Activity should always
return a result. The result should include the App Widget ID
- passed by the Intent that launched the Activity (saved in the Intent extras as
+ passed by the Intent that launched the Activity (saved in the Intent extras
+as
{@link android.appwidget.AppWidgetManager#EXTRA_APPWIDGET_ID}).</li>
- <li>The {@link android.appwidget.AppWidgetProvider#onUpdate(Context,AppWidgetManager,int[])
- onUpdate()} method <strong>will not be called</strong> when the App Widget is created
- (the system will not send the ACTION_APPWIDGET_UPDATE broadcast when a configuration Activity
- is launched). It is the responsibility of the configuration Activity to request an update from the
+ <li>The
+ {@link android.appwidget.AppWidgetProvider#onUpdate(Context,AppWidgetManager,int[]) onUpdate()}
+ method <strong>will not be called</strong> when the App Widget
+is created
+ (the system will not send the ACTION_APPWIDGET_UPDATE broadcast when a
+configuration Activity
+ is launched). It is the responsibility of the configuration Activity to
+request an update from the
AppWidgetManager when the App Widget is first created. However,
- {@link android.appwidget.AppWidgetProvider#onUpdate(Context,AppWidgetManager,int[])
- onUpdate()} will be called for subsequent updates—it is only skipped the first time.</li>
+{@link android.appwidget.AppWidgetProvider#onUpdate(Context,AppWidgetManager,int[]) onUpdate()}
+ will be called for subsequent updates—it is only skipped
+the first time.</li>
</ul>
-<p>See the code snippets in the following section for an example of how to return a result
+<p>See the code snippets in the following section for an example of how to
+return a result
from the configuration and update the App Widget.</p>
-<h3 id="UpdatingFromTheConfiguration">Updating the App Widget from the configuration Activity</h3>
+<h3 id="UpdatingFromTheConfiguration">Updating the App Widget from the
+configuration Activity</h3>
-<p>When an App Widget uses a configuration Activity, it is the responsibility of the Activity
+<p>When an App Widget uses a configuration Activity, it is the responsibility of
+the Activity
to update the App Widget when configuration is complete.
You can do so by requesting an update directly from the
{@link android.appwidget.AppWidgetManager}.</p>
@@ -448,20 +603,24 @@
</pre>
</li>
<li>Perform your App Widget configuration.</li>
- <li>When the configuration is complete, get an instance of the AppWidgetManager by calling
+ <li>When the configuration is complete, get an instance of the
+AppWidgetManager by calling
{@link android.appwidget.AppWidgetManager#getInstance(Context)}:
<pre>
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
</pre>
</li>
- <li>Update the App Widget with a {@link android.widget.RemoteViews} layout by calling
+ <li>Update the App Widget with a {@link android.widget.RemoteViews} layout by
+calling
{@link android.appwidget.AppWidgetManager#updateAppWidget(int,RemoteViews)}:
<pre>
-RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.example_appwidget);
+RemoteViews views = new RemoteViews(context.getPackageName(),
+R.layout.example_appwidget);
appWidgetManager.updateAppWidget(mAppWidgetId, views);
</pre>
</li>
- <li>Finally, create the return Intent, set it with the Activity result, and finish the Activity:</li>
+ <li>Finally, create the return Intent, set it with the Activity result, and
+finish the Activity:</li>
<pre>
Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
@@ -471,14 +630,657 @@
</li>
</ol>
-<p class="note"><strong>Tip:</strong> When your configuration Activity first opens, set
-the Activity result to RESULT_CANCELED. This way, if the user backs-out of the Activity before
-reaching the end, the App Widget host is notified that the configuration was cancelled and the
+<p class="note"><strong>Tip:</strong> When your configuration Activity first
+opens, set
+the Activity result to RESULT_CANCELED. This way, if the user backs-out of the
+Activity before
+reaching the end, the App Widget host is notified that the configuration was
+cancelled and the
App Widget will not be added.</p>
<p>See the <a
-href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/appwidget/ExampleAppWidgetConfigure.html">
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/
+appwidget/ExampleAppWidgetConfigure.html">
ExampleAppWidgetConfigure.java</a> sample class in ApiDemos for an example.</p>
+<h2 id="preview">Setting a Preview Image</h2>
+
+<p>Android 3.0 introduces the {@link
+
+
+android.appwidget.AppWidgetProviderInfo#previewImage} field, which specifies a
+preview of what the app widget looks like. This preview is shown to the user from the
+widget picker. If this field is not supplied, the app widget's icon is used for
+the preview.</p>
+
+<p>This is how you specify this setting in XML:</p>
+
+<pre><appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
+ ...
+ android:previewImage="@drawable/preview">
+</appwidget-provider></pre>
+
+<p>To help create a preview image for your app widget (to specify in the {@link
+android.appwidget.AppWidgetProviderInfo#previewImage} field), the Android
+emulator includes an application called "Widget Preview." To create a
+preview image, launch this application, select the app widget for your
+application and set it up how you'd like your preview image to appear, then save
+it and place it in your application's drawable resources.</p>
+
+<h2 id="collections">Using App Widgets with Collections</h2>
+
+<p>Android 3.0 introduces App Widgets with collections. These kinds of App
+Widgets use the {@link android.widget.RemoteViewsService} to display collections
+that are backed by remote data, such as from a <a
+href="{@docRoot}guide/topics/providers/content-providers.html">content
+provider</a>. The data provided by the {@link android.widget.RemoteViewsService}
+is presented in the App Widget using one of the following view types, which
+we’ll refer to as “collection views:”</p>
+
+<dl>
+ <dt>{@link android.widget.ListView}</dt>
+ <dd>A view that shows items in a
+vertically scrolling
+list. For an example, see the Gmail app widget. </dd>
+<dt>{@link android.widget.GridView}</dt>
+<dd>A view that shows items in
+two-dimensional scrolling grid. For an example, see the Bookmarks app
+widget.</dd>
+<dt>{@link android.widget.StackView}</dt>
+<dd>A
+stacked card view (kind of like a rolodex), where the user can flick the front
+card up/down to see the previous/next card, respectively. Examples include
+the YouTube and Books app widgets. </dd>
+<dt>{@link android.widget.AdapterViewFlipper}</dt>
+<dd>An adapter-backed simple
+{@link
+android.widget.ViewAnimator} that animates between two or more views. Only one
+child is shown at a time. </dd>
+</dl>
+
+<p>As stated above, these collection views display collections backed by remote
+data. This means that they use an {@link android.widget.Adapter} to bind their
+user interface to their data. An {@link android.widget.Adapter} binds individual
+items from a set of data into individual {@link android.view.View} objects.
+Because these collection views are backed by adapters, the Android framework
+must include extra architecture to support their use in app widgets. In the
+context of an app widget, the {@link android.widget.Adapter} is replaced by a
+{@link android.widget.RemoteViewsService.RemoteViewsFactory RemoteViewsFactory},
+which is simply a thin wrapper around the {@link android.widget.Adapter}
+interface.
+ When
+requested for a specific item in the collection, the {@link
+android.widget.RemoteViewsService.RemoteViewsFactory RemoteViewsFactory} creates
+and returns the item for the collection as a {@link android.widget.RemoteViews}
+object.
+In order to include a collection view in your app widget, you
+must implement {@link android.widget.RemoteViewsService} and {@link
+android.widget.RemoteViewsService.RemoteViewsFactory RemoteViewsFactory}.</p>
+
+<p> {@link android.widget.RemoteViewsService} is a service that allows a remote
+adapter to request {@link
+android.widget.RemoteViews} objects. {@link
+android.widget.RemoteViewsService.RemoteViewsFactory RemoteViewsFactory} is an
+interface for an adapter between a collection view (such as {@link
+android.widget.ListView}, {@link android.widget.GridView}, and so on) and the
+underlying data for that view. From the <a
+href="{@docRoot}resources/samples/StackWidget/index.html">StackView Widget
+sample</a>, here is an example of the boilerplate code you use to implement
+this service and interface:
+</p>
+
+<pre>
+public class StackWidgetService extends RemoteViewsService {
+ @Override
+ public RemoteViewsFactory onGetViewFactory(Intent intent) {
+ return new StackRemoteViewsFactory(this.getApplicationContext(), intent);
+ }
+}
+
+class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
+
+//... include adapter-like methods here. See the StackView Widget sample.
+
+}
+</pre>
+
+<h3 id="collection_sample">Sample application</h3>
+
+<p>The code excerpts in this section are drawn from the <a
+href="{@docRoot}resources/samples/StackWidget/index.html">StackView Widget
+sample</a>:</p>
+
+<p>
+<img src="{@docRoot}resources/samples/images/StackWidget.png" alt="StackView
+Widget" />
+</p>
+
+<p>This sample consists of a stack of 10 views, which display the values
+<code>"0!"</code> through <code>"9!"</code> The sample
+app widget has these primary behaviors:</p>
+
+<ul>
+
+ <li>The user can vertically fling the top view in the
+app widget to display the next or previous view. This is a built-in StackView
+behavior.</li>
+
+ <li>Without any user interaction, the app widget automatically advances
+through
+its views in sequence, like a slide show. This is due to the setting
+<code>android:autoAdvanceViewId="@id/stack_view"</code> in the
+<code>res/xml/stackwidgetinfo.xml</code> file. This setting applies to the view
+ID,
+which in this case is the view ID of the stack view.</li>
+
+ <li>If the user touches the top view, the app widget displays the {@link
+android.widget.Toast} message "Touched view <em>n</em>," where
+<em>n</em> is the index (position) of the touched view. For more discussion of
+how this is implemented, see
+<a href="#behavior">Adding behavior to individual items</a>.</li>
+
+</ul>
+<h3 id="implementing_collections">Implementing app widgets with collections</h3>
+
+<p>To implement an App Widget with collections, you follow the same basic steps
+you would use to implement any app widget. The following sections describe the
+additional steps you need to perform to implement an App Widget with
+collections.</p>
+
+<h4>Manifest for app widgets with collections</h4>
+
+<p> In addition to the requirements listed in <a href="#Manifest">Declaring an
+App Widget in the Manifest</a>, to make it possible for App Widgets with
+collections to bind to your {@link android.widget.RemoteViewsService}, you must
+declare the service in your manifest file with the permission {@link
+android.Manifest.permission#BIND_REMOTEVIEWS}. This prevents other applications
+from freely accessing your app widget's data. For example, when creating an App
+Widget that uses {@link android.widget.RemoteViewsService} to populate a
+collection view, the manifest entry may look like this:</p>
+
+<pre><service android:name="MyWidgetService"
+...
+android:permission="android.permission.BIND_REMOTEVIEWS" /></pre>
+
+<p>The line <code>android:name="MyWidgetService"</code>
+refers to your subclass of {@link android.widget.RemoteViewsService}. </p>
+
+<h4>Layout for app widgets with collections</h4>
+
+<p>The main requirement for your app widget layout XML file is that it
+include one of the collection views: {@link android.widget.ListView},
+{@link android.widget.GridView}, {@link android.widget.StackView}, or
+{@link android.widget.AdapterViewFlipper}. Here is the
+<code>widget_layout.xml</code> for
+the <a href="{@docRoot}resources/samples/StackWidget/index.html">StackView
+Widget sample</a>:</p>
+
+<pre><?xml version="1.0" encoding="utf-8"?>
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <StackView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/stack_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:loopViews="true" />
+ <TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/empty_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:background="@drawable/widget_item_background"
+ android:textColor="#ffffff"
+ android:textStyle="bold"
+ android:text="@string/empty_view_text"
+ android:textSize="20sp" />
+</FrameLayout></pre>
+
+<p> Note that empty views must be siblings of the collection view for which the
+empty view represents empty state. </p>
+
+<p>In addition to the layout file for your entire app widget, you must create
+another layout file that defines the layout for each item in the collection (for
+example, a layout for each book in a collection of books). For example, the <a
+href="{@docRoot}resources/samples/StackWidget/index.html">StackView Widget
+sample</a> only has one layout file, <code>widget_item.xml</code>, since all
+items use the same layout. But the <a
+href="{@docRoot}resources/samples/WeatherListWidget/index.html">
+WeatherListWidget sample</a> has two layout files:
+<code>dark_widget_item.xml</code> and <code>light_widget_item.xml</code>.</p>
+
+
+
+<h4 id="AppWidgetProvider-collections">AppWidgetProvider class for app widgets with collections</h4>
+
+<p>As with a regular app widget, the bulk of your code in your {@link
+android.appwidget.AppWidgetProvider} subclass typically goes in {@link
+android.appwidget.AppWidgetProvider#onUpdate(android.content.Context,
+android.appwidget.AppWidgetManager, int[]) onUpdate()}. The major difference in
+your implementation for {@link
+android.appwidget.AppWidgetProvider#onUpdate(android.content.Context,
+android.appwidget.AppWidgetManager, int[]) onUpdate()} when creating an app
+widget with collections is that you must call {@link
+android.widget.RemoteViews#setRemoteAdapter setRemoteAdapter()}. This tells the
+collection view where to get its data. The {@link
+android.widget.RemoteViewsService} can then return your implementation of {@link
+android.widget.RemoteViewsService.RemoteViewsFactory RemoteViewsFactory}, and
+the widget can serve up the appropriate data. When you call this method, you
+must pass an intent that points to your implementation of {@link
+android.widget.RemoteViewsService} and the App Widget ID that specifies the app
+widget to update.</p>
+
+
+<p>For example, here's how the StackView Widget sample implements the {@link
+android.appwidget.AppWidgetProvider#onUpdate(android.content.Context,
+android.appwidget.AppWidgetManager, int[]) onUpdate()} callback method to set
+the {@link
+android.widget.RemoteViewsService} as the remote adapter for the app widget
+collection:</p>
+
+<pre>public void onUpdate(Context context, AppWidgetManager appWidgetManager,
+int[] appWidgetIds) {
+ // update each of the app widgets with the remote adapter
+ for (int i = 0; i < appWidgetIds.length; ++i) {
+
+ // Set up the intent that starts the StackViewService, which will
+ // provide the views for this collection.
+ Intent intent = new Intent(context, StackWidgetService.class);
+ // Add the app widget ID to the intent extras.
+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
+ intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
+ // Instantiate the RemoteViews object for the App Widget layout.
+ RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
+ // Set up the RemoteViews object to use a RemoteViews adapter.
+ // This adapter connects
+ // to a RemoteViewsService through the specified intent.
+ // This is how you populate the data.
+ rv.setRemoteAdapter(appWidgetIds[i], R.id.stack_view, intent);
+
+ // The empty view is displayed when the collection has no items.
+ // It should be in the same layout used to instantiate the RemoteViews
+ // object above.
+ rv.setEmptyView(R.id.stack_view, R.id.empty_view);
+
+ //
+ // Do additional processing specific to this app widget...
+ //
+
+ appWidgetManager.updateAppWidget(appWidgetIds[i], rv);
+ }
+ super.onUpdate(context, appWidgetManager, appWidgetIds);
+}</pre>
+
+<h4>RemoteViewsService class</h4>
+
+<div class="sidebox-wrapper">
+<div class="sidebox">
+<h3>Persisting data</h3>
+ <p>You can’t rely on a single instance of your service, or any data it
+contains, to persist. You should therefore not store any data in your {@link
+android.widget.RemoteViewsService} (unless it is static). If you want your
+app widget’s data to persist, the best approach is to use a {@link
+android.content.ContentProvider} whose data persists beyond the process
+lifecycle.</p> </div>
+</div>
+
+<p>As described above, your {@link android.widget.RemoteViewsService} subclass
+provides the {@link android.widget.RemoteViewsService.RemoteViewsFactory
+RemoteViewsFactory} used to populate the remote collection view.</p>
+
+<p>Specifically, you need to
+perform these steps:</p>
+
+<ol>
+ <li>Subclass {@link android.widget.RemoteViewsService}. {@link
+android.widget.RemoteViewsService} is the service through which
+a remote adapter can request {@link android.widget.RemoteViews}. </li>
+
+ <li>In your {@link android.widget.RemoteViewsService} subclass, include a
+class that implements the {@link
+android.widget.RemoteViewsService.RemoteViewsFactory RemoteViewsFactory}
+interface. {@link android.widget.RemoteViewsService.RemoteViewsFactory
+RemoteViewsFactory} is an interface for an adapter between a remote collection
+view (such as {@link android.widget.ListView}, {@link android.widget.GridView},
+and so on) and the underlying data for that view. Your implementation is
+responsible for making a {@link android.widget.RemoteViews} object for each
+item in the data set. This interface is a thin wrapper around {@link
+android.widget.Adapter}.</li>
+</ol>
+
+<p>The primary contents of the {@link android.widget.RemoteViewsService}
+implementation is its {@link
+android.widget.RemoteViewsService.RemoteViewsFactory RemoteViewsFactory},
+described below.</p>
+
+<h4>RemoteViewsFactory interface</h4>
+
+<p>Your custom class that implements the {@link
+android.widget.RemoteViewsService.RemoteViewsFactory RemoteViewsFactory}
+interface provides the app widget with the data for the items in its collection.
+To
+do this, it combines your app widget item XML layout file with a source of data.
+This source of data could be anything from a database to a simple array. In the
+<a href="{@docRoot}resources/samples/StackWidget/index.html">StackView Widget
+sample</a>, the data source is an array of <code>WidgetItems</code>. The {@link
+android.widget.RemoteViewsService.RemoteViewsFactory RemoteViewsFactory}
+functions as an adapter to glue the data to the remote collection view.</p>
+
+<p>The two most important methods you need to implement for your
+
+{@link android.widget.RemoteViewsService.RemoteViewsFactory RemoteViewsFactory}
+subclass are
+{@link android.widget.RemoteViewsService.RemoteViewsFactory#onCreate()
+onCreate()} and
+{@link android.widget.RemoteViewsService.RemoteViewsFactory#getViewAt(int)
+getViewAt()}
+.</p>
+
+<p>The system calls {@link
+android.widget.RemoteViewsService.RemoteViewsFactory#onCreate() onCreate()} when
+creating your factory for the first time. This is where you set up any
+connections and/or cursors to your data source. For example, the <a
+href="{@docRoot}resources/samples/StackWidget/index.html">StackView Widget
+sample</a> uses {@link
+android.widget.RemoteViewsService.RemoteViewsFactory#onCreate() onCreate()} to
+initialize an array of <code>WidgetItem</code> objects. When your app widget is
+active, the system accesses these objects using their index position in the
+array and the text they contain is displayed </p>
+
+<p>Here is an excerpt from the the <a
+href="{@docRoot}resources/samples/StackWidget/index.html">StackView Widget</a>
+sample's
+{@link android.widget.RemoteViewsService.RemoteViewsFactory
+RemoteViewsFactory} implementation that shows portions of the {@link
+android.widget.RemoteViewsService.RemoteViewsFactory#onCreate() onCreate()}
+method:</p>
+
+<pre>class StackRemoteViewsFactory implements
+RemoteViewsService.RemoteViewsFactory {
+ private static final int mCount = 10;
+ private List<WidgetItem> mWidgetItems = new ArrayList<WidgetItem>();
+ private Context mContext;
+ private int mAppWidgetId;
+
+ public StackRemoteViewsFactory(Context context, Intent intent) {
+ mContext = context;
+ mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
+ AppWidgetManager.INVALID_APPWIDGET_ID);
+ }
+
+ public void onCreate() {
+ // In onCreate() you setup any connections / cursors to your data source. Heavy lifting,
+ // for example downloading or creating content etc, should be deferred to onDataSetChanged()
+ // or getViewAt(). Taking more than 20 seconds in this call will result in an ANR.
+ for (int i = 0; i < mCount; i++) {
+ mWidgetItems.add(new WidgetItem(i + "!"));
+ }
+ ...
+ }
+...</pre>
+
+<p>The {@link android.widget.RemoteViewsService.RemoteViewsFactory
+RemoteViewsFactory} method {@link
+android.widget.RemoteViewsService.RemoteViewsFactory#getViewAt(int) getViewAt()}
+returns a {@link android.widget.RemoteViews} object corresponding to the data at
+the specified <code>position</code> in the data set. Here is an excerpt from
+the <a
+href="http://developer.android.com/resources/samples/StackWidget/index.html">
+StackView Widget</a> sample's {@link
+android.widget.RemoteViewsService.RemoteViewsFactory RemoteViewsFactory}
+implementation:</p>
+
+<pre>public RemoteViews getViewAt(int position) {
+
+ // Construct a remote views item based on the app widget item XML file,
+ // and set the text based on the position.
+ RemoteViews rv = new RemoteViews(mContext.getPackageName(), R.layout.widget_item);
+ rv.setTextViewText(R.id.widget_item, mWidgetItems.get(position).text);
+
+ ...
+ // Return the remote views object.
+ return rv;
+}</pre>
+
+<h4 id="behavior">Adding behavior to individual items</h4>
+
+<p>The above sections show you how to bind your data to your app widget
+collection. But what if you want to add dynamic behavior to the individual items
+in your collection view?</p>
+
+<p> As described in <a href="#AppWidgetProvider">Using the AppWidgetProvider
+Class</a>, you normally use {@link
+android.widget.RemoteViews#setOnClickPendingIntent(int,
+android.app.PendingIntent) setOnClickPendingIntent()} to set an object's click
+behavior—such as to cause a button to launch an {@link
+android.app.Activity}. But this approach is not allowed for child views in an
+individual collection item (to clarify, you could use {@link
+android.widget.RemoteViews#setOnClickPendingIntent(int,
+android.app.PendingIntent) setOnClickPendingIntent()} to set up a global button
+in the Gmail app widget that launches the app, for example, but not on the
+individual list items). Instead, to add click behavior to individual items in a
+collection, you use {@link
+android.widget.RemoteViews#setOnClickFillInIntent(int, android.content.Intent)
+setOnClickFillInIntent()}. This entails setting up up a pending intent template
+for your collection view, and then setting a fill-in intent on each item in the
+collection via your {@link android.widget.RemoteViewsService.RemoteViewsFactory
+RemoteViewsFactory}.</p>
+<p>This section uses the <a
+href="{@docRoot}resources/samples/StackWidget/index.html">StackView Widget
+sample</a> to describe how to add behavior to individual items. In the <a
+href="{@docRoot}resources/samples/StackWidget/index.html">StackView Widget
+sample</a>, if the user touches the top view, the app widget displays the {@link
+android.widget.Toast} message "Touched view <em>n</em>," where
+<em>n</em> is the index (position) of the touched view. This is how it
+works:</p>
+
+<ul>
+ <li>The <code>StackWidgetProvider</code> (an {@link
+android.appwidget.AppWidgetProvider} subclass) creates a pending intent that has
+a custom action called <code>TOAST_ACTION</code>.</li>
+ <li>When the user touches a view, the intent is fired and it broadcasts
+<code>TOAST_ACTION</code>.</li>
+
+ <li>This broadcast is intercepted by the <code>StackWidgetProvider</code>'s
+{@link android.appwidget.AppWidgetProvider#onReceive(android.content.Context,
+android.content.Intent) onReceive()} method, and the app widget displays the
+{@link
+android.widget.Toast} message for the touched view. The data for the collection
+items is provided by the {@link
+android.widget.RemoteViewsService.RemoteViewsFactory RemoteViewsFactory}, via
+the {@link android.widget.RemoteViewsService}.</li>
+</ul>
+
+<p class="note"><strong>Note:</strong> The <a
+href="{@docRoot}resources/samples/StackWidget/index.html">StackView Widget
+sample</a> uses a broadcast, but typically an app widget would simply launch an
+activity in a scenario like this one.</p>
+
+<h5>Setting up the pending intent template</h5>
+
+<p>The <code>StackWidgetProvider</code> ({@link
+android.appwidget.AppWidgetProvider} subclass) sets up a pending intent.
+Individuals items of a collection cannot set up their own pending intents.
+Instead, the collection as a whole sets up a pending intent template, and the
+individual items set a fill-in intent to create unique behavior on an
+item-by-item
+basis.</p>
+
+<p>This class also receives the broadcast that is sent when the user touches a
+view. It processes this event in its {@link
+android.appwidget.AppWidgetProvider#onReceive(android.content.Context,
+android.content.Intent) onReceive()} method. If the intent's action is
+<code>TOAST_ACTION</code>, the app widget displays a {@link
+android.widget.Toast}
+message for the current view.</p>
+
+<pre>public class StackWidgetProvider extends AppWidgetProvider {
+ public static final String TOAST_ACTION = "com.example.android.stackwidget.TOAST_ACTION";
+ public static final String EXTRA_ITEM = "com.example.android.stackwidget.EXTRA_ITEM";
+
+ ...
+
+ // Called when the BroadcastReceiver receives an Intent broadcast.
+ // Checks to see whether the intent's action is TOAST_ACTION. If it is, the app widget
+ // displays a Toast message for the current item.
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ AppWidgetManager mgr = AppWidgetManager.getInstance(context);
+ if (intent.getAction().equals(TOAST_ACTION)) {
+ int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
+ AppWidgetManager.INVALID_APPWIDGET_ID);
+ int viewIndex = intent.getIntExtra(EXTRA_ITEM, 0);
+ Toast.makeText(context, "Touched view " + viewIndex, Toast.LENGTH_SHORT).show();
+ }
+ super.onReceive(context, intent);
+ }
+
+ @Override
+ public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
+ // update each of the app widgets with the remote adapter
+ for (int i = 0; i < appWidgetIds.length; ++i) {
+
+ // Sets up the intent that points to the StackViewService that will
+ // provide the views for this collection.
+ Intent intent = new Intent(context, StackWidgetService.class);
+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
+ // When intents are compared, the extras are ignored, so we need to embed the extras
+ // into the data so that the extras will not be ignored.
+ intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
+ RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
+ rv.setRemoteAdapter(appWidgetIds[i], R.id.stack_view, intent);
+
+ // The empty view is displayed when the collection has no items. It should be a sibling
+ // of the collection view.
+ rv.setEmptyView(R.id.stack_view, R.id.empty_view);
+
+ // This section makes it possible for items to have individualized behavior.
+ // It does this by setting up a pending intent template. Individuals items of a collection
+ // cannot set up their own pending intents. Instead, the collection as a whole sets
+ // up a pending intent template, and the individual items set a fillInIntent
+ // to create unique behavior on an item-by-item basis.
+ Intent toastIntent = new Intent(context, StackWidgetProvider.class);
+ // Set the action for the intent.
+ // When the user touches a particular view, it will have the effect of
+ // broadcasting TOAST_ACTION.
+ toastIntent.setAction(StackWidgetProvider.TOAST_ACTION);
+ toastIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
+ intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
+ PendingIntent toastPendingIntent = PendingIntent.getBroadcast(context, 0, toastIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ rv.setPendingIntentTemplate(R.id.stack_view, toastPendingIntent);
+
+ appWidgetManager.updateAppWidget(appWidgetIds[i], rv);
+ }
+ super.onUpdate(context, appWidgetManager, appWidgetIds);
+ }
+}</pre>
+
+<h5><strong>Setting the fill-in Intent</strong></h5>
+
+<p>Your {@link android.widget.RemoteViewsService.RemoteViewsFactory
+RemoteViewsFactory} must set a fill-in intent on each item in the collection.
+This makes it possible to distinguish the individual on-click action of a given
+item. The fill-in intent is then combined with the {@link
+android.app.PendingIntent} template in order to determine the final intent that
+will be executed when the item is clicked. </p>
+
+<pre>
+public class StackWidgetService extends RemoteViewsService {
+ @Override
+ public RemoteViewsFactory onGetViewFactory(Intent intent) {
+ return new StackRemoteViewsFactory(this.getApplicationContext(), intent);
+ }
+}
+
+class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
+ private static final int mCount = 10;
+ private List<WidgetItem> mWidgetItems = new ArrayList<WidgetItem>();
+ private Context mContext;
+ private int mAppWidgetId;
+
+ public StackRemoteViewsFactory(Context context, Intent intent) {
+ mContext = context;
+ mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
+ AppWidgetManager.INVALID_APPWIDGET_ID);
+ }
+
+ // Initialize the data set.
+ public void onCreate() {
+ // In onCreate() you set up any connections / cursors to your data source. Heavy lifting,
+ // for example downloading or creating content etc, should be deferred to onDataSetChanged()
+ // or getViewAt(). Taking more than 20 seconds in this call will result in an ANR.
+ for (int i = 0; i < mCount; i++) {
+ mWidgetItems.add(new WidgetItem(i + "!"));
+ }
+ ...
+ }
+ ...
+
+ // Given the position (index) of a WidgetItem in the array, use the item's text value in
+ // combination with the app widget item XML file to construct a RemoteViews object.
+ public RemoteViews getViewAt(int position) {
+ // position will always range from 0 to getCount() - 1.
+
+ // Construct a RemoteViews item based on the app widget item XML file, and set the
+ // text based on the position.
+ RemoteViews rv = new RemoteViews(mContext.getPackageName(), R.layout.widget_item);
+ rv.setTextViewText(R.id.widget_item, mWidgetItems.get(position).text);
+
+ // Next, set a fill-intent, which will be used to fill in the pending intent template
+ // that is set on the collection view in StackWidgetProvider.
+ Bundle extras = new Bundle();
+ extras.putInt(StackWidgetProvider.EXTRA_ITEM, position);
+ Intent fillInIntent = new Intent();
+ fillInIntent.putExtras(extras);
+ // Make it possible to distinguish the individual on-click
+ // action of a given item
+ rv.setOnClickFillInIntent(R.id.widget_item, fillInIntent);
+
+ ...
+
+ // Return the RemoteViews object.
+ return rv;
+ }
+ ...
+ }</pre>
+
+<h3 id="fresh">Keeping Collection Data Fresh</h3>
+
+<p>The following figure illustrates the flow that occurs in an App Widget that
+uses
+collections when updates occur. It shows how the App Widget code interacts with
+the {@link android.widget.RemoteViewsService.RemoteViewsFactory
+RemoteViewsFactory}, and how you can trigger updates:</p>
+
+<img src="{@docRoot}images/appwidget_collections.png" alt="" />
+
+<p>One feature of App Widgets that use collections is the ability to provide
+users with up-to-date content. For example, consider the Android 3.0 Gmail
+app widget, which provides users with a snapshot of their inbox. To make this
+possible, you need to be able to trigger your {@link
+android.widget.RemoteViewsService.RemoteViewsFactory RemoteViewsFactory} and
+collection view to fetch and display new data. You achieve this with the {@link
+android.appwidget.AppWidgetManager} call {@link
+android.appwidget.AppWidgetManager#notifyAppWidgetViewDataChanged(int, int)
+notifyAppWidgetViewDataChanged()}. This call results in a callback to your
+<code>RemoteViewsFactory</code>’s {@link
+android.widget.RemoteViewsService.RemoteViewsFactory#onDataSetChanged()
+onDataSetChanged()} method, which gives you the opportunity to fetch any new
+data. Note that you can perform
+processing-intensive operations synchronously within the {@link
+android.widget.RemoteViewsService.RemoteViewsFactory#onDataSetChanged()
+onDataSetChanged()} callback. You are guaranteed that this call will be
+completed before the metadata or view data is fetched from the {@link
+android.widget.RemoteViewsService.RemoteViewsFactory RemoteViewsFactory}. In
+addition, you can perform processing-intensive operations within the {@link
+android.widget.RemoteViewsService.RemoteViewsFactory#getViewAt(int) getViewAt()}
+method. If this call takes a long time, the loading view (specified by the
+<code>RemoteViewsFactory</code>’s {@link
+android.widget.RemoteViewsService.RemoteViewsFactory#getLoadingView()} method)
+will be displayed in the corresponding position of the collection view until it
+returns.</p>
diff --git a/docs/html/guide/topics/manifest/manifest-element.jd b/docs/html/guide/topics/manifest/manifest-element.jd
index a8125b3..598e88f 100644
--- a/docs/html/guide/topics/manifest/manifest-element.jd
+++ b/docs/html/guide/topics/manifest/manifest-element.jd
@@ -141,9 +141,14 @@
</tr>
</table>
+<p class="caution"><strong>Caution:</strong> If your application uses the Android Market's Copy
+ Protection feature, it cannot be installed to a device's SD card. However, if you use Android
+ Market's <a href="{@docRoot}guide/publishing/licensing.html">Application Licensing</a> instead,
+ your application <em>can</em> be installed to internal or external storage, including SD cards.</p>
+
<p class="note"><strong>Note:</strong> By default, your application will be installed on the
-internal storage and cannot be installed on the external storage unless you define this attribute
-to be either "{@code auto}" or "{@code preferExternal}".</p>
+ internal storage and cannot be installed on the external storage unless you define this attribute
+ to be either "{@code auto}" or "{@code preferExternal}".</p>
<p>When an application is installed on the external storage:</p>
<ul>
diff --git a/docs/html/images/appwidget.png b/docs/html/images/appwidget.png
index b72b80b..ab6e3de 100644
--- a/docs/html/images/appwidget.png
+++ b/docs/html/images/appwidget.png
Binary files differ
diff --git a/docs/html/images/appwidget_collections.png b/docs/html/images/appwidget_collections.png
new file mode 100644
index 0000000..4bce4a7
--- /dev/null
+++ b/docs/html/images/appwidget_collections.png
Binary files differ
diff --git a/include/drm/drm_framework_common.h b/include/drm/drm_framework_common.h
index 454fc99..9057f98 100644
--- a/include/drm/drm_framework_common.h
+++ b/include/drm/drm_framework_common.h
@@ -30,14 +30,18 @@
* Error code for DRM Frameowrk
*/
enum {
- DRM_ERROR_BASE = -2000,
+ // The following constant values should be in sync with
+ // media/stagefright/MediaErrors.h
+ ERROR_BASE = -2000,
- DRM_ERROR_UNKNOWN = DRM_ERROR_BASE,
- DRM_ERROR_LICENSE_EXPIRED = DRM_ERROR_BASE - 1,
- DRM_ERROR_SESSION_NOT_OPENED = DRM_ERROR_BASE - 2,
- DRM_ERROR_DECRYPT_UNIT_NOT_INITIALIZED = DRM_ERROR_BASE - 3,
- DRM_ERROR_DECRYPT = DRM_ERROR_BASE - 4,
- DRM_ERROR_CANNOT_HANDLE = DRM_ERROR_BASE - 5,
+ DRM_ERROR_UNKNOWN = ERROR_BASE,
+ DRM_ERROR_NO_LICENSE = ERROR_BASE - 1,
+ DRM_ERROR_LICENSE_EXPIRED = ERROR_BASE - 2,
+ DRM_ERROR_SESSION_NOT_OPENED = ERROR_BASE - 3,
+ DRM_ERROR_DECRYPT_UNIT_NOT_INITIALIZED = ERROR_BASE - 4,
+ DRM_ERROR_DECRYPT = ERROR_BASE - 5,
+ DRM_ERROR_CANNOT_HANDLE = ERROR_BASE - 6,
+ DRM_ERROR_TAMPER_DETECTED = ERROR_BASE - 7,
DRM_NO_ERROR = NO_ERROR
};
diff --git a/include/media/stagefright/MediaErrors.h b/include/media/stagefright/MediaErrors.h
index 1a6d548..21d00b8 100644
--- a/include/media/stagefright/MediaErrors.h
+++ b/include/media/stagefright/MediaErrors.h
@@ -41,7 +41,18 @@
INFO_FORMAT_CHANGED = MEDIA_ERROR_BASE - 12,
INFO_DISCONTINUITY = MEDIA_ERROR_BASE - 13,
- ERROR_NO_LICENSE = MEDIA_ERROR_BASE - 14,
+ // The following constant values should be in sync with
+ // drm/drm_framework_common.h
+ DRM_ERROR_BASE = -2000,
+
+ ERROR_DRM_UNKNOWN = DRM_ERROR_BASE,
+ ERROR_DRM_NO_LICENSE = DRM_ERROR_BASE - 1,
+ ERROR_DRM_LICENSE_EXPIRED = DRM_ERROR_BASE - 2,
+ ERROR_DRM_SESSION_NOT_OPENED = DRM_ERROR_BASE - 3,
+ ERROR_DRM_DECRYPT_UNIT_NOT_INITIALIZED = DRM_ERROR_BASE - 4,
+ ERROR_DRM_DECRYPT = DRM_ERROR_BASE - 5,
+ ERROR_DRM_CANNOT_HANDLE = DRM_ERROR_BASE - 6,
+ ERROR_DRM_TAMPER_DETECTED = DRM_ERROR_BASE - 7,
// Heartbeat Error Codes
HEARTBEAT_ERROR_BASE = -3000,
diff --git a/media/java/android/mtp/MtpStorage.java b/media/java/android/mtp/MtpStorage.java
index 21a18ca..7932d34 100644
--- a/media/java/android/mtp/MtpStorage.java
+++ b/media/java/android/mtp/MtpStorage.java
@@ -16,6 +16,8 @@
package android.mtp;
+import android.os.storage.StorageVolume;
+
/**
* This class represents a storage unit on an MTP device.
* Used only for MTP support in USB responder mode.
@@ -31,13 +33,12 @@
private final long mReserveSpace;
private final boolean mRemovable;
- public MtpStorage(int id, String path, String description,
- long reserveSpace, boolean removable) {
- mStorageId = id;
- mPath = path;
- mDescription = description;
- mReserveSpace = reserveSpace;
- mRemovable = removable;
+ public MtpStorage(StorageVolume volume) {
+ mStorageId = volume.getStorageId();
+ mPath = volume.getPath();
+ mDescription = volume.getDescription();
+ mReserveSpace = volume.getMtpReserveSpace();
+ mRemovable = volume.isRemovable();
}
/**
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 027a1ce..a8e0a4d 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -24,10 +24,12 @@
#include "include/ARTSPController.h"
#include "include/AwesomePlayer.h"
+#include "include/DRMExtractor.h"
#include "include/SoftwareRenderer.h"
#include "include/NuCachedSource2.h"
#include "include/ThrottledSource.h"
#include "include/MPEG2TSExtractor.h"
+#include "include/WVMExtractor.h"
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
@@ -312,7 +314,7 @@
if (mDecryptHandle != NULL) {
CHECK(mDrmManagerClient);
if (RightsStatus::RIGHTS_VALID != mDecryptHandle->status) {
- notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_NO_LICENSE);
+ notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_DRM_NO_LICENSE);
}
}
@@ -447,6 +449,7 @@
cancelPlayerEvents();
+ mWVMExtractor.clear();
mCachedSource.clear();
mAudioTrack.clear();
mVideoTrack.clear();
@@ -554,6 +557,11 @@
*durationUs = cachedDataRemaining * 8000000ll / bitrate;
*eos = (finalStatus != OK);
return true;
+ } else if (mWVMExtractor != NULL) {
+ status_t finalStatus;
+ *durationUs = mWVMExtractor->getCachedDurationUs(&finalStatus);
+ *eos = (finalStatus != OK);
+ return true;
}
return false;
@@ -646,6 +654,30 @@
}
}
}
+ } else if (mWVMExtractor != NULL) {
+ status_t finalStatus;
+
+ int64_t cachedDurationUs
+ = mWVMExtractor->getCachedDurationUs(&finalStatus);
+
+ bool eos = (finalStatus != OK);
+
+ if (eos) {
+ if (finalStatus == ERROR_END_OF_STREAM) {
+ notifyListener_l(MEDIA_BUFFERING_UPDATE, 100);
+ }
+ if (mFlags & PREPARING) {
+ LOGV("cache has reached EOS, prepare is done.");
+ finishAsyncPrepare_l();
+ }
+ } else {
+ int percentage = 100.0 * (double)cachedDurationUs / mDurationUs;
+ if (percentage > 100) {
+ percentage = 100;
+ }
+
+ notifyListener_l(MEDIA_BUFFERING_UPDATE, percentage);
+ }
}
int64_t cachedDurationUs;
@@ -1320,7 +1352,7 @@
mVideoBuffer = NULL;
}
- if (mSeeking == SEEK && mCachedSource != NULL && mAudioSource != NULL
+ if (mSeeking == SEEK && isStreamingHTTP() && mAudioSource != NULL
&& !(mFlags & SEEK_PREVIEW)) {
// We're going to seek the video source first, followed by
// the audio source.
@@ -1654,8 +1686,19 @@
status_t AwesomePlayer::finishSetDataSource_l() {
sp<DataSource> dataSource;
+ bool isWidevineStreaming = false;
+ if (!strncasecmp("widevine://", mUri.string(), 11)) {
+ isWidevineStreaming = true;
+
+ String8 newURI = String8("http://");
+ newURI.append(mUri.string() + 11);
+
+ mUri = newURI;
+ }
+
if (!strncasecmp("http://", mUri.string(), 7)
- || !strncasecmp("https://", mUri.string(), 8)) {
+ || !strncasecmp("https://", mUri.string(), 8)
+ || isWidevineStreaming) {
mConnectingDataSource = new NuHTTPDataSource(
(mFlags & INCOGNITO) ? NuHTTPDataSource::kFlagIncognito : 0);
@@ -1670,39 +1713,48 @@
return err;
}
+ if (!isWidevineStreaming) {
+ // The widevine extractor does its own caching.
+
#if 0
- mCachedSource = new NuCachedSource2(
- new ThrottledSource(
- mConnectingDataSource, 50 * 1024 /* bytes/sec */));
+ mCachedSource = new NuCachedSource2(
+ new ThrottledSource(
+ mConnectingDataSource, 50 * 1024 /* bytes/sec */));
#else
- mCachedSource = new NuCachedSource2(mConnectingDataSource);
+ mCachedSource = new NuCachedSource2(mConnectingDataSource);
#endif
- mConnectingDataSource.clear();
- dataSource = mCachedSource;
-
- // We're going to prefill the cache before trying to instantiate
- // the extractor below, as the latter is an operation that otherwise
- // could block on the datasource for a significant amount of time.
- // During that time we'd be unable to abort the preparation phase
- // without this prefill.
-
- mLock.unlock();
-
- for (;;) {
- status_t finalStatus;
- size_t cachedDataRemaining =
- mCachedSource->approxDataRemaining(&finalStatus);
-
- if (finalStatus != OK || cachedDataRemaining >= kHighWaterMarkBytes
- || (mFlags & PREPARE_CANCELLED)) {
- break;
- }
-
- usleep(200000);
+ dataSource = mCachedSource;
+ } else {
+ dataSource = mConnectingDataSource;
}
- mLock.lock();
+ mConnectingDataSource.clear();
+
+ if (mCachedSource != NULL) {
+ // We're going to prefill the cache before trying to instantiate
+ // the extractor below, as the latter is an operation that otherwise
+ // could block on the datasource for a significant amount of time.
+ // During that time we'd be unable to abort the preparation phase
+ // without this prefill.
+
+ mLock.unlock();
+
+ for (;;) {
+ status_t finalStatus;
+ size_t cachedDataRemaining =
+ mCachedSource->approxDataRemaining(&finalStatus);
+
+ if (finalStatus != OK || cachedDataRemaining >= kHighWaterMarkBytes
+ || (mFlags & PREPARE_CANCELLED)) {
+ break;
+ }
+
+ usleep(200000);
+ }
+
+ mLock.lock();
+ }
if (mFlags & PREPARE_CANCELLED) {
LOGI("Prepare cancelled while waiting for initial cache fill.");
@@ -1740,21 +1792,48 @@
return UNKNOWN_ERROR;
}
- sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
+ sp<MediaExtractor> extractor;
- if (extractor == NULL) {
- return UNKNOWN_ERROR;
+ if (isWidevineStreaming) {
+ String8 mimeType;
+ float confidence;
+ sp<AMessage> dummy;
+ bool success = SniffDRM(dataSource, &mimeType, &confidence, &dummy);
+
+ if (!success
+ || strcasecmp(
+ mimeType.string(), MEDIA_MIMETYPE_CONTAINER_WVM)) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ mWVMExtractor = new WVMExtractor(dataSource);
+ mWVMExtractor->setAdaptiveStreamingMode(true);
+ extractor = mWVMExtractor;
+ } else {
+ extractor = MediaExtractor::Create(dataSource);
+
+ if (extractor == NULL) {
+ return UNKNOWN_ERROR;
+ }
}
dataSource->getDrmInfo(&mDecryptHandle, &mDrmManagerClient);
if (mDecryptHandle != NULL) {
CHECK(mDrmManagerClient);
if (RightsStatus::RIGHTS_VALID != mDecryptHandle->status) {
- notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_NO_LICENSE);
+ notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_DRM_NO_LICENSE);
}
}
- return setDataSource_l(extractor);
+ status_t err = setDataSource_l(extractor);
+
+ if (err != OK) {
+ mWVMExtractor.clear();
+
+ return err;
+ }
+
+ return OK;
}
void AwesomePlayer::abortPrepare(status_t err) {
@@ -1815,7 +1894,7 @@
mFlags |= PREPARING_CONNECTED;
- if (mCachedSource != NULL || mRTSPController != NULL) {
+ if (isStreamingHTTP() || mRTSPController != NULL) {
postBufferingEvent_l();
} else {
finishAsyncPrepare_l();
@@ -1852,4 +1931,8 @@
postCheckAudioStatusEvent_l();
}
+bool AwesomePlayer::isStreamingHTTP() const {
+ return mCachedSource != NULL || mWVMExtractor != NULL;
+}
+
} // namespace android
diff --git a/media/libstagefright/DRMExtractor.cpp b/media/libstagefright/DRMExtractor.cpp
index 2809df5..6db922e 100644
--- a/media/libstagefright/DRMExtractor.cpp
+++ b/media/libstagefright/DRMExtractor.cpp
@@ -146,18 +146,14 @@
DrmBuffer *pDecryptedDrmBuffer = &decryptedDrmBuffer;
if ((err = mDrmManagerClient->decrypt(mDecryptHandle, mTrackId,
- &encryptedDrmBuffer, &pDecryptedDrmBuffer)) != DRM_NO_ERROR) {
+ &encryptedDrmBuffer, &pDecryptedDrmBuffer)) != NO_ERROR) {
if (decryptedDrmBuffer.data) {
delete [] decryptedDrmBuffer.data;
decryptedDrmBuffer.data = NULL;
}
- if (err == DRM_ERROR_LICENSE_EXPIRED) {
- return ERROR_NO_LICENSE;
- } else {
- return ERROR_IO;
- }
+ return err;
}
CHECK(pDecryptedDrmBuffer == &decryptedDrmBuffer);
diff --git a/media/libstagefright/WVMExtractor.cpp b/media/libstagefright/WVMExtractor.cpp
index 7c72852..83a1eaa 100644
--- a/media/libstagefright/WVMExtractor.cpp
+++ b/media/libstagefright/WVMExtractor.cpp
@@ -45,7 +45,8 @@
static Mutex gWVMutex;
WVMExtractor::WVMExtractor(const sp<DataSource> &source)
- : mDataSource(source) {
+ : mDataSource(source),
+ mUseAdaptiveStreaming(false) {
{
Mutex::Autolock autoLock(gWVMutex);
if (gVendorLibHandle == NULL) {
@@ -100,5 +101,21 @@
return mImpl->getMetaData();
}
+int64_t WVMExtractor::getCachedDurationUs(status_t *finalStatus) {
+ // TODO: Fill this with life.
+
+ *finalStatus = OK;
+
+ return 0;
+}
+
+void WVMExtractor::setAdaptiveStreamingMode(bool adaptive) {
+ mUseAdaptiveStreaming = adaptive;
+}
+
+bool WVMExtractor::getAdaptiveStreamingMode() const {
+ return mUseAdaptiveStreaming;
+}
+
} //namespace android
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 4e6f75c..3a21f25 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -44,6 +44,8 @@
class DrmManagerClinet;
class DecryptHandle;
+struct WVMExtractor;
+
struct AwesomeRenderer : public RefBase {
AwesomeRenderer() {}
@@ -219,6 +221,8 @@
DrmManagerClient *mDrmManagerClient;
DecryptHandle *mDecryptHandle;
+ sp<WVMExtractor> mWVMExtractor;
+
status_t setDataSource_l(
const char *uri,
const KeyedVector<String8, String8> *headers = NULL);
@@ -268,6 +272,8 @@
status_t startAudioPlayer_l();
+ bool isStreamingHTTP() const;
+
AwesomePlayer(const AwesomePlayer &);
AwesomePlayer &operator=(const AwesomePlayer &);
};
diff --git a/media/libstagefright/include/WVMExtractor.h b/media/libstagefright/include/WVMExtractor.h
index 0da45a8..62e5aa5 100644
--- a/media/libstagefright/include/WVMExtractor.h
+++ b/media/libstagefright/include/WVMExtractor.h
@@ -19,6 +19,7 @@
#define WVM_EXTRACTOR_H_
#include <media/stagefright/MediaExtractor.h>
+#include <utils/Errors.h>
namespace android {
@@ -33,12 +34,31 @@
virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
virtual sp<MetaData> getMetaData();
+ // Return the amount of data cached from the current
+ // playback positiion (in us).
+ // While more data is still being fetched *finalStatus == OK,
+ // Once fetching is completed (no more data available), *finalStatus != OK
+ // If fetching completed normally (i.e. reached EOS instead of IO error)
+ // *finalStatus == ERROR_END_OF_STREAM
+ int64_t getCachedDurationUs(status_t *finalStatus);
+
+ // Set to use adaptive streaming mode by the WV component.
+ // If adaptive == true, adaptive streaming mode will be used.
+ // Default mode is non-adaptive streaming mode.
+ // Should set to use adaptive streaming mode only if widevine:// protocol
+ // is used.
+ void setAdaptiveStreamingMode(bool adaptive);
+
+ // Retrieve the adaptive streaming mode used by the WV component.
+ bool getAdaptiveStreamingMode() const;
+
protected:
virtual ~WVMExtractor();
private:
sp<DataSource> mDataSource;
sp<MediaExtractor> mImpl;
+ bool mUseAdaptiveStreaming;
WVMExtractor(const WVMExtractor &);
WVMExtractor &operator=(const WVMExtractor &);
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 376d42f..347d70a 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -1145,6 +1145,11 @@
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
+ // compute storage ID for each volume
+ int length = mVolumes.size();
+ for (int i = 0; i < length; i++) {
+ mVolumes.get(i).setStorageId(i);
+ }
parser.close();
}
}
diff --git a/services/java/com/android/server/wm/DragState.java b/services/java/com/android/server/wm/DragState.java
index c8f8ff3..118cd55 100644
--- a/services/java/com/android/server/wm/DragState.java
+++ b/services/java/com/android/server/wm/DragState.java
@@ -156,9 +156,8 @@
}
if (mDragInProgress && newWin.isPotentialDragTarget()) {
- DragEvent event = DragEvent.obtain(DragEvent.ACTION_DRAG_STARTED,
- touchX - newWin.mFrame.left, touchY - newWin.mFrame.top,
- null, desc, null, false);
+ DragEvent event = obtainDragEvent(newWin, DragEvent.ACTION_DRAG_STARTED,
+ touchX, touchY, null, desc, null, false);
try {
newWin.mClient.dispatchDragEvent(event);
// track each window that we've notified that the drag is starting
@@ -267,9 +266,8 @@
Slog.d(WindowManagerService.TAG, "sending DRAG_EXITED to " + mTargetWindow);
}
// force DRAG_EXITED_EVENT if appropriate
- DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_EXITED,
- x - mTargetWindow.mFrame.left, y - mTargetWindow.mFrame.top,
- null, null, null, false);
+ DragEvent evt = obtainDragEvent(mTargetWindow, DragEvent.ACTION_DRAG_EXITED,
+ x, y, null, null, null, false);
mTargetWindow.mClient.dispatchDragEvent(evt);
if (myPid != mTargetWindow.mSession.mPid) {
evt.recycle();
@@ -279,9 +277,8 @@
if (false && WindowManagerService.DEBUG_DRAG) {
Slog.d(WindowManagerService.TAG, "sending DRAG_LOCATION to " + touchedWin);
}
- DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_LOCATION,
- x - touchedWin.mFrame.left, y - touchedWin.mFrame.top,
- null, null, null, false);
+ DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DRAG_LOCATION,
+ x, y, null, null, null, false);
touchedWin.mClient.dispatchDragEvent(evt);
if (myPid != touchedWin.mSession.mPid) {
evt.recycle();
@@ -310,8 +307,7 @@
}
final int myPid = Process.myPid();
final IBinder token = touchedWin.mClient.asBinder();
- DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DROP,
- x - touchedWin.mFrame.left, y - touchedWin.mFrame.top,
+ DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DROP, x, y,
null, null, mData, false);
try {
touchedWin.mClient.dispatchDragEvent(evt);
@@ -365,4 +361,16 @@
return touchedWin;
}
+
+ private static DragEvent obtainDragEvent(WindowState win, int action,
+ float x, float y, Object localState,
+ ClipDescription description, ClipData data, boolean result) {
+ float winX = x - win.mFrame.left;
+ float winY = y - win.mFrame.top;
+ if (win.mEnforceSizeCompat) {
+ winX *= win.mGlobalScale;
+ winY *= win.mGlobalScale;
+ }
+ return DragEvent.obtain(action, winX, winY, localState, description, data, result);
+ }
}
\ No newline at end of file
diff --git a/services/java/com/android/server/wm/FadeInOutAnimation.java b/services/java/com/android/server/wm/FadeInOutAnimation.java
deleted file mode 100644
index 06f7657..0000000
--- a/services/java/com/android/server/wm/FadeInOutAnimation.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.Animation;
-import android.view.animation.Transformation;
-
-/**
- * Animation that fade in after 0.5 interpolate time, or fade out in reverse order.
- * This is used for opening/closing transition for apps in compatible mode.
- */
-class FadeInOutAnimation extends Animation {
- boolean mFadeIn;
-
- public FadeInOutAnimation(boolean fadeIn) {
- setInterpolator(new AccelerateInterpolator());
- setDuration(WindowManagerService.DEFAULT_FADE_IN_OUT_DURATION);
- mFadeIn = fadeIn;
- }
-
- @Override
- protected void applyTransformation(float interpolatedTime, Transformation t) {
- float x = interpolatedTime;
- if (!mFadeIn) {
- x = 1.0f - x; // reverse the interpolation for fade out
- }
- t.setAlpha(x);
- }
-}
\ No newline at end of file
diff --git a/services/java/com/android/server/wm/InputMonitor.java b/services/java/com/android/server/wm/InputMonitor.java
index a3e8be0..9dc92d3 100644
--- a/services/java/com/android/server/wm/InputMonitor.java
+++ b/services/java/com/android/server/wm/InputMonitor.java
@@ -205,7 +205,7 @@
inputWindow.ownerPid = child.mSession.mPid;
inputWindow.ownerUid = child.mSession.mUid;
- final Rect frame = child.mScaledFrame;
+ final Rect frame = child.mFrame;
inputWindow.frameLeft = frame.left;
inputWindow.frameTop = frame.top;
inputWindow.frameRight = frame.right;
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 6103b0f..607c60a 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -603,9 +603,6 @@
// The frame use to limit the size of the app running in compatibility mode.
Rect mCompatibleScreenFrame = new Rect();
float mCompatibleScreenScale;
- // The surface used to fill the outer rim of the app running in compatibility mode.
- Surface mBackgroundFillerSurface = null;
- WindowState mBackgroundFillerTarget = null;
public static WindowManagerService main(Context context,
PowerManagerService pm, boolean haveInputMethods) {
@@ -1774,7 +1771,7 @@
boolean rawChanged = false;
float wpx = mLastWallpaperX >= 0 ? mLastWallpaperX : 0.5f;
float wpxs = mLastWallpaperXStep >= 0 ? mLastWallpaperXStep : -1.0f;
- int availw = wallpaperWin.mScaledFrame.right-wallpaperWin.mScaledFrame.left-dw;
+ int availw = wallpaperWin.mFrame.right-wallpaperWin.mFrame.left-dw;
int offset = availw > 0 ? -(int)(availw*wpx+.5f) : 0;
changed = wallpaperWin.mXOffset != offset;
if (changed) {
@@ -2369,6 +2366,11 @@
w.mGivenVisibleInsets.set(visibleInsets);
w.mGivenTouchableRegion.set(touchableRegion);
w.mTouchableInsets = touchableInsets;
+ if (w.mGlobalScale != 1) {
+ w.mGivenContentInsets.scale(w.mGlobalScale);
+ w.mGivenVisibleInsets.scale(w.mGlobalScale);
+ w.mGivenTouchableRegion.scale(w.mGlobalScale);
+ }
mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
}
@@ -2704,7 +2706,7 @@
if (win.mAppToken != null) {
win.mAppToken.updateReportedVisibilityLocked();
}
- outFrame.set(win.mFrame);
+ outFrame.set(win.mCompatFrame);
outContentInsets.set(win.mContentInsets);
outVisibleInsets.set(win.mVisibleInsets);
if (localLOGV) Slog.v(
@@ -2903,18 +2905,14 @@
}
private boolean applyAnimationLocked(AppWindowToken wtoken,
- WindowManager.LayoutParams lp, int transit, boolean enter, boolean bgFiller) {
+ WindowManager.LayoutParams lp, int transit, boolean enter) {
// Only apply an animation if the display isn't frozen. If it is
// frozen, there is no reason to animate and it can cause strange
// artifacts when we unfreeze the display if some different animation
// is running.
if (!mDisplayFrozen && mPolicy.isScreenOn()) {
Animation a;
- if (bgFiller) {
- a = new FadeInOutAnimation(enter);
- if (DEBUG_ANIM) Slog.v(TAG,
- "applying FadeInOutAnimation for a window in compatibility mode");
- } else if (mNextAppTransitionPackage != null) {
+ if (mNextAppTransitionPackage != null) {
a = loadAnimation(mNextAppTransitionPackage, enter ?
mNextAppTransitionEnter : mNextAppTransitionExit);
} else {
@@ -3706,7 +3704,7 @@
}
boolean setTokenVisibilityLocked(AppWindowToken wtoken, WindowManager.LayoutParams lp,
- boolean visible, int transit, boolean performLayout, boolean bgFiller) {
+ boolean visible, int transit, boolean performLayout) {
boolean delayed = false;
if (wtoken.clientHidden == visible) {
@@ -3728,7 +3726,7 @@
if (wtoken.animation == sDummyAnimation) {
wtoken.animation = null;
}
- applyAnimationLocked(wtoken, lp, transit, visible, bgFiller);
+ applyAnimationLocked(wtoken, lp, transit, visible);
changed = true;
if (wtoken.animation != null) {
delayed = runningAppAnimation = true;
@@ -3882,7 +3880,7 @@
final long origId = Binder.clearCallingIdentity();
setTokenVisibilityLocked(wtoken, null, visible, WindowManagerPolicy.TRANSIT_UNSET,
- true, false);
+ true);
wtoken.updateReportedVisibilityLocked();
Binder.restoreCallingIdentity(origId);
}
@@ -4009,7 +4007,7 @@
if (basewtoken != null && (wtoken=basewtoken.appWindowToken) != null) {
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Removing app token: " + wtoken);
delayed = setTokenVisibilityLocked(wtoken, null, false,
- WindowManagerPolicy.TRANSIT_UNSET, true, false);
+ WindowManagerPolicy.TRANSIT_UNSET, true);
wtoken.inPendingTransaction = false;
mOpeningApps.remove(wtoken);
wtoken.waitingToShow = false;
@@ -4830,7 +4828,7 @@
// Don't include wallpaper in bounds calculation
if (!ws.mIsWallpaper) {
- final Rect wf = ws.mScaledFrame;
+ final Rect wf = ws.mFrame;
final Rect cr = ws.mContentInsets;
int left = wf.left + cr.left;
int top = wf.top + cr.top;
@@ -5549,8 +5547,8 @@
// Override display width and height with what we are computing,
// to be sure they remain consistent.
- dm.widthPixels = mPolicy.getNonDecorDisplayWidth(dw);
- dm.heightPixels = mPolicy.getNonDecorDisplayHeight(dh);
+ dm.widthPixels = dm.realWidthPixels = mPolicy.getNonDecorDisplayWidth(dw);
+ dm.heightPixels = dm.realHeightPixels = mPolicy.getNonDecorDisplayHeight(dh);
mCompatibleScreenScale = CompatibilityInfo.updateCompatibleScreenFrame(
dm, mCompatibleScreenFrame, null);
@@ -6500,8 +6498,22 @@
}
if (mBaseDisplayWidth < mInitialDisplayWidth
|| mBaseDisplayHeight < mInitialDisplayHeight) {
- Rect outer = new Rect(0, 0, mInitialDisplayWidth, mInitialDisplayHeight);
- Rect inner = new Rect(0, 0, mBaseDisplayWidth, mBaseDisplayHeight);
+ int initW, initH, baseW, baseH;
+ final boolean rotated = (mRotation == Surface.ROTATION_90
+ || mRotation == Surface.ROTATION_270);
+ if (rotated) {
+ initW = mInitialDisplayHeight;
+ initH = mInitialDisplayWidth;
+ baseW = mBaseDisplayHeight;
+ baseH = mBaseDisplayWidth;
+ } else {
+ initW = mInitialDisplayWidth;
+ initH = mInitialDisplayHeight;
+ baseW = mBaseDisplayWidth;
+ baseH = mBaseDisplayHeight;
+ }
+ Rect outer = new Rect(0, 0, initW, initH);
+ Rect inner = new Rect(0, 0, baseW, baseH);
try {
mBlackFrame = new BlackFrame(mFxSession, outer, inner, MASK_LAYER);
} catch (Surface.OutOfResourcesException e) {
@@ -6851,7 +6863,6 @@
}
win.prelayout();
mPolicy.layoutWindowLw(win, win.mAttrs, null);
- win.evalNeedsBackgroundFiller(innerDw, innerDh);
win.mLayoutSeq = seq;
if (DEBUG_LAYOUT) Slog.v(TAG, "-> mFrame="
+ win.mFrame + " mContainingFrame="
@@ -6888,7 +6899,6 @@
}
win.prelayout();
mPolicy.layoutWindowLw(win, win.mAttrs, win.mAttachedWindow);
- win.evalNeedsBackgroundFiller(innerDw, innerDh);
win.mLayoutSeq = seq;
if (DEBUG_LAYOUT) Slog.v(TAG, "-> mFrame="
+ win.mFrame + " mContainingFrame="
@@ -7340,7 +7350,6 @@
LayoutParams animLp = null;
int bestAnimLayer = -1;
boolean fullscreenAnim = false;
- boolean needBgFiller = false;
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
"New wallpaper target=" + mWallpaperTarget
@@ -7378,16 +7387,8 @@
if (wtoken.appFullscreen) {
WindowState ws = wtoken.findMainWindow();
if (ws != null) {
- // If this is a compatibility mode
- // window, we will always use its anim.
- if (ws.mNeedsBackgroundFiller) {
- animLp = ws.mAttrs;
- bestAnimLayer = Integer.MAX_VALUE;
- needBgFiller = true;
- } else if (!fullscreenAnim || ws.mLayer > bestAnimLayer) {
- animLp = ws.mAttrs;
- bestAnimLayer = ws.mLayer;
- }
+ animLp = ws.mAttrs;
+ bestAnimLayer = ws.mLayer;
fullscreenAnim = true;
}
} else if (!fullscreenAnim) {
@@ -7449,7 +7450,7 @@
wtoken.inPendingTransaction = false;
wtoken.animation = null;
setTokenVisibilityLocked(wtoken, animLp, true,
- transit, false, needBgFiller);
+ transit, false);
wtoken.updateReportedVisibilityLocked();
wtoken.waitingToShow = false;
wtoken.showAllWindowsLocked();
@@ -7462,7 +7463,7 @@
wtoken.inPendingTransaction = false;
wtoken.animation = null;
setTokenVisibilityLocked(wtoken, animLp, false,
- transit, false, needBgFiller);
+ transit, false);
wtoken.updateReportedVisibilityLocked();
wtoken.waitingToHide = false;
// Force the allDrawn flag, because we want to start
@@ -7629,8 +7630,6 @@
boolean dimming = false;
boolean covered = false;
boolean syswin = false;
- boolean backgroundFillerWasShown = mBackgroundFillerTarget != null;
- mBackgroundFillerTarget = null;
final int N = mWindows.size();
@@ -7659,8 +7658,7 @@
w.computeShownFrameLocked();
if (localLOGV) Slog.v(
TAG, "Placing surface #" + i + " " + w.mSurface
- + ": new=" + w.mShownFrame + ", old="
- + w.mLastShownFrame);
+ + ": new=" + w.mShownFrame);
int width, height;
if ((w.mAttrs.flags & w.mAttrs.FLAG_SCALED) != 0) {
@@ -7668,13 +7666,9 @@
// the requested size.
width = w.mRequestedWidth;
height = w.mRequestedHeight;
- w.mLastRequestedWidth = width;
- w.mLastRequestedHeight = height;
- w.mLastShownFrame.set(w.mShownFrame);
} else {
- width = w.mShownFrame.width();
- height = w.mShownFrame.height();
- w.mLastShownFrame.set(w.mShownFrame);
+ width = w.mCompatFrame.width();
+ height = w.mCompatFrame.height();
}
if (w.mSurface != null) {
@@ -7741,8 +7735,8 @@
}
if (localLOGV) Slog.v(TAG, "Resizing " + w
+ ": configChanged=" + configChanged
- + " last=" + w.mLastFrame + " frame=" + w.mFrame);
- boolean frameChanged = !w.mLastFrame.equals(w.mFrame);
+ + " last=" + w.mLastCompatFrame + " frame=" + w.mCompatFrame);
+ boolean frameChanged = !w.mLastCompatFrame.equals(w.mCompatFrame);
if (frameChanged
|| w.mContentInsetsChanged
|| w.mVisibleInsetsChanged
@@ -7758,6 +7752,7 @@
}
w.mLastFrame.set(w.mFrame);
+ w.mLastCompatFrame.set(w.mCompatFrame);
w.mLastContentInsets.set(w.mContentInsets);
w.mLastVisibleInsets.set(w.mVisibleInsets);
// If the screen is currently frozen, then keep
@@ -7793,7 +7788,7 @@
}
}
if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG,
- "Resizing window " + w + " to " + w.mFrame);
+ "Resizing window " + w + " to " + w.mCompatFrame);
mResizingWindows.add(w);
} else if (w.mOrientationChanging) {
if (!w.mDrawPending && !w.mCommitDrawPending) {
@@ -7932,16 +7927,6 @@
final boolean obscuredChanged = w.mObscured != obscured;
- if (mBackgroundFillerTarget != null) {
- if (w.isAnimating()) {
- // Background filler is below all other windows that
- // are animating.
- mBackgroundFillerTarget = w;
- } else if (w.mIsWallpaper) {
- mBackgroundFillerTarget = w;
- }
- }
-
// Update effect.
if (!(w.mObscured=obscured)) {
if (w.mSurface != null) {
@@ -7970,12 +7955,6 @@
// so we want to leave all of them as unblurred (for
// performance reasons).
obscured = true;
- } else if (w.mNeedsBackgroundFiller && w.mHasDrawn
- && w.mViewVisibility == View.VISIBLE
- && (canBeSeen || w.isAnimating())) {
- // This window is in compatibility mode, and needs background filler.
- obscured = true;
- mBackgroundFillerTarget = w;
} else if (canBeSeen && !obscured &&
(attrFlags&FLAG_BLUR_BEHIND|FLAG_DIM_BEHIND) != 0) {
if (localLOGV) Slog.v(TAG, "Win " + w
@@ -8042,47 +8021,6 @@
}
}
- if (mBackgroundFillerTarget != null) {
- if (mBackgroundFillerSurface == null) {
- try {
- mBackgroundFillerSurface = new Surface(mFxSession, 0,
- "BackGroundFiller",
- 0, dw, dh,
- PixelFormat.OPAQUE,
- Surface.FX_SURFACE_NORMAL);
- } catch (Exception e) {
- Slog.e(TAG, "Exception creating filler surface", e);
- }
- if (SHOW_TRANSACTIONS) Slog.i(TAG, " BG FILLER "
- + mBackgroundFillerSurface + ": CREATE");
- }
- try {
- if (SHOW_TRANSACTIONS) Slog.i(TAG, " BG FILLER "
- + mBackgroundFillerSurface + " SHOW: pos=(0,0) ("
- + dw + "x" + dh + ") layer="
- + (mBackgroundFillerTarget.mLayer - 1));
- mBackgroundFillerSurface.setPosition(0, 0);
- mBackgroundFillerSurface.setSize(dw, dh);
- // Using the same layer as Dim because they will never be shown at the
- // same time. NOTE: we do NOT use mAnimLayer, because we don't
- // want this surface dragged up in front of stuff that is animating.
- mBackgroundFillerSurface.setLayer(mBackgroundFillerTarget.mLayer
- - LAYER_OFFSET_DIM);
- mBackgroundFillerSurface.show();
- } catch (RuntimeException e) {
- Slog.e(TAG, "Exception showing filler surface");
- }
- } else if (backgroundFillerWasShown) {
- mBackgroundFillerTarget = null;
- if (SHOW_TRANSACTIONS) Slog.i(TAG, " BG FILLER "
- + mBackgroundFillerSurface + " HIDE");
- try {
- mBackgroundFillerSurface.hide();
- } catch (RuntimeException e) {
- Slog.e(TAG, "Exception hiding filler surface", e);
- }
- }
-
if (mDimAnimator != null && mDimAnimator.mDimShown) {
animating |= mDimAnimator.updateSurface(dimming, currentTime,
mDisplayFrozen || !mPolicy.isScreenOn());
@@ -8137,7 +8075,7 @@
WindowState win = mResizingWindows.get(i);
try {
if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG,
- "Reporting new frame to " + win + ": " + win.mFrame);
+ "Reporting new frame to " + win + ": " + win.mCompatFrame);
int diff = 0;
boolean configChanged =
win.mConfiguration != mCurConfiguration
@@ -8146,13 +8084,13 @@
if ((DEBUG_RESIZE || DEBUG_ORIENTATION || DEBUG_CONFIGURATION)
&& configChanged) {
Slog.i(TAG, "Sending new config to window " + win + ": "
- + win.mFrame.width() + "x" + win.mFrame.height()
+ + win.mCompatFrame.width() + "x" + win.mCompatFrame.height()
+ " / " + mCurConfiguration + " / 0x"
+ Integer.toHexString(diff));
}
win.mConfiguration = mCurConfiguration;
- win.mClient.resized(win.mFrame.width(),
- win.mFrame.height(), win.mLastContentInsets,
+ win.mClient.resized(win.mCompatFrame.width(),
+ win.mCompatFrame.height(), win.mLastContentInsets,
win.mLastVisibleInsets, win.mDrawPending,
configChanged ? win.mConfiguration : null);
win.mContentInsetsChanged = false;
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index 2014e9d..144341b 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -18,7 +18,6 @@
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
@@ -83,23 +82,30 @@
boolean mAttachedHidden; // is our parent window hidden?
boolean mLastHidden; // was this window last hidden?
boolean mWallpaperVisible; // for wallpaper, what was last vis report?
+
+ /**
+ * The window size that was requested by the application. These are in
+ * the application's coordinate space (without compatibility scale applied).
+ */
int mRequestedWidth;
int mRequestedHeight;
- int mLastRequestedWidth;
- int mLastRequestedHeight;
+
int mLayer;
int mAnimLayer;
int mLastLayer;
boolean mHaveFrame;
boolean mObscured;
- boolean mNeedsBackgroundFiller;
boolean mTurnOnScreen;
int mLayoutSeq = -1;
Configuration mConfiguration = null;
- // Actual frame shown on-screen (may be modified by animation)
+ /**
+ * Actual frame shown on-screen (may be modified by animation). These
+ * are in the screen's coordinate space (WITH the compatibility scale
+ * applied).
+ */
final Rect mShownFrame = new Rect();
final Rect mLastShownFrame = new Rect();
@@ -110,14 +116,16 @@
boolean mSurfaceResized;
/**
- * Insets that determine the actually visible area
+ * Insets that determine the actually visible area. These are in the application's
+ * coordinate space (without compatibility scale applied).
*/
final Rect mVisibleInsets = new Rect();
final Rect mLastVisibleInsets = new Rect();
boolean mVisibleInsetsChanged;
/**
- * Insets that are covered by system windows
+ * Insets that are covered by system windows. These are in the application's
+ * coordinate space (without compatibility scale applied).
*/
final Rect mContentInsets = new Rect();
final Rect mLastContentInsets = new Rect();
@@ -157,16 +165,20 @@
// Current transformation being applied.
boolean mHaveMatrix;
float mGlobalScale=1;
+ float mInvGlobalScale=1;
float mDsDx=1, mDtDx=0, mDsDy=0, mDtDy=1;
float mLastDsDx=1, mLastDtDx=0, mLastDsDy=0, mLastDtDy=1;
float mHScale=1, mVScale=1;
float mLastHScale=1, mLastVScale=1;
final Matrix mTmpMatrix = new Matrix();
- // "Real" frame that the application sees.
+ // "Real" frame that the application sees, in display coordinate space.
final Rect mFrame = new Rect();
final Rect mLastFrame = new Rect();
- final Rect mScaledFrame = new Rect();
+ // Frame that is scaled to the application's coordinate space when in
+ // screen size compatibility mode.
+ final Rect mCompatFrame = new Rect();
+ final Rect mLastCompatFrame = new Rect();
final Rect mContainingFrame = new Rect();
final Rect mDisplayFrame = new Rect();
@@ -346,8 +358,6 @@
mSurface = null;
mRequestedWidth = 0;
mRequestedHeight = 0;
- mLastRequestedWidth = 0;
- mLastRequestedHeight = 0;
mXOffset = 0;
mYOffset = 0;
mLayer = 0;
@@ -373,23 +383,40 @@
final Rect display = mDisplayFrame;
display.set(df);
- if (mEnforceSizeCompat) {
- container.intersect(mService.mCompatibleScreenFrame);
- if ((mAttrs.flags & FLAG_LAYOUT_NO_LIMITS) == 0) {
- display.intersect(mService.mCompatibleScreenFrame);
- }
- }
-
final int pw = container.right - container.left;
final int ph = container.bottom - container.top;
int w,h;
- if ((mAttrs.flags & mAttrs.FLAG_SCALED) != 0) {
- w = mAttrs.width < 0 ? pw : mAttrs.width;
- h = mAttrs.height< 0 ? ph : mAttrs.height;
+ if ((mAttrs.flags & WindowManager.LayoutParams.FLAG_SCALED) != 0) {
+ if (mAttrs.width < 0) {
+ w = pw;
+ } else if (mEnforceSizeCompat) {
+ w = (int)(mAttrs.width * mGlobalScale + .5f);
+ } else {
+ w = mAttrs.width;
+ }
+ if (mAttrs.height < 0) {
+ h = ph;
+ } else if (mEnforceSizeCompat) {
+ h = (int)(mAttrs.height * mGlobalScale + .5f);
+ } else {
+ h = mAttrs.height;
+ }
} else {
- w = mAttrs.width == mAttrs.MATCH_PARENT ? pw : mRequestedWidth;
- h = mAttrs.height== mAttrs.MATCH_PARENT ? ph : mRequestedHeight;
+ if (mAttrs.width == WindowManager.LayoutParams.MATCH_PARENT) {
+ w = pw;
+ } else if (mEnforceSizeCompat) {
+ w = (int)(mRequestedWidth * mGlobalScale + .5f);
+ } else {
+ w = mRequestedWidth;
+ }
+ if (mAttrs.height == WindowManager.LayoutParams.MATCH_PARENT) {
+ h = ph;
+ } else if (mEnforceSizeCompat) {
+ h = (int)(mRequestedHeight * mGlobalScale + .5f);
+ } else {
+ h = mRequestedHeight;
+ }
}
if (!mParentFrame.equals(pf)) {
@@ -412,37 +439,24 @@
//System.out.println("In: w=" + w + " h=" + h + " container=" +
// container + " x=" + mAttrs.x + " y=" + mAttrs.y);
+ float x, y;
+ if (mEnforceSizeCompat) {
+ x = mAttrs.x * mGlobalScale;
+ y = mAttrs.y * mGlobalScale;
+ } else {
+ x = mAttrs.x;
+ y = mAttrs.y;
+ }
+
Gravity.apply(mAttrs.gravity, w, h, container,
- (int) (mAttrs.x + mAttrs.horizontalMargin * pw),
- (int) (mAttrs.y + mAttrs.verticalMargin * ph), frame);
+ (int) (x + mAttrs.horizontalMargin * pw),
+ (int) (y + mAttrs.verticalMargin * ph), frame);
//System.out.println("Out: " + mFrame);
// Now make sure the window fits in the overall display.
Gravity.applyDisplay(mAttrs.gravity, df, frame);
- int adjRight=0, adjBottom=0;
-
- if (mEnforceSizeCompat) {
- // Adjust window offsets by the scaling factor.
- int xoff = (int)((frame.left-mService.mCompatibleScreenFrame.left)*mGlobalScale)
- - (frame.left-mService.mCompatibleScreenFrame.left);
- int yoff = (int)((frame.top-mService.mCompatibleScreenFrame.top)*mGlobalScale)
- - (frame.top-mService.mCompatibleScreenFrame.top);
- frame.offset(xoff, yoff);
-
- // We are temporarily going to apply the compatibility scale
- // to the window so that we can correctly associate it with the
- // content and visible frame.
- adjRight = frame.right - frame.left;
- adjRight = (int)((adjRight)*mGlobalScale + .5f) - adjRight;
- adjBottom = frame.bottom - frame.top;
- adjBottom = (int)((adjBottom)*mGlobalScale + .5f) - adjBottom;
- frame.right += adjRight;
- frame.bottom += adjBottom;
- }
- mScaledFrame.set(frame);
-
// Make sure the content and visible frames are inside of the
// final window frame.
if (content.left < frame.left) content.left = frame.left;
@@ -466,20 +480,17 @@
visibleInsets.right = frame.right-visible.right;
visibleInsets.bottom = frame.bottom-visible.bottom;
+ mCompatFrame.set(frame);
if (mEnforceSizeCompat) {
- // Scale the computed insets back to the window's compatibility
- // coordinate space, and put frame back to correct size.
- final float invScale = 1.0f/mGlobalScale;
- contentInsets.left = (int)(contentInsets.left*invScale);
- contentInsets.top = (int)(contentInsets.top*invScale);
- contentInsets.right = (int)(contentInsets.right*invScale);
- contentInsets.bottom = (int)(contentInsets.bottom*invScale);
- visibleInsets.left = (int)(visibleInsets.left*invScale);
- visibleInsets.top = (int)(visibleInsets.top*invScale);
- visibleInsets.right = (int)(visibleInsets.right*invScale);
- visibleInsets.bottom = (int)(visibleInsets.bottom*invScale);
- frame.right -= adjRight;
- frame.bottom -= adjBottom;
+ // If there is a size compatibility scale being applied to the
+ // window, we need to apply this to its insets so that they are
+ // reported to the app in its coordinate space.
+ contentInsets.scale(mInvGlobalScale);
+ visibleInsets.scale(mInvGlobalScale);
+
+ // Also the scaled frame that we report to the app needs to be
+ // adjusted to be in its coordinate space.
+ mCompatFrame.scale(mInvGlobalScale);
}
if (mIsWallpaper && (fw != frame.width() || fh != frame.height())) {
@@ -592,12 +603,12 @@
if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(
WindowManagerService.TAG, "Creating surface in session "
+ mSession.mSurfaceSession + " window " + this
- + " w=" + mFrame.width()
- + " h=" + mFrame.height() + " format="
+ + " w=" + mCompatFrame.width()
+ + " h=" + mCompatFrame.height() + " format="
+ mAttrs.format + " flags=" + flags);
- int w = mFrame.width();
- int h = mFrame.height();
+ int w = mCompatFrame.width();
+ int h = mCompatFrame.height();
if ((mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) {
// for a scaled surface, we always want the requested
// size.
@@ -650,8 +661,9 @@
+ ", animLayer=" + mAnimLayer);
if (WindowManagerService.SHOW_TRANSACTIONS) {
Slog.i(WindowManagerService.TAG, ">>> OPEN TRANSACTION createSurfaceLocked");
- WindowManagerService.logSurface(this, "CREATE pos=(" + mFrame.left + "," + mFrame.top + ") (" +
- mFrame.width() + "x" + mFrame.height() + "), layer=" +
+ WindowManagerService.logSurface(this, "CREATE pos=(" + mFrame.left
+ + "," + mFrame.top + ") (" +
+ mCompatFrame.width() + "x" + mCompatFrame.height() + "), layer=" +
mAnimLayer + " HIDE", null);
}
Surface.openTransaction();
@@ -862,10 +874,10 @@
if (!mLocalAnimating) {
if (WindowManagerService.DEBUG_ANIM) Slog.v(
WindowManagerService.TAG, "Starting animation in " + this +
- " @ " + currentTime + ": ww=" + mScaledFrame.width() +
- " wh=" + mScaledFrame.height() +
+ " @ " + currentTime + ": ww=" + mFrame.width() +
+ " wh=" + mFrame.height() +
" dw=" + dw + " dh=" + dh + " scale=" + mService.mWindowAnimationScale);
- mAnimation.initialize(mScaledFrame.width(), mScaledFrame.height(), dw, dh);
+ mAnimation.initialize(mFrame.width(), mFrame.height(), dw, dh);
mAnimation.setStartTime(currentTime);
mLocalAnimating = true;
mAnimating = true;
@@ -1035,8 +1047,9 @@
void prelayout() {
if (mEnforceSizeCompat) {
mGlobalScale = mService.mCompatibleScreenScale;
+ mInvGlobalScale = 1/mGlobalScale;
} else {
- mGlobalScale = 1;
+ mGlobalScale = mInvGlobalScale = 1;
}
}
@@ -1334,35 +1347,9 @@
&& mService.mPolicy.isScreenOn();
}
- void evalNeedsBackgroundFiller(int screenWidth, int screenHeight) {
- mNeedsBackgroundFiller =
- // only if the application is requesting compatible window
- mEnforceSizeCompat &&
- // only if it's visible
- mHasDrawn && mViewVisibility == View.VISIBLE &&
- // not needed if the compat window is actually full screen
- !isFullscreenIgnoringCompat(screenWidth, screenHeight) &&
- // and only if the application fills the compatible screen
- mFrame.left <= mService.mCompatibleScreenFrame.left &&
- mFrame.top <= mService.mCompatibleScreenFrame.top &&
- mFrame.right >= mService.mCompatibleScreenFrame.right &&
- mFrame.bottom >= mService.mCompatibleScreenFrame.bottom;
- }
-
boolean isFullscreen(int screenWidth, int screenHeight) {
- if (mEnforceSizeCompat) {
- return mFrame.left <= mService.mCompatibleScreenFrame.left &&
- mFrame.top <= mService.mCompatibleScreenFrame.top &&
- mFrame.right >= mService.mCompatibleScreenFrame.right &&
- mFrame.bottom >= mService.mCompatibleScreenFrame.bottom;
- } else {
- return isFullscreenIgnoringCompat(screenWidth, screenHeight);
- }
- }
-
- boolean isFullscreenIgnoringCompat(int screenWidth, int screenHeight) {
- return mScaledFrame.left <= 0 && mScaledFrame.top <= 0 &&
- mScaledFrame.right >= screenWidth && mScaledFrame.bottom >= screenHeight;
+ return mFrame.left <= 0 && mFrame.top <= 0 &&
+ mFrame.right >= screenWidth && mFrame.bottom >= screenHeight;
}
void removeLocked() {
@@ -1492,38 +1479,28 @@
return true;
}
- private static void applyScaledInsets(Region outRegion, Rect frame, Rect inset, float scale) {
- if (scale != 1) {
- outRegion.set(frame.left + (int)(inset.left*scale),
- frame.top + (int)(inset.top*scale),
- frame.right - (int)(inset.right*scale),
- frame.bottom - (int)(inset.bottom*scale));
- } else {
- outRegion.set(
- frame.left + inset.left, frame.top + inset.top,
- frame.right - inset.right, frame.bottom - inset.bottom);
- }
+ private static void applyInsets(Region outRegion, Rect frame, Rect inset) {
+ outRegion.set(
+ frame.left + inset.left, frame.top + inset.top,
+ frame.right - inset.right, frame.bottom - inset.bottom);
}
public void getTouchableRegion(Region outRegion) {
- final Rect frame = mScaledFrame;
+ final Rect frame = mFrame;
switch (mTouchableInsets) {
default:
case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME:
outRegion.set(frame);
break;
case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT:
- applyScaledInsets(outRegion, frame, mGivenContentInsets, mGlobalScale);
+ applyInsets(outRegion, frame, mGivenContentInsets);
break;
case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE:
- applyScaledInsets(outRegion, frame, mGivenVisibleInsets, mGlobalScale);
+ applyInsets(outRegion, frame, mGivenVisibleInsets);
break;
case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION: {
final Region givenTouchableRegion = mGivenTouchableRegion;
outRegion.set(givenTouchableRegion);
- if (mGlobalScale != 1) {
- outRegion.scale(mGlobalScale);
- }
outRegion.translate(frame.left, frame.top);
break;
}
@@ -1586,8 +1563,7 @@
}
pw.print(prefix); pw.print("Requested w="); pw.print(mRequestedWidth);
pw.print(" h="); pw.print(mRequestedHeight);
- pw.print(" mLayoutSeq="); pw.print(mLayoutSeq);
- pw.print(" mNeedsBackgroundFiller="); pw.println(mNeedsBackgroundFiller);
+ pw.print(" mLayoutSeq="); pw.println(mLayoutSeq);
if (mXOffset != 0 || mYOffset != 0) {
pw.print(prefix); pw.print("Offsets x="); pw.print(mXOffset);
pw.print(" y="); pw.println(mYOffset);
@@ -1608,8 +1584,12 @@
pw.println();
pw.print(prefix); pw.print("mFrame="); mFrame.printShortString(pw);
pw.print(" last="); mLastFrame.printShortString(pw);
- pw.print(" scaled="); mScaledFrame.printShortString(pw);
pw.println();
+ if (mEnforceSizeCompat) {
+ pw.print(prefix); pw.print("mCompatFrame="); mCompatFrame.printShortString(pw);
+ pw.print(" last="); mLastCompatFrame.printShortString(pw);
+ pw.println();
+ }
pw.print(prefix); pw.print("mContainingFrame=");
mContainingFrame.printShortString(pw);
pw.print(" mParentFrame=");
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index 3c0c9a4..144ec42 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -201,7 +201,8 @@
Capability.VIEW_MANIPULATION,
Capability.PLAY_ANIMATION,
Capability.ANIMATED_VIEW_MANIPULATION,
- Capability.ADAPTER_BINDING);
+ Capability.ADAPTER_BINDING,
+ Capability.EXTENDED_VIEWINFO);
BridgeAssetManager.initSystem();
@@ -399,15 +400,6 @@
throw new IllegalArgumentException("viewObject is not a View");
}
- @Override
- public int getViewBaseline(Object viewObject) {
- if (viewObject instanceof View) {
- return ((View) viewObject).getBaseline();
- }
-
- throw new IllegalArgumentException("viewObject is not a View");
- }
-
/**
* Returns the lock for the bridge
*/
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java
index 1ca3182..3d50b2a 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java
@@ -19,21 +19,23 @@
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
+import android.view.Gravity;
import android.widget.TextView;
/**
* Base class for mocked views.
- *
+ *
* TODO: implement onDraw and draw a rectangle in a random color with the name of the class
* (or better the id of the view).
*/
public class MockView extends TextView {
-
+
public MockView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
-
+
setText(this.getClass().getSimpleName());
setTextColor(0xFF000000);
+ setGravity(Gravity.CENTER);
}
@Override
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index c53cb23..b800519 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -74,6 +74,7 @@
import android.view.View.AttachInfo;
import android.view.View.MeasureSpec;
import android.view.ViewGroup.LayoutParams;
+import android.view.ViewGroup.MarginLayoutParams;
import android.widget.AbsListView;
import android.widget.AbsSpinner;
import android.widget.AdapterView;
@@ -513,7 +514,7 @@
mViewRoot.draw(mCanvas);
}
- mViewInfoList = startVisitingViews(mViewRoot, 0);
+ mViewInfoList = startVisitingViews(mViewRoot, 0, params.getExtendedViewInfoMode());
// success!
return SUCCESS.createResult();
@@ -1255,7 +1256,7 @@
}
}
- private List<ViewInfo> startVisitingViews(View view, int offset) {
+ private List<ViewInfo> startVisitingViews(View view, int offset, boolean setExtendedInfo) {
if (view == null) {
return null;
}
@@ -1264,7 +1265,7 @@
offset += view.getTop();
if (view == mContentRoot) {
- return visitAllChildren(mContentRoot, offset);
+ return visitAllChildren(mContentRoot, offset, setExtendedInfo);
}
// otherwise, look for mContentRoot in the children
@@ -1272,7 +1273,8 @@
ViewGroup group = ((ViewGroup) view);
for (int i = 0; i < group.getChildCount(); i++) {
- List<ViewInfo> list = startVisitingViews(group.getChildAt(i), offset);
+ List<ViewInfo> list = startVisitingViews(group.getChildAt(i), offset,
+ setExtendedInfo);
if (list != null) {
return list;
}
@@ -1287,8 +1289,9 @@
* bounds of all the views.
* @param view the root View
* @param offset an offset for the view bounds.
+ * @param setExtendedInfo whether to set the extended view info in the {@link ViewInfo} object.
*/
- private ViewInfo visit(View view, int offset) {
+ private ViewInfo visit(View view, int offset, boolean setExtendedInfo) {
if (view == null) {
return null;
}
@@ -1298,9 +1301,22 @@
view.getLeft(), view.getTop() + offset, view.getRight(), view.getBottom() + offset,
view, view.getLayoutParams());
+ if (setExtendedInfo) {
+ MarginLayoutParams marginParams = null;
+ LayoutParams params = view.getLayoutParams();
+ if (params instanceof MarginLayoutParams) {
+ marginParams = (MarginLayoutParams) params;
+ }
+ result.setExtendedInfo(view.getBaseline(),
+ marginParams != null ? marginParams.leftMargin : 0,
+ marginParams != null ? marginParams.topMargin : 0,
+ marginParams != null ? marginParams.rightMargin : 0,
+ marginParams != null ? marginParams.bottomMargin : 0);
+ }
+
if (view instanceof ViewGroup) {
ViewGroup group = ((ViewGroup) view);
- result.setChildren(visitAllChildren(group, 0 /*offset*/));
+ result.setChildren(visitAllChildren(group, 0 /*offset*/, setExtendedInfo));
}
return result;
@@ -1311,15 +1327,17 @@
* containing the bounds of all the views.
* @param view the root View
* @param offset an offset for the view bounds.
+ * @param setExtendedInfo whether to set the extended view info in the {@link ViewInfo} object.
*/
- private List<ViewInfo> visitAllChildren(ViewGroup viewGroup, int offset) {
+ private List<ViewInfo> visitAllChildren(ViewGroup viewGroup, int offset,
+ boolean setExtendedInfo) {
if (viewGroup == null) {
return null;
}
List<ViewInfo> children = new ArrayList<ViewInfo>();
for (int i = 0; i < viewGroup.getChildCount(); i++) {
- children.add(visit(viewGroup.getChildAt(i), offset));
+ children.add(visit(viewGroup.getChildAt(i), offset, setExtendedInfo));
}
return children;
}