Home | History | Annotate | Download | only in provider
      1 package com.android.email.provider;
      2 
      3 import com.android.mail.providers.UIProvider;
      4 import com.android.mail.utils.LogTag;
      5 import com.android.mail.utils.LogUtils;
      6 import com.android.mail.utils.StorageLowState;
      7 
      8 import android.content.Context;
      9 import android.net.ConnectivityManager;
     10 import android.net.NetworkInfo;
     11 import android.os.Handler;
     12 import android.text.format.DateUtils;
     13 
     14 import java.util.HashMap;
     15 import java.util.Map;
     16 
     17 /**
     18  * This class implements a singleton that monitors a mailbox refresh activated by the user.
     19  * The refresh requests a sync but sometimes the sync doesn't happen till much later. This class
     20  * checks if a sync has been started for a specific mailbox. It checks for no network connectivity
     21  * and low storage conditions which prevent a sync and notifies the the caller using a callback.
     22  * If no sync is started after a certain timeout, it gives up and notifies the caller.
     23  */
     24 public class RefreshStatusMonitor {
     25     private static final String TAG = LogTag.getLogTag();
     26 
     27     private static final int REMOVE_REFRESH_STATUS_DELAY_MS = 250;
     28     public static final long REMOVE_REFRESH_TIMEOUT_MS = DateUtils.MINUTE_IN_MILLIS;
     29     private static final int MAX_RETRY =
     30             (int) (REMOVE_REFRESH_TIMEOUT_MS / REMOVE_REFRESH_STATUS_DELAY_MS);
     31 
     32     private static RefreshStatusMonitor sInstance = null;
     33     private final Handler mHandler;
     34     private boolean mIsStorageLow = false;
     35     private final Map<Long, Boolean> mMailboxSync = new HashMap<Long, Boolean>();
     36 
     37     private final Context mContext;
     38 
     39     public static RefreshStatusMonitor getInstance(Context context) {
     40         synchronized (RefreshStatusMonitor.class) {
     41             if (sInstance == null) {
     42                 sInstance = new RefreshStatusMonitor(context.getApplicationContext());
     43             }
     44         }
     45         return sInstance;
     46     }
     47 
     48     private RefreshStatusMonitor(Context context) {
     49         mContext = context;
     50         mHandler = new Handler(mContext.getMainLooper());
     51         StorageLowState.registerHandler(new StorageLowState
     52                     .LowStorageHandler() {
     53                 @Override
     54                 public void onStorageLow() {
     55                     mIsStorageLow = true;
     56                 }
     57 
     58                 @Override
     59                 public void onStorageOk() {
     60                     mIsStorageLow = false;
     61                 }
     62             });
     63     }
     64 
     65     public void monitorRefreshStatus(long mailboxId, Callback callback) {
     66         synchronized (mMailboxSync) {
     67             if (!mMailboxSync.containsKey(mailboxId))
     68                 mMailboxSync.put(mailboxId, false);
     69                 mHandler.postDelayed(
     70                         new RemoveRefreshStatusRunnable(mailboxId, callback),
     71                         REMOVE_REFRESH_STATUS_DELAY_MS);
     72         }
     73     }
     74 
     75     public void setSyncStarted(long mailboxId) {
     76         synchronized (mMailboxSync) {
     77             // only if we're tracking this mailbox
     78             if (mMailboxSync.containsKey(mailboxId)) {
     79                 LogUtils.d(TAG, "RefreshStatusMonitor: setSyncStarted: mailboxId=%d", mailboxId);
     80                 mMailboxSync.put(mailboxId, true);
     81             }
     82         }
     83     }
     84 
     85     private boolean isConnected() {
     86         final ConnectivityManager connectivityManager =
     87                 ((ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE));
     88         final NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
     89         return (networkInfo != null) && networkInfo.isConnected();
     90     }
     91 
     92     private class RemoveRefreshStatusRunnable implements Runnable {
     93         private final long mMailboxId;
     94         private final Callback mCallback;
     95 
     96         private int mNumRetries = 0;
     97 
     98 
     99         RemoveRefreshStatusRunnable(long mailboxId, Callback callback) {
    100             mMailboxId = mailboxId;
    101             mCallback = callback;
    102         }
    103 
    104         @Override
    105         public void run() {
    106             synchronized (mMailboxSync) {
    107                 final Boolean isSyncRunning = mMailboxSync.get(mMailboxId);
    108                 if (Boolean.FALSE.equals(isSyncRunning)) {
    109                     if (mIsStorageLow) {
    110                         LogUtils.d(TAG, "RefreshStatusMonitor: mailboxId=%d LOW STORAGE",
    111                                 mMailboxId);
    112                         // The device storage is low and sync will never succeed.
    113                         mCallback.onRefreshCompleted(
    114                                 mMailboxId, UIProvider.LastSyncResult.STORAGE_ERROR);
    115                         mMailboxSync.remove(mMailboxId);
    116                     } else if (!isConnected()) {
    117                         LogUtils.d(TAG, "RefreshStatusMonitor: mailboxId=%d NOT CONNECTED",
    118                                 mMailboxId);
    119                         // The device is not connected to the Internet. A sync will never succeed.
    120                         mCallback.onRefreshCompleted(
    121                                 mMailboxId, UIProvider.LastSyncResult.CONNECTION_ERROR);
    122                         mMailboxSync.remove(mMailboxId);
    123                     } else {
    124                         // The device is connected to the Internet. It might take a short while for
    125                         // the sync manager to initiate our sync, so let's post this runnable again
    126                         // and hope that we have started syncing by then.
    127                         mNumRetries++;
    128                         LogUtils.d(TAG, "RefreshStatusMonitor: mailboxId=%d Retry %d",
    129                                 mMailboxId, mNumRetries);
    130                         if (mNumRetries > MAX_RETRY) {
    131                             LogUtils.d(TAG, "RefreshStatusMonitor: mailboxId=%d TIMEOUT",
    132                                     mMailboxId);
    133                             // Hide the sync status bar if it's been a while since sync was
    134                             // requested and still hasn't started.
    135                             mMailboxSync.remove(mMailboxId);
    136                             mCallback.onTimeout(mMailboxId);
    137                             // TODO: Displaying a user friendly message in addition.
    138                         } else {
    139                             mHandler.postDelayed(this, REMOVE_REFRESH_STATUS_DELAY_MS);
    140                         }
    141                     }
    142                 } else {
    143                     // Some sync is currently in progress. We're done
    144                     LogUtils.d(TAG, "RefreshStatusMonitor: mailboxId=%d SYNC DETECTED", mMailboxId);
    145                     // it's not quite a success yet, the sync just started but we need to clear the
    146                     // error so the retry bar goes away.
    147                     mCallback.onRefreshCompleted(
    148                             mMailboxId, UIProvider.LastSyncResult.SUCCESS);
    149                     mMailboxSync.remove(mMailboxId);
    150                 }
    151             }
    152         }
    153     }
    154 
    155     public interface Callback {
    156         void onRefreshCompleted(long mailboxId, int result);
    157         void onTimeout(long mailboxId);
    158     }
    159 }
    160