Merge "Add icons to Sharesheet pin and group selection UI" into rvc-dev

This commit is contained in:
Mike Digman
2020-05-28 16:06:14 +00:00
committed by Android (Google) Code Review
7 changed files with 284 additions and 177 deletions

View File

@@ -1614,7 +1614,7 @@ public class ChooserActivity extends ResolverActivity implements
targetList = Collections.singletonList(ti);
}
ResolverTargetActionsDialogFragment f = new ResolverTargetActionsDialogFragment(
ChooserTargetActionsDialogFragment f = new ChooserTargetActionsDialogFragment(
targetList, mChooserMultiProfilePagerAdapter.getCurrentUserHandle());
f.show(getFragmentManager(), TARGET_DETAILS_FRAGMENT_TAG);
@@ -1674,15 +1674,9 @@ public class ChooserActivity extends ResolverActivity implements
if (targetInfo instanceof MultiDisplayResolveInfo) {
MultiDisplayResolveInfo mti = (MultiDisplayResolveInfo) targetInfo;
if (!mti.hasSelected()) {
// Stacked apps get a disambiguation first
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), labels, which);
mti, which,
mChooserMultiProfilePagerAdapter.getCurrentUserHandle());
f.show(getFragmentManager(), TARGET_DETAILS_FRAGMENT_TAG);
return;
@@ -2973,16 +2967,16 @@ public class ChooserActivity extends ResolverActivity implements
itemView.setOnClickListener(v -> startSelected(mListPosition,
false/* always */, true/* filterd */));
TargetInfo ti = mChooserMultiProfilePagerAdapter.getActiveListAdapter()
.targetInfoForPosition(mListPosition, /* filtered */ true);
itemView.setOnLongClickListener(v -> {
final TargetInfo ti = mChooserMultiProfilePagerAdapter.getActiveListAdapter()
.targetInfoForPosition(mListPosition, /* filtered */ true);
// This should always be the case for ItemViewHolder, check for sanity
if (ti instanceof DisplayResolveInfo) {
itemView.setOnLongClickListener(v -> {
// This should always be the case for ItemViewHolder, check for sanity
if (ti instanceof DisplayResolveInfo) {
showTargetDetails((DisplayResolveInfo) ti);
return true;
});
}
}
return true;
});
}
}
}
@@ -3310,16 +3304,15 @@ public class ChooserActivity extends ResolverActivity implements
// Direct Share targets should not show any menu
if (!isDirectShare) {
final TargetInfo ti = mChooserListAdapter.targetInfoForPosition(
holder.getItemIndex(column), true);
// This should always be the case for non-DS targets, check for sanity
if (ti instanceof DisplayResolveInfo) {
v.setOnLongClickListener(v1 -> {
v.setOnLongClickListener(v1 -> {
final TargetInfo ti = mChooserListAdapter.targetInfoForPosition(
holder.getItemIndex(column), true);
// This should always be the case for non-DS targets, check for sanity
if (ti instanceof DisplayResolveInfo) {
showTargetDetails((DisplayResolveInfo) ti);
return true;
});
}
}
return true;
});
}
holder.addView(i, v);

View File

