Loading core/java/com/android/internal/app/ChooserActivity.java +64 −9 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import android.content.Intent; import android.content.IntentSender; import android.content.IntentSender.SendIntentException; import android.content.ServiceConnection; import android.content.SharedPreferences; import android.content.pm.ActivityInfo; import android.content.pm.LabeledIntent; import android.content.pm.PackageManager; Loading @@ -35,6 +36,7 @@ import android.graphics.Color; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.IBinder; import android.os.Message; Loading Loading @@ -68,6 +70,7 @@ import com.android.internal.R; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.MetricsProto.MetricsEvent; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; Loading @@ -91,6 +94,11 @@ public class ChooserActivity extends ResolverActivity { private ChooserListAdapter mChooserListAdapter; private ChooserRowAdapter mChooserRowAdapter; private SharedPreferences mPinnedSharedPrefs; private static final float PINNED_TARGET_SCORE_BOOST = 1000.f; private static final String PINNED_SHARED_PREFS_NAME = "chooser_pin_settings"; private static final String TARGET_DETAILS_FRAGMENT_TAG = "targetDetailsFragment"; private final List<ChooserTargetServiceConnection> mServiceConnections = new ArrayList<>(); private static final int CHOOSER_TARGET_SERVICE_RESULT = 1; Loading Loading @@ -207,12 +215,30 @@ public class ChooserActivity extends ResolverActivity { mRefinementIntentSender = intent.getParcelableExtra( Intent.EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER); setSafeForwardingMode(true); mPinnedSharedPrefs = getPinnedSharedPrefs(this); super.onCreate(savedInstanceState, target, title, defaultTitleRes, initialIntents, null, false); MetricsLogger.action(this, MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN); } static SharedPreferences getPinnedSharedPrefs(Context context) { // The code below is because in the android:ui process, no one can hear you scream. // The package info in the context isn't initialized in the way it is for normal apps, // so the standard, name-based context.getSharedPreferences doesn't work. Instead, we // build the path manually below using the same policy that appears in ContextImpl. // This fails silently under the hood if there's a problem, so if we find ourselves in // the case where we don't have access to credential encrypted storage we just won't // have our pinned target info. final File prefsFile = new File(new File( Environment.getDataUserCredentialEncryptedPackageDirectory(null, context.getUserId(), context.getPackageName()), "shared_prefs"), PINNED_SHARED_PREFS_NAME + ".xml"); return context.getSharedPreferences(prefsFile, MODE_PRIVATE); } @Override protected void onDestroy() { super.onDestroy(); Loading Loading @@ -243,7 +269,7 @@ public class ChooserActivity extends ResolverActivity { } @Override void onActivityStarted(TargetInfo cti) { public void onActivityStarted(TargetInfo cti) { if (mChosenComponentSender != null) { final ComponentName target = cti.getResolvedComponentName(); if (target != null) { Loading @@ -259,7 +285,7 @@ public class ChooserActivity extends ResolverActivity { } @Override void onPrepareAdapterView(AbsListView adapterView, ResolveListAdapter adapter, public void onPrepareAdapterView(AbsListView adapterView, ResolveListAdapter adapter, boolean alwaysUseOption) { final ListView listView = adapterView instanceof ListView ? (ListView) adapterView : null; mChooserListAdapter = (ChooserListAdapter) adapter; Loading @@ -272,17 +298,17 @@ public class ChooserActivity extends ResolverActivity { } @Override int getLayoutResource() { public int getLayoutResource() { return R.layout.chooser_grid; } @Override boolean shouldGetActivityMetadata() { public boolean shouldGetActivityMetadata() { return true; } @Override boolean shouldAutoLaunchSingleChoice(TargetInfo target) { public boolean shouldAutoLaunchSingleChoice(TargetInfo target) { final Intent intent = target.getResolvedIntent(); final ResolveInfo resolve = target.getResolveInfo(); Loading @@ -299,6 +325,16 @@ public class ChooserActivity extends ResolverActivity { return false; } @Override public void showTargetDetails(ResolveInfo ri) { ComponentName name = ri.activityInfo.getComponentName(); boolean pinned = mPinnedSharedPrefs.getBoolean(name.flattenToString(), false); ResolverTargetActionsDialogFragment f = new ResolverTargetActionsDialogFragment(ri.loadLabel(getPackageManager()), name, pinned); f.show(getFragmentManager(), TARGET_DETAILS_FRAGMENT_TAG); } private void modifyTargetIntent(Intent in) { final String action = in.getAction(); if (Intent.ACTION_SEND.equals(action) || Loading Loading @@ -340,7 +376,7 @@ public class ChooserActivity extends ResolverActivity { } @Override void startSelected(int which, boolean always, boolean filtered) { public void startSelected(int which, boolean always, boolean filtered) { super.startSelected(which, always, filtered); if (mChooserListAdapter != null) { Loading Loading @@ -471,7 +507,7 @@ public class ChooserActivity extends ResolverActivity { mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT); } void onSetupVoiceInteraction() { public void onSetupVoiceInteraction() { // Do nothing. We'll send the voice stuff ourselves. } Loading Loading @@ -543,7 +579,7 @@ public class ChooserActivity extends ResolverActivity { } @Override ResolveListAdapter createAdapter(Context context, List<Intent> payloadIntents, public ResolveListAdapter createAdapter(Context context, List<Intent> payloadIntents, Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid, boolean filterLastUsed) { final ChooserListAdapter adapter = new ChooserListAdapter(context, payloadIntents, Loading Loading @@ -711,6 +747,11 @@ public class ChooserActivity extends ResolverActivity { } return results; } @Override public boolean isPinned() { return mSourceInfo != null ? mSourceInfo.isPinned() : false; } } public class ChooserListAdapter extends ResolveListAdapter { Loading Loading @@ -776,6 +817,20 @@ public class ChooserActivity extends ResolverActivity { return false; } @Override public boolean isComponentPinned(ComponentName name) { return mPinnedSharedPrefs.getBoolean(name.flattenToString(), false); } @Override public float getScore(DisplayResolveInfo target) { float score = super.getScore(target); if (target.isPinned()) { score += PINNED_TARGET_SCORE_BOOST; } return score; } @Override public View onCreateView(ViewGroup parent) { return mInflater.inflate( Loading Loading @@ -1121,7 +1176,7 @@ public class ChooserActivity extends ResolverActivity { v.setOnLongClickListener(new OnLongClickListener() { @Override public boolean onLongClick(View v) { showAppDetails( showTargetDetails( mChooserListAdapter.resolveInfoForPosition( holder.itemIndices[column], true)); return true; Loading core/java/com/android/internal/app/ResolverActivity.java +68 −34 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.app.ActivityThread; import android.app.VoiceInteractor.PickOptionRequest; import android.app.VoiceInteractor.PickOptionRequest.Option; import android.app.VoiceInteractor.Prompt; import android.content.pm.ComponentInfo; import android.os.AsyncTask; import android.provider.Settings; import android.text.TextUtils; Loading Loading @@ -336,12 +337,12 @@ public class ResolverActivity extends Activity { /** * Perform any initialization needed for voice interaction. */ void onSetupVoiceInteraction() { public void onSetupVoiceInteraction() { // Do it right now. Subclasses may delay this and send it later. sendVoiceChoicesIfNeeded(); } void sendVoiceChoicesIfNeeded() { public void sendVoiceChoicesIfNeeded() { if (!isVoiceInteraction()) { // Clearly not needed. return; Loading Loading @@ -382,7 +383,7 @@ public class ResolverActivity extends Activity { return null; } int getLayoutResource() { public int getLayoutResource() { return R.layout.resolver_list; } Loading Loading @@ -591,7 +592,7 @@ public class ResolverActivity extends Activity { mAlwaysUseOption); } void startSelected(int which, boolean always, boolean filtered) { public void startSelected(int which, boolean always, boolean filtered) { if (isFinishing()) { return; } Loading Loading @@ -761,7 +762,7 @@ public class ResolverActivity extends Activity { return true; } void safelyStartActivity(TargetInfo cti) { public void safelyStartActivity(TargetInfo cti) { // If needed, show that intent is forwarded // from managed profile to owner or other way around. if (mProfileSwitchMessageId != -1) { Loading Loading @@ -791,26 +792,26 @@ public class ResolverActivity extends Activity { } } void onActivityStarted(TargetInfo cti) { public void onActivityStarted(TargetInfo cti) { // Do nothing } boolean shouldGetActivityMetadata() { public boolean shouldGetActivityMetadata() { return false; } boolean shouldAutoLaunchSingleChoice(TargetInfo target) { public boolean shouldAutoLaunchSingleChoice(TargetInfo target) { return true; } void showAppDetails(ResolveInfo ri) { public void showTargetDetails(ResolveInfo ri) { Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) .setData(Uri.fromParts("package", ri.activityInfo.packageName, null)) .addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); startActivity(in); } ResolveListAdapter createAdapter(Context context, List<Intent> payloadIntents, public ResolveListAdapter createAdapter(Context context, List<Intent> payloadIntents, Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid, boolean filterLastUsed) { return new ResolveListAdapter(context, payloadIntents, initialIntents, rList, Loading @@ -820,7 +821,7 @@ public class ResolverActivity extends Activity { /** * Returns true if the activity is finishing and creation should halt */ boolean configureContentView(List<Intent> payloadIntents, Intent[] initialIntents, public boolean configureContentView(List<Intent> payloadIntents, Intent[] initialIntents, List<ResolveInfo> rList, boolean alwaysUseOption) { // The last argument of createAdapter is whether to do special handling // of the last used choice to highlight it in the list. We need to always Loading Loading @@ -867,7 +868,7 @@ public class ResolverActivity extends Activity { return false; } void onPrepareAdapterView(AbsListView adapterView, ResolveListAdapter adapter, public void onPrepareAdapterView(AbsListView adapterView, ResolveListAdapter adapter, boolean alwaysUseOption) { final boolean useHeader = adapter.hasFilteredItem(); final ListView listView = adapterView instanceof ListView ? (ListView) adapterView : null; Loading Loading @@ -898,7 +899,7 @@ public class ResolverActivity extends Activity { && Objects.equals(lhs.activityInfo.packageName, rhs.activityInfo.packageName); } final class DisplayResolveInfo implements TargetInfo { public final class DisplayResolveInfo implements TargetInfo { private final ResolveInfo mResolveInfo; private final CharSequence mDisplayLabel; private Drawable mDisplayIcon; Loading @@ -906,8 +907,9 @@ public class ResolverActivity extends Activity { private final CharSequence mExtendedInfo; private final Intent mResolvedIntent; private final List<Intent> mSourceIntents = new ArrayList<>(); private boolean mPinned; DisplayResolveInfo(Intent originalIntent, ResolveInfo pri, CharSequence pLabel, public DisplayResolveInfo(Intent originalIntent, ResolveInfo pri, CharSequence pLabel, CharSequence pInfo, Intent pOrigIntent) { mSourceIntents.add(originalIntent); mResolveInfo = pri; Loading @@ -932,6 +934,7 @@ public class ResolverActivity extends Activity { mExtendedInfo = other.mExtendedInfo; mResolvedIntent = new Intent(other.mResolvedIntent); mResolvedIntent.fillIn(fillInIntent, flags); mPinned = other.mPinned; } public ResolveInfo getResolveInfo() { Loading Loading @@ -1026,6 +1029,15 @@ public class ResolverActivity extends Activity { activity.startActivityAsUser(mResolvedIntent, options, user); return false; } @Override public boolean isPinned() { return mPinned; } public void setPinned(boolean pinned) { mPinned = pinned; } } /** Loading @@ -1039,7 +1051,7 @@ public class ResolverActivity extends Activity { * * @return the resolved intent for this target */ public Intent getResolvedIntent(); Intent getResolvedIntent(); /** * Get the resolved component name that represents this target. Note that this may not Loading @@ -1048,7 +1060,7 @@ public class ResolverActivity extends Activity { * * @return the resolved ComponentName for this target */ public ComponentName getResolvedComponentName(); ComponentName getResolvedComponentName(); /** * Start the activity referenced by this target. Loading @@ -1057,7 +1069,7 @@ public class ResolverActivity extends Activity { * @param options ActivityOptions bundle * @return true if the start completed successfully */ public boolean start(Activity activity, Bundle options); boolean start(Activity activity, Bundle options); /** * Start the activity referenced by this target as if the ResolverActivity's caller Loading @@ -1068,7 +1080,7 @@ public class ResolverActivity extends Activity { * @param userId userId to start as or {@link UserHandle#USER_NULL} for activity's caller * @return true if the start completed successfully */ public boolean startAsCaller(Activity activity, Bundle options, int userId); boolean startAsCaller(Activity activity, Bundle options, int userId); /** * Start the activity referenced by this target as a given user. Loading @@ -1078,7 +1090,7 @@ public class ResolverActivity extends Activity { * @param user handle for the user to start the activity as * @return true if the start completed successfully */ public boolean startAsUser(Activity activity, Bundle options, UserHandle user); boolean startAsUser(Activity activity, Bundle options, UserHandle user); /** * Return the ResolveInfo about how and why this target matched the original query Loading @@ -1086,14 +1098,14 @@ public class ResolverActivity extends Activity { * * @return ResolveInfo representing this target's match */ public ResolveInfo getResolveInfo(); ResolveInfo getResolveInfo(); /** * Return the human-readable text label for this target. * * @return user-visible target label */ public CharSequence getDisplayLabel(); CharSequence getDisplayLabel(); /** * Return any extended info for this target. This may be used to disambiguate Loading @@ -1101,35 +1113,40 @@ public class ResolverActivity extends Activity { * * @return human-readable disambig string or null if none present */ public CharSequence getExtendedInfo(); CharSequence getExtendedInfo(); /** * @return The drawable that should be used to represent this target */ public Drawable getDisplayIcon(); Drawable getDisplayIcon(); /** * @return The (small) icon to badge the target with */ public Drawable getBadgeIcon(); Drawable getBadgeIcon(); /** * @return The content description for the badge icon */ public CharSequence getBadgeContentDescription(); CharSequence getBadgeContentDescription(); /** * Clone this target with the given fill-in information. */ public TargetInfo cloneFilledIn(Intent fillInIntent, int flags); TargetInfo cloneFilledIn(Intent fillInIntent, int flags); /** * @return the list of supported source intents deduped against this single target */ public List<Intent> getAllSourceIntents(); List<Intent> getAllSourceIntents(); /** * @return true if this target should be pinned to the front by the request of the user */ boolean isPinned(); } class ResolveListAdapter extends BaseAdapter { public class ResolveListAdapter extends BaseAdapter { private final List<Intent> mIntents; private final Intent[] mInitialIntents; private final List<ResolveInfo> mBaseResolveList; Loading Loading @@ -1376,9 +1393,12 @@ public class ResolverActivity extends Activity { } } if (!found) { into.add(new ResolvedComponentInfo(new ComponentName( newInfo.activityInfo.packageName, newInfo.activityInfo.name), intent, newInfo)); final ComponentName name = new ComponentName( newInfo.activityInfo.packageName, newInfo.activityInfo.name); final ResolvedComponentInfo rci = new ResolvedComponentInfo(name, intent, newInfo); rci.setPinned(isComponentPinned(name)); into.add(rci); } } } Loading Loading @@ -1454,6 +1474,7 @@ public class ResolverActivity extends Activity { final Intent replaceIntent = getReplacementIntent(add.activityInfo, intent); final DisplayResolveInfo dri = new DisplayResolveInfo(intent, add, roLabel, extraInfo, replaceIntent); dri.setPinned(rci.isPinned()); addResolveInfo(dri); if (replaceIntent == intent) { // Only add alternates if we didn't get a specific replacement from Loading Loading @@ -1537,11 +1558,11 @@ public class ResolverActivity extends Activity { return false; } protected int getDisplayResolveInfoCount() { public int getDisplayResolveInfoCount() { return mDisplayList.size(); } protected DisplayResolveInfo getDisplayResolveInfo(int index) { public DisplayResolveInfo getDisplayResolveInfo(int index) { // Used to query services. We only query services for primary targets, not alternates. return mDisplayList.get(index); } Loading Loading @@ -1571,6 +1592,10 @@ public class ResolverActivity extends Activity { return !TextUtils.isEmpty(info.getExtendedInfo()); } public boolean isComponentPinned(ComponentName name) { return false; } public final void bindView(int position, View view) { onBindView(view, getItem(position)); } Loading Loading @@ -1607,6 +1632,7 @@ public class ResolverActivity extends Activity { static final class ResolvedComponentInfo { public final ComponentName name; private boolean mPinned; private final List<Intent> mIntents = new ArrayList<>(); private final List<ResolveInfo> mResolveInfos = new ArrayList<>(); Loading Loading @@ -1649,6 +1675,14 @@ public class ResolverActivity extends Activity { } return -1; } public boolean isPinned() { return mPinned; } public void setPinned(boolean pinned) { mPinned = pinned; } } static class ViewHolder { Loading Loading @@ -1702,7 +1736,7 @@ public class ResolverActivity extends Activity { return false; } ResolveInfo ri = mAdapter.resolveInfoForPosition(position, true); showAppDetails(ri); showTargetDetails(ri); return true; } Loading core/java/com/android/internal/app/ResolverComparator.java +22 −10 Original line number Diff line number Diff line Loading @@ -47,8 +47,8 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> { private static final boolean DEBUG = false; // Two weeks private static final long USAGE_STATS_PERIOD = 1000 * 60 * 60 * 24 * 14; // One week private static final long USAGE_STATS_PERIOD = 1000 * 60 * 60 * 24 * 7; private static final long RECENCY_TIME_PERIOD = 1000 * 60 * 60 * 12; Loading Loading @@ -171,6 +171,17 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> { } } final boolean lPinned = lhsp.isPinned(); final boolean rPinned = rhsp.isPinned(); if (lPinned && !rPinned) { return -1; } else if (!lPinned && rPinned) { return 1; } // Pinned items stay stable within a normal lexical sort and ignore scoring. if (!lPinned && !rPinned) { if (mStats != null) { final ScoredTarget lhsTarget = mScoredTargets.get(new ComponentName( lhs.activityInfo.packageName, lhs.activityInfo.name)); Loading @@ -182,6 +193,7 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> { return diff > 0 ? 1 : -1; } } } CharSequence sa = lhs.loadLabel(mPm); if (sa == null) sa = lhs.activityInfo.name; Loading core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java 0 → 100644 +98 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.app; import android.app.AlertDialog.Builder; import android.app.Dialog; import android.app.DialogFragment; import android.content.ComponentName; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.net.Uri; import android.os.Bundle; import android.provider.Settings; import com.android.internal.R; /** * Shows a dialog with actions to take on a chooser target */ public class ResolverTargetActionsDialogFragment extends DialogFragment implements DialogInterface.OnClickListener { private static final String NAME_KEY = "componentName"; private static final String PINNED_KEY = "pinned"; private static final String TITLE_KEY = "title"; // Sync with R.array.resolver_target_actions_* resources private static final int TOGGLE_PIN_INDEX = 0; private static final int APP_INFO_INDEX = 1; public ResolverTargetActionsDialogFragment() { } public ResolverTargetActionsDialogFragment(CharSequence title, ComponentName name, boolean pinned) { Bundle args = new Bundle(); args.putCharSequence(TITLE_KEY, title); args.putParcelable(NAME_KEY, name); args.putBoolean(PINNED_KEY, pinned); setArguments(args); } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final Bundle args = getArguments(); final int itemRes = args.getBoolean(PINNED_KEY, false) ? R.array.resolver_target_actions_unpin : R.array.resolver_target_actions_pin; return new Builder(getContext()) .setCancelable(true) .setItems(itemRes, this) .setTitle(args.getCharSequence(TITLE_KEY)) .create(); } @Override public void onClick(DialogInterface dialog, int which) { final Bundle args = getArguments(); ComponentName name = args.getParcelable(NAME_KEY); switch (which) { case TOGGLE_PIN_INDEX: SharedPreferences sp = ChooserActivity.getPinnedSharedPrefs(getContext()); final String key = name.flattenToString(); boolean currentVal = sp.getBoolean(name.flattenToString(), false); if (currentVal) { sp.edit().remove(key).apply(); } else { sp.edit().putBoolean(key, true).apply(); } // Force the chooser to requery and resort things getActivity().recreate(); break; case APP_INFO_INDEX: Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) .setData(Uri.fromParts("package", name.getPackageName(), null)) .addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); startActivity(in); break; } dismiss(); } } core/res/res/values/arrays.xml +11 −0 Original line number Diff line number Diff line Loading @@ -402,4 +402,15 @@ <item>@color/Red_700</item> </array> <!-- Used in ResolverTargetActionsDialogFragment --> <string-array name="resolver_target_actions_pin"> <item>@string/pin_target</item> <item>@string/app_info</item> </string-array> <string-array name="resolver_target_actions_unpin"> <item>@string/unpin_target</item> <item>@string/app_info</item> </string-array> </resources> Loading
core/java/com/android/internal/app/ChooserActivity.java +64 −9 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import android.content.Intent; import android.content.IntentSender; import android.content.IntentSender.SendIntentException; import android.content.ServiceConnection; import android.content.SharedPreferences; import android.content.pm.ActivityInfo; import android.content.pm.LabeledIntent; import android.content.pm.PackageManager; Loading @@ -35,6 +36,7 @@ import android.graphics.Color; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.IBinder; import android.os.Message; Loading Loading @@ -68,6 +70,7 @@ import com.android.internal.R; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.MetricsProto.MetricsEvent; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; Loading @@ -91,6 +94,11 @@ public class ChooserActivity extends ResolverActivity { private ChooserListAdapter mChooserListAdapter; private ChooserRowAdapter mChooserRowAdapter; private SharedPreferences mPinnedSharedPrefs; private static final float PINNED_TARGET_SCORE_BOOST = 1000.f; private static final String PINNED_SHARED_PREFS_NAME = "chooser_pin_settings"; private static final String TARGET_DETAILS_FRAGMENT_TAG = "targetDetailsFragment"; private final List<ChooserTargetServiceConnection> mServiceConnections = new ArrayList<>(); private static final int CHOOSER_TARGET_SERVICE_RESULT = 1; Loading Loading @@ -207,12 +215,30 @@ public class ChooserActivity extends ResolverActivity { mRefinementIntentSender = intent.getParcelableExtra( Intent.EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER); setSafeForwardingMode(true); mPinnedSharedPrefs = getPinnedSharedPrefs(this); super.onCreate(savedInstanceState, target, title, defaultTitleRes, initialIntents, null, false); MetricsLogger.action(this, MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN); } static SharedPreferences getPinnedSharedPrefs(Context context) { // The code below is because in the android:ui process, no one can hear you scream. // The package info in the context isn't initialized in the way it is for normal apps, // so the standard, name-based context.getSharedPreferences doesn't work. Instead, we // build the path manually below using the same policy that appears in ContextImpl. // This fails silently under the hood if there's a problem, so if we find ourselves in // the case where we don't have access to credential encrypted storage we just won't // have our pinned target info. final File prefsFile = new File(new File( Environment.getDataUserCredentialEncryptedPackageDirectory(null, context.getUserId(), context.getPackageName()), "shared_prefs"), PINNED_SHARED_PREFS_NAME + ".xml"); return context.getSharedPreferences(prefsFile, MODE_PRIVATE); } @Override protected void onDestroy() { super.onDestroy(); Loading Loading @@ -243,7 +269,7 @@ public class ChooserActivity extends ResolverActivity { } @Override void onActivityStarted(TargetInfo cti) { public void onActivityStarted(TargetInfo cti) { if (mChosenComponentSender != null) { final ComponentName target = cti.getResolvedComponentName(); if (target != null) { Loading @@ -259,7 +285,7 @@ public class ChooserActivity extends ResolverActivity { } @Override void onPrepareAdapterView(AbsListView adapterView, ResolveListAdapter adapter, public void onPrepareAdapterView(AbsListView adapterView, ResolveListAdapter adapter, boolean alwaysUseOption) { final ListView listView = adapterView instanceof ListView ? (ListView) adapterView : null; mChooserListAdapter = (ChooserListAdapter) adapter; Loading @@ -272,17 +298,17 @@ public class ChooserActivity extends ResolverActivity { } @Override int getLayoutResource() { public int getLayoutResource() { return R.layout.chooser_grid; } @Override boolean shouldGetActivityMetadata() { public boolean shouldGetActivityMetadata() { return true; } @Override boolean shouldAutoLaunchSingleChoice(TargetInfo target) { public boolean shouldAutoLaunchSingleChoice(TargetInfo target) { final Intent intent = target.getResolvedIntent(); final ResolveInfo resolve = target.getResolveInfo(); Loading @@ -299,6 +325,16 @@ public class ChooserActivity extends ResolverActivity { return false; } @Override public void showTargetDetails(ResolveInfo ri) { ComponentName name = ri.activityInfo.getComponentName(); boolean pinned = mPinnedSharedPrefs.getBoolean(name.flattenToString(), false); ResolverTargetActionsDialogFragment f = new ResolverTargetActionsDialogFragment(ri.loadLabel(getPackageManager()), name, pinned); f.show(getFragmentManager(), TARGET_DETAILS_FRAGMENT_TAG); } private void modifyTargetIntent(Intent in) { final String action = in.getAction(); if (Intent.ACTION_SEND.equals(action) || Loading Loading @@ -340,7 +376,7 @@ public class ChooserActivity extends ResolverActivity { } @Override void startSelected(int which, boolean always, boolean filtered) { public void startSelected(int which, boolean always, boolean filtered) { super.startSelected(which, always, filtered); if (mChooserListAdapter != null) { Loading Loading @@ -471,7 +507,7 @@ public class ChooserActivity extends ResolverActivity { mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT); } void onSetupVoiceInteraction() { public void onSetupVoiceInteraction() { // Do nothing. We'll send the voice stuff ourselves. } Loading Loading @@ -543,7 +579,7 @@ public class ChooserActivity extends ResolverActivity { } @Override ResolveListAdapter createAdapter(Context context, List<Intent> payloadIntents, public ResolveListAdapter createAdapter(Context context, List<Intent> payloadIntents, Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid, boolean filterLastUsed) { final ChooserListAdapter adapter = new ChooserListAdapter(context, payloadIntents, Loading Loading @@ -711,6 +747,11 @@ public class ChooserActivity extends ResolverActivity { } return results; } @Override public boolean isPinned() { return mSourceInfo != null ? mSourceInfo.isPinned() : false; } } public class ChooserListAdapter extends ResolveListAdapter { Loading Loading @@ -776,6 +817,20 @@ public class ChooserActivity extends ResolverActivity { return false; } @Override public boolean isComponentPinned(ComponentName name) { return mPinnedSharedPrefs.getBoolean(name.flattenToString(), false); } @Override public float getScore(DisplayResolveInfo target) { float score = super.getScore(target); if (target.isPinned()) { score += PINNED_TARGET_SCORE_BOOST; } return score; } @Override public View onCreateView(ViewGroup parent) { return mInflater.inflate( Loading Loading @@ -1121,7 +1176,7 @@ public class ChooserActivity extends ResolverActivity { v.setOnLongClickListener(new OnLongClickListener() { @Override public boolean onLongClick(View v) { showAppDetails( showTargetDetails( mChooserListAdapter.resolveInfoForPosition( holder.itemIndices[column], true)); return true; Loading
core/java/com/android/internal/app/ResolverActivity.java +68 −34 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.app.ActivityThread; import android.app.VoiceInteractor.PickOptionRequest; import android.app.VoiceInteractor.PickOptionRequest.Option; import android.app.VoiceInteractor.Prompt; import android.content.pm.ComponentInfo; import android.os.AsyncTask; import android.provider.Settings; import android.text.TextUtils; Loading Loading @@ -336,12 +337,12 @@ public class ResolverActivity extends Activity { /** * Perform any initialization needed for voice interaction. */ void onSetupVoiceInteraction() { public void onSetupVoiceInteraction() { // Do it right now. Subclasses may delay this and send it later. sendVoiceChoicesIfNeeded(); } void sendVoiceChoicesIfNeeded() { public void sendVoiceChoicesIfNeeded() { if (!isVoiceInteraction()) { // Clearly not needed. return; Loading Loading @@ -382,7 +383,7 @@ public class ResolverActivity extends Activity { return null; } int getLayoutResource() { public int getLayoutResource() { return R.layout.resolver_list; } Loading Loading @@ -591,7 +592,7 @@ public class ResolverActivity extends Activity { mAlwaysUseOption); } void startSelected(int which, boolean always, boolean filtered) { public void startSelected(int which, boolean always, boolean filtered) { if (isFinishing()) { return; } Loading Loading @@ -761,7 +762,7 @@ public class ResolverActivity extends Activity { return true; } void safelyStartActivity(TargetInfo cti) { public void safelyStartActivity(TargetInfo cti) { // If needed, show that intent is forwarded // from managed profile to owner or other way around. if (mProfileSwitchMessageId != -1) { Loading Loading @@ -791,26 +792,26 @@ public class ResolverActivity extends Activity { } } void onActivityStarted(TargetInfo cti) { public void onActivityStarted(TargetInfo cti) { // Do nothing } boolean shouldGetActivityMetadata() { public boolean shouldGetActivityMetadata() { return false; } boolean shouldAutoLaunchSingleChoice(TargetInfo target) { public boolean shouldAutoLaunchSingleChoice(TargetInfo target) { return true; } void showAppDetails(ResolveInfo ri) { public void showTargetDetails(ResolveInfo ri) { Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) .setData(Uri.fromParts("package", ri.activityInfo.packageName, null)) .addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); startActivity(in); } ResolveListAdapter createAdapter(Context context, List<Intent> payloadIntents, public ResolveListAdapter createAdapter(Context context, List<Intent> payloadIntents, Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid, boolean filterLastUsed) { return new ResolveListAdapter(context, payloadIntents, initialIntents, rList, Loading @@ -820,7 +821,7 @@ public class ResolverActivity extends Activity { /** * Returns true if the activity is finishing and creation should halt */ boolean configureContentView(List<Intent> payloadIntents, Intent[] initialIntents, public boolean configureContentView(List<Intent> payloadIntents, Intent[] initialIntents, List<ResolveInfo> rList, boolean alwaysUseOption) { // The last argument of createAdapter is whether to do special handling // of the last used choice to highlight it in the list. We need to always Loading Loading @@ -867,7 +868,7 @@ public class ResolverActivity extends Activity { return false; } void onPrepareAdapterView(AbsListView adapterView, ResolveListAdapter adapter, public void onPrepareAdapterView(AbsListView adapterView, ResolveListAdapter adapter, boolean alwaysUseOption) { final boolean useHeader = adapter.hasFilteredItem(); final ListView listView = adapterView instanceof ListView ? (ListView) adapterView : null; Loading Loading @@ -898,7 +899,7 @@ public class ResolverActivity extends Activity { && Objects.equals(lhs.activityInfo.packageName, rhs.activityInfo.packageName); } final class DisplayResolveInfo implements TargetInfo { public final class DisplayResolveInfo implements TargetInfo { private final ResolveInfo mResolveInfo; private final CharSequence mDisplayLabel; private Drawable mDisplayIcon; Loading @@ -906,8 +907,9 @@ public class ResolverActivity extends Activity { private final CharSequence mExtendedInfo; private final Intent mResolvedIntent; private final List<Intent> mSourceIntents = new ArrayList<>(); private boolean mPinned; DisplayResolveInfo(Intent originalIntent, ResolveInfo pri, CharSequence pLabel, public DisplayResolveInfo(Intent originalIntent, ResolveInfo pri, CharSequence pLabel, CharSequence pInfo, Intent pOrigIntent) { mSourceIntents.add(originalIntent); mResolveInfo = pri; Loading @@ -932,6 +934,7 @@ public class ResolverActivity extends Activity { mExtendedInfo = other.mExtendedInfo; mResolvedIntent = new Intent(other.mResolvedIntent); mResolvedIntent.fillIn(fillInIntent, flags); mPinned = other.mPinned; } public ResolveInfo getResolveInfo() { Loading Loading @@ -1026,6 +1029,15 @@ public class ResolverActivity extends Activity { activity.startActivityAsUser(mResolvedIntent, options, user); return false; } @Override public boolean isPinned() { return mPinned; } public void setPinned(boolean pinned) { mPinned = pinned; } } /** Loading @@ -1039,7 +1051,7 @@ public class ResolverActivity extends Activity { * * @return the resolved intent for this target */ public Intent getResolvedIntent(); Intent getResolvedIntent(); /** * Get the resolved component name that represents this target. Note that this may not Loading @@ -1048,7 +1060,7 @@ public class ResolverActivity extends Activity { * * @return the resolved ComponentName for this target */ public ComponentName getResolvedComponentName(); ComponentName getResolvedComponentName(); /** * Start the activity referenced by this target. Loading @@ -1057,7 +1069,7 @@ public class ResolverActivity extends Activity { * @param options ActivityOptions bundle * @return true if the start completed successfully */ public boolean start(Activity activity, Bundle options); boolean start(Activity activity, Bundle options); /** * Start the activity referenced by this target as if the ResolverActivity's caller Loading @@ -1068,7 +1080,7 @@ public class ResolverActivity extends Activity { * @param userId userId to start as or {@link UserHandle#USER_NULL} for activity's caller * @return true if the start completed successfully */ public boolean startAsCaller(Activity activity, Bundle options, int userId); boolean startAsCaller(Activity activity, Bundle options, int userId); /** * Start the activity referenced by this target as a given user. Loading @@ -1078,7 +1090,7 @@ public class ResolverActivity extends Activity { * @param user handle for the user to start the activity as * @return true if the start completed successfully */ public boolean startAsUser(Activity activity, Bundle options, UserHandle user); boolean startAsUser(Activity activity, Bundle options, UserHandle user); /** * Return the ResolveInfo about how and why this target matched the original query Loading @@ -1086,14 +1098,14 @@ public class ResolverActivity extends Activity { * * @return ResolveInfo representing this target's match */ public ResolveInfo getResolveInfo(); ResolveInfo getResolveInfo(); /** * Return the human-readable text label for this target. * * @return user-visible target label */ public CharSequence getDisplayLabel(); CharSequence getDisplayLabel(); /** * Return any extended info for this target. This may be used to disambiguate Loading @@ -1101,35 +1113,40 @@ public class ResolverActivity extends Activity { * * @return human-readable disambig string or null if none present */ public CharSequence getExtendedInfo(); CharSequence getExtendedInfo(); /** * @return The drawable that should be used to represent this target */ public Drawable getDisplayIcon(); Drawable getDisplayIcon(); /** * @return The (small) icon to badge the target with */ public Drawable getBadgeIcon(); Drawable getBadgeIcon(); /** * @return The content description for the badge icon */ public CharSequence getBadgeContentDescription(); CharSequence getBadgeContentDescription(); /** * Clone this target with the given fill-in information. */ public TargetInfo cloneFilledIn(Intent fillInIntent, int flags); TargetInfo cloneFilledIn(Intent fillInIntent, int flags); /** * @return the list of supported source intents deduped against this single target */ public List<Intent> getAllSourceIntents(); List<Intent> getAllSourceIntents(); /** * @return true if this target should be pinned to the front by the request of the user */ boolean isPinned(); } class ResolveListAdapter extends BaseAdapter { public class ResolveListAdapter extends BaseAdapter { private final List<Intent> mIntents; private final Intent[] mInitialIntents; private final List<ResolveInfo> mBaseResolveList; Loading Loading @@ -1376,9 +1393,12 @@ public class ResolverActivity extends Activity { } } if (!found) { into.add(new ResolvedComponentInfo(new ComponentName( newInfo.activityInfo.packageName, newInfo.activityInfo.name), intent, newInfo)); final ComponentName name = new ComponentName( newInfo.activityInfo.packageName, newInfo.activityInfo.name); final ResolvedComponentInfo rci = new ResolvedComponentInfo(name, intent, newInfo); rci.setPinned(isComponentPinned(name)); into.add(rci); } } } Loading Loading @@ -1454,6 +1474,7 @@ public class ResolverActivity extends Activity { final Intent replaceIntent = getReplacementIntent(add.activityInfo, intent); final DisplayResolveInfo dri = new DisplayResolveInfo(intent, add, roLabel, extraInfo, replaceIntent); dri.setPinned(rci.isPinned()); addResolveInfo(dri); if (replaceIntent == intent) { // Only add alternates if we didn't get a specific replacement from Loading Loading @@ -1537,11 +1558,11 @@ public class ResolverActivity extends Activity { return false; } protected int getDisplayResolveInfoCount() { public int getDisplayResolveInfoCount() { return mDisplayList.size(); } protected DisplayResolveInfo getDisplayResolveInfo(int index) { public DisplayResolveInfo getDisplayResolveInfo(int index) { // Used to query services. We only query services for primary targets, not alternates. return mDisplayList.get(index); } Loading Loading @@ -1571,6 +1592,10 @@ public class ResolverActivity extends Activity { return !TextUtils.isEmpty(info.getExtendedInfo()); } public boolean isComponentPinned(ComponentName name) { return false; } public final void bindView(int position, View view) { onBindView(view, getItem(position)); } Loading Loading @@ -1607,6 +1632,7 @@ public class ResolverActivity extends Activity { static final class ResolvedComponentInfo { public final ComponentName name; private boolean mPinned; private final List<Intent> mIntents = new ArrayList<>(); private final List<ResolveInfo> mResolveInfos = new ArrayList<>(); Loading Loading @@ -1649,6 +1675,14 @@ public class ResolverActivity extends Activity { } return -1; } public boolean isPinned() { return mPinned; } public void setPinned(boolean pinned) { mPinned = pinned; } } static class ViewHolder { Loading Loading @@ -1702,7 +1736,7 @@ public class ResolverActivity extends Activity { return false; } ResolveInfo ri = mAdapter.resolveInfoForPosition(position, true); showAppDetails(ri); showTargetDetails(ri); return true; } Loading
core/java/com/android/internal/app/ResolverComparator.java +22 −10 Original line number Diff line number Diff line Loading @@ -47,8 +47,8 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> { private static final boolean DEBUG = false; // Two weeks private static final long USAGE_STATS_PERIOD = 1000 * 60 * 60 * 24 * 14; // One week private static final long USAGE_STATS_PERIOD = 1000 * 60 * 60 * 24 * 7; private static final long RECENCY_TIME_PERIOD = 1000 * 60 * 60 * 12; Loading Loading @@ -171,6 +171,17 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> { } } final boolean lPinned = lhsp.isPinned(); final boolean rPinned = rhsp.isPinned(); if (lPinned && !rPinned) { return -1; } else if (!lPinned && rPinned) { return 1; } // Pinned items stay stable within a normal lexical sort and ignore scoring. if (!lPinned && !rPinned) { if (mStats != null) { final ScoredTarget lhsTarget = mScoredTargets.get(new ComponentName( lhs.activityInfo.packageName, lhs.activityInfo.name)); Loading @@ -182,6 +193,7 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> { return diff > 0 ? 1 : -1; } } } CharSequence sa = lhs.loadLabel(mPm); if (sa == null) sa = lhs.activityInfo.name; Loading
core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java 0 → 100644 +98 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.app; import android.app.AlertDialog.Builder; import android.app.Dialog; import android.app.DialogFragment; import android.content.ComponentName; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.net.Uri; import android.os.Bundle; import android.provider.Settings; import com.android.internal.R; /** * Shows a dialog with actions to take on a chooser target */ public class ResolverTargetActionsDialogFragment extends DialogFragment implements DialogInterface.OnClickListener { private static final String NAME_KEY = "componentName"; private static final String PINNED_KEY = "pinned"; private static final String TITLE_KEY = "title"; // Sync with R.array.resolver_target_actions_* resources private static final int TOGGLE_PIN_INDEX = 0; private static final int APP_INFO_INDEX = 1; public ResolverTargetActionsDialogFragment() { } public ResolverTargetActionsDialogFragment(CharSequence title, ComponentName name, boolean pinned) { Bundle args = new Bundle(); args.putCharSequence(TITLE_KEY, title); args.putParcelable(NAME_KEY, name); args.putBoolean(PINNED_KEY, pinned); setArguments(args); } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final Bundle args = getArguments(); final int itemRes = args.getBoolean(PINNED_KEY, false) ? R.array.resolver_target_actions_unpin : R.array.resolver_target_actions_pin; return new Builder(getContext()) .setCancelable(true) .setItems(itemRes, this) .setTitle(args.getCharSequence(TITLE_KEY)) .create(); } @Override public void onClick(DialogInterface dialog, int which) { final Bundle args = getArguments(); ComponentName name = args.getParcelable(NAME_KEY); switch (which) { case TOGGLE_PIN_INDEX: SharedPreferences sp = ChooserActivity.getPinnedSharedPrefs(getContext()); final String key = name.flattenToString(); boolean currentVal = sp.getBoolean(name.flattenToString(), false); if (currentVal) { sp.edit().remove(key).apply(); } else { sp.edit().putBoolean(key, true).apply(); } // Force the chooser to requery and resort things getActivity().recreate(); break; case APP_INFO_INDEX: Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) .setData(Uri.fromParts("package", name.getPackageName(), null)) .addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); startActivity(in); break; } dismiss(); } }
core/res/res/values/arrays.xml +11 −0 Original line number Diff line number Diff line Loading @@ -402,4 +402,15 @@ <item>@color/Red_700</item> </array> <!-- Used in ResolverTargetActionsDialogFragment --> <string-array name="resolver_target_actions_pin"> <item>@string/pin_target</item> <item>@string/app_info</item> </string-array> <string-array name="resolver_target_actions_unpin"> <item>@string/unpin_target</item> <item>@string/app_info</item> </string-array> </resources>