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