浏览代码

Merge pull request #1168 from owncloud/avoidDuplicateFiles

Avoid duplicate files
Maria Asensio 9 年之前
父节点
当前提交
896e4859c0

+ 49 - 1
res/layout/upload_files_layout.xml

@@ -30,11 +30,59 @@
         android:layout_weight="1"
         class="com.owncloud.android.ui.fragment.LocalFileListFragment" />
 
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <ImageView
+            android:layout_width="match_parent"
+            android:layout_height="1dp"
+            android:src="@drawable/uploader_list_separator"/>
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="8dp"
+        android:paddingLeft="16dp"
+        android:paddingRight="16dp">
+
+        <RadioGroup xmlns:android="http://schemas.android.com/apk/res/android"
+            android:id="@+id/drawer_radio_group"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:orientation="horizontal">
+
+            <RadioButton
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/upload_copy_files"
+                android:id="@+id/upload_radio_copy"
+                android:paddingRight="8dp"
+                android:checked="false" />
+
+            <RadioButton
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/upload_move_files"
+                android:id="@+id/upload_radio_move"
+                android:paddingRight="8dp"
+                android:checked="false" />
+        </RadioGroup>
+    </LinearLayout>
+
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:gravity="center"
-        android:orientation="horizontal" >
+        android:orientation="horizontal"
+        android:paddingLeft="16dp"
+        android:paddingRight="16dp"
+        android:paddingBottom="16dp">
 
         <android.support.v7.widget.AppCompatButton
             android:id="@+id/upload_files_btn_cancel"

+ 9 - 0
res/values/attrs.xml

@@ -11,4 +11,13 @@
 
 	</declare-styleable>
 
+	<string-array name="pref_behaviour_entries">
+		<item>@string/pref_behaviour_entries_keep_file</item>
+		<item>@string/pref_behaviour_entries_move</item>
+	</string-array>
+
+	<string-array name="pref_behaviour_entryValues">
+		<item>NOTHING</item>
+		<item>MOVE</item>
+	</string-array>
 </resources>

+ 7 - 0
res/values/strings.xml

@@ -369,6 +369,13 @@
     <string name="file_list__footer__files">%1$d files</string>
     <string name="file_list__footer__files_and_folder">%1$d files, 1 folder</string>
     <string name="file_list__footer__files_and_folders">%1$d files, %2$d folders</string>
+    <string name="prefs_instant_behaviour_dialogTitle">Original file will be...</string>
+    <string name="prefs_instant_behaviour_title">Original file will be...</string>
+    <string name="upload_copy_files">Copy file</string>
+    <string name="upload_move_files">Move file</string>
+
+    <string name="pref_behaviour_entries_keep_file">kept in original folder</string>
+    <string name="pref_behaviour_entries_move">moved to app folder</string>
 
     <string name="share_dialog_title">Sharing</string>
     <string name="share_with_user_section_title">Share with users and groups</string>

+ 9 - 1
res/xml/preferences.xml

@@ -26,7 +26,7 @@
 	</PreferenceCategory>
 
     <PreferenceCategory android:title="@string/prefs_category_instant_uploading" android:key="instant_uploading_category">
-         <com.owncloud.android.ui.CheckBoxPreferenceWithLongTitle android:key="instant_uploading"
+		<com.owncloud.android.ui.CheckBoxPreferenceWithLongTitle android:key="instant_uploading"
 	                        android:title="@string/prefs_instant_upload"
 	                        android:summary="@string/prefs_instant_upload_summary"/>
          <com.owncloud.android.ui.PreferenceWithLongSummary
@@ -44,6 +44,14 @@
 	    <com.owncloud.android.ui.CheckBoxPreferenceWithLongTitle
 	        				android:title="@string/instant_video_upload_on_wifi"
 	        				android:key="instant_video_upload_on_wifi"/>
