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