blob: e4de4b85273bff6361cc14c7f6103c65ab9e953d [file] [log] [blame]
Svetoslav Ganov8c433762013-08-02 14:22:19 -07001/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.printspooler;
18
19import android.app.Notification;
20import android.app.NotificationManager;
21import android.app.PendingIntent;
22import android.content.BroadcastReceiver;
23import android.content.Context;
24import android.content.Intent;
25import android.os.Build;
26import android.os.RemoteException;
27import android.os.ServiceManager;
28import android.os.UserHandle;
29import android.print.IPrintManager;
30import android.print.PrintJobInfo;
31import android.print.PrintManager;
32import android.util.Log;
33
34/**
35 * This class is responsible for updating the print notifications
36 * based on print job state transitions.
37 */
38public class NotificationController {
39 public static final boolean DEBUG = true && Build.IS_DEBUGGABLE;
40
41 public static final String LOG_TAG = "NotificationController";
42
43 private static final String INTENT_ACTION_CANCEL_PRINTJOB = "INTENT_ACTION_CANCEL_PRINTJOB";
44 private static final String INTENT_ACTION_RESTART_PRINTJOB = "INTENT_ACTION_RESTART_PRINTJOB";
45 private static final String INTENT_EXTRA_PRINTJOB_ID = "INTENT_EXTRA_PRINTJOB_ID";
46
47 private final Context mContext;
48 private final NotificationManager mNotificationManager;
49
50 public NotificationController(Context context) {
51 mContext = context;
52 mNotificationManager = (NotificationManager)
53 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
54 }
55
56 public void onPrintJobStateChanged(PrintJobInfo printJob, int oldState) {
57 if (DEBUG) {
58 Log.i(LOG_TAG, "onPrintJobStateChanged() printJobId: " + printJob.getId()
59 + " oldState: " + PrintJobInfo.stateToString(oldState)
60 + " newState:" + PrintJobInfo.stateToString(printJob.getState()));
61 }
62 switch (printJob.getState()) {
63 case PrintJobInfo.STATE_QUEUED: {
64 createQueuingNotificaiton(printJob);
65 } break;
66
67 case PrintJobInfo.STATE_STARTED: {
68 createPrintingNotificaiton(printJob);
69 } break;
70
71 case PrintJobInfo.STATE_FAILED: {
72 createFailedNotificaiton(printJob);
73 } break;
74
75 case PrintJobInfo.STATE_COMPLETED:
76 case PrintJobInfo.STATE_CANCELED: {
77 removeNotification(printJob.getId());
78 } break;
79 }
80 }
81
82 private void createQueuingNotificaiton(PrintJobInfo printJob) {
83 Notification.Builder builder = new Notification.Builder(mContext)
84 // TODO: Use appropriate icon when assets are ready
85 .setSmallIcon(android.R.drawable.ic_secure)
86 .setContentTitle(mContext.getString(R.string.queued_notification_title_template,
87 printJob.getLabel()))
88 // TODO: Use appropriate icon when assets are ready
89 .addAction(android.R.drawable.ic_secure, mContext.getString(R.string.cancel),
90 createCancelIntent(printJob.getId()))
91 .setContentText(printJob.getPrinterId().getPrinterName())
92 .setOngoing(true)
93 .setWhen(System.currentTimeMillis())
94 .setShowWhen(true);
95 mNotificationManager.notify(printJob.getId(), builder.build());
96 }
97
98 private void createPrintingNotificaiton(PrintJobInfo printJob) {
99 Notification.Builder builder = new Notification.Builder(mContext)
100 // TODO: Use appropriate icon when assets are ready
101 .setSmallIcon(android.R.drawable.ic_secure)
102 .setContentTitle(mContext.getString(R.string.printing_notification_title_template,
103 printJob.getLabel()))
104 // TODO: Use appropriate icon when assets are ready
105 .addAction(android.R.drawable.ic_secure, mContext.getString(R.string.cancel),
106 createCancelIntent(printJob.getId()))
107 .setContentText(printJob.getPrinterId().getPrinterName())
108 .setOngoing(true)
109 .setWhen(System.currentTimeMillis())
110 .setShowWhen(true);
111 mNotificationManager.notify(printJob.getId(), builder.build());
112 }
113
114 private void createFailedNotificaiton(PrintJobInfo printJob) {
115 Notification.Builder builder = new Notification.Builder(mContext)
116 // TODO: Use appropriate icon when assets are ready
117 .setSmallIcon(android.R.drawable.ic_secure)
118 .setContentTitle(mContext.getString(R.string.failed_notification_title_template,
119 printJob.getLabel()))
120 // TODO: Use appropriate icon when assets are ready
121 .addAction(android.R.drawable.ic_secure, mContext.getString(R.string.cancel),
122 createCancelIntent(printJob.getId()))
123 // TODO: Use appropriate icon when assets are ready
124 .addAction(android.R.drawable.ic_secure, mContext.getString(R.string.restart),
125 createRestartIntent(printJob.getId()))
126 .setContentText(printJob.getFailureReason())
127 .setOngoing(true)
128 .setWhen(System.currentTimeMillis())
129 .setShowWhen(true);
130 mNotificationManager.notify(printJob.getId(), builder.build());
131 }
132
133 private void removeNotification(int printJobId) {
134 mNotificationManager.cancel(printJobId);
135 }
136
137 private PendingIntent createCancelIntent(int printJobId) {
138 Intent intent = new Intent(mContext, NotificationBroadcastReceiver.class);
139 intent.setAction(INTENT_ACTION_CANCEL_PRINTJOB + "_" + String.valueOf(printJobId));
140 intent.putExtra(INTENT_EXTRA_PRINTJOB_ID, printJobId);
141 return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
142 }
143
144 private PendingIntent createRestartIntent(int printJobId) {
145 Intent intent = new Intent(mContext, NotificationBroadcastReceiver.class);
146 intent.setAction(INTENT_ACTION_RESTART_PRINTJOB + "_" + String.valueOf(printJobId));
147 intent.putExtra(INTENT_EXTRA_PRINTJOB_ID, printJobId);
148 return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
149 }
150
151 public static final class NotificationBroadcastReceiver extends BroadcastReceiver {
152 private static final String LOG_TAG = "NotificationBroadcastReceiver";
153
154 @Override
155 public void onReceive(Context context, Intent intent) {
156 String action = intent.getAction();
157 if (action != null && action.startsWith(INTENT_ACTION_CANCEL_PRINTJOB)) {
158 final int printJobId = intent.getExtras().getInt(INTENT_EXTRA_PRINTJOB_ID);
159 handleCancelPrintJob(context, printJobId);
160 } else if (action != null && action.startsWith(INTENT_ACTION_RESTART_PRINTJOB)) {
161 final int printJobId = intent.getExtras().getInt(INTENT_EXTRA_PRINTJOB_ID);
162 handleRestartPrintJob(context, printJobId);
163 }
164 }
165
166 private void handleCancelPrintJob(final Context context, final int printJobId) {
167 if (DEBUG) {
168 Log.i(LOG_TAG, "handleCancelPrintJob() printJobId:" + printJobId);
169 }
170
171 PrintSpooler printSpooler = PrintSpooler.getInstance(context);
172
173 final PrintJobInfo printJob = printSpooler.getPrintJobInfo(printJobId,
174 PrintManager.APP_ID_ANY);
175
176 if (printJob == null || printJob.getState() == PrintJobInfo.STATE_CANCELED) {
177 return;
178 }
179
180 // Put up a notification that we are trying to cancel.
181 NotificationManager notificationManager = (NotificationManager)
182 context.getSystemService(Context.NOTIFICATION_SERVICE);
183
184 Notification.Builder builder = new Notification.Builder(context)
185 // TODO: Use appropriate icon when assets are ready
186 .setSmallIcon(android.R.drawable.ic_secure)
187 .setContentTitle(context.getString(
188 R.string.cancelling_notification_title_template,
189 printJob.getLabel()))
190 .setContentText(printJob.getPrinterId().getPrinterName())
191 .setOngoing(true)
192 .setWhen(System.currentTimeMillis())
193 .setShowWhen(true);
194
195 notificationManager.notify(printJob.getId(), builder.build());
196
197 // We need to request the cancellation to be done by the print
198 // manager service since it has to communicate with the managing
199 // print service to request the cancellation. Also we need the
200 // system service to be bound to the spooler since canceling a
201 // print job will trigger persistence of current jobs which is
202 // done on another thread and until it finishes the spooler has
203 // to be kept around.
204 IPrintManager printManager = IPrintManager.Stub.asInterface(
205 ServiceManager.getService(Context.PRINT_SERVICE));
206
207 try {
208 printManager.cancelPrintJob(printJobId, PrintManager.APP_ID_ANY,
209 UserHandle.myUserId());
210 } catch (RemoteException re) {
211 Log.i(LOG_TAG, "Error requestion print job cancellation", re);
212 }
213 }
214
215 private void handleRestartPrintJob(final Context context, final int printJobId) {
216 if (DEBUG) {
217 Log.i(LOG_TAG, "handleRestartPrintJob() printJobId:" + printJobId);
218 }
219
220 PrintSpooler printSpooler = PrintSpooler.getInstance(context);
221
222 PrintJobInfo printJob = printSpooler.getPrintJobInfo(printJobId,
223 PrintManager.APP_ID_ANY);
224
225 if (printJob == null || printJob.getState() != PrintJobInfo.STATE_FAILED) {
226 return;
227 }
228
229 // We need to request the restart to be done by the print manager
230 // service since the latter must be bound to the spooler because
231 // restarting a print job will trigger persistence of current jobs
232 // which is done on another thread and until it finishes the spooler has
233 // to be kept around.
234 IPrintManager printManager = IPrintManager.Stub.asInterface(
235 ServiceManager.getService(Context.PRINT_SERVICE));
236
237 try {
238 printManager.restartPrintJob(printJobId, PrintManager.APP_ID_ANY,
239 UserHandle.myUserId());
240 } catch (RemoteException re) {
241 Log.i(LOG_TAG, "Error requestion print job restart", re);
242 }
243 }
244 }
245}