Merge lp:~karni/ubuntuone-android-files/basic-upload into lp:ubuntuone-android-files
- basic-upload
- Merge into trunk-2011
Proposed by
Michał Karnicki
Status: | Merged |
---|---|
Approved by: | Chad Miller |
Approved revision: | 10 |
Merged at revision: | 9 |
Proposed branch: | lp:~karni/ubuntuone-android-files/basic-upload |
Merge into: | lp:ubuntuone-android-files |
Diff against target: |
1315 lines (+650/-192) 15 files modified
.classpath (+1/-1) res/layout/activity_list.xml (+3/-4) res/values/service.xml (+15/-1) res/values/strings.xml (+2/-2) src/com/ubuntuone/android/files/UbuntuOneFiles.java (+24/-3) src/com/ubuntuone/android/files/service/ISyncClient.aidl (+2/-1) src/com/ubuntuone/android/files/service/ISyncClientListener.java (+12/-0) src/com/ubuntuone/android/files/service/ISyncDaemon.aidl (+1/-1) src/com/ubuntuone/android/files/service/SyncClient.java (+209/-10) src/com/ubuntuone/android/files/service/SyncDaemon.java (+218/-122) src/com/ubuntuone/android/files/ui/CustomListActivity.java (+79/-4) src/com/ubuntuone/android/files/ui/SharesActivity.java (+1/-2) src/com/ubuntuone/android/files/ui/UdfsActivity.java (+1/-1) src/com/ubuntuone/android/files/util/CustomListActivityAdapter.java (+8/-6) src/com/ubuntuone/android/files/util/PathTool.java (+74/-34) |
To merge this branch: | bzr merge lp:~karni/ubuntuone-android-files/basic-upload |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Chad Miller (community) | Approve | ||
Review via email: mp+44621@code.launchpad.net |
Commit message
Description of the change
Upload fully functional, supporting uploads from MediaContentPro
Application needs little tweaks to support different media -- currently upload button provides picture upload functionality.
To post a comment you must log in.
Revision history for this message
Chad Miller (cmiller) : | # |
review:
Approve
Revision history for this message
Chad Miller (cmiller) wrote : | # |
- 11. By Michał Karnicki
-
SyncDaemon cleanup, some i18n
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file '.classpath' |
2 | --- .classpath 2010-12-10 17:40:16 +0000 |
3 | +++ .classpath 2010-12-24 00:07:14 +0000 |
4 | @@ -5,7 +5,7 @@ |
5 | <classpathentry kind="lib" path="libs/oauth-consumer-20100527.jar"/> |
6 | <classpathentry kind="lib" path="libs/signpost-commonshttp4-1.2.jar"/> |
7 | <classpathentry kind="lib" path="libs/signpost-core-1.2.jar"/> |
8 | - <classpathentry kind="lib" path="libs/storageprotocol-1.1.0-SNAPSHOT.jar"/> |
9 | + <classpathentry kind="lib" path="libs/storageprotocol-1.1.0-20101221.192202-1.jar"/> |
10 | <classpathentry kind="src" path="src"/> |
11 | <classpathentry kind="src" path="gen"/> |
12 | <classpathentry kind="lib" path="libs/protobuf-java-2.3.0.jar"/> |
13 | |
14 | === modified file 'res/layout/activity_list.xml' |
15 | --- res/layout/activity_list.xml 2010-12-10 17:40:16 +0000 |
16 | +++ res/layout/activity_list.xml 2010-12-24 00:07:14 +0000 |
17 | @@ -14,12 +14,11 @@ |
18 | android:visibility="gone" /> |
19 | |
20 | <ImageView style="@style/TitleBarSeparator" |
21 | - android:id="@+id/btn_title_share_separator" /> |
22 | + android:id="@+id/btn_title_upload_separator" /> |
23 | |
24 | <ImageButton style="@style/TitleBarAction" |
25 | - android:id="@+id/btn_title_share" |
26 | - android:src="@drawable/ic_title_share_default" |
27 | - android:onClick="onRefreshClick" /> |
28 | + android:id="@+id/btn_title_upload" |
29 | + android:src="@drawable/ic_title_share_default" /> |
30 | |
31 | <ImageView style="@style/TitleBarSeparator" |
32 | android:id="@+id/btn_title_search_separator" /> |
33 | |
34 | === modified file 'res/values/service.xml' |
35 | --- res/values/service.xml 2010-11-25 18:05:31 +0000 |
36 | +++ res/values/service.xml 2010-12-24 00:07:14 +0000 |
37 | @@ -1,4 +1,18 @@ |
38 | <?xml version="1.0" encoding="utf-8"?> |
39 | <resources> |
40 | - <string name="service_notification"></string> |
41 | + <!-- non-translatable --> |
42 | + <item type="id" name="connecting_notification_id" /> |
43 | + <item type="id" name="syncing_notification_id" /> |
44 | + <item type="id" name="downloading_notification_id" /> |
45 | + <item type="id" name="uploading_notification_id" /> |
46 | + |
47 | + <!-- translatable --> |
48 | + <string name="service_notification_title">Ubuntu One Service</string> |
49 | + <string name="connecting_notification_ticker">Connecting...</string> |
50 | + <string name="connecting_notification_subtitle">Connecting...</string> |
51 | + <string name="authenticating_notification_subtitle">Authenticating...</string> |
52 | + <string name="syncing_notification_ticker">Syncing...</string> |
53 | + <string name="syncing_notification_title">Ubuntu One Syncing</string> |
54 | + <string name="syncing_notification_subtitle">Syncing...</string> |
55 | + |
56 | </resources> |
57 | |
58 | === modified file 'res/values/strings.xml' |
59 | --- res/values/strings.xml 2010-12-10 17:40:16 +0000 |
60 | +++ res/values/strings.xml 2010-12-24 00:07:14 +0000 |
61 | @@ -4,8 +4,8 @@ |
62 | <string name="title_text">Ubuntu One Files</string> |
63 | |
64 | <!-- Login screen --> |
65 | - <string name="login_btn_text">Log in</string> |
66 | - <string name="login_desc_text">This is the description</string> |
67 | + <string name="login_btn_text">Launch the browser to log in</string> |
68 | + <string name="login_desc_text">The application will now use the browser to securely log you in to your personal cloud storage.</string> |
69 | |
70 | <!-- Dashboard items --> |
71 | <string name="btn_files">Files</string> |
72 | |
73 | === modified file 'src/com/ubuntuone/android/files/UbuntuOneFiles.java' |
74 | --- src/com/ubuntuone/android/files/UbuntuOneFiles.java 2010-12-21 16:16:54 +0000 |
75 | +++ src/com/ubuntuone/android/files/UbuntuOneFiles.java 2010-12-24 00:07:14 +0000 |
76 | @@ -196,11 +196,32 @@ |
77 | |
78 | } |
79 | |
80 | - public void onFileUploaded(long id) throws RemoteException { |
81 | -// Toast.makeText(CustomListActivity.this, id + " uploaded", |
82 | -// Toast.LENGTH_SHORT); |
83 | + public void onFileUploaded(final String filename) throws RemoteException { |
84 | + if (sForegroundActivity != null) { |
85 | + sForegroundActivity.runOnUiThread(new Runnable() { |
86 | + public void run() { |
87 | + Toast.makeText(sForegroundActivity, filename + " uploaded", |
88 | + Toast.LENGTH_SHORT).show(); |
89 | + if (sForegroundActivity instanceof CustomListActivity) { |
90 | + ((CustomListActivity)sForegroundActivity).refreshListAdapter(); |
91 | + } |
92 | + } |
93 | + }); |
94 | + } |
95 | } |
96 | |
97 | + public void onRefresh() throws RemoteException { |
98 | + if (sForegroundActivity != null) { |
99 | + sForegroundActivity.runOnUiThread(new Runnable() { |
100 | + public void run() { |
101 | + if (sForegroundActivity instanceof CustomListActivity) { |
102 | + ((CustomListActivity)sForegroundActivity).refreshListAdapter(); |
103 | + } |
104 | + } |
105 | + }); |
106 | + } |
107 | + } |
108 | + |
109 | public void onFileDownloaded(long id) throws RemoteException { |
110 | // Toast.makeText(CustomListActivity.this, id + " downloaded", |
111 | // Toast.LENGTH_SHORT); |
112 | |
113 | === modified file 'src/com/ubuntuone/android/files/service/ISyncClient.aidl' |
114 | --- src/com/ubuntuone/android/files/service/ISyncClient.aidl 2010-12-10 17:40:16 +0000 |
115 | +++ src/com/ubuntuone/android/files/service/ISyncClient.aidl 2010-12-24 00:07:14 +0000 |
116 | @@ -2,9 +2,10 @@ |
117 | |
118 | interface ISyncClient { |
119 | void onFileDownloaded(in long id); |
120 | - void onFileUploaded(in long id); |
121 | + void onFileUploaded(in String filename); |
122 | void onFilePublished(in long id); |
123 | void onFileUnpublished(in long id); |
124 | + void onRefresh(); |
125 | void onServiceIsWorking(); |
126 | void onServiceIsIdle(); |
127 | void onServiceIsWorkingLock(); |
128 | |
129 | === modified file 'src/com/ubuntuone/android/files/service/ISyncClientListener.java' |
130 | --- src/com/ubuntuone/android/files/service/ISyncClientListener.java 2010-12-21 16:16:54 +0000 |
131 | +++ src/com/ubuntuone/android/files/service/ISyncClientListener.java 2010-12-24 00:07:14 +0000 |
132 | @@ -42,8 +42,20 @@ |
133 | |
134 | public void onClientSetupFailed(final Throwable t); |
135 | |
136 | + public void onFileDownloaded(Long id); |
137 | + |
138 | + public void onFileDownloadFailed(final Throwable t); |
139 | + |
140 | + public void onFileUploading(final String filename); |
141 | + |
142 | + public void onFileUploaded(final String filename); |
143 | + |
144 | + public void onFileUploadFailed(final Throwable t); |
145 | + |
146 | public void onClientSyncing(); |
147 | |
148 | + public void onClientSyncingProgress(final int progress); |
149 | + |
150 | public void onClientSynced(); |
151 | |
152 | public void onClientSyncFailed(final Throwable t); |
153 | |
154 | === modified file 'src/com/ubuntuone/android/files/service/ISyncDaemon.aidl' |
155 | --- src/com/ubuntuone/android/files/service/ISyncDaemon.aidl 2010-12-21 15:40:59 +0000 |
156 | +++ src/com/ubuntuone/android/files/service/ISyncDaemon.aidl 2010-12-24 00:07:14 +0000 |
157 | @@ -11,5 +11,5 @@ |
158 | void unregisterCallback(in ISyncClient client); |
159 | |
160 | void downloadFile(in long id); |
161 | - void uploadFile(in Uri uri, in String path); |
162 | + void uploadFile(in Uri uri, in String volume, in String node); |
163 | } |
164 | |
165 | === modified file 'src/com/ubuntuone/android/files/service/SyncClient.java' |
166 | --- src/com/ubuntuone/android/files/service/SyncClient.java 2010-12-21 16:16:54 +0000 |
167 | +++ src/com/ubuntuone/android/files/service/SyncClient.java 2010-12-24 00:07:14 +0000 |
168 | @@ -22,9 +22,13 @@ |
169 | |
170 | package com.ubuntuone.android.files.service; |
171 | |
172 | +import java.io.ByteArrayInputStream; |
173 | +import java.io.ByteArrayOutputStream; |
174 | import java.io.File; |
175 | +import java.io.FileNotFoundException; |
176 | import java.io.FileOutputStream; |
177 | import java.io.IOException; |
178 | +import java.io.InputStream; |
179 | import java.net.URISyntaxException; |
180 | import java.util.ArrayList; |
181 | import java.util.Arrays; |
182 | @@ -39,6 +43,8 @@ |
183 | import android.content.Context; |
184 | import android.database.Cursor; |
185 | import android.net.Uri; |
186 | +import android.provider.MediaStore; |
187 | +import android.provider.MediaStore.MediaColumns; |
188 | import android.util.Config; |
189 | import android.util.Log; |
190 | import android.webkit.MimeTypeMap; |
191 | @@ -54,8 +60,13 @@ |
192 | import com.ubuntuone.android.files.provider.FilesContract.Volumes; |
193 | import com.ubuntuone.android.files.util.OAuthUtilities; |
194 | import com.ubuntuone.storageprotocol.Client; |
195 | +import com.ubuntuone.storageprotocol.HashUtils; |
196 | import com.ubuntuone.storageprotocol.IConnector; |
197 | +import com.ubuntuone.storageprotocol.HashUtils.HashInfo; |
198 | +import com.ubuntuone.storageprotocol.StorageProtocol.AccountInfo; |
199 | import com.ubuntuone.storageprotocol.StorageProtocol.NodeState; |
200 | +import com.ubuntuone.storageprotocol.StorageProtocol.VolumeDeleted; |
201 | +import com.ubuntuone.storageprotocol.StorageProtocol.VolumeNewGeneration; |
202 | import com.ubuntuone.storageprotocol.StorageProtocol.FileInfo.FileType; |
203 | import com.ubuntuone.storageprotocol.StorageProtocol.Volumes.VolumeType; |
204 | import com.ubuntuone.storageprotocol.request.GetContent; |
205 | @@ -68,7 +79,9 @@ |
206 | import com.ubuntuone.storageprotocol.request.ListVolumes.Root; |
207 | import com.ubuntuone.storageprotocol.request.ListVolumes.UDF; |
208 | import com.ubuntuone.storageprotocol.request.ListVolumes.Volume; |
209 | +import com.ubuntuone.storageprotocol.request.MakeObject.MakeFile; |
210 | import com.ubuntuone.storageprotocol.request.Query.QueryItem; |
211 | +import com.ubuntuone.storageprotocol.request.Request.ClientCallback; |
212 | |
213 | public class SyncClient extends Client { |
214 | private static final String TAG = SyncClient.class.getSimpleName(); |
215 | @@ -103,6 +116,7 @@ |
216 | this.mService = service; |
217 | this.mContext = UbuntuOneFiles.getInstance().getApplicationContext(); |
218 | this.mResolver = UbuntuOneFiles.getInstance().getContentResolver(); |
219 | + setVolumeNewGenerationCallback(volumeNewGenerationCallback); |
220 | } |
221 | |
222 | private boolean mIsConnecting = false; |
223 | @@ -230,8 +244,57 @@ |
224 | } |
225 | }); |
226 | } |
227 | + |
228 | + @Override |
229 | + public void setAccountInfoCallback( |
230 | + ClientCallback<AccountInfo> accountInfoCallback) { |
231 | + // TODO Auto-generated method stub |
232 | + super.setAccountInfoCallback(accountInfoCallback); |
233 | + } |
234 | + |
235 | + @Override |
236 | + public void setFreeSpaceCallback( |
237 | + ClientCallback<Map<Integer, Object>> freeSpaceCallback) { |
238 | + // TODO Auto-generated method stub |
239 | + super.setFreeSpaceCallback(freeSpaceCallback); |
240 | + } |
241 | + |
242 | + @Override |
243 | + public void setVolumeCreatedCallback( |
244 | + ClientCallback<com.ubuntuone.storageprotocol.StorageProtocol.Volumes> volumeCreatedCallback) { |
245 | + // TODO Auto-generated method stub |
246 | + super.setVolumeCreatedCallback(volumeCreatedCallback); |
247 | + } |
248 | + |
249 | + @Override |
250 | + public void setVolumeDeletedCallback( |
251 | + ClientCallback<VolumeDeleted> volumeDeletedCallback) { |
252 | + // TODO Auto-generated method stub |
253 | + super.setVolumeDeletedCallback(volumeDeletedCallback); |
254 | + } |
255 | + |
256 | + @Override |
257 | + public void setVolumeNewGenerationCallback( |
258 | + ClientCallback<VolumeNewGeneration> volumeNewGenerationCallback) { |
259 | + super.setVolumeNewGenerationCallback(volumeNewGenerationCallback); |
260 | + |
261 | + } |
262 | + |
263 | + private ClientCallback<VolumeNewGeneration> volumeNewGenerationCallback |
264 | + = new ClientCallback<VolumeNewGeneration>() { |
265 | + |
266 | + public void callback(VolumeNewGeneration newGeneration) { |
267 | + Log.d(TAG, "volume " |
268 | + + newGeneration.getVolume().toStringUtf8() |
269 | + + " at generation " + newGeneration.getGeneration()); |
270 | + } |
271 | + |
272 | + }; |
273 | + |
274 | + private AtomicInteger volumesSyncing = new AtomicInteger(0); |
275 | |
276 | public void getVolumes() { |
277 | + mService.onClientSyncing(); |
278 | final Deferred d = listVolumes().getDeferred(); |
279 | d.addCallbacks(new Callback<ListVolumes>() { |
280 | public Object callback(ListVolumes o) throws Exception { |
281 | @@ -287,6 +350,51 @@ |
282 | }); |
283 | } |
284 | |
285 | + /** |
286 | + * The client has been notified about content change, |
287 | + * verify and sync if necessary. |
288 | + */ |
289 | + @Override |
290 | + public void notifyNodeState(NodeState message) { |
291 | + super.notifyNodeState(message); |
292 | + Log.d(TAG, "notifyNodeState " + message); |
293 | +// getVolumes(); |
294 | + |
295 | +// final NodeState nodeState = message; |
296 | +// final String volume = nodeState.getShare().toStringUtf8(); |
297 | +// final String node = nodeState.getNode().toStringUtf8(); |
298 | +// final String hash = nodeState.getHash().toStringUtf8(); |
299 | +// String currentHash = null; |
300 | +// String type = null; |
301 | +// if (LOGI) Log.i(TAG, "received nodeState for " + node); |
302 | +// |
303 | +// Cursor c = FilesUtilities.getFile(mResolver, volume, node); |
304 | +// if (c.moveToNext()) { |
305 | +// currentHash = c.getString(c.getColumnIndex(Files.HASH)); |
306 | +// if (currentHash == null) |
307 | +// currentHash = ""; |
308 | +// type = c.getString(c.getColumnIndex(Files.TYPE)); |
309 | +// } |
310 | +// c.close(); |
311 | +// |
312 | +// if (FileType.DIRECTORY.toString().equals(type)) { |
313 | +// // update cached directory, if necessary |
314 | +// if (currentHash.equals(hash) == false) { |
315 | +// mService. |
316 | +// if (nodeStateNotifications.contains(node) == false) { |
317 | +// nodeStateNotifications.add(node); |
318 | +// service.updateDirContents(node); |
319 | +// } |
320 | +// } |
321 | +// } else { |
322 | +// if (currentHash.equals(hash) == false) { |
323 | +// if (LOGI) Log.v(TAG, "download node " + node); |
324 | +// //service.downloadFile(cfData.getLong(CloudFiles._ID)); |
325 | +// //updateDirContents(node); |
326 | +// } |
327 | +// } |
328 | + } |
329 | + |
330 | // ========== update volume entry ========== |
331 | |
332 | private void updateRoot(final Root root) |
333 | @@ -396,14 +504,13 @@ |
334 | } |
335 | if (id != null) { |
336 | // Remove meta. |
337 | - mResolver.delete(Files.CONTENT_URI, Files._ID + "=?", |
338 | - new String[]{id}); |
339 | + mResolver.delete(Files.buildFileUri(id), null, null); |
340 | } |
341 | return; |
342 | } |
343 | |
344 | // If this is an unnamed node, it's a volume root - give it a name. |
345 | - if (name == null) {// || name.length() == 0) { |
346 | + if (name == null || name.length() == 0) { |
347 | Volume v = sVolumes.get(volume); |
348 | if (v instanceof Root) { |
349 | parent = VolumeType.ROOT.toString(); |
350 | @@ -418,8 +525,7 @@ |
351 | name = ((Share)v).getName(); |
352 | } |
353 | } |
354 | - // Escape any left slashes. |
355 | - //name.replace("/", "//"); |
356 | + |
357 | |
358 | Cursor c = FilesUtilities.getFile(mResolver, volume, node); |
359 | final ContentValues values = Files.getContentValues( |
360 | @@ -478,7 +584,7 @@ |
361 | parentC.close(); |
362 | values.remove(Files.DATA); |
363 | values.put(Files.DATA, parentPath + "/" + name); |
364 | - Log.d(TAG, ">>> " + values.getAsString(Files.DATA)); |
365 | + //Log.d(TAG, ">>> " + values.getAsString(Files.DATA)); |
366 | |
367 | // Insert file entry. |
368 | mResolver.insert(Files.CONTENT_URI, values); |
369 | @@ -576,12 +682,12 @@ |
370 | }); |
371 | d.addCallbacks(new Callback<Object>() { |
372 | public Object callback(Object o) throws Exception { |
373 | - mService.onDownloadedFile(id); |
374 | + mService.onFileDownloaded(id); |
375 | return null; |
376 | } |
377 | }, new Callback<Failure>() { |
378 | public Object callback(Failure f) throws Exception { |
379 | - mService.onDownloadFileFailed(f.getCause()); |
380 | + mService.onFileDownloadFailed(f.getCause()); |
381 | return null; |
382 | } |
383 | }); |
384 | @@ -595,10 +701,95 @@ |
385 | |
386 | // ========== end of download file ========== |
387 | |
388 | + // ========== upload file ========== |
389 | + |
390 | + public void uploadFile(final Uri uri, final String volume, final String parent) { |
391 | + if (LOGD) Log.d(TAG, "uploadFile"); |
392 | + if (LOGV) Log.v(TAG, uri + " under " + parent); |
393 | + Context ctx = UbuntuOneFiles.getInstance(); |
394 | + InputStream inputStream = null; |
395 | + try { |
396 | + inputStream = ctx.getContentResolver().openInputStream(uri); |
397 | + } catch (FileNotFoundException e) { |
398 | + mService.onFileUploadFailed(e.getCause()); |
399 | + return; |
400 | + } |
401 | + final InputStream fInputStream = inputStream; |
402 | + |
403 | + /* |
404 | + * TODO |
405 | + * - possibly speed this up |
406 | + * - add custom file upload |
407 | + */ |
408 | + Uri fileSource = null; |
409 | + String uriStr = uri.toString(); |
410 | + if (uriStr.startsWith( |
411 | + MediaStore.Images.Media.EXTERNAL_CONTENT_URI.toString())) |
412 | + fileSource = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; |
413 | + else if (uriStr.startsWith( |
414 | + MediaStore.Video.Media.EXTERNAL_CONTENT_URI.toString())) |
415 | + fileSource = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; |
416 | + else if (uriStr.startsWith( |
417 | + MediaStore.Audio.Media.EXTERNAL_CONTENT_URI.toString())) |
418 | + fileSource = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; |
419 | + else fileSource = uri; |
420 | + |
421 | + String[] projection = new String[] { MediaColumns._ID, |
422 | + MediaColumns.DISPLAY_NAME, MediaColumns.SIZE }; |
423 | + |
424 | + Cursor c = ctx.getContentResolver() |
425 | + .query(fileSource, |
426 | + projection, |
427 | + MediaColumns._ID + " = ?", |
428 | + new String[] { uri.getLastPathSegment() }, |
429 | + null); |
430 | + |
431 | + String _filename = null; |
432 | + //long size = -1; |
433 | + if (c.moveToFirst()) { |
434 | + _filename = c.getString(c.getColumnIndex(MediaColumns.DISPLAY_NAME)); |
435 | + //size = c.getLong(c.getColumnIndex(MediaColumns.SIZE)); |
436 | + } |
437 | + c.close(); |
438 | + if (_filename == null) |
439 | + mService.onFileUploadFailed(new Throwable( |
440 | + "could not get filename for upload")); |
441 | + |
442 | + if (LOGV) Log.v(TAG, "from media store: " + _filename); |
443 | + final String filename = _filename; |
444 | + |
445 | + final Deferred d = makeFile(volume, parent, _filename).getDeferred(); |
446 | + d.addCallback(new Callback<MakeFile>() { |
447 | + public Object callback(MakeFile req) throws Exception { |
448 | + if (LOGV) Log.v(TAG, "new file id: " + req.getNewId()); |
449 | + ByteArrayOutputStream os = new ByteArrayOutputStream(); |
450 | + HashInfo hashInfo = HashUtils.getHashInfo(fInputStream, os); |
451 | + InputStream deflated = new ByteArrayInputStream(os.toByteArray()); |
452 | + return putContent(volume, req.getNewId(), "", |
453 | + hashInfo.hash, hashInfo.crc32, hashInfo.size, |
454 | + hashInfo.deflatedSize, deflated).getDeferred(); |
455 | + } |
456 | + }); |
457 | + d.addCallbacks(new Callback<Object>() { |
458 | + public Object callback(Object o) throws Exception { |
459 | + mService.onFileUploaded(filename); |
460 | + return null; |
461 | + } |
462 | + }, new Callback<Failure>() { |
463 | + public Object callback(Failure f) throws Exception { |
464 | + mService.onFileUploadFailed(f.getCause()); |
465 | + return null; |
466 | + } |
467 | + }); |
468 | + } |
469 | + |
470 | + // ========== end of upload file ========== |
471 | + |
472 | // ========== sync volume ========== |
473 | |
474 | private void syncVolume(final Volume volume, final Boolean fromScratch) { |
475 | final String volumeId = volume.getVolumeId(); |
476 | + volumesSyncing.incrementAndGet(); |
477 | Log.d(TAG, "syncVolume(volume = " + volumeId |
478 | + ", fromScratch = " + fromScratch + ")"); |
479 | // retrieve known generation, if any |
480 | @@ -624,17 +815,25 @@ |
481 | |
482 | d.addCallback(new Callback<GetDelta>() { |
483 | public Object callback(final GetDelta getDelta) throws Exception { |
484 | - for (FileInfoDelta fileInfo : getDelta.getResponse()) { |
485 | - Log.d(TAG, fileInfo.getName() |
486 | + final List<FileInfoDelta> fileInfoList = getDelta.getResponse(); |
487 | + final int size = fileInfoList.size(); |
488 | + int progress = 0; |
489 | + for (FileInfoDelta fileInfo : fileInfoList) { |
490 | + progress++; |
491 | + if (LOGV) Log.d(TAG, fileInfo.getName() |
492 | + ", parent_id: " + fileInfo.getParentId().toStringUtf8() |
493 | + ", node_id: " + fileInfo.getNodeId().toStringUtf8() |
494 | + ", is_live: " + fileInfo.isLive() |
495 | + ", size: " + fileInfo.getSize()); |
496 | + mService.onClientSyncingProgress(100*progress/size); |
497 | updateFile(fileInfo); |
498 | } |
499 | if (LOGD) Log.d(TAG, "End generation: " + getDelta.getEndGeneration() |
500 | + ", free_bytes: " + getDelta.getFreeBytes() |
501 | + ", full delta: " + getDelta.isFull()); |
502 | + int vs = volumesSyncing.decrementAndGet(); |
503 | + if (vs == 0) |
504 | + mService.onClientSynced(); |
505 | return getDelta; |
506 | } |
507 | }); |
508 | |
509 | === modified file 'src/com/ubuntuone/android/files/service/SyncDaemon.java' |
510 | --- src/com/ubuntuone/android/files/service/SyncDaemon.java 2010-12-21 16:16:54 +0000 |
511 | +++ src/com/ubuntuone/android/files/service/SyncDaemon.java 2010-12-24 00:07:14 +0000 |
512 | @@ -27,223 +27,320 @@ |
513 | import android.app.PendingIntent; |
514 | import android.app.Service; |
515 | import android.content.Intent; |
516 | -import android.net.ConnectivityManager; |
517 | import android.net.Uri; |
518 | import android.os.IBinder; |
519 | -import android.os.Message; |
520 | import android.os.RemoteException; |
521 | import android.util.Config; |
522 | import android.util.Log; |
523 | import android.widget.Toast; |
524 | |
525 | +import com.ubuntuone.android.files.Preferences; |
526 | import com.ubuntuone.android.files.R; |
527 | import com.ubuntuone.android.files.UbuntuOneFiles; |
528 | import com.ubuntuone.android.files.ui.DashboardActivity; |
529 | import com.ubuntuone.storageprotocol.IConnector; |
530 | import com.ubuntuone.storageprotocol.oio.OIOConnector; |
531 | |
532 | -@SuppressWarnings("unused") |
533 | +/* |
534 | + * TODO: |
535 | + * - extend mClientCallback interface to provide more feedback from the service, |
536 | + * majority of TODOs in this file should be simple calls to better callback |
537 | + * interface. |
538 | + */ |
539 | public final class SyncDaemon extends Service implements ISyncClientListener { |
540 | |
541 | private static final String TAG = SyncDaemon.class.getSimpleName(); |
542 | |
543 | - private static boolean LOGV = Config.DEBUG || Log.isLoggable(TAG, Log.VERBOSE); |
544 | private static boolean LOGD = Config.DEBUG || Log.isLoggable(TAG, Log.DEBUG); |
545 | private static boolean LOGI = Config.DEBUG || Log.isLoggable(TAG, Log.INFO); |
546 | - private static boolean LOGW = Config.DEBUG || Log.isLoggable(TAG, Log.WARN); |
547 | + private static boolean LOGW = Config.DEBUG || Log.isLoggable(TAG, Log.ERROR); |
548 | private static boolean LOGE = Config.DEBUG || Log.isLoggable(TAG, Log.ERROR); |
549 | |
550 | - // Ubuntu One cloud storage client |
551 | + /** |
552 | + * Ubuntu One storage protocol client. |
553 | + */ |
554 | private SyncClient mClient; |
555 | |
556 | - // Activity callback interface |
557 | + /** |
558 | + * OIO connector used by {@link SyncClient} |
559 | + */ |
560 | + private IConnector mConnector; |
561 | + |
562 | + /** |
563 | + * Callback interface to running {@link UbuntuOneFiles} instance. |
564 | + */ |
565 | private ISyncClient mClientCallback; |
566 | |
567 | - private IConnector mConnector; |
568 | |
569 | public static final String ACTION_START = |
570 | "com.ubuntuone.android.files.service.ACTION_START"; |
571 | public static final String ACTION_STOP = |
572 | "com.ubuntuone.android.files.service.ACTION_STOP"; |
573 | |
574 | - private ConnectivityManager mCM; |
575 | + /** |
576 | + * A {@link PendingIntent} used after tap on general notifications. |
577 | + */ |
578 | + private PendingIntent mDashboardIntent; |
579 | + |
580 | + /** |
581 | + * {@link NotificationManager} used for connecting, syncing, uploading and |
582 | + * downloading notifications. |
583 | + */ |
584 | private NotificationManager mNM; |
585 | - private Notification mConnectionNotification; |
586 | - private static int CONNECTION_NOTIFICATION = 100; |
587 | - private Notification mTransferNotification; |
588 | - private static int TRANSFER_NOTIFICATION = 200; |
589 | + |
590 | + private Notification mConnectingNotification; |
591 | + private Notification mSyncingNotification; |
592 | + |
593 | + /* |
594 | + * Notification ID's used to track above notifications. |
595 | + */ |
596 | + private static int CONNECTING_NOTIFICATION_ID = |
597 | + R.id.connecting_notification_id; |
598 | + private static int SYNCING_NOTIFICATION_ID = |
599 | + R.id.syncing_notification_id; |
600 | |
601 | @Override |
602 | public void onCreate() { |
603 | super.onCreate(); |
604 | + if (LOGI) Log.i(TAG, "starting Ubuntu One service"); |
605 | + |
606 | + // Initialize notofication manager. |
607 | mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); |
608 | - mCM = (ConnectivityManager)getSystemService(CONNECTIVITY_SERVICE); |
609 | |
610 | + // Setup U1 storage protocol client. |
611 | mConnector = new OIOConnector(true); |
612 | mClient = new SyncClient(this, mConnector); |
613 | |
614 | -// IntentFilter intentFilter = new IntentFilter(); |
615 | -// intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); |
616 | -// intentFilter.addAction(ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED); |
617 | -// registerReceiver(mNetworkObserver, intentFilter); |
618 | + // Setup intent fired on general notification tap. |
619 | + mDashboardIntent = PendingIntent.getActivity(this, 0, |
620 | + new Intent(this, DashboardActivity.class), 0); |
621 | |
622 | + // Connect. |
623 | + // TODO U1F: Connection settings. |
624 | connect(); |
625 | } |
626 | - |
627 | - public SyncClient getSyncClient() { |
628 | - return mClient; |
629 | - } |
630 | |
631 | public boolean isWorking() { |
632 | - return (mClient != null) ? mClient.mTasksRunning.get() > 0 : false; |
633 | + return mClient.mTasksRunning.get() > 0; |
634 | } |
635 | |
636 | public void connect() { |
637 | - if (mConnectionNotification == null) { |
638 | - mConnectionNotification = new Notification(R.drawable.launcher, |
639 | - "Connecting...", System.currentTimeMillis()); |
640 | - PendingIntent contentIntent = PendingIntent.getActivity(this, 0, |
641 | - new Intent(this, DashboardActivity.class), 0); |
642 | - mConnectionNotification.setLatestEventInfo(this, |
643 | - "Ubuntu One Service", "Connecting...", contentIntent); |
644 | + if (mConnectingNotification == null) { |
645 | + mConnectingNotification = new Notification( |
646 | + R.drawable.launcher, |
647 | + getText(R.string.connecting_notification_ticker), |
648 | + System.currentTimeMillis()); |
649 | } |
650 | + mConnectingNotification.setLatestEventInfo(this, |
651 | + getText(R.string.service_notification_title), |
652 | + getText(R.string.connecting_notification_subtitle), |
653 | + mDashboardIntent); |
654 | mClient.connectClient(); |
655 | } |
656 | |
657 | - private boolean maybeReconnect() { |
658 | - if (mConnector.isConnected() == false) { |
659 | - mClient.connectClient(); |
660 | - return true; |
661 | - } |
662 | - return false; |
663 | - } |
664 | - |
665 | public boolean isConnected() { |
666 | return mClient.isConnected(); |
667 | } |
668 | |
669 | - public void authenticte() { |
670 | - PendingIntent contentIntent = PendingIntent.getActivity(this, 0, |
671 | - new Intent(this, DashboardActivity.class), 0); |
672 | - mConnectionNotification.setLatestEventInfo(this, |
673 | - "Ubuntu One Service", "Authenticating...", contentIntent); |
674 | - mNM.notify(CONNECTION_NOTIFICATION, mConnectionNotification); |
675 | - mClient.authenticateClient(); |
676 | - } |
677 | - |
678 | - public void setup() { |
679 | - mClient.setupClient(); |
680 | - } |
681 | - |
682 | - public void getVolumes() { |
683 | - mClient.getVolumes(); |
684 | - } |
685 | - |
686 | - public void disconect() { |
687 | - mClient.disconnectClient(); |
688 | - } |
689 | - |
690 | - private void downloadFile(Long id) { |
691 | - if (maybeReconnect()) |
692 | - return; |
693 | - mClient.downloadFile(id); |
694 | - } |
695 | - |
696 | - public void onDownloadedFile(long id) { |
697 | - if (LOGD) Log.d(TAG, "onDownloadedFile"); |
698 | - try { |
699 | - mClientCallback.onFileDownloaded(id); |
700 | - } catch (RemoteException e) { |
701 | - if (LOGE) Log.e(TAG, "", e); |
702 | - } |
703 | - } |
704 | - |
705 | - public void onDownloadFileFailed(Throwable eek) { |
706 | - if (LOGD) Log.d(TAG, "onDownloadFileFailed"); |
707 | - if (LOGE) Log.e(TAG, "", eek); |
708 | - } |
709 | - |
710 | - public void uploadFile(Uri uri, String path) { |
711 | - |
712 | - } |
713 | - |
714 | public void onClientConnecting() { |
715 | - Log.d(TAG, "onClientConnecting"); |
716 | - mNM.notify(CONNECTION_NOTIFICATION, mConnectionNotification); |
717 | + if (LOGD) Log.d(TAG, "onClientConnecting"); |
718 | + mNM.notify(CONNECTING_NOTIFICATION_ID, mConnectingNotification); |
719 | } |
720 | |
721 | public void onClientConnected() { |
722 | - Log.d(TAG, "onClientConnected"); |
723 | + if (LOGD) Log.d(TAG, "onClientConnected"); |
724 | + if (LOGI) Log.i(TAG, "Ubuntu One service has connected"); |
725 | authenticte(); |
726 | } |
727 | |
728 | public void onClientConnectFailed(final Throwable t) { |
729 | - mNM.cancel(CONNECTION_NOTIFICATION); |
730 | - Log.d(TAG, "onClientConnectFailed"); |
731 | + Log.d(TAG, "onClientConnectFailed", t); |
732 | + mNM.cancel(CONNECTING_NOTIFICATION_ID); |
733 | + // TODO Notify UbuntuOneFiles about failed connection attempt. |
734 | + } |
735 | + |
736 | + public void authenticte() { |
737 | + mConnectingNotification.setLatestEventInfo(this, |
738 | + getText(R.string.service_notification_title), |
739 | + getText(R.string.authenticating_notification_subtitle), |
740 | + mDashboardIntent); |
741 | + mClient.authenticateClient(); |
742 | + } |
743 | + |
744 | + public boolean isAuthenticated() { |
745 | + return mClient.isAuthenticated(); |
746 | } |
747 | |
748 | public void onClientAuthenticating() { |
749 | - Log.d(TAG, "onClientAuthenticating"); |
750 | + if (LOGD) Log.d(TAG, "onClientAuthenticating"); |
751 | + // Show notification with updated subtitle. |
752 | + mNM.notify(CONNECTING_NOTIFICATION_ID, mConnectingNotification); |
753 | } |
754 | |
755 | public void onClientAuthenticated() { |
756 | - Log.d(TAG, "onClientAuthenticated"); |
757 | - mNM.cancel(CONNECTION_NOTIFICATION); |
758 | + if (LOGD) Log.d(TAG, "onClientAuthenticated"); |
759 | + if (LOGI) Log.i(TAG, "Ubuntu One service has authenticated"); |
760 | + mNM.cancel(CONNECTING_NOTIFICATION_ID); |
761 | setup(); |
762 | } |
763 | |
764 | private int mAuthsFailed = 0; |
765 | |
766 | public void onClientAuthenticateFailed(final Throwable t) { |
767 | - Log.d(TAG, "onClientAuthenticatedFailed"); |
768 | + if (LOGD) Log.d(TAG, "onClientAuthenticatedFailed"); |
769 | + if (LOGW) Log.w(TAG, "Ubuntu One service failed to authenticate", t); |
770 | mAuthsFailed++; |
771 | if (UbuntuOneFiles.sForegroundActivity != null) { |
772 | Toast.makeText(UbuntuOneFiles.sForegroundActivity, |
773 | "Authentication failed.\nRetrying...", Toast.LENGTH_SHORT) |
774 | .show(); |
775 | - Message m; |
776 | - } |
777 | - // TODO Notify any app instances about the failure |
778 | - } |
779 | - |
780 | - public void onClientDisconnecting() { |
781 | - Log.d(TAG, "onClientDisconnecting"); |
782 | - } |
783 | - |
784 | - public void onClientDisconnected() { |
785 | - Log.d(TAG, "onClientDisconnected"); |
786 | - } |
787 | - |
788 | - public void onClientDisconnectFailed(final Throwable t) { |
789 | - Log.d(TAG, "onClientDisconnectFailed"); |
790 | - // TODO Notify any app instances about the failure |
791 | + } |
792 | + if (mAuthsFailed > 3) { |
793 | + if (LOGE) Log.e(TAG, "Ubuntu One service auth failed, " + |
794 | + "requesting re-auth"); |
795 | + Preferences.updateAccessToken(null, null); |
796 | + disconect(); |
797 | + // TODO Notify UbuntuOneFiles instance about need to re-auth. |
798 | + } |
799 | + authenticte(); |
800 | + } |
801 | + |
802 | + public void setup() { |
803 | + mClient.setupClient(); |
804 | } |
805 | |
806 | public void onClientSetup() { |
807 | - Log.d(TAG, "onClientSetup"); |
808 | + if (LOGD) Log.d(TAG, "onClientSetup"); |
809 | } |
810 | |
811 | public void onClientSetupDone() { |
812 | - Log.d(TAG, "onClientSetupDone"); |
813 | - getVolumes(); |
814 | + if (LOGD) Log.d(TAG, "onClientSetupDone"); |
815 | + sync(); |
816 | } |
817 | |
818 | public void onClientSetupFailed(Throwable t) { |
819 | - Log.d(TAG, "onClientSetupFailed"); |
820 | - // TODO Notify any app instances about the failure |
821 | - } |
822 | - |
823 | + if (LOGD) Log.d(TAG, "onClientSetupFailed"); |
824 | + if (LOGE) Log.e(TAG, "Ubuntu One service failed to setup", t); |
825 | + disconect(); |
826 | + // TODO Notify UbuntuOneFiles instance about the setup issue. |
827 | + } |
828 | + |
829 | + public void sync() { |
830 | + mClient.getVolumes(); |
831 | + } |
832 | + |
833 | public void onClientSyncing() { |
834 | - Log.d(TAG, "onClientSyncing"); |
835 | + if (LOGD) Log.d(TAG, "onClientSyncing"); |
836 | + if (LOGI) Log.i(TAG, "Ubuntu One service is synchronizing"); |
837 | + if (mSyncingNotification == null) { |
838 | + mSyncingNotification = new Notification( |
839 | + R.drawable.launcher, |
840 | + getText(R.string.syncing_notification_ticker), |
841 | + System.currentTimeMillis()); |
842 | + } |
843 | + mSyncingNotification.setLatestEventInfo(this, |
844 | + getText(R.string.service_notification_title), |
845 | + getText(R.string.syncing_notification_subtitle), |
846 | + mDashboardIntent); |
847 | + mNM.notify(SYNCING_NOTIFICATION_ID, mSyncingNotification); |
848 | } |
849 | |
850 | + public void onClientSyncingProgress(int progress) { |
851 | + String status = String.format("Progress (per volume): %d %%", progress); |
852 | + // mSyncingNotification is not null, onClientSyncing has been called |
853 | + mSyncingNotification.setLatestEventInfo(this, |
854 | + getText(R.string.syncing_notification_title), |
855 | + status, |
856 | + mDashboardIntent); |
857 | + mNM.notify(SYNCING_NOTIFICATION_ID, mSyncingNotification); |
858 | + } |
859 | + |
860 | public void onClientSynced() { |
861 | - Log.d(TAG, "onClientSynced"); |
862 | + if (LOGD) Log.d(TAG, "onClientSynced"); |
863 | + if (LOGI) Log.i(TAG, "Ubuntu One service has synchronized"); |
864 | + mNM.cancel(SYNCING_NOTIFICATION_ID); |
865 | + try { |
866 | + // FIXME Adjust mCallbackInterface, as written in top TODO. |
867 | + mClientCallback.onRefresh(); |
868 | + } catch (RemoteException e) { |
869 | + if (LOGE) Log.e(TAG, "", e); |
870 | + } |
871 | } |
872 | |
873 | public void onClientSyncFailed(final Throwable t) { |
874 | - Log.d(TAG, "onClientSyncFailed"); |
875 | - // TODO Notify any app instances about the failure |
876 | + if (LOGD) Log.d(TAG, "onClientSyncFailed"); |
877 | + if (LOGE) Log.e(TAG, "Ubuntu One service failed to synchronize", t); |
878 | + mNM.cancel(SYNCING_NOTIFICATION_ID); |
879 | + // TODO Notify any app instances about the sync failure. |
880 | + } |
881 | + |
882 | + public void disconect() { |
883 | + mClient.disconnectClient(); |
884 | + } |
885 | + |
886 | + public void onClientDisconnecting() { |
887 | + if (LOGD) Log.d(TAG, "onClientDisconnecting"); |
888 | + // XXX Show notification about disconnection? |
889 | + } |
890 | + |
891 | + public void onClientDisconnected() { |
892 | + if (LOGD) Log.d(TAG, "onClientDisconnected"); |
893 | + if (LOGI) Log.i(TAG, "Ubuntu One service has disconnected"); |
894 | + } |
895 | + |
896 | + public void onClientDisconnectFailed(final Throwable t) { |
897 | + if (LOGD) Log.d(TAG, "onClientDisconnectFailed"); |
898 | + if (LOGE) Log.e(TAG, "Ubuntu One service failed to disconnect", t); |
899 | + // TODO Notify app instance about the disconnection failure. |
900 | + } |
901 | + |
902 | + private void downloadFile(Long id) { |
903 | + // XXX Should we limit the loggin overhead? This is kinda verbose. |
904 | + if (LOGI) Log.i(TAG, "downloadFile, id=" + id); |
905 | + mClient.downloadFile(id); |
906 | + } |
907 | + |
908 | + public void onFileDownloaded(Long id) { |
909 | + if (LOGD) Log.d(TAG, "onDownloadedFile"); |
910 | + if (LOGI) Log.i(TAG, "downloaded file, id=" + id); |
911 | + try { |
912 | + mClientCallback.onFileDownloaded(id); |
913 | + } catch (RemoteException e) { |
914 | + if (LOGE) Log.e(TAG, "", e); |
915 | + } |
916 | + } |
917 | + |
918 | + public void onFileDownloadFailed(Throwable t) { |
919 | + if (LOGD) Log.d(TAG, "onFileDownloadFailed"); |
920 | + if (LOGE) Log.e(TAG, "Ubuntu One service failed to download file", t); |
921 | + // TODO Notify app instance about file download failure. |
922 | + } |
923 | + |
924 | + public void uploadFile(Uri uri, String volume, String parent) { |
925 | + if (LOGD) Log.d(TAG, "uploadFile"); |
926 | + if (LOGI) Log.i(TAG, "uploadFile " + uri.toString()); |
927 | + mClient.uploadFile(uri, volume, parent); |
928 | + } |
929 | + |
930 | + public void onFileUploading(String filename) { |
931 | + if (LOGD) Log.d(TAG, "onFileUploading"); |
932 | + if (LOGI) Log.i(TAG, "uploading " + filename); |
933 | + } |
934 | + |
935 | + public void onFileUploaded(String filename) { |
936 | + if (LOGD) Log.d(TAG, "onFileUploaded"); |
937 | + if (LOGI) Log.i(TAG, "uploaded " + filename); |
938 | + try { |
939 | + mClientCallback.onFileUploaded(filename); |
940 | + } catch (RemoteException e) { |
941 | + if (LOGE) Log.e(TAG, "", e); |
942 | + } |
943 | + } |
944 | + |
945 | + public void onFileUploadFailed(Throwable t) { |
946 | + if (LOGD) Log.d(TAG, "onFileUploadFailed"); |
947 | + if (LOGE) Log.e(TAG, "failed to upload file", t); |
948 | + // TODO Notify app instance about file upload failure. |
949 | } |
950 | |
951 | @Override |
952 | @@ -271,7 +368,6 @@ |
953 | |
954 | public void registerCallback(ISyncClient client) throws RemoteException { |
955 | SyncDaemon.this.mClientCallback = client; |
956 | - maybeReconnect(); |
957 | if (mClient.mTasksRunning.get() > 0) |
958 | mClientCallback.onServiceIsWorking(); |
959 | } |
960 | @@ -286,8 +382,8 @@ |
961 | SyncDaemon.this.downloadFile(id); |
962 | } |
963 | |
964 | - public void uploadFile(Uri uri, String node) throws RemoteException { |
965 | - SyncDaemon.this.uploadFile(uri, node); |
966 | + public void uploadFile(Uri uri, String volume, String parent) throws RemoteException { |
967 | + SyncDaemon.this.uploadFile(uri, volume, parent); |
968 | } |
969 | }; |
970 | |
971 | |
972 | === modified file 'src/com/ubuntuone/android/files/ui/CustomListActivity.java' |
973 | --- src/com/ubuntuone/android/files/ui/CustomListActivity.java 2010-12-21 16:16:54 +0000 |
974 | +++ src/com/ubuntuone/android/files/ui/CustomListActivity.java 2010-12-24 00:07:14 +0000 |
975 | @@ -35,6 +35,7 @@ |
976 | import android.util.Log; |
977 | import android.view.KeyEvent; |
978 | import android.view.View; |
979 | +import android.view.View.OnClickListener; |
980 | import android.widget.ImageButton; |
981 | import android.widget.ImageView; |
982 | import android.widget.ListAdapter; |
983 | @@ -49,6 +50,7 @@ |
984 | import com.ubuntuone.android.files.provider.FilesContract.Files; |
985 | import com.ubuntuone.android.files.provider.FilesContract.FilesStatus; |
986 | import com.ubuntuone.android.files.util.CustomListActivityAdapter; |
987 | +import com.ubuntuone.storageprotocol.StorageProtocol.Volumes.VolumeType; |
988 | |
989 | public abstract class CustomListActivity extends ListActivity { |
990 | private static final String TAG = CustomListActivity.class.getSimpleName(); |
991 | @@ -71,6 +73,21 @@ |
992 | TextView titleText = (TextView)findViewById(R.id.title_text); |
993 | titleText.setText(getActionBarTitle()); |
994 | registerForContextMenu(getListView()); |
995 | + |
996 | + ImageButton upload = (ImageButton) findViewById(R.id.btn_title_upload); |
997 | + upload.setOnClickListener(new OnClickListener() { |
998 | + public void onClick(View v) { |
999 | + // FIXME just hide the upload button where appropriate |
1000 | + if (mAdapter.getPathTool().isAtRoot() && |
1001 | + !VolumeType.ROOT.equals( |
1002 | + mAdapter.getPathTool().getType())) { |
1003 | + Toast.makeText(CustomListActivity.this, |
1004 | + "Can't upload here", Toast.LENGTH_SHORT).show(); |
1005 | + } else { |
1006 | + onUploadImage(); |
1007 | + } |
1008 | + } |
1009 | + }); |
1010 | } |
1011 | |
1012 | @Override |
1013 | @@ -102,12 +119,72 @@ |
1014 | }); |
1015 | } |
1016 | |
1017 | + public void onUploadImage() { |
1018 | + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); |
1019 | + intent.setType("image/*"); |
1020 | + |
1021 | + Intent chooser = Intent.createChooser(intent, "Pick image with"); |
1022 | + startActivityForResult(chooser, REQUEST_GET_FILE); |
1023 | + } |
1024 | + |
1025 | + public void onUploadVideo() { |
1026 | + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); |
1027 | + intent.setType("video/*"); |
1028 | + |
1029 | + Intent chooser = Intent.createChooser(intent, "Pick video with"); |
1030 | + startActivityForResult(chooser, REQUEST_GET_FILE); |
1031 | + } |
1032 | + |
1033 | + public void onUploadAudio() { |
1034 | + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); |
1035 | + intent.setType("audio/*"); |
1036 | + |
1037 | + Intent chooser = Intent.createChooser(intent, "Pick audio with"); |
1038 | + startActivityForResult(chooser, REQUEST_GET_FILE); |
1039 | + } |
1040 | + |
1041 | + private static final int REQUEST_GET_FILE = 1; |
1042 | + |
1043 | + @Override |
1044 | + protected void onActivityResult(int requestCode, int resultCode, Intent data) { |
1045 | + if (LOGD) |
1046 | + Log.d(TAG, "onActivityResult"); |
1047 | + super.onActivityResult(requestCode, resultCode, data); |
1048 | + |
1049 | + switch (requestCode) { |
1050 | + case REQUEST_GET_FILE: |
1051 | + if (resultCode == RESULT_OK && data != null) { |
1052 | + final Uri uri = data.getData(); |
1053 | + final String volume = mAdapter.getPathTool().getVolume(); |
1054 | + final String node = mAdapter.getPathTool().getCurrentNode(); |
1055 | + Log.d(TAG, "uploading " + uri + " under " + volume + " - " + node); |
1056 | + new Thread(new Runnable() { |
1057 | + public void run() { |
1058 | + try { |
1059 | + UbuntuOneFiles.getService() |
1060 | + .uploadFile(uri, volume, node); |
1061 | + } catch (RemoteException e) { |
1062 | + e.printStackTrace(); |
1063 | + } |
1064 | + } |
1065 | + }).start(); |
1066 | + } else { |
1067 | + if (LOGE) Log.e(TAG, "no file selected"); |
1068 | + Toast.makeText(this, |
1069 | + "No file selected", Toast.LENGTH_SHORT).show(); |
1070 | + } |
1071 | + break; |
1072 | + default: |
1073 | + break; |
1074 | + } |
1075 | + } |
1076 | + |
1077 | protected abstract CharSequence getActionBarTitle(); |
1078 | |
1079 | protected void setUploadButtonVisible(boolean visible) { |
1080 | - ImageButton upload = (ImageButton) findViewById(R.id.btn_title_share); |
1081 | + ImageButton upload = (ImageButton) findViewById(R.id.btn_title_upload); |
1082 | upload.setVisibility(visible ? View.VISIBLE : View.GONE); |
1083 | - ImageView separator = (ImageView)findViewById(R.id.btn_title_share_separator); |
1084 | + ImageView separator = (ImageView)findViewById(R.id.btn_title_upload_separator); |
1085 | separator.setVisibility(visible ? View.VISIBLE : View.GONE); |
1086 | } |
1087 | |
1088 | @@ -177,7 +254,6 @@ |
1089 | final String type = cfData.getString(Files.MIME); |
1090 | final String data = cfData.getString(Files.DATA); |
1091 | |
1092 | - Log.d(TAG, ">>> status: " + status); |
1093 | // FIXME use FileStatus. fields instead of those terrible ints.. |
1094 | switch (status) { |
1095 | case 1: // FileStatus.READY |
1096 | @@ -186,7 +262,6 @@ |
1097 | Intent.FLAG_GRANT_READ_URI_PERMISSION | |
1098 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); |
1099 | intent.setDataAndType(Uri.fromFile(new File(data)), type); |
1100 | - Log.d(TAG, ">>>>> type = " + type); |
1101 | Intent chooser = Intent.createChooser(intent, "Open with"); |
1102 | startActivityForResult(chooser, 0); |
1103 | break; |
1104 | |
1105 | === modified file 'src/com/ubuntuone/android/files/ui/SharesActivity.java' |
1106 | --- src/com/ubuntuone/android/files/ui/SharesActivity.java 2010-12-21 16:16:54 +0000 |
1107 | +++ src/com/ubuntuone/android/files/ui/SharesActivity.java 2010-12-24 00:07:14 +0000 |
1108 | @@ -33,9 +33,8 @@ |
1109 | @Override |
1110 | protected void onCreate(Bundle savedInstanceState) { |
1111 | super.onCreate(savedInstanceState); |
1112 | - setUploadButtonVisible(false); |
1113 | // at top level, upload button invisible |
1114 | - setUploadButtonVisible(false); |
1115 | + setUploadButtonVisible(true); |
1116 | setListAdapter(CustomListActivityAdapter.buildAdapter( |
1117 | this, VolumeType.SHARE)); |
1118 | } |
1119 | |
1120 | === modified file 'src/com/ubuntuone/android/files/ui/UdfsActivity.java' |
1121 | --- src/com/ubuntuone/android/files/ui/UdfsActivity.java 2010-12-21 16:16:54 +0000 |
1122 | +++ src/com/ubuntuone/android/files/ui/UdfsActivity.java 2010-12-24 00:07:14 +0000 |
1123 | @@ -34,7 +34,7 @@ |
1124 | protected void onCreate(Bundle savedInstanceState) { |
1125 | super.onCreate(savedInstanceState); |
1126 | // at top level, upload button invisible |
1127 | - setUploadButtonVisible(false); |
1128 | + setUploadButtonVisible(true); |
1129 | setListAdapter(CustomListActivityAdapter.buildAdapter( |
1130 | this, VolumeType.UDF)); |
1131 | } |
1132 | |
1133 | === modified file 'src/com/ubuntuone/android/files/util/CustomListActivityAdapter.java' |
1134 | --- src/com/ubuntuone/android/files/util/CustomListActivityAdapter.java 2010-12-21 18:34:44 +0000 |
1135 | +++ src/com/ubuntuone/android/files/util/CustomListActivityAdapter.java 2010-12-24 00:07:14 +0000 |
1136 | @@ -297,18 +297,20 @@ |
1137 | */ |
1138 | public void cd(final Context ctx, final long id) { |
1139 | ContentResolver r = ctx.getContentResolver(); |
1140 | - String[] dirProjection = new String[]{Files._ID, Files.NODE, Files.NAME}; |
1141 | - String dirSelection = Files._ID + "=" + id; |
1142 | - //String[] dirSelectionArgs = new String[]{new Long(id).toString()}; |
1143 | + String[] dirProjection = |
1144 | + new String[]{Files._ID, Files.VOLUME, Files.NODE, Files.NAME}; |
1145 | + String dirSelection = Files._ID + "=?"; |
1146 | + String[] dirSelectionArgs = new String[]{new Long(id).toString()}; |
1147 | Cursor dirC = r.query(Files.CONTENT_URI, |
1148 | - dirProjection, dirSelection, null, null); |
1149 | + dirProjection, dirSelection, dirSelectionArgs, null); |
1150 | + String volume = null; |
1151 | String node = null; |
1152 | String name = null; |
1153 | if (dirC.moveToFirst()) { |
1154 | + volume = dirC.getString(dirC.getColumnIndex(Files.VOLUME)); |
1155 | node = dirC.getString(dirC.getColumnIndex(Files.NODE)); |
1156 | name = dirC.getString(dirC.getColumnIndex(Files.NAME)); |
1157 | - mPathTool.cd(node, name); |
1158 | - |
1159 | + mPathTool.cd(volume, node, name); |
1160 | } |
1161 | dirC.close(); |
1162 | |
1163 | |
1164 | === modified file 'src/com/ubuntuone/android/files/util/PathTool.java' |
1165 | --- src/com/ubuntuone/android/files/util/PathTool.java 2010-12-21 16:16:54 +0000 |
1166 | +++ src/com/ubuntuone/android/files/util/PathTool.java 2010-12-24 00:07:14 +0000 |
1167 | @@ -25,47 +25,65 @@ |
1168 | import java.io.File; |
1169 | import java.util.Stack; |
1170 | |
1171 | +import android.content.ContentResolver; |
1172 | +import android.database.Cursor; |
1173 | +import android.util.Config; |
1174 | import android.util.Log; |
1175 | |
1176 | +import com.ubuntuone.android.files.UbuntuOneFiles; |
1177 | +import com.ubuntuone.android.files.provider.FilesContract.Files; |
1178 | +import com.ubuntuone.android.files.provider.FilesContract.Volumes; |
1179 | import com.ubuntuone.storageprotocol.StorageProtocol.Volumes.VolumeType; |
1180 | |
1181 | public final class PathTool { |
1182 | + private static final String TAG = PathTool.class.getSimpleName(); |
1183 | + |
1184 | + private static boolean LOGV = Config.DEBUG; |
1185 | + private static boolean LOGD = Config.DEBUG; |
1186 | + private static boolean LOGW = Log.isLoggable(TAG, Log.WARN); |
1187 | |
1188 | public static final String STORAGE_FOLDER = "u1"; |
1189 | + public static final String PRIMARY_FOLDER = "Ubuntu One"; |
1190 | |
1191 | - private VolumeType mType; |
1192 | + private VolumeType mType = null; |
1193 | private String mVolume = null; |
1194 | private Stack<String> mNodePath = null; |
1195 | private Stack<String> mNamePath = null; |
1196 | - |
1197 | - public PathTool() { |
1198 | - // delegate |
1199 | - this(null, null, null); |
1200 | - } |
1201 | - |
1202 | + |
1203 | public PathTool(final VolumeType type) { |
1204 | - this(); |
1205 | - this.mType = type; |
1206 | - if (VolumeType.ROOT.toString().equals(type.toString())) |
1207 | - mNamePath.push("/Ubuntu One"); |
1208 | - } |
1209 | - |
1210 | - public PathTool(final String volume, final String node, final String path) { |
1211 | - mVolume = volume; |
1212 | + mType = type; |
1213 | mNodePath = new Stack<String>(); |
1214 | - mNodePath.clear(); |
1215 | mNamePath = new Stack<String>(); |
1216 | - mNamePath.clear(); |
1217 | - mNamePath.push("sdcard/" + STORAGE_FOLDER); |
1218 | + mNamePath.push("sdcard/" + STORAGE_FOLDER); |
1219 | |
1220 | - if (node != null && path != null) { |
1221 | - String newPath = "/" + path; |
1222 | - |
1223 | - mNodePath.push(node); |
1224 | - mNamePath.push(newPath); |
1225 | - File f = new File(newPath); |
1226 | - f.mkdirs(); |
1227 | - } |
1228 | + if (VolumeType.ROOT.equals(type)) { |
1229 | + mNamePath.push(PRIMARY_FOLDER); |
1230 | + // setup root volumeId and nodeId |
1231 | + ContentResolver cr = |
1232 | + UbuntuOneFiles.getInstance().getContentResolver(); |
1233 | + String[] projection = new String[]{Volumes._ID, Volumes.VOLUME, |
1234 | + Volumes.NODE}; |
1235 | + String selection = Volumes.TYPE + "=?"; |
1236 | + String[] selectionArgs = new String[]{VolumeType.ROOT.toString()}; |
1237 | + Cursor c = cr.query(Volumes.CONTENT_URI, projection, |
1238 | + selection, selectionArgs, null); |
1239 | + String volume = null; |
1240 | + String node = null; |
1241 | + if (c.moveToFirst()) { |
1242 | + volume = c.getString(c.getColumnIndex(Volumes.VOLUME)); |
1243 | + node = c.getString(c.getColumnIndex(Volumes.NODE)); |
1244 | + } |
1245 | + c.close(); |
1246 | + if (volume != null && node != null) { |
1247 | + mVolume = volume; |
1248 | + mNodePath.push(node); |
1249 | + } else { |
1250 | + if (LOGW) Log.w(TAG, "setting up root PathTool, " + |
1251 | + "but volume or node null"); |
1252 | + } |
1253 | + File f = new File(getNamePath()); |
1254 | + f.mkdirs(); |
1255 | + } |
1256 | } |
1257 | |
1258 | public VolumeType getType() { |
1259 | @@ -111,26 +129,48 @@ |
1260 | } |
1261 | |
1262 | public boolean isAtRoot() { |
1263 | - return mNodePath.size() == 0; |
1264 | + return mNodePath.size() == ((VolumeType.ROOT.equals(mType)) ? 1 : 0); |
1265 | } |
1266 | |
1267 | /** |
1268 | * @return cd .. |
1269 | */ |
1270 | public boolean cd() { |
1271 | - if (mNodePath.size() > 0) { |
1272 | - mNodePath.pop(); |
1273 | - mNamePath.pop(); |
1274 | - Log.d("Path", "at " + getNamePath()); |
1275 | - return true; |
1276 | + if (VolumeType.ROOT.equals(mType)) { |
1277 | + if (mNodePath.size() > 1) { |
1278 | + mNodePath.pop(); |
1279 | + mNamePath.pop(); |
1280 | + Log.d(TAG, "path at vol=" + mVolume |
1281 | + + ", node=" + getCurrentNode() |
1282 | + + ", path=" + getNamePath()); |
1283 | + return true; |
1284 | + } |
1285 | + } else { |
1286 | + if (mNodePath.size() > 0) { |
1287 | + mNodePath.pop(); |
1288 | + mNamePath.pop(); |
1289 | + if (mNodePath.size() == 0) |
1290 | + mVolume = null; |
1291 | + Log.d(TAG, "path at vol=" + mVolume |
1292 | + + ", node=" + getCurrentNode() |
1293 | + + ", path=" + getNamePath()); |
1294 | + return true; |
1295 | + } |
1296 | } |
1297 | + mVolume = null; |
1298 | return false; |
1299 | } |
1300 | |
1301 | - public void cd(final String node, final String name) { |
1302 | + public void cd(final String volume, final String node, final String name) { |
1303 | + // in case of UDF or Share, we have to set mVolume and initial node |
1304 | + if (!VolumeType.ROOT.equals(mType) && mNodePath.size() == 0) { |
1305 | + mVolume = volume; |
1306 | + } |
1307 | mNodePath.push(node); |
1308 | mNamePath.push(name); |
1309 | - Log.d("Path", "at " + getNamePath()); |
1310 | + Log.d(TAG, "path at vol=" + mVolume |
1311 | + + ", node=" + getCurrentNode() |
1312 | + + ", path=" + getNamePath()); |
1313 | } |
1314 | |
1315 | /** |
226 + InputStream _in = null;
I don't like the leading underscore here.
I'm not certain (Java is not my forte), but if you leave a variable unassigned, the value will still be null, but the compiler will detect cases in which an unassigned value would be used. I don't know if that would be best here, but there are three initializations to null nearby where the other behavior from the compiler might be better.
327 +// Log.d(TAG, fileInfo.getName()
You might as well kill this comment.
355 - private static int TRANSFER_ NOTIFICATION = 200;
Are these milliseconds?
403 + public void uploadFile(Uri uri, String volume, String parent) {
This deserves a logging statement.
454 + "Syncing...", System. currentTimeMill is());
We should be trying to localize as we go.