Browse Source

Improve error feedback oC/1881

AndyScherzinger 7 years ago
parent
commit
c1570c6a54
25 changed files with 391 additions and 282 deletions
  1. 6 6
      src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java
  2. 8 4
      src/main/java/com/owncloud/android/db/UploadResult.java
  3. 2 3
      src/main/java/com/owncloud/android/operations/DetectAuthenticationMethodOperation.java
  4. 3 3
      src/main/java/com/owncloud/android/operations/OAuth2GetAccessToken.java
  5. 9 9
      src/main/java/com/owncloud/android/operations/UpdateOCVersionOperation.java
  6. 8 5
      src/main/java/com/owncloud/android/ui/activity/ErrorsWhileCopyingHandlerActivity.java
  7. 14 15
      src/main/java/com/owncloud/android/ui/activity/FileActivity.java
  8. 15 37
      src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java
  9. 8 12
      src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.java
  10. 2 4
      src/main/java/com/owncloud/android/ui/activity/LogHistoryActivity.java
  11. 6 4
      src/main/java/com/owncloud/android/ui/activity/ManageSpaceActivity.java
  12. 3 4
      src/main/java/com/owncloud/android/ui/activity/PassCodeActivity.java
  13. 16 19
      src/main/java/com/owncloud/android/ui/activity/Preferences.java
  14. 5 9
      src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java
  15. 6 5
      src/main/java/com/owncloud/android/ui/activity/ShareActivity.java
  16. 2 5
      src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java
  17. 49 10
      src/main/java/com/owncloud/android/ui/adapter/ExpandableUploadListAdapter.java
  18. 16 7
      src/main/java/com/owncloud/android/ui/dialog/CreateFolderDialogFragment.java
  19. 16 6
      src/main/java/com/owncloud/android/ui/dialog/RenameFileDialogFragment.java
  20. 5 4
      src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.java
  21. 19 9
      src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java
  22. 5 3
      src/main/java/com/owncloud/android/ui/fragment/ShareFileFragment.java
  23. 5 30
      src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java
  24. 160 68
      src/main/java/com/owncloud/android/utils/ErrorMessageAdapter.java
  25. 3 1
      src/main/res/values/strings.xml

+ 6 - 6
src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java

@@ -2259,7 +2259,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
     @Override
     public void onFailedSavingCertificate() {
         dismissDialog(SAML_DIALOG_TAG);
-        Toast.makeText(this, R.string.ssl_validator_not_saved, Toast.LENGTH_LONG).show();
+        showSnackMessage(R.string.ssl_validator_not_saved);
     }
 
     @Override
@@ -2337,11 +2337,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         dialog.show(ft, CREDENTIALS_DIALOG_TAG);
 
         if (!mIsFirstAuthAttempt) {
-            Toast.makeText(
-                    getApplicationContext(),
-                    getText(R.string.saml_authentication_wrong_pass),
-                    Toast.LENGTH_LONG
-            ).show();
+            showSnackMessage(R.string.saml_authentication_wrong_pass);
         } else {
             mIsFirstAuthAttempt = false;
         }
@@ -2353,4 +2349,8 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
     public void doNegativeAuthenticatioDialogClick() {
         mIsFirstAuthAttempt = true;
     }
+
+    private void showSnackMessage(int messageResource) {
+        Snackbar.make(findViewById(android.R.id.content), messageResource, Snackbar.LENGTH_LONG).show();
+    }
 }

+ 8 - 4
src/main/java/com/owncloud/android/db/UploadResult.java

@@ -37,7 +37,8 @@ public enum UploadResult {
     DELAYED_FOR_CHARGING(11),
     MAINTENANCE_MODE(12),
     LOCK_FAILED(13),
-    DELAYED_IN_POWER_SAVE_MODE(14);
+    DELAYED_IN_POWER_SAVE_MODE(14),
+    SSL_RECOVERABLE_PEER_UNVERIFIED(15);
 
     private final int value;
 
@@ -83,6 +84,8 @@ public enum UploadResult {
                 return LOCK_FAILED;
             case 14:
                 return DELAYED_IN_POWER_SAVE_MODE;
+            case 15:
+                return SSL_RECOVERABLE_PEER_UNVERIFIED;
         }
         return null;
     }
