Merge lp:~karni/ubuntuone-android-files/basic-upload into lp:ubuntuone-android-files

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
Reviewer Review Type Date Requested Status
Chad Miller (community) Approve
Review via email: mp+44621@code.launchpad.net

Description of the change

Upload fully functional, supporting uploads from MediaContentProvider, based on source Uri.

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 :

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.currentTimeMillis());

We should be trying to localize as we go.

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 /**

Subscribers

People subscribed via source and target branches

to status/vote changes: