Browse Source

Merge pull request #1954 from nextcloud/new-ardevd-storage

New, overhauled data storage management
Tobias Kaminsky 7 years ago
parent
commit
43943205ca

+ 49 - 7
src/gplay/java/com/owncloud/android/utils/PushUtils.java

@@ -1,4 +1,4 @@
-/**
+/*
  * Nextcloud Android client application
  *
  * @author Mario Danic
@@ -26,6 +26,7 @@ import android.accounts.OperationCanceledException;
 import android.content.Context;
 import android.text.TextUtils;
 import android.util.Base64;
+import android.util.Log;
 
 import com.google.gson.Gson;
 import com.owncloud.android.MainApp;
@@ -99,8 +100,9 @@ public class PushUtils {
     }
     
     private static int generateRsa2048KeyPair() {
-        String keyPath = MainApp.getStoragePath() + File.separator + MainApp.getDataFolder() + File.separator
-                + KEYPAIR_FOLDER;
+        migratePushKeys();
+        String keyPath = MainApp.getAppContext().getFilesDir().getAbsolutePath() + File.separator + 
+                MainApp.getDataFolder() + File.separator + KEYPAIR_FOLDER;
 
         String privateKeyPath = keyPath + File.separator + KEYPAIR_FILE_NAME + KEYPAIR_PRIV_EXTENSION;
         String publicKeyPath = keyPath + File.separator + KEYPAIR_FILE_NAME + KEYPAIR_PUB_EXTENSION;
@@ -227,8 +229,8 @@ public class PushUtils {
                                             publicKey,
                                             context.getResources().getString(R.string.push_server_url));
 
-                            RemoteOperationResult remoteOperationResult = registerAccountDeviceForNotificationsOperation.
-                                    execute(mClient);
+                            RemoteOperationResult remoteOperationResult = 
+                                    registerAccountDeviceForNotificationsOperation.execute(mClient);
 
                             if (remoteOperationResult.isSuccess()) {
                                 PushResponse pushResponse = remoteOperationResult.getPushResponseData();
@@ -271,8 +273,8 @@ public class PushUtils {
     }
 
     public static Key readKeyFromFile(boolean readPublicKey) {
-        String keyPath = MainApp.getStoragePath() + File.separator + MainApp.getDataFolder() + File.separator
-                + KEYPAIR_FOLDER;
+        String keyPath = MainApp.getAppContext().getFilesDir().getAbsolutePath() + File.separator + 
+                MainApp.getDataFolder() + File.separator + KEYPAIR_FOLDER;
 
         String privateKeyPath = keyPath + File.separator + KEYPAIR_FILE_NAME + KEYPAIR_PRIV_EXTENSION;
         String publicKeyPath = keyPath + File.separator + KEYPAIR_FILE_NAME + KEYPAIR_PUB_EXTENSION;
@@ -334,4 +336,44 @@ public class PushUtils {
 
         return -1;
     }
+
+    private static void migratePushKeys() {
+        Context context = MainApp.getAppContext();
+
+        if (!PreferenceManager.getKeysMigration(context)) {
+            String oldKeyPath = MainApp.getStoragePath() + File.separator + MainApp.getDataFolder()
+                    + File.separator + "nc-keypair";
+            File oldPrivateKeyFile = new File(oldKeyPath, "push_key.priv");
+            File oldPublicKeyFile = new File(oldKeyPath, "push_key.pub");
+
+            String keyPath = context.getDir("nc-keypair", Context.MODE_PRIVATE).getAbsolutePath();
+            File privateKeyFile = new File(keyPath, "push_key.priv");
+            File publicKeyFile = new File(keyPath, "push_key.pub");
+
+            if ((privateKeyFile.exists() && publicKeyFile.exists()) ||
+                    (!oldPrivateKeyFile.exists() && !oldPublicKeyFile.exists())) {
+                PreferenceManager.setKeysMigration(context, true);
+            } else {
+                if (oldPrivateKeyFile.exists()) {
+                    try {
+                        FileStorageUtils.moveFile(oldPrivateKeyFile, privateKeyFile);
+                    } catch (IOException e) {
+                        Log.e(TAG, "Failed to move old private key to new location");
+                    }
+                }
+
+                if (oldPublicKeyFile.exists()) {
+                    try {
+                        FileStorageUtils.moveFile(oldPublicKeyFile, publicKeyFile);
+                    } catch (IOException e) {
+                        Log.e(TAG, "Failed to move old public key to new location");
+                    }
+                }
+
+                if (privateKeyFile.exists() && publicKeyFile.exists()) {
+                    PreferenceManager.setKeysMigration(context, true);
+                }
+            }
+        }
+    }
 }

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

@@ -33,7 +33,6 @@ import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.os.Build;
 import android.os.Bundle;
-import android.os.Environment;
 import android.os.StrictMode;
 import android.support.annotation.StringRes;
 import android.support.multidex.MultiDexApplication;
@@ -115,8 +114,8 @@ public class MainApp extends MultiDexApplication {
 
         SharedPreferences appPrefs =
                 PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
-        MainApp.storagePath = appPrefs.getString(Preferences.PreferenceKeys.STORAGE_PATH, Environment.
-                getExternalStorageDirectory().getAbsolutePath());
+        MainApp.storagePath = appPrefs.getString(Preferences.PreferenceKeys.STORAGE_PATH,
+                getApplicationContext().getFilesDir().getAbsolutePath());
 
         boolean isSamlAuth = AUTH_ON.equals(getString(R.string.auth_method_saml_web_sso));
 

+ 24 - 18
src/main/java/com/owncloud/android/datamodel/OCFile.java

@@ -28,7 +28,9 @@ import android.content.Context;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.support.v4.content.FileProvider;
 
+import com.owncloud.android.R;
 import com.owncloud.android.lib.common.network.WebdavUtils;
 import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.utils.MimeType;
@@ -316,6 +318,19 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
         return mLocalUri;
     }
 
+
+    public Uri getLegacyExposedFileUri(Context context) {
+        if (mLocalPath == null || mLocalPath.length() == 0) {
+            return null;
+        }
+
+        if (mExposedFileUri == null) {
+            return Uri.parse(ContentResolver.SCHEME_FILE + "://" + WebdavUtils.encodePath(mLocalPath));
+        }
+
+        return mExposedFileUri;
+
+    }
     /*
         Partly disabled because not all apps understand paths that we get via this method for now
      */
