Просмотр исходного кода

Merge pull request #1624 from owncloud/stable

Release 2.0.0
David A. Velasco 9 лет назад
Родитель
Сommit
858d828605
26 измененных файлов с 289 добавлено и 279 удалено
  1. 3 3
      AndroidManifest.xml
  2. 10 3
      CHANGELOG.md
  3. 2 2
      oc_jb_workaround/AndroidManifest.xml
  4. 1 1
      owncloud-android-library
  5. 4 4
      res/layout/list_fragment.xml
  6. 2 2
      res/layout/search_users_groups_layout.xml
  7. 4 1
      res/values/strings.xml
  8. 40 43
      src/com/owncloud/android/MainApp.java
  9. 2 1
      src/com/owncloud/android/authentication/AccountUtils.java
  10. 3 1
      src/com/owncloud/android/authentication/AuthenticatorActivity.java
  11. 2 1
      src/com/owncloud/android/datamodel/OCFile.java
  12. 0 36
      src/com/owncloud/android/files/FileOperationsHelper.java
  13. 16 44
      src/com/owncloud/android/operations/CreateShareViaLinkOperation.java
  14. 7 8
      src/com/owncloud/android/operations/UploadFileOperation.java
  15. 1 1
      src/com/owncloud/android/providers/FileContentProvider.java
  16. 1 4
      src/com/owncloud/android/services/OperationsService.java
  17. 37 24
      src/com/owncloud/android/ui/activity/CopyToClipboardActivity.java
  18. 1 44
      src/com/owncloud/android/ui/activity/FileActivity.java
  19. 20 17
      src/com/owncloud/android/ui/activity/FileDisplayActivity.java
  20. 14 1
      src/com/owncloud/android/ui/activity/PassCodeActivity.java
  21. 1 5
      src/com/owncloud/android/ui/activity/Preferences.java
  22. 71 14
      src/com/owncloud/android/ui/activity/ShareActivity.java
  23. 7 2
      src/com/owncloud/android/ui/dialog/SamlWebViewDialog.java
  24. 3 2
      src/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.java
  25. 17 7
      src/com/owncloud/android/ui/fragment/EditShareFragment.java
  26. 20 8
      src/com/owncloud/android/ui/fragment/ShareFileFragment.java

+ 3 - 3
AndroidManifest.xml

@@ -19,8 +19,8 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.owncloud.android"
-    android:versionCode="10900100"
-    android:versionName="1.9.1" >
+    android:versionCode="20000000"
+    android:versionName="2.0.0" >
 
     <uses-sdk
         android:minSdkVersion="14"
@@ -72,7 +72,7 @@
         </activity>
         <activity android:name=".ui.activity.UploadFilesActivity" />
         <activity android:name=".ui.activity.Uploader"
-                  android:launchMode="singleInstance"
+                  android:taskAffinity=""
                   android:excludeFromRecents="true">
             <intent-filter>
                 <action android:name="android.intent.action.SEND" />

+ 10 - 3
CHANGELOG.md

@@ -1,3 +1,13 @@
+## 2.0.0 (April 2016)
+- Uploads view: track the progress of your uploads and handle failures
+- Federated sharing: share files with users in other ownCloud servers
+- Improvements on the UI following material design lines
+- Set a shared-by-link folder as editable
+- Wifi-only for instant uploads stop on Wifi loss
+- Be warned of server certificate changed in any action
+- Improvements when other apps send files to ownCloud
+- Bug fixing
+
 ## 1.9.1 (February 2016)
 - Set and edit permissions on internal shared data
 - Instant uploads: avoid file duplications, set policy in app settings
@@ -79,6 +89,3 @@
 - Settings view updated
 - Improved subjects in e-mails
 - Bugs fixed
-
-
-

+ 2 - 2
oc_jb_workaround/AndroidManifest.xml

@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.owncloud.android.workaround.accounts"
-    android:versionCode="0100027"
-    android:versionName="1.0.27" >
+    android:versionCode="0100028"
+    android:versionName="1.0.28" >
 
     <uses-sdk
         android:minSdkVersion="16"

+ 1 - 1
owncloud-android-library

@@ -1 +1 @@
-Subproject commit 39e3ddaa07b0943b034b34a84a33b4dc4c7475d0
+Subproject commit 19e30a6236ca75a6e223ba6b7b0bd901b6615e12

+ 4 - 4
res/layout/list_fragment.xml

@@ -113,21 +113,21 @@
             fab:fab_title=""/>
 
         <com.getbase.floatingactionbutton.FloatingActionButton
-            android:id="@+id/fab_mkdir"
+            android:id="@+id/fab_upload_from_app"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             fab:fab_size="mini"
-            fab:fab_icon="@drawable/ic_action_create_dir"
+            fab:fab_icon="@drawable/ic_import"
             fab:fab_colorNormal="@color/primary_button_background_color"
             fab:fab_colorPressed="@color/owncloud_blue"
             fab:fab_title=""/>
 
         <com.getbase.floatingactionbutton.FloatingActionButton
-            android:id="@+id/fab_upload_from_app"
+            android:id="@+id/fab_mkdir"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             fab:fab_size="mini"
-            fab:fab_icon="@drawable/ic_import"
+            fab:fab_icon="@drawable/ic_action_create_dir"
             fab:fab_colorNormal="@color/primary_button_background_color"
             fab:fab_colorPressed="@color/owncloud_blue"
             fab:fab_title=""/>

+ 2 - 2
res/layout/search_users_groups_layout.xml

@@ -23,11 +23,11 @@
     android:id="@+id/search_layout"
     android:minWidth="200dp"
     android:layout_marginRight="@dimen/standard_margin"
-    android:layout_marginBottom="20dp">
+    android:layout_marginBottom="@dimen/standard_half_margin">
 
     <TextView
         android:id="@+id/searchUserGroupsTitle"
-        android:layout_width="fill_parent"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:text="@string/share_with_title"
         android:textAppearance="@style/TextAppearance.AppCompat.Title"

+ 4 - 1
res/values/strings.xml

@@ -324,6 +324,9 @@
 
     <string name="copy_link">Copy link</string>
     <string name="clipboard_text_copied">Copied to clipboard</string>
+    <string name="clipboard_no_text_to_copy">No text received to copy to clipboard</string>
+    <string name="clipboard_uxexpected_error">Unexpected error while copying to clipboard</string>
+    <string name="clipboard_label">Text copied from %1$s</string>
 
     <string name="error_cant_bind_to_operations_service">Critical error: cannot perform operations</string>
 
@@ -446,7 +449,7 @@
     <string name="action_switch_list_view">List view</string>
 
     <string name="manage_space_title">Manage space</string>
-    <string name="manage_space_description">Settings, database and server certificates from %1$s\'s data will be deleted permanentlty. \n\nDownloaded files will be kept untouched.\n\nThis process can take some time.</string>
+    <string name="manage_space_description">Settings, database and server certificates from %1$s\'s data will be deleted permanently. \n\nDownloaded files will be kept untouched.\n\nThis process can take some time.</string>
     <string name="manage_space_clear_data">Clear data</string>
     <string name="manage_space_error">Some files could not be deleted.</string>
 

+ 40 - 43
src/com/owncloud/android/MainApp.java

@@ -25,7 +25,6 @@ import android.app.Application;
 import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.os.Build;
 import android.os.Bundle;
 
 import com.owncloud.android.authentication.PassCodeManager;
