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