@@ -98,7 +101,6 @@ public enum UploadResult {
             case WRONG_CONNECTION:
             case INCORRECT_ADDRESS:
             case SSL_ERROR:
-            case SSL_RECOVERABLE_PEER_UNVERIFIED:
                 return NETWORK_CONNECTION;
             case ACCOUNT_EXCEPTION:
             case UNAUTHORIZED:
@@ -121,13 +123,15 @@ public enum UploadResult {
                 return DELAYED_FOR_CHARGING;
             case DELAYED_IN_POWER_SAVE_MODE:
                 return DELAYED_IN_POWER_SAVE_MODE;
+            case MAINTENANCE_MODE:
+                return MAINTENANCE_MODE;
+            case SSL_RECOVERABLE_PEER_UNVERIFIED:
+                return SSL_RECOVERABLE_PEER_UNVERIFIED;
             case UNKNOWN_ERROR:
                 if (result.getException() instanceof java.io.FileNotFoundException) {
                     return FILE_ERROR;
                 }
                 return UNKNOWN;
-            case MAINTENANCE_MODE:
-                return MAINTENANCE_MODE;
             case LOCK_FAILED:
                 return LOCK_FAILED;
             default:

+ 2 - 3
src/main/java/com/owncloud/android/operations/DetectAuthenticationMethodOperation.java

@@ -118,16 +118,15 @@ public class DetectAuthenticationMethodOperation extends RemoteOperation {
         Log_OC.d(TAG, "Authentication method found: " + authenticationMethodToString(authMethod));
         
         if (!authMethod.equals(AuthenticationMethod.UNKNOWN)) {
-            result = new RemoteOperationResult(true, result.getHttpCode(), null);
+            result = new RemoteOperationResult(true, result.getHttpCode(), result.getHttpPhrase(), null);
         }
-        ArrayList<Object> data = new ArrayList<Object>();
+        ArrayList<Object> data = new ArrayList<>();
         data.add(authMethod);
         result.setData(data);
         return result;  // same result instance, so that other errors
                         // can be handled by the caller transparently
 	}
 	
-	
 	private String authenticationMethodToString(AuthenticationMethod value) {
 	    switch (value){
 	    case NONE:

+ 3 - 3
src/main/java/com/owncloud/android/operations/OAuth2GetAccessToken.java

@@ -100,15 +100,15 @@ public class OAuth2GetAccessToken extends RemoteOperation {
                         result = new RemoteOperationResult(ResultCode.OAUTH2_ERROR);
                     
                     } else {
-                        result = new RemoteOperationResult(true, status, postMethod.getResponseHeaders());
-                        ArrayList<Object> data = new ArrayList<Object>();
+                        result = new RemoteOperationResult(true, postMethod);
+                        ArrayList<Object> data = new ArrayList<>();
                         data.add(mResultTokenMap);
                         result.setData(data);
                     }
                     
                 } else {
+                    result = new RemoteOperationResult(false, postMethod);
                     client.exhaustResponse(postMethod.getResponseBodyAsStream());
-                    result = new RemoteOperationResult(false, status, postMethod.getResponseHeaders());
                 }
             }
             

+ 9 - 9
src/main/java/com/owncloud/android/operations/UpdateOCVersionOperation.java

@@ -64,22 +64,22 @@ public class UpdateOCVersionOperation extends RemoteOperation {
         String statUrl = accountMngr.getUserData(mAccount, Constants.KEY_OC_BASE_URL);
         statUrl += AccountUtils.STATUS_PATH;
         RemoteOperationResult result = null;
-        GetMethod get = null;
+        GetMethod getMethod = null;
 
         String webDav = client.getWebdavUri().toString();
 
         try {
-            get = new GetMethod(statUrl);
-            int status = client.executeMethod(get);
+            getMethod = new GetMethod(statUrl);
+            int status = client.executeMethod(getMethod);
             if (status != HttpStatus.SC_OK) {
-                client.exhaustResponse(get.getResponseBodyAsStream());
-                result = new RemoteOperationResult(false, status, get.getResponseHeaders());
+                result = new RemoteOperationResult(false, getMethod);
+                client.exhaustResponse(getMethod.getResponseBodyAsStream());
                 
             } else {
-                String response = get.getResponseBodyAsString();
+                String response = getMethod.getResponseBodyAsString();
                 if (response != null) {
                     JSONObject json = new JSONObject(response);
-                    if (json != null && json.getString("version") != null) {
+                    if (json.getString("version") != null) {
 
                         String version = json.getString("version");
                         mOwnCloudVersion = new OwnCloudVersion(version);
@@ -112,8 +112,8 @@ public class UpdateOCVersionOperation extends RemoteOperation {
             Log_OC.e(TAG, "Check for update of ownCloud server version at " + webDav + ": " + result.getLogMessage(), e);
             
         } finally {
-            if (get != null) {
-                get.releaseConnection();
+            if (getMethod != null) {
+                getMethod.releaseConnection();
             }
         }
         return result;

+ 8 - 5
src/main/java/com/owncloud/android/ui/activity/ErrorsWhileCopyingHandlerActivity.java

@@ -26,6 +26,7 @@ import android.content.Intent;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.Handler;
+import android.support.design.widget.Snackbar;
 import android.support.v4.app.DialogFragment;
 import android.support.v7.app.AppCompatActivity;
 import android.text.method.ScrollingMovementMethod;
@@ -61,8 +62,7 @@ import java.util.ArrayList;
  * 
  * Shown when the error notification summarizing the list of errors is clicked by the user.
  */
-public class ErrorsWhileCopyingHandlerActivity  extends AppCompatActivity
-        implements OnClickListener {
+public class ErrorsWhileCopyingHandlerActivity  extends AppCompatActivity implements OnClickListener {
 
     private static final String TAG = ErrorsWhileCopyingHandlerActivity.class.getSimpleName();
 
@@ -282,9 +282,12 @@ public class ErrorsWhileCopyingHandlerActivity  extends AppCompatActivity
                 finish();
                 
             } else {
-                Toast t = Toast.makeText(ErrorsWhileCopyingHandlerActivity.this,
-                        getString(R.string.foreign_files_fail), Toast.LENGTH_LONG);
-                t.show();
+                Snackbar snackbar = Snackbar.make(
+                        findViewById(android.R.id.content),
+                        R.string.foreign_files_fail,
+                        Snackbar.LENGTH_LONG
+                );
+                snackbar.show();
             }
         }
     }    

+ 14 - 15
src/main/java/com/owncloud/android/ui/activity/FileActivity.java

@@ -31,6 +31,7 @@ import android.content.ServiceConnection;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.support.design.widget.Snackbar;
 import android.support.v4.app.Fragment;
 import android.support.v4.app.FragmentManager;
 import android.support.v4.app.FragmentTransaction;
@@ -305,10 +306,7 @@ public abstract class FileActivity extends DrawerActivity
             requestCredentialsUpdate(this);
 
             if (result.getCode() == ResultCode.UNAUTHORIZED) {
-                Toast t = Toast.makeText(this, ErrorMessageAdapter.getErrorCauseMessage(result,
-                        operation, getResources()),
-                    Toast.LENGTH_LONG);
-                t.show();
+                showSnackMessage(ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()));
             }
 
         } else if (!result.isSuccess() && ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED.equals(result.getCode())) {
@@ -326,10 +324,7 @@ public abstract class FileActivity extends DrawerActivity
                 updateFileFromDB();
 
             } else if (result.getCode() != ResultCode.CANCELLED) {
-                Toast t = Toast.makeText(this,
-                        ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
-                        Toast.LENGTH_LONG);
-                t.show();
+                showSnackMessage(ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()));
             }
 
         } else if (operation instanceof SynchronizeFileOperation) {
@@ -340,10 +335,7 @@ public abstract class FileActivity extends DrawerActivity
                 updateFileFromDB();
 
             } else {
-                Toast t = Toast.makeText(this,
-                        ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
-                        Toast.LENGTH_LONG);
-                t.show();
+                showSnackMessage(ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()));
             }
         }
     }
@@ -379,8 +371,7 @@ public abstract class FileActivity extends DrawerActivity
             }
             OwnCloudClient client;
             OwnCloudAccount ocAccount = new OwnCloudAccount(account, context);
-            client = (OwnCloudClientManagerFactory.getDefaultSingleton().
-                    removeClientFor(ocAccount));
+            client = (OwnCloudClientManagerFactory.getDefaultSingleton().removeClientFor(ocAccount));
             if (client != null) {
                 OwnCloudCredentials cred = client.getCredentials();
                 if (cred != null) {
@@ -406,7 +397,7 @@ public abstract class FileActivity extends DrawerActivity
             startActivityForResult(updateAccountCredentials, REQUEST_CODE__UPDATE_CREDENTIALS);
 
         } catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException e) {
-            Toast.makeText(context, R.string.auth_account_does_not_exist, Toast.LENGTH_SHORT).show();
+            showSnackMessage(getString(R.string.auth_account_does_not_exist));
         }
 
     }
@@ -585,4 +576,12 @@ public abstract class FileActivity extends DrawerActivity
         // nothing to do
     }
 
+    /**
+     * Show a temporary message in a Snackbar bound to the content view
+     *
+     * @param message Message to show.
+     */
+    public void showSnackMessage(String message) {
+        Snackbar.make(findViewById(android.R.id.content),message,Snackbar.LENGTH_LONG).show();
+    }
 }

+ 15 - 37
src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java

@@ -58,7 +58,6 @@ import android.view.View;
 import android.view.ViewTreeObserver;
 import android.widget.EditText;
 import android.widget.ImageView;
-import android.widget.Toast;
 
 import com.owncloud.android.MainApp;
 import com.owncloud.android.R;
@@ -921,14 +920,10 @@ public class FileDisplayActivity extends HookActivity
 
         } else {
             Log_OC.d(TAG, "User clicked on 'Update' with no selection");
-            Toast t = Toast.makeText(this, getString(R.string.filedisplay_no_file_selected),
-                    Toast.LENGTH_LONG);
-            t.show();
-            return;
+            showSnackMessage(getString(R.string.filedisplay_no_file_selected));
         }
     }
 