+		<com.owncloud.android.ui.dialog.OwnCloudListPreference android:key="prefs_instant_behaviour"
+			android:dialogTitle="@string/prefs_instant_behaviour_dialogTitle"
+			android:title="@string/prefs_instant_behaviour_title"
+			android:entries="@array/pref_behaviour_entries"
+			android:entryValues="@array/pref_behaviour_entryValues"
+			android:defaultValue="NOTHING"
+			android:summary="%s"
+			/>
 	    <!-- DISABLED FOR RELEASE UNTIL FIXED
 	    CheckBoxPreference android:key="log_to_file"
 	                        android:title="@string/prefs_log_title"

+ 6 - 4
src/com/owncloud/android/datamodel/FileDataStorageManager.java

@@ -1605,10 +1605,12 @@ public class FileDataStorageManager {
         return shares;
     }
 
-    public void triggerMediaScan(String path) {
-        Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
-        intent.setData(Uri.fromFile(new File(path)));
-        MainApp.getAppContext().sendBroadcast(intent);
+    public static void triggerMediaScan(String path) {
+        if (path != null) {
+            Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
+            intent.setData(Uri.fromFile(new File(path)));
+            MainApp.getAppContext().sendBroadcast(intent);
+        }
     }
 
     public void deleteFileInMediaScan(String path) {

+ 27 - 0
src/com/owncloud/android/files/InstantUploadBroadcastReceiver.java

@@ -34,6 +34,7 @@ import android.accounts.Account;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.SharedPreferences;
 import android.database.Cursor;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo.State;
@@ -122,9 +123,27 @@ public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
         i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
         i.putExtra(FileUploader.KEY_MIME_TYPE, mime_type);
         i.putExtra(FileUploader.KEY_INSTANT_UPLOAD, true);
+
+        // instant upload behaviour
+        i = addInstantUploadBehaviour(i, context);
+
         context.startService(i);
     }
 
+    private Intent addInstantUploadBehaviour(Intent i, Context context){
+        SharedPreferences appPreferences = PreferenceManager.getDefaultSharedPreferences(context);
+        String behaviour = appPreferences.getString("prefs_instant_behaviour", "NOTHING");
+
+        if (behaviour.equalsIgnoreCase("NOTHING")) {
+            Log_OC.d(TAG, "upload file and do nothing");
+            i.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, FileUploader.LOCAL_BEHAVIOUR_FORGET);
+        } else if (behaviour.equalsIgnoreCase("MOVE")) {
+            i.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, FileUploader.LOCAL_BEHAVIOUR_MOVE);
+            Log_OC.d(TAG, "upload file and move file to oc folder");
+        }
+        return i;
+    }
+
     private void handleNewVideoAction(Context context, Intent intent) {
         Cursor c = null;
         String file_path = null;
@@ -167,6 +186,10 @@ public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
         i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
         i.putExtra(FileUploader.KEY_MIME_TYPE, mime_type);
         i.putExtra(FileUploader.KEY_INSTANT_UPLOAD, true);
+
+        // instant upload behaviour
+        i = addInstantUploadBehaviour(i, context);
+
         context.startService(i);
 
     }
@@ -207,6 +230,10 @@ public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
                         i.putExtra(FileUploader.KEY_REMOTE_FILE, FileStorageUtils.getInstantUploadFilePath(context, f.getName()));
                         i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
                         i.putExtra(FileUploader.KEY_INSTANT_UPLOAD, true);
