OSDN Git Service

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