blob: 2ac8c1ca6b57e3c7a051ed07852620270ab590bd [file] [log] [blame]
Chiao Chengd80c4342012-12-03 17:15:58 -08001/*
2 * Copyright (C) 2011 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.contacts.common.vcard;
18
19import android.app.Activity;
20import android.app.Notification;
21import android.app.NotificationManager;
22import android.app.PendingIntent;
23import android.content.ContentUris;
24import android.content.Context;
25import android.content.Intent;
26import android.net.Uri;
27import android.os.Handler;
28import android.os.Message;
Brian Attwell144bec22014-10-16 22:26:23 -070029import android.provider.ContactsContract;
Chiao Chengd80c4342012-12-03 17:15:58 -080030import android.provider.ContactsContract.RawContacts;
Wenyi Wangf5afb232015-12-15 11:09:36 -080031import android.support.v4.app.NotificationCompat;
Chiao Chengd80c4342012-12-03 17:15:58 -080032import android.widget.Toast;
33
34import com.android.contacts.common.R;
35import com.android.vcard.VCardEntry;
36
Elliott Hughes4ab296b2014-10-10 12:00:13 -070037import java.text.NumberFormat;
38
Chiao Chengd80c4342012-12-03 17:15:58 -080039public class NotificationImportExportListener implements VCardImportExportListener,
40 Handler.Callback {
41 /** The tag used by vCard-related notifications. */
42 /* package */ static final String DEFAULT_NOTIFICATION_TAG = "VCardServiceProgress";
43 /**
44 * The tag used by vCard-related failure notifications.
45 * <p>
46 * Use a different tag from {@link #DEFAULT_NOTIFICATION_TAG} so that failures do not get
47 * replaced by other notifications and vice-versa.
48 */
49 /* package */ static final String FAILURE_NOTIFICATION_TAG = "VCardServiceFailure";
50
51 private final NotificationManager mNotificationManager;
52 private final Activity mContext;
53 private final Handler mHandler;
54
55 public NotificationImportExportListener(Activity activity) {
56 mContext = activity;
57 mNotificationManager = (NotificationManager) activity.getSystemService(
58 Context.NOTIFICATION_SERVICE);
59 mHandler = new Handler(this);
60 }
61
62 @Override
63 public boolean handleMessage(Message msg) {
64 String text = (String) msg.obj;
65 Toast.makeText(mContext, text, Toast.LENGTH_LONG).show();
66 return true;
67 }
68
69 @Override
70 public void onImportProcessed(ImportRequest request, int jobId, int sequence) {
71 // Show a notification about the status
72 final String displayName;
73 final String message;
74 if (request.displayName != null) {
75 displayName = request.displayName;
76 message = mContext.getString(R.string.vcard_import_will_start_message, displayName);
77 } else {
78 displayName = mContext.getString(R.string.vcard_unknown_filename);
79 message = mContext.getString(
80 R.string.vcard_import_will_start_message_with_default_name);
81 }
82
83 // We just want to show notification for the first vCard.
84 if (sequence == 0) {
85 // TODO: Ideally we should detect the current status of import/export and
86 // show "started" when we can import right now and show "will start" when
87 // we cannot.
88 mHandler.obtainMessage(0, message).sendToTarget();
89 }
90
91 final Notification notification = constructProgressNotification(mContext,
92 VCardService.TYPE_IMPORT, message, message, jobId, displayName, -1, 0);
93 mNotificationManager.notify(DEFAULT_NOTIFICATION_TAG, jobId, notification);
94 }
95
96 @Override
97 public void onImportParsed(ImportRequest request, int jobId, VCardEntry entry, int currentCount,
98 int totalCount) {
99 if (entry.isIgnorable()) {
100 return;
101 }
102
103 final String totalCountString = String.valueOf(totalCount);
104 final String tickerText =
105 mContext.getString(R.string.progress_notifier_message,
106 String.valueOf(currentCount),
107 totalCountString,
108 entry.getDisplayName());
109 final String description = mContext.getString(R.string.importing_vcard_description,
110 entry.getDisplayName());
111
112 final Notification notification = constructProgressNotification(
113 mContext.getApplicationContext(), VCardService.TYPE_IMPORT, description, tickerText,
114 jobId, request.displayName, totalCount, currentCount);
115 mNotificationManager.notify(DEFAULT_NOTIFICATION_TAG, jobId, notification);
116 }
117
118 @Override
119 public void onImportFinished(ImportRequest request, int jobId, Uri createdUri) {
120 final String description = mContext.getString(R.string.importing_vcard_finished_title,
121 request.displayName);
122 final Intent intent;
123 if (createdUri != null) {
124 final long rawContactId = ContentUris.parseId(createdUri);
125 final Uri contactUri = RawContacts.getContactLookupUri(
126 mContext.getContentResolver(), ContentUris.withAppendedId(
127 RawContacts.CONTENT_URI, rawContactId));
128 intent = new Intent(Intent.ACTION_VIEW, contactUri);
129 } else {
Brian Attwell144bec22014-10-16 22:26:23 -0700130 intent = new Intent(Intent.ACTION_VIEW);
131 intent.setType(ContactsContract.Contacts.CONTENT_TYPE);
Chiao Chengd80c4342012-12-03 17:15:58 -0800132 }
Brian Attwell4a0f7bf2015-02-06 15:14:44 -0800133 intent.setPackage(mContext.getPackageName());
Chiao Chengd80c4342012-12-03 17:15:58 -0800134 final Notification notification =
135 NotificationImportExportListener.constructFinishNotification(mContext,
136 description, null, intent);
137 mNotificationManager.notify(NotificationImportExportListener.DEFAULT_NOTIFICATION_TAG,
138 jobId, notification);
139 }
140
141 @Override
142 public void onImportFailed(ImportRequest request) {
143 // TODO: a little unkind to show Toast in this case, which is shown just a moment.
144 // Ideally we should show some persistent something users can notice more easily.
145 mHandler.obtainMessage(0,
146 mContext.getString(R.string.vcard_import_request_rejected_message)).sendToTarget();
147 }
148
149 @Override
150 public void onImportCanceled(ImportRequest request, int jobId) {
151 final String description = mContext.getString(R.string.importing_vcard_canceled_title,
152 request.displayName);
153 final Notification notification =
154 NotificationImportExportListener.constructCancelNotification(mContext, description);
155 mNotificationManager.notify(NotificationImportExportListener.DEFAULT_NOTIFICATION_TAG,
156 jobId, notification);
157 }
158
159 @Override
160 public void onExportProcessed(ExportRequest request, int jobId) {
Walter Jang7a243012015-07-09 10:19:35 -0700161 final String displayName = ExportVCardActivity.getOpenableUriDisplayName(mContext,
162 request.destUri);
Wenyi Wangbf38d7e2016-02-04 14:08:45 -0800163 final String message = mContext.getString(R.string.contacts_export_will_start_message);
Chiao Chengd80c4342012-12-03 17:15:58 -0800164
165 mHandler.obtainMessage(0, message).sendToTarget();
166 final Notification notification =
167 NotificationImportExportListener.constructProgressNotification(mContext,
168 VCardService.TYPE_EXPORT, message, message, jobId, displayName, -1, 0);
169 mNotificationManager.notify(DEFAULT_NOTIFICATION_TAG, jobId, notification);
170 }
171
172 @Override
173 public void onExportFailed(ExportRequest request) {
174 mHandler.obtainMessage(0,
175 mContext.getString(R.string.vcard_export_request_rejected_message)).sendToTarget();
176 }
177
178 @Override
179 public void onCancelRequest(CancelRequest request, int type) {
180 final String description = type == VCardService.TYPE_IMPORT ?
181 mContext.getString(R.string.importing_vcard_canceled_title, request.displayName) :
182 mContext.getString(R.string.exporting_vcard_canceled_title, request.displayName);
183 final Notification notification = constructCancelNotification(mContext, description);
184 mNotificationManager.notify(DEFAULT_NOTIFICATION_TAG, request.jobId, notification);
185 }
186
187 /**
188 * Constructs a {@link Notification} showing the current status of import/export.
189 * Users can cancel the process with the Notification.
190 *
191 * @param context
192 * @param type import/export
193 * @param description Content of the Notification.
194 * @param tickerText
195 * @param jobId
196 * @param displayName Name to be shown to the Notification (e.g. "finished importing XXXX").
197 * Typycally a file name.
198 * @param totalCount The number of vCard entries to be imported. Used to show progress bar.
199 * -1 lets the system show the progress bar with "indeterminate" state.
200 * @param currentCount The index of current vCard. Used to show progress bar.
201 */
202 /* package */ static Notification constructProgressNotification(
203 Context context, int type, String description, String tickerText,
204 int jobId, String displayName, int totalCount, int currentCount) {
205 // Note: We cannot use extra values here (like setIntExtra()), as PendingIntent doesn't
206 // preserve them across multiple Notifications. PendingIntent preserves the first extras
207 // (when flag is not set), or update them when PendingIntent#getActivity() is called
208 // (See PendingIntent#FLAG_UPDATE_CURRENT). In either case, we cannot preserve extras as we
209 // expect (for each vCard import/export request).
210 //
211 // We use query parameter in Uri instead.
212 // Scheme and Authority is arbitorary, assuming CancelActivity never refers them.
213 final Intent intent = new Intent(context, CancelActivity.class);
214 final Uri uri = (new Uri.Builder())
215 .scheme("invalidscheme")
216 .authority("invalidauthority")
217 .appendQueryParameter(CancelActivity.JOB_ID, String.valueOf(jobId))
218 .appendQueryParameter(CancelActivity.DISPLAY_NAME, displayName)
219 .appendQueryParameter(CancelActivity.TYPE, String.valueOf(type)).build();
220 intent.setData(uri);
221
Wenyi Wangf5afb232015-12-15 11:09:36 -0800222 final NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
Chiao Chengd80c4342012-12-03 17:15:58 -0800223 builder.setOngoing(true)
224 .setProgress(totalCount, currentCount, totalCount == - 1)
225 .setTicker(tickerText)
226 .setContentTitle(description)
Brian Attwelld829b5f2014-10-16 21:34:02 -0700227 .setColor(context.getResources().getColor(R.color.dialtacts_theme_color))
Chiao Chengd80c4342012-12-03 17:15:58 -0800228 .setSmallIcon(type == VCardService.TYPE_IMPORT
229 ? android.R.drawable.stat_sys_download
230 : android.R.drawable.stat_sys_upload)
231 .setContentIntent(PendingIntent.getActivity(context, 0, intent, 0));
232 if (totalCount > 0) {
Elliott Hughes4ab296b2014-10-10 12:00:13 -0700233 String percentage =
234 NumberFormat.getPercentInstance().format((double) currentCount / totalCount);
235 builder.setContentText(percentage);
Chiao Chengd80c4342012-12-03 17:15:58 -0800236 }
237 return builder.getNotification();
238 }
239
240 /**
241 * Constructs a Notification telling users the process is canceled.
242 *
243 * @param context
244 * @param description Content of the Notification
245 */
246 /* package */ static Notification constructCancelNotification(
247 Context context, String description) {
Wenyi Wangf5afb232015-12-15 11:09:36 -0800248 return new NotificationCompat.Builder(context)
Chiao Chengd80c4342012-12-03 17:15:58 -0800249 .setAutoCancel(true)
250 .setSmallIcon(android.R.drawable.stat_notify_error)
Brian Attwelld829b5f2014-10-16 21:34:02 -0700251 .setColor(context.getResources().getColor(R.color.dialtacts_theme_color))
Chiao Chengd80c4342012-12-03 17:15:58 -0800252 .setContentTitle(description)
253 .setContentText(description)
Brian Attwell4a0f7bf2015-02-06 15:14:44 -0800254 // Launch an intent that won't resolve to anything. Restrict the intent to this
255 // app to make sure that no other app can steal this pending-intent b/19296918.
256 .setContentIntent(PendingIntent
257 .getActivity(context, 0, new Intent(context.getPackageName(), null), 0))
Chiao Chengd80c4342012-12-03 17:15:58 -0800258 .getNotification();
259 }
260
261 /**
262 * Constructs a Notification telling users the process is finished.
263 *
264 * @param context
265 * @param description Content of the Notification
266 * @param intent Intent to be launched when the Notification is clicked. Can be null.
267 */
268 /* package */ static Notification constructFinishNotification(
269 Context context, String title, String description, Intent intent) {
Wenyi Wangbf38d7e2016-02-04 14:08:45 -0800270 return constructFinishNotificationWithFlags(context, title, description, intent, 0);
271 }
272
273 /**
274 * @param flags use FLAG_ACTIVITY_NEW_TASK to set it as new task, to get rid of cached files.
275 */
276 /* package */ static Notification constructFinishNotificationWithFlags(
277 Context context, String title, String description, Intent intent, int flags) {
Wenyi Wangf5afb232015-12-15 11:09:36 -0800278 return new NotificationCompat.Builder(context)
Chiao Chengd80c4342012-12-03 17:15:58 -0800279 .setAutoCancel(true)
Brian Attwelld829b5f2014-10-16 21:34:02 -0700280 .setColor(context.getResources().getColor(R.color.dialtacts_theme_color))
guanxiongliu81163052016-04-16 19:51:27 -0700281 .setSmallIcon(R.drawable.ic_check_mark)
Chiao Chengd80c4342012-12-03 17:15:58 -0800282 .setContentTitle(title)
283 .setContentText(description)
Brian Attwell4a0f7bf2015-02-06 15:14:44 -0800284 // If no intent provided, include an intent that won't resolve to anything.
285 // Restrict the intent to this app to make sure that no other app can steal this
286 // pending-intent b/19296918.
Chiao Chengd80c4342012-12-03 17:15:58 -0800287 .setContentIntent(PendingIntent.getActivity(context, 0,
Wenyi Wangbf38d7e2016-02-04 14:08:45 -0800288 (intent != null ? intent : new Intent(context.getPackageName(), null)),
289 flags))
Chiao Chengd80c4342012-12-03 17:15:58 -0800290 .getNotification();
291 }
292
293 /**
294 * Constructs a Notification telling the vCard import has failed.
295 *
296 * @param context
297 * @param reason The reason why the import has failed. Shown in description field.
298 */
299 /* package */ static Notification constructImportFailureNotification(
300 Context context, String reason) {
Wenyi Wangf5afb232015-12-15 11:09:36 -0800301 return new NotificationCompat.Builder(context)
Chiao Chengd80c4342012-12-03 17:15:58 -0800302 .setAutoCancel(true)
Brian Attwelld829b5f2014-10-16 21:34:02 -0700303 .setColor(context.getResources().getColor(R.color.dialtacts_theme_color))
Chiao Chengd80c4342012-12-03 17:15:58 -0800304 .setSmallIcon(android.R.drawable.stat_notify_error)
305 .setContentTitle(context.getString(R.string.vcard_import_failed))
306 .setContentText(reason)
Brian Attwell4a0f7bf2015-02-06 15:14:44 -0800307 // Launch an intent that won't resolve to anything. Restrict the intent to this
308 // app to make sure that no other app can steal this pending-intent b/19296918.
309 .setContentIntent(PendingIntent
310 .getActivity(context, 0, new Intent(context.getPackageName(), null), 0))
Chiao Chengd80c4342012-12-03 17:15:58 -0800311 .getNotification();
312 }
313
314 @Override
315 public void onComplete() {
316 mContext.finish();
317 }
318}