+
+                        // instant upload behaviour
+                        i = addInstantUploadBehaviour(i, context);
+
                         context.startService(i);
 
                     } else {

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

@@ -244,7 +244,7 @@ public class FileUploader extends Service
 
         boolean forceOverwrite = intent.getBooleanExtra(KEY_FORCE_OVERWRITE, false);
         boolean isInstant = intent.getBooleanExtra(KEY_INSTANT_UPLOAD, false);
-        int localAction = intent.getIntExtra(KEY_LOCAL_BEHAVIOUR, LOCAL_BEHAVIOUR_COPY);
+        int localAction = intent.getIntExtra(KEY_LOCAL_BEHAVIOUR, LOCAL_BEHAVIOUR_FORGET);
 
         if (intent.hasExtra(KEY_FILE) && files == null) {
             Log_OC.e(TAG, "Incorrect array for OCFiles provided in upload intent");

+ 36 - 14
src/com/owncloud/android/operations/UploadFileOperation.java

@@ -26,6 +26,7 @@ import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.nio.channels.FileChannel;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Set;
@@ -39,6 +40,7 @@ import android.content.Context;
 import android.net.Uri;
 
 import com.owncloud.android.MainApp;
+import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.files.services.FileUploader;
 import com.owncloud.android.lib.common.OwnCloudClient;
@@ -330,8 +332,7 @@ public class UploadFileOperation extends RemoteOperation {
             // location in the ownCloud local folder
             if (result.isSuccess()) {
                 if (mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_FORGET) {
-                    mFile.setStoragePath(null);
-
+                    mFile.setStoragePath("");
                 } else {
                     mFile.setStoragePath(expectedPath);
                     File fileToMove = null;
@@ -345,21 +346,42 @@ public class UploadFileOperation extends RemoteOperation {
                     if (!expectedFile.equals(fileToMove)) {
                         File expectedFolder = expectedFile.getParentFile();
                         expectedFolder.mkdirs();
-                        if (!expectedFolder.isDirectory() || !fileToMove.renameTo(expectedFile)) {
-                            mFile.setStoragePath(null); // forget the local file
-                            // by now, treat this as a success; the file was
-                            // uploaded; the user won't like that the local file
-                            // is not linked, but this should be a very rare
-                            // fail;
-                            // the best option could be show a warning message
-                            // (but not a fail)
-                            // result = new
-                            // RemoteOperationResult(ResultCode.LOCAL_STORAGE_NOT_MOVED);
-                            // return result;
+
+                        if (expectedFolder.isDirectory()){
+                            if (!fileToMove.renameTo(expectedFile)){
+                                // try to copy and then delete
+                                expectedFile.createNewFile();
+                                FileChannel inChannel = new FileInputStream(fileToMove).getChannel();
+                                FileChannel outChannel = new FileOutputStream(expectedFile).getChannel();
+
+                                try {
+                                    inChannel.transferTo(0, inChannel.size(), outChannel);
+                                    fileToMove.delete();
+                                } catch (Exception e){
+                                    mFile.setStoragePath(null); // forget the local file
+                                    // by now, treat this as a success; the file was
+                                    // uploaded; the user won't like that the local file
+                                    // is not linked, but this should be a very rare
+                                    // fail;
+                                    // the best option could be show a warning message
+                                    // (but not a fail)
+                                    // result = new
+                                    // RemoteOperationResult(ResultCode.LOCAL_STORAGE_NOT_MOVED);
+                                    // return result;
+                                }
+                                finally {
+                                    if (inChannel != null) inChannel.close();
+                                    if (outChannel != null) outChannel.close();
+                                }
+                            }
+
+                        } else {
+                            mFile.setStoragePath(null);
                         }
                     }
                 }
-
+                FileDataStorageManager.triggerMediaScan(originalFile.getAbsolutePath());
+                FileDataStorageManager.triggerMediaScan(expectedFile.getAbsolutePath());
             } else if (result.getHttpCode() == HttpStatus.SC_PRECONDITION_FAILED ) {
                 result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
             }

+ 22 - 2
src/com/owncloud/android/ui/activity/Preferences.java

@@ -106,6 +106,7 @@ public class Preferences extends PreferenceActivity
     private String mUploadPath;
     private PreferenceCategory mPrefInstantUploadCategory;
     private Preference mPrefInstantUpload;
+    private Preference mPrefInstantUploadBehaviour;
     private Preference mPrefInstantUploadPath;
     private Preference mPrefInstantUploadPathWiFi;
     private Preference mPrefInstantVideoUpload;
@@ -393,6 +394,9 @@ public class Preferences extends PreferenceActivity
             @Override
             public boolean onPreferenceChange(Preference preference, Object newValue) {
                 toggleInstantPictureOptions((Boolean) newValue);
+                toggleInstantUploadBehaviour(
+                        ((CheckBoxPreference)mPrefInstantVideoUpload).isChecked(),
+                        (Boolean) newValue);
                 return true;
             }
         });
@@ -420,14 +424,22 @@ public class Preferences extends PreferenceActivity
         toggleInstantVideoOptions(((CheckBoxPreference) mPrefInstantVideoUpload).isChecked());
         
         mPrefInstantVideoUpload.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
-            
+
             @Override
             public boolean onPreferenceChange(Preference preference, Object newValue) {
                 toggleInstantVideoOptions((Boolean) newValue);
+                toggleInstantUploadBehaviour(
+                        (Boolean) newValue,
+                        ((CheckBoxPreference) mPrefInstantUpload).isChecked());
                 return true;
             }
         });
-            
+
+        mPrefInstantUploadBehaviour = findPreference("prefs_instant_behaviour");
+        toggleInstantUploadBehaviour(
+                ((CheckBoxPreference)mPrefInstantVideoUpload).isChecked(),
+                ((CheckBoxPreference)mPrefInstantUpload).isChecked());
+
         /* About App */
        pAboutApp = (Preference) findPreference("about_app");
        if (pAboutApp != null) { 
@@ -472,6 +484,14 @@ public class Preferences extends PreferenceActivity
         }
     }
 
