• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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 package com.android.bips.ui;
18 
19 import android.content.Intent;
20 import android.content.pm.PackageInfo;
21 import android.content.pm.PackageManager;
22 import android.net.Uri;
23 import android.os.Bundle;
24 import android.print.PrintManager;
25 import android.printservice.recommendation.RecommendationInfo;
26 import android.util.Log;
27 
28 import androidx.preference.Preference;
29 import androidx.preference.PreferenceCategory;
30 import androidx.preference.PreferenceFragmentCompat;
31 
32 import com.android.bips.R;
33 import com.android.bips.flags.Flags;
34 
35 import java.net.InetAddress;
36 import java.text.Collator;
37 import java.util.ArrayList;
38 import java.util.Collections;
39 import java.util.HashMap;
40 import java.util.List;
41 import java.util.Map;
42 
43 /**
44  * A fragment allowing the user to review recommended print services and install or enable them.
45  */
46 public class MoreOptionsFragment extends PreferenceFragmentCompat implements
47         PrintManager.PrintServiceRecommendationsChangeListener {
48     private static final String TAG = MoreOptionsFragment.class.getSimpleName();
49     private static final boolean DEBUG = false;
50 
51     private static final String KEY_RECOMMENDATION_CATEGORY = "recommendation_category";
52     private static final String KEY_MANAGE = "manage";
53     private static final String PACKAGE_NAME_VENDING = "com.android.vending";
54     private static final Collator COLLATOR = Collator.getInstance();
55 
56     private PrintManager mPrintManager;
57     private PackageManager mPackageManager;
58     private PreferenceCategory mRecommendations;
59     private MoreOptionsActivity mActivity;
60     private boolean mHasVending = false;
61     private Map<String, RecommendationItem> mItems = new HashMap<>();
62 
63     @Override
onCreatePreferences(Bundle savedInstanceState, String rootKey)64     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
65         if ((Flags.printerInfoDetails())) {
66             addPreferencesFromResource(R.xml.more_options_prefs_new);
67         } else {
68             addPreferencesFromResource(R.xml.more_options_prefs);
69         }
70 
71         mRecommendations = getPreferenceScreen().findPreference(
72                 KEY_RECOMMENDATION_CATEGORY);
73         if ((Flags.printerInfoDetails())) {
74             mRecommendations.setIconSpaceReserved(false);
75         }
76 
77         getPreferenceScreen().findPreference(KEY_MANAGE)
78                 .setOnPreferenceClickListener(preference -> {
79                     startActivity(new Intent(android.provider.Settings.ACTION_PRINT_SETTINGS)
80                             .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
81                     return true;
82                 });
83     }
84 
85     @Override
onStart()86     public void onStart() {
87         super.onStart();
88         mPackageManager = getContext().getPackageManager();
89 
90         try {
91             // Attempt to get vending package, throws if missing
92             mPackageManager.getPackageInfo(PACKAGE_NAME_VENDING, 0);
93             mHasVending = true;
94         } catch (PackageManager.NameNotFoundException ignored) {
95         }
96 
97         mActivity = (MoreOptionsActivity) getActivity();
98         if ((Flags.printerInfoDetails())) {
99             mActivity.setTitle(mActivity.getResources().getString(R.string.recommendation_link));
100         }
101 
102         mPrintManager = getContext().getSystemService(PrintManager.class);
103 
104         // Start listening for recommendation changes
105         mPrintManager.addPrintServiceRecommendationsChangeListener(this, null);
106         onPrintServiceRecommendationsChanged();
107     }
108 
109     @Override
onStop()110     public void onStop() {
111         super.onStop();
112         mPrintManager.removePrintServiceRecommendationsChangeListener(this);
113     }
114 
115     @Override
onPrintServiceRecommendationsChanged()116     public void onPrintServiceRecommendationsChanged() {
117         List<RecommendationInfo> infos = mPrintManager.getPrintServiceRecommendations();
118         if (DEBUG) Log.d(TAG, "All recommendations count=" + infos.size());
119         // Update items list with new recommendations. Recommendations don't go away.
120         for (RecommendationInfo info : infos) {
121             for (InetAddress address : info.getDiscoveredPrinters()) {
122                 if (address.equals(mActivity.mPrinterAddress)) {
123                     // This recommendation matches so create or update an item for it
124                     String packageName = info.getPackageName().toString();
125                     RecommendationItem item = getOrCreateItem(info);
126                     try {
127                         // If this doesn't throw then the service is installed
128                         item.mPrintService = mPackageManager.getPackageInfo(packageName, 0);
129                     } catch (PackageManager.NameNotFoundException e) {
130                         item.mPrintService = null;
131                     }
132                     break;
133                 }
134             }
135         }
136 
137         // Update preferences with ordering
138         List<RecommendationItem> itemList = new ArrayList<>(mItems.values());
139         Collections.sort(itemList);
140         for (int index = 0; index < itemList.size(); index++) {
141             itemList.get(index).updatePreference(index);
142         }
143 
144         if (DEBUG) Log.d(TAG, "For this printer=" + mRecommendations.getPreferenceCount());
145 
146         // Show group if not empty
147         if (mRecommendations.getPreferenceCount() == 0) {
148             getPreferenceScreen().removePreference(mRecommendations);
149         } else {
150             getPreferenceScreen().addPreference(mRecommendations);
151         }
152     }
153 
getOrCreateItem(RecommendationInfo recommendationInfo)154     private RecommendationItem getOrCreateItem(RecommendationInfo recommendationInfo) {
155         String packageName = recommendationInfo.getPackageName().toString();
156         RecommendationItem item = mItems.get(packageName);
157         if (item == null) {
158             item = new RecommendationItem(recommendationInfo);
159             mItems.put(packageName, item);
160         } else {
161             item.mRecommendationInfo = recommendationInfo;
162         }
163         return item;
164     }
165 
166     /** An item corresponding to a recommended print service. */
167     private class RecommendationItem implements Comparable<RecommendationItem> {
168         RecommendationInfo mRecommendationInfo;
169         String mPackageName;
170         Preference mPreference = new Preference(getContext());
171         /** Present only if the corresponding print service is installed. */
172         PackageInfo mPrintService;
173 
RecommendationItem(RecommendationInfo info)174         RecommendationItem(RecommendationInfo info) {
175             mRecommendationInfo = info;
176             mPackageName = mRecommendationInfo.getPackageName().toString();
177         }
178 
updatePreference(int order)179         void updatePreference(int order) {
180             mPreference.setKey(mPackageName);
181             mPreference.setTitle(mRecommendationInfo.getName());
182             mPreference.setOrder(order);
183             if (mPrintService != null) {
184                 updateEnabler();
185                 if (mRecommendations.findPreference(mPackageName) == null) {
186                     mRecommendations.addPreference(mPreference);
187                 }
188             } else if (mHasVending) {
189                 updateDownloader();
190                 if (mRecommendations.findPreference(mPackageName) == null) {
191                     mRecommendations.addPreference(mPreference);
192                 }
193             } else {
194                 mRecommendations.removePreference(mPreference);
195             }
196         }
197 
198         /** Update the preference to allow the user to enable an installed print service. */
updateEnabler()199         private void updateEnabler() {
200             try {
201                 // Set icon from application if possible
202                 mPreference.setIcon(mPackageManager.getApplicationIcon(
203                         mPrintService.packageName));
204             } catch (PackageManager.NameNotFoundException e) {
205                 mPreference.setIcon(null);
206                 mPreference.setIconSpaceReserved(true);
207             }
208             mPreference.setSummary(R.string.recommendation_enable_summary);
209             mPreference.setOnPreferenceClickListener(preference -> {
210                 // There's no activity to go directly to the print service screen
211                 startActivity(new Intent(android.provider.Settings.ACTION_PRINT_SETTINGS)
212                         .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
213                 return true;
214             });
215         }
216 
217         /** Update this preference to reflect a print service that must be downloaded. */
updateDownloader()218         private void updateDownloader() {
219             mPreference.setIcon(R.drawable.ic_download_from_market);
220             mPreference.setSummary(R.string.recommendation_install_summary);
221             mPreference.setOnPreferenceClickListener(preference -> {
222                 Uri printServiceUri = Uri.parse("market://details?id="
223                         + mRecommendationInfo.getPackageName());
224                 startActivity(new Intent(Intent.ACTION_VIEW, printServiceUri).setFlags(
225                         Intent.FLAG_ACTIVITY_NEW_TASK));
226                 return true;
227             });
228         }
229 
230         @Override
compareTo(RecommendationItem other)231         public int compareTo(RecommendationItem other) {
232             // Sort items:
233             // - first by single-vendor (more likely to be manufacturer-specific),
234             // - then alphabetically.
235             if (mRecommendationInfo.recommendsMultiVendorService()
236                     != other.mRecommendationInfo.recommendsMultiVendorService()) {
237                 return mRecommendationInfo.recommendsMultiVendorService() ? 1 : -1;
238             }
239 
240             return COLLATOR.compare(mRecommendationInfo.getName().toString(),
241                     other.mRecommendationInfo.getName().toString());
242         }
243     }
244 }
245