summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.xml69
-rw-r--r--core/java/android/app/ContextImpl.java6
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java8
-rw-r--r--core/java/android/hardware/usb/IUsbManager.aidl20
-rw-r--r--core/java/android/hardware/usb/UsbManager.java98
-rw-r--r--core/java/android/os/Environment.java11
-rw-r--r--core/java/android/view/HardwareRenderer.java2
-rw-r--r--core/java/android/view/ViewGroup.java2
-rw-r--r--core/java/android/webkit/DebugFlags.java4
-rw-r--r--core/java/android/webkit/WebView.java39
-rw-r--r--core/java/android/webkit/WebViewCore.java1
-rw-r--r--core/java/android/webkit/ZoomManager.java50
-rw-r--r--core/java/android/widget/HorizontalScrollView.java4
-rw-r--r--core/java/android/widget/PopupWindow.java2
-rw-r--r--core/java/android/widget/SearchView.java40
-rw-r--r--core/java/android/widget/StackView.java22
-rw-r--r--core/java/android/widget/TextView.java226
-rw-r--r--core/java/com/android/internal/widget/ActionBarView.java57
-rw-r--r--core/res/AndroidManifest.xml6
-rw-r--r--core/res/res/drawable-hdpi/text_cursor_holo_dark.9.pngbin274 -> 159 bytes
-rw-r--r--core/res/res/drawable-hdpi/text_cursor_holo_light.9.pngbin249 -> 159 bytes
-rw-r--r--core/res/res/drawable-mdpi/text_cursor_holo_dark.9.pngbin274 -> 159 bytes
-rw-r--r--core/res/res/drawable-mdpi/text_cursor_holo_light.9.pngbin249 -> 159 bytes
-rw-r--r--core/res/res/layout/action_bar_home.xml14
-rw-r--r--core/res/res/layout/search_view.xml4
-rw-r--r--core/res/res/raw-xlarge/incognito_mode_start_page.html24
-rw-r--r--docs/html/images/avd-manager.pngbin45294 -> 35192 bytes
-rw-r--r--[-rwxr-xr-x]docs/html/images/billing_package.pngbin79624 -> 44231 bytes
-rw-r--r--docs/html/images/developing/adt-props-isLib.pngbin102350 -> 36772 bytes
-rw-r--r--docs/html/images/developing/adt-props-libRef.pngbin113604 -> 39556 bytes
-rw-r--r--docs/html/images/licensing_add_library.pngbin70285 -> 44097 bytes
-rw-r--r--docs/html/images/licensing_gapis_8.pngbin84597 -> 46457 bytes
-rw-r--r--docs/html/images/licensing_package.pngbin78821 -> 44177 bytes
-rw-r--r--media/java/android/mtp/MtpClient.java57
-rw-r--r--packages/SystemUI/AndroidManifest.xml18
-rw-r--r--packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml4
-rw-r--r--packages/SystemUI/res/layout-xlarge/status_bar_recent_panel.xml2
-rw-r--r--packages/SystemUI/res/values/strings.xml7
-rw-r--r--packages/SystemUI/src/com/android/systemui/usb/UsbDisconnectedReceiver.java67
-rw-r--r--packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java170
-rw-r--r--packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java (renamed from services/java/com/android/server/usb/UsbResolverActivity.java)48
-rw-r--r--services/java/com/android/server/usb/UsbDeviceSettingsManager.java222
-rw-r--r--services/java/com/android/server/usb/UsbService.java19
-rw-r--r--services/java/com/android/server/wm/WindowManagerService.java31
-rw-r--r--services/jni/com_android_server_InputManager.cpp6
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java105
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java2
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java37
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java2
49 files changed, 1143 insertions, 363 deletions
diff --git a/api/current.xml b/api/current.xml
index 978b96e432db..cbccf93e5f64 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -95457,6 +95457,32 @@
visibility="public"
>
</method>
+<method name="hasPermission"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="device" type="android.hardware.usb.UsbDevice">
+</parameter>
+</method>
+<method name="hasPermission"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="accessory" type="android.hardware.usb.UsbAccessory">
+</parameter>
+</method>
<method name="isFunctionEnabled"
return="boolean"
abstract="false"
@@ -95509,6 +95535,36 @@
<parameter name="device" type="android.hardware.usb.UsbDevice">
</parameter>
</method>
+<method name="requestPermission"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="device" type="android.hardware.usb.UsbDevice">
+</parameter>
+<parameter name="pi" type="android.app.PendingIntent">
+</parameter>
+</method>
+<method name="requestPermission"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="accessory" type="android.hardware.usb.UsbAccessory">
+</parameter>
+<parameter name="pi" type="android.app.PendingIntent">
+</parameter>
+</method>
<field name="ACTION_USB_ACCESSORY_ATTACHED"
type="java.lang.String"
transient="false"
@@ -95586,6 +95642,17 @@
visibility="public"
>
</field>
+<field name="EXTRA_PERMISSION_GRANTED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;permission&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="USB_CONFIGURATION"
type="java.lang.String"
transient="false"
@@ -267323,7 +267390,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="arg0" type="T">
+<parameter name="t" type="T">
</parameter>
</method>
</interface>
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 539e94601049..cc1f81c5ec00 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -401,10 +401,10 @@ class ContextImpl extends Context {
return new UiModeManager();
}});
- registerService(USB_SERVICE, new StaticServiceFetcher() {
- public Object createStaticService() {
+ registerService(USB_SERVICE, new ServiceFetcher() {
+ public Object createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(USB_SERVICE);
- return new UsbManager(IUsbManager.Stub.asInterface(b));
+ return new UsbManager(ctx, IUsbManager.Stub.asInterface(b));
}});
registerService(VIBRATOR_SERVICE, new ServiceFetcher() {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 440cb5406b85..efe263336063 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1150,8 +1150,12 @@ public class DevicePolicyManager {
* fail (most commonly returning {@link #ENCRYPTION_STATUS_ACTIVE}).
*
* <p>This policy controls encryption of the secure (application data) storage area. Data
- * written to other areas (e.g. the directory returned by
- * {@link android.os.Environment#getExternalStorageDirectory()} may or may not be encrypted.
+ * written to other storage areas may or may not be encrypted, and this policy does not require
+ * or control the encryption of any other storage areas.
+ * There is one exception: If {@link android.os.Environment#isExternalStorageEmulated()} is
+ * {@code true}, then the directory returned by
+ * {@link android.os.Environment#getExternalStorageDirectory()} must be written to disk
+ * within the encrypted storage area.
*
* <p>Important Note: On some devices, it is possible to encrypt storage without requiring
* the user to create a device PIN or Password. In this case, the storage is encrypted, but
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index be65bdb40389..c79a4583f8a8 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -16,6 +16,7 @@
package android.hardware.usb;
+import android.app.PendingIntent;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbDevice;
import android.os.Bundle;
@@ -50,6 +51,25 @@ interface IUsbManager
*/
void setAccessoryPackage(in UsbAccessory accessory, String packageName);
+ /* Returns true if the caller has permission to access the device. */
+ boolean hasDevicePermission(in UsbDevice device);
+
+ /* Returns true if the caller has permission to access the accessory. */
+ boolean hasAccessoryPermission(in UsbAccessory accessory);
+
+ /* Requests permission for the given package to access the device.
+ * Will display a system dialog to query the user if permission
+ * had not already been given.
+ */
+ void requestDevicePermission(in UsbDevice device, String packageName, in PendingIntent pi);
+
+ /* Requests permission for the given package to access the accessory.
+ * Will display a system dialog to query the user if permission
+ * had not already been given. Result is returned via pi.
+ */
+ void requestAccessoryPermission(in UsbAccessory accessory, String packageName,
+ in PendingIntent pi);
+
/* Grants permission for the given UID to access the device */
void grantDevicePermission(in UsbDevice device, int uid);
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 668317909a7f..9f1b8eaab2d7 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -17,6 +17,8 @@
package android.hardware.usb;
+import android.app.PendingIntent;
+import android.content.Context;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
@@ -176,12 +178,24 @@ public class UsbManager {
*/
public static final String EXTRA_ACCESSORY = "accessory";
- private IUsbManager mService;
+ /**
+ * Name of extra added to the {@link android.app.PendingIntent}
+ * passed into
+ * {#requestPermission(android.content.Context, android.hardware.usb.UsbDevice, android.app.PendingIntent)}
+ * or
+ * {#requestPermission(android.content.Context, android.hardware.usb.UsbAccessory, android.app.PendingIntent)}
+ * containing a boolean value indicating whether the user granted permission or not.
+ */
+ public static final String EXTRA_PERMISSION_GRANTED = "permission";
+
+ private final Context mContext;
+ private final IUsbManager mService;
/**
* {@hide}
*/
- public UsbManager(IUsbManager service) {
+ public UsbManager(Context context, IUsbManager service) {
+ mContext = context;
mService = service;
}
@@ -245,7 +259,7 @@ public class UsbManager {
return new UsbAccessory[] { accessory };
}
} catch (RemoteException e) {
- Log.e(TAG, "RemoteException in getAccessoryList" , e);
+ Log.e(TAG, "RemoteException in getAccessoryList", e);
return null;
}
}
@@ -260,11 +274,87 @@ public class UsbManager {
try {
return mService.openAccessory(accessory);
} catch (RemoteException e) {
- Log.e(TAG, "RemoteException in openAccessory" , e);
+ Log.e(TAG, "RemoteException in openAccessory", e);
return null;
}
}
+ /**
+ * Returns true if the caller has permission to access the device.
+ *
+ * @param device to check permissions for
+ * @return true if caller has permission
+ */
+ public boolean hasPermission(UsbDevice device) {
+ try {
+ return mService.hasDevicePermission(device);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in hasPermission", e);
+ return false;
+ }
+ }
+
+ /**
+ * Returns true if the caller has permission to access the accessory.
+ *
+ * @param accessory to check permissions for
+ * @return true if caller has permission
+ */
+ public boolean hasPermission(UsbAccessory accessory) {
+ try {
+ return mService.hasAccessoryPermission(accessory);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in hasPermission", e);
+ return false;
+ }
+ }
+
+ /**
+ * Requests permission for the given package to access the device.
+ * This may result in a system dialog being displayed to the user
+ * if permission had not already been granted.
+ * Success or failure is returned via the {@link android.app.PendingIntent} pi.
+ * The following extras will be added to pi:
+ * <ul>
+ * <li> {@link #EXTRA_DEVICE} containing the device passed into this call
+ * <li> {@link #EXTRA_PERMISSION_GRANTED} containing boolean indicating whether
+ * permission was granted by the user
+ * </ul>
+ *
+ * @param device to request permissions for
+ * @param pi PendingIntent for returning result
+ */
+ public void requestPermission(UsbDevice device, PendingIntent pi) {
+ try {
+ mService.requestDevicePermission(device, mContext.getPackageName(), pi);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in requestPermission", e);
+ }
+ }
+
+ /**
+ * Requests permission for the given package to access the accessory.
+ * This may result in a system dialog being displayed to the user
+ * if permission had not already been granted.
+ * Success or failure is returned via the {@link android.app.PendingIntent} pi.
+ * The following extras will be added to pi:
+ * <ul>
+ * <li> {@link #EXTRA_ACCESSORY} containing the accessory passed into this call
+ * <li> {@link #EXTRA_PERMISSION_GRANTED} containing boolean indicating whether
+ * permission was granted by the user
+ * </ul>
+ *
+ * @param accessory to request permissions for
+ * @param pi PendingIntent for returning result
+ */
+ public void requestPermission(UsbAccessory accessory, PendingIntent pi) {
+ try {
+ mService.requestAccessoryPermission(accessory, mContext.getPackageName(), pi);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in requestPermission", e);
+ }
+ }
+
private static File getFunctionEnableFile(String function) {
return new File("/sys/class/usb_composite/" + function + "/enable");
}
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index ec5030c7f5db..e308c2c48a8d 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -423,9 +423,16 @@ public class Environment {
/**
* Returns whether the device has an external storage device which is
- * emulated. If true, the device does not have real external storage
- * and certain system services such as the package manager use this
+ * emulated. If true, the device does not have real external storage, and the directory
+ * returned by {@link #getExternalStorageDirectory()} will be allocated using a portion of
+ * the internal storage system.
+ *
+ * <p>Certain system services, such as the package manager, use this
* to determine where to install an application.
+ *
+ * <p>Emulated external storage may also be encrypted - see
+ * {@link android.app.admin.DevicePolicyManager#setStorageEncryption(
+ * android.content.ComponentName, boolean)} for additional details.
*/
public static boolean isExternalStorageEmulated() {
if (mIsExternalStorageEmulated == null) {
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index c078c08d2e61..0cf7ae60efa3 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -609,7 +609,7 @@ public abstract class HardwareRenderer {
DisplayList displayList = view.getDisplayList();
if (displayList != null) {
if (canvas.drawDisplayList(displayList, mRedrawClip)) {
- if (mRedrawClip.isEmpty()) {
+ if (mRedrawClip.isEmpty() || view.getParent() == null) {
view.invalidate();
} else {
view.getParent().invalidateChild(view, mRedrawClip);
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 6ef680bacfb6..f9692dad0a30 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3680,7 +3680,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
// flag coming from the child that initiated the invalidate
if (view != null) {
if ((view.mViewFlags & FADING_EDGE_MASK) != 0 &&
- view.getSolidColor() == 0 && !view.isOpaque()) {
+ view.getSolidColor() == 0) {
opaqueFlag = DIRTY;
}
if ((view.mPrivateFlags & DIRTY_MASK) != DIRTY) {
diff --git a/core/java/android/webkit/DebugFlags.java b/core/java/android/webkit/DebugFlags.java
index 2dac7e91151a..3cb5e24ec26c 100644
--- a/core/java/android/webkit/DebugFlags.java
+++ b/core/java/android/webkit/DebugFlags.java
@@ -31,7 +31,7 @@ class DebugFlags {
public static final boolean CACHE_MANAGER = false;
public static final boolean CALLBACK_PROXY = false;
public static final boolean COOKIE_MANAGER = false;
- public static final boolean COOKIE_SYNC_MANAGER = true;
+ public static final boolean COOKIE_SYNC_MANAGER = false;
public static final boolean FRAME_LOADER = false;
public static final boolean J_WEB_CORE_JAVA_BRIDGE = false;// HIGHLY VERBOSE
public static final boolean LOAD_LISTENER = false;
@@ -41,7 +41,7 @@ class DebugFlags {
public static final boolean URL_UTIL = false;
public static final boolean WEB_BACK_FORWARD_LIST = false;
public static final boolean WEB_SETTINGS = false;
- public static final boolean WEB_SYNC_MANAGER = true;
+ public static final boolean WEB_SYNC_MANAGER = false;
public static final boolean WEB_TEXT_VIEW = false;
public static final boolean WEB_VIEW = false;
public static final boolean WEB_VIEW_CORE = false;
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index eb7269b16e70..9b1f157d9c72 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -3017,7 +3017,8 @@ public class WebView extends AbsoluteLayout
}
/**
- * Start an ActionMode for finding text in this WebView.
+ * Start an ActionMode for finding text in this WebView. Only works if this
+ * WebView is attached to the view system.
* @param text If non-null, will be the initial text to search for.
* Otherwise, the last String searched for in this WebView will
* be used to start.
@@ -3027,7 +3028,7 @@ public class WebView extends AbsoluteLayout
*/
public boolean showFindDialog(String text, boolean showIme) {
FindActionModeCallback callback = new FindActionModeCallback(mContext);
- if (startActionMode(callback) == null) {
+ if (getParent() == null || startActionMode(callback) == null) {
// Could not start the action mode, so end Find on page
return false;
}
@@ -4035,15 +4036,10 @@ public class WebView extends AbsoluteLayout
}
}
- void setBaseLayer(int layer, Rect invalRect, boolean showVisualIndciator) {
+ void setBaseLayer(int layer, Region invalRegion, boolean showVisualIndicator) {
if (mNativeClass == 0)
return;
- if (invalRect == null) {
- Rect rect = new Rect(0, 0, mContentWidth, mContentHeight);
- nativeSetBaseLayer(layer, rect, showVisualIndciator);
- } else {
- nativeSetBaseLayer(layer, invalRect, showVisualIndciator);
- }
+ nativeSetBaseLayer(layer, invalRegion, showVisualIndicator);
}
private void onZoomAnimationStart() {
@@ -5570,7 +5566,7 @@ public class WebView extends AbsoluteLayout
ted.mReprocess = mDeferTouchProcess;
ted.mNativeLayer = nativeScrollableLayer(
contentX, contentY, ted.mNativeLayerRect, null);
- ted.mDontEnqueueResult = true;
+ ted.mSequence = mTouchEventQueue.nextTouchSequence();
mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
if (mDeferTouchProcess) {
// still needs to set them for compute deltaX/Y
@@ -5617,7 +5613,7 @@ public class WebView extends AbsoluteLayout
ted.mReprocess = mDeferTouchProcess;
ted.mNativeLayer = mScrollingLayer;
ted.mNativeLayerRect.set(mScrollingLayerRect);
- ted.mDontEnqueueResult = true;
+ ted.mSequence = mTouchEventQueue.nextTouchSequence();
mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
mLastSentTouchTime = eventTime;
if (mDeferTouchProcess) {
@@ -5799,7 +5795,7 @@ public class WebView extends AbsoluteLayout
ted.mReprocess = mDeferTouchProcess;
ted.mNativeLayer = mScrollingLayer;
ted.mNativeLayerRect.set(mScrollingLayerRect);
- ted.mDontEnqueueResult = true;
+ ted.mSequence = mTouchEventQueue.nextTouchSequence();
mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
}
mLastTouchUpTime = eventTime;
@@ -5822,7 +5818,7 @@ public class WebView extends AbsoluteLayout
ted.mNativeLayer = nativeScrollableLayer(
contentX, contentY,
ted.mNativeLayerRect, null);
- ted.mDontEnqueueResult = true;
+ ted.mSequence = mTouchEventQueue.nextTouchSequence();
mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
} else if (mPreventDefault != PREVENT_DEFAULT_YES){
mZoomManager.handleDoubleTap(mLastTouchX, mLastTouchY);
@@ -6040,7 +6036,7 @@ public class WebView extends AbsoluteLayout
ted.mAction = MotionEvent.ACTION_CANCEL;
ted.mNativeLayer = nativeScrollableLayer(
x, y, ted.mNativeLayerRect, null);
- ted.mDontEnqueueResult = true;
+ ted.mSequence = mTouchEventQueue.nextTouchSequence();
mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
mPreventDefault = PREVENT_DEFAULT_IGNORE;
}
@@ -7524,7 +7520,7 @@ public class WebView extends AbsoluteLayout
ted.mNativeLayer = nativeScrollableLayer(
ted.mPoints[0].x, ted.mPoints[0].y,
ted.mNativeLayerRect, null);
- ted.mDontEnqueueResult = true;
+ ted.mSequence = mTouchEventQueue.nextTouchSequence();
mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
} else if (mPreventDefault != PREVENT_DEFAULT_YES) {
mTouchMode = TOUCH_DONE_MODE;
@@ -7570,7 +7566,7 @@ public class WebView extends AbsoluteLayout
case NEW_PICTURE_MSG_ID: {
// called for new content
final WebViewCore.DrawData draw = (WebViewCore.DrawData) msg.obj;
- setBaseLayer(draw.mBaseLayer, draw.mInvalRegion.getBounds(),
+ setBaseLayer(draw.mBaseLayer, draw.mInvalRegion,
getSettings().getShowVisualIndicator());
final Point viewSize = draw.mViewSize;
WebViewCore.ViewState viewState = draw.mViewState;
@@ -7740,9 +7736,12 @@ public class WebView extends AbsoluteLayout
break;
}
TouchEventData ted = (TouchEventData) msg.obj;
- if (!ted.mDontEnqueueResult) {
- mTouchEventQueue.enqueueTouchEvent(ted);
- }
+
+ // WebCore is responding to us; remove pending timeout.
+ // It will be re-posted when needed.
+ removeMessages(PREVENT_DEFAULT_TIMEOUT);
+
+ mTouchEventQueue.enqueueTouchEvent(ted);
break;
case REQUEST_KEYBOARD:
@@ -8612,7 +8611,7 @@ public class WebView extends AbsoluteLayout
private native void nativeSetFindIsEmpty();
private native void nativeSetFindIsUp(boolean isUp);
private native void nativeSetHeightCanMeasure(boolean measure);
- private native void nativeSetBaseLayer(int layer, Rect invalRect,
+ private native void nativeSetBaseLayer(int layer, Region invalRegion,
boolean showVisualIndicator);
private native void nativeShowCursorTimed();
private native void nativeReplaceBaseContent(int content);
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index f367b93820de..b920a30fd2a9 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -827,7 +827,6 @@ final class WebViewCore {
Rect mNativeLayerRect = new Rect();
long mSequence;
boolean mNativeResult;
- boolean mDontEnqueueResult;
}
static class GeolocationPermissionsData {
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index f27ced89cbf8..942425af8e3b 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -16,6 +16,9 @@
package android.webkit;
+import java.util.LinkedList;
+import java.util.Queue;
+
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Canvas;
@@ -23,6 +26,7 @@ import android.graphics.Point;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.SystemClock;
+import android.util.FloatMath;
import android.util.Log;
import android.view.ScaleGestureDetector;
import android.view.View;
@@ -112,6 +116,24 @@ class ZoomManager {
private float mZoomCenterY;
/*
+ * Similar to mZoomCenterX(Y), these track the focus point of the scale
+ * gesture. The difference is these get updated every time when onScale is
+ * invoked no matter if a zooming really happens.
+ */
+ private float mFocusX;
+ private float mFocusY;
+
+ /*
+ * mFocusMovement keeps track of the total movement that the focus point
+ * has been through. Comparing to the difference of mCurrlen and mPrevLen,
+ * it determines if the gesture is for panning or zooming or both.
+ */
+ private static final int FOCUS_QUEUE_SIZE = 5;
+ private float mFocusMovementSum;
+ private Queue<Float> mFocusMovementQueue;
+
+
+ /*
* These values represent the point around which the screen should be
* centered after zooming. In other words it is used to determine the center
* point of the visible document after the page has finished zooming. This
@@ -196,6 +218,8 @@ class ZoomManager {
* viewport size is.
*/
setZoomOverviewWidth(WebView.DEFAULT_VIEWPORT_WIDTH);
+
+ mFocusMovementQueue = new LinkedList<Float>();
}
/**
@@ -715,10 +739,11 @@ class ZoomManager {
}
private class ScaleDetectorListener implements ScaleGestureDetector.OnScaleGestureListener {
-
public boolean onScaleBegin(ScaleGestureDetector detector) {
mInitialZoomOverview = false;
dismissZoomPicker();
+ mFocusMovementSum = 0;
+ mFocusMovementQueue.clear();
mWebView.mViewManager.startZoom();
mWebView.onPinchToZoomAnimationStart();
return true;
@@ -729,6 +754,29 @@ class ZoomManager {
float scale = Math.max(
computeScaleWithLimits(detector.getScaleFactor() * mActualScale),
getZoomOverviewScale());
+
+ float prevFocusX = mFocusX;
+ float prevFocusY = mFocusY;
+ mFocusX = detector.getFocusX();
+ mFocusY = detector.getFocusY();
+ float focusDelta = (prevFocusX == 0 && prevFocusY == 0) ? 0 :
+ FloatMath.sqrt((mFocusX - prevFocusX) * (mFocusX - prevFocusX)
+ + (mFocusY - prevFocusY) * (mFocusY - prevFocusY));
+ mFocusMovementSum += focusDelta;
+ mFocusMovementQueue.add(focusDelta);
+ if (mFocusMovementQueue.size() > FOCUS_QUEUE_SIZE) {
+ mFocusMovementSum -= mFocusMovementQueue.remove();
+ }
+ float deltaSpan = Math.abs(detector.getCurrentSpan() - detector.getPreviousSpan());
+
+ // If the user moves the fingers but keeps the same distance between them,
+ // we should do panning only.
+ if (mFocusMovementSum > deltaSpan) {
+ mFocusMovementSum = 0;
+ mFocusMovementQueue.clear();
+ return true;
+ }
+
if (mPinchToZoomAnimating || willScaleTriggerZoom(scale)) {
mPinchToZoomAnimating = true;
// limit the scale change per step
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index d92588cbc646..17b3bdad7115 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -477,8 +477,7 @@ public class HorizontalScrollView extends FrameLayout {
break;
case MotionEvent.ACTION_POINTER_DOWN: {
final int index = ev.getActionIndex();
- final float x = ev.getX(index);
- mLastMotionX = x;
+ mLastMotionX = ev.getX(index);
mActivePointerId = ev.getPointerId(index);
break;
}
@@ -1446,6 +1445,7 @@ public class HorizontalScrollView extends FrameLayout {
super.setOverScrollMode(mode);
}
+ @SuppressWarnings({"SuspiciousNameCombination"})
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 9002b1ddaf8a..72b70bc1a119 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1039,7 +1039,7 @@ public class PopupWindow {
*
* @return true if the popup is translated upwards to fit on screen
*/
- boolean findDropDownPosition(View anchor, WindowManager.LayoutParams p,
+ private boolean findDropDownPosition(View anchor, WindowManager.LayoutParams p,
int xoff, int yoff) {
anchor.getLocationInWindow(mDrawingLocation);
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index 2d164fd75812..6088654a78c1 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -83,6 +83,7 @@ public class SearchView extends LinearLayout {
private CursorAdapter mSuggestionsAdapter;
private View mSearchButton;
private View mSubmitButton;
+ private View mSearchPlate;
private View mSubmitArea;
private ImageView mCloseButton;
private View mSearchEditFrame;
@@ -190,6 +191,7 @@ public class SearchView extends LinearLayout {
mQueryTextView.setSearchView(this);
mSearchEditFrame = findViewById(R.id.search_edit_frame);
+ mSearchPlate = findViewById(R.id.search_plate);
mSubmitArea = findViewById(R.id.submit_area);
mSubmitButton = findViewById(R.id.search_go_btn);
mCloseButton = (ImageView) findViewById(R.id.search_close_btn);
@@ -258,6 +260,7 @@ public class SearchView extends LinearLayout {
mSearchable = searchable;
if (mSearchable != null) {
updateSearchAutoComplete();
+ updateQueryHint();
}
// Cache the voice search capability
mVoiceButtonEnabled = hasVoiceSearch();
@@ -575,19 +578,19 @@ public class SearchView extends LinearLayout {
}
private void updateSubmitButton(boolean hasText) {
- mSubmitButton.setVisibility(
- isSubmitAreaEnabled() ? (hasText ? VISIBLE : INVISIBLE) : GONE);
+ int visibility = GONE;
+ if (isSubmitAreaEnabled() && hasFocus() && (hasText || !mVoiceButtonEnabled)) {
+ visibility = VISIBLE;
+ }
+ mSubmitButton.setVisibility(visibility);
}
private void updateSubmitArea() {
int visibility = GONE;
- if (isSubmitAreaEnabled()) {
- if (mSubmitButton.getVisibility() == VISIBLE
- || mVoiceButton.getVisibility() == VISIBLE) {
- visibility = VISIBLE;
- } else {
- visibility = INVISIBLE;
- }
+ if (isSubmitAreaEnabled()
+ && (mSubmitButton.getVisibility() == VISIBLE
+ || mVoiceButton.getVisibility() == VISIBLE)) {
+ visibility = VISIBLE;
}
mSubmitArea.setVisibility(visibility);
}
@@ -601,6 +604,11 @@ public class SearchView extends LinearLayout {
mCloseButton.getDrawable().setState(hasText ? ENABLED_STATE_SET : EMPTY_STATE_SET);
}
+ private void updateFocusedState(boolean focused) {
+ mSearchPlate.getBackground().setState(focused ? FOCUSED_STATE_SET : EMPTY_STATE_SET);
+ mSubmitArea.getBackground().setState(focused ? FOCUSED_STATE_SET : EMPTY_STATE_SET);
+ }
+
private void setImeVisibility(boolean visible) {
InputMethodManager imm = (InputMethodManager)
getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
@@ -851,16 +859,11 @@ public class SearchView extends LinearLayout {
* Update the visibility of the voice button. There are actually two voice search modes,
* either of which will activate the button.
* @param empty whether the search query text field is empty. If it is, then the other
- * criteria apply to make the voice button visible. Otherwise the voice button will not
- * be visible - i.e., if the user has typed a query, remove the voice button.
+ * criteria apply to make the voice button visible.
*/
private void updateVoiceButton(boolean empty) {
- // If the voice button is to be visible, show it
- // Else, make it gone if the submit button is enabled, otherwise invisible to
- // avoid losing the real-estate
- int visibility = mSubmitButtonEnabled ? GONE : INVISIBLE;
-
- if (mVoiceButtonEnabled && !isIconified() && empty) {
+ int visibility = GONE;
+ if (mVoiceButtonEnabled && !isIconified() && (empty || !mSubmitButtonEnabled)) {
visibility = VISIBLE;
mSubmitButton.setVisibility(GONE);
}
@@ -958,7 +961,8 @@ public class SearchView extends LinearLayout {
}
void onTextFocusChanged() {
- updateCloseButton();
+ updateViewsVisibility(isIconified());
+ updateFocusedState(mQueryTextView.hasFocus());
}
private boolean onItemClicked(int position, int actionKey, String actionMsg) {
diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java
index bab469b01cf9..21c61bdf67ab 100644
--- a/core/java/android/widget/StackView.java
+++ b/core/java/android/widget/StackView.java
@@ -531,6 +531,8 @@ public class StackView extends AdapterViewAnimator {
@Override
protected void dispatchDraw(Canvas canvas) {
+ boolean expandClipRegion = false;
+
canvas.getClipBounds(stackInvalidateRect);
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
@@ -540,12 +542,22 @@ public class StackView extends AdapterViewAnimator {
child.getAlpha() == 0f || child.getVisibility() != VISIBLE) {
lp.resetInvalidateRect();
}
- stackInvalidateRect.union(lp.getInvalidateRect());
+ Rect childInvalidateRect = lp.getInvalidateRect();
+ if (!childInvalidateRect.isEmpty()) {
+ expandClipRegion = true;
+ stackInvalidateRect.union(childInvalidateRect);
+ }
+ }
+
+ // We only expand the clip bounds if necessary.
+ if (expandClipRegion) {
+ canvas.save(Canvas.CLIP_SAVE_FLAG);
+ canvas.clipRect(stackInvalidateRect, Region.Op.UNION);
+ super.dispatchDraw(canvas);
+ canvas.restore();
+ } else {
+ super.dispatchDraw(canvas);
}
- canvas.save(Canvas.CLIP_SAVE_FLAG);
- canvas.clipRect(stackInvalidateRect, Region.Op.UNION);
- super.dispatchDraw(canvas);
- canvas.restore();
}
private void onLayout() {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 09c1ac5e916a..13b9285f6fd5 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -4498,8 +4498,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
*/
canvas.restore();
-
- updateCursorControllerPositions();
}
private void updateCursorsPositions() {
@@ -4557,15 +4555,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* @hide
*/
protected void updateCursorControllerPositions() {
- // No need to create the controllers if they were not already
- if (mInsertionPointCursorController != null &&
- mInsertionPointCursorController.isShowing()) {
- mInsertionPointCursorController.updatePosition();
- }
- if (mSelectionModifierCursorController != null &&
- mSelectionModifierCursorController.isShowing()) {
- mSelectionModifierCursorController.updatePosition();
- }
+ // TODO remove
}
@Override
@@ -7356,14 +7346,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
if (isTextEditable() || mTextIsSelectable) {
- if (mScrollX != oldScrollX || mScrollY != oldScrollY) {
+ if (mScrollX != oldScrollX || mScrollY != oldScrollY) { // TODO remove
// Hide insertion anchor while scrolling. Leave selection.
- hideInsertionPointCursorController();
- // No need to create the controller, since there is nothing to update.
- if (mSelectionModifierCursorController != null &&
- mSelectionModifierCursorController.isShowing()) {
- mSelectionModifierCursorController.updatePosition();
- }
+ hideInsertionPointCursorController(); // TODO any motion should hide it
}
if (touchIsFinished) {
@@ -7373,7 +7358,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
handled |= imm != null && imm.showSoftInput(this, 0);
}
-
boolean selectAllGotFocus = mSelectAllOnFocus && didTouchFocusSelect();
if (!selectAllGotFocus && hasSelection()) {
startSelectionActionMode();
@@ -8653,26 +8637,31 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
- private class HandleView extends View implements ViewTreeObserver.OnScrollChangedListener {
+ private class HandleView extends View implements ViewTreeObserver.OnPreDrawListener {
private Drawable mDrawable;
- private final ScrollingPopupWindow mContainer;
- private int mPositionX;
- private int mPositionY;
+ private final PopupWindow mContainer;
+ // Position with respect to the parent TextView
+ private int mPositionX, mPositionY;
private final CursorController mController;
private boolean mIsDragging;
- private float mTouchToWindowOffsetX;
- private float mTouchToWindowOffsetY;
+ // Offset from touch position to mPosition
+ private float mTouchToWindowOffsetX, mTouchToWindowOffsetY;
private float mHotspotX;
// Offsets the hotspot point up, so that cursor is not hidden by the finger when moving up
private float mTouchOffsetY;
// Where the touch position should be on the handle to ensure a maximum cursor visibility
private float mIdealVerticalOffset;
- private int mLastParentX;
- private int mLastParentY;
+ // Parent's (TextView) position in window
+ private int mLastParentX, mLastParentY;
private float mDownPositionX, mDownPositionY;
+ // PopupWindow container absolute position with respect to the enclosing window
private int mContainerPositionX, mContainerPositionY;
- private long mTouchTimer;
+ // Visible or not (scrolled off screen), whether or not this handle should be visible
+ private boolean mIsActive = false;
+ // The insertion handle can have an associated PastePopupMenu
private boolean mIsInsertionHandle = false;
+ // Used to detect taps on the insertion handle, which will affect the PastePopupMenu
+ private long mTouchTimer;
private PastePopupMenu mPastePopupWindow;
// Touch-up filter: number of previous positions remembered
@@ -8684,12 +8673,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private int mPreviousOffsetIndex = 0;
private int mNumberPreviousOffsets = 0;
- public void startTouchUpFilter(int offset) {
+ private void startTouchUpFilter(int offset) {
mNumberPreviousOffsets = 0;
addPositionToTouchUpFilter(offset);
}
- public void addPositionToTouchUpFilter(int offset) {
+ private void addPositionToTouchUpFilter(int offset) {
if (mNumberPreviousOffsets > 0 &&
mPreviousOffsets[mPreviousOffsetIndex] == offset) {
// Make sure only actual changes of position are recorded.
@@ -8702,7 +8691,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
mNumberPreviousOffsets++;
}
- public void filterOnTouchUp() {
+ private void filterOnTouchUp() {
final long now = SystemClock.uptimeMillis();
int i = 0;
int index = mPreviousOffsetIndex;
@@ -8725,16 +8714,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
public HandleView(CursorController controller, int pos) {
super(TextView.this.mContext);
mController = controller;
- mContainer = new ScrollingPopupWindow(TextView.this.mContext, null,
+ mContainer = new PopupWindow(TextView.this.mContext, null,
com.android.internal.R.attr.textSelectHandleWindowStyle);
mContainer.setSplitTouchEnabled(true);
mContainer.setClippingEnabled(false);
mContainer.setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL);
+ mContainer.setContentView(this);
- setOrientation(pos);
+ setPosition(pos);
}
- public void setOrientation(int pos) {
+ private void setPosition(int pos) {
int handleWidth;
switch (pos) {
case LEFT: {
@@ -8774,38 +8764,48 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
final int handleHeight = mDrawable.getIntrinsicHeight();
-
mTouchOffsetY = -0.3f * handleHeight;
mIdealVerticalOffset = 0.7f * handleHeight;
+
invalidate();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- setMeasuredDimension(mDrawable.getIntrinsicWidth(),
- mDrawable.getIntrinsicHeight());
+ setMeasuredDimension(mDrawable.getIntrinsicWidth(), mDrawable.getIntrinsicHeight());
}
public void show() {
- if (!isPositionVisible()) {
- hide();
- return;
- }
- mContainer.setContentView(this);
- mContainerPositionX = mPositionX;
- mContainerPositionY = mPositionY - TextView.this.getHeight();
- mContainer.showAsDropDown(TextView.this, mContainerPositionX, mContainerPositionY);
+ updateContainerPosition();
+ if (isShowing()) {
+ mContainer.update(mContainerPositionX, mContainerPositionY,
+ mRight - mLeft, mBottom - mTop);
- // Hide paste view when handle is moved on screen.
- hidePastePopupWindow();
+ hidePastePopupWindow();
+ } else {
+ mContainer.showAtLocation(TextView.this, 0,
+ mContainerPositionX, mContainerPositionY);
+
+ mIsActive = true;
+
+ ViewTreeObserver vto = TextView.this.getViewTreeObserver();
+ vto.addOnPreDrawListener(this);
+ }
}
- public void hide() {
+ private void dismiss() {
mIsDragging = false;
mContainer.dismiss();
hidePastePopupWindow();
+ }
+
+ public void hide() {
+ dismiss();
+
+ mIsActive = false;
+
ViewTreeObserver vto = TextView.this.getViewTreeObserver();
- vto.removeOnScrollChangedListener(this);
+ vto.removeOnPreDrawListener(this);
}
public boolean isShowing() {
@@ -8856,44 +8856,59 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private void moveTo(int x, int y) {
mPositionX = x - TextView.this.mScrollX;
mPositionY = y - TextView.this.mScrollY;
- if (isPositionVisible()) {
- int[] coords = null;
- if (mContainer.isShowing()) {
- final int containerPositionX = mPositionX;
- final int containerPositionY = mPositionY - TextView.this.getHeight();
-
- if (containerPositionX != mContainerPositionX ||
- containerPositionY != mContainerPositionY) {
- mContainerPositionX = containerPositionX;
- mContainerPositionY = containerPositionY;
-
- mContainer.update(TextView.this, mContainerPositionX, mContainerPositionY,
- mRight - mLeft, mBottom - mTop);
-
- // Hide paste popup window as soon as a scroll occurs.
- hidePastePopupWindow();
- }
- } else {
- show();
+
+ if (mIsDragging) {
+ TextView.this.getLocationInWindow(mTempCoords);
+ if (mTempCoords[0] != mLastParentX || mTempCoords[1] != mLastParentY) {
+ mTouchToWindowOffsetX += mTempCoords[0] - mLastParentX;
+ mTouchToWindowOffsetY += mTempCoords[1] - mLastParentY;
+ mLastParentX = mTempCoords[0];
+ mLastParentY = mTempCoords[1];
}
+ // Hide paste popup window as soon as the handle is dragged.
+ hidePastePopupWindow();
+ }
+ }
- if (mIsDragging) {
- if (coords == null) {
- coords = mTempCoords;
- TextView.this.getLocationInWindow(coords);
+ /**
+ * Updates the global container's position.
+ * @return whether or not the position has actually changed
+ */
+ private boolean updateContainerPosition() {
+ // TODO Prevent this using different HandleView subclasses
+ mController.updateOffset(this, mController.getCurrentOffset(this));
+ TextView.this.getLocationInWindow(mTempCoords);
+ final int containerPositionX = mTempCoords[0] + mPositionX;
+ final int containerPositionY = mTempCoords[1] + mPositionY;
+
+ if (containerPositionX != mContainerPositionX ||
+ containerPositionY != mContainerPositionY) {
+ mContainerPositionX = containerPositionX;
+ mContainerPositionY = containerPositionY;
+ return true;
+ }
+ return false;
+ }
+
+ public boolean onPreDraw() {
+ if (updateContainerPosition()) {
+ if (isPositionVisible()) {
+ mContainer.update(mContainerPositionX, mContainerPositionY,
+ mRight - mLeft, mBottom - mTop);
+
+ if (mIsActive && !isShowing()) {
+ show();
}
- if (coords[0] != mLastParentX || coords[1] != mLastParentY) {
- mTouchToWindowOffsetX += coords[0] - mLastParentX;
- mTouchToWindowOffsetY += coords[1] - mLastParentY;
- mLastParentX = coords[0];
- mLastParentY = coords[1];
+ } else {
+ if (isShowing()) {
+ dismiss();
}
- // Hide paste popup window as soon as the handle is dragged.
- hidePastePopupWindow();
}
- } else {
- hide();
+
+ // Hide paste popup as soon as the view is scrolled or moved
+ hidePastePopupWindow();
}
+ return true;
}
@Override
@@ -8979,7 +8994,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return mIsDragging;
}
- void positionAtCursor(final int offset) {
+ void positionAtCursor(int offset) {
addPositionToTouchUpFilter(offset);
final int width = mDrawable.getIntrinsicWidth();
final int height = mDrawable.getIntrinsicHeight();
@@ -9013,50 +9028,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
mPastePopupWindow.hide();
}
}
-
- /**
- * A popup window, attached to a view, and that listens to scroll events in its anchors'
- * view hierarchy, so that it is automatically moved on such events.
- */
- private class ScrollingPopupWindow extends PopupWindow {
-
- private int[] mDrawingLocations = new int[2];
-
- public ScrollingPopupWindow(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- @Override
- public boolean findDropDownPosition(View anchor, WindowManager.LayoutParams p,
- int xoff, int yoff) {
- anchor.getLocationInWindow(mDrawingLocations);
- p.x = mDrawingLocations[0] + xoff;
- p.y = mDrawingLocations[1] + anchor.getHeight() + yoff;
-
- // Hide paste popup as soon as the view is scrolled.
- hidePastePopupWindow();
-
- if (!isPositionVisible()) {
- dismiss();
- onHandleBecomeInvisible();
- }
-
- return false;
- }
- }
-
- public void onScrollChanged() {
- if (isPositionVisible()) {
- show();
- ViewTreeObserver vto = TextView.this.getViewTreeObserver();
- vto.removeOnScrollChangedListener(this);
- }
- }
-
- public void onHandleBecomeInvisible() {
- ViewTreeObserver vto = TextView.this.getViewTreeObserver();
- vto.addOnScrollChangedListener(this);
- }
}
private class InsertionPointCursorController implements CursorController {
@@ -9074,7 +9045,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
public void show(int delayBeforePaste) {
- updatePosition();
+ getHandle().show();
hideDelayed();
removePastePopupCallback();
final long durationSinceCutOrCopy = SystemClock.uptimeMillis() - sLastCutOrCopyTime;
@@ -9213,7 +9184,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (mEndHandle == null) mEndHandle = new HandleView(this, HandleView.RIGHT);
mIsShowing = true;
- updatePosition();
mStartHandle.show();
mEndHandle.show();
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index a41b348af629..586ba87707bf 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -43,6 +43,7 @@ import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.Window;
import android.widget.AdapterView;
+import android.widget.FrameLayout;
import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -957,4 +958,60 @@ public class ActionBarView extends ViewGroup {
}
}
}
+
+ private static class HomeView extends FrameLayout {
+ private View mUpView;
+ private View mIconView;
+
+ public HomeView(Context context) {
+ this(context, null);
+ }
+
+ public HomeView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ mUpView = findViewById(com.android.internal.R.id.up);
+ mIconView = (ImageView) findViewById(com.android.internal.R.id.home);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ measureChildWithMargins(mUpView, widthMeasureSpec, 0, heightMeasureSpec, 0);
+ final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams();
+ int width = upLp.leftMargin + mUpView.getMeasuredWidth() + upLp.rightMargin;
+ int height = upLp.topMargin + mUpView.getMeasuredHeight() + upLp.bottomMargin;
+ measureChildWithMargins(mIconView, widthMeasureSpec, width, heightMeasureSpec, 0);
+ final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams();
+ width += iconLp.leftMargin + mIconView.getMeasuredWidth() + iconLp.rightMargin;
+ height = Math.max(height,
+ iconLp.topMargin + mIconView.getMeasuredHeight() + iconLp.bottomMargin);
+ setMeasuredDimension(width, height);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ final int vCenter = (b - t) / 2;
+ int width = r - l;
+ if (mUpView.getVisibility() != GONE) {
+ final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams();
+ final int upHeight = mUpView.getMeasuredHeight();
+ final int upWidth = mUpView.getMeasuredWidth();
+ final int upTop = t + vCenter - upHeight / 2;
+ mUpView.layout(l, upTop, l + upWidth, upTop + upHeight);
+ final int upOffset = upLp.leftMargin + upWidth + upLp.rightMargin;
+ width -= upOffset;
+ l += upOffset;
+ }
+ final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams();
+ final int iconHeight = mIconView.getMeasuredHeight();
+ final int iconWidth = mIconView.getMeasuredWidth();
+ final int hCenter = (r - l) / 2;
+ final int iconLeft = l + iconLp.leftMargin + hCenter - iconWidth / 2;
+ final int iconTop = t + iconLp.topMargin + vCenter - iconHeight / 2;
+ mIconView.layout(iconLeft, iconTop, iconLeft + iconWidth, iconTop + iconHeight);
+ }
+ }
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0ad174fbf38f..c684e7e7b871 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1379,12 +1379,6 @@
android:excludeFromRecents="true">
</activity>
- <activity android:name="com.android.server.usb.UsbResolverActivity"
- android:theme="@style/Theme.Holo.Dialog.Alert"
- android:finishOnCloseSystemDialogs="true"
- android:excludeFromRecents="true">
- </activity>
-
<service android:name="com.android.server.LoadAverageService"
android:exported="true" />
diff --git a/core/res/res/drawable-hdpi/text_cursor_holo_dark.9.png b/core/res/res/drawable-hdpi/text_cursor_holo_dark.9.png
index b9435b645117..ae77fa0cadbc 100644
--- a/core/res/res/drawable-hdpi/text_cursor_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/text_cursor_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/text_cursor_holo_light.9.png b/core/res/res/drawable-hdpi/text_cursor_holo_light.9.png
index 477d820cc965..c6bdfcc6a3bd 100644
--- a/core/res/res/drawable-hdpi/text_cursor_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/text_cursor_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_cursor_holo_dark.9.png b/core/res/res/drawable-mdpi/text_cursor_holo_dark.9.png
index b9435b645117..ae77fa0cadbc 100644
--- a/core/res/res/drawable-mdpi/text_cursor_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/text_cursor_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_cursor_holo_light.9.png b/core/res/res/drawable-mdpi/text_cursor_holo_light.9.png
index 477d820cc965..c6bdfcc6a3bd 100644
--- a/core/res/res/drawable-mdpi/text_cursor_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/text_cursor_holo_light.9.png
Binary files differ
diff --git a/core/res/res/layout/action_bar_home.xml b/core/res/res/layout/action_bar_home.xml
index 7867577a9442..c82f91d7dfd8 100644
--- a/core/res/res/layout/action_bar_home.xml
+++ b/core/res/res/layout/action_bar_home.xml
@@ -14,14 +14,14 @@
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:background="?android:attr/selectableItemBackground"
- android:orientation="horizontal">
+<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="top|left"
+ android:layout_gravity="center_vertical|left"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -33,4 +33,4 @@
android:paddingRight="16dip"
android:layout_gravity="center"
android:scaleType="center" />
-</LinearLayout>
+</view>
diff --git a/core/res/res/layout/search_view.xml b/core/res/res/layout/search_view.xml
index 93b6deb6e57e..c52b73f874d2 100644
--- a/core/res/res/layout/search_view.xml
+++ b/core/res/res/layout/search_view.xml
@@ -54,6 +54,10 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical"
+ android:layout_marginLeft="4dip"
+ android:layout_marginRight="4dip"
+ android:layout_marginTop="4dip"
+ android:layout_marginBottom="4dip"
android:orientation="horizontal">
<!-- Inner layout contains the app icon, button(s) and EditText -->
diff --git a/core/res/res/raw-xlarge/incognito_mode_start_page.html b/core/res/res/raw-xlarge/incognito_mode_start_page.html
new file mode 100644
index 000000000000..492658d29484
--- /dev/null
+++ b/core/res/res/raw-xlarge/incognito_mode_start_page.html
@@ -0,0 +1,24 @@
+<html>
+ <head>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"/>
+ <title>New incognito tab</title>
+ </head>
+ <body>
+ <p><strong>You've gone incognito</strong>. Pages you view in this tab
+ won't appear in your browser history or search history, and they won't
+ leave other traces, like cookies, on your device after you close the
+ incognito tab. Any files you download or bookmarks you create will be
+ preserved, however.</p>
+
+ <p><strong>Going incognito doesn't affect the behavior of other people,
+ servers, or software. Be wary of:</strong></p>
+
+ <ul>
+ <li>Websites that collect or share information about you</li>
+ <li>Internet service providers or employers that track the pages you visit</li>
+ <li>Malicious software that tracks your keystrokes in exchange for free smileys</li>
+ <li>Surveillance by secret agents</li>
+ <li>People standing behind you</li>
+ </ul>
+ </body>
+</html>
diff --git a/docs/html/images/avd-manager.png b/docs/html/images/avd-manager.png
index 69ce972f8dbc..c33d8a885177 100644
--- a/docs/html/images/avd-manager.png
+++ b/docs/html/images/avd-manager.png
Binary files differ
diff --git a/docs/html/images/billing_package.png b/docs/html/images/billing_package.png
index ec04c2d98153..951e117d4446 100755..100644
--- a/docs/html/images/billing_package.png
+++ b/docs/html/images/billing_package.png
Binary files differ
diff --git a/docs/html/images/developing/adt-props-isLib.png b/docs/html/images/developing/adt-props-isLib.png
index 18bdb3392380..49c911175b96 100644
--- a/docs/html/images/developing/adt-props-isLib.png
+++ b/docs/html/images/developing/adt-props-isLib.png
Binary files differ
diff --git a/docs/html/images/developing/adt-props-libRef.png b/docs/html/images/developing/adt-props-libRef.png
index e61df51424e4..73bccbd0e6d4 100644
--- a/docs/html/images/developing/adt-props-libRef.png
+++ b/docs/html/images/developing/adt-props-libRef.png
Binary files differ
diff --git a/docs/html/images/licensing_add_library.png b/docs/html/images/licensing_add_library.png
index 90b4435ab017..3bbe6d5c6efc 100644
--- a/docs/html/images/licensing_add_library.png
+++ b/docs/html/images/licensing_add_library.png
Binary files differ
diff --git a/docs/html/images/licensing_gapis_8.png b/docs/html/images/licensing_gapis_8.png
index 43ad26233261..480d98992143 100644
--- a/docs/html/images/licensing_gapis_8.png
+++ b/docs/html/images/licensing_gapis_8.png
Binary files differ
diff --git a/docs/html/images/licensing_package.png b/docs/html/images/licensing_package.png
index 5da5632672b6..eb2c5cfd3f2c 100644
--- a/docs/html/images/licensing_package.png
+++ b/docs/html/images/licensing_package.png
Binary files differ
diff --git a/media/java/android/mtp/MtpClient.java b/media/java/android/mtp/MtpClient.java
index 40e2f9beed36..d25dcb9f5a68 100644
--- a/media/java/android/mtp/MtpClient.java
+++ b/media/java/android/mtp/MtpClient.java
@@ -16,6 +16,7 @@
package android.mtp;
+import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -41,6 +42,9 @@ public class MtpClient {
private static final String TAG = "MtpClient";
+ private static final String ACTION_USB_PERMISSION =
+ "android.mtp.MtpClient.action.USB_PERMISSION";
+
private final Context mContext;
private final UsbManager mUsbManager;
private final ArrayList<Listener> mListeners = new ArrayList<Listener>();
@@ -49,29 +53,47 @@ public class MtpClient {
// mDevices is also used for synchronization in this class.
private final HashMap<String, MtpDevice> mDevices = new HashMap<String, MtpDevice>();
+ private final PendingIntent mPermissionIntent;
+
private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
UsbDevice usbDevice = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
String deviceName = usbDevice.getDeviceName();
synchronized (mDevices) {
MtpDevice mtpDevice = mDevices.get(deviceName);
- if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) {
+ if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
if (mtpDevice == null) {
mtpDevice = openDeviceLocked(usbDevice);
}
if (mtpDevice != null) {
- mDevices.put(deviceName, mtpDevice);
for (Listener listener : mListeners) {
listener.deviceAdded(mtpDevice);
}
}
- } else if (mtpDevice != null) {
- mDevices.remove(deviceName);
- for (Listener listener : mListeners) {
- listener.deviceRemoved(mtpDevice);
+ } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
+ if (mtpDevice != null) {
+ mDevices.remove(deviceName);
+ for (Listener listener : mListeners) {
+ listener.deviceRemoved(mtpDevice);
+ }
+ }
+ } else if (ACTION_USB_PERMISSION.equals(action)) {
+ boolean permission = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED,
+ false);
+ Log.d(TAG, "ACTION_USB_PERMISSION: " + permission);
+ if (permission) {
+ if (mtpDevice == null) {
+ mtpDevice = openDeviceLocked(usbDevice);
+ }
+ if (mtpDevice != null) {
+ for (Listener listener : mListeners) {
+ listener.deviceAdded(mtpDevice);
+ }
+ }
}
}
}
@@ -126,10 +148,11 @@ public class MtpClient {
public MtpClient(Context context) {
mContext = context;
mUsbManager = (UsbManager)context.getSystemService(Context.USB_SERVICE);
-
+ mPermissionIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_USB_PERMISSION), 0);
IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
+ filter.addAction(ACTION_USB_PERMISSION);
context.registerReceiver(mUsbReceiver, filter);
}
@@ -142,9 +165,14 @@ public class MtpClient {
*/
private MtpDevice openDeviceLocked(UsbDevice usbDevice) {
if (isCamera(usbDevice)) {
- MtpDevice mtpDevice = new MtpDevice(usbDevice);
- if (mtpDevice.open(mUsbManager)) {
- return mtpDevice;
+ if (!mUsbManager.hasPermission(usbDevice)) {
+ mUsbManager.requestPermission(usbDevice, mPermissionIntent);
+ } else {
+ MtpDevice mtpDevice = new MtpDevice(usbDevice);
+ if (mtpDevice.open(mUsbManager)) {
+ mDevices.put(usbDevice.getDeviceName(), mtpDevice);
+ return mtpDevice;
+ }
}
}
return null;
@@ -218,13 +246,8 @@ public class MtpClient {
// Query the USB manager since devices might have attached
// before we added our listener.
for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) {
- String deviceName = usbDevice.getDeviceName();
- MtpDevice mtpDevice = mDevices.get(deviceName);
- if (mtpDevice == null) {
- mtpDevice = openDeviceLocked(usbDevice);
- }
- if (mtpDevice != null) {
- mDevices.put(deviceName, mtpDevice);
+ if (mDevices.get(usbDevice.getDeviceName()) == null) {
+ openDeviceLocked(usbDevice);
}
}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index d3d175089680..fee245f4b456 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -8,6 +8,7 @@
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.GET_TASKS" />
+ <uses-permission android:name="android.permission.MANAGE_USB" />
<application
android:persistent="true"
@@ -39,5 +40,22 @@
android:exported="true">
</activity>
+ <!-- started from UsbDeviceSettingsManager -->
+ <activity android:name=".usb.UsbPermissionActivity"
+ android:exported="true"
+ android:permission="android.permission.MANAGE_USB"
+ android:theme="@*android:style/Theme.Holo.Dialog.Alert"
+ android:finishOnCloseSystemDialogs="true"
+ android:excludeFromRecents="true">
+ </activity>
+
+ <!-- started from UsbDeviceSettingsManager -->
+ <activity android:name=".usb.UsbResolverActivity"
+ android:exported="true"
+ android:permission="android.permission.MANAGE_USB"
+ android:theme="@*android:style/Theme.Holo.Dialog.Alert"
+ android:finishOnCloseSystemDialogs="true"
+ android:excludeFromRecents="true">
+ </activity>
</application>
</manifest>
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml b/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml
index bfa6c36a096e..3f172e68f3f8 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar_recent_item.xml
@@ -38,8 +38,8 @@
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
- android:layout_marginLeft="123dip"
- android:layout_marginTop="16dip"
+ android:layout_marginLeft="131dip"
+ android:layout_marginTop="13dip"
android:maxWidth="@dimen/status_bar_recents_thumbnail_max_width"
android:maxHeight="@dimen/status_bar_recents_thumbnail_max_height"
android:adjustViewBounds="true"
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel.xml
index eda19b742d17..42940be6f1b8 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar_recent_panel.xml
@@ -51,7 +51,7 @@
android:stackFromBottom="true"
android:fadingEdge="vertical"
android:scrollbars="none"
- android:fadingEdgeLength="30dip"
+ android:fadingEdgeLength="20dip"
android:listSelector="@drawable/recents_thumbnail_bg_selector"
/>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index ebd48e7b68af..becad6aa24f0 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -115,4 +115,11 @@
<!-- Label of a toggle switch to disable use of the physical keyboard in favor of the IME. [CHAR LIMIT=25] -->
<string name="status_bar_use_physical_keyboard">Use physical keyboard</string>
+
+ <!-- Prompt for the USB device permission dialog [CHAR LIMIT=80] -->
+ <string name="usb_device_permission_prompt">Allow the application %1$s to access the USB device?</string>
+
+ <!-- Prompt for the USB accessory permission dialog [CHAR LIMIT=80] -->
+ <string name="usb_accessory_permission_prompt">Allow the application %1$s to access the USB accessory?</string>
+
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDisconnectedReceiver.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDisconnectedReceiver.java
new file mode 100644
index 000000000000..1edebbbacd79
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDisconnectedReceiver.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.usb;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.usb.UsbAccessory;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
+
+// This class is used to close UsbPermissionsActivity and UsbResolverActivity
+// if their device/accessory is disconnected while the dialog is still open
+class UsbDisconnectedReceiver extends BroadcastReceiver {
+ private final Activity mActivity;
+ private UsbDevice mDevice;
+ private UsbAccessory mAccessory;
+
+ public UsbDisconnectedReceiver(Activity activity, UsbDevice device) {
+ mActivity = activity;
+ mDevice = device;
+
+ IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED);
+ activity.registerReceiver(this, filter);
+ }
+
+ public UsbDisconnectedReceiver(Activity activity, UsbAccessory accessory) {
+ mActivity = activity;
+ mAccessory = accessory;
+
+ IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
+ activity.registerReceiver(this, filter);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
+ UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ if (device != null && device.equals(mDevice)) {
+ mActivity.finish();
+ }
+ } else if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) {
+ UsbAccessory accessory =
+ (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
+ if (accessory != null && accessory.equals(mAccessory)) {
+ mActivity.finish();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
new file mode 100644
index 000000000000..f1784df0e425
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
@@ -0,0 +1,170 @@
+/*
+ * 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.systemui.usb;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.hardware.usb.IUsbManager;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbAccessory;
+import android.hardware.usb.UsbManager;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.TextView;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+
+import com.android.systemui.R;
+
+public class UsbPermissionActivity extends AlertActivity
+ implements DialogInterface.OnClickListener, CheckBox.OnCheckedChangeListener {
+
+ private static final String TAG = "UsbPermissionActivity";
+
+ private CheckBox mAlwaysCheck;
+ private TextView mClearDefaultHint;
+ private UsbDevice mDevice;
+ private UsbAccessory mAccessory;
+ private PendingIntent mPendingIntent;
+ private String mPackageName;
+ private int mUid;
+ private boolean mPermissionGranted;
+ private UsbDisconnectedReceiver mDisconnectedReceiver;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ Intent intent = getIntent();
+ mDevice = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ mAccessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
+ mPendingIntent = (PendingIntent)intent.getParcelableExtra(Intent.EXTRA_INTENT);
+ mUid = intent.getIntExtra("uid", 0);
+ mPackageName = intent.getStringExtra("package");
+
+ PackageManager packageManager = getPackageManager();
+ ApplicationInfo aInfo;
+ try {
+ aInfo = packageManager.getApplicationInfo(mPackageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "unable to look up package name", e);
+ finish();
+ return;
+ }
+ String appName = aInfo.loadLabel(packageManager).toString();
+
+ final AlertController.AlertParams ap = mAlertParams;
+ ap.mIcon = aInfo.loadIcon(packageManager);
+ ap.mTitle = appName;
+ if (mDevice == null) {
+ ap.mMessage = getString(R.string.usb_accessory_permission_prompt, appName);
+ mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mAccessory);
+ } else {
+ ap.mMessage = getString(R.string.usb_device_permission_prompt, appName);
+ mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mDevice);
+ }
+ ap.mPositiveButtonText = getString(com.android.internal.R.string.ok);
+ ap.mNegativeButtonText = getString(com.android.internal.R.string.cancel);
+ ap.mPositiveButtonListener = this;
+ ap.mNegativeButtonListener = this;
+
+ // add "always use" checkbox
+ LayoutInflater inflater = (LayoutInflater)getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ ap.mView = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null);
+ mAlwaysCheck = (CheckBox)ap.mView.findViewById(com.android.internal.R.id.alwaysUse);
+ mAlwaysCheck.setText(com.android.internal.R.string.alwaysUse);
+ mAlwaysCheck.setOnCheckedChangeListener(this);
+ mClearDefaultHint = (TextView)ap.mView.findViewById(
+ com.android.internal.R.id.clearDefaultHint);
+ mClearDefaultHint.setVisibility(View.GONE);
+
+ setupAlert();
+
+ }
+
+ @Override
+ public void onDestroy() {
+ IBinder b = ServiceManager.getService(USB_SERVICE);
+ IUsbManager service = IUsbManager.Stub.asInterface(b);
+
+ // send response via pending intent
+ Intent intent = new Intent();
+ try {
+ if (mDevice != null) {
+ intent.putExtra(UsbManager.EXTRA_DEVICE, mDevice);
+ if (mPermissionGranted) {
+ service.grantDevicePermission(mDevice, mUid);
+ if (mAlwaysCheck.isChecked()) {
+ service.setDevicePackage(mDevice, mPackageName);
+ }
+ }
+ }
+ if (mAccessory != null) {
+ intent.putExtra(UsbManager.EXTRA_ACCESSORY, mAccessory);
+ if (mPermissionGranted) {
+ service.grantAccessoryPermission(mAccessory, mUid);
+ if (mAlwaysCheck.isChecked()) {
+ service.setAccessoryPackage(mAccessory, mPackageName);
+ }
+ }
+ }
+ intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, mPermissionGranted);
+ mPendingIntent.send(this, 0, intent);
+ } catch (PendingIntent.CanceledException e) {
+ Log.w(TAG, "PendingIntent was cancelled");
+ } catch (RemoteException e) {
+ Log.e(TAG, "IUsbService connection failed", e);
+ }
+
+ if (mDisconnectedReceiver != null) {
+ unregisterReceiver(mDisconnectedReceiver);
+ }
+ super.onDestroy();
+ }
+
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == AlertDialog.BUTTON_POSITIVE) {
+ mPermissionGranted = true;
+ }
+ finish();
+ }
+
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (mClearDefaultHint == null) return;
+
+ if(isChecked) {
+ mClearDefaultHint.setVisibility(View.VISIBLE);
+ } else {
+ mClearDefaultHint.setVisibility(View.GONE);
+ }
+ }
+}
diff --git a/services/java/com/android/server/usb/UsbResolverActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java
index e8a09a5cca71..84d73dd9301d 100644
--- a/services/java/com/android/server/usb/UsbResolverActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.usb;
+package com.android.systemui.usb;
import com.android.internal.app.ResolverActivity;
@@ -39,6 +39,10 @@ public class UsbResolverActivity extends ResolverActivity {
public static final String TAG = "UsbResolverActivity";
public static final String EXTRA_RESOLVE_INFOS = "rlist";
+ private UsbDevice mDevice;
+ private UsbAccessory mAccessory;
+ private UsbDisconnectedReceiver mDisconnectedReceiver;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
Intent intent = getIntent();
@@ -50,7 +54,6 @@ public class UsbResolverActivity extends ResolverActivity {
}
Intent target = (Intent)targetParcelable;
ArrayList<ResolveInfo> rList = intent.getParcelableArrayListExtra(EXTRA_RESOLVE_INFOS);
- Log.d(TAG, "rList.size() " + rList.size());
CharSequence title = getResources().getText(com.android.internal.R.string.chooseUsbActivity);
super.onCreate(savedInstanceState, target, title, null, rList,
true, /* Set alwaysUseOption to true to enable "always use this app" checkbox. */
@@ -58,6 +61,27 @@ public class UsbResolverActivity extends ResolverActivity {
This is necessary because this activity is needed for the user to allow
the application permission to access the device */
);
+
+ mDevice = (UsbDevice)target.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ if (mDevice != null) {
+ mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mDevice);
+ } else {
+ mAccessory = (UsbAccessory)target.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
+ if (mAccessory == null) {
+ Log.e(TAG, "no device or accessory");
+ finish();
+ return;
+ }
+ mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mAccessory);
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ if (mDisconnectedReceiver != null) {
+ unregisterReceiver(mDisconnectedReceiver);
+ }
+ super.onDestroy();
}
protected void onIntentSelected(ResolveInfo ri, Intent intent, boolean alwaysCheck) {
@@ -65,28 +89,24 @@ public class UsbResolverActivity extends ResolverActivity {
IBinder b = ServiceManager.getService(USB_SERVICE);
IUsbManager service = IUsbManager.Stub.asInterface(b);
int uid = ri.activityInfo.applicationInfo.uid;
- String action = intent.getAction();
- if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
- UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ if (mDevice != null) {
// grant permission for the device
- service.grantDevicePermission(device, uid);
+ service.grantDevicePermission(mDevice, uid);
// set or clear default setting
if (alwaysCheck) {
- service.setDevicePackage(device, ri.activityInfo.packageName);
+ service.setDevicePackage(mDevice, ri.activityInfo.packageName);
} else {
- service.setDevicePackage(device, null);
+ service.setDevicePackage(mDevice, null);
}
- } else if (UsbManager.ACTION_USB_ACCESSORY_ATTACHED.equals(action)) {
- UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(
- UsbManager.EXTRA_ACCESSORY);
+ } else if (mAccessory != null) {
// grant permission for the accessory
- service.grantAccessoryPermission(accessory, uid);
+ service.grantAccessoryPermission(mAccessory, uid);
// set or clear default setting
if (alwaysCheck) {
- service.setAccessoryPackage(accessory, ri.activityInfo.packageName);
+ service.setAccessoryPackage(mAccessory, ri.activityInfo.packageName);
} else {
- service.setAccessoryPackage(accessory, null);
+ service.setAccessoryPackage(mAccessory, null);
}
}
diff --git a/services/java/com/android/server/usb/UsbDeviceSettingsManager.java b/services/java/com/android/server/usb/UsbDeviceSettingsManager.java
index 2f22fe1a5d7c..29e6f94d5971 100644
--- a/services/java/com/android/server/usb/UsbDeviceSettingsManager.java
+++ b/services/java/com/android/server/usb/UsbDeviceSettingsManager.java
@@ -16,11 +16,13 @@
package com.android.server.usb;
+import android.app.PendingIntent;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
@@ -602,50 +604,20 @@ class UsbDeviceSettingsManager {
}
public void deviceAttached(UsbDevice device) {
- Intent deviceIntent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
- deviceIntent.putExtra(UsbManager.EXTRA_DEVICE, device);
- deviceIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
+ intent.putExtra(UsbManager.EXTRA_DEVICE, device);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
ArrayList<ResolveInfo> matches;
String defaultPackage;
synchronized (mLock) {
- matches = getDeviceMatchesLocked(device, deviceIntent);
+ matches = getDeviceMatchesLocked(device, intent);
// Launch our default activity directly, if we have one.
// Otherwise we will start the UsbResolverActivity to allow the user to choose.
defaultPackage = mDevicePreferenceMap.get(new DeviceFilter(device));
}
- int count = matches.size();
- // don't show the resolver activity if there are no choices available
- if (count == 0) return;
-
- if (defaultPackage != null) {
- for (int i = 0; i < count; i++) {
- ResolveInfo rInfo = matches.get(i);
- if (rInfo.activityInfo != null &&
- defaultPackage.equals(rInfo.activityInfo.packageName)) {
- try {
- deviceIntent.setComponent(new ComponentName(
- defaultPackage, rInfo.activityInfo.name));
- mContext.startActivity(deviceIntent);
- } catch (ActivityNotFoundException e) {
- Log.e(TAG, "startActivity failed", e);
- }
- return;
- }
- }
- }
-
- Intent intent = new Intent(mContext, UsbResolverActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
- intent.putExtra(Intent.EXTRA_INTENT, deviceIntent);
- intent.putParcelableArrayListExtra(UsbResolverActivity.EXTRA_RESOLVE_INFOS, matches);
- try {
- mContext.startActivity(intent);
- } catch (ActivityNotFoundException e) {
- Log.w(TAG, "unable to start UsbResolverActivity");
- }
+ resolveActivity(intent, matches, defaultPackage, device, null);
}
public void deviceDetached(UsbDevice device) {
@@ -656,49 +628,86 @@ class UsbDeviceSettingsManager {
}
public void accessoryAttached(UsbAccessory accessory) {
- Intent accessoryIntent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
- accessoryIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
- accessoryIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
+ intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
ArrayList<ResolveInfo> matches;
String defaultPackage;
synchronized (mLock) {
- matches = getAccessoryMatchesLocked(accessory, accessoryIntent);
+ matches = getAccessoryMatchesLocked(accessory, intent);
// Launch our default activity directly, if we have one.
// Otherwise we will start the UsbResolverActivity to allow the user to choose.
defaultPackage = mAccessoryPreferenceMap.get(new AccessoryFilter(accessory));
}
+ resolveActivity(intent, matches, defaultPackage, null, accessory);
+ }
+
+ private void resolveActivity(Intent intent, ArrayList<ResolveInfo> matches,
+ String defaultPackage, UsbDevice device, UsbAccessory accessory) {
int count = matches.size();
// don't show the resolver activity if there are no choices available
if (count == 0) return;
- if (defaultPackage != null) {
+ ResolveInfo defaultRI = null;
+ if (count == 1 && defaultPackage == null) {
+ // Check to see if our single choice is on the system partition.
+ // If so, treat it as our default without calling UsbResolverActivity
+ ResolveInfo rInfo = matches.get(0);
+ if (rInfo.activityInfo != null &&
+ rInfo.activityInfo.applicationInfo != null &&
+ (rInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ defaultRI = rInfo;
+ int uid = rInfo.activityInfo.applicationInfo.uid;
+ // grant permission
+ if (device != null) {
+ grantDevicePermission(device, uid);
+ } else if (accessory != null) {
+ grantAccessoryPermission(accessory, uid);
+ }
+ }
+ }
+
+ if (defaultRI == null && defaultPackage != null) {
+ // look for default activity
for (int i = 0; i < count; i++) {
ResolveInfo rInfo = matches.get(i);
if (rInfo.activityInfo != null &&
defaultPackage.equals(rInfo.activityInfo.packageName)) {
- try {
- accessoryIntent.setComponent(new ComponentName(
- defaultPackage, rInfo.activityInfo.name));
- mContext.startActivity(accessoryIntent);
- } catch (ActivityNotFoundException e) {
- Log.e(TAG, "startActivity failed", e);
- }
- return;
+ defaultRI = rInfo;
+ break;
}
}
}
- Intent intent = new Intent(mContext, UsbResolverActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ if (defaultRI != null) {
+ // start default activity directly
+ try {
+ intent.setComponent(
+ new ComponentName(defaultRI.activityInfo.packageName,
+ defaultRI.activityInfo.name));
+ mContext.startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "startActivity failed", e);
+ }
+ } else {
+ long identity = Binder.clearCallingIdentity();
- intent.putExtra(Intent.EXTRA_INTENT, accessoryIntent);
- intent.putParcelableArrayListExtra(UsbResolverActivity.EXTRA_RESOLVE_INFOS, matches);
- try {
- mContext.startActivity(intent);
- } catch (ActivityNotFoundException e) {
- Log.w(TAG, "unable to start UsbResolverActivity");
+ // start UsbResolverActivity so user can choose an activity
+ Intent resolverIntent = new Intent();
+ resolverIntent.setClassName("com.android.systemui",
+ "com.android.systemui.usb.UsbResolverActivity");
+ resolverIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ resolverIntent.putExtra(Intent.EXTRA_INTENT, intent);
+ resolverIntent.putParcelableArrayListExtra("rlist", matches);
+ try {
+ mContext.startActivity(resolverIntent);
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "unable to start UsbResolverActivity");
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
}
@@ -709,40 +718,121 @@ class UsbDeviceSettingsManager {
mContext.sendBroadcast(intent);
}
- public void checkPermission(UsbDevice device) {
- if (device == null) return;
+ public boolean hasPermission(UsbDevice device) {
synchronized (mLock) {
- ArrayList<DeviceFilter> filterList = mDevicePermissionMap.get(Binder.getCallingUid());
+ ArrayList<DeviceFilter> filterList =
+ mDevicePermissionMap.get(Binder.getCallingUid());
if (filterList != null) {
int count = filterList.size();
for (int i = 0; i < count; i++) {
DeviceFilter filter = filterList.get(i);
if (filter.equals(device)) {
// permission allowed
- return;
+ return true;
}
}
}
}
- throw new SecurityException("User has not given permission to device " + device);
+ return false;
}
- public void checkPermission(UsbAccessory accessory) {
- if (accessory == null) return;
+ public boolean hasPermission(UsbAccessory accessory) {
synchronized (mLock) {
- ArrayList<AccessoryFilter> filterList = mAccessoryPermissionMap.get(Binder.getCallingUid());
+ ArrayList<AccessoryFilter> filterList =
+ mAccessoryPermissionMap.get(Binder.getCallingUid());
if (filterList != null) {
int count = filterList.size();
for (int i = 0; i < count; i++) {
AccessoryFilter filter = filterList.get(i);
if (filter.equals(accessory)) {
// permission allowed
- return;
+ return true;
}
}
}
}
- throw new SecurityException("User has not given permission to accessory " + accessory);
+ return false;
+ }
+
+ public void checkPermission(UsbDevice device) {
+ if (!hasPermission(device)) {
+ throw new SecurityException("User has not given permission to device " + device);
+ }
+ }
+
+ public void checkPermission(UsbAccessory accessory) {
+ if (!hasPermission(accessory)) {
+ throw new SecurityException("User has not given permission to accessory " + accessory);
+ }
+ }
+
+ private void requestPermissionDialog(Intent intent, String packageName, PendingIntent pi) {
+ int uid = Binder.getCallingUid();
+
+ // compare uid with packageName to foil apps pretending to be someone else
+ try {
+ ApplicationInfo aInfo = mContext.getPackageManager().getApplicationInfo(packageName, 0);
+ if (aInfo.uid != uid) {
+ throw new IllegalArgumentException("package " + packageName +
+ " does not match caller's uid " + uid);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new IllegalArgumentException("package " + packageName + " not found");
+ }
+
+ long identity = Binder.clearCallingIdentity();
+ intent.setClassName("com.android.systemui",
+ "com.android.systemui.usb.UsbPermissionActivity");
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(Intent.EXTRA_INTENT, pi);
+ intent.putExtra("package", packageName);
+ intent.putExtra("uid", uid);
+ try {
+ mContext.startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "unable to start UsbPermissionActivity");
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ public void requestPermission(UsbDevice device, String packageName, PendingIntent pi) {
+ Intent intent = new Intent();
+
+ // respond immediately if permission has already been granted
+ if (hasPermission(device)) {
+ intent.putExtra(UsbManager.EXTRA_DEVICE, device);
+ intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
+ try {
+ pi.send(mContext, 0, intent);
+ } catch (PendingIntent.CanceledException e) {
+ Log.w(TAG, "requestPermission PendingIntent was cancelled");
+ }
+ return;
+ }
+
+ // start UsbPermissionActivity so user can choose an activity
+ intent.putExtra(UsbManager.EXTRA_DEVICE, device);
+ requestPermissionDialog(intent, packageName, pi);
+ }
+
+ public void requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi) {
+ Intent intent = new Intent();
+
+ // respond immediately if permission has already been granted
+ if (hasPermission(accessory)) {
+ intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+ intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
+ try {
+ pi.send(mContext, 0, intent);
+ } catch (PendingIntent.CanceledException e) {
+ Log.w(TAG, "requestPermission PendingIntent was cancelled");
+ }
+ return;
+ }
+
+ intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+ requestPermissionDialog(intent, packageName, pi);
}
public void setDevicePackage(UsbDevice device, String packageName) {
diff --git a/services/java/com/android/server/usb/UsbService.java b/services/java/com/android/server/usb/UsbService.java
index 8170c61b5ec5..d0a24920943b 100644
--- a/services/java/com/android/server/usb/UsbService.java
+++ b/services/java/com/android/server/usb/UsbService.java
@@ -16,6 +16,7 @@
package com.android.server.usb;
+import android.app.PendingIntent;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -454,6 +455,24 @@ public class UsbService extends IUsbManager.Stub {
mDeviceManager.setAccessoryPackage(accessory, packageName);
}
+ public boolean hasDevicePermission(UsbDevice device) {
+ return mDeviceManager.hasPermission(device);
+ }
+
+ public boolean hasAccessoryPermission(UsbAccessory accessory) {
+ return mDeviceManager.hasPermission(accessory);
+ }
+
+ public void requestDevicePermission(UsbDevice device, String packageName,
+ PendingIntent pi) {
+ mDeviceManager.requestPermission(device, packageName, pi);
+ }
+
+ public void requestAccessoryPermission(UsbAccessory accessory, String packageName,
+ PendingIntent pi) {
+ mDeviceManager.requestPermission(accessory, packageName, pi);
+ }
+
public void grantDevicePermission(UsbDevice device, int uid) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
mDeviceManager.grantDevicePermission(device, uid);
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index eed41a0c0e9a..8ccfbbab68c7 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -4791,13 +4791,17 @@ public class WindowManagerService extends IWindowManager.Stub
if (maxLayer < ws.mAnimLayer) {
maxLayer = ws.mAnimLayer;
}
- final Rect wf = ws.mFrame;
- final Rect cr = ws.mContentInsets;
- int left = wf.left + cr.left;
- int top = wf.top + cr.top;
- int right = wf.right - cr.right;
- int bottom = wf.bottom - cr.bottom;
- frame.union(left, top, right, bottom);
+
+ // Don't include wallpaper in bounds calculation
+ if (!ws.mIsWallpaper) {
+ final Rect wf = ws.mFrame;
+ final Rect cr = ws.mContentInsets;
+ int left = wf.left + cr.left;
+ int top = wf.top + cr.top;
+ int right = wf.right - cr.right;
+ int bottom = wf.bottom - cr.bottom;
+ frame.union(left, top, right, bottom);
+ }
}
Binder.restoreCallingIdentity(ident);
@@ -5460,8 +5464,9 @@ public class WindowManagerService extends IWindowManager.Stub
shortSize = (int)(shortSize/dm.density);
// These semi-magic numbers define our compatibility modes for
- // applications with different screens. Don't change unless you
- // make sure to test lots and lots of apps!
+ // applications with different screens. These are guarantees to
+ // app developers about the space they can expect for a particular
+ // configuration. DO NOT CHANGE!
if (longSize < 470) {
// This is shorter than an HVGA normal density screen (which
// is 480 pixels on its long side).
@@ -5469,12 +5474,12 @@ public class WindowManagerService extends IWindowManager.Stub
| Configuration.SCREENLAYOUT_LONG_NO;
} else {
// What size is this screen screen?
- if (longSize >= 800 && shortSize >= 600) {
- // SVGA or larger screens at medium density are the point
+ if (longSize >= 960 && shortSize >= 720) {
+ // 1.5xVGA or larger screens at medium density are the point
// at which we consider it to be an extra large screen.
mScreenLayout = Configuration.SCREENLAYOUT_SIZE_XLARGE;
- } else if (longSize >= 530 && shortSize >= 400) {
- // SVGA or larger screens at high density are the point
+ } else if (longSize >= 640 && shortSize >= 480) {
+ // VGA or larger screens at medium density are the point
// at which we consider it to be a large screen.
mScreenLayout = Configuration.SCREENLAYOUT_SIZE_LARGE;
} else {
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 3be3b1b03aa8..bd4e7875630f 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -727,14 +727,14 @@ void NativeInputManager::handleInterceptActions(jint wmActions, nsecs_t when,
};
if (wmActions & WM_ACTION_GO_TO_SLEEP) {
-#ifdef DEBUG_INPUT_DISPATCHER_POLICY
+#if DEBUG_INPUT_DISPATCHER_POLICY
LOGD("handleInterceptActions: Going to sleep.");
#endif
android_server_PowerManagerService_goToSleep(when);
}
if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) {
-#ifdef DEBUG_INPUT_DISPATCHER_POLICY
+#if DEBUG_INPUT_DISPATCHER_POLICY
LOGD("handleInterceptActions: Poking user activity.");
#endif
android_server_PowerManagerService_userActivity(when, POWER_MANAGER_BUTTON_EVENT);
@@ -743,7 +743,7 @@ void NativeInputManager::handleInterceptActions(jint wmActions, nsecs_t when,
if (wmActions & WM_ACTION_PASS_TO_USER) {
policyFlags |= POLICY_FLAG_PASS_TO_USER;
} else {
-#ifdef DEBUG_INPUT_DISPATCHER_POLICY
+#if DEBUG_INPUT_DISPATCHER_POLICY
LOGD("handleInterceptActions: Not passing key to user.");
#endif
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
index 6c9f48fb003c..db14e53b3aff 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
@@ -28,10 +28,12 @@ import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.Area;
+import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
+import java.awt.geom.RoundRectangle2D;
/**
* Delegate implementing the native methods of android.graphics.Path
@@ -331,58 +333,91 @@ public final class Path_Delegate {
@LayoutlibDelegate
/*package*/ static void native_addOval(int nPath, RectF oval, int dir) {
- // FIXME
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
- "Path.addOval is not supported.", null, null /*data*/);
+ Path_Delegate pathDelegate = sManager.getDelegate(nPath);
+ if (pathDelegate == null) {
+ return;
+ }
+
+ pathDelegate.mPath.append(new Ellipse2D.Float(
+ oval.left, oval.top, oval.width(), oval.height()), false);
}
@LayoutlibDelegate
/*package*/ static void native_addCircle(int nPath, float x, float y, float radius, int dir) {
- // FIXME
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
- "Path.addCircle is not supported.", null, null /*data*/);
+ Path_Delegate pathDelegate = sManager.getDelegate(nPath);
+ if (pathDelegate == null) {
+ return;
+ }
+
+ // because x/y is the center of the circle, need to offset this by the radius
+ pathDelegate.mPath.append(new Ellipse2D.Float(
+ x - radius, y - radius, radius * 2, radius * 2), false);
}
@LayoutlibDelegate
/*package*/ static void native_addArc(int nPath, RectF oval,
float startAngle, float sweepAngle) {
- // FIXME
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
- "Path.addArc is not supported.", null, null /*data*/);
+ Path_Delegate pathDelegate = sManager.getDelegate(nPath);
+ if (pathDelegate == null) {
+ return;
+ }
+
+ // because x/y is the center of the circle, need to offset this by the radius
+ pathDelegate.mPath.append(new Arc2D.Float(
+ oval.left, oval.top, oval.width(), oval.height(),
+ startAngle, sweepAngle, Arc2D.OPEN), false);
}
@LayoutlibDelegate
- /*package*/ static void native_addRoundRect(int nPath, RectF rect,
- float rx, float ry, int dir) {
- // FIXME
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
- "Path.addRoundRect is not supported.", null, null /*data*/);
+ /*package*/ static void native_addRoundRect(
+ int nPath, RectF rect, float rx, float ry, int dir) {
+
+ Path_Delegate pathDelegate = sManager.getDelegate(nPath);
+ if (pathDelegate == null) {
+ return;
+ }
+
+ pathDelegate.mPath.append(new RoundRectangle2D.Float(
+ rect.left, rect.top, rect.width(), rect.height(), rx * 2, ry * 2), false);
}
@LayoutlibDelegate
- /*package*/ static void native_addRoundRect(int nPath, RectF r, float[] radii, int dir) {
- // FIXME
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
- "Path.addRoundRect is not supported.", null, null /*data*/);
+ /*package*/ static void native_addRoundRect(int nPath, RectF rect, float[] radii, int dir) {
+ // Java2D doesn't support different rounded corners in each corner, so just use the
+ // first value.
+ native_addRoundRect(nPath, rect, radii[0], radii[1], dir);
+
+ // there can be a case where this API is used but with similar values for all corners, so
+ // in that case we don't warn.
+ // we only care if 2 corners are different so just compare to the next one.
+ for (int i = 0 ; i < 3 ; i++) {
+ if (radii[i * 2] != radii[(i + 1) * 2] || radii[i * 2 + 1] != radii[(i + 1) * 2 + 1]) {
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+ "Different corner sizes are not supported in Path.addRoundRect.",
+ null, null /*data*/);
+ break;
+ }
+ }
}
@LayoutlibDelegate
/*package*/ static void native_addPath(int nPath, int src, float dx, float dy) {
- // FIXME
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
- "Path.addPath is not supported.", null, null /*data*/);
+ addPath(nPath, src, AffineTransform.getTranslateInstance(dx, dy));
}
@LayoutlibDelegate
/*package*/ static void native_addPath(int nPath, int src) {
- native_addPath(nPath, src, 0, 0);
+ addPath(nPath, src, null /*transform*/);
}
@LayoutlibDelegate
/*package*/ static void native_addPath(int nPath, int src, int matrix) {
- // FIXME
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
- "Path.addPath is not supported.", null, null /*data*/);
+ Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(matrix);
+ if (matrixDelegate == null) {
+ return;
+ }
+
+ addPath(nPath, src, matrixDelegate.getAffineTransform());
}
@LayoutlibDelegate
@@ -487,6 +522,26 @@ public final class Path_Delegate {
return null;
}
+ private static void addPath(int destPath, int srcPath, AffineTransform transform) {
+ Path_Delegate destPathDelegate = sManager.getDelegate(destPath);
+ if (destPathDelegate == null) {
+ return;
+ }
+
+ Path_Delegate srcPathDelegate = sManager.getDelegate(srcPath);
+ if (srcPathDelegate == null) {
+ return;
+ }
+
+ if (transform != null) {
+ destPathDelegate.mPath.append(
+ srcPathDelegate.mPath.getPathIterator(transform), false);
+ } else {
+ destPathDelegate.mPath.append(srcPathDelegate.mPath, false);
+ }
+ }
+
+
/**
* Returns whether the path is empty.
* @return true if the path is empty.
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 acc7379990d4..e6e96476c18a 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -192,7 +192,7 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
Capability.UNBOUND_RENDERING,
Capability.CUSTOM_BACKGROUND_COLOR,
Capability.RENDER,
- Capability.LAYOUT_ONLY,
+ //Capability.LAYOUT_ONLY, // disable to run on ADT 10.0 which doesn't include this.
Capability.EMBEDDED_LAYOUT,
Capability.VIEW_MANIPULATION,
Capability.PLAY_ANIMATION,
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java
index c1d760069188..138a455004be 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java
@@ -412,9 +412,7 @@ public final class BridgeTypedArray extends TypedArray {
return LayoutParams.MATCH_PARENT;
} else if (s.equals(BridgeConstants.WRAP_CONTENT)) {
return LayoutParams.WRAP_CONTENT;
- }
-
- if (RenderResources.REFERENCE_NULL.equals(s)) {
+ } else if (RenderResources.REFERENCE_NULL.equals(s)) {
return defValue;
}
@@ -486,23 +484,32 @@ public final class BridgeTypedArray extends TypedArray {
return LayoutParams.MATCH_PARENT;
} else if (s.equals(BridgeConstants.WRAP_CONTENT)) {
return LayoutParams.WRAP_CONTENT;
- }
-
- if (RenderResources.REFERENCE_NULL.equals(s)) {
+ } else if (RenderResources.REFERENCE_NULL.equals(s)) {
return defValue;
}
- // FIXME huh?
+ if (ResourceHelper.stringToFloat(s, mValue)) {
+ float f = mValue.getDimension(mBridgeResources.mMetrics);
- float f = getDimension(index, defValue);
- final int res = (int)(f+0.5f);
- if (res != 0) return res;
- if (f == 0) return 0;
- if (f > 0) return 1;
+ if (f < 0) {
+ // negative values are not allowed in pixel dimensions
+ Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+ "Negative pixel dimension: " + s,
+ null, null /*data*/);
+ return defValue;
+ }
- Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
- "Can't convert to dimension: " + Integer.toString(index),
- null, null /*data*/);
+ if (f == 0) return 0;
+ if (f < 1) return 1;
+
+ return (int)(f+0.5f);
+ }
+
+ // looks like we were unable to resolve the dimension value
+ Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+ String.format(
+ "\"%1$s\" in attribute \"%2$s\" is not a valid format.",
+ s, mNames[index]), null /*data*/);
return defValue;
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
index 69f46e6e335e..649160eb4501 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
@@ -377,7 +377,7 @@ public final class ResourceHelper {
}
// check the first character
- if (buf[0] < '0' && buf[0] > '9' && buf[0] != '.') {
+ if (buf[0] < '0' && buf[0] > '9' && buf[0] != '.' && buf[0] != '-') {
return false;
}