Home | History | Annotate | Download | only in vcard
      1 /*
      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 
     17 package com.android.contacts.vcard;
     18 
     19 import com.android.contacts.R;
     20 import com.android.vcard.VCardEntry;
     21 
     22 import android.app.Activity;
     23 import android.app.Notification;
     24 import android.app.NotificationManager;
     25 import android.app.PendingIntent;
     26 import android.content.ContentUris;
     27 import android.content.Context;
     28 import android.content.Intent;
     29 import android.net.Uri;
     30 import android.os.Handler;
     31 import android.os.Message;
     32 import android.provider.ContactsContract.RawContacts;
     33 import android.widget.RemoteViews;
     34 import android.widget.Toast;
     35 
     36 public class NotificationImportExportListener implements VCardImportExportListener,
     37         Handler.Callback {
     38     /** The tag used by vCard-related notifications. */
     39     /* package */ static final String DEFAULT_NOTIFICATION_TAG = "VCardServiceProgress";
     40     /**
     41      * The tag used by vCard-related failure notifications.
     42      * <p>
     43      * Use a different tag from {@link #DEFAULT_NOTIFICATION_TAG} so that failures do not get
     44      * replaced by other notifications and vice-versa.
     45      */
     46     /* package */ static final String FAILURE_NOTIFICATION_TAG = "VCardServiceFailure";
     47 
     48     private final NotificationManager mNotificationManager;
     49     private final Activity mContext;
     50     private final Handler mHandler;
     51 
     52     public NotificationImportExportListener(Activity activity) {
     53         mContext = activity;
     54         mNotificationManager = (NotificationManager) activity.getSystemService(
     55                 Context.NOTIFICATION_SERVICE);
     56         mHandler = new Handler(this);
     57     }
     58 
     59     @Override
     60     public boolean handleMessage(Message msg) {
     61         String text = (String) msg.obj;
     62         Toast.makeText(mContext, text, Toast.LENGTH_LONG).show();
     63         return true;
     64     }
     65 
     66     @Override
     67     public void onImportProcessed(ImportRequest request, int jobId, int sequence) {
     68         // Show a notification about the status
     69         final String displayName;
     70         final String message;
     71         if (request.displayName != null) {
     72             displayName = request.displayName;
     73             message = mContext.getString(R.string.vcard_import_will_start_message, displayName);
     74         } else {
     75             displayName = mContext.getString(R.string.vcard_unknown_filename);
     76             message = mContext.getString(
     77                     R.string.vcard_import_will_start_message_with_default_name);
     78         }
     79 
     80         // We just want to show notification for the first vCard.
     81         if (sequence == 0) {
     82             // TODO: Ideally we should detect the current status of import/export and
     83             // show "started" when we can import right now and show "will start" when
     84             // we cannot.
     85             mHandler.obtainMessage(0, message).sendToTarget();
     86         }
     87 
     88         final Notification notification = constructProgressNotification(mContext,
     89                 VCardService.TYPE_IMPORT, message, message, jobId, displayName, -1, 0);
     90         mNotificationManager.notify(DEFAULT_NOTIFICATION_TAG, jobId, notification);
     91     }
     92 
     93     @Override
     94     public void onImportParsed(ImportRequest request, int jobId, VCardEntry entry, int currentCount,
     95             int totalCount) {
     96         if (entry.isIgnorable()) {
     97             return;
     98         }
     99 
    100         final String totalCountString = String.valueOf(totalCount);
    101         final String tickerText =
    102                 mContext.getString(R.string.progress_notifier_message,
    103                         String.valueOf(currentCount),
    104                         totalCountString,
    105                         entry.getDisplayName());
    106         final String description = mContext.getString(R.string.importing_vcard_description,
    107                 entry.getDisplayName());
    108 
    109         final Notification notification = constructProgressNotification(
    110                 mContext.getApplicationContext(), VCardService.TYPE_IMPORT, description, tickerText,
    111                 jobId, request.displayName, totalCount, currentCount);
    112         mNotificationManager.notify(DEFAULT_NOTIFICATION_TAG, jobId, notification);
    113     }
    114 
    115     @Override
    116     public void onImportFinished(ImportRequest request, int jobId, Uri createdUri) {
    117         final String description = mContext.getString(R.string.importing_vcard_finished_title,
    118                 request.displayName);
    119         final Intent intent;
    120         if (createdUri != null) {
    121             final long rawContactId = ContentUris.parseId(createdUri);
    122             final Uri contactUri = RawContacts.getContactLookupUri(
    123                     mContext.getContentResolver(), ContentUris.withAppendedId(
    124                             RawContacts.CONTENT_URI, rawContactId));
    125             intent = new Intent(Intent.ACTION_VIEW, contactUri);
    126         } else {
    127             intent = null;
    128         }
    129         final Notification notification =
    130                 NotificationImportExportListener.constructFinishNotification(mContext,
    131                 description, null, intent);
    132         mNotificationManager.notify(NotificationImportExportListener.DEFAULT_NOTIFICATION_TAG,
    133                 jobId, notification);
    134     }
    135 
    136     @Override
    137     public void onImportFailed(ImportRequest request) {
    138         // TODO: a little unkind to show Toast in this case, which is shown just a moment.
    139         // Ideally we should show some persistent something users can notice more easily.
    140         mHandler.obtainMessage(0,
    141                 mContext.getString(R.string.vcard_import_request_rejected_message)).sendToTarget();
    142     }
    143 
    144     @Override
    145     public void onImportCanceled(ImportRequest request, int jobId) {
    146         final String description = mContext.getString(R.string.importing_vcard_canceled_title,
    147                 request.displayName);
    148         final Notification notification =
    149                 NotificationImportExportListener.constructCancelNotification(mContext, description);
    150         mNotificationManager.notify(NotificationImportExportListener.DEFAULT_NOTIFICATION_TAG,
    151                 jobId, notification);
    152     }
    153 
    154     @Override
    155     public void onExportProcessed(ExportRequest request, int jobId) {
    156         final String displayName = request.destUri.getLastPathSegment();
    157         final String message = mContext.getString(R.string.vcard_export_will_start_message,
    158                 displayName);
    159 
    160         mHandler.obtainMessage(0, message).sendToTarget();
    161         final Notification notification =
    162                 NotificationImportExportListener.constructProgressNotification(mContext,
    163                         VCardService.TYPE_EXPORT, message, message, jobId, displayName, -1, 0);
    164         mNotificationManager.notify(DEFAULT_NOTIFICATION_TAG, jobId, notification);
    165     }
    166 
    167     @Override
    168     public void onExportFailed(ExportRequest request) {
    169         mHandler.obtainMessage(0,
    170                 mContext.getString(R.string.vcard_export_request_rejected_message)).sendToTarget();
    171     }
    172 
    173     @Override
    174     public void onCancelRequest(CancelRequest request, int type) {
    175         final String description = type == VCardService.TYPE_IMPORT ?
    176                 mContext.getString(R.string.importing_vcard_canceled_title, request.displayName) :
    177                 mContext.getString(R.string.exporting_vcard_canceled_title, request.displayName);
    178         final Notification notification = constructCancelNotification(mContext, description);
    179         mNotificationManager.notify(DEFAULT_NOTIFICATION_TAG, request.jobId, notification);
    180     }
    181 
    182     /**
    183      * Constructs a {@link Notification} showing the current status of import/export.
    184      * Users can cancel the process with the Notification.
    185      *
    186      * @param context
    187      * @param type import/export
    188      * @param description Content of the Notification.
    189      * @param tickerText
    190      * @param jobId
    191      * @param displayName Name to be shown to the Notification (e.g. "finished importing XXXX").
    192      * Typycally a file name.
    193      * @param totalCount The number of vCard entries to be imported. Used to show progress bar.
    194      * -1 lets the system show the progress bar with "indeterminate" state.
    195      * @param currentCount The index of current vCard. Used to show progress bar.
    196      */
    197     /* package */ static Notification constructProgressNotification(
    198             Context context, int type, String description, String tickerText,
    199             int jobId, String displayName, int totalCount, int currentCount) {
    200         // Note: We cannot use extra values here (like setIntExtra()), as PendingIntent doesn't
    201         // preserve them across multiple Notifications. PendingIntent preserves the first extras
    202         // (when flag is not set), or update them when PendingIntent#getActivity() is called
    203         // (See PendingIntent#FLAG_UPDATE_CURRENT). In either case, we cannot preserve extras as we
    204         // expect (for each vCard import/export request).
    205         //
    206         // We use query parameter in Uri instead.
    207         // Scheme and Authority is arbitorary, assuming CancelActivity never refers them.
    208         final Intent intent = new Intent(context, CancelActivity.class);
    209         final Uri uri = (new Uri.Builder())
    210                 .scheme("invalidscheme")
    211                 .authority("invalidauthority")
    212                 .appendQueryParameter(CancelActivity.JOB_ID, String.valueOf(jobId))
    213                 .appendQueryParameter(CancelActivity.DISPLAY_NAME, displayName)
    214                 .appendQueryParameter(CancelActivity.TYPE, String.valueOf(type)).build();
    215         intent.setData(uri);
    216 
    217         final Notification.Builder builder = new Notification.Builder(context);
    218         builder.setOngoing(true)
    219                 .setProgress(totalCount, currentCount, totalCount == - 1)
    220                 .setTicker(tickerText)
    221                 .setContentTitle(description)
    222                 .setSmallIcon(type == VCardService.TYPE_IMPORT
    223                         ? android.R.drawable.stat_sys_download
    224                         : android.R.drawable.stat_sys_upload)
    225                 .setContentIntent(PendingIntent.getActivity(context, 0, intent, 0));
    226         if (totalCount > 0) {
    227             builder.setContentText(context.getString(R.string.percentage,
    228                     String.valueOf(currentCount * 100 / totalCount)));
    229         }
    230         return builder.getNotification();
    231     }
    232 
    233     /**
    234      * Constructs a Notification telling users the process is canceled.
    235      *
    236      * @param context
    237      * @param description Content of the Notification
    238      */
    239     /* package */ static Notification constructCancelNotification(
    240             Context context, String description) {
    241         return new Notification.Builder(context)
    242                 .setAutoCancel(true)
    243                 .setSmallIcon(android.R.drawable.stat_notify_error)
    244                 .setContentTitle(description)
    245                 .setContentText(description)
    246                 .setContentIntent(PendingIntent.getActivity(context, 0, new Intent(), 0))
    247                 .getNotification();
    248     }
    249 
    250     /**
    251      * Constructs a Notification telling users the process is finished.
    252      *
    253      * @param context
    254      * @param description Content of the Notification
    255      * @param intent Intent to be launched when the Notification is clicked. Can be null.
    256      */
    257     /* package */ static Notification constructFinishNotification(
    258             Context context, String title, String description, Intent intent) {
    259         return new Notification.Builder(context)
    260                 .setAutoCancel(true)
    261                 .setSmallIcon(android.R.drawable.stat_sys_download_done)
    262                 .setContentTitle(title)
    263                 .setContentText(description)
    264                 .setContentIntent(PendingIntent.getActivity(context, 0,
    265                         (intent != null ? intent : new Intent()), 0))
    266                 .getNotification();
    267     }
    268 
    269     /**
    270      * Constructs a Notification telling the vCard import has failed.
    271      *
    272      * @param context
    273      * @param reason The reason why the import has failed. Shown in description field.
    274      */
    275     /* package */ static Notification constructImportFailureNotification(
    276             Context context, String reason) {
    277         return new Notification.Builder(context)
    278                 .setAutoCancel(true)
    279                 .setSmallIcon(android.R.drawable.stat_notify_error)
    280                 .setContentTitle(context.getString(R.string.vcard_import_failed))
    281                 .setContentText(reason)
    282                 .setContentIntent(PendingIntent.getActivity(context, 0, new Intent(), 0))
    283                 .getNotification();
    284     }
    285 
    286     @Override
    287     public void onComplete() {
    288         mContext.finish();
    289     }
    290 }
    291