@@ -324,25 +339,16 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
             return null;
         }
         if (mExposedFileUri == null) {
-            /*if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
-                // TODO - use FileProvider with any Android version, with deeper testing -> 2.2.0
-                mExposedFileUri = Uri.parse(
-                        ContentResolver.SCHEME_FILE + "://" + WebdavUtils.encodePath(mLocalPath)
-                );
-            } else {
-                // Use the FileProvider to get a content URI
-                try {
-                    mExposedFileUri = FileProvider.getUriForFile(
-                            context,
-                            context.getString(R.string.file_provider_authority),
-                            new File(mLocalPath)
-                    );
-                } catch (IllegalArgumentException e) {
-                    Log_OC.e(TAG, "File can't be exported");
-                }
+            try {
+                mExposedFileUri = FileProvider.getUriForFile(
+                        context,
+                        context.getString(R.string.file_provider_authority),
+                        new File(mLocalPath));
+            } catch (IllegalArgumentException ex) {
+                // Could not share file using FileProvider URI scheme.
+                // Fall back to legacy URI parsing.
+                getLegacyExposedFileUri(context);
             }
-        }*/
-            return Uri.parse(ContentResolver.SCHEME_FILE + "://" + WebdavUtils.encodePath(mLocalPath));
         }
         
         return mExposedFileUri;

+ 17 - 14
src/main/java/com/owncloud/android/datastorage/DataStorageProvider.java

@@ -21,7 +21,7 @@
 
 package com.owncloud.android.datastorage;
 
-import android.os.Build;
+import android.os.Environment;
 
 import com.owncloud.android.MainApp;
 import com.owncloud.android.datastorage.providers.EnvironmentStoragePointProvider;
@@ -31,7 +31,6 @@ import com.owncloud.android.datastorage.providers.MountCommandStoragePointProvid
 import com.owncloud.android.datastorage.providers.SystemDefaultStoragePointProvider;
 import com.owncloud.android.datastorage.providers.VDCStoragePointProvider;
 
-import java.io.File;
 import java.util.Vector;
 
 /**
@@ -63,18 +62,15 @@ public class DataStorageProvider {
             return mCachedStoragePoints.toArray(new StoragePoint[mCachedStoragePoints.size()]);
         }
 
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
-            for (File f : MainApp.getAppContext().getExternalMediaDirs()) {
-                if (f != null) {
-                    mCachedStoragePoints.add(new StoragePoint(f.getAbsolutePath(), f.getAbsolutePath()));
-                }
-            }
-        } else {
-            for (IStoragePointProvider p : mStorageProviders) {
-                if (p.canProvideStoragePoints()) {
-                    mCachedStoragePoints.addAll(p.getAvailableStoragePoint());
-                }
-            }
+        // Add internal storage directory
+        mCachedStoragePoints.add(new StoragePoint(MainApp.getAppContext().getFilesDir().getAbsolutePath(),
+                MainApp.getAppContext().getFilesDir().getAbsolutePath()));
+
+        // Add external storage directory if available.
+        if (isExternalStorageWritable()) {
+            mCachedStoragePoints.add(new StoragePoint(
+                    MainApp.getAppContext().getExternalFilesDir(null).getAbsolutePath(),
+                    MainApp.getAppContext().getExternalFilesDir(null).getAbsolutePath()));
         }
 
         return mCachedStoragePoints.toArray(new StoragePoint[mCachedStoragePoints.size()]);
@@ -97,4 +93,11 @@ public class DataStorageProvider {
     public void removeStoragePointProvider(IStoragePointProvider provider) {
         mStorageProviders.remove(provider);
     }
+
+    /* Checks if external storage is available for read and write */
+    public boolean isExternalStorageWritable() {
+        String state = Environment.getExternalStorageState();
+        return Environment.MEDIA_MOUNTED.equals(state);
+    }
+
 }

+ 3 - 13
src/main/java/com/owncloud/android/datastorage/providers/SystemDefaultStoragePointProvider.java

@@ -25,11 +25,8 @@ import com.owncloud.android.MainApp;
 import com.owncloud.android.R;
 import com.owncloud.android.datastorage.StoragePoint;
 
-import java.io.File;
 import java.util.Vector;
 
-import static android.os.Environment.getExternalStorageDirectory;
-
 /**
  * @author Bartosz Przybylski
  */
@@ -44,16 +41,9 @@ public class SystemDefaultStoragePointProvider extends AbstractStoragePointProvi
         Vector<StoragePoint> result = new Vector<>();
 
         final String defaultStringDesc = MainApp.getAppContext().getString(R.string.storage_description_default);
-        File path;
-        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
-            path = MainApp.getAppContext().getExternalMediaDirs()[0];
-        } else {
-            path = getExternalStorageDirectory();
-        }
-
-        if (path != null && path.canWrite()) {
-            result.add(new StoragePoint(defaultStringDesc, path.getAbsolutePath()));
-        }
+        // Add private internal storage data directory.
+        result.add(new StoragePoint(defaultStringDesc,
+                MainApp.getAppContext().getFilesDir().getAbsolutePath()));
 
         return result;
     }

+ 10 - 0
src/main/java/com/owncloud/android/db/PreferenceManager.java

