diff options
Diffstat (limited to 'core/java')
| -rw-r--r-- | core/java/android/content/Intent.java | 52 | ||||
| -rw-r--r-- | core/java/android/preference/ListPreference.java | 4 | ||||
| -rw-r--r-- | core/java/android/preference/SeekBarVolumizer.java | 7 | ||||
| -rw-r--r-- | core/java/android/preference/VolumePreference.java | 2 | ||||
| -rw-r--r-- | core/java/android/view/Surface.java | 123 | ||||
| -rw-r--r-- | core/java/android/widget/TimePickerSpinnerDelegate.java | 47 | ||||
| -rw-r--r-- | core/java/com/android/internal/app/ChooserActivity.java | 26 | ||||
| -rw-r--r-- | core/java/com/android/internal/app/ResolverActivity.java | 6 |
8 files changed, 232 insertions, 35 deletions
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index af6f181..7676e4b 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -876,12 +876,44 @@ public class Intent implements Parcelable, Cloneable { * related methods. */ public static Intent createChooser(Intent target, CharSequence title) { + return createChooser(target, title, null); + } + + /** + * Convenience function for creating a {@link #ACTION_CHOOSER} Intent. + * + * <p>Builds a new {@link #ACTION_CHOOSER} Intent that wraps the given + * target intent, also optionally supplying a title. If the target + * intent has specified {@link #FLAG_GRANT_READ_URI_PERMISSION} or + * {@link #FLAG_GRANT_WRITE_URI_PERMISSION}, then these flags will also be + * set in the returned chooser intent, with its ClipData set appropriately: + * either a direct reflection of {@link #getClipData()} if that is non-null, + * or a new ClipData built from {@link #getData()}.</p> + * + * <p>The caller may optionally supply an {@link IntentSender} to receive a callback + * when the user makes a choice. This can be useful if the calling application wants + * to remember the last chosen target and surface it as a more prominent or one-touch + * affordance elsewhere in the UI for next time.</p> + * + * @param target The Intent that the user will be selecting an activity + * to perform. + * @param title Optional title that will be displayed in the chooser. + * @param sender Optional IntentSender to be called when a choice is made. + * @return Return a new Intent object that you can hand to + * {@link Context#startActivity(Intent) Context.startActivity()} and + * related methods. + */ + public static Intent createChooser(Intent target, CharSequence title, IntentSender sender) { Intent intent = new Intent(ACTION_CHOOSER); intent.putExtra(EXTRA_INTENT, target); if (title != null) { intent.putExtra(EXTRA_TITLE, title); } + if (sender != null) { + intent.putExtra(EXTRA_CHOSEN_COMPONENT_INTENT_SENDER, sender); + } + // Migrate any clip data and flags from target. int permFlags = target.getFlags() & (FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION | FLAG_GRANT_PERSISTABLE_URI_PERMISSION @@ -3140,6 +3172,26 @@ public class Intent implements Parcelable, Cloneable { "android.intent.extra.REPLACEMENT_EXTRAS"; /** + * An {@link IntentSender} that will be notified if a user successfully chooses a target + * component to handle an action in an {@link #ACTION_CHOOSER} activity. The IntentSender + * will have the extra {@link #EXTRA_CHOSEN_COMPONENT} appended to it containing the + * {@link ComponentName} of the chosen component. + * + * <p>In some situations this callback may never come, for example if the user abandons + * the chooser, switches to another task or any number of other reasons. Apps should not + * be written assuming that this callback will always occur.</p> + */ + public static final String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER = + "android.intent.extra.CHOSEN_COMPONENT_INTENT_SENDER"; + + /** + * The {@link ComponentName} chosen by the user to complete an action. + * + * @see #EXTRA_CHOSEN_COMPONENT_INTENT_SENDER + */ + public static final String EXTRA_CHOSEN_COMPONENT = "android.intent.extra.CHOSEN_COMPONENT"; + + /** * A {@link android.view.KeyEvent} object containing the event that * triggered the creation of the Intent it is in. */ diff --git a/core/java/android/preference/ListPreference.java b/core/java/android/preference/ListPreference.java index 8081a54..9482a72 100644 --- a/core/java/android/preference/ListPreference.java +++ b/core/java/android/preference/ListPreference.java @@ -162,10 +162,10 @@ public class ListPreference extends DialogPreference { @Override public CharSequence getSummary() { final CharSequence entry = getEntry(); - if (mSummary == null || entry == null) { + if (mSummary == null) { return super.getSummary(); } else { - return String.format(mSummary, entry); + return String.format(mSummary, entry == null ? "" : entry); } } diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java index a680b51..3130b64 100644 --- a/core/java/android/preference/SeekBarVolumizer.java +++ b/core/java/android/preference/SeekBarVolumizer.java @@ -130,6 +130,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba } private void postStartSample() { + if (mHandler == null) return; mHandler.removeMessages(MSG_START_SAMPLE); mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_SAMPLE), isSamplePlaying() ? CHECK_RINGTONE_PLAYBACK_DELAY_MS : 0); @@ -150,7 +151,8 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba } } - void postStopSample() { + private void postStopSample() { + if (mHandler == null) return; // remove pending delayed start messages mHandler.removeMessages(MSG_START_SAMPLE); mHandler.removeMessages(MSG_STOP_SAMPLE); @@ -200,7 +202,8 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba postSetVolume(progress); } - void postSetVolume(int progress) { + private void postSetVolume(int progress) { + if (mHandler == null) return; // Do the volume changing separately to give responsive UI mLastProgress = progress; mHandler.removeMessages(MSG_SET_STREAM_VOLUME); diff --git a/core/java/android/preference/VolumePreference.java b/core/java/android/preference/VolumePreference.java index 86d96f2..0d4c0b6 100644 --- a/core/java/android/preference/VolumePreference.java +++ b/core/java/android/preference/VolumePreference.java @@ -117,7 +117,7 @@ public class VolumePreference extends SeekBarDialogPreference implements public void onActivityStop() { if (mSeekBarVolumizer != null) { - mSeekBarVolumizer.postStopSample(); + mSeekBarVolumizer.stopSample(); } } diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index 3770b8a..132e25c 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -87,6 +87,8 @@ public class Surface implements Parcelable { // non compatibility mode. private Matrix mCompatibleMatrix; + private HwuiContext mHwuiContext; + /** @hide */ @IntDef({ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270}) @Retention(RetentionPolicy.SOURCE) @@ -171,6 +173,10 @@ public class Surface implements Parcelable { nativeRelease(mNativeObject); setNativeObjectLocked(0); } + if (mHwuiContext != null) { + mHwuiContext.destroy(); + mHwuiContext = null; + } } } @@ -264,27 +270,60 @@ public class Surface implements Parcelable { * @param canvas The canvas previously obtained from {@link #lockCanvas}. */ public void unlockCanvasAndPost(Canvas canvas) { + synchronized (mLock) { + checkNotReleasedLocked(); + + if (mHwuiContext != null) { + mHwuiContext.unlockAndPost(canvas); + } else { + unlockSwCanvasAndPost(canvas); + } + } + } + + private void unlockSwCanvasAndPost(Canvas canvas) { if (canvas != mCanvas) { throw new IllegalArgumentException("canvas object must be the same instance that " + "was previously returned by lockCanvas"); } + if (mNativeObject != mLockedObject) { + Log.w(TAG, "WARNING: Surface's mNativeObject (0x" + + Long.toHexString(mNativeObject) + ") != mLockedObject (0x" + + Long.toHexString(mLockedObject) +")"); + } + if (mLockedObject == 0) { + throw new IllegalStateException("Surface was not locked"); + } + try { + nativeUnlockCanvasAndPost(mLockedObject, canvas); + } finally { + nativeRelease(mLockedObject); + mLockedObject = 0; + } + } + /** + * Gets a {@link Canvas} for drawing into this surface. + * + * After drawing into the provided {@link Canvas}, the caller must + * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface. + * + * Unlike {@link #lockCanvas(Rect)} this will return a hardware-accelerated + * canvas. See the <a href="{@docRoot}guide/topics/graphics/hardware-accel.html#unsupported"> + * unsupported drawing operations</a> for a list of what is and isn't + * supported in a hardware-accelerated canvas. + * + * @return A canvas for drawing into the surface. + * + * @throws IllegalStateException If the canvas cannot be locked. + */ + public Canvas lockHardwareCanvas() { synchronized (mLock) { checkNotReleasedLocked(); - if (mNativeObject != mLockedObject) { - Log.w(TAG, "WARNING: Surface's mNativeObject (0x" + - Long.toHexString(mNativeObject) + ") != mLockedObject (0x" + - Long.toHexString(mLockedObject) +")"); - } - if (mLockedObject == 0) { - throw new IllegalStateException("Surface was not locked"); - } - try { - nativeUnlockCanvasAndPost(mLockedObject, canvas); - } finally { - nativeRelease(mLockedObject); - mLockedObject = 0; + if (mHwuiContext == null) { + mHwuiContext = new HwuiContext(); } + return mHwuiContext.lockCanvas(); } } @@ -415,6 +454,9 @@ public class Surface implements Parcelable { } mNativeObject = ptr; mGenerationId += 1; + if (mHwuiContext != null) { + mHwuiContext.updateSurface(); + } } } @@ -518,4 +560,59 @@ public class Surface implements Parcelable { mOrigMatrix.set(m); } } + + private final class HwuiContext { + private final RenderNode mRenderNode; + private long mHwuiRenderer; + private HardwareCanvas mCanvas; + + HwuiContext() { + mRenderNode = RenderNode.create("HwuiCanvas", null); + mRenderNode.setClipToBounds(false); + mHwuiRenderer = nHwuiCreate(mRenderNode.mNativeRenderNode, mNativeObject); + } + + Canvas lockCanvas() { + if (mCanvas != null) { + throw new IllegalStateException("Surface was already locked!"); + } + mCanvas = mRenderNode.start(0, 0); + return mCanvas; + } + + void unlockAndPost(Canvas canvas) { + if (canvas != mCanvas) { + throw new IllegalArgumentException("canvas object must be the same instance that " + + "was previously returned by lockCanvas"); + } + mRenderNode.end(mCanvas); + mCanvas = null; + nHwuiDraw(mHwuiRenderer); + } + + void updateSurface() { + nHwuiSetSurface(mHwuiRenderer, mNativeObject); + } + + void destroy() { + if (mHwuiRenderer != 0) { + nHwuiDestroy(mHwuiRenderer); + mHwuiRenderer = 0; + } + } + + @Override + protected void finalize() throws Throwable { + try { + destroy(); + } finally { + super.finalize(); + } + } + } + + private static native long nHwuiCreate(long rootNode, long surface); + private static native void nHwuiSetSurface(long renderer, long surface); + private static native void nHwuiDraw(long renderer); + private static native void nHwuiDestroy(long renderer); } diff --git a/core/java/android/widget/TimePickerSpinnerDelegate.java b/core/java/android/widget/TimePickerSpinnerDelegate.java index e4342b1..d9c4114 100644 --- a/core/java/android/widget/TimePickerSpinnerDelegate.java +++ b/core/java/android/widget/TimePickerSpinnerDelegate.java @@ -105,6 +105,10 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate im private String mMinutePickerDescription; private String mSelectMinutes; + // Most recent time announcement values for accessibility. + private CharSequence mLastAnnouncedText; + private boolean mLastAnnouncedIsHour; + private Calendar mTempCalendar; public TimePickerSpinnerDelegate(TimePicker delegator, Context context, AttributeSet attrs, @@ -224,11 +228,11 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate im // Enable or disable the AM/PM view. updateHeaderAmPm(); // Update Hour and Minutes - updateHeaderHour(mInitialHourOfDay, true); + updateHeaderHour(mInitialHourOfDay, false); // Update time separator updateHeaderSeparator(); // Update Minutes - updateHeaderMinute(mInitialMinute); + updateHeaderMinute(mInitialMinute, false); // Invalidate everything mDelegator.invalidate(); } @@ -293,7 +297,7 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate im return; } mInitialHourOfDay = currentHour; - updateHeaderHour(currentHour, true /* accessibility announce */); + updateHeaderHour(currentHour, true); updateHeaderAmPm(); mRadialTimePickerView.setCurrentHour(currentHour); mRadialTimePickerView.setAmOrPm(mInitialHourOfDay < 12 ? AM : PM); @@ -329,7 +333,7 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate im return; } mInitialMinute = currentMinute; - updateHeaderMinute(currentMinute); + updateHeaderMinute(currentMinute, true); mRadialTimePickerView.setCurrentMinute(currentMinute); mDelegator.invalidate(); onTimeChanged(); @@ -357,7 +361,7 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate im generateLegalTimesTree(); int hour = mRadialTimePickerView.getCurrentHour(); mInitialHourOfDay = hour; - updateHeaderHour(hour, false /* no accessibility announce */); + updateHeaderHour(hour, false); updateHeaderAmPm(); updateRadialPicker(mRadialTimePickerView.getCurrentItemShowing()); mDelegator.invalidate(); @@ -604,19 +608,17 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate im @Override public void onValueSelected(int pickerIndex, int newValue, boolean autoAdvance) { if (pickerIndex == HOUR_INDEX) { - updateHeaderHour(newValue, false); - String announcement = String.format("%d", newValue); if (mAllowAutoAdvance && autoAdvance) { + updateHeaderHour(newValue, false); setCurrentItemShowing(MINUTE_INDEX, true, false); - announcement += ". " + mSelectMinutes; + mRadialTimePickerView.announceForAccessibility(newValue + ". " + mSelectMinutes); } else { + updateHeaderHour(newValue, true); mRadialTimePickerView.setContentDescription( mHourPickerDescription + ": " + newValue); } - - mRadialTimePickerView.announceForAccessibility(announcement); } else if (pickerIndex == MINUTE_INDEX){ - updateHeaderMinute(newValue); + updateHeaderMinute(newValue, true); mRadialTimePickerView.setContentDescription(mMinutePickerDescription + ": " + newValue); } else if (pickerIndex == AMPM_INDEX) { updateAmPmLabelStates(newValue); @@ -664,7 +666,16 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate im CharSequence text = String.format(format, value); mHourView.setText(text); if (announce) { - mRadialTimePickerView.announceForAccessibility(text); + tryAnnounceForAccessibility(text, 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; } } @@ -715,13 +726,15 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate im return -1; } - private void updateHeaderMinute(int value) { + private void updateHeaderMinute(int value, boolean announceForAccessibility) { if (value == 60) { value = 0; } - CharSequence text = String.format(mCurrentLocale, "%02d", value); - mRadialTimePickerView.announceForAccessibility(text); + final CharSequence text = String.format(mCurrentLocale, "%02d", value); mMinuteView.setText(text); + if (announceForAccessibility) { + tryAnnounceForAccessibility(text, false); + } } /** @@ -921,8 +934,8 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate im if (!allowEmptyDisplay && mTypedTimes.isEmpty()) { int hour = mRadialTimePickerView.getCurrentHour(); int minute = mRadialTimePickerView.getCurrentMinute(); - updateHeaderHour(hour, true); - updateHeaderMinute(minute); + updateHeaderHour(hour, false); + updateHeaderMinute(minute, false); if (!mIs24HourView) { updateAmPmLabelStates(hour < 12 ? AM : PM); } diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 5267811..0bc1a8d 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -16,13 +16,20 @@ package com.android.internal.app; +import android.app.Activity; +import android.content.ComponentName; import android.content.Intent; +import android.content.IntentSender; import android.os.Bundle; import android.os.Parcelable; import android.util.Log; +import android.util.Slog; public class ChooserActivity extends ResolverActivity { + private static final String TAG = "ChooserActivity"; + private Bundle mReplacementExtras; + private IntentSender mChosenComponentSender; @Override protected void onCreate(Bundle savedInstanceState) { @@ -60,11 +67,14 @@ public class ChooserActivity extends ResolverActivity { initialIntents[i] = in; } } + mChosenComponentSender = intent.getParcelableExtra( + Intent.EXTRA_CHOSEN_COMPONENT_INTENT_SENDER); setSafeForwardingMode(true); super.onCreate(savedInstanceState, target, title, defaultTitleRes, initialIntents, null, false); } + @Override public Intent getReplacementIntent(String packageName, Intent defIntent) { if (mReplacementExtras != null) { final Bundle replExtras = mReplacementExtras.getBundle(packageName); @@ -77,6 +87,22 @@ public class ChooserActivity extends ResolverActivity { return defIntent; } + @Override + public void onActivityStarted(Intent intent) { + if (mChosenComponentSender != null) { + final ComponentName target = intent.getComponent(); + if (target != null) { + final Intent fillIn = new Intent().putExtra(Intent.EXTRA_CHOSEN_COMPONENT, target); + try { + mChosenComponentSender.sendIntent(this, Activity.RESULT_OK, fillIn, null, null); + } catch (IntentSender.SendIntentException e) { + Slog.e(TAG, "Unable to launch supplied IntentSender to report " + + "the chosen component: " + e); + } + } + } + } + private void modifyTargetIntent(Intent in) { final String action = in.getAction(); if (Intent.ACTION_SEND.equals(action) || diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index b9cbc62..ccffa19 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -644,10 +644,12 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic public void safelyStartActivity(Intent intent) { if (!mSafeForwardingMode) { startActivity(intent); + onActivityStarted(intent); return; } try { startActivityAsCaller(intent, null, UserHandle.USER_NULL); + onActivityStarted(intent); } catch (RuntimeException e) { String launchedFromPackage; try { @@ -662,6 +664,10 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic } } + public void onActivityStarted(Intent intent) { + // Do nothing + } + void showAppDetails(ResolveInfo ri) { Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) .setData(Uri.fromParts("package", ri.activityInfo.packageName, null)) |
