diff options
Diffstat (limited to 'core/java/android')
19 files changed, 525 insertions, 110 deletions
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 5085b1e..b29035d 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -2090,8 +2090,15 @@ public final class ActivityThread { r.activity.mCalled = false; mInstrumentation.callActivityOnPause(r.activity); // We need to keep around the original state, in case - // we need to be created again. - r.state = oldState; + // we need to be created again. But we only do this + // for pre-Honeycomb apps, which always save their state + // when pausing, so we can not have them save their state + // when restarting from a paused state. For HC and later, + // we want to (and can) let the state be saved as the normal + // part of stopping the activity. + if (r.isPreHoneycomb()) { + r.state = oldState; + } if (!r.activity.mCalled) { throw new SuperNotCalledException( "Activity " + r.intent.getComponent().toShortString() + diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java index d3ba497..5e6b090 100644 --- a/core/java/android/app/Fragment.java +++ b/core/java/android/app/Fragment.java @@ -28,6 +28,7 @@ import android.os.Parcelable; import android.util.AndroidRuntimeException; import android.util.AttributeSet; import android.util.DebugUtils; +import android.util.Log; import android.util.SparseArray; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; @@ -108,7 +109,9 @@ final class FragmentState implements Parcelable { mInstance.mRetainInstance = mRetainInstance; mInstance.mDetached = mDetached; mInstance.mFragmentManager = activity.mFragments; - + if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG, + "Instantiated fragment " + mInstance); + return mInstance; } diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index 7e1daa4..6058bdc 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -1074,6 +1074,7 @@ final class FragmentManagerImpl extends FragmentManager { f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1)); mActive.set(f.mIndex, f); } + if (DEBUG) Log.v(TAG, "Allocated fragment index " + f); } void makeInactive(Fragment f) { @@ -1081,7 +1082,7 @@ final class FragmentManagerImpl extends FragmentManager { return; } - if (DEBUG) Log.v(TAG, "Freeing fragment index " + f.mIndex); + if (DEBUG) Log.v(TAG, "Freeing fragment index " + f); mActive.set(f.mIndex, null); if (mAvailIndices == null) { mAvailIndices = new ArrayList<Integer>(); @@ -1493,6 +1494,7 @@ final class FragmentManagerImpl extends FragmentManager { fragments.add(f); f.mRetaining = true; f.mTargetIndex = f.mTarget != null ? f.mTarget.mIndex : -1; + if (DEBUG) Log.v(TAG, "retainNonConfig: keeping retained " + f); } } } diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index 01b68d4..ed95ae5 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -38,6 +38,7 @@ import android.util.SparseArray; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; +import android.view.accessibility.AccessibilityNodeInfo; import android.widget.Adapter; import android.widget.AdapterView; import android.widget.BaseAdapter; @@ -523,6 +524,12 @@ public class AppWidgetHostView extends FrameLayout { return tv; } + @Override + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(info); + info.setClassName(AppWidgetHostView.class.getName()); + } + private static class ParcelableSparseArray extends SparseArray<Parcelable> implements Parcelable { public int describeContents() { return 0; diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index c791e47..da09a18 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -810,11 +810,17 @@ public class Intent implements Parcelable, Cloneable { * <p> * As a convenience, an Intent of this form can be created with the * {@link #createChooser} function. - * <p>Input: No data should be specified. get*Extra must have + * <p> + * If the target {@link #EXTRA_INTENT} contains {@link ClipData}, you should + * also copy it to this intent along with relevant flags, such as + * {@link #FLAG_GRANT_READ_URI_PERMISSION}. + * <p> + * Input: No data should be specified. get*Extra must have * a {@link #EXTRA_INTENT} field containing the Intent being executed, * and can optionally have a {@link #EXTRA_TITLE} field containing the * title text to display in the chooser. - * <p>Output: Depends on the protocol of {@link #EXTRA_INTENT}. + * <p> + * Output: Depends on the protocol of {@link #EXTRA_INTENT}. */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_CHOOSER = "android.intent.action.CHOOSER"; @@ -835,8 +841,17 @@ public class Intent implements Parcelable, Cloneable { if (title != null) { intent.putExtra(EXTRA_TITLE, title); } + + // Migrate any clip data and flags from target. + final ClipData targetClipData = target.getClipData(); + if (targetClipData != null) { + intent.setClipData(targetClipData); + intent.addFlags(target.getFlags() + & (FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION)); + } return intent; } + /** * Activity Action: Allow the user to select a particular kind of data and * return it. This is different than {@link #ACTION_PICK} in that here we @@ -6587,19 +6602,35 @@ public class Intent implements Parcelable, Cloneable { /** * Migrate any {@link #EXTRA_STREAM} in {@link #ACTION_SEND} and - * {@link #ACTION_SEND_MULTIPLE} to {@link ClipData}. + * {@link #ACTION_SEND_MULTIPLE} to {@link ClipData}. Also inspects nested + * intents in {@link #ACTION_CHOOSER}. * + * @return Whether any contents were migrated. * @hide */ - public void migrateExtraStreamToClipData() { + public boolean migrateExtraStreamToClipData() { // Refuse to touch if extras already parcelled - if (mExtras != null && mExtras.isParcelled()) return; + if (mExtras != null && mExtras.isParcelled()) return false; // Bail when someone already gave us ClipData - if (getClipData() != null) return; + if (getClipData() != null) return false; final String action = getAction(); - if (ACTION_SEND.equals(action)) { + if (ACTION_CHOOSER.equals(action)) { + // Inspect target intent to see if we need to migrate + final Intent target = getParcelableExtra(EXTRA_INTENT); + if (target.migrateExtraStreamToClipData()) { + // Since we migrated in child, we need to promote ClipData and + // flags to ourselves to grant. + setClipData(target.getClipData()); + addFlags(target.getFlags() + & (FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION)); + return true; + } else { + return false; + } + + } else if (ACTION_SEND.equals(action)) { try { final Uri stream = getParcelableExtra(EXTRA_STREAM); final CharSequence text = getCharSequenceExtra(EXTRA_TEXT); @@ -6610,6 +6641,7 @@ public class Intent implements Parcelable, Cloneable { new ClipData.Item(text, htmlText, null, stream)); setClipData(clipData); addFlags(FLAG_GRANT_READ_URI_PERMISSION); + return true; } } catch (ClassCastException e) { } @@ -6626,14 +6658,14 @@ public class Intent implements Parcelable, Cloneable { if (texts != null) { if (num >= 0 && num != texts.size()) { // Wha...! F- you. - return; + return false; } num = texts.size(); } if (htmlTexts != null) { if (num >= 0 && num != htmlTexts.size()) { // Wha...! F- you. - return; + return false; } num = htmlTexts.size(); } @@ -6648,10 +6680,13 @@ public class Intent implements Parcelable, Cloneable { setClipData(clipData); addFlags(FLAG_GRANT_READ_URI_PERMISSION); + return true; } } catch (ClassCastException e) { } } + + return false; } private static ClipData.Item makeClipItem(ArrayList<Uri> streams, ArrayList<CharSequence> texts, diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java index e1f1db2..16f9a18 100644 --- a/core/java/android/view/AccessibilityInteractionController.java +++ b/core/java/android/view/AccessibilityInteractionController.java @@ -491,20 +491,28 @@ final class AccessibilityInteractionController { if ((direction & View.FOCUS_ACCESSIBILITY) == View.FOCUS_ACCESSIBILITY) { AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider(); if (provider != null) { - next = provider.accessibilityFocusSearch(direction, - virtualDescendantId); - } else if (virtualDescendantId == View.NO_ID) { - View nextView = root.focusSearch(direction); - if (nextView != null) { - // If the focus search reached a node with a provider - // we delegate to the provider to find the next one. - provider = nextView.getAccessibilityNodeProvider(); - if (provider != null) { - next = provider.accessibilityFocusSearch(direction, - virtualDescendantId); - } else { - next = nextView.createAccessibilityNodeInfo(); - } + next = provider.accessibilityFocusSearch(direction, virtualDescendantId); + if (next != null) { + return; + } + } + View nextView = root.focusSearch(direction); + while (nextView != null) { + // If the focus search reached a node with a provider + // we delegate to the provider to find the next one. + // If the provider does not return a virtual view to + // take accessibility focus we try the next view found + // by the focus search algorithm. + provider = nextView.getAccessibilityNodeProvider(); + if (provider != null) { + next = provider.accessibilityFocusSearch(direction, View.NO_ID); + if (next != null) { + break; + } + nextView = nextView.focusSearch(direction); + } else { + next = nextView.createAccessibilityNodeInfo(); + break; } } } else { diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 14523d3..a4fcd41 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -6027,7 +6027,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal return; } if ((focusableMode & FOCUSABLES_ACCESSIBILITY) == FOCUSABLES_ACCESSIBILITY) { - if (canTakeAccessibilityFocusFromHover()) { + if (canTakeAccessibilityFocusFromHover() || getAccessibilityNodeProvider() != null) { views.add(this); return; } @@ -6156,12 +6156,15 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * @hide */ public void clearAccessibilityFocus() { - if ((mPrivateFlags2 & ACCESSIBILITY_FOCUSED) != 0) { - mPrivateFlags2 &= ~ACCESSIBILITY_FOCUSED; - ViewRootImpl viewRootImpl = getViewRootImpl(); - if (viewRootImpl != null) { + ViewRootImpl viewRootImpl = getViewRootImpl(); + if (viewRootImpl != null) { + View focusHost = viewRootImpl.getAccessibilityFocusedHost(); + if (focusHost != null && ViewRootImpl.isViewDescendantOf(focusHost, this)) { viewRootImpl.setAccessibilityFocusedHost(null); } + } + if ((mPrivateFlags2 & ACCESSIBILITY_FOCUSED) != 0) { + mPrivateFlags2 &= ~ACCESSIBILITY_FOCUSED; invalidate(); sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED); notifyAccessibilityStateChanged(); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index f86e036..41cd887 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -488,7 +488,9 @@ public final class ViewRootImpl implements ViewParent, mFallbackEventHandler.setView(view); mWindowAttributes.copyFrom(attrs); attrs = mWindowAttributes; - + + setAccessibilityFocusedHost(null); + if (view instanceof RootViewSurfaceTaker) { mSurfaceHolderCallback = ((RootViewSurfaceTaker)view).willYouTakeTheSurface(); @@ -556,6 +558,7 @@ public final class ViewRootImpl implements ViewParent, mInputChannel = null; mFallbackEventHandler.setView(null); unscheduleTraversals(); + setAccessibilityFocusedHost(null); throw new RuntimeException("Adding window failed", e); } finally { if (restore) { @@ -575,6 +578,7 @@ public final class ViewRootImpl implements ViewParent, mAdded = false; mFallbackEventHandler.setView(null); unscheduleTraversals(); + setAccessibilityFocusedHost(null); switch (res) { case WindowManagerImpl.ADD_BAD_APP_TOKEN: case WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN: @@ -635,8 +639,6 @@ public final class ViewRootImpl implements ViewParent, if (view.getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) { view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); } - - setAccessibilityFocusedHost(null); } } } @@ -1853,18 +1855,15 @@ public final class ViewRootImpl implements ViewParent, performDraw(); } } else { - // End any pending transitions on this non-visible window - if (mPendingTransitions != null && mPendingTransitions.size() > 0) { + if (viewVisibility == View.VISIBLE) { + // Try again + scheduleTraversals(); + } else if (mPendingTransitions != null && mPendingTransitions.size() > 0) { for (int i = 0; i < mPendingTransitions.size(); ++i) { mPendingTransitions.get(i).endChangingAnimations(); } mPendingTransitions.clear(); } - - if (viewVisibility == View.VISIBLE) { - // Try again - scheduleTraversals(); - } } } @@ -2543,11 +2542,51 @@ public final class ViewRootImpl implements ViewParent, } void setAccessibilityFocusedHost(View host) { - if (mAccessibilityFocusedHost != null && mAccessibilityFocusedVirtualView == null) { + // If we have a virtual view with accessibility focus we need + // to clear the focus and invalidate the virtual view bounds. + if (mAccessibilityFocusedVirtualView != null) { + + AccessibilityNodeInfo focusNode = mAccessibilityFocusedVirtualView; + View focusHost = mAccessibilityFocusedHost; + focusHost.clearAccessibilityFocusNoCallbacks(); + + // Wipe the state of the current accessibility focus since + // the call into the provider to clear accessibility focus + // will fire an accessibility event which will end up calling + // this method and we want to have clean state when this + // invocation happens. + mAccessibilityFocusedHost = null; + mAccessibilityFocusedVirtualView = null; + + AccessibilityNodeProvider provider = focusHost.getAccessibilityNodeProvider(); + if (provider != null) { + // Invalidate the area of the cleared accessibility focus. + focusNode.getBoundsInParent(mTempRect); + focusHost.invalidate(mTempRect); + // Clear accessibility focus in the virtual node. + final int virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId( + focusNode.getSourceNodeId()); + provider.performAction(virtualNodeId, + AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null); + } + } + if (mAccessibilityFocusedHost != null) { + // Clear accessibility focus in the view. mAccessibilityFocusedHost.clearAccessibilityFocusNoCallbacks(); } + + // Set the new focus host. mAccessibilityFocusedHost = host; - mAccessibilityFocusedVirtualView = null; + + // If the host has a provide find the virtual descendant that has focus. + if (mAccessibilityFocusedHost != null) { + AccessibilityNodeProvider provider = + mAccessibilityFocusedHost.getAccessibilityNodeProvider(); + if (provider != null) { + mAccessibilityFocusedVirtualView = provider.findAccessibilityFocus(View.NO_ID); + return; + } + } } public void requestChildFocus(View child, View focused) { @@ -2633,6 +2672,8 @@ public final class ViewRootImpl implements ViewParent, destroyHardwareRenderer(); + setAccessibilityFocusedHost(null); + mView = null; mAttachInfo.mRootView = null; mAttachInfo.mSurface = null; @@ -4608,6 +4649,31 @@ public final class ViewRootImpl implements ViewParent, if (mView == null) { return false; } + // Watch for accessibility focus change events from virtual nodes + // to keep track of accessibility focus being on a virtual node. + final int eventType = event.getEventType(); + switch (eventType) { + case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: { + final long sourceId = event.getSourceNodeId(); + // If the event is not from a virtual node we are not interested. + final int virtualViewId = AccessibilityNodeInfo.getVirtualDescendantId(sourceId); + if (virtualViewId == AccessibilityNodeInfo.UNDEFINED) { + break; + } + final int realViewId = AccessibilityNodeInfo.getAccessibilityViewId(sourceId); + View focusHost = mView.findViewByAccessibilityId(realViewId); + setAccessibilityFocusedHost(focusHost); + } break; + case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: { + final long sourceId = event.getSourceNodeId(); + // If the event is not from a virtual node we are not interested. + final int virtualViewId = AccessibilityNodeInfo.getVirtualDescendantId(sourceId); + if (virtualViewId == AccessibilityNodeInfo.UNDEFINED) { + break; + } + setAccessibilityFocusedHost(null); + } break; + } mAccessibilityManager.sendAccessibilityEvent(event); return true; } diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java index 8e529b9..ce67718 100644 --- a/core/java/android/webkit/WebViewClassic.java +++ b/core/java/android/webkit/WebViewClassic.java @@ -7986,7 +7986,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc } nativeSetTextSelection(mNativeClass, data.mSelectTextPtr); - if (data.mSelectionReason == TextSelectionData.REASON_ACCESSIBILITY_INJECTOR) { + if ((data.mSelectionReason == TextSelectionData.REASON_ACCESSIBILITY_INJECTOR) + || (!mSelectingText + && data.mSelectionReason != TextSelectionData.REASON_SELECT_WORD)) { selectionDone(); mShowTextSelectionExtra = true; invalidate(); diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 4adfd6a..7aa9a0b 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -22,7 +22,6 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.database.Cursor; import android.graphics.Point; import android.graphics.Rect; -import android.graphics.Region; import android.media.MediaFile; import android.net.ProxyProperties; import android.net.Uri; @@ -864,6 +863,7 @@ public final class WebViewCore { static class TextSelectionData { static final int REASON_UNKNOWN = 0; static final int REASON_ACCESSIBILITY_INJECTOR = 1; + static final int REASON_SELECT_WORD = 2; public TextSelectionData(int start, int end, int selectTextPtr) { mStart = start; mEnd = end; @@ -1718,12 +1718,16 @@ public final class WebViewCore { break; } case SELECT_WORD_AT: { + mTextSelectionChangeReason + = TextSelectionData.REASON_SELECT_WORD; int x = msg.arg1; int y = msg.arg2; if (!nativeSelectWordAt(mNativeClass, x, y)) { mWebViewClassic.mPrivateHandler.obtainMessage(WebViewClassic.SHOW_CARET_HANDLE) .sendToTarget(); } + mTextSelectionChangeReason + = TextSelectionData.REASON_UNKNOWN; break; } case SELECT_ALL: diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index ab9d370..04c8cdc 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -1480,6 +1480,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te @Override public boolean performAccessibilityAction(int action, Bundle arguments) { + if (super.performAccessibilityAction(action, arguments)) { + return true; + } switch (action) { case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: { if (getLastVisiblePosition() < getCount() - 1) { @@ -1496,7 +1499,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } return false; } - return super.performAccessibilityAction(action, arguments); + return false; } /** diff --git a/core/java/android/widget/ActivityChooserModel.java b/core/java/android/widget/ActivityChooserModel.java index c6104bc..fe6c4f5 100644 --- a/core/java/android/widget/ActivityChooserModel.java +++ b/core/java/android/widget/ActivityChooserModel.java @@ -765,16 +765,6 @@ public class ActivityChooserModel extends DataSetObservable { } /** - * Gets whether the given observer is already registered. - * - * @param observer The observer. - * @return True if already registered. - */ - public boolean isRegisteredObserver(DataSetObserver observer) { - return mObservers.contains(observer); - } - - /** * Represents a record in the history. */ public final static class HistoricalRecord { diff --git a/core/java/android/widget/ActivityChooserView.java b/core/java/android/widget/ActivityChooserView.java index 0c0bb1e..be6b4e2 100644 --- a/core/java/android/widget/ActivityChooserView.java +++ b/core/java/android/widget/ActivityChooserView.java @@ -20,10 +20,8 @@ import com.android.internal.R; import android.content.Context; import android.content.Intent; -import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; -import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.database.DataSetObserver; @@ -176,11 +174,6 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod private int mDefaultActionButtonContentDescription; /** - * Whether this view has a default activity affordance. - */ - private boolean mHasDefaultActivity; - - /** * Create a new instance. * * @param context The application environment. @@ -252,8 +245,6 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod Resources resources = context.getResources(); mListPopupMaxWidth = Math.max(resources.getDisplayMetrics().widthPixels / 2, resources.getDimensionPixelSize(com.android.internal.R.dimen.config_prefDialogWidth)); - - updateHasDefaultActivity(); } /** @@ -267,21 +258,6 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod } } - @Override - protected void onConfigurationChanged(Configuration newConfig) { - Configuration oldConfig = mContext.getResources().getConfiguration(); - final int changed = oldConfig.diff(newConfig); - if ((changed & ActivityInfo.CONFIG_SCREEN_SIZE) != 0 - || (changed & ActivityInfo.CONFIG_ORIENTATION) != 0) { - updateHasDefaultActivity(); - } - } - - private void updateHasDefaultActivity() { - mHasDefaultActivity = mContext.getResources().getBoolean( - R.bool.activity_chooser_view_has_default_activity); - } - /** * Sets the background for the button that expands the activity * overflow list. @@ -407,8 +383,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod protected void onAttachedToWindow() { super.onAttachedToWindow(); ActivityChooserModel dataModel = mAdapter.getDataModel(); - if (dataModel != null - && !dataModel.isRegisteredObserver(mModelDataSetOberver)) { + if (dataModel != null) { dataModel.registerObserver(mModelDataSetOberver); } mIsAttachedToWindow = true; @@ -418,8 +393,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod protected void onDetachedFromWindow() { super.onDetachedFromWindow(); ActivityChooserModel dataModel = mAdapter.getDataModel(); - if (dataModel != null - && dataModel.isRegisteredObserver(mModelDataSetOberver)) { + if (dataModel != null) { dataModel.unregisterObserver(mModelDataSetOberver); } ViewTreeObserver viewTreeObserver = getViewTreeObserver(); @@ -522,7 +496,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod // Default activity button. final int activityCount = mAdapter.getActivityCount(); final int historySize = mAdapter.getHistorySize(); - if (mHasDefaultActivity && activityCount > 0 && historySize > 0) { + if (activityCount > 0 && historySize > 0) { mDefaultActivityButton.setVisibility(VISIBLE); ResolveInfo activity = mAdapter.getDefaultActivity(); PackageManager packageManager = mContext.getPackageManager(); @@ -538,9 +512,9 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod } // Activity chooser content. if (mDefaultActivityButton.getVisibility() == VISIBLE) { - mActivityChooserContent.setBackground(mActivityChooserContentBackground); + mActivityChooserContent.setBackgroundDrawable(mActivityChooserContentBackground); } else { - mActivityChooserContent.setBackground(null); + mActivityChooserContent.setBackgroundDrawable(null); } } @@ -603,7 +577,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod // OnLongClickListener#onLongClick @Override public boolean onLongClick(View view) { - if (mHasDefaultActivity && view == mDefaultActivityButton) { + if (view == mDefaultActivityButton) { if (mAdapter.getCount() > 0) { mIsSelectingDefaultActivity = true; showPopupUnchecked(mInitialActivityCount); @@ -656,16 +630,14 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod public void setDataModel(ActivityChooserModel dataModel) { ActivityChooserModel oldDataModel = mAdapter.getDataModel(); - if (oldDataModel != null) { + if (oldDataModel != null && isShown()) { oldDataModel.unregisterObserver(mModelDataSetOberver); } mDataModel = dataModel; - if (dataModel != null) { + if (dataModel != null && isShown()) { dataModel.registerObserver(mModelDataSetOberver); - notifyDataSetChanged(); - } else { - notifyDataSetInvalidated(); } + notifyDataSetChanged(); } @Override diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java index 03fdc39..b2c8164 100644 --- a/core/java/android/widget/Gallery.java +++ b/core/java/android/widget/Gallery.java @@ -20,6 +20,7 @@ import android.annotation.Widget; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Rect; +import android.os.Bundle; import android.util.AttributeSet; import android.util.Log; import android.view.ContextMenu.ContextMenuInfo; @@ -1367,6 +1368,35 @@ public class Gallery extends AbsSpinner implements GestureDetector.OnGestureList public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.setClassName(Gallery.class.getName()); + info.setScrollable(mItemCount > 1); + if (mItemCount > 0 && mSelectedPosition < mItemCount - 1) { + info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD); + } + if (mItemCount > 0 && mSelectedPosition > 0) { + info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD); + } + } + + @Override + public boolean performAccessibilityAction(int action, Bundle arguments) { + if (super.performAccessibilityAction(action, arguments)) { + return true; + } + switch (action) { + case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: { + if (mItemCount > 0 && mSelectedPosition < mItemCount - 1) { + final int currentChildIndex = mSelectedPosition - mFirstPosition; + return scrollToChild(currentChildIndex + 1); + } + } return false; + case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: { + if (mItemCount > 0 && mSelectedPosition > 0) { + final int currentChildIndex = mSelectedPosition - mFirstPosition; + return scrollToChild(currentChildIndex - 1); + } + } return false; + } + return false; } /** diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java index ffabd1d..f889cb7 100644 --- a/core/java/android/widget/HorizontalScrollView.java +++ b/core/java/android/widget/HorizontalScrollView.java @@ -739,6 +739,9 @@ public class HorizontalScrollView extends FrameLayout { @Override public boolean performAccessibilityAction(int action, Bundle arguments) { + if (super.performAccessibilityAction(action, arguments)) { + return true; + } switch (action) { case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: { final int viewportWidth = getWidth() - mPaddingLeft - mPaddingRight; @@ -757,7 +760,7 @@ public class HorizontalScrollView extends FrameLayout { } } return false; } - return super.performAccessibilityAction(action, arguments); + return false; } @Override diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java index 7c809b3..b825e1b 100644 --- a/core/java/android/widget/NumberPicker.java +++ b/core/java/android/widget/NumberPicker.java @@ -950,6 +950,8 @@ public class NumberPicker extends LinearLayout { provider.sendAccessibilityEventForVirtualView(hoveredVirtualViewId, AccessibilityEvent.TYPE_VIEW_HOVER_ENTER); mLastHoveredChildVirtualViewId = hoveredVirtualViewId; + provider.performAction(hoveredVirtualViewId, + AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); } break; case MotionEvent.ACTION_HOVER_MOVE: { if (mLastHoveredChildVirtualViewId != hoveredVirtualViewId @@ -960,6 +962,8 @@ public class NumberPicker extends LinearLayout { provider.sendAccessibilityEventForVirtualView(hoveredVirtualViewId, AccessibilityEvent.TYPE_VIEW_HOVER_ENTER); mLastHoveredChildVirtualViewId = hoveredVirtualViewId; + provider.performAction(hoveredVirtualViewId, + AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); } } break; case MotionEvent.ACTION_HOVER_EXIT: { @@ -1413,9 +1417,16 @@ public class NumberPicker extends LinearLayout { } @Override - public void sendAccessibilityEvent(int eventType) { - // Do not send accessibility events - we want the user to - // perceive this widget as several controls rather as a whole. + public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { + // We do not want the real descendant to be considered focus search + // since it is managed by the accessibility node provider. + if ((focusableMode & FOCUSABLES_ACCESSIBILITY) == FOCUSABLES_ACCESSIBILITY) { + if (canTakeAccessibilityFocusFromHover() || getAccessibilityNodeProvider() != null) { + views.add(this); + return; + } + } + super.addFocusables(views, direction, focusableMode); } @Override @@ -2072,7 +2083,12 @@ public class NumberPicker extends LinearLayout { } } + /** + * Class for managing virtual view tree rooted at this picker. + */ class AccessibilityNodeProviderImpl extends AccessibilityNodeProvider { + private static final int UNDEFINED = Integer.MIN_VALUE; + private static final int VIRTUAL_VIEW_ID_INCREMENT = 1; private static final int VIRTUAL_VIEW_ID_INPUT = 2; @@ -2083,6 +2099,8 @@ public class NumberPicker extends LinearLayout { private final int[] mTempArray = new int[2]; + private int mAccessibilityFocusedView = UNDEFINED; + @Override public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) { switch (virtualViewId) { @@ -2137,6 +2155,37 @@ public class NumberPicker extends LinearLayout { @Override public boolean performAction(int virtualViewId, int action, Bundle arguments) { switch (virtualViewId) { + case View.NO_ID: { + switch (action) { + case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: { + if (mAccessibilityFocusedView != virtualViewId) { + mAccessibilityFocusedView = virtualViewId; + requestAccessibilityFocus(); + return true; + } + } return false; + case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: { + if (mAccessibilityFocusedView == virtualViewId) { + mAccessibilityFocusedView = UNDEFINED; + clearAccessibilityFocus(); + return true; + } + return false; + } + case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: { + if (getWrapSelectorWheel() || getValue() < getMaxValue()) { + changeValueByOne(true); + return true; + } + } return false; + case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: { + if (getWrapSelectorWheel() || getValue() > getMinValue()) { + changeValueByOne(false); + return true; + } + } return false; + } + } break; case VIRTUAL_VIEW_ID_INPUT: { switch (action) { case AccessibilityNodeInfo.ACTION_FOCUS: { @@ -2149,25 +2198,182 @@ public class NumberPicker extends LinearLayout { mInputText.clearFocus(); return true; } - } break; + return false; + } + case AccessibilityNodeInfo.ACTION_CLICK: { + showSoftInput(); + return true; + } + case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: { + if (mAccessibilityFocusedView != virtualViewId) { + mAccessibilityFocusedView = virtualViewId; + sendAccessibilityEventForVirtualView(virtualViewId, + AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); + mInputText.invalidate(); + return true; + } + } return false; + case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: { + if (mAccessibilityFocusedView == virtualViewId) { + mAccessibilityFocusedView = UNDEFINED; + sendAccessibilityEventForVirtualView(virtualViewId, + AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED); + mInputText.invalidate(); + return true; + } + } return false; + default: { + return mInputText.performAccessibilityAction(action, arguments); + } } - } break; + } return false; + case VIRTUAL_VIEW_ID_INCREMENT: { + switch (action) { + case AccessibilityNodeInfo.ACTION_CLICK: { + NumberPicker.this.changeValueByOne(true); + sendAccessibilityEventForVirtualView(virtualViewId, + AccessibilityEvent.TYPE_VIEW_CLICKED); + } return true; + case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: { + if (mAccessibilityFocusedView != virtualViewId) { + mAccessibilityFocusedView = virtualViewId; + sendAccessibilityEventForVirtualView(virtualViewId, + AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); + invalidate(0, mBottomSelectionDividerBottom, mRight, mBottom); + return true; + } + } return false; + case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: { + if (mAccessibilityFocusedView == virtualViewId) { + mAccessibilityFocusedView = UNDEFINED; + sendAccessibilityEventForVirtualView(virtualViewId, + AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED); + invalidate(0, mBottomSelectionDividerBottom, mRight, mBottom); + return true; + } + } return false; + } + } return false; + case VIRTUAL_VIEW_ID_DECREMENT: { + switch (action) { + case AccessibilityNodeInfo.ACTION_CLICK: { + final boolean increment = (virtualViewId == VIRTUAL_VIEW_ID_INCREMENT); + NumberPicker.this.changeValueByOne(increment); + sendAccessibilityEventForVirtualView(virtualViewId, + AccessibilityEvent.TYPE_VIEW_CLICKED); + } return true; + case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: { + if (mAccessibilityFocusedView != virtualViewId) { + mAccessibilityFocusedView = virtualViewId; + sendAccessibilityEventForVirtualView(virtualViewId, + AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); + invalidate(0, 0, mRight, mTopSelectionDividerTop); + return true; + } + } return false; + case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: { + if (mAccessibilityFocusedView == virtualViewId) { + mAccessibilityFocusedView = UNDEFINED; + sendAccessibilityEventForVirtualView(virtualViewId, + AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED); + invalidate(0, 0, mRight, mTopSelectionDividerTop); + return true; + } + } return false; + } + } return false; } return super.performAction(virtualViewId, action, arguments); } + @Override + public AccessibilityNodeInfo findAccessibilityFocus(int virtualViewId) { + return createAccessibilityNodeInfo(mAccessibilityFocusedView); + } + + @Override + public AccessibilityNodeInfo accessibilityFocusSearch(int direction, int virtualViewId) { + switch (direction) { + case View.ACCESSIBILITY_FOCUS_DOWN: + case View.ACCESSIBILITY_FOCUS_FORWARD: { + switch (mAccessibilityFocusedView) { + case UNDEFINED: { + return createAccessibilityNodeInfo(View.NO_ID); + } + case View.NO_ID: { + if (hasVirtualDecrementButton()) { + return createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_DECREMENT); + } + } + //$FALL-THROUGH$ + case VIRTUAL_VIEW_ID_DECREMENT: { + return createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_INPUT); + } + case VIRTUAL_VIEW_ID_INPUT: { + if (hasVirtualIncrementButton()) { + return createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_INCREMENT); + } + } + //$FALL-THROUGH$ + case VIRTUAL_VIEW_ID_INCREMENT: { + View nextFocus = NumberPicker.this.focusSearch(direction); + if (nextFocus != null) { + return nextFocus.createAccessibilityNodeInfo(); + } + return null; + } + } + } break; + case View.ACCESSIBILITY_FOCUS_UP: + case View.ACCESSIBILITY_FOCUS_BACKWARD: { + switch (mAccessibilityFocusedView) { + case UNDEFINED: { + return createAccessibilityNodeInfo(View.NO_ID); + } + case View.NO_ID: { + if (hasVirtualIncrementButton()) { + return createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_INCREMENT); + } + } + //$FALL-THROUGH$ + case VIRTUAL_VIEW_ID_INCREMENT: { + return createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_INPUT); + } + case VIRTUAL_VIEW_ID_INPUT: { + if (hasVirtualDecrementButton()) { + return createAccessibilityNodeInfo(VIRTUAL_VIEW_ID_DECREMENT); + } + } + //$FALL-THROUGH$ + case VIRTUAL_VIEW_ID_DECREMENT: { + View nextFocus = NumberPicker.this.focusSearch(direction); + if (nextFocus != null) { + return nextFocus.createAccessibilityNodeInfo(); + } + return null; + } + } + } break; + } + return null; + } + public void sendAccessibilityEventForVirtualView(int virtualViewId, int eventType) { switch (virtualViewId) { case VIRTUAL_VIEW_ID_DECREMENT: { - sendAccessibilityEventForVirtualButton(virtualViewId, eventType, - getVirtualDecrementButtonText()); + if (hasVirtualDecrementButton()) { + sendAccessibilityEventForVirtualButton(virtualViewId, eventType, + getVirtualDecrementButtonText()); + } } break; case VIRTUAL_VIEW_ID_INPUT: { sendAccessibilityEventForVirtualText(eventType); } break; case VIRTUAL_VIEW_ID_INCREMENT: { - sendAccessibilityEventForVirtualButton(virtualViewId, eventType, - getVirtualIncrementButtonText()); + if (hasVirtualIncrementButton()) { + sendAccessibilityEventForVirtualButton(virtualViewId, eventType, + getVirtualIncrementButtonText()); + } } break; } } @@ -2227,8 +2433,13 @@ public class NumberPicker extends LinearLayout { private AccessibilityNodeInfo createAccessibiltyNodeInfoForInputText() { AccessibilityNodeInfo info = mInputText.createAccessibilityNodeInfo(); - info.setLongClickable(true); info.setSource(NumberPicker.this, VIRTUAL_VIEW_ID_INPUT); + if (mAccessibilityFocusedView != VIRTUAL_VIEW_ID_INPUT) { + info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS); + } + if (mAccessibilityFocusedView == VIRTUAL_VIEW_ID_INPUT) { + info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS); + } return info; } @@ -2252,6 +2463,15 @@ public class NumberPicker extends LinearLayout { getLocationOnScreen(locationOnScreen); boundsInScreen.offset(locationOnScreen[0], locationOnScreen[1]); info.setBoundsInScreen(boundsInScreen); + + if (mAccessibilityFocusedView != virtualViewId) { + info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS); + } + if (mAccessibilityFocusedView == virtualViewId) { + info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS); + } + info.addAction(AccessibilityNodeInfo.ACTION_CLICK); + return info; } @@ -2261,9 +2481,15 @@ public class NumberPicker extends LinearLayout { info.setClassName(NumberPicker.class.getName()); info.setPackageName(mContext.getPackageName()); info.setSource(NumberPicker.this); - info.addChild(NumberPicker.this, VIRTUAL_VIEW_ID_DECREMENT); + + if (hasVirtualDecrementButton()) { + info.addChild(NumberPicker.this, VIRTUAL_VIEW_ID_DECREMENT); + } info.addChild(NumberPicker.this, VIRTUAL_VIEW_ID_INPUT); - info.addChild(NumberPicker.this, VIRTUAL_VIEW_ID_INCREMENT); + if (hasVirtualIncrementButton()) { + info.addChild(NumberPicker.this, VIRTUAL_VIEW_ID_INCREMENT); + } + info.setParent((View) getParent()); info.setEnabled(NumberPicker.this.isEnabled()); info.setScrollable(true); @@ -2276,9 +2502,31 @@ public class NumberPicker extends LinearLayout { getLocationOnScreen(locationOnScreen); boundsInScreen.offset(locationOnScreen[0], locationOnScreen[1]); info.setBoundsInScreen(boundsInScreen); + + if (mAccessibilityFocusedView != View.NO_ID) { + info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS); + } + if (mAccessibilityFocusedView == View.NO_ID) { + info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS); + } + if (getWrapSelectorWheel() || getValue() < getMaxValue()) { + info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD); + } + if (getWrapSelectorWheel() || getValue() > getMinValue()) { + info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD); + } + return info; } + private boolean hasVirtualDecrementButton() { + return getWrapSelectorWheel() || getValue() > getMinValue(); + } + + private boolean hasVirtualIncrementButton() { + return getWrapSelectorWheel() || getValue() < getMaxValue(); + } + private String getVirtualDecrementButtonText() { int value = mValue - 1; if (mWrapSelectorWheel) { diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index b398ce4..a499743 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -742,6 +742,9 @@ public class ScrollView extends FrameLayout { @Override public boolean performAccessibilityAction(int action, Bundle arguments) { + if (super.performAccessibilityAction(action, arguments)) { + return true; + } switch (action) { case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: { final int viewportHeight = getHeight() - mPaddingBottom - mPaddingTop; @@ -760,7 +763,7 @@ public class ScrollView extends FrameLayout { } } return false; } - return super.performAccessibilityAction(action, arguments); + return false; } @Override diff --git a/core/java/android/widget/ShareActionProvider.java b/core/java/android/widget/ShareActionProvider.java index 21840ca..4045497 100644 --- a/core/java/android/widget/ShareActionProvider.java +++ b/core/java/android/widget/ShareActionProvider.java @@ -44,7 +44,6 @@ import com.android.internal.R; * <code> * // In Activity#onCreateOptionsMenu * public boolean onCreateOptionsMenu(Menu menu) { - * getManuInflater().inflate(R.menu.my_menu, menu); * // Get the menu item. * MenuItem menuItem = menu.findItem(R.id.my_menu_item); * // Get the provider and hold onto it to set/change the share intent. @@ -246,7 +245,7 @@ public class ShareActionProvider extends ActionProvider { * call {@link android.app.Activity#invalidateOptionsMenu()} to recreate the * action view. You should <strong>not</strong> call * {@link android.app.Activity#invalidateOptionsMenu()} from - * {@link android.app.Activity#onCreateOptionsMenu(Menu)}. + * {@link android.app.Activity#onCreateOptionsMenu(Menu)}." * <p> * <code> * private void doShare(Intent intent) { diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java index 22df3bc..dd0915b 100644 --- a/core/java/android/widget/StackView.java +++ b/core/java/android/widget/StackView.java @@ -32,6 +32,7 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; import android.graphics.TableMaskFilter; +import android.os.Bundle; import android.util.AttributeSet; import android.util.Log; import android.view.InputDevice; @@ -1228,6 +1229,35 @@ public class StackView extends AdapterViewAnimator { public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.setClassName(StackView.class.getName()); + info.setScrollable(getChildCount() > 1); + if (getDisplayedChild() < getChildCount() - 1) { + info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD); + } + if (getDisplayedChild() > 0) { + info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD); + } + } + + @Override + public boolean performAccessibilityAction(int action, Bundle arguments) { + if (super.performAccessibilityAction(action, arguments)) { + return true; + } + switch (action) { + case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: { + if (getDisplayedChild() < getChildCount() - 1) { + showNext(); + return true; + } + } return false; + case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: { + if (getDisplayedChild() > 0) { + showPrevious(); + return true; + } + } return false; + } + return false; } class LayoutParams extends ViewGroup.LayoutParams { |
