blob: 81727ab4222a8ae2b453e2c991c30e6d23e02e29 [file] [log] [blame]
Svetoslav269403b2013-08-14 17:31:04 -07001/*
2 * Copyright (C) 2013 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
Svetoslava798c0a2014-05-15 10:47:19 -070017package com.android.printspooler.ui;
Svetoslav269403b2013-08-14 17:31:04 -070018
19import android.app.Activity;
20import android.app.AlertDialog;
21import android.app.Dialog;
22import android.app.DialogFragment;
23import android.app.Fragment;
24import android.app.FragmentTransaction;
Svetoslav Ganov860f8a62013-09-14 00:59:03 -070025import android.content.ActivityNotFoundException;
Svetoslav269403b2013-08-14 17:31:04 -070026import android.content.ComponentName;
Svetoslav Ganov860f8a62013-09-14 00:59:03 -070027import android.content.Context;
Svetoslav269403b2013-08-14 17:31:04 -070028import android.content.DialogInterface;
29import android.content.Intent;
Philip P. Moltmannbb9f6862015-12-01 14:44:24 -080030import android.content.IntentSender.SendIntentException;
Svetoslav Ganov860f8a62013-09-14 00:59:03 -070031import android.content.pm.ActivityInfo;
Svetoslav269403b2013-08-14 17:31:04 -070032import android.content.pm.PackageInfo;
33import android.content.pm.PackageManager;
34import android.content.pm.PackageManager.NameNotFoundException;
35import android.content.pm.ResolveInfo;
Svetoslav Ganov860f8a62013-09-14 00:59:03 -070036import android.content.pm.ServiceInfo;
Philip P. Moltmann03755a12015-11-25 13:35:41 -080037import android.database.ContentObserver;
Svetoslav Ganov264c5662013-10-05 12:58:17 -070038import android.database.DataSetObserver;
Svetoslav39e71de2013-10-02 19:30:32 -070039import android.graphics.drawable.Drawable;
Svetoslav269403b2013-08-14 17:31:04 -070040import android.net.Uri;
41import android.os.Bundle;
Philip P. Moltmann03755a12015-11-25 13:35:41 -080042import android.os.Handler;
Svetoslav Ganov860f8a62013-09-14 00:59:03 -070043import android.print.PrintManager;
Svetoslav269403b2013-08-14 17:31:04 -070044import android.print.PrinterId;
45import android.print.PrinterInfo;
46import android.printservice.PrintServiceInfo;
Svetoslav Ganov0ea7d1e2013-10-05 11:46:48 -070047import android.provider.Settings;
Svetoslav269403b2013-08-14 17:31:04 -070048import android.text.TextUtils;
Svetoslav Ganov860f8a62013-09-14 00:59:03 -070049import android.util.Log;
Svetoslav1c664b62013-11-04 12:30:08 -080050import android.view.ContextMenu;
51import android.view.ContextMenu.ContextMenuInfo;
Svetoslav269403b2013-08-14 17:31:04 -070052import android.view.Menu;
Svetoslav269403b2013-08-14 17:31:04 -070053import android.view.MenuItem;
54import android.view.View;
Philip P. Moltmannbb9f6862015-12-01 14:44:24 -080055import android.view.View.OnClickListener;
Svetoslav269403b2013-08-14 17:31:04 -070056import android.view.ViewGroup;
Svetoslav53e8a262013-10-08 11:25:32 -070057import android.view.accessibility.AccessibilityManager;
Svetoslav Ganoved513d7e2013-10-17 09:53:05 -070058import android.widget.AdapterView;
Svetoslav1c664b62013-11-04 12:30:08 -080059import android.widget.AdapterView.AdapterContextMenuInfo;
Svetoslav269403b2013-08-14 17:31:04 -070060import android.widget.ArrayAdapter;
61import android.widget.BaseAdapter;
62import android.widget.Filter;
63import android.widget.Filterable;
Svetoslav39e71de2013-10-02 19:30:32 -070064import android.widget.ImageView;
Svetoslav269403b2013-08-14 17:31:04 -070065import android.widget.ListView;
66import android.widget.SearchView;
67import android.widget.TextView;
68
Philip P. Moltmann8141bdf2015-12-21 17:03:05 -080069import com.android.internal.content.PackageMonitor;
Svetoslava798c0a2014-05-15 10:47:19 -070070import com.android.printspooler.R;
71
Svetoslav269403b2013-08-14 17:31:04 -070072import java.util.ArrayList;
73import java.util.List;
74
75/**
Svetoslava798c0a2014-05-15 10:47:19 -070076 * This is an activity for selecting a printer.
Svetoslav269403b2013-08-14 17:31:04 -070077 */
Svetoslava798c0a2014-05-15 10:47:19 -070078public final class SelectPrinterActivity extends Activity {
Svetoslav269403b2013-08-14 17:31:04 -070079
Svetoslav Ganov860f8a62013-09-14 00:59:03 -070080 private static final String LOG_TAG = "SelectPrinterFragment";
81
Svetoslava798c0a2014-05-15 10:47:19 -070082 public static final String INTENT_EXTRA_PRINTER_ID = "INTENT_EXTRA_PRINTER_ID";
Svetoslav269403b2013-08-14 17:31:04 -070083
Svetoslava798c0a2014-05-15 10:47:19 -070084 private static final String FRAGMENT_TAG_ADD_PRINTER_DIALOG =
85 "FRAGMENT_TAG_ADD_PRINTER_DIALOG";
Svetoslav269403b2013-08-14 17:31:04 -070086
Svetoslava798c0a2014-05-15 10:47:19 -070087 private static final String FRAGMENT_ARGUMENT_PRINT_SERVICE_INFOS =
88 "FRAGMENT_ARGUMENT_PRINT_SERVICE_INFOS";
Svetoslav269403b2013-08-14 17:31:04 -070089
Svetoslav1c664b62013-11-04 12:30:08 -080090 private static final String EXTRA_PRINTER_ID = "EXTRA_PRINTER_ID";
91
Philip P. Moltmann03755a12015-11-25 13:35:41 -080092 /** If there are any enabled print services */
Philip P. Moltmann953639c2015-11-11 15:30:16 -080093 private boolean mHasEnabledPrintServices;
94
Svetoslav269403b2013-08-14 17:31:04 -070095 private final ArrayList<PrintServiceInfo> mAddPrinterServices =
Svetoslava798c0a2014-05-15 10:47:19 -070096 new ArrayList<>();
97
98 private PrinterRegistry mPrinterRegistry;
Svetoslav269403b2013-08-14 17:31:04 -070099
Svetoslav Ganoved513d7e2013-10-17 09:53:05 -0700100 private ListView mListView;
101
Svetoslav Ganov56ddf1f2013-10-05 19:55:49 -0700102 private AnnounceFilterResult mAnnounceFilterResult;
103
Philip P. Moltmann03755a12015-11-25 13:35:41 -0800104 /** Monitor if new print services get enabled or disabled */
Philip P. Moltmann8141bdf2015-12-21 17:03:05 -0800105 private ContentObserver mPrintServicesDisabledObserver;
106 private PackageMonitor mPackageObserver;
Philip P. Moltmann03755a12015-11-25 13:35:41 -0800107
Svetoslav269403b2013-08-14 17:31:04 -0700108 @Override
109 public void onCreate(Bundle savedInstanceState) {
110 super.onCreate(savedInstanceState);
Svetoslav309f6a02014-08-18 16:10:59 -0700111 getActionBar().setIcon(R.drawable.ic_print);
Svetoslav269403b2013-08-14 17:31:04 -0700112
Svetoslava798c0a2014-05-15 10:47:19 -0700113 setContentView(R.layout.select_printer_activity);
114
115 mPrinterRegistry = new PrinterRegistry(this, null);
Svetoslav Ganoved513d7e2013-10-17 09:53:05 -0700116
117 // Hook up the list view.
Svetoslava798c0a2014-05-15 10:47:19 -0700118 mListView = (ListView) findViewById(android.R.id.list);
Svetoslav Ganov264c5662013-10-05 12:58:17 -0700119 final DestinationAdapter adapter = new DestinationAdapter();
120 adapter.registerDataSetObserver(new DataSetObserver() {
121 @Override
122 public void onChanged() {
Svetoslava798c0a2014-05-15 10:47:19 -0700123 if (!isFinishing() && adapter.getCount() <= 0) {
Svetoslav Ganov264c5662013-10-05 12:58:17 -0700124 updateEmptyView(adapter);
125 }
126 }
127
128 @Override
129 public void onInvalidated() {
Svetoslava798c0a2014-05-15 10:47:19 -0700130 if (!isFinishing()) {
Svetoslav Ganov24c686b2013-10-05 18:52:06 -0700131 updateEmptyView(adapter);
132 }
Svetoslav Ganov264c5662013-10-05 12:58:17 -0700133 }
134 });
Svetoslav Ganoved513d7e2013-10-17 09:53:05 -0700135 mListView.setAdapter(adapter);
136
137 mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
138 @Override
139 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Svetoslav1c664b62013-11-04 12:30:08 -0800140 if (!((DestinationAdapter) mListView.getAdapter()).isActionable(position)) {
141 return;
142 }
Svetoslava798c0a2014-05-15 10:47:19 -0700143
Svetoslav Ganoved513d7e2013-10-17 09:53:05 -0700144 PrinterInfo printer = (PrinterInfo) mListView.getAdapter().getItem(position);
Svetoslava798c0a2014-05-15 10:47:19 -0700145 onPrinterSelected(printer.getId());
Svetoslav Ganoved513d7e2013-10-17 09:53:05 -0700146 }
147 });
148
Svetoslav1c664b62013-11-04 12:30:08 -0800149 registerForContextMenu(mListView);
Svetoslav269403b2013-08-14 17:31:04 -0700150 }
151
152 @Override
Svetoslava798c0a2014-05-15 10:47:19 -0700153 public boolean onCreateOptionsMenu(Menu menu) {
154 super.onCreateOptionsMenu(menu);
155
156 getMenuInflater().inflate(R.menu.select_printer_activity, menu);
Svetoslav269403b2013-08-14 17:31:04 -0700157
158 MenuItem searchItem = menu.findItem(R.id.action_search);
159 SearchView searchView = (SearchView) searchItem.getActionView();
160 searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
161 @Override
162 public boolean onQueryTextSubmit(String query) {
163 return true;
164 }
165
166 @Override
167 public boolean onQueryTextChange(String searchString) {
Svetoslav Ganoved513d7e2013-10-17 09:53:05 -0700168 ((DestinationAdapter) mListView.getAdapter()).getFilter().filter(searchString);
Svetoslav269403b2013-08-14 17:31:04 -0700169 return true;
170 }
171 });
Svetoslav Ganov56ddf1f2013-10-05 19:55:49 -0700172 searchView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
173 @Override
174 public void onViewAttachedToWindow(View view) {
Svetoslava798c0a2014-05-15 10:47:19 -0700175 if (AccessibilityManager.getInstance(SelectPrinterActivity.this).isEnabled()) {
Svetoslav53e8a262013-10-08 11:25:32 -0700176 view.announceForAccessibility(getString(
177 R.string.print_search_box_shown_utterance));
178 }
Svetoslav Ganov56ddf1f2013-10-05 19:55:49 -0700179 }
180 @Override
181 public void onViewDetachedFromWindow(View view) {
Svetoslava798c0a2014-05-15 10:47:19 -0700182 if (!isFinishing() && AccessibilityManager.getInstance(
183 SelectPrinterActivity.this).isEnabled()) {
Svetoslav53e8a262013-10-08 11:25:32 -0700184 view.announceForAccessibility(getString(
185 R.string.print_search_box_hidden_utterance));
186 }
Svetoslav Ganov56ddf1f2013-10-05 19:55:49 -0700187 }
188 });
Svetoslav269403b2013-08-14 17:31:04 -0700189
Svetoslava798c0a2014-05-15 10:47:19 -0700190 return true;
Svetoslav269403b2013-08-14 17:31:04 -0700191 }
192
193 @Override
Svetoslav1c664b62013-11-04 12:30:08 -0800194 public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
195 if (view == mListView) {
196 final int position = ((AdapterContextMenuInfo) menuInfo).position;
197 PrinterInfo printer = (PrinterInfo) mListView.getAdapter().getItem(position);
198
199 menu.setHeaderTitle(printer.getName());
200
201 // Add the select menu item if applicable.
202 if (printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE) {
203 MenuItem selectItem = menu.add(Menu.NONE, R.string.print_select_printer,
204 Menu.NONE, R.string.print_select_printer);
205 Intent intent = new Intent();
206 intent.putExtra(EXTRA_PRINTER_ID, printer.getId());
207 selectItem.setIntent(intent);
208 }
209
210 // Add the forget menu item if applicable.
Svetoslava798c0a2014-05-15 10:47:19 -0700211 if (mPrinterRegistry.isFavoritePrinter(printer.getId())) {
Svetoslav1c664b62013-11-04 12:30:08 -0800212 MenuItem forgetItem = menu.add(Menu.NONE, R.string.print_forget_printer,
213 Menu.NONE, R.string.print_forget_printer);
214 Intent intent = new Intent();
215 intent.putExtra(EXTRA_PRINTER_ID, printer.getId());
216 forgetItem.setIntent(intent);
217 }
218 }
219 }
220
221 @Override
222 public boolean onContextItemSelected(MenuItem item) {
223 switch (item.getItemId()) {
224 case R.string.print_select_printer: {
Svetoslava798c0a2014-05-15 10:47:19 -0700225 PrinterId printerId = item.getIntent().getParcelableExtra(EXTRA_PRINTER_ID);
226 onPrinterSelected(printerId);
Svetoslav1c664b62013-11-04 12:30:08 -0800227 } return true;
228
229 case R.string.print_forget_printer: {
Svetoslava798c0a2014-05-15 10:47:19 -0700230 PrinterId printerId = item.getIntent().getParcelableExtra(EXTRA_PRINTER_ID);
231 mPrinterRegistry.forgetFavoritePrinter(printerId);
Svetoslav1c664b62013-11-04 12:30:08 -0800232 } return true;
233 }
234 return false;
235 }
236
Philip P. Moltmann03755a12015-11-25 13:35:41 -0800237 /**
238 * Adjust the UI if the enabled print services changed.
239 */
240 private synchronized void onPrintServicesUpdate() {
Svetoslava798c0a2014-05-15 10:47:19 -0700241 updateServicesWithAddPrinterActivity();
Philip P. Moltmann953639c2015-11-11 15:30:16 -0800242 updateEmptyView((DestinationAdapter)mListView.getAdapter());
Svetoslava798c0a2014-05-15 10:47:19 -0700243 invalidateOptionsMenu();
Svetoslav269403b2013-08-14 17:31:04 -0700244 }
245
Philip P. Moltmann03755a12015-11-25 13:35:41 -0800246 /**
247 * Register listener for changes to the enabled print services.
248 */
249 private void registerServiceMonitor() {
Philip P. Moltmann8141bdf2015-12-21 17:03:05 -0800250 // Listen for services getting disabled
251 mPrintServicesDisabledObserver = new ContentObserver(new Handler()) {
Philip P. Moltmann03755a12015-11-25 13:35:41 -0800252 @Override
253 public void onChange(boolean selfChange) {
254 onPrintServicesUpdate();
255 }
256 };
257
Philip P. Moltmann8141bdf2015-12-21 17:03:05 -0800258 // Listen for services getting installed or uninstalled
259 mPackageObserver = new PackageMonitor() {
260 @Override
261 public void onPackageModified(String packageName) {
262 onPrintServicesUpdate();
263 }
264
265 @Override
266 public void onPackageRemoved(String packageName, int uid) {
267 onPrintServicesUpdate();
268 }
269
270 @Override
271 public void onPackageAdded(String packageName, int uid) {
272 onPrintServicesUpdate();
273 }
274 };
275
Philip P. Moltmann03755a12015-11-25 13:35:41 -0800276 getContentResolver().registerContentObserver(
Philip P. Moltmann8141bdf2015-12-21 17:03:05 -0800277 Settings.Secure.getUriFor(Settings.Secure.DISABLED_PRINT_SERVICES), false,
278 mPrintServicesDisabledObserver);
279
280 mPackageObserver.register(this, getMainLooper(), false);
Philip P. Moltmann03755a12015-11-25 13:35:41 -0800281 }
282
283 /**
Philip P. Moltmann8141bdf2015-12-21 17:03:05 -0800284 * Unregister the listeners for changes to the enabled print services.
Philip P. Moltmann03755a12015-11-25 13:35:41 -0800285 */
286 private void unregisterServiceMonitorIfNeeded() {
Philip P. Moltmann8141bdf2015-12-21 17:03:05 -0800287 getContentResolver().unregisterContentObserver(mPrintServicesDisabledObserver);
288 mPackageObserver.unregister();
Philip P. Moltmann03755a12015-11-25 13:35:41 -0800289 }
290
291 @Override
292 public void onStart() {
293 super.onStart();
294 registerServiceMonitor();
295 onPrintServicesUpdate();
296 }
297
Svetoslav269403b2013-08-14 17:31:04 -0700298 @Override
Svetoslavebec4682013-10-08 17:17:08 -0700299 public void onPause() {
300 if (mAnnounceFilterResult != null) {
301 mAnnounceFilterResult.remove();
302 }
303 super.onPause();
304 }
305
306 @Override
Philip P. Moltmann03755a12015-11-25 13:35:41 -0800307 public void onStop() {
308 unregisterServiceMonitorIfNeeded();
309 super.onStop();
310 }
311
312 @Override
Svetoslav269403b2013-08-14 17:31:04 -0700313 public boolean onOptionsItemSelected(MenuItem item) {
314 if (item.getItemId() == R.id.action_add_printer) {
315 showAddPrinterSelectionDialog();
316 return true;
317 }
318 return super.onOptionsItemSelected(item);
319 }
320
Svetoslava798c0a2014-05-15 10:47:19 -0700321 private void onPrinterSelected(PrinterId printerId) {
322 Intent intent = new Intent();
323 intent.putExtra(INTENT_EXTRA_PRINTER_ID, printerId);
324 setResult(RESULT_OK, intent);
325 finish();
326 }
327
328 private void updateServicesWithAddPrinterActivity() {
Philip P. Moltmann953639c2015-11-11 15:30:16 -0800329 mHasEnabledPrintServices = true;
Svetoslav269403b2013-08-14 17:31:04 -0700330 mAddPrinterServices.clear();
331
Svetoslav Ganov860f8a62013-09-14 00:59:03 -0700332 // Get all enabled print services.
Svetoslava798c0a2014-05-15 10:47:19 -0700333 PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE);
Svetoslav Ganov860f8a62013-09-14 00:59:03 -0700334 List<PrintServiceInfo> enabledServices = printManager.getEnabledPrintServices();
Svetoslav269403b2013-08-14 17:31:04 -0700335
Svetoslav Ganov860f8a62013-09-14 00:59:03 -0700336 // No enabled print services - done.
337 if (enabledServices.isEmpty()) {
Philip P. Moltmann953639c2015-11-11 15:30:16 -0800338 mHasEnabledPrintServices = false;
Svetoslav269403b2013-08-14 17:31:04 -0700339 return;
340 }
341
342 // Find the services with valid add printers activities.
Svetoslav Ganov860f8a62013-09-14 00:59:03 -0700343 final int enabledServiceCount = enabledServices.size();
344 for (int i = 0; i < enabledServiceCount; i++) {
345 PrintServiceInfo enabledService = enabledServices.get(i);
Svetoslav269403b2013-08-14 17:31:04 -0700346
Svetoslava798c0a2014-05-15 10:47:19 -0700347 // No add printers activity declared - next.
Svetoslav Ganov860f8a62013-09-14 00:59:03 -0700348 if (TextUtils.isEmpty(enabledService.getAddPrintersActivityName())) {
Svetoslav269403b2013-08-14 17:31:04 -0700349 continue;
350 }
351
Svetoslav Ganov860f8a62013-09-14 00:59:03 -0700352 ServiceInfo serviceInfo = enabledService.getResolveInfo().serviceInfo;
Svetoslav269403b2013-08-14 17:31:04 -0700353 ComponentName addPrintersComponentName = new ComponentName(
Svetoslav Ganov860f8a62013-09-14 00:59:03 -0700354 serviceInfo.packageName, enabledService.getAddPrintersActivityName());
355 Intent addPritnersIntent = new Intent()
Svetoslav269403b2013-08-14 17:31:04 -0700356 .setComponent(addPrintersComponentName);
357
358 // The add printers activity is valid - add it.
Svetoslava798c0a2014-05-15 10:47:19 -0700359 PackageManager pm = getPackageManager();
Svetoslav Ganov860f8a62013-09-14 00:59:03 -0700360 List<ResolveInfo> resolvedActivities = pm.queryIntentActivities(addPritnersIntent, 0);
361 if (!resolvedActivities.isEmpty()) {
362 // The activity is a component name, therefore it is one or none.
363 ActivityInfo activityInfo = resolvedActivities.get(0).activityInfo;
364 if (activityInfo.exported
365 && (activityInfo.permission == null
Svetoslava798c0a2014-05-15 10:47:19 -0700366 || pm.checkPermission(activityInfo.permission, getPackageName())
Svetoslav Ganov860f8a62013-09-14 00:59:03 -0700367 == PackageManager.PERMISSION_GRANTED)) {
368 mAddPrinterServices.add(enabledService);
369 }
Svetoslav269403b2013-08-14 17:31:04 -0700370 }
371 }
372 }
373
374 private void showAddPrinterSelectionDialog() {
375 FragmentTransaction transaction = getFragmentManager().beginTransaction();
376 Fragment oldFragment = getFragmentManager().findFragmentByTag(
Svetoslava798c0a2014-05-15 10:47:19 -0700377 FRAGMENT_TAG_ADD_PRINTER_DIALOG);
Svetoslav269403b2013-08-14 17:31:04 -0700378 if (oldFragment != null) {
379 transaction.remove(oldFragment);
380 }
381 AddPrinterAlertDialogFragment newFragment = new AddPrinterAlertDialogFragment();
382 Bundle arguments = new Bundle();
Svetoslava798c0a2014-05-15 10:47:19 -0700383 arguments.putParcelableArrayList(FRAGMENT_ARGUMENT_PRINT_SERVICE_INFOS,
Svetoslav269403b2013-08-14 17:31:04 -0700384 mAddPrinterServices);
385 newFragment.setArguments(arguments);
Svetoslava798c0a2014-05-15 10:47:19 -0700386 transaction.add(newFragment, FRAGMENT_TAG_ADD_PRINTER_DIALOG);
Svetoslav269403b2013-08-14 17:31:04 -0700387 transaction.commit();
388 }
389
Svetoslav Ganov264c5662013-10-05 12:58:17 -0700390 public void updateEmptyView(DestinationAdapter adapter) {
Svetoslav Ganoved513d7e2013-10-17 09:53:05 -0700391 if (mListView.getEmptyView() == null) {
Svetoslava798c0a2014-05-15 10:47:19 -0700392 View emptyView = findViewById(R.id.empty_print_state);
Svetoslav Ganoved513d7e2013-10-17 09:53:05 -0700393 mListView.setEmptyView(emptyView);
Svetoslav Ganov24c686b2013-10-05 18:52:06 -0700394 }
Svetoslava798c0a2014-05-15 10:47:19 -0700395 TextView titleView = (TextView) findViewById(R.id.title);
396 View progressBar = findViewById(R.id.progress_bar);
Philip P. Moltmann953639c2015-11-11 15:30:16 -0800397 if (!mHasEnabledPrintServices) {
398 titleView.setText(R.string.print_no_print_services);
399 progressBar.setVisibility(View.GONE);
400 } else if (adapter.getUnfilteredCount() <= 0) {
Svetoslav Ganov264c5662013-10-05 12:58:17 -0700401 titleView.setText(R.string.print_searching_for_printers);
402 progressBar.setVisibility(View.VISIBLE);
403 } else {
404 titleView.setText(R.string.print_no_printers);
405 progressBar.setVisibility(View.GONE);
406 }
407 }
408
Svetoslavebec4682013-10-08 17:17:08 -0700409 private void announceSearchResultIfNeeded() {
Svetoslava798c0a2014-05-15 10:47:19 -0700410 if (AccessibilityManager.getInstance(this).isEnabled()) {
Svetoslavebec4682013-10-08 17:17:08 -0700411 if (mAnnounceFilterResult == null) {
412 mAnnounceFilterResult = new AnnounceFilterResult();
413 }
414 mAnnounceFilterResult.post();
Svetoslav Ganov56ddf1f2013-10-05 19:55:49 -0700415 }
Svetoslav Ganov56ddf1f2013-10-05 19:55:49 -0700416 }
417
Svetoslav269403b2013-08-14 17:31:04 -0700418 public static class AddPrinterAlertDialogFragment extends DialogFragment {
419
Svetoslav Ganov0ea7d1e2013-10-05 11:46:48 -0700420 private String mAddPrintServiceItem;
Svetoslav269403b2013-08-14 17:31:04 -0700421
422 @Override
423 @SuppressWarnings("unchecked")
424 public Dialog onCreateDialog(Bundle savedInstanceState) {
425 AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
426 .setTitle(R.string.choose_print_service);
427
428 final List<PrintServiceInfo> printServices = (List<PrintServiceInfo>) (List<?>)
Svetoslava798c0a2014-05-15 10:47:19 -0700429 getArguments().getParcelableArrayList(FRAGMENT_ARGUMENT_PRINT_SERVICE_INFOS);
Svetoslav269403b2013-08-14 17:31:04 -0700430
Svetoslava798c0a2014-05-15 10:47:19 -0700431 final ArrayAdapter<String> adapter = new ArrayAdapter<>(
Svetoslav Ganov0ea7d1e2013-10-05 11:46:48 -0700432 getActivity(), android.R.layout.simple_list_item_1);
Svetoslav269403b2013-08-14 17:31:04 -0700433 final int printServiceCount = printServices.size();
434 for (int i = 0; i < printServiceCount; i++) {
435 PrintServiceInfo printService = printServices.get(i);
436 adapter.add(printService.getResolveInfo().loadLabel(
437 getActivity().getPackageManager()).toString());
438 }
Svetoslava798c0a2014-05-15 10:47:19 -0700439
Svetoslav Ganov0ea7d1e2013-10-05 11:46:48 -0700440 final String searchUri = Settings.Secure.getString(getActivity().getContentResolver(),
441 Settings.Secure.PRINT_SERVICE_SEARCH_URI);
Svetoslava798c0a2014-05-15 10:47:19 -0700442 final Intent viewIntent;
Svetoslav Ganov0ea7d1e2013-10-05 11:46:48 -0700443 if (!TextUtils.isEmpty(searchUri)) {
444 Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(searchUri));
445 if (getActivity().getPackageManager().resolveActivity(intent, 0) != null) {
Svetoslava798c0a2014-05-15 10:47:19 -0700446 viewIntent = intent;
Svetoslav Ganov0ea7d1e2013-10-05 11:46:48 -0700447 mAddPrintServiceItem = getString(R.string.add_print_service_label);
448 adapter.add(mAddPrintServiceItem);
449 } else {
Svetoslava798c0a2014-05-15 10:47:19 -0700450 viewIntent = null;
Svetoslav Ganov0ea7d1e2013-10-05 11:46:48 -0700451 }
452 } else {
Svetoslava798c0a2014-05-15 10:47:19 -0700453 viewIntent = null;
Svetoslav Ganov0ea7d1e2013-10-05 11:46:48 -0700454 }
Svetoslav269403b2013-08-14 17:31:04 -0700455
456 builder.setAdapter(adapter, new DialogInterface.OnClickListener() {
457 @Override
458 public void onClick(DialogInterface dialog, int which) {
Svetoslav Ganov0ea7d1e2013-10-05 11:46:48 -0700459 String item = adapter.getItem(which);
Svetoslava798c0a2014-05-15 10:47:19 -0700460 if (item.equals(mAddPrintServiceItem)) {
Svetoslav Ganov0ea7d1e2013-10-05 11:46:48 -0700461 try {
Svetoslava798c0a2014-05-15 10:47:19 -0700462 startActivity(viewIntent);
463 } catch (ActivityNotFoundException anfe) {
464 Log.w(LOG_TAG, "Couldn't start add printer activity", anfe);
465 }
Svetoslav Ganov0ea7d1e2013-10-05 11:46:48 -0700466 } else {
467 PrintServiceInfo printService = printServices.get(which);
468 ComponentName componentName = new ComponentName(
469 printService.getResolveInfo().serviceInfo.packageName,
470 printService.getAddPrintersActivityName());
471 Intent intent = new Intent(Intent.ACTION_MAIN);
472 intent.setComponent(componentName);
473 try {
474 startActivity(intent);
475 } catch (ActivityNotFoundException anfe) {
Svetoslava798c0a2014-05-15 10:47:19 -0700476 Log.w(LOG_TAG, "Couldn't start add printer activity", anfe);
Svetoslav Ganov0ea7d1e2013-10-05 11:46:48 -0700477 }
Svetoslav Ganov860f8a62013-09-14 00:59:03 -0700478 }
Svetoslav269403b2013-08-14 17:31:04 -0700479 }
480 });
481
Svetoslav269403b2013-08-14 17:31:04 -0700482 return builder.create();
483 }
484 }
485
Svetoslava798c0a2014-05-15 10:47:19 -0700486 private final class DestinationAdapter extends BaseAdapter implements Filterable {
Svetoslav269403b2013-08-14 17:31:04 -0700487
488 private final Object mLock = new Object();
489
Svetoslava798c0a2014-05-15 10:47:19 -0700490 private final List<PrinterInfo> mPrinters = new ArrayList<>();
Svetoslav269403b2013-08-14 17:31:04 -0700491
Svetoslava798c0a2014-05-15 10:47:19 -0700492 private final List<PrinterInfo> mFilteredPrinters = new ArrayList<>();
Svetoslav269403b2013-08-14 17:31:04 -0700493
494 private CharSequence mLastSearchString;
495
496 public DestinationAdapter() {
Svetoslava798c0a2014-05-15 10:47:19 -0700497 mPrinterRegistry.setOnPrintersChangeListener(new PrinterRegistry.OnPrintersChangeListener() {
498 @Override
499 public void onPrintersChanged(List<PrinterInfo> printers) {
500 synchronized (mLock) {
501 mPrinters.clear();
502 mPrinters.addAll(printers);
503 mFilteredPrinters.clear();
504 mFilteredPrinters.addAll(printers);
505 if (!TextUtils.isEmpty(mLastSearchString)) {
506 getFilter().filter(mLastSearchString);
507 }
508 }
509 notifyDataSetChanged();
510 }
511
512 @Override
513 public void onPrintersInvalid() {
514 synchronized (mLock) {
515 mPrinters.clear();
516 mFilteredPrinters.clear();
517 }
518 notifyDataSetInvalidated();
519 }
520 });
Svetoslav269403b2013-08-14 17:31:04 -0700521 }
522
523 @Override
524 public Filter getFilter() {
525 return new Filter() {
526 @Override
527 protected FilterResults performFiltering(CharSequence constraint) {
528 synchronized (mLock) {
529 if (TextUtils.isEmpty(constraint)) {
530 return null;
531 }
532 FilterResults results = new FilterResults();
Svetoslava798c0a2014-05-15 10:47:19 -0700533 List<PrinterInfo> filteredPrinters = new ArrayList<>();
Svetoslav269403b2013-08-14 17:31:04 -0700534 String constraintLowerCase = constraint.toString().toLowerCase();
535 final int printerCount = mPrinters.size();
536 for (int i = 0; i < printerCount; i++) {
537 PrinterInfo printer = mPrinters.get(i);
538 if (printer.getName().toLowerCase().contains(constraintLowerCase)) {
539 filteredPrinters.add(printer);
540 }
541 }
542 results.values = filteredPrinters;
543 results.count = filteredPrinters.size();
544 return results;
545 }
546 }
547
548 @Override
549 @SuppressWarnings("unchecked")
550 protected void publishResults(CharSequence constraint, FilterResults results) {
Svetoslav Ganov56ddf1f2013-10-05 19:55:49 -0700551 final boolean resultCountChanged;
Svetoslav269403b2013-08-14 17:31:04 -0700552 synchronized (mLock) {
Svetoslav Ganov56ddf1f2013-10-05 19:55:49 -0700553 final int oldPrinterCount = mFilteredPrinters.size();
Svetoslav269403b2013-08-14 17:31:04 -0700554 mLastSearchString = constraint;
555 mFilteredPrinters.clear();
556 if (results == null) {
557 mFilteredPrinters.addAll(mPrinters);
558 } else {
559 List<PrinterInfo> printers = (List<PrinterInfo>) results.values;
560 mFilteredPrinters.addAll(printers);
561 }
Svetoslav Ganov56ddf1f2013-10-05 19:55:49 -0700562 resultCountChanged = (oldPrinterCount != mFilteredPrinters.size());
563 }
564 if (resultCountChanged) {
Svetoslavebec4682013-10-08 17:17:08 -0700565 announceSearchResultIfNeeded();
Svetoslav269403b2013-08-14 17:31:04 -0700566 }
567 notifyDataSetChanged();
568 }
569 };
570 }
571
Svetoslav Ganov264c5662013-10-05 12:58:17 -0700572 public int getUnfilteredCount() {
573 synchronized (mLock) {
574 return mPrinters.size();
575 }
576 }
577
Svetoslav269403b2013-08-14 17:31:04 -0700578 @Override
579 public int getCount() {
580 synchronized (mLock) {
581 return mFilteredPrinters.size();
582 }
583 }
584
585 @Override
586 public Object getItem(int position) {
587 synchronized (mLock) {
588 return mFilteredPrinters.get(position);
589 }
590 }
591
592 @Override
593 public long getItemId(int position) {
594 return position;
595 }
596
597 @Override
Svetoslava798c0a2014-05-15 10:47:19 -0700598 public View getDropDownView(int position, View convertView, ViewGroup parent) {
Svetoslav269403b2013-08-14 17:31:04 -0700599 return getView(position, convertView, parent);
600 }
601
602 @Override
603 public View getView(int position, View convertView, ViewGroup parent) {
604 if (convertView == null) {
Svetoslava798c0a2014-05-15 10:47:19 -0700605 convertView = getLayoutInflater().inflate(
Svetoslav Ganoved513d7e2013-10-17 09:53:05 -0700606 R.layout.printer_list_item, parent, false);
Svetoslav269403b2013-08-14 17:31:04 -0700607 }
608
Svetoslav1c664b62013-11-04 12:30:08 -0800609 convertView.setEnabled(isActionable(position));
Svetoslav Ganov69b91e02013-10-07 22:17:30 -0700610
Philip P. Moltmannbb9f6862015-12-01 14:44:24 -0800611 final PrinterInfo printer = (PrinterInfo) getItem(position);
Svetoslava798c0a2014-05-15 10:47:19 -0700612
613 CharSequence title = printer.getName();
Philip P. Moltmannbb9f6862015-12-01 14:44:24 -0800614 Drawable icon = printer.loadIcon(SelectPrinterActivity.this);
Svetoslav269403b2013-08-14 17:31:04 -0700615
Philip P. Moltmannbb9f6862015-12-01 14:44:24 -0800616 CharSequence printServiceLabel;
Svetoslav269403b2013-08-14 17:31:04 -0700617 try {
Philip P. Moltmannbb9f6862015-12-01 14:44:24 -0800618 PackageInfo packageInfo = getPackageManager().getPackageInfo(
619 printer.getId().getServiceName().getPackageName(), 0);
620
621 printServiceLabel = packageInfo.applicationInfo.loadLabel(getPackageManager());
622 } catch (NameNotFoundException e) {
623 printServiceLabel = null;
624 }
625
626 CharSequence description = printer.getDescription();
627
628 CharSequence subtitle;
629 if (printServiceLabel == null) {
630 subtitle = description;
631 } else if (description == null) {
632 subtitle = printServiceLabel;
633 } else {
634 subtitle = getString(R.string.printer_extended_description_template,
635 printServiceLabel, description);
Svetoslav269403b2013-08-14 17:31:04 -0700636 }
637
638 TextView titleView = (TextView) convertView.findViewById(R.id.title);
639 titleView.setText(title);
640
641 TextView subtitleView = (TextView) convertView.findViewById(R.id.subtitle);
642 if (!TextUtils.isEmpty(subtitle)) {
643 subtitleView.setText(subtitle);
644 subtitleView.setVisibility(View.VISIBLE);
645 } else {
646 subtitleView.setText(null);
647 subtitleView.setVisibility(View.GONE);
648 }
649
Philip P. Moltmannbb9f6862015-12-01 14:44:24 -0800650 ImageView moreInfoView = (ImageView) convertView.findViewById(R.id.more_info);
651 if (printer.getInfoIntent() != null) {
652 moreInfoView.setVisibility(View.VISIBLE);
653 moreInfoView.setOnClickListener(new OnClickListener() {
654 @Override
655 public void onClick(View v) {
656 try {
657 startIntentSender(printer.getInfoIntent().getIntentSender(), null, 0, 0, 0);
658 } catch (SendIntentException e) {
659 Log.e(LOG_TAG, "Could not execute pending info intent: %s", e);
660 }
661 }
662 });
663 }
Svetoslav39e71de2013-10-02 19:30:32 -0700664
665 ImageView iconView = (ImageView) convertView.findViewById(R.id.icon);
666 if (icon != null) {
667 iconView.setImageDrawable(icon);
668 iconView.setVisibility(View.VISIBLE);
669 } else {
670 iconView.setVisibility(View.GONE);
671 }
672
Svetoslav269403b2013-08-14 17:31:04 -0700673 return convertView;
674 }
675
Svetoslav1c664b62013-11-04 12:30:08 -0800676 public boolean isActionable(int position) {
Svetoslav Ganov69b91e02013-10-07 22:17:30 -0700677 PrinterInfo printer = (PrinterInfo) getItem(position);
678 return printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE;
679 }
Svetoslav269403b2013-08-14 17:31:04 -0700680 }
Svetoslav Ganov56ddf1f2013-10-05 19:55:49 -0700681
682 private final class AnnounceFilterResult implements Runnable {
683 private static final int SEARCH_RESULT_ANNOUNCEMENT_DELAY = 1000; // 1 sec
684
685 public void post() {
686 remove();
Svetoslav Ganoved513d7e2013-10-17 09:53:05 -0700687 mListView.postDelayed(this, SEARCH_RESULT_ANNOUNCEMENT_DELAY);
Svetoslav Ganov56ddf1f2013-10-05 19:55:49 -0700688 }
689
690 public void remove() {
Svetoslav Ganoved513d7e2013-10-17 09:53:05 -0700691 mListView.removeCallbacks(this);
Svetoslav Ganov56ddf1f2013-10-05 19:55:49 -0700692 }
693
694 @Override
695 public void run() {
Svetoslav Ganoved513d7e2013-10-17 09:53:05 -0700696 final int count = mListView.getAdapter().getCount();
Svetoslav Ganov56ddf1f2013-10-05 19:55:49 -0700697 final String text;
698 if (count <= 0) {
699 text = getString(R.string.print_no_printers);
700 } else {
Svetoslava798c0a2014-05-15 10:47:19 -0700701 text = getResources().getQuantityString(
Svetoslav Ganov56ddf1f2013-10-05 19:55:49 -0700702 R.plurals.print_search_result_count_utterance, count, count);
703 }
Svetoslav Ganoved513d7e2013-10-17 09:53:05 -0700704 mListView.announceForAccessibility(text);
Svetoslav Ganov56ddf1f2013-10-05 19:55:49 -0700705 }
706 }
Svetoslav269403b2013-08-14 17:31:04 -0700707}