@@ -87,48 +86,46 @@ public class MainApp extends Application {
         }
 
         // register global protection with pass code
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
-            this.registerActivityLifecycleCallbacks( new ActivityLifecycleCallbacks() {
-
-                @Override
-                public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
-                    Log_OC.d(activity.getClass().getSimpleName(),  "onCreate(Bundle) starting" );
-                    PassCodeManager.getPassCodeManager().onActivityCreated(activity);
-                }
-
-                @Override
-                public void onActivityStarted(Activity activity) {
-                    Log_OC.d(activity.getClass().getSimpleName(),  "onStart() starting" );
-                    PassCodeManager.getPassCodeManager().onActivityStarted(activity);
-                }
-
-                @Override
-                public void onActivityResumed(Activity activity) {
-                    Log_OC.d(activity.getClass().getSimpleName(), "onResume() starting" );
-                }
-
-                @Override
-                public void onActivityPaused(Activity activity) {
-                    Log_OC.d(activity.getClass().getSimpleName(), "onPause() ending");
-                }
-
-                @Override
-                public void onActivityStopped(Activity activity) {
-                    Log_OC.d(activity.getClass().getSimpleName(), "onStop() ending" );
-                    PassCodeManager.getPassCodeManager().onActivityStopped(activity);
-                }
-
-                @Override
-                public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
-                    Log_OC.d(activity.getClass().getSimpleName(), "onSaveInstanceState(Bundle) starting" );
-                }
-
-                @Override
-                public void onActivityDestroyed(Activity activity) {
-                    Log_OC.d(activity.getClass().getSimpleName(), "onDestroy() ending" );
-                }
-            });
-        }
+        registerActivityLifecycleCallbacks( new ActivityLifecycleCallbacks() {
+
+            @Override
+            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
+                Log_OC.d(activity.getClass().getSimpleName(),  "onCreate(Bundle) starting" );
+                PassCodeManager.getPassCodeManager().onActivityCreated(activity);
+            }
+
+            @Override
+            public void onActivityStarted(Activity activity) {
+                Log_OC.d(activity.getClass().getSimpleName(),  "onStart() starting" );
+                PassCodeManager.getPassCodeManager().onActivityStarted(activity);
+            }
+
+            @Override
+            public void onActivityResumed(Activity activity) {
+                Log_OC.d(activity.getClass().getSimpleName(), "onResume() starting" );
+            }
+
+            @Override
+            public void onActivityPaused(Activity activity) {
+                Log_OC.d(activity.getClass().getSimpleName(), "onPause() ending");
+            }
+
+            @Override
+            public void onActivityStopped(Activity activity) {
+                Log_OC.d(activity.getClass().getSimpleName(), "onStop() ending" );
+                PassCodeManager.getPassCodeManager().onActivityStopped(activity);
+            }
+
+            @Override
+            public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
+                Log_OC.d(activity.getClass().getSimpleName(), "onSaveInstanceState(Bundle) starting" );
+            }
+
+            @Override
+            public void onActivityDestroyed(Activity activity) {
+                Log_OC.d(activity.getClass().getSimpleName(), "onDestroy() ending" );
+            }
+        });
     }
 
     public static Context getAppContext() {

+ 2 - 1
src/com/owncloud/android/authentication/AccountUtils.java

@@ -194,7 +194,8 @@ public class AccountUtils {
                 for (Account account : ocAccounts) {
                     // build new account name
                     serverUrl = accountMgr.getUserData(account, Constants.KEY_OC_BASE_URL);
-                    username = account.name.substring(0, account.name.lastIndexOf('@'));
+                    username = com.owncloud.android.lib.common.accounts.AccountUtils.
+                            getUsernameForAccount(account);
                     newAccountName = com.owncloud.android.lib.common.accounts.AccountUtils.
                             buildAccountName(Uri.parse(serverUrl), username);
 

+ 3 - 1
src/com/owncloud/android/authentication/AuthenticatorActivity.java

@@ -494,7 +494,9 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         boolean isPasswordExposed = false;
         if (savedInstanceState == null) {
             if (mAccount != null) {
-                presetUserName = mAccount.name.substring(0, mAccount.name.lastIndexOf('@'));
+                presetUserName =
+                    com.owncloud.android.lib.common.accounts.AccountUtils.
+                        getUsernameForAccount(mAccount);
             }
             
         } else {

+ 2 - 1
src/com/owncloud/android/datamodel/OCFile.java

@@ -443,7 +443,8 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
      * @return remote path
      */
     public String getParentRemotePath() {
-        return new File(getRemotePath()).getParent();
+        String parentPath = new File(getRemotePath()).getParent();
+        return (parentPath.endsWith("/")) ? parentPath : (parentPath + "/");
     }
 
     /**

+ 0 - 36
src/com/owncloud/android/files/FileOperationsHelper.java

@@ -205,25 +205,6 @@ public class FileOperationsHelper {
         }
     }
 
-    public void shareFileWithLinkToApp(OCFile file, String password, Intent sendIntent) {
-        
-        if (file != null) {
-            mFileActivity.showLoadingDialog(mFileActivity.getApplicationContext().
-                    getString(R.string.wait_a_moment));
-
-            Intent service = new Intent(mFileActivity, OperationsService.class);
-            service.setAction(OperationsService.ACTION_CREATE_SHARE_VIA_LINK);
-            service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
-            service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
-            service.putExtra(OperationsService.EXTRA_SHARE_PASSWORD, password);
-            service.putExtra(OperationsService.EXTRA_SEND_INTENT, sendIntent);
-            mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service);
-            
-        } else {
-            Log_OC.wtf(TAG, "Trying to open a NULL OCFile");
-        }
-    }
-
     /**
      * Helper method to share a file with a known sharee. Starts a request to do it in {@link OperationsService}
      *
@@ -331,23 +312,6 @@ public class FileOperationsHelper {
     }
 
 
-    /**
-     * Starts a dialog that requests a password to the user to protect a share link.
-     *
-     * @param   file            File which public share will be protected by the requested password
-     * @param   createShare     When 'true', the request for password will be followed by the creation of a new
-     *                          public link; when 'false', a public share is assumed to exist, and the password
-     *                          is bound to it.
-     */
-    public void requestPasswordForShareViaLink(OCFile file, boolean createShare) {
-        SharePasswordDialogFragment dialog =
-                SharePasswordDialogFragment.newInstance(file, createShare);
-        dialog.show(
-            mFileActivity.getSupportFragmentManager(),
-            SharePasswordDialogFragment.PASSWORD_FRAGMENT
-        );
-    }
-
     /**
      * Updates a public share on a file to set its password.
      * Starts a request to do it in {@link OperationsService}

+ 16 - 44
src/com/owncloud/android/operations/CreateShareViaLinkOperation.java

@@ -26,10 +26,6 @@ package com.owncloud.android.operations;
  */
 
 
-import android.content.Context;
-import android.content.Intent;
-
-import com.owncloud.android.R;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.lib.common.OwnCloudClient;
 import com.owncloud.android.lib.common.operations.RemoteOperation;
@@ -41,31 +37,26 @@ import com.owncloud.android.lib.resources.shares.OCShare;
 import com.owncloud.android.lib.resources.shares.ShareType;
 import com.owncloud.android.operations.common.SyncOperation;
 
+import java.util.ArrayList;
+
 public class CreateShareViaLinkOperation extends SyncOperation {
 
     private String mPath;
     private String mPassword;
-    private Intent mSendIntent;
-    private String mFileName;
 
     /**
      * Constructor
      * @param path          Full path of the file/folder being shared. Mandatory argument
      * @param password      Password to protect a public link share.
      *                      Only available for public link shares
-     *  @param sendIntent   Optional Intent with the information of an app where the link to the new share (if public)
-     *                      should be posted later.
      */
     public CreateShareViaLinkOperation(
             String path,
-            String password,
-            Intent sendIntent
+            String password
     ) {
 
         mPath = path;
         mPassword = password;
-        mSendIntent = sendIntent;
-        mFileName = null;
     }
 
     @Override
@@ -101,9 +92,19 @@ public class CreateShareViaLinkOperation extends SyncOperation {
         
         if (result.isSuccess()) {
             if (result.getData().size() > 0) {
-                OCShare share = (OCShare) result.getData().get(0);
-                updateData(share);
-            } 
+                Object item = result.getData().get(0);
+                if (item instanceof  OCShare) {
+                    updateData((OCShare) item);
+                } else {
+                    ArrayList<Object> data = result.getData();
+                    result = new RemoteOperationResult(
+                        RemoteOperationResult.ResultCode.SHARE_NOT_FOUND
+                    );
+                    result.setData(data);
+                }
+            } else {
+                result = new RemoteOperationResult(RemoteOperationResult.ResultCode.SHARE_NOT_FOUND);
+            }
         }
         
         return result;
@@ -117,32 +118,6 @@ public class CreateShareViaLinkOperation extends SyncOperation {
         return mPassword;
     }
 
-    public Intent getSendIntent() {
-        return mSendIntent;
-    }
-
-    public Intent getSendIntentWithSubject(Context context) {
-        if (context != null && mSendIntent != null && mSendIntent.getStringExtra(Intent.EXTRA_SUBJECT) != null) {
-            if (getClient() == null || getClient().getCredentials() == null ||
-                    getClient().getCredentials().getUsername() == null) {
-                mSendIntent.putExtra(
-                        Intent.EXTRA_SUBJECT,
-                        context.getString(R.string.subject_shared_with_you, mFileName)
-                );
-            } else {
-                mSendIntent.putExtra(
-                        Intent.EXTRA_SUBJECT,
-                        context.getString(
-                                R.string.subject_user_shared_with_you,
-                                getClient().getCredentials().getUsername(),
-                                mFileName
-                        )
-                );
-            }
-        }
-        return mSendIntent;
-    }
-
     private void updateData(OCShare share) {
         // Update DB with the response
         share.setPath(mPath);
@@ -160,9 +135,6 @@ public class CreateShareViaLinkOperation extends SyncOperation {
             file.setPublicLink(share.getShareLink());
             file.setShareViaLink(true);
             getStorageManager().saveFile(file);
-            if (mSendIntent != null) {
-                mSendIntent.putExtra(Intent.EXTRA_TEXT, share.getShareLink());
-            }
         }
     }
 

+ 7 - 8
src/com/owncloud/android/operations/UploadFileOperation.java

@@ -379,16 +379,15 @@ public class UploadFileOperation extends SyncOperation {
                     mFile.setStoragePath("");
                 } else {
                     mFile.setStoragePath(expectedPath);
-                    File fileToMove;
+
                     if (temporalFile != null) {         // FileUploader.LOCAL_BEHAVIOUR_COPY
-                        fileToMove = temporalFile;
+                        move(temporalFile, expectedFile);
                     } else {                            // FileUploader.LOCAL_BEHAVIOUR_MOVE
-                        fileToMove = originalFile;
+                        move(originalFile, expectedFile);
+                        getStorageManager().deleteFileInMediaScan(originalFile.getAbsolutePath());
                     }
-                    move(fileToMove, expectedFile);
+                    FileDataStorageManager.triggerMediaScan(expectedFile.getAbsolutePath());
                 }
-                FileDataStorageManager.triggerMediaScan(originalFile.getAbsolutePath());
-                FileDataStorageManager.triggerMediaScan(expectedFile.getAbsolutePath());
 
             } else if (result.getHttpCode() == HttpStatus.SC_PRECONDITION_FAILED ) {
                 result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
@@ -766,8 +765,8 @@ public class UploadFileOperation extends SyncOperation {
         }
 
         if (mWasRenamed) {
-            OCFile oldFile = mOldFile;
-            if (oldFile.fileExists()) {
+            OCFile oldFile = getStorageManager().getFileByPath(mOldFile.getRemotePath());
+            if (oldFile != null) {
                 oldFile.setStoragePath(null);
                 getStorageManager().saveFile(oldFile);
                 getStorageManager().saveConflict(oldFile, null);

+ 1 - 1
src/com/owncloud/android/providers/FileContentProvider.java

@@ -897,7 +897,7 @@ public class FileContentProvider extends ContentProvider {
 			for (Account account : accounts) {
                 // build both old and new account name
                 serverUrl = ama.getUserData(account, AccountUtils.Constants.KEY_OC_BASE_URL);
-                username = account.name.substring(0, account.name.lastIndexOf('@'));
+                username = AccountUtils.getUsernameForAccount(account);
                 oldAccountName = AccountUtils.buildAccountNameOld(Uri.parse(serverUrl), username);
                 newAccountName = AccountUtils.buildAccountName(Uri.parse(serverUrl), username);
 

+ 1 - 4
src/com/owncloud/android/services/OperationsService.java

@@ -83,7 +83,6 @@ public class OperationsService extends Service {
     public static final String EXTRA_SERVER_URL = "SERVER_URL";
     public static final String EXTRA_OAUTH2_QUERY_PARAMETERS = "OAUTH2_QUERY_PARAMETERS";
     public static final String EXTRA_REMOTE_PATH = "REMOTE_PATH";
-    public static final String EXTRA_SEND_INTENT = "SEND_INTENT";
     public static final String EXTRA_NEWNAME = "NEWNAME";
     public static final String EXTRA_REMOVE_ONLY_LOCAL = "REMOVE_LOCAL_COPY";
     public static final String EXTRA_CREATE_FULL_PATH = "CREATE_FULL_PATH";
@@ -563,12 +562,10 @@ public class OperationsService extends Service {
                 if (action.equals(ACTION_CREATE_SHARE_VIA_LINK)) {  // Create public share via link
                     String remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH);
                     String password = operationIntent.getStringExtra(EXTRA_SHARE_PASSWORD);
-                    Intent sendIntent = operationIntent.getParcelableExtra(EXTRA_SEND_INTENT);
                     if (remotePath.length() > 0) {
                         operation = new CreateShareViaLinkOperation(
                                 remotePath,
-                                password,
-                                sendIntent
+                                password
                         );
                     }
 

+ 37 - 24
src/com/owncloud/android/ui/activity/CopyToClipboardActivity.java

@@ -21,12 +21,13 @@
 package com.owncloud.android.ui.activity;
 
 import com.owncloud.android.R;
+import com.owncloud.android.lib.common.utils.Log_OC;
 
 import android.app.Activity;
 import android.content.ClipData;
 import android.content.Intent;
 import android.os.Bundle;
-import android.text.ClipboardManager;
+import android.content.ClipboardManager;
 import android.widget.Toast;
 
 /**
@@ -34,33 +35,45 @@ import android.widget.Toast;
  */
 @SuppressWarnings("deprecation")
 public class CopyToClipboardActivity extends Activity {
-    
+
+    private static final String TAG = CopyToClipboardActivity.class.getName();
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        
-        // get the clipboard system service
-        ClipboardManager clipboardManager = (ClipboardManager) this.getSystemService(CLIPBOARD_SERVICE);
-        
-        // get the text to copy into the clipboard 
-        Intent intent = getIntent();
-        CharSequence text = intent.getCharSequenceExtra(Intent.EXTRA_TEXT);
-        
-        // and put the text the clipboard
-        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
-            // API level >= 11 -> modern Clipboard
-            ClipData clip = ClipData.newPlainText("ownCloud was here", text);
-            ((android.content.ClipboardManager)clipboardManager).setPrimaryClip(clip);
-            
-        } else {
-            // API level >= 11 -> legacy Clipboard
-            clipboardManager.setText(text);    
+
+        try {
+
+            // get the clipboard system service
+            ClipboardManager clipboardManager = (ClipboardManager) this.getSystemService(CLIPBOARD_SERVICE);
+
+            // get the text to copy into the clipboard
+            Intent intent = getIntent();
+            CharSequence text = intent.getCharSequenceExtra(Intent.EXTRA_TEXT);
+
+            if (text != null && text.length() > 0) {
+                // minimum API level >= 11 -> only modern Clipboard
+                ClipData clip = ClipData.newPlainText(
+                    getString(R.string.clipboard_label, getString(R.string.app_name)),
+                    text
+                );
+                clipboardManager.setPrimaryClip(clip);
+
+                // API level < 11 -> legacy Clipboard - NOT SUPPORTED ANYMORE
+                // clipboardManager.setText(text);
+
+                // alert the user that the text is in the clipboard and we're done
+                Toast.makeText(this, R.string.clipboard_text_copied, Toast.LENGTH_SHORT).show();
+            } else {
+                Toast.makeText(this, R.string.clipboard_no_text_to_copy, Toast.LENGTH_SHORT).show();
+            }
+
+        } catch (Exception e) {
+            Toast.makeText(this, R.string.clipboard_uxexpected_error, Toast.LENGTH_SHORT).show();
+            Log_OC.e(TAG, "Exception caught while copying to clipboard", e);
         }
-        
-        // alert the user that the text is in the clipboard and we're done
-        Toast.makeText(this, R.string.clipboard_text_copied, Toast.LENGTH_SHORT).show();
-        
+
         finish();
-    }    
+    }
 
 }

+ 1 - 44
src/com/owncloud/android/ui/activity/FileActivity.java

@@ -73,7 +73,6 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
 import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.lib.resources.status.OCCapability;
-import com.owncloud.android.operations.CreateShareViaLinkOperation;
 import com.owncloud.android.operations.CreateShareWithShareeOperation;
 import com.owncloud.android.operations.GetSharesForFileOperation;
 import com.owncloud.android.operations.SynchronizeFileOperation;
@@ -87,7 +86,6 @@ import com.owncloud.android.ui.NavigationDrawerItem;
 import com.owncloud.android.ui.adapter.NavigationDrawerListAdapter;
 import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
 import com.owncloud.android.ui.dialog.LoadingDialog;
-import com.owncloud.android.ui.dialog.SharePasswordDialogFragment;
 import com.owncloud.android.ui.dialog.SslUntrustedCertDialog;
 import com.owncloud.android.utils.ErrorMessageAdapter;
 
@@ -111,7 +109,6 @@ public class FileActivity extends AppCompatActivity
     private static final String DIALOG_WAIT_TAG = "DIALOG_WAIT";
 
     private static final String KEY_WAITING_FOR_OP_ID = "WAITING_FOR_OP_ID";
-    private static final String DIALOG_SHARE_PASSWORD = "DIALOG_SHARE_PASSWORD";
     private static final String KEY_ACTION_BAR_TITLE = "ACTION_BAR_TITLE";
 
     public static final int REQUEST_CODE__UPDATE_CREDENTIALS = 0;
@@ -789,9 +786,6 @@ public class FileActivity extends AppCompatActivity
                 t.show();
             }
 
-        } else if (operation instanceof CreateShareViaLinkOperation) {
-            onCreateShareViaLinkOperationFinish((CreateShareViaLinkOperation) operation, result);
-
         } else if (operation instanceof SynchronizeFileOperation) {
             onSynchronizeFileOperationFinish((SynchronizeFileOperation) operation, result);
 
@@ -887,44 +881,6 @@ public class FileActivity extends AppCompatActivity
         }
     }
 
-    private void onCreateShareViaLinkOperationFinish(CreateShareViaLinkOperation operation,
-                                                     RemoteOperationResult result) {
-        if (result.isSuccess()) {
-            updateFileFromDB();
-
-            Intent sendIntent = operation.getSendIntentWithSubject(this);
-            if (sendIntent != null) {
-                startActivity(sendIntent);
-            }
-
-        } else {
-            // Detect Failure (403) --> needs Password
-            if (result.getCode() == ResultCode.SHARE_FORBIDDEN) {
-                String password = operation.getPassword();
-                if ((password == null || password.length() == 0) &&
-                    getCapabilities().getFilesSharingPublicEnabled().isUnknown())
-                    {
-                    // Was tried without password, but not sure that it's optional. Try with password.
-                    // Try with password before giving up.
-                    // See also ShareFileFragment#OnShareViaLinkListener
-                    SharePasswordDialogFragment dialog =
-                            SharePasswordDialogFragment.newInstance(new OCFile(operation.getPath()), true);
-                    dialog.show(getSupportFragmentManager(), DIALOG_SHARE_PASSWORD);
-                } else {
-                    Toast t = Toast.makeText(this,
-                        ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
-                        Toast.LENGTH_LONG);
-                    t.show();
-                }
-            } else {
-                Toast t = Toast.makeText(this,
-                        ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
-                        Toast.LENGTH_LONG);
-                t.show();
-            }
-        }
-    }
-
     private void onSynchronizeFileOperationFinish(SynchronizeFileOperation operation,
                                                   RemoteOperationResult result) {
         OCFile syncedFile = operation.getLocalFile();
@@ -1044,6 +1000,7 @@ public class FileActivity extends AppCompatActivity
     public void restart(){
         Intent i = new Intent(this, FileDisplayActivity.class);
         i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+        i.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
         startActivity(i);
     }
 

+ 20 - 17
src/com/owncloud/android/ui/activity/FileDisplayActivity.java

@@ -765,23 +765,26 @@ public class FileDisplayActivity extends HookActivity
         OCFile currentDir = getCurrentDir();
         String remotePath = (currentDir != null) ? currentDir.getRemotePath() : OCFile.ROOT_PATH;
 
-        if (filePath.startsWith(UriUtils.URI_CONTENT_SCHEME)) {
-            Cursor cursor = getContentResolver().query(Uri.parse(filePath), null, null, null, null);
-            try {
-                if (cursor != null && cursor.moveToFirst()) {
-                    String displayName = cursor.getString(cursor.getColumnIndex(
-                            OpenableColumns.DISPLAY_NAME));
-                    Log_OC.v(TAG, "Display Name: " + displayName);
-
-                    displayName.replace(File.separatorChar, '_');
-                    displayName.replace(File.pathSeparatorChar, '_');
-                    remotePath += displayName + DisplayUtils.getComposedFileExtension(filePath);
-
-                }
-                // and what happens in case of error?; wrong target name for the upload
-            } finally {
-                cursor.close();
-            }
+        if (selectedImageUri.toString().startsWith(UriUtils.URI_CONTENT_SCHEME)) {
+//            Cursor cursor = getContentResolver().query(Uri.parse(filePath), null, null, null, null);
+//            try {
+//                if (cursor != null && cursor.moveToFirst()) {
+//                    String displayName = cursor.getString(cursor.getColumnIndex(
+//                            OpenableColumns.DISPLAY_NAME));
+//                    Log_OC.v(TAG, "Display Name: " + displayName);
+//
+//                    displayName.replace(File.separatorChar, '_');
+//                    displayName.replace(File.pathSeparatorChar, '_');
+//                    remotePath += displayName + DisplayUtils.getComposedFileExtension(filePath);
+//
+//                }
+//                // and what happens in case of error?; wrong target name for the upload
+//            } finally {
+//                cursor.close();
+//            }
+            // Pending to be fixed
+            Toast.makeText(this, R.string.common_error_unknown, Toast.LENGTH_SHORT).show();
+            return;
 
         } else {
             remotePath += new File(filePath).getName();

+ 14 - 1
src/com/owncloud/android/ui/activity/PassCodeActivity.java

@@ -35,6 +35,7 @@ import android.text.TextWatcher;
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.View.OnClickListener;
+import android.view.inputmethod.InputMethodManager;
 import android.widget.Button;
 import android.widget.EditText;
 import android.widget.TextView;
@@ -298,6 +299,7 @@ public class PassCodeActivity extends AppCompatActivity {
         if (ACTION_CHECK.equals(getIntent().getAction())) {
             if (checkPassCode()) {
                 /// pass code accepted in request, user is allowed to access the app
+                hideSoftKeyboard();
                 finish();
 
             }  else {
@@ -310,7 +312,7 @@ public class PassCodeActivity extends AppCompatActivity {
                 Intent resultIntent = new Intent();
                 resultIntent.putExtra(KEY_CHECK_RESULT, true);
                 setResult(RESULT_OK, resultIntent);
-
+                hideSoftKeyboard();
                 finish();
             } else {
                 showErrorAndRestart(R.string.pass_code_wrong, R.string.pass_code_enter_pass_code,
@@ -334,6 +336,17 @@ public class PassCodeActivity extends AppCompatActivity {
         }
     }
 
+    private void hideSoftKeyboard() {
+        View focusedView = getCurrentFocus();
+        if (focusedView != null) {
+            InputMethodManager inputMethodManager =
+                (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
+            inputMethodManager.hideSoftInputFromWindow(
+                focusedView.getWindowToken(),
+                0
+            );
+        }
+    }
 
     private void showErrorAndRestart(int errorMessage, int headerMessage,
                                      int explanationVisibility) {

+ 1 - 5
src/com/owncloud/android/ui/activity/Preferences.java

@@ -300,11 +300,7 @@ public class Preferences extends PreferenceActivity
                         
                         String appName = getString(R.string.app_name);
                         String downloadUrl = getString(R.string.url_app_download);
-                        Account currentAccount = AccountUtils.
-                                getCurrentOwnCloudAccount(Preferences.this);
-                        String username = currentAccount.name.substring(0,
-                                currentAccount.name.lastIndexOf('@'));
-                        
+
                         String recommendSubject =
                                 String.format(getString(R.string.recommend_subject),
                                 appName);

+ 71 - 14
src/com/owncloud/android/ui/activity/ShareActivity.java

@@ -28,9 +28,11 @@ import android.os.Bundle;
 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;
+import com.owncloud.android.lib.common.accounts.AccountUtils;
 import com.owncloud.android.lib.common.operations.RemoteOperation;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult;
 import com.owncloud.android.lib.common.utils.Log_OC;
@@ -42,10 +44,12 @@ import com.owncloud.android.operations.UnshareOperation;
 import com.owncloud.android.operations.UpdateSharePermissionsOperation;
 import com.owncloud.android.providers.UsersAndGroupsSearchProvider;
 import com.owncloud.android.ui.dialog.ShareLinkToDialog;
+import com.owncloud.android.ui.dialog.SharePasswordDialogFragment;
 import com.owncloud.android.ui.fragment.EditShareFragment;
 import com.owncloud.android.ui.fragment.SearchShareesFragment;
 import com.owncloud.android.ui.fragment.ShareFileFragment;
 import com.owncloud.android.ui.fragment.ShareFragmentListener;
+import com.owncloud.android.utils.ErrorMessageAdapter;
 import com.owncloud.android.utils.GetShareWithUsersAsyncTask;
 
 
@@ -62,10 +66,10 @@ public class ShareActivity extends FileActivity
     private static final String TAG_SEARCH_FRAGMENT = "SEARCH_USER_AND_GROUPS_FRAGMENT";
     private static final String TAG_EDIT_SHARE_FRAGMENT = "EDIT_SHARE_FRAGMENT";
 
-    /**
-     * Tag for dialog
-     */
+    /// Tags for dialog fragments
     private static final String FTAG_CHOOSER_DIALOG = "CHOOSER_DIALOG";
+    private static final String FTAG_SHARE_PASSWORD_DIALOG = "SHARE_PASSWORD_DIALOG";
+
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -207,17 +211,8 @@ public class ShareActivity extends FileActivity
             refreshSharesFromStorageManager();
         }
 
-        if (operation instanceof CreateShareViaLinkOperation && result.isSuccess()) {
-            // Send link to the app
-            String link = ((OCShare) (result.getData().get(0))).getShareLink();
-            Log_OC.d(TAG, "Share link = " + link);
-
-            Intent intentToShareLink = new Intent(Intent.ACTION_SEND);
-            intentToShareLink.putExtra(Intent.EXTRA_TEXT, link);
-            intentToShareLink.setType("text/plain");
-            String[] packagesToExclude = new String[]{getPackageName()};
-            DialogFragment chooserDialog = ShareLinkToDialog.newInstance(intentToShareLink, packagesToExclude);
-            chooserDialog.show(getSupportFragmentManager(), FTAG_CHOOSER_DIALOG);
+        if (operation instanceof CreateShareViaLinkOperation) {
+            onCreateShareViaLinkOperationFinish((CreateShareViaLinkOperation) operation, result);
         }
 
         if (operation instanceof UnshareOperation && result.isSuccess() && getEditShareFragment() != null) {
@@ -286,4 +281,66 @@ public class ShareActivity extends FileActivity
         return (EditShareFragment) getSupportFragmentManager().findFragmentByTag(TAG_EDIT_SHARE_FRAGMENT);
     }
 
+
+    private void onCreateShareViaLinkOperationFinish(CreateShareViaLinkOperation operation,
+                                                     RemoteOperationResult result) {
+        if (result.isSuccess()) {
+            updateFileFromDB();
+
+            // Create dialog to allow the user choose an app to send the link
+            Intent intentToShareLink = new Intent(Intent.ACTION_SEND);
+            String link = ((OCShare) (result.getData().get(0))).getShareLink();
+            intentToShareLink.putExtra(Intent.EXTRA_TEXT, link);
+            intentToShareLink.setType("text/plain");
+            String username = AccountUtils.getUsernameForAccount(getAccount());
+            if (username != null) {
+                intentToShareLink.putExtra(
+                    Intent.EXTRA_SUBJECT,
+                    getString(
+                        R.string.subject_user_shared_with_you,
+                        username,
+                        getFile().getFileName()
+                    )
+                );
+            } else {
+                intentToShareLink.putExtra(
+                    Intent.EXTRA_SUBJECT,
+                    getString(
+                        R.string.subject_shared_with_you,
+                        getFile().getFileName()
+                    )
+                );
+            }
+
+            String[] packagesToExclude = new String[]{getPackageName()};
+            DialogFragment chooserDialog = ShareLinkToDialog.newInstance(intentToShareLink, packagesToExclude);
+            chooserDialog.show(getSupportFragmentManager(), FTAG_CHOOSER_DIALOG);
+
+        } else {
+            // Detect Failure (403) --> maybe needs password
+            String password = operation.getPassword();
+            if (result.getCode() == RemoteOperationResult.ResultCode.SHARE_FORBIDDEN    &&
+                    (password == null || password.length() == 0)                        &&
+                    getCapabilities().getFilesSharingPublicEnabled().isUnknown()) {
+                    // Was tried without password, but not sure that it's optional.
+
+                // Try with password before giving up; see also ShareFileFragment#OnShareViaLinkListener
+                ShareFileFragment shareFileFragment = getShareFileFragment();
+                if (shareFileFragment != null
+                    && shareFileFragment.isAdded()) {   // only if added to the view hierarchy!!
+
+                    shareFileFragment.requestPasswordForShareViaLink(true);
+                }
+
+            } else {
+                Toast t = Toast.makeText(this,
+                    ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
+                    Toast.LENGTH_LONG);
+                t.show();
+            }
+        }
+
+    }
+
+
 }

+ 7 - 2
src/com/owncloud/android/ui/dialog/SamlWebViewDialog.java

@@ -148,11 +148,16 @@ public class SamlWebViewDialog extends DialogFragment {
             
             WebSettings webSettings = mSsoWebView.getSettings();
             webSettings.setJavaScriptEnabled(true);
-            webSettings.setBuiltInZoomControls(false);
-            webSettings.setLoadWithOverviewMode(false);
             webSettings.setSavePassword(false);
             webSettings.setUserAgentString(MainApp.getUserAgent());
             webSettings.setSaveFormData(false);
+            // next two settings grant that non-responsive webs are zoomed out when loaded
+            webSettings.setUseWideViewPort(true);
+            webSettings.setLoadWithOverviewMode(true);
+            // next three settings allow the user use pinch gesture to zoom in / out
+            webSettings.setSupportZoom(true);
+            webSettings.setBuiltInZoomControls(true);
+            webSettings.setDisplayZoomControls(false);
             
             CookieManager cookieManager = CookieManager.getInstance();
             cookieManager.setAcceptCookie(true);

+ 3 - 2
src/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.java

@@ -55,8 +55,9 @@ public class SharePasswordDialogFragment extends DialogFragment
      * Public factory method to create new SharePasswordDialogFragment instances.
      *
      * @param   file            OCFile bound to the public share that which password will be set or updated
-     * @param   createShare     When 'true', the public share will be created; when 'false', will be assumed
-     *                          that the public share already exists, and its state will be directly updated.
+     * @param   createShare     When 'true', the request for password will be followed by the creation of a new
+     *                          public link; when 'false', a public share is assumed to exist, and the password
+     *                          is bound to it.
      * @return                  Dialog ready to show.
      */
     public static SharePasswordDialogFragment newInstance(OCFile file, boolean createShare) {

+ 17 - 7
src/com/owncloud/android/ui/fragment/EditShareFragment.java

@@ -256,16 +256,25 @@ public class EditShareFragment extends Fragment {
                     boolean isFederated = ShareType.FEDERATED.equals(mShare.getShareType());
                     if (mFile.isFolder()) {
                         if (isChecked) {
-                            for (int i = 0; i < sSubordinateCheckBoxIds.length; i++) {
-                                //noinspection ConstantConditions, prevented in the method beginning
-                                subordinate = (CompoundButton) getView().findViewById(sSubordinateCheckBoxIds[i]);
-                                if (!isFederated) {
+                            if (!isFederated) {
+                                /// not federated shares -> enable all the subpermisions
+                                for (int i = 0; i < sSubordinateCheckBoxIds.length; i++) {
+                                    //noinspection ConstantConditions, prevented in the method beginning
+                                    subordinate = (CompoundButton) getView().findViewById(sSubordinateCheckBoxIds[i]);
                                     subordinate.setVisibility(View.VISIBLE);
-                                }
-                                if (!subordinate.isChecked() &&
+                                    if (!subordinate.isChecked() &&
                                         !mFile.isSharedWithMe()) {          // see (1)
+                                        toggleDisablingListener(subordinate);
+                                    }
+                                }
+                            } else {
+                                /// federated share -> enable delete subpermission, as server side; TODO why?
+                                //noinspection ConstantConditions, prevented in the method beginning
+                                subordinate = (CompoundButton) getView().findViewById(R.id.canEditDeleteCheckBox);
+                                if (!subordinate.isChecked()) {
                                     toggleDisablingListener(subordinate);
                                 }
+
                             }
                         } else {
                             for (int i = 0; i < sSubordinateCheckBoxIds.length; i++) {
@@ -279,7 +288,8 @@ public class EditShareFragment extends Fragment {
                         }
                     }
 
-                    if(!(mFile.isFolder() && isChecked && mFile.isSharedWithMe())) {    // see (1)
+                    if(!(mFile.isFolder() && isChecked && mFile.isSharedWithMe())       // see (1)
+                        || isFederated ) {
                         updatePermissionsToShare();
                     }
 

+ 20 - 8
src/com/owncloud/android/ui/fragment/ShareFileFragment.java

@@ -51,6 +51,7 @@ import com.owncloud.android.lib.resources.status.OCCapability;
 import com.owncloud.android.ui.activity.FileActivity;
 import com.owncloud.android.ui.adapter.ShareUserListAdapter;
 import com.owncloud.android.ui.dialog.ExpirationDatePickerDialogFragment;
+import com.owncloud.android.ui.dialog.SharePasswordDialogFragment;
 import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.MimetypeIconUtil;
 
@@ -279,16 +280,16 @@ public class ShareFileFragment extends Fragment
                 if (mCapabilities != null &&
                         mCapabilities.getFilesSharingPublicPasswordEnforced().isTrue()) {
                     // password enforced by server, request to the user before trying to create
-                    ((FileActivity) getActivity()).getFileOperationsHelper().
-                            requestPasswordForShareViaLink(mFile, true);
+                    requestPasswordForShareViaLink(true);
 
                 } else {
                     // create without password if not enforced by server or we don't know if enforced;
                     ((FileActivity) getActivity()).getFileOperationsHelper().
                             shareFileViaLink(mFile, null);
 
-                    // FileActivtiy#onCreateShareViaLinkOperationFinish still handles the guess of enforcement
-                    // for server in versions previous to OwnCloudVersion#MINIMUM_VERSION_CAPABILITIES_API
+                    // ShareActivity#onCreateShareViaLinkOperationFinish will take care if password
+                    // is enforced by the server but app doesn't know, or if server version is
+                    // older than OwnCloudVersion#MINIMUM_VERSION_CAPABILITIES_API
                 }
 
             } else {
@@ -429,8 +430,7 @@ public class ShareFileFragment extends Fragment
                 return;
             }
             if (isChecked) {
-                ((FileActivity) getActivity()).getFileOperationsHelper().
-                        requestPasswordForShareViaLink(mFile, false);
+                requestPasswordForShareViaLink(false);
             } else {
                 ((FileActivity) getActivity()).getFileOperationsHelper().
                         setPasswordToShareViaLink(mFile, "");   // "" clears
@@ -451,8 +451,7 @@ public class ShareFileFragment extends Fragment
         @Override
         public void onClick(View passwordView) {
             if (mPublicShare != null && mPublicShare.isPasswordProtected()) {
-                ((FileActivity) getActivity()).getFileOperationsHelper().
-                        requestPasswordForShareViaLink(mFile, false);
+                requestPasswordForShareViaLink(false);
             }
         }
     }
@@ -844,4 +843,17 @@ public class ShareFileFragment extends Fragment
         listView.requestLayout();
     }
 
+
+    /**
+     * Starts a dialog that requests a password to the user to protect a share link.
+     *
+     * @param   createShare     When 'true', the request for password will be followed by the creation of a new
+     *                          public link; when 'false', a public share is assumed to exist, and the password
+     *                          is bound to it.
+     */
+    public void requestPasswordForShareViaLink(boolean createShare) {
+        SharePasswordDialogFragment dialog = SharePasswordDialogFragment.newInstance(mFile, createShare);
+        dialog.show(getFragmentManager(),SharePasswordDialogFragment.PASSWORD_FRAGMENT);
+    }
+
 }