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