Merge lp:~karni/ubuntuone-android-files/handling-expired-tokens-better into lp:ubuntuone-android-files
- handling-expired-tokens-better
- Merge into trunk-2011
Proposed by
Michał Karnicki
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 469 | ||||
Proposed branch: | lp:~karni/ubuntuone-android-files/handling-expired-tokens-better | ||||
Merge into: | lp:ubuntuone-android-files | ||||
Diff against target: |
856 lines (+292/-114) 10 files modified
src/com/ubuntuone/android/files/UbuntuOneFiles.java (+23/-1) src/com/ubuntuone/android/files/activity/FilesActivity.java (+66/-83) src/com/ubuntuone/android/files/activity/LoginActivity.java (+10/-1) src/com/ubuntuone/android/files/event/ActivityStateEvent.java (+37/-0) src/com/ubuntuone/android/files/event/AuthStateEvent.java (+41/-0) src/com/ubuntuone/android/files/event/SyncStateEvent.java (+37/-0) src/com/ubuntuone/android/files/fragment/SignInFragment.java (+4/-1) src/com/ubuntuone/android/files/receiver/BatteryStatusReceiver.java (+10/-3) src/com/ubuntuone/android/files/service/MetaService.java (+32/-15) src/com/ubuntuone/android/files/service/UpDownService.java (+32/-10) |
||||
To merge this branch: | bzr merge lp:~karni/ubuntuone-android-files/handling-expired-tokens-better | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mike McCracken (community) | visual only | Approve | |
Review via email: mp+196220@code.launchpad.net |
Commit message
Description of the change
We didn't get robust enough UX for handling sign-out situation (i.e. notification not dismissed when log-in form shown, app not showing log-in form when simply restoring state when relaunched from home screen), so this time I used Otto event bus to nail it.
- Robust notification/
- Fix the metadata sync indicator/spinner for good.
- Silence some battery charging logs in auto upload service.
To post a comment you must log in.
Revision history for this message
Michał Karnicki (karni) wrote : | # |
Revision history for this message
Mike McCracken (mikemc) wrote : | # |
Looks reasonable to me.
review:
Approve
(visual only)
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added file 'libs/otto-1.3.3.jar' |
2 | Binary files libs/otto-1.3.3.jar 1970-01-01 00:00:00 +0000 and libs/otto-1.3.3.jar 2013-11-22 03:45:33 +0000 differ |
3 | === modified file 'src/com/ubuntuone/android/files/UbuntuOneFiles.java' |
4 | --- src/com/ubuntuone/android/files/UbuntuOneFiles.java 2013-01-15 23:02:20 +0000 |
5 | +++ src/com/ubuntuone/android/files/UbuntuOneFiles.java 2013-11-22 03:45:33 +0000 |
6 | @@ -1,7 +1,7 @@ |
7 | /* |
8 | * Ubuntu One Files - access Ubuntu One cloud storage on Android platform. |
9 | * |
10 | - * Copyright 2011-2012 Canonical Ltd. |
11 | + * Copyright 2011-2013 Canonical Ltd. |
12 | * |
13 | * This file is part of Ubuntu One Files. |
14 | * |
15 | @@ -29,9 +29,13 @@ |
16 | import android.content.pm.PackageManager.NameNotFoundException; |
17 | import android.preference.PreferenceManager; |
18 | |
19 | +import com.squareup.otto.Bus; |
20 | +import com.squareup.otto.Produce; |
21 | +import com.squareup.otto.ThreadEnforcer; |
22 | import com.ubuntuone.android.files.activity.FilesActivity; |
23 | import com.ubuntuone.android.files.activity.LoginActivity; |
24 | import com.ubuntuone.android.files.activity.StorageActivity; |
25 | +import com.ubuntuone.android.files.event.AuthStateEvent; |
26 | import com.ubuntuone.android.files.provider.MetaUtilities; |
27 | import com.ubuntuone.android.files.provider.TransfersContract.TransferPriority; |
28 | import com.ubuntuone.android.files.service.AutoUploadService; |
29 | @@ -46,12 +50,29 @@ |
30 | |
31 | private static UbuntuOneFiles sAppInstance; |
32 | |
33 | + private Bus mBus = new Bus(ThreadEnforcer.ANY); |
34 | + private AuthStateEvent mAuthStateEvent = new AuthStateEvent(); |
35 | + |
36 | + public static Bus getBus() { |
37 | + return sAppInstance.mBus; |
38 | + } |
39 | + |
40 | private MediaScannerHelper mMediaScannerHelper; |
41 | |
42 | public UbuntuOneFiles() { |
43 | super(); |
44 | sAppInstance = this; |
45 | setupLogging(); |
46 | + mBus.register(this); |
47 | + } |
48 | + |
49 | + @Produce |
50 | + public AuthStateEvent lastAuthStateEvent() { |
51 | + return mAuthStateEvent; |
52 | + } |
53 | + |
54 | + public void setLastAuthStateEvent(AuthStateEvent event) { |
55 | + mAuthStateEvent = event; |
56 | } |
57 | |
58 | @Override |
59 | @@ -97,6 +118,7 @@ |
60 | |
61 | @Override |
62 | public void onTerminate() { |
63 | + mBus.unregister(this); |
64 | Log.i(TAG, "Terminating application"); |
65 | super.onTerminate(); |
66 | } |
67 | |
68 | === modified file 'src/com/ubuntuone/android/files/activity/FilesActivity.java' |
69 | --- src/com/ubuntuone/android/files/activity/FilesActivity.java 2013-11-21 19:16:17 +0000 |
70 | +++ src/com/ubuntuone/android/files/activity/FilesActivity.java 2013-11-22 03:45:33 +0000 |
71 | @@ -1,7 +1,7 @@ |
72 | /* |
73 | * Ubuntu One Files - access Ubuntu One cloud storage on Android platform. |
74 | * |
75 | - * Copyright 2011-2012 Canonical Ltd. |
76 | + * Copyright 2011-2013 Canonical Ltd. |
77 | * |
78 | * This file is part of Ubuntu One Files. |
79 | * |
80 | @@ -41,13 +41,11 @@ |
81 | import android.app.Dialog; |
82 | import android.app.ProgressDialog; |
83 | import android.content.ActivityNotFoundException; |
84 | -import android.content.BroadcastReceiver; |
85 | import android.content.ContentResolver; |
86 | import android.content.Context; |
87 | import android.content.DialogInterface; |
88 | import android.content.DialogInterface.OnClickListener; |
89 | import android.content.Intent; |
90 | -import android.content.IntentFilter; |
91 | import android.content.res.Configuration; |
92 | import android.database.Cursor; |
93 | import android.graphics.Bitmap; |
94 | @@ -78,17 +76,24 @@ |
95 | import android.widget.Toast; |
96 | |
97 | import com.google.android.apps.analytics.GoogleAnalyticsTracker; |
98 | +import com.squareup.otto.Bus; |
99 | +import com.squareup.otto.Produce; |
100 | +import com.squareup.otto.Subscribe; |
101 | import com.ubuntuone.android.files.Analytics; |
102 | import com.ubuntuone.android.files.Preferences; |
103 | import com.ubuntuone.android.files.R; |
104 | +import com.ubuntuone.android.files.UbuntuOneFiles; |
105 | +import com.ubuntuone.android.files.event.ActivityStateEvent; |
106 | +import com.ubuntuone.android.files.event.AuthStateEvent; |
107 | +import com.ubuntuone.android.files.event.SyncStateEvent; |
108 | import com.ubuntuone.android.files.holder.FileViewHolder; |
109 | import com.ubuntuone.android.files.provider.MetaContract.Nodes; |
110 | import com.ubuntuone.android.files.provider.MetaContract.ResourceState; |
111 | import com.ubuntuone.android.files.provider.MetaUtilities; |
112 | import com.ubuntuone.android.files.provider.TransfersContract.Downloads; |
113 | +import com.ubuntuone.android.files.service.AutoUploadService; |
114 | import com.ubuntuone.android.files.service.MetaService; |
115 | import com.ubuntuone.android.files.service.MetaService.Status; |
116 | -import com.ubuntuone.android.files.service.AutoUploadService; |
117 | import com.ubuntuone.android.files.service.MetaServiceHelper; |
118 | import com.ubuntuone.android.files.service.UpDownService; |
119 | import com.ubuntuone.android.files.service.UpDownService.OnDownloadListener; |
120 | @@ -161,10 +166,11 @@ |
121 | |
122 | private GoogleAnalyticsTracker mTracker; |
123 | |
124 | + private Bus mBus; |
125 | + private ActivityStateEvent mActivityStateEvent = new ActivityStateEvent(); |
126 | + |
127 | private Handler mHandler; |
128 | private DetachableResultReceiver mReceiver; |
129 | - private SignOutBroadcastReceiver mSignOutReceiver; |
130 | - private boolean mSignOutReceiverRegistered = false; |
131 | |
132 | private ContentResolver mResolver; |
133 | |
134 | @@ -197,6 +203,8 @@ |
135 | protected void onCreate(Bundle savedInstanceState) { |
136 | super.onCreate(savedInstanceState); |
137 | |
138 | + mBus = UbuntuOneFiles.getBus(); |
139 | + |
140 | mTracker = GoogleAnalyticsTracker.getInstance(); |
141 | mTracker.start(Analytics.U1F_ACCOUNT, this); |
142 | mTracker.trackPageView(TAG); |
143 | @@ -360,23 +368,48 @@ |
144 | } |
145 | |
146 | @Override |
147 | + protected void onStart() { |
148 | + super.onStart(); |
149 | + |
150 | + mActivityStateEvent.setIsVisible(true); |
151 | + mBus.post(mActivityStateEvent); |
152 | + } |
153 | + |
154 | + @Override |
155 | protected void onResume() { |
156 | super.onResume(); |
157 | + mBus.register(this); |
158 | + |
159 | // This should be moved to onCreate, really. |
160 | ChangeLogUtils.maybeShowChangelog(this); |
161 | |
162 | if (mReceiver != null) { |
163 | mReceiver.setReceiver(this); |
164 | - if (MetaService.isSyncRunning()) { |
165 | - awaitWithListEmptyTextView(); |
166 | - showSpinner(); |
167 | - } else { |
168 | - hideSpinner(); |
169 | - } |
170 | } |
171 | - registerSignOutReceiver(); |
172 | setCursorAdapterInBackground(); |
173 | } |
174 | + |
175 | + @Subscribe |
176 | + public void onSyncStateEvent(final SyncStateEvent event) { |
177 | + if (event.isRunning()) { |
178 | + awaitWithListEmptyTextView(); |
179 | + showSpinner(); |
180 | + } else { |
181 | + hideSpinner(); |
182 | + } |
183 | + } |
184 | + |
185 | + @Produce |
186 | + public ActivityStateEvent lastActivityStateEvent() { |
187 | + return mActivityStateEvent; |
188 | + } |
189 | + |
190 | + @Subscribe |
191 | + public void onAuthStateEvent(final AuthStateEvent event) { |
192 | + if (!event.isAuthenticated()) { |
193 | + signIn(); |
194 | + } |
195 | + } |
196 | |
197 | /** |
198 | * Requests appropriate cursor and sets the {@link FilesAdapter} on |
199 | @@ -416,14 +449,22 @@ |
200 | |
201 | @Override |
202 | protected void onPause() { |
203 | - unregisterSignOutReceiver(); |
204 | if (mReceiver != null) { |
205 | mReceiver.detach(); |
206 | } |
207 | + mBus.unregister(this); |
208 | super.onPause(); |
209 | } |
210 | |
211 | @Override |
212 | + protected void onStop() { |
213 | + mActivityStateEvent.setIsVisible(false); |
214 | + mBus.post(mActivityStateEvent); |
215 | + |
216 | + super.onStop(); |
217 | + } |
218 | + |
219 | + @Override |
220 | public void onDestroy() { |
221 | if (mTracker != null) { |
222 | mTracker.dispatch(); |
223 | @@ -863,7 +904,9 @@ |
224 | |
225 | private void onActionBarRereshClicked() { |
226 | if (!NetworkUtil.isConnected(this)) { |
227 | - hideSpinner(); |
228 | + if (mLoaderItem != null) { |
229 | + mLoaderItem.setLoading(false); |
230 | + } |
231 | showDialog(DIALOG_NO_NETWORK); |
232 | } else if (mPathTracker.isAtRoot()) { |
233 | onRefresh(null); |
234 | @@ -993,33 +1036,15 @@ |
235 | resultData.getString(MetaService.EXTRA_RESOURCE_PATH); |
236 | |
237 | switch (resultCode) { |
238 | - case Status.RUNNING: |
239 | - showSpinner(); |
240 | - awaitWithListEmptyTextView(); |
241 | - break; |
242 | - case Status.PROGRESS: |
243 | - // Unused. |
244 | - break; |
245 | case Status.FINISHED: |
246 | - hideSpinner(); |
247 | setCursorAdapterInBackground(); |
248 | |
249 | if (lastResourcePath != null && |
250 | lastResourcePath.equals(currentVolumeResourcePath)) { |
251 | - resetListEmptyTextView(null); |
252 | isGettingVolume = false; |
253 | return; |
254 | } |
255 | |
256 | - final String currentResourcePath = mPathTracker.getCurrentNode(); |
257 | - if (currentResourcePath == null) { |
258 | - // Most probably the user has no files yet. If he has, |
259 | - // he should already see them and this will have no impact. |
260 | - resetListEmptyTextView(null); |
261 | - } else if (currentResourcePath.equals(lastResourcePath)) { |
262 | - resetListEmptyTextView(lastResourcePath); |
263 | - } |
264 | - |
265 | if (mCheckDirectorySizeDialog != null |
266 | && mCheckDirectorySizeDialog.isShowing()) { |
267 | final String contextResourcePath = |
268 | @@ -1073,7 +1098,7 @@ |
269 | runOnUiThread(new Runnable() { |
270 | public void run() { |
271 | hideSpinner(); |
272 | - resetListEmptyTextView(lastResourcePath); |
273 | + |
274 | final String msg = String.format( |
275 | getString(R.string.error_fmt), errorMessage); |
276 | UIUtil.showToast(FilesActivity.this, msg, true); |
277 | @@ -1407,6 +1432,8 @@ |
278 | // This should avoid delayed update of the list-empty label. |
279 | if (NetworkUtil.isConnected(this)) { |
280 | awaitWithListEmptyTextView(); |
281 | + } else { |
282 | + mEmptyTextView.setText(R.string.no_network); |
283 | } |
284 | |
285 | // Save the position of the list, just for the previous screen. |
286 | @@ -1439,6 +1466,7 @@ |
287 | private void showSpinner() { |
288 | mHandler.post(new Runnable() { |
289 | public void run() { |
290 | + mEmptyTextView.setText(R.string.loading_files); |
291 | if (mLoaderItem != null) |
292 | mLoaderItem.setLoading(true); |
293 | } |
294 | @@ -1448,6 +1476,7 @@ |
295 | private void hideSpinner() { |
296 | mHandler.post(new Runnable() { |
297 | public void run() { |
298 | + mEmptyTextView.setText(R.string.folder_is_empty); |
299 | if (mLoaderItem != null) { |
300 | mLoaderItem.setLoading(false); |
301 | } |
302 | @@ -1473,28 +1502,6 @@ |
303 | }); |
304 | } |
305 | |
306 | - private void resetListEmptyTextView(final String resourcePath) { |
307 | - if (!NetworkUtil.isConnected(FilesActivity.this)) { |
308 | - mEmptyTextView.setText(R.string.no_network); |
309 | - return; |
310 | - } |
311 | - |
312 | - String directoryEmpty = getString(R.string.folder_is_empty); |
313 | - if (mEmptyTextView != null && |
314 | - mEmptyTextView.getText().equals(directoryEmpty)) { |
315 | - return; |
316 | - } |
317 | - mHandler.post(new Runnable() { |
318 | - public void run() { |
319 | - String node = mPathTracker.getCurrentNode(); |
320 | - if (resourcePath == null || node == null || |
321 | - resourcePath.equals(node)) { |
322 | - mEmptyTextView.setText(R.string.folder_is_empty); |
323 | - } |
324 | - } |
325 | - }); |
326 | - } |
327 | - |
328 | public void downloadFile(final String resourcePath) { |
329 | if (NetworkUtil.isConnected(this)) { |
330 | TransferUtils.dequeueByResourcePath(getContentResolver(), |
331 | @@ -1913,33 +1920,9 @@ |
332 | changeCursor(filesCursor); |
333 | notifyDataSetChanged(); |
334 | |
335 | - onRefresh(resourcePath); |
336 | - } |
337 | - } |
338 | - |
339 | - private void registerSignOutReceiver() { |
340 | - if (mSignOutReceiver == null || !mSignOutReceiverRegistered) { |
341 | - mSignOutReceiver = new SignOutBroadcastReceiver(); |
342 | - IntentFilter intentFilter = |
343 | - new IntentFilter("com.ubuntuone.android.files.SIGN_OUT"); |
344 | - LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this); |
345 | - lbm.registerReceiver(mSignOutReceiver, intentFilter); |
346 | - mSignOutReceiverRegistered = true; |
347 | - } |
348 | - } |
349 | - |
350 | - private void unregisterSignOutReceiver() { |
351 | - if (mSignOutReceiver != null && mSignOutReceiverRegistered) { |
352 | - LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this); |
353 | - lbm.unregisterReceiver(mSignOutReceiver); |
354 | - mSignOutReceiverRegistered = false; |
355 | - } |
356 | - } |
357 | - |
358 | - private class SignOutBroadcastReceiver extends BroadcastReceiver { |
359 | - @Override |
360 | - public void onReceive(Context context, Intent intent) { |
361 | - signIn(); |
362 | + if (NetworkUtil.isConnected(FilesActivity.this)) { |
363 | + onRefresh(resourcePath); |
364 | + } |
365 | } |
366 | } |
367 | |
368 | |
369 | === modified file 'src/com/ubuntuone/android/files/activity/LoginActivity.java' |
370 | --- src/com/ubuntuone/android/files/activity/LoginActivity.java 2013-11-21 19:15:53 +0000 |
371 | +++ src/com/ubuntuone/android/files/activity/LoginActivity.java 2013-11-22 03:45:33 +0000 |
372 | @@ -1,7 +1,7 @@ |
373 | /* |
374 | * Ubuntu One Files - access Ubuntu One cloud storage on Android platform. |
375 | * |
376 | - * Copyright (C) 2011 Canonical Ltd. |
377 | + * Copyright (C) 2011-2013 Canonical Ltd. |
378 | * Author: Michał Karnicki <michal.karnicki@canonical.com> |
379 | * |
380 | * This file is part of Ubuntu One Files. |
381 | @@ -25,6 +25,7 @@ |
382 | import android.accounts.Account; |
383 | import android.accounts.AccountAuthenticatorResponse; |
384 | import android.accounts.AccountManager; |
385 | +import android.app.NotificationManager; |
386 | import android.content.Intent; |
387 | import android.os.Bundle; |
388 | import android.support.v4.app.FragmentActivity; |
389 | @@ -82,6 +83,8 @@ |
390 | protected void onCreate(Bundle savedInstanceState) { |
391 | super.onCreate(savedInstanceState); |
392 | |
393 | + dismissReauthenticateNotification(); |
394 | + |
395 | setContentView(R.layout.fragment_content); |
396 | |
397 | final Intent intent = getIntent(); |
398 | @@ -142,6 +145,12 @@ |
399 | break; |
400 | } |
401 | } |
402 | + |
403 | + public void dismissReauthenticateNotification() { |
404 | + NotificationManager nm = (NotificationManager) |
405 | + getApplicationContext().getSystemService(NOTIFICATION_SERVICE); |
406 | + nm.cancel(R.id.stat_please_reauthenticate); |
407 | + } |
408 | |
409 | @Override |
410 | public void finish() { |
411 | |
412 | === added directory 'src/com/ubuntuone/android/files/event' |
413 | === added file 'src/com/ubuntuone/android/files/event/ActivityStateEvent.java' |
414 | --- src/com/ubuntuone/android/files/event/ActivityStateEvent.java 1970-01-01 00:00:00 +0000 |
415 | +++ src/com/ubuntuone/android/files/event/ActivityStateEvent.java 2013-11-22 03:45:33 +0000 |
416 | @@ -0,0 +1,37 @@ |
417 | +/* |
418 | + * Ubuntu One Files - access Ubuntu One cloud storage on Android platform. |
419 | + * |
420 | + * Copyright 2011-2013 Canonical Ltd. |
421 | + * |
422 | + * This file is part of Ubuntu One Files. |
423 | + * |
424 | + * This program is free software: you can redistribute it and/or modify |
425 | + * it under the terms of the GNU Affero General Public License as |
426 | + * published by the Free Software Foundation, either version 3 of the |
427 | + * License, or (at your option) any later version. |
428 | + * |
429 | + * This program is distributed in the hope that it will be useful, |
430 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
431 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
432 | + * GNU Affero General Public License for more details. |
433 | + * |
434 | + * You should have received a copy of the GNU Affero General Public License |
435 | + * along with this program. If not, see http://www.gnu.org/licenses |
436 | + */ |
437 | + |
438 | +package com.ubuntuone.android.files.event; |
439 | + |
440 | +public class ActivityStateEvent { |
441 | + boolean isVisible = true; |
442 | + |
443 | + public ActivityStateEvent() { |
444 | + } |
445 | + |
446 | + public void setIsVisible(boolean visible) { |
447 | + this.isVisible = visible; |
448 | + } |
449 | + |
450 | + public boolean isVisible() { |
451 | + return isVisible; |
452 | + } |
453 | +} |
454 | |
455 | === added file 'src/com/ubuntuone/android/files/event/AuthStateEvent.java' |
456 | --- src/com/ubuntuone/android/files/event/AuthStateEvent.java 1970-01-01 00:00:00 +0000 |
457 | +++ src/com/ubuntuone/android/files/event/AuthStateEvent.java 2013-11-22 03:45:33 +0000 |
458 | @@ -0,0 +1,41 @@ |
459 | +/* |
460 | + * Ubuntu One Files - access Ubuntu One cloud storage on Android platform. |
461 | + * |
462 | + * Copyright 2011-2013 Canonical Ltd. |
463 | + * |
464 | + * This file is part of Ubuntu One Files. |
465 | + * |
466 | + * This program is free software: you can redistribute it and/or modify |
467 | + * it under the terms of the GNU Affero General Public License as |
468 | + * published by the Free Software Foundation, either version 3 of the |
469 | + * License, or (at your option) any later version. |
470 | + * |
471 | + * This program is distributed in the hope that it will be useful, |
472 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
473 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
474 | + * GNU Affero General Public License for more details. |
475 | + * |
476 | + * You should have received a copy of the GNU Affero General Public License |
477 | + * along with this program. If not, see http://www.gnu.org/licenses |
478 | + */ |
479 | + |
480 | +package com.ubuntuone.android.files.event; |
481 | + |
482 | +public class AuthStateEvent { |
483 | + boolean isAuthenticated = false; |
484 | + |
485 | + public AuthStateEvent() { |
486 | + } |
487 | + |
488 | + public AuthStateEvent(boolean isAuthenticated) { |
489 | + this.isAuthenticated = isAuthenticated; |
490 | + } |
491 | + |
492 | + public void setIsAuthenticated(boolean authenticated) { |
493 | + this.isAuthenticated = authenticated; |
494 | + } |
495 | + |
496 | + public boolean isAuthenticated() { |
497 | + return isAuthenticated; |
498 | + } |
499 | +} |
500 | |
501 | === added file 'src/com/ubuntuone/android/files/event/SyncStateEvent.java' |
502 | --- src/com/ubuntuone/android/files/event/SyncStateEvent.java 1970-01-01 00:00:00 +0000 |
503 | +++ src/com/ubuntuone/android/files/event/SyncStateEvent.java 2013-11-22 03:45:33 +0000 |
504 | @@ -0,0 +1,37 @@ |
505 | +/* |
506 | + * Ubuntu One Files - access Ubuntu One cloud storage on Android platform. |
507 | + * |
508 | + * Copyright 2011-2013 Canonical Ltd. |
509 | + * |
510 | + * This file is part of Ubuntu One Files. |
511 | + * |
512 | + * This program is free software: you can redistribute it and/or modify |
513 | + * it under the terms of the GNU Affero General Public License as |
514 | + * published by the Free Software Foundation, either version 3 of the |
515 | + * License, or (at your option) any later version. |
516 | + * |
517 | + * This program is distributed in the hope that it will be useful, |
518 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
519 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
520 | + * GNU Affero General Public License for more details. |
521 | + * |
522 | + * You should have received a copy of the GNU Affero General Public License |
523 | + * along with this program. If not, see http://www.gnu.org/licenses |
524 | + */ |
525 | + |
526 | +package com.ubuntuone.android.files.event; |
527 | + |
528 | +public class SyncStateEvent { |
529 | + boolean isRunning = false; |
530 | + |
531 | + public SyncStateEvent() { |
532 | + } |
533 | + |
534 | + public void setIsRunning(boolean isRunning) { |
535 | + this.isRunning = isRunning; |
536 | + } |
537 | + |
538 | + public boolean isRunning() { |
539 | + return isRunning; |
540 | + } |
541 | +} |
542 | |
543 | === modified file 'src/com/ubuntuone/android/files/fragment/SignInFragment.java' |
544 | --- src/com/ubuntuone/android/files/fragment/SignInFragment.java 2013-11-15 14:25:16 +0000 |
545 | +++ src/com/ubuntuone/android/files/fragment/SignInFragment.java 2013-11-22 03:45:33 +0000 |
546 | @@ -1,7 +1,7 @@ |
547 | /* |
548 | * Ubuntu One Files - access Ubuntu One cloud storage on Android platform. |
549 | * |
550 | - * Copyright 2011-2012 Canonical Ltd. |
551 | + * Copyright 2011-2013 Canonical Ltd. |
552 | * |
553 | * This file is part of Ubuntu One Files. |
554 | * |
555 | @@ -47,6 +47,8 @@ |
556 | import com.ubuntuone.android.files.Constants; |
557 | import com.ubuntuone.android.files.Preferences; |
558 | import com.ubuntuone.android.files.R; |
559 | +import com.ubuntuone.android.files.UbuntuOneFiles; |
560 | +import com.ubuntuone.android.files.event.AuthStateEvent; |
561 | import com.ubuntuone.android.files.service.AutoUploadService; |
562 | import com.ubuntuone.android.files.util.AuthenticateUserTask; |
563 | import com.ubuntuone.android.files.util.AuthenticateUserTask.AuthenticateUserTaskCallback; |
564 | @@ -281,6 +283,7 @@ |
565 | } |
566 | } |
567 | |
568 | + UbuntuOneFiles.getInstance().setLastAuthStateEvent(new AuthStateEvent(true)); |
569 | activity.setResult(Activity.RESULT_OK); |
570 | activity.finish(); |
571 | } |
572 | |
573 | === modified file 'src/com/ubuntuone/android/files/receiver/BatteryStatusReceiver.java' |
574 | --- src/com/ubuntuone/android/files/receiver/BatteryStatusReceiver.java 2013-01-30 22:13:28 +0000 |
575 | +++ src/com/ubuntuone/android/files/receiver/BatteryStatusReceiver.java 2013-11-22 03:45:33 +0000 |
576 | @@ -35,7 +35,10 @@ |
577 | { |
578 | private final static String TAG = BatteryStatusReceiver.class.getSimpleName(); |
579 | |
580 | + boolean lastIsPlugged = false; |
581 | boolean isPlugged = false; |
582 | + |
583 | + boolean lastIsCharging = false; |
584 | boolean isCharging = false; |
585 | |
586 | private OnAutoUploadEventListener stateListener; |
587 | @@ -50,7 +53,7 @@ |
588 | public void onReceive(Context context, Intent intent) { |
589 | String action = intent.getAction(); |
590 | if (Intent.ACTION_BATTERY_CHANGED.equals(action)) { |
591 | - Log.i(TAG, action); |
592 | + Log.d(TAG, action); |
593 | onActionBatteryChanged(context); |
594 | } else { |
595 | Log.w(TAG, "Unhandled broadcast: " + action); |
596 | @@ -64,7 +67,11 @@ |
597 | |
598 | public void onActionBatteryChanged(Context context) { |
599 | updateBatteryState(context); |
600 | - stateListener.onAutoUploadEventReceived(); |
601 | + if (lastIsPlugged != isPlugged || lastIsCharging != isCharging) { |
602 | + lastIsPlugged = isPlugged; |
603 | + lastIsCharging = isCharging; |
604 | + stateListener.onAutoUploadEventReceived(); |
605 | + } |
606 | } |
607 | |
608 | public void updateBatteryState(Context context) { |
609 | @@ -79,7 +86,7 @@ |
610 | isCharging = (statusFlag != -1) && |
611 | (statusFlag == BatteryManager.BATTERY_STATUS_CHARGING || |
612 | statusFlag == BatteryManager.BATTERY_STATUS_FULL); |
613 | - Log.i(TAG, String.format(Locale.US, |
614 | + Log.d(TAG, String.format(Locale.US, |
615 | "Battery state: isPlugged %b, isCharging %b", |
616 | isPlugged, isCharging)); |
617 | } |
618 | |
619 | === modified file 'src/com/ubuntuone/android/files/service/MetaService.java' |
620 | --- src/com/ubuntuone/android/files/service/MetaService.java 2013-02-06 23:07:30 +0000 |
621 | +++ src/com/ubuntuone/android/files/service/MetaService.java 2013-11-22 03:45:33 +0000 |
622 | @@ -1,7 +1,7 @@ |
623 | /* |
624 | * Ubuntu One Files - access Ubuntu One cloud storage on Android platform. |
625 | * |
626 | - * Copyright 2011-2012 Canonical Ltd. |
627 | + * Copyright 2011-2013 Canonical Ltd. |
628 | * |
629 | * This file is part of Ubuntu One Files. |
630 | * |
631 | @@ -34,6 +34,7 @@ |
632 | import android.content.ContentProviderOperation; |
633 | import android.content.ContentResolver; |
634 | import android.content.ContentValues; |
635 | +import android.content.Context; |
636 | import android.content.Intent; |
637 | import android.content.OperationApplicationException; |
638 | import android.database.Cursor; |
639 | @@ -42,11 +43,14 @@ |
640 | import android.os.IBinder; |
641 | import android.os.RemoteException; |
642 | import android.os.ResultReceiver; |
643 | -import android.support.v4.content.LocalBroadcastManager; |
644 | |
645 | +import com.squareup.otto.Bus; |
646 | +import com.squareup.otto.Produce; |
647 | import com.ubuntuone.android.files.Constants; |
648 | import com.ubuntuone.android.files.Preferences; |
649 | import com.ubuntuone.android.files.UbuntuOneFiles; |
650 | +import com.ubuntuone.android.files.event.AuthStateEvent; |
651 | +import com.ubuntuone.android.files.event.SyncStateEvent; |
652 | import com.ubuntuone.android.files.provider.MetaContract; |
653 | import com.ubuntuone.android.files.provider.MetaContract.Nodes; |
654 | import com.ubuntuone.android.files.provider.MetaContract.ResourceState; |
655 | @@ -106,7 +110,9 @@ |
656 | public final int ERROR = 5; |
657 | } |
658 | |
659 | - public static boolean sSyncRunning = false; |
660 | + private Bus mBus; |
661 | + private SyncStateEvent mSyncStateEvent = new SyncStateEvent(); |
662 | + private AuthStateEvent mAuthStateEvent = new AuthStateEvent(); |
663 | |
664 | private ContentResolver contentResolver; |
665 | |
666 | @@ -116,6 +122,7 @@ |
667 | |
668 | public MetaService() { |
669 | super(MetaService.class.getSimpleName()); |
670 | + mBus = UbuntuOneFiles.getBus(); |
671 | } |
672 | |
673 | @Override |
674 | @@ -123,10 +130,20 @@ |
675 | return null; |
676 | } |
677 | |
678 | + @Produce public SyncStateEvent lastSyncStateEvent() { |
679 | + return mSyncStateEvent; |
680 | + } |
681 | + |
682 | + private void sendSyncState(boolean isRunning) { |
683 | + mSyncStateEvent.setIsRunning(isRunning); |
684 | + mBus.post(mSyncStateEvent); |
685 | + } |
686 | + |
687 | @Override |
688 | public void onCreate() { |
689 | super.onCreate(); |
690 | Log.d(TAG, "onCreate()"); |
691 | + mBus.register(this); |
692 | |
693 | contentResolver = getContentResolver(); |
694 | httpClient = HttpClientProvider.getInstance(); |
695 | @@ -140,7 +157,7 @@ |
696 | |
697 | @Override |
698 | protected void onHandleIntent(Intent intent) { |
699 | - sSyncRunning = true; |
700 | + sendSyncState(true); |
701 | Thread.currentThread().setPriority(Thread.MIN_PRIORITY); |
702 | |
703 | final String action = intent.getAction(); |
704 | @@ -173,17 +190,14 @@ |
705 | } else if (ACTION_DELETE_NODE.equals(action)) { |
706 | deleteNode(resourcePath, receiver); |
707 | } |
708 | - sSyncRunning = false; |
709 | + sendSyncState(false); |
710 | } |
711 | |
712 | @Override |
713 | public void onDestroy() { |
714 | + Log.d(TAG, "onDestroy()"); |
715 | + mBus.unregister(this); |
716 | super.onDestroy(); |
717 | - Log.d(TAG, "onDestroy()"); |
718 | - } |
719 | - |
720 | - public static boolean isSyncRunning() { |
721 | - return sSyncRunning; |
722 | } |
723 | |
724 | public void onUbuntuOneFailure(U1Failure failure, ResultReceiver receiver) { |
725 | @@ -202,11 +216,14 @@ |
726 | Bundle data; |
727 | switch (statusCode) { |
728 | case HttpStatus.SC_UNAUTHORIZED: |
729 | - Preferences.invalidateToken(this); |
730 | - |
731 | - Intent intent = new Intent("com.ubuntuone.android.files.SIGN_OUT"); |
732 | - LocalBroadcastManager.getInstance(getApplicationContext()) |
733 | - .sendBroadcast(intent); |
734 | + Log.w(TAG, "Received HTTP Unauthorized response."); |
735 | + Context context = UbuntuOneFiles.getInstance().getApplicationContext(); |
736 | + Preferences.invalidateToken(context); |
737 | + UbuntuOneFiles.getInstance().setLastAuthStateEvent(new AuthStateEvent(false)); |
738 | + |
739 | + mAuthStateEvent.setIsAuthenticated(false); |
740 | + mBus.post(mAuthStateEvent); |
741 | + |
742 | stopSelf(); |
743 | break; |
744 | case HttpStatus.SC_NOT_FOUND: |
745 | |
746 | === modified file 'src/com/ubuntuone/android/files/service/UpDownService.java' |
747 | --- src/com/ubuntuone/android/files/service/UpDownService.java 2013-11-15 14:33:02 +0000 |
748 | +++ src/com/ubuntuone/android/files/service/UpDownService.java 2013-11-22 03:45:33 +0000 |
749 | @@ -54,6 +54,8 @@ |
750 | import android.support.v4.content.LocalBroadcastManager; |
751 | |
752 | import com.google.android.apps.analytics.GoogleAnalyticsTracker; |
753 | +import com.squareup.otto.Bus; |
754 | +import com.squareup.otto.Subscribe; |
755 | import com.ubuntuone.android.files.Alarms; |
756 | import com.ubuntuone.android.files.Analytics; |
757 | import com.ubuntuone.android.files.Constants; |
758 | @@ -63,6 +65,8 @@ |
759 | import com.ubuntuone.android.files.activity.FilesActivity; |
760 | import com.ubuntuone.android.files.activity.LoginActivity; |
761 | import com.ubuntuone.android.files.activity.PreferencesActivity; |
762 | +import com.ubuntuone.android.files.event.ActivityStateEvent; |
763 | +import com.ubuntuone.android.files.event.AuthStateEvent; |
764 | import com.ubuntuone.android.files.provider.MetaContract.Nodes; |
765 | import com.ubuntuone.android.files.provider.MetaContract.ResourceState; |
766 | import com.ubuntuone.android.files.provider.MetaContract.Volumes; |
767 | @@ -120,6 +124,10 @@ |
768 | |
769 | private static final String PART = ".part"; |
770 | |
771 | + private Bus mBus; |
772 | + private AuthStateEvent mAuthStateEvent = new AuthStateEvent(); |
773 | + private boolean isAppVisible = false; |
774 | + |
775 | private ConnectivityManager connectivityManager; |
776 | private NotificationManager notificationManager; |
777 | |
778 | @@ -157,10 +165,16 @@ |
779 | private final int downloadNotificationId = R.id.stat_ongoing_download_id; |
780 | |
781 | private GoogleAnalyticsTracker mTracker; |
782 | + |
783 | + @Subscribe public void onActivityStateEvent(final ActivityStateEvent event) { |
784 | + isAppVisible = event.isVisible(); |
785 | + } |
786 | |
787 | @Override |
788 | public void onCreate() { |
789 | super.onCreate(); |
790 | + mBus = UbuntuOneFiles.getBus(); |
791 | + mBus.register(this); |
792 | duration = System.currentTimeMillis(); |
793 | } |
794 | |
795 | @@ -172,10 +186,10 @@ |
796 | mTracker = GoogleAnalyticsTracker.getInstance(); |
797 | mTracker.startNewSession(Analytics.U1F_ACCOUNT, this); |
798 | |
799 | - connectivityManager = (ConnectivityManager) getSystemService( |
800 | - CONNECTIVITY_SERVICE); |
801 | - notificationManager = (NotificationManager) getSystemService( |
802 | - NOTIFICATION_SERVICE); |
803 | + connectivityManager = (ConnectivityManager) |
804 | + getSystemService(CONNECTIVITY_SERVICE); |
805 | + notificationManager = (NotificationManager) getApplicationContext() |
806 | + .getSystemService(NOTIFICATION_SERVICE); |
807 | |
808 | contentResolver = getContentResolver(); |
809 | |
810 | @@ -343,6 +357,7 @@ |
811 | |
812 | @Override |
813 | public void onDestroy() { |
814 | + mBus.unregister(this); |
815 | long elapsed = System.currentTimeMillis() - duration; |
816 | Log.i(TAG, "Operation time " + UIUtil.formatTime(elapsed)); |
817 | super.onDestroy(); |
818 | @@ -1123,7 +1138,6 @@ |
819 | // TODO log failure status codes in GA |
820 | switch (failure.getStatusCode()) { |
821 | case HttpStatus.SC_UNAUTHORIZED: |
822 | - showReauthenticateNotification(); |
823 | onUnauthorizedResponse(); |
824 | break; |
825 | case HttpStatus.SC_PAYMENT_REQUIRED: |
826 | @@ -1143,10 +1157,10 @@ |
827 | Log.w(TAG, "Received HTTP Unauthorized response."); |
828 | Context context = UbuntuOneFiles.getInstance().getApplicationContext(); |
829 | Preferences.invalidateToken(context); |
830 | - |
831 | - Intent intent = new Intent("com.ubuntuone.android.files.SIGN_OUT"); |
832 | - LocalBroadcastManager.getInstance(getApplicationContext()) |
833 | - .sendBroadcast(intent); |
834 | + UbuntuOneFiles.getInstance().setLastAuthStateEvent(new AuthStateEvent(false)); |
835 | + |
836 | + requireReauthentication(); |
837 | + |
838 | stopSelf(); |
839 | } |
840 | |
841 | @@ -1207,7 +1221,15 @@ |
842 | notificationManager.notify(R.id.stat_failed_upload_id, notification); |
843 | } |
844 | |
845 | - private void showReauthenticateNotification() { |
846 | + private void requireReauthentication() { |
847 | + if (isAppVisible) { |
848 | + // Show the log-in activity. |
849 | + mAuthStateEvent.setIsAuthenticated(false); |
850 | + mBus.post(mAuthStateEvent); |
851 | + return; |
852 | + } |
853 | + |
854 | + // Post a log-in notification. |
855 | final String title = getString(R.string.please_reauthenticate); |
856 | final String text = getString(R.string.you_have_been_signed_out); |
857 |
Tested by removing the auth token when:
- app is in background. Upload a picture. Notice the re-authentication notification.
- app is in foreground. Do anything. Notice the login form show up.
In case the log in form shows up when launched from the home screen, the notification goes away (as it should). If app is in foreground, it goes back to the app once you use the log in form, rather than drop to home screen.