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 
17 package com.android.settings.print;
18 
19 import android.app.Activity;
20 import android.app.settings.SettingsEnums;
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.IntentSender.SendIntentException;
25 import android.content.pm.ResolveInfo;
26 import android.graphics.drawable.Drawable;
27 import android.os.Bundle;
28 import android.print.PrintManager;
29 import android.print.PrinterDiscoverySession;
30 import android.print.PrinterDiscoverySession.OnPrintersChangeListener;
31 import android.print.PrinterId;
32 import android.print.PrinterInfo;
33 import android.printservice.PrintServiceInfo;
34 import android.text.TextUtils;
35 import android.util.Log;
36 import android.util.TypedValue;
37 import android.view.LayoutInflater;
38 import android.view.Menu;
39 import android.view.MenuInflater;
40 import android.view.MenuItem;
41 import android.view.View;
42 import android.view.View.OnClickListener;
43 import android.view.ViewGroup;
44 import android.view.accessibility.AccessibilityManager;
45 import android.widget.Filter;
46 import android.widget.Filterable;
47 import android.widget.ImageView;
48 import android.widget.LinearLayout;
49 import android.widget.SearchView;
50 import android.widget.Switch;
51 import android.widget.TextView;
52 
53 import androidx.annotation.NonNull;
54 import androidx.loader.app.LoaderManager;
55 import androidx.loader.content.Loader;
56 import androidx.recyclerview.widget.RecyclerView;
57 import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver;
58 
59 import com.android.settings.R;
60 import com.android.settings.SettingsActivity;
61 import com.android.settings.SettingsPreferenceFragment;
62 import com.android.settings.widget.SwitchBar;
63 import com.android.settings.widget.ToggleSwitch;
64 
65 import java.util.ArrayList;
66 import java.util.LinkedHashMap;
67 import java.util.List;
68 import java.util.Map;
69 
70 /**
71  * Fragment with print service settings.
72  */
73 public class PrintServiceSettingsFragment extends SettingsPreferenceFragment
74         implements SwitchBar.OnSwitchChangeListener,
75         LoaderManager.LoaderCallbacks<List<PrintServiceInfo>> {
76 
77     private static final String LOG_TAG = "PrintServiceSettings";
78 
79     private static final int LOADER_ID_PRINTERS_LOADER = 1;
80     private static final int LOADER_ID_PRINT_SERVICE_LOADER = 2;
81 
82     private final AdapterDataObserver mDataObserver = new AdapterDataObserver() {
83         @Override
84         public void onChanged() {
85             invalidateOptionsMenuIfNeeded();
86             updateEmptyView();
87         }
88 
89         private void invalidateOptionsMenuIfNeeded() {
90             final int unfilteredItemCount = mPrintersAdapter.getUnfilteredCount();
91             if ((mLastUnfilteredItemCount <= 0 && unfilteredItemCount > 0)
92                     || mLastUnfilteredItemCount > 0 && unfilteredItemCount <= 0) {
93                 getActivity().invalidateOptionsMenu();
94             }
95             mLastUnfilteredItemCount = unfilteredItemCount;
96         }
97     };
98 
99     private SwitchBar mSwitchBar;
100     private ToggleSwitch mToggleSwitch;
101 
102     private String mPreferenceKey;
103 
104     private Intent mSettingsIntent;
105 
106     private Intent mAddPrintersIntent;
107 
108     private ComponentName mComponentName;
109 
110     private PrintersAdapter mPrintersAdapter;
111 
112     private int mLastUnfilteredItemCount;
113 
114     private boolean mServiceEnabled;
115 
116     private SearchView mSearchView;
117 
118     @Override
getMetricsCategory()119     public int getMetricsCategory() {
120         return SettingsEnums.PRINT_SERVICE_SETTINGS;
121     }
122 
123     @Override
onCreate(Bundle icicle)124     public void onCreate(Bundle icicle) {
125         super.onCreate(icicle);
126 
127         String title = getArguments().getString(PrintSettingsFragment.EXTRA_TITLE);
128         if (!TextUtils.isEmpty(title)) {
129             getActivity().setTitle(title);
130         }
131     }
132 
133     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)134     public View onCreateView(LayoutInflater inflater, ViewGroup container,
135             Bundle savedInstanceState) {
136         View root = super.onCreateView(inflater, container, savedInstanceState);
137 
138         mServiceEnabled = getArguments().getBoolean(PrintSettingsFragment.EXTRA_CHECKED);
139 
140         return root;
141     }
142 
143     @Override
onStart()144     public void onStart() {
145         super.onStart();
146         initComponents();
147         updateUiForArguments();
148         updateEmptyView();
149         updateUiForServiceState();
150     }
151 
152     @Override
onPause()153     public void onPause() {
154         if (mSearchView != null) {
155             mSearchView.setOnQueryTextListener(null);
156         }
157         super.onPause();
158     }
159 
160     @Override
onStop()161     public void onStop() {
162         super.onStop();
163         mSwitchBar.removeOnSwitchChangeListener(this);
164         mSwitchBar.hide();
165         mPrintersAdapter.unregisterAdapterDataObserver(mDataObserver);
166     }
167 
onPreferenceToggled(String preferenceKey, boolean enabled)168     private void onPreferenceToggled(String preferenceKey, boolean enabled) {
169         ((PrintManager)getContext().getSystemService(Context.PRINT_SERVICE))
170                 .setPrintServiceEnabled(mComponentName, enabled);
171     }
172 
updateEmptyView()173     private void updateEmptyView() {
174         ViewGroup contentRoot = (ViewGroup) getListView().getParent();
175         View emptyView = getEmptyView();
176         if (!mToggleSwitch.isChecked()) {
177             if (emptyView != null) {
178                 contentRoot.removeView(emptyView);
179                 emptyView = null;
180             }
181             if (emptyView == null) {
182                 emptyView = getActivity().getLayoutInflater().inflate(
183                         R.layout.empty_print_state, contentRoot, false);
184                 TextView textView = (TextView) emptyView.findViewById(R.id.message);
185                 textView.setText(R.string.print_service_disabled);
186                 contentRoot.addView(emptyView);
187                 setEmptyView(emptyView);
188             }
189         } else if (mPrintersAdapter.getUnfilteredCount() <= 0) {
190             if (emptyView != null) {
191                 contentRoot.removeView(emptyView);
192                 emptyView = null;
193             }
194             if (emptyView == null) {
195                 emptyView = getActivity().getLayoutInflater().inflate(
196                         R.layout.empty_printers_list_service_enabled, contentRoot, false);
197                 contentRoot.addView(emptyView);
198                 setEmptyView(emptyView);
199             }
200         } else if (mPrintersAdapter.getItemCount() <= 0) {
201             if (emptyView != null) {
202                 contentRoot.removeView(emptyView);
203                 emptyView = null;
204             }
205             if (emptyView == null) {
206                 emptyView = getActivity().getLayoutInflater().inflate(
207                         R.layout.empty_print_state, contentRoot, false);
208                 TextView textView = (TextView) emptyView.findViewById(R.id.message);
209                 textView.setText(R.string.print_no_printers_found);
210                 contentRoot.addView(emptyView);
211                 setEmptyView(emptyView);
212             }
213         } else if (mPrintersAdapter.getItemCount() > 0) {
214             if (emptyView != null) {
215                 contentRoot.removeView(emptyView);
216             }
217         }
218     }
219 
updateUiForServiceState()220     private void updateUiForServiceState() {
221         if (mServiceEnabled) {
222             mSwitchBar.setCheckedInternal(true);
223             mPrintersAdapter.enable();
224         } else {
225             mSwitchBar.setCheckedInternal(false);
226             mPrintersAdapter.disable();
227         }
228         getActivity().invalidateOptionsMenu();
229     }
230 
initComponents()231     private void initComponents() {
232         mPrintersAdapter = new PrintersAdapter();
233         mPrintersAdapter.registerAdapterDataObserver(mDataObserver);
234 
235         final SettingsActivity activity = (SettingsActivity) getActivity();
236 
237         mSwitchBar = activity.getSwitchBar();
238         mSwitchBar.addOnSwitchChangeListener(this);
239         mSwitchBar.show();
240 
241         mToggleSwitch = mSwitchBar.getSwitch();
242         mToggleSwitch.setOnBeforeCheckedChangeListener((toggleSwitch, checked) -> {
243             onPreferenceToggled(mPreferenceKey, checked);
244             return false;
245         });
246 
247         getListView().setAdapter(mPrintersAdapter);
248     }
249 
250 
251     @Override
onSwitchChanged(Switch switchView, boolean isChecked)252     public void onSwitchChanged(Switch switchView, boolean isChecked) {
253         updateEmptyView();
254     }
255 
updateUiForArguments()256     private void updateUiForArguments() {
257         Bundle arguments = getArguments();
258 
259         // Component name.
260         mComponentName = ComponentName.unflattenFromString(arguments
261                 .getString(PrintSettingsFragment.EXTRA_SERVICE_COMPONENT_NAME));
262 
263         // Key.
264         mPreferenceKey = mComponentName.flattenToString();
265 
266         // Enabled.
267         final boolean enabled = arguments.getBoolean(PrintSettingsFragment.EXTRA_CHECKED);
268         mSwitchBar.setCheckedInternal(enabled);
269 
270         getLoaderManager().initLoader(LOADER_ID_PRINT_SERVICE_LOADER, null, this);
271         setHasOptionsMenu(true);
272     }
273 
274     @Override
onCreateLoader(int id, Bundle args)275     public Loader<List<PrintServiceInfo>> onCreateLoader(int id, Bundle args) {
276         return new SettingsPrintServicesLoader(
277                 (PrintManager) getContext().getSystemService(Context.PRINT_SERVICE), getContext(),
278                 PrintManager.ALL_SERVICES);
279     }
280 
281     @Override
onLoadFinished(Loader<List<PrintServiceInfo>> loader, List<PrintServiceInfo> services)282     public void onLoadFinished(Loader<List<PrintServiceInfo>> loader,
283             List<PrintServiceInfo> services) {
284         PrintServiceInfo service = null;
285 
286         if (services != null) {
287             final int numServices = services.size();
288             for (int i = 0; i < numServices; i++) {
289                 if (services.get(i).getComponentName().equals(mComponentName)) {
290                     service = services.get(i);
291                     break;
292                 }
293             }
294         }
295 
296         if (service == null) {
297             // The print service was uninstalled
298             finishFragment();
299         }
300 
301         mServiceEnabled = service.isEnabled();
302 
303         if (service.getSettingsActivityName() != null) {
304             Intent settingsIntent = new Intent(Intent.ACTION_MAIN);
305 
306             settingsIntent.setComponent(
307                     new ComponentName(service.getComponentName().getPackageName(),
308                             service.getSettingsActivityName()));
309 
310             List<ResolveInfo> resolvedActivities = getPackageManager().queryIntentActivities(
311                     settingsIntent, 0);
312             if (!resolvedActivities.isEmpty()) {
313                 // The activity is a component name, therefore it is one or none.
314                 if (resolvedActivities.get(0).activityInfo.exported) {
315                     mSettingsIntent = settingsIntent;
316                 }
317             }
318         } else {
319             mSettingsIntent = null;
320         }
321 
322         if (service.getAddPrintersActivityName() != null) {
323             Intent addPrintersIntent = new Intent(Intent.ACTION_MAIN);
324 
325             addPrintersIntent.setComponent(
326                     new ComponentName(service.getComponentName().getPackageName(),
327                             service.getAddPrintersActivityName()));
328 
329             List<ResolveInfo> resolvedActivities = getPackageManager().queryIntentActivities(
330                     addPrintersIntent, 0);
331             if (!resolvedActivities.isEmpty()) {
332                 // The activity is a component name, therefore it is one or none.
333                 if (resolvedActivities.get(0).activityInfo.exported) {
334                     mAddPrintersIntent = addPrintersIntent;
335                 }
336             }
337         } else {
338             mAddPrintersIntent = null;
339         }
340 
341         updateUiForServiceState();
342     }
343 
344     @Override
onLoaderReset(Loader<List<PrintServiceInfo>> loader)345     public void onLoaderReset(Loader<List<PrintServiceInfo>> loader) {
346         updateUiForServiceState();
347     }
348 
349     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)350     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
351         super.onCreateOptionsMenu(menu, inflater);
352         inflater.inflate(R.menu.print_service_settings, menu);
353 
354         MenuItem addPrinters = menu.findItem(R.id.print_menu_item_add_printer);
355         if (mServiceEnabled && mAddPrintersIntent != null) {
356             addPrinters.setIntent(mAddPrintersIntent);
357         } else {
358             menu.removeItem(R.id.print_menu_item_add_printer);
359         }
360 
361         MenuItem settings = menu.findItem(R.id.print_menu_item_settings);
362         if (mServiceEnabled && mSettingsIntent != null) {
363             settings.setIntent(mSettingsIntent);
364         } else {
365             menu.removeItem(R.id.print_menu_item_settings);
366         }
367 
368         MenuItem searchItem = menu.findItem(R.id.print_menu_item_search);
369         if (mServiceEnabled && mPrintersAdapter.getUnfilteredCount() > 0) {
370             mSearchView = (SearchView) searchItem.getActionView();
371             mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
372                 @Override
373                 public boolean onQueryTextSubmit(String query) {
374                     return true;
375                 }
376 
377                 @Override
378                 public boolean onQueryTextChange(String searchString) {
379                     mPrintersAdapter.getFilter().filter(searchString);
380                     return true;
381                 }
382             });
383             mSearchView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
384                 @Override
385                 public void onViewAttachedToWindow(View view) {
386                     if (AccessibilityManager.getInstance(getActivity()).isEnabled()) {
387                         view.announceForAccessibility(getString(
388                                 R.string.print_search_box_shown_utterance));
389                     }
390                 }
391                 @Override
392                 public void onViewDetachedFromWindow(View view) {
393                     Activity activity = getActivity();
394                     if (activity != null && !activity.isFinishing()
395                             && AccessibilityManager.getInstance(activity).isEnabled()) {
396                         view.announceForAccessibility(getString(
397                                 R.string.print_search_box_hidden_utterance));
398                     }
399                 }
400             });
401         } else {
402             menu.removeItem(R.id.print_menu_item_search);
403         }
404     }
405 
406     public static class ViewHolder extends RecyclerView.ViewHolder {
407 
ViewHolder(@onNull View itemView)408         public ViewHolder(@NonNull View itemView) {
409             super(itemView);
410         }
411     }
412 
413 
414     private final class PrintersAdapter extends RecyclerView.Adapter<ViewHolder>
415             implements LoaderManager.LoaderCallbacks<List<PrinterInfo>>, Filterable {
416 
417         private final Object mLock = new Object();
418 
419         private final List<PrinterInfo> mPrinters = new ArrayList<PrinterInfo>();
420 
421         private final List<PrinterInfo> mFilteredPrinters = new ArrayList<PrinterInfo>();
422 
423         private CharSequence mLastSearchString;
424 
enable()425         public void enable() {
426             getLoaderManager().initLoader(LOADER_ID_PRINTERS_LOADER, null, this);
427         }
428 
disable()429         public void disable() {
430             getLoaderManager().destroyLoader(LOADER_ID_PRINTERS_LOADER);
431             mPrinters.clear();
432         }
433 
getUnfilteredCount()434         public int getUnfilteredCount() {
435             return mPrinters.size();
436         }
437 
438         @Override
getFilter()439         public Filter getFilter() {
440             return new Filter() {
441                 @Override
442                 protected FilterResults performFiltering(CharSequence constraint) {
443                     synchronized (mLock) {
444                         if (TextUtils.isEmpty(constraint)) {
445                             return null;
446                         }
447                         FilterResults results = new FilterResults();
448                         List<PrinterInfo> filteredPrinters = new ArrayList<PrinterInfo>();
449                         String constraintLowerCase = constraint.toString().toLowerCase();
450                         final int printerCount = mPrinters.size();
451                         for (int i = 0; i < printerCount; i++) {
452                             PrinterInfo printer = mPrinters.get(i);
453                             String name = printer.getName();
454                             if (name != null && name.toLowerCase().contains(constraintLowerCase)) {
455                                 filteredPrinters.add(printer);
456                             }
457                         }
458                         results.values = filteredPrinters;
459                         results.count = filteredPrinters.size();
460                         return results;
461                     }
462                 }
463 
464                 @Override
465                 @SuppressWarnings("unchecked")
466                 protected void publishResults(CharSequence constraint, FilterResults results) {
467                     synchronized (mLock) {
468                         mLastSearchString = constraint;
469                         mFilteredPrinters.clear();
470                         if (results == null) {
471                             mFilteredPrinters.addAll(mPrinters);
472                         } else {
473                             List<PrinterInfo> printers = (List<PrinterInfo>) results.values;
474                             mFilteredPrinters.addAll(printers);
475                         }
476                     }
477                     notifyDataSetChanged();
478 
479                 }
480             };
481         }
482 
483         @Override
484         public int getItemCount() {
485             synchronized (mLock) {
486                 return mFilteredPrinters.size();
487             }
488         }
489 
490         private Object getItem(int position) {
491             synchronized (mLock) {
492                 return mFilteredPrinters.get(position);
493             }
494         }
495 
496         @Override
497         public long getItemId(int position) {
498             return position;
499         }
500 
501         /**
502          * Checks if a printer can be used for printing
503          *
504          * @param position The position of the printer in the list
505          * @return true iff the printer can be used for printing.
506          */
507         public boolean isActionable(int position) {
508             PrinterInfo printer = (PrinterInfo) getItem(position);
509             return printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE;
510         }
511 
512         @NonNull
513         @Override
514         public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
515             final View view = LayoutInflater.from(parent.getContext())
516                     .inflate(R.layout.printer_dropdown_item, parent, false);
517             return new ViewHolder(view);
518         }
519 
520         @Override
521         public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
522             holder.itemView.setEnabled(isActionable(position));
523 
524             final PrinterInfo printer = (PrinterInfo) getItem(position);
525             CharSequence title = printer.getName();
526             CharSequence subtitle = printer.getDescription();
527             Drawable icon = printer.loadIcon(getActivity());
528 
529             TextView titleView = holder.itemView.findViewById(R.id.title);
530             titleView.setText(title);
531 
532             TextView subtitleView = holder.itemView.findViewById(R.id.subtitle);
533             if (!TextUtils.isEmpty(subtitle)) {
534                 subtitleView.setText(subtitle);
535                 subtitleView.setVisibility(View.VISIBLE);
536             } else {
537                 subtitleView.setText(null);
538                 subtitleView.setVisibility(View.GONE);
539             }
540 
541             LinearLayout moreInfoView = holder.itemView.findViewById(R.id.more_info);
542             if (printer.getInfoIntent() != null) {
543                 moreInfoView.setVisibility(View.VISIBLE);
544                 moreInfoView.setOnClickListener(new OnClickListener() {
545                     @Override
546                     public void onClick(View v) {
547                         try {
548                             getActivity().startIntentSender(
549                                     printer.getInfoIntent().getIntentSender(), null, 0, 0, 0);
550                         } catch (SendIntentException e) {
551                             Log.e(LOG_TAG, "Could not execute pending info intent: %s", e);
552                         }
553                     }
554                 });
555             } else {
556                 moreInfoView.setVisibility(View.GONE);
557             }
558 
559             ImageView iconView = holder.itemView.findViewById(R.id.icon);
560             if (icon != null) {
561                 iconView.setVisibility(View.VISIBLE);
562                 if (!isActionable(position)) {
563                     icon.mutate();
564 
565                     TypedValue value = new TypedValue();
566                     getActivity().getTheme().resolveAttribute(android.R.attr.disabledAlpha, value,
567                             true);
568                     icon.setAlpha((int)(value.getFloat() * 255));
569                 }
570                 iconView.setImageDrawable(icon);
571             } else {
572                 iconView.setVisibility(View.GONE);
573             }
574 
575             holder.itemView.setOnClickListener(v -> {
576                 PrinterInfo pi = (PrinterInfo) getItem(position);
577 
578                 if (pi.getInfoIntent() != null) {
579                     try {
580                         getActivity().startIntentSender(pi.getInfoIntent().getIntentSender(),
581                                 null, 0, 0, 0);
582                     } catch (SendIntentException e) {
583                         Log.e(LOG_TAG, "Could not execute info intent: %s", e);
584                     }
585                 }
586             });
587         }
588 
589         @Override
590         public Loader<List<PrinterInfo>> onCreateLoader(int id, Bundle args) {
591             if (id == LOADER_ID_PRINTERS_LOADER) {
592                 return new PrintersLoader(getContext());
593             }
594             return null;
595         }
596 
597         @Override
598         public void onLoadFinished(Loader<List<PrinterInfo>> loader,
599                 List<PrinterInfo> printers) {
600             synchronized (mLock) {
601                 mPrinters.clear();
602                 final int printerCount = printers.size();
603                 for (int i = 0; i < printerCount; i++) {
604                     PrinterInfo printer = printers.get(i);
605                     if (printer.getId().getServiceName().equals(mComponentName)) {
606                         mPrinters.add(printer);
607                     }
608                 }
609                 mFilteredPrinters.clear();
610                 mFilteredPrinters.addAll(mPrinters);
611                 if (!TextUtils.isEmpty(mLastSearchString)) {
612                     getFilter().filter(mLastSearchString);
613                 }
614             }
615             notifyDataSetChanged();
616         }
617 
618         @Override
619         public void onLoaderReset(Loader<List<PrinterInfo>> loader) {
620             synchronized (mLock) {
621                 mPrinters.clear();
622                 mFilteredPrinters.clear();
623                 mLastSearchString = null;
624             }
625             notifyDataSetChanged();
626         }
627     }
628 
629     private static class PrintersLoader extends Loader<List<PrinterInfo>> {
630 
631         private static final String LOG_TAG = "PrintersLoader";
632 
633         private static final boolean DEBUG = false;
634 
635         private final Map<PrinterId, PrinterInfo> mPrinters =
636                 new LinkedHashMap<PrinterId, PrinterInfo>();
637 
638         private PrinterDiscoverySession mDiscoverySession;
639 
640         public PrintersLoader(Context context) {
641             super(context);
642         }
643 
644         @Override
645         public void deliverResult(List<PrinterInfo> printers) {
646             if (isStarted()) {
647                 super.deliverResult(printers);
648             }
649         }
650 
651         @Override
652         protected void onStartLoading() {
653             if (DEBUG) {
654                 Log.i(LOG_TAG, "onStartLoading()");
655             }
656             // The contract is that if we already have a valid,
657             // result the we have to deliver it immediately.
658             if (!mPrinters.isEmpty()) {
659                 deliverResult(new ArrayList<PrinterInfo>(mPrinters.values()));
660             }
661             // We want to start discovery at this point.
662             onForceLoad();
663         }
664 
665         @Override
666         protected void onStopLoading() {
667             if (DEBUG) {
668                 Log.i(LOG_TAG, "onStopLoading()");
669             }
670             onCancelLoad();
671         }
672 
673         @Override
674         protected void onForceLoad() {
675             if (DEBUG) {
676                 Log.i(LOG_TAG, "onForceLoad()");
677             }
678             loadInternal();
679         }
680 
681         @Override
682         protected boolean onCancelLoad() {
683             if (DEBUG) {
684                 Log.i(LOG_TAG, "onCancelLoad()");
685             }
686             return cancelInternal();
687         }
688 
689         @Override
690         protected void onReset() {
691             if (DEBUG) {
692                 Log.i(LOG_TAG, "onReset()");
693             }
694             onStopLoading();
695             mPrinters.clear();
696             if (mDiscoverySession != null) {
697                 mDiscoverySession.destroy();
698                 mDiscoverySession = null;
699             }
700         }
701 
702         @Override
703         protected void onAbandon() {
704             if (DEBUG) {
705                 Log.i(LOG_TAG, "onAbandon()");
706             }
707             onStopLoading();
708         }
709 
710         private boolean cancelInternal() {
711             if (mDiscoverySession != null
712                     && mDiscoverySession.isPrinterDiscoveryStarted()) {
713                 mDiscoverySession.stopPrinterDiscovery();
714                 return true;
715             }
716             return false;
717         }
718 
719         private void loadInternal() {
720             if (mDiscoverySession == null) {
721                 PrintManager printManager = (PrintManager) getContext()
722                         .getSystemService(Context.PRINT_SERVICE);
723                 mDiscoverySession = printManager.createPrinterDiscoverySession();
724                 mDiscoverySession.setOnPrintersChangeListener(new OnPrintersChangeListener() {
725                     @Override
726                     public void onPrintersChanged() {
727                         deliverResult(new ArrayList<PrinterInfo>(
728                                 mDiscoverySession.getPrinters()));
729                     }
730                 });
731             }
732             mDiscoverySession.startPrinterDiscovery(null);
733         }
734     }
735 }
736