-
     private void requestUploadOfContentFromApps(Intent contentIntent, int resultCode) {
 
         ArrayList<Parcelable> streamsToUpload = new ArrayList<>();
@@ -1215,15 +1210,14 @@ public class FileDisplayActivity extends HookActivity
                                 getStorageManager().getFileByPath(getCurrentDir().getRemotePath());
 
                         if (currentDir == null) {
-                            // current folder was removed from the server 
-                            Toast.makeText(FileDisplayActivity.this,
+                            // current folder was removed from the server
+                            showSnackMessage(
                                     String.format(
                                             getString(R.string.
                                                     sync_current_folder_was_removed),
-                                            synchFolderRemotePath),
-
-                                    Toast.LENGTH_LONG)
-                                    .show();
+                                            synchFolderRemotePath
+                                    )
+                            );
 
                             browseToRoot();
 
@@ -1362,13 +1356,12 @@ public class FileDisplayActivity extends HookActivity
                     }
                     if (renamedInUpload) {
                         String newName = (new File(uploadedRemotePath)).getName();
-                        Toast msg = Toast.makeText(
-                                context,
+                        showSnackMessage(
                                 String.format(
                                         getString(R.string.filedetails_renamed_in_upload_msg),
-                                        newName),
-                                Toast.LENGTH_LONG);
-                        msg.show();
+                                        newName
+                                )
+                        );
                     }
                     if (uploadWasFine || getFile().fileExists()) {
                         ((FileDetailFragment) details).updateFileDetails(false, true);
@@ -1667,10 +1660,7 @@ public class FileDisplayActivity extends HookActivity
      */
     private void onRemoveFileOperationFinish(RemoveFileOperation operation,
                                              RemoteOperationResult result) {
-        Toast msg = Toast.makeText(this,
-                ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
-                Toast.LENGTH_LONG);
-        msg.show();
+        showSnackMessage(ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()));
 
         if (result.isSuccess()) {
             OCFile removedFile = operation.getFile();
@@ -1708,10 +1698,7 @@ public class FileDisplayActivity extends HookActivity
             refreshListOfFilesFragment(false);
         } else {
             try {
-                Toast msg = Toast.makeText(FileDisplayActivity.this,
-                        ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
-                        Toast.LENGTH_LONG);
-                msg.show();
+                showSnackMessage(ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()));
 
             } catch (NotFoundException e) {
                 Log_OC.e(TAG, "Error while trying to show fail message ", e);
@@ -1731,10 +1718,7 @@ public class FileDisplayActivity extends HookActivity
             refreshListOfFilesFragment(false);
         } else {
             try {
-                Toast msg = Toast.makeText(FileDisplayActivity.this,
-                        ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
-                        Toast.LENGTH_LONG);
-                msg.show();
+                showSnackMessage(ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()));
 
             } catch (NotFoundException e) {
                 Log_OC.e(TAG, "Error while trying to show fail message ", e);
@@ -1785,10 +1769,7 @@ public class FileDisplayActivity extends HookActivity
             }
 
         } else {
-            Toast msg = Toast.makeText(this,
-                    ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
-                    Toast.LENGTH_LONG);
-            msg.show();
+            showSnackMessage(ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()));
 
             if (result.isSslRecoverableException()) {
                 mLastSslUntrustedServerResult = result;
@@ -1821,10 +1802,7 @@ public class FileDisplayActivity extends HookActivity
             refreshListOfFilesFragment(false);
         } else {
             try {
-                Toast msg = Toast.makeText(FileDisplayActivity.this,
-                        ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
-                        Toast.LENGTH_LONG);
-                msg.show();
+                showSnackMessage(ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()));
 
             } catch (NotFoundException e) {
                 Log_OC.e(TAG, "Error while trying to show fail message ", e);

+ 8 - 12
src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.java

@@ -39,7 +39,6 @@ import android.view.MenuItem;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.Button;
-import android.widget.Toast;
 
 import com.owncloud.android.R;
 import com.owncloud.android.datamodel.OCFile;
@@ -419,10 +418,7 @@ public class FolderPickerActivity extends FileActivity implements FileFragment.C
             refreshListOfFilesFragment(false);
         } else {
             try {
-                Toast msg = Toast.makeText(FolderPickerActivity.this, 
-                        ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()), 
-                        Toast.LENGTH_LONG); 
-                msg.show();
+                showSnackMessage(ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()));
 
             } catch (NotFoundException e) {
                 Log_OC.e(TAG, "Error while trying to show fail message " , e);
@@ -461,13 +457,13 @@ public class FolderPickerActivity extends FileActivity implements FileFragment.C
                             getStorageManager().getFileByPath(getCurrentFolder().getRemotePath());
     
                         if (currentDir == null) {
-                            // current folder was removed from the server 
-                            Toast.makeText( FolderPickerActivity.this, 
-                                            String.format(
-                                                    getString(R.string.sync_current_folder_was_removed), 
-                                                    getCurrentFolder().getFileName()), 
-                                            Toast.LENGTH_LONG)
-                                .show();
+                            // current folder was removed from the server
+                            showSnackMessage(
+                                    String.format(
+                                            getString(R.string.sync_current_folder_was_removed),
+                                            getCurrentFolder().getFileName()
+                                    )
+                            );
                             browseToRoot();
                             
                         } else {

+ 2 - 4
src/main/java/com/owncloud/android/ui/activity/LogHistoryActivity.java

@@ -26,6 +26,7 @@ import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Build;
 import android.os.Bundle;
+import android.support.design.widget.Snackbar;
 import android.support.v4.app.Fragment;
 import android.support.v4.app.FragmentManager;
 import android.support.v4.app.FragmentTransaction;
@@ -35,7 +36,6 @@ import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.Button;
 import android.widget.TextView;
-import android.widget.Toast;
 
 import com.owncloud.android.R;
 import com.owncloud.android.lib.common.utils.Log_OC;
@@ -187,16 +187,14 @@ public class LogHistoryActivity extends ToolbarActivity {
         try {
             startActivity(intent);
         } catch (ActivityNotFoundException e) {
-            Toast.makeText(this, getString(R.string.log_send_no_mail_app), Toast.LENGTH_LONG).show();
+            Snackbar.make(findViewById(android.R.id.content),R.string.log_send_no_mail_app,Snackbar.LENGTH_LONG).show();
             Log_OC.i(TAG, "Could not find app for sending log history.");
         }
 
     }
 
     /**
-     *
      * Class for loading the log data async
-     *
      */
     private class LoadingLogTask extends AsyncTask<String, Void, String> {
         private final WeakReference<TextView> textViewReference;

+ 6 - 4
src/main/java/com/owncloud/android/ui/activity/ManageSpaceActivity.java

@@ -24,13 +24,13 @@ import android.content.SharedPreferences;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.preference.PreferenceManager;
+import android.support.design.widget.Snackbar;
 import android.support.v7.app.ActionBar;
 import android.support.v7.app.AppCompatActivity;
 import android.view.MenuItem;
 import android.view.View;
 import android.widget.Button;
 import android.widget.TextView;
-import android.widget.Toast;
 
 import com.owncloud.android.R;
 import com.owncloud.android.lib.common.utils.Log_OC;
@@ -142,9 +142,11 @@ public class ManageSpaceActivity extends AppCompatActivity {
         protected void onPostExecute(Boolean result) {
             super.onPostExecute(result);
             if (!result) {
-                Toast.makeText(getApplicationContext(),
-                        getString(R.string.manage_space_clear_data),
-                        Toast.LENGTH_LONG).show();
+                Snackbar.make(
+                        findViewById(android.R.id.content),
+                        R.string.manage_space_clear_data,
+                        Snackbar.LENGTH_LONG
+                ).show();
             } else {
                 finish();
                 System.exit(0);

+ 3 - 4
src/main/java/com/owncloud/android/ui/activity/PassCodeActivity.java

@@ -27,6 +27,7 @@ import android.content.SharedPreferences;
 import android.graphics.PorterDuff;
 import android.os.Bundle;
 import android.preference.PreferenceManager;
+import android.support.design.widget.Snackbar;
 import android.support.v7.app.AppCompatActivity;
 import android.text.Editable;
 import android.text.TextWatcher;
@@ -37,7 +38,6 @@ import android.view.inputmethod.InputMethodManager;
 import android.widget.Button;
 import android.widget.EditText;
 import android.widget.TextView;
-import android.widget.Toast;
 
 import com.owncloud.android.R;
 import com.owncloud.android.lib.common.utils.Log_OC;
@@ -377,9 +377,8 @@ public class PassCodeActivity extends AppCompatActivity {
     private void showErrorAndRestart(int errorMessage, int headerMessage,
                                      int explanationVisibility) {
         Arrays.fill(mPassCodeDigits, null);
-        CharSequence errorSeq = getString(errorMessage);
-        Toast.makeText(this, errorSeq, Toast.LENGTH_LONG).show();
-        mPassCodeHdr.setText(headerMessage);                // TODO check if really needed
+        Snackbar.make(findViewById(android.R.id.content), getString(errorMessage), Snackbar.LENGTH_LONG).show();
+        mPassCodeHdr.setText(headerMessage);                          // TODO check if really needed
         mPassCodeHdrExplanation.setVisibility(explanationVisibility); // TODO check if really needed
         clearBoxes();
     }

+ 16 - 19
src/main/java/com/owncloud/android/ui/activity/Preferences.java

@@ -46,6 +46,7 @@ import android.preference.PreferenceManager;
 import android.preference.PreferenceScreen;
 import android.preference.SwitchPreference;
 import android.support.annotation.LayoutRes;
+import android.support.design.widget.Snackbar;
 import android.support.v4.content.res.ResourcesCompat;
 import android.support.v7.app.ActionBar;
 import android.support.v7.app.AppCompatDelegate;
@@ -54,7 +55,6 @@ import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.Toast;
 
 import com.owncloud.android.BuildConfig;
 import com.owncloud.android.MainApp;
@@ -272,11 +272,7 @@ public class Preferences extends PreferenceActivity
                                     return true;
                                 } else {
                                     if (incoming) {
-                                        Toast.makeText(
-                                                MainApp.getAppContext(),
-                                                R.string.prefs_fingerprint_notsetup,
-                                                Toast.LENGTH_LONG)
-                                                .show();
+                                        showSnackMessage(R.string.prefs_fingerprint_notsetup);
                                         fPrint.setChecked(false);
                                     }
                                     SharedPreferences appPrefs =
@@ -366,11 +362,7 @@ public class Preferences extends PreferenceActivity
                             launchDavDroidLogin();
                         } catch (Throwable t) {
                             Log_OC.e(TAG, "Base Uri for account could not be resolved to call DAVdroid!", t);
-                            Toast.makeText(
-                                    MainApp.getAppContext(),
-                                    R.string.prefs_calendar_contacts_address_resolve_error,
-                                    Toast.LENGTH_SHORT)
-                                    .show();
+                            showSnackMessage(R.string.prefs_calendar_contacts_address_resolve_error);
                         }
                         return true;
                     }
@@ -675,11 +667,7 @@ public class Preferences extends PreferenceActivity
                         Uri.parse("https://f-droid.org/repository/browse/?fdid=at.bitfire.davdroid"));
                 startActivity(downloadIntent);
 
-                Toast.makeText(
-                        MainApp.getAppContext(),
-                        R.string.prefs_calendar_contacts_no_store_error,
-                        Toast.LENGTH_SHORT)
-                        .show();
+                showSnackMessage(R.string.prefs_calendar_contacts_no_store_error);
             }
         }
     }
@@ -751,7 +739,7 @@ public class Preferences extends PreferenceActivity
                 }
                 appPrefs.putBoolean(PassCodeActivity.PREFERENCE_SET_PASSCODE, true);
                 appPrefs.apply();
