SelectPrinterActivity.java revision 8141bdfa56f13c3946bed12ba7801e492ec25c11
1/* 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 17package com.android.printspooler.ui; 18 19import android.app.Activity; 20import android.app.AlertDialog; 21import android.app.Dialog; 22import android.app.DialogFragment; 23import android.app.Fragment; 24import android.app.FragmentTransaction; 25import android.content.ActivityNotFoundException; 26import android.content.ComponentName; 27import android.content.Context; 28import android.content.DialogInterface; 29import android.content.Intent; 30import android.content.IntentSender.SendIntentException; 31import android.content.pm.ActivityInfo; 32import android.content.pm.PackageInfo; 33import android.content.pm.PackageManager; 34import android.content.pm.PackageManager.NameNotFoundException; 35import android.content.pm.ResolveInfo; 36import android.content.pm.ServiceInfo; 37import android.database.ContentObserver; 38import android.database.DataSetObserver; 39import android.graphics.drawable.Drawable; 40import android.net.Uri; 41import android.os.Bundle; 42import android.os.Handler; 43import android.print.PrintManager; 44import android.print.PrinterId; 45import android.print.PrinterInfo; 46import android.printservice.PrintServiceInfo; 47import android.provider.Settings; 48import android.text.TextUtils; 49import android.util.Log; 50import android.view.ContextMenu; 51import android.view.ContextMenu.ContextMenuInfo; 52import android.view.Menu; 53import android.view.MenuItem; 54import android.view.View; 55import android.view.View.OnClickListener; 56import android.view.ViewGroup; 57import android.view.accessibility.AccessibilityManager; 58import android.widget.AdapterView; 59import android.widget.AdapterView.AdapterContextMenuInfo; 60import android.widget.ArrayAdapter; 61import android.widget.BaseAdapter; 62import android.widget.Filter; 63import android.widget.Filterable; 64import android.widget.ImageView; 65import android.widget.ListView; 66import android.widget.SearchView; 67import android.widget.TextView; 68 69import com.android.internal.content.PackageMonitor; 70import com.android.printspooler.R; 71 72import java.util.ArrayList; 73import java.util.List; 74 75/** 76 * This is an activity for selecting a printer. 77 */ 78public final class SelectPrinterActivity extends Activity { 79 80 private static final String LOG_TAG = "SelectPrinterFragment"; 81 82 public static final String INTENT_EXTRA_PRINTER_ID = "INTENT_EXTRA_PRINTER_ID"; 83 84 private static final String FRAGMENT_TAG_ADD_PRINTER_DIALOG = 85 "FRAGMENT_TAG_ADD_PRINTER_DIALOG"; 86 87 private static final String FRAGMENT_ARGUMENT_PRINT_SERVICE_INFOS = 88 "FRAGMENT_ARGUMENT_PRINT_SERVICE_INFOS"; 89 90 private static final String EXTRA_PRINTER_ID = "EXTRA_PRINTER_ID"; 91 92 /** If there are any enabled print services */ 93 private boolean mHasEnabledPrintServices; 94 95 private final ArrayList<PrintServiceInfo> mAddPrinterServices = 96 new ArrayList<>(); 97 98 private PrinterRegistry mPrinterRegistry; 99 100 private ListView mListView; 101 102 private AnnounceFilterResult mAnnounceFilterResult; 103 104 /** Monitor if new print services get enabled or disabled */ 105 private ContentObserver mPrintServicesDisabledObserver; 106 private PackageMonitor mPackageObserver; 107 108 @Override 109 public void onCreate(Bundle savedInstanceState) { 110 super.onCreate(savedInstanceState); 111 getActionBar().setIcon(R.drawable.ic_print); 112 113 setContentView(R.layout.select_printer_activity); 114 115 mPrinterRegistry = new PrinterRegistry(this, null); 116 117 // Hook up the list view. 118 mListView = (ListView) findViewById(android.R.id.list); 119 final DestinationAdapter adapter = new DestinationAdapter(); 120 adapter.registerDataSetObserver(new DataSetObserver() { 121 @Override 122 public void onChanged() { 123 if (!isFinishing() && adapter.getCount() <= 0) { 124 updateEmptyView(adapter); 125 } 126 } 127 128 @Override 129 public void onInvalidated() { 130 if (!isFinishing()) { 131 updateEmptyView(adapter); 132 } 133 } 134 }); 135 mListView.setAdapter(adapter); 136 137 mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 138 @Override 139 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 140 if (!((DestinationAdapter) mListView.getAdapter()).isActionable(position)) { 141 return; 142 } 143 144 PrinterInfo printer = (PrinterInfo) mListView.getAdapter().getItem(position); 145 onPrinterSelected(printer.getId()); 146 } 147 }); 148 149 registerForContextMenu(mListView); 150 } 151 152 @Override 153 public boolean onCreateOptionsMenu(Menu menu) { 154 super.onCreateOptionsMenu(menu); 155 156 getMenuInflater().inflate(R.menu.select_printer_activity, menu); 157 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) { 168 ((DestinationAdapter) mListView.getAdapter()).getFilter().filter(searchString); 169 return true; 170 } 171 }); 172 searchView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { 173 @Override 174 public void onViewAttachedToWindow(View view) { 175 if (AccessibilityManager.getInstance(SelectPrinterActivity.this).isEnabled()) { 176 view.announceForAccessibility(getString( 177 R.string.print_search_box_shown_utterance)); 178 } 179 } 180 @Override 181 public void onViewDetachedFromWindow(View view) { 182 if (!isFinishing() && AccessibilityManager.getInstance( 183 SelectPrinterActivity.this).isEnabled()) { 184 view.announceForAccessibility(getString( 185 R.string.print_search_box_hidden_utterance)); 186 } 187 } 188 }); 189 190 return true; 191 } 192 193 @Override 194 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. 211 if (mPrinterRegistry.isFavoritePrinter(printer.getId())) { 212 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: { 225 PrinterId printerId = item.getIntent().getParcelableExtra(EXTRA_PRINTER_ID); 226 onPrinterSelected(printerId); 227 } return true; 228 229 case R.string.print_forget_printer: { 230 PrinterId printerId = item.getIntent().getParcelableExtra(EXTRA_PRINTER_ID); 231 mPrinterRegistry.forgetFavoritePrinter(printerId); 232 } return true; 233 } 234 return false; 235 } 236 237 /** 238 * Adjust the UI if the enabled print services changed. 239 */ 240 private synchronized void onPrintServicesUpdate() { 241 updateServicesWithAddPrinterActivity(); 242 updateEmptyView((DestinationAdapter)mListView.getAdapter()); 243 invalidateOptionsMenu(); 244 } 245 246 /** 247 * Register listener for changes to the enabled print services. 248 */ 249 private void registerServiceMonitor() { 250 // Listen for services getting disabled 251 mPrintServicesDisabledObserver = new ContentObserver(new Handler()) { 252 @Override 253 public void onChange(boolean selfChange) { 254 onPrintServicesUpdate(); 255 } 256 }; 257 258 // 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 276 getContentResolver().registerContentObserver( 277 Settings.Secure.getUriFor(Settings.Secure.DISABLED_PRINT_SERVICES), false, 278 mPrintServicesDisabledObserver); 279 280 mPackageObserver.register(this, getMainLooper(), false); 281 } 282 283 /** 284 * Unregister the listeners for changes to the enabled print services. 285 */ 286 private void unregisterServiceMonitorIfNeeded() { 287 getContentResolver().unregisterContentObserver(mPrintServicesDisabledObserver); 288 mPackageObserver.unregister(); 289 } 290 291 @Override 292 public void onStart() { 293 super.onStart(); 294 registerServiceMonitor(); 295 onPrintServicesUpdate(); 296 } 297 298 @Override 299 public void onPause() { 300 if (mAnnounceFilterResult != null) { 301 mAnnounceFilterResult.remove(); 302 } 303 super.onPause(); 304 } 305 306 @Override 307 public void onStop() { 308 unregisterServiceMonitorIfNeeded(); 309 super.onStop(); 310 } 311 312 @Override 313 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 321 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() { 329 mHasEnabledPrintServices = true; 330 mAddPrinterServices.clear(); 331 332 // Get all enabled print services. 333 PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE); 334 List<PrintServiceInfo> enabledServices = printManager.getEnabledPrintServices(); 335 336 // No enabled print services - done. 337 if (enabledServices.isEmpty()) { 338 mHasEnabledPrintServices = false; 339 return; 340 } 341 342 // Find the services with valid add printers activities. 343 final int enabledServiceCount = enabledServices.size(); 344 for (int i = 0; i < enabledServiceCount; i++) { 345 PrintServiceInfo enabledService = enabledServices.get(i); 346 347 // No add printers activity declared - next. 348 if (TextUtils.isEmpty(enabledService.getAddPrintersActivityName())) { 349 continue; 350 } 351 352 ServiceInfo serviceInfo = enabledService.getResolveInfo().serviceInfo; 353 ComponentName addPrintersComponentName = new ComponentName( 354 serviceInfo.packageName, enabledService.getAddPrintersActivityName()); 355 Intent addPritnersIntent = new Intent() 356 .setComponent(addPrintersComponentName); 357 358 // The add printers activity is valid - add it. 359 PackageManager pm = getPackageManager(); 360 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 366 || pm.checkPermission(activityInfo.permission, getPackageName()) 367 == PackageManager.PERMISSION_GRANTED)) { 368 mAddPrinterServices.add(enabledService); 369 } 370 } 371 } 372 } 373 374 private void showAddPrinterSelectionDialog() { 375 FragmentTransaction transaction = getFragmentManager().beginTransaction(); 376 Fragment oldFragment = getFragmentManager().findFragmentByTag( 377 FRAGMENT_TAG_ADD_PRINTER_DIALOG); 378 if (oldFragment != null) { 379 transaction.remove(oldFragment); 380 } 381 AddPrinterAlertDialogFragment newFragment = new AddPrinterAlertDialogFragment(); 382 Bundle arguments = new Bundle(); 383 arguments.putParcelableArrayList(FRAGMENT_ARGUMENT_PRINT_SERVICE_INFOS, 384 mAddPrinterServices); 385 newFragment.setArguments(arguments); 386 transaction.add(newFragment, FRAGMENT_TAG_ADD_PRINTER_DIALOG); 387 transaction.commit(); 388 } 389 390 public void updateEmptyView(DestinationAdapter adapter) { 391 if (mListView.getEmptyView() == null) { 392 View emptyView = findViewById(R.id.empty_print_state); 393 mListView.setEmptyView(emptyView); 394 } 395 TextView titleView = (TextView) findViewById(R.id.title); 396 View progressBar = findViewById(R.id.progress_bar); 397 if (!mHasEnabledPrintServices) { 398 titleView.setText(R.string.print_no_print_services); 399 progressBar.setVisibility(View.GONE); 400 } else if (adapter.getUnfilteredCount() <= 0) { 401 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 409 private void announceSearchResultIfNeeded() { 410 if (AccessibilityManager.getInstance(this).isEnabled()) { 411 if (mAnnounceFilterResult == null) { 412 mAnnounceFilterResult = new AnnounceFilterResult(); 413 } 414 mAnnounceFilterResult.post(); 415 } 416 } 417 418 public static class AddPrinterAlertDialogFragment extends DialogFragment { 419 420 private String mAddPrintServiceItem; 421 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<?>) 429 getArguments().getParcelableArrayList(FRAGMENT_ARGUMENT_PRINT_SERVICE_INFOS); 430 431 final ArrayAdapter<String> adapter = new ArrayAdapter<>( 432 getActivity(), android.R.layout.simple_list_item_1); 433 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 } 439 440 final String searchUri = Settings.Secure.getString(getActivity().getContentResolver(), 441 Settings.Secure.PRINT_SERVICE_SEARCH_URI); 442 final Intent viewIntent; 443 if (!TextUtils.isEmpty(searchUri)) { 444 Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(searchUri)); 445 if (getActivity().getPackageManager().resolveActivity(intent, 0) != null) { 446 viewIntent = intent; 447 mAddPrintServiceItem = getString(R.string.add_print_service_label); 448 adapter.add(mAddPrintServiceItem); 449 } else { 450 viewIntent = null; 451 } 452 } else { 453 viewIntent = null; 454 } 455 456 builder.setAdapter(adapter, new DialogInterface.OnClickListener() { 457 @Override 458 public void onClick(DialogInterface dialog, int which) { 459 String item = adapter.getItem(which); 460 if (item.equals(mAddPrintServiceItem)) { 461 try { 462 startActivity(viewIntent); 463 } catch (ActivityNotFoundException anfe) { 464 Log.w(LOG_TAG, "Couldn't start add printer activity", anfe); 465 } 466 } 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) { 476 Log.w(LOG_TAG, "Couldn't start add printer activity", anfe); 477 } 478 } 479 } 480 }); 481 482 return builder.create(); 483 } 484 } 485 486 private final class DestinationAdapter extends BaseAdapter implements Filterable { 487 488 private final Object mLock = new Object(); 489 490 private final List<PrinterInfo> mPrinters = new ArrayList<>(); 491 492 private final List<PrinterInfo> mFilteredPrinters = new ArrayList<>(); 493 494 private CharSequence mLastSearchString; 495 496 public DestinationAdapter() { 497 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 }); 521 } 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(); 533 List<PrinterInfo> filteredPrinters = new ArrayList<>(); 534 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) { 551 final boolean resultCountChanged; 552 synchronized (mLock) { 553 final int oldPrinterCount = mFilteredPrinters.size(); 554 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 } 562 resultCountChanged = (oldPrinterCount != mFilteredPrinters.size()); 563 } 564 if (resultCountChanged) { 565 announceSearchResultIfNeeded(); 566 } 567 notifyDataSetChanged(); 568 } 569 }; 570 } 571 572 public int getUnfilteredCount() { 573 synchronized (mLock) { 574 return mPrinters.size(); 575 } 576 } 577 578 @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 598 public View getDropDownView(int position, View convertView, ViewGroup parent) { 599 return getView(position, convertView, parent); 600 } 601 602 @Override 603 public View getView(int position, View convertView, ViewGroup parent) { 604 if (convertView == null) { 605 convertView = getLayoutInflater().inflate( 606 R.layout.printer_list_item, parent, false); 607 } 608 609 convertView.setEnabled(isActionable(position)); 610 611 final PrinterInfo printer = (PrinterInfo) getItem(position); 612 613 CharSequence title = printer.getName(); 614 Drawable icon = printer.loadIcon(SelectPrinterActivity.this); 615 616 CharSequence printServiceLabel; 617 try { 618 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); 636 } 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 650 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 } 664 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 673 return convertView; 674 } 675 676 public boolean isActionable(int position) { 677 PrinterInfo printer = (PrinterInfo) getItem(position); 678 return printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE; 679 } 680 } 681 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(); 687 mListView.postDelayed(this, SEARCH_RESULT_ANNOUNCEMENT_DELAY); 688 } 689 690 public void remove() { 691 mListView.removeCallbacks(this); 692 } 693 694 @Override 695 public void run() { 696 final int count = mListView.getAdapter().getCount(); 697 final String text; 698 if (count <= 0) { 699 text = getString(R.string.print_no_printers); 700 } else { 701 text = getResources().getQuantityString( 702 R.plurals.print_search_result_count_utterance, count, count); 703 } 704 mListView.announceForAccessibility(text); 705 } 706 } 707} 708