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         final RemoteViews remoteViews =
    201                 new RemoteViews(context.getPackageName(),
    202                         R.layout.status_bar_ongoing_event_progress_bar);
    203         remoteViews.setTextViewText(R.id.status_description, description);
    204         remoteViews.setProgressBar(R.id.status_progress_bar, totalCount, currentCount,
    205                 totalCount == -1);
    206         final String percentage;
    207         if (totalCount > 0) {
    208             percentage = context.getString(R.string.percentage,
    209                     String.valueOf(currentCount * 100/totalCount));
    210         } else {
    211             percentage = "";
    212         }
    213         remoteViews.setTextViewText(R.id.status_progress_text, percentage);
    214         final int icon = (type == VCardService.TYPE_IMPORT ? android.R.drawable.stat_sys_download :
    215                 android.R.drawable.stat_sys_upload);
    216         remoteViews.setImageViewResource(R.id.status_icon, icon);
    217 
    218         final Notification notification = new Notification();
    219         notification.icon = icon;
    220         notification.tickerText = tickerText;
    221         notification.contentView = remoteViews;
    222         notification.flags |= Notification.FLAG_ONGOING_EVENT;
    223 
    224         // Note: We cannot use extra values here (like setIntExtra()), as PendingIntent doesn't
    225         // preserve them across multiple Notifications. PendingIntent preserves the first extras
    226         // (when flag is not set), or update them when PendingIntent#getActivity() is called
    227         // (See PendingIntent#FLAG_UPDATE_CURRENT). In either case, we cannot preserve extras as we
    228         // expect (for each vCard import/export request).
    229         //
    230         // We use query parameter in Uri instead.
    231         // Scheme and Authority is arbitorary, assuming CancelActivity never refers them.
    232         final Intent intent = new Intent(context, CancelActivity.class);
    233         final Uri uri = (new Uri.Builder())
    234                 .scheme("invalidscheme")
    235                 .authority("invalidauthority")
    236                 .appendQueryParameter(CancelActivity.JOB_ID, String.valueOf(jobId))
    237                 .appendQueryParameter(CancelActivity.DISPLAY_NAME, displayName)
    238                 .appendQueryParameter(CancelActivity.TYPE, String.valueOf(type)).build();
    239         intent.setData(uri);
    240 
    241         notification.contentIntent = PendingIntent.getActivity(context, 0, intent, 0);
    242         return notification;
    243     }
    244 
    245     /**
    246      * Constructs a Notification telling users the process is canceled.
    247      *
    248      * @param context
    249      * @param description Content of the Notification
    250      */
    251     /* package */ static Notification constructCancelNotification(
    252             Context context, String description) {
    253         return new Notification.Builder(context)
    254                 .setAutoCancel(true)
    255                 .setSmallIcon(android.R.drawable.stat_notify_error)
    256                 .setContentTitle(description)
    257                 .setContentText(description)
    258                 .setContentIntent(PendingIntent.getActivity(context, 0, new Intent(), 0))
    259                 .getNotification();
    260     }
    261 
    262     /**
    263      * Constructs a Notification telling users the process is finished.
    264      *
    265      * @param context
    266      * @param description Content of the Notification
    267      * @param intent Intent to be launched when the Notification is clicked. Can be null.
    268      */
    269     /* package */ static Notification constructFinishNotification(
    270             Context context, String title, String description, Intent intent) {
    271         return new Notification.Builder(context)
    272                 .setAutoCancel(true)
    273                 .setSmallIcon(android.R.drawable.stat_sys_download_done)
    274                 .setContentTitle(title)
    275                 .setContentText(description)
    276                 .setContentIntent(PendingIntent.getActivity(context, 0,
    277                         (intent != null ? intent : new Intent()), 0))
    278                 .getNotification();
    279     }
    280 
    281     /**
    282      * Constructs a Notification telling the vCard import has failed.
    283      *
    284      * @param context
    285      * @param reason The reason why the import has failed. Shown in description field.
    286      */
    287     /* package */ static Notification constructImportFailureNotification(
    288             Context context, String reason) {
    289         return new Notification.Builder(context)
    290                 .setAutoCancel(true)
    291                 .setSmallIcon(android.R.drawable.stat_notify_error)
    292                 .setContentTitle(context.getString(R.string.vcard_import_failed))
    293                 .setContentText(reason)
    294                 .setContentIntent(PendingIntent.getActivity(context, 0, new Intent(), 0))
    295                 .getNotification();
    296     }
    297 
    298     @Override
    299     public void onComplete() {
    300         mContext.finish();
    301     }
    302 }
    303