-                Toast.makeText(this, R.string.pass_code_stored, Toast.LENGTH_LONG).show();
+                showSnackMessage(R.string.pass_code_stored);
             }
         } else if (requestCode == ACTION_CONFIRM_PASSCODE && resultCode == RESULT_OK) {
             if (data.getBooleanExtra(PassCodeActivity.KEY_CHECK_RESULT, false)) {
@@ -761,10 +749,10 @@ public class Preferences extends PreferenceActivity
                 appPrefs.putBoolean(PassCodeActivity.PREFERENCE_SET_PASSCODE, false);
                 appPrefs.apply();
 
-                Toast.makeText(this, R.string.pass_code_removed, Toast.LENGTH_LONG).show();
+                showSnackMessage(R.string.pass_code_removed);
             }
         } else if (requestCode == ACTION_REQUEST_CODE_DAVDROID_SETUP && resultCode == RESULT_OK) {
-            Toast.makeText(this, R.string.prefs_calendar_contacts_sync_setup_successful, Toast.LENGTH_LONG).show();
+            showSnackMessage(R.string.prefs_calendar_contacts_sync_setup_successful);
         }
     }
 
@@ -911,4 +899,13 @@ public class Preferences extends PreferenceActivity
     public void onCancelMigration() {
         // Migration was canceled so we don't do anything
     }
+
+    /**
+     * Show a temporary message in a Snackbar bound to the content view
+     *
+     * @param messageResource Message to show.
+     */
+    private void showSnackMessage(int messageResource) {
+        Snackbar.make(findViewById(android.R.id.content), messageResource, Snackbar.LENGTH_LONG).show();
+    }
 }

+ 5 - 9
src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java

@@ -70,7 +70,6 @@ import android.widget.ListView;
 import android.widget.ProgressBar;
 import android.widget.Spinner;
 import android.widget.TextView;
-import android.widget.Toast;
 
 import com.owncloud.android.MainApp;
 import com.owncloud.android.R;
@@ -976,10 +975,7 @@ public class ReceiveExternalFilesActivity extends FileActivity
             populateDirectoryList();
         } else {
             try {
-                Toast msg = Toast.makeText(this,
-                        ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
-                        Toast.LENGTH_LONG);
-                msg.show();
+                showSnackMessage(ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()));
 
             } catch (NotFoundException e) {
                 Log_OC.e(TAG, "Error while trying to show fail message ", e);
@@ -1108,12 +1104,12 @@ public class ReceiveExternalFilesActivity extends FileActivity
 
                         if (currentDir == null) {
                             // current folder was removed from the server
-                            Toast.makeText(context,
+                            showSnackMessage(
                                     String.format(
                                             getString(R.string.sync_current_folder_was_removed),
-                                            getCurrentFolder().getFileName()),
-                                    Toast.LENGTH_LONG)
-                                    .show();
+                                            getCurrentFolder().getFileName()
+                                    )
+                            );
                             browseToRoot();
 
                         } else {

+ 6 - 5
src/main/java/com/owncloud/android/ui/activity/ShareActivity.java

@@ -25,10 +25,10 @@ import android.app.SearchManager;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
+import android.support.design.widget.Snackbar;
 import android.support.v4.app.DialogFragment;
 import android.support.v4.app.Fragment;
 import android.support.v4.app.FragmentTransaction;
-import android.widget.Toast;
 
 import com.owncloud.android.R;
 import com.owncloud.android.datamodel.OCFile;
@@ -354,10 +354,11 @@ public class ShareActivity extends FileActivity implements ShareFragmentListener
                 }
 
             } else {
-                Toast t = Toast.makeText(this,
-                    ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
-                    Toast.LENGTH_LONG);
-                t.show();
+                Snackbar.make(
+                        findViewById(android.R.id.content),
+                        ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
+                        Snackbar.LENGTH_LONG
+                ).show();
             }
         }
     }

+ 2 - 5
src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java

@@ -40,7 +40,6 @@ import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
-import android.widget.Toast;
 
 import com.evernote.android.job.JobRequest;
 import com.evernote.android.job.util.support.PersistableBundleCompat;
@@ -168,8 +167,7 @@ public class UploadListActivity extends FileActivity implements UploadListFragme
         /// TODO is this path still active?
         File f = new File(file.getLocalPath());
         if(!f.exists()) {
-            Toast.makeText(this, "Cannot open. Local file does not exist.",
-                    Toast.LENGTH_SHORT).show();
+            showSnackMessage(getString(R.string.local_file_not_found_toast));
         } else {
             openFileWithDefault(file.getLocalPath());
         }
@@ -190,9 +188,8 @@ public class UploadListActivity extends FileActivity implements UploadListFragme
         try {
             startActivity(myIntent);
         } catch (ActivityNotFoundException e) {
-            Toast.makeText(this, "Found no app to open this file.", Toast.LENGTH_LONG).show();
+            showSnackMessage(getString(R.string.file_list_no_app_for_file_type));
             Log_OC.i(TAG, "Could not find app for sending log history.");
-
         }        
     }
 

+ 49 - 10
src/main/java/com/owncloud/android/ui/adapter/ExpandableUploadListAdapter.java

@@ -23,6 +23,7 @@ import android.accounts.Account;
 import android.content.Context;
 import android.database.DataSetObserver;
 import android.graphics.Bitmap;
+import android.support.design.widget.Snackbar;
 import android.text.format.DateUtils;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -355,13 +356,50 @@ public class ExpandableUploadListAdapter extends BaseExpandableListAdapter imple
             
             view.setOnClickListener(null);
 
+            // retry
+            if (upload.getUploadStatus() == UploadStatus.UPLOAD_FAILED) {
+                if (UploadResult.CREDENTIAL_ERROR.equals(upload.getLastResult())) {
+                    view.setOnClickListener(new OnClickListener() {
+                        @Override
+                        public void onClick(View v) {
+                            mParentActivity.getFileOperationsHelper().checkCurrentCredentials(
+                                upload.getAccount(mParentActivity)
+                            );
+                        }
+                    });
+
+                } else {
+                    // not a credentials error
+                    view.setOnClickListener(new OnClickListener() {
+                        @Override
+                        public void onClick(View v) {
+                        File file = new File(upload.getLocalPath());
+                        if (file.exists()) {
+                            FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
+                            requester.retry(mParentActivity, upload);
+                            refreshView();
+                        } else {
+                            Snackbar.make(
+                                    v.getRootView().findViewById(android.R.id.content),
+                                    mParentActivity.getString(R.string.local_file_not_found_toast),
+                                    Snackbar.LENGTH_LONG
+                            ).show();
+                        }
+                        }
+                    });
+                }
+            } else {
+                view.setOnClickListener(null);
+            }
+
             /// Set icon or thumbnail
             ImageView fileIcon = (ImageView) view.findViewById(R.id.thumbnail);
             fileIcon.setImageResource(R.drawable.file);
 
-            /** Cancellation needs do be checked and done before changing the drawable in fileIcon, or
+            /*
+             * Cancellation needs do be checked and done before changing the drawable in fileIcon, or
              * {@link ThumbnailsCacheManager#cancelPotentialWork} will NEVER cancel any task.
-             **/
+             */
             OCFile fakeFileToCheatThumbnailsCacheManagerInterface = new OCFile(upload.getRemotePath());
             fakeFileToCheatThumbnailsCacheManagerInterface.setStoragePath(upload.getLocalPath());
             fakeFileToCheatThumbnailsCacheManagerInterface.setMimetype(upload.getMimeType());
@@ -531,11 +569,6 @@ public class ExpandableUploadListAdapter extends BaseExpandableListAdapter imple
                                 R.string.uploads_view_upload_status_service_interrupted
                         );
                         break;
-                    case UNKNOWN:
-                        status = mParentActivity.getString(
-                                R.string.uploads_view_upload_status_unknown_fail
-                        );
-                        break;
                     case CANCELLED:
                         // should not get here ; cancelled uploads should be wiped out
                         status = mParentActivity.getString(
@@ -549,15 +582,21 @@ public class ExpandableUploadListAdapter extends BaseExpandableListAdapter imple
                     case MAINTENANCE_MODE:
                         status = mParentActivity.getString(R.string.maintenance_mode);
                         break;
-                    case LOCK_FAILED:
-                        status = mParentActivity.getString(R.string.lock_failed);
+                    case SSL_RECOVERABLE_PEER_UNVERIFIED:
+                        status =
+                                mParentActivity.getString(
+                                        R.string.uploads_view_upload_status_failed_ssl_certificate_not_trusted
+                                );
+                        break;
+                    case UNKNOWN:
+                        status = mParentActivity.getString(R.string.uploads_view_upload_status_unknown_fail);
                         break;
                     case DELAYED_IN_POWER_SAVE_MODE:
                         status = mParentActivity.getString(
                                 R.string.uploads_view_upload_status_waiting_exit_power_save_mode);
                         break;
                     default:
-                        status = "Naughty devs added a new fail result but no description for the user";
+                        status = "New fail result but no description for the user";
                         break;
                 }
                 break;

+ 16 - 7
src/main/java/com/owncloud/android/ui/dialog/CreateFolderDialogFragment.java

@@ -24,6 +24,7 @@ import android.app.Dialog;
 import android.content.DialogInterface;
 import android.graphics.PorterDuff;
 import android.os.Bundle;
+import android.support.design.widget.Snackbar;
 import android.support.v4.app.DialogFragment;
 import android.support.v7.app.AlertDialog;
 import android.view.LayoutInflater;
@@ -31,7 +32,6 @@ import android.view.View;
 import android.view.WindowManager.LayoutParams;
 import android.widget.EditText;
 import android.widget.TextView;
-import android.widget.Toast;
 
 import com.owncloud.android.R;
 import com.owncloud.android.datamodel.OCFile;
@@ -116,10 +116,7 @@ public class CreateFolderDialogFragment
                         .getText().toString().trim();
             
             if (newFolderName.length() <= 0) {
-                Toast.makeText(
-                        getActivity(),
-                        R.string.filename_empty, 
-                        Toast.LENGTH_LONG).show();
+                showSnackMessage(R.string.filename_empty);
                 return;
             }
             boolean serverWithForbiddenChars = ((ComponentsGetter)getActivity()).
@@ -132,7 +129,7 @@ public class CreateFolderDialogFragment
                 } else {
                     messageId = R.string.filename_forbidden_characters;
                 }
-                Toast.makeText(getActivity(), messageId, Toast.LENGTH_LONG).show();
+                showSnackMessage(messageId);
 
                 return;
             }
@@ -143,5 +140,17 @@ public class CreateFolderDialogFragment
                 getFileOperationsHelper().createFolder(path, false);
         }
     }
-        
+
+    /**
+     * Show a temporary message in a Snackbar bound to the content view of the parent Activity
+     *
+     * @param messageResource Message to show.
+     */
+    private void showSnackMessage(int messageResource) {
+        Snackbar.make(
+                getActivity().findViewById(android.R.id.content),
+                messageResource,
+                Snackbar.LENGTH_LONG
+        ).show();
+    }
 }

+ 16 - 6
src/main/java/com/owncloud/android/ui/dialog/RenameFileDialogFragment.java

@@ -30,6 +30,7 @@ import android.app.Dialog;
 import android.content.DialogInterface;
 import android.graphics.PorterDuff;
 import android.os.Bundle;
+import android.support.design.widget.Snackbar;
 import android.support.v4.app.DialogFragment;
 import android.support.v7.app.AlertDialog;
 import android.view.LayoutInflater;
@@ -37,7 +38,6 @@ import android.view.View;
 import android.view.WindowManager.LayoutParams;
 import android.widget.EditText;
 import android.widget.TextView;
-import android.widget.Toast;
 
 import com.owncloud.android.R;
 import com.owncloud.android.datamodel.OCFile;
@@ -130,10 +130,7 @@ public class RenameFileDialogFragment
                     .getText().toString().trim();
             
             if (newFileName.length() <= 0) {
-                Toast.makeText(
-                        getActivity(),
-                        R.string.filename_empty, 
-                        Toast.LENGTH_LONG).show();
+                showSnackMessage(R.string.filename_empty);
                 return;
             }
 
@@ -147,7 +144,7 @@ public class RenameFileDialogFragment
                 } else {
                     messageId = R.string.filename_forbidden_characters;
                 }
-                Toast.makeText(getActivity(), messageId, Toast.LENGTH_LONG).show();
+                showSnackMessage(messageId);
                 return;
             }
 
@@ -156,4 +153,17 @@ public class RenameFileDialogFragment
 
         }
     }
+
+    /**
+     * Show a temporary message in a Snackbar bound to the content view of the parent Activity
+     *
+     * @param messageResource Message to show.
+     */
+    private void showSnackMessage(int messageResource) {
+        Snackbar.make(
+                getActivity().findViewById(android.R.id.content),
+                messageResource,
+                Snackbar.LENGTH_LONG
+        ).show();
+    }
 }

