• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 
18 package com.android.internal.app;
19 
20 import static android.content.Context.ACTIVITY_SERVICE;
21 
22 import static com.android.internal.app.ResolverListAdapter.ResolveInfoPresentationGetter;
23 
24 import static java.util.stream.Collectors.toList;
25 
26 import android.annotation.NonNull;
27 import android.annotation.Nullable;
28 import android.app.ActivityManager;
29 import android.app.Dialog;
30 import android.app.DialogFragment;
31 import android.content.ComponentName;
32 import android.content.DialogInterface;
33 import android.content.SharedPreferences;
34 import android.content.pm.PackageManager;
35 import android.graphics.Color;
36 import android.graphics.drawable.ColorDrawable;
37 import android.graphics.drawable.Drawable;
38 import android.os.Bundle;
39 import android.os.UserHandle;
40 import android.util.Pair;
41 import android.view.LayoutInflater;
42 import android.view.View;
43 import android.view.ViewGroup;
44 import android.widget.ImageView;
45 import android.widget.TextView;
46 
47 import com.android.internal.R;
48 import com.android.internal.app.chooser.DisplayResolveInfo;
49 import com.android.internal.widget.RecyclerView;
50 
51 import java.util.ArrayList;
52 import java.util.List;
53 import java.util.Optional;
54 
55 /**
56  * Shows a dialog with actions to take on a chooser target.
57  */
58 public class ChooserTargetActionsDialogFragment extends DialogFragment
59         implements DialogInterface.OnClickListener {
60 
61     protected ArrayList<DisplayResolveInfo> mTargetInfos = new ArrayList<>();
62     protected UserHandle mUserHandle;
63 
64     public static final String USER_HANDLE_KEY = "user_handle";
65     public static final String TARGET_INFOS_KEY = "target_infos";
66 
ChooserTargetActionsDialogFragment()67     public ChooserTargetActionsDialogFragment() {}
68 
69     @Override
onCreate(Bundle savedInstanceState)70     public void onCreate(Bundle savedInstanceState) {
71         super.onCreate(savedInstanceState);
72         if (savedInstanceState != null) {
73             setStateFromBundle(savedInstanceState);
74         } else {
75             setStateFromBundle(getArguments());
76         }
77     }
78 
setStateFromBundle(Bundle b)79     void setStateFromBundle(Bundle b) {
80         mTargetInfos = (ArrayList<DisplayResolveInfo>) b.get(TARGET_INFOS_KEY);
81         mUserHandle = (UserHandle) b.get(USER_HANDLE_KEY);
82     }
83 
84     @Override
onSaveInstanceState(Bundle outState)85     public void onSaveInstanceState(Bundle outState) {
86         super.onSaveInstanceState(outState);
87 
88         outState.putParcelable(ChooserTargetActionsDialogFragment.USER_HANDLE_KEY,
89                 mUserHandle);
90         outState.putParcelableArrayList(ChooserTargetActionsDialogFragment.TARGET_INFOS_KEY,
91                 mTargetInfos);
92     }
93 
94     /**
95      * Recreate the layout from scratch to match new Sharesheet redlines
96      */
97     @Override
onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState)98     public View onCreateView(LayoutInflater inflater,
99             @Nullable ViewGroup container,
100             Bundle savedInstanceState) {
101         if (savedInstanceState != null) {
102             setStateFromBundle(savedInstanceState);
103         } else {
104             setStateFromBundle(getArguments());
105         }
106         // Make the background transparent to show dialog rounding
107         Optional.of(getDialog()).map(Dialog::getWindow)
108                 .ifPresent(window -> {
109                     window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
110                 });
111 
112         // Fetch UI details from target info
113         List<Pair<Drawable, CharSequence>> items = mTargetInfos.stream().map(dri -> {
114             return new Pair<>(getItemIcon(dri), getItemLabel(dri));
115         }).collect(toList());
116 
117         View v = inflater.inflate(R.layout.chooser_dialog, container, false);
118 
119         TextView title = v.findViewById(R.id.title);
120         ImageView icon = v.findViewById(R.id.icon);
121         RecyclerView rv = v.findViewById(R.id.listContainer);
122 
123         final ResolveInfoPresentationGetter pg = getProvidingAppPresentationGetter();
124         title.setText(pg.getLabel());
125         icon.setImageDrawable(pg.getIcon(mUserHandle));
126         rv.setAdapter(new VHAdapter(items));
127 
128         return v;
129     }
130 
131     class VHAdapter extends RecyclerView.Adapter<VH> {
132 
133         List<Pair<Drawable, CharSequence>> mItems;
134 
VHAdapter(List<Pair<Drawable, CharSequence>> items)135         VHAdapter(List<Pair<Drawable, CharSequence>> items) {
136             mItems = items;
137         }
138 
139         @NonNull
140         @Override
onCreateViewHolder(@onNull ViewGroup parent, int viewType)141         public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
142             return new VH(LayoutInflater.from(parent.getContext()).inflate(
143                     R.layout.chooser_dialog_item, parent, false));
144         }
145 
146         @Override
onBindViewHolder(@onNull VH holder, int position)147         public void onBindViewHolder(@NonNull VH holder, int position) {
148             holder.bind(mItems.get(position), position);
149         }
150 
151         @Override
getItemCount()152         public int getItemCount() {
153             return mItems.size();
154         }
155     }
156 
157     class VH extends RecyclerView.ViewHolder {
158         TextView mLabel;
159         ImageView mIcon;
160 
VH(@onNull View itemView)161         VH(@NonNull View itemView) {
162             super(itemView);
163             mLabel = itemView.findViewById(R.id.text);
164             mIcon = itemView.findViewById(R.id.icon);
165         }
166 
bind(Pair<Drawable, CharSequence> item, int position)167         public void bind(Pair<Drawable, CharSequence> item, int position) {
168             mLabel.setText(item.second);
169 
170             if (item.first == null) {
171                 mIcon.setVisibility(View.GONE);
172             } else {
173                 mIcon.setVisibility(View.VISIBLE);
174                 mIcon.setImageDrawable(item.first);
175             }
176 
177             itemView.setOnClickListener(v -> onClick(getDialog(), position));
178         }
179     }
180 
181     @Override
onClick(DialogInterface dialog, int which)182     public void onClick(DialogInterface dialog, int which) {
183         pinComponent(mTargetInfos.get(which).getResolvedComponentName());
184         ((ChooserActivity) getActivity()).handlePackagesChanged();
185         dismiss();
186     }
187 
pinComponent(ComponentName name)188     private void pinComponent(ComponentName name) {
189         SharedPreferences sp = ChooserActivity.getPinnedSharedPrefs(getContext());
190         final String key = name.flattenToString();
191         boolean currentVal = sp.getBoolean(name.flattenToString(), false);
192         if (currentVal) {
193             sp.edit().remove(key).apply();
194         } else {
195             sp.edit().putBoolean(key, true).apply();
196         }
197     }
198 
getPinIcon(boolean isPinned)199     private Drawable getPinIcon(boolean isPinned) {
200         return isPinned
201                 ? getContext().getDrawable(R.drawable.ic_close)
202                 : getContext().getDrawable(R.drawable.ic_chooser_pin_dialog);
203     }
204 
getPinLabel(boolean isPinned, CharSequence targetLabel)205     private CharSequence getPinLabel(boolean isPinned, CharSequence targetLabel) {
206         return isPinned
207                 ? getResources().getString(R.string.unpin_specific_target, targetLabel)
208                 : getResources().getString(R.string.pin_specific_target, targetLabel);
209     }
210 
211     @NonNull
getItemLabel(DisplayResolveInfo dri)212     protected CharSequence getItemLabel(DisplayResolveInfo dri) {
213         final PackageManager pm = getContext().getPackageManager();
214         return getPinLabel(dri.isPinned(), dri.getResolveInfo().loadLabel(pm));
215     }
216 
217     @Nullable
getItemIcon(DisplayResolveInfo dri)218     protected Drawable getItemIcon(DisplayResolveInfo dri) {
219         return getPinIcon(dri.isPinned());
220     }
221 
getProvidingAppPresentationGetter()222     private ResolveInfoPresentationGetter getProvidingAppPresentationGetter() {
223         final ActivityManager am = (ActivityManager) getContext()
224                 .getSystemService(ACTIVITY_SERVICE);
225         final int iconDpi = am.getLauncherLargeIconDensity();
226 
227         // Use the matching application icon and label for the title, any TargetInfo will do
228         return new ResolveInfoPresentationGetter(getContext(), iconDpi,
229                 mTargetInfos.get(0).getResolveInfo());
230     }
231 
232 }
233