Merge "Fix up DisconnectCause description."
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index 31e6e25..0cf8df1 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -65,6 +65,12 @@
public abstract class CameraCaptureSession implements AutoCloseable {
/**
+ * Used to identify invalid session ID.
+ * @hide
+ */
+ public static final int SESSION_ID_NONE = -1;
+
+ /**
* Get the camera device that this session is created for.
*/
public abstract CameraDevice getDevice();
@@ -168,10 +174,11 @@
* @throws IllegalArgumentException if the request targets no Surfaces or Surfaces that are not
* configured as outputs for this session; or a reprocess
* capture request is submitted in a non-reprocessible capture
- * session; or the capture targets a Surface in the middle
- * of being {@link #prepare prepared}; or the handler is
- * null, the listener is not null, and the calling thread has
- * no looper.
+ * session; or the reprocess capture request was created with
+ * a {@link TotalCaptureResult} from a different session; or
+ * the capture targets a Surface in the middle of being
+ * {@link #prepare prepared}; or the handler is null, the
+ * listener is not null, and the calling thread has no looper.
*
* @see #captureBurst
* @see #setRepeatingRequest
@@ -226,7 +233,9 @@
* capture request is submitted in a non-reprocessible capture
* session; or the list of requests contains both requests to
* capture images from the camera and reprocess capture
- * requests; or one of the captures targets a Surface in the
+ * requests; or one of the reprocess capture requests was
+ * created with a {@link TotalCaptureResult} from a different
+ * session; or one of the captures targets a Surface in the
* middle of being {@link #prepare prepared}; or if the handler
* is null, the listener is not null, and the calling thread
* has no looper.
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 35727e8..19d17b1 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -158,6 +158,9 @@
private final HashSet<Surface> mSurfaceSet;
private final CameraMetadataNative mSettings;
private boolean mIsReprocess;
+ // Each reprocess request must be tied to a reprocessible session ID.
+ // Valid only for reprocess requests (mIsReprocess == true).
+ private int mReprocessibleSessionId;
private Object mUserTag;
@@ -170,6 +173,7 @@
mSettings = new CameraMetadataNative();
mSurfaceSet = new HashSet<Surface>();
mIsReprocess = false;
+ mReprocessibleSessionId = CameraCaptureSession.SESSION_ID_NONE;
}
/**
@@ -182,6 +186,7 @@
mSettings = new CameraMetadataNative(source.mSettings);
mSurfaceSet = (HashSet<Surface>) source.mSurfaceSet.clone();
mIsReprocess = source.mIsReprocess;
+ mReprocessibleSessionId = source.mReprocessibleSessionId;
mUserTag = source.mUserTag;
}
@@ -189,11 +194,36 @@
* Take ownership of passed-in settings.
*
* Used by the Builder to create a mutable CaptureRequest.
+ *
+ * @param settings Settings for this capture request.
+ * @param isReprocess Indicates whether to create a reprocess capture request. {@code true}
+ * to create a reprocess capture request. {@code false} to create a regular
+ * capture request.
+ * @param reprocessibleSessionId The ID of the camera capture session this capture is created
+ * for. This is used to validate if the application submits a
+ * reprocess capture request to the same session where
+ * the {@link TotalCaptureResult}, used to create the reprocess
+ * capture, came from.
+ *
+ * @throws IllegalArgumentException If creating a reprocess capture request with an invalid
+ * reprocessibleSessionId.
+ *
+ * @see CameraDevice#createReprocessCaptureRequest
*/
- private CaptureRequest(CameraMetadataNative settings, boolean isReprocess) {
+ private CaptureRequest(CameraMetadataNative settings, boolean isReprocess,
+ int reprocessibleSessionId) {
mSettings = CameraMetadataNative.move(settings);
mSurfaceSet = new HashSet<Surface>();
mIsReprocess = isReprocess;
+ if (isReprocess) {
+ if (reprocessibleSessionId == CameraCaptureSession.SESSION_ID_NONE) {
+ throw new IllegalArgumentException("Create a reprocess capture request with an " +
+ "invalid session ID: " + reprocessibleSessionId);
+ }
+ mReprocessibleSessionId = reprocessibleSessionId;
+ } else {
+ mReprocessibleSessionId = CameraCaptureSession.SESSION_ID_NONE;
+ }
}
/**
@@ -277,6 +307,23 @@
}
/**
+ * Get the reprocessible session ID this reprocess capture request is associated with.
+ *
+ * @return the reprocessible session ID this reprocess capture request is associated with
+ *
+ * @throws IllegalStateException if this capture request is not a reprocess capture request.
+ * @hide
+ */
+ public int getReprocessibleSessionId() {
+ if (mIsReprocess == false ||
+ mReprocessibleSessionId == CameraCaptureSession.SESSION_ID_NONE) {
+ throw new IllegalStateException("Getting the reprocessible session ID for a "+
+ "non-reprocess capture request is illegal.");
+ }
+ return mReprocessibleSessionId;
+ }
+
+ /**
* Determine whether this CaptureRequest is equal to another CaptureRequest.
*
* <p>A request is considered equal to another is if it's set of key/values is equal, it's
@@ -298,7 +345,8 @@
&& Objects.equals(mUserTag, other.mUserTag)
&& mSurfaceSet.equals(other.mSurfaceSet)
&& mSettings.equals(other.mSettings)
- && mIsReprocess == other.mIsReprocess;
+ && mIsReprocess == other.mIsReprocess
+ && mReprocessibleSessionId == other.mReprocessibleSessionId;
}
@Override
@@ -347,6 +395,7 @@
}
mIsReprocess = (in.readInt() == 0) ? false : true;
+ mReprocessibleSessionId = CameraCaptureSession.SESSION_ID_NONE;
}
@Override
@@ -397,10 +446,23 @@
* Initialize the builder using the template; the request takes
* ownership of the template.
*
+ * @param template Template settings for this capture request.
+ * @param reprocess Indicates whether to create a reprocess capture request. {@code true}
+ * to create a reprocess capture request. {@code false} to create a regular
+ * capture request.
+ * @param reprocessibleSessionId The ID of the camera capture session this capture is
+ * created for. This is used to validate if the application
+ * submits a reprocess capture request to the same session
+ * where the {@link TotalCaptureResult}, used to create the
+ * reprocess capture, came from.
+ *
+ * @throws IllegalArgumentException If creating a reprocess capture request with an invalid
+ * reprocessibleSessionId.
* @hide
*/
- public Builder(CameraMetadataNative template, boolean reprocess) {
- mRequest = new CaptureRequest(template, reprocess);
+ public Builder(CameraMetadataNative template, boolean reprocess,
+ int reprocessibleSessionId) {
+ mRequest = new CaptureRequest(template, reprocess, reprocessibleSessionId);
}
/**
diff --git a/core/java/android/hardware/camera2/TotalCaptureResult.java b/core/java/android/hardware/camera2/TotalCaptureResult.java
index 6f7dd78..fb3c098 100644
--- a/core/java/android/hardware/camera2/TotalCaptureResult.java
+++ b/core/java/android/hardware/camera2/TotalCaptureResult.java
@@ -50,6 +50,7 @@
public final class TotalCaptureResult extends CaptureResult {
private final List<CaptureResult> mPartialResults;
+ private final int mSessionId;
/**
* Takes ownership of the passed-in camera metadata and the partial results
@@ -58,7 +59,7 @@
* @hide
*/
public TotalCaptureResult(CameraMetadataNative results, CaptureRequest parent,
- CaptureResultExtras extras, List<CaptureResult> partials) {
+ CaptureResultExtras extras, List<CaptureResult> partials, int sessionId) {
super(results, parent, extras);
if (partials == null) {
@@ -66,6 +67,8 @@
} else {
mPartialResults = partials;
}
+
+ mSessionId = sessionId;
}
/**
@@ -78,6 +81,7 @@
super(results, sequenceId);
mPartialResults = new ArrayList<>();
+ mSessionId = CameraCaptureSession.SESSION_ID_NONE;
}
/**
@@ -95,4 +99,14 @@
public List<CaptureResult> getPartialResults() {
return Collections.unmodifiableList(mPartialResults);
}
+
+ /**
+ * Get the ID of the session where the capture request of this result was submitted.
+ *
+ * @return The session ID
+ * @hide
+ */
+ public int getSessionId() {
+ return mSessionId;
+ }
}
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index c74204d..3c19529 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -156,9 +156,10 @@
} else if (request.isReprocess() && !isReprocessible()) {
throw new IllegalArgumentException("this capture session cannot handle reprocess " +
"requests");
+ } else if (request.isReprocess() && request.getReprocessibleSessionId() != mId) {
+ throw new IllegalArgumentException("capture request was created for another session");
}
-
checkNotClosed();
handler = checkHandler(handler, callback);
@@ -185,12 +186,17 @@
if (reprocess && !isReprocessible()) {
throw new IllegalArgumentException("this capture session cannot handle reprocess " +
"requests");
+ } else if (reprocess && requests.get(0).getReprocessibleSessionId() != mId) {
+ throw new IllegalArgumentException("capture request was created for another session");
}
for (int i = 1; i < requests.size(); i++) {
if (requests.get(i).isReprocess() != reprocess) {
throw new IllegalArgumentException("cannot mix regular and reprocess capture " +
" requests");
+ } else if (reprocess && requests.get(i).getReprocessibleSessionId() != mId) {
+ throw new IllegalArgumentException("capture request was created for another " +
+ "session");
}
}
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 1e680dfd..ff4ad79 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -585,8 +585,8 @@
return null;
}
- CaptureRequest.Builder builder =
- new CaptureRequest.Builder(templatedRequest, /*reprocess*/false);
+ CaptureRequest.Builder builder = new CaptureRequest.Builder(
+ templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE);
return builder;
}
@@ -601,7 +601,8 @@
CameraMetadataNative resultMetadata = new
CameraMetadataNative(inputResult.getNativeCopy());
- return new CaptureRequest.Builder(resultMetadata, /*reprocess*/true);
+ return new CaptureRequest.Builder(resultMetadata, /*reprocess*/true,
+ inputResult.getSessionId());
}
}
@@ -763,7 +764,7 @@
if (callback != null) {
mCaptureCallbackMap.put(requestId, new CaptureCallbackHolder(callback,
- requestList, handler, repeating));
+ requestList, handler, repeating, mNextSessionId - 1));
} else {
if (DEBUG) {
Log.d(TAG, "Listen for request " + requestId + " is null");
@@ -1095,9 +1096,10 @@
private final CaptureCallback mCallback;
private final List<CaptureRequest> mRequestList;
private final Handler mHandler;
+ private final int mSessionId;
CaptureCallbackHolder(CaptureCallback callback, List<CaptureRequest> requestList,
- Handler handler, boolean repeating) {
+ Handler handler, boolean repeating, int sessionId) {
if (callback == null || handler == null) {
throw new UnsupportedOperationException(
"Must have a valid handler and a valid callback");
@@ -1106,6 +1108,7 @@
mHandler = handler;
mRequestList = new ArrayList<CaptureRequest>(requestList);
mCallback = callback;
+ mSessionId = sessionId;
}
public boolean isRepeating() {
@@ -1140,6 +1143,10 @@
return mHandler;
}
+ public int getSessionId() {
+ return mSessionId;
+ }
+
}
/**
@@ -1643,8 +1650,8 @@
List<CaptureResult> partialResults =
mFrameNumberTracker.popPartialResults(frameNumber);
- final TotalCaptureResult resultAsCapture =
- new TotalCaptureResult(result, request, resultExtras, partialResults);
+ final TotalCaptureResult resultAsCapture = new TotalCaptureResult(result,
+ request, resultExtras, partialResults, holder.getSessionId());
// Final capture result
resultDispatch = new Runnable() {
@@ -1665,7 +1672,8 @@
holder.getHandler().post(resultDispatch);
// Collect the partials for a total result; or mark the frame as totally completed
- mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult, isReprocess);
+ mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult,
+ isReprocess);
// Fire onCaptureSequenceCompleted
if (!isPartialResult) {
diff --git a/core/java/android/provider/Browser.java b/core/java/android/provider/Browser.java
index 69a05c4..bae06b8 100644
--- a/core/java/android/provider/Browser.java
+++ b/core/java/android/provider/Browser.java
@@ -16,6 +16,7 @@
package android.provider;
+import android.annotation.RequiresPermission;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
@@ -32,6 +33,9 @@
import android.util.Log;
import android.webkit.WebIconDatabase;
+import static android.Manifest.permission.READ_HISTORY_BOOKMARKS;
+import static android.Manifest.permission.WRITE_HISTORY_BOOKMARKS;
+
public class Browser {
private static final String LOGTAG = "browser";
@@ -41,6 +45,8 @@
* {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS} permission and writing to it
* requires the {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS} permission.
*/
+ @RequiresPermission.Read(@RequiresPermission(READ_HISTORY_BOOKMARKS))
+ @RequiresPermission.Write(@RequiresPermission(WRITE_HISTORY_BOOKMARKS))
public static final Uri BOOKMARKS_URI = Uri.parse("content://browser/bookmarks");
/**
@@ -122,6 +128,8 @@
* {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS} permission and writing to it
* requires the {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS} permission.
*/
+ @RequiresPermission.Read(@RequiresPermission(READ_HISTORY_BOOKMARKS))
+ @RequiresPermission.Write(@RequiresPermission(WRITE_HISTORY_BOOKMARKS))
public static final Uri SEARCHES_URI = Uri.parse("content://browser/searches");
/**
@@ -233,6 +241,7 @@
*
* @param cr The ContentResolver used to access the database.
*/
+ @RequiresPermission(READ_HISTORY_BOOKMARKS)
public static final Cursor getAllBookmarks(ContentResolver cr) throws
IllegalStateException {
return cr.query(Bookmarks.CONTENT_URI,
@@ -248,6 +257,7 @@
*
* @param cr The ContentResolver used to access the database.
*/
+ @RequiresPermission(READ_HISTORY_BOOKMARKS)
public static final Cursor getAllVisitedUrls(ContentResolver cr) throws
IllegalStateException {
return cr.query(Combined.CONTENT_URI,
@@ -308,6 +318,7 @@
* @param real If true, this is an actual visit, and should add to the
* number of visits. If false, the user entered it manually.
*/
+ @RequiresPermission(allOf = {READ_HISTORY_BOOKMARKS, WRITE_HISTORY_BOOKMARKS})
public static final void updateVisitedHistory(ContentResolver cr,
String url, boolean real) {
long now = System.currentTimeMillis();
@@ -358,6 +369,7 @@
* @param cr The ContentResolver used to access the database.
* @hide pending API council approval
*/
+ @RequiresPermission(READ_HISTORY_BOOKMARKS)
public static final String[] getVisitedHistory(ContentResolver cr) {
Cursor c = null;
String[] str = null;
@@ -393,6 +405,7 @@
*
* @param cr The ContentResolver used to access the database.
*/
+ @RequiresPermission(allOf = {READ_HISTORY_BOOKMARKS, WRITE_HISTORY_BOOKMARKS})
public static final void truncateHistory(ContentResolver cr) {
// TODO make a single request to the provider to do this in a single transaction
Cursor cursor = null;
@@ -424,6 +437,7 @@
* @param cr The ContentResolver used to access the database.
* @return boolean True if the history can be cleared.
*/
+ @RequiresPermission(READ_HISTORY_BOOKMARKS)
public static final boolean canClearHistory(ContentResolver cr) {
Cursor cursor = null;
boolean ret = false;
@@ -446,6 +460,7 @@
* Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS}
* @param cr The ContentResolver used to access the database.
*/
+ @RequiresPermission(WRITE_HISTORY_BOOKMARKS)
public static final void clearHistory(ContentResolver cr) {
deleteHistoryWhere(cr, null);
}
@@ -461,6 +476,7 @@
* @param whereClause String to limit the items affected.
* null means all items.
*/
+ @RequiresPermission(allOf = {READ_HISTORY_BOOKMARKS, WRITE_HISTORY_BOOKMARKS})
private static final void deleteHistoryWhere(ContentResolver cr, String whereClause) {
Cursor cursor = null;
try {
@@ -486,6 +502,7 @@
* @param end Last date to remove. If -1, all dates after begin.
* Non-inclusive.
*/
+ @RequiresPermission(WRITE_HISTORY_BOOKMARKS)
public static final void deleteHistoryTimeFrame(ContentResolver cr,
long begin, long end) {
String whereClause;
@@ -511,6 +528,7 @@
* @param cr The ContentResolver used to access the database.
* @param url url to remove.
*/
+ @RequiresPermission(WRITE_HISTORY_BOOKMARKS)
public static final void deleteFromHistory(ContentResolver cr,
String url) {
cr.delete(History.CONTENT_URI, History.URL + "=?", new String[] { url });
@@ -523,6 +541,7 @@
* @param cr The ContentResolver used to access the database.
* @param search The string to add to the searches database.
*/
+ @RequiresPermission(allOf = {READ_HISTORY_BOOKMARKS, WRITE_HISTORY_BOOKMARKS})
public static final void addSearchUrl(ContentResolver cr, String search) {
// The content provider will take care of updating existing searches instead of duplicating
ContentValues values = new ContentValues();
@@ -536,6 +555,7 @@
* Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS}
* @param cr The ContentResolver used to access the database.
*/
+ @RequiresPermission(WRITE_HISTORY_BOOKMARKS)
public static final void clearSearches(ContentResolver cr) {
// FIXME: Should this clear the urls to which these searches lead?
// (i.e. remove google.com/query= blah blah blah)
@@ -557,6 +577,7 @@
* @param listener IconListener that gets the icons once they are
* retrieved.
*/
+ @RequiresPermission(READ_HISTORY_BOOKMARKS)
public static final void requestAllIcons(ContentResolver cr, String where,
WebIconDatabase.IconListener listener) {
// Do nothing: this is no longer used.
diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java
index c6b4d7e..113e597 100644
--- a/core/java/android/widget/DayPickerView.java
+++ b/core/java/android/widget/DayPickerView.java
@@ -196,9 +196,23 @@
}
@Override
+ public void onRtlPropertiesChanged(@ResolvedLayoutDir int layoutDirection) {
+ super.onRtlPropertiesChanged(layoutDirection);
+
+ requestLayout();
+ }
+
+ @Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- final ImageButton leftButton = mPrevButton;
- final ImageButton rightButton = mNextButton;
+ final ImageButton leftButton;
+ final ImageButton rightButton;
+ if (isLayoutRtl()) {
+ leftButton = mNextButton;
+ rightButton = mPrevButton;
+ } else {
+ leftButton = mPrevButton;
+ rightButton = mNextButton;
+ }
final int width = right - left;
final int height = bottom - top;
diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java
index 0249c22..2778f0f 100644
--- a/core/java/android/widget/SimpleMonthView.java
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -162,7 +162,6 @@
mTitleFormatter = new SimpleDateFormat(titleFormat, locale);
mDayOfWeekFormatter = new SimpleDateFormat(DAY_OF_WEEK_FORMAT, locale);
- setClickable(true);
initPaints(res);
}
@@ -318,7 +317,8 @@
final int x = (int) (event.getX() + 0.5f);
final int y = (int) (event.getY() + 0.5f);
- switch (event.getAction()) {
+ final int action = event.getAction();
+ switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
final int touchedItem = getDayAtLocation(x, y);
@@ -326,6 +326,10 @@
mTouchedItem = touchedItem;
invalidate();
}
+ if (action == MotionEvent.ACTION_DOWN && touchedItem < 0) {
+ // Touch something that's not an item, reject event.
+ return false;
+ }
break;
case MotionEvent.ACTION_UP:
@@ -376,9 +380,16 @@
for (int col = 0; col < DAYS_IN_WEEK; col++) {
final int colCenter = colWidth * col + colWidth / 2;
+ final int colCenterRtl;
+ if (isLayoutRtl()) {
+ colCenterRtl = mPaddedWidth - colCenter;
+ } else {
+ colCenterRtl = colCenter;
+ }
+
final int dayOfWeek = (col + mWeekStart) % DAYS_IN_WEEK;
final String label = getDayOfWeekLabel(dayOfWeek);
- canvas.drawText(label, colCenter, rowCenter - halfLineHeight, p);
+ canvas.drawText(label, colCenterRtl, rowCenter - halfLineHeight, p);
}
}
@@ -402,6 +413,13 @@
for (int day = 1, col = findDayOffset(); day <= mDaysInMonth; day++) {
final int colCenter = colWidth * col + colWidth / 2;
+ final int colCenterRtl;
+ if (isLayoutRtl()) {
+ colCenterRtl = mPaddedWidth - colCenter;
+ } else {
+ colCenterRtl = colCenter;
+ }
+
int stateMask = 0;
if (day >= mEnabledDayStart && day <= mEnabledDayEnd) {
@@ -413,12 +431,12 @@
stateMask |= StateSet.VIEW_STATE_ACTIVATED;
// Adjust the circle to be centered on the row.
- canvas.drawCircle(colCenter, rowCenter, mDaySelectorRadius, mDaySelectorPaint);
+ canvas.drawCircle(colCenterRtl, rowCenter, mDaySelectorRadius, mDaySelectorPaint);
} else if (mTouchedItem == day) {
stateMask |= StateSet.VIEW_STATE_PRESSED;
// Adjust the circle to be centered on the row.
- canvas.drawCircle(colCenter, rowCenter, mDaySelectorRadius, mDayHighlightPaint);
+ canvas.drawCircle(colCenterRtl, rowCenter, mDaySelectorRadius, mDayHighlightPaint);
}
final boolean isDayToday = mToday == day;
@@ -431,7 +449,7 @@
}
p.setColor(dayTextColor);
- canvas.drawText(Integer.toString(day), colCenter, rowCenter - halfLineHeight, p);
+ canvas.drawText(Integer.toString(day), colCenterRtl, rowCenter - halfLineHeight, p);
col++;
@@ -583,6 +601,13 @@
}
@Override
+ public void onRtlPropertiesChanged(@ResolvedLayoutDir int layoutDirection) {
+ super.onRtlPropertiesChanged(layoutDirection);
+
+ requestLayout();
+ }
+
+ @Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (!changed) {
return;
@@ -657,8 +682,16 @@
return -1;
}
+ // Adjust for RTL after applying padding.
+ final int paddedXRtl;
+ if (isLayoutRtl()) {
+ paddedXRtl = mPaddedWidth - paddedX;
+ } else {
+ paddedXRtl = paddedX;
+ }
+
final int row = (paddedY - headerHeight) / mDayHeight;
- final int col = (paddedX * DAYS_IN_WEEK) / mPaddedWidth;
+ final int col = (paddedXRtl * DAYS_IN_WEEK) / mPaddedWidth;
final int index = col + row * DAYS_IN_WEEK;
final int day = index + 1 - findDayOffset();
if (day < 1 || day > mDaysInMonth) {
@@ -681,10 +714,15 @@
final int index = id - 1 + findDayOffset();
- // Compute left edge.
+ // Compute left edge, taking into account RTL.
final int col = index % DAYS_IN_WEEK;
final int colWidth = mCellWidth;
- final int left = getPaddingLeft() + col * colWidth;
+ final int left;
+ if (isLayoutRtl()) {
+ left = getWidth() - getPaddingRight() - (col + 1) * colWidth;
+ } else {
+ left = getPaddingLeft() + col * colWidth;
+ }
// Compute top edge.
final int row = index / DAYS_IN_WEEK;
diff --git a/core/java/com/android/internal/widget/SwipeDismissLayout.java b/core/java/com/android/internal/widget/SwipeDismissLayout.java
index 89990c2..191662c 100644
--- a/core/java/com/android/internal/widget/SwipeDismissLayout.java
+++ b/core/java/com/android/internal/widget/SwipeDismissLayout.java
@@ -111,7 +111,7 @@
}
private void init(Context context) {
- ViewConfiguration vc = ViewConfiguration.get(getContext());
+ ViewConfiguration vc = ViewConfiguration.get(context);
mSlop = vc.getScaledTouchSlop();
mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
@@ -290,7 +290,7 @@
float deltaX = ev.getRawX() - mDownX;
float deltaY = ev.getRawY() - mDownY;
if ((deltaX * deltaX) + (deltaY * deltaY) > mSlop * mSlop) {
- mSwiping = deltaX > mSlop * 2 && Math.abs(deltaY) < mSlop * 2;
+ mSwiping = deltaX > mSlop * 2 && Math.abs(deltaY) < Math.abs(deltaX);
} else {
mSwiping = false;
}
@@ -299,9 +299,9 @@
private void updateDismiss(MotionEvent ev) {
float deltaX = ev.getRawX() - mDownX;
+ mVelocityTracker.addMovement(ev);
+ mVelocityTracker.computeCurrentVelocity(1000);
if (!mDismissed) {
- mVelocityTracker.addMovement(ev);
- mVelocityTracker.computeCurrentVelocity(1000);
if (deltaX > (getWidth() * DISMISS_MIN_DRAG_WIDTH_RATIO) &&
ev.getRawX() >= mLastX) {
@@ -311,7 +311,9 @@
// Check if the user tried to undo this.
if (mDismissed && mSwiping) {
// Check if the user's finger is actually back
- if (deltaX < (getWidth() * DISMISS_MIN_DRAG_WIDTH_RATIO)) {
+ if (deltaX < (getWidth() * DISMISS_MIN_DRAG_WIDTH_RATIO) ||
+ // or user is flinging back left
+ mVelocityTracker.getXVelocity() < -mMinFlingVelocity) {
mDismissed = false;
}
}
diff --git a/core/java/com/android/internal/widget/ViewPager.java b/core/java/com/android/internal/widget/ViewPager.java
index 5c08daf..441e640 100644
--- a/core/java/com/android/internal/widget/ViewPager.java
+++ b/core/java/com/android/internal/widget/ViewPager.java
@@ -27,9 +27,9 @@
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.MathUtils;
import android.view.FocusFinder;
import android.view.Gravity;
import android.view.KeyEvent;
@@ -43,7 +43,6 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-import android.view.accessibility.AccessibilityRecord;
import android.view.animation.Interpolator;
import android.widget.EdgeEffect;
import android.widget.Scroller;
@@ -84,8 +83,9 @@
*/
public class ViewPager extends ViewGroup {
private static final String TAG = "ViewPager";
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = true;
+ private static final int MAX_SCROLL_X = 2 << 23;
private static final boolean USE_CACHE = false;
private static final int DEFAULT_OFFSCREEN_PAGES = 1;
@@ -108,9 +108,13 @@
static class ItemInfo {
Object object;
- int position;
boolean scrolling;
float widthFactor;
+
+ /** Logical position of the item within the pager adapter. */
+ int position;
+
+ /** Offset between the starting edges of the item and its container. */
float offset;
}
@@ -146,6 +150,12 @@
private int mTopPageBounds;
private int mBottomPageBounds;
+ /**
+ * The increment used to move in the "left" direction. Dependent on layout
+ * direction.
+ */
+ private int mLeftIncr = -1;
+
// Offsets of the first and last items, if known.
// Set during population, used to determine if we are at the beginning
// or end of the pager data set during touch scrolling.
@@ -198,14 +208,10 @@
// "catching" the flinging pager.
private static final int CLOSE_ENOUGH = 2; // dp
- private boolean mFakeDragging;
- private long mFakeDragBeginTime;
-
private final EdgeEffect mLeftEdge;
private final EdgeEffect mRightEdge;
private boolean mFirstLayout = true;
- private boolean mNeedCalculatePageOffsets = false;
private boolean mCalledSuper;
private int mDecorChildCount;
@@ -473,7 +479,7 @@
mAdapterChangeListener = listener;
}
- private int getClientWidth() {
+ private int getPaddedWidth() {
return getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
}
@@ -504,36 +510,33 @@
return mCurItem;
}
- void setCurrentItemInternal(int item, boolean smoothScroll, boolean always) {
- setCurrentItemInternal(item, smoothScroll, always, 0);
+ boolean setCurrentItemInternal(int item, boolean smoothScroll, boolean always) {
+ return setCurrentItemInternal(item, smoothScroll, always, 0);
}
- void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) {
+ boolean setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) {
if (mAdapter == null || mAdapter.getCount() <= 0) {
setScrollingCacheEnabled(false);
- return;
- }
- if (!always && mCurItem == item && mItems.size() != 0) {
- setScrollingCacheEnabled(false);
- return;
+ return false;
}
- if (item < 0) {
- item = 0;
- } else if (item >= mAdapter.getCount()) {
- item = mAdapter.getCount() - 1;
+ item = MathUtils.constrain(item, 0, mAdapter.getCount() - 1);
+ if (!always && mCurItem == item && mItems.size() != 0) {
+ setScrollingCacheEnabled(false);
+ return false;
}
+
final int pageLimit = mOffscreenPageLimit;
if (item > (mCurItem + pageLimit) || item < (mCurItem - pageLimit)) {
// We are doing a jump by more than one page. To avoid
// glitches, we want to keep all current pages in the view
// until the scroll ends.
- for (int i=0; i<mItems.size(); i++) {
+ for (int i = 0; i < mItems.size(); i++) {
mItems.get(i).scrolling = true;
}
}
- final boolean dispatchSelected = mCurItem != item;
+ final boolean dispatchSelected = mCurItem != item;
if (mFirstLayout) {
// We don't have any idea how big we are yet and shouldn't have any pages either.
// Just set things up and let the pending layout handle things.
@@ -549,38 +552,55 @@
populate(item);
scrollToItem(item, smoothScroll, velocity, dispatchSelected);
}
+
+ return true;
}
- private void scrollToItem(int item, boolean smoothScroll, int velocity,
+ private void scrollToItem(int position, boolean smoothScroll, int velocity,
boolean dispatchSelected) {
- final ItemInfo curInfo = infoForPosition(item);
- int destX = 0;
- if (curInfo != null) {
- final int width = getClientWidth();
- destX = (int) (width * Math.max(mFirstOffset,
- Math.min(curInfo.offset, mLastOffset)));
- }
+ final int destX = getLeftEdgeForItem(position);
+
if (smoothScroll) {
smoothScrollTo(destX, 0, velocity);
+
if (dispatchSelected && mOnPageChangeListener != null) {
- mOnPageChangeListener.onPageSelected(item);
+ mOnPageChangeListener.onPageSelected(position);
}
if (dispatchSelected && mInternalPageChangeListener != null) {
- mInternalPageChangeListener.onPageSelected(item);
+ mInternalPageChangeListener.onPageSelected(position);
}
} else {
if (dispatchSelected && mOnPageChangeListener != null) {
- mOnPageChangeListener.onPageSelected(item);
+ mOnPageChangeListener.onPageSelected(position);
}
if (dispatchSelected && mInternalPageChangeListener != null) {
- mInternalPageChangeListener.onPageSelected(item);
+ mInternalPageChangeListener.onPageSelected(position);
}
+
completeScroll(false);
scrollTo(destX, 0);
pageScrolled(destX);
}
}
+ private int getLeftEdgeForItem(int position) {
+ final ItemInfo info = infoForPosition(position);
+ if (info == null) {
+ return 0;
+ }
+
+ final int width = getPaddedWidth();
+ final int scaledOffset = (int) (width * MathUtils.constrain(
+ info.offset, mFirstOffset, mLastOffset));
+
+ if (isLayoutRtl()) {
+ final int itemWidth = (int) (width * info.widthFactor + 0.5f);
+ return MAX_SCROLL_X - itemWidth - scaledOffset;
+ } else {
+ return scaledOffset;
+ }
+ }
+
/**
* Set a listener that will be invoked whenever the page changes or is incrementally
* scrolled. See {@link OnPageChangeListener}.
@@ -784,7 +804,7 @@
setScrollingCacheEnabled(true);
setScrollState(SCROLL_STATE_SETTLING);
- final int width = getClientWidth();
+ final int width = getPaddedWidth();
final int halfWidth = width / 2;
final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dx) / width);
final float distance = halfWidth + halfWidth *
@@ -968,7 +988,7 @@
float extraWidthLeft = 0.f;
int itemIndex = curIndex - 1;
ItemInfo ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
- final int clientWidth = getClientWidth();
+ final int clientWidth = getPaddedWidth();
final float leftWidthNeeded = clientWidth <= 0 ? 0 :
2.f - curItem.widthFactor + (float) getPaddingLeft() / (float) clientWidth;
for (int pos = mCurItem - 1; pos >= 0; pos--) {
@@ -981,7 +1001,7 @@
mAdapter.destroyItem(this, pos, ii.object);
if (DEBUG) {
Log.i(TAG, "populate() - destroyItem() with pos: " + pos +
- " view: " + ((View) ii.object));
+ " view: " + ii.object);
}
itemIndex--;
curIndex--;
@@ -1015,7 +1035,7 @@
mAdapter.destroyItem(this, pos, ii.object);
if (DEBUG) {
Log.i(TAG, "populate() - destroyItem() with pos: " + pos +
- " view: " + ((View) ii.object));
+ " view: " + ii.object);
}
ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
}
@@ -1099,49 +1119,51 @@
private void calculatePageOffsets(ItemInfo curItem, int curIndex, ItemInfo oldCurInfo) {
final int N = mAdapter.getCount();
- final int width = getClientWidth();
+ final int width = getPaddedWidth();
final float marginOffset = width > 0 ? (float) mPageMargin / width : 0;
+
// Fix up offsets for later layout.
if (oldCurInfo != null) {
final int oldCurPosition = oldCurInfo.position;
+
// Base offsets off of oldCurInfo.
if (oldCurPosition < curItem.position) {
int itemIndex = 0;
- ItemInfo ii = null;
float offset = oldCurInfo.offset + oldCurInfo.widthFactor + marginOffset;
- for (int pos = oldCurPosition + 1;
- pos <= curItem.position && itemIndex < mItems.size(); pos++) {
- ii = mItems.get(itemIndex);
+ for (int pos = oldCurPosition + 1; pos <= curItem.position && itemIndex < mItems.size(); pos++) {
+ ItemInfo ii = mItems.get(itemIndex);
while (pos > ii.position && itemIndex < mItems.size() - 1) {
itemIndex++;
ii = mItems.get(itemIndex);
}
+
while (pos < ii.position) {
// We don't have an item populated for this,
// ask the adapter for an offset.
offset += mAdapter.getPageWidth(pos) + marginOffset;
pos++;
}
+
ii.offset = offset;
offset += ii.widthFactor + marginOffset;
}
} else if (oldCurPosition > curItem.position) {
int itemIndex = mItems.size() - 1;
- ItemInfo ii = null;
float offset = oldCurInfo.offset;
- for (int pos = oldCurPosition - 1;
- pos >= curItem.position && itemIndex >= 0; pos--) {
- ii = mItems.get(itemIndex);
+ for (int pos = oldCurPosition - 1; pos >= curItem.position && itemIndex >= 0; pos--) {
+ ItemInfo ii = mItems.get(itemIndex);
while (pos < ii.position && itemIndex > 0) {
itemIndex--;
ii = mItems.get(itemIndex);
}
+
while (pos > ii.position) {
// We don't have an item populated for this,
// ask the adapter for an offset.
offset -= mAdapter.getPageWidth(pos) + marginOffset;
pos--;
}
+
offset -= ii.widthFactor + marginOffset;
ii.offset = offset;
}
@@ -1155,6 +1177,7 @@
mFirstOffset = curItem.position == 0 ? curItem.offset : -Float.MAX_VALUE;
mLastOffset = curItem.position == N - 1 ?
curItem.offset + curItem.widthFactor - 1 : Float.MAX_VALUE;
+
// Previous pages
for (int i = curIndex - 1; i >= 0; i--, pos--) {
final ItemInfo ii = mItems.get(i);
@@ -1165,8 +1188,10 @@
ii.offset = offset;
if (ii.position == 0) mFirstOffset = offset;
}
+
offset = curItem.offset + curItem.widthFactor + marginOffset;
pos = curItem.position + 1;
+
// Next pages
for (int i = curIndex + 1; i < itemCount; i++, pos++) {
final ItemInfo ii = mItems.get(i);
@@ -1179,8 +1204,6 @@
ii.offset = offset;
offset += ii.widthFactor + marginOffset;
}
-
- mNeedCalculatePageOffsets = false;
}
/**
@@ -1546,34 +1569,47 @@
// Page views. Do this once we have the right padding offsets from above.
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
- if (child.getVisibility() != GONE) {
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- ItemInfo ii;
- if (!lp.isDecor && (ii = infoForChild(child)) != null) {
- int loff = (int) (childWidth * ii.offset);
- int childLeft = paddingLeft + loff;
- int childTop = paddingTop;
- if (lp.needsMeasure) {
- // This was added during layout and needs measurement.
- // Do it now that we know what we're working with.
- lp.needsMeasure = false;
- final int widthSpec = MeasureSpec.makeMeasureSpec(
- (int) (childWidth * lp.widthFactor),
- MeasureSpec.EXACTLY);
- final int heightSpec = MeasureSpec.makeMeasureSpec(
- (int) (height - paddingTop - paddingBottom),
- MeasureSpec.EXACTLY);
- child.measure(widthSpec, heightSpec);
- }
- if (DEBUG) Log.v(TAG, "Positioning #" + i + " " + child + " f=" + ii.object
- + ":" + childLeft + "," + childTop + " " + child.getMeasuredWidth()
- + "x" + child.getMeasuredHeight());
- child.layout(childLeft, childTop,
- childLeft + child.getMeasuredWidth(),
- childTop + child.getMeasuredHeight());
- }
+ if (child.getVisibility() == GONE) {
+ continue;
}
+
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ if (lp.isDecor) {
+ continue;
+ }
+
+ final ItemInfo ii = infoForChild(child);
+ if (ii == null) {
+ continue;
+ }
+
+ if (lp.needsMeasure) {
+ // This was added during layout and needs measurement.
+ // Do it now that we know what we're working with.
+ lp.needsMeasure = false;
+ final int widthSpec = MeasureSpec.makeMeasureSpec(
+ (int) (childWidth * lp.widthFactor),
+ MeasureSpec.EXACTLY);
+ final int heightSpec = MeasureSpec.makeMeasureSpec(
+ (int) (height - paddingTop - paddingBottom),
+ MeasureSpec.EXACTLY);
+ child.measure(widthSpec, heightSpec);
+ }
+
+ final int childMeasuredWidth = child.getMeasuredWidth();
+ final int startOffset = (int) (childWidth * ii.offset);
+ final int childLeft;
+ if (isLayoutRtl()) {
+ childLeft = MAX_SCROLL_X - paddingRight - startOffset - childMeasuredWidth;
+ } else {
+ childLeft = paddingLeft + startOffset;
+ }
+
+ final int childTop = paddingTop;
+ child.layout(childLeft, childTop, childLeft + childMeasuredWidth,
+ childTop + child.getMeasuredHeight());
}
+
mTopPageBounds = paddingTop;
mBottomPageBounds = height - paddingBottom;
mDecorChildCount = decorCount;
@@ -1587,13 +1623,14 @@
@Override
public void computeScroll() {
if (!mScroller.isFinished() && mScroller.computeScrollOffset()) {
- int oldX = getScrollX();
- int oldY = getScrollY();
- int x = mScroller.getCurrX();
- int y = mScroller.getCurrY();
+ final int oldX = getScrollX();
+ final int oldY = getScrollY();
+ final int x = mScroller.getCurrX();
+ final int y = mScroller.getCurrY();
if (oldX != x || oldY != y) {
scrollTo(x, y);
+
if (!pageScrolled(x)) {
mScroller.abortAnimation();
scrollTo(0, y);
@@ -1609,7 +1646,7 @@
completeScroll(true);
}
- private boolean pageScrolled(int xpos) {
+ private boolean pageScrolled(int scrollX) {
if (mItems.size() == 0) {
mCalledSuper = false;
onPageScrolled(0, 0, 0);
@@ -1619,12 +1656,21 @@
}
return false;
}
- final ItemInfo ii = infoForCurrentScrollPosition();
- final int width = getClientWidth();
+
+ // Translate to scrollX to scrollStart for RTL.
+ final int scrollStart;
+ if (isLayoutRtl()) {
+ scrollStart = MAX_SCROLL_X - scrollX;
+ } else {
+ scrollStart = scrollX;
+ }
+
+ final ItemInfo ii = infoForFirstVisiblePage();
+ final int width = getPaddedWidth();
final int widthWithMargin = width + mPageMargin;
final float marginOffset = (float) mPageMargin / width;
final int currentPage = ii.position;
- final float pageOffset = (((float) xpos / width) - ii.offset) /
+ final float pageOffset = (((float) scrollStart / width) - ii.offset) /
(ii.widthFactor + marginOffset);
final int offsetPixels = (int) (pageOffset * widthWithMargin);
@@ -1706,7 +1752,7 @@
if (lp.isDecor) continue;
- final float transformPos = (float) (child.getLeft() - scrollX) / getClientWidth();
+ final float transformPos = (float) (child.getLeft() - scrollX) / getPaddedWidth();
mPageTransformer.transformPage(child, transformPos);
}
}
@@ -1785,11 +1831,11 @@
// are dragging.
if (action != MotionEvent.ACTION_DOWN) {
if (mIsBeingDragged) {
- if (DEBUG) Log.v(TAG, "Intercept returning true!");
+ if (DEBUG) Log.v(TAG, "Being dragged, intercept returning true!");
return true;
}
if (mIsUnableToDrag) {
- if (DEBUG) Log.v(TAG, "Intercept returning false!");
+ if (DEBUG) Log.v(TAG, "Unable to drag, intercept returning false!");
return false;
}
}
@@ -1903,13 +1949,6 @@
@Override
public boolean onTouchEvent(MotionEvent ev) {
- if (mFakeDragging) {
- // A fake drag is in progress already, ignore this real one
- // but still eat the touch events.
- // (It is likely that the user is multi-touching the screen.)
- return true;
- }
-
if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) {
// Don't handle edge touches immediately -- they may actually belong to one of our
// descendants.
@@ -1978,19 +2017,26 @@
if (mIsBeingDragged) {
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
- int initialVelocity = (int) velocityTracker.getXVelocity(mActivePointerId);
+ final int initialVelocity = (int) velocityTracker.getXVelocity(mActivePointerId);
+
mPopulatePending = true;
- final int width = getClientWidth();
- final int scrollX = getScrollX();
- final ItemInfo ii = infoForCurrentScrollPosition();
+
+ final float scrollStart = getScrollStart();
+ final float scrolledPages = scrollStart / getPaddedWidth();
+ final ItemInfo ii = infoForFirstVisiblePage();
final int currentPage = ii.position;
- final float pageOffset = (((float) scrollX / width) - ii.offset) / ii.widthFactor;
- final int activePointerIndex =
- ev.findPointerIndex(mActivePointerId);
+ final float nextPageOffset;
+ if (isLayoutRtl()) {
+ nextPageOffset = (ii.offset - scrolledPages) / ii.widthFactor;
+ } else {
+ nextPageOffset = (scrolledPages - ii.offset) / ii.widthFactor;
+ }
+
+ final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(activePointerIndex);
final int totalDelta = (int) (x - mInitialMotionX);
- int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,
- totalDelta);
+ final int nextPage = determineTargetPage(
+ currentPage, nextPageOffset, initialVelocity, totalDelta);
setCurrentItemInternal(nextPage, true, true, initialVelocity);
mActivePointerId = INVALID_POINTER;
@@ -2038,48 +2084,79 @@
private boolean performDrag(float x) {
boolean needsInvalidate = false;
+ final int width = getPaddedWidth();
final float deltaX = mLastMotionX - x;
mLastMotionX = x;
- float oldScrollX = getScrollX();
- float scrollX = oldScrollX + deltaX;
- final int width = getClientWidth();
-
- float leftBound = width * mFirstOffset;
- float rightBound = width * mLastOffset;
- boolean leftAbsolute = true;
- boolean rightAbsolute = true;
-
- final ItemInfo firstItem = mItems.get(0);
- final ItemInfo lastItem = mItems.get(mItems.size() - 1);
- if (firstItem.position != 0) {
- leftAbsolute = false;
- leftBound = firstItem.offset * width;
- }
- if (lastItem.position != mAdapter.getCount() - 1) {
- rightAbsolute = false;
- rightBound = lastItem.offset * width;
+ final EdgeEffect startEdge;
+ final EdgeEffect endEdge;
+ if (isLayoutRtl()) {
+ startEdge = mRightEdge;
+ endEdge = mLeftEdge;
+ } else {
+ startEdge = mLeftEdge;
+ endEdge = mRightEdge;
}
- if (scrollX < leftBound) {
- if (leftAbsolute) {
- float over = leftBound - scrollX;
- mLeftEdge.onPull(Math.abs(over) / width);
+ // Translate scroll to relative coordinates.
+ final float nextScrollX = getScrollX() + deltaX;
+ final float scrollStart;
+ if (isLayoutRtl()) {
+ scrollStart = MAX_SCROLL_X - nextScrollX;
+ } else {
+ scrollStart = nextScrollX;
+ }
+
+ final float startBound;
+ final ItemInfo startItem = mItems.get(0);
+ final boolean startAbsolute = startItem.position == 0;
+ if (startAbsolute) {
+ startBound = startItem.offset * width;
+ } else {
+ startBound = width * mFirstOffset;
+ }
+
+ final float endBound;
+ final ItemInfo endItem = mItems.get(mItems.size() - 1);
+ final boolean endAbsolute = endItem.position == mAdapter.getCount() - 1;
+ if (endAbsolute) {
+ endBound = endItem.offset * width;
+ } else {
+ endBound = width * mLastOffset;
+ }
+
+ final float clampedScrollStart;
+ if (scrollStart < startBound) {
+ if (startAbsolute) {
+ final float over = startBound - scrollStart;
+ startEdge.onPull(Math.abs(over) / width);
needsInvalidate = true;
}
- scrollX = leftBound;
- } else if (scrollX > rightBound) {
- if (rightAbsolute) {
- float over = scrollX - rightBound;
- mRightEdge.onPull(Math.abs(over) / width);
+ clampedScrollStart = startBound;
+ } else if (scrollStart > endBound) {
+ if (endAbsolute) {
+ final float over = scrollStart - endBound;
+ endEdge.onPull(Math.abs(over) / width);
needsInvalidate = true;
}
- scrollX = rightBound;
+ clampedScrollStart = endBound;
+ } else {
+ clampedScrollStart = scrollStart;
}
- // Don't lose the rounded component
- mLastMotionX += scrollX - (int) scrollX;
- scrollTo((int) scrollX, getScrollY());
- pageScrolled((int) scrollX);
+
+ // Translate back to absolute coordinates.
+ final float targetScrollX;
+ if (isLayoutRtl()) {
+ targetScrollX = MAX_SCROLL_X - clampedScrollStart;
+ } else {
+ targetScrollX = clampedScrollStart;
+ }
+
+ // Don't lose the rounded component.
+ mLastMotionX += targetScrollX - (int) targetScrollX;
+
+ scrollTo((int) targetScrollX, getScrollY());
+ pageScrolled((int) targetScrollX);
return needsInvalidate;
}
@@ -2088,19 +2165,23 @@
* @return Info about the page at the current scroll position.
* This can be synthetic for a missing middle page; the 'object' field can be null.
*/
- private ItemInfo infoForCurrentScrollPosition() {
- final int width = getClientWidth();
- final float scrollOffset = width > 0 ? (float) getScrollX() / width : 0;
+ private ItemInfo infoForFirstVisiblePage() {
+ final int startOffset = getScrollStart();
+ final int width = getPaddedWidth();
+ final float scrollOffset = width > 0 ? (float) startOffset / width : 0;
final float marginOffset = width > 0 ? (float) mPageMargin / width : 0;
+
int lastPos = -1;
float lastOffset = 0.f;
float lastWidth = 0.f;
boolean first = true;
-
ItemInfo lastItem = null;
- for (int i = 0; i < mItems.size(); i++) {
+
+ final int N = mItems.size();
+ for (int i = 0; i < N; i++) {
ItemInfo ii = mItems.get(i);
- float offset;
+
+ // Seek to position.
if (!first && ii.position != lastPos + 1) {
// Create a synthetic item for a missing page.
ii = mTempItem;
@@ -2109,17 +2190,18 @@
ii.widthFactor = mAdapter.getPageWidth(ii.position);
i--;
}
- offset = ii.offset;
- final float leftBound = offset;
- final float rightBound = offset + ii.widthFactor + marginOffset;
- if (first || scrollOffset >= leftBound) {
- if (scrollOffset < rightBound || i == mItems.size() - 1) {
+ final float offset = ii.offset;
+ final float startBound = offset;
+ if (first || scrollOffset >= startBound) {
+ final float endBound = offset + ii.widthFactor + marginOffset;
+ if (scrollOffset < endBound || i == mItems.size() - 1) {
return ii;
}
} else {
return lastItem;
}
+
first = false;
lastPos = ii.position;
lastOffset = offset;
@@ -2130,13 +2212,28 @@
return lastItem;
}
+ private int getScrollStart() {
+ if (isLayoutRtl()) {
+ return MAX_SCROLL_X - getScrollX();
+ } else {
+ return getScrollX();
+ }
+ }
+
+ /**
+ * @param currentPage the position of the page with the first visible starting edge
+ * @param pageOffset the fraction of the right-hand page that's visible
+ * @param velocity the velocity of the touch event stream
+ * @param deltaX the distance of the touch event stream
+ * @return the position of the target page
+ */
private int determineTargetPage(int currentPage, float pageOffset, int velocity, int deltaX) {
int targetPage;
if (Math.abs(deltaX) > mFlingDistance && Math.abs(velocity) > mMinimumVelocity) {
- targetPage = velocity > 0 ? currentPage : currentPage + 1;
+ targetPage = currentPage - (velocity < 0 ? mLeftIncr : 0);
} else {
final float truncator = currentPage >= mCurItem ? 0.4f : 0.6f;
- targetPage = (int) (currentPage + pageOffset + truncator);
+ targetPage = (int) (currentPage - mLeftIncr * (pageOffset + truncator));
}
if (mItems.size() > 0) {
@@ -2144,7 +2241,7 @@
final ItemInfo lastItem = mItems.get(mItems.size() - 1);
// Only let the user target pages we have items for
- targetPage = Math.max(firstItem.position, Math.min(targetPage, lastItem.position));
+ targetPage = MathUtils.constrain(targetPage, firstItem.position, lastItem.position);
}
return targetPage;
@@ -2205,6 +2302,7 @@
int itemIndex = 0;
ItemInfo ii = mItems.get(0);
float offset = ii.offset;
+
final int itemCount = mItems.size();
final int firstPos = ii.position;
final int lastPos = mItems.get(itemCount - 1).position;
@@ -2213,156 +2311,39 @@
ii = mItems.get(++itemIndex);
}
- float drawAt;
+ final float itemOffset;
+ final float widthFactor;
if (pos == ii.position) {
- drawAt = (ii.offset + ii.widthFactor) * width;
- offset = ii.offset + ii.widthFactor + marginOffset;
+ itemOffset = ii.offset;
+ widthFactor = ii.widthFactor;
} else {
- float widthFactor = mAdapter.getPageWidth(pos);
- drawAt = (offset + widthFactor) * width;
- offset += widthFactor + marginOffset;
+ itemOffset = offset;
+ widthFactor = mAdapter.getPageWidth(pos);
}
- if (drawAt + mPageMargin > scrollX) {
- mMarginDrawable.setBounds((int) drawAt, mTopPageBounds,
- (int) (drawAt + mPageMargin + 0.5f), mBottomPageBounds);
+ final float left;
+ final float scaledOffset = itemOffset * width;
+ if (isLayoutRtl()) {
+ left = MAX_SCROLL_X - scaledOffset;
+ } else {
+ left = scaledOffset + widthFactor * width;
+ }
+
+ offset = itemOffset + widthFactor + marginOffset;
+
+ if (left + mPageMargin > scrollX) {
+ mMarginDrawable.setBounds((int) left, mTopPageBounds,
+ (int) (left + mPageMargin + 0.5f), mBottomPageBounds);
mMarginDrawable.draw(canvas);
}
- if (drawAt > scrollX + width) {
+ if (left > scrollX + width) {
break; // No more visible, no sense in continuing
}
}
}
}
- /**
- * Start a fake drag of the pager.
- *
- * <p>A fake drag can be useful if you want to synchronize the motion of the ViewPager
- * with the touch scrolling of another view, while still letting the ViewPager
- * control the snapping motion and fling behavior. (e.g. parallax-scrolling tabs.)
- * Call {@link #fakeDragBy(float)} to simulate the actual drag motion. Call
- * {@link #endFakeDrag()} to complete the fake drag and fling as necessary.
- *
- * <p>During a fake drag the ViewPager will ignore all touch events. If a real drag
- * is already in progress, this method will return false.
- *
- * @return true if the fake drag began successfully, false if it could not be started.
- *
- * @see #fakeDragBy(float)
- * @see #endFakeDrag()
- */
- public boolean beginFakeDrag() {
- if (mIsBeingDragged) {
- return false;
- }
- mFakeDragging = true;
- setScrollState(SCROLL_STATE_DRAGGING);
- mInitialMotionX = mLastMotionX = 0;
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- } else {
- mVelocityTracker.clear();
- }
- final long time = SystemClock.uptimeMillis();
- final MotionEvent ev = MotionEvent.obtain(time, time, MotionEvent.ACTION_DOWN, 0, 0, 0);
- mVelocityTracker.addMovement(ev);
- ev.recycle();
- mFakeDragBeginTime = time;
- return true;
- }
-
- /**
- * End a fake drag of the pager.
- *
- * @see #beginFakeDrag()
- * @see #fakeDragBy(float)
- */
- public void endFakeDrag() {
- if (!mFakeDragging) {
- throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first.");
- }
-
- final VelocityTracker velocityTracker = mVelocityTracker;
- velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
- int initialVelocity = (int) velocityTracker.getXVelocity(mActivePointerId);
- mPopulatePending = true;
- final int width = getClientWidth();
- final int scrollX = getScrollX();
- final ItemInfo ii = infoForCurrentScrollPosition();
- final int currentPage = ii.position;
- final float pageOffset = (((float) scrollX / width) - ii.offset) / ii.widthFactor;
- final int totalDelta = (int) (mLastMotionX - mInitialMotionX);
- int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,
- totalDelta);
- setCurrentItemInternal(nextPage, true, true, initialVelocity);
- endDrag();
-
- mFakeDragging = false;
- }
-
- /**
- * Fake drag by an offset in pixels. You must have called {@link #beginFakeDrag()} first.
- *
- * @param xOffset Offset in pixels to drag by.
- * @see #beginFakeDrag()
- * @see #endFakeDrag()
- */
- public void fakeDragBy(float xOffset) {
- if (!mFakeDragging) {
- throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first.");
- }
-
- mLastMotionX += xOffset;
-
- float oldScrollX = getScrollX();
- float scrollX = oldScrollX - xOffset;
- final int width = getClientWidth();
-
- float leftBound = width * mFirstOffset;
- float rightBound = width * mLastOffset;
-
- final ItemInfo firstItem = mItems.get(0);
- final ItemInfo lastItem = mItems.get(mItems.size() - 1);
- if (firstItem.position != 0) {
- leftBound = firstItem.offset * width;
- }
- if (lastItem.position != mAdapter.getCount() - 1) {
- rightBound = lastItem.offset * width;
- }
-
- if (scrollX < leftBound) {
- scrollX = leftBound;
- } else if (scrollX > rightBound) {
- scrollX = rightBound;
- }
- // Don't lose the rounded component
- mLastMotionX += scrollX - (int) scrollX;
- scrollTo((int) scrollX, getScrollY());
- pageScrolled((int) scrollX);
-
- // Synthesize an event for the VelocityTracker.
- final long time = SystemClock.uptimeMillis();
- final MotionEvent ev = MotionEvent.obtain(mFakeDragBeginTime, time, MotionEvent.ACTION_MOVE,
- mLastMotionX, 0, 0);
- mVelocityTracker.addMovement(ev);
- ev.recycle();
- }
-
- /**
- * Returns true if a fake drag is in progress.
- *
- * @return true if currently in a fake drag, false otherwise.
- *
- * @see #beginFakeDrag()
- * @see #fakeDragBy(float)
- * @see #endFakeDrag()
- */
- public boolean isFakeDragging() {
- return mFakeDragging;
- }
-
private void onSecondaryPointerUp(MotionEvent ev) {
final int pointerIndex = ev.getActionIndex();
final int pointerId = ev.getPointerId(pointerIndex);
@@ -2408,7 +2389,7 @@
return false;
}
- final int width = getClientWidth();
+ final int width = getPaddedWidth();
final int scrollX = getScrollX();
if (direction < 0) {
return (scrollX > (int) (width * mFirstOffset));
@@ -2438,12 +2419,11 @@
final int count = group.getChildCount();
// Count backwards - let topmost views consume scroll distance first.
for (int i = count - 1; i >= 0; i--) {
- // TODO: Add versioned support here for transformed views.
- // This will not work for transformed views in Honeycomb+
+ // TODO: Add support for transformed views.
final View child = group.getChildAt(i);
- if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() &&
- y + scrollY >= child.getTop() && y + scrollY < child.getBottom() &&
- canScroll(child, true, dx, x + scrollX - child.getLeft(),
+ if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight()
+ && y + scrollY >= child.getTop() && y + scrollY < child.getBottom()
+ && canScroll(child, true, dx, x + scrollX - child.getLeft(),
y + scrollY - child.getTop())) {
return true;
}
@@ -2582,19 +2562,22 @@
}
boolean pageLeft() {
- if (mCurItem > 0) {
- setCurrentItem(mCurItem-1, true);
- return true;
- }
- return false;
+ return setCurrentItemInternal(mCurItem + mLeftIncr, true, false);
}
boolean pageRight() {
- if (mAdapter != null && mCurItem < (mAdapter.getCount()-1)) {
- setCurrentItem(mCurItem+1, true);
- return true;
+ return setCurrentItemInternal(mCurItem - mLeftIncr, true, false);
+ }
+
+ @Override
+ public void onRtlPropertiesChanged(@ResolvedLayoutDir int layoutDirection) {
+ super.onRtlPropertiesChanged(layoutDirection);
+
+ if (layoutDirection == LAYOUT_DIRECTION_LTR) {
+ mLeftIncr = -1;
+ } else {
+ mLeftIncr = 1;
}
- return false;
}
/**
diff --git a/core/res/res/drawable/ic_chevron_right.xml b/core/res/res/drawable/ic_chevron_end.xml
similarity index 91%
rename from core/res/res/drawable/ic_chevron_right.xml
rename to core/res/res/drawable/ic_chevron_end.xml
index 4e6d8e3..8570d26 100644
--- a/core/res/res/drawable/ic_chevron_right.xml
+++ b/core/res/res/drawable/ic_chevron_end.xml
@@ -18,7 +18,8 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="?attr/colorControlNormal">
+ android:tint="?attr/colorControlNormal"
+ android:autoMirrored="true">
<path
android:fillColor="#FF000000"
android:pathData="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6,-6z"/>
diff --git a/core/res/res/drawable/ic_chevron_left.xml b/core/res/res/drawable/ic_chevron_start.xml
similarity index 91%
rename from core/res/res/drawable/ic_chevron_left.xml
rename to core/res/res/drawable/ic_chevron_start.xml
index dc24706..d412ce0 100644
--- a/core/res/res/drawable/ic_chevron_left.xml
+++ b/core/res/res/drawable/ic_chevron_start.xml
@@ -18,7 +18,8 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="?attr/colorControlNormal">
+ android:tint="?attr/colorControlNormal"
+ android:autoMirrored="true">
<path
android:fillColor="#FF000000"
android:pathData="M15.41 7.41L14 6l-6 6 6 6 1.41,-1.41L10.83 12z"/>
diff --git a/core/res/res/layout/date_picker_header_material.xml b/core/res/res/layout/date_picker_header_material.xml
index 8125544..2150341 100644
--- a/core/res/res/layout/date_picker_header_material.xml
+++ b/core/res/res/layout/date_picker_header_material.xml
@@ -42,7 +42,7 @@
<TextView
android:id="@+id/date_picker_header_date"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.Material.DatePicker.DateLabel"
android:gravity="start"
diff --git a/core/res/res/layout/day_picker_content_material.xml b/core/res/res/layout/day_picker_content_material.xml
index 1852bfa..b582d74 100644
--- a/core/res/res/layout/day_picker_content_material.xml
+++ b/core/res/res/layout/day_picker_content_material.xml
@@ -30,7 +30,7 @@
android:layout_height="wrap_content"
android:minWidth="48dp"
android:minHeight="48dp"
- android:src="@drawable/ic_chevron_left"
+ android:src="@drawable/ic_chevron_start"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/date_picker_prev_month_button"
android:visibility="invisible" />
@@ -41,7 +41,7 @@
android:layout_height="wrap_content"
android:minWidth="48dp"
android:minHeight="48dp"
- android:src="@drawable/ic_chevron_right"
+ android:src="@drawable/ic_chevron_end"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/date_picker_next_month_button"
android:visibility="invisible" />
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7e24150..5b13325 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2235,8 +2235,6 @@
<java-symbol type="dimen" name="floating_toolbar_horizontal_margin" />
<java-symbol type="dimen" name="floating_toolbar_vertical_margin" />
- <java-symbol type="drawable" name="ic_chevron_left" />
- <java-symbol type="drawable" name="ic_chevron_right" />
<java-symbol type="string" name="date_picker_prev_month_button" />
<java-symbol type="string" name="date_picker_next_month_button" />
<java-symbol type="layout" name="date_picker_month_item_material" />
diff --git a/docs/html/guide/appendix/media-formats.jd b/docs/html/guide/appendix/media-formats.jd
index 19f510a..2a908ba 100644
--- a/docs/html/guide/appendix/media-formats.jd
+++ b/docs/html/guide/appendix/media-formats.jd
@@ -71,7 +71,7 @@
</tr>
<tr>
-<td rowspan="11">Audio</td>
+<td rowspan="12">Audio</td>
<td>AAC LC</td>
<td style="text-align: center;"><big>•</big></td>
<td style="text-align: center;"><big>•</big></td>
@@ -180,6 +180,15 @@
</tr>
<tr>
+<td>Opus</td>
+<td style="text-align: center;"></td>
+<td style="text-align: center;"><big>•</big><br><small>(Android 5.0+)</small></td>
+<td></td>
+<td>
+ Matroska (.mkv)</td>
+</tr>
+
+<tr>
<td rowspan="5">Image</td>
<td>JPEG</td>
<td style="text-align: center;"><big>•</big></td>
@@ -235,7 +244,7 @@
<tr>
-<td rowspan="4">Video</td>
+<td rowspan="6">Video</td>
<td>H.263</td>
<td style="text-align: center;"><big>•</big></td>
<td style="text-align: center;"><big>•</big></td>
@@ -257,6 +266,15 @@
</tr>
<tr>
+<td>H.265 HEVC</td>
+<td style="text-align: center;" nowrap></td>
+<td style="text-align: center;" nowrap><big>•</big><br><small>(Android 5.0+)</small></td>
+<td>Main Profile Level 3 for mobile devices and Main Profile Level 4.1 for Android TV</td>
+<td>
+ • MPEG-4 (.mp4)<br>
+</tr>
+
+<tr>
<td>MPEG-4 SP</td>
<td> </td>
<td style="text-align: center;"><big>•</big></td>
@@ -275,6 +293,16 @@
• Matroska (.mkv, Android 4.0+)</td>
</tr>
+<tr>
+<td>VP9</td>
+<td style="text-align: center;" nowrap></td>
+<td style="text-align: center;" nowrap><big>•</big><br><small>(Android 4.4+)</small></td>
+<td></td>
+<td>
+ • <a href="http://www.webmproject.org/">WebM</a> (.webm)<br>
+ • Matroska (.mkv, Android 4.0+)</td>
+</tr>
+
</tbody></table>
diff --git a/docs/html/training/tv/start/start.jd b/docs/html/training/tv/start/start.jd
index 2766e90..0f5871f 100644
--- a/docs/html/training/tv/start/start.jd
+++ b/docs/html/training/tv/start/start.jd
@@ -1,4 +1,4 @@
-page.title=Get Started with TV Apps
+page.title=Getting Started with TV Apps
page.tags="leanback","recyclerview","launcher"
trainingnavtop=true
@@ -10,6 +10,7 @@
<div id="tb">
<h2>This lesson teaches you how to</h2>
<ol>
+ <li><a href="#media">Determine Media Format Support</a></li>
<li><a href="#dev-project">Setup a TV Project</a></li>
<li><a href="#tv-libraries">Add TV Support Libraries</a></li>
<li><a href="#build-it">Build TV Apps</a></li>
@@ -42,6 +43,18 @@
minimum required changes to enable an app to run on TV devices.
</p>
+<h2 id="media">Determine Media Format Support</h2>
+
+<p>See the following documentation for information about the codecs, protocols, and formats
+supported by Android TV.</p>
+
+<ul>
+ <li><a href="{@docRoot}guide/appendix/media-formats.html">Supported Media Formats</a></li>
+ <li><a class="external-link" href="https://source.android.com/devices/drm.html">DRM</a></li>
+ <li><code><a href="{@docRoot}reference/android/drm/package-summary.html">android.drm</a></code></li>
+ <li><a href="{@docRoot}guide/topics/media/exoplayer.html">ExoPlayer</a></li>
+ <li>{@link android.media.MediaPlayer android.media.MediaPlayer}</li>
+</ul>
<h2 id="dev-project">Set up a TV Project</h2>
@@ -284,9 +297,15 @@
TV devices.
</li>
<li>
- <a href="{@docRoot}training/tv/games/index.html">Games for TV</a> - TV devices are a great
+ <a href="{@docRoot}training/tv/games/index.html">Building TV Games</a> - TV devices are a great
platform for games. See this topic for information on building great game experiences for TV.
</li>
+ <li>
+ <a href="{@docRoot}training/tv/tif/index.html">Building Live TV Apps</a> - Present your video
+ content in a linear, "broadcast TV" style with channels and programs that your users can access
+ through a program guide as well as the channel up/down buttons.
+ </li>
+
</ul>
diff --git a/keystore/java/android/security/KeyStoreCipherSpi.java b/keystore/java/android/security/KeyStoreCipherSpi.java
index 37e00b2..1f8d8ec 100644
--- a/keystore/java/android/security/KeyStoreCipherSpi.java
+++ b/keystore/java/android/security/KeyStoreCipherSpi.java
@@ -152,29 +152,58 @@
@Override
protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
- init(opmode, key, random);
- initAlgorithmSpecificParameters();
- ensureKeystoreOperationInitialized();
+ resetAll();
+
+ boolean success = false;
+ try {
+ init(opmode, key, random);
+ initAlgorithmSpecificParameters();
+ ensureKeystoreOperationInitialized();
+ success = true;
+ } finally {
+ if (!success) {
+ resetAll();
+ }
+ }
}
@Override
protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
- init(opmode, key, random);
- initAlgorithmSpecificParameters(params);
- ensureKeystoreOperationInitialized();
+ resetAll();
+
+ boolean success = false;
+ try {
+ init(opmode, key, random);
+ initAlgorithmSpecificParameters(params);
+ ensureKeystoreOperationInitialized();
+ success = true;
+ } finally {
+ if (!success) {
+ resetAll();
+ }
+ }
}
@Override
protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params,
SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
- init(opmode, key, random);
- initAlgorithmSpecificParameters(params);
- ensureKeystoreOperationInitialized();
+ resetAll();
+
+ boolean success = false;
+ try {
+ init(opmode, key, random);
+ initAlgorithmSpecificParameters(params);
+ ensureKeystoreOperationInitialized();
+ success = true;
+ } finally {
+ if (!success) {
+ resetAll();
+ }
+ }
}
private void init(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
- resetAll();
if (!(key instanceof KeyStoreSecretKey)) {
throw new InvalidKeyException(
"Unsupported key: " + ((key != null) ? key.getClass().getName() : "null"));
diff --git a/keystore/java/android/security/KeyStoreHmacSpi.java b/keystore/java/android/security/KeyStoreHmacSpi.java
index a19bbda..f8b6fef 100644
--- a/keystore/java/android/security/KeyStoreHmacSpi.java
+++ b/keystore/java/android/security/KeyStoreHmacSpi.java
@@ -69,9 +69,10 @@
private final int mKeymasterDigest;
private final int mMacSizeBytes;
- private String mKeyAliasInKeyStore;
+ // Fields below are populated by engineInit and should be preserved after engineDoFinal.
+ private KeyStoreSecretKey mKey;
- // The fields below are reset by the engineReset operation.
+ // Fields below are reset when engineDoFinal succeeds.
private KeyStoreCryptoOperationChunkedStreamer mChunkedStreamer;
private IBinder mOperationToken;
private Long mOperationHandle;
@@ -89,28 +90,39 @@
@Override
protected void engineInit(Key key, AlgorithmParameterSpec params) throws InvalidKeyException,
InvalidAlgorithmParameterException {
+ resetAll();
+
+ boolean success = false;
+ try {
+ init(key, params);
+ ensureKeystoreOperationInitialized();
+ success = true;
+ } finally {
+ if (!success) {
+ resetAll();
+ }
+ }
+ }
+
+ private void init(Key key, AlgorithmParameterSpec params) throws InvalidKeyException,
+ InvalidAlgorithmParameterException {
if (key == null) {
throw new InvalidKeyException("key == null");
} else if (!(key instanceof KeyStoreSecretKey)) {
throw new InvalidKeyException(
"Only Android KeyStore secret keys supported. Key: " + key);
}
+ mKey = (KeyStoreSecretKey) key;
if (params != null) {
throw new InvalidAlgorithmParameterException(
"Unsupported algorithm parameters: " + params);
}
- mKeyAliasInKeyStore = ((KeyStoreSecretKey) key).getAlias();
- if (mKeyAliasInKeyStore == null) {
- throw new InvalidKeyException("Key's KeyStore alias not known");
- }
- engineReset();
- ensureKeystoreOperationInitialized();
}
- @Override
- protected void engineReset() {
+ private void resetAll() {
+ mKey = null;
IBinder operationToken = mOperationToken;
if (operationToken != null) {
mOperationToken = null;
@@ -120,11 +132,26 @@
mChunkedStreamer = null;
}
+ private void resetWhilePreservingInitState() {
+ IBinder operationToken = mOperationToken;
+ if (operationToken != null) {
+ mOperationToken = null;
+ mKeyStore.abort(operationToken);
+ }
+ mOperationHandle = null;
+ mChunkedStreamer = null;
+ }
+
+ @Override
+ protected void engineReset() {
+ resetWhilePreservingInitState();
+ }
+
private void ensureKeystoreOperationInitialized() {
if (mChunkedStreamer != null) {
return;
}
- if (mKeyAliasInKeyStore == null) {
+ if (mKey == null) {
throw new IllegalStateException("Not initialized");
}
@@ -132,7 +159,8 @@
keymasterArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_HMAC);
keymasterArgs.addInt(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
- OperationResult opResult = mKeyStore.begin(mKeyAliasInKeyStore,
+ OperationResult opResult = mKeyStore.begin(
+ mKey.getAlias(),
KeymasterDefs.KM_PURPOSE_SIGN,
true,
keymasterArgs,
@@ -184,7 +212,7 @@
throw KeyStore.getCryptoOperationException(e);
}
- engineReset();
+ resetWhilePreservingInitState();
return result;
}
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index f0e2f5c5..46d33b4 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -16,6 +16,7 @@
package android.media.tv;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.Context;
@@ -424,7 +425,7 @@
* @return a CharSequence containing the TV input's label. If the TV input does not have
* a label, its name is returned.
*/
- public CharSequence loadLabel(Context context) {
+ public CharSequence loadLabel(@NonNull Context context) {
if (TextUtils.isEmpty(mLabel)) {
return mService.loadLabel(context.getPackageManager());
} else {
@@ -452,7 +453,7 @@
* @return a Drawable containing the TV input's icon. If the TV input does not have an icon,
* application's icon is returned. If it's unavailable too, {@code null} is returned.
*/
- public Drawable loadIcon(Context context) {
+ public Drawable loadIcon(@NonNull Context context) {
if (mIconUri == null) {
return loadServiceIcon(context);
}
@@ -506,7 +507,7 @@
* @param flags The flags used for parceling.
*/
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(mId);
dest.writeString(mParentId);
mService.writeToParcel(dest, flags);
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 105084e..0f265de 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -16,6 +16,7 @@
package android.media.tv;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.graphics.Rect;
@@ -932,7 +933,7 @@
* @return the {@link TvInputInfo} for a given TV input. {@code null} if not found.
*/
@Nullable
- public TvInputInfo getTvInputInfo(String inputId) {
+ public TvInputInfo getTvInputInfo(@NonNull String inputId) {
if (inputId == null) {
throw new IllegalArgumentException("inputId cannot be null");
}
@@ -956,7 +957,7 @@
* @param inputId The id of the TV input.
* @throws IllegalArgumentException if the argument is {@code null}.
*/
- public int getInputState(String inputId) {
+ public int getInputState(@NonNull String inputId) {
if (inputId == null) {
throw new IllegalArgumentException("inputId cannot be null");
}
@@ -977,7 +978,7 @@
* @param handler A {@link Handler} that the status change will be delivered to.
* @throws IllegalArgumentException if any of the arguments is {@code null}.
*/
- public void registerCallback(TvInputCallback callback, Handler handler) {
+ public void registerCallback(@NonNull TvInputCallback callback, @NonNull Handler handler) {
if (callback == null) {
throw new IllegalArgumentException("callback cannot be null");
}
@@ -995,7 +996,7 @@
* @param callback The existing callback to remove.
* @throws IllegalArgumentException if any of the arguments is {@code null}.
*/
- public void unregisterCallback(final TvInputCallback callback) {
+ public void unregisterCallback(@NonNull final TvInputCallback callback) {
if (callback == null) {
throw new IllegalArgumentException("callback cannot be null");
}
@@ -1047,7 +1048,7 @@
* @param rating The TV content rating to check.
* @return {@code true} if the given TV content rating is blocked, {@code false} otherwise.
*/
- public boolean isRatingBlocked(TvContentRating rating) {
+ public boolean isRatingBlocked(@NonNull TvContentRating rating) {
if (rating == null) {
throw new IllegalArgumentException("rating cannot be null");
}
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 382ec91..4258534 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -16,6 +16,7 @@
package android.media.tv;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
@@ -314,7 +315,7 @@
@SystemApi
public void notifySessionEvent(final String eventType, final Bundle eventArgs) {
if (eventType == null) {
- throw new IllegalArgumentException("eventType should not be null.");
+ throw new IllegalArgumentException("eventType cannot be null");
}
executeOrPostRunnable(new Runnable() {
@Override
@@ -544,7 +545,10 @@
* @see #notifyContentAllowed
* @see TvInputManager
*/
- public void notifyContentBlocked(final TvContentRating rating) {
+ public void notifyContentBlocked(@NonNull final TvContentRating rating) {
+ if (rating == null) {
+ throw new IllegalArgumentException("rating cannot be null");
+ }
executeOrPostRunnable(new Runnable() {
@Override
public void run() {
@@ -828,7 +832,7 @@
* @hide
*/
@SystemApi
- public void onAppPrivateCommand(String action, Bundle data) {
+ public void onAppPrivateCommand(@NonNull String action, Bundle data) {
}
/**
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 6456049..d248b12 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -16,6 +16,7 @@
package android.media.tv;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.Context;
@@ -273,7 +274,7 @@
* @param inputId The ID of TV input which will play the given channel.
* @param channelUri The URI of a channel.
*/
- public void tune(String inputId, Uri channelUri) {
+ public void tune(@NonNull String inputId, Uri channelUri) {
tune(inputId, channelUri, null);
}
@@ -494,7 +495,7 @@
* @hide
*/
@SystemApi
- public void sendAppPrivateCommand(String action, Bundle data) {
+ public void sendAppPrivateCommand(@NonNull String action, Bundle data) {
if (TextUtils.isEmpty(action)) {
throw new IllegalArgumentException("action cannot be null or an empty string");
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
index 6f33672..d71b44b 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -19,6 +19,7 @@
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.ICameraDeviceCallbacks;
@@ -170,7 +171,8 @@
assertEquals(CameraBinderTestUtils.NO_ERROR, status);
assertFalse(metadata.isEmpty());
- CaptureRequest.Builder request = new CaptureRequest.Builder(metadata, /*reprocess*/false);
+ CaptureRequest.Builder request = new CaptureRequest.Builder(metadata, /*reprocess*/false,
+ CameraCaptureSession.SESSION_ID_NONE);
assertFalse(request.isEmpty());
assertFalse(metadata.isEmpty());
if (needStream) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a48a4d9..4970e0f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5508,17 +5508,20 @@
if (app.isolated) {
mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
}
- app.kill(reason, true);
- handleAppDiedLocked(app, true, allowRestart);
- removeLruProcessLocked(app);
-
+ boolean willRestart = false;
if (app.persistent && !app.isolated) {
if (!callerWillRestart) {
- addAppLocked(app.info, false, null /* ABI override */);
+ willRestart = true;
} else {
needRestart = true;
}
}
+ app.kill(reason, true);
+ handleAppDiedLocked(app, willRestart, allowRestart);
+ if (willRestart) {
+ removeLruProcessLocked(app);
+ addAppLocked(app.info, false, null /* ABI override */);
+ }
} else {
mRemovedProcesses.add(app);
}
diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java
index db3ae91..c8e5c3a 100644
--- a/services/core/java/com/android/server/pm/KeySetManagerService.java
+++ b/services/core/java/com/android/server/pm/KeySetManagerService.java
@@ -488,7 +488,7 @@
}
public String encodePublicKey(PublicKey k) throws IOException {
- return new String(Base64.encode(k.getEncoded(), 0));
+ return new String(Base64.encode(k.getEncoded(), Base64.NO_WRAP));
}
public void dumpLPr(PrintWriter pw, String packageName,
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index e914cd4..a210223 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -281,7 +281,7 @@
Slog.w(WindowManagerService.TAG, "removeAllWindows: removing win=" + win);
}
- service.removeWindowLocked(win.mSession, win);
+ service.removeWindowLocked(win);
}
allAppWindows.clear();
windows.clear();
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index bb53534..c24fcb3 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -78,7 +78,7 @@
WindowState windowState = (WindowState) inputWindowHandle.windowState;
if (windowState != null) {
Slog.i(WindowManagerService.TAG, "WINDOW DIED " + windowState);
- mService.removeWindowLocked(windowState.mSession, windowState);
+ mService.removeWindowLocked(windowState);
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 38fc959..c5bdbb0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2660,11 +2660,11 @@
if (win == null) {
return;
}
- removeWindowLocked(session, win);
+ removeWindowLocked(win);
}
}
- public void removeWindowLocked(Session session, WindowState win) {
+ void removeWindowLocked(WindowState win) {
if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {
if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "Starting window removed " + win);
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index cd9689e..ec70879 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1169,10 +1169,10 @@
WindowState win = mService.windowForClientLocked(mSession, mClient, false);
Slog.i(TAG, "WIN DEATH: " + win);
if (win != null) {
- mService.removeWindowLocked(mSession, win);
+ mService.removeWindowLocked(win);
} else if (mHasSurface) {
Slog.e(TAG, "!!! LEAK !!! Window removed but surface still valid.");
- mService.removeWindowLocked(mSession, WindowState.this);
+ mService.removeWindowLocked(WindowState.this);
}
}
} catch (IllegalArgumentException ex) {
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index bbeee8c..b303505 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -83,7 +83,7 @@
WindowState win = windows.get(winNdx);
if (WindowManagerService.DEBUG_WINDOW_MOVEMENT) Slog.w(WindowManagerService.TAG,
"removeAllWindows: removing win=" + win);
- win.mService.removeWindowLocked(win.mSession, win);
+ win.mService.removeWindowLocked(win);
}
windows.clear();
}