+ 5 - 4
src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.java

@@ -22,6 +22,7 @@ import android.app.Dialog;
 import android.content.DialogInterface;
 import android.graphics.PorterDuff;
 import android.os.Bundle;
+import android.support.design.widget.Snackbar;
 import android.support.v4.app.DialogFragment;
 import android.support.v7.app.AlertDialog;
 import android.view.LayoutInflater;
@@ -29,7 +30,6 @@ import android.view.View;
 import android.view.WindowManager;
 import android.widget.EditText;
 import android.widget.TextView;
-import android.widget.Toast;
 
 import com.owncloud.android.R;
 import com.owncloud.android.datamodel.OCFile;
@@ -115,10 +115,11 @@ public class SharePasswordDialogFragment extends DialogFragment
                         .getText().toString();
 
             if (password.length() <= 0) {
-                Toast.makeText(
-                        getActivity(),
+                Snackbar.make(
+                        getActivity().findViewById(android.R.id.content),
                         R.string.share_link_empty_password,
-                        Toast.LENGTH_LONG).show();
+                        Snackbar.LENGTH_LONG
+                ).show();
                 return;
             }
 

+ 19 - 9
src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java

@@ -56,7 +56,6 @@ import android.widget.ListView;
 import android.widget.PopupMenu;
 import android.widget.RelativeLayout;
 import android.widget.TextView;
-import android.widget.Toast;
 
 import com.owncloud.android.MainApp;
 import com.owncloud.android.R;
@@ -398,7 +397,7 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
 
     /**
      * registers {@link android.view.View.OnClickListener} and {@link android.view.View.OnLongClickListener}
-     * on the Upload mini FAB for the linked action and {@link Toast} showing the underlying action.
+     * on the Upload mini FAB for the linked action and {@link Snackbar} showing the underlying action.
      */
     private void registerFabUploadListeners() {
         getFabUpload().setOnClickListener(new View.OnClickListener() {
@@ -414,7 +413,7 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
         getFabUpload().setOnLongClickListener(new View.OnLongClickListener() {
             @Override
             public boolean onLongClick(View v) {
-                Toast.makeText(getActivity(), R.string.actionbar_upload, Toast.LENGTH_SHORT).show();
+                showSnackMessage(R.string.actionbar_upload);
                 return true;
             }
         });
@@ -422,7 +421,7 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
 
     /**
      * registers {@link android.view.View.OnClickListener} and {@link android.view.View.OnLongClickListener}
-     * on the 'Create Dir' mini FAB for the linked action and {@link Toast} showing the underlying action.
+     * on the 'Create Dir' mini FAB for the linked action and {@link Snackbar} showing the underlying action.
      */
     private void registerFabMkDirListeners() {
         getFabMkdir().setOnClickListener(new View.OnClickListener() {
@@ -439,7 +438,7 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
         getFabMkdir().setOnLongClickListener(new View.OnLongClickListener() {
             @Override
             public boolean onLongClick(View v) {
-                Toast.makeText(getActivity(), R.string.actionbar_mkdir, Toast.LENGTH_SHORT).show();
+                showSnackMessage(R.string.actionbar_mkdir);
                 return true;
             }
         });
@@ -447,7 +446,7 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
 
     /**
      * registers {@link android.view.View.OnClickListener} and {@link android.view.View.OnLongClickListener}
-     * on the Upload from App mini FAB for the linked action and {@link Toast} showing the underlying action.
+     * on the Upload from App mini FAB for the linked action and {@link Snackbar} showing the underlying action.
      */
     private void registerFabUploadFromAppListeners() {
         getFabUploadFromApp().setOnClickListener(new View.OnClickListener() {
@@ -471,9 +470,7 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
         getFabUploadFromApp().setOnLongClickListener(new View.OnLongClickListener() {
             @Override
             public boolean onLongClick(View v) {
-                Toast.makeText(getActivity(),
-                        R.string.actionbar_upload_from_apps,
-                        Toast.LENGTH_SHORT).show();
+                showSnackMessage(R.string.actionbar_upload_from_apps);
                 return true;
             }
         });
@@ -1663,4 +1660,17 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
         }
     }
 
+
+    /**
+     * Show a temporary message in a Snackbar bound to the content view of the parent Activity
+     *
+     * @param messageResource Message to show.
+     */
+    private void showSnackMessage(int messageResource) {
+        Snackbar.make(
+                getActivity().findViewById(android.R.id.content),
+                messageResource,
+                Snackbar.LENGTH_LONG
+        ).show();
+    }
 }

+ 5 - 3
src/main/java/com/owncloud/android/ui/fragment/ShareFileFragment.java

@@ -43,7 +43,6 @@ import android.widget.ListAdapter;
 import android.widget.ListView;
 import android.widget.ScrollView;
 import android.widget.TextView;
-import android.widget.Toast;
 
 import com.owncloud.android.R;
 import com.owncloud.android.authentication.AccountUtils;
@@ -247,8 +246,11 @@ public class ShareFileFragment extends Fragment implements ShareUserListAdapter.
                     // Show Search Fragment
                     mListener.showSearchUsersAndGroups();
                 } else {
-                    String message = getString(R.string.share_sharee_unavailable);
-                    Toast.makeText(getActivity(), message, Toast.LENGTH_LONG).show();
+                    Snackbar.make(
+                            getActivity().findViewById(android.R.id.content),
+                            getString(R.string.share_sharee_unavailable),
+                            Snackbar.LENGTH_LONG
+                    ).show();
                 }
             }
         });

+ 5 - 30
src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java

@@ -182,10 +182,10 @@ public class FileOperationsHelper {
                 try {
                     mFileActivity.startActivity(openFileWithIntent);
                 } catch (ActivityNotFoundException anfe) {
-                    showNoAppForFileTypeToast(mFileActivity.getApplicationContext());
+                    mFileActivity.showSnackMessage(mFileActivity.getString(R.string.file_list_no_app_for_file_type));
                 }
             } else {
-                showNoAppForFileTypeToast(mFileActivity.getApplicationContext());
+                mFileActivity.showSnackMessage(mFileActivity.getString(R.string.file_list_no_app_for_file_type));
             }
 
         } else {
@@ -193,18 +193,6 @@ public class FileOperationsHelper {
         }
     }
 
-    /**
-     * Displays a toast stating that no application could be found to open the file.
-     *
-     * @param context the context to be able to show a toast.
-     */
-    private void showNoAppForFileTypeToast(Context context) {
-        Toast.makeText(context,
-                R.string.file_list_no_app_for_file_type, Toast.LENGTH_SHORT)
-                .show();
-    }
-
-
     /**
      * Helper method to share a file via a public link. Starts a request to do it in {@link OperationsService}
      *
@@ -234,11 +222,7 @@ public class FileOperationsHelper {
 
         } else {
             // Show a Message
-            Toast t = Toast.makeText(
-                    mFileActivity, mFileActivity.getString(R.string.share_link_no_support_share_api),
-                    Toast.LENGTH_LONG
-            );
-            t.show();
+            mFileActivity.showSnackMessage(mFileActivity.getString(R.string.share_link_no_support_share_api));
         }
     }
 
@@ -259,11 +243,7 @@ public class FileOperationsHelper {
             }
         } else {
             // Show a Message
-            Toast t = Toast.makeText(
-                    mFileActivity, mFileActivity.getString(R.string.share_link_no_support_share_api),
-                    Toast.LENGTH_LONG
-            );
-            t.show();
+            mFileActivity.showSnackMessage(mFileActivity.getString(R.string.share_link_no_support_share_api));
         }
     }
 
@@ -352,11 +332,7 @@ public class FileOperationsHelper {
 
         } else {
             // Show a Message
-            Toast t = Toast.makeText(mFileActivity,
-                    mFileActivity.getString(R.string.share_link_no_support_share_api),
-                    Toast.LENGTH_LONG);
-            t.show();
-
+            mFileActivity.showSnackMessage(mFileActivity.getString(R.string.share_link_no_support_share_api));
         }
     }
 
@@ -370,7 +346,6 @@ public class FileOperationsHelper {
         intent.putExtra(FileActivity.EXTRA_FILE, file);
         intent.putExtra(FileActivity.EXTRA_ACCOUNT, mFileActivity.getAccount());
         mFileActivity.startActivity(intent);
-
     }
 
 

+ 160 - 68
src/main/java/com/owncloud/android/utils/ErrorMessageAdapter.java

@@ -1,25 +1,29 @@
 /**
- * ownCloud Android client application
+ *   ownCloud Android client application
  *
- * @author masensio
- * Copyright (C) 2014 ownCloud Inc.
+ *   @author masensio
+ *   Copyright (C) 2016 ownCloud GmbH.
  *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License version 2,
+ *   as published by the Free Software Foundation.
  *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+
 package com.owncloud.android.utils;
 
 import android.content.res.Resources;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
 
 import com.owncloud.android.R;
 import com.owncloud.android.lib.common.operations.RemoteOperation;
@@ -47,35 +51,82 @@ import java.net.SocketTimeoutException;
 
 /**
  * Class to choose proper error messages to show to the user depending on the results of operations,
- * always following the same policy.
+ * always following the same policy
  */
 public class ErrorMessageAdapter {
 
-    public static String getErrorCauseMessage(RemoteOperationResult result,
-                                              RemoteOperation operation, Resources res) {
+    public ErrorMessageAdapter() { }
+
+    /**
+     * Return an internationalized user message corresponding to an operation result
+     * and the operation performed.
+     *
+     * @param result        Result of a {@link RemoteOperation} performed.
+     * @param operation     Operation performed.
+     * @param res           Reference to app resources, for i18n.
+     * @return              User message corresponding to 'result' and 'operation'.
+     */
+    @NonNull
+    public static String getErrorCauseMessage(
+            RemoteOperationResult result,
+            RemoteOperation operation,
+            Resources res
+    ) {
+        String message = getSpecificMessageForResultAndOperation(result, operation, res);
+
+        if (message == null || message.length() <= 0) {
+            message = getCommonMessageForResult(result, res);
+        }
 
-        String message = null;
+        if (message == null || message.length() <= 0) {
+            message = getGenericErrorMessageForOperation(operation, res);
+        }
 
-        if (!result.isSuccess() && isCommonError(result.getCode())) {
-            message = getCommonErrorMessage(result, res);
+        if (message == null) {
+            if (result.isSuccess()) {
+                message = res.getString(R.string.common_ok);
 
-        } else if (operation instanceof UploadFileOperation) {
+            } else {
+                message = res.getString(R.string.common_error_unknown);
+            }
+        }
+
+        return message;
+    }
+
+    /**
+     * Return a user message corresponding to an operation result and specific for the operation
+     * performed.
+     *
+     * @param result        Result of a {@link RemoteOperation} performed.
+     * @param operation     Operation performed.
+     * @param res           Reference to app resources, for i18n.
+     * @return              User message corresponding to 'result' and 'operation', or NULL if there is no
+     *                      specific message for both.
+     */
+    @Nullable
+    private static String getSpecificMessageForResultAndOperation(
+            RemoteOperationResult result,
+            RemoteOperation operation,
+            Resources res
+    ) {
+
+        String message = null;
+
+        if (operation instanceof UploadFileOperation) {
 
             if (result.isSuccess()) {
                 message = String.format(
                         res.getString(R.string.uploader_upload_succeeded_content_single),
                         ((UploadFileOperation) operation).getFileName());
             } else {
+
                 if (result.getCode() == ResultCode.LOCAL_STORAGE_FULL
                         || result.getCode() == ResultCode.LOCAL_STORAGE_NOT_COPIED) {
                     message = String.format(
                             res.getString(R.string.error__upload__local_file_not_copied),
                             ((UploadFileOperation) operation).getFileName(),
                             res.getString(R.string.app_name));
-                /*
-                } else if (result.getCode() == ResultCode.QUOTA_EXCEEDED) {
-                    message = res.getString(R.string.failed_upload_quota_exceeded_text);
-                    */
 
                 } else if (result.getCode() == ResultCode.FORBIDDEN) {
                     message = String.format(res.getString(R.string.forbidden_permissions),
@@ -84,10 +135,6 @@ public class ErrorMessageAdapter {
                 } else if (result.getCode() == ResultCode.INVALID_CHARACTER_DETECT_IN_SERVER) {
                     message = res.getString(R.string.filename_forbidden_charaters_from_server);
 
-                } else {
-                    message = String.format(
-                            res.getString(R.string.uploader_upload_failed_content_single),
-                            ((UploadFileOperation) operation).getFileName());
                 }
             }
 
@@ -102,10 +149,6 @@ public class ErrorMessageAdapter {
                 if (result.getCode() == ResultCode.FILE_NOT_FOUND) {
                     message = res.getString(R.string.downloader_download_file_not_found);
 
-                } else {
-                    message = String.format(
-                            res.getString(R.string.downloader_download_failed_content), new File(
-                                    ((DownloadFileOperation) operation).getSavePath()).getName());
                 }
             }
 
@@ -118,9 +161,6 @@ public class ErrorMessageAdapter {
                     // Error --> No permissions
                     message = String.format(res.getString(R.string.forbidden_permissions),
                             res.getString(R.string.forbidden_permissions_delete));
-
-                } else {
-                    message = res.getString(R.string.remove_fail_msg);
                 }
             }
 
@@ -139,8 +179,6 @@ public class ErrorMessageAdapter {
             } else if (result.getCode() == ResultCode.INVALID_CHARACTER_DETECT_IN_SERVER) {
                 message = res.getString(R.string.filename_forbidden_charaters_from_server);
 
-            } else {
-                message = res.getString(R.string.rename_server_fail_msg);
             }
 
         } else if (operation instanceof SynchronizeFileOperation) {
@@ -159,8 +197,6 @@ public class ErrorMessageAdapter {
             } else if (result.getCode() == ResultCode.INVALID_CHARACTER_DETECT_IN_SERVER) {
                 message = res.getString(R.string.filename_forbidden_charaters_from_server);
 
-            } else {
-                message = res.getString(R.string.create_dir_fail_msg);
             }
 
         } else if (operation instanceof CreateShareViaLinkOperation ||
@@ -169,7 +205,7 @@ public class ErrorMessageAdapter {
             if (result.getData() != null && result.getData().size() > 0) {
                 message = (String) result.getData().get(0);     // share API sends its own error messages
 
-            } else if (result.getCode() == ResultCode.SHARE_NOT_FOUND) {
+            } else if (result.getCode() == ResultCode.SHARE_NOT_FOUND)  {
                 message = res.getString(R.string.share_link_file_no_exist);
 
             } else if (result.getCode() == ResultCode.SHARE_FORBIDDEN) {
@@ -177,9 +213,6 @@ public class ErrorMessageAdapter {
                 message = String.format(res.getString(R.string.forbidden_permissions),
                         res.getString(R.string.share_link_forbidden_permissions));
 
-            } else {    // Generic error
-                // Show a Message, operation finished without success
-                message = res.getString(R.string.share_link_file_error);
             }
 
         } else if (operation instanceof UnshareOperation) {
@@ -195,9 +228,6 @@ public class ErrorMessageAdapter {
                 message = String.format(res.getString(R.string.forbidden_permissions),
                         res.getString(R.string.unshare_link_forbidden_permissions));
 
-            } else {    // Generic error
-                // Show a Message, operation finished without success
-                message = res.getString(R.string.unshare_link_file_error);
             }
 
         } else if (operation instanceof UpdateShareViaLinkOperation ||
@@ -214,9 +244,6 @@ public class ErrorMessageAdapter {
                 message = String.format(res.getString(R.string.forbidden_permissions),
                         res.getString(R.string.update_link_forbidden_permissions));
 
-            } else {    // Generic error
-                // Show a Message, operation finished without success
-                message = res.getString(R.string.update_link_file_error);
             }
 
         } else if (operation instanceof MoveFileOperation) {
@@ -236,9 +263,6 @@ public class ErrorMessageAdapter {
             } else if (result.getCode() == ResultCode.INVALID_CHARACTER_DETECT_IN_SERVER) {
                 message = res.getString(R.string.filename_forbidden_charaters_from_server);
 
-            } else {    // Generic error
-                // Show a Message, operation finished without success
-                message = res.getString(R.string.move_file_error);
             }
 
         } else if (operation instanceof SynchronizeFolderOperation) {
@@ -247,13 +271,10 @@ public class ErrorMessageAdapter {
                 String folderPathName = new File(
                         ((SynchronizeFolderOperation) operation).getFolderPath()).getName();
                 if (result.getCode() == ResultCode.FILE_NOT_FOUND) {
-                    message = String.format(res.getString(R.string.sync_current_folder_was_removed),
-                            folderPathName);
-
-                } else {    // Generic error
-                    // Show a Message, operation finished without success
-                    message = String.format(res.getString(R.string.sync_folder_failed_content),
-                            folderPathName);
+                    message = String.format(
+                            res.getString(R.string.sync_current_folder_was_removed),
+                            folderPathName
+                    );
                 }
             }
 
@@ -271,21 +292,27 @@ public class ErrorMessageAdapter {
                 message = String.format(res.getString(R.string.forbidden_permissions),
                         res.getString(R.string.forbidden_permissions_copy));
 
-            } else {    // Generic error
-                // Show a Message, operation finished without success
-                message = res.getString(R.string.copy_file_error);
             }
         }
 
         return message;
     }
 
-    private static String getCommonErrorMessage(RemoteOperationResult result, Resources res) {
+
+    /**
+     * Return a user message corresponding to an operation result with no knowledge about the operation
+     * performed.
+     *
+     * @param result        Result of a {@link RemoteOperation} performed.
+     * @param res           Reference to app resources, for i18n.
+     * @return              User message corresponding to 'result'.
+     */
+    @Nullable
+    private static String getCommonMessageForResult(RemoteOperationResult result, Resources res) {
 
         String message = null;
 
         if (!result.isSuccess()) {
-
             if (result.getCode() == ResultCode.WRONG_CONNECTION) {
                 message = res.getString(R.string.network_error_socket_exception);
 
@@ -294,24 +321,89 @@ public class ErrorMessageAdapter {
 
                 if (result.getException() instanceof SocketTimeoutException) {
                     message = res.getString(R.string.network_error_socket_timeout_exception);
+
                 } else if (result.getException() instanceof ConnectTimeoutException) {
                     message = res.getString(R.string.network_error_connect_timeout_exception);
                 }
 
             } else if (result.getCode() == ResultCode.HOST_NOT_AVAILABLE) {
                 message = res.getString(R.string.network_host_not_available);
+
             } else if (result.getCode() == ResultCode.MAINTENANCE_MODE) {
                 message = res.getString(R.string.maintenance_mode);
+
+            } else if (result.getCode() == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED) {
+                message = res.getString(
+                        R.string.uploads_view_upload_status_failed_ssl_certificate_not_trusted
+                );
+
+            } else if (result.getHttpPhrase() != null && result.getHttpPhrase().length() > 0) {
+                // last chance: error message from server
+                message = result.getHttpPhrase();
             }
         }
 
         return message;
     }
 
-    private static boolean isCommonError(RemoteOperationResult.ResultCode code) {
-        return code == ResultCode.WRONG_CONNECTION ||
-                code == ResultCode.TIMEOUT ||
-                code == ResultCode.HOST_NOT_AVAILABLE ||
-                code == ResultCode.MAINTENANCE_MODE;
+
+    /**
+     * Return a user message corresponding to a generic error for a given operation.
+     *
+     * @param operation     Operation performed.
+     * @param res           Reference to app resources, for i18n.
+     * @return              User message corresponding to a generic error of 'operation'.
+     */
+    @Nullable
+    private static String getGenericErrorMessageForOperation(RemoteOperation operation, Resources res) {
+        String message = null;
+
+        if (operation instanceof UploadFileOperation) {
+            message = String.format(
+                    res.getString(R.string.uploader_upload_failed_content_single),
+                    ((UploadFileOperation) operation).getFileName());
+
+        } else if (operation instanceof DownloadFileOperation) {
+            message = String.format(
+                    res.getString(R.string.downloader_download_failed_content),
+                    new File(((DownloadFileOperation) operation).getSavePath()).getName()
+            );
+
+        } else if (operation instanceof RemoveFileOperation) {
+            message = res.getString(R.string.remove_fail_msg);
+
+        } else if (operation instanceof RenameFileOperation) {
+            message = res.getString(R.string.rename_server_fail_msg);
+
+        } else if (operation instanceof CreateFolderOperation) {
+            message = res.getString(R.string.create_dir_fail_msg);
+
+        } else if (operation instanceof CreateShareViaLinkOperation ||
+                operation instanceof CreateShareWithShareeOperation
+                ) {
+            message = res.getString(R.string.share_link_file_error);
+
+        } else if (operation instanceof UnshareOperation) {
+            message = res.getString(R.string.unshare_link_file_error);
+
+        } else if (operation instanceof UpdateShareViaLinkOperation ||
+                operation instanceof UpdateSharePermissionsOperation
+                ) {
+            message = res.getString(R.string.update_link_file_error);
+
+        } else if (operation instanceof MoveFileOperation) {
+            message = res.getString(R.string.move_file_error);
+
+        } else if (operation instanceof SynchronizeFolderOperation) {
+            String folderPathName = new File(
+                    ((SynchronizeFolderOperation) operation).getFolderPath()
+            ).getName();
+            message = String.format(res.getString(R.string.sync_folder_failed_content), folderPathName);
+
+        } else if (operation instanceof CopyFileOperation) {
+            message = res.getString(R.string.copy_file_error);
+        }
+
+        return message;
     }
 }

+ 3 - 1
src/main/res/values/strings.xml

@@ -150,6 +150,8 @@
     <string name="uploader_upload_failed_ticker">Upload failed</string>
     <string name="uploader_upload_failed_content_single">Could not upload %1$s</string>
     <string name="uploader_upload_failed_credentials_error">Upload failed, you need to log in again</string>
+    <string name="uploader_upload_failed_ssl_certificate_not_trusted">Server certificate is not trusted</string>
+    <string name="uploads_view_upload_status_failed_ssl_certificate_not_trusted">Server certificate is not trusted</string>
     <string name="uploads_view_title">Uploads</string>
     <string name="uploads_view_group_current_uploads">Current</string>
     <string name="uploads_view_group_failed_uploads">Failed / Pending restart</string>
@@ -300,7 +302,7 @@
     <string name="oauth_check_onoff">Log in with OAuth 2.0</string> 
     <string name="oauth_login_connection">Connecting to OAuth 2.0 server&#8230;</string>
         
-    <string name="ssl_validator_header">The identity of the site could not be verified</string>
+    <string name="ssl_validator_header">The identity of the server could not be verified</string>
     <string name="ssl_validator_reason_cert_not_trusted">- The server certificate is not trusted</string>
     <string name="ssl_validator_reason_cert_expired">- The server certificate expired</string>
     <string name="ssl_validator_reason_cert_not_yet_valid">- The server certificate valid dates are in the future</string>