@@ -17,64 +17,50 @@
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 android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import com.android.internal.app.chooser.DisplayResolveInfo;
import com.android.internal.app.chooser.MultiDisplayResolveInfo;
/**
* 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
public class ChooserStackedAppDialogFragment extends ChooserTargetActionsDialogFragment
implements DialogInterface.OnClickListener {
private static final String TITLE_KEY = "title";
private static final String PINNED_KEY = "pinned";
private MultiDisplayResolveInfo mTargetInfos;
private CharSequence[] mLabels;
private MultiDisplayResolveInfo mMultiDisplayResolveInfo;
private int mParentWhich;
public ChooserStackedAppDialogFragment() {
}
public ChooserStackedAppDialogFragment(CharSequence title,
MultiDisplayResolveInfo targets, CharSequence[] labels, int parentWhich) {
Bundle args = new Bundle();
args.putCharSequence(TITLE_KEY, title);
mTargetInfos = targets;
mLabels = labels;
public ChooserStackedAppDialogFragment(MultiDisplayResolveInfo targets,
int parentWhich, UserHandle userHandle) {
super(targets.getTargets(), userHandle);
mMultiDisplayResolveInfo = targets;
mParentWhich = parentWhich;
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();
protected CharSequence getItemLabel(DisplayResolveInfo dri) {
final PackageManager pm = getContext().getPackageManager();
return dri.getResolveInfo().loadLabel(pm);
}
@Override
protected Drawable getItemIcon(DisplayResolveInfo dri) {
// Show no icon for the group disambig dialog, null hides the imageview
return null;
}
@Override
public void onClick(DialogInterface dialog, int which) {
final Bundle args = getArguments();
mTargetInfos.setSelected(which);
mMultiDisplayResolveInfo.setSelected(which);
((ChooserActivity) getActivity()).startSelected(mParentWhich, false, true);
dismiss();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
// Dismiss on config changed (eg: rotation)
// TODO: Maintain state on config change
super.onConfigurationChanged(newConfig);
dismiss();
}
}

View File

@@ -0,0 +1,173 @@
/*
* 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 static android.content.Context.ACTIVITY_SERVICE;
import static com.android.internal.app.ResolverListAdapter.ResolveInfoPresentationGetter;
import static java.util.stream.Collectors.toList;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AlertDialog.Builder;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.ComponentName;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.UserHandle;
import android.util.Pair;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
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.
*/
public class ChooserTargetActionsDialogFragment extends DialogFragment
implements DialogInterface.OnClickListener {
protected List<DisplayResolveInfo> mTargetInfos = new ArrayList<>();
protected UserHandle mUserHandle;
public ChooserTargetActionsDialogFragment() {
}
public ChooserTargetActionsDialogFragment(List<DisplayResolveInfo> targets,
UserHandle userHandle) {
mUserHandle = userHandle;
mTargetInfos = targets;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Fetch UI details from target info
List<Pair<CharSequence, Drawable>> items = mTargetInfos.stream().map(dri -> {
return new Pair<>(getItemLabel(dri), getItemIcon(dri));
}).collect(toList());
final ResolveInfoPresentationGetter pg = getProvidingAppPresentationGetter();
return new Builder(getContext())
.setTitle(pg.getLabel())
.setIcon(pg.getIcon(mUserHandle))
.setCancelable(true)
.setAdapter(getAdapterForContent(items), this)
.create();
}
protected ArrayAdapter<Pair<CharSequence, Drawable>> getAdapterForContent(
List<Pair<CharSequence, Drawable>> items) {
return new ArrayAdapter<Pair<CharSequence, Drawable>>(getContext(),
R.layout.chooser_dialog_item, R.id.text, items) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = super.getView(position, convertView, parent); // super recycles views
TextView label = v.findViewById(R.id.text);
ImageView icon = v.findViewById(R.id.icon);
Pair<CharSequence, Drawable> pair = getItem(position);
label.setText(pair.first);
// Hide icon view if one isn't available
if (pair.second == null) {
icon.setVisibility(View.GONE);
} else {
icon.setImageDrawable(pair.second);
icon.setVisibility(View.VISIBLE);
}
return v;
}
};
}
@Override
public void onClick(DialogInterface dialog, int which) {
pinComponent(mTargetInfos.get(which).getResolvedComponentName());
((ChooserActivity) getActivity()).handlePackagesChanged();
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();
}
}
private Drawable getPinIcon(boolean isPinned) {
return isPinned
? getContext().getDrawable(R.drawable.ic_close)
: getContext().getDrawable(R.drawable.ic_chooser_pin_dialog);
}
private CharSequence getPinLabel(boolean isPinned, CharSequence targetLabel) {
return isPinned
? getResources().getString(R.string.unpin_specific_target, targetLabel)
: getResources().getString(R.string.pin_specific_target, targetLabel);
}
@NonNull
protected CharSequence getItemLabel(DisplayResolveInfo dri) {
final PackageManager pm = getContext().getPackageManager();
return getPinLabel(dri.isPinned(), dri.getResolveInfo().loadLabel(pm));
}
@Nullable
protected Drawable getItemIcon(DisplayResolveInfo dri) {
return getPinIcon(dri.isPinned());
}
private ResolveInfoPresentationGetter getProvidingAppPresentationGetter() {
final ActivityManager am = (ActivityManager) getContext()
.getSystemService(ACTIVITY_SERVICE);
final int iconDpi = am.getLauncherLargeIconDensity();
// Use the matching application icon and label for the title, any TargetInfo will do
return new ResolveInfoPresentationGetter(getContext(), iconDpi,
mTargetInfos.get(0).getResolveInfo());
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
// Dismiss on config changed (eg: rotation)
// TODO: Maintain state on config change
super.onConfigurationChanged(newConfig);
dismiss();
}
}

View File

