Browse Source

- InstantUpload path const not longer at two places, only at
InstantUploadService
- new method to create the full path for the instantupload's
- add QUOTA_EXCEEDED to RemoteOperationResult.ResultCode enum to hande
http-507 from oc
- handle RuntimeException at connection lost as HOST_NOT_AVAILABLE
- add the error message for failed uploads for every not uploaded
picture, espacially for QUOTA_EXCEEDED is it necessary to show that to
the user

Matthias Baumann 12 years ago
parent
commit
7392cfe5ab

+ 3 - 1
res/values-de/strings.xml

@@ -225,5 +225,7 @@
   <string name="failed_upload_headline_delete_all_btn">Ausgewählte löschen </string>
   <string name="failed_upload_retry_text">Versuche ausgewählte erneut hochzuladen</string>
   <string name="failed_upload_load_more_images">Weitere Bilder laden</string>
-<string name="failed_upload_retry_do_nothing_text">Upload nicht gestarted, Sie sind nicht online für ein Softupload</string>
+  <string name="failed_upload_retry_do_nothing_text">Upload nicht gestarted, Sie sind nicht online für ein Softupload</string>
+  <string name="failed_upload_failure_text">Fehlermeldung: </string>
+  <string name="failed_upload_quota_exceeded_text">Bitte überprüfen sie ihre Serverkonfiguration, möglicherweise ist ihre Upload Limit überschritten</string>
 </resources>

+ 2 - 1
res/values/strings.xml

@@ -241,5 +241,6 @@
     <string name="failed_upload_retry_text">retry to upload the image: </string>
     <string name="failed_upload_load_more_images">Load more Picrures</string>
     <string name="failed_upload_retry_do_nothing_text">do nothing you are not online for instant upload</string>
-
+	<string name="failed_upload_failure_text">Failure Message: </string>
+	<string name="failed_upload_quota_exceeded_text">Pleas check your server configuration,maye your quota is exceeded.</string>
 </resources>

+ 10 - 5
src/com/owncloud/android/db/DbHandler.java

