summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java53
-rw-r--r--core/java/com/android/internal/app/ChooserListAdapter.java27
-rw-r--r--core/java/com/android/internal/app/ChooserStackedAppDialogFragment.java86
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java2
-rw-r--r--core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java95
-rw-r--r--core/java/com/android/internal/app/chooser/DisplayResolveInfo.java11
-rw-r--r--core/java/com/android/internal/app/chooser/MultiDisplayResolveInfo.java55
-rw-r--r--core/res/res/values/strings.xml6
-rw-r--r--core/res/res/values/symbols.xml2
9 files changed, 301 insertions, 36 deletions
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 8856f99272b6..f361784e81d7 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -111,6 +111,7 @@ import com.android.internal.app.ResolverListAdapter.ActivityInfoPresentationGett
import com.android.internal.app.ResolverListAdapter.ViewHolder;
import com.android.internal.app.chooser.ChooserTargetInfo;
import com.android.internal.app.chooser.DisplayResolveInfo;
+import com.android.internal.app.chooser.MultiDisplayResolveInfo;
import com.android.internal.app.chooser.NotSelectableTargetInfo;
import com.android.internal.app.chooser.SelectableTargetInfo;
import com.android.internal.app.chooser.SelectableTargetInfo.SelectableTargetInfoCommunicator;
@@ -1352,17 +1353,31 @@ public class ChooserActivity extends ResolverActivity implements
return getIntent().getBooleanExtra(Intent.EXTRA_AUTO_LAUNCH_SINGLE_CHOICE, true);
}
- @Override
- public void showTargetDetails(ResolveInfo ri) {
- if (ri == null) {
+ void showTargetDetails(TargetInfo ti) {
+ if (ti == null) {
return;
}
-
- ComponentName name = ri.activityInfo.getComponentName();
+ ComponentName name = ti.getResolveInfo().activityInfo.getComponentName();
boolean pinned = mPinnedSharedPrefs.getBoolean(name.flattenToString(), false);
- ResolverTargetActionsDialogFragment f =
- new ResolverTargetActionsDialogFragment(ri.loadLabel(getPackageManager()),
- name, pinned);
+
+ ResolverTargetActionsDialogFragment f;
+
+ // For multiple targets, include info on all targets
+ if (ti instanceof MultiDisplayResolveInfo) {
+ MultiDisplayResolveInfo mti = (MultiDisplayResolveInfo) ti;
+ List<CharSequence> labels = new ArrayList<>();
+
+ for (TargetInfo innerInfo : mti.getTargets()) {
+ labels.add(innerInfo.getResolveInfo().loadLabel(getPackageManager()));
+ }
+ f = new ResolverTargetActionsDialogFragment(
+ mti.getResolveInfo().loadLabel(getPackageManager()), name, mti.getTargets(),
+ labels);
+ } else {
+ f = new ResolverTargetActionsDialogFragment(
+ ti.getResolveInfo().loadLabel(getPackageManager()), name, pinned);
+ }
+
f.show(getFragmentManager(), TARGET_DETAILS_FRAGMENT_TAG);
}
@@ -1416,8 +1431,26 @@ public class ChooserActivity extends ResolverActivity implements
}
final long selectionCost = System.currentTimeMillis() - mChooserShownTime;
+
+ // Stacked apps get a disambiguation first
+ if (targetInfo instanceof MultiDisplayResolveInfo) {
+ MultiDisplayResolveInfo mti = (MultiDisplayResolveInfo) targetInfo;
+ CharSequence[] labels = new CharSequence[mti.getTargets().size()];
+ int i = 0;
+ for (TargetInfo ti : mti.getTargets()) {
+ labels[i++] = ti.getResolveInfo().loadLabel(getPackageManager());
+ }
+ ChooserStackedAppDialogFragment f = new ChooserStackedAppDialogFragment(
+ targetInfo.getDisplayLabel(),
+ ((MultiDisplayResolveInfo) targetInfo).getTargets(), labels);
+
+ f.show(getFragmentManager(), TARGET_DETAILS_FRAGMENT_TAG);
+ return;
+ }
+
super.startSelected(which, always, filtered);
+
if (currentListAdapter.getCount() > 0) {
// Log the index of which type of target the user picked.
// Lower values mean the ranking was better.
@@ -2363,7 +2396,7 @@ public class ChooserActivity extends ResolverActivity implements
itemView.setOnLongClickListener(v -> {
showTargetDetails(
mChooserMultiProfilePagerAdapter.getActiveListAdapter()
- .resolveInfoForPosition(mListPosition, /* filtered */ true));
+ .targetInfoForPosition(mListPosition, /* filtered */ true));
return true;
});
}
@@ -2615,7 +2648,7 @@ public class ChooserActivity extends ResolverActivity implements
@Override
public boolean onLongClick(View v) {
showTargetDetails(
- mChooserListAdapter.resolveInfoForPosition(
+ mChooserListAdapter.targetInfoForPosition(
holder.getItemIndex(column), true));
return true;
}
diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java
index 4eccf21677d5..a8a676d03971 100644
--- a/core/java/com/android/internal/app/ChooserListAdapter.java
+++ b/core/java/com/android/internal/app/ChooserListAdapter.java
@@ -38,17 +38,22 @@ import com.android.internal.R;
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
import com.android.internal.app.chooser.ChooserTargetInfo;
import com.android.internal.app.chooser.DisplayResolveInfo;
+import com.android.internal.app.chooser.MultiDisplayResolveInfo;
import com.android.internal.app.chooser.SelectableTargetInfo;
import com.android.internal.app.chooser.TargetInfo;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
public class ChooserListAdapter extends ResolverListAdapter {
private static final String TAG = "ChooserListAdapter";
private static final boolean DEBUG = false;
+ private boolean mEnableStackedApps = true;
+
public static final int NO_POSITION = -1;
public static final int TARGET_BAD = -1;
public static final int TARGET_CALLER = 0;
@@ -218,7 +223,25 @@ public class ChooserListAdapter extends ResolverListAdapter {
void updateAlphabeticalList() {
mSortedList.clear();
- mSortedList.addAll(mDisplayList);
+ if (mEnableStackedApps) {
+ // Consolidate multiple targets from same app.
+ Map<String, DisplayResolveInfo> consolidated = new HashMap<>();
+ for (DisplayResolveInfo info : mDisplayList) {
+ String packageName = info.getResolvedComponentName().getPackageName();
+ if (consolidated.get(packageName) != null) {
+ // create consolidated target
+ MultiDisplayResolveInfo multiDisplayResolveInfo =
+ new MultiDisplayResolveInfo(packageName, info);
+ multiDisplayResolveInfo.addTarget(consolidated.get(packageName));
+ consolidated.put(packageName, multiDisplayResolveInfo);
+ } else {
+ consolidated.put(packageName, info);
+ }
+ }
+ mSortedList.addAll(consolidated.values());
+ } else {
+ mSortedList.addAll(mDisplayList);
+ }
Collections.sort(mSortedList, new ChooserActivity.AzInfoComparator(mContext));
}
@@ -270,7 +293,7 @@ public class ChooserListAdapter extends ResolverListAdapter {
}
int getAlphaTargetCount() {
- int standardCount = super.getCount();
+ int standardCount = mSortedList.size();
return standardCount > mChooserListCommunicator.getMaxRankedTargets() ? standardCount : 0;
}
diff --git a/core/java/com/android/internal/app/ChooserStackedAppDialogFragment.java b/core/java/com/android/internal/app/ChooserStackedAppDialogFragment.java
new file mode 100644
index 000000000000..ff6582d10535
--- /dev/null
+++ b/core/java/com/android/internal/app/ChooserStackedAppDialogFragment.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2019 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.DialogInterface;
+import android.content.res.Configuration;
+import android.os.Bundle;
+
+import com.android.internal.app.chooser.DisplayResolveInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Shows individual actions for a "stacked" app target - such as an app with multiple posting
+ * streams represented in the Sharesheet.
+ */
+public class ChooserStackedAppDialogFragment extends DialogFragment
+ implements DialogInterface.OnClickListener {
+ private static final String TITLE_KEY = "title";
+ private static final String PINNED_KEY = "pinned";
+
+ private List<DisplayResolveInfo> mTargetInfos = new ArrayList<>();
+ private CharSequence[] mLabels;
+
+ public ChooserStackedAppDialogFragment() {
+ }
+
+ public ChooserStackedAppDialogFragment(CharSequence title) {
+ Bundle args = new Bundle();
+ args.putCharSequence(TITLE_KEY, title);
+ setArguments(args);
+ }
+
+ public ChooserStackedAppDialogFragment(CharSequence title,
+ List<DisplayResolveInfo> targets, CharSequence[] labels) {
+ Bundle args = new Bundle();
+ args.putCharSequence(TITLE_KEY, title);
+ mTargetInfos = targets;
+ mLabels = labels;
+ setArguments(args);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Bundle args = getArguments();
+ return new Builder(getContext())
+ .setCancelable(true)
+ .setItems(mLabels, this)
+ .setTitle(args.getCharSequence(TITLE_KEY))
+ .create();
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ final Bundle args = getArguments();
+ mTargetInfos.get(which).start(getActivity(), null);
+ dismiss();
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ // Dismiss on config changed (eg: rotation)
+ // TODO: Maintain state on config change
+ super.onConfigurationChanged(newConfig);
+ dismiss();
+ }
+}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index cb7f2e424e7b..8dc3a072b47e 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1160,7 +1160,7 @@ public class ResolverActivity extends Activity implements
return !target.isSuspended();
}
- public void showTargetDetails(ResolveInfo ri) {
+ 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);
diff --git a/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java b/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java
index df91c4a1f88d..bdbe2109cf05 100644
--- a/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java
+++ b/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java
@@ -24,14 +24,19 @@ import android.content.ComponentName;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import com.android.internal.R;
+import com.android.internal.app.chooser.DisplayResolveInfo;
+
+import java.util.ArrayList;
+import java.util.List;
/**
- * Shows a dialog with actions to take on a chooser target
+ * Shows a dialog with actions to take on a chooser target.
*/
public class ResolverTargetActionsDialogFragment extends DialogFragment
implements DialogInterface.OnClickListener {
@@ -43,6 +48,10 @@ public class ResolverTargetActionsDialogFragment extends DialogFragment
private static final int TOGGLE_PIN_INDEX = 0;
private static final int APP_INFO_INDEX = 1;
+ private List<DisplayResolveInfo> mTargetInfos = new ArrayList<>();
+ private List<CharSequence> mLabels = new ArrayList<>();
+ private boolean[] mPinned;
+
public ResolverTargetActionsDialogFragment() {
}
@@ -55,15 +64,43 @@ public class ResolverTargetActionsDialogFragment extends DialogFragment
setArguments(args);
}
+ public ResolverTargetActionsDialogFragment(CharSequence title, ComponentName name,
+ List<DisplayResolveInfo> targets, List<CharSequence> labels) {
+ Bundle args = new Bundle();
+ args.putCharSequence(TITLE_KEY, title);
+ args.putParcelable(NAME_KEY, name);
+ mTargetInfos = targets;
+ mLabels = labels;
+ 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;
+ String[] defaultActions = getResources().getStringArray(itemRes);
+ CharSequence[] items;
+
+ if (mTargetInfos == null || mTargetInfos.size() < 2) {
+ items = defaultActions;
+ } else {
+ // Pin item for each sub-item
+ items = new CharSequence[mTargetInfos.size() + 1];
+ for (int i = 0; i < mTargetInfos.size(); i++) {
+ items[i] = mTargetInfos.get(i).isPinned()
+ ? getResources().getString(R.string.unpin_specific_target, mLabels.get(i))
+ : getResources().getString(R.string.pin_specific_target, mLabels.get(i));
+ }
+ // "App info"
+ items[mTargetInfos.size()] = defaultActions[1];
+ }
+
+
return new Builder(getContext())
.setCancelable(true)
- .setItems(itemRes, this)
+ .setItems(items, this)
.setTitle(args.getCharSequence(TITLE_KEY))
.create();
}
@@ -72,27 +109,41 @@ public class ResolverTargetActionsDialogFragment extends DialogFragment
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;
+ if (which == 0 || (mTargetInfos.size() > 0 && which < mTargetInfos.size())) {
+ if (mTargetInfos == null || mTargetInfos.size() == 0) {
+ pinComponent(name);
+ } else {
+ pinComponent(mTargetInfos.get(which).getResolvedComponentName());
+ }
+ // Force the chooser to requery and resort things
+ getActivity().recreate();
+ } else {
+ // Last item in dialog is App Info
+ 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);
+ }
+ dismiss();
+ }
+
+ private void pinComponent(ComponentName name) {
+ 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();
}
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ // Dismiss on config changed (eg: rotation)
+ // TODO: Maintain state on config change
+ super.onConfigurationChanged(newConfig);
dismiss();
}
+
}
diff --git a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
index f92637c1bf01..86a9af3db196 100644
--- a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
+++ b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
@@ -91,6 +91,16 @@ public class DisplayResolveInfo implements TargetInfo {
mResolveInfoPresentationGetter = resolveInfoPresentationGetter;
}
+ DisplayResolveInfo(DisplayResolveInfo other) {
+ mSourceIntents.addAll(other.getAllSourceIntents());
+ mResolveInfo = other.mResolveInfo;
+ mDisplayLabel = other.mDisplayLabel;
+ mDisplayIcon = other.mDisplayIcon;
+ mExtendedInfo = other.mExtendedInfo;
+ mResolvedIntent = other.mResolvedIntent;
+ mResolveInfoPresentationGetter = other.mResolveInfoPresentationGetter;
+ }
+
public ResolveInfo getResolveInfo() {
return mResolveInfo;
}
@@ -189,4 +199,5 @@ public class DisplayResolveInfo implements TargetInfo {
public void setPinned(boolean pinned) {
mPinned = pinned;
}
+
}
diff --git a/core/java/com/android/internal/app/chooser/MultiDisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/MultiDisplayResolveInfo.java
new file mode 100644
index 000000000000..4c52411d6376
--- /dev/null
+++ b/core/java/com/android/internal/app/chooser/MultiDisplayResolveInfo.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2019 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.chooser;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a "stack" of chooser targets for various activities within the same component.
+ */
+public class MultiDisplayResolveInfo extends DisplayResolveInfo {
+
+ List<DisplayResolveInfo> mTargetInfos = new ArrayList<>();
+ String mPackageName;
+ // We'll use this DRI for basic presentation info - eg icon, name.
+ final DisplayResolveInfo mBaseInfo;
+
+ /**
+ * @param firstInfo A representative DRI to use for the main icon, title, etc for this Info.
+ */
+ public MultiDisplayResolveInfo(String packageName, DisplayResolveInfo firstInfo) {
+ super(firstInfo);
+ mBaseInfo = firstInfo;
+ mTargetInfos.add(firstInfo);
+ }
+
+ /**
+ * Add another DisplayResolveInfo to the list included for this target.
+ */
+ public void addTarget(DisplayResolveInfo target) {
+ mTargetInfos.add(target);
+ }
+
+ /**
+ * List of all DisplayResolveInfos included in this target.
+ */
+ public List<DisplayResolveInfo> getTargets() {
+ return mTargetInfos;
+ }
+
+}
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index de1b5baec4b2..ab10738327b3 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4946,8 +4946,12 @@
<!-- Resolver target actions strings -->
<!-- Pin this app to the top of the Sharesheet app list. [CHAR LIMIT=60]-->
<string name="pin_target">Pin</string>
+ <!-- Pin this app to the top of the Sharesheet app list. [CHAR LIMIT=60]-->
+ <string name="pin_specific_target">Pin <xliff:g id="label" example="Tweet">%1$s</xliff:g></string>
+ <!-- Un-pin this app in the Sharesheet, so that it is sorted normally. [CHAR LIMIT=60]-->
+ <string name="unpin_target">Unpin </string>
<!-- Un-pin this app in the Sharesheet, so that it is sorted normally. [CHAR LIMIT=60]-->
- <string name="unpin_target">Unpin</string>
+ <string name="unpin_specific_target">Unpin <xliff:g id="label" example="Tweet">%1$s</xliff:g></string>
<!-- View application info for a target. -->
<string name="app_info">App info</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 011dc3961c4d..1631e0f00f07 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2958,6 +2958,8 @@
<!-- Resolver target actions -->
<java-symbol type="array" name="resolver_target_actions_pin" />
<java-symbol type="array" name="resolver_target_actions_unpin" />
+ <java-symbol type="string" name="pin_specific_target" />
+ <java-symbol type="string" name="unpin_specific_target" />
<java-symbol type="array" name="non_removable_euicc_slots" />