OSDN Git Service

b7c74da9bfe17abdecbc0c5500873ebc757a1592
[android-x86/packages-apps-Settings.git] / src / com / android / settings / print / PrintSettingsFragment.java
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.ActivityManager;
20 import android.app.LoaderManager.LoaderCallbacks;
21 import android.content.AsyncTaskLoader;
22 import android.content.ComponentName;
23 import android.content.ContentResolver;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.Loader;
27 import android.content.pm.PackageManager;
28 import android.content.pm.ResolveInfo;
29 import android.database.ContentObserver;
30 import android.net.Uri;
31 import android.os.Bundle;
32 import android.os.Handler;
33 import android.os.Message;
34 import android.os.UserHandle;
35 import android.os.UserManager;
36 import android.os.Process;
37 import android.preference.Preference;
38 import android.preference.PreferenceCategory;
39 import android.preference.PreferenceScreen;
40 import android.print.PrintJob;
41 import android.print.PrintJobId;
42 import android.print.PrintJobInfo;
43 import android.print.PrintManager;
44 import android.print.PrintManager.PrintJobStateChangeListener;
45 import android.printservice.PrintServiceInfo;
46 import android.provider.SearchIndexableResource;
47 import android.provider.Settings;
48 import android.text.TextUtils;
49 import android.text.format.DateUtils;
50 import android.util.Log;
51 import android.view.Menu;
52 import android.view.MenuInflater;
53 import android.view.MenuItem;
54 import android.view.View;
55 import android.view.ViewGroup;
56 import android.widget.AdapterView;
57 import android.widget.TextView;
58
59 import com.android.internal.content.PackageMonitor;
60 import com.android.settings.UserSpinnerAdapter;
61 import com.android.settings.UserSpinnerAdapter.UserDetails;
62 import com.android.settings.DialogCreatable;
63 import com.android.settings.R;
64 import com.android.settings.SettingsPreferenceFragment;
65 import com.android.settings.Utils;
66 import com.android.settings.search.BaseSearchIndexProvider;
67 import com.android.settings.search.Indexable;
68 import com.android.settings.search.SearchIndexableRaw;
69
70 import java.text.DateFormat;
71 import java.util.ArrayList;
72 import java.util.List;
73
74 import android.widget.AdapterView.OnItemSelectedListener;
75 import android.widget.Spinner;
76
77 /**
78  * Fragment with the top level print settings.
79  */
80 public class PrintSettingsFragment extends SettingsPreferenceFragment
81         implements DialogCreatable, Indexable, OnItemSelectedListener {
82
83     private static final int LOADER_ID_PRINT_JOBS_LOADER = 1;
84
85     private static final String PRINT_JOBS_CATEGORY = "print_jobs_category";
86     private static final String PRINT_SERVICES_CATEGORY = "print_services_category";
87
88     // Extras passed to sub-fragments.
89     static final String EXTRA_PREFERENCE_KEY = "EXTRA_PREFERENCE_KEY";
90     static final String EXTRA_CHECKED = "EXTRA_CHECKED";
91     static final String EXTRA_TITLE = "EXTRA_TITLE";
92     static final String EXTRA_ENABLE_WARNING_TITLE = "EXTRA_ENABLE_WARNING_TITLE";
93     static final String EXTRA_ENABLE_WARNING_MESSAGE = "EXTRA_ENABLE_WARNING_MESSAGE";
94     static final String EXTRA_SETTINGS_TITLE = "EXTRA_SETTINGS_TITLE";
95     static final String EXTRA_SETTINGS_COMPONENT_NAME = "EXTRA_SETTINGS_COMPONENT_NAME";
96     static final String EXTRA_ADD_PRINTERS_TITLE = "EXTRA_ADD_PRINTERS_TITLE";
97     static final String EXTRA_ADD_PRINTERS_COMPONENT_NAME = "EXTRA_ADD_PRINTERS_COMPONENT_NAME";
98     static final String EXTRA_SERVICE_COMPONENT_NAME = "EXTRA_SERVICE_COMPONENT_NAME";
99
100     static final String EXTRA_PRINT_JOB_ID = "EXTRA_PRINT_JOB_ID";
101
102     private static final String EXTRA_PRINT_SERVICE_COMPONENT_NAME =
103             "EXTRA_PRINT_SERVICE_COMPONENT_NAME";
104
105     private final PackageMonitor mSettingsPackageMonitor = new SettingsPackageMonitor();
106
107     private final Handler mHandler = new Handler() {
108         @Override
109         public void dispatchMessage(Message msg) {
110             updateServicesPreferences();
111         }
112     };
113
114     private final SettingsContentObserver mSettingsContentObserver =
115             new SettingsContentObserver(mHandler) {
116         @Override
117         public void onChange(boolean selfChange, Uri uri) {
118             updateServicesPreferences();
119         }
120     };
121
122     private PreferenceCategory mActivePrintJobsCategory;
123     private PreferenceCategory mPrintServicesCategory;
124
125     private PrintJobsController mPrintJobsController;
126     private UserSpinnerAdapter mProfileSpinnerAdapter;
127     private Spinner mSpinner;
128
129     @Override
130     public void onCreate(Bundle icicle) {
131         super.onCreate(icicle);
132         addPreferencesFromResource(R.xml.print_settings);
133
134         mActivePrintJobsCategory = (PreferenceCategory) findPreference(
135                 PRINT_JOBS_CATEGORY);
136         mPrintServicesCategory = (PreferenceCategory) findPreference(
137                 PRINT_SERVICES_CATEGORY);
138         getPreferenceScreen().removePreference(mActivePrintJobsCategory);
139
140         mPrintJobsController = new PrintJobsController();
141         getActivity().getLoaderManager().initLoader(LOADER_ID_PRINT_JOBS_LOADER,
142                 null, mPrintJobsController);
143     }
144
145     @Override
146     public void onResume() {
147         super.onResume();
148         mSettingsPackageMonitor.register(getActivity(), getActivity().getMainLooper(), false);
149         mSettingsContentObserver.register(getContentResolver());
150         updateServicesPreferences();
151         setHasOptionsMenu(true);
152         startSubSettingsIfNeeded();
153     }
154
155     @Override
156     public void onPause() {
157         mSettingsPackageMonitor.unregister();
158         mSettingsContentObserver.unregister(getContentResolver());
159         super.onPause();
160     }
161
162     @Override
163     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
164         super.onCreateOptionsMenu(menu, inflater);
165         String searchUri = Settings.Secure.getString(getContentResolver(),
166                 Settings.Secure.PRINT_SERVICE_SEARCH_URI);
167         if (!TextUtils.isEmpty(searchUri)) {
168             MenuItem menuItem = menu.add(R.string.print_menu_item_add_service);
169             menuItem.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_NEVER);
170             menuItem.setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse(searchUri)));
171         }
172     }
173
174     @Override
175     public void onViewCreated(View view, Bundle savedInstanceState) {
176         super.onViewCreated(view, savedInstanceState);
177         ViewGroup contentRoot = (ViewGroup) getListView().getParent();
178         View emptyView = getActivity().getLayoutInflater().inflate(
179                 R.layout.empty_print_state, contentRoot, false);
180         TextView textView = (TextView) emptyView.findViewById(R.id.message);
181         textView.setText(R.string.print_no_services_installed);
182         contentRoot.addView(emptyView);
183         getListView().setEmptyView(emptyView);
184
185         final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
186         mProfileSpinnerAdapter = Utils.createUserSpinnerAdapter(um, getActivity());
187         if (mProfileSpinnerAdapter != null) {
188             mSpinner = (Spinner) getActivity().getLayoutInflater().inflate(
189                     R.layout.spinner_view, null);
190             mSpinner.setAdapter(mProfileSpinnerAdapter);
191             mSpinner.setOnItemSelectedListener(this);
192             setPinnedHeaderView(mSpinner);
193         }
194     }
195
196     private void updateServicesPreferences() {
197         if (getPreferenceScreen().findPreference(PRINT_SERVICES_CATEGORY) == null) {
198             getPreferenceScreen().addPreference(mPrintServicesCategory);
199         } else {
200             // Since services category is auto generated we have to do a pass
201             // to generate it since services can come and go.
202             mPrintServicesCategory.removeAll();
203         }
204
205         List<ComponentName> enabledServices = PrintSettingsUtils
206                 .readEnabledPrintServices(getActivity());
207
208         List<ResolveInfo> installedServices = getActivity().getPackageManager()
209                 .queryIntentServices(
210                         new Intent(android.printservice.PrintService.SERVICE_INTERFACE),
211                         PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
212
213         final int installedServiceCount = installedServices.size();
214         for (int i = 0; i < installedServiceCount; i++) {
215             ResolveInfo installedService = installedServices.get(i);
216
217             PreferenceScreen preference = getPreferenceManager().createPreferenceScreen(
218                     getActivity());
219
220             String title = installedService.loadLabel(getPackageManager()).toString();
221             preference.setTitle(title);
222
223             ComponentName componentName = new ComponentName(
224                     installedService.serviceInfo.packageName,
225                     installedService.serviceInfo.name);
226             preference.setKey(componentName.flattenToString());
227
228             preference.setOrder(i);
229             preference.setFragment(PrintServiceSettingsFragment.class.getName());
230             preference.setPersistent(false);
231
232             final boolean serviceEnabled = enabledServices.contains(componentName);
233             if (serviceEnabled) {
234                 preference.setSummary(getString(R.string.print_feature_state_on));
235             } else {
236                 preference.setSummary(getString(R.string.print_feature_state_off));
237             }
238
239             Bundle extras = preference.getExtras();
240             extras.putString(EXTRA_PREFERENCE_KEY, preference.getKey());
241             extras.putBoolean(EXTRA_CHECKED, serviceEnabled);
242             extras.putString(EXTRA_TITLE, title);
243
244             PrintServiceInfo printServiceInfo = PrintServiceInfo.create(
245                     installedService, getActivity());
246
247             CharSequence applicationLabel = installedService.loadLabel(getPackageManager());
248
249             extras.putString(EXTRA_ENABLE_WARNING_TITLE, getString(
250                     R.string.print_service_security_warning_title, applicationLabel));
251             extras.putString(EXTRA_ENABLE_WARNING_MESSAGE, getString(
252                     R.string.print_service_security_warning_summary, applicationLabel));
253
254             String settingsClassName = printServiceInfo.getSettingsActivityName();
255             if (!TextUtils.isEmpty(settingsClassName)) {
256                 extras.putString(EXTRA_SETTINGS_TITLE,
257                         getString(R.string.print_menu_item_settings));
258                 extras.putString(EXTRA_SETTINGS_COMPONENT_NAME,
259                         new ComponentName(installedService.serviceInfo.packageName,
260                                 settingsClassName).flattenToString());
261             }
262
263             String addPrinterClassName = printServiceInfo.getAddPrintersActivityName();
264             if (!TextUtils.isEmpty(addPrinterClassName)) {
265                 extras.putString(EXTRA_ADD_PRINTERS_TITLE,
266                         getString(R.string.print_menu_item_add_printers));
267                 extras.putString(EXTRA_ADD_PRINTERS_COMPONENT_NAME,
268                         new ComponentName(installedService.serviceInfo.packageName,
269                                 addPrinterClassName).flattenToString());
270             }
271
272             extras.putString(EXTRA_SERVICE_COMPONENT_NAME, componentName.flattenToString());
273
274             mPrintServicesCategory.addPreference(preference);
275         }
276
277         if (mPrintServicesCategory.getPreferenceCount() == 0) {
278             getPreferenceScreen().removePreference(mPrintServicesCategory);
279         }
280     }
281
282     private void startSubSettingsIfNeeded() {
283         if (getArguments() == null) {
284             return;
285         }
286         String componentName = getArguments().getString(EXTRA_PRINT_SERVICE_COMPONENT_NAME);
287         if (componentName != null) {
288             getArguments().remove(EXTRA_PRINT_SERVICE_COMPONENT_NAME);
289             Preference prereference = findPreference(componentName);
290             if (prereference != null) {
291                 prereference.performClick(getPreferenceScreen());
292             }
293         }
294     }
295
296     @Override
297     public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
298         UserHandle selectedUser = mProfileSpinnerAdapter.getUserHandle(position);
299         if (selectedUser.getIdentifier() != UserHandle.myUserId()) {
300             Intent intent = new Intent(Settings.ACTION_PRINT_SETTINGS);
301             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
302             intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
303             getActivity().startActivityAsUser(intent, selectedUser);
304             // Go back to default selection, which is the first one
305             mSpinner.setSelection(0);
306         }
307     }
308
309     @Override
310     public void onNothingSelected(AdapterView<?> parent) {
311         // Nothing to do
312     }
313
314     private class SettingsPackageMonitor extends PackageMonitor {
315         @Override
316         public void onPackageAdded(String packageName, int uid) {
317             mHandler.obtainMessage().sendToTarget();
318         }
319
320         @Override
321         public void onPackageAppeared(String packageName, int reason) {
322             mHandler.obtainMessage().sendToTarget();
323         }
324
325         @Override
326         public void onPackageDisappeared(String packageName, int reason) {
327             mHandler.obtainMessage().sendToTarget();
328         }
329
330         @Override
331         public void onPackageRemoved(String packageName, int uid) {
332             mHandler.obtainMessage().sendToTarget();
333         }
334     }
335
336     private static abstract class SettingsContentObserver extends ContentObserver {
337
338         public SettingsContentObserver(Handler handler) {
339             super(handler);
340         }
341
342         public void register(ContentResolver contentResolver) {
343             contentResolver.registerContentObserver(Settings.Secure.getUriFor(
344                     Settings.Secure.ENABLED_PRINT_SERVICES), false, this);
345         }
346
347         public void unregister(ContentResolver contentResolver) {
348             contentResolver.unregisterContentObserver(this);
349         }
350
351         @Override
352         public abstract void onChange(boolean selfChange, Uri uri);
353     }
354
355     private final class PrintJobsController implements LoaderCallbacks<List<PrintJobInfo>> {
356
357         @Override
358         public Loader<List<PrintJobInfo>> onCreateLoader(int id, Bundle args) {
359             if (id == LOADER_ID_PRINT_JOBS_LOADER) {
360                 return new PrintJobsLoader(getActivity());
361             }
362             return null;
363         }
364
365         @Override
366         public void onLoadFinished(Loader<List<PrintJobInfo>> loader,
367                 List<PrintJobInfo> printJobs) {
368             if (printJobs == null || printJobs.isEmpty()) {
369                 getPreferenceScreen().removePreference(mActivePrintJobsCategory);
370             } else {
371                 if (getPreferenceScreen().findPreference(PRINT_JOBS_CATEGORY) == null) {
372                     getPreferenceScreen().addPreference(mActivePrintJobsCategory);
373                 }
374
375                 mActivePrintJobsCategory.removeAll();
376
377                 final int printJobCount = printJobs.size();
378                 for (int i = 0; i < printJobCount; i++) {
379                     PrintJobInfo printJob = printJobs.get(i);
380
381                     PreferenceScreen preference = getPreferenceManager()
382                             .createPreferenceScreen(getActivity());
383
384                     preference.setPersistent(false);
385                     preference.setFragment(PrintJobSettingsFragment.class.getName());
386                     preference.setKey(printJob.getId().flattenToString());
387
388                     switch (printJob.getState()) {
389                         case PrintJobInfo.STATE_QUEUED:
390                         case PrintJobInfo.STATE_STARTED: {
391                             if (!printJob.isCancelling()) {
392                                 preference.setTitle(getString(
393                                         R.string.print_printing_state_title_template,
394                                         printJob.getLabel()));
395                             } else {
396                                 preference.setTitle(getString(
397                                         R.string.print_cancelling_state_title_template,
398                                         printJob.getLabel()));
399                             }
400                         } break;
401
402                         case PrintJobInfo.STATE_FAILED: {
403                             preference.setTitle(getString(
404                                     R.string.print_failed_state_title_template,
405                                     printJob.getLabel()));
406                         } break;
407
408                         case PrintJobInfo.STATE_BLOCKED: {
409                             if (!printJob.isCancelling()) {
410                                 preference.setTitle(getString(
411                                         R.string.print_blocked_state_title_template,
412                                         printJob.getLabel()));
413                             } else {
414                                 preference.setTitle(getString(
415                                         R.string.print_cancelling_state_title_template,
416                                         printJob.getLabel()));
417                             }
418                         } break;
419                     }
420
421                     preference.setSummary(getString(R.string.print_job_summary,
422                             printJob.getPrinterName(), DateUtils.formatSameDayTime(
423                                     printJob.getCreationTime(), printJob.getCreationTime(),
424                                     DateFormat.SHORT, DateFormat.SHORT)));
425
426                     switch (printJob.getState()) {
427                         case PrintJobInfo.STATE_QUEUED:
428                         case PrintJobInfo.STATE_STARTED: {
429                             preference.setIcon(R.drawable.ic_print);
430                         } break;
431
432                         case PrintJobInfo.STATE_FAILED:
433                         case PrintJobInfo.STATE_BLOCKED: {
434                             preference.setIcon(R.drawable.ic_print_error);
435                         } break;
436                     }
437
438                     Bundle extras = preference.getExtras();
439                     extras.putString(EXTRA_PRINT_JOB_ID, printJob.getId().flattenToString());
440
441                     mActivePrintJobsCategory.addPreference(preference);
442                 }
443             }
444         }
445
446         @Override
447         public void onLoaderReset(Loader<List<PrintJobInfo>> loader) {
448             getPreferenceScreen().removePreference(mActivePrintJobsCategory);
449         }
450     }
451
452     private static final class PrintJobsLoader extends AsyncTaskLoader<List<PrintJobInfo>> {
453
454         private static final String LOG_TAG = "PrintJobsLoader";
455
456         private static final boolean DEBUG = false;
457
458         private List<PrintJobInfo> mPrintJobs = new ArrayList<PrintJobInfo>();
459
460         private final PrintManager mPrintManager;
461
462         private PrintJobStateChangeListener mPrintJobStateChangeListener;
463
464         public PrintJobsLoader(Context context) {
465             super(context);
466             mPrintManager = ((PrintManager) context.getSystemService(
467                     Context.PRINT_SERVICE)).getGlobalPrintManagerForUser(
468                         ActivityManager.getCurrentUser());
469         }
470
471         @Override
472         public void deliverResult(List<PrintJobInfo> printJobs) {
473             if (isStarted()) {
474                 super.deliverResult(printJobs);
475             }
476         }
477
478         @Override
479         protected void onStartLoading() {
480             if (DEBUG) {
481                 Log.i(LOG_TAG, "onStartLoading()");
482             }
483             // If we already have a result, deliver it immediately.
484             if (!mPrintJobs.isEmpty()) {
485                 deliverResult(new ArrayList<PrintJobInfo>(mPrintJobs));
486             }
487             // Start watching for changes.
488             if (mPrintJobStateChangeListener == null) {
489                 mPrintJobStateChangeListener = new PrintJobStateChangeListener() {
490                     @Override
491                     public void onPrintJobStateChanged(PrintJobId printJobId) {
492                         onForceLoad();
493                     }
494                 };
495                 mPrintManager.addPrintJobStateChangeListener(
496                         mPrintJobStateChangeListener);
497             }
498             // If the data changed or we have no data - load it now.
499             if (mPrintJobs.isEmpty()) {
500                 onForceLoad();
501             }
502         }
503
504         @Override
505         protected void onStopLoading() {
506             if (DEBUG) {
507                 Log.i(LOG_TAG, "onStopLoading()");
508             }
509             // Cancel the load in progress if possible.
510             onCancelLoad();
511         }
512
513         @Override
514         protected void onReset() {
515             if (DEBUG) {
516                 Log.i(LOG_TAG, "onReset()");
517             }
518             // Stop loading.
519             onStopLoading();
520             // Clear the cached result.
521             mPrintJobs.clear();
522             // Stop watching for changes.
523             if (mPrintJobStateChangeListener != null) {
524                 mPrintManager.removePrintJobStateChangeListener(
525                         mPrintJobStateChangeListener);
526                 mPrintJobStateChangeListener = null;
527             }
528         }
529
530         @Override
531         public List<PrintJobInfo> loadInBackground() {
532             List<PrintJobInfo> printJobInfos = null;
533             List<PrintJob> printJobs = mPrintManager.getPrintJobs();
534             final int printJobCount = printJobs.size();
535             for (int i = 0; i < printJobCount; i++) {
536                 PrintJobInfo printJob = printJobs.get(i).getInfo();
537                 if (shouldShowToUser(printJob)) {
538                     if (printJobInfos == null) {
539                         printJobInfos = new ArrayList<PrintJobInfo>();
540                     }
541                     printJobInfos.add(printJob);
542                 }
543             }
544             return printJobInfos;
545         }
546
547         private static boolean shouldShowToUser(PrintJobInfo printJob) {
548             switch (printJob.getState()) {
549                 case PrintJobInfo.STATE_QUEUED:
550                 case PrintJobInfo.STATE_STARTED:
551                 case PrintJobInfo.STATE_BLOCKED:
552                 case PrintJobInfo.STATE_FAILED: {
553                     return true;
554                 }
555             }
556             return false;
557         }
558     }
559
560     public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
561             new BaseSearchIndexProvider() {
562         @Override
563         public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
564             List<SearchIndexableRaw> indexables = new ArrayList<SearchIndexableRaw>();
565
566             PackageManager packageManager = context.getPackageManager();
567             PrintManager printManager = (PrintManager) context.getSystemService(
568                     Context.PRINT_SERVICE);
569
570             String screenTitle = context.getResources().getString(R.string.print_settings);
571             SearchIndexableRaw data = new SearchIndexableRaw(context);
572             data.title = screenTitle;
573             data.screenTitle = screenTitle;
574             indexables.add(data);
575
576             // Indexing all services, regardless if enabled.
577             List<PrintServiceInfo> services = printManager.getInstalledPrintServices();
578             final int serviceCount = services.size();
579             for (int i = 0; i < serviceCount; i++) {
580                 PrintServiceInfo service = services.get(i);
581
582                 ComponentName componentName = new ComponentName(
583                         service.getResolveInfo().serviceInfo.packageName,
584                         service.getResolveInfo().serviceInfo.name);
585
586                 data = new SearchIndexableRaw(context);
587                 data.key = componentName.flattenToString();
588                 data.title = service.getResolveInfo().loadLabel(packageManager).toString();
589                 data.summaryOn = context.getString(R.string.print_feature_state_on);
590                 data.summaryOff = context.getString(R.string.print_feature_state_off);
591                 data.screenTitle = screenTitle;
592                 indexables.add(data);
593             }
594
595             return indexables;
596         }
597
598         @Override
599         public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
600                 boolean enabled) {
601             List<SearchIndexableResource> indexables = new ArrayList<SearchIndexableResource>();
602             SearchIndexableResource indexable = new SearchIndexableResource(context);
603             indexable.xmlResId = R.xml.print_settings;
604             indexables.add(indexable);
605             return indexables;
606         }
607     };
608 }