diff options
Diffstat (limited to 'core/java/android')
| -rw-r--r-- | core/java/android/bluetooth/le/BluetoothLeScanner.java | 5 | ||||
| -rw-r--r-- | core/java/android/content/Intent.java | 52 | ||||
| -rw-r--r-- | core/java/android/hardware/hdmi/HdmiClient.java | 10 | ||||
| -rw-r--r-- | core/java/android/hardware/hdmi/HdmiControlManager.java | 29 | ||||
| -rw-r--r-- | core/java/android/hardware/hdmi/HdmiTvClient.java | 18 | ||||
| -rw-r--r-- | core/java/android/hardware/hdmi/IHdmiControlService.aidl | 1 | ||||
| -rw-r--r-- | core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl | 3 | ||||
| -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/view/ViewGroup.java | 11 | ||||
| -rw-r--r-- | core/java/android/view/WindowManager.java | 9 | ||||
| -rw-r--r-- | core/java/android/widget/TimePickerSpinnerDelegate.java | 47 |
14 files changed, 266 insertions, 55 deletions
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java index a57c3ca..93ea299 100644 --- a/core/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java @@ -51,6 +51,7 @@ public final class BluetoothLeScanner { private static final String TAG = "BluetoothLeScanner"; private static final boolean DBG = true; + private static final boolean VDBG = false; private final IBluetoothManager mBluetoothManager; private final Handler mHandler; @@ -317,7 +318,7 @@ public final class BluetoothLeScanner { */ @Override public void onScanResult(final ScanResult scanResult) { - if (DBG) Log.d(TAG, "onScanResult() - " + scanResult.toString()); + if (VDBG) Log.d(TAG, "onScanResult() - " + scanResult.toString()); // Check null in case the scan has been stopped synchronized (this) { @@ -346,7 +347,7 @@ public final class BluetoothLeScanner { @Override public void onFoundOrLost(final boolean onFound, final ScanResult scanResult) { - if (DBG) { + if (VDBG) { Log.d(TAG, "onFoundOrLost() - onFound = " + onFound + " " + scanResult.toString()); } 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/hardware/hdmi/HdmiClient.java b/core/java/android/hardware/hdmi/HdmiClient.java index aba90e4..c2b9846 100644 --- a/core/java/android/hardware/hdmi/HdmiClient.java +++ b/core/java/android/hardware/hdmi/HdmiClient.java @@ -3,7 +3,6 @@ package android.hardware.hdmi; import android.annotation.NonNull; import android.annotation.SystemApi; import android.hardware.hdmi.HdmiControlManager.VendorCommandListener; -import android.hardware.hdmi.IHdmiVendorCommandListener; import android.os.RemoteException; import android.util.Log; @@ -91,8 +90,13 @@ public abstract class HdmiClient { final VendorCommandListener listener) { return new IHdmiVendorCommandListener.Stub() { @Override - public void onReceived(int srcAddress, byte[] params, boolean hasVendorId) { - listener.onReceived(srcAddress, params, hasVendorId); + public void onReceived(int srcAddress, int destAddress, byte[] params, + boolean hasVendorId) { + listener.onReceived(srcAddress, destAddress, params, hasVendorId); + } + @Override + public void onControlStateChanged(boolean enabled, int reason) { + listener.onControlStateChanged(enabled, reason); } }; } diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java index 30f3576..ff2ba1e 100644 --- a/core/java/android/hardware/hdmi/HdmiControlManager.java +++ b/core/java/android/hardware/hdmi/HdmiControlManager.java @@ -236,6 +236,15 @@ public final class HdmiControlManager { /** Clear timer error - CEC is disabled. */ public static final int CLEAR_TIMER_STATUS_CEC_DISABLE = 0xA2; + /** The HdmiControlService is started. */ + public static final int CONTROL_STATE_CHANGED_REASON_START = 0; + /** The state of HdmiControlService is changed by changing of settings. */ + public static final int CONTROL_STATE_CHANGED_REASON_SETTING = 1; + /** The HdmiControlService is enabled to wake up. */ + public static final int CONTROL_STATE_CHANGED_REASON_WAKEUP = 2; + /** The HdmiControlService will be disabled to standby. */ + public static final int CONTROL_STATE_CHANGED_REASON_STANDBY = 3; + // True if we have a logical device of type playback hosted in the system. private final boolean mHasPlaybackDevice; // True if we have a logical device of type TV hosted in the system. @@ -339,11 +348,29 @@ public final class HdmiControlManager { * Called when a vendor command is received. * * @param srcAddress source logical address + * @param destAddress destination logical address * @param params vendor-specific parameters * @param hasVendorId {@code true} if the command is <Vendor Command * With ID>. The first 3 bytes of params is vendor id. */ - void onReceived(int srcAddress, byte[] params, boolean hasVendorId); + void onReceived(int srcAddress, int destAddress, byte[] params, boolean hasVendorId); + + /** + * The callback is called: + * <ul> + * <li> before HdmiControlService is disabled. + * <li> after HdmiControlService is enabled and the local address is assigned. + * </ul> + * The client shouldn't hold the thread too long since this is a blocking call. + * + * @param enabled {@code true} if HdmiControlService is enabled. + * @param reason the reason code why the state of HdmiControlService is changed. + * @see #CONTROL_STATE_CHANGED_REASON_START + * @see #CONTROL_STATE_CHANGED_REASON_SETTING + * @see #CONTROL_STATE_CHANGED_REASON_WAKEUP + * @see #CONTROL_STATE_CHANGED_REASON_STANDBY + */ + void onControlStateChanged(boolean enabled, int reason); } /** diff --git a/core/java/android/hardware/hdmi/HdmiTvClient.java b/core/java/android/hardware/hdmi/HdmiTvClient.java index 9d92fd9..683d04b 100644 --- a/core/java/android/hardware/hdmi/HdmiTvClient.java +++ b/core/java/android/hardware/hdmi/HdmiTvClient.java @@ -22,6 +22,9 @@ import android.hardware.hdmi.HdmiTimerRecordSources.TimerRecordSource; import android.os.RemoteException; import android.util.Log; +import java.util.Collections; +import java.util.List; + import libcore.util.EmptyArray; /** @@ -150,6 +153,21 @@ public final class HdmiTvClient extends HdmiClient { } /** + * Returns all the CEC devices connected to TV. + * + * @return list of {@link HdmiDeviceInfo} for connected CEC devices. + * Empty list is returned if there is none. + */ + public List<HdmiDeviceInfo> getDeviceList() { + try { + return mService.getDeviceList(); + } catch (RemoteException e) { + Log.e("TAG", "Failed to call getDeviceList():", e); + return Collections.<HdmiDeviceInfo>emptyList(); + } + } + + /** * Set system audio volume * * @param oldIndex current volume index diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl index 4866a9a..c1e924e 100644 --- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl +++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl @@ -59,6 +59,7 @@ interface IHdmiControlService { void setSystemAudioMute(boolean mute); void setInputChangeListener(IHdmiInputChangeListener listener); List<HdmiDeviceInfo> getInputDevices(); + List<HdmiDeviceInfo> getDeviceList(); void sendVendorCommand(int deviceType, int targetAddress, in byte[] params, boolean hasVendorId); void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType); diff --git a/core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl b/core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl index 55cc925..a16e878 100644 --- a/core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl +++ b/core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl @@ -23,5 +23,6 @@ package android.hardware.hdmi; * @hide */ oneway interface IHdmiVendorCommandListener { - void onReceived(int logicalAddress, in byte[] operands, boolean hasVendorId); + void onReceived(int logicalAddress, int destAddress, in byte[] operands, boolean hasVendorId); + void onControlStateChanged(boolean enabled, int reason); } 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/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 7538dff..134171a 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -4078,9 +4078,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> */ public void removeView(View view) { - removeViewInternal(view); - requestLayout(); - invalidate(true); + if (removeViewInternal(view)) { + requestLayout(); + invalidate(true); + } } /** @@ -4143,11 +4144,13 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager invalidate(true); } - private void removeViewInternal(View view) { + private boolean removeViewInternal(View view) { final int index = indexOfChild(view); if (index >= 0) { removeViewInternal(index, view); + return true; } + return false; } private void removeViewInternal(int index, View view) { diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 47ee52e..75c9ebd 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -523,15 +523,6 @@ public interface WindowManager extends ViewManager { public static final int TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW+27; /** - * Window type: Recents. Same layer as {@link #TYPE_SYSTEM_DIALOG} but only appears on - * one user's screen. - * In multiuser systems shows on all users' windows. - * @hide - */ - public static final int TYPE_RECENTS_OVERLAY = FIRST_SYSTEM_WINDOW+28; - - - /** * Window type: keyguard scrim window. Shows if keyguard needs to be restarted. * In multiuser systems shows on all users' windows. * @hide 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); } |
