Merge "Reducing number of threads used in Recents."
diff --git a/api/current.txt b/api/current.txt
index d70317e..f75ef0b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1395,6 +1395,7 @@
field public static final int windowAllowReturnTransitionOverlap = 16843835; // 0x101043b
field public static final int windowAnimationStyle = 16842926; // 0x10100ae
field public static final int windowBackground = 16842836; // 0x1010054
+ field public static final int windowBackgroundFallback = 16844035; // 0x1010503
field public static final int windowClipToOutline = 16843947; // 0x10104ab
field public static final int windowCloseOnTouchOutside = 16843611; // 0x101035b
field public static final int windowContentOverlay = 16842841; // 0x1010059
@@ -1420,7 +1421,6 @@
field public static final int windowNoTitle = 16842838; // 0x1010056
field public static final int windowOverscan = 16843727; // 0x10103cf
field public static final int windowReenterTransition = 16843951; // 0x10104af
- field public static final int windowResizingBackground = 16844035; // 0x1010503
field public static final int windowReturnTransition = 16843950; // 0x10104ae
field public static final int windowSharedElementEnterTransition = 16843833; // 0x1010439
field public static final int windowSharedElementExitTransition = 16843834; // 0x101043a
@@ -25035,6 +25035,7 @@
field public static final int OUTGOING_TYPE = 2; // 0x2
field public static final java.lang.String PHONE_ACCOUNT_COMPONENT_NAME = "subscription_component_name";
field public static final java.lang.String PHONE_ACCOUNT_ID = "subscription_id";
+ field public static final java.lang.String POST_DIAL_DIGITS = "post_dial_digits";
field public static final int PRESENTATION_ALLOWED = 1; // 0x1
field public static final int PRESENTATION_PAYPHONE = 4; // 0x4
field public static final int PRESENTATION_RESTRICTED = 2; // 0x2
diff --git a/api/system-current.txt b/api/system-current.txt
index f44065e..c4c6f1c 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1491,6 +1491,7 @@
field public static final int windowAllowReturnTransitionOverlap = 16843835; // 0x101043b
field public static final int windowAnimationStyle = 16842926; // 0x10100ae
field public static final int windowBackground = 16842836; // 0x1010054
+ field public static final int windowBackgroundFallback = 16844035; // 0x1010503
field public static final int windowClipToOutline = 16843947; // 0x10104ab
field public static final int windowCloseOnTouchOutside = 16843611; // 0x101035b
field public static final int windowContentOverlay = 16842841; // 0x1010059
@@ -1516,7 +1517,6 @@
field public static final int windowNoTitle = 16842838; // 0x1010056
field public static final int windowOverscan = 16843727; // 0x10103cf
field public static final int windowReenterTransition = 16843951; // 0x10104af
- field public static final int windowResizingBackground = 16844035; // 0x1010503
field public static final int windowReturnTransition = 16843950; // 0x10104ae
field public static final int windowSharedElementEnterTransition = 16843833; // 0x1010439
field public static final int windowSharedElementExitTransition = 16843834; // 0x101043a
@@ -26993,6 +26993,7 @@
field public static final int OUTGOING_TYPE = 2; // 0x2
field public static final java.lang.String PHONE_ACCOUNT_COMPONENT_NAME = "subscription_component_name";
field public static final java.lang.String PHONE_ACCOUNT_ID = "subscription_id";
+ field public static final java.lang.String POST_DIAL_DIGITS = "post_dial_digits";
field public static final int PRESENTATION_ALLOWED = 1; // 0x1
field public static final int PRESENTATION_PAYPHONE = 4; // 0x4
field public static final int PRESENTATION_RESTRICTED = 2; // 0x2
diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index dc2aa3a..d1000ad 100644
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -1410,9 +1410,11 @@
}
private void removeMessages() {
- mHandler.removeMessages(MSG_REPEAT);
- mHandler.removeMessages(MSG_LONGPRESS);
- mHandler.removeMessages(MSG_SHOW_PREVIEW);
+ if (mHandler != null) {
+ mHandler.removeMessages(MSG_REPEAT);
+ mHandler.removeMessages(MSG_LONGPRESS);
+ mHandler.removeMessages(MSG_SHOW_PREVIEW);
+ }
}
@Override
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 1273772b..5852f5f 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.IntegerRes;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -42,6 +43,8 @@
import java.util.Map;
import java.util.Set;
+import dalvik.system.VMRuntime;
+
/**
* Container for a message (data and object references) that can
* be sent through an IBinder. A Parcel can contain both flattened data
@@ -193,6 +196,7 @@
* indicating that we're responsible for its lifecycle.
*/
private boolean mOwnsNativeParcelObject;
+ private long mNativeSize;
private RuntimeException mStack;
@@ -244,7 +248,7 @@
private static native int nativeDataAvail(long nativePtr);
private static native int nativeDataPosition(long nativePtr);
private static native int nativeDataCapacity(long nativePtr);
- private static native void nativeSetDataSize(long nativePtr, int size);
+ private static native long nativeSetDataSize(long nativePtr, int size);
private static native void nativeSetDataPosition(long nativePtr, int pos);
private static native void nativeSetDataCapacity(long nativePtr, int size);
@@ -259,7 +263,7 @@
private static native void nativeWriteDouble(long nativePtr, double val);
private static native void nativeWriteString(long nativePtr, String val);
private static native void nativeWriteStrongBinder(long nativePtr, IBinder val);
- private static native void nativeWriteFileDescriptor(long nativePtr, FileDescriptor val);
+ private static native long nativeWriteFileDescriptor(long nativePtr, FileDescriptor val);
private static native byte[] nativeCreateByteArray(long nativePtr);
private static native byte[] nativeReadBlob(long nativePtr);
@@ -272,13 +276,13 @@
private static native FileDescriptor nativeReadFileDescriptor(long nativePtr);
private static native long nativeCreate();
- private static native void nativeFreeBuffer(long nativePtr);
+ private static native long nativeFreeBuffer(long nativePtr);
private static native void nativeDestroy(long nativePtr);
private static native byte[] nativeMarshall(long nativePtr);
- private static native void nativeUnmarshall(
+ private static native long nativeUnmarshall(
long nativePtr, byte[] data, int offset, int length);
- private static native void nativeAppendFrom(
+ private static native long nativeAppendFrom(
long thisNativePtr, long otherNativePtr, int offset, int length);
private static native boolean nativeHasFileDescriptors(long nativePtr);
private static native void nativeWriteInterfaceToken(long nativePtr, String interfaceName);
@@ -390,7 +394,7 @@
* @param size The new number of bytes in the Parcel.
*/
public final void setDataSize(int size) {
- nativeSetDataSize(mNativePtr, size);
+ updateNativeSize(nativeSetDataSize(mNativePtr, size));
}
/**
@@ -442,11 +446,11 @@
* Set the bytes in data to be the raw bytes of this Parcel.
*/
public final void unmarshall(byte[] data, int offset, int length) {
- nativeUnmarshall(mNativePtr, data, offset, length);
+ updateNativeSize(nativeUnmarshall(mNativePtr, data, offset, length));
}
public final void appendFrom(Parcel parcel, int offset, int length) {
- nativeAppendFrom(mNativePtr, parcel.mNativePtr, offset, length);
+ updateNativeSize(nativeAppendFrom(mNativePtr, parcel.mNativePtr, offset, length));
}
/**
@@ -599,7 +603,24 @@
* if {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE} is set.</p>
*/
public final void writeFileDescriptor(FileDescriptor val) {
- nativeWriteFileDescriptor(mNativePtr, val);
+ updateNativeSize(nativeWriteFileDescriptor(mNativePtr, val));
+ }
+
+ private void updateNativeSize(long newNativeSize) {
+ if (mOwnsNativeParcelObject) {
+ if (newNativeSize > Integer.MAX_VALUE) {
+ newNativeSize = Integer.MAX_VALUE;
+ }
+ if (newNativeSize != mNativeSize) {
+ int delta = (int) (newNativeSize - mNativeSize);
+ if (delta > 0) {
+ VMRuntime.getRuntime().registerNativeAllocation(delta);
+ } else {
+ VMRuntime.getRuntime().registerNativeFree(-delta);
+ }
+ mNativeSize = newNativeSize;
+ }
+ }
}
/**
@@ -2545,7 +2566,7 @@
private void freeBuffer() {
if (mOwnsNativeParcelObject) {
- nativeFreeBuffer(mNativePtr);
+ updateNativeSize(nativeFreeBuffer(mNativePtr));
}
}
@@ -2553,6 +2574,7 @@
if (mNativePtr != 0) {
if (mOwnsNativeParcelObject) {
nativeDestroy(mNativePtr);
+ updateNativeSize(0);
}
mNativePtr = 0;
}
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 342f8c7..4b63c36 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -38,7 +38,6 @@
import android.telecom.TelecomManager;
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
-import android.util.Log;
import com.android.internal.telephony.CallerInfo;
import com.android.internal.telephony.PhoneConstants;
@@ -406,6 +405,14 @@
public static final String SUB_ID = "sub_id";
/**
+ * The post-dial portion of a dialed number, including any digits dialed after a
+ * {@link TelecomManager#DTMF_CHARACTER_PAUSE} or a {@link
+ * TelecomManager#DTMF_CHARACTER_WAIT} and these characters themselves.
+ * <P>Type: TEXT</P>
+ */
+ public static final String POST_DIAL_DIGITS = "post_dial_digits";
+
+ /**
* If a successful call is made that is longer than this duration, update the phone number
* in the ContactsProvider with the normalized version of the number, based on the user's
* current country code.
@@ -436,7 +443,7 @@
public static Uri addCall(CallerInfo ci, Context context, String number,
int presentation, int callType, int features, PhoneAccountHandle accountHandle,
long start, int duration, Long dataUsage) {
- return addCall(ci, context, number, presentation, callType, features, accountHandle,
+ return addCall(ci, context, number, "", presentation, callType, features, accountHandle,
start, duration, dataUsage, false, false);
}
@@ -466,10 +473,11 @@
* {@hide}
*/
public static Uri addCall(CallerInfo ci, Context context, String number,
- int presentation, int callType, int features, PhoneAccountHandle accountHandle,
- long start, int duration, Long dataUsage, boolean addForAllUsers) {
- return addCall(ci, context, number, presentation, callType, features, accountHandle,
- start, duration, dataUsage, addForAllUsers, false);
+ String postDialDigits, int presentation, int callType, int features,
+ PhoneAccountHandle accountHandle, long start, int duration, Long dataUsage,
+ boolean addForAllUsers) {
+ return addCall(ci, context, number, postDialDigits, presentation, callType, features,
+ accountHandle, start, duration, dataUsage, addForAllUsers, false);
}
/**
@@ -479,6 +487,8 @@
* if the contact is unknown.
* @param context the context used to get the ContentResolver
* @param number the phone number to be added to the calls db
+ * @param postDialDigits the post-dial digits that were dialed after the number,
+ * if it was outgoing. Otherwise it is ''.
* @param presentation enum value from PhoneConstants.PRESENTATION_xxx, which
* is set by the network and denotes the number presenting rules for
* "allowed", "payphone", "restricted" or "unknown"
@@ -499,8 +509,9 @@
* {@hide}
*/
public static Uri addCall(CallerInfo ci, Context context, String number,
- int presentation, int callType, int features, PhoneAccountHandle accountHandle,
- long start, int duration, Long dataUsage, boolean addForAllUsers, boolean is_read) {
+ String postDialDigits, int presentation, int callType, int features,
+ PhoneAccountHandle accountHandle, long start, int duration, Long dataUsage,
+ boolean addForAllUsers, boolean is_read) {
final ContentResolver resolver = context.getContentResolver();
int numberPresentation = PRESENTATION_ALLOWED;
@@ -551,6 +562,7 @@
ContentValues values = new ContentValues(6);
values.put(NUMBER, number);
+ values.put(POST_DIAL_DIGITS, postDialDigits);
values.put(NUMBER_PRESENTATION, Integer.valueOf(numberPresentation));
values.put(TYPE, Integer.valueOf(callType));
values.put(FEATURES, features);
diff --git a/core/java/android/util/LocaleList.java b/core/java/android/util/LocaleList.java
index 379651e..0d5c135 100644
--- a/core/java/android/util/LocaleList.java
+++ b/core/java/android/util/LocaleList.java
@@ -37,6 +37,11 @@
*/
public final class LocaleList {
private final Locale[] mList;
+ // This is a comma-separated list of the locales in the LocaleList created at construction time,
+ // basically the result of running each locale's toLanguageTag() method and concatenating them
+ // with commas in between.
+ private final String mStringRepresentation;
+
private static final Locale[] sEmptyList = new Locale[0];
private static final LocaleList sEmptyLocaleList = new LocaleList();
@@ -95,15 +100,9 @@
return sb.toString();
}
+ @NonNull
public String toLanguageTags() {
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < mList.length; ++i) {
- sb.append(mList[i].toLanguageTag());
- if (i < mList.length - 1) {
- sb.append(',');
- }
- }
- return sb.toString();
+ return mStringRepresentation;
}
/**
@@ -112,6 +111,7 @@
*/
public LocaleList() {
mList = sEmptyList;
+ mStringRepresentation = "";
}
/**
@@ -121,9 +121,11 @@
public LocaleList(@Nullable Locale locale) {
if (locale == null) {
mList = sEmptyList;
+ mStringRepresentation = "";
} else {
mList = new Locale[1];
mList[0] = (Locale) locale.clone();
+ mStringRepresentation = locale.toLanguageTag();
}
}
@@ -134,9 +136,11 @@
public LocaleList(@Nullable Locale[] list) {
if (list == null || list.length == 0) {
mList = sEmptyList;
+ mStringRepresentation = "";
} else {
final Locale[] localeList = new Locale[list.length];
final HashSet<Locale> seenLocales = new HashSet<Locale>();
+ final StringBuilder sb = new StringBuilder();
for (int i = 0; i < list.length; ++i) {
final Locale l = list[i];
if (l == null) {
@@ -144,11 +148,17 @@
} else if (seenLocales.contains(l)) {
throw new IllegalArgumentException();
} else {
- seenLocales.add(l);
- localeList[i] = (Locale) l.clone();
+ final Locale localeClone = (Locale) l.clone();
+ localeList[i] = localeClone;
+ sb.append(localeClone.toLanguageTag());
+ if (i < list.length - 1) {
+ sb.append(',');
+ }
+ seenLocales.add(localeClone);
}
}
mList = localeList;
+ mStringRepresentation = sb.toString();
}
}
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index 986c0f8..8e5af79 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -29,6 +29,8 @@
import java.util.Locale;
+import libcore.icu.LocaleData;
+
/**
* A widget for selecting the time of day, in either 24-hour or AM/PM mode.
* <p>
@@ -303,6 +305,16 @@
void onValidationChanged(boolean valid);
}
+ static String[] getAmPmStrings(Context context) {
+ final Locale locale = context.getResources().getConfiguration().locale;
+ final LocaleData d = LocaleData.get(locale);
+
+ final String[] result = new String[2];
+ result[0] = d.amPm[0].length() > 4 ? d.narrowAm : d.amPm[0];
+ result[1] = d.amPm[1].length() > 4 ? d.narrowPm : d.amPm[1];
+ return result;
+ }
+
/**
* An abstract class which can be used as a start for TimePicker implementations
*/
diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java
index 6d41c1d..4dc5fd3e 100644
--- a/core/java/android/widget/TimePickerClockDelegate.java
+++ b/core/java/android/widget/TimePickerClockDelegate.java
@@ -29,21 +29,22 @@
import android.text.format.DateUtils;
import android.text.style.TtsSpan;
import android.util.AttributeSet;
-import android.util.Log;
import android.util.StateSet;
import android.view.HapticFeedbackConstants;
-import android.view.KeyCharacterMap;
-import android.view.KeyEvent;
import android.view.LayoutInflater;
+import android.view.MotionEvent;
import android.view.View;
import android.view.View.AccessibilityDelegate;
+import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import com.android.internal.R;
+import com.android.internal.widget.NumericTextView;
+import com.android.internal.widget.NumericTextView.OnValueChangedListener;
-import java.util.ArrayList;
import java.util.Calendar;
import java.util.Locale;
@@ -52,7 +53,12 @@
*/
class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate implements
RadialTimePickerView.OnValueSelectedListener {
- private static final String TAG = "TimePickerClockDelegate";
+ /**
+ * Delay in milliseconds before valid but potentially incomplete, for
+ * example "1" but not "12", keyboard edits are propagated from the
+ * hour / minute fields to the radial picker.
+ */
+ private static final long DELAY_COMMIT_MILLIS = 2000;
// Index used by RadialPickerLayout
private static final int HOUR_INDEX = 0;
@@ -61,9 +67,6 @@
// NOT a real index for the purpose of what's showing.
private static final int AMPM_INDEX = 2;
- // Also NOT a real index, just used for keyboard mode.
- private static final int ENABLE_PICKER_INDEX = 3;
-
private static final int[] ATTRS_TEXT_COLOR = new int[] {R.attr.textColor};
private static final int[] ATTRS_DISABLED_ALPHA = new int[] {R.attr.disabledAlpha};
@@ -74,18 +77,14 @@
private static final int HOURS_IN_HALF_DAY = 12;
- private final View mHeaderView;
- private final TextView mHourView;
- private final TextView mMinuteView;
+ private final NumericTextView mHourView;
+ private final NumericTextView mMinuteView;
private final View mAmPmLayout;
- private final CheckedTextView mAmLabel;
- private final CheckedTextView mPmLabel;
+ private final RadioButton mAmLabel;
+ private final RadioButton mPmLabel;
private final RadialTimePickerView mRadialTimePickerView;
private final TextView mSeparatorView;
- private final String mAmText;
- private final String mPmText;
-
private boolean mIsEnabled = true;
private boolean mAllowAutoAdvance;
private int mInitialHourOfDay;
@@ -93,20 +92,14 @@
private boolean mIs24HourView;
private boolean mIsAmPmAtStart;
- // For hardware IME input.
- private char mPlaceholderText;
- private String mDoublePlaceholderText;
- private String mDeletedKeyFormat;
- private boolean mInKbMode;
- private ArrayList<Integer> mTypedTimes = new ArrayList<Integer>();
- private Node mLegalTimesTree;
- private int mAmKeyCode;
- private int mPmKeyCode;
-
// Accessibility strings.
private String mSelectHours;
private String mSelectMinutes;
+ // Localization data.
+ private boolean mHourFormatShowLeadingZero;
+ private boolean mHourFormatStartsAtZero;
+
// Most recent time announcement values for accessibility.
private CharSequence mLastAnnouncedText;
private boolean mLastAnnouncedIsHour;
@@ -127,43 +120,42 @@
mSelectHours = res.getString(R.string.select_hours);
mSelectMinutes = res.getString(R.string.select_minutes);
- String[] amPmStrings = TimePickerSpinnerDelegate.getAmPmStrings(context);
- mAmText = amPmStrings[0];
- mPmText = amPmStrings[1];
-
final int layoutResourceId = a.getResourceId(R.styleable.TimePicker_internalLayout,
R.layout.time_picker_material);
final View mainView = inflater.inflate(layoutResourceId, delegator);
-
- mHeaderView = mainView.findViewById(R.id.time_header);
+ final View headerView = mainView.findViewById(R.id.time_header);
+ headerView.setOnTouchListener(new NearestTouchDelegate());
// Set up hour/minute labels.
- mHourView = (TextView) mainView.findViewById(R.id.hours);
+ mHourView = (NumericTextView) mainView.findViewById(R.id.hours);
mHourView.setOnClickListener(mClickListener);
+ mHourView.setOnFocusChangeListener(mFocusListener);
+ mHourView.setOnDigitEnteredListener(mDigitEnteredListener);
mHourView.setAccessibilityDelegate(
new ClickActionDelegate(context, R.string.select_hours));
mSeparatorView = (TextView) mainView.findViewById(R.id.separator);
- mMinuteView = (TextView) mainView.findViewById(R.id.minutes);
+ mMinuteView = (NumericTextView) mainView.findViewById(R.id.minutes);
mMinuteView.setOnClickListener(mClickListener);
+ mMinuteView.setOnFocusChangeListener(mFocusListener);
+ mMinuteView.setOnDigitEnteredListener(mDigitEnteredListener);
mMinuteView.setAccessibilityDelegate(
new ClickActionDelegate(context, R.string.select_minutes));
-
- // Now that we have text appearances out of the way, make sure the hour
- // and minute views are correctly sized.
- mHourView.setMinWidth(computeStableWidth(mHourView, 24));
- mMinuteView.setMinWidth(computeStableWidth(mMinuteView, 60));
-
- final SpannableStringBuilder amLabel = new SpannableStringBuilder()
- .append(amPmStrings[0], new TtsSpan.VerbatimBuilder(amPmStrings[0]).build(), 0);
+ mMinuteView.setRange(0, 59);
// Set up AM/PM labels.
mAmPmLayout = mainView.findViewById(R.id.ampm_layout);
- mAmLabel = (CheckedTextView) mAmPmLayout.findViewById(R.id.am_label);
+ mAmPmLayout.setOnTouchListener(new NearestTouchDelegate());
+
+ final String[] amPmStrings = TimePicker.getAmPmStrings(context);
+ mAmLabel = (RadioButton) mAmPmLayout.findViewById(R.id.am_label);
mAmLabel.setText(obtainVerbatim(amPmStrings[0]));
mAmLabel.setOnClickListener(mClickListener);
- mPmLabel = (CheckedTextView) mAmPmLayout.findViewById(R.id.pm_label);
+ ensureMinimumTextWidth(mAmLabel);
+
+ mPmLabel = (RadioButton) mAmPmLayout.findViewById(R.id.pm_label);
mPmLabel.setText(obtainVerbatim(amPmStrings[1]));
mPmLabel.setOnClickListener(mClickListener);
+ ensureMinimumTextWidth(mPmLabel);
// For the sake of backwards compatibility, attempt to extract the text
// color from the header time text appearance. If it's set, we'll let
@@ -195,7 +187,7 @@
// Set up header background, if available.
if (a.hasValueOrEmpty(R.styleable.TimePicker_headerBackground)) {
- mHeaderView.setBackground(a.getDrawable(R.styleable.TimePicker_headerBackground));
+ headerView.setBackground(a.getDrawable(R.styleable.TimePicker_headerBackground));
}
a.recycle();
@@ -207,18 +199,66 @@
mAllowAutoAdvance = true;
- // Set up for keyboard mode.
- mDoublePlaceholderText = res.getString(R.string.time_placeholder);
- mDeletedKeyFormat = res.getString(R.string.deleted_key);
- mPlaceholderText = mDoublePlaceholderText.charAt(0);
- mAmKeyCode = mPmKeyCode = -1;
- generateLegalTimesTree();
+ // Updates mHourFormat variables used below.
+ updateHourFormat(mCurrentLocale, mIs24HourView);
- // Initialize with current time
- final Calendar calendar = Calendar.getInstance(mCurrentLocale);
- final int currentHour = calendar.get(Calendar.HOUR_OF_DAY);
- final int currentMinute = calendar.get(Calendar.MINUTE);
- initialize(currentHour, currentMinute, false /* 12h */, HOUR_INDEX);
+ // Update hour text field.
+ final int minHour = mHourFormatStartsAtZero ? 0 : 1;
+ final int maxHour = (mIs24HourView ? 23 : 11) + minHour;
+ mHourView.setRange(minHour, maxHour);
+ mHourView.setShowLeadingZeroes(mHourFormatShowLeadingZero);
+
+ // Initialize with current time.
+ mTempCalendar = Calendar.getInstance(mCurrentLocale);
+ final int currentHour = mTempCalendar.get(Calendar.HOUR_OF_DAY);
+ final int currentMinute = mTempCalendar.get(Calendar.MINUTE);
+ initialize(currentHour, currentMinute, mIs24HourView, HOUR_INDEX);
+ }
+
+ /**
+ * Ensures that a TextView is wide enough to contain its text without
+ * wrapping or clipping. Measures the specified view and sets the minimum
+ * width to the view's desired width.
+ *
+ * @param v the text view to measure
+ */
+ private static void ensureMinimumTextWidth(TextView v) {
+ v.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ // Set both the TextView and the View version of minimum
+ // width because they are subtly different.
+ final int minWidth = v.getMeasuredWidth();
+ v.setMinWidth(minWidth);
+ v.setMinimumWidth(minWidth);
+ }
+
+ /**
+ * Determines how the hour should be formatted and updates member variables
+ * related to hour formatting.
+ *
+ * @param locale the locale in which the view is displayed
+ * @param is24Hour whether the view is in 24-hour (hour-of-day) mode
+ */
+ private void updateHourFormat(Locale locale, boolean is24Hour) {
+ final String bestDateTimePattern = DateFormat.getBestDateTimePattern(
+ locale, is24Hour ? "Hm" : "hm");
+ final int lengthPattern = bestDateTimePattern.length();
+ boolean showLeadingZero = false;
+ char hourFormat = '\0';
+
+ for (int i = 0; i < lengthPattern; i++) {
+ final char c = bestDateTimePattern.charAt(i);
+ if (c == 'H' || c == 'h' || c == 'K' || c == 'k') {
+ hourFormat = c;
+ if (i + 1 < lengthPattern && c == bestDateTimePattern.charAt(i + 1)) {
+ showLeadingZero = true;
+ }
+ break;
+ }
+ }
+
+ mHourFormatShowLeadingZero = showLeadingZero;
+ mHourFormatStartsAtZero = hourFormat == 'K' || hourFormat == 'H';
}
private static final CharSequence obtainVerbatim(String text) {
@@ -290,51 +330,24 @@
}
}
- private int computeStableWidth(TextView v, int maxNumber) {
- int maxWidth = 0;
-
- for (int i = 0; i < maxNumber; i++) {
- final String text = String.format("%02d", i);
- v.setText(text);
- v.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
-
- final int width = v.getMeasuredWidth();
- if (width > maxWidth) {
- maxWidth = width;
- }
- }
-
- return maxWidth;
- }
-
private void initialize(int hourOfDay, int minute, boolean is24HourView, int index) {
mInitialHourOfDay = hourOfDay;
mInitialMinute = minute;
mIs24HourView = is24HourView;
- mInKbMode = false;
updateUI(index);
}
private void setupListeners() {
- mHeaderView.setOnKeyListener(mKeyListener);
- mHeaderView.setOnFocusChangeListener(mFocusListener);
- mHeaderView.setFocusable(true);
-
mRadialTimePickerView.setOnValueSelectedListener(this);
}
private void updateUI(int index) {
- // Update RadialPicker values
- updateRadialPicker(index);
- // Enable or disable the AM/PM view.
updateHeaderAmPm();
- // Update Hour and Minutes
updateHeaderHour(mInitialHourOfDay, false);
- // Update time separator
updateHeaderSeparator();
- // Update Minutes
updateHeaderMinute(mInitialMinute, false);
- // Invalidate everything
+ updateRadialPicker(index);
+
mDelegator.invalidate();
}
@@ -447,14 +460,11 @@
if (is24HourView == mIs24HourView) {
return;
}
+
mIs24HourView = is24HourView;
- generateLegalTimesTree();
- int hour = mRadialTimePickerView.getCurrentHour();
- mInitialHourOfDay = hour;
- updateHeaderHour(hour, false);
- updateHeaderAmPm();
- updateRadialPicker(mRadialTimePickerView.getCurrentItemShowing());
- mDelegator.invalidate();
+ mInitialHourOfDay = getCurrentHour();
+
+ updateUI(mRadialTimePickerView.getCurrentItemShowing());
}
/**
@@ -499,20 +509,14 @@
@Override
public Parcelable onSaveInstanceState(Parcelable superState) {
return new SavedState(superState, getCurrentHour(), getCurrentMinute(),
- is24HourView(), inKbMode(), getTypedTimes(), getCurrentItemShowing());
+ is24HourView(), getCurrentItemShowing());
}
@Override
public void onRestoreInstanceState(Parcelable state) {
- SavedState ss = (SavedState) state;
- setInKbMode(ss.inKbMode());
- setTypedTimes(ss.getTypesTimes());
+ final SavedState ss = (SavedState) state;
initialize(ss.getHour(), ss.getMinute(), ss.is24HourMode(), ss.getCurrentItemShowing());
mRadialTimePickerView.invalidate();
- if (mInKbMode) {
- tryStartingKbMode(-1);
- mHourView.invalidate();
- }
}
@Override
@@ -543,33 +547,6 @@
}
/**
- * Set whether in keyboard mode or not.
- *
- * @param inKbMode True means in keyboard mode.
- */
- private void setInKbMode(boolean inKbMode) {
- mInKbMode = inKbMode;
- }
-
- /**
- * @return true if in keyboard mode
- */
- private boolean inKbMode() {
- return mInKbMode;
- }
-
- private void setTypedTimes(ArrayList<Integer> typeTimes) {
- mTypedTimes = typeTimes;
- }
-
- /**
- * @return an array of typed times
- */
- private ArrayList<Integer> getTypedTimes() {
- return mTypedTimes;
- }
-
- /**
* @return the index of the current item showing
*/
private int getCurrentItemShowing() {
@@ -595,19 +572,14 @@
private final int mHour;
private final int mMinute;
private final boolean mIs24HourMode;
- private final boolean mInKbMode;
- private final ArrayList<Integer> mTypedTimes;
private final int mCurrentItemShowing;
private SavedState(Parcelable superState, int hour, int minute, boolean is24HourMode,
- boolean isKbMode, ArrayList<Integer> typedTimes,
- int currentItemShowing) {
+ int currentItemShowing) {
super(superState);
mHour = hour;
mMinute = minute;
mIs24HourMode = is24HourMode;
- mInKbMode = isKbMode;
- mTypedTimes = typedTimes;
mCurrentItemShowing = currentItemShowing;
}
@@ -616,8 +588,6 @@
mHour = in.readInt();
mMinute = in.readInt();
mIs24HourMode = (in.readInt() == 1);
- mInKbMode = (in.readInt() == 1);
- mTypedTimes = in.readArrayList(getClass().getClassLoader());
mCurrentItemShowing = in.readInt();
}
@@ -633,14 +603,6 @@
return mIs24HourMode;
}
- public boolean inKbMode() {
- return mInKbMode;
- }
-
- public ArrayList<Integer> getTypesTimes() {
- return mTypedTimes;
- }
-
public int getCurrentItemShowing() {
return mCurrentItemShowing;
}
@@ -651,13 +613,11 @@
dest.writeInt(mHour);
dest.writeInt(mMinute);
dest.writeInt(mIs24HourMode ? 1 : 0);
- dest.writeInt(mInKbMode ? 1 : 0);
- dest.writeList(mTypedTimes);
dest.writeInt(mCurrentItemShowing);
}
@SuppressWarnings({"unused", "hiding"})
- public static final Parcelable.Creator<SavedState> CREATOR = new Creator<SavedState>() {
+ public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
@@ -703,12 +663,6 @@
case AMPM_INDEX:
updateAmPmLabelStates(newValue);
break;
- case ENABLE_PICKER_INDEX:
- if (!isTypedTimeFullyLegal()) {
- mTypedTimes.clear();
- }
- finishKbMode();
- break;
}
if (mOnTimeChangedListener != null) {
@@ -716,61 +670,41 @@
}
}
- private void updateHeaderHour(int value, boolean announce) {
- final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mCurrentLocale,
- (mIs24HourView) ? "Hm" : "hm");
- final int lengthPattern = bestDateTimePattern.length();
- boolean hourWithTwoDigit = false;
- char hourFormat = '\0';
- // Check if the returned pattern is single or double 'H', 'h', 'K', 'k'. We also save
- // the hour format that we found.
- for (int i = 0; i < lengthPattern; i++) {
- final char c = bestDateTimePattern.charAt(i);
- if (c == 'H' || c == 'h' || c == 'K' || c == 'k') {
- hourFormat = c;
- if (i + 1 < lengthPattern && c == bestDateTimePattern.charAt(i + 1)) {
- hourWithTwoDigit = true;
- }
- break;
- }
+ /**
+ * Converts hour-of-day (0-23) time into a localized hour number.
+ *
+ * @param hourOfDay the hour-of-day (0-23)
+ * @return a localized hour number
+ */
+ private int getLocalizedHour(int hourOfDay) {
+ if (!mIs24HourView) {
+ // Convert to hour-of-am-pm.
+ hourOfDay %= 12;
}
- final String format;
- if (hourWithTwoDigit) {
- format = "%02d";
- } else {
- format = "%d";
+
+ if (!mHourFormatStartsAtZero && hourOfDay == 0) {
+ // Convert to clock-hour (either of-day or of-am-pm).
+ hourOfDay = mIs24HourView ? 24 : 12;
}
- if (mIs24HourView) {
- // 'k' means 1-24 hour
- if (hourFormat == 'k' && value == 0) {
- value = 24;
- }
- } else {
- // 'K' means 0-11 hour
- value = modulo12(value, hourFormat == 'K');
- }
- CharSequence text = String.format(format, value);
- mHourView.setText(text);
+
+ return hourOfDay;
+ }
+
+ private void updateHeaderHour(int hourOfDay, boolean announce) {
+ final int localizedHour = getLocalizedHour(hourOfDay);
+ mHourView.setValue(localizedHour);
+
if (announce) {
- tryAnnounceForAccessibility(text, true);
+ tryAnnounceForAccessibility(mHourView.getText(), true);
}
}
- private void tryAnnounceForAccessibility(CharSequence text, boolean isHour) {
- if (mLastAnnouncedIsHour != isHour || !text.equals(mLastAnnouncedText)) {
- // TODO: Find a better solution, potentially live regions?
- mDelegator.announceForAccessibility(text);
- mLastAnnouncedText = text;
- mLastAnnouncedIsHour = isHour;
- }
- }
+ private void updateHeaderMinute(int minuteOfHour, boolean announce) {
+ mMinuteView.setValue(minuteOfHour);
- private static int modulo12(int n, boolean startWithZero) {
- int value = n % 12;
- if (value == 0 && !startWithZero) {
- value = 12;
+ if (announce) {
+ tryAnnounceForAccessibility(mMinuteView.getText(), false);
}
- return value;
}
/**
@@ -812,14 +746,12 @@
return -1;
}
- private void updateHeaderMinute(int value, boolean announceForAccessibility) {
- if (value == 60) {
- value = 0;
- }
- final CharSequence text = String.format(mCurrentLocale, "%02d", value);
- mMinuteView.setText(text);
- if (announceForAccessibility) {
- tryAnnounceForAccessibility(text, false);
+ private void tryAnnounceForAccessibility(CharSequence text, boolean isHour) {
+ if (mLastAnnouncedIsHour != isHour || !text.equals(mLastAnnouncedText)) {
+ // TODO: Find a better solution, potentially live regions?
+ mDelegator.announceForAccessibility(text);
+ mLastAnnouncedText = text;
+ mLastAnnouncedIsHour = isHour;
}
}
@@ -848,477 +780,82 @@
mRadialTimePickerView.setAmOrPm(amOrPm);
}
- /**
- * For keyboard mode, processes key events.
- *
- * @param keyCode the pressed key.
- *
- * @return true if the key was successfully processed, false otherwise.
- */
- private boolean processKeyUp(int keyCode) {
- if (keyCode == KeyEvent.KEYCODE_DEL) {
- if (mInKbMode) {
- if (!mTypedTimes.isEmpty()) {
- int deleted = deleteLastTypedKey();
- String deletedKeyStr;
- if (deleted == getAmOrPmKeyCode(AM)) {
- deletedKeyStr = mAmText;
- } else if (deleted == getAmOrPmKeyCode(PM)) {
- deletedKeyStr = mPmText;
- } else {
- deletedKeyStr = String.format("%d", getValFromKeyCode(deleted));
+ private final OnValueChangedListener mDigitEnteredListener = new OnValueChangedListener() {
+ @Override
+ public void onValueChanged(NumericTextView view, int value,
+ boolean isValid, boolean isFinished) {
+ final Runnable commitCallback;
+ final View nextFocusTarget;
+ if (view == mHourView) {
+ commitCallback = mCommitHour;
+ nextFocusTarget = view.isFocused() ? mMinuteView : null;
+ } else if (view == mMinuteView) {
+ commitCallback = mCommitMinute;
+ nextFocusTarget = null;
+ } else {
+ return;
+ }
+
+ view.removeCallbacks(commitCallback);
+
+ if (isValid) {
+ if (isFinished) {
+ // Done with hours entry, make visual updates
+ // immediately and move to next focus if needed.
+ commitCallback.run();
+
+ if (nextFocusTarget != null) {
+ nextFocusTarget.requestFocus();
}
- mDelegator.announceForAccessibility(
- String.format(mDeletedKeyFormat, deletedKeyStr));
- updateDisplay(true);
- }
- }
- } else if (keyCode == KeyEvent.KEYCODE_0 || keyCode == KeyEvent.KEYCODE_1
- || keyCode == KeyEvent.KEYCODE_2 || keyCode == KeyEvent.KEYCODE_3
- || keyCode == KeyEvent.KEYCODE_4 || keyCode == KeyEvent.KEYCODE_5
- || keyCode == KeyEvent.KEYCODE_6 || keyCode == KeyEvent.KEYCODE_7
- || keyCode == KeyEvent.KEYCODE_8 || keyCode == KeyEvent.KEYCODE_9
- || (!mIs24HourView &&
- (keyCode == getAmOrPmKeyCode(AM) || keyCode == getAmOrPmKeyCode(PM)))) {
- if (!mInKbMode) {
- if (mRadialTimePickerView == null) {
- // Something's wrong, because time picker should definitely not be null.
- Log.e(TAG, "Unable to initiate keyboard mode, TimePicker was null.");
- return true;
- }
- mTypedTimes.clear();
- tryStartingKbMode(keyCode);
- return true;
- }
- // We're already in keyboard mode.
- if (addKeyIfLegal(keyCode)) {
- updateDisplay(false);
- }
- return true;
- }
- return false;
- }
-
- /**
- * Try to start keyboard mode with the specified key.
- *
- * @param keyCode The key to use as the first press. Keyboard mode will not be started if the
- * key is not legal to start with. Or, pass in -1 to get into keyboard mode without a starting
- * key.
- */
- private void tryStartingKbMode(int keyCode) {
- if (keyCode == -1 || addKeyIfLegal(keyCode)) {
- mInKbMode = true;
- onValidationChanged(false);
- updateDisplay(false);
- mRadialTimePickerView.setInputEnabled(false);
- }
- }
-
- private boolean addKeyIfLegal(int keyCode) {
- // If we're in 24hour mode, we'll need to check if the input is full. If in AM/PM mode,
- // we'll need to see if AM/PM have been typed.
- if ((mIs24HourView && mTypedTimes.size() == 4) ||
- (!mIs24HourView && isTypedTimeFullyLegal())) {
- return false;
- }
-
- mTypedTimes.add(keyCode);
- if (!isTypedTimeLegalSoFar()) {
- deleteLastTypedKey();
- return false;
- }
-
- int val = getValFromKeyCode(keyCode);
- mDelegator.announceForAccessibility(String.format("%d", val));
- // Automatically fill in 0's if AM or PM was legally entered.
- if (isTypedTimeFullyLegal()) {
- if (!mIs24HourView && mTypedTimes.size() <= 3) {
- mTypedTimes.add(mTypedTimes.size() - 1, KeyEvent.KEYCODE_0);
- mTypedTimes.add(mTypedTimes.size() - 1, KeyEvent.KEYCODE_0);
- }
- onValidationChanged(true);
- }
-
- return true;
- }
-
- /**
- * Traverse the tree to see if the keys that have been typed so far are legal as is,
- * or may become legal as more keys are typed (excluding backspace).
- */
- private boolean isTypedTimeLegalSoFar() {
- Node node = mLegalTimesTree;
- for (int keyCode : mTypedTimes) {
- node = node.canReach(keyCode);
- if (node == null) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Check if the time that has been typed so far is completely legal, as is.
- */
- private boolean isTypedTimeFullyLegal() {
- if (mIs24HourView) {
- // For 24-hour mode, the time is legal if the hours and minutes are each legal. Note:
- // getEnteredTime() will ONLY call isTypedTimeFullyLegal() when NOT in 24hour mode.
- int[] values = getEnteredTime(null);
- return (values[0] >= 0 && values[1] >= 0 && values[1] < 60);
- } else {
- // For AM/PM mode, the time is legal if it contains an AM or PM, as those can only be
- // legally added at specific times based on the tree's algorithm.
- return (mTypedTimes.contains(getAmOrPmKeyCode(AM)) ||
- mTypedTimes.contains(getAmOrPmKeyCode(PM)));
- }
- }
-
- private int deleteLastTypedKey() {
- int deleted = mTypedTimes.remove(mTypedTimes.size() - 1);
- if (!isTypedTimeFullyLegal()) {
- onValidationChanged(false);
- }
- return deleted;
- }
-
- /**
- * Get out of keyboard mode. If there is nothing in typedTimes, revert to TimePicker's time.
- */
- private void finishKbMode() {
- mInKbMode = false;
- if (!mTypedTimes.isEmpty()) {
- int values[] = getEnteredTime(null);
- mRadialTimePickerView.setCurrentHour(values[0]);
- mRadialTimePickerView.setCurrentMinute(values[1]);
- if (!mIs24HourView) {
- mRadialTimePickerView.setAmOrPm(values[2]);
- }
- mTypedTimes.clear();
- }
- updateDisplay(false);
- mRadialTimePickerView.setInputEnabled(true);
- }
-
- /**
- * Update the hours, minutes, and AM/PM displays with the typed times. If the typedTimes is
- * empty, either show an empty display (filled with the placeholder text), or update from the
- * timepicker's values.
- *
- * @param allowEmptyDisplay if true, then if the typedTimes is empty, use the placeholder text.
- * Otherwise, revert to the timepicker's values.
- */
- private void updateDisplay(boolean allowEmptyDisplay) {
- if (!allowEmptyDisplay && mTypedTimes.isEmpty()) {
- int hour = mRadialTimePickerView.getCurrentHour();
- int minute = mRadialTimePickerView.getCurrentMinute();
- updateHeaderHour(hour, false);
- updateHeaderMinute(minute, false);
- if (!mIs24HourView) {
- updateAmPmLabelStates(hour < 12 ? AM : PM);
- }
- setCurrentItemShowing(mRadialTimePickerView.getCurrentItemShowing(), true, true);
- onValidationChanged(true);
- } else {
- boolean[] enteredZeros = {false, false};
- int[] values = getEnteredTime(enteredZeros);
- String hourFormat = enteredZeros[0] ? "%02d" : "%2d";
- String minuteFormat = (enteredZeros[1]) ? "%02d" : "%2d";
- String hourStr = (values[0] == -1) ? mDoublePlaceholderText :
- String.format(hourFormat, values[0]).replace(' ', mPlaceholderText);
- String minuteStr = (values[1] == -1) ? mDoublePlaceholderText :
- String.format(minuteFormat, values[1]).replace(' ', mPlaceholderText);
- mHourView.setText(hourStr);
- mHourView.setActivated(false);
- mMinuteView.setText(minuteStr);
- mMinuteView.setActivated(false);
- if (!mIs24HourView) {
- updateAmPmLabelStates(values[2]);
- }
- }
- }
-
- private int getValFromKeyCode(int keyCode) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_0:
- return 0;
- case KeyEvent.KEYCODE_1:
- return 1;
- case KeyEvent.KEYCODE_2:
- return 2;
- case KeyEvent.KEYCODE_3:
- return 3;
- case KeyEvent.KEYCODE_4:
- return 4;
- case KeyEvent.KEYCODE_5:
- return 5;
- case KeyEvent.KEYCODE_6:
- return 6;
- case KeyEvent.KEYCODE_7:
- return 7;
- case KeyEvent.KEYCODE_8:
- return 8;
- case KeyEvent.KEYCODE_9:
- return 9;
- default:
- return -1;
- }
- }
-
- /**
- * Get the currently-entered time, as integer values of the hours and minutes typed.
- *
- * @param enteredZeros A size-2 boolean array, which the caller should initialize, and which
- * may then be used for the caller to know whether zeros had been explicitly entered as either
- * hours of minutes. This is helpful for deciding whether to show the dashes, or actual 0's.
- *
- * @return A size-3 int array. The first value will be the hours, the second value will be the
- * minutes, and the third will be either AM or PM.
- */
- private int[] getEnteredTime(boolean[] enteredZeros) {
- int amOrPm = -1;
- int startIndex = 1;
- if (!mIs24HourView && isTypedTimeFullyLegal()) {
- int keyCode = mTypedTimes.get(mTypedTimes.size() - 1);
- if (keyCode == getAmOrPmKeyCode(AM)) {
- amOrPm = AM;
- } else if (keyCode == getAmOrPmKeyCode(PM)){
- amOrPm = PM;
- }
- startIndex = 2;
- }
- int minute = -1;
- int hour = -1;
- for (int i = startIndex; i <= mTypedTimes.size(); i++) {
- int val = getValFromKeyCode(mTypedTimes.get(mTypedTimes.size() - i));
- if (i == startIndex) {
- minute = val;
- } else if (i == startIndex+1) {
- minute += 10 * val;
- if (enteredZeros != null && val == 0) {
- enteredZeros[1] = true;
- }
- } else if (i == startIndex+2) {
- hour = val;
- } else if (i == startIndex+3) {
- hour += 10 * val;
- if (enteredZeros != null && val == 0) {
- enteredZeros[0] = true;
+ } else {
+ // May still be making changes. Postpone visual
+ // updates to prevent distracting the user.
+ view.postDelayed(commitCallback, DELAY_COMMIT_MILLIS);
}
}
}
+ };
- return new int[] { hour, minute, amOrPm };
- }
+ private final Runnable mCommitHour = new Runnable() {
+ @Override
+ public void run() {
+ setCurrentHour(mHourView.getValue());
+ }
+ };
- /**
- * Get the keycode value for AM and PM in the current language.
- */
- private int getAmOrPmKeyCode(int amOrPm) {
- // Cache the codes.
- if (mAmKeyCode == -1 || mPmKeyCode == -1) {
- // Find the first character in the AM/PM text that is unique.
- final KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
- final CharSequence amText = mAmText.toLowerCase(mCurrentLocale);
- final CharSequence pmText = mPmText.toLowerCase(mCurrentLocale);
- final int N = Math.min(amText.length(), pmText.length());
- for (int i = 0; i < N; i++) {
- final char amChar = amText.charAt(i);
- final char pmChar = pmText.charAt(i);
- if (amChar != pmChar) {
- // There should be 4 events: a down and up for both AM and PM.
- final KeyEvent[] events = kcm.getEvents(new char[] { amChar, pmChar });
- if (events != null && events.length == 4) {
- mAmKeyCode = events[0].getKeyCode();
- mPmKeyCode = events[2].getKeyCode();
- } else {
- Log.e(TAG, "Unable to find keycodes for AM and PM.");
- }
- break;
+ private final Runnable mCommitMinute = new Runnable() {
+ @Override
+ public void run() {
+ setCurrentMinute(mMinuteView.getValue());
+ }
+ };
+
+ private final View.OnFocusChangeListener mFocusListener = new View.OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean focused) {
+ if (focused) {
+ switch (v.getId()) {
+ case R.id.am_label:
+ setAmOrPm(AM);
+ break;
+ case R.id.pm_label:
+ setAmOrPm(PM);
+ break;
+ case R.id.hours:
+ setCurrentItemShowing(HOUR_INDEX, true, true);
+ break;
+ case R.id.minutes:
+ setCurrentItemShowing(MINUTE_INDEX, true, true);
+ break;
+ default:
+ // Failed to handle this click, don't vibrate.
+ return;
}
+
+ tryVibrate();
}
}
-
- if (amOrPm == AM) {
- return mAmKeyCode;
- } else if (amOrPm == PM) {
- return mPmKeyCode;
- }
-
- return -1;
- }
-
- /**
- * Create a tree for deciding what keys can legally be typed.
- */
- private void generateLegalTimesTree() {
- // Create a quick cache of numbers to their keycodes.
- final int k0 = KeyEvent.KEYCODE_0;
- final int k1 = KeyEvent.KEYCODE_1;
- final int k2 = KeyEvent.KEYCODE_2;
- final int k3 = KeyEvent.KEYCODE_3;
- final int k4 = KeyEvent.KEYCODE_4;
- final int k5 = KeyEvent.KEYCODE_5;
- final int k6 = KeyEvent.KEYCODE_6;
- final int k7 = KeyEvent.KEYCODE_7;
- final int k8 = KeyEvent.KEYCODE_8;
- final int k9 = KeyEvent.KEYCODE_9;
-
- // The root of the tree doesn't contain any numbers.
- mLegalTimesTree = new Node();
- if (mIs24HourView) {
- // We'll be re-using these nodes, so we'll save them.
- Node minuteFirstDigit = new Node(k0, k1, k2, k3, k4, k5);
- Node minuteSecondDigit = new Node(k0, k1, k2, k3, k4, k5, k6, k7, k8, k9);
- // The first digit must be followed by the second digit.
- minuteFirstDigit.addChild(minuteSecondDigit);
-
- // The first digit may be 0-1.
- Node firstDigit = new Node(k0, k1);
- mLegalTimesTree.addChild(firstDigit);
-
- // When the first digit is 0-1, the second digit may be 0-5.
- Node secondDigit = new Node(k0, k1, k2, k3, k4, k5);
- firstDigit.addChild(secondDigit);
- // We may now be followed by the first minute digit. E.g. 00:09, 15:58.
- secondDigit.addChild(minuteFirstDigit);
-
- // When the first digit is 0-1, and the second digit is 0-5, the third digit may be 6-9.
- Node thirdDigit = new Node(k6, k7, k8, k9);
- // The time must now be finished. E.g. 0:55, 1:08.
- secondDigit.addChild(thirdDigit);
-
- // When the first digit is 0-1, the second digit may be 6-9.
- secondDigit = new Node(k6, k7, k8, k9);
- firstDigit.addChild(secondDigit);
- // We must now be followed by the first minute digit. E.g. 06:50, 18:20.
- secondDigit.addChild(minuteFirstDigit);
-
- // The first digit may be 2.
- firstDigit = new Node(k2);
- mLegalTimesTree.addChild(firstDigit);
-
- // When the first digit is 2, the second digit may be 0-3.
- secondDigit = new Node(k0, k1, k2, k3);
- firstDigit.addChild(secondDigit);
- // We must now be followed by the first minute digit. E.g. 20:50, 23:09.
- secondDigit.addChild(minuteFirstDigit);
-
- // When the first digit is 2, the second digit may be 4-5.
- secondDigit = new Node(k4, k5);
- firstDigit.addChild(secondDigit);
- // We must now be followd by the last minute digit. E.g. 2:40, 2:53.
- secondDigit.addChild(minuteSecondDigit);
-
- // The first digit may be 3-9.
- firstDigit = new Node(k3, k4, k5, k6, k7, k8, k9);
- mLegalTimesTree.addChild(firstDigit);
- // We must now be followed by the first minute digit. E.g. 3:57, 8:12.
- firstDigit.addChild(minuteFirstDigit);
- } else {
- // We'll need to use the AM/PM node a lot.
- // Set up AM and PM to respond to "a" and "p".
- Node ampm = new Node(getAmOrPmKeyCode(AM), getAmOrPmKeyCode(PM));
-
- // The first hour digit may be 1.
- Node firstDigit = new Node(k1);
- mLegalTimesTree.addChild(firstDigit);
- // We'll allow quick input of on-the-hour times. E.g. 1pm.
- firstDigit.addChild(ampm);
-
- // When the first digit is 1, the second digit may be 0-2.
- Node secondDigit = new Node(k0, k1, k2);
- firstDigit.addChild(secondDigit);
- // Also for quick input of on-the-hour times. E.g. 10pm, 12am.
- secondDigit.addChild(ampm);
-
- // When the first digit is 1, and the second digit is 0-2, the third digit may be 0-5.
- Node thirdDigit = new Node(k0, k1, k2, k3, k4, k5);
- secondDigit.addChild(thirdDigit);
- // The time may be finished now. E.g. 1:02pm, 1:25am.
- thirdDigit.addChild(ampm);
-
- // When the first digit is 1, the second digit is 0-2, and the third digit is 0-5,
- // the fourth digit may be 0-9.
- Node fourthDigit = new Node(k0, k1, k2, k3, k4, k5, k6, k7, k8, k9);
- thirdDigit.addChild(fourthDigit);
- // The time must be finished now. E.g. 10:49am, 12:40pm.
- fourthDigit.addChild(ampm);
-
- // When the first digit is 1, and the second digit is 0-2, the third digit may be 6-9.
- thirdDigit = new Node(k6, k7, k8, k9);
- secondDigit.addChild(thirdDigit);
- // The time must be finished now. E.g. 1:08am, 1:26pm.
- thirdDigit.addChild(ampm);
-
- // When the first digit is 1, the second digit may be 3-5.
- secondDigit = new Node(k3, k4, k5);
- firstDigit.addChild(secondDigit);
-
- // When the first digit is 1, and the second digit is 3-5, the third digit may be 0-9.
- thirdDigit = new Node(k0, k1, k2, k3, k4, k5, k6, k7, k8, k9);
- secondDigit.addChild(thirdDigit);
- // The time must be finished now. E.g. 1:39am, 1:50pm.
- thirdDigit.addChild(ampm);
-
- // The hour digit may be 2-9.
- firstDigit = new Node(k2, k3, k4, k5, k6, k7, k8, k9);
- mLegalTimesTree.addChild(firstDigit);
- // We'll allow quick input of on-the-hour-times. E.g. 2am, 5pm.
- firstDigit.addChild(ampm);
-
- // When the first digit is 2-9, the second digit may be 0-5.
- secondDigit = new Node(k0, k1, k2, k3, k4, k5);
- firstDigit.addChild(secondDigit);
-
- // When the first digit is 2-9, and the second digit is 0-5, the third digit may be 0-9.
- thirdDigit = new Node(k0, k1, k2, k3, k4, k5, k6, k7, k8, k9);
- secondDigit.addChild(thirdDigit);
- // The time must be finished now. E.g. 2:57am, 9:30pm.
- thirdDigit.addChild(ampm);
- }
- }
-
- /**
- * Simple node class to be used for traversal to check for legal times.
- * mLegalKeys represents the keys that can be typed to get to the node.
- * mChildren are the children that can be reached from this node.
- */
- private class Node {
- private int[] mLegalKeys;
- private ArrayList<Node> mChildren;
-
- public Node(int... legalKeys) {
- mLegalKeys = legalKeys;
- mChildren = new ArrayList<Node>();
- }
-
- public void addChild(Node child) {
- mChildren.add(child);
- }
-
- public boolean containsKey(int key) {
- for (int i = 0; i < mLegalKeys.length; i++) {
- if (mLegalKeys[i] == key) {
- return true;
- }
- }
- return false;
- }
-
- public Node canReach(int key) {
- if (mChildren == null) {
- return null;
- }
- for (Node child : mChildren) {
- if (child.containsKey(key)) {
- return child;
- }
- }
- return null;
- }
- }
+ };
private final View.OnClickListener mClickListener = new View.OnClickListener() {
@Override
@@ -1347,28 +884,55 @@
}
};
- private final View.OnKeyListener mKeyListener = new View.OnKeyListener() {
- @Override
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- if (event.getAction() == KeyEvent.ACTION_UP) {
- return processKeyUp(keyCode);
+ /**
+ * Delegates unhandled touches in a view group to the nearest child view.
+ */
+ private static class NearestTouchDelegate implements View.OnTouchListener {
+ private View mInitialTouchTarget;
+
+ @Override
+ public boolean onTouch(View view, MotionEvent motionEvent) {
+ final int actionMasked = motionEvent.getActionMasked();
+ if (actionMasked == MotionEvent.ACTION_DOWN) {
+ mInitialTouchTarget = findNearestChild((ViewGroup) view,
+ (int) motionEvent.getX(), (int) motionEvent.getY());
+ }
+
+ final View child = mInitialTouchTarget;
+ if (child == null) {
+ return false;
+ }
+
+ final float offsetX = view.getScrollX() - child.getLeft();
+ final float offsetY = view.getScrollY() - child.getTop();
+ motionEvent.offsetLocation(offsetX, offsetY);
+ final boolean handled = child.dispatchTouchEvent(motionEvent);
+ motionEvent.offsetLocation(-offsetX, -offsetY);
+
+ if (actionMasked == MotionEvent.ACTION_UP
+ || actionMasked == MotionEvent.ACTION_CANCEL) {
+ mInitialTouchTarget = null;
+ }
+
+ return handled;
}
- return false;
- }
- };
- private final View.OnFocusChangeListener mFocusListener = new View.OnFocusChangeListener() {
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- if (!hasFocus && mInKbMode && isTypedTimeFullyLegal()) {
- finishKbMode();
+ private View findNearestChild(ViewGroup v, int x, int y) {
+ View bestChild = null;
+ int bestDist = Integer.MAX_VALUE;
- if (mOnTimeChangedListener != null) {
- mOnTimeChangedListener.onTimeChanged(mDelegator,
- mRadialTimePickerView.getCurrentHour(),
- mRadialTimePickerView.getCurrentMinute());
+ for (int i = 0, count = v.getChildCount(); i < count; i++) {
+ final View child = v.getChildAt(i);
+ final int dX = x - (child.getLeft() + child.getWidth() / 2);
+ final int dY = y - (child.getTop() + child.getHeight() / 2);
+ final int dist = dX * dX + dY * dY;
+ if (bestDist > dist) {
+ bestChild = child;
+ bestDist = dist;
}
}
+
+ return bestChild;
}
- };
+ }
}
diff --git a/core/java/com/android/internal/policy/DecorContext.java b/core/java/com/android/internal/policy/DecorContext.java
index aa603c1..4f17c39 100644
--- a/core/java/com/android/internal/policy/DecorContext.java
+++ b/core/java/com/android/internal/policy/DecorContext.java
@@ -17,7 +17,6 @@
package com.android.internal.policy;
import android.content.Context;
-import android.content.res.TypedArray;
import android.view.ContextThemeWrapper;
import android.view.WindowManager;
import android.view.WindowManagerImpl;
@@ -31,7 +30,6 @@
class DecorContext extends ContextThemeWrapper {
private PhoneWindow mPhoneWindow;
private WindowManager mWindowManager;
- private TypedArray mWindowStyle;
public DecorContext(Context context) {
super(context, null);
@@ -54,13 +52,4 @@
}
return super.getSystemService(name);
}
-
- public TypedArray getWindowStyle() {
- synchronized (this) {
- if (mWindowStyle == null) {
- mWindowStyle = obtainStyledAttributes(com.android.internal.R.styleable.Window);
- }
- return mWindowStyle;
- }
- }
}
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 8b81812..4644211 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -31,7 +31,6 @@
import android.animation.ObjectAnimator;
import android.app.ActivityManagerNative;
import android.app.SearchManager;
-import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.os.UserHandle;
@@ -5438,20 +5437,7 @@
* */
private Drawable getResizingBackgroundDrawable() {
final Context context = mDecor.getContext();
- final TypedArray windowStyle;
- if (context instanceof DecorContext) {
- windowStyle = ((DecorContext) context).getWindowStyle();
- } else {
- windowStyle = getWindowStyle();
- }
- final int resourceId =
- windowStyle.getResourceId(R.styleable.Window_windowResizingBackground, 0);
- if (resourceId != 0) {
- return context.getDrawable(resourceId);
- }
- // The app didn't set a resizing background color. In this case we try to use the app's
- // background drawable for the resizing background.
if (mBackgroundResource != 0) {
final Drawable drawable = context.getDrawable(mBackgroundResource);
if (drawable != null) {
@@ -5459,8 +5445,6 @@
}
}
- // The app background drawable isn't currently set. This might be because the app cleared
- // it. In this case we try to use the app's background fallback drawable.
if (mBackgroundFallbackResource != 0) {
final Drawable fallbackDrawable = context.getDrawable(mBackgroundFallbackResource);
if (fallbackDrawable != null) {
@@ -5468,7 +5452,9 @@
}
}
- return new ColorDrawable(context.getResources().getInteger(
- com.android.internal.R.integer.config_windowResizingBackgroundColorARGB));
+ // We shouldn't really get here as the background fallback should be always available since
+ // it is defaulted by the system.
+ Log.w(TAG, "Failed to find background drawable for PhoneWindow=" + this);
+ return null;
}
}
diff --git a/core/java/com/android/internal/widget/NumericTextView.java b/core/java/com/android/internal/widget/NumericTextView.java
new file mode 100644
index 0000000..27c5834
--- /dev/null
+++ b/core/java/com/android/internal/widget/NumericTextView.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.util.StateSet;
+import android.view.KeyEvent;
+import android.widget.TextView;
+
+/**
+ * Extension of TextView that can handle displaying and inputting a range of
+ * numbers.
+ * <p>
+ * Clients of this view should never call {@link #setText(CharSequence)} or
+ * {@link #setHint(CharSequence)} directly. Instead, they should call
+ * {@link #setValue(int)} to modify the currently displayed value.
+ */
+public class NumericTextView extends TextView {
+ private static final int RADIX = 10;
+ private static final double LOG_RADIX = Math.log(RADIX);
+
+ private int mMinValue = 0;
+ private int mMaxValue = 99;
+
+ /** Number of digits in the maximum value. */
+ private int mMaxCount = 2;
+
+ private boolean mShowLeadingZeroes = true;
+
+ private int mValue;
+
+ /** Number of digits entered during editing mode. */
+ private int mCount;
+
+ /** Used to restore the value after an aborted edit. */
+ private int mPreviousValue;
+
+ private OnValueChangedListener mListener;
+
+ public NumericTextView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ // Generate the hint text color based on disabled state.
+ final int textColorDisabled = getTextColors().getColorForState(StateSet.get(0), 0);
+ setHintTextColor(textColorDisabled);
+
+ setFocusable(true);
+ }
+
+ @Override
+ protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
+ super.onFocusChanged(focused, direction, previouslyFocusedRect);
+
+ if (focused) {
+ mPreviousValue = mValue;
+ mValue = 0;
+ mCount = 0;
+
+ // Transfer current text to hint.
+ setHint(getText());
+ setText("");
+ } else {
+ if (mCount == 0) {
+ // No digits were entered, revert to previous value.
+ mValue = mPreviousValue;
+
+ setText(getHint());
+ setHint("");
+ }
+
+ // Ensure the committed value is within range.
+ if (mValue < mMinValue) {
+ mValue = mMinValue;
+ }
+
+ setValue(mValue);
+
+ if (mListener != null) {
+ mListener.onValueChanged(this, mValue, true, true);
+ }
+ }
+ }
+
+ /**
+ * Sets the currently displayed value.
+ * <p>
+ * The specified {@code value} must be within the range specified by
+ * {@link #setRange(int, int)} (e.g. between {@link #getRangeMinimum()}
+ * and {@link #getRangeMaximum()}).
+ *
+ * @param value the value to display
+ */
+ public final void setValue(int value) {
+ if (mValue != value) {
+ mValue = value;
+
+ updateDisplayedValue();
+ }
+ }
+
+ /**
+ * Returns the currently displayed value.
+ * <p>
+ * If the value is currently being edited, returns the live value which may
+ * not be within the range specified by {@link #setRange(int, int)}.
+ *
+ * @return the currently displayed value
+ */
+ public final int getValue() {
+ return mValue;
+ }
+
+ /**
+ * Sets the valid range (inclusive).
+ *
+ * @param minValue the minimum valid value (inclusive)
+ * @param maxValue the maximum valid value (inclusive)
+ */
+ public final void setRange(int minValue, int maxValue) {
+ if (mMinValue != minValue) {
+ mMinValue = minValue;
+ }
+
+ if (mMaxValue != maxValue) {
+ mMaxValue = maxValue;
+ mMaxCount = 1 + (int) (Math.log(maxValue) / LOG_RADIX);
+
+ updateMinimumWidth();
+ updateDisplayedValue();
+ }
+ }
+
+ /**
+ * @return the minimum value value (inclusive)
+ */
+ public final int getRangeMinimum() {
+ return mMinValue;
+ }
+
+ /**
+ * @return the maximum value value (inclusive)
+ */
+ public final int getRangeMaximum() {
+ return mMaxValue;
+ }
+
+ /**
+ * Sets whether this view shows leading zeroes.
+ * <p>
+ * When leading zeroes are shown, the displayed value will be padded
+ * with zeroes to the width of the maximum value as specified by
+ * {@link #setRange(int, int)} (see also {@link #getRangeMaximum()}.
+ * <p>
+ * For example, with leading zeroes shown, a maximum of 99 and value of
+ * 9 would display "09". A maximum of 100 and a value of 9 would display
+ * "009". With leading zeroes hidden, both cases would show "9".
+ *
+ * @param showLeadingZeroes {@code true} to show leading zeroes,
+ * {@code false} to hide them
+ */
+ public final void setShowLeadingZeroes(boolean showLeadingZeroes) {
+ if (mShowLeadingZeroes != showLeadingZeroes) {
+ mShowLeadingZeroes = showLeadingZeroes;
+
+ updateDisplayedValue();
+ }
+ }
+
+ public final boolean getShowLeadingZeroes() {
+ return mShowLeadingZeroes;
+ }
+
+ /**
+ * Computes the display value and updates the text of the view.
+ * <p>
+ * This method should be called whenever the current value or display
+ * properties (leading zeroes, max digits) change.
+ */
+ private void updateDisplayedValue() {
+ final String format;
+ if (mShowLeadingZeroes) {
+ format = "%0" + mMaxCount + "d";
+ } else {
+ format = "%d";
+ }
+
+ // Always use String.format() rather than Integer.toString()
+ // to obtain correctly localized values.
+ setText(String.format(format, mValue));
+ }
+
+ /**
+ * Computes the minimum width in pixels required to display all possible
+ * values and updates the minimum width of the view.
+ * <p>
+ * This method should be called whenever the maximum value changes.
+ */
+ private void updateMinimumWidth() {
+ final CharSequence previousText = getText();
+ int maxWidth = 0;
+
+ for (int i = 0; i < mMaxValue; i++) {
+ setText(String.format("%0" + mMaxCount + "d", i));
+ measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ final int width = getMeasuredWidth();
+ if (width > maxWidth) {
+ maxWidth = width;
+ }
+ }
+
+ setText(previousText);
+ setMinWidth(maxWidth);
+ setMinimumWidth(maxWidth);
+ }
+
+ public final void setOnDigitEnteredListener(OnValueChangedListener listener) {
+ mListener = listener;
+ }
+
+ public final OnValueChangedListener getOnDigitEnteredListener() {
+ return mListener;
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ return isKeyCodeNumeric(keyCode)
+ || (keyCode == KeyEvent.KEYCODE_DEL)
+ || super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
+ return isKeyCodeNumeric(keyCode)
+ || (keyCode == KeyEvent.KEYCODE_DEL)
+ || super.onKeyMultiple(keyCode, repeatCount, event);
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ return handleKeyUp(keyCode)
+ || super.onKeyUp(keyCode, event);
+ }
+
+ private boolean handleKeyUp(int keyCode) {
+ if (keyCode == KeyEvent.KEYCODE_DEL) {
+ // Backspace removes the least-significant digit, if available.
+ if (mCount > 0) {
+ mValue /= RADIX;
+ mCount--;
+ }
+ } else if (isKeyCodeNumeric(keyCode)) {
+ if (mCount < mMaxCount) {
+ final int keyValue = numericKeyCodeToInt(keyCode);
+ final int newValue = mValue * RADIX + keyValue;
+ if (newValue <= mMaxValue) {
+ mValue = newValue;
+ mCount++;
+ }
+ }
+ } else {
+ return false;
+ }
+
+ final String formattedValue;
+ if (mCount > 0) {
+ // If the user types 01, we should always show the leading 0 even if
+ // getShowLeadingZeroes() is false. Preserve typed leading zeroes by
+ // using the number of digits entered as the format width.
+ formattedValue = String.format("%0" + mCount + "d", mValue);
+ } else {
+ formattedValue = "";
+ }
+
+ setText(formattedValue);
+
+ if (mListener != null) {
+ final boolean isValid = mValue >= mMinValue;
+ final boolean isFinished = mCount >= mMaxCount || mValue * RADIX > mMaxValue;
+ mListener.onValueChanged(this, mValue, isValid, isFinished);
+ }
+
+ return true;
+ }
+
+ private static boolean isKeyCodeNumeric(int keyCode) {
+ return keyCode == KeyEvent.KEYCODE_0 || keyCode == KeyEvent.KEYCODE_1
+ || keyCode == KeyEvent.KEYCODE_2 || keyCode == KeyEvent.KEYCODE_3
+ || keyCode == KeyEvent.KEYCODE_4 || keyCode == KeyEvent.KEYCODE_5
+ || keyCode == KeyEvent.KEYCODE_6 || keyCode == KeyEvent.KEYCODE_7
+ || keyCode == KeyEvent.KEYCODE_8 || keyCode == KeyEvent.KEYCODE_9;
+ }
+
+ private static int numericKeyCodeToInt(int keyCode) {
+ return keyCode - KeyEvent.KEYCODE_0;
+ }
+
+ public interface OnValueChangedListener {
+ /**
+ * Called when the value displayed by {@code view} changes.
+ *
+ * @param view the view whose value changed
+ * @param value the new value
+ * @param isValid {@code true} if the value is valid (e.g. within the
+ * range specified by {@link #setRange(int, int)}),
+ * {@code false} otherwise
+ * @param isFinished {@code true} if the no more digits may be entered,
+ * {@code false} if more digits may be entered
+ */
+ void onValueChanged(NumericTextView view, int value, boolean isValid, boolean isFinished);
+ }
+}
diff --git a/core/jni/android/graphics/MinikinUtils.cpp b/core/jni/android/graphics/MinikinUtils.cpp
index d1780d6..7fd288a 100644
--- a/core/jni/android/graphics/MinikinUtils.cpp
+++ b/core/jni/android/graphics/MinikinUtils.cpp
@@ -33,11 +33,11 @@
FontStyle resolved = resolvedFace->fStyle;
/* Prepare minikin FontStyle */
- const std::string& lang = paint->getTextLocale();
- FontLanguage minikinLang(lang.c_str(), lang.size());
+ const std::string& langs = paint->getTextLocales();
+ FontLanguages minikinLangs(langs.c_str(), langs.size());
FontVariant minikinVariant = (paint->getFontVariant() == VARIANT_ELEGANT) ? VARIANT_ELEGANT
: VARIANT_COMPACT;
- FontStyle minikinStyle(minikinLang, minikinVariant, resolved.getWeight(), resolved.getItalic());
+ FontStyle minikinStyle(minikinLangs, minikinVariant, resolved.getWeight(), resolved.getItalic());
/* Prepare minikin Paint */
// Note: it would be nice to handle fractional size values (it would improve smooth zoom
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 314e4b6..6b09450 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -43,12 +43,10 @@
#include "Paint.h"
#include "TypefaceImpl.h"
-#include <vector>
+#include <cassert>
+#include <cstring>
#include <memory>
-
-// temporary for debugging
-#include <Caches.h>
-#include <utils/Log.h>
+#include <vector>
namespace android {
@@ -71,12 +69,12 @@
paint->setTextEncoding(Paint::kGlyphID_TextEncoding);
}
-struct LocaleCacheEntry {
- std::string javaLocale;
- std::string languageTag;
+struct LocalesCacheEntry {
+ std::string javaLocales;
+ std::string languageTags;
};
-static thread_local LocaleCacheEntry sSingleEntryLocaleCache;
+static thread_local LocalesCacheEntry sSingleEntryLocalesCache;
namespace PaintGlue {
enum MoveOpt {
@@ -360,17 +358,57 @@
output[0] = '\0';
}
- static void setTextLocale(JNIEnv* env, jobject clazz, jlong objHandle, jstring locale) {
- Paint* obj = reinterpret_cast<Paint*>(objHandle);
- ScopedUtfChars localeChars(env, locale);
- if (sSingleEntryLocaleCache.javaLocale != localeChars.c_str()) {
- sSingleEntryLocaleCache.javaLocale = localeChars.c_str();
- char langTag[ULOC_FULLNAME_CAPACITY];
- toLanguageTag(langTag, ULOC_FULLNAME_CAPACITY, localeChars.c_str());
- sSingleEntryLocaleCache.languageTag = langTag;
+ static void toLanguageTags(std::string* output, const char* locales) {
+ if (output == NULL) {
+ return;
+ }
+ if (locales == NULL) {
+ output->clear();
+ return;
}
- obj->setTextLocale(sSingleEntryLocaleCache.languageTag);
+ char langTag[ULOC_FULLNAME_CAPACITY];
+ const char* commaLoc = strchr(locales, ',');
+ if (commaLoc == NULL) {
+ assert(locales[0] != '\0'); // the string should not be empty
+ toLanguageTag(langTag, ULOC_FULLNAME_CAPACITY, locales);
+ *output = langTag;
+ return;
+ }
+
+ size_t len = strlen(locales);
+ char locale[len];
+ output->clear();
+ output->reserve(len);
+ const char* lastStart = locales;
+ do {
+ assert(lastStart > commaLoc); // the substring should not be empty
+ strncpy(locale, lastStart, commaLoc - lastStart);
+ locale[commaLoc - lastStart] = '\0';
+ toLanguageTag(langTag, ULOC_FULLNAME_CAPACITY, locale);
+ if (langTag[0] != '\0') {
+ output->append(langTag);
+ output->push_back(',');
+ }
+ lastStart = commaLoc + 1;
+ commaLoc = strchr(lastStart, ',');
+ } while (commaLoc != NULL);
+ assert(lastStart[0] != '\0'); // the final substring should not be empty
+ toLanguageTag(langTag, ULOC_FULLNAME_CAPACITY, lastStart);
+ if (langTag[0] != '\0') {
+ output->append(langTag);
+ }
+ }
+
+ static void setTextLocales(JNIEnv* env, jobject clazz, jlong objHandle, jstring locales) {
+ Paint* obj = reinterpret_cast<Paint*>(objHandle);
+ ScopedUtfChars localesChars(env, locales);
+ if (sSingleEntryLocalesCache.javaLocales != localesChars.c_str()) {
+ sSingleEntryLocalesCache.javaLocales = localesChars.c_str();
+ toLanguageTags(&sSingleEntryLocalesCache.languageTags, localesChars.c_str());
+ }
+
+ obj->setTextLocales(sSingleEntryLocalesCache.languageTags);
}
static jboolean isElegantTextHeight(JNIEnv* env, jobject, jlong paintHandle) {
@@ -952,7 +990,7 @@
{"nSetRasterizer","!(JJ)J", (void*) PaintGlue::setRasterizer},
{"nGetTextAlign","!(J)I", (void*) PaintGlue::getTextAlign},
{"nSetTextAlign","!(JI)V", (void*) PaintGlue::setTextAlign},
- {"nSetTextLocale","!(JLjava/lang/String;)V", (void*) PaintGlue::setTextLocale},
+ {"nSetTextLocales","!(JLjava/lang/String;)V", (void*) PaintGlue::setTextLocales},
{"nIsElegantTextHeight","!(J)Z", (void*) PaintGlue::isElegantTextHeight},
{"nSetElegantTextHeight","!(JZ)V", (void*) PaintGlue::setElegantTextHeight},
{"nGetTextSize","!(J)F", (void*) PaintGlue::getTextSize},
diff --git a/core/jni/android/graphics/Paint.h b/core/jni/android/graphics/Paint.h
index 6df22ff..7a34bc2 100644
--- a/core/jni/android/graphics/Paint.h
+++ b/core/jni/android/graphics/Paint.h
@@ -53,12 +53,12 @@
return mFontFeatureSettings;
}
- void setTextLocale(const std::string &textLocale) {
- mTextLocale = textLocale;
+ void setTextLocales(const std::string &textLocales) {
+ mTextLocales = textLocales;
}
- const std::string& getTextLocale() const {
- return mTextLocale;
+ const std::string& getTextLocales() const {
+ return mTextLocales;
}
void setFontVariant(FontVariant variant) {
@@ -80,7 +80,7 @@
private:
float mLetterSpacing = 0;
std::string mFontFeatureSettings;
- std::string mTextLocale;
+ std::string mTextLocales;
FontVariant mFontVariant;
uint32_t mHyphenEdit = 0;
};
diff --git a/core/jni/android/graphics/PaintImpl.cpp b/core/jni/android/graphics/PaintImpl.cpp
index da85018..d5a0972 100644
--- a/core/jni/android/graphics/PaintImpl.cpp
+++ b/core/jni/android/graphics/PaintImpl.cpp
@@ -23,12 +23,12 @@
namespace android {
Paint::Paint() : SkPaint(),
- mLetterSpacing(0), mFontFeatureSettings(), mTextLocale(), mFontVariant(VARIANT_DEFAULT) {
+ mLetterSpacing(0), mFontFeatureSettings(), mTextLocales(), mFontVariant(VARIANT_DEFAULT) {
}
Paint::Paint(const Paint& paint) : SkPaint(paint),
mLetterSpacing(paint.mLetterSpacing), mFontFeatureSettings(paint.mFontFeatureSettings),
- mTextLocale(paint.mTextLocale), mFontVariant(paint.mFontVariant),
+ mTextLocales(paint.mTextLocales), mFontVariant(paint.mFontVariant),
mHyphenEdit(paint.mHyphenEdit) {
}
@@ -39,7 +39,7 @@
SkPaint::operator=(other);
mLetterSpacing = other.mLetterSpacing;
mFontFeatureSettings = other.mFontFeatureSettings;
- mTextLocale = other.mTextLocale;
+ mTextLocales = other.mTextLocales;
mFontVariant = other.mFontVariant;
mHyphenEdit = other.mHyphenEdit;
return *this;
@@ -49,7 +49,7 @@
return static_cast<const SkPaint&>(a) == static_cast<const SkPaint&>(b)
&& a.mLetterSpacing == b.mLetterSpacing
&& a.mFontFeatureSettings == b.mFontFeatureSettings
- && a.mTextLocale == b.mTextLocale
+ && a.mTextLocales == b.mTextLocales
&& a.mFontVariant == b.mFontVariant
&& a.mHyphenEdit == b.mHyphenEdit;
}
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index a14afa0..41aa9ca 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -114,7 +114,7 @@
return parcel ? parcel->dataCapacity() : 0;
}
-static void android_os_Parcel_setDataSize(JNIEnv* env, jclass clazz, jlong nativePtr, jint size)
+static jlong android_os_Parcel_setDataSize(JNIEnv* env, jclass clazz, jlong nativePtr, jint size)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
@@ -122,7 +122,9 @@
if (err != NO_ERROR) {
signalExceptionForError(env, clazz, err);
}
+ return parcel->getOpenAshmemSize();
}
+ return 0;
}
static void android_os_Parcel_setDataPosition(JNIEnv* env, jclass clazz, jlong nativePtr, jint pos)
@@ -304,7 +306,7 @@
}
}
-static void android_os_Parcel_writeFileDescriptor(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
+static jlong android_os_Parcel_writeFileDescriptor(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
@@ -313,7 +315,9 @@
if (err != NO_ERROR) {
signalExceptionForError(env, clazz, err);
}
+ return parcel->getOpenAshmemSize();
}
+ return 0;
}
static jbyteArray android_os_Parcel_createByteArray(JNIEnv* env, jclass clazz, jlong nativePtr)
@@ -546,12 +550,14 @@
return reinterpret_cast<jlong>(parcel);
}
-static void android_os_Parcel_freeBuffer(JNIEnv* env, jclass clazz, jlong nativePtr)
+static jlong android_os_Parcel_freeBuffer(JNIEnv* env, jclass clazz, jlong nativePtr)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
parcel->freeData();
+ return parcel->getOpenAshmemSize();
}
+ return 0;
}
static void android_os_Parcel_destroy(JNIEnv* env, jclass clazz, jlong nativePtr)
@@ -589,12 +595,12 @@
return ret;
}
-static void android_os_Parcel_unmarshall(JNIEnv* env, jclass clazz, jlong nativePtr,
- jbyteArray data, jint offset, jint length)
+static jlong android_os_Parcel_unmarshall(JNIEnv* env, jclass clazz, jlong nativePtr,
+ jbyteArray data, jint offset, jint length)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel == NULL || length < 0) {
- return;
+ return 0;
}
jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(data, 0);
@@ -608,24 +614,26 @@
env->ReleasePrimitiveArrayCritical(data, array, 0);
}
+ return parcel->getOpenAshmemSize();
}
-static void android_os_Parcel_appendFrom(JNIEnv* env, jclass clazz, jlong thisNativePtr,
- jlong otherNativePtr, jint offset, jint length)
+static jlong android_os_Parcel_appendFrom(JNIEnv* env, jclass clazz, jlong thisNativePtr,
+ jlong otherNativePtr, jint offset, jint length)
{
Parcel* thisParcel = reinterpret_cast<Parcel*>(thisNativePtr);
if (thisParcel == NULL) {
- return;
+ return 0;
}
Parcel* otherParcel = reinterpret_cast<Parcel*>(otherNativePtr);
if (otherParcel == NULL) {
- return;
+ return thisParcel->getOpenAshmemSize();
}
status_t err = thisParcel->appendFrom(otherParcel, offset, length);
if (err != NO_ERROR) {
signalExceptionForError(env, clazz, err);
}
+ return thisParcel->getOpenAshmemSize();
}
static jboolean android_os_Parcel_hasFileDescriptors(JNIEnv* env, jclass clazz, jlong nativePtr)
@@ -718,7 +726,7 @@
{"nativeDataAvail", "(J)I", (void*)android_os_Parcel_dataAvail},
{"nativeDataPosition", "(J)I", (void*)android_os_Parcel_dataPosition},
{"nativeDataCapacity", "(J)I", (void*)android_os_Parcel_dataCapacity},
- {"nativeSetDataSize", "(JI)V", (void*)android_os_Parcel_setDataSize},
+ {"nativeSetDataSize", "(JI)J", (void*)android_os_Parcel_setDataSize},
{"nativeSetDataPosition", "(JI)V", (void*)android_os_Parcel_setDataPosition},
{"nativeSetDataCapacity", "(JI)V", (void*)android_os_Parcel_setDataCapacity},
@@ -733,7 +741,7 @@
{"nativeWriteDouble", "(JD)V", (void*)android_os_Parcel_writeDouble},
{"nativeWriteString", "(JLjava/lang/String;)V", (void*)android_os_Parcel_writeString},
{"nativeWriteStrongBinder", "(JLandroid/os/IBinder;)V", (void*)android_os_Parcel_writeStrongBinder},
- {"nativeWriteFileDescriptor", "(JLjava/io/FileDescriptor;)V", (void*)android_os_Parcel_writeFileDescriptor},
+ {"nativeWriteFileDescriptor", "(JLjava/io/FileDescriptor;)J", (void*)android_os_Parcel_writeFileDescriptor},
{"nativeCreateByteArray", "(J)[B", (void*)android_os_Parcel_createByteArray},
{"nativeReadBlob", "(J)[B", (void*)android_os_Parcel_readBlob},
@@ -751,12 +759,12 @@
{"clearFileDescriptor", "(Ljava/io/FileDescriptor;)V", (void*)android_os_Parcel_clearFileDescriptor},
{"nativeCreate", "()J", (void*)android_os_Parcel_create},
- {"nativeFreeBuffer", "(J)V", (void*)android_os_Parcel_freeBuffer},
+ {"nativeFreeBuffer", "(J)J", (void*)android_os_Parcel_freeBuffer},
{"nativeDestroy", "(J)V", (void*)android_os_Parcel_destroy},
{"nativeMarshall", "(J)[B", (void*)android_os_Parcel_marshall},
- {"nativeUnmarshall", "(J[BII)V", (void*)android_os_Parcel_unmarshall},
- {"nativeAppendFrom", "(JJII)V", (void*)android_os_Parcel_appendFrom},
+ {"nativeUnmarshall", "(J[BII)J", (void*)android_os_Parcel_unmarshall},
+ {"nativeAppendFrom", "(JJII)J", (void*)android_os_Parcel_appendFrom},
{"nativeHasFileDescriptors", "(J)Z", (void*)android_os_Parcel_hasFileDescriptors},
{"nativeWriteInterfaceToken", "(JLjava/lang/String;)V", (void*)android_os_Parcel_writeInterfaceToken},
{"nativeEnforceInterface", "(JLjava/lang/String;)V", (void*)android_os_Parcel_enforceInterface},
diff --git a/core/res/res/color/primary_text_secondary_when_activated_material_inverse.xml b/core/res/res/color/primary_text_secondary_when_activated_material_inverse.xml
index baa8958..905fd4d 100644
--- a/core/res/res/color/primary_text_secondary_when_activated_material_inverse.xml
+++ b/core/res/res/color/primary_text_secondary_when_activated_material_inverse.xml
@@ -1,8 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2015 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
+ android:state_enabled="false"
+ android:color="?attr/textColorSecondaryInverse"
+ android:alpha="?attr/disabledAlpha" />
+ <item
android:state_activated="true"
android:color="?attr/textColorPrimaryInverse" />
<item
android:color="?attr/textColorSecondaryInverse" />
-</selector>
\ No newline at end of file
+</selector>
diff --git a/core/res/res/drawable/time_picker_editable_background.xml b/core/res/res/drawable/time_picker_editable_background.xml
new file mode 100644
index 0000000..72e863b
--- /dev/null
+++ b/core/res/res/drawable/time_picker_editable_background.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2015 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true">
+ <layer-list>
+ <item android:gravity="bottom"
+ android:height="10dp">
+ <shape android:shape="line"
+ android:tint="?attr/textColorPrimaryInverse">
+ <stroke android:color="@color/white"
+ android:width="2dp" />
+ </shape>
+ </item>
+ </layer-list>
+ </item>
+ <item android:drawable="@color/transparent" />
+</selector>
diff --git a/core/res/res/layout-land/time_picker_material.xml b/core/res/res/layout-land/time_picker_material.xml
index bb347cb..7a0c38f 100644
--- a/core/res/res/layout-land/time_picker_material.xml
+++ b/core/res/res/layout-land/time_picker_material.xml
@@ -51,15 +51,17 @@
<!-- The hour should always be to the left of the separator,
regardless of the current locale's layout direction. -->
- <TextView
+ <com.android.internal.widget.NumericTextView
android:id="@+id/hours"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
+ android:background="@drawable/time_picker_editable_background"
android:singleLine="true"
android:ellipsize="none"
android:gravity="right"
- android:includeFontPadding="false" />
+ android:focusable="true"
+ android:nextFocusForward="@+id/minutes" />
<TextView
android:id="@+id/separator"
@@ -71,57 +73,56 @@
<!-- The minutes should always be to the right of the separator,
regardless of the current locale's layout direction. -->
- <TextView
+ <com.android.internal.widget.NumericTextView
android:id="@+id/minutes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
+ android:background="@drawable/time_picker_editable_background"
android:singleLine="true"
android:ellipsize="none"
android:gravity="left"
- android:includeFontPadding="false" />
+ android:focusable="true"
+ android:nextFocusForward="@+id/am_label" />
</LinearLayout>
- <!-- The layout alignment of this view will switch between toRightOf
- @id/minutes and toLeftOf @id/hours depending on the locale. -->
- <LinearLayout
+ <RadioGroup
android:id="@+id/ampm_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/time_layout"
android:layout_centerHorizontal="true"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp"
android:orientation="vertical"
android:layoutDirection="locale">
-
- <CheckedTextView
+ <RadioButton
android:id="@+id/am_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:minHeight="48dp"
- android:minWidth="48dp"
- android:gravity="bottom"
+ android:padding="8dp"
+ android:layout_marginBottom="-8dp"
android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
- android:paddingStart="@dimen/timepicker_ampm_horizontal_padding"
- android:paddingEnd="@dimen/timepicker_ampm_horizontal_padding"
- android:paddingTop="4dp"
- android:paddingBottom="6dp"
android:lines="1"
- android:ellipsize="none" />
-
- <CheckedTextView
+ android:ellipsize="none"
+ android:focusable="true"
+ android:background="?android:attr/selectableItemBackground"
+ android:includeFontPadding="false"
+ android:nextFocusForward="@+id/pm_label"
+ android:button="@null" />
+ <RadioButton
android:id="@+id/pm_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:minHeight="48dp"
- android:minWidth="48dp"
- android:gravity="top"
+ android:padding="8dp"
android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
- android:paddingStart="@dimen/timepicker_ampm_horizontal_padding"
- android:paddingEnd="@dimen/timepicker_ampm_horizontal_padding"
android:lines="1"
android:ellipsize="none"
- android:includeFontPadding="false" />
- </LinearLayout>
+ android:focusable="true"
+ android:background="?android:attr/selectableItemBackground"
+ android:includeFontPadding="false"
+ android:button="@null" />
+ </RadioGroup>
</RelativeLayout>
<ViewStub
diff --git a/core/res/res/layout/time_picker_header_material.xml b/core/res/res/layout/time_picker_header_material.xml
index acdc509..7019ced 100644
--- a/core/res/res/layout/time_picker_header_material.xml
+++ b/core/res/res/layout/time_picker_header_material.xml
@@ -21,23 +21,24 @@
android:id="@+id/time_header"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="horizontal"
- android:padding="@dimen/timepicker_separator_padding"
android:paddingStart="16dp"
android:paddingEnd="16dp">
<!-- The hour should always be to the left of the separator,
regardless of the current locale's layout direction. -->
- <TextView
+ <com.android.internal.widget.NumericTextView
android:id="@+id/hours"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/separator"
android:layout_alignBaseline="@+id/separator"
android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
+ android:background="@drawable/time_picker_editable_background"
android:singleLine="true"
android:ellipsize="none"
- android:gravity="right" />
+ android:gravity="right"
+ android:focusable="true"
+ android:nextFocusForward="@+id/minutes" />
<TextView
android:id="@+id/separator"
@@ -51,50 +52,57 @@
<!-- The minutes should always be to the left of the separator,
regardless of the current locale's layout direction. -->
- <TextView
+ <com.android.internal.widget.NumericTextView
android:id="@+id/minutes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/separator"
android:layout_alignBaseline="@+id/separator"
android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
+ android:background="@drawable/time_picker_editable_background"
android:singleLine="true"
android:ellipsize="none"
- android:gravity="left" />
+ android:gravity="left"
+ android:focusable="true"
+ android:nextFocusForward="@+id/am_label" />
<!-- The layout alignment of this view will switch between toRightOf
@id/minutes and toLeftOf @id/hours depending on the locale. -->
- <LinearLayout
+ <RadioGroup
android:id="@+id/ampm_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/minutes"
android:layout_alignBaseline="@+id/minutes"
- android:paddingStart="8dp"
- android:paddingEnd="8dp"
+ android:paddingStart="4dp"
+ android:paddingEnd="4dp"
android:orientation="vertical"
android:baselineAlignedChildIndex="1">
- <CheckedTextView
+ <RadioButton
android:id="@+id/am_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:minWidth="48dp"
- android:minHeight="48dp"
- android:gravity="bottom"
- android:paddingTop="@dimen/timepicker_am_top_padding"
+ android:padding="8dp"
+ android:layout_marginBottom="-8dp"
android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
android:lines="1"
- android:ellipsize="none" />
- <CheckedTextView
+ android:ellipsize="none"
+ android:focusable="true"
+ android:background="?android:attr/selectableItemBackground"
+ android:includeFontPadding="false"
+ android:nextFocusForward="@+id/pm_label"
+ android:button="@null" />
+ <RadioButton
android:id="@+id/pm_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:minWidth="48dp"
- android:minHeight="48dp"
- android:gravity="top"
- android:paddingTop="@dimen/timepicker_pm_top_padding"
+ android:padding="8dp"
android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
android:lines="1"
- android:ellipsize="none" />
- </LinearLayout>
+ android:ellipsize="none"
+ android:focusable="true"
+ android:background="?android:attr/selectableItemBackground"
+ android:includeFontPadding="false"
+ android:button="@null" />
+ </RadioGroup>
</RelativeLayout>
diff --git a/core/res/res/values-mcc204-mnc04/config.xml b/core/res/res/values-mcc204-mnc04/config.xml
index fb639ca..0f39e42 100755
--- a/core/res/res/values-mcc204-mnc04/config.xml
+++ b/core/res/res/values-mcc204-mnc04/config.xml
@@ -44,14 +44,5 @@
<item>false</item>
</string-array>
- <!-- String containing the apn value for tethering. May be overriden by secure settings
- TETHER_DUN_APN. Value is a comma separated series of strings:
- "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type",
- Or string format of ApnSettingV3.
- note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
- <string-array translatable="false" name="config_tether_apndata">
- <item>[ApnSettingV3]SaskTel Tethering,inet.stm.sk.ca,,,,,,,,,204,04,,DUN,,,true,0,,,,,,,gid,5A</item>
- </string-array>
-
<string translatable="false" name="prohibit_manual_network_selection_in_gobal_mode">true;BAE0000000000000</string>
</resources>
diff --git a/core/res/res/values-mcc302-mnc780/config.xml b/core/res/res/values-mcc302-mnc780/config.xml
index 51abd36..a48f695 100644
--- a/core/res/res/values-mcc302-mnc780/config.xml
+++ b/core/res/res/values-mcc302-mnc780/config.xml
@@ -21,25 +21,6 @@
for different hardware and product builds. -->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Array of ConnectivityManager.TYPE_xxxx values allowable for tethering -->
- <!-- Common options are [1, 4] for TYPE_WIFI and TYPE_MOBILE_DUN or
- <!== [0,1,5,7] for TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI and TYPE_BLUETOOTH -->
- <integer-array translatable="false" name="config_tether_upstream_types">
- <item>1</item>
- <item>4</item>
- <item>7</item>
- <item>9</item>
- </integer-array>
-
- <!-- String containing the apn value for tethering. May be overriden by secure settings
- TETHER_DUN_APN. Value is a comma separated series of strings:
- "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type",
- Or string format of ApnSettingV3.
- note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
- <string-array translatable="false" name="config_tether_apndata">
- <item>SaskTel Tethering,inet.stm.sk.ca,,,,,,,,,302,780,,DUN</item>
- </string-array>
-
<!-- Don't use roaming icon for considered operators -->
<string-array translatable="false" name="config_operatorConsideredNonRoaming">
<item>302</item>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 8e36eb0..093ea80 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -323,14 +323,10 @@
<attr name="windowBackground" format="reference" />
<!-- Drawable to draw selectively within the inset areas when the windowBackground
has been set to null. This protects against seeing visual garbage in the
- surface when the app has not drawn any content into this area. -->
+ surface when the app has not drawn any content into this area. One example is
+ when the user is resizing a window of an activity that has
+ {@link android.R.attr#resizeableActivity} set for multi-window mode. -->
<attr name="windowBackgroundFallback" format="reference" />
- <!-- Drawable used to fill in areas the app has not rendered content to yet when the user is
- resizing the window of an activity that has {@link android.R.attr#resizeableActivity}
- set for multi-window mode. If unset, the system will try to use windowBackground if
- set, then windowBackgroundFallback if set. Otherwise, the system default resizing
- background color -->
- <attr name="windowResizingBackground" format="reference" />
<!-- Drawable to use as a frame around the window. -->
<attr name="windowFrame" format="reference" />
<!-- Flag indicating whether there should be no title on this window. -->
@@ -1850,7 +1846,6 @@
<declare-styleable name="Window">
<attr name="windowBackground" />
<attr name="windowBackgroundFallback" />
- <attr name="windowResizingBackground" />
<attr name="windowContentOverlay" />
<attr name="windowFrame" />
<attr name="windowNoTitle" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d20b09f..74c745b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1249,6 +1249,9 @@
<!-- Operating volatage for bluetooth controller. 0 by default-->
<integer translatable="false" name="config_bluetooth_operating_voltage_mv">4</integer>
+ <!-- Whether supported profiles should be reloaded upon enabling bluetooth -->
+ <bool name="config_bluetooth_reload_supported_profiles_when_enabled">false</bool>
+
<!-- The default data-use polling period. -->
<integer name="config_datause_polling_period_sec">600</integer>
@@ -2349,9 +2352,6 @@
is non-interactive. -->
<bool name="config_cameraDoubleTapPowerGestureEnabled">true</bool>
- <!-- Default background color to use when resizing a window if the app didn't specify one. -->
- <integer name="config_windowResizingBackgroundColorARGB">0xFF808080</integer>
-
<!-- Name of the component to handle network policy notifications. If present,
disables NetworkPolicyManagerService's presentation of data-usage notifications. -->
<string translatable="false" name="config_networkPolicyNotificationComponent"></string>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 839e81b..c05b585 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2677,7 +2677,7 @@
<public type="attr" name="level" />
<public type="attr" name="contextPopupMenuStyle" />
<public type="attr" name="textAppearancePopupMenuHeader" />
- <public type="attr" name="windowResizingBackground" />
+ <public type="attr" name="windowBackgroundFallback" />
<public type="style" name="Theme.Material.DayNight" />
<public type="style" name="Theme.Material.DayNight.DarkActionBar" />
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 6f239e6..3baddbb 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -370,6 +370,7 @@
<java-symbol type="integer" name="config_bluetooth_rx_cur_ma" />
<java-symbol type="integer" name="config_bluetooth_tx_cur_ma" />
<java-symbol type="integer" name="config_bluetooth_operating_voltage_mv" />
+ <java-symbol type="bool" name="config_bluetooth_reload_supported_profiles_when_enabled" />
<java-symbol type="integer" name="config_cursorWindowSize" />
<java-symbol type="integer" name="config_drawLockTimeoutMillis" />
<java-symbol type="integer" name="config_doublePressOnPowerBehavior" />
@@ -2332,7 +2333,5 @@
<java-symbol type="string" name="config_iccHotswapPromptForRestartDialogComponent" />
- <java-symbol type="integer" name="config_windowResizingBackgroundColorARGB" />
-
<java-symbol type="string" name="config_packagedKeyboardName" />
</resources>
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 11b4a9e..35182f9 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1335,7 +1335,7 @@
return;
}
mLocales = new LocaleList(locale);
- nSetTextLocale(mNativePaint, locale.toString());
+ nSetTextLocales(mNativePaint, locale.toString());
}
/**
@@ -1372,8 +1372,7 @@
}
if (locales.equals(mLocales)) return;
mLocales = locales;
- // TODO: Pass the whole LocaleList to native code
- nSetTextLocale(mNativePaint, locales.getPrimary().toString());
+ nSetTextLocales(mNativePaint, locales.toLanguageTags());
}
/**
@@ -2715,8 +2714,8 @@
private static native void nSetTextAlign(long paintPtr,
int align);
- private static native void nSetTextLocale(long paintPtr,
- String locale);
+ private static native void nSetTextLocales(long paintPtr,
+ String locales);
private static native float nGetTextAdvances(long paintPtr, long typefacePtr,
char[] text, int index, int count, int contextIndex, int contextCount,
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 842f575..4385e70 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -27,6 +27,7 @@
utils/LinearAllocator.cpp \
utils/NinePatchImpl.cpp \
utils/StringUtils.cpp \
+ utils/TestWindowContext.cpp \
AmbientShadow.cpp \
AnimationContext.cpp \
Animator.cpp \
@@ -84,8 +85,7 @@
hwui_cflags := \
-DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES \
-DATRACE_TAG=ATRACE_TAG_VIEW -DLOG_TAG=\"OpenGLRenderer\" \
- -Wall -Wno-unused-parameter -Wunreachable-code \
- -ffast-math -O3 -Werror
+ -Wall -Wno-unused-parameter -Wunreachable-code -Werror
ifeq (true, $(HWUI_NEW_OPS))
hwui_src_files += \
@@ -261,7 +261,8 @@
LOCAL_SRC_FILES += \
microbench/DisplayListCanvasBench.cpp \
- microbench/LinearAllocatorBench.cpp
+ microbench/LinearAllocatorBench.cpp \
+ microbench/ShadowBench.cpp
ifeq (true, $(HWUI_NEW_OPS))
LOCAL_SRC_FILES += \
diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp
index 12a3e76..0835c29 100644
--- a/libs/hwui/TessellationCache.cpp
+++ b/libs/hwui/TessellationCache.cpp
@@ -217,7 +217,7 @@
}
}
-static void tessellateShadows(
+void tessellateShadows(
const Matrix4* drawTransform, const Rect* localClip,
bool isCasterOpaque, const SkPath* casterPerimeter,
const Matrix4* casterTransformXY, const Matrix4* casterTransformZ,
diff --git a/libs/hwui/TessellationCache.h b/libs/hwui/TessellationCache.h
index b54666b..06e567e 100644
--- a/libs/hwui/TessellationCache.h
+++ b/libs/hwui/TessellationCache.h
@@ -17,16 +17,22 @@
#ifndef ANDROID_HWUI_TESSELLATION_CACHE_H
#define ANDROID_HWUI_TESSELLATION_CACHE_H
-#include <utils/LruCache.h>
-#include <utils/Mutex.h>
-
#include "Debug.h"
+#include "Matrix.h"
+#include "Rect.h"
+#include "Vector.h"
+#include "thread/TaskProcessor.h"
#include "utils/Macros.h"
#include "utils/Pair.h"
+#include <SkPaint.h>
+
+#include <utils/LruCache.h>
+#include <utils/Mutex.h>
+#include <utils/StrongPointer.h>
+
class SkBitmap;
class SkCanvas;
-class SkPaint;
class SkPath;
struct SkRect;
@@ -185,6 +191,13 @@
}; // class TessellationCache
+void tessellateShadows(
+ const Matrix4* drawTransform, const Rect* localClip,
+ bool isCasterOpaque, const SkPath* casterPerimeter,
+ const Matrix4* casterTransformXY, const Matrix4* casterTransformZ,
+ const Vector3& lightCenter, float lightRadius,
+ VertexBuffer& ambientBuffer, VertexBuffer& spotBuffer);
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/Vector.h b/libs/hwui/Vector.h
index 7c3f2fd..6367dbd 100644
--- a/libs/hwui/Vector.h
+++ b/libs/hwui/Vector.h
@@ -135,8 +135,8 @@
}
- void dump() {
- ALOGD("Vector3[%.2f, %.2f, %.2f]", x, y, z);
+ void dump(const char* label = "Vector3") const {
+ ALOGD("%s[%.2f, %.2f, %.2f]", label, x, y, z);
}
};
diff --git a/libs/hwui/microbench/ShadowBench.cpp b/libs/hwui/microbench/ShadowBench.cpp
new file mode 100644
index 0000000..bd51693
--- /dev/null
+++ b/libs/hwui/microbench/ShadowBench.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <benchmark/Benchmark.h>
+
+#include "Matrix.h"
+#include "Rect.h"
+#include "Vector.h"
+#include "VertexBuffer.h"
+#include "TessellationCache.h"
+#include "microbench/MicroBench.h"
+
+#include <SkPath.h>
+
+#include <memory>
+
+using namespace android;
+using namespace android::uirenderer;
+
+struct ShadowTestData {
+ Matrix4 drawTransform;
+ Rect localClip;
+ Matrix4 casterTransformXY;
+ Matrix4 casterTransformZ;
+ Vector3 lightCenter;
+ float lightRadius;
+};
+
+void createShadowTestData(ShadowTestData* out) {
+ static float SAMPLE_DRAW_TRANSFORM[] = {
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1,
+ };
+ static float SAMPLE_CASTERXY[] = {
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 32, 32, 0, 1,
+ };
+ static float SAMPLE_CASTERZ[] = {
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 32, 32, 32, 1,
+ };
+ static Rect SAMPLE_CLIP(0, 0, 1536, 2048);
+ static Vector3 SAMPLE_LIGHT_CENTER{768, -400, 1600};
+ static float SAMPLE_LIGHT_RADIUS = 1600;
+
+ out->drawTransform.load(SAMPLE_DRAW_TRANSFORM);
+ out->localClip = SAMPLE_CLIP;
+ out->casterTransformXY.load(SAMPLE_CASTERXY);
+ out->casterTransformZ.load(SAMPLE_CASTERZ);
+ out->lightCenter = SAMPLE_LIGHT_CENTER;
+ out->lightRadius = SAMPLE_LIGHT_RADIUS;
+}
+
+static inline void tessellateShadows(ShadowTestData& testData, bool opaque,
+ const SkPath& shape, VertexBuffer* ambient, VertexBuffer* spot) {
+ tessellateShadows(&testData.drawTransform, &testData.localClip,
+ opaque, &shape, &testData.casterTransformXY,
+ &testData.casterTransformZ, testData.lightCenter,
+ testData.lightRadius, *ambient, *spot);
+}
+
+BENCHMARK_NO_ARG(BM_TessellateShadows_roundrect_opaque);
+void BM_TessellateShadows_roundrect_opaque::Run(int iters) {
+ ShadowTestData shadowData;
+ createShadowTestData(&shadowData);
+ SkPath path;
+ path.reset();
+ path.addRoundRect(SkRect::MakeLTRB(0, 0, 100, 100), 5, 5);
+
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; i++) {
+ std::unique_ptr<VertexBuffer> ambient(new VertexBuffer);
+ std::unique_ptr<VertexBuffer> spot(new VertexBuffer);
+ tessellateShadows(shadowData, true, path, ambient.get(), spot.get());
+ MicroBench::DoNotOptimize(ambient.get());
+ MicroBench::DoNotOptimize(spot.get());
+ }
+ StopBenchmarkTiming();
+}
+
+BENCHMARK_NO_ARG(BM_TessellateShadows_roundrect_translucent);
+void BM_TessellateShadows_roundrect_translucent::Run(int iters) {
+ ShadowTestData shadowData;
+ createShadowTestData(&shadowData);
+ SkPath path;
+ path.reset();
+ path.addRoundRect(SkRect::MakeLTRB(0, 0, 100, 100), 5, 5);
+
+ StartBenchmarkTiming();
+ for (int i = 0; i < iters; i++) {
+ std::unique_ptr<VertexBuffer> ambient(new VertexBuffer);
+ std::unique_ptr<VertexBuffer> spot(new VertexBuffer);
+ tessellateShadows(shadowData, false, path, ambient.get(), spot.get());
+ MicroBench::DoNotOptimize(ambient.get());
+ MicroBench::DoNotOptimize(spot.get());
+ }
+ StopBenchmarkTiming();
+}
diff --git a/libs/hwui/utils/TestWindowContext.cpp b/libs/hwui/utils/TestWindowContext.cpp
new file mode 100644
index 0000000..84aae75
--- /dev/null
+++ b/libs/hwui/utils/TestWindowContext.cpp
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "TestWindowContext.h"
+
+#include "AnimationContext.h"
+#include "DisplayListCanvas.h"
+#include "IContextFactory.h"
+#include "RenderNode.h"
+#include "SkTypes.h"
+#include "gui/BufferQueue.h"
+#include "gui/CpuConsumer.h"
+#include "gui/IGraphicBufferConsumer.h"
+#include "gui/IGraphicBufferProducer.h"
+#include "gui/Surface.h"
+#include "renderthread/RenderProxy.h"
+
+
+namespace {
+
+/**
+ * Helper class for setting up android::uirenderer::renderthread::RenderProxy.
+ */
+class ContextFactory : public android::uirenderer::IContextFactory {
+public:
+ android::uirenderer::AnimationContext* createAnimationContext
+ (android::uirenderer::renderthread::TimeLord& clock) override {
+ return new android::uirenderer::AnimationContext(clock);
+ }
+};
+
+} // anonymous namespace
+
+namespace android {
+namespace uirenderer {
+
+/**
+ Android strong pointers (android::sp) can't hold forward-declared classes,
+ so we have to use pointer-to-implementation here if we want to hide the
+ details from our non-framework users.
+*/
+
+class TestWindowContext::TestWindowData {
+
+public:
+
+ TestWindowData(SkISize size) : mSize(size) {
+ android::BufferQueue::createBufferQueue(&mProducer, &mConsumer);
+ mCpuConsumer = new android::CpuConsumer(mConsumer, 1);
+ mCpuConsumer->setName(android::String8("TestWindowContext"));
+ mCpuConsumer->setDefaultBufferSize(mSize.width(), mSize.height());
+ mAndroidSurface = new android::Surface(mProducer);
+ native_window_set_buffers_dimensions(mAndroidSurface.get(),
+ mSize.width(), mSize.height());
+ native_window_set_buffers_format(mAndroidSurface.get(),
+ android::PIXEL_FORMAT_RGBA_8888);
+ native_window_set_usage(mAndroidSurface.get(),
+ GRALLOC_USAGE_SW_READ_OFTEN |
+ GRALLOC_USAGE_SW_WRITE_NEVER |
+ GRALLOC_USAGE_HW_RENDER);
+ mRootNode.reset(new android::uirenderer::RenderNode());
+ mRootNode->incStrong(nullptr);
+ mRootNode->mutateStagingProperties().setLeftTopRightBottom
+ (0, 0, mSize.width(), mSize.height());
+ mRootNode->mutateStagingProperties().setClipToBounds(false);
+ mRootNode->setPropertyFieldsDirty(android::uirenderer::RenderNode::GENERIC);
+ ContextFactory factory;
+ mProxy.reset
+ (new android::uirenderer::renderthread::RenderProxy(false,
+ mRootNode.get(),
+ &factory));
+ mProxy->loadSystemProperties();
+ mProxy->initialize(mAndroidSurface.get());
+ float lightX = mSize.width() / 2.0f;
+ android::uirenderer::Vector3 lightVector { lightX, -200.0f, 800.0f };
+ mProxy->setup(mSize.width(), mSize.height(), 800.0f,
+ 255 * 0.075f, 255 * 0.15f);
+ mProxy->setLightCenter(lightVector);
+ mCanvas.reset(new
+ android::uirenderer::DisplayListCanvas(mSize.width(),
+ mSize.height()));
+ }
+
+ SkCanvas* prepareToDraw() {
+ //mCanvas->reset(mSize.width(), mSize.height());
+ mCanvas->clipRect(0, 0, mSize.width(), mSize.height(),
+ SkRegion::Op::kReplace_Op);
+ return mCanvas->asSkCanvas();
+ }
+
+ void finishDrawing() {
+ mRootNode->setStagingDisplayList(mCanvas->finishRecording());
+ mProxy->syncAndDrawFrame();
+ // Surprisingly, calling mProxy->fence() here appears to make no difference to
+ // the timings we record.
+ }
+
+ void fence() {
+ mProxy->fence();
+ }
+
+ bool capturePixels(SkBitmap* bmp) {
+ SkImageInfo destinationConfig =
+ SkImageInfo::Make(mSize.width(), mSize.height(),
+ kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+ bmp->allocPixels(destinationConfig);
+ sk_memset32((uint32_t*) bmp->getPixels(), SK_ColorRED,
+ mSize.width() * mSize.height());
+
+ android::CpuConsumer::LockedBuffer nativeBuffer;
+ android::status_t retval = mCpuConsumer->lockNextBuffer(&nativeBuffer);
+ if (retval == android::BAD_VALUE) {
+ SkDebugf("write_canvas_png() got no buffer; returning transparent");
+ // No buffer ready to read - commonly triggered by dm sending us
+ // a no-op source, or calling code that doesn't do anything on this
+ // backend.
+ bmp->eraseColor(SK_ColorTRANSPARENT);
+ return false;
+ } else if (retval) {
+ SkDebugf("Failed to lock buffer to read pixels: %d.", retval);
+ return false;
+ }
+
+ // Move the pixels into the destination SkBitmap
+
+ SK_ALWAYSBREAK(nativeBuffer.format == android::PIXEL_FORMAT_RGBA_8888 &&
+ "Native buffer not RGBA!");
+ SkImageInfo nativeConfig =
+ SkImageInfo::Make(nativeBuffer.width, nativeBuffer.height,
+ kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+
+ // Android stride is in pixels, Skia stride is in bytes
+ SkBitmap nativeWrapper;
+ bool success =
+ nativeWrapper.installPixels(nativeConfig, nativeBuffer.data, nativeBuffer.stride * 4);
+ if (!success) {
+ SkDebugf("Failed to wrap HWUI buffer in a SkBitmap");
+ return false;
+ }
+
+ SK_ALWAYSBREAK(bmp->colorType() == kRGBA_8888_SkColorType &&
+ "Destination buffer not RGBA!");
+ success =
+ nativeWrapper.readPixels(destinationConfig, bmp->getPixels(), bmp->rowBytes(), 0, 0);
+ if (!success) {
+ SkDebugf("Failed to extract pixels from HWUI buffer");
+ return false;
+ }
+
+ mCpuConsumer->unlockBuffer(nativeBuffer);
+
+ return true;
+ }
+
+private:
+
+ std::unique_ptr<android::uirenderer::RenderNode> mRootNode;
+ std::unique_ptr<android::uirenderer::renderthread::RenderProxy> mProxy;
+ std::unique_ptr<android::uirenderer::DisplayListCanvas> mCanvas;
+ android::sp<android::IGraphicBufferProducer> mProducer;
+ android::sp<android::IGraphicBufferConsumer> mConsumer;
+ android::sp<android::CpuConsumer> mCpuConsumer;
+ android::sp<android::Surface> mAndroidSurface;
+ SkISize mSize;
+};
+
+
+TestWindowContext::TestWindowContext() :
+ mData (nullptr) { }
+
+void TestWindowContext::initialize(int width, int height) {
+ mData = new TestWindowData(SkISize::Make(width, height));
+}
+
+SkCanvas* TestWindowContext::prepareToDraw() {
+ return mData ? mData->prepareToDraw() : nullptr;
+}
+
+void TestWindowContext::finishDrawing() {
+ if (mData) {
+ mData->finishDrawing();
+ }
+}
+
+void TestWindowContext::fence() {
+ if (mData) {
+ mData->fence();
+ }
+}
+
+bool TestWindowContext::capturePixels(SkBitmap* bmp) {
+ return mData ? mData->capturePixels(bmp) : false;
+}
+
+} // namespace uirenderer
+} // namespace android
+
diff --git a/libs/hwui/utils/TestWindowContext.h b/libs/hwui/utils/TestWindowContext.h
new file mode 100644
index 0000000..445a11b
--- /dev/null
+++ b/libs/hwui/utils/TestWindowContext.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef TESTWINDOWCONTEXT_H_
+#define TESTWINDOWCONTEXT_H_
+
+#include <cutils/compiler.h>
+
+class SkBitmap;
+class SkCanvas;
+
+namespace android {
+
+namespace uirenderer {
+
+/**
+ Wraps all libui/libgui classes and types that external tests depend on,
+ exposing only primitive Skia types.
+*/
+
+class ANDROID_API TestWindowContext {
+
+public:
+
+ TestWindowContext();
+
+ /// We need to know the size of the window.
+ void initialize(int width, int height);
+
+ /// Returns a canvas to draw into; NULL if not yet initialize()d.
+ SkCanvas* prepareToDraw();
+
+ /// Flushes all drawing commands to HWUI; no-op if not yet initialize()d.
+ void finishDrawing();
+
+ /// Blocks until HWUI has processed all pending drawing commands;
+ /// no-op if not yet initialize()d.
+ void fence();
+
+ /// Returns false if not yet initialize()d.
+ bool capturePixels(SkBitmap* bmp);
+
+private:
+ /// Hidden implementation.
+ class TestWindowData;
+
+ TestWindowData* mData;
+
+};
+
+} // namespace uirenderer
+} // namespace android
+
+#endif // TESTWINDOWCONTEXT_H_
+
diff --git a/libs/packagelistparser/Android.mk b/libs/packagelistparser/Android.mk
deleted file mode 100644
index 802a3cb..0000000
--- a/libs/packagelistparser/Android.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-#########################
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libpackagelistparser
-LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := packagelistparser.c
-LOCAL_COPY_HEADERS_TO := packagelistparser
-LOCAL_COPY_HEADERS := packagelistparser.h
-LOCAL_SHARED_LIBRARIES := liblog
-
-LOCAL_CLANG := true
-LOCAL_SANITIZE := integer
-
-include $(BUILD_SHARED_LIBRARY)
-
-#########################
-include $(CLEAR_VARS)
-
-
-LOCAL_MODULE := libpackagelistparser
-LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := packagelistparser.c
-LOCAL_COPY_HEADERS_TO := packagelistparser
-LOCAL_COPY_HEADERS := packagelistparser.h
-LOCAL_STATIC_LIBRARIES := liblog
-
-LOCAL_CLANG := true
-LOCAL_SANITIZE := integer
-
-include $(BUILD_STATIC_LIBRARY)
diff --git a/libs/packagelistparser/packagelistparser.c b/libs/packagelistparser/packagelistparser.c
deleted file mode 100644
index 3e2539c..0000000
--- a/libs/packagelistparser/packagelistparser.c
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- * Copyright 2015, Intel Corporation
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * Written by William Roberts <william.c.roberts@intel.com>
- *
- */
-
-#include <errno.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <sys/limits.h>
-
-#define LOG_TAG "packagelistparser"
-#include <utils/Log.h>
-
-#include "packagelistparser.h"
-
-#define CLOGE(fmt, ...) \
- do {\
- IF_ALOGE() {\
- ALOGE(fmt, ##__VA_ARGS__);\
- }\
- } while(0)
-
-static size_t get_gid_cnt(const char *gids)
-{
- size_t cnt;
-
- if (*gids == '\0') {
- return 0;
- }
-
- if (!strcmp(gids, "none")) {
- return 0;
- }
-
- for (cnt = 1; gids[cnt]; gids[cnt] == ',' ? cnt++ : *gids++)
- ;
-
- return cnt;
-}
-
-static bool parse_gids(char *gids, gid_t *gid_list, size_t *cnt)
-{
- gid_t gid;
- char* token;
- char *endptr;
- size_t cmp = 0;
-
- while ((token = strsep(&gids, ",\r\n"))) {
-
- if (cmp > *cnt) {
- return false;
- }
-
- gid = strtoul(token, &endptr, 10);
- if (*endptr != '\0') {
- return false;
- }
-
- /*
- * if unsigned long is greater than size of gid_t,
- * prevent a truncation based roll-over
- */
- if (gid > GID_MAX) {
- CLOGE("A gid in field \"gid list\" greater than GID_MAX");
- return false;
- }
-
- gid_list[cmp++] = gid;
- }
- return true;
-}
-
-extern bool packagelist_parse(pfn_on_package callback, void *userdata)
-{
-
- FILE *fp;
- char *cur;
- char *next;
- char *endptr;
- unsigned long tmp;
- ssize_t bytesread;
-
- bool rc = false;
- char *buf = NULL;
- size_t buflen = 0;
- unsigned long lineno = 1;
- const char *errmsg = NULL;
- struct pkg_info *pkg_info = NULL;
-
- fp = fopen(PACKAGES_LIST_FILE, "re");
- if (!fp) {
- CLOGE("Could not open: \"%s\", error: \"%s\"\n", PACKAGES_LIST_FILE,
- strerror(errno));
- return false;
- }
-
- while ((bytesread = getline(&buf, &buflen, fp)) > 0) {
-
- pkg_info = calloc(1, sizeof(*pkg_info));
- if (!pkg_info) {
- goto err;
- }
-
- next = buf;
-
- cur = strsep(&next, " \t\r\n");
- if (!cur) {
- errmsg = "Could not get next token for \"package name\"";
- goto err;
- }
-
- pkg_info->name = strdup(cur);
- if (!pkg_info->name) {
- goto err;
- }
-
- cur = strsep(&next, " \t\r\n");
- if (!cur) {
- errmsg = "Could not get next token for field \"uid\"";
- goto err;
- }
-
- tmp = strtoul(cur, &endptr, 10);
- if (*endptr != '\0') {
- errmsg = "Could not convert field \"uid\" to integer value";
- goto err;
- }
-
- /*
- * if unsigned long is greater than size of uid_t,
- * prevent a truncation based roll-over
- */
- if (tmp > UID_MAX) {
- errmsg = "Field \"uid\" greater than UID_MAX";
- goto err;
- }
-
- pkg_info->uid = (uid_t) tmp;
-
- cur = strsep(&next, " \t\r\n");
- if (!cur) {
- errmsg = "Could not get next token for field \"debuggable\"";
- goto err;
- }
-
- tmp = strtoul(cur, &endptr, 10);
- if (*endptr != '\0') {
- errmsg = "Could not convert field \"debuggable\" to integer value";
- goto err;
- }
-
- /* should be a valid boolean of 1 or 0 */
- if (!(tmp == 0 || tmp == 1)) {
- errmsg = "Field \"debuggable\" is not 0 or 1 boolean value";
- goto err;
- }
-
- pkg_info->debuggable = (bool) tmp;
-
- cur = strsep(&next, " \t\r\n");
- if (!cur) {
- errmsg = "Could not get next token for field \"data dir\"";
- goto err;
- }
-
- pkg_info->data_dir = strdup(cur);
- if (!pkg_info->data_dir) {
- goto err;
- }
-
- cur = strsep(&next, " \t\r\n");
- if (!cur) {
- errmsg = "Could not get next token for field \"seinfo\"";
- goto err;
- }
-
- pkg_info->seinfo = strdup(cur);
- if (!pkg_info->seinfo) {
- goto err;
- }
-
- cur = strsep(&next, " \t\r\n");
- if (!cur) {
- errmsg = "Could not get next token for field \"gid(s)\"";
- goto err;
- }
-
- /*
- * Parse the gid list, could be in the form of none, single gid or list:
- * none
- * gid
- * gid, gid ...
- */
- pkg_info->gids.cnt = get_gid_cnt(cur);
- if (pkg_info->gids.cnt > 0) {
-
- pkg_info->gids.gids = calloc(pkg_info->gids.cnt, sizeof(gid_t));
- if (!pkg_info->gids.gids) {
- goto err;
- }
-
- rc = parse_gids(cur, pkg_info->gids.gids, &pkg_info->gids.cnt);
- if (!rc) {
- errmsg = "Could not parse field \"gid list\"";
- goto err;
- }
- }
-
- rc = callback(pkg_info, userdata);
- if (rc == false) {
- /*
- * We do not log this as this can be intentional from
- * callback to abort processing. We go to out to not
- * free the pkg_info
- */
- rc = true;
- goto out;
- }
- lineno++;
- }
-
- rc = true;
-
-out:
- free(buf);
- fclose(fp);
- return rc;
-
-err:
- if (errmsg) {
- CLOGE("Error Parsing \"%s\" on line: %lu for reason: %s",
- PACKAGES_LIST_FILE, lineno, errmsg);
- }
- rc = false;
- packagelist_free(pkg_info);
- goto out;
-}
-
-void packagelist_free(pkg_info *info)
-{
- if (info) {
- free(info->name);
- free(info->data_dir);
- free(info->seinfo);
- free(info->gids.gids);
- free(info);
- }
-}
diff --git a/libs/packagelistparser/packagelistparser.h b/libs/packagelistparser/packagelistparser.h
deleted file mode 100644
index d602c26..0000000
--- a/libs/packagelistparser/packagelistparser.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright 2015, Intel Corporation
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * Written by William Roberts <william.c.roberts@intel.com>
- *
- * This is a parser library for parsing the packages.list file generated
- * by PackageManager service.
- *
- * This simple parser is sensitive to format changes in
- * frameworks/base/services/core/java/com/android/server/pm/Settings.java
- * A dependency note has been added to that file to correct
- * this parser.
- */
-
-#ifndef PACKAGELISTPARSER_H_
-#define PACKAGELISTPARSER_H_
-
-#include <stdbool.h>
-#include <sys/cdefs.h>
-#include <sys/types.h>
-
-__BEGIN_DECLS
-
-/** The file containing the list of installed packages on the system */
-#define PACKAGES_LIST_FILE "/data/system/packages.list"
-
-typedef struct pkg_info pkg_info;
-typedef struct gid_list gid_list;
-
-struct gid_list {
- size_t cnt;
- gid_t *gids;
-};
-
-struct pkg_info {
- char *name;
- uid_t uid;
- bool debuggable;
- char *data_dir;
- char *seinfo;
- gid_list gids;
- void *private_data;
-};
-
-/**
- * Callback function to be used by packagelist_parse() routine.
- * @param info
- * The parsed package information
- * @param userdata
- * The supplied userdata pointer to packagelist_parse()
- * @return
- * true to keep processing, false to stop.
- */
-typedef bool (*pfn_on_package)(pkg_info *info, void *userdata);
-
-/**
- * Parses the file specified by PACKAGES_LIST_FILE and invokes the callback on
- * each entry found. Once the callback is invoked, ownership of the pkg_info pointer
- * is passed to the callback routine, thus they are required to perform any cleanup
- * desired.
- * @param callback
- * The callback function called on each parsed line of the packages list.
- * @param userdata
- * An optional userdata supplied pointer to pass to the callback function.
- * @return
- * true on success false on failure.
- */
-extern bool packagelist_parse(pfn_on_package callback, void *userdata);
-
-/**
- * Frees a pkg_info structure.
- * @param info
- * The struct to free
- */
-extern void packagelist_free(pkg_info *info);
-
-__END_DECLS
-
-#endif /* PACKAGELISTPARSER_H_ */
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Events.java b/packages/DocumentsUI/src/com/android/documentsui/Events.java
index c06ea0a..d4c3ba3 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Events.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Events.java
@@ -38,9 +38,7 @@
* Returns true if event was triggered by a finger or stylus touch.
*/
static boolean isTouchEvent(MotionEvent e) {
- return isTouchType(e.getToolType(0))
- // Temporarily work around uiautomator's missing tool type support.
- || isUnknownType(e.getToolType(0));
+ return isTouchType(e.getToolType(0));
}
/**
@@ -51,7 +49,7 @@
}
/**
- * Returns true if type is finger or stylus.
+ * Returns true if event was triggered by a finger or stylus touch.
*/
static boolean isTouchType(int toolType) {
return toolType == MotionEvent.TOOL_TYPE_FINGER
@@ -59,13 +57,6 @@
}
/**
- * Returns true if type is unknown.
- */
- static boolean isUnknownType(int toolType) {
- return toolType == MotionEvent.TOOL_TYPE_UNKNOWN;
- }
-
- /**
* Returns true if event was triggered by a finger or stylus touch.
*/
static boolean isActionDown(MotionEvent e) {
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 8f634e1..8241ddf 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -36,7 +36,6 @@
<style name="RecentsTheme.Wallpaper">
<!-- Wallpaper -->
<item name="android:windowBackground">@color/transparent</item>
- <item name="android:windowResizingBackground">@color/transparent</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowShowWallpaper">true</item>
</style>
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index 3759c91..be7071e 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -64,6 +64,10 @@
case RS_TYPE_FLOAT_32: \
len = _env->GetArrayLength((jfloatArray)data); \
ptr = _env->GetFloatArrayElements((jfloatArray)data, flag); \
+ if (ptr == nullptr) { \
+ ALOGE("Failed to get Java array elements."); \
+ return; \
+ } \
typeBytes = 4; \
if (usePadding) { \
srcPtr = ptr; \
@@ -89,6 +93,10 @@
case RS_TYPE_FLOAT_64: \
len = _env->GetArrayLength((jdoubleArray)data); \
ptr = _env->GetDoubleArrayElements((jdoubleArray)data, flag); \
+ if (ptr == nullptr) { \
+ ALOGE("Failed to get Java array elements."); \
+ return; \
+ } \
typeBytes = 8; \
if (usePadding) { \
srcPtr = ptr; \
@@ -115,6 +123,10 @@
case RS_TYPE_UNSIGNED_8: \
len = _env->GetArrayLength((jbyteArray)data); \
ptr = _env->GetByteArrayElements((jbyteArray)data, flag); \
+ if (ptr == nullptr) { \
+ ALOGE("Failed to get Java array elements."); \
+ return; \
+ } \
typeBytes = 1; \
if (usePadding) { \
srcPtr = ptr; \
@@ -141,6 +153,10 @@
case RS_TYPE_UNSIGNED_16: \
len = _env->GetArrayLength((jshortArray)data); \
ptr = _env->GetShortArrayElements((jshortArray)data, flag); \
+ if (ptr == nullptr) { \
+ ALOGE("Failed to get Java array elements."); \
+ return; \
+ } \
typeBytes = 2; \
if (usePadding) { \
srcPtr = ptr; \
@@ -167,6 +183,10 @@
case RS_TYPE_UNSIGNED_32: \
len = _env->GetArrayLength((jintArray)data); \
ptr = _env->GetIntArrayElements((jintArray)data, flag); \
+ if (ptr == nullptr) { \
+ ALOGE("Failed to get Java array elements."); \
+ return; \
+ } \
typeBytes = 4; \
if (usePadding) { \
srcPtr = ptr; \
@@ -193,6 +213,10 @@
case RS_TYPE_UNSIGNED_64: \
len = _env->GetArrayLength((jlongArray)data); \
ptr = _env->GetLongArrayElements((jlongArray)data, flag); \
+ if (ptr == nullptr) { \
+ ALOGE("Failed to get Java array elements."); \
+ return; \
+ } \
typeBytes = 8; \
if (usePadding) { \
srcPtr = ptr; \
@@ -332,16 +356,40 @@
jlong* jFieldIDs = _env->GetLongArrayElements(fieldIDArray, nullptr);
jsize fieldIDs_length = _env->GetArrayLength(fieldIDArray);
+ if (jFieldIDs == nullptr) {
+ ALOGE("Failed to get Java array elements: fieldIDs.");
+ return ret;
+ }
+
jlong* jValues = _env->GetLongArrayElements(valueArray, nullptr);
jsize values_length = _env->GetArrayLength(valueArray);
+ if (jValues == nullptr) {
+ ALOGE("Failed to get Java array elements: values.");
+ return ret;
+ }
+
jint* jSizes = _env->GetIntArrayElements(sizeArray, nullptr);
jsize sizes_length = _env->GetArrayLength(sizeArray);
+ if (jSizes == nullptr) {
+ ALOGE("Failed to get Java array elements: sizes.");
+ return ret;
+ }
+
jlong* jDepClosures =
_env->GetLongArrayElements(depClosureArray, nullptr);
jsize depClosures_length = _env->GetArrayLength(depClosureArray);
+ if (jDepClosures == nullptr) {
+ ALOGE("Failed to get Java array elements: depClosures.");
+ return ret;
+ }
+
jlong* jDepFieldIDs =
_env->GetLongArrayElements(depFieldIDArray, nullptr);
jsize depFieldIDs_length = _env->GetArrayLength(depFieldIDArray);
+ if (jDepFieldIDs == nullptr) {
+ ALOGE("Failed to get Java array elements: depFieldIDs.");
+ return ret;
+ }
size_t numValues, numDependencies;
RsScriptFieldID* fieldIDs;
@@ -435,12 +483,31 @@
jbyte* jParams = _env->GetByteArrayElements(paramArray, nullptr);
jsize jParamLength = _env->GetArrayLength(paramArray);
+ if (jParams == nullptr) {
+ ALOGE("Failed to get Java array elements: params.");
+ return ret;
+ }
+
jlong* jFieldIDs = _env->GetLongArrayElements(fieldIDArray, nullptr);
jsize fieldIDs_length = _env->GetArrayLength(fieldIDArray);
+ if (jFieldIDs == nullptr) {
+ ALOGE("Failed to get Java array elements: fieldIDs.");
+ return ret;
+ }
+
jlong* jValues = _env->GetLongArrayElements(valueArray, nullptr);
jsize values_length = _env->GetArrayLength(valueArray);
+ if (jValues == nullptr) {
+ ALOGE("Failed to get Java array elements: values.");
+ return ret;
+ }
+
jint* jSizes = _env->GetIntArrayElements(sizeArray, nullptr);
jsize sizes_length = _env->GetArrayLength(sizeArray);
+ if (jSizes == nullptr) {
+ ALOGE("Failed to get Java array elements: sizes.");
+ return ret;
+ }
size_t numValues;
RsScriptFieldID* fieldIDs;
@@ -515,6 +582,10 @@
jlong* jClosures = _env->GetLongArrayElements(closureArray, nullptr);
jsize numClosures = _env->GetArrayLength(closureArray);
+ if (jClosures == nullptr) {
+ ALOGE("Failed to get Java array elements: closures.");
+ return ret;
+ }
RsClosure* closures;
@@ -720,6 +791,11 @@
}
jint len = _env->GetArrayLength(str);
jbyte * cptr = (jbyte *) _env->GetPrimitiveArrayCritical(str, 0);
+ if (cptr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return;
+ }
+
rsAssignName((RsContext)con, (void *)obj, (const char *)cptr, len);
_env->ReleasePrimitiveArrayCritical(str, cptr, JNI_ABORT);
}
@@ -916,6 +992,10 @@
ALOGD("nContextGetMessage, con(%p), len(%i)", (RsContext)con, len);
}
jint *ptr = _env->GetIntArrayElements(data, nullptr);
+ if (ptr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return 0;
+ }
size_t receiveLen;
uint32_t subID;
int id = rsContextGetMessage((RsContext)con,
@@ -936,6 +1016,10 @@
ALOGD("nContextPeekMessage, con(%p)", (RsContext)con);
}
jint *auxDataPtr = _env->GetIntArrayElements(auxData, nullptr);
+ if (auxDataPtr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return 0;
+ }
size_t receiveLen;
uint32_t subID;
int id = rsContextPeekMessage((RsContext)con, &receiveLen, sizeof(receiveLen),
@@ -970,6 +1054,10 @@
if (data) {
len = _env->GetArrayLength(data);
ptr = _env->GetIntArrayElements(data, nullptr);
+ if (ptr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return;
+ }
}
if (kLogApi) {
ALOGD("nContextSendMessage, con(%p), id(%i), len(%i)", (RsContext)con, id, len);
@@ -1004,7 +1092,15 @@
}
jlong *jIds = _env->GetLongArrayElements(_ids, nullptr);
+ if (jIds == nullptr) {
+ ALOGE("Failed to get Java array elements: ids");
+ return 0;
+ }
jint *jArraySizes = _env->GetIntArrayElements(_arraySizes, nullptr);
+ if (jArraySizes == nullptr) {
+ ALOGE("Failed to get Java array elements: arraySizes");
+ return 0;
+ }
RsElement *ids = (RsElement*)malloc(fieldCount * sizeof(RsElement));
uint32_t *arraySizes = (uint32_t *)malloc(fieldCount * sizeof(uint32_t));
@@ -1311,6 +1407,10 @@
sizeBytes);
}
jbyte *ptr = _env->GetByteArrayElements(data, nullptr);
+ if (ptr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return;
+ }
rsAllocationElementData((RsContext)con, (RsAllocation)alloc,
xoff, yoff, zoff,
lod, ptr, sizeBytes, compIdx);
@@ -1449,6 +1549,10 @@
sizeBytes);
}
jbyte *ptr = _env->GetByteArrayElements(data, nullptr);
+ if (ptr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return;
+ }
rsAllocationElementRead((RsContext)con, (RsAllocation)alloc,
xoff, yoff, zoff,
lod, ptr, sizeBytes, compIdx);
@@ -1775,6 +1879,10 @@
}
jint len = _env->GetArrayLength(data);
jbyte *ptr = _env->GetByteArrayElements(data, nullptr);
+ if (ptr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return;
+ }
rsScriptSetVarV((RsContext)con, (RsScript)script, slot, ptr, len);
_env->ReleaseByteArrayElements(data, ptr, JNI_ABORT);
}
@@ -1787,6 +1895,10 @@
}
jint len = _env->GetArrayLength(data);
jbyte *ptr = _env->GetByteArrayElements(data, nullptr);
+ if (ptr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return;
+ }
rsScriptGetVarV((RsContext)con, (RsScript)script, slot, ptr, len);
_env->ReleaseByteArrayElements(data, ptr, 0);
}
@@ -1800,8 +1912,16 @@
}
jint len = _env->GetArrayLength(data);
jbyte *ptr = _env->GetByteArrayElements(data, nullptr);
+ if (ptr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return;
+ }
jint dimsLen = _env->GetArrayLength(dims) * sizeof(int);
jint *dimsPtr = _env->GetIntArrayElements(dims, nullptr);
+ if (dimsPtr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return;
+ }
rsScriptSetVarVE((RsContext)con, (RsScript)script, slot, ptr, len, (RsElement)elem,
(const uint32_t*) dimsPtr, dimsLen);
_env->ReleaseByteArrayElements(data, ptr, JNI_ABORT);
@@ -1819,6 +1939,10 @@
jint length = _env->GetArrayLength(timeZone);
jbyte* timeZone_ptr;
timeZone_ptr = (jbyte *) _env->GetPrimitiveArrayCritical(timeZone, (jboolean *)0);
+ if (timeZone_ptr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return;
+ }
rsScriptSetTimeZone((RsContext)con, (RsScript)script, (const char *)timeZone_ptr, length);
@@ -1844,6 +1968,10 @@
}
jint len = _env->GetArrayLength(data);
jbyte *ptr = _env->GetByteArrayElements(data, nullptr);
+ if (ptr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return;
+ }
rsScriptInvokeV((RsContext)con, (RsScript)script, slot, ptr, len);
_env->ReleaseByteArrayElements(data, ptr, JNI_ABORT);
}
@@ -1870,8 +1998,12 @@
return;
}
- // TODO (b/20760800): Check in_ptr is not null
in_ptr = _env->GetLongArrayElements(ains, nullptr);
+ if (in_ptr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return;
+ }
+
if (sizeof(RsAllocation) == sizeof(jlong)) {
in_allocs = (RsAllocation*)in_ptr;
@@ -1897,6 +2029,10 @@
if (params != nullptr) {
param_len = _env->GetArrayLength(params);
param_ptr = _env->GetByteArrayElements(params, nullptr);
+ if (param_ptr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return;
+ }
}
RsScriptCall sc, *sca = nullptr;
@@ -1908,6 +2044,10 @@
if (limits != nullptr) {
limit_len = _env->GetArrayLength(limits);
limit_ptr = _env->GetIntArrayElements(limits, nullptr);
+ if (limit_ptr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return;
+ }
assert(limit_len == 6);
UNUSED(limit_len); // As the assert might not be compiled.
@@ -1966,6 +2106,10 @@
if (limits != nullptr) {
limit_len = _env->GetArrayLength(limits);
limit_ptr = _env->GetIntArrayElements(limits, nullptr);
+ if (limit_ptr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return;
+ }
// We expect to be passed an array [x1, x2] which specifies
// the sub-range for a 1-dimensional reduction.
@@ -2037,6 +2181,10 @@
}
script_ptr = (jbyte *)
_env->GetPrimitiveArrayCritical(scriptRef, (jboolean *)0);
+ if (script_ptr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return ret;
+ }
//rsScriptCSetText((RsContext)con, (const char *)script_ptr, length);
@@ -2104,6 +2252,10 @@
jint kernelsLen = _env->GetArrayLength(_kernels);
jlong *jKernelsPtr = _env->GetLongArrayElements(_kernels, nullptr);
+ if (jKernelsPtr == nullptr) {
+ ALOGE("Failed to get Java array elements: kernels");
+ return 0;
+ }
RsScriptKernelID* kernelsPtr = (RsScriptKernelID*) malloc(sizeof(RsScriptKernelID) * kernelsLen);
for(int i = 0; i < kernelsLen; ++i) {
kernelsPtr[i] = (RsScriptKernelID)jKernelsPtr[i];
@@ -2111,6 +2263,10 @@
jint srcLen = _env->GetArrayLength(_src);
jlong *jSrcPtr = _env->GetLongArrayElements(_src, nullptr);
+ if (jSrcPtr == nullptr) {
+ ALOGE("Failed to get Java array elements: src");
+ return 0;
+ }
RsScriptKernelID* srcPtr = (RsScriptKernelID*) malloc(sizeof(RsScriptKernelID) * srcLen);
for(int i = 0; i < srcLen; ++i) {
srcPtr[i] = (RsScriptKernelID)jSrcPtr[i];
@@ -2118,6 +2274,10 @@
jint dstkLen = _env->GetArrayLength(_dstk);
jlong *jDstkPtr = _env->GetLongArrayElements(_dstk, nullptr);
+ if (jDstkPtr == nullptr) {
+ ALOGE("Failed to get Java array elements: dstk");
+ return 0;
+ }
RsScriptKernelID* dstkPtr = (RsScriptKernelID*) malloc(sizeof(RsScriptKernelID) * dstkLen);
for(int i = 0; i < dstkLen; ++i) {
dstkPtr[i] = (RsScriptKernelID)jDstkPtr[i];
@@ -2125,6 +2285,10 @@
jint dstfLen = _env->GetArrayLength(_dstf);
jlong *jDstfPtr = _env->GetLongArrayElements(_dstf, nullptr);
+ if (jDstfPtr == nullptr) {
+ ALOGE("Failed to get Java array elements: dstf");
+ return 0;
+ }
RsScriptKernelID* dstfPtr = (RsScriptKernelID*) malloc(sizeof(RsScriptKernelID) * dstfLen);
for(int i = 0; i < dstfLen; ++i) {
dstfPtr[i] = (RsScriptKernelID)jDstfPtr[i];
@@ -2132,6 +2296,10 @@
jint typesLen = _env->GetArrayLength(_types);
jlong *jTypesPtr = _env->GetLongArrayElements(_types, nullptr);
+ if (jTypesPtr == nullptr) {
+ ALOGE("Failed to get Java array elements: types");
+ return 0;
+ }
RsType* typesPtr = (RsType*) malloc(sizeof(RsType) * typesLen);
for(int i = 0; i < typesLen; ++i) {
typesPtr[i] = (RsType)jTypesPtr[i];
@@ -2244,6 +2412,10 @@
AutoJavaStringToUTF8 shaderUTF(_env, shader);
jlong *jParamPtr = _env->GetLongArrayElements(params, nullptr);
jint paramLen = _env->GetArrayLength(params);
+ if (jParamPtr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return 0;
+ }
int texCount = _env->GetArrayLength(texNames);
AutoJavaStringArrayToUTF8 names(_env, texNames, texCount);
@@ -2277,6 +2449,10 @@
AutoJavaStringToUTF8 shaderUTF(_env, shader);
jlong *jParamPtr = _env->GetLongArrayElements(params, nullptr);
jint paramLen = _env->GetArrayLength(params);
+ if (jParamPtr == nullptr) {
+ ALOGE("Failed to get Java array elements");
+ return 0;
+ }
if (kLogApi) {
ALOGD("nProgramVertexCreate, con(%p), paramLen(%i)", (RsContext)con, paramLen);
@@ -2392,6 +2568,10 @@
jint vtxLen = _env->GetArrayLength(_vtx);
jlong *jVtxPtr = _env->GetLongArrayElements(_vtx, nullptr);
+ if (jVtxPtr == nullptr) {
+ ALOGE("Failed to get Java array elements: vtx");
+ return 0;
+ }
RsAllocation* vtxPtr = (RsAllocation*) malloc(sizeof(RsAllocation) * vtxLen);
for(int i = 0; i < vtxLen; ++i) {
vtxPtr[i] = (RsAllocation)(uintptr_t)jVtxPtr[i];
@@ -2399,6 +2579,10 @@
jint idxLen = _env->GetArrayLength(_idx);
jlong *jIdxPtr = _env->GetLongArrayElements(_idx, nullptr);
+ if (jIdxPtr == nullptr) {
+ ALOGE("Failed to get Java array elements: idx");
+ return 0;
+ }
RsAllocation* idxPtr = (RsAllocation*) malloc(sizeof(RsAllocation) * idxLen);
for(int i = 0; i < idxLen; ++i) {
idxPtr[i] = (RsAllocation)(uintptr_t)jIdxPtr[i];
@@ -2406,6 +2590,10 @@
jint primLen = _env->GetArrayLength(_prim);
jint *primPtr = _env->GetIntArrayElements(_prim, nullptr);
+ if (primPtr == nullptr) {
+ ALOGE("Failed to get Java array elements: prim");
+ return 0;
+ }
jlong id = (jlong)(uintptr_t)rsMeshCreate((RsContext)con,
(RsAllocation *)vtxPtr, vtxLen,
diff --git a/services/core/java/com/android/server/wm/CircularDisplayMask.java b/services/core/java/com/android/server/wm/CircularDisplayMask.java
index 7c2da2d..be3e922 100644
--- a/services/core/java/com/android/server/wm/CircularDisplayMask.java
+++ b/services/core/java/com/android/server/wm/CircularDisplayMask.java
@@ -56,7 +56,7 @@
int screenOffset, int maskThickness) {
mScreenSize = new Point();
display.getSize(mScreenSize);
- if (mScreenSize.x != mScreenSize.y) {
+ if (mScreenSize.x != mScreenSize.y + screenOffset) {
Slog.w(TAG, "Screen dimensions of displayId = " + display.getDisplayId() +
"are not equal, circularMask will not be drawn.");
mDimensionsUnequal = true;
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 3521682..f5e97e5 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -265,21 +265,27 @@
}
void broadcastDragEndedLw() {
+ final int myPid = Process.myPid();
+
if (WindowManagerService.DEBUG_DRAG) {
Slog.d(WindowManagerService.TAG, "broadcasting DRAG_ENDED");
}
- DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED,
- 0, 0, null, null, null, null, mDragResult);
- for (WindowState ws: mNotifiedWindows) {
+ for (WindowState ws : mNotifiedWindows) {
+ DragEvent evt = DragEvent.obtain(DragEvent.ACTION_DRAG_ENDED,
+ 0, 0, null, null, null, null, mDragResult);
try {
ws.mClient.dispatchDragEvent(evt);
} catch (RemoteException e) {
Slog.w(WindowManagerService.TAG, "Unable to drag-end window " + ws);
}
+ // if the current window is in the same process,
+ // the dispatch has already recycled the event
+ if (myPid != ws.mSession.mPid) {
+ evt.recycle();
+ }
}
mNotifiedWindows.clear();
mDragInProgress = false;
- evt.recycle();
}
void endDragLw() {