summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/backup/FullBackup.java13
-rw-r--r--core/java/android/view/GestureDetector.java119
-rw-r--r--core/java/android/view/ScaleGestureDetector.java456
-rw-r--r--core/java/android/webkit/AccessibilityInjector.java49
-rw-r--r--core/java/android/webkit/BrowserFrame.java53
-rw-r--r--core/java/android/webkit/JavascriptInterface.java33
-rw-r--r--core/java/android/webkit/WebViewClassic.java57
-rw-r--r--core/java/android/webkit/WebViewCore.java3
-rw-r--r--core/java/android/webkit/ZoomManager.java4
-rw-r--r--core/res/AndroidManifest.xml27
-rw-r--r--docs/downloads/training/BitmapFun.zipbin315657 -> 397971 bytes
-rw-r--r--docs/html/design/style/iconography.jd2
-rw-r--r--docs/html/guide/google/gcm/adv.jd1
-rw-r--r--docs/html/guide/google/gcm/gcm.jd14
-rw-r--r--docs/html/guide/google/gcm/gs.jd5
-rw-r--r--docs/html/guide/google/gcm/server-javadoc/allclasses-frame.html4
-rw-r--r--docs/html/guide/google/gcm/server-javadoc/allclasses-noframe.html4
-rw-r--r--docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/Constants.html4
-rw-r--r--docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/InvalidRequestException.html4
-rw-r--r--docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/Message.Builder.html4
-rw-r--r--docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/Message.html4
-rw-r--r--docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/MulticastResult.html4
-rw-r--r--docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/Result.html4
-rw-r--r--docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/Sender.html6
-rw-r--r--docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/package-frame.html4
-rw-r--r--docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/package-summary.html4
-rw-r--r--docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/package-tree.html4
-rw-r--r--docs/html/guide/google/gcm/server-javadoc/constant-values.html4
-rw-r--r--docs/html/guide/google/gcm/server-javadoc/default.css243
-rw-r--r--docs/html/guide/google/gcm/server-javadoc/deprecated-list.html4
-rw-r--r--docs/html/guide/google/gcm/server-javadoc/help-doc.html4
-rw-r--r--docs/html/guide/google/gcm/server-javadoc/index-all.html4
-rw-r--r--docs/html/guide/google/gcm/server-javadoc/index.html2
-rw-r--r--docs/html/guide/google/gcm/server-javadoc/overview-tree.html4
-rw-r--r--docs/html/guide/google/gcm/server-javadoc/serialized-form.html4
-rw-r--r--docs/html/guide/topics/connectivity/nfc/nfc.jd103
-rw-r--r--docs/html/guide/topics/ui/themes.jd9
-rw-r--r--docs/html/training/displaying-bitmaps/cache-bitmap.jd86
-rw-r--r--docs/html/training/displaying-bitmaps/display-bitmap.jd24
-rw-r--r--docs/html/training/displaying-bitmaps/index.jd6
-rw-r--r--docs/html/training/displaying-bitmaps/process-bitmap.jd14
-rw-r--r--packages/SystemUI/res/drawable-hdpi/ic_sysbar_back.pngbin1343 -> 1053 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.pngbin1345 -> 1063 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.pngbin970 -> 774 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.pngbin965 -> 769 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back.pngbin1584 -> 1480 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back.pngbin1183 -> 910 bytes
-rw-r--r--packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back.pngbin2117 -> 2126 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back.pngbin1830 -> 1421 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.pngbin1796 -> 1429 bytes
-rw-r--r--packages/SystemUI/res/layout/status_bar_expanded.xml2
-rw-r--r--packages/SystemUI/res/values/config.xml3
-rw-r--r--packages/SystemUI/res/values/dimens.xml6
-rw-r--r--packages/SystemUI/res/values/ids.xml1
-rw-r--r--packages/SystemUI/src/com/android/systemui/ExpandHelper.java406
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java7
-rw-r--r--services/java/com/android/server/BackupManagerService.java63
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java4
-rw-r--r--tools/aapt/Bundle.h9
-rw-r--r--tools/aapt/Main.cpp22
-rw-r--r--tools/aapt/Resource.cpp265
-rw-r--r--tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_back.pngbin0 -> 1053 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_back_default.pngbin1007 -> 0 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_home.pngbin0 -> 1064 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_home_default.pngbin852 -> 0 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_recent.pngbin0 -> 711 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_recent_default.pngbin574 -> 0 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_back.pngbin0 -> 774 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_back_default.pngbin782 -> 0 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_home.pngbin0 -> 836 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_home_default.pngbin695 -> 0 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_recent.pngbin0 -> 591 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_recent_default.pngbin534 -> 0 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/navigation_bar.xml20
-rw-r--r--tools/layoutlib/bridge/resources/bars/status_bar.xml (renamed from tools/layoutlib/bridge/resources/bars/phone_system_bar.xml)0
-rw-r--r--tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_back.pngbin0 -> 1421 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_back_default.pngbin1283 -> 0 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_home.pngbin0 -> 1421 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_home_default.pngbin1029 -> 0 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_recent.pngbin0 -> 749 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_recent_default.pngbin644 -> 0 bytes
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java3
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java14
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FakeActionBar.java3
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java54
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java (renamed from tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/PhoneSystemBar.java)7
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TabletSystemBar.java10
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java3
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java71
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java25
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java12
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java151
95 files changed, 1782 insertions, 823 deletions
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index d7f1c9f0fab4..f859599e18ce 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -64,7 +64,9 @@ public class FullBackup {
/**
* Copy data from a socket to the given File location on permanent storage. The
- * modification time and access mode of the resulting file will be set if desired.
+ * modification time and access mode of the resulting file will be set if desired,
+ * although group/all rwx modes will be stripped: the restored file will not be
+ * accessible from outside the target application even if the original file was.
* If the {@code type} parameter indicates that the result should be a directory,
* the socket parameter may be {@code null}; even if it is valid, no data will be
* read from it in this case.
@@ -79,8 +81,9 @@ public class FullBackup {
* @param type Must be either {@link BackupAgent#TYPE_FILE} for ordinary file data
* or {@link BackupAgent#TYPE_DIRECTORY} for a directory.
* @param mode Unix-style file mode (as used by the chmod(2) syscall) to be set on
- * the output file or directory. If this parameter is negative then neither
- * the mode nor the mtime parameters will be used.
+ * the output file or directory. group/all rwx modes are stripped even if set
+ * in this parameter. If this parameter is negative then neither
+ * the mode nor the mtime values will be applied to the restored file.
* @param mtime A timestamp in the standard Unix epoch that will be imposed as the
* last modification time of the output file. if the {@code mode} parameter is
* negative then this parameter will be ignored.
@@ -105,8 +108,6 @@ public class FullBackup {
if (!parent.exists()) {
// in practice this will only be for the default semantic directories,
// and using the default mode for those is appropriate.
- // TODO: support the edge case of apps that have adjusted the
- // permissions on these core directories
parent.mkdirs();
}
out = new FileOutputStream(outFile);
@@ -146,6 +147,8 @@ public class FullBackup {
// Now twiddle the state to match the backup, assuming all went well
if (mode >= 0 && outFile != null) {
try {
+ // explicitly prevent emplacement of files accessible by outside apps
+ mode &= 0700;
Libcore.os.chmod(outFile.getPath(), (int)mode);
} catch (ErrnoException e) {
e.rethrowAsIOException();
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index 0114a419f23c..4bbdd4e3b534 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -226,17 +226,12 @@ public class GestureDetector {
*/
private boolean mIsDoubleTapping;
- private float mLastMotionY;
- private float mLastMotionX;
+ private float mLastFocusX;
+ private float mLastFocusY;
+ private float mDownFocusX;
+ private float mDownFocusY;
private boolean mIsLongpressEnabled;
-
- /**
- * True if we are at a target API level of >= Froyo or the developer can
- * explicitly set it. If true, input events with > 1 pointer will be ignored
- * so we can work side by side with multitouch gesture detectors.
- */
- private boolean mIgnoreMultitouch;
/**
* Determines speed during touch scrolling
@@ -349,8 +344,16 @@ public class GestureDetector {
* @throws NullPointerException if {@code listener} is null.
*/
public GestureDetector(Context context, OnGestureListener listener, Handler handler) {
- this(context, listener, handler, context != null &&
- context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.FROYO);
+ if (handler != null) {
+ mHandler = new GestureHandler(handler);
+ } else {
+ mHandler = new GestureHandler();
+ }
+ mListener = listener;
+ if (listener instanceof OnDoubleTapListener) {
+ setOnDoubleTapListener((OnDoubleTapListener) listener);
+ }
+ init(context);
}
/**
@@ -362,31 +365,19 @@ public class GestureDetector {
* @param listener the listener invoked for all the callbacks, this must
* not be null.
* @param handler the handler to use
- * @param ignoreMultitouch whether events involving more than one pointer should
- * be ignored.
*
* @throws NullPointerException if {@code listener} is null.
*/
public GestureDetector(Context context, OnGestureListener listener, Handler handler,
- boolean ignoreMultitouch) {
- if (handler != null) {
- mHandler = new GestureHandler(handler);
- } else {
- mHandler = new GestureHandler();
- }
- mListener = listener;
- if (listener instanceof OnDoubleTapListener) {
- setOnDoubleTapListener((OnDoubleTapListener) listener);
- }
- init(context, ignoreMultitouch);
+ boolean unused) {
+ this(context, listener, handler);
}
- private void init(Context context, boolean ignoreMultitouch) {
+ private void init(Context context) {
if (mListener == null) {
throw new NullPointerException("OnGestureListener must not be null");
}
mIsLongpressEnabled = true;
- mIgnoreMultitouch = ignoreMultitouch;
// Fallback to support pre-donuts releases
int touchSlop, doubleTapSlop, doubleTapTouchSlop;
@@ -456,34 +447,41 @@ public class GestureDetector {
}
final int action = ev.getAction();
- final float y = ev.getY();
- final float x = ev.getX();
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev);
+ final boolean pointerUp =
+ (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP;
+ final int skipIndex = pointerUp ? ev.getActionIndex() : -1;
+
+ // Determine focal point
+ float sumX = 0, sumY = 0;
+ final int count = ev.getPointerCount();
+ for (int i = 0; i < count; i++) {
+ if (skipIndex == i) continue;
+ sumX += ev.getX(i);
+ sumY += ev.getY(i);
+ }
+ final int div = pointerUp ? count - 1 : count;
+ final float focusX = sumX / div;
+ final float focusY = sumY / div;
+
boolean handled = false;
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_POINTER_DOWN:
- if (mIgnoreMultitouch) {
- // Multitouch event - abort.
- cancel();
- }
+ mDownFocusX = mLastFocusX = focusX;
+ mDownFocusY = mLastFocusY = focusY;
+ // Cancel long press and taps
+ cancelTaps();
break;
case MotionEvent.ACTION_POINTER_UP:
- // Ending a multitouch gesture and going back to 1 finger
- if (mIgnoreMultitouch && ev.getPointerCount() == 2) {
- int index = (((action & MotionEvent.ACTION_POINTER_INDEX_MASK)
- >> MotionEvent.ACTION_POINTER_INDEX_SHIFT) == 0) ? 1 : 0;
- mLastMotionX = ev.getX(index);
- mLastMotionY = ev.getY(index);
- mVelocityTracker.recycle();
- mVelocityTracker = VelocityTracker.obtain();
- }
+ mDownFocusX = mLastFocusX = focusX;
+ mDownFocusY = mLastFocusY = focusY;
break;
case MotionEvent.ACTION_DOWN:
@@ -504,8 +502,8 @@ public class GestureDetector {
}
}
- mLastMotionX = x;
- mLastMotionY = y;
+ mDownFocusX = mLastFocusX = focusX;
+ mDownFocusY = mLastFocusY = focusY;
if (mCurrentDownEvent != null) {
mCurrentDownEvent.recycle();
}
@@ -525,22 +523,22 @@ public class GestureDetector {
break;
case MotionEvent.ACTION_MOVE:
- if (mInLongPress || (mIgnoreMultitouch && ev.getPointerCount() > 1)) {
+ if (mInLongPress) {
break;
}
- final float scrollX = mLastMotionX - x;
- final float scrollY = mLastMotionY - y;
+ final float scrollX = mLastFocusX - focusX;
+ final float scrollY = mLastFocusY - focusY;
if (mIsDoubleTapping) {
// Give the move events of the double-tap
handled |= mDoubleTapListener.onDoubleTapEvent(ev);
} else if (mAlwaysInTapRegion) {
- final int deltaX = (int) (x - mCurrentDownEvent.getX());
- final int deltaY = (int) (y - mCurrentDownEvent.getY());
+ final int deltaX = (int) (focusX - mDownFocusX);
+ final int deltaY = (int) (focusY - mDownFocusY);
int distance = (deltaX * deltaX) + (deltaY * deltaY);
if (distance > mTouchSlopSquare) {
handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
- mLastMotionX = x;
- mLastMotionY = y;
+ mLastFocusX = focusX;
+ mLastFocusY = focusY;
mAlwaysInTapRegion = false;
mHandler.removeMessages(TAP);
mHandler.removeMessages(SHOW_PRESS);
@@ -551,8 +549,8 @@ public class GestureDetector {
}
} else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {
handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
- mLastMotionX = x;
- mLastMotionY = y;
+ mLastFocusX = focusX;
+ mLastFocusY = focusY;
}
break;
@@ -571,9 +569,10 @@ public class GestureDetector {
// A fling must travel the minimum tap distance
final VelocityTracker velocityTracker = mVelocityTracker;
+ final int pointerId = ev.getPointerId(0);
velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
- final float velocityY = velocityTracker.getYVelocity();
- final float velocityX = velocityTracker.getXVelocity();
+ final float velocityY = velocityTracker.getYVelocity(pointerId);
+ final float velocityX = velocityTracker.getXVelocity(pointerId);
if ((Math.abs(velocityY) > mMinimumFlingVelocity)
|| (Math.abs(velocityX) > mMinimumFlingVelocity)){
@@ -622,6 +621,18 @@ public class GestureDetector {
}
}
+ private void cancelTaps() {
+ mHandler.removeMessages(SHOW_PRESS);
+ mHandler.removeMessages(LONG_PRESS);
+ mHandler.removeMessages(TAP);
+ mIsDoubleTapping = false;
+ mAlwaysInTapRegion = false;
+ mAlwaysInBiggerTapRegion = false;
+ if (mInLongPress) {
+ mInLongPress = false;
+ }
+ }
+
private boolean isConsideredDoubleTap(MotionEvent firstDown, MotionEvent firstUp,
MotionEvent secondDown) {
if (!mAlwaysInBiggerTapRegion) {
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index 73f94bc2f530..bcb8800621e1 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -17,14 +17,13 @@
package android.view;
import android.content.Context;
-import android.util.DisplayMetrics;
import android.util.FloatMath;
-import android.util.Log;
/**
- * Detects transformation gestures involving more than one pointer ("multitouch")
- * using the supplied {@link MotionEvent}s. The {@link OnScaleGestureListener}
- * callback will notify users when a particular gesture event has occurred.
+ * Detects scaling transformation gestures using the supplied {@link MotionEvent}s.
+ * The {@link OnScaleGestureListener} callback will notify users when a particular
+ * gesture event has occurred.
+ *
* This class should only be used with {@link MotionEvent}s reported via touch.
*
* To use this class:
@@ -87,8 +86,8 @@ public class ScaleGestureDetector {
* pointers going up.
*
* Once a scale has ended, {@link ScaleGestureDetector#getFocusX()}
- * and {@link ScaleGestureDetector#getFocusY()} will return the location
- * of the pointer remaining on the screen.
+ * and {@link ScaleGestureDetector#getFocusY()} will return focal point
+ * of the pointers remaining on the screen.
*
* @param detector The detector reporting the event - use this to
* retrieve extended info about event state.
@@ -121,43 +120,23 @@ public class ScaleGestureDetector {
}
}
- /**
- * This value is the threshold ratio between our previous combined pressure
- * and the current combined pressure. We will only fire an onScale event if
- * the computed ratio between the current and previous event pressures is
- * greater than this value. When pressure decreases rapidly between events
- * the position values can often be imprecise, as it usually indicates
- * that the user is in the process of lifting a pointer off of the device.
- * Its value was tuned experimentally.
- */
- private static final float PRESSURE_THRESHOLD = 0.67f;
-
private final Context mContext;
private final OnScaleGestureListener mListener;
- private boolean mGestureInProgress;
-
- private MotionEvent mPrevEvent;
- private MotionEvent mCurrEvent;
private float mFocusX;
private float mFocusY;
- private float mPrevFingerDiffX;
- private float mPrevFingerDiffY;
- private float mCurrFingerDiffX;
- private float mCurrFingerDiffY;
- private float mCurrLen;
- private float mPrevLen;
- private float mScaleFactor;
- private float mCurrPressure;
- private float mPrevPressure;
- private long mTimeDelta;
-
- private boolean mInvalidGesture;
-
- // Pointer IDs currently responsible for the two fingers controlling the gesture
- private int mActiveId0;
- private int mActiveId1;
- private boolean mActive0MostRecent;
+
+ private float mCurrSpan;
+ private float mPrevSpan;
+ private float mInitialSpan;
+ private float mCurrSpanX;
+ private float mCurrSpanY;
+ private float mPrevSpanX;
+ private float mPrevSpanY;
+ private long mCurrTime;
+ private long mPrevTime;
+ private boolean mInProgress;
+ private int mSpanSlop;
/**
* Consistency verifier for debugging purposes.
@@ -169,8 +148,21 @@ public class ScaleGestureDetector {
public ScaleGestureDetector(Context context, OnScaleGestureListener listener) {
mContext = context;
mListener = listener;
+ mSpanSlop = ViewConfiguration.get(context).getScaledTouchSlop() * 2;
}
+ /**
+ * Accepts MotionEvents and dispatches events to a {@link OnScaleGestureListener}
+ * when appropriate.
+ *
+ * <p>Applications should pass a complete and consistent event stream to this method.
+ * A complete and consistent event stream involves all MotionEvents from the initial
+ * ACTION_DOWN to the final ACTION_UP or ACTION_CANCEL.</p>
+ *
+ * @param event The event to process
+ * @return true if the event was processed and the detector wants to receive the
+ * rest of the MotionEvents in this event stream.
+ */
public boolean onTouchEvent(MotionEvent event) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(event, 0);
@@ -178,265 +170,115 @@ public class ScaleGestureDetector {
final int action = event.getActionMasked();
- if (action == MotionEvent.ACTION_DOWN) {
- reset(); // Start fresh
- }
-
- boolean handled = true;
- if (mInvalidGesture) {
- handled = false;
- } else if (!mGestureInProgress) {
- switch (action) {
- case MotionEvent.ACTION_DOWN: {
- mActiveId0 = event.getPointerId(0);
- mActive0MostRecent = true;
- }
- break;
-
- case MotionEvent.ACTION_UP:
- reset();
- break;
-
- case MotionEvent.ACTION_POINTER_DOWN: {
- // We have a new multi-finger gesture
- if (mPrevEvent != null) mPrevEvent.recycle();
- mPrevEvent = MotionEvent.obtain(event);
- mTimeDelta = 0;
-
- int index1 = event.getActionIndex();
- int index0 = event.findPointerIndex(mActiveId0);
- mActiveId1 = event.getPointerId(index1);
- if (index0 < 0 || index0 == index1) {
- // Probably someone sending us a broken event stream.
- index0 = findNewActiveIndex(event, mActiveId1, -1);
- mActiveId0 = event.getPointerId(index0);
- }
- mActive0MostRecent = false;
-
- setContext(event);
-
- mGestureInProgress = mListener.onScaleBegin(this);
- break;
- }
+ final boolean streamComplete = action == MotionEvent.ACTION_UP ||
+ action == MotionEvent.ACTION_CANCEL;
+ if (action == MotionEvent.ACTION_DOWN || streamComplete) {
+ // Reset any scale in progress with the listener.
+ // If it's an ACTION_DOWN we're beginning a new event stream.
+ // This means the app probably didn't give us all the events. Shame on it.
+ if (mInProgress) {
+ mListener.onScaleEnd(this);
+ mInProgress = false;
+ mInitialSpan = 0;
}
- } else {
- // Transform gesture in progress - attempt to handle it
- switch (action) {
- case MotionEvent.ACTION_POINTER_DOWN: {
- // End the old gesture and begin a new one with the most recent two fingers.
- mListener.onScaleEnd(this);
- final int oldActive0 = mActiveId0;
- final int oldActive1 = mActiveId1;
- reset();
-
- mPrevEvent = MotionEvent.obtain(event);
- mActiveId0 = mActive0MostRecent ? oldActive0 : oldActive1;
- mActiveId1 = event.getPointerId(event.getActionIndex());
- mActive0MostRecent = false;
-
- int index0 = event.findPointerIndex(mActiveId0);
- if (index0 < 0 || mActiveId0 == mActiveId1) {
- // Probably someone sending us a broken event stream.
- Log.e(TAG, "Got " + MotionEvent.actionToString(action) +
- " with bad state while a gesture was in progress. " +
- "Did you forget to pass an event to " +
- "ScaleGestureDetector#onTouchEvent?");
- index0 = findNewActiveIndex(event, mActiveId1, -1);
- mActiveId0 = event.getPointerId(index0);
- }
-
- setContext(event);
-
- mGestureInProgress = mListener.onScaleBegin(this);
- }
- break;
-
- case MotionEvent.ACTION_POINTER_UP: {
- final int pointerCount = event.getPointerCount();
- final int actionIndex = event.getActionIndex();
- final int actionId = event.getPointerId(actionIndex);
-
- boolean gestureEnded = false;
- if (pointerCount > 2) {
- if (actionId == mActiveId0) {
- final int newIndex = findNewActiveIndex(event, mActiveId1, actionIndex);
- if (newIndex >= 0) {
- mListener.onScaleEnd(this);
- mActiveId0 = event.getPointerId(newIndex);
- mActive0MostRecent = true;
- mPrevEvent = MotionEvent.obtain(event);
- setContext(event);
- mGestureInProgress = mListener.onScaleBegin(this);
- } else {
- gestureEnded = true;
- }
- } else if (actionId == mActiveId1) {
- final int newIndex = findNewActiveIndex(event, mActiveId0, actionIndex);
- if (newIndex >= 0) {
- mListener.onScaleEnd(this);
- mActiveId1 = event.getPointerId(newIndex);
- mActive0MostRecent = false;
- mPrevEvent = MotionEvent.obtain(event);
- setContext(event);
- mGestureInProgress = mListener.onScaleBegin(this);
- } else {
- gestureEnded = true;
- }
- }
- mPrevEvent.recycle();
- mPrevEvent = MotionEvent.obtain(event);
- setContext(event);
- } else {
- gestureEnded = true;
- }
-
- if (gestureEnded) {
- // Gesture ended
- setContext(event);
-
- // Set focus point to the remaining finger
- final int activeId = actionId == mActiveId0 ? mActiveId1 : mActiveId0;
- final int index = event.findPointerIndex(activeId);
- mFocusX = event.getX(index);
- mFocusY = event.getY(index);
-
- mListener.onScaleEnd(this);
- reset();
- mActiveId0 = activeId;
- mActive0MostRecent = true;
- }
- }
- break;
-
- case MotionEvent.ACTION_CANCEL:
- mListener.onScaleEnd(this);
- reset();
- break;
-
- case MotionEvent.ACTION_UP:
- reset();
- break;
-
- case MotionEvent.ACTION_MOVE: {
- setContext(event);
-
- // Only accept the event if our relative pressure is within
- // a certain limit - this can help filter shaky data as a
- // finger is lifted.
- if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) {
- final boolean updatePrevious = mListener.onScale(this);
-
- if (updatePrevious) {
- mPrevEvent.recycle();
- mPrevEvent = MotionEvent.obtain(event);
- }
- }
- }
- break;
+
+ if (streamComplete) {
+ return true;
}
}
- if (!handled && mInputEventConsistencyVerifier != null) {
- mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
+ final boolean configChanged =
+ action == MotionEvent.ACTION_POINTER_UP ||
+ action == MotionEvent.ACTION_POINTER_DOWN;
+ final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP;
+ final int skipIndex = pointerUp ? event.getActionIndex() : -1;
+
+ // Determine focal point
+ float sumX = 0, sumY = 0;
+ final int count = event.getPointerCount();
+ for (int i = 0; i < count; i++) {
+ if (skipIndex == i) continue;
+ sumX += event.getX(i);
+ sumY += event.getY(i);
}
- return handled;
- }
-
- private int findNewActiveIndex(MotionEvent ev, int otherActiveId, int removedPointerIndex) {
- final int pointerCount = ev.getPointerCount();
-
- // It's ok if this isn't found and returns -1, it simply won't match.
- final int otherActiveIndex = ev.findPointerIndex(otherActiveId);
-
- // Pick a new id and update tracking state.
- for (int i = 0; i < pointerCount; i++) {
- if (i != removedPointerIndex && i != otherActiveIndex) {
- return i;
- }
+ final int div = pointerUp ? count - 1 : count;
+ final float focusX = sumX / div;
+ final float focusY = sumY / div;
+
+ // Determine average deviation from focal point
+ float devSumX = 0, devSumY = 0;
+ for (int i = 0; i < count; i++) {
+ if (skipIndex == i) continue;
+ devSumX += Math.abs(event.getX(i) - focusX);
+ devSumY += Math.abs(event.getY(i) - focusY);
}
- return -1;
- }
-
- private void setContext(MotionEvent curr) {
- if (mCurrEvent != null) {
- mCurrEvent.recycle();
+ final float devX = devSumX / div;
+ final float devY = devSumY / div;
+
+ // Span is the average distance between touch points through the focal point;
+ // i.e. the diameter of the circle with a radius of the average deviation from
+ // the focal point.
+ final float spanX = devX * 2;
+ final float spanY = devY * 2;
+ final float span = FloatMath.sqrt(spanX * spanX + spanY * spanY);
+
+ // Dispatch begin/end events as needed.
+ // If the configuration changes, notify the app to reset its current state by beginning
+ // a fresh scale event stream.
+ final boolean wasInProgress = mInProgress;
+ mFocusX = focusX;
+ mFocusY = focusY;
+ if (mInProgress && (span == 0 || configChanged)) {
+ mListener.onScaleEnd(this);
+ mInProgress = false;
+ mInitialSpan = span;
+ }
+ if (configChanged) {
+ mPrevSpanX = mCurrSpanX = spanX;
+ mPrevSpanY = mCurrSpanY = spanY;
+ mInitialSpan = mPrevSpan = mCurrSpan = span;
+ }
+ if (!mInProgress && span != 0 &&
+ (wasInProgress || Math.abs(span - mInitialSpan) > mSpanSlop)) {
+ mPrevSpanX = mCurrSpanX = spanX;
+ mPrevSpanY = mCurrSpanY = spanY;
+ mPrevSpan = mCurrSpan = span;
+ mInProgress = mListener.onScaleBegin(this);
}
- mCurrEvent = MotionEvent.obtain(curr);
-
- mCurrLen = -1;
- mPrevLen = -1;
- mScaleFactor = -1;
- final MotionEvent prev = mPrevEvent;
+ // Handle motion; focal point and span/scale factor are changing.
+ if (action == MotionEvent.ACTION_MOVE) {
+ mCurrSpanX = spanX;
+ mCurrSpanY = spanY;
+ mCurrSpan = span;
- final int prevIndex0 = prev.findPointerIndex(mActiveId0);
- final int prevIndex1 = prev.findPointerIndex(mActiveId1);
- final int currIndex0 = curr.findPointerIndex(mActiveId0);
- final int currIndex1 = curr.findPointerIndex(mActiveId1);
+ boolean updatePrev = true;
+ if (mInProgress) {
+ updatePrev = mListener.onScale(this);
+ }
- if (prevIndex0 < 0 || prevIndex1 < 0 || currIndex0 < 0 || currIndex1 < 0) {
- mInvalidGesture = true;
- Log.e(TAG, "Invalid MotionEvent stream detected.", new Throwable());
- if (mGestureInProgress) {
- mListener.onScaleEnd(this);
+ if (updatePrev) {
+ mPrevSpanX = mCurrSpanX;
+ mPrevSpanY = mCurrSpanY;
+ mPrevSpan = mCurrSpan;
}
- return;
}
- final float px0 = prev.getX(prevIndex0);
- final float py0 = prev.getY(prevIndex0);
- final float px1 = prev.getX(prevIndex1);
- final float py1 = prev.getY(prevIndex1);
- final float cx0 = curr.getX(currIndex0);
- final float cy0 = curr.getY(currIndex0);
- final float cx1 = curr.getX(currIndex1);
- final float cy1 = curr.getY(currIndex1);
-
- final float pvx = px1 - px0;
- final float pvy = py1 - py0;
- final float cvx = cx1 - cx0;
- final float cvy = cy1 - cy0;
- mPrevFingerDiffX = pvx;
- mPrevFingerDiffY = pvy;
- mCurrFingerDiffX = cvx;
- mCurrFingerDiffY = cvy;
-
- mFocusX = cx0 + cvx * 0.5f;
- mFocusY = cy0 + cvy * 0.5f;
- mTimeDelta = curr.getEventTime() - prev.getEventTime();
- mCurrPressure = curr.getPressure(currIndex0) + curr.getPressure(currIndex1);
- mPrevPressure = prev.getPressure(prevIndex0) + prev.getPressure(prevIndex1);
- }
-
- private void reset() {
- if (mPrevEvent != null) {
- mPrevEvent.recycle();
- mPrevEvent = null;
- }
- if (mCurrEvent != null) {
- mCurrEvent.recycle();
- mCurrEvent = null;
- }
- mGestureInProgress = false;
- mActiveId0 = -1;
- mActiveId1 = -1;
- mInvalidGesture = false;
+ return true;
}
/**
- * Returns {@code true} if a two-finger scale gesture is in progress.
- * @return {@code true} if a scale gesture is in progress, {@code false} otherwise.
+ * Returns {@code true} if a scale gesture is in progress.
*/
public boolean isInProgress() {
- return mGestureInProgress;
+ return mInProgress;
}
/**
* Get the X coordinate of the current gesture's focal point.
- * If a gesture is in progress, the focal point is directly between
- * the two pointers forming the gesture.
- * If a gesture is ending, the focal point is the location of the
- * remaining pointer on the screen.
+ * If a gesture is in progress, the focal point is between
+ * each of the pointers forming the gesture.
+ *
* If {@link #isInProgress()} would return false, the result of this
* function is undefined.
*
@@ -448,10 +290,9 @@ public class ScaleGestureDetector {
/**
* Get the Y coordinate of the current gesture's focal point.
- * If a gesture is in progress, the focal point is directly between
- * the two pointers forming the gesture.
- * If a gesture is ending, the focal point is the location of the
- * remaining pointer on the screen.
+ * If a gesture is in progress, the focal point is between
+ * each of the pointers forming the gesture.
+ *
* If {@link #isInProgress()} would return false, the result of this
* function is undefined.
*
@@ -462,73 +303,63 @@ public class ScaleGestureDetector {
}
/**
- * Return the current distance between the two pointers forming the
- * gesture in progress.
+ * Return the average distance between each of the pointers forming the
+ * gesture in progress through the focal point.
*
* @return Distance between pointers in pixels.
*/
public float getCurrentSpan() {
- if (mCurrLen == -1) {
- final float cvx = mCurrFingerDiffX;
- final float cvy = mCurrFingerDiffY;
- mCurrLen = FloatMath.sqrt(cvx*cvx + cvy*cvy);
- }
- return mCurrLen;
+ return mCurrSpan;
}
/**
- * Return the current x distance between the two pointers forming the
- * gesture in progress.
+ * Return the average X distance between each of the pointers forming the
+ * gesture in progress through the focal point.
*
* @return Distance between pointers in pixels.
*/
public float getCurrentSpanX() {
- return mCurrFingerDiffX;
+ return mCurrSpanX;
}
/**
- * Return the current y distance between the two pointers forming the
- * gesture in progress.
+ * Return the average Y distance between each of the pointers forming the
+ * gesture in progress through the focal point.
*
* @return Distance between pointers in pixels.
*/
public float getCurrentSpanY() {
- return mCurrFingerDiffY;
+ return mCurrSpanY;
}
/**
- * Return the previous distance between the two pointers forming the
- * gesture in progress.
+ * Return the previous average distance between each of the pointers forming the
+ * gesture in progress through the focal point.
*
* @return Previous distance between pointers in pixels.
*/
public float getPreviousSpan() {
- if (mPrevLen == -1) {
- final float pvx = mPrevFingerDiffX;
- final float pvy = mPrevFingerDiffY;
- mPrevLen = FloatMath.sqrt(pvx*pvx + pvy*pvy);
- }
- return mPrevLen;
+ return mPrevSpan;
}
/**
- * Return the previous x distance between the two pointers forming the
- * gesture in progress.
+ * Return the previous average X distance between each of the pointers forming the
+ * gesture in progress through the focal point.
*
* @return Previous distance between pointers in pixels.
*/
public float getPreviousSpanX() {
- return mPrevFingerDiffX;
+ return mPrevSpanX;
}
/**
- * Return the previous y distance between the two pointers forming the
- * gesture in progress.
+ * Return the previous average Y distance between each of the pointers forming the
+ * gesture in progress through the focal point.
*
* @return Previous distance between pointers in pixels.
*/
public float getPreviousSpanY() {
- return mPrevFingerDiffY;
+ return mPrevSpanY;
}
/**
@@ -539,10 +370,7 @@ public class ScaleGestureDetector {
* @return The current scaling factor.
*/
public float getScaleFactor() {
- if (mScaleFactor == -1) {
- mScaleFactor = getCurrentSpan() / getPreviousSpan();
- }
- return mScaleFactor;
+ return mPrevSpan > 0 ? mCurrSpan / mPrevSpan : 1;
}
/**
@@ -552,7 +380,7 @@ public class ScaleGestureDetector {
* @return Time difference since the last scaling event in milliseconds.
*/
public long getTimeDelta() {
- return mTimeDelta;
+ return mCurrTime - mPrevTime;
}
/**
@@ -561,6 +389,6 @@ public class ScaleGestureDetector {
* @return Current event time in milliseconds.
*/
public long getEventTime() {
- return mCurrEvent.getEventTime();
+ return mCurrTime;
}
}
diff --git a/core/java/android/webkit/AccessibilityInjector.java b/core/java/android/webkit/AccessibilityInjector.java
index 7dfb5bb102e2..c3654bd57382 100644
--- a/core/java/android/webkit/AccessibilityInjector.java
+++ b/core/java/android/webkit/AccessibilityInjector.java
@@ -34,6 +34,7 @@ import org.json.JSONObject;
import java.net.URI;
import java.net.URISyntaxException;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@@ -53,7 +54,7 @@ class AccessibilityInjector {
private final WebView mWebView;
// The Java objects that are exposed to JavaScript.
- private TextToSpeech mTextToSpeech;
+ private TextToSpeechWrapper mTextToSpeech;
private CallbackHandler mCallback;
// Lazily loaded helper objects.
@@ -349,11 +350,8 @@ class AccessibilityInjector {
if (mTextToSpeech != null) {
return;
}
-
- final String pkgName = mContext.getPackageName();
-
- mTextToSpeech = new TextToSpeech(mContext, null, null, pkgName + ".**webview**", true);
- mWebView.addJavascriptInterface(mTextToSpeech, ALIAS_TTS_JS_INTERFACE);
+ mTextToSpeech = new TextToSpeechWrapper(mContext);
+ mWebViewClassic.addJavascriptInterface(mTextToSpeech, ALIAS_TTS_JS_INTERFACE, true);
}
/**
@@ -377,7 +375,7 @@ class AccessibilityInjector {
}
mCallback = new CallbackHandler(ALIAS_TRAVERSAL_JS_INTERFACE);
- mWebView.addJavascriptInterface(mCallback, ALIAS_TRAVERSAL_JS_INTERFACE);
+ mWebViewClassic.addJavascriptInterface(mCallback, ALIAS_TRAVERSAL_JS_INTERFACE, true);
}
private void removeCallbackApis() {
@@ -504,10 +502,46 @@ class AccessibilityInjector {
final String jsonString = mAccessibilityJSONObject.toString();
final String jsCode = String.format(ACCESSIBILITY_ANDROIDVOX_TEMPLATE, jsonString);
+ if (mCallback == null) return false;
return mCallback.performAction(mWebView, jsCode);
}
/**
+ * Used to protect the TextToSpeech class, only exposing the methods we want to expose.
+ */
+ private static class TextToSpeechWrapper {
+ private TextToSpeech mTextToSpeech;
+
+ public TextToSpeechWrapper(Context context) {
+ final String pkgName = context.getPackageName();
+ mTextToSpeech = new TextToSpeech(context, null, null, pkgName + ".**webview**", true);
+ }
+
+ @JavascriptInterface
+ @SuppressWarnings("unused")
+ public boolean isSpeaking() {
+ return mTextToSpeech.isSpeaking();
+ }
+
+ @JavascriptInterface
+ @SuppressWarnings("unused")
+ public int speak(String text, int queueMode, HashMap<String, String> params) {
+ return mTextToSpeech.speak(text, queueMode, params);
+ }
+
+ @JavascriptInterface
+ @SuppressWarnings("unused")
+ public int stop() {
+ return mTextToSpeech.stop();
+ }
+
+ @SuppressWarnings("unused")
+ protected void shutdown() {
+ mTextToSpeech.shutdown();
+ }
+ }
+
+ /**
* Exposes result interface to JavaScript.
*/
private static class CallbackHandler {
@@ -603,6 +637,7 @@ class AccessibilityInjector {
* @param id The result id of the request as a {@link String}.
* @param result The result of the request as a {@link String}.
*/
+ @JavascriptInterface
@SuppressWarnings("unused")
public void onResult(String id, String result) {
final long resultId;
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 510899093558..9e454a7b1934 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -88,8 +88,19 @@ class BrowserFrame extends Handler {
// Is this frame the main frame?
private boolean mIsMainFrame;
+ // Javascript interface object
+ private class JSObject {
+ Object object;
+ boolean requireAnnotation;
+
+ public JSObject(Object object, boolean requireAnnotation) {
+ this.object = object;
+ this.requireAnnotation = requireAnnotation;
+ }
+ }
+
// Attached Javascript interfaces
- private Map<String, Object> mJavaScriptObjects;
+ private Map<String, JSObject> mJavaScriptObjects;
private Set<Object> mRemovedJavaScriptObjects;
// Key store handler when Chromium HTTP stack is used.
@@ -233,10 +244,8 @@ class BrowserFrame extends Handler {
}
sConfigCallback.addHandler(this);
- mJavaScriptObjects = javascriptInterfaces;
- if (mJavaScriptObjects == null) {
- mJavaScriptObjects = new HashMap<String, Object>();
- }
+ mJavaScriptObjects = new HashMap<String, JSObject>();
+ addJavaScriptObjects(javascriptInterfaces);
mRemovedJavaScriptObjects = new HashSet<Object>();
mSettings = settings;
@@ -245,7 +254,6 @@ class BrowserFrame extends Handler {
mWebViewCore = w;
mSearchBox = new SearchBoxImpl(mWebViewCore, mCallbackProxy);
- mJavaScriptObjects.put(SearchBoxImpl.JS_INTERFACE_NAME, mSearchBox);
AssetManager am = context.getAssets();
nativeCreateFrame(w, am, proxy.getBackForwardList());
@@ -591,15 +599,32 @@ class BrowserFrame extends Handler {
Iterator<String> iter = mJavaScriptObjects.keySet().iterator();
while (iter.hasNext()) {
String interfaceName = iter.next();
- Object object = mJavaScriptObjects.get(interfaceName);
- if (object != null) {
+ JSObject jsobject = mJavaScriptObjects.get(interfaceName);
+ if (jsobject != null && jsobject.object != null) {
nativeAddJavascriptInterface(nativeFramePointer,
- mJavaScriptObjects.get(interfaceName), interfaceName);
+ jsobject.object, interfaceName, jsobject.requireAnnotation);
}
}
mRemovedJavaScriptObjects.clear();
+ }
+
+ /*
+ * Add javascript objects to the internal list of objects. The default behavior
+ * is to allow access to inherited methods (no annotation needed). This is only
+ * used when js objects are passed through a constructor (via a hidden constructor).
+ *
+ */
+ private void addJavaScriptObjects(Map<String, Object> javascriptInterfaces) {
- stringByEvaluatingJavaScriptFromString(SearchBoxImpl.JS_BRIDGE);
+ if (javascriptInterfaces == null) return;
+ Iterator<String> iter = javascriptInterfaces.keySet().iterator();
+ while (iter.hasNext()) {
+ String interfaceName = iter.next();
+ Object object = javascriptInterfaces.get(interfaceName);
+ if (object != null) {
+ mJavaScriptObjects.put(interfaceName, new JSObject(object, false));
+ }
+ }
}
/**
@@ -619,11 +644,11 @@ class BrowserFrame extends Handler {
}
}
- public void addJavascriptInterface(Object obj, String interfaceName) {
+ public void addJavascriptInterface(Object obj, String interfaceName,
+ boolean requireAnnotation) {
assert obj != null;
removeJavascriptInterface(interfaceName);
-
- mJavaScriptObjects.put(interfaceName, obj);
+ mJavaScriptObjects.put(interfaceName, new JSObject(obj, requireAnnotation));
}
public void removeJavascriptInterface(String interfaceName) {
@@ -1249,7 +1274,7 @@ class BrowserFrame extends Handler {
* Add a javascript interface to the main frame.
*/
private native void nativeAddJavascriptInterface(int nativeFramePointer,
- Object obj, String interfaceName);
+ Object obj, String interfaceName, boolean requireAnnotation);
public native void clearCache();
diff --git a/core/java/android/webkit/JavascriptInterface.java b/core/java/android/webkit/JavascriptInterface.java
new file mode 100644
index 000000000000..a5312a2c0bf0
--- /dev/null
+++ b/core/java/android/webkit/JavascriptInterface.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation that allows exposing methods to JavaScript.
+ *
+ * @hide
+ */
+@SuppressWarnings("javadoc")
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface JavascriptInterface {
+} \ No newline at end of file
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index 84a6129e8c4d..d1d867c6735f 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -1336,20 +1336,40 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
private void onHandleUiTouchEvent(MotionEvent ev) {
final ScaleGestureDetector detector =
- mZoomManager.getMultiTouchGestureDetector();
+ mZoomManager.getScaleGestureDetector();
- float x = ev.getX();
- float y = ev.getY();
+ int action = ev.getActionMasked();
+ final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP;
+ final boolean configChanged =
+ action == MotionEvent.ACTION_POINTER_UP ||
+ action == MotionEvent.ACTION_POINTER_DOWN;
+ final int skipIndex = pointerUp ? ev.getActionIndex() : -1;
+
+ // Determine focal point
+ float sumX = 0, sumY = 0;
+ final int count = ev.getPointerCount();
+ for (int i = 0; i < count; i++) {
+ if (skipIndex == i) continue;
+ sumX += ev.getX(i);
+ sumY += ev.getY(i);
+ }
+ final int div = pointerUp ? count - 1 : count;
+ float x = sumX / div;
+ float y = sumY / div;
+
+ if (configChanged) {
+ mLastTouchX = Math.round(x);
+ mLastTouchY = Math.round(y);
+ mLastTouchTime = ev.getEventTime();
+ mWebView.cancelLongPress();
+ mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
+ }
if (detector != null) {
detector.onTouchEvent(ev);
if (detector.isInProgress()) {
mLastTouchTime = ev.getEventTime();
- x = detector.getFocusX();
- y = detector.getFocusY();
- mWebView.cancelLongPress();
- mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
if (!mZoomManager.supportsPanDuringZoom()) {
return;
}
@@ -1360,14 +1380,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
}
- int action = ev.getActionMasked();
if (action == MotionEvent.ACTION_POINTER_DOWN) {
cancelTouch();
action = MotionEvent.ACTION_DOWN;
- } else if (action == MotionEvent.ACTION_POINTER_UP && ev.getPointerCount() >= 2) {
- // set mLastTouchX/Y to the remaining points for multi-touch.
- mLastTouchX = Math.round(x);
- mLastTouchY = Math.round(y);
} else if (action == MotionEvent.ACTION_MOVE) {
// negative x or y indicate it is on the edge, skip it.
if (x < 0 || y < 0) {
@@ -4070,12 +4085,20 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
@Override
public void addJavascriptInterface(Object object, String name) {
+ addJavascriptInterface(object, name, false);
+ }
+
+ /**
+ * @hide
+ */
+ public void addJavascriptInterface(Object object, String name, boolean requireAnnotation) {
if (object == null) {
return;
}
WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
arg.mObject = object;
arg.mInterfaceName = name;
+ arg.mRequireAnnotation = requireAnnotation;
mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg);
}
@@ -4345,7 +4368,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
// A multi-finger gesture can look like a long press; make sure we don't take
// long press actions if we're scaling.
- final ScaleGestureDetector detector = mZoomManager.getMultiTouchGestureDetector();
+ final ScaleGestureDetector detector = mZoomManager.getScaleGestureDetector();
if (detector != null && detector.isInProgress()) {
return false;
}
@@ -4513,11 +4536,11 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
private void ensureSelectionHandles() {
if (mSelectHandleCenter == null) {
mSelectHandleCenter = mContext.getResources().getDrawable(
- com.android.internal.R.drawable.text_select_handle_middle);
+ com.android.internal.R.drawable.text_select_handle_middle).mutate();
mSelectHandleLeft = mContext.getResources().getDrawable(
- com.android.internal.R.drawable.text_select_handle_left);
+ com.android.internal.R.drawable.text_select_handle_left).mutate();
mSelectHandleRight = mContext.getResources().getDrawable(
- com.android.internal.R.drawable.text_select_handle_right);
+ com.android.internal.R.drawable.text_select_handle_right).mutate();
mHandleAlpha.setAlpha(mHandleAlpha.getAlpha());
mSelectHandleCenterOffset = new Point(0,
-mSelectHandleCenter.getIntrinsicHeight());
@@ -5752,7 +5775,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
* and the middle point for multi-touch.
*/
private void handleTouchEventCommon(MotionEvent event, int action, int x, int y) {
- ScaleGestureDetector detector = mZoomManager.getMultiTouchGestureDetector();
+ ScaleGestureDetector detector = mZoomManager.getScaleGestureDetector();
long eventTime = event.getEventTime();
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 728ddbfc9fe1..ae49202806a6 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -839,6 +839,7 @@ public final class WebViewCore {
static class JSInterfaceData {
Object mObject;
String mInterfaceName;
+ boolean mRequireAnnotation;
}
static class JSKeyData {
@@ -1508,7 +1509,7 @@ public final class WebViewCore {
case ADD_JS_INTERFACE:
JSInterfaceData jsData = (JSInterfaceData) msg.obj;
mBrowserFrame.addJavascriptInterface(jsData.mObject,
- jsData.mInterfaceName);
+ jsData.mInterfaceName, jsData.mRequireAnnotation);
break;
case REMOVE_JS_INTERFACE:
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index 88301191c654..80a67826e1d6 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -204,7 +204,7 @@ class ZoomManager {
*/
private boolean mAllowPanAndScale;
- // use the framework's ScaleGestureDetector to handle multi-touch
+ // use the framework's ScaleGestureDetector to handle scaling gestures
private ScaleGestureDetector mScaleDetector;
private boolean mPinchToZoomAnimating = false;
@@ -768,7 +768,7 @@ class ZoomManager {
return isZoomAnimating();
}
- public ScaleGestureDetector getMultiTouchGestureDetector() {
+ public ScaleGestureDetector getScaleGestureDetector() {
return mScaleDetector;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 6546fedc18e0..f7ffb48767d3 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -133,6 +133,31 @@
<protected-broadcast android:name="android.os.UpdateLock.UPDATE_LOCK_CHANGED" />
+ <protected-broadcast android:name="android.intent.action.ANY_DATA_STATE" />
+
+ <protected-broadcast android:name="com.android.server.WifiManager.action.START_SCAN" />
+ <protected-broadcast android:name="com.android.server.WifiManager.action.DELAYED_DRIVER_STOP" />
+ <protected-broadcast android:name="android.net.wifi.WIFI_STATE_CHANGED" />
+ <protected-broadcast android:name="android.net.wifi.WIFI_AP_STATE_CHANGED" />
+ <protected-broadcast android:name="android.net.wifi.SCAN_RESULTS" />
+ <protected-broadcast android:name="android.net.wifi.RSSI_CHANGED" />
+ <protected-broadcast android:name="android.net.wifi.STATE_CHANGE" />
+ <protected-broadcast android:name="android.net.wifi.LINK_CONFIGURATION_CHANGED" />
+ <protected-broadcast android:name="android.net.wifi.CONFIGURED_NETWORKS_CHANGE" />
+ <protected-broadcast android:name="android.net.wifi.supplicant.CONNECTION_CHANGE" />
+ <protected-broadcast android:name="android.net.wifi.supplicant.STATE_CHANGE" />
+ <protected-broadcast android:name="android.net.wifi.p2p.STATE_CHANGED" />
+ <protected-broadcast android:name="android.net.wifi.p2p.DISCOVERY_STATE_CHANGE" />
+ <protected-broadcast android:name="android.net.wifi.p2p.THIS_DEVICE_CHANGED" />
+ <protected-broadcast android:name="android.net.wifi.p2p.PEERS_CHANGED" />
+ <protected-broadcast android:name="android.net.wifi.p2p.CONNECTION_STATE_CHANGE" />
+ <protected-broadcast android:name="android.net.wifi.p2p.PERSISTENT_GROUPS_CHANGED" />
+ <protected-broadcast android:name="android.net.conn.TETHER_STATE_CHANGED" />
+ <protected-broadcast android:name="android.net.conn.INET_CONDITION_ACTION" />
+
+
+
+
<!-- ====================================== -->
<!-- Permissions for things that cost money -->
<!-- ====================================== -->
@@ -1625,7 +1650,7 @@
<permission android:name="android.permission.SERIAL_PORT"
android:label="@string/permlab_serialPort"
android:description="@string/permdesc_serialPort"
- android:protectionLevel="normal" />
+ android:protectionLevel="signature|system" />
<!-- Allows the holder to access content providers from outside an ApplicationThread.
This permission is enforced by the ActivityManagerService on the corresponding APIs,
diff --git a/docs/downloads/training/BitmapFun.zip b/docs/downloads/training/BitmapFun.zip
index e7e71f9f85a0..e48bfd304185 100644
--- a/docs/downloads/training/BitmapFun.zip
+++ b/docs/downloads/training/BitmapFun.zip
Binary files differ
diff --git a/docs/html/design/style/iconography.jd b/docs/html/design/style/iconography.jd
index 76f4aa4c7d3f..ce11cf7fb3ca 100644
--- a/docs/html/design/style/iconography.jd
+++ b/docs/html/design/style/iconography.jd
@@ -109,10 +109,8 @@ files for further customization.
</p>
<p>
-
<a onClick="_gaq.push(['_trackEvent', 'Design', 'Download', 'Action Bar Icons (@iconography page)']);"
href="{@docRoot}downloads/design/Android_Design_Icons_20120814.zip">Download the Action Bar Icon Pack</a>
-
</p>
<div class="layout-content-row">
diff --git a/docs/html/guide/google/gcm/adv.jd b/docs/html/guide/google/gcm/adv.jd
index 640fb5252e59..136062408e9f 100644
--- a/docs/html/guide/google/gcm/adv.jd
+++ b/docs/html/guide/google/gcm/adv.jd
@@ -51,7 +51,6 @@ delivered right away unless the <code>delay_while_idle</code> flag is set to tru
<p>If the device is not connected to GCM, the message will be stored until a connection is established (again respecting the collapse key rules). When a connection is established, GCM will deliver all pending messages to the device, regardless of the <code>delay_while_idle</code> flag. If the device never gets connected again (for instance, if it was factory reset), the message will eventually time out and be discarded from GCM storage. The default timeout is 4 weeks, unless the <code>time_to_live</code> flag is set.</p>
-
<p>Finally, when GCM attempts to deliver a message to the device and the application was uninstalled, GCM will discard that message right away and invalidate the registration ID. Future attempts to send a message to that device will get a <code>NotRegistered</code> error. See <a href="#unreg">How Unregistration Works</a> for more information.</p>
<p>Although is not possible to track the status of each individual message, the Google APIs Console stats are broken down by messages sent to device, messages collapsed, and messages waiting for delivery.</p>
diff --git a/docs/html/guide/google/gcm/gcm.jd b/docs/html/guide/google/gcm/gcm.jd
index b85c163b107c..57d56b23fd33 100644
--- a/docs/html/guide/google/gcm/gcm.jd
+++ b/docs/html/guide/google/gcm/gcm.jd
@@ -57,9 +57,15 @@ page.title=GCM Architectural Overview
</div>
<p>Google Cloud Messaging for Android (GCM) is a free service that helps
-developers send data from servers to their Android applications on Android devices. This could be a lightweight message telling the Android application that there is new data to be fetched from the server (for instance, a movie uploaded by a friend), or it could be a message containing up to 4kb of payload data (so apps like instant messaging can consume the message directly). The GCM service handles all aspects of queueing of
- messages and delivery to the target Android application running on the target
- device.</p>
+developers send data from servers to their Android applications on Android
+devices. This could be a lightweight message telling the Android application
+that there is new data to be fetched from the server (for instance, a movie
+uploaded by a friend), or it could be a message containing up to 4kb of payload
+data (so apps like instant messaging can consume the message directly). The GCM
+service handles all aspects of queueing of messages and delivery to the target
+Android application running on the target device.</p>
+
+
<p class="note"> To jump right into using GCM with your Android
applications, see the instructions in <a href="gs.html">Getting Started</a>.</p>
@@ -664,7 +670,7 @@ sent. Optional. The default value is <code>false</code>, and must be a JSON bool
</tr>
<tr>
<td><code>time_to_live</code></td>
- <td>How long (in seconds) the message should be kept on GCM storage if the device is offline. Optional (default time-to-live is 4 weeks, and must be set as a JSON number). </td>
+ <td>How long (in seconds) the message should be kept on GCM storage if the device is offline. Optional (default time-to-live is 4 weeks, and must be set as a JSON number).</td>
</tr>
<tr>
<td><code>restricted_package_name</code></td>
diff --git a/docs/html/guide/google/gcm/gs.jd b/docs/html/guide/google/gcm/gs.jd
index beef28155e9b..fc5c4a264702 100644
--- a/docs/html/guide/google/gcm/gs.jd
+++ b/docs/html/guide/google/gcm/gs.jd
@@ -145,8 +145,9 @@ page.title=GCM: Getting Started
<li>If the value is dynamic, the service should override the <code>getSenderIds()</code> method.</li>
</ul>
-<h4>
- Step 3: Write the my_app_package.GCMIntentService class</h4>
+
+<h4>Step 3: Write the my_app_package.GCMIntentService class</h4>
+
<p>Next write the <code>my_app_package.GCMIntentService</code> class, overriding the following callback methods (which are called by <code>GCMBroadcastReceiver</code>):<br>
</p>
<ul>
diff --git a/docs/html/guide/google/gcm/server-javadoc/allclasses-frame.html b/docs/html/guide/google/gcm/server-javadoc/allclasses-frame.html
index cf7dc28250c5..80ee78490c62 100644
--- a/docs/html/guide/google/gcm/server-javadoc/allclasses-frame.html
+++ b/docs/html/guide/google/gcm/server-javadoc/allclasses-frame.html
@@ -2,12 +2,12 @@
<!--NewPage-->
<HTML>
<HEAD>
-<!-- Generated by javadoc (build 1.6.0_26) on Mon Jul 16 14:12:10 PDT 2012 -->
+<!-- Generated by javadoc (build 1.6.0_26) on Wed Aug 29 14:55:34 PDT 2012 -->
<TITLE>
All Classes
</TITLE>
-<META NAME="date" CONTENT="2012-07-16">
+<META NAME="date" CONTENT="2012-08-29">
<LINK REL ="stylesheet" TYPE="text/css" HREF="default.css" TITLE="Style">
diff --git a/docs/html/guide/google/gcm/server-javadoc/allclasses-noframe.html b/docs/html/guide/google/gcm/server-javadoc/allclasses-noframe.html
index 299085ccc8a0..966598d18979 100644
--- a/docs/html/guide/google/gcm/server-javadoc/allclasses-noframe.html
+++ b/docs/html/guide/google/gcm/server-javadoc/allclasses-noframe.html
@@ -2,12 +2,12 @@
<!--NewPage-->
<HTML>
<HEAD>
-<!-- Generated by javadoc (build 1.6.0_26) on Mon Jul 16 14:12:10 PDT 2012 -->
+<!-- Generated by javadoc (build 1.6.0_26) on Wed Aug 29 14:55:34 PDT 2012 -->
<TITLE>
All Classes
</TITLE>
-<META NAME="date" CONTENT="2012-07-16">
+<META NAME="date" CONTENT="2012-08-29">
<LINK REL ="stylesheet" TYPE="text/css" HREF="default.css" TITLE="Style">
diff --git a/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/Constants.html b/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/Constants.html
index 7384dfd631d3..515bba4ca048 100644
--- a/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/Constants.html
+++ b/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/Constants.html
@@ -2,12 +2,12 @@
<!--NewPage-->
<HTML>
<HEAD>
-<!-- Generated by javadoc (build 1.6.0_26) on Mon Jul 16 14:12:09 PDT 2012 -->
+<!-- Generated by javadoc (build 1.6.0_26) on Wed Aug 29 14:55:34 PDT 2012 -->
<TITLE>
Constants
</TITLE>
-<META NAME="date" CONTENT="2012-07-16">
+<META NAME="date" CONTENT="2012-08-29">
<LINK REL ="stylesheet" TYPE="text/css" HREF="../../../../../default.css" TITLE="Style">
diff --git a/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/InvalidRequestException.html b/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/InvalidRequestException.html
index 56de78347b28..bb0974cac7ec 100644
--- a/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/InvalidRequestException.html
+++ b/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/InvalidRequestException.html
@@ -2,12 +2,12 @@
<!--NewPage-->
<HTML>
<HEAD>
-<!-- Generated by javadoc (build 1.6.0_26) on Mon Jul 16 14:12:10 PDT 2012 -->
+<!-- Generated by javadoc (build 1.6.0_26) on Wed Aug 29 14:55:34 PDT 2012 -->
<TITLE>
InvalidRequestException
</TITLE>
-<META NAME="date" CONTENT="2012-07-16">
+<META NAME="date" CONTENT="2012-08-29">
<LINK REL ="stylesheet" TYPE="text/css" HREF="../../../../../default.css" TITLE="Style">
diff --git a/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/Message.Builder.html b/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/Message.Builder.html
index 7d5110c8d6e9..c2ee6488850f 100644
--- a/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/Message.Builder.html
+++ b/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/Message.Builder.html
@@ -2,12 +2,12 @@
<!--NewPage-->
<HTML>
<HEAD>
-<!-- Generated by javadoc (build 1.6.0_26) on Mon Jul 16 14:12:10 PDT 2012 -->
+<!-- Generated by javadoc (build 1.6.0_26) on Wed Aug 29 14:55:34 PDT 2012 -->
<TITLE>
Message.Builder
</TITLE>
-<META NAME="date" CONTENT="2012-07-16">
+<META NAME="date" CONTENT="2012-08-29">
<LINK REL ="stylesheet" TYPE="text/css" HREF="../../../../../default.css" TITLE="Style">
diff --git a/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/Message.html b/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/Message.html
index 37a8a7492cdd..5dbd26222371 100644
--- a/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/Message.html
+++ b/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/Message.html
@@ -2,12 +2,12 @@
<!--NewPage-->
<HTML>
<HEAD>
-<!-- Generated by javadoc (build 1.6.0_26) on Mon Jul 16 14:12:10 PDT 2012 -->
+<!-- Generated by javadoc (build 1.6.0_26) on Wed Aug 29 14:55:34 PDT 2012 -->
<TITLE>
Message
</TITLE>
-<META NAME="date" CONTENT="2012-07-16">
+<META NAME="date" CONTENT="2012-08-29">
<LINK REL ="stylesheet" TYPE="text/css" HREF="../../../../../default.css" TITLE="Style">
diff --git a/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/MulticastResult.html b/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/MulticastResult.html
index 21752cad0e1f..072148813d6e 100644
--- a/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/MulticastResult.html
+++ b/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/MulticastResult.html
@@ -2,12 +2,12 @@
<!--NewPage-->
<HTML>
<HEAD>
-<!-- Generated by javadoc (build 1.6.0_26) on Mon Jul 16 14:12:10 PDT 2012 -->
+<!-- Generated by javadoc (build 1.6.0_26) on Wed Aug 29 14:55:34 PDT 2012 -->
<TITLE>
MulticastResult
</TITLE>
-<META NAME="date" CONTENT="2012-07-16">
+<META NAME="date" CONTENT="2012-08-29">
<LINK REL ="stylesheet" TYPE="text/css" HREF="../../../../../default.css" TITLE="Style">
diff --git a/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/Result.html b/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/Result.html
index 512b8f5f8c4b..a4aad29aaa23 100644
--- a/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/Result.html
+++ b/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/Result.html
@@ -2,12 +2,12 @@
<!--NewPage-->
<HTML>
<HEAD>
-<!-- Generated by javadoc (build 1.6.0_26) on Mon Jul 16 14:12:10 PDT 2012 -->
+<!-- Generated by javadoc (build 1.6.0_26) on Wed Aug 29 14:55:34 PDT 2012 -->
<TITLE>
Result
</TITLE>
-<META NAME="date" CONTENT="2012-07-16">
+<META NAME="date" CONTENT="2012-08-29">
<LINK REL ="stylesheet" TYPE="text/css" HREF="../../../../../default.css" TITLE="Style">
diff --git a/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/Sender.html b/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/Sender.html
index 5224e15d2325..fabda9862820 100644
--- a/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/Sender.html
+++ b/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/Sender.html
@@ -2,12 +2,12 @@
<!--NewPage-->
<HTML>
<HEAD>
-<!-- Generated by javadoc (build 1.6.0_26) on Mon Jul 16 14:12:10 PDT 2012 -->
+<!-- Generated by javadoc (build 1.6.0_26) on Wed Aug 29 14:55:34 PDT 2012 -->
<TITLE>
Sender
</TITLE>
-<META NAME="date" CONTENT="2012-07-16">
+<META NAME="date" CONTENT="2012-08-29">
<LINK REL ="stylesheet" TYPE="text/css" HREF="../../../../../default.css" TITLE="Style">
@@ -591,6 +591,8 @@ protected static java.lang.String <B>getString</B>(java.io.InputStream&nbsp;stre
<p>
If the stream ends in a newline character, it will be stripped.
+ <p>
+ If the stream is null, returns an empty string.
<P>
<DD><DL>
diff --git a/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/package-frame.html b/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/package-frame.html
index 9f099b32e918..1bc4fd93d5e4 100644
--- a/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/package-frame.html
+++ b/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/package-frame.html
@@ -2,12 +2,12 @@
<!--NewPage-->
<HTML>
<HEAD>
-<!-- Generated by javadoc (build 1.6.0_26) on Mon Jul 16 14:12:10 PDT 2012 -->
+<!-- Generated by javadoc (build 1.6.0_26) on Wed Aug 29 14:55:34 PDT 2012 -->
<TITLE>
com.google.android.gcm.server
</TITLE>
-<META NAME="date" CONTENT="2012-07-16">
+<META NAME="date" CONTENT="2012-08-29">
<LINK REL ="stylesheet" TYPE="text/css" HREF="../../../../../default.css" TITLE="Style">
diff --git a/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/package-summary.html b/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/package-summary.html
index eddcca1724ba..de791c710e6f 100644
--- a/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/package-summary.html
+++ b/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/package-summary.html
@@ -2,12 +2,12 @@
<!--NewPage-->
<HTML>
<HEAD>
-<!-- Generated by javadoc (build 1.6.0_26) on Mon Jul 16 14:12:10 PDT 2012 -->
+<!-- Generated by javadoc (build 1.6.0_26) on Wed Aug 29 14:55:34 PDT 2012 -->
<TITLE>
com.google.android.gcm.server
</TITLE>
-<META NAME="date" CONTENT="2012-07-16">
+<META NAME="date" CONTENT="2012-08-29">
<LINK REL ="stylesheet" TYPE="text/css" HREF="../../../../../default.css" TITLE="Style">
diff --git a/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/package-tree.html b/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/package-tree.html
index d3d1c43a250f..d50931267aa3 100644
--- a/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/package-tree.html
+++ b/docs/html/guide/google/gcm/server-javadoc/com/google/android/gcm/server/package-tree.html
@@ -2,12 +2,12 @@
<!--NewPage-->
<HTML>
<HEAD>
-<!-- Generated by javadoc (build 1.6.0_26) on Mon Jul 16 14:12:10 PDT 2012 -->
+<!-- Generated by javadoc (build 1.6.0_26) on Wed Aug 29 14:55:34 PDT 2012 -->
<TITLE>
com.google.android.gcm.server Class Hierarchy
</TITLE>
-<META NAME="date" CONTENT="2012-07-16">
+<META NAME="date" CONTENT="2012-08-29">
<LINK REL ="stylesheet" TYPE="text/css" HREF="../../../../../default.css" TITLE="Style">
diff --git a/docs/html/guide/google/gcm/server-javadoc/constant-values.html b/docs/html/guide/google/gcm/server-javadoc/constant-values.html
index 66df66481e29..68db1cb729ac 100644
--- a/docs/html/guide/google/gcm/server-javadoc/constant-values.html
+++ b/docs/html/guide/google/gcm/server-javadoc/constant-values.html
@@ -2,12 +2,12 @@
<!--NewPage-->
<HTML>
<HEAD>
-<!-- Generated by javadoc (build 1.6.0_26) on Mon Jul 16 14:12:10 PDT 2012 -->
+<!-- Generated by javadoc (build 1.6.0_26) on Wed Aug 29 14:55:34 PDT 2012 -->
<TITLE>
Constant Field Values
</TITLE>
-<META NAME="date" CONTENT="2012-07-16">
+<META NAME="date" CONTENT="2012-08-29">
<LINK REL ="stylesheet" TYPE="text/css" HREF="default.css" TITLE="Style">
diff --git a/docs/html/guide/google/gcm/server-javadoc/default.css b/docs/html/guide/google/gcm/server-javadoc/default.css
index 2513e6989e9e..7c395c758798 100644
--- a/docs/html/guide/google/gcm/server-javadoc/default.css
+++ b/docs/html/guide/google/gcm/server-javadoc/default.css
@@ -530,12 +530,12 @@ h3:target {
}
.design ol {
counter-reset: item; }
- .design ol li {
+ .design ol>li {
font-size: 14px;
line-height: 20px;
list-style-type: none;
position: relative; }
- .design ol li:before {
+ .design ol>li:before {
content: counter(item) ". ";
counter-increment: item;
position: absolute;
@@ -561,16 +561,18 @@ h3:target {
content: "9. "; }
.design ol li.value-10:before {
content: "10. "; }
-.design .with-callouts ol li {
+.design .with-callouts ol>li {
list-style-position: inside;
margin-left: 0; }
- .design .with-callouts ol li:before {
+ .design .with-callouts ol>li:before {
display: inline;
left: -20px;
float: left;
width: 17px;
color: #33b5e5;
font-weight: 500; }
+.design .with-callouts ul>li {
+ list-style-position: outside; }
/* special list items */
li.no-bullet {
@@ -1079,22 +1081,71 @@ color-stop(50%, #acbc00), color-stop(50%, #bdde00), color-stop(100%, #bdde00));
Print Only
========================================================================== */
@media print {
-a {
- color: inherit;
-}
-.nav-x, .nav-y {
- display: none;
-}
-.str { color: #060; }
-.kwd { color: #006; font-weight: bold; }
-.com { color: #600; font-style: italic; }
-.typ { color: #404; font-weight: bold; }
-.lit { color: #044; }
-.pun { color: #440; }
-.pln { color: #000; }
-.tag { color: #006; font-weight: bold; }
-.atn { color: #404; }
-.atv { color: #060; }
+ /* configure printed page */
+ @page {
+ margin: 0.75in 1in;
+ widows: 4;
+ orphans: 4;
+ }
+
+ /* reset spacing metrics */
+ html, body, .wrap {
+ margin: 0 !important;
+ padding: 0 !important;
+ width: auto !important;
+ }
+
+ /* leave enough space on the left for bullets */
+ body {
+ padding-left: 20px !important;
+ }
+ #doc-col {
+ margin-left: 0;
+ }
+
+ /* hide a bunch of non-content elements */
+ #header, #footer, #nav-x, #side-nav,
+ .training-nav-top, .training-nav-bottom,
+ #doc-col .content-footer,
+ .nav-x, .nav-y,
+ .paging-links,
+ a.totop {
+ display: none !important;
+ }
+
+ /* remove extra space above page titles */
+ #doc-col .content-header {
+ margin-top: 0;
+ }
+
+ /* bump up spacing above subheadings */
+ h2 {
+ margin-top: 40px !important;
+ }
+
+ /* print link URLs where possible and give links default text color */
+ p a:after {
+ content: " (" attr(href) ")";
+ font-size: 80%;
+ }
+ p a {
+ word-wrap: break-word;
+ }
+ a {
+ color: inherit;
+ }
+
+ /* syntax highlighting rules */
+ .str { color: #060; }
+ .kwd { color: #006; font-weight: bold; }
+ .com { color: #600; font-style: italic; }
+ .typ { color: #404; font-weight: bold; }
+ .lit { color: #044; }
+ .pun { color: #440; }
+ .pln { color: #000; }
+ .tag { color: #006; font-weight: bold; }
+ .atn { color: #404; }
+ .atv { color: #060; }
}
/* =============================================================================
@@ -2033,8 +2084,11 @@ div.toggle-content.closed .toggle-content-toggleme {
#jd-content img.toggle-content-img {
margin:0 5px 5px 0;
}
-div.toggle-content > p {
- padding:0 0 5px;
+div.toggle-content p {
+ margin:10px 0 0;
+}
+div.toggle-content-toggleme {
+ padding:0 0 0 15px;
}
@@ -2145,14 +2199,9 @@ table.blank th, table.blank td {
.nolist {
list-style:none;
- padding:0;
- margin:0 0 1em 1em;
+ margin-left:0;
}
-.nolist li {
- padding:0 0 2px;
- margin:0;
-}
pre.classic {
background-color:transparent;
@@ -2180,6 +2229,12 @@ p.table-caption {
color:#666;
}
+div.note,
+div.caution,
+div.warning {
+ margin: 0 0 15px;
+}
+
p.note, div.note,
p.caution, div.caution,
p.warning, div.warning {
@@ -2898,10 +2953,6 @@ ul#search_filtered {
/* SEARCH RESULTS */
-/* disable twiddle and size selectors for left column */
-#leftSearchControl div {
- padding:0;
-}
#leftSearchControl .gsc-twiddle {
background-image : none;
@@ -3475,7 +3526,7 @@ EndColorStr='#ececec');
.morehover:hover {
opacity:1;
- height:345px;
+ height:385px;
width:268px;
-webkit-transition-property:height, -webkit-opacity;
}
@@ -3489,7 +3540,7 @@ EndColorStr='#ececec');
.morehover .mid {
width:228px;
background:url(../images/more_mid.png) repeat-y;
- padding:10px 20px 10px 20px;
+ padding:10px 20px 0 20px;
}
.morehover .mid .header {
@@ -3598,15 +3649,19 @@ a.download-sdk {
padding-top: 14px;
}
+#nav-x .wrap {
+ min-height:34px;
+}
+
#nav-x .wrap,
#searchResults.wrap {
max-width:940px;
border-bottom:1px solid #CCC;
- min-height:34px;
-
}
-
+#searchResults.wrap #leftSearchControl {
+ min-height:700px
+}
.nav-x {
margin-left:0;
margin-bottom:0;
@@ -3762,7 +3817,8 @@ a.download-sdk {
height: 300px;
}
.slideshow-develop img.play {
- width:350px;
+ max-width:350px;
+ max-height:240px;
margin:20px 0 0 90px;
-webkit-transform: perspective(800px ) rotateY( 35deg );
box-shadow: -16px 20px 40px rgba(0, 0, 0, 0.3);
@@ -3817,6 +3873,7 @@ a.download-sdk {
.feed .feed-nav li {
list-style: none;
float: left;
+ height: 21px; /* +4px bottom border = 25px; same as .feed-nav */
margin-right: 25px;
cursor: pointer;
}
@@ -3969,21 +4026,24 @@ a.download-sdk {
.landing-docs {
margin:20px 0 0;
}
-.landing-banner {
- height:280px;
-}
.landing-banner .col-6:first-child,
-.landing-docs .col-6:first-child {
+.landing-docs .col-6:first-child,
+.landing-docs .col-12 {
margin-left:0;
+ min-height:280px;
}
.landing-banner .col-6:last-child,
-.landing-docs .col-6:last-child {
+.landing-docs .col-6:last-child,
+.landing-docs .col-12 {
margin-right:0;
}
.landing-banner h1 {
margin-top:0;
}
+.landing-docs {
+ clear:left;
+}
.landing-docs h3 {
font-size:14px;
line-height:21px;
@@ -4002,4 +4062,99 @@ a.download-sdk {
.plusone {
float:right;
-} \ No newline at end of file
+}
+
+
+
+/************* HOME/LANDING PAGE *****************/
+
+.slideshow-home {
+ height: 500px;
+ width: 940px;
+ border-bottom: 1px solid #CCC;
+ position: relative;
+ margin: 0;
+}
+.slideshow-home .frame {
+ width: 940px;
+ height: 500px;
+}
+.slideshow-home .content-left {
+ float: left;
+ text-align: center;
+ vertical-align: center;
+ margin: 0 0 0 35px;
+}
+.slideshow-home .content-right {
+ margin: 80px 0 0 0;
+}
+.slideshow-home .content-right p {
+ margin-bottom: 10px;
+}
+.slideshow-home .content-right p:last-child {
+ margin-top: 15px;
+}
+.slideshow-home .content-right h1 {
+ padding:0;
+}
+.slideshow-home .item {
+ height: 500px;
+ width: 940px;
+}
+.home-sections {
+ padding: 30px 20px 20px;
+ margin: 20px 0;
+ background: -webkit-linear-gradient(top, #F6F6F6,#F9F9F9);
+}
+.home-sections ul {
+ margin: 0;
+}
+.home-sections ul li {
+ float: left;
+ display: block;
+ list-style: none;
+ width: 170px;
+ height: 35px;
+ border: 1px solid #ccc;
+ background: white;
+ margin-right: 10px;
+ border-radius: 1px;
+ -webkit-border-radius: 1px;
+ -moz-border-radius: 1px;
+ box-shadow: 1px 1px 5px #EEE;
+ -webkit-box-shadow: 1px 1px 5px #EEE;
+ -moz-box-shadow: 1px 1px 5px #EEE;
+ background: white;
+}
+.home-sections ul li:hover {
+ background: #F9F9F9;
+ border: 1px solid #CCC;
+}
+.home-sections ul li a,
+.home-sections ul li a:hover {
+ font-weight: bold;
+ margin-top: 8px;
+ line-height: 18px;
+ float: left;
+ width: 100%;
+ text-align: center;
+ color: #09c !important;
+}
+.home-sections ul li a {
+ font-weight: bold;
+ margin-top: 8px;
+ line-height: 18px;
+ float: left;
+ width:100%;
+ text-align:center;
+}
+.home-sections ul li img {
+ float: left;
+ margin: -8px 0 0 10px;
+}
+.home-sections ul li.last {
+ margin-right: 0px;
+}
+.fullpage #footer {
+ margin-top: -40px;
+}
diff --git a/docs/html/guide/google/gcm/server-javadoc/deprecated-list.html b/docs/html/guide/google/gcm/server-javadoc/deprecated-list.html
index 729b2bf5fc19..04b9aa520951 100644
--- a/docs/html/guide/google/gcm/server-javadoc/deprecated-list.html
+++ b/docs/html/guide/google/gcm/server-javadoc/deprecated-list.html
@@ -2,12 +2,12 @@
<!--NewPage-->
<HTML>
<HEAD>
-<!-- Generated by javadoc (build 1.6.0_26) on Mon Jul 16 14:12:10 PDT 2012 -->
+<!-- Generated by javadoc (build 1.6.0_26) on Wed Aug 29 14:55:34 PDT 2012 -->
<TITLE>
Deprecated List
</TITLE>
-<META NAME="date" CONTENT="2012-07-16">
+<META NAME="date" CONTENT="2012-08-29">
<LINK REL ="stylesheet" TYPE="text/css" HREF="default.css" TITLE="Style">
diff --git a/docs/html/guide/google/gcm/server-javadoc/help-doc.html b/docs/html/guide/google/gcm/server-javadoc/help-doc.html
index 7f5286cf67fd..c479cffeb596 100644
--- a/docs/html/guide/google/gcm/server-javadoc/help-doc.html
+++ b/docs/html/guide/google/gcm/server-javadoc/help-doc.html
@@ -2,12 +2,12 @@
<!--NewPage-->
<HTML>
<HEAD>
-<!-- Generated by javadoc (build 1.6.0_26) on Mon Jul 16 14:12:10 PDT 2012 -->
+<!-- Generated by javadoc (build 1.6.0_26) on Wed Aug 29 14:55:34 PDT 2012 -->
<TITLE>
API Help
</TITLE>
-<META NAME="date" CONTENT="2012-07-16">
+<META NAME="date" CONTENT="2012-08-29">
<LINK REL ="stylesheet" TYPE="text/css" HREF="default.css" TITLE="Style">
diff --git a/docs/html/guide/google/gcm/server-javadoc/index-all.html b/docs/html/guide/google/gcm/server-javadoc/index-all.html
index 0b095ec1de0f..97aa300b462f 100644
--- a/docs/html/guide/google/gcm/server-javadoc/index-all.html
+++ b/docs/html/guide/google/gcm/server-javadoc/index-all.html
@@ -2,12 +2,12 @@
<!--NewPage-->
<HTML>
<HEAD>
-<!-- Generated by javadoc (build 1.6.0_26) on Mon Jul 16 14:12:10 PDT 2012 -->
+<!-- Generated by javadoc (build 1.6.0_26) on Wed Aug 29 14:55:34 PDT 2012 -->
<TITLE>
Index
</TITLE>
-<META NAME="date" CONTENT="2012-07-16">
+<META NAME="date" CONTENT="2012-08-29">
<LINK REL ="stylesheet" TYPE="text/css" HREF="./default.css" TITLE="Style">
diff --git a/docs/html/guide/google/gcm/server-javadoc/index.html b/docs/html/guide/google/gcm/server-javadoc/index.html
index d8ba0ef42ad6..d3c3821bd2b6 100644
--- a/docs/html/guide/google/gcm/server-javadoc/index.html
+++ b/docs/html/guide/google/gcm/server-javadoc/index.html
@@ -2,7 +2,7 @@
<!--NewPage-->
<HTML>
<HEAD>
-<!-- Generated by javadoc on Mon Jul 16 14:12:10 PDT 2012-->
+<!-- Generated by javadoc on Wed Aug 29 14:55:34 PDT 2012-->
<TITLE>
Generated Documentation (Untitled)
</TITLE>
diff --git a/docs/html/guide/google/gcm/server-javadoc/overview-tree.html b/docs/html/guide/google/gcm/server-javadoc/overview-tree.html
index b8e28ad54fb0..c9afea6fd713 100644
--- a/docs/html/guide/google/gcm/server-javadoc/overview-tree.html
+++ b/docs/html/guide/google/gcm/server-javadoc/overview-tree.html
@@ -2,12 +2,12 @@
<!--NewPage-->
<HTML>
<HEAD>
-<!-- Generated by javadoc (build 1.6.0_26) on Mon Jul 16 14:12:10 PDT 2012 -->
+<!-- Generated by javadoc (build 1.6.0_26) on Wed Aug 29 14:55:34 PDT 2012 -->
<TITLE>
Class Hierarchy
</TITLE>
-<META NAME="date" CONTENT="2012-07-16">
+<META NAME="date" CONTENT="2012-08-29">
<LINK REL ="stylesheet" TYPE="text/css" HREF="default.css" TITLE="Style">
diff --git a/docs/html/guide/google/gcm/server-javadoc/serialized-form.html b/docs/html/guide/google/gcm/server-javadoc/serialized-form.html
index 7a1378fd6217..ab99e4112e4b 100644
--- a/docs/html/guide/google/gcm/server-javadoc/serialized-form.html
+++ b/docs/html/guide/google/gcm/server-javadoc/serialized-form.html
@@ -2,12 +2,12 @@
<!--NewPage-->
<HTML>
<HEAD>
-<!-- Generated by javadoc (build 1.6.0_26) on Mon Jul 16 14:12:10 PDT 2012 -->
+<!-- Generated by javadoc (build 1.6.0_26) on Wed Aug 29 14:55:34 PDT 2012 -->
<TITLE>
Serialized Form
</TITLE>
-<META NAME="date" CONTENT="2012-07-16">
+<META NAME="date" CONTENT="2012-08-29">
<LINK REL ="stylesheet" TYPE="text/css" HREF="default.css" TITLE="Style">
diff --git a/docs/html/guide/topics/connectivity/nfc/nfc.jd b/docs/html/guide/topics/connectivity/nfc/nfc.jd
index 51c7bee04f26..5011872760a9 100644
--- a/docs/html/guide/topics/connectivity/nfc/nfc.jd
+++ b/docs/html/guide/topics/connectivity/nfc/nfc.jd
@@ -318,8 +318,8 @@ other two intents, giving the user a better experience.</p>
</pre>
</li>
- <li>The <code>uses-feature</code> element so that your application shows up in Google
-Play only for devices that have NFC hardware:
+ <li>The <code>uses-feature</code> element so that your application shows up in Google Play
+ only for devices that have NFC hardware:
<pre>
&lt;uses-feature android:name="android.hardware.nfc" android:required="true" /&gt;
</pre>
@@ -511,13 +511,24 @@ contain the payload and allow you to enumerate the tag's technologies:</p>
<h2 id="creating-records">Creating Common Types of NDEF Records</h2>
<p>This section describes how to create common types of NDEF records to help you when writing to
-NFC tags or sending data with Android Beam. It also describes how to create the corresponding
+NFC tags or sending data with Android Beam. Starting with Android 4.0 (API level 14), the
+{@link android.nfc.NdefRecord#createUri createUri()} method is available to help you create
+URI records automatically. Starting in Android 4.1 (API level 16), {@link android.nfc.NdefRecord#createExternal createExternal()}
+and {@link android.nfc.NdefRecord#createMime createMime()} are available to help you create
+MIME and external type NDEF records. Use these helper methods whenever possible to avoid mistakes
+when manually creating NDEF records.</p>
+
+<p>
+This section also describes how to create the corresponding
intent filter for the record. All of these NDEF record examples should be in the first NDEF
record of the NDEF message that you are writing to a tag or beaming.</p>
<h3 id="abs-uri">TNF_ABSOLUTE_URI</h3>
-<p>Given the following {@link android.nfc.NdefRecord#TNF_ABSOLUTE_URI} NDEF record, which is
-stored as the first record inside of an {@link android.nfc.NdefMessage}:</p>
+<p class="note"><strong>Note:</strong> We recommend that you use the
+ <a href="#well-known-uri"><code>RTD_URI</code></a> type instead
+ of {@link android.nfc.NdefRecord#TNF_ABSOLUTE_URI}, because it is more efficient.</p>
+
+<p>You can create a {@link android.nfc.NdefRecord#TNF_ABSOLUTE_URI} NDEF record in the following way:</p>
<pre>
NdefRecord uriRecord = new NdefRecord(
@@ -526,7 +537,7 @@ NdefRecord uriRecord = new NdefRecord(
new byte[0], new byte[0]);
</pre>
-<p>the intent filter would look like this:</p>
+<p>The intent filter for the previous NDEF record would look like this:</p>
<pre>
&lt;intent-filter&gt;
&lt;action android:name="android.nfc.action.NDEF_DISCOVERED" /&gt;
@@ -537,32 +548,35 @@ NdefRecord uriRecord = new NdefRecord(
&lt;/intent-filter&gt;
</pre>
-
<h3 id="mime">TNF_MIME_MEDIA</h3>
-<p>Given the following {@link android.nfc.NdefRecord#TNF_MIME_MEDIA} NDEF record, which is stored as
-the first record inside
-of an {@link android.nfc.NdefMessage}:</p>
+<p>You can create a {@link android.nfc.NdefRecord#TNF_MIME_MEDIA} NDEF record in the following ways.</p>
+
+<p>Using the {@link android.nfc.NdefRecord#createMime createMime()} method:</p>
+<pre>
+NdefRecord mimeRecord = NdefRecord.createMime("application/vnd.com.example.android.beam",
+ "Beam me up, Android".getBytes(Charset.forName("US-ASCII")));
+</pre>
+
+<p>Creating the {@link android.nfc.NdefRecord} manually:</p>
<pre>
NdefRecord mimeRecord = new NdefRecord(
NdefRecord.TNF_MIME_MEDIA ,
- "application/com.example.android.beam".getBytes(Charset.forName("US-ASCII")),
+ "application/vnd.com.example.android.beam".getBytes(Charset.forName("US-ASCII")),
new byte[0], "Beam me up, Android!".getBytes(Charset.forName("US-ASCII")));
</pre>
-<p>the intent filter would look like this:</p>
+<p>The intent filter for the previous NDEF records would look like this:</p>
<pre>
&lt;intent-filter&gt;
&lt;action android:name="android.nfc.action.NDEF_DISCOVERED" /&gt;
&lt;category android:name="android.intent.category.DEFAULT" /&gt;
- &lt;data android:mimeType="application/com.example.android.beam" /&gt;
+ &lt;data android:mimeType="application/vnd.com.example.android.beam" /&gt;
&lt;/intent-filter&gt;
</pre>
-
<h3 id="well-known-text">TNF_WELL_KNOWN with RTD_TEXT</h3>
-<p>Given the following {@link android.nfc.NdefRecord#TNF_WELL_KNOWN} NDEF record, which is stored as
-the first record inside of an {@link android.nfc.NdefMessage}:</p>
+<p>You can create a {@link android.nfc.NdefRecord#TNF_WELL_KNOWN} NDEF record in the following way:</p>
<pre>
public NdefRecord createTextRecord(String payload, Locale locale, boolean encodeInUtf8) {
byte[] langBytes = locale.getLanguage().getBytes(Charset.forName("US-ASCII"));
@@ -592,9 +606,20 @@ public NdefRecord createTextRecord(String payload, Locale locale, boolean encode
<h3 id="well-known-uri">TNF_WELL_KNOWN with RTD_URI</h3>
-<p>Given the following {@link android.nfc.NdefRecord#TNF_WELL_KNOWN} NDEF record, which is stored as
-the first record inside of an {@link android.nfc.NdefMessage}:</p>
+<p>You can create a {@link android.nfc.NdefRecord#TNF_WELL_KNOWN} NDEF record in the following ways.</p>
+
+<p>Using the {@link android.nfc.NdefRecord#createUri(String)} method:</p>
+<pre>
+NdefRecord rtdUriRecord1 = NdefRecord.createUri("http://example.com");
+</pre>
+<p>Using the {@link android.nfc.NdefRecord#createUri(Uri)} method:</p>
+<pre>
+Uri uri = new Uri("http://example.com");
+NdefRecord rtdUriRecord2 = NdefRecord.createUri(uri);
+</pre>
+
+<p>Creating the {@link android.nfc.NdefRecord} manually:</p>
<pre>
byte[] uriField = "example.com".getBytes(Charset.forName("US-ASCII"));
byte[] payload = new byte[uriField.length + 1]; //add 1 for the URI Prefix
@@ -604,7 +629,7 @@ NdefRecord rtdUriRecord = new NdefRecord(
NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_URI, new byte[0], payload);
</pre>
-<p>the intent filter would look like this:</p>
+<p>The intent filter for the previous NDEF records would look like this:</p>
<pre>
&lt;intent-filter&gt;
@@ -617,24 +642,32 @@ NdefRecord rtdUriRecord = new NdefRecord(
</pre>
<h3 id="ext-type">TNF_EXTERNAL_TYPE</h3>
-<p>Given the following {@link android.nfc.NdefRecord#TNF_EXTERNAL_TYPE} NDEF record, which is stored
-as the first record inside of an {@link android.nfc.NdefMessage}:</p>
+<p>You can create a {@link android.nfc.NdefRecord#TNF_EXTERNAL_TYPE} NDEF record in the following ways:</p>
+
+<p>Using the {@link android.nfc.NdefRecord#createExternal createExternal()} method:
+<pre>
+byte[] payload; //assign to your data
+String domain = "com.example"; //usually your app's package name
+String type = "externalType";
+NdefRecord extRecord = NdefRecord.createExternal(domain, type, payload);
+</pre>
+<p>Creating the {@link android.nfc.NdefRecord} manually:</p>
<pre>
byte[] payload;
...
-NdefRecord mimeRecord = new NdefRecord(
- NdefRecord.TNF_EXTERNAL_TYPE, "example.com:externalType", new byte[0], payload);
+NdefRecord extRecord = new NdefRecord(
+ NdefRecord.TNF_EXTERNAL_TYPE, "com.example:externalType", new byte[0], payload);
</pre>
-<p>the intent filter would look like this:</p>
+<p>The intent filter for the previous NDEF records would look like this:</p>
<pre>
&lt;intent-filter&gt;
&lt;action android:name="android.nfc.action.NDEF_DISCOVERED" /&gt;
&lt;category android:name="android.intent.category.DEFAULT" /&gt;
&lt;data android:scheme="vnd.android.nfc"
android:host="ext"
- android:pathPrefix="/example.com:externalType"/&gt;
+ android:pathPrefix="/com.example:externalType"/&gt;
&lt;/intent-filter&gt;
</pre>
@@ -840,8 +873,8 @@ public class Beam extends Activity implements CreateNdefMessageCallback {
String text = ("Beam me up, Android!\n\n" +
"Beam Time: " + System.currentTimeMillis());
NdefMessage msg = new NdefMessage(
- new NdefRecord[] { createMimeRecord(
- "application/com.example.android.beam", text.getBytes())
+ new NdefRecord[] { createMime(
+ "application/vnd.com.example.android.beam", text.getBytes())
/**
* The Android Application Record (AAR) is commented out. When a device
* receives a push with an AAR in it, the application specified in the AAR
@@ -882,22 +915,12 @@ public class Beam extends Activity implements CreateNdefMessageCallback {
// record 0 contains the MIME type, record 1 is the AAR, if present
textView.setText(new String(msg.getRecords()[0].getPayload()));
}
-
- /**
- * Creates a custom MIME type encapsulated in an NDEF record
- */
- public NdefRecord createMimeRecord(String mimeType, byte[] payload) {
- byte[] mimeBytes = mimeType.getBytes(Charset.forName("US-ASCII"));
- NdefRecord mimeRecord = new NdefRecord(
- NdefRecord.TNF_MIME_MEDIA, mimeBytes, new byte[0], payload);
- return mimeRecord;
- }
}
</pre>
<p>Note that this code comments out an AAR, which you can remove. If you enable the AAR, the
application specified in the AAR always receives the Android Beam message. If the application is not
-present, Google Play launches to download the application. Therefore, the following intent
+present, Google Play is started to download the application. Therefore, the following intent
filter is not technically necessary for Android 4.0 devices or later if the AAR is used:
</p>
@@ -905,13 +928,13 @@ filter is not technically necessary for Android 4.0 devices or later if the AAR
&lt;intent-filter&gt;
&lt;action android:name="android.nfc.action.NDEF_DISCOVERED"/&gt;
&lt;category android:name="android.intent.category.DEFAULT"/&gt;
- &lt;data android:mimeType="application/com.example.android.beam"/&gt;
+ &lt;data android:mimeType="application/vnd.com.example.android.beam"/&gt;
&lt;/intent-filter&gt;
</pre>
<p>With this intent filter, the <code>com.example.android.beam</code> application now can be started
when it scans an NFC tag or receives an Android Beam with an AAR of
type <code>com.example.android.beam</code>, or when an NDEF formatted message contains a MIME record
-of type <code>application/com.example.android.beam</code>.</p>
+of type <code>application/vnd.com.example.android.beam</code>.</p>
<p>Even though AARs guarantee an application is started or downloaded, intent filters are
recommended, because they let you start an Activity of your choice in your
diff --git a/docs/html/guide/topics/ui/themes.jd b/docs/html/guide/topics/ui/themes.jd
index d787492d8104..bc1c4f01f931 100644
--- a/docs/html/guide/topics/ui/themes.jd
+++ b/docs/html/guide/topics/ui/themes.jd
@@ -413,8 +413,8 @@ thoroughly describe the styles, so viewing the actual source code for these styl
themes will give you a better understanding of what style properties each one provides.
For a better reference to the Android styles and themes, see the following source code:</p>
<ul>
- <li><a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=core/res/res/values/styles.xml;h=d7b654e49809cb97a35682754b1394af5c8bc88b;hb=HEAD">Android Styles (styles.xml)</a></li>
- <li><a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=core/res/res/values/themes.xml;h=6b3d7407d1c895a3c297e60d5beac98e2d34c271;hb=HEAD">Android Themes (themes.xml)</a></li>
+ <li><a href="https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/res/res/values/styles.xml">Android Styles (styles.xml)</a></li>
+ <li><a href="https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/res/res/values/themes.xml">Android Themes (themes.xml)</a></li>
</ul>
<p>These files will help you learn through example. For instance, in the Android themes source code,
@@ -422,9 +422,8 @@ you'll find a declaration for <code>&lt;style name="Theme.Dialog"&gt;</code>. In
you'll see all of the properties that are used to style dialogs that are used by the Android
framework.</p>
-<p>For more information about the syntax used to create styles in XML, see
-<a href="{@docRoot}guide/topics/resources/available-resources.html#stylesandthemes">Available Resource Types:
-Style and Themes</a>.</p>
+<p>For more information about the syntax for styles and themes in XML, see the
+<a href="{@docRoot}guide/topics/resources/style-resource.html">Style Resource</a> document.</p>
<p>For a reference of available style attributes that you can use to define a style or theme
(e.g., "windowBackground" or "textAppearance"), see {@link android.R.attr} or the respective
diff --git a/docs/html/training/displaying-bitmaps/cache-bitmap.jd b/docs/html/training/displaying-bitmaps/cache-bitmap.jd
index 94abe2186561..2a333ccae62f 100644
--- a/docs/html/training/displaying-bitmaps/cache-bitmap.jd
+++ b/docs/html/training/displaying-bitmaps/cache-bitmap.jd
@@ -96,7 +96,7 @@ and leave the rest of your app little memory to work with.</p>
<p>Here’s an example of setting up a {@link android.util.LruCache} for bitmaps:</p>
<pre>
-private LruCache<String, Bitmap> mMemoryCache;
+private LruCache&lt;String, Bitmap&gt; mMemoryCache;
&#64;Override
protected void onCreate(Bundle savedInstanceState) {
@@ -109,7 +109,7 @@ protected void onCreate(Bundle savedInstanceState) {
// Use 1/8th of the available memory for this memory cache.
final int cacheSize = 1024 * 1024 * memClass / 8;
- mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
+ mMemoryCache = new LruCache&lt;String, Bitmap&gt;(cacheSize) {
&#64;Override
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in bytes rather than number of items.
@@ -159,7 +159,7 @@ public void loadBitmap(int resId, ImageView imageView) {
updated to add entries to the memory cache:</p>
<pre>
-class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
+class BitmapWorkerTask extends AsyncTask&lt;Integer, Void, Bitmap&gt; {
...
// Decode image in background.
&#64;Override
@@ -179,7 +179,7 @@ class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
rely on images being available in this cache. Components like {@link android.widget.GridView} with
larger datasets can easily fill up a memory cache. Your application could be interrupted by another
task like a phone call, and while in the background it might be killed and the memory cache
-destroyed. Once the user resumes, your application it has to process each image again.</p>
+destroyed. Once the user resumes, your application has to process each image again.</p>
<p>A disk cache can be used in these cases to persist processed bitmaps and help decrease loading
times where images are no longer available in a memory cache. Of course, fetching images from disk
@@ -190,18 +190,14 @@ be unpredictable.</p>
appropriate place to store cached images if they are accessed more frequently, for example in an
image gallery application.</p>
-<p>Included in the sample code of this class is a basic {@code DiskLruCache} implementation.
-However, a more robust and recommended {@code DiskLruCache} solution is included in the Android 4.0
-source code ({@code libcore/luni/src/main/java/libcore/io/DiskLruCache.java}). Back-porting this
-class for use on previous Android releases should be fairly straightforward (a <a
-href="http://www.google.com/search?q=disklrucache">quick search</a> shows others who have already
-implemented this solution).</p>
-
-<p>Here’s updated example code that uses the simple {@code DiskLruCache} included in the sample
-application of this class:</p>
+<p>The sample code of this class uses a {@code DiskLruCache} implementation that is pulled from the
+<a href="https://android.googlesource.com/platform/libcore/+/master/luni/src/main/java/libcore/io/DiskLruCache.java">Android source</a>. Here’s updated example code that adds a disk cache in addition
+to the existing memory cache:</p>
<pre>
-private DiskLruCache mDiskCache;
+private DiskLruCache mDiskLruCache;
+private final Object mDiskCacheLock = new Object();
+private boolean mDiskCacheStarting = true;
private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB
private static final String DISK_CACHE_SUBDIR = "thumbnails";
@@ -210,12 +206,26 @@ protected void onCreate(Bundle savedInstanceState) {
...
// Initialize memory cache
...
- File cacheDir = getCacheDir(this, DISK_CACHE_SUBDIR);
- mDiskCache = DiskLruCache.openCache(this, cacheDir, DISK_CACHE_SIZE);
+ // Initialize disk cache on background thread
+ File cacheDir = getDiskCacheDir(this, DISK_CACHE_SUBDIR);
+ new InitDiskCacheTask().execute(cacheDir);
...
}
-class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
+class InitDiskCacheTask extends AsyncTask&lt;File, Void, Void&gt; {
+ &#64;Override
+ protected Void doInBackground(File... params) {
+ synchronized (mDiskCacheLock) {
+ File cacheDir = params[0];
+ mDiskLruCache = DiskLruCache.open(cacheDir, DISK_CACHE_SIZE);
+ mDiskCacheStarting = false; // Finished initialization
+ mDiskCacheLock.notifyAll(); // Wake any waiting threads
+ }
+ return null;
+ }
+}
+
+class BitmapWorkerTask extends AsyncTask&lt;Integer, Void, Bitmap&gt; {
...
// Decode image in background.
&#64;Override
@@ -232,7 +242,7 @@ class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
}
// Add final bitmap to caches
- addBitmapToCache(String.valueOf(imageKey, bitmap);
+ addBitmapToCache(imageKey, bitmap);
return bitmap;
}
@@ -246,28 +256,48 @@ public void addBitmapToCache(String key, Bitmap bitmap) {
}
// Also add to disk cache
- if (!mDiskCache.containsKey(key)) {
- mDiskCache.put(key, bitmap);
+ synchronized (mDiskCacheLock) {
+ if (mDiskLruCache != null && mDiskLruCache.get(key) == null) {
+ mDiskLruCache.put(key, bitmap);
+ }
}
}
public Bitmap getBitmapFromDiskCache(String key) {
- return mDiskCache.get(key);
+ synchronized (mDiskCacheLock) {
+ // Wait while disk cache is started from background thread
+ while (mDiskCacheStarting) {
+ try {
+ mDiskCacheLock.wait();
+ } catch (InterruptedException e) {}
+ }
+ if (mDiskLruCache != null) {
+ return mDiskLruCache.get(key);
+ }
+ }
+ return null;
}
// Creates a unique subdirectory of the designated app cache directory. Tries to use external
// but if not mounted, falls back on internal storage.
-public static File getCacheDir(Context context, String uniqueName) {
+public static File getDiskCacheDir(Context context, String uniqueName) {
// Check if media is mounted or storage is built-in, if so, try and use external cache dir
// otherwise use internal cache dir
- final String cachePath = Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED
- || !Environment.isExternalStorageRemovable() ?
- context.getExternalCacheDir().getPath() : context.getCacheDir().getPath();
+ final String cachePath =
+ Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) ||
+ !isExternalStorageRemovable() ? getExternalCacheDir(context).getPath() :
+ context.getCacheDir().getPath();
return new File(cachePath + File.separator + uniqueName);
}
</pre>
+<p class="note"><strong>Note:</strong> Even initializing the disk cache requires disk operations
+and therefore should not take place on the main thread. However, this does mean there's a chance
+the cache is accessed before initialization. To address this, in the above implementation, a lock
+object ensures that the app does not read from the disk cache until the cache has been
+initialized.</p>
+
<p>While the memory cache is checked in the UI thread, the disk cache is checked in the background
thread. Disk operations should never take place on the UI thread. When image processing is
complete, the final bitmap is added to both the memory and disk cache for future use.</p>
@@ -292,7 +322,7 @@ android.widget.ImageView} objects.</p>
changes using a {@link android.app.Fragment}:</p>
<pre>
-private LruCache<String, Bitmap> mMemoryCache;
+private LruCache&lt;String, Bitmap&gt; mMemoryCache;
&#64;Override
protected void onCreate(Bundle savedInstanceState) {
@@ -301,7 +331,7 @@ protected void onCreate(Bundle savedInstanceState) {
RetainFragment.findOrCreateRetainFragment(getFragmentManager());
mMemoryCache = RetainFragment.mRetainedCache;
if (mMemoryCache == null) {
- mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
+ mMemoryCache = new LruCache&lt;String, Bitmap&gt;(cacheSize) {
... // Initialize cache here as usual
}
mRetainFragment.mRetainedCache = mMemoryCache;
@@ -311,7 +341,7 @@ protected void onCreate(Bundle savedInstanceState) {
class RetainFragment extends Fragment {
private static final String TAG = "RetainFragment";
- public LruCache<String, Bitmap> mRetainedCache;
+ public LruCache&lt;String, Bitmap&gt; mRetainedCache;
public RetainFragment() {}
diff --git a/docs/html/training/displaying-bitmaps/display-bitmap.jd b/docs/html/training/displaying-bitmaps/display-bitmap.jd
index 5eac04cbd7f7..4572c42b2434 100644
--- a/docs/html/training/displaying-bitmaps/display-bitmap.jd
+++ b/docs/html/training/displaying-bitmaps/display-bitmap.jd
@@ -103,7 +103,8 @@ public class ImageDetailActivity extends FragmentActivity {
}
</pre>
-<p>The details {@link android.app.Fragment} holds the {@link android.widget.ImageView} children:</p>
+<p>Here is an implementation of the details {@link android.app.Fragment} which holds the {@link android.widget.ImageView} children. This might seem like a perfectly reasonable approach, but can
+you see the drawbacks of this implementation? How could it be improved?</p>
<pre>
public class ImageDetailFragment extends Fragment {
@@ -146,11 +147,11 @@ public class ImageDetailFragment extends Fragment {
}
</pre>
-<p>Hopefully you noticed the issue with this implementation; The images are being read from
-resources on the UI thread which can lead to an application hanging and being force closed. Using an
-{@link android.os.AsyncTask} as described in the <a href="process-bitmap.html">Processing Bitmaps Off
-the UI Thread</a> lesson, it’s straightforward to move image loading and processing to a background
-thread:</p>
+<p>Hopefully you noticed the issue: the images are being read from resources on the UI thread,
+which can lead to an application hanging and being force closed. Using an
+{@link android.os.AsyncTask} as described in the <a href="process-bitmap.html">Processing Bitmaps
+Off the UI Thread</a> lesson, it’s straightforward to move image loading and processing to a
+background thread:</p>
<pre>
public class ImageDetailActivity extends FragmentActivity {
@@ -190,7 +191,7 @@ modifications for a memory cache:</p>
<pre>
public class ImageDetailActivity extends FragmentActivity {
...
- private LruCache<String, Bitmap> mMemoryCache;
+ private LruCache&lt;String, Bitmap&gt; mMemoryCache;
&#64;Override
public void onCreate(Bundle savedInstanceState) {
@@ -229,7 +230,8 @@ UI remains fluid, memory usage remains under control and concurrency is handled
the way {@link android.widget.GridView} recycles its children views).</p>
<p>To start with, here is a standard {@link android.widget.GridView} implementation with {@link
-android.widget.ImageView} children placed inside a {@link android.app.Fragment}:</p>
+android.widget.ImageView} children placed inside a {@link android.app.Fragment}. Again, this might
+seem like a perfectly reasonable approach, but what would make it better?</p>
<pre>
public class ImageGridFragment extends Fragment implements AdapterView.OnItemClickListener {
@@ -261,7 +263,7 @@ public class ImageGridFragment extends Fragment implements AdapterView.OnItemCli
}
&#64;Override
- public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
+ public void onItemClick(AdapterView&lt;?&gt; parent, View v, int position, long id) {
final Intent i = new Intent(getActivity(), ImageDetailActivity.class);
i.putExtra(ImageDetailActivity.EXTRA_IMAGE, position);
startActivity(i);
@@ -345,13 +347,13 @@ public class ImageGridFragment extends Fragment implements AdapterView.OnItemCli
}
static class AsyncDrawable extends BitmapDrawable {
- private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
+ private final WeakReference&lt;BitmapWorkerTask&gt; bitmapWorkerTaskReference;
public AsyncDrawable(Resources res, Bitmap bitmap,
BitmapWorkerTask bitmapWorkerTask) {
super(res, bitmap);
bitmapWorkerTaskReference =
- new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
+ new WeakReference&lt;BitmapWorkerTask&gt;(bitmapWorkerTask);
}
public BitmapWorkerTask getBitmapWorkerTask() {
diff --git a/docs/html/training/displaying-bitmaps/index.jd b/docs/html/training/displaying-bitmaps/index.jd
index 78371ad64fa0..b91172b9c731 100644
--- a/docs/html/training/displaying-bitmaps/index.jd
+++ b/docs/html/training/displaying-bitmaps/index.jd
@@ -43,8 +43,8 @@ exception:<br />{@code java.lang.OutofMemoryError: bitmap size exceeds VM budget
perform under this minimum memory limit. However, keep in mind many devices are configured with
higher limits.</li>
<li>Bitmaps take up a lot of memory, especially for rich images like photographs. For example, the
- camera on the <a href="http://www.google.com/nexus/">Galaxy Nexus</a> takes photos up to 2592x1936
- pixels (5 megapixels). If the bitmap configuration used is {@link
+ camera on the <a href="http://www.android.com/devices/detail/galaxy-nexus">Galaxy Nexus</a> takes
+ photos up to 2592x1936 pixels (5 megapixels). If the bitmap configuration used is {@link
android.graphics.Bitmap.Config ARGB_8888} (the default from the Android 2.3 onward) then loading
this image into memory takes about 19MB of memory (2592*1936*4 bytes), immediately exhausting the
per-app limit on some devices.</li>
@@ -75,4 +75,4 @@ exception:<br />{@code java.lang.OutofMemoryError: bitmap size exceeds VM budget
components like {@link android.support.v4.view.ViewPager} and {@link android.widget.GridView}
using a background thread and bitmap cache.</dd>
-</dl> \ No newline at end of file
+</dl>
diff --git a/docs/html/training/displaying-bitmaps/process-bitmap.jd b/docs/html/training/displaying-bitmaps/process-bitmap.jd
index d1e346c86ad5..d4fcff31f52c 100644
--- a/docs/html/training/displaying-bitmaps/process-bitmap.jd
+++ b/docs/html/training/displaying-bitmaps/process-bitmap.jd
@@ -62,13 +62,13 @@ decodeSampledBitmapFromResource()}</a>: </p>
<a name="BitmapWorkerTask"></a>
<pre>
-class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
- private final WeakReference<ImageView> imageViewReference;
+class BitmapWorkerTask extends AsyncTask&lt;Integer, Void, Bitmap&gt; {
+ private final WeakReference&lt;ImageView&gt; imageViewReference;
private int data = 0;
public BitmapWorkerTask(ImageView imageView) {
// Use a WeakReference to ensure the ImageView can be garbage collected
- imageViewReference = new WeakReference<ImageView>(imageView);
+ imageViewReference = new WeakReference&lt;ImageView&gt;(imageView);
}
// Decode image in background.
@@ -133,13 +133,13 @@ completes:</p>
<a name="AsyncDrawable"></a>
<pre>
static class AsyncDrawable extends BitmapDrawable {
- private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
+ private final WeakReference&lt;BitmapWorkerTask&gt; bitmapWorkerTaskReference;
public AsyncDrawable(Resources res, Bitmap bitmap,
BitmapWorkerTask bitmapWorkerTask) {
super(res, bitmap);
bitmapWorkerTaskReference =
- new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
+ new WeakReference&lt;BitmapWorkerTask&gt;(bitmapWorkerTask);
}
public BitmapWorkerTask getBitmapWorkerTask() {
@@ -211,7 +211,7 @@ one associated with the {@link android.widget.ImageView}:</p>
<a name="BitmapWorkerTaskUpdated"></a>
<pre>
-class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
+class BitmapWorkerTask extends AsyncTask&lt;Integer, Void, Bitmap&gt; {
...
&#64;Override
@@ -236,4 +236,4 @@ class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
android.widget.GridView} components as well as any other components that recycle their child
views. Simply call {@code loadBitmap} where you normally set an image to your {@link
android.widget.ImageView}. For example, in a {@link android.widget.GridView} implementation this
-would be in the {@link android.widget.Adapter#getView getView()} method of the backing adapter.</p> \ No newline at end of file
+would be in the {@link android.widget.Adapter#getView getView()} method of the backing adapter.</p>
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back.png
index 6ae32f184393..84e6bc89c082 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png
index fdc56bbd4f60..782d2147fdcc 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png
index ea7c4e35f40c..a00bc5b5f33b 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png
index 49d5101a3e45..860570124ccf 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back.png
index 33e56e8b2fcb..38bd0cd18f42 100644
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back.png
index 2fb191db9c9c..0c12c1601e56 100644
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back.png
index ce008f39865b..477df5f4c860 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back.png
index b971088cb60b..bd60cd65878a 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png
index d2d78429f1f6..5272c91a2179 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 5841978c2cd0..17dbcaca6d0f 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -67,7 +67,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fadingEdge="none"
- android:overScrollMode="always"
+ android:overScrollMode="ifContentScrolls"
>
<com.android.systemui.statusbar.policy.NotificationRowLayout
android:id="@+id/latestItems"
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 26dba675cf56..fd5ef4ecbee4 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -65,5 +65,8 @@
<!-- Vibration duration for MultiWaveView used in SearchPanelView -->
<integer translatable="false" name="config_search_panel_view_vibration_duration">20</integer>
+
+ <!-- The length of the vibration when the notificaiotn pops open. -->
+ <integer name="blinds_pop_duration_ms">10</integer>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 6c40461f10ac..c6fd66abbec4 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -150,4 +150,10 @@
<!-- Height of the carrier/wifi name label -->
<dimen name="carrier_label_height">24dp</dimen>
+
+ <!-- The distance you can pull a notificaiton before it pops open -->
+ <dimen name="blinds_pop_threshold">32dp</dimen>
+
+ <!-- The size of the gesture span needed to activate the "pull" notification expansion -->
+ <dimen name="pull_span_min">25dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 8ebbc520197d..4a732008137a 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -18,4 +18,5 @@
<resources>
<item type="id" name="expandable_tag" />
<item type="id" name="user_expanded_tag" />
+ <item type="id" name="user_lock_tag" />
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index 5dd15c34a313..dcfd0b346bdf 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -22,28 +22,35 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
+import android.os.Vibrator;
import android.util.Slog;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
+import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
+import java.util.Stack;
+
public class ExpandHelper implements Gefingerpoken, OnClickListener {
public interface Callback {
View getChildAtRawPosition(float x, float y);
View getChildAtPosition(float x, float y);
boolean canChildBeExpanded(View v);
- boolean setUserExpandedChild(View v, boolean userxpanded);
+ boolean setUserExpandedChild(View v, boolean userExpanded);
+ boolean setUserLockedChild(View v, boolean userLocked);
}
private static final String TAG = "ExpandHelper";
protected static final boolean DEBUG = false;
+ protected static final boolean DEBUG_SCALE = false;
+ protected static final boolean DEBUG_GLOW = false;
private static final long EXPAND_DURATION = 250;
private static final long GLOW_DURATION = 150;
- // Set to false to disable focus-based gestures (two-finger pull).
+ // Set to false to disable focus-based gestures (spread-finger vertical pull).
private static final boolean USE_DRAG = true;
// Set to false to disable scale-based gestures (both horizontal and vertical).
private static final boolean USE_SPAN = true;
@@ -62,7 +69,14 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener {
@SuppressWarnings("unused")
private Context mContext;
- private boolean mStretching;
+ private boolean mExpanding;
+ private static final int NONE = 0;
+ private static final int BLINDS = 1<<0;
+ private static final int PULL = 1<<1;
+ private static final int STRETCH = 1<<2;
+ private int mExpansionStyle = NONE;
+ private boolean mWatchingForPull;
+ private boolean mHasPopped;
private View mEventSource;
private View mCurrView;
private View mCurrViewTopGlow;
@@ -70,14 +84,21 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener {
private float mOldHeight;
private float mNaturalHeight;
private float mInitialTouchFocusY;
+ private float mInitialTouchY;
private float mInitialTouchSpan;
+ private int mTouchSlop;
+ private int mLastMotionY;
+ private float mPopLimit;
+ private int mPopDuration;
+ private float mPullGestureMinXSpan;
private Callback mCallback;
- private ScaleGestureDetector mDetector;
+ private ScaleGestureDetector mSGD;
private ViewScaler mScaler;
private ObjectAnimator mScaleAnimation;
private AnimatorSet mGlowAnimationSet;
private ObjectAnimator mGlowTopAnimation;
private ObjectAnimator mGlowBottomAnimation;
+ private Vibrator mVibrator;
private int mSmallSize;
private int mLargeSize;
@@ -85,6 +106,8 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener {
private int mGravity;
+ private View mScrollView;
+
private class ViewScaler {
View mView;
@@ -93,7 +116,7 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener {
mView = v;
}
public void setHeight(float h) {
- if (DEBUG) Slog.v(TAG, "SetHeight: setting to " + h);
+ if (DEBUG_SCALE) Slog.v(TAG, "SetHeight: setting to " + h);
ViewGroup.LayoutParams lp = mView.getLayoutParams();
lp.height = (int)h;
mView.setLayoutParams(lp);
@@ -104,11 +127,12 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener {
if (height < 0) {
height = mView.getMeasuredHeight();
}
- return (float) height;
+ return height;
}
public int getNaturalHeight(int maximum) {
ViewGroup.LayoutParams lp = mView.getLayoutParams();
- if (DEBUG) Slog.v(TAG, "Inspecting a child of type: " + mView.getClass().getName());
+ if (DEBUG_SCALE) Slog.v(TAG, "Inspecting a child of type: " +
+ mView.getClass().getName());
int oldHeight = lp.height;
lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
mView.setLayoutParams(lp);
@@ -142,6 +166,9 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener {
mGravity = Gravity.TOP;
mScaleAnimation = ObjectAnimator.ofFloat(mScaler, "height", 0f);
mScaleAnimation.setDuration(EXPAND_DURATION);
+ mPopLimit = mContext.getResources().getDimension(R.dimen.blinds_pop_threshold);
+ mPopDuration = mContext.getResources().getInteger(R.integer.blinds_pop_duration_ms);
+ mPullGestureMinXSpan = mContext.getResources().getDimension(R.dimen.pull_span_min);
AnimatorListenerAdapter glowVisibilityController = new AnimatorListenerAdapter() {
@Override
@@ -169,74 +196,108 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener {
mGlowAnimationSet.play(mGlowTopAnimation).with(mGlowBottomAnimation);
mGlowAnimationSet.setDuration(GLOW_DURATION);
- mDetector =
- new ScaleGestureDetector(context,
+ final ViewConfiguration configuration = ViewConfiguration.get(mContext);
+ mTouchSlop = configuration.getScaledTouchSlop();
+
+ mSGD = new ScaleGestureDetector(context,
new ScaleGestureDetector.SimpleOnScaleGestureListener() {
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
- if (DEBUG) Slog.v(TAG, "onscalebegin()");
- float x = detector.getFocusX();
- float y = detector.getFocusY();
-
- View v = null;
- if (mEventSource != null) {
- int[] location = new int[2];
- mEventSource.getLocationOnScreen(location);
- x += (float) location[0];
- y += (float) location[1];
- v = mCallback.getChildAtRawPosition(x, y);
- } else {
- v = mCallback.getChildAtPosition(x, y);
- }
+ if (DEBUG_SCALE) Slog.v(TAG, "onscalebegin()");
+ float focusX = detector.getFocusX();
+ float focusY = detector.getFocusY();
// your fingers have to be somewhat close to the bounds of the view in question
- mInitialTouchFocusY = detector.getFocusY();
+ mInitialTouchFocusY = focusY;
mInitialTouchSpan = Math.abs(detector.getCurrentSpan());
- if (DEBUG) Slog.d(TAG, "got mInitialTouchSpan: (" + mInitialTouchSpan + ")");
+ if (DEBUG_SCALE) Slog.d(TAG, "got mInitialTouchSpan: (" + mInitialTouchSpan + ")");
- mStretching = initScale(v);
- return mStretching;
+ final View underFocus = findView(focusX, focusY);
+ if (underFocus != null) {
+ startExpanding(underFocus, STRETCH);
+ }
+ return mExpanding;
}
@Override
public boolean onScale(ScaleGestureDetector detector) {
- if (DEBUG) Slog.v(TAG, "onscale() on " + mCurrView);
-
- // are we scaling or dragging?
- float span = Math.abs(detector.getCurrentSpan()) - mInitialTouchSpan;
- span *= USE_SPAN ? 1f : 0f;
- float drag = detector.getFocusY() - mInitialTouchFocusY;
- drag *= USE_DRAG ? 1f : 0f;
- drag *= mGravity == Gravity.BOTTOM ? -1f : 1f;
- float pull = Math.abs(drag) + Math.abs(span) + 1f;
- float hand = drag * Math.abs(drag) / pull + span * Math.abs(span) / pull;
- if (DEBUG) Slog.d(TAG, "current span handle is: " + hand);
- hand = hand + mOldHeight;
- float target = hand;
- if (DEBUG) Slog.d(TAG, "target is: " + target);
- hand = hand < mSmallSize ? mSmallSize : (hand > mLargeSize ? mLargeSize : hand);
- hand = hand > mNaturalHeight ? mNaturalHeight : hand;
- if (DEBUG) Slog.d(TAG, "scale continues: hand =" + hand);
- mScaler.setHeight(hand);
-
- // glow if overscale
- float stretch = (float) Math.abs((target - hand) / mMaximumStretch);
- float strength = 1f / (1f + (float) Math.pow(Math.E, -1 * ((8f * stretch) - 5f)));
- if (DEBUG) Slog.d(TAG, "stretch: " + stretch + " strength: " + strength);
- setGlow(GLOW_BASE + strength * (1f - GLOW_BASE));
+ if (DEBUG_SCALE) Slog.v(TAG, "onscale() on " + mCurrView);
+ updateExpansion();
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
- if (DEBUG) Slog.v(TAG, "onscaleend()");
+ if (DEBUG_SCALE) Slog.v(TAG, "onscaleend()");
// I guess we're alone now
- if (DEBUG) Slog.d(TAG, "scale end");
- finishScale(false);
+ if (DEBUG_SCALE) Slog.d(TAG, "scale end");
+ finishExpanding(false);
+ clearView();
}
});
}
+ private void updateExpansion() {
+ // are we scaling or dragging?
+ float span = Math.abs(mSGD.getCurrentSpan()) - mInitialTouchSpan;
+ span *= USE_SPAN ? 1f : 0f;
+ float drag = mSGD.getFocusY() - mInitialTouchFocusY;
+ drag *= USE_DRAG ? 1f : 0f;
+ drag *= mGravity == Gravity.BOTTOM ? -1f : 1f;
+ float pull = Math.abs(drag) + Math.abs(span) + 1f;
+ float hand = drag * Math.abs(drag) / pull + span * Math.abs(span) / pull;
+ float target = hand + mOldHeight;
+ float newHeight = clamp(target);
+ mScaler.setHeight(newHeight);
+
+ setGlow(calculateGlow(target, newHeight));
+ }
+
+ private float clamp(float target) {
+ float out = target;
+ out = out < mSmallSize ? mSmallSize : (out > mLargeSize ? mLargeSize : out);
+ out = out > mNaturalHeight ? mNaturalHeight : out;
+ return out;
+ }
+
+ private View findView(float x, float y) {
+ View v = null;
+ if (mEventSource != null) {
+ int[] location = new int[2];
+ mEventSource.getLocationOnScreen(location);
+ x += location[0];
+ y += location[1];
+ v = mCallback.getChildAtRawPosition(x, y);
+ } else {
+ v = mCallback.getChildAtPosition(x, y);
+ }
+ return v;
+ }
+
+ private boolean isInside(View v, float x, float y) {
+ if (DEBUG) Slog.d(TAG, "isinside (" + x + ", " + y + ")");
+
+ if (v == null) {
+ if (DEBUG) Slog.d(TAG, "isinside null subject");
+ return false;
+ }
+ if (mEventSource != null) {
+ int[] location = new int[2];
+ mEventSource.getLocationOnScreen(location);
+ x += location[0];
+ y += location[1];
+ if (DEBUG) Slog.d(TAG, " to global (" + x + ", " + y + ")");
+ }
+ int[] location = new int[2];
+ v.getLocationOnScreen(location);
+ x -= location[0];
+ y -= location[1];
+ if (DEBUG) Slog.d(TAG, " to local (" + x + ", " + y + ")");
+ if (DEBUG) Slog.d(TAG, " inside (" + v.getWidth() + ", " + v.getHeight() + ")");
+ boolean inside = (x > 0f && y > 0f && x < v.getWidth() & y < v.getHeight());
+ return inside;
+ }
+
public void setEventSource(View eventSource) {
mEventSource = eventSource;
}
@@ -245,13 +306,26 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener {
mGravity = gravity;
}
+ public void setScrollView(View scrollView) {
+ mScrollView = scrollView;
+ }
+
+ private float calculateGlow(float target, float actual) {
+ // glow if overscale
+ if (DEBUG_GLOW) Slog.d(TAG, "target: " + target + " actual: " + actual);
+ float stretch = Math.abs((target - actual) / mMaximumStretch);
+ float strength = 1f / (1f + (float) Math.pow(Math.E, -1 * ((8f * stretch) - 5f)));
+ if (DEBUG_GLOW) Slog.d(TAG, "stretch: " + stretch + " strength: " + strength);
+ return (GLOW_BASE + strength * (1f - GLOW_BASE));
+ }
+
public void setGlow(float glow) {
if (!mGlowAnimationSet.isRunning() || glow == 0f) {
if (mGlowAnimationSet.isRunning()) {
- mGlowAnimationSet.cancel();
+ mGlowAnimationSet.end();
}
if (mCurrViewTopGlow != null && mCurrViewBottomGlow != null) {
- if (glow == 0f || mCurrViewTopGlow.getAlpha() == 0f) {
+ if (glow == 0f || mCurrViewTopGlow.getAlpha() == 0f) {
// animate glow in and out
mGlowTopAnimation.setTarget(mCurrViewTopGlow);
mGlowBottomAnimation.setTarget(mCurrViewBottomGlow);
@@ -276,72 +350,196 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener {
View.INVISIBLE : View.VISIBLE);
}
+ @Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
- if (DEBUG) Slog.d(TAG, "interceptTouch: act=" + (ev.getAction()) +
- " stretching=" + mStretching);
- mDetector.onTouchEvent(ev);
- return mStretching;
+ final int action = ev.getAction();
+ if (DEBUG_SCALE) Slog.d(TAG, "intercept: act=" + MotionEvent.actionToString(action) +
+ " expanding=" + mExpanding +
+ (0 != (mExpansionStyle & BLINDS) ? " (blinds)" : "") +
+ (0 != (mExpansionStyle & PULL) ? " (pull)" : "") +
+ (0 != (mExpansionStyle & STRETCH) ? " (stretch)" : ""));
+ // check for a spread-finger vertical pull gesture
+ mSGD.onTouchEvent(ev);
+ final int x = (int) mSGD.getFocusX();
+ final int y = (int) mSGD.getFocusY();
+ if (mExpanding) {
+ return true;
+ } else {
+ if ((action == MotionEvent.ACTION_MOVE) && 0 != (mExpansionStyle & BLINDS)) {
+ // we've begun Venetian blinds style expansion
+ return true;
+ }
+ final float xspan = mSGD.getCurrentSpanX();
+ if ((action == MotionEvent.ACTION_MOVE &&
+ xspan > mPullGestureMinXSpan &&
+ xspan > mSGD.getCurrentSpanY())) {
+ // detect a vertical pulling gesture with fingers somewhat separated
+ if (DEBUG_SCALE) Slog.v(TAG, "got pull gesture (xspan=" + xspan + "px)");
+
+ mInitialTouchFocusY = y;
+
+ final View underFocus = findView(x, y);
+ if (underFocus != null) {
+ startExpanding(underFocus, PULL);
+ }
+ return true;
+ }
+ if (mScrollView != null && mScrollView.getScrollY() > 0) {
+ return false;
+ }
+ // Now look for other gestures
+ switch (action & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_MOVE: {
+ if (mWatchingForPull) {
+ final int yDiff = y - mLastMotionY;
+ if (yDiff > mTouchSlop) {
+ if (DEBUG) Slog.v(TAG, "got venetian gesture (dy=" + yDiff + "px)");
+ mLastMotionY = y;
+ final View underFocus = findView(x, y);
+ if (underFocus != null) {
+ startExpanding(underFocus, BLINDS);
+ mInitialTouchY = mLastMotionY;
+ mHasPopped = false;
+ }
+ }
+ }
+ break;
+ }
+
+ case MotionEvent.ACTION_DOWN:
+ mWatchingForPull = isInside(mScrollView, x, y);
+ mLastMotionY = y;
+ break;
+
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ if (DEBUG) Slog.d(TAG, "up/cancel");
+ finishExpanding(false);
+ clearView();
+ break;
+ }
+ return mExpanding;
+ }
}
+ @Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
- if (DEBUG) Slog.d(TAG, "touch: act=" + (action) + " stretching=" + mStretching);
- if (mStretching) {
- if (DEBUG) Slog.d(TAG, "detector ontouch");
- mDetector.onTouchEvent(ev);
- }
+ if (DEBUG_SCALE) Slog.d(TAG, "touch: act=" + MotionEvent.actionToString(action) +
+ " expanding=" + mExpanding +
+ (0 != (mExpansionStyle & BLINDS) ? " (blinds)" : "") +
+ (0 != (mExpansionStyle & PULL) ? " (pull)" : "") +
+ (0 != (mExpansionStyle & STRETCH) ? " (stretch)" : ""));
+
+ mSGD.onTouchEvent(ev);
+
switch (action) {
+ case MotionEvent.ACTION_MOVE: {
+ if (0 != (mExpansionStyle & BLINDS)) {
+ final float rawHeight = ev.getY() - mInitialTouchY + mOldHeight;
+ final float newHeight = clamp(rawHeight);
+ final boolean wasClosed = (mOldHeight == mSmallSize);
+ boolean isFinished = false;
+ if (rawHeight > mNaturalHeight) {
+ isFinished = true;
+ }
+ if (rawHeight < mSmallSize) {
+ isFinished = true;
+ }
+
+ final float pull = Math.abs(ev.getY() - mInitialTouchY);
+ if (mHasPopped || pull > mPopLimit) {
+ if (!mHasPopped) {
+ vibrate(mPopDuration);
+ mHasPopped = true;
+ }
+ }
+
+ if (mHasPopped) {
+ mScaler.setHeight(newHeight);
+ setGlow(GLOW_BASE);
+ } else {
+ setGlow(calculateGlow(4f * pull, 0f));
+ }
+
+ final int x = (int) mSGD.getFocusX();
+ final int y = (int) mSGD.getFocusY();
+ View underFocus = findView(x, y);
+ if (isFinished && underFocus != null && underFocus != mCurrView) {
+ finishExpanding(false); // @@@ needed?
+ startExpanding(underFocus, BLINDS);
+ mInitialTouchY = y;
+ mHasPopped = false;
+ }
+ return true;
+ }
+
+ if (mExpanding) {
+ updateExpansion();
+ return true;
+ }
+
+ break;
+ }
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
- if (DEBUG) Slog.d(TAG, "cancel");
- mStretching = false;
+ if (DEBUG) Slog.d(TAG, "up/cancel");
+ finishExpanding(false);
clearView();
break;
}
return true;
}
- private boolean initScale(View v) {
- if (v != null) {
- if (DEBUG) Slog.d(TAG, "scale begins on view: " + v);
- mStretching = true;
- setView(v);
- setGlow(GLOW_BASE);
- mScaler.setView(v);
- mOldHeight = mScaler.getHeight();
- if (mCallback.canChildBeExpanded(v)) {
- if (DEBUG) Slog.d(TAG, "working on an expandable child");
- mNaturalHeight = mScaler.getNaturalHeight(mLargeSize);
- } else {
- if (DEBUG) Slog.d(TAG, "working on a non-expandable child");
- mNaturalHeight = mOldHeight;
- }
- if (DEBUG) Slog.d(TAG, "got mOldHeight: " + mOldHeight +
- " mNaturalHeight: " + mNaturalHeight);
- v.getParent().requestDisallowInterceptTouchEvent(true);
+
+ private void startExpanding(View v, int expandType) {
+ mExpanding = true;
+ mExpansionStyle = expandType;
+ if (DEBUG) Slog.d(TAG, "scale type " + expandType + " beginning on view: " + v);
+ mCallback.setUserLockedChild(v, true);
+ setView(v);
+ setGlow(GLOW_BASE);
+ mScaler.setView(v);
+ mOldHeight = mScaler.getHeight();
+ if (mCallback.canChildBeExpanded(v)) {
+ if (DEBUG) Slog.d(TAG, "working on an expandable child");
+ mNaturalHeight = mScaler.getNaturalHeight(mLargeSize);
+ } else {
+ if (DEBUG) Slog.d(TAG, "working on a non-expandable child");
+ mNaturalHeight = mOldHeight;
}
- return mStretching;
+ if (DEBUG) Slog.d(TAG, "got mOldHeight: " + mOldHeight +
+ " mNaturalHeight: " + mNaturalHeight);
+ v.getParent().requestDisallowInterceptTouchEvent(true);
}
- private void finishScale(boolean force) {
+ private void finishExpanding(boolean force) {
+ if (!mExpanding) return;
+
+ float currentHeight = mScaler.getHeight();
+ float targetHeight = mSmallSize;
float h = mScaler.getHeight();
final boolean wasClosed = (mOldHeight == mSmallSize);
if (wasClosed) {
- h = (force || h > mSmallSize) ? mNaturalHeight : mSmallSize;
+ targetHeight = (force || currentHeight > mSmallSize) ? mNaturalHeight : mSmallSize;
} else {
- h = (force || h < mNaturalHeight) ? mSmallSize : mNaturalHeight;
+ targetHeight = (force || currentHeight < mNaturalHeight) ? mSmallSize : mNaturalHeight;
}
- if (DEBUG && mCurrView != null) mCurrView.setBackgroundColor(0);
if (mScaleAnimation.isRunning()) {
mScaleAnimation.cancel();
}
- mScaleAnimation.setFloatValues(h);
- mScaleAnimation.setupStartValues();
- mScaleAnimation.start();
- mStretching = false;
setGlow(0f);
mCallback.setUserExpandedChild(mCurrView, h == mNaturalHeight);
+ if (targetHeight != currentHeight) {
+ mScaleAnimation.setFloatValues(targetHeight);
+ mScaleAnimation.setupStartValues();
+ mScaleAnimation.start();
+ }
+ mCallback.setUserLockedChild(mCurrView, false);
+
+ mExpanding = false;
+ mExpansionStyle = NONE;
+
if (DEBUG) Slog.d(TAG, "scale was finished on view: " + mCurrView);
- clearView();
}
private void clearView() {
@@ -357,7 +555,7 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener {
mCurrViewTopGlow = g.findViewById(R.id.top_glow);
mCurrViewBottomGlow = g.findViewById(R.id.bottom_glow);
if (DEBUG) {
- String debugLog = "Looking for glows: " +
+ String debugLog = "Looking for glows: " +
(mCurrViewTopGlow != null ? "found top " : "didn't find top") +
(mCurrViewBottomGlow != null ? "found bottom " : "didn't find bottom");
Slog.v(TAG, debugLog);
@@ -367,8 +565,20 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener {
@Override
public void onClick(View v) {
- initScale(v);
- finishScale(true);
+ startExpanding(v, STRETCH);
+ finishExpanding(true);
+ clearView();
+ }
+ /**
+ * Triggers haptic feedback.
+ */
+ private synchronized void vibrate(long duration) {
+ if (mVibrator == null) {
+ mVibrator = (android.os.Vibrator)
+ mContext.getSystemService(Context.VIBRATOR_SERVICE);
+ }
+ mVibrator.vibrate(duration);
}
}
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 1204a8917d05..8d7734e0dcfa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -783,16 +783,20 @@ public abstract class BaseStatusBar extends SystemUI implements
int N = mNotificationData.size();
for (int i = 0; i < N; i++) {
NotificationData.Entry entry = mNotificationData.get(i);
- if (i == (N-1)) {
- if (DEBUG) Slog.d(TAG, "expanding top notification at " + i);
- expandView(entry, true);
- } else {
- if (!entry.userExpanded()) {
- if (DEBUG) Slog.d(TAG, "collapsing notification at " + i);
- expandView(entry, false);
+ if (!entry.userLocked()) {
+ if (i == (N-1)) {
+ if (DEBUG) Slog.d(TAG, "expanding top notification at " + i);
+ expandView(entry, true);
} else {
- if (DEBUG) Slog.d(TAG, "ignoring user-modified notification at " + i);
+ if (!entry.userExpanded()) {
+ if (DEBUG) Slog.d(TAG, "collapsing notification at " + i);
+ expandView(entry, false);
+ } else {
+ if (DEBUG) Slog.d(TAG, "ignoring user-modified notification at " + i);
+ }
}
+ } else {
+ if (DEBUG) Slog.d(TAG, "ignoring notification being held by user at " + i);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index dfd8cf8a4689..c82f25008f2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -71,6 +71,18 @@ public class NotificationData {
public boolean setUserExpanded(boolean userExpanded) {
return NotificationData.setUserExpanded(row, userExpanded);
}
+ /**
+ * Return whether the entry is being touched by the user.
+ */
+ public boolean userLocked() {
+ return NotificationData.getUserLocked(row);
+ }
+ /**
+ * Set the flag indicating that this is being touched by the user.
+ */
+ public boolean setUserLocked(boolean userLocked) {
+ return NotificationData.setUserLocked(row, userLocked);
+ }
}
private final ArrayList<Entry> mEntries = new ArrayList<Entry>();
private final Comparator<Entry> mEntryCmp = new Comparator<Entry>() {
@@ -197,4 +209,18 @@ public class NotificationData {
public static boolean setUserExpanded(View row, boolean userExpanded) {
return writeBooleanTag(row, R.id.user_expanded_tag, userExpanded);
}
+
+ /**
+ * Return whether the entry is being touched by the user.
+ */
+ public static boolean getUserLocked(View row) {
+ return readBooleanTag(row, R.id.user_lock_tag);
+ }
+
+ /**
+ * Set whether the entry is being touched by the user.
+ */
+ public static boolean setUserLocked(View row, boolean userLocked) {
+ return writeBooleanTag(row, R.id.user_lock_tag, userLocked);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 931756197c23..2628631338fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -53,6 +53,7 @@ public class StatusBarWindowView extends FrameLayout
int maxHeight = getResources().getDimensionPixelSize(R.dimen.notification_row_max_height);
mExpandHelper = new ExpandHelper(mContext, latestItems, minHeight, maxHeight);
mExpandHelper.setEventSource(this);
+ mExpandHelper.setScrollView(scroller);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
index 61e5ab63d6e8..89eed1bd6138 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
@@ -78,6 +78,7 @@ public class NotificationRowLayout
super(context, attrs, defStyle);
mRealLayoutTransition = new LayoutTransition();
+ mRealLayoutTransition.setAnimateParentHierarchy(true);
setLayoutTransitionsEnabled(true);
setOrientation(LinearLayout.VERTICAL);
@@ -161,7 +162,12 @@ public class NotificationRowLayout
return NotificationData.setUserExpanded(v, userExpanded);
}
+ public boolean setUserLockedChild(View v, boolean userLocked) {
+ return NotificationData.setUserLocked(v, userLocked);
+ }
+
public void onChildDismissed(View v) {
+ if (DEBUG) Slog.v(TAG, "onChildDismissed: " + v + " mRemoveViews=" + mRemoveViews);
final View veto = v.findViewById(R.id.veto);
if (veto != null && veto.getVisibility() != View.GONE && mRemoveViews) {
veto.performClick();
@@ -225,6 +231,7 @@ public class NotificationRowLayout
* get removed properly.
*/
public void setViewRemoval(boolean removeViews) {
+ if (DEBUG) Slog.v(TAG, "setViewRemoval: " + removeViews);
mRemoveViews = removeViews;
}
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 2167c492e5a9..1f3f172bf28f 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -2450,6 +2450,21 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
+ // Cull any packages that run as system-domain uids but do not define their
+ // own backup agents
+ for (int i = 0; i < packagesToBackup.size(); ) {
+ PackageInfo pkg = packagesToBackup.get(i);
+ if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID)
+ && (pkg.applicationInfo.backupAgentName == null)) {
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "... ignoring non-agent system package " + pkg.packageName);
+ }
+ packagesToBackup.remove(i);
+ } else {
+ i++;
+ }
+ }
+
FileOutputStream ofstream = new FileOutputStream(mOutputFile.getFileDescriptor());
OutputStream out = null;
@@ -3664,29 +3679,37 @@ class BackupManagerService extends IBackupManager.Stub {
// Fall through to IGNORE if the app explicitly disallows backup
final int flags = pkgInfo.applicationInfo.flags;
if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
- // Verify signatures against any installed version; if they
- // don't match, then we fall though and ignore the data. The
- // signatureMatch() method explicitly ignores the signature
- // check for packages installed on the system partition, because
- // such packages are signed with the platform cert instead of
- // the app developer's cert, so they're different on every
- // device.
- if (signaturesMatch(sigs, pkgInfo)) {
- if (pkgInfo.versionCode >= version) {
- Slog.i(TAG, "Sig + version match; taking data");
- policy = RestorePolicy.ACCEPT;
+ // Restore system-uid-space packages only if they have
+ // defined a custom backup agent
+ if ((pkgInfo.applicationInfo.uid >= Process.FIRST_APPLICATION_UID)
+ || (pkgInfo.applicationInfo.backupAgentName != null)) {
+ // Verify signatures against any installed version; if they
+ // don't match, then we fall though and ignore the data. The
+ // signatureMatch() method explicitly ignores the signature
+ // check for packages installed on the system partition, because
+ // such packages are signed with the platform cert instead of
+ // the app developer's cert, so they're different on every
+ // device.
+ if (signaturesMatch(sigs, pkgInfo)) {
+ if (pkgInfo.versionCode >= version) {
+ Slog.i(TAG, "Sig + version match; taking data");
+ policy = RestorePolicy.ACCEPT;
+ } else {
+ // The data is from a newer version of the app than
+ // is presently installed. That means we can only
+ // use it if the matching apk is also supplied.
+ Slog.d(TAG, "Data version " + version
+ + " is newer than installed version "
+ + pkgInfo.versionCode + " - requiring apk");
+ policy = RestorePolicy.ACCEPT_IF_APK;
+ }
} else {
- // The data is from a newer version of the app than
- // is presently installed. That means we can only
- // use it if the matching apk is also supplied.
- Slog.d(TAG, "Data version " + version
- + " is newer than installed version "
- + pkgInfo.versionCode + " - requiring apk");
- policy = RestorePolicy.ACCEPT_IF_APK;
+ Slog.w(TAG, "Restore manifest signatures do not match "
+ + "installed application for " + info.packageName);
}
} else {
- Slog.w(TAG, "Restore manifest signatures do not match "
- + "installed application for " + info.packageName);
+ Slog.w(TAG, "Package " + info.packageName
+ + " is system level with no agent");
}
} else {
if (DEBUG) Slog.i(TAG, "Restore manifest from "
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
index b7569daa84c8..4896efb9fc81 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
@@ -512,8 +512,8 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
|| !TextUtils.equals(plmn, curPlmn)) {
boolean showSpn = !mEmergencyOnly && !TextUtils.isEmpty(spn)
&& (rule & SIMRecords.SPN_RULE_SHOW_SPN) == SIMRecords.SPN_RULE_SHOW_SPN;
- boolean showPlmn = !TextUtils.isEmpty(plmn) &&
- (rule & SIMRecords.SPN_RULE_SHOW_PLMN) == SIMRecords.SPN_RULE_SHOW_PLMN;
+ boolean showPlmn = !TextUtils.isEmpty(plmn) && (mEmergencyOnly ||
+ ((rule & SIMRecords.SPN_RULE_SHOW_PLMN) == SIMRecords.SPN_RULE_SHOW_PLMN));
if (DBG) {
log(String.format("updateSpnDisplay: changed sending intent" + " rule=" + rule +
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index daeadc06fba9..8e0be1cb1d39 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -62,7 +62,8 @@ public:
mMinSdkVersion(NULL), mTargetSdkVersion(NULL), mMaxSdkVersion(NULL),
mVersionCode(NULL), mVersionName(NULL), mCustomPackage(NULL), mExtraPackages(NULL),
mMaxResVersion(NULL), mDebugMode(false), mNonConstantId(false), mProduct(NULL),
- mUseCrunchCache(false), mArgc(0), mArgv(NULL)
+ mUseCrunchCache(false), mErrorOnFailedInsert(false), mOutputTextSymbols(NULL),
+ mArgc(0), mArgv(NULL)
{}
~Bundle(void) {}
@@ -113,6 +114,8 @@ public:
void setAutoAddOverlay(bool val) { mAutoAddOverlay = val; }
bool getGenDependencies() { return mGenDependencies; }
void setGenDependencies(bool val) { mGenDependencies = val; }
+ bool getErrorOnFailedInsert() { return mErrorOnFailedInsert; }
+ void setErrorOnFailedInsert(bool val) { mErrorOnFailedInsert = val; }
bool getUTF16StringsOption() {
return mWantUTF16 || !isMinSdkAtLeast(SDK_FROYO);
@@ -174,6 +177,8 @@ public:
void setProduct(const char * val) { mProduct = val; }
void setUseCrunchCache(bool val) { mUseCrunchCache = val; }
bool getUseCrunchCache() const { return mUseCrunchCache; }
+ const char* getOutputTextSymbols() const { return mOutputTextSymbols; }
+ void setOutputTextSymbols(const char* val) { mOutputTextSymbols = val; }
/*
* Set and get the file specification.
@@ -280,6 +285,8 @@ private:
bool mNonConstantId;
const char* mProduct;
bool mUseCrunchCache;
+ bool mErrorOnFailedInsert;
+ const char* mOutputTextSymbols;
/* file specification */
int mArgc;
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 9f05c6a6a38e..d48394ad4c89 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -70,7 +70,8 @@ void usage(void)
" [--product product1,product2,...] \\\n"
" [-c CONFIGS] [--preferred-configurations CONFIGS] \\\n"
" [-o] \\\n"
- " [raw-files-dir [raw-files-dir] ...]\n"
+ " [raw-files-dir [raw-files-dir] ...] \\\n"
+ " [--output-text-symbols DIR]\n"
"\n"
" Package the android resources. It will read assets and resources that are\n"
" supplied with the -M -A -S or raw-files-dir arguments. The -J -P -F and -R\n"
@@ -179,6 +180,14 @@ void usage(void)
" Make the resources ID non constant. This is required to make an R java class\n"
" that does not contain the final value but is used to make reusable compiled\n"
" libraries that need to access resources.\n"
+ " --error-on-failed-insert\n"
+ " Forces aapt to return an error if it fails to insert values into the manifest\n"
+ " with --debug-mode, --min-sdk-version, --target-sdk-version --version-code\n"
+ " and --version-name.\n"
+ " Insertion typically fails if the manifest already defines the attribute.\n"
+ " --output-text-symbols\n"
+ " Generates a text file containing the resource symbols of the R class in the\n"
+ " specified folder.\n"
" --ignore-assets\n"
" Assets to be ignored. Default pattern is:\n"
" %s\n",
@@ -547,6 +556,17 @@ int main(int argc, char* const argv[])
bundle.setInstrumentationPackageNameOverride(argv[0]);
} else if (strcmp(cp, "-auto-add-overlay") == 0) {
bundle.setAutoAddOverlay(true);
+ } else if (strcmp(cp, "-error-on-failed-insert") == 0) {
+ bundle.setErrorOnFailedInsert(true);
+ } else if (strcmp(cp, "-output-text-symbols") == 0) {
+ argc--;
+ argv++;
+ if (!argc) {
+ fprintf(stderr, "ERROR: No argument supplied for '-output-text-symbols' option\n");
+ wantUsage = true;
+ goto bail;
+ }
+ bundle.setOutputTextSymbols(argv[0]);
} else if (strcmp(cp, "-product") == 0) {
argc--;
argv++;
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index ee076e602e5e..77168f948640 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -673,24 +673,40 @@ static bool applyFileOverlay(Bundle *bundle,
return true;
}
-void addTagAttribute(const sp<XMLNode>& node, const char* ns8,
- const char* attr8, const char* value)
+/*
+ * Inserts an attribute in a given node, only if the attribute does not
+ * exist.
+ * If errorOnFailedInsert is true, and the attribute already exists, returns false.
+ * Returns true otherwise, even if the attribute already exists.
+ */
+bool addTagAttribute(const sp<XMLNode>& node, const char* ns8,
+ const char* attr8, const char* value, bool errorOnFailedInsert)
{
if (value == NULL) {
- return;
+ return true;
}
-
+
const String16 ns(ns8);
const String16 attr(attr8);
-
+
if (node->getAttribute(ns, attr) != NULL) {
+ if (errorOnFailedInsert) {
+ fprintf(stderr, "Error: AndroidManifest.xml already defines %s (in %s);"
+ " cannot insert new value %s.\n",
+ String8(attr).string(), String8(ns).string(), value);
+ return false;
+ }
+
fprintf(stderr, "Warning: AndroidManifest.xml already defines %s (in %s);"
" using existing value in manifest.\n",
String8(attr).string(), String8(ns).string());
- return;
+
+ // don't stop the build.
+ return true;
}
node->addAttribute(ns, attr, String16(value));
+ return true;
}
static void fullyQualifyClassName(const String8& package, sp<XMLNode> node,
@@ -728,11 +744,17 @@ status_t massageManifest(Bundle* bundle, sp<XMLNode> root)
fprintf(stderr, "No <manifest> tag.\n");
return UNKNOWN_ERROR;
}
-
- addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionCode",
- bundle->getVersionCode());
- addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionName",
- bundle->getVersionName());
+
+ bool errorOnFailedInsert = bundle->getErrorOnFailedInsert();
+
+ if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionCode",
+ bundle->getVersionCode(), errorOnFailedInsert)) {
+ return UNKNOWN_ERROR;
+ }
+ if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionName",
+ bundle->getVersionName(), errorOnFailedInsert)) {
+ return UNKNOWN_ERROR;
+ }
if (bundle->getMinSdkVersion() != NULL
|| bundle->getTargetSdkVersion() != NULL
@@ -743,18 +765,27 @@ status_t massageManifest(Bundle* bundle, sp<XMLNode> root)
root->insertChildAt(vers, 0);
}
- addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "minSdkVersion",
- bundle->getMinSdkVersion());
- addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "targetSdkVersion",
- bundle->getTargetSdkVersion());
- addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "maxSdkVersion",
- bundle->getMaxSdkVersion());
+ if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "minSdkVersion",
+ bundle->getMinSdkVersion(), errorOnFailedInsert)) {
+ return UNKNOWN_ERROR;
+ }
+ if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "targetSdkVersion",
+ bundle->getTargetSdkVersion(), errorOnFailedInsert)) {
+ return UNKNOWN_ERROR;
+ }
+ if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "maxSdkVersion",
+ bundle->getMaxSdkVersion(), errorOnFailedInsert)) {
+ return UNKNOWN_ERROR;
+ }
}
if (bundle->getDebugMode()) {
sp<XMLNode> application = root->getChildElement(String16(), String16("application"));
if (application != NULL) {
- addTagAttribute(application, RESOURCES_ANDROID_NAMESPACE, "debuggable", "true");
+ if (!addTagAttribute(application, RESOURCES_ANDROID_NAMESPACE, "debuggable", "true",
+ errorOnFailedInsert)) {
+ return UNKNOWN_ERROR;
+ }
}
}
@@ -1821,6 +1852,110 @@ static status_t writeLayoutClasses(
return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
}
+static status_t writeTextLayoutClasses(
+ FILE* fp, const sp<AaptAssets>& assets,
+ const sp<AaptSymbols>& symbols, bool includePrivate)
+{
+ String16 attr16("attr");
+ String16 package16(assets->getPackage());
+
+ bool hasErrors = false;
+
+ size_t i;
+ size_t N = symbols->getNestedSymbols().size();
+ for (i=0; i<N; i++) {
+ sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i);
+ String16 nclassName16(symbols->getNestedSymbols().keyAt(i));
+ String8 realClassName(nclassName16);
+ if (fixupSymbol(&nclassName16) != NO_ERROR) {
+ hasErrors = true;
+ }
+ String8 nclassName(nclassName16);
+
+ SortedVector<uint32_t> idents;
+ Vector<uint32_t> origOrder;
+ Vector<bool> publicFlags;
+
+ size_t a;
+ size_t NA = nsymbols->getSymbols().size();
+ for (a=0; a<NA; a++) {
+ const AaptSymbolEntry& sym(nsymbols->getSymbols().valueAt(a));
+ int32_t code = sym.typeCode == AaptSymbolEntry::TYPE_INT32
+ ? sym.int32Val : 0;
+ bool isPublic = true;
+ if (code == 0) {
+ String16 name16(sym.name);
+ uint32_t typeSpecFlags;
+ code = assets->getIncludedResources().identifierForName(
+ name16.string(), name16.size(),
+ attr16.string(), attr16.size(),
+ package16.string(), package16.size(), &typeSpecFlags);
+ if (code == 0) {
+ fprintf(stderr, "ERROR: In <declare-styleable> %s, unable to find attribute %s\n",
+ nclassName.string(), sym.name.string());
+ hasErrors = true;
+ }
+ isPublic = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
+ }
+ idents.add(code);
+ origOrder.add(code);
+ publicFlags.add(isPublic);
+ }
+
+ NA = idents.size();
+
+ fprintf(fp, "int[] styleable %s {", nclassName.string());
+
+ for (a=0; a<NA; a++) {
+ if (a != 0) {
+ fprintf(fp, ",");
+ }
+ fprintf(fp, " 0x%08x", idents[a]);
+ }
+
+ fprintf(fp, " }\n");
+
+ for (a=0; a<NA; a++) {
+ ssize_t pos = idents.indexOf(origOrder.itemAt(a));
+ if (pos >= 0) {
+ const AaptSymbolEntry& sym = nsymbols->getSymbols().valueAt(a);
+ if (!publicFlags.itemAt(a) && !includePrivate) {
+ continue;
+ }
+ String8 name8(sym.name);
+ String16 comment(sym.comment);
+ String16 typeComment;
+ if (comment.size() <= 0) {
+ comment = getAttributeComment(assets, name8, &typeComment);
+ } else {
+ getAttributeComment(assets, name8, &typeComment);
+ }
+ String16 name(name8);
+ if (fixupSymbol(&name) != NO_ERROR) {
+ hasErrors = true;
+ }
+
+ uint32_t typeSpecFlags = 0;
+ String16 name16(sym.name);
+ assets->getIncludedResources().identifierForName(
+ name16.string(), name16.size(),
+ attr16.string(), attr16.size(),
+ package16.string(), package16.size(), &typeSpecFlags);
+ //printf("%s:%s/%s: 0x%08x\n", String8(package16).string(),
+ // String8(attr16).string(), String8(name16).string(), typeSpecFlags);
+ const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
+
+ fprintf(fp,
+ "int styleable %s_%s %d\n",
+ nclassName.string(),
+ String8(name).string(), (int)pos);
+ }
+ }
+ }
+
+ return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
+}
+
static status_t writeSymbolClass(
FILE* fp, const sp<AaptAssets>& assets, bool includePrivate,
const sp<AaptSymbols>& symbols, const String8& className, int indent,
@@ -1848,7 +1983,6 @@ static status_t writeSymbolClass(
continue;
}
String16 name(sym.name);
- String8 realName(name);
if (fixupSymbol(&name) != NO_ERROR) {
return UNKNOWN_ERROR;
}
@@ -1960,6 +2094,51 @@ static status_t writeSymbolClass(
return NO_ERROR;
}
+static status_t writeTextSymbolClass(
+ FILE* fp, const sp<AaptAssets>& assets, bool includePrivate,
+ const sp<AaptSymbols>& symbols, const String8& className)
+{
+ size_t i;
+ status_t err = NO_ERROR;
+
+ size_t N = symbols->getSymbols().size();
+ for (i=0; i<N; i++) {
+ const AaptSymbolEntry& sym = symbols->getSymbols().valueAt(i);
+ if (sym.typeCode != AaptSymbolEntry::TYPE_INT32) {
+ continue;
+ }
+
+ if (!assets->isJavaSymbol(sym, includePrivate)) {
+ continue;
+ }
+
+ String16 name(sym.name);
+ if (fixupSymbol(&name) != NO_ERROR) {
+ return UNKNOWN_ERROR;
+ }
+
+ fprintf(fp, "int %s %s 0x%08x\n",
+ className.string(),
+ String8(name).string(), (int)sym.int32Val);
+ }
+
+ N = symbols->getNestedSymbols().size();
+ for (i=0; i<N; i++) {
+ sp<AaptSymbols> nsymbols = symbols->getNestedSymbols().valueAt(i);
+ String8 nclassName(symbols->getNestedSymbols().keyAt(i));
+ if (nclassName == "styleable") {
+ err = writeTextLayoutClasses(fp, assets, nsymbols, includePrivate);
+ } else {
+ err = writeTextSymbolClass(fp, assets, includePrivate, nsymbols, nclassName);
+ }
+ if (err != NO_ERROR) {
+ return err;
+ }
+ }
+
+ return NO_ERROR;
+}
+
status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets,
const String8& package, bool includePrivate)
{
@@ -1967,11 +2146,15 @@ status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets,
return NO_ERROR;
}
+ const char* textSymbolsDest = bundle->getOutputTextSymbols();
+
+ String8 R("R");
const size_t N = assets->getSymbols().size();
for (size_t i=0; i<N; i++) {
sp<AaptSymbols> symbols = assets->getSymbols().valueAt(i);
String8 className(assets->getSymbols().keyAt(i));
String8 dest(bundle->getRClassDir());
+
if (bundle->getMakePackageDirs()) {
String8 pkg(package);
const char* last = pkg.string();
@@ -2003,14 +2186,14 @@ status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets,
}
fprintf(fp,
- "/* AUTO-GENERATED FILE. DO NOT MODIFY.\n"
- " *\n"
- " * This class was automatically generated by the\n"
- " * aapt tool from the resource data it found. It\n"
- " * should not be modified by hand.\n"
- " */\n"
- "\n"
- "package %s;\n\n", package.string());
+ "/* AUTO-GENERATED FILE. DO NOT MODIFY.\n"
+ " *\n"
+ " * This class was automatically generated by the\n"
+ " * aapt tool from the resource data it found. It\n"
+ " * should not be modified by hand.\n"
+ " */\n"
+ "\n"
+ "package %s;\n\n", package.string());
status_t err = writeSymbolClass(fp, assets, includePrivate, symbols,
className, 0, bundle->getNonConstantId());
@@ -2019,14 +2202,37 @@ status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets,
}
fclose(fp);
+ if (textSymbolsDest != NULL && R == className) {
+ String8 textDest(textSymbolsDest);
+ textDest.appendPath(className);
+ textDest.append(".txt");
+
+ FILE* fp = fopen(textDest.string(), "w+");
+ if (fp == NULL) {
+ fprintf(stderr, "ERROR: Unable to open text symbol file %s: %s\n",
+ textDest.string(), strerror(errno));
+ return UNKNOWN_ERROR;
+ }
+ if (bundle->getVerbose()) {
+ printf(" Writing text symbols for class %s.\n", className.string());
+ }
+
+ status_t err = writeTextSymbolClass(fp, assets, includePrivate, symbols,
+ className);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ fclose(fp);
+ }
+
// If we were asked to generate a dependency file, we'll go ahead and add this R.java
// as a target in the dependency file right next to it.
- if (bundle->getGenDependencies()) {
+ if (bundle->getGenDependencies() && R == className) {
// Add this R.java to the dependency file
String8 dependencyFile(bundle->getRClassDir());
dependencyFile.appendPath("R.java.d");
- fp = fopen(dependencyFile.string(), "a");
+ FILE *fp = fopen(dependencyFile.string(), "a");
fprintf(fp,"%s \\\n", dest.string());
fclose(fp);
}
@@ -2036,7 +2242,6 @@ status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets,
}
-
class ProguardKeepSet
{
public:
diff --git a/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_back.png b/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_back.png
new file mode 100644
index 000000000000..84e6bc89c082
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_back.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_back_default.png b/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_back_default.png
deleted file mode 100644
index ac5a97b24a93..000000000000
--- a/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_back_default.png
+++ /dev/null
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_home.png b/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_home.png
new file mode 100644
index 000000000000..38e4f45719e7
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_home.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_home_default.png b/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_home_default.png
deleted file mode 100644
index a90dc9b131bb..000000000000
--- a/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_home_default.png
+++ /dev/null
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_recent.png b/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_recent.png
new file mode 100644
index 000000000000..bf9f3009c32c
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_recent_default.png b/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_recent_default.png
deleted file mode 100644
index cb3c4331089f..000000000000
--- a/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_recent_default.png
+++ /dev/null
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_back.png b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_back.png
new file mode 100644
index 000000000000..a00bc5b5f33b
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_back.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_back_default.png b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_back_default.png
deleted file mode 100644
index 5ab09f0adaad..000000000000
--- a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_back_default.png
+++ /dev/null
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_home.png b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_home.png
new file mode 100644
index 000000000000..dc3183bf640f
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_home.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_home_default.png b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_home_default.png
deleted file mode 100644
index 62ca4276f326..000000000000
--- a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_home_default.png
+++ /dev/null
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_recent.png b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_recent.png
new file mode 100644
index 000000000000..b07f611ab98c
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_recent_default.png b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_recent_default.png
deleted file mode 100644
index ff698fb6efa6..000000000000
--- a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_recent_default.png
+++ /dev/null
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/navigation_bar.xml b/tools/layoutlib/bridge/resources/bars/navigation_bar.xml
new file mode 100644
index 000000000000..599ca08bf02c
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/navigation_bar.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+ <ImageView
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"/>
+ <ImageView
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"/>
+ <ImageView
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+</merge>
diff --git a/tools/layoutlib/bridge/resources/bars/phone_system_bar.xml b/tools/layoutlib/bridge/resources/bars/status_bar.xml
index d3c492eacf43..d3c492eacf43 100644
--- a/tools/layoutlib/bridge/resources/bars/phone_system_bar.xml
+++ b/tools/layoutlib/bridge/resources/bars/status_bar.xml
diff --git a/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_back.png b/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_back.png
new file mode 100644
index 000000000000..bd60cd65878a
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_back_default.png b/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_back_default.png
deleted file mode 100644
index 4cb305d35930..000000000000
--- a/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_back_default.png
+++ /dev/null
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_home.png b/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_home.png
new file mode 100644
index 000000000000..c5bc5c96ef05
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_home.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_home_default.png b/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_home_default.png
deleted file mode 100644
index 31d35c8f5570..000000000000
--- a/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_home_default.png
+++ /dev/null
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_recent.png b/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_recent.png
new file mode 100644
index 000000000000..f621d9cc7242
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_recent_default.png b/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_recent_default.png
deleted file mode 100644
index f0cc341ddb98..000000000000
--- a/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_recent_default.png
+++ /dev/null
Binary files differ
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 daf520b5415a..bf8658e402b0 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -210,7 +210,8 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
Capability.PLAY_ANIMATION,
Capability.ANIMATED_VIEW_MANIPULATION,
Capability.ADAPTER_BINDING,
- Capability.EXTENDED_VIEWINFO);
+ Capability.EXTENDED_VIEWINFO,
+ Capability.FIXED_SCALABLE_NINE_PATCH);
BridgeAssetManager.initSystem();
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
index 62c886b90306..ec9ec75283eb 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
@@ -56,15 +56,19 @@ import java.io.InputStream;
*
* It also provides a few utility methods to configure the content of the layout.
*/
-abstract class CustomBar extends LinearLayout {
+public abstract class CustomBar extends LinearLayout {
protected abstract TextView getStyleableTextView();
- protected CustomBar(Context context, Density density, String layoutPath, String name)
- throws XmlPullParserException {
+ protected CustomBar(Context context, Density density, int orientation, String layoutPath,
+ String name) throws XmlPullParserException {
super(context);
- setOrientation(LinearLayout.HORIZONTAL);
- setGravity(Gravity.CENTER_VERTICAL);
+ setOrientation(orientation);
+ if (orientation == LinearLayout.HORIZONTAL) {
+ setGravity(Gravity.CENTER_VERTICAL);
+ } else {
+ setGravity(Gravity.CENTER_HORIZONTAL);
+ }
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FakeActionBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FakeActionBar.java
index 68f5aba1fc12..226649dbc406 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FakeActionBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FakeActionBar.java
@@ -21,6 +21,7 @@ import com.android.resources.Density;
import org.xmlpull.v1.XmlPullParserException;
import android.content.Context;
+import android.widget.LinearLayout;
import android.widget.TextView;
public class FakeActionBar extends CustomBar {
@@ -29,7 +30,7 @@ public class FakeActionBar extends CustomBar {
public FakeActionBar(Context context, Density density, String label, String icon)
throws XmlPullParserException {
- super(context, density, "/bars/action_bar.xml", "action_bar.xml");
+ super(context, density, LinearLayout.HORIZONTAL, "/bars/action_bar.xml", "action_bar.xml");
// Cannot access the inside items through id because no R.id values have been
// created for them.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
new file mode 100644
index 000000000000..cc90d6b33abe
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
@@ -0,0 +1,54 @@
+/*
+ * 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.layoutlib.bridge.bars;
+
+import com.android.resources.Density;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.Context;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class NavigationBar extends CustomBar {
+
+ public NavigationBar(Context context, Density density, int orientation) throws XmlPullParserException {
+ super(context, density, orientation, "/bars/navigation_bar.xml", "navigation_bar.xml");
+
+ setBackgroundColor(0xFF000000);
+
+ // Cannot access the inside items through id because no R.id values have been
+ // created for them.
+ // We do know the order though.
+ // 0 is a spacer.
+ int back = 1;
+ int recent = 3;
+ if (orientation == LinearLayout.VERTICAL) {
+ back = 3;
+ recent = 1;
+ }
+
+ loadIcon(back, "ic_sysbar_back.png", density);
+ loadIcon(2, "ic_sysbar_home.png", density);
+ loadIcon(recent, "ic_sysbar_recent.png", density);
+ }
+
+ @Override
+ protected TextView getStyleableTextView() {
+ return null;
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/PhoneSystemBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
index 7521011c76e8..5c084121d123 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/PhoneSystemBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
@@ -25,12 +25,13 @@ import android.content.Context;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LevelListDrawable;
import android.view.Gravity;
+import android.widget.LinearLayout;
import android.widget.TextView;
-public class PhoneSystemBar extends CustomBar {
+public class StatusBar extends CustomBar {
- public PhoneSystemBar(Context context, Density density) throws XmlPullParserException {
- super(context, density, "/bars/phone_system_bar.xml", "phone_system_bar.xml");
+ public StatusBar(Context context, Density density) throws XmlPullParserException {
+ super(context, density, LinearLayout.HORIZONTAL, "/bars/status_bar.xml", "status_bar.xml");
// FIXME: use FILL_H?
setGravity(Gravity.START | Gravity.TOP | Gravity.RIGHT);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TabletSystemBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TabletSystemBar.java
index 456ddb4983ce..5ddb34eece8c 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TabletSystemBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TabletSystemBar.java
@@ -24,21 +24,23 @@ import org.xmlpull.v1.XmlPullParserException;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LevelListDrawable;
+import android.widget.LinearLayout;
import android.widget.TextView;
public class TabletSystemBar extends CustomBar {
public TabletSystemBar(Context context, Density density) throws XmlPullParserException {
- super(context, density, "/bars/tablet_system_bar.xml", "tablet_system_bar.xml");
+ super(context, density, LinearLayout.HORIZONTAL,
+ "/bars/tablet_system_bar.xml", "tablet_system_bar.xml");
setBackgroundColor(0xFF000000);
// Cannot access the inside items through id because no R.id values have been
// created for them.
// We do know the order though.
- loadIcon(0, "ic_sysbar_back_default.png", density);
- loadIcon(1, "ic_sysbar_home_default.png", density);
- loadIcon(2, "ic_sysbar_recent_default.png", density);
+ loadIcon(0, "ic_sysbar_back.png", density);
+ loadIcon(1, "ic_sysbar_home.png", density);
+ loadIcon(2, "ic_sysbar_recent.png", density);
// 3 is the spacer
loadIcon(4, "stat_sys_wifi_signal_4_fully.png", density);
Drawable drawable = loadIcon(5, ResourceType.DRAWABLE, "stat_sys_battery_charge");
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java
index 5f5ebc48d9f4..c27859f901c1 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java
@@ -21,6 +21,7 @@ import com.android.resources.Density;
import org.xmlpull.v1.XmlPullParserException;
import android.content.Context;
+import android.widget.LinearLayout;
import android.widget.TextView;
public class TitleBar extends CustomBar {
@@ -29,7 +30,7 @@ public class TitleBar extends CustomBar {
public TitleBar(Context context, Density density, String label)
throws XmlPullParserException {
- super(context, density, "/bars/title_bar.xml", "title_bar.xml");
+ super(context, density, LinearLayout.HORIZONTAL, "/bars/title_bar.xml", "title_bar.xml");
// Cannot access the inside items through id because no R.id values have been
// created for them.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java
index a235ec3b9850..803849fd696a 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java
@@ -21,9 +21,12 @@ import org.kxml2.io.KXmlParser;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
+import java.io.IOException;
import java.io.InputStream;
/**
@@ -38,14 +41,21 @@ public class ParserFactory {
public static XmlPullParser create(File f)
throws XmlPullParserException, FileNotFoundException {
- KXmlParser parser = instantiateParser(f.getName());
- parser.setInput(new FileInputStream(f), ENCODING);
- return parser;
+ InputStream stream = new FileInputStream(f);
+ return create(stream, f.getName(), f.length());
}
public static XmlPullParser create(InputStream stream, String name)
+ throws XmlPullParserException {
+ return create(stream, name, -1);
+ }
+
+ private static XmlPullParser create(InputStream stream, String name, long size)
throws XmlPullParserException {
KXmlParser parser = instantiateParser(name);
+
+ stream = readAndClose(stream, name, size);
+
parser.setInput(stream, ENCODING);
return parser;
}
@@ -61,6 +71,61 @@ public class ParserFactory {
return parser;
}
+ private static InputStream readAndClose(InputStream stream, String name, long size)
+ throws XmlPullParserException {
+ // just a sanity check. It's doubtful we'll have such big files!
+ if (size > Integer.MAX_VALUE) {
+ throw new XmlPullParserException("File " + name + " is too big to be parsed");
+ }
+ int intSize = (int) size;
+
+ // create a buffered reader to facilitate reading.
+ BufferedInputStream bufferedStream = new BufferedInputStream(stream);
+ try {
+ int avail;
+ if (intSize != -1) {
+ avail = intSize;
+ } else {
+ // get the size to read.
+ avail = bufferedStream.available();
+ }
+
+ // create the initial buffer and read it.
+ byte[] buffer = new byte[avail];
+ int read = stream.read(buffer);
+
+ // this is the easy case.
+ if (read == intSize) {
+ return new ByteArrayInputStream(buffer);
+ }
+
+ // check if there is more to read (read() does not necessarily read all that
+ // available() returned!)
+ while ((avail = bufferedStream.available()) > 0) {
+ if (read + avail > buffer.length) {
+ // just allocate what is needed. We're mostly reading small files
+ // so it shouldn't be too problematic.
+ byte[] moreBuffer = new byte[read + avail];
+ System.arraycopy(buffer, 0, moreBuffer, 0, read);
+ buffer = moreBuffer;
+ }
+
+ read += stream.read(buffer, read, avail);
+ }
+
+ // return a new stream encapsulating this buffer.
+ return new ByteArrayInputStream(buffer);
+
+ } catch (IOException e) {
+ throw new XmlPullParserException("Failed to read " + name, null, e);
+ } finally {
+ try {
+ bufferedStream.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+
private static class CustomParser extends KXmlParser {
private final String mName;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
index 7323c6af8839..1db7994a5772 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
@@ -20,11 +20,12 @@ import static com.android.ide.common.rendering.api.Result.Status.ERROR_LOCK_INTE
import static com.android.ide.common.rendering.api.Result.Status.ERROR_TIMEOUT;
import static com.android.ide.common.rendering.api.Result.Status.SUCCESS;
+import com.android.ide.common.rendering.api.HardwareConfig;
import com.android.ide.common.rendering.api.LayoutLog;
import com.android.ide.common.rendering.api.RenderParams;
import com.android.ide.common.rendering.api.RenderResources;
-import com.android.ide.common.rendering.api.Result;
import com.android.ide.common.rendering.api.RenderResources.FrameworkResourceIdProvider;
+import com.android.ide.common.rendering.api.Result;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.resources.Density;
@@ -98,19 +99,21 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso
return result;
}
+ HardwareConfig hardwareConfig = mParams.getHardwareConfig();
+
// setup the display Metrics.
DisplayMetrics metrics = new DisplayMetrics();
- metrics.densityDpi = mParams.getDensity().getDpiValue();
+ metrics.densityDpi = hardwareConfig.getDensity().getDpiValue();
metrics.density = metrics.noncompatDensity =
metrics.densityDpi / (float) DisplayMetrics.DENSITY_DEFAULT;
metrics.scaledDensity = metrics.noncompatScaledDensity = metrics.density;
- metrics.widthPixels = metrics.noncompatWidthPixels = mParams.getScreenWidth();
- metrics.heightPixels = metrics.noncompatHeightPixels = mParams.getScreenHeight();
- metrics.xdpi = metrics.noncompatXdpi = mParams.getXdpi();
- metrics.ydpi = metrics.noncompatYdpi = mParams.getYdpi();
+ metrics.widthPixels = metrics.noncompatWidthPixels = hardwareConfig.getScreenWidth();
+ metrics.heightPixels = metrics.noncompatHeightPixels = hardwareConfig.getScreenHeight();
+ metrics.xdpi = metrics.noncompatXdpi = hardwareConfig.getXdpi();
+ metrics.ydpi = metrics.noncompatYdpi = hardwareConfig.getYdpi();
RenderResources resources = mParams.getResources();
@@ -305,7 +308,9 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso
private Configuration getConfiguration() {
Configuration config = new Configuration();
- ScreenSize screenSize = mParams.getConfigScreenSize();
+ HardwareConfig hardwareConfig = mParams.getHardwareConfig();
+
+ ScreenSize screenSize = hardwareConfig.getScreenSize();
if (screenSize != null) {
switch (screenSize) {
case SMALL:
@@ -323,13 +328,13 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso
}
}
- Density density = mParams.getDensity();
+ Density density = hardwareConfig.getDensity();
if (density == null) {
density = Density.MEDIUM;
}
- config.screenWidthDp = mParams.getScreenWidth() / density.getDpiValue();
- config.screenHeightDp = mParams.getScreenHeight() / density.getDpiValue();
+ config.screenWidthDp = hardwareConfig.getScreenWidth() / density.getDpiValue();
+ config.screenHeightDp = hardwareConfig.getScreenHeight() / density.getDpiValue();
if (config.screenHeightDp < config.screenWidthDp) {
config.smallestScreenWidthDp = config.screenHeightDp;
} else {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java
index 813321080b7b..b6771315b09b 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java
@@ -19,6 +19,7 @@ package com.android.layoutlib.bridge.impl;
import static com.android.ide.common.rendering.api.Result.Status.ERROR_UNKNOWN;
import com.android.ide.common.rendering.api.DrawableParams;
+import com.android.ide.common.rendering.api.HardwareConfig;
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.common.rendering.api.Result;
import com.android.ide.common.rendering.api.Result.Status;
@@ -59,6 +60,7 @@ public class RenderDrawable extends RenderAction<DrawableParams> {
try {
// get the drawable resource value
DrawableParams params = getParams();
+ HardwareConfig hardwareConfig = params.getHardwareConfig();
ResourceValue drawableResource = params.getDrawable();
// resolve it
@@ -75,15 +77,15 @@ public class RenderDrawable extends RenderAction<DrawableParams> {
// get the actual Drawable object to draw
Drawable d = ResourceHelper.getDrawable(drawableResource, context);
- content.setBackgroundDrawable(d);
+ content.setBackground(d);
// set the AttachInfo on the root view.
AttachInfo_Accessor.setAttachInfo(content);
// measure
- int w = params.getScreenWidth();
- int h = params.getScreenHeight();
+ int w = hardwareConfig.getScreenWidth();
+ int h = hardwareConfig.getScreenHeight();
int w_spec = MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY);
int h_spec = MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY);
content.measure(w_spec, h_spec);
@@ -99,11 +101,11 @@ public class RenderDrawable extends RenderAction<DrawableParams> {
// create an Android bitmap around the BufferedImage
Bitmap bitmap = Bitmap_Delegate.createBitmap(image,
- true /*isMutable*/, params.getDensity());
+ true /*isMutable*/, hardwareConfig.getDensity());
// create a Canvas around the Android bitmap
Canvas canvas = new Canvas(bitmap);
- canvas.setDensity(params.getDensity().getDpiValue());
+ canvas.setDensity(hardwareConfig.getDensity().getDpiValue());
// and draw
content.draw(canvas);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index e93b41d27a9b..7169db3ed98d 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -24,6 +24,7 @@ import static com.android.ide.common.rendering.api.Result.Status.ERROR_VIEWGROUP
import static com.android.ide.common.rendering.api.Result.Status.SUCCESS;
import com.android.ide.common.rendering.api.AdapterBinding;
+import com.android.ide.common.rendering.api.HardwareConfig;
import com.android.ide.common.rendering.api.IAnimationListener;
import com.android.ide.common.rendering.api.ILayoutPullParser;
import com.android.ide.common.rendering.api.IProjectCallback;
@@ -42,13 +43,16 @@ import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.android.BridgeLayoutParamsMapAttributes;
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
+import com.android.layoutlib.bridge.bars.CustomBar;
import com.android.layoutlib.bridge.bars.FakeActionBar;
-import com.android.layoutlib.bridge.bars.PhoneSystemBar;
+import com.android.layoutlib.bridge.bars.NavigationBar;
+import com.android.layoutlib.bridge.bars.StatusBar;
import com.android.layoutlib.bridge.bars.TabletSystemBar;
import com.android.layoutlib.bridge.bars.TitleBar;
import com.android.layoutlib.bridge.impl.binding.FakeAdapter;
import com.android.layoutlib.bridge.impl.binding.FakeExpandableAdapter;
import com.android.resources.ResourceType;
+import com.android.resources.ScreenOrientation;
import com.android.resources.ScreenSize;
import com.android.util.Pair;
@@ -120,7 +124,8 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
private boolean mWindowIsFloating;
private int mStatusBarSize;
- private int mSystemBarSize;
+ private int mNavigationBarSize;
+ private int mNavigationBarOrientation = LinearLayout.HORIZONTAL;
private int mTitleBarSize;
private int mActionBarSize;
@@ -183,7 +188,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
findBackground(resources);
findStatusBar(resources, metrics);
findActionBar(resources, metrics);
- findSystemBar(resources, metrics);
+ findNavigationBar(resources, metrics);
// build the inflater and parser.
mInflater = new BridgeInflater(context, params.getProjectCallback());
@@ -209,19 +214,57 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
try {
SessionParams params = getParams();
+ HardwareConfig hardwareConfig = params.getHardwareConfig();
BridgeContext context = getContext();
+
// the view group that receives the window background.
ViewGroup backgroundView = null;
if (mWindowIsFloating || params.isForceNoDecor()) {
backgroundView = mViewRoot = mContentRoot = new FrameLayout(context);
} else {
+ if (hasSoftwareButtons() && mNavigationBarOrientation == LinearLayout.VERTICAL) {
+ /*
+ * This is a special case where the navigation bar is on the right.
+ +-------------------------------------------------+---+
+ | Status bar (always) | |
+ +-------------------------------------------------+ |
+ | (Layout with background drawable) | |
+ | +---------------------------------------------+ | |
+ | | Title/Action bar (optional) | | |
+ | +---------------------------------------------+ | |
+ | | Content, vertical extending | | |
+ | | | | |
+ | +---------------------------------------------+ | |
+ +-------------------------------------------------+---+
+
+ So we create a horizontal layout, with the nav bar on the right,
+ and the left part is the normal layout below without the nav bar at
+ the bottom
+ */
+ LinearLayout topLayout = new LinearLayout(context);
+ mViewRoot = topLayout;
+ topLayout.setOrientation(LinearLayout.HORIZONTAL);
+
+ try {
+ NavigationBar navigationBar = new NavigationBar(context,
+ hardwareConfig.getDensity(), LinearLayout.VERTICAL);
+ navigationBar.setLayoutParams(
+ new LinearLayout.LayoutParams(
+ mNavigationBarSize,
+ LayoutParams.MATCH_PARENT));
+ topLayout.addView(navigationBar);
+ } catch (XmlPullParserException e) {
+
+ }
+ }
+
/*
* we're creating the following layout
*
+-------------------------------------------------+
- | System bar (only in phone UI) |
+ | Status bar (except for xlarge tablets) |
+-------------------------------------------------+
| (Layout with background drawable) |
| +---------------------------------------------+ |
@@ -231,20 +274,31 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
| | | |
| +---------------------------------------------+ |
+-------------------------------------------------+
- | System bar (only in tablet UI) |
+ | Navigation bar for soft buttons, maybe see above|
+-------------------------------------------------+
*/
LinearLayout topLayout = new LinearLayout(context);
- mViewRoot = topLayout;
topLayout.setOrientation(LinearLayout.VERTICAL);
+ // if we don't already have a view root this is it
+ if (mViewRoot == null) {
+ mViewRoot = topLayout;
+ } else {
+ LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
+ LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
+ layoutParams.weight = 1;
+ topLayout.setLayoutParams(layoutParams);
+
+ // this is the case of soft buttons + vertical bar.
+ // this top layout is the first layout in the horizontal layout. see above)
+ mViewRoot.addView(topLayout, 0);
+ }
if (mStatusBarSize > 0) {
// system bar
try {
- PhoneSystemBar systemBar = new PhoneSystemBar(context,
- params.getDensity());
+ StatusBar systemBar = new StatusBar(context, hardwareConfig.getDensity());
systemBar.setLayoutParams(
new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, mStatusBarSize));
@@ -268,7 +322,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
if (mActionBarSize > 0) {
try {
FakeActionBar actionBar = new FakeActionBar(context,
- params.getDensity(),
+ hardwareConfig.getDensity(),
params.getAppLabel(), params.getAppIcon());
actionBar.setLayoutParams(
new LinearLayout.LayoutParams(
@@ -280,7 +334,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
} else if (mTitleBarSize > 0) {
try {
TitleBar titleBar = new TitleBar(context,
- params.getDensity(), params.getAppLabel());
+ hardwareConfig.getDensity(), params.getAppLabel());
titleBar.setLayoutParams(
new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, mTitleBarSize));
@@ -298,15 +352,23 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
mContentRoot.setLayoutParams(layoutParams);
backgroundLayout.addView(mContentRoot);
- if (mSystemBarSize > 0) {
+ if (mNavigationBarOrientation == LinearLayout.HORIZONTAL &&
+ mNavigationBarSize > 0) {
// system bar
try {
- TabletSystemBar systemBar = new TabletSystemBar(context,
- params.getDensity());
- systemBar.setLayoutParams(
+ CustomBar navigationBar;
+ if (hardwareConfig.getScreenSize() == ScreenSize.XLARGE) {
+ navigationBar = new TabletSystemBar(context,
+ hardwareConfig.getDensity());
+ } else {
+ navigationBar = new NavigationBar(context,
+ hardwareConfig.getDensity(), LinearLayout.HORIZONTAL);
+ }
+
+ navigationBar.setLayoutParams(
new LinearLayout.LayoutParams(
- LayoutParams.MATCH_PARENT, mSystemBarSize));
- topLayout.addView(systemBar);
+ LayoutParams.MATCH_PARENT, mNavigationBarSize));
+ topLayout.addView(navigationBar);
} catch (XmlPullParserException e) {
}
@@ -334,7 +396,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
// get the background drawable
if (mWindowBackground != null && backgroundView != null) {
Drawable d = ResourceHelper.getDrawable(mWindowBackground, context);
- backgroundView.setBackgroundDrawable(d);
+ backgroundView.setBackground(d);
}
return SUCCESS.createResult();
@@ -377,13 +439,14 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
}
RenderingMode renderingMode = params.getRenderingMode();
+ HardwareConfig hardwareConfig = params.getHardwareConfig();
// only do the screen measure when needed.
boolean newRenderSize = false;
if (mMeasuredScreenWidth == -1) {
newRenderSize = true;
- mMeasuredScreenWidth = params.getScreenWidth();
- mMeasuredScreenHeight = params.getScreenHeight();
+ mMeasuredScreenWidth = hardwareConfig.getScreenWidth();
+ mMeasuredScreenHeight = hardwareConfig.getScreenHeight();
if (renderingMode != RenderingMode.NORMAL) {
int widthMeasureSpecMode = renderingMode.isHorizExpand() ?
@@ -483,11 +546,11 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
// create an Android bitmap around the BufferedImage
Bitmap bitmap = Bitmap_Delegate.createBitmap(mImage,
- true /*isMutable*/, params.getDensity());
+ true /*isMutable*/, hardwareConfig.getDensity());
// create a Canvas around the Android bitmap
mCanvas = new Canvas(bitmap);
- mCanvas.setDensity(params.getDensity().getDpiValue());
+ mCanvas.setDensity(hardwareConfig.getDensity().getDpiValue());
}
if (freshRender && newImage == false) {
@@ -960,12 +1023,14 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
}
}
- private boolean isTabletUi() {
- return getParams().getConfigScreenSize() == ScreenSize.XLARGE;
+ private boolean hasSoftwareButtons() {
+ return getParams().getHardwareConfig().hasSoftwareButtons();
}
private void findStatusBar(RenderResources resources, DisplayMetrics metrics) {
- if (isTabletUi() == false) {
+ // status bar is on for all but x-large device
+ HardwareConfig hardwareConfig = getParams().getHardwareConfig();
+ if (hardwareConfig.getScreenSize() != ScreenSize.XLARGE) {
boolean windowFullscreen = getBooleanThemeValue(resources,
"windowFullscreen", false /*defaultValue*/);
@@ -1050,22 +1115,48 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
}
}
- private void findSystemBar(RenderResources resources, DisplayMetrics metrics) {
- if (isTabletUi() && mWindowIsFloating == false) {
+ private void findNavigationBar(RenderResources resources, DisplayMetrics metrics) {
+ if (hasSoftwareButtons() && mWindowIsFloating == false) {
// default value
- mSystemBarSize = 48; // ??
+ mNavigationBarSize = 48; // ??
+
+ HardwareConfig hardwareConfig = getParams().getHardwareConfig();
+
+ boolean barOnBottom = true;
+
+ if (hardwareConfig.getOrientation() == ScreenOrientation.LANDSCAPE) {
+ // compute the dp of the screen.
+ int shortSize = hardwareConfig.getScreenHeight();
+
+ // compute in dp
+ int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT / hardwareConfig.getDensity().getDpiValue();
+
+ if (shortSizeDp < 600) {
+ // 0-599dp: "phone" UI with bar on the side
+ barOnBottom = false;
+ } else {
+ // 600+dp: "tablet" UI with bar on the bottom
+ barOnBottom = true;
+ }
+ }
+
+ if (barOnBottom) {
+ mNavigationBarOrientation = LinearLayout.HORIZONTAL;
+ } else {
+ mNavigationBarOrientation = LinearLayout.VERTICAL;
+ }
// get the real value
ResourceValue value = resources.getFrameworkResource(ResourceType.DIMEN,
- "status_bar_height");
+ barOnBottom ? "navigation_bar_height" : "navigation_bar_width");
if (value != null) {
- TypedValue typedValue = ResourceHelper.getValue("status_bar_height",
+ TypedValue typedValue = ResourceHelper.getValue("navigation_bar_height",
value.getValue(), true /*requireUnit*/);
if (typedValue != null) {
// compute the pixel value based on the display metrics
- mSystemBarSize = (int)typedValue.getDimension(metrics);
+ mNavigationBarSize = (int)typedValue.getDimension(metrics);
}
}
}