@@ -34,7 +34,7 @@ public class DbHandler {
     private SQLiteDatabase mDB;
     private OpenerHelper mHelper;
     private final String mDatabaseName = "ownCloud";
-    private final int mDatabaseVersion = 2;
+    private final int mDatabaseVersion = 3;
 
     private final String TABLE_INSTANT_UPLOAD = "instant_upload";
 
@@ -50,19 +50,21 @@ public class DbHandler {
         mDB.close();
     }
 
-    public boolean putFileForLater(String filepath, String account) {
+    public boolean putFileForLater(String filepath, String account, String message) {
         ContentValues cv = new ContentValues();
         cv.put("path", filepath);
         cv.put("account", account);
         cv.put("attempt", UPLOAD_STATUS_UPLOAD_LATER);
+        cv.put("message", message);
         long result = mDB.insert(TABLE_INSTANT_UPLOAD, null, cv);
         Log.d(TABLE_INSTANT_UPLOAD, "putFileForLater returns with: " + result + " for file: " + filepath);
         return result != -1;
     }
 
-    public int updateFileState(String filepath, Integer status) {
+    public int updateFileState(String filepath, Integer status, String message) {
         ContentValues cv = new ContentValues();
         cv.put("attempt", status);
+        cv.put("message", message);
         int result = mDB.update(TABLE_INSTANT_UPLOAD, cv, "path=?", new String[] { filepath });
         Log.d(TABLE_INSTANT_UPLOAD, "updateFileState returns with: " + result + " for file: " + filepath);
         return result;
@@ -100,12 +102,15 @@ public class DbHandler {
         @Override
         public void onCreate(SQLiteDatabase db) {
             db.execSQL("CREATE TABLE " + TABLE_INSTANT_UPLOAD + " (" + " _id INTEGER PRIMARY KEY, " + " path TEXT,"
-                    + " account TEXT,attempt INTEGER);");
+                    + " account TEXT,attempt INTEGER,message TEXT);");
         }
 
         @Override
         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-            db.execSQL("ALTER TABLE " + TABLE_INSTANT_UPLOAD + " ADD COLUMN attempt;");
+            if (oldVersion < 2) {
+                db.execSQL("ALTER TABLE " + TABLE_INSTANT_UPLOAD + " ADD COLUMN attempt INTEGER;");
+            }
+            db.execSQL("ALTER TABLE " + TABLE_INSTANT_UPLOAD + " ADD COLUMN message TEXT;");
 
         }
     }

+ 4 - 4
src/com/owncloud/android/files/InstantUploadBroadcastReceiver.java

@@ -38,10 +38,10 @@ import com.owncloud.android.AccountUtils;
 import com.owncloud.android.authenticator.AccountAuthenticator;
 import com.owncloud.android.db.DbHandler;
 import com.owncloud.android.files.services.FileUploader;
+import com.owncloud.android.utils.FileStorageUtils;
 
 public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
 
-    public static String INSTANT_UPLOAD_DIR = "/InstantUpload/";
     private static String TAG = "PhotoTakenBroadcastReceiver";
     private static final String[] CONTENT_PROJECTION = { Media.DATA, Media.DISPLAY_NAME, Media.MIME_TYPE, Media.SIZE };
     private static String NEW_PHOTO_ACTION = "com.android.camera.NEW_PICTURE";
@@ -100,7 +100,7 @@ public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
 
         // same always temporally the picture to upload
         DbHandler db = new DbHandler(context);
-        db.putFileForLater(file_path, account.name);
+        db.putFileForLater(file_path, account.name, null);
         db.close();
 
         if (!isOnline(context) || (instantUploadViaWiFiOnly(context) && !isConnectedViaWiFi(context))) {
@@ -121,7 +121,7 @@ public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
         Intent i = new Intent(context, FileUploader.class);
         i.putExtra(FileUploader.KEY_ACCOUNT, account);
         i.putExtra(FileUploader.KEY_LOCAL_FILE, file_path);
-        i.putExtra(FileUploader.KEY_REMOTE_FILE, INSTANT_UPLOAD_DIR + file_name);
+        i.putExtra(FileUploader.KEY_REMOTE_FILE, FileStorageUtils.getInstantUploadFilePath(file_name));
         i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
         i.putExtra(FileUploader.KEY_MIME_TYPE, mime_type);
         i.putExtra(FileUploader.KEY_INSTANT_UPLOAD, true);
@@ -164,7 +164,7 @@ public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
                         Intent i = new Intent(context, FileUploader.class);
                         i.putExtra(FileUploader.KEY_ACCOUNT, account);
                         i.putExtra(FileUploader.KEY_LOCAL_FILE, file_path);
-                        i.putExtra(FileUploader.KEY_REMOTE_FILE, INSTANT_UPLOAD_DIR + f.getName());
+                        i.putExtra(FileUploader.KEY_REMOTE_FILE, FileStorageUtils.getInstantUploadFilePath(f.getName()));
                         i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
                         i.putExtra(FileUploader.KEY_INSTANT_UPLOAD, true);
                         context.startService(i);

+ 13 - 27
src/com/owncloud/android/files/services/FileUploader.java

@@ -54,7 +54,6 @@ import com.owncloud.android.authenticator.AccountAuthenticator;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.db.DbHandler;
-import com.owncloud.android.files.InstantUploadBroadcastReceiver;
 import com.owncloud.android.network.OwnCloudClientUtils;
 import com.owncloud.android.operations.ChunkedUploadFileOperation;
 import com.owncloud.android.operations.RemoteOperationResult;
@@ -422,28 +421,10 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
 
             // / create remote folder for instant uploads
             if (mCurrentUpload.isRemoteFolderToBeCreated()) {
-                mUploadClient.createDirectory(InstantUploadBroadcastReceiver.INSTANT_UPLOAD_DIR); // ignoring
-                                                                                                  // result;
-                                                                                                  // fail
-                                                                                                  // could
-                                                                                                  // just
-                                                                                                  // mean
-                                                                                                  // that
-                                                                                                  // it
-                                                                                                  // already
-                                                                                                  // exists,
-                                                                                                  // but
-                                                                                                  // local
-                                                                                                  // database
-                                                                                                  // is
-                                                                                                  // not
-                                                                                                  // synchronized;
-                                                                                                  // the
-                                                                                                  // upload
-                                                                                                  // will
-                                                                                                  // be
-                                                                                                  // tried
-                                                                                                  // anyway
+                mUploadClient.createDirectory(InstantUploadService.INSTANT_UPLOAD_DIR);
+                // ignoring result fail could just mean that it already exists,
+                // but local database is not synchronized the upload will be
+                // tried anyway
             }
 
             // / perform the upload
@@ -543,11 +524,11 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
     }
 
     private boolean checkAndFixInstantUploadDirectory(FileDataStorageManager storageManager) {
-        OCFile instantUploadDir = storageManager.getFileByPath(InstantUploadBroadcastReceiver.INSTANT_UPLOAD_DIR);
+        OCFile instantUploadDir = storageManager.getFileByPath(InstantUploadService.INSTANT_UPLOAD_DIR);
         if (instantUploadDir == null) {
             // first instant upload in the account, or never account not
             // synchronized after the remote InstantUpload folder was created
-            OCFile newDir = new OCFile(InstantUploadBroadcastReceiver.INSTANT_UPLOAD_DIR);
+            OCFile newDir = new OCFile(InstantUploadService.INSTANT_UPLOAD_DIR);
             newDir.setMimetype("DIR");
             OCFile path = storageManager.getFileByPath(OCFile.PATH_SEPARATOR);
 
@@ -738,8 +719,13 @@ public class FileUploader extends Service implements OnDatatransferProgressListe
             mNotificationManager.notify(R.string.uploader_upload_failed_ticker, finalNotification);
 
             DbHandler db = new DbHandler(this.getBaseContext());
-            if (db.updateFileState(upload.getOriginalStoragePath(), DbHandler.UPLOAD_STATUS_UPLOAD_FAILED) == 0) {
-                db.putFileForLater(upload.getOriginalStoragePath(), upload.getAccount().name);
+            String message = uploadResult.getLogMessage() + " errorCode: " + uploadResult.getCode();
+            Log.e(TAG, message+" Http-Code: "+uploadResult.getHttpCode());            
+            if (uploadResult.getCode() == ResultCode.QUOTA_EXCEEDED) {
+                message = getString(R.string.failed_upload_quota_exceeded_text);
+            } 
+            if (db.updateFileState(upload.getOriginalStoragePath(), DbHandler.UPLOAD_STATUS_UPLOAD_FAILED, message) == 0) {
+                db.putFileForLater(upload.getOriginalStoragePath(), upload.getAccount().name, message);
             }
             db.close();
 

+ 24 - 27
src/com/owncloud/android/files/services/InstantUploadService.java

@@ -23,16 +23,17 @@ import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
 
-import com.owncloud.android.network.OwnCloudClientUtils;
-
-import eu.alefzero.webdav.WebdavClient;
-
 import android.accounts.Account;
 import android.app.Service;
 import android.content.Intent;
 import android.os.IBinder;
 import android.util.Log;
 
+import com.owncloud.android.network.OwnCloudClientUtils;
+import com.owncloud.android.utils.FileStorageUtils;
+
+import eu.alefzero.webdav.WebdavClient;
+
 public class InstantUploadService extends Service {
 
     public static String KEY_FILE_PATH = "KEY_FILEPATH";
@@ -43,7 +44,7 @@ public class InstantUploadService extends Service {
 
     private static String TAG = "InstantUploadService";
     // TODO make it configurable over the settings dialog
-    public static String INSTANT_UPLOAD_DIR = "/InstantUpload";
+    public static final String INSTANT_UPLOAD_DIR = "/InstantUpload";
     private UploaderRunnable mUploaderRunnable;
 
     @Override
@@ -59,54 +60,50 @@ public class InstantUploadService extends Service {
             Log.w(TAG, "Not all required information was provided, abording");
             return Service.START_NOT_STICKY;
         }
-        
+
         if (mUploaderRunnable == null) {
             mUploaderRunnable = new UploaderRunnable();
         }
-        
+
         String filename = intent.getStringExtra(KEY_DISPLAY_NAME);
         String filepath = intent.getStringExtra(KEY_FILE_PATH);
         String mimetype = intent.getStringExtra(KEY_MIME_TYPE);
         Account account = intent.getParcelableExtra(KEY_ACCOUNT);
         long filesize = intent.getLongExtra(KEY_FILE_SIZE, -1);
-        
+
         mUploaderRunnable.addElementToQueue(filename, filepath, mimetype, filesize, account);
-        
+
         // starting new thread for new download doesnt seems like a good idea
         // maybe some thread pool or single background thread would be better
         Log.d(TAG, "Starting instant upload thread");
         new Thread(mUploaderRunnable).start();
-        
+
         return Service.START_STICKY;
     }
-    
+
     private class UploaderRunnable implements Runnable {
-        
+
         Object mLock;
         List<HashMap<String, Object>> mHashMapList;
-        
+
         public UploaderRunnable() {
             mHashMapList = new LinkedList<HashMap<String, Object>>();
             mLock = new Object();
         }
-        
-        public void addElementToQueue(String filename,
-                                      String filepath,
-                                      String mimetype,
-                                      long length,
-                                      Account account) {
+
+        public void addElementToQueue(String filename, String filepath, String mimetype, long length, Account account) {
             HashMap<String, Object> new_map = new HashMap<String, Object>();
             new_map.put(KEY_ACCOUNT, account);
             new_map.put(KEY_DISPLAY_NAME, filename);
             new_map.put(KEY_FILE_PATH, filepath);
             new_map.put(KEY_MIME_TYPE, mimetype);
             new_map.put(KEY_FILE_SIZE, length);
-            
+
             synchronized (mLock) {
                 mHashMapList.add(new_map);
             }
         }
-        
+
         private HashMap<String, Object> getFirstObject() {
             synchronized (mLock) {
                 if (mHashMapList.size() == 0)
@@ -116,10 +113,10 @@ public class InstantUploadService extends Service {
                 return ret;
             }
         }
-        
+
         public void run() {
             HashMap<String, Object> working_map;
-            
+
             while ((working_map = getFirstObject()) != null) {
                 Account account = (Account) working_map.get(KEY_ACCOUNT);
                 String filename = (String) working_map.get(KEY_DISPLAY_NAME);
@@ -127,15 +124,15 @@ public class InstantUploadService extends Service {
                 String mimetype = (String) working_map.get(KEY_MIME_TYPE);
                 
                 WebdavClient wdc = OwnCloudClientUtils.createOwnCloudClient(account, getApplicationContext());
-                
-                wdc.createDirectory(INSTANT_UPLOAD_DIR);    // fail could just mean that it already exists; put will be tried anyway
+
+                wdc.createDirectory(INSTANT_UPLOAD_DIR); // fail could just mean that it already exists put will be tried anyway
                 try {
-                    wdc.putFile(filepath, INSTANT_UPLOAD_DIR + "/" + filename, mimetype);
+                    wdc.putFile(filepath, FileStorageUtils.getInstantUploadFilePath(filename), mimetype);
                 } catch (Exception e) {
                     // nothing to do; this service is deprecated, indeed
                 }
             }
         }
     }
-    
+
 }

+ 73 - 90
src/com/owncloud/android/operations/RemoteOperationResult.java

@@ -33,142 +33,126 @@ import org.apache.commons.httpclient.HttpException;
 import org.apache.commons.httpclient.HttpStatus;
 import org.apache.jackrabbit.webdav.DavException;
 
-import com.owncloud.android.network.CertificateCombinedException;
+import android.util.Log;
 
+import com.owncloud.android.network.CertificateCombinedException;
 
 /**
  * The result of a remote operation required to an ownCloud server.
  * 
- * Provides a common classification of remote operation results for all the application. 
+ * Provides a common classification of remote operation results for all the
+ * application.
  * 
  * @author David A. Velasco
  */
 public class RemoteOperationResult implements Serializable {
-    
+
     /** Generated - should be refreshed every time the class changes!! */
     private static final long serialVersionUID = -7805531062432602444L;
+    private static final String TAG = "RemoteOperationResult";
 
-    
-    public enum ResultCode { 
-        OK,
-        OK_SSL,
-        OK_NO_SSL,
-        UNHANDLED_HTTP_CODE,
-        UNAUTHORIZED,        
-        FILE_NOT_FOUND, 
-        INSTANCE_NOT_CONFIGURED, 
-        UNKNOWN_ERROR, 
-        WRONG_CONNECTION,  
-        TIMEOUT, 
-        INCORRECT_ADDRESS, 
-        HOST_NOT_AVAILABLE, 
-        NO_NETWORK_CONNECTION, 
-        SSL_ERROR,
-        SSL_RECOVERABLE_PEER_UNVERIFIED,
-        BAD_OC_VERSION,
-        CANCELLED, 
-        INVALID_LOCAL_FILE_NAME, 
-        INVALID_OVERWRITE,
-        CONFLICT, 
-        SYNC_CONFLICT,
-        LOCAL_STORAGE_FULL, 
-        LOCAL_STORAGE_NOT_MOVED, 
-        LOCAL_STORAGE_NOT_COPIED
+    public enum ResultCode {
+        OK, OK_SSL, OK_NO_SSL, UNHANDLED_HTTP_CODE, UNAUTHORIZED, FILE_NOT_FOUND, INSTANCE_NOT_CONFIGURED, UNKNOWN_ERROR, WRONG_CONNECTION, TIMEOUT, INCORRECT_ADDRESS, HOST_NOT_AVAILABLE, NO_NETWORK_CONNECTION, SSL_ERROR, SSL_RECOVERABLE_PEER_UNVERIFIED, BAD_OC_VERSION, CANCELLED, INVALID_LOCAL_FILE_NAME, INVALID_OVERWRITE, CONFLICT, SYNC_CONFLICT, LOCAL_STORAGE_FULL, LOCAL_STORAGE_NOT_MOVED, LOCAL_STORAGE_NOT_COPIED, QUOTA_EXCEEDED
     }
 
     private boolean mSuccess = false;
     private int mHttpCode = -1;
     private Exception mException = null;
     private ResultCode mCode = ResultCode.UNKNOWN_ERROR;
-    
+
     public RemoteOperationResult(ResultCode code) {
         mCode = code;
         mSuccess = (code == ResultCode.OK || code == ResultCode.OK_SSL || code == ResultCode.OK_NO_SSL);
     }
-    
+
     public RemoteOperationResult(boolean success, int httpCode) {
-        mSuccess = success; 
+        mSuccess = success;
         mHttpCode = httpCode;
 
         if (success) {
             mCode = ResultCode.OK;
-            
+
         } else if (httpCode > 0) {
             switch (httpCode) {
-                case HttpStatus.SC_UNAUTHORIZED:
-                    mCode = ResultCode.UNAUTHORIZED;
-                    break;
-                case HttpStatus.SC_NOT_FOUND:
-                    mCode = ResultCode.FILE_NOT_FOUND;
-                    break;
-                case HttpStatus.SC_INTERNAL_SERVER_ERROR:
-                    mCode = ResultCode.INSTANCE_NOT_CONFIGURED;
-                    break;
-                case HttpStatus.SC_CONFLICT:
-                    mCode = ResultCode.CONFLICT;
-                    break;
-                default:
-                    mCode = ResultCode.UNHANDLED_HTTP_CODE;
+            case HttpStatus.SC_UNAUTHORIZED:
+                mCode = ResultCode.UNAUTHORIZED;
+                break;
+            case HttpStatus.SC_NOT_FOUND:
+                mCode = ResultCode.FILE_NOT_FOUND;
+                break;
+            case HttpStatus.SC_INTERNAL_SERVER_ERROR:
+                mCode = ResultCode.INSTANCE_NOT_CONFIGURED;
+                break;
+            case HttpStatus.SC_CONFLICT:
+                mCode = ResultCode.CONFLICT;
+                break;
+            case HttpStatus.SC_INSUFFICIENT_STORAGE:
+                mCode = ResultCode.QUOTA_EXCEEDED;
+                break;
+            default:
+                mCode = ResultCode.UNHANDLED_HTTP_CODE;
+                Log.d(TAG, "RemoteOperationResult has prcessed UNHANDLED_HTTP_CODE: " + httpCode);
             }
         }
     }
-    
+
     public RemoteOperationResult(Exception e) {
-        mException = e; 
-        
+        mException = e;
+
         if (e instanceof OperationCancelledException) {
             mCode = ResultCode.CANCELLED;
-            
-        } else if (e instanceof SocketException) {  
+
+        } else if (e instanceof SocketException) {
             mCode = ResultCode.WRONG_CONNECTION;
-        
+
         } else if (e instanceof SocketTimeoutException) {
             mCode = ResultCode.TIMEOUT;
-        
+
         } else if (e instanceof ConnectTimeoutException) {
             mCode = ResultCode.TIMEOUT;
-            
+
         } else if (e instanceof MalformedURLException) {
             mCode = ResultCode.INCORRECT_ADDRESS;
-        
+
         } else if (e instanceof UnknownHostException) {
             mCode = ResultCode.HOST_NOT_AVAILABLE;
-        
+
         } else if (e instanceof SSLException || e instanceof RuntimeException) {
             CertificateCombinedException se = getCertificateCombinedException(e);
             if (se != null) {
                 mException = se;
-                if (se.isRecoverable()) { 
+                if (se.isRecoverable()) {
                     mCode = ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED;
                 }
-                
-            } else { 
+            } else if (e instanceof RuntimeException) {
+                mCode = ResultCode.HOST_NOT_AVAILABLE;
+
+            } else {
                 mCode = ResultCode.SSL_ERROR;
             }
-            
+
         } else {
             mCode = ResultCode.UNKNOWN_ERROR;
         }
-        
+
     }
-    
-    
+
     public boolean isSuccess() {
         return mSuccess;
     }
-    
+
     public boolean isCancelled() {
         return mCode == ResultCode.CANCELLED;
     }
-    
+
     public int getHttpCode() {
         return mHttpCode;
     }
-    
+
     public ResultCode getCode() {
         return mCode;
     }
-    
+
     public Exception getException() {
         return mException;
     }
@@ -176,11 +160,11 @@ public class RemoteOperationResult implements Serializable {
     public boolean isSslRecoverableException() {
         return mCode == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED;
     }
-    
+
     private CertificateCombinedException getCertificateCombinedException(Exception e) {
         CertificateCombinedException result = null;
         if (e instanceof CertificateCombinedException) {
-            return (CertificateCombinedException)e;
+            return (CertificateCombinedException) e;
         }
         Throwable cause = mException.getCause();
         Throwable previousCause = null;
@@ -189,39 +173,38 @@ public class RemoteOperationResult implements Serializable {
             cause = cause.getCause();
         }
         if (cause != null && cause instanceof CertificateCombinedException) {
-            result = (CertificateCombinedException)cause; 
+            result = (CertificateCombinedException) cause;
         }
         return result;
     }
-    
-    
+
     public String getLogMessage() {
-        
+
         if (mException != null) {
             if (mException instanceof OperationCancelledException) {
                 return "Operation cancelled by the caller";
-                
-            } else if (mException instanceof SocketException) {  
+
+            } else if (mException instanceof SocketException) {
                 return "Socket exception";
-        
+
             } else if (mException instanceof SocketTimeoutException) {
                 return "Socket timeout exception";
-        
+
             } else if (mException instanceof ConnectTimeoutException) {
                 return "Connect timeout exception";
-            
+
             } else if (mException instanceof MalformedURLException) {
                 return "Malformed URL exception";
-        
+
             } else if (mException instanceof UnknownHostException) {
                 return "Unknown host exception";
-        
+
             } else if (mException instanceof CertificateCombinedException) {
                 if (((CertificateCombinedException) mException).isRecoverable())
                     return "SSL recoverable exception";
                 else
                     return "SSL exception";
-                
+
             } else if (mException instanceof SSLException) {
                 return "SSL exception";
 
@@ -238,25 +221,25 @@ public class RemoteOperationResult implements Serializable {
                 return "Unexpected exception";
             }
         }
-        
+
         if (mCode == ResultCode.INSTANCE_NOT_CONFIGURED) {
             return "The ownCloud server is not configured!";
-            
+
         } else if (mCode == ResultCode.NO_NETWORK_CONNECTION) {
             return "No network connection";
-            
+
         } else if (mCode == ResultCode.BAD_OC_VERSION) {
             return "No valid ownCloud version was found at the server";
-            
+
         } else if (mCode == ResultCode.LOCAL_STORAGE_FULL) {
             return "Local storage full";
-            
+
         } else if (mCode == ResultCode.LOCAL_STORAGE_NOT_MOVED) {
             return "Error while moving file to final directory";
         }
-        
-        return "Operation finished with HTTP status code " + mHttpCode + " (" + (isSuccess()?"success":"fail") + ")";
-        
+
+        return "Operation finished with HTTP status code " + mHttpCode + " (" + (isSuccess() ? "success" : "fail") + ")";
+
     }
 
 }

+ 60 - 11
src/com/owncloud/android/ui/activity/InstantUploadActivity.java

@@ -32,6 +32,7 @@ import android.util.SparseArray;
 import android.view.Gravity;
 import android.view.View;
 import android.view.View.OnClickListener;
+import android.view.View.OnLongClickListener;
 import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.CheckBox;
@@ -39,6 +40,7 @@ import android.widget.CompoundButton;
 import android.widget.CompoundButton.OnCheckedChangeListener;
 import android.widget.ImageButton;
 import android.widget.LinearLayout;
+import android.widget.TextView;
 import android.widget.Toast;
 
 import com.owncloud.android.AccountUtils;
@@ -46,7 +48,7 @@ import com.owncloud.android.R;
 import com.owncloud.android.db.DbHandler;
 import com.owncloud.android.files.InstantUploadBroadcastReceiver;
 import com.owncloud.android.files.services.FileUploader;
-import com.owncloud.android.files.services.InstantUploadService;
+import com.owncloud.android.utils.FileStorageUtils;
 
 /**
  * This Activity is used to display a list with images they could not be
@@ -77,6 +79,7 @@ public class InstantUploadActivity extends Activity {
     private int lastLoadImageIdx = 0;
 
     private SparseArray<String> fileList = null;
+    CheckBox failed_upload_all_cb;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -87,7 +90,7 @@ public class InstantUploadActivity extends Activity {
         delete_all_btn.setOnClickListener(getDeleteListner());
         Button retry_all_btn = (Button) findViewById(R.id.failed_upload_retry_all_btn);
         retry_all_btn.setOnClickListener(getRetryListner());
-        CheckBox failed_upload_all_cb = (CheckBox) findViewById(R.id.failed_upload_headline_cb);
+        this.failed_upload_all_cb = (CheckBox) findViewById(R.id.failed_upload_headline_cb);
         failed_upload_all_cb.setOnCheckedChangeListener(getCheckAllListener());
         listView = (LinearLayout) findViewById(R.id.failed_upload_scrollviewlayout);
 
@@ -121,11 +124,12 @@ public class InstantUploadActivity extends Activity {
 
                     lastLoadImageIdx++;
                     String imp_path = c.getString(1);
+                    String message = c.getString(4);
                     fileList.put(lastLoadImageIdx, imp_path);
-                    LinearLayout rowLayout = getLinearLayout(lastLoadImageIdx);
+                    LinearLayout rowLayout = getHorizontalLinearLayout(lastLoadImageIdx);
                     rowLayout.addView(getFileCheckbox(lastLoadImageIdx));
                     rowLayout.addView(getImageButton(imp_path, lastLoadImageIdx));
-                    rowLayout.addView(getFileButton(imp_path, lastLoadImageIdx));
+                    rowLayout.addView(getFileButton(imp_path, message, lastLoadImageIdx));
                     listView.addView(rowLayout);
                     Log.d(LOG_TAG, imp_path + " on idx: " + lastLoadImageIdx);
                     if (lastLoadImageIdx % MAX_LOAD_IMAGES == 0) {
@@ -241,6 +245,7 @@ public class InstantUploadActivity extends Activity {
             public void onClick(View v) {
 
                 try {
+
                     List<CheckBox> list = getCheckboxList();
                     for (CheckBox checkbox : list) {
                         boolean to_retry = checkbox.isChecked();
@@ -259,6 +264,9 @@ public class InstantUploadActivity extends Activity {
                     // refresh the List
                     listView.removeAllViews();
                     loadListView(true);
+                    if (failed_upload_all_cb != null) {
+                        failed_upload_all_cb.setChecked(false);
+                    }
                 }
 
             }
@@ -276,6 +284,7 @@ public class InstantUploadActivity extends Activity {
 
             @Override
             public void onClick(View v) {
+
                 final DbHandler dbh = new DbHandler(getApplicationContext());
                 try {
                     List<CheckBox> list = getCheckboxList();
@@ -297,13 +306,16 @@ public class InstantUploadActivity extends Activity {
                     // refresh the List
                     listView.removeAllViews();
                     loadListView(true);
+                    if (failed_upload_all_cb != null) {
+                        failed_upload_all_cb.setChecked(false);
+                    }
                 }
 
             }
         };
     }
 
-    private LinearLayout getLinearLayout(int id) {
+    private LinearLayout getHorizontalLinearLayout(int id) {
         LinearLayout linearLayout = new LinearLayout(getApplicationContext());
         linearLayout.setId(id);
         linearLayout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
@@ -313,14 +325,51 @@ public class InstantUploadActivity extends Activity {
         return linearLayout;
     }
 
-    private Button getFileButton(final String img_path, int id) {
-        Button retryButton = new Button(this);
+    private LinearLayout getVerticalLinearLayout() {
+        LinearLayout linearLayout = new LinearLayout(getApplicationContext());
+        linearLayout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
+                LinearLayout.LayoutParams.MATCH_PARENT));
+        linearLayout.setGravity(Gravity.TOP);
+        linearLayout.setOrientation(LinearLayout.VERTICAL);
+        return linearLayout;
+    }
+
+    private View getFileButton(final String img_path, String message, int id) {
+
+        TextView failureTextView = new TextView(this);
+        failureTextView.setText(getString(R.string.failed_upload_failure_text) + message);
+        failureTextView.setBackgroundResource(R.color.owncloud_white);
+        failureTextView.setTextSize(8);
+        failureTextView.setOnLongClickListener(getOnLongClickListener(message));
+        failureTextView.setPadding(5, 5, 5, 10);
+        TextView retryButton = new TextView(this);
         retryButton.setId(id);
         retryButton.setText(img_path);
         retryButton.setBackgroundResource(R.color.owncloud_white);
         retryButton.setTextSize(8);
         retryButton.setOnClickListener(getImageButtonOnClickListener(img_path));
-        return retryButton;
+        retryButton.setOnLongClickListener(getOnLongClickListener(message));
+        retryButton.setPadding(5, 5, 5, 10);
+        LinearLayout verticalLayout = getVerticalLinearLayout();
+        verticalLayout.addView(retryButton);
+        verticalLayout.addView(failureTextView);
+
+        return verticalLayout;
+    }
+
+    private OnLongClickListener getOnLongClickListener(final String message) {
+        return new OnLongClickListener() {
+
+            @Override
+            public boolean onLongClick(View v) {
+                Log.d(LOG_TAG, message);
+                Toast toast = Toast.makeText(InstantUploadActivity.this, getString(R.string.failed_upload_retry_text)
+                        + message, Toast.LENGTH_LONG);
+                toast.show();
+                return true;
+            }
+
+        };
     }
 
     private CheckBox getFileCheckbox(int id) {
@@ -390,13 +439,13 @@ public class InstantUploadActivity extends Activity {
      */
     private void startUpload(String img_path) {
         // extract filename
-        String filename = img_path.substring(img_path.lastIndexOf('/'), img_path.length());
+        String filename = FileStorageUtils.getInstantUploadFilePath(img_path);
         if (canInstantUpload()) {
             Account account = AccountUtils.getCurrentOwnCloudAccount(InstantUploadActivity.this);
             // add file again to upload queue
             DbHandler db = new DbHandler(InstantUploadActivity.this);
             try {
-                db.updateFileState(img_path, DbHandler.UPLOAD_STATUS_UPLOAD_LATER);
+                db.updateFileState(img_path, DbHandler.UPLOAD_STATUS_UPLOAD_LATER, null);
             } finally {
                 db.close();
             }
@@ -404,7 +453,7 @@ public class InstantUploadActivity extends Activity {
             Intent i = new Intent(InstantUploadActivity.this, FileUploader.class);
             i.putExtra(FileUploader.KEY_ACCOUNT, account);
             i.putExtra(FileUploader.KEY_LOCAL_FILE, img_path);
-            i.putExtra(FileUploader.KEY_REMOTE_FILE, InstantUploadService.INSTANT_UPLOAD_DIR + "/" + filename);
+            i.putExtra(FileUploader.KEY_REMOTE_FILE, filename);
             i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
             i.putExtra(com.owncloud.android.files.services.FileUploader.KEY_INSTANT_UPLOAD, true);
 

+ 34 - 9
src/com/owncloud/android/utils/FileStorageUtils.java

@@ -20,12 +20,14 @@ package com.owncloud.android.utils;
 
 import java.io.File;
 
+import android.annotation.SuppressLint;
 import android.net.Uri;
 import android.os.Environment;
 import android.os.StatFs;
+import android.util.Log;
 
 import com.owncloud.android.datamodel.OCFile;
-
+import com.owncloud.android.files.services.InstantUploadService;
 
 /**
  * Static methods to help in access to local file system.
@@ -33,33 +35,56 @@ import com.owncloud.android.datamodel.OCFile;
  * @author David A. Velasco
  */
 public class FileStorageUtils {
-    
+    private static final String LOG_TAG = "FileStorageUtils";
+
     public static final String getSavePath(String accountName) {
         File sdCard = Environment.getExternalStorageDirectory();
-        return sdCard.getAbsolutePath() + "/owncloud/" + Uri.encode(accountName, "@");   
-            // URL encoding is an 'easy fix' to overcome that NTFS and FAT32 don't allow ":" in file names, that can be in the accountName since 0.1.190B
+        return sdCard.getAbsolutePath() + "/owncloud/" + Uri.encode(accountName, "@");
+        // URL encoding is an 'easy fix' to overcome that NTFS and FAT32 don't allow ":" in file names, that can be in the accountName since 0.1.190B
     }
-    
+
     public static final String getDefaultSavePathFor(String accountName, OCFile file) {
         return getSavePath(accountName) + file.getRemotePath();
     }
-    
+
     public static final String getTemporalPath(String accountName) {
         File sdCard = Environment.getExternalStorageDirectory();
         return sdCard.getAbsolutePath() + "/owncloud/tmp/" + Uri.encode(accountName, "@");
             // URL encoding is an 'easy fix' to overcome that NTFS and FAT32 don't allow ":" in file names, that can be in the accountName since 0.1.190B
     }
 
+    @SuppressLint("NewApi")
     public static final long getUsableSpace(String accountName) {
         File savePath = Environment.getExternalStorageDirectory();
         if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD) {
             return savePath.getUsableSpace();
-            
+
         } else {
             StatFs stats = new StatFs(savePath.getAbsolutePath());
             return stats.getAvailableBlocks() * stats.getBlockSize();
         }
-        
+
+    }
+
+    // to ensure we will not add the slash twice between filename and
+    // folder-name
+    private static String getFileName(String filepath) {
+        if (filepath != null && !"".equals(filepath)) {
+            int psi = filepath.lastIndexOf('/');
+            String filename = filepath;
+            if (psi > -1) {
+                filename = filepath.substring(psi + 1, filepath.length());
+                Log.d(LOG_TAG, "extracted filename :" + filename);
+            }
+            return filename;
+        } else {
+            // Toast
+            Log.w(LOG_TAG, "the given filename was null or empty");
+            return null;
+        }
+    }
+
+    public static String getInstantUploadFilePath(String fileName) {
+        return InstantUploadService.INSTANT_UPLOAD_DIR + "/" + getFileName(fileName);
     }
-    
 }