@@ -1,117 +0,0 @@
/*
* 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 static android.content.Context.ACTIVITY_SERVICE;
import static com.android.internal.app.ResolverListAdapter.ResolveInfoPresentationGetter;
import android.app.ActivityManager;
import android.app.AlertDialog.Builder;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.ComponentName;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.UserHandle;
import com.android.internal.R;
import com.android.internal.app.chooser.DisplayResolveInfo;
import com.android.internal.app.chooser.TargetInfo;
import java.util.ArrayList;
import java.util.List;
/**
* Shows a dialog with actions to take on a chooser target.
*/
public class ResolverTargetActionsDialogFragment extends DialogFragment
implements DialogInterface.OnClickListener {
private List<DisplayResolveInfo> mTargetInfos = new ArrayList<>();
private UserHandle mUserHandle;
public ResolverTargetActionsDialogFragment() {
}
public ResolverTargetActionsDialogFragment(List<DisplayResolveInfo> targets,
UserHandle userHandle) {
mUserHandle = userHandle;
mTargetInfos = targets;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Bundle args = getArguments();
final PackageManager pm = getContext().getPackageManager();
// Pin item for each sub-item
CharSequence[] items = new CharSequence[mTargetInfos.size()];
for (int i = 0; i < mTargetInfos.size(); i++) {
final TargetInfo ti = mTargetInfos.get(i);
final CharSequence label = ti.getResolveInfo().loadLabel(pm);
items[i] = ti.isPinned()
? getResources().getString(R.string.unpin_specific_target, label)
: getResources().getString(R.string.pin_specific_target, label);
}
// Use the matching application icon and label for the title, any TargetInfo will do
final ActivityManager am = (ActivityManager) getContext()
.getSystemService(ACTIVITY_SERVICE);
final int iconDpi = am.getLauncherLargeIconDensity();
final ResolveInfoPresentationGetter pg = new ResolveInfoPresentationGetter(getContext(),
iconDpi, mTargetInfos.get(0).getResolveInfo());
return new Builder(getContext())
.setTitle(pg.getLabel())
.setIcon(pg.getIcon(mUserHandle))
.setCancelable(true)
.setItems(items, this)
.create();
}
@Override
public void onClick(DialogInterface dialog, int which) {
pinComponent(mTargetInfos.get(which).getResolvedComponentName());
((ChooserActivity) getActivity()).handlePackagesChanged();
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();
}
}

View File

@@ -0,0 +1,25 @@
<!--
~ Copyright (C) 2020 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M14,4v5c0,1.12 0.37,2.16 1,3H9c0.65,-0.86 1,-1.9 1,-3V4H14M17,2H7C6.45,2 6,2.45 6,3c0,0.55 0.45,1 1,1c0,0 0,0 0,0l1,0v5c0,1.66 -1.34,3 -3,3v2h5.97v7l1,1l1,-1v-7H19v-2c0,0 0,0 0,0c-1.66,0 -3,-1.34 -3,-3V4l1,0c0,0 0,0 0,0c0.55,0 1,-0.45 1,-1C18,2.45 17.55,2 17,2L17,2z"
android:fillColor="#FF000000"/>
</vector>

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2020 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:gravity="start|center_vertical"
android:paddingStart="?attr/dialogPreferredPadding"
android:paddingEnd="?attr/dialogPreferredPadding"
android:minHeight="48dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Icon and text aligns with aligns with alert_dialog_title_material -->
<ImageView android:id="@+id/icon"
android:tint="?android:attr/textColorAlertDialogListItem"
android:padding="4dp"
android:layout_marginEnd="8dp"
android:layout_width="32dp"
android:layout_height="32dp"/>
<!-- Using text style from select_dialog_item_material -->
<TextView android:id="@+id/text"
android:textAppearance="?android:attr/textAppearanceListItemSmall"
android:textColor="?android:attr/textColorAlertDialogListItem"
android:lines="1"
android:ellipsize="end"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>

View File

@@ -2747,6 +2747,7 @@
<java-symbol type="drawable" name="ic_chooser_group_arrow"/>
<java-symbol type="drawable" name="chooser_group_background"/>
<java-symbol type="drawable" name="ic_chooser_pin"/>
<java-symbol type="drawable" name="ic_chooser_pin_dialog"/>
<java-symbol type="drawable" name="chooser_pinned_background"/>
<java-symbol type="integer" name="config_maxShortcutTargetsPerApp" />
<java-symbol type="layout" name="resolve_grid_item" />
@@ -3833,10 +3834,12 @@
<java-symbol type="string" name="config_factoryResetPackage" />
<java-symbol type="array" name="config_highRefreshRateBlacklist" />
<java-symbol type="layout" name="chooser_dialog_item" />
<java-symbol type="id" name="chooser_copy_button" />
<java-symbol type="layout" name="chooser_action_button" />
<java-symbol type="dimen" name="chooser_action_button_icon_size" />
<java-symbol type="string" name="config_defaultNearbySharingComponent" />
<java-symbol type="drawable" name="ic_close" />
<java-symbol type="bool" name="config_automotiveHideNavBarForKeyboard" />