@@ -53,6 +53,7 @@ public abstract class PreferenceManager {
     private static final String PREF__INSTANT_VIDEO_UPLOAD_PATH_USE_SUBFOLDERS
             = "instant_video_upload_path_use_subfolders";
     private static final String PREF__LEGACY_CLEAN = "legacyClean";
+    private static final String PREF__KEYS_MIGRATION = "keysMigration";
     private static final String PREF__AUTO_UPLOAD_UPDATE_PATH = "autoUploadPathUpdate";
     private static final String PREF__PUSH_TOKEN = "pushToken";
     private static final String PREF__AUTO_UPLOAD_SPLIT_OUT = "autoUploadEntriesSplitOut";
@@ -286,6 +287,11 @@ public abstract class PreferenceManager {
         return getDefaultSharedPreferences(context).getBoolean(PREF__LEGACY_CLEAN, false);
     }
 
+    public static boolean getKeysMigration(Context context) {
+        return getDefaultSharedPreferences(context).getBoolean(PREF__KEYS_MIGRATION, false);
+    }
+
+
     /**
      * Gets the auto upload paths flag last set.
      *
@@ -316,6 +322,10 @@ public abstract class PreferenceManager {
         saveBooleanPreference(context, PREF__LEGACY_CLEAN, legacyClean);
     }
 
+    public static void setKeysMigration(Context context, boolean keysMigration) {
+        saveBooleanPreference(context, PREF__KEYS_MIGRATION, keysMigration);
+    }
+
     public static void setAutoUploadInit(Context context, boolean autoUploadInit) {
         saveBooleanPreference(context, PREF__AUTO_UPLOAD_INIT, autoUploadInit);
     }

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

@@ -1017,8 +1017,9 @@ public class Preferences extends PreferenceActivity
     private void loadStoragePath() {
         SharedPreferences appPrefs =
                 PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
-        mStoragePath = appPrefs.getString(PreferenceKeys.STORAGE_PATH, Environment.getExternalStorageDirectory()
-                .getAbsolutePath());
+        // Load storage path from shared preferences. Use private internal storage by default.
+        mStoragePath = appPrefs.getString(PreferenceKeys.STORAGE_PATH,
+                getApplicationContext().getFilesDir().getAbsolutePath());
         String storageDescription = DataStorageProvider.getInstance().getStorageDescriptionByPath(mStoragePath);
         mPrefStoragePath.setSummary(storageDescription);
     }

+ 18 - 2
src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java

@@ -72,6 +72,7 @@ import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 import java.util.regex.Matcher;
@@ -197,6 +198,20 @@ public class FileOperationsHelper {
         if (file != null) {
             String storagePath = file.getStoragePath();
 
+            String[] officeExtensions = MainApp.getAppContext().getResources().getStringArray(R.array
+                    .ms_office_extensions);
+
+            Uri fileUri;
+
+            if (file.getFileName().contains(".") &&
+                    Arrays.asList(officeExtensions).contains(file.getFileName().substring(file.getFileName().
+                            lastIndexOf(".") + 1, file.getFileName().length())) &&
+                    !file.getStoragePath().startsWith(MainApp.getAppContext().getFilesDir().getAbsolutePath())) {
+                fileUri = file.getLegacyExposedFileUri(mFileActivity);
+            } else {
+                fileUri = file.getExposedFileUri(mFileActivity);
+            }
+
             Intent openFileWithIntent = null;
             int lastIndexOfDot = storagePath.lastIndexOf('.');
             if (lastIndexOfDot >= 0) {
@@ -204,8 +219,9 @@ public class FileOperationsHelper {
                 String guessedMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExt);
                 if (guessedMimeType != null) {
                     openFileWithIntent = new Intent(Intent.ACTION_VIEW);
+                    openFileWithIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
                     openFileWithIntent.setDataAndType(
-                            file.getExposedFileUri(mFileActivity),
+                            fileUri,
                             guessedMimeType
                     );
                 }
@@ -218,7 +234,7 @@ public class FileOperationsHelper {
             if (openFileWithIntent == null) {
                 openFileWithIntent = new Intent(Intent.ACTION_VIEW);
                 openFileWithIntent.setDataAndType(
-                        file.getExposedFileUri(mFileActivity),
+                        fileUri,
                         file.getMimetype()
                 );
             }

+ 8 - 0
src/main/java/com/owncloud/android/utils/FileStorageUtils.java

@@ -355,6 +355,14 @@ public class FileStorageUtils {
         return ret;
     }
 
+    public static boolean moveFile(File sourceFile, File targetFile) throws IOException {
+        if (copyFile(sourceFile, targetFile)) {
+            return sourceFile.delete();
+        } else {
+            return false;
+        }
+    }
+
     public static void deleteRecursively(File file, FileDataStorageManager storageManager) {
         if (file.isDirectory()) {
             for (File child : file.listFiles()) {

+ 32 - 0
src/main/res/values/arrays.xml

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <array name="ms_office_extensions">
+        <item>doc</item>
+        <item>dot</item>
+        <item>docx</item>
+        <item>dotx</item>
+        <item>docm</item>
+        <item>dotm</item>
+        <item>xls</item>
+        <item>xlt</item>
+        <item>xla</item>
+        <item>xlsx</item>
+        <item>xltx</item>
+        <item>xlsm</item>
+        <item>xltm</item>
+        <item>xlam</item>
+        <item>xlsb</item>
+        <item>ppt</item>
+        <item>pot</item>
+        <item>pps</item>
+        <item>ppa</item>
+        <item>pptx</item>
+        <item>potx</item>
+        <item>ppsx</item>
+        <item>ppam</item>
+        <item>pptm</item>
+        <item>potm</item>
+        <item>ppsm</item>
+        <item>mdb</item>
+    </array>
+</resources>

+ 1 - 0
src/main/res/xml/exposed_filepaths.xml

@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <paths>
+    <files-path name="user_files_internal" path="nextcloud/"/>
     <files-path
         path="log/"
         name="log"/>

+ 12 - 7
src/modified/java/com/owncloud/android/utils/PushUtils.java

@@ -1,4 +1,4 @@
-/**
+/*
  * Nextcloud Android client application
  *
  * @author Mario Danic
@@ -99,8 +99,9 @@ public class PushUtils {
     }
 
     private static int generateRsa2048KeyPair() {
-        String keyPath = MainApp.getStoragePath() + File.separator + MainApp.getDataFolder() + File.separator
-                + KEYPAIR_FOLDER;
+        migratePushKeys();
+        String keyPath = MainApp.getAppContext().getFilesDir().getAbsolutePath() + File.separator + 
+                MainApp.getDataFolder() + File.separator + KEYPAIR_FOLDER;
 
         String privateKeyPath = keyPath + File.separator + KEYPAIR_FILE_NAME + KEYPAIR_PRIV_EXTENSION;
         String publicKeyPath = keyPath + File.separator + KEYPAIR_FILE_NAME + KEYPAIR_PUB_EXTENSION;
@@ -227,8 +228,8 @@ public class PushUtils {
                                             publicKey,
                                             context.getResources().getString(R.string.push_server_url));
 
-                            RemoteOperationResult remoteOperationResult = registerAccountDeviceForNotificationsOperation.
-                                    execute(mClient);
+                            RemoteOperationResult remoteOperationResult = 
+                                    registerAccountDeviceForNotificationsOperation.execute(mClient);
 
                             if (remoteOperationResult.isSuccess()) {
                                 PushResponse pushResponse = remoteOperationResult.getPushResponseData();
@@ -267,8 +268,8 @@ public class PushUtils {
     }
 
     public static Key readKeyFromFile(boolean readPublicKey) {
-        String keyPath = MainApp.getStoragePath() + File.separator + MainApp.getDataFolder() + File.separator
-                + KEYPAIR_FOLDER;
+        String keyPath = MainApp.getAppContext().getFilesDir().getAbsolutePath() + File.separator + 
+                MainApp.getDataFolder() + File.separator + KEYPAIR_FOLDER;
 
         String privateKeyPath = keyPath + File.separator + KEYPAIR_FILE_NAME + KEYPAIR_PRIV_EXTENSION;
         String publicKeyPath = keyPath + File.separator + KEYPAIR_FILE_NAME + KEYPAIR_PUB_EXTENSION;
@@ -330,4 +331,8 @@ public class PushUtils {
 
         return -1;
     }
+
+    private static void migratePushKeys() {
+    
+    }
 }