diff options
-rw-r--r-- | core/java/android/app/Activity.java | 46 | ||||
-rw-r--r-- | core/java/android/app/Instrumentation.java | 9 | ||||
-rw-r--r-- | core/java/android/content/Context.java | 28 | ||||
-rw-r--r-- | core/java/android/content/ContextWrapper.java | 11 | ||||
-rw-r--r-- | core/java/android/view/View.java | 77 | ||||
-rw-r--r-- | core/java/android/view/ViewGroup.java | 20 | ||||
-rw-r--r-- | core/java/android/widget/Editor.java | 37 | ||||
-rw-r--r-- | core/java/android/widget/TextView.java | 47 |
8 files changed, 261 insertions, 14 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index b5817df..a0d59d9 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -94,6 +94,7 @@ import android.view.View.OnCreateContextMenuListener; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.view.ViewManager; +import android.view.ViewRootImpl; import android.view.Window; import android.view.WindowManager; import android.view.WindowManagerGlobal; @@ -4467,21 +4468,38 @@ public class Activity extends ContextThemeWrapper */ public void startActivityFromFragment(@NonNull Fragment fragment, Intent intent, int requestCode, @Nullable Bundle options) { + startActivityForResult(fragment.mWho, intent, requestCode, options); + } + + /** + * @hide + */ + @Override + public void startActivityForResult( + String who, Intent intent, int requestCode, @Nullable Bundle options) { if (options != null) { mActivityTransitionState.startExitOutTransition(this, options); } Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity( - this, mMainThread.getApplicationThread(), mToken, fragment, + this, mMainThread.getApplicationThread(), mToken, who, intent, requestCode, options); if (ar != null) { mMainThread.sendActivityResult( - mToken, fragment.mWho, requestCode, + mToken, who, requestCode, ar.getResultCode(), ar.getResultData()); } } /** + * @hide + */ + @Override + public boolean canStartActivityForResult() { + return true; + } + + /** * Same as calling {@link #startIntentSenderFromChild(Activity, IntentSender, * int, Intent, int, int, int, Bundle)} with no options. */ @@ -6364,12 +6382,24 @@ public class Activity extends ContextThemeWrapper onActivityResult(requestCode, resultCode, data); } } else { - Fragment frag = mFragments.findFragmentByWho(who); - if (frag != null) { - if (isRequestPermissionResult(data)) { - dispatchRequestPermissionsResultToFragment(requestCode, data, frag); - } else { - frag.onActivityResult(requestCode, resultCode, data); + if (who.startsWith("@android:view:")) { + ArrayList<ViewRootImpl> views = WindowManagerGlobal.getInstance().getRootViews( + getActivityToken()); + for (ViewRootImpl viewRoot : views) { + if (viewRoot.getView() != null + && viewRoot.getView().dispatchActivityResult( + who, requestCode, resultCode, data)) { + return; + } + } + } else { + Fragment frag = mFragments.findFragmentByWho(who); + if (frag != null) { + if (isRequestPermissionResult(data)) { + dispatchRequestPermissionsResultToFragment(requestCode, data, frag); + } else { + frag.onActivityResult(requestCode, resultCode, data); + } } } } diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index 5572d30..fc96464 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -1568,7 +1568,7 @@ public class Instrumentation { /** * Like {@link #execStartActivity(android.content.Context, android.os.IBinder, - * android.os.IBinder, Fragment, android.content.Intent, int, android.os.Bundle)}, + * android.os.IBinder, String, android.content.Intent, int, android.os.Bundle)}, * but for calls from a {#link Fragment}. * * @param who The Context from which the activity is being started. @@ -1576,7 +1576,7 @@ public class Instrumentation { * is being started. * @param token Internal token identifying to the system who is starting * the activity; may be null. - * @param target Which fragment is performing the start (and thus receiving + * @param target Which element is performing the start (and thus receiving * any result). * @param intent The actual Intent to start. * @param requestCode Identifier for this request's result; less than zero @@ -1595,7 +1595,7 @@ public class Instrumentation { * {@hide} */ public ActivityResult execStartActivity( - Context who, IBinder contextThread, IBinder token, Fragment target, + Context who, IBinder contextThread, IBinder token, String target, Intent intent, int requestCode, Bundle options) { IApplicationThread whoThread = (IApplicationThread) contextThread; if (mActivityMonitors != null) { @@ -1619,8 +1619,7 @@ public class Instrumentation { int result = ActivityManagerNative.getDefault() .startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), - token, target != null ? target.mWho : null, - requestCode, 0, null, options); + token, target, requestCode, 0, null, options); checkStartActivityResult(result, intent); } catch (RemoteException e) { } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 9450dce..5cd8025 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -1298,6 +1298,34 @@ public abstract class Context { } /** + * Version of {@link #startActivity(Intent, Bundle)} that returns a result to the caller. This + * is only supported for Views and Fragments. + * @param who The identifier for the calling element that will receive the result. + * @param intent The intent to start. + * @param requestCode The code that will be returned with onActivityResult() identifying this + * request. + * @param options Additional options for how the Activity should be started. + * May be null if there are no options. See {@link android.app.ActivityOptions} + * for how to build the Bundle supplied here; there are no supported definitions + * for building it manually. + * @hide + */ + public void startActivityForResult( + @NonNull String who, Intent intent, int requestCode, @Nullable Bundle options) { + throw new RuntimeException("This method is only implemented for Activity-based Contexts. " + + "Check canStartActivityForResult() before calling."); + } + + /** + * Identifies whether this Context instance will be able to process calls to + * {@link #startActivityForResult(String, Intent, int, Bundle)}. + * @hide + */ + public boolean canStartActivityForResult() { + return false; + } + + /** * Same as {@link #startActivities(Intent[], Bundle)} with no options * specified. * diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 8c5a87c..92f0079 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -336,6 +336,17 @@ public class ContextWrapper extends Context { mBase.startActivityAsUser(intent, user); } + /** @hide **/ + public void startActivityForResult( + String who, Intent intent, int requestCode, Bundle options) { + mBase.startActivityForResult(who, intent, requestCode, options); + } + + /** @hide **/ + public boolean canStartActivityForResult() { + return mBase.canStartActivityForResult(); + } + @Override public void startActivity(Intent intent, Bundle options) { mBase.startActivity(intent, options); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index b6f1e3b..60d2ceb 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -30,6 +30,7 @@ import android.annotation.Nullable; import android.annotation.Size; import android.content.ClipData; import android.content.Context; +import android.content.Intent; import android.content.res.ColorStateList; import android.content.res.Configuration; import android.content.res.Resources; @@ -3550,6 +3551,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private static SparseArray<String> mAttributeMap; /** + * @hide + */ + String mStartActivityRequestWho; + + /** * Simple constructor to use when creating a view from code. * * @param context The Context the view is running in, through which it can @@ -4915,6 +4921,58 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Call {@link Context#startActivityForResult(String, Intent, int, Bundle)} for the View's + * Context, creating a unique View identifier to retrieve the result. + * + * @param intent The Intent to be started. + * @param requestCode The request code to use. + * @hide + */ + public void startActivityForResult(Intent intent, int requestCode) { + mStartActivityRequestWho = "@android:view:" + System.identityHashCode(this); + getContext().startActivityForResult(mStartActivityRequestWho, intent, requestCode, null); + } + + /** + * If this View corresponds to the calling who, dispatches the activity result. + * @param who The identifier for the targeted View to receive the result. + * @param requestCode The integer request code originally supplied to + * startActivityForResult(), allowing you to identify who this + * result came from. + * @param resultCode The integer result code returned by the child activity + * through its setResult(). + * @param data An Intent, which can return result data to the caller + * (various data can be attached to Intent "extras"). + * @return {@code true} if the activity result was dispatched. + * @hide + */ + public boolean dispatchActivityResult( + String who, int requestCode, int resultCode, Intent data) { + if (mStartActivityRequestWho != null && mStartActivityRequestWho.equals(who)) { + onActivityResult(requestCode, resultCode, data); + mStartActivityRequestWho = null; + return true; + } + return false; + } + + /** + * Receive the result from a previous call to {@link #startActivityForResult(Intent, int)}. + * + * @param requestCode The integer request code originally supplied to + * startActivityForResult(), allowing you to identify who this + * result came from. + * @param resultCode The integer result code returned by the child activity + * through its setResult(). + * @param data An Intent, which can return result data to the caller + * (various data can be attached to Intent "extras"). + * @hide + */ + public void onActivityResult(int requestCode, int resultCode, Intent data) { + // Do nothing. + } + + /** * Register a callback to be invoked when a hardware key is pressed in this view. * Key presses in software input methods will generally not trigger the methods of * this listener. @@ -13980,6 +14038,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @CallSuper protected Parcelable onSaveInstanceState() { mPrivateFlags |= PFLAG_SAVE_STATE_CALLED; + if (mStartActivityRequestWho != null) { + BaseSavedState state = new BaseSavedState(AbsSavedState.EMPTY_STATE); + state.mStartActivityRequestWhoSaved = mStartActivityRequestWho; + return state; + } return BaseSavedState.EMPTY_STATE; } @@ -14039,13 +14102,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @CallSuper protected void onRestoreInstanceState(Parcelable state) { mPrivateFlags |= PFLAG_SAVE_STATE_CALLED; - if (state != BaseSavedState.EMPTY_STATE && state != null) { + if (state != null && !(state instanceof AbsSavedState)) { throw new IllegalArgumentException("Wrong state class, expecting View State but " + "received " + state.getClass().toString() + " instead. This usually happens " + "when two views of different type have the same id in the same hierarchy. " + "This view's id is " + ViewDebug.resolveId(mContext, getId()) + ". Make sure " + "other views do not use the same id."); } + if (state != null && state instanceof BaseSavedState) { + mStartActivityRequestWho = ((BaseSavedState) state).mStartActivityRequestWhoSaved; + } } /** @@ -20735,6 +20801,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * state in {@link android.view.View#onSaveInstanceState()}. */ public static class BaseSavedState extends AbsSavedState { + String mStartActivityRequestWhoSaved; + /** * Constructor used when reading from a parcel. Reads the state of the superclass. * @@ -20742,6 +20810,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public BaseSavedState(Parcel source) { super(source); + mStartActivityRequestWhoSaved = source.readString(); } /** @@ -20753,6 +20822,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, super(superState); } + @Override + public void writeToParcel(Parcel out, int flags) { + super.writeToParcel(out, flags); + out.writeString(mStartActivityRequestWhoSaved); + } + public static final Parcelable.Creator<BaseSavedState> CREATOR = new Parcelable.Creator<BaseSavedState>() { public BaseSavedState createFromParcel(Parcel in) { diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index d0705bb..8d06ce2 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -19,6 +19,7 @@ package android.view; import android.animation.LayoutTransition; import android.annotation.IdRes; import android.content.Context; +import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.TypedArray; @@ -814,6 +815,25 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } /** + * @hide + */ + @Override + public boolean dispatchActivityResult( + String who, int requestCode, int resultCode, Intent data) { + if (super.dispatchActivityResult(who, requestCode, resultCode, data)) { + return true; + } + int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + if (child.dispatchActivityResult(who, requestCode, resultCode, data)) { + return true; + } + } + return false; + } + + /** * Find the nearest view in the specified direction that wants to take * focus. * diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 5fb0c92..acb92dd 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -27,6 +27,7 @@ import android.content.UndoManager; import android.content.UndoOperation; import android.content.UndoOwner; import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; @@ -109,6 +110,7 @@ import java.text.BreakIterator; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; +import java.util.List; /** * Helper class used by TextView to handle editable text views. @@ -2980,6 +2982,8 @@ public class Editor { } } + addIntentMenuItemsForTextProcessing(menu); + if (menu.hasVisibleItems() || mode.getCustomView() != null) { mTextView.setHasTransientState(true); return true; @@ -3036,6 +3040,32 @@ public class Editor { styledAttributes.recycle(); } + private void addIntentMenuItemsForTextProcessing(Menu menu) { + if (mTextView.canProcessText()) { + PackageManager packageManager = mTextView.getContext().getPackageManager(); + List<ResolveInfo> supportedActivities = + packageManager.queryIntentActivities(createProcessTextIntent(), 0); + for (ResolveInfo info : supportedActivities) { + menu.add(info.loadLabel(packageManager)) + .setIntent(createProcessTextIntentForResolveInfo(info)) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM + | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + } + } + } + + private Intent createProcessTextIntent() { + return new Intent() + .setAction(Intent.ACTION_PROCESS_TEXT) + .setType("text/plain"); + } + + private Intent createProcessTextIntentForResolveInfo(ResolveInfo info) { + return createProcessTextIntent() + .putExtra(Intent.EXTRA_PROCESS_TEXT_READONLY, !mTextView.isTextEditable()) + .setClassName(info.activityInfo.packageName, info.activityInfo.name); + } + @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { updateReplaceItem(menu); @@ -3060,6 +3090,13 @@ public class Editor { @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + if (item.getIntent() != null + && item.getIntent().getAction().equals(Intent.ACTION_PROCESS_TEXT)) { + item.getIntent().putExtra(Intent.EXTRA_PROCESS_TEXT, mTextView.getSelectedText()); + mTextView.startActivityForResult( + item.getIntent(), TextView.PROCESS_TEXT_REQUEST_CODE); + return true; + } if (mCustomSelectionActionModeCallback != null && mCustomSelectionActionModeCallback.onActionItemClicked(mode, item)) { return true; diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 11439e4..5d60f99 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -27,6 +27,7 @@ import android.annotation.XmlRes; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; +import android.content.Intent; import android.content.UndoManager; import android.content.res.ColorStateList; import android.content.res.CompatibilityInfo; @@ -289,6 +290,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // System wide time for last cut or copy action. static long LAST_CUT_OR_COPY_TIME; + /** + * @hide + */ + static final int PROCESS_TEXT_REQUEST_CODE = 100; + private ColorStateList mTextColor; private ColorStateList mHintTextColor; private ColorStateList mLinkTextColor; @@ -1414,6 +1420,23 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } + /** + * @hide + */ + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == PROCESS_TEXT_REQUEST_CODE) { + CharSequence result = data != null + ? data.getCharSequenceExtra(Intent.EXTRA_PROCESS_TEXT) + : ""; + if (isTextEditable()) { + replaceSelectionWithText(result); + } else { + Toast.makeText(getContext(), String.valueOf(result), Toast.LENGTH_LONG).show(); + } + } + } + private void setTypefaceFromAttrs(String familyName, int typefaceIndex, int styleIndex) { Typeface tf = null; if (familyName != null) { @@ -7474,6 +7497,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return selectionStart >= 0 && selectionStart != selectionEnd; } + String getSelectedText() { + if (hasSelection()) { + return String.valueOf(mText.subSequence(getSelectionStart(), getSelectionEnd())); + } + return null; + } + /** * Sets the properties of this field (lines, horizontally scrolling, * transformation method) to be for a single-line input. @@ -9066,6 +9096,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener hasPrimaryClip()); } + boolean canProcessText() { + if (!getContext().canStartActivityForResult() || getId() == View.NO_ID + || hasPasswordTransformationMethod()) { + return false; + } + + if (mText.length() > 0 && hasSelection() && mEditor != null) { + return true; + } + + return false; + } + boolean selectAllText() { // Need to hide insert point cursor controller before settings selection, otherwise insert // point cursor controller obtains cursor update event and update cursor with cancelling @@ -9078,6 +9121,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return length > 0; } + void replaceSelectionWithText(CharSequence text) { + ((Editable) mText).replace(getSelectionStart(), getSelectionEnd(), text); + } + /** * Paste clipboard content between min and max positions. */ |