Merge "Fix issue #3194697: default orientation for portrait-mode apps on stingray is backward?"
diff --git a/api/current.xml b/api/current.xml
index e185858..a30426a 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -26765,6 +26765,21 @@
<parameter name="day" type="int">
</parameter>
</method>
+<method name="setRange"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="startYear" type="int">
+</parameter>
+<parameter name="endYear" type="int">
+</parameter>
+</method>
<method name="updateDate"
return="void"
abstract="false"
@@ -185102,6 +185117,25 @@
<parameter name="event" type="android.view.KeyEvent">
</parameter>
</method>
+<method name="forwardDelete"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="view" type="android.view.View">
+</parameter>
+<parameter name="content" type="android.text.Editable">
+</parameter>
+<parameter name="keyCode" type="int">
+</parameter>
+<parameter name="event" type="android.view.KeyEvent">
+</parameter>
+</method>
<method name="onKeyOther"
return="boolean"
abstract="false"
@@ -185324,25 +185358,6 @@
<parameter name="event" type="android.view.KeyEvent">
</parameter>
</method>
-<method name="forwardDelete"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="view" type="android.view.View">
-</parameter>
-<parameter name="content" type="android.text.Editable">
-</parameter>
-<parameter name="keyCode" type="int">
-</parameter>
-<parameter name="event" type="android.view.KeyEvent">
-</parameter>
-</method>
<method name="onKeyOther"
return="boolean"
abstract="false"
@@ -235118,6 +235133,21 @@
<parameter name="onDateChangedListener" type="android.widget.DatePicker.OnDateChangedListener">
</parameter>
</method>
+<method name="setRange"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="startYear" type="int">
+</parameter>
+<parameter name="endYear" type="int">
+</parameter>
+</method>
<method name="updateDate"
return="void"
abstract="false"
diff --git a/core/java/android/app/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java
index 448b3ef..e6e55ee 100644
--- a/core/java/android/app/DatePickerDialog.java
+++ b/core/java/android/app/DatePickerDialog.java
@@ -21,7 +21,6 @@
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
-import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@@ -118,6 +117,28 @@
mDatePicker.init(year, month, day, null);
}
+ /**
+ * Sets the range of years in which dates can be selected.
+ * <p>
+ * Note: If the range is set to a value that does not include the currently
+ * selected date the value of the picker shown by this dialog will be
+ * updated to the closest date in the range.
+ * </p>
+ *
+ * @param startYear The start year of the range.
+ * @param endYear The end year of the range.
+ */
+ public void setRange(int startYear, int endYear) {
+ mDatePicker.setRange(startYear, endYear);
+ }
+
+ /**
+ * Sets the current date.
+ *
+ * @param year The date year.
+ * @param monthOfYear The date month.
+ * @param dayOfMonth The date day of month.
+ */
public void updateDate(int year, int monthOfYear, int dayOfMonth) {
mDatePicker.updateDate(year, monthOfYear, dayOfMonth);
}
diff --git a/core/java/android/nfc/technology/Ndef.java b/core/java/android/nfc/technology/Ndef.java
index cd12249..53db0c5 100644
--- a/core/java/android/nfc/technology/Ndef.java
+++ b/core/java/android/nfc/technology/Ndef.java
@@ -140,6 +140,7 @@
return null;
}
}
+
/**
* Overwrite the primary NDEF message
* @throws IOException
diff --git a/core/java/android/nfc/technology/NdefFormatable.java b/core/java/android/nfc/technology/NdefFormatable.java
index 899b95f..222c558 100644
--- a/core/java/android/nfc/technology/NdefFormatable.java
+++ b/core/java/android/nfc/technology/NdefFormatable.java
@@ -45,16 +45,6 @@
}
/**
- * Returns whether a tag can be formatted with {@link
- * NdefFormatable#format(NdefMessage)}
- */
- public boolean canBeFormatted() throws IOException {
- checkConnected();
-
- throw new UnsupportedOperationException();
- }
-
- /**
* Formats a tag as NDEF, if possible. You may supply a first
* NdefMessage to be written on the tag.
*/
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 55d3b16..a4ad97b 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5830,6 +5830,7 @@
mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
invalidate();
}
+ mBackgroundSizeChanged = true;
}
}
@@ -5882,6 +5883,7 @@
mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
invalidate();
}
+ mBackgroundSizeChanged = true;
}
}
@@ -5937,7 +5939,7 @@
mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
invalidate();
}
-
+ mBackgroundSizeChanged = true;
}
}
@@ -5990,6 +5992,7 @@
mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
invalidate();
}
+ mBackgroundSizeChanged = true;
}
}
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index e98c0bd..668490d 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -104,6 +104,14 @@
}
};
+ // mini-month day-picker
+ mMiniMonthDayPicker = (DayPicker) findViewById(R.id.mini_month_day_picker);
+ mMiniMonthDayPicker.setOnDateChangeListener(new DayPicker.OnSelectedDayChangeListener() {
+ public void onSelectedDayChange(DayPicker view, int year, int month, int monthDay) {
+ updateDateUnchecked(year, month, monthDay);
+ }
+ });
+
// day
mDayPicker = (NumberPicker) findViewById(R.id.day);
mDayPicker.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER);
@@ -121,24 +129,11 @@
mYearPicker.setOnLongPressUpdateInterval(100);
mYearPicker.setOnChangeListener(onChangeListener);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DatePicker);
- int mStartYear = a.getInt(R.styleable.DatePicker_startYear, DEFAULT_START_YEAR);
- int mEndYear = a.getInt(R.styleable.DatePicker_endYear, DEFAULT_END_YEAR);
- mYearPicker.setRange(mStartYear, mEndYear);
+ int startYear = a.getInt(R.styleable.DatePicker_startYear, DEFAULT_START_YEAR);
+ int endYear = a.getInt(R.styleable.DatePicker_endYear, DEFAULT_END_YEAR);
+ setRange(startYear, endYear);
a.recycle();
- // mini-month day-picker
- mMiniMonthDayPicker = (DayPicker) findViewById(R.id.mini_month_day_picker);
- mTempCalendar.clear();
- mTempCalendar.set(mStartYear, 0, 1);
- Calendar endRangeDate = (Calendar) mTempCalendar.clone();
- endRangeDate.set(mEndYear, 11, 31);
- mMiniMonthDayPicker.setRange(mTempCalendar, endRangeDate);
- mMiniMonthDayPicker.setOnDateChangeListener(new DayPicker.OnSelectedDayChangeListener() {
- public void onSelectedDayChange(DayPicker view, int year, int month, int monthDay) {
- updateDateUnchecked(year, month, monthDay);
- }
- });
-
// initialize to current date
mTempCalendar.setTimeInMillis(System.currentTimeMillis());
init(mTempCalendar.get(Calendar.YEAR), mTempCalendar.get(Calendar.MONTH),
@@ -148,6 +143,40 @@
reorderPickers();
}
+ /**
+ * Sets the range of years in which dates can be selected.
+ * <p>
+ * Note: If the range is set to a value that does not include the currently
+ * selected date the value of this picker will be updated to the closest
+ * date in the range.
+ * </p>
+ *
+ * @param startYear The start year of the range.
+ * @param endYear The end year of the range.
+ */
+ public void setRange(int startYear, int endYear) {
+ // set ranges of the widgets
+ mYearPicker.setRange(startYear, endYear);
+ mTempCalendar.clear();
+ Calendar startRangeDate = (Calendar) mTempCalendar.clone();
+ startRangeDate.set(startYear, 0, 1);
+ Calendar endRangeDate = (Calendar) mTempCalendar.clone();
+ endRangeDate.set(endYear, 11, 31);
+ mMiniMonthDayPicker.setRange(startRangeDate, endRangeDate);
+
+ // update state if current date is outside of the range
+ mTempCalendar.set(Calendar.YEAR, getYear());
+ mTempCalendar.set(Calendar.MONTH, getMonth());
+ mTempCalendar.set(Calendar.DAY_OF_MONTH, getDayOfMonth());
+ if (mTempCalendar.before(startRangeDate)) {
+ updateDate(startRangeDate.get(Calendar.YEAR), startRangeDate.get(Calendar.MONTH),
+ startRangeDate.get(Calendar.DAY_OF_MONTH));
+ } else if (mTempCalendar.after(endRangeDate)) {
+ updateDate(endRangeDate.get(Calendar.YEAR), endRangeDate.get(Calendar.MONTH),
+ endRangeDate.get(Calendar.DAY_OF_MONTH));
+ }
+ }
+
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
diff --git a/core/java/android/widget/DayPicker.java b/core/java/android/widget/DayPicker.java
index cdf51f7..02805be 100644
--- a/core/java/android/widget/DayPicker.java
+++ b/core/java/android/widget/DayPicker.java
@@ -366,22 +366,38 @@
* @param endRangeDate The end date.
*/
public void setRange(Calendar startRangeDate, Calendar endRangeDate) {
- boolean doSetupAdapter = false;
+ boolean rangeChanged = false;
if (mRangeStartDate.get(Calendar.DAY_OF_YEAR) != startRangeDate.get(Calendar.DAY_OF_YEAR)
|| mRangeStartDate.get(Calendar.YEAR) != startRangeDate.get(Calendar.YEAR)) {
mRangeStartDate.setTimeInMillis(startRangeDate.getTimeInMillis());
mRangeStartDate.setTimeZone(startRangeDate.getTimeZone());
- doSetupAdapter = true;
+ rangeChanged = true;
}
if (mRangeEndDate.get(Calendar.DAY_OF_YEAR) != endRangeDate.get(Calendar.DAY_OF_YEAR)
|| mRangeEndDate.get(Calendar.YEAR) != endRangeDate.get(Calendar.YEAR)) {
mRangeEndDate.setTimeInMillis(endRangeDate.getTimeInMillis());
mRangeEndDate.setTimeZone(endRangeDate.getTimeZone());
- doSetupAdapter = true;
-
+ rangeChanged = true;
}
- if (doSetupAdapter) {
- setUpAdapter();
+
+ if (!rangeChanged) {
+ return;
+ }
+
+ // now recreate the adapter since we have a new range to handle
+ mAdapter = null;
+ setUpAdapter();
+
+ // set the current date to today if in the range
+ // otherwise to the closest end of the range
+ mTempCalendar.clear();
+ mTempCalendar.setTimeInMillis(System.currentTimeMillis());
+ if (mTempCalendar.before(mRangeStartDate)) {
+ goTo(mRangeStartDate, false, true, true);
+ } else if (mTempCalendar.after(mRangeEndDate)) {
+ goTo(mRangeEndDate, false, true, true);
+ } else {
+ goTo(mTempCalendar, false, true, true);
}
}
@@ -629,7 +645,6 @@
}
// Figure out where we are
- int offset = child.getBottom() < mWeekMinVisibleHeight ? 1 : 0;
long currScroll = view.getFirstVisiblePosition() * child.getHeight() - child.getBottom();
// If we have moved since our last call update the direction
@@ -645,6 +660,7 @@
// causes the month to transition when two full weeks of a month are
// visible when scrolling up, and when the first day in a month reaches
// the top of the screen when scrolling down.
+ int offset = child.getBottom() < mWeekMinVisibleHeight ? 1 : 0;
if (mIsScrollingUp) {
child = (WeekView) view.getChildAt(SCROLL_HYST_WEEKS + offset);
} else if (offset != 0) {
@@ -712,8 +728,9 @@
throw new IllegalArgumentException("fromDate: " + mRangeStartDate.getTime()
+ " does not precede toDate: " + toDate.getTime());
}
+
int fromDateDayOfWeek = mRangeStartDate.get(Calendar.DAY_OF_WEEK);
- long diff = (fromDateDayOfWeek - toDate.getFirstDayOfWeek()) * MILLIS_IN_DAY;
+ long diff = (fromDateDayOfWeek - mFirstDayOfWeek) * MILLIS_IN_DAY;
if (diff < 0) {
diff = diff + MILLIS_IN_WEEK;
}
@@ -874,7 +891,16 @@
protected void init() {
mGestureDetector = new GestureDetector(mContext, new CalendarGestureListener());
mSelectedWeek = getWeeksDelta(mSelectedDay);
- mTotalWeekCount = getWeeksDelta(mRangeEndDate);
+
+ // make adjustment to fit the range last week with needed overflow
+ mTempCalendar.setTimeInMillis(mRangeEndDate.getTimeInMillis());
+ mTempCalendar.setTimeZone(mRangeEndDate.getTimeZone());
+ int diff = mFirstDayOfWeek - mRangeEndDate.get(Calendar.DAY_OF_WEEK);
+ if (diff < 0) {
+ diff += DAYS_PER_WEEK;
+ }
+ mTempCalendar.add(Calendar.DAY_OF_WEEK, diff);
+ mTotalWeekCount = getWeeksDelta(mTempCalendar);
}
/**
@@ -892,7 +918,6 @@
mSelectedWeek = getWeeksDelta(mSelectedDay);
mFocusMonth = mSelectedDay.get(Calendar.MONTH);
notifyDataSetChanged();
- invalidate(); // Test
}
/**
@@ -1004,9 +1029,12 @@
if (mGestureDetector.onTouchEvent(event)) {
WeekView weekView = (WeekView) v;
weekView.getDayFromLocation(event.getX(), mTempCalendar);
- if (mTempCalendar.get(Calendar.YEAR) != 0) {
- onDayTapped(mTempCalendar);
+ // it is possible that the touched day is outside the valid range
+ // we draw whole weeks but range end can fall not on the week end
+ if (mTempCalendar.before(mRangeStartDate) || mTempCalendar.after(mRangeEndDate)) {
+ return true;
}
+ onDayTapped(mTempCalendar);
return true;
}
return false;
@@ -1019,6 +1047,7 @@
*/
protected void onDayTapped(Calendar day) {
setSelectedDay(day);
+ setMonthDisplayed(day);
}
/**
@@ -1244,8 +1273,8 @@
mNumCells = mShowWeekNumber ? mWeekDayCount + 1 : mWeekDayCount;
}
mWeek = ((int[]) params.get(VIEW_PARAMS_WEEK))[0];
- mTempCalendar.clear();
- mTempCalendar.set(1900, 0, 1);
+ mTempCalendar.setTimeInMillis(mRangeStartDate.getTimeInMillis());
+ mTempCalendar.setTimeZone(mRangeStartDate.getTimeZone());
mTempCalendar.add(Calendar.WEEK_OF_YEAR, mWeek);
if (params.containsKey(VIEW_PARAMS_WEEK_START)) {
mTempCalendar.setFirstDayOfWeek(((int[]) params.get(VIEW_PARAMS_WEEK_START))[0]);
@@ -1277,7 +1306,12 @@
for (; i < mNumCells; i++) {
mFocusDay[i] = (mTempCalendar.get(Calendar.MONTH) == focusMonth);
- mDayNumbers[i] = Integer.toString(mTempCalendar.get(Calendar.DAY_OF_MONTH));
+ // do not draw dates outside the valid range to avoid user confusion
+ if (mTempCalendar.before(mRangeStartDate) || mTempCalendar.after(mRangeEndDate)) {
+ mDayNumbers[i] = "";
+ } else {
+ mDayNumbers[i] = Integer.toString(mTempCalendar.get(Calendar.DAY_OF_MONTH));
+ }
mTempCalendar.add(Calendar.DAY_OF_MONTH, 1);
}
// We do one extra add at the end of the loop, if that pushed us to
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 8c22f97..a236d27 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -1189,9 +1189,9 @@
*/
private int getWrappedSelectorIndex(int selectorIndex) {
if (selectorIndex > mEnd) {
- return mStart + (selectorIndex - mEnd) % (mEnd - mStart);
+ return mStart + (selectorIndex - mEnd) % (mEnd - mStart) - 1;
} else if (selectorIndex < mStart) {
- return mEnd - (mStart - selectorIndex) % (mEnd - mStart);
+ return mEnd - (mStart - selectorIndex) % (mEnd - mStart) + 1;
}
return selectorIndex;
}
diff --git a/core/res/assets/webkit/youtube.html b/core/res/assets/webkit/youtube.html
index 289f8cf..d808bcf 100644
--- a/core/res/assets/webkit/youtube.html
+++ b/core/res/assets/webkit/youtube.html
@@ -38,19 +38,29 @@
// All images are loaded, so display them.
// (Note that the images are loaded from javascript, so might load
// after document.onload fires)
- ctx.drawImage(background, 0, 0, width, height);
+
playWidth = play.width;
playHeight = play.height;
logoWidth = logo.width;
logoHeight = logo.height;
var ratio = 1;
// If the page is smaller than it 'should' be in either dimension
- // we scale the play button and logo according to the dimension that
- // has been shrunk the most.
+ // we scale the background, play button and logo according to the
+ // dimension that has been shrunk the most.
if (width / height > defWidth / defHeight && height < defHeight) {
ratio = height / defHeight;
+ // Stretch the background in this dimension only.
+ backgroundHeight = background.height / ratio;
+ ctx.drawImage(background, 0, 0, background.width, background.height,
+ 0, (height - backgroundHeight) / 2, width, backgroundHeight);
} else if (width / height < defWidth / defHeight && width < defWidth) {
ratio = width / defWidth;
+ backgroundWidth = background.width / ratio;
+ ctx.drawImage(background, 0, 0, background.width, background.height,
+ (width - backgroundWidth) / 2, 0, backgroundWidth, height);
+ } else {
+ // In this case stretch the background in both dimensions to fill the space.
+ ctx.drawImage(background, 0, 0, width, height);
}
playWidth *= ratio;
playHeight *= ratio;
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index 9662817..4ccdd9a 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -22,6 +22,20 @@
#include <utils/Log.h>
#include <utils/threads.h>
#include <core/SkBitmap.h>
+
+// Please do not enable "USE_PRIVATE_NATIVE_BITMAP_CONSTUCTOR"
+// This mode will be removed and it is kept here for
+// convenient comparsion just in case. Will be removed soon.
+// Tests show that this mode is also ~30ms slower,
+// when rotation is involved.
+#define USE_PRIVATE_NATIVE_BITMAP_CONSTUCTOR 0
+
+#if (!USE_PRIVATE_NATIVE_BITMAP_CONSTUCTOR)
+#include <core/SkCanvas.h>
+#include <core/SkDevice.h>
+#include <core/SkScalar.h>
+#endif
+
#include <media/mediametadataretriever.h>
#include <private/media/VideoFrame.h>
@@ -35,8 +49,15 @@
struct fields_t {
jfieldID context;
jclass bitmapClazz;
+#if USE_PRIVATE_NATIVE_BITMAP_CONSTUCTOR
jmethodID bitmapConstructor;
+ jmethodID createBitmapRotationMethod;
+#else
+ jfieldID nativeBitmap;
jmethodID createBitmapMethod;
+ jclass configClazz;
+ jmethodID createConfigMethod;
+#endif
};
static fields_t fields;
@@ -162,6 +183,12 @@
return NULL;
}
+ LOGV("Dimension = %dx%d and bytes = %d",
+ videoFrame->mDisplayWidth,
+ videoFrame->mDisplayHeight,
+ videoFrame->mSize);
+
+#if USE_PRIVATE_NATIVE_BITMAP_CONSTUCTOR
jobject matrix = NULL;
if (videoFrame->mRotationAngle != 0) {
LOGD("Create a rotation matrix: %d degrees", videoFrame->mRotationAngle);
@@ -217,9 +244,9 @@
jobject jSrcBitmap = env->NewObject(fields.bitmapClazz,
fields.bitmapConstructor, (int) bitmap, NULL, true, NULL, -1);
- LOGV("Return a new bitmap constructed with the rotation matrix");
- return env->CallStaticObjectMethod(
- fields.bitmapClazz, fields.createBitmapMethod,
+ jobject jBitmap = env->CallStaticObjectMethod(
+ fields.bitmapClazz,
+ fields.createBitmapRotationMethod,
jSrcBitmap, // source Bitmap
0, // x
0, // y
@@ -227,6 +254,39 @@
videoFrame->mDisplayHeight, // height
matrix, // transform matrix
false); // filter
+
+#else
+
+ jobject config = env->CallStaticObjectMethod(
+ fields.configClazz,
+ fields.createConfigMethod,
+ SkBitmap::kRGB_565_Config);
+
+ jobject jBitmap = env->CallStaticObjectMethod(
+ fields.bitmapClazz,
+ fields.createBitmapMethod,
+ videoFrame->mDisplayWidth,
+ videoFrame->mDisplayHeight,
+ config);
+ SkBitmap *bitmap =
+ (SkBitmap *) env->GetIntField(jBitmap, fields.nativeBitmap);
+
+ bitmap->lockPixels();
+
+ memcpy((uint8_t*)bitmap->getPixels(),
+ (uint8_t*)videoFrame + sizeof(VideoFrame), videoFrame->mSize);
+
+ bitmap->unlockPixels();
+
+ if (videoFrame->mRotationAngle != 0) {
+ SkDevice device(*bitmap);
+ SkCanvas canvas(&device);
+ canvas.rotate((SkScalar) (videoFrame->mRotationAngle * 1.0));
+ canvas.drawBitmap(*bitmap, 0, 0);
+ }
+#endif
+ LOGV("Return a new bitmap constructed with the rotation matrix");
+ return jBitmap;
}
static jbyteArray android_media_MediaMetadataRetriever_extractAlbumArt(JNIEnv *env, jobject thiz)
@@ -293,7 +353,6 @@
static void android_media_MediaMetadataRetriever_native_finalize(JNIEnv *env, jobject thiz)
{
LOGV("native_finalize");
-
// No lock is needed, since android_media_MediaMetadataRetriever_release() is protected
android_media_MediaMetadataRetriever_release(env, thiz);
}
@@ -320,21 +379,52 @@
jniThrowException(env, "java/lang/RuntimeException", "Can't find android/graphics/Bitmap");
return;
}
-
+#if USE_PRIVATE_NATIVE_BITMAP_CONSTUCTOR
fields.bitmapConstructor = env->GetMethodID(fields.bitmapClazz, "<init>", "(I[BZ[BI)V");
if (fields.bitmapConstructor == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Can't find Bitmap constructor");
return;
}
- fields.createBitmapMethod =
+ fields.createBitmapRotationMethod =
env->GetStaticMethodID(fields.bitmapClazz, "createBitmap",
"(Landroid/graphics/Bitmap;IIIILandroid/graphics/Matrix;Z)"
"Landroid/graphics/Bitmap;");
- if (fields.createBitmapMethod == NULL) {
+ if (fields.createBitmapRotationMethod == NULL) {
jniThrowException(env, "java/lang/RuntimeException",
"Can't find Bitmap.createBitmap method");
return;
}
+#else
+ fields.createBitmapMethod =
+ env->GetStaticMethodID(fields.bitmapClazz, "createBitmap",
+ "(IILandroid/graphics/Bitmap$Config;)"
+ "Landroid/graphics/Bitmap;");
+ if (fields.createBitmapMethod == NULL) {
+ jniThrowException(env, "java/lang/RuntimeException",
+ "Can't find Bitmap.createBitmap(int, int, Config) method");
+ return;
+ }
+ fields.nativeBitmap = env->GetFieldID(fields.bitmapClazz, "mNativeBitmap", "I");
+ if (fields.nativeBitmap == NULL) {
+ jniThrowException(env, "java/lang/RuntimeException",
+ "Can't find Bitmap.mNativeBitmap field");
+ }
+
+ fields.configClazz = env->FindClass("android/graphics/Bitmap$Config");
+ if (fields.configClazz == NULL) {
+ jniThrowException(env, "java/lang/RuntimeException",
+ "Can't find Bitmap$Config class");
+ return;
+ }
+ fields.createConfigMethod =
+ env->GetStaticMethodID(fields.configClazz, "nativeToConfig",
+ "(I)Landroid/graphics/Bitmap$Config;");
+ if (fields.createConfigMethod == NULL) {
+ jniThrowException(env, "java/lang/RuntimeException",
+ "Can't find Bitmap$Config.nativeToConfig(int) method");
+ return;
+ }
+#endif
}
static void android_media_MediaMetadataRetriever_native_setup(JNIEnv *env, jobject thiz)
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index d3dab13..967fa49 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -32,8 +32,6 @@
#include <media/stagefright/MetaData.h>
#include <surfaceflinger/Surface.h>
-#define SHUTDOWN_ON_DISCONTINUITY 0
-
namespace android {
////////////////////////////////////////////////////////////////////////////////
@@ -78,6 +76,26 @@
(new AMessage(kWhatStart, id()))->post();
}
+// static
+bool NuPlayer::IsFlushingState(FlushStatus state, bool *formatChange) {
+ switch (state) {
+ case FLUSHING_DECODER:
+ if (formatChange != NULL) {
+ *formatChange = false;
+ }
+ return true;
+
+ case FLUSHING_DECODER_FORMATCHANGE:
+ if (formatChange != NULL) {
+ *formatChange = true;
+ }
+ return true;
+
+ default:
+ return false;
+ }
+}
+
void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatSetDataSource:
@@ -181,28 +199,30 @@
} else if (what == ACodec::kWhatEOS) {
mRenderer->queueEOS(audio, ERROR_END_OF_STREAM);
} else if (what == ACodec::kWhatFlushCompleted) {
+ bool formatChange;
+
if (audio) {
- CHECK_EQ((int)mFlushingAudio, (int)FLUSHING_DECODER);
+ CHECK(IsFlushingState(mFlushingAudio, &formatChange));
mFlushingAudio = FLUSHED;
} else {
- CHECK_EQ((int)mFlushingVideo, (int)FLUSHING_DECODER);
+ CHECK(IsFlushingState(mFlushingVideo, &formatChange));
mFlushingVideo = FLUSHED;
}
LOGI("decoder %s flush completed", audio ? "audio" : "video");
-#if SHUTDOWN_ON_DISCONTINUITY
- LOGI("initiating %s decoder shutdown",
- audio ? "audio" : "video");
+ if (formatChange) {
+ LOGI("initiating %s decoder shutdown",
+ audio ? "audio" : "video");
- (audio ? mAudioDecoder : mVideoDecoder)->initiateShutdown();
+ (audio ? mAudioDecoder : mVideoDecoder)->initiateShutdown();
- if (audio) {
- mFlushingAudio = SHUTTING_DOWN_DECODER;
- } else {
- mFlushingVideo = SHUTTING_DOWN_DECODER;
+ if (audio) {
+ mFlushingAudio = SHUTTING_DOWN_DECODER;
+ } else {
+ mFlushingVideo = SHUTTING_DOWN_DECODER;
+ }
}
-#endif
finishFlushIfPossible();
} else if (what == ACodec::kWhatOutputFormatChanged) {
@@ -451,8 +471,8 @@
sp<AMessage> reply;
CHECK(msg->findMessage("reply", &reply));
- if ((audio && mFlushingAudio == FLUSHING_DECODER)
- || (!audio && mFlushingVideo == FLUSHING_DECODER)) {
+ if ((audio && IsFlushingState(mFlushingAudio))
+ || (!audio && IsFlushingState(mFlushingVideo))) {
reply->setInt32("err", INFO_DISCONTINUITY);
reply->post();
return OK;
@@ -467,14 +487,25 @@
return err;
} else if (err != OK) {
if (err == INFO_DISCONTINUITY) {
- LOGI("%s discontinuity", audio ? "audio" : "video");
+ int32_t formatChange;
+ if (!accessUnit->meta()->findInt32(
+ "format-change", &formatChange)) {
+ formatChange = 0;
+ }
+
+ LOGI("%s discontinuity (formatChange=%d)",
+ audio ? "audio" : "video", formatChange);
+
(audio ? mAudioDecoder : mVideoDecoder)->signalFlush();
mRenderer->flush(audio);
if (audio) {
CHECK(mFlushingAudio == NONE
|| mFlushingAudio == AWAITING_DISCONTINUITY);
- mFlushingAudio = FLUSHING_DECODER;
+
+ mFlushingAudio = formatChange
+ ? FLUSHING_DECODER_FORMATCHANGE : FLUSHING_DECODER;
+
if (mFlushingVideo == NONE) {
mFlushingVideo = (mVideoDecoder != NULL)
? AWAITING_DISCONTINUITY
@@ -483,7 +514,10 @@
} else {
CHECK(mFlushingVideo == NONE
|| mFlushingVideo == AWAITING_DISCONTINUITY);
- mFlushingVideo = FLUSHING_DECODER;
+
+ mFlushingVideo = formatChange
+ ? FLUSHING_DECODER_FORMATCHANGE : FLUSHING_DECODER;
+
if (mFlushingAudio == NONE) {
mFlushingAudio = (mAudioDecoder != NULL)
? AWAITING_DISCONTINUITY
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index fad1ce1..d4e7428 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -79,6 +79,7 @@
NONE,
AWAITING_DISCONTINUITY,
FLUSHING_DECODER,
+ FLUSHING_DECODER_FORMATCHANGE,
SHUTTING_DOWN_DECODER,
FLUSHED,
SHUT_DOWN,
@@ -104,6 +105,8 @@
void finishFlushIfPossible();
+ static bool IsFlushingState(FlushStatus state, bool *formatChange = NULL);
+
DISALLOW_EVIL_CONSTRUCTORS(NuPlayer);
};
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index afacb2e..ee9b5739 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -334,7 +334,7 @@
if (mStreamType == 0x1b && mSource != NULL) {
// Don't signal discontinuities on audio streams.
- mSource->queueDiscontinuity();
+ mSource->queueDiscontinuity(true /* formatChange */);
}
break;
}
@@ -348,7 +348,7 @@
if (mSource != NULL) {
mSource->clear();
- mSource->queueDiscontinuity();
+ mSource->queueDiscontinuity(!isASeek);
}
break;
}
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index 7a1d5b0..a8fe2c1 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -63,8 +63,6 @@
int32_t discontinuity;
if ((*buffer)->meta()->findInt32("discontinuity", &discontinuity)
&& discontinuity) {
- buffer->clear();
-
return INFO_DISCONTINUITY;
}
@@ -125,10 +123,14 @@
mCondition.signal();
}
-void AnotherPacketSource::queueDiscontinuity() {
+void AnotherPacketSource::queueDiscontinuity(bool formatChange) {
sp<ABuffer> buffer = new ABuffer(0);
buffer->meta()->setInt32("discontinuity", true);
+ if (formatChange) {
+ buffer->meta()->setInt32("format-change", true);
+ }
+
Mutex::Autolock autoLock(mLock);
mBuffers.push_back(buffer);
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
index 2bc7404..f25a067 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.h
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
@@ -42,7 +42,7 @@
status_t nextBufferTime(int64_t *timeUs);
void queueAccessUnit(const sp<ABuffer> &buffer);
- void queueDiscontinuity();
+ void queueDiscontinuity(bool formatChange);
void signalEOS(status_t result);
void clear();
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index f82a243..3e2eac2 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -1097,6 +1097,7 @@
// gotten a positive report we don't want to overwrite, but if not we need to
// clear this now to turn our cellular sig strength white
mDefaultInetConditionPublished = 0;
+ intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
}
intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, switchTo);
} else {
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 748f2e9..94ed813 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -6814,6 +6814,11 @@
// Input channel
InputChannel mInputChannel;
+ // Used to improve performance of toString()
+ String mStringNameCache;
+ CharSequence mLastTitle;
+ boolean mWasPaused;
+
WindowState(Session s, IWindow c, WindowToken token,
WindowState attachedWindow, WindowManager.LayoutParams a,
int viewVisibility) {
@@ -8131,9 +8136,14 @@
@Override
public String toString() {
- return "Window{"
- + Integer.toHexString(System.identityHashCode(this))
- + " " + mAttrs.getTitle() + " paused=" + mToken.paused + "}";
+ if (mStringNameCache == null || mLastTitle != mAttrs.getTitle()
+ || mWasPaused != mToken.paused) {
+ mLastTitle = mAttrs.getTitle();
+ mWasPaused = mToken.paused;
+ mStringNameCache = "Window{" + Integer.toHexString(System.identityHashCode(this))
+ + " " + mLastTitle + " paused=" + mWasPaused + "}";
+ }
+ return mStringNameCache;
}
}
diff --git a/voip/java/android/net/sip/SipAudioCall.java b/voip/java/android/net/sip/SipAudioCall.java
index 51236fe9..ce18ec5 100644
--- a/voip/java/android/net/sip/SipAudioCall.java
+++ b/voip/java/android/net/sip/SipAudioCall.java
@@ -519,10 +519,15 @@
* @param session the session that receives the incoming call
* @param sessionDescription the session description of the incoming call
* @throws SipException if the SIP service fails to attach this object to
- * the session
+ * the session or VOIP API is not supported by the device
+ * @see SipManager#isVoipSupported
*/
public void attachCall(SipSession session, String sessionDescription)
throws SipException {
+ if (!SipManager.isVoipSupported(mContext)) {
+ throw new SipException("VOIP API is not supported");
+ }
+
synchronized (this) {
mSipSession = session;
mPeerSd = sessionDescription;
@@ -548,10 +553,15 @@
* SIP protocol) is used if {@code timeout} is zero or negative.
* @see Listener#onError
* @throws SipException if the SIP service fails to create a session for the
- * call
+ * call or VOIP API is not supported by the device
+ * @see SipManager#isVoipSupported
*/
public void makeCall(SipProfile peerProfile, SipSession sipSession,
int timeout) throws SipException {
+ if (!SipManager.isVoipSupported(mContext)) {
+ throw new SipException("VOIP API is not supported");
+ }
+
synchronized (this) {
mSipSession = sipSession;
try {
@@ -595,6 +605,9 @@
public void holdCall(int timeout) throws SipException {
synchronized (this) {
if (mHold) return;
+ if (mSipSession == null) {
+ throw new SipException("Not in a call to hold call");
+ }
mSipSession.changeCall(createHoldOffer().encode(), timeout);
mHold = true;
setAudioGroupMode();
@@ -614,6 +627,9 @@
*/
public void answerCall(int timeout) throws SipException {
synchronized (this) {
+ if (mSipSession == null) {
+ throw new SipException("No call to answer");
+ }
try {
mAudioStream = new AudioStream(InetAddress.getByName(
getLocalIp()));
diff --git a/voip/java/android/net/sip/SipManager.java b/voip/java/android/net/sip/SipManager.java
index 2e386620..dce46fe 100644
--- a/voip/java/android/net/sip/SipManager.java
+++ b/voip/java/android/net/sip/SipManager.java
@@ -133,7 +133,7 @@
}
/**
- * Returns true if the system supports SIP-based VoIP.
+ * Returns true if the system supports SIP-based VOIP API.
*/
public static boolean isVoipSupported(Context context) {
return context.getPackageManager().hasSystemFeature(
@@ -305,12 +305,17 @@
* @param timeout the timeout value in seconds. Default value (defined by
* SIP protocol) is used if {@code timeout} is zero or negative.
* @return a {@link SipAudioCall} object
- * @throws SipException if calling the SIP service results in an error
+ * @throws SipException if calling the SIP service results in an error or
+ * VOIP API is not supported by the device
* @see SipAudioCall.Listener#onError
+ * @see #isVoipSupported
*/
public SipAudioCall makeAudioCall(SipProfile localProfile,
SipProfile peerProfile, SipAudioCall.Listener listener, int timeout)
throws SipException {
+ if (!isVoipSupported(mContext)) {
+ throw new SipException("VOIP API is not supported");
+ }
SipAudioCall call = new SipAudioCall(mContext, localProfile);
call.setListener(listener);
SipSession s = createSipSession(localProfile, null);
@@ -332,12 +337,17 @@
* @param timeout the timeout value in seconds. Default value (defined by
* SIP protocol) is used if {@code timeout} is zero or negative.
* @return a {@link SipAudioCall} object
- * @throws SipException if calling the SIP service results in an error
+ * @throws SipException if calling the SIP service results in an error or
+ * VOIP API is not supported by the device
* @see SipAudioCall.Listener#onError
+ * @see #isVoipSupported
*/
public SipAudioCall makeAudioCall(String localProfileUri,
String peerProfileUri, SipAudioCall.Listener listener, int timeout)
throws SipException {
+ if (!isVoipSupported(mContext)) {
+ throw new SipException("VOIP API is not supported");
+ }
try {
return makeAudioCall(
new SipProfile.Builder(localProfileUri).build(),