+    private void toggleInstantUploadBehaviour(Boolean video, Boolean picture){
+        if (picture || video){
+            mPrefInstantUploadCategory.addPreference(mPrefInstantUploadBehaviour);
+        } else {
+            mPrefInstantUploadCategory.removePreference(mPrefInstantUploadBehaviour);
+        }
+    }
+
     @Override
     public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
 

+ 38 - 4
src/com/owncloud/android/ui/activity/UploadFilesActivity.java

@@ -22,9 +22,11 @@ package com.owncloud.android.ui.activity;
 
 import android.accounts.Account;
 import android.content.Intent;
+import android.content.SharedPreferences;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.Environment;
+import android.preference.PreferenceManager;
 import android.support.v4.app.DialogFragment;
 import android.support.v7.app.ActionBar;
 import android.view.MenuItem;
@@ -33,9 +35,11 @@ import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.widget.ArrayAdapter;
 import android.widget.Button;
+import android.widget.RadioButton;
 import android.widget.TextView;
 
 import com.owncloud.android.R;
+import com.owncloud.android.files.services.FileUploader;
 import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
 import com.owncloud.android.ui.dialog.ConfirmationDialogFragment.ConfirmationDialogFragmentListener;
@@ -73,8 +77,10 @@ public class UploadFilesActivity extends FileActivity implements
     private static final String TAG = "UploadFilesActivity";
     private static final String WAIT_DIALOG_TAG = "WAIT";
     private static final String QUERY_TO_MOVE_DIALOG_TAG = "QUERY_TO_MOVE";
-    
-    
+    private RadioButton mRadioBtnCopyFiles;
+    private RadioButton mRadioBtnMoveFiles;
+
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         Log_OC.d(TAG, "onCreate() start");
@@ -112,6 +118,21 @@ public class UploadFilesActivity extends FileActivity implements
         mCancelBtn.setOnClickListener(this);
         mUploadBtn = (Button) findViewById(R.id.upload_files_btn_upload);
         mUploadBtn.setOnClickListener(this);
+
+        SharedPreferences appPreferences = PreferenceManager
+                .getDefaultSharedPreferences(getApplicationContext());
+
+        Integer localBehaviour = appPreferences.getInt("prefs_uploader_behaviour", FileUploader.LOCAL_BEHAVIOUR_COPY);
+
+        mRadioBtnMoveFiles = (RadioButton) findViewById(R.id.upload_radio_move);
+        if (localBehaviour == FileUploader.LOCAL_BEHAVIOUR_MOVE){
+            mRadioBtnMoveFiles.setChecked(true);
+        }
+
+        mRadioBtnCopyFiles = (RadioButton) findViewById(R.id.upload_radio_copy);
+        if (localBehaviour == FileUploader.LOCAL_BEHAVIOUR_COPY){
+            mRadioBtnCopyFiles.setChecked(true);
+        }
         
             
         // Action bar setup
@@ -342,9 +363,22 @@ public class UploadFilesActivity extends FileActivity implements
                 // return the list of selected files (success)
                 Intent data = new Intent();
                 data.putExtra(EXTRA_CHOSEN_FILES, mFileListFragment.getCheckedFilePaths());
-                setResult(RESULT_OK, data);
+
+                SharedPreferences.Editor appPreferencesEditor = PreferenceManager
+                        .getDefaultSharedPreferences(getApplicationContext()).edit();
+
+
+                if (mRadioBtnMoveFiles.isChecked()){
+                    setResult(RESULT_OK_AND_MOVE, data);
+                    appPreferencesEditor.putInt("prefs_uploader_behaviour",
+                            FileUploader.LOCAL_BEHAVIOUR_MOVE);
+                } else {
+                    setResult(RESULT_OK, data);
+                    appPreferencesEditor.putInt("prefs_uploader_behaviour",
+                            FileUploader.LOCAL_BEHAVIOUR_COPY);
+                }
+                appPreferencesEditor.apply();
                 finish();
-                
             } else {
                 // show a dialog to query the user if wants to move the selected files
                 // to the ownCloud folder instead of copying

+ 107 - 0
src/com/owncloud/android/ui/dialog/OwnCloudListPreference.java

@@ -0,0 +1,107 @@
+package com.owncloud.android.ui.dialog;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Build;
+import android.os.Bundle;
+import android.preference.ListPreference;
+import android.preference.PreferenceManager;
+import android.support.v7.app.AppCompatDialog;
+import android.util.AttributeSet;
+
+import com.owncloud.android.R;
+import com.owncloud.android.lib.common.utils.Log_OC;
+
+import java.lang.reflect.Method;
+
+public class OwnCloudListPreference extends ListPreference {
+    private static final String TAG = OwnCloudListPreference.class.getSimpleName();
+
+    private Context mContext;
+    private AppCompatDialog mDialog;
+
+    public OwnCloudListPreference(Context context) {
+        super(context);
+        this.mContext = context;
+    }
+
+    public OwnCloudListPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        this.mContext = context;
+    }
+
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    public OwnCloudListPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    public OwnCloudListPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    protected void showDialog(Bundle state) {
+        if (getEntries() == null || getEntryValues() == null) {
+            throw new IllegalStateException(
+                    "ListPreference requires an entries array and an entryValues array.");
+        }
+
+        int preselect = findIndexOfValue(getValue());
+
+        // same thing happens for the Standard ListPreference though
+        android.support.v7.app.AlertDialog.Builder builder =
+                new android.support.v7.app.AlertDialog.Builder(mContext, R.style.ownCloud_AlertDialog)
+                .setTitle(getDialogTitle())
+                .setIcon(getDialogIcon())
+                .setSingleChoiceItems(getEntries(), preselect, this);
+
+        PreferenceManager pm = getPreferenceManager();
+        try {
+            Method method = pm.getClass().getDeclaredMethod(
+                    "registerOnActivityDestroyListener",
+                    PreferenceManager.OnActivityDestroyListener.class);
+            method.setAccessible(true);
+            method.invoke(pm, this);
+        } catch (Exception e) {
+            // no way to handle this but logging it
+            Log_OC.e(TAG, "error invoking registerOnActivityDestroyListener", e);
+        }
+
+        mDialog = builder.create();
+        if (state != null) {
+            mDialog.onRestoreInstanceState(state);
+        }
+        mDialog.show();
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        if (which >= 0 && getEntryValues() != null) {
+            String value = getEntryValues()[which].toString();
+            if (callChangeListener(value)) {
+                setValue(value);
+
+                // Workaround for pre kitkat since they don't support change listener within setValue
+                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
+                    setSummary(getEntries()[which]);
+                }
+            }
+            dialog.dismiss();
+        }
+    }
+
+    @Override
+    public AppCompatDialog getDialog() {
+        return mDialog;
+    }
+
+    @Override
+    public void onActivityDestroy() {
+        super.onActivityDestroy();
+        if (mDialog != null && mDialog.isShowing()) {
+            mDialog.dismiss();
+        }
+    }
+}