mirror of
https://github.com/nextcloud/android.git
synced 2024-10-22 20:16:02 +03:00
commit
9f8fdaeac0
22 changed files with 1483 additions and 103 deletions
|
@ -56,6 +56,9 @@
|
|||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
|
||||
<application
|
||||
android:name=".MainApp"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
|
@ -79,6 +82,8 @@
|
|||
android:taskAffinity=""
|
||||
android:excludeFromRecents="true"
|
||||
android:theme="@style/Theme.ownCloud.NoActionBar">
|
||||
<activity android:name=".ui.activity.LocalDirectorySelectorActivity" />
|
||||
<activity android:name=".ui.activity.StorageMigrationActivity" />
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
|
||||
|
|
52
res/layout/migration_layout.xml
Normal file
52
res/layout/migration_layout.xml
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Nextcloud Android client application
|
||||
|
||||
Copyright (C) 2016 Bartosz Przybylski
|
||||
Copyright (C) 2016 Nextcloud
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public
|
||||
License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/migrationProgress"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:paddingLeft="30dp"
|
||||
android:paddingRight="30dp"
|
||||
android:progress="50"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/migrationText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:text=""
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/finishButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:text="@string/drawer_close"/>
|
||||
</LinearLayout>
|
|
@ -356,6 +356,28 @@
|
|||
<string name="uploader_upload_forbidden_permissions">to upload in this folder</string>
|
||||
<string name="downloader_download_file_not_found">The file is no longer available on the server</string>
|
||||
|
||||
<string name="file_migration_dialog_title">Updating storage path</string>
|
||||
<string name="file_migration_finish_button">Finish</string>
|
||||
<string name="file_migration_preparing">Preparing for migration…</string>
|
||||
<string name="file_migration_checking_destination">Checking destination…</string>
|
||||
<string name="file_migration_saving_accounts_configuration">Saving accounts configuration…</string>
|
||||
<string name="file_migration_waiting_for_unfinished_sync">Waiting for unfinished synchronizations…</string>
|
||||
<string name="file_migration_migrating">Moving data…</string>
|
||||
<string name="file_migration_updating_index">Updating index…</string>
|
||||
<string name="file_migration_cleaning">Cleaning…</string>
|
||||
<string name="file_migration_restoring_accounts_configuration">Restoring accounts configuration…</string>
|
||||
<string name="file_migration_ok_finished">Finished</string>
|
||||
<string name="file_migration_failed_not_enough_space">ERROR: Not enough space</string>
|
||||
<string name="file_migration_failed_not_writable">ERROR: File is not writable</string>
|
||||
<string name="file_migration_failed_not_readable">ERROR: File is not readable</string>
|
||||
<string name="file_migration_failed_dir_already_exists">ERROR: Nextcloud directory already exists</string>
|
||||
<string name="file_migration_failed_while_coping">ERROR: While migrating</string>
|
||||
<string name="file_migration_failed_while_updating_index">ERROR: While updating index</string>
|
||||
|
||||
<string name="file_migration_directory_already_exists">Data folder already exists, what to do?</string>
|
||||
<string name="file_migration_override_data_folder">Override</string>
|
||||
<string name="file_migration_use_data_folder">Use existing</string>
|
||||
|
||||
<string name="prefs_category_accounts">Accounts</string>
|
||||
<string name="prefs_add_account">Add account</string>
|
||||
<string name="drawer_manage_accounts">Manage accounts</string>
|
||||
|
@ -422,6 +444,8 @@
|
|||
<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="pref_behaviour_entries_delete_file">deleted</string>
|
||||
<string name="prefs_storage_path">Storage path</string>
|
||||
<string name="prefs_common">Common</string>
|
||||
|
||||
<string name="share_dialog_title">Sharing</string>
|
||||
<string name="share_file">Share %1$s</string>
|
||||
|
@ -506,4 +530,8 @@
|
|||
<item quantity="other">%d selected</item>
|
||||
</plurals>
|
||||
|
||||
<string name="storage_description_default">Default</string>
|
||||
<string name="storage_description_sd_no">SD card %1$d</string>
|
||||
<string name="storage_description_unknown">Unknown</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -18,6 +18,11 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<PreferenceCategory android:title="@string/prefs_category_general">
|
||||
<ListPreference
|
||||
android:title="@string/prefs_storage_path"
|
||||
android:key="storage_path" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/prefs_category_instant_uploading" android:key="instant_uploading_category">
|
||||
<com.owncloud.android.ui.CheckBoxPreferenceWithLongTitle android:key="instant_uploading"
|
||||
|
|
|
@ -23,16 +23,19 @@ package com.owncloud.android;
|
|||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
import com.owncloud.android.authentication.PassCodeManager;
|
||||
import com.owncloud.android.datamodel.ThumbnailsCacheManager;
|
||||
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
|
||||
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory.Policy;
|
||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||
import com.owncloud.android.ui.activity.Preferences;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -54,13 +57,20 @@ public class MainApp extends Application {
|
|||
|
||||
private static Context mContext;
|
||||
|
||||
private static String storagePath;
|
||||
|
||||
private static boolean mOnlyOnDevice = false;
|
||||
|
||||
|
||||
public void onCreate(){
|
||||
super.onCreate();
|
||||
MainApp.mContext = getApplicationContext();
|
||||
|
||||
|
||||
SharedPreferences appPrefs =
|
||||
PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||
MainApp.storagePath = appPrefs.getString(Preferences.Keys.STORAGE_PATH, Environment.
|
||||
getExternalStorageDirectory().getAbsolutePath());
|
||||
|
||||
boolean isSamlAuth = AUTH_ON.equals(getString(R.string.auth_method_saml_web_sso));
|
||||
|
||||
OwnCloudClientManagerFactory.setUserAgent(getUserAgent());
|
||||
|
@ -80,8 +90,7 @@ public class MainApp extends Application {
|
|||
// Set folder for store logs
|
||||
Log_OC.setLogDataFolder(dataFolder);
|
||||
|
||||
//TODO: to be changed/fixed whenever SD card support gets merged.
|
||||
Log_OC.startLogging(Environment.getExternalStorageDirectory().getAbsolutePath());
|
||||
Log_OC.startLogging(MainApp.storagePath);
|
||||
Log_OC.d("Debug", "start logging");
|
||||
}
|
||||
|
||||
|
@ -132,6 +141,14 @@ public class MainApp extends Application {
|
|||
return MainApp.mContext;
|
||||
}
|
||||
|
||||
public static String getStoragePath(){
|
||||
return MainApp.storagePath;
|
||||
}
|
||||
|
||||
public static void setStoragePath(String path){
|
||||
MainApp.storagePath = path;
|
||||
}
|
||||
|
||||
// Methods to obtain Strings referring app_name
|
||||
// From AccountAuthenticator
|
||||
// public static final String ACCOUNT_TYPE = "owncloud";
|
||||
|
|
|
@ -46,11 +46,6 @@ import com.owncloud.android.utils.MimeType;
|
|||
import com.owncloud.android.utils.MimeTypeUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
@ -727,44 +722,69 @@ public class FileDataStorageManager {
|
|||
if (!targetFolder.exists()) {
|
||||
targetFolder.mkdirs();
|
||||
}
|
||||
copied = copyFile(localFile, targetFile);
|
||||
copied = FileStorageUtils.copyFile(localFile, targetFile);
|
||||
}
|
||||
Log_OC.d(TAG, "Local file COPIED : " + copied);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean copyFile(File src, File target) {
|
||||
boolean ret = true;
|
||||
public void migrateStoredFiles(String srcPath, String dstPath) throws Exception {
|
||||
Cursor c = null;
|
||||
if (getContentResolver() != null) {
|
||||
c = getContentResolver().query(ProviderTableMeta.CONTENT_URI_FILE,
|
||||
null,
|
||||
ProviderTableMeta.FILE_STORAGE_PATH + " IS NOT NULL",
|
||||
null,
|
||||
null);
|
||||
|
||||
InputStream in = null;
|
||||
OutputStream out = null;
|
||||
|
||||
try {
|
||||
in = new FileInputStream(src);
|
||||
out = new FileOutputStream(target);
|
||||
byte[] buf = new byte[1024];
|
||||
int len;
|
||||
while ((len = in.read(buf)) > 0) {
|
||||
out.write(buf, 0, len);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
ret = false;
|
||||
} finally {
|
||||
if (in != null) try {
|
||||
in.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
if (out != null) try {
|
||||
out.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace(System.err);
|
||||
} else {
|
||||
try {
|
||||
c = getContentProviderClient().query(ProviderTableMeta.CONTENT_URI_FILE,
|
||||
new String[]{ProviderTableMeta._ID, ProviderTableMeta.FILE_STORAGE_PATH},
|
||||
ProviderTableMeta.FILE_STORAGE_PATH + " IS NOT NULL",
|
||||
null,
|
||||
null);
|
||||
} catch (RemoteException e) {
|
||||
Log_OC.e(TAG, e.getMessage());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
ArrayList<ContentProviderOperation> operations =
|
||||
new ArrayList<ContentProviderOperation>(c.getCount());
|
||||
if (c.moveToFirst()) {
|
||||
do {
|
||||
ContentValues cv = new ContentValues();
|
||||
long fileId = c.getLong(c.getColumnIndex(ProviderTableMeta._ID));
|
||||
String oldFileStoragePath = c.getString(c.getColumnIndex(ProviderTableMeta.FILE_STORAGE_PATH));
|
||||
|
||||
if (oldFileStoragePath.startsWith(srcPath)) {
|
||||
|
||||
cv.put(
|
||||
ProviderTableMeta.FILE_STORAGE_PATH,
|
||||
oldFileStoragePath.replaceFirst(srcPath, dstPath));
|
||||
|
||||
operations.add(
|
||||
ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
|
||||
withValues(cv).
|
||||
withSelection(
|
||||
ProviderTableMeta._ID + "=?",
|
||||
new String[]{String.valueOf(fileId)}
|
||||
)
|
||||
.build());
|
||||
}
|
||||
|
||||
} while (c.moveToNext());
|
||||
}
|
||||
c.close();
|
||||
|
||||
/// 3. apply updates in batch
|
||||
if (getContentResolver() != null) {
|
||||
getContentResolver().applyBatch(MainApp.getAuthority(), operations);
|
||||
} else {
|
||||
getContentProviderClient().applyBatch(operations);
|
||||
}
|
||||
}
|
||||
|
||||
private Vector<OCFile> getFolderContent(long parentId, boolean onlyOnDevice) {
|
||||
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
/**
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Bartosz Przybylski
|
||||
* Copyright (C) 2016 Nextcloud
|
||||
* Copyright (C) 2016 Bartosz Przybylski
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.owncloud.android.datastorage;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
import com.owncloud.android.MainApp;
|
||||
import com.owncloud.android.R;
|
||||
import com.owncloud.android.datastorage.providers.EnvironmentStoragePointProvider;
|
||||
import com.owncloud.android.datastorage.providers.HardcodedStoragePointProvider;
|
||||
import com.owncloud.android.datastorage.providers.IStoragePointProvider;
|
||||
import com.owncloud.android.datastorage.providers.MountCommandStoragePointProvider;
|
||||
import com.owncloud.android.datastorage.providers.SystemDefaultStoragePointProvider;
|
||||
import com.owncloud.android.datastorage.providers.VDCStoragePointProvider;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* @author Bartosz Przybylski
|
||||
*/
|
||||
public class DataStorageProvider {
|
||||
|
||||
private static final Vector<IStoragePointProvider> mStorageProviders = new Vector<>();
|
||||
private static final UniqueStorageList mCachedStoragePoints = new UniqueStorageList();
|
||||
private static final DataStorageProvider sInstance = new DataStorageProvider() {{
|
||||
// There is no system wide way to get usb storage so we need to provide multiple
|
||||
// handcrafted ways to add those.
|
||||
addStoragePointProvider(new SystemDefaultStoragePointProvider());
|
||||
addStoragePointProvider(new EnvironmentStoragePointProvider());
|
||||
addStoragePointProvider(new VDCStoragePointProvider());
|
||||
addStoragePointProvider(new MountCommandStoragePointProvider());
|
||||
addStoragePointProvider(new HardcodedStoragePointProvider());
|
||||
}};
|
||||
|
||||
|
||||
public static DataStorageProvider getInstance() {
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
private DataStorageProvider() {}
|
||||
|
||||
public StoragePoint[] getAvailableStoragePoints() {
|
||||
if (mCachedStoragePoints.size() != 0)
|
||||
return mCachedStoragePoints.toArray(new StoragePoint[mCachedStoragePoints.size()]);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
for (File f : MainApp.getAppContext().getExternalFilesDirs(null)) {
|
||||
if (f != null) {
|
||||
mCachedStoragePoints.add(new StoragePoint(f.getAbsolutePath(), f.getAbsolutePath()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (IStoragePointProvider p : mStorageProviders)
|
||||
if (p.canProvideStoragePoints()) {
|
||||
mCachedStoragePoints.addAll(p.getAvailableStoragePoint());
|
||||
}
|
||||
}
|
||||
|
||||
return mCachedStoragePoints.toArray(new StoragePoint[mCachedStoragePoints.size()]);
|
||||
}
|
||||
|
||||
public String getStorageDescriptionByPath(String path) {
|
||||
for (StoragePoint s : getAvailableStoragePoints())
|
||||
if (s.getPath().equals(path))
|
||||
return s.getDescription();
|
||||
return MainApp.getAppContext().getString(R.string.storage_description_unknown);
|
||||
}
|
||||
|
||||
public void addStoragePointProvider(IStoragePointProvider provider) {
|
||||
mStorageProviders.add(provider);
|
||||
}
|
||||
|
||||
public void removeStoragePointProvider(IStoragePointProvider provider) {
|
||||
mStorageProviders.remove(provider);
|
||||
}
|
||||
}
|
43
src/com/owncloud/android/datastorage/StoragePoint.java
Normal file
43
src/com/owncloud/android/datastorage/StoragePoint.java
Normal file
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Bartosz Przybylski
|
||||
* Copyright (C) 2016 Nextcloud
|
||||
* Copyright (C) 2016 Bartosz Przybylski
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.owncloud.android.datastorage;
|
||||
|
||||
/**
|
||||
* @author Bartosz Przybylski
|
||||
*/
|
||||
public class StoragePoint implements Comparable<StoragePoint> {
|
||||
private String mDescription;
|
||||
private String mPath;
|
||||
|
||||
public StoragePoint(String description, String path) {
|
||||
mDescription = description;
|
||||
mPath = path;
|
||||
}
|
||||
|
||||
public String getPath() { return mPath; }
|
||||
public String getDescription() { return mDescription; }
|
||||
|
||||
@Override
|
||||
public int compareTo(StoragePoint another) {
|
||||
return mPath.compareTo(another.getPath());
|
||||
}
|
||||
}
|
54
src/com/owncloud/android/datastorage/UniqueStorageList.java
Normal file
54
src/com/owncloud/android/datastorage/UniqueStorageList.java
Normal file
|
@ -0,0 +1,54 @@
|
|||
/**
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Bartosz Przybylski
|
||||
* Copyright (C) 2016 Nextcloud
|
||||
* Copyright (C) 2016 Bartosz Przybylski
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.owncloud.android.datastorage;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* @author Bartosz Przybylski
|
||||
*/
|
||||
public class UniqueStorageList extends Vector<StoragePoint> {
|
||||
@Override
|
||||
public boolean add(StoragePoint sp) {
|
||||
try {
|
||||
for (StoragePoint s : this) {
|
||||
String thisCanonPath = new File(s.getPath()).getCanonicalPath();
|
||||
String otherCanonPath = new File(sp.getPath()).getCanonicalPath();
|
||||
if (thisCanonPath.equals(otherCanonPath))
|
||||
return true;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
return super.add(sp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean addAll(Collection<? extends StoragePoint> collection) {
|
||||
for (StoragePoint sp : collection)
|
||||
add(sp);
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/**
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Bartosz Przybylski
|
||||
* Copyright (C) 2016 Nextcloud
|
||||
* Copyright (C) 2016 Bartosz Przybylski
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.owncloud.android.datastorage.providers;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author Bartosz Przybylski
|
||||
*/
|
||||
abstract public class AbstractCommandLineStoragePoint extends AbstractStoragePointProvider {
|
||||
|
||||
static protected final int sCommandLineOKReturnValue = 0;
|
||||
|
||||
protected abstract String[] getCommand();
|
||||
|
||||
@Override
|
||||
public boolean canProvideStoragePoints() {
|
||||
Process process;
|
||||
try {
|
||||
process = new ProcessBuilder().command(Arrays.asList(getCommand())).start();
|
||||
process.waitFor();
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
return process != null && process.exitValue() == sCommandLineOKReturnValue;
|
||||
}
|
||||
|
||||
protected String getCommandLineResult() {
|
||||
String s = "";
|
||||
try {
|
||||
final Process process = new ProcessBuilder().command(getCommand())
|
||||
.redirectErrorStream(true).start();
|
||||
|
||||
process.waitFor();
|
||||
final InputStream is = process.getInputStream();
|
||||
final byte buffer[] = new byte[1024];
|
||||
while (is.read(buffer) != -1)
|
||||
s += new String(buffer);
|
||||
is.close();
|
||||
} catch (final Exception e) { }
|
||||
return s;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Bartosz Przybylski
|
||||
* Copyright (C) 2016 Nextcloud
|
||||
* Copyright (C) 2016 Bartosz Przybylski
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.owncloud.android.datastorage.providers;
|
||||
|
||||
import com.owncloud.android.datastorage.StoragePoint;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* @author Bartosz Przybylski
|
||||
*/
|
||||
abstract public class AbstractStoragePointProvider implements IStoragePointProvider {
|
||||
|
||||
protected boolean canBeAddedToAvailableList(Vector<StoragePoint> currentList, String path) {
|
||||
if (path == null) return false;
|
||||
for (StoragePoint storage : currentList)
|
||||
if (storage.getPath().equals(path))
|
||||
return false;
|
||||
File f = new File(path);
|
||||
return f.exists() && f.isDirectory() && f.canRead() && f.canWrite();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/**
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Bartosz Przybylski
|
||||
* Copyright (C) 2016 Nextcloud
|
||||
* Copyright (C) 2016 Bartosz Przybylski
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.owncloud.android.datastorage.providers;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.owncloud.android.datastorage.StoragePoint;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* @author Bartosz Przybylski
|
||||
*/
|
||||
public class EnvironmentStoragePointProvider extends AbstractStoragePointProvider {
|
||||
|
||||
private static final String sSecondaryStorageEnvName = "SECONDARY_STORAGE";
|
||||
|
||||
@Override
|
||||
public boolean canProvideStoragePoints() {
|
||||
return !TextUtils.isEmpty(System.getenv(sSecondaryStorageEnvName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector<StoragePoint> getAvailableStoragePoint() {
|
||||
Vector<StoragePoint> result = new Vector<>();
|
||||
|
||||
addEntriesFromEnv(result, sSecondaryStorageEnvName);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void addEntriesFromEnv(Vector<StoragePoint> result, String envName) {
|
||||
String env = System.getenv(envName);
|
||||
if (env != null)
|
||||
for (String p : env.split(":"))
|
||||
if (canBeAddedToAvailableList(result, p))
|
||||
result.add(new StoragePoint(p, p));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/**
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Bartosz Przybylski
|
||||
* Copyright (C) 2016 Nextcloud
|
||||
* Copyright (C) 2016 Bartosz Przybylski
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.owncloud.android.datastorage.providers;
|
||||
|
||||
import com.owncloud.android.datastorage.StoragePoint;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* @author Bartosz Przybylski
|
||||
*/
|
||||
public class HardcodedStoragePointProvider extends AbstractStoragePointProvider {
|
||||
|
||||
static private final String[] sPaths = {
|
||||
"/mnt/external_sd/",
|
||||
"/mnt/extSdCard/",
|
||||
"/storage/extSdCard",
|
||||
"/storage/sdcard1/",
|
||||
"/storage/usbcard1/"
|
||||
};
|
||||
|
||||
@Override
|
||||
public boolean canProvideStoragePoints() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector<StoragePoint> getAvailableStoragePoint() {
|
||||
Vector<StoragePoint> result = new Vector<>();
|
||||
|
||||
for (String s : sPaths)
|
||||
if (canBeAddedToAvailableList(result, s))
|
||||
result.add(new StoragePoint(s, s));
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/**
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Bartosz Przybylski
|
||||
* Copyright (C) 2016 Nextcloud
|
||||
* Copyright (C) 2016 Bartosz Przybylski
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.owncloud.android.datastorage.providers;
|
||||
|
||||
import com.owncloud.android.datastorage.StoragePoint;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* @author Bartosz Przybylski
|
||||
*/
|
||||
public interface IStoragePointProvider {
|
||||
|
||||
/**
|
||||
* This method is used for querying storage provider to check if it can provide
|
||||
* usable and reliable data storage places.
|
||||
*
|
||||
* @return true if provider can reliably return storage path
|
||||
*/
|
||||
boolean canProvideStoragePoints();
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @return available storage points
|
||||
*/
|
||||
Vector<StoragePoint> getAvailableStoragePoint();
|
||||
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/**
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Bartosz Przybylski
|
||||
* Copyright (C) 2016 Nextcloud
|
||||
* Copyright (C) 2016 Bartosz Przybylski
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.owncloud.android.datastorage.providers;
|
||||
|
||||
import com.owncloud.android.datastorage.StoragePoint;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Vector;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author Bartosz Przybylski
|
||||
*/
|
||||
public class MountCommandStoragePointProvider extends AbstractCommandLineStoragePoint {
|
||||
|
||||
static private final String[] sCommand = new String[] { "mount" };
|
||||
|
||||
private static Pattern sPattern = Pattern.compile("(?i).*vold.*(vfat|ntfs|exfat|fat32|ext3|ext4).*rw.*");
|
||||
|
||||
@Override
|
||||
protected String[] getCommand() {
|
||||
return sCommand;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector<StoragePoint> getAvailableStoragePoint() {
|
||||
Vector<StoragePoint> result = new Vector<>();
|
||||
|
||||
for (String p : getPotentialPaths(getCommandLineResult()))
|
||||
if (canBeAddedToAvailableList(result, p))
|
||||
result.add(new StoragePoint(p, p));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Vector<String> getPotentialPaths(String mounted) {
|
||||
final Vector<String> result = new Vector<>();
|
||||
|
||||
for (String line : mounted.split("\n"))
|
||||
if (!line.toLowerCase(Locale.US).contains("asec") && sPattern.matcher(line).matches()) {
|
||||
String parts[] = line.split(" ");
|
||||
for (String path : parts) {
|
||||
if (path.startsWith("/") &&
|
||||
!path.toLowerCase(Locale.US).contains("vold"))
|
||||
result.add(path);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/**
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Bartosz Przybylski
|
||||
* Copyright (C) 2016 Nextcloud
|
||||
* Copyright (C) 2016 Bartosz Przybylski
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.owncloud.android.datastorage.providers;
|
||||
|
||||
import android.os.Environment;
|
||||
|
||||
import com.owncloud.android.MainApp;
|
||||
import com.owncloud.android.R;
|
||||
import com.owncloud.android.datastorage.StoragePoint;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* @author Bartosz Przybylski
|
||||
*/
|
||||
public class SystemDefaultStoragePointProvider extends AbstractStoragePointProvider {
|
||||
@Override
|
||||
public boolean canProvideStoragePoints() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector<StoragePoint> getAvailableStoragePoint() {
|
||||
Vector<StoragePoint> result = new Vector<>();
|
||||
|
||||
final String defaultStringDesc =
|
||||
MainApp.getAppContext().getString(R.string.storage_description_default);
|
||||
final String path = Environment.getExternalStorageDirectory().getAbsolutePath();
|
||||
result.add(new StoragePoint(defaultStringDesc, path));
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/**
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Bartosz Przybylski
|
||||
* Copyright (C) 2016 Nextcloud
|
||||
* Copyright (C) 2016 Bartosz Przybylski
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.owncloud.android.datastorage.providers;
|
||||
|
||||
import com.owncloud.android.datastorage.StoragePoint;
|
||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* @author Bartosz Przybylski
|
||||
*/
|
||||
public class VDCStoragePointProvider extends AbstractCommandLineStoragePoint {
|
||||
|
||||
static private final String TAG = VDCStoragePointProvider.class.getSimpleName();
|
||||
|
||||
static private final String[] sVDCVolListCommand = new String[]{ "/system/bin/vdc", "volume", "list" };
|
||||
static private final int sVDCVolumeList = 110;
|
||||
|
||||
|
||||
@Override
|
||||
public Vector<StoragePoint> getAvailableStoragePoint() {
|
||||
Vector<StoragePoint> result = new Vector<>();
|
||||
|
||||
result.addAll(getPaths(getCommandLineResult()));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] getCommand() {
|
||||
return sVDCVolListCommand;
|
||||
}
|
||||
|
||||
private Vector<StoragePoint> getPaths(String vdcResources) {
|
||||
Vector<StoragePoint> result = new Vector<>();
|
||||
|
||||
for (String line : vdcResources.split("\n")) {
|
||||
String vdcLine[] = line.split(" ");
|
||||
try {
|
||||
int status = Integer.parseInt(vdcLine[0]);
|
||||
if (status != sVDCVolumeList)
|
||||
continue;
|
||||
final String description = vdcLine[1];
|
||||
final String path = vdcLine[2];
|
||||
|
||||
if (canBeAddedToAvailableList(result, path))
|
||||
result.add(new StoragePoint(description, path));
|
||||
|
||||
} catch (NumberFormatException e) {
|
||||
Log_OC.e(TAG, "Incorrect VDC output format " + e);
|
||||
} catch (Exception e) {
|
||||
Log_OC.e(TAG, "Unexpected exception on VDC parsing " + e);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/**
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Bartosz Przybylski
|
||||
* Copyright (C) 2016 ownCloud Inc.
|
||||
* Copyright (C) 2016 Nextcloud
|
||||
* Copyright (C) 2016 Bartosz Przybylski
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.owncloud.android.ui.activity;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
import com.owncloud.android.R;
|
||||
|
||||
/**
|
||||
* Created by Bartosz Przybylski on 07.11.2015.
|
||||
*/
|
||||
public class LocalDirectorySelectorActivity extends UploadFilesActivity {
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mUploadBtn.setText(R.string.folder_picker_choose_button_text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (v.getId() == R.id.upload_files_btn_cancel) {
|
||||
setResult(RESULT_CANCELED);
|
||||
finish();
|
||||
|
||||
} else if (v.getId() == R.id.upload_files_btn_upload) {
|
||||
Intent resultIntent = new Intent();
|
||||
resultIntent.putExtra(EXTRA_CHOSEN_FILES, getInitialDirectory().getAbsolutePath());
|
||||
setResult(RESULT_OK, resultIntent);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -31,7 +31,9 @@ import android.content.pm.PackageManager.NameNotFoundException;
|
|||
import android.content.res.Configuration;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.preference.CheckBoxPreference;
|
||||
import android.preference.ListPreference;
|
||||
import android.preference.Preference;
|
||||
import android.preference.Preference.OnPreferenceChangeListener;
|
||||
import android.preference.Preference.OnPreferenceClickListener;
|
||||
|
@ -55,21 +57,24 @@ import com.owncloud.android.MainApp;
|
|||
import com.owncloud.android.R;
|
||||
import com.owncloud.android.authentication.AccountUtils;
|
||||
import com.owncloud.android.datamodel.OCFile;
|
||||
import com.owncloud.android.datastorage.DataStorageProvider;
|
||||
import com.owncloud.android.datastorage.StoragePoint;
|
||||
import com.owncloud.android.lib.common.OwnCloudAccount;
|
||||
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
|
||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||
import com.owncloud.android.ui.PreferenceWithLongSummary;
|
||||
import com.owncloud.android.utils.DisplayUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/**
|
||||
* An Activity that allows the user to change the application's settings.
|
||||
*
|
||||
* It proxies the necessary calls via {@link android.support.v7.app.AppCompatDelegate} to be used
|
||||
* with AppCompat.
|
||||
*/
|
||||
public class Preferences extends PreferenceActivity {
|
||||
public class Preferences extends PreferenceActivity
|
||||
implements StorageMigration.StorageMigrationProgressListener {
|
||||
|
||||
private static final String TAG = Preferences.class.getSimpleName();
|
||||
|
||||
|
@ -90,7 +95,6 @@ public class Preferences extends PreferenceActivity {
|
|||
private Preference pAboutApp;
|
||||
private AppCompatDelegate mDelegate;
|
||||
|
||||
private PreferenceCategory mAccountsPrefCategory = null;
|
||||
private String mUploadPath;
|
||||
private PreferenceCategory mPrefInstantUploadCategory;
|
||||
private Preference mPrefInstantUpload;
|
||||
|
@ -105,6 +109,14 @@ public class Preferences extends PreferenceActivity {
|
|||
private Preference mPrefInstantVideoUploadPathWiFi;
|
||||
private Preference mPrefInstantVideoUploadOnlyOnCharging;
|
||||
private String mUploadVideoPath;
|
||||
private ListPreference mPrefStoragePath;
|
||||
private String mStoragePath;
|
||||
|
||||
public static class Keys {
|
||||
public static final String STORAGE_PATH = "storage_path";
|
||||
public static final String INSTANT_UPLOAD_PATH = "instant_upload_path";
|
||||
public static final String INSTANT_VIDEO_UPLOAD_PATH = "instant_video_upload_path";
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
|
@ -128,7 +140,6 @@ public class Preferences extends PreferenceActivity {
|
|||
getWindow().getDecorView().findViewById(actionBarTitleId).
|
||||
setContentDescription(getString(R.string.actionbar_settings));
|
||||
}
|
||||
|
||||
// Load package info
|
||||
String temp;
|
||||
try {
|
||||
|
@ -137,9 +148,9 @@ public class Preferences extends PreferenceActivity {
|
|||
} catch (NameNotFoundException e) {
|
||||
temp = "";
|
||||
Log_OC.e(TAG, "Error while showing about dialog", e);
|
||||
}
|
||||
}
|
||||
final String appVersion = temp;
|
||||
|
||||
|
||||
// Register context menu for list of preferences.
|
||||
registerForContextMenu(getListView());
|
||||
|
||||
|
@ -206,7 +217,7 @@ public class Preferences extends PreferenceActivity {
|
|||
}
|
||||
|
||||
boolean helpEnabled = getResources().getBoolean(R.bool.help_enabled);
|
||||
Preference pHelp = findPreference("help");
|
||||
Preference pHelp = findPreference("help");
|
||||
if (pHelp != null ){
|
||||
if (helpEnabled) {
|
||||
pHelp.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
|
@ -225,7 +236,7 @@ public class Preferences extends PreferenceActivity {
|
|||
preferenceCategory.removePreference(pHelp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
boolean recommendEnabled = getResources().getBoolean(R.bool.recommend_enabled);
|
||||
Preference pRecommend = findPreference("recommend");
|
||||
if (pRecommend != null){
|
||||
|
@ -234,11 +245,11 @@ public class Preferences extends PreferenceActivity {
|
|||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_SENDTO);
|
||||
Intent intent = new Intent(Intent.ACTION_SENDTO);
|
||||
intent.setType("text/plain");
|
||||
intent.setData(Uri.parse(getString(R.string.mail_recommend)));
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
intent.setData(Uri.parse(getString(R.string.mail_recommend)));
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
String appName = getString(R.string.app_name);
|
||||
String downloadUrl = getString(R.string.url_app_download);
|
||||
|
||||
|
@ -247,12 +258,12 @@ public class Preferences extends PreferenceActivity {
|
|||
appName);
|
||||
String recommendText = String.format(getString(R.string.recommend_text),
|
||||
appName, downloadUrl);
|
||||
|
||||
|
||||
intent.putExtra(Intent.EXTRA_SUBJECT, recommendSubject);
|
||||
intent.putExtra(Intent.EXTRA_TEXT, recommendText);
|
||||
startActivity(intent);
|
||||
|
||||
return(true);
|
||||
return true;
|
||||
|
||||
}
|
||||
});
|
||||
|
@ -260,7 +271,7 @@ public class Preferences extends PreferenceActivity {
|
|||
preferenceCategory.removePreference(pRecommend);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
boolean feedbackEnabled = getResources().getBoolean(R.bool.feedback_enabled);
|
||||
Preference pFeedback = findPreference("feedback");
|
||||
if (pFeedback != null){
|
||||
|
@ -274,11 +285,11 @@ public class Preferences extends PreferenceActivity {
|
|||
Intent intent = new Intent(Intent.ACTION_SENDTO);
|
||||
intent.setType("text/plain");
|
||||
intent.putExtra(Intent.EXTRA_SUBJECT, feedback);
|
||||
|
||||
intent.setData(Uri.parse(feedbackMail));
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
intent.setData(Uri.parse(feedbackMail));
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
startActivity(intent);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
@ -327,7 +338,38 @@ public class Preferences extends PreferenceActivity {
|
|||
}
|
||||
}
|
||||
|
||||
mPrefInstantUploadPath = findPreference("instant_upload_path");
|
||||
mPrefStoragePath = (ListPreference) findPreference(Keys.STORAGE_PATH);
|
||||
if (mPrefStoragePath != null) {
|
||||
StoragePoint[] storageOptions = DataStorageProvider.getInstance().getAvailableStoragePoints();
|
||||
String[] entries = new String[storageOptions.length];
|
||||
String[] values = new String[storageOptions.length];
|
||||
for (int i = 0; i < storageOptions.length; ++i) {
|
||||
entries[i] = storageOptions[i].getDescription();
|
||||
values[i] = storageOptions[i].getPath();
|
||||
}
|
||||
mPrefStoragePath.setEntries(entries);
|
||||
mPrefStoragePath.setEntryValues(values);
|
||||
|
||||
mPrefStoragePath.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
String newPath = (String)newValue;
|
||||
if (mStoragePath.equals(newPath))
|
||||
return true;
|
||||
|
||||
StorageMigration storageMigration = new StorageMigration(Preferences.this, mStoragePath, newPath);
|
||||
|
||||
storageMigration.setStorageMigrationProgressListener(Preferences.this);
|
||||
|
||||
storageMigration.migrate();
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
mPrefInstantUploadPath = (PreferenceWithLongSummary)findPreference(Keys.INSTANT_UPLOAD_PATH);
|
||||
if (mPrefInstantUploadPath != null){
|
||||
|
||||
mPrefInstantUploadPath.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
|
@ -343,7 +385,7 @@ public class Preferences extends PreferenceActivity {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
mPrefInstantUploadCategory =
|
||||
(PreferenceCategory) findPreference("instant_uploading_category");
|
||||
|
||||
|
@ -351,11 +393,11 @@ public class Preferences extends PreferenceActivity {
|
|||
mPrefInstantUploadPathWiFi = findPreference("instant_upload_on_wifi");
|
||||
mPrefInstantPictureUploadOnlyOnCharging = findPreference("instant_upload_on_charging");
|
||||
mPrefInstantUpload = findPreference("instant_uploading");
|
||||
|
||||
|
||||
toggleInstantPictureOptions(((CheckBoxPreference) mPrefInstantUpload).isChecked());
|
||||
|
||||
|
||||
mPrefInstantUpload.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
toggleInstantPictureOptions((Boolean) newValue);
|
||||
|
@ -365,8 +407,8 @@ public class Preferences extends PreferenceActivity {
|
|||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
mPrefInstantVideoUploadPath = findPreference("instant_video_upload_path");
|
||||
|
||||
mPrefInstantVideoUploadPath = findPreference(Keys.INSTANT_VIDEO_UPLOAD_PATH);
|
||||
if (mPrefInstantVideoUploadPath != null){
|
||||
|
||||
mPrefInstantVideoUploadPath.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
|
@ -389,7 +431,7 @@ public class Preferences extends PreferenceActivity {
|
|||
mPrefInstantVideoUpload = findPreference("instant_video_uploading");
|
||||
mPrefInstantVideoUploadOnlyOnCharging = findPreference("instant_video_upload_on_charging");
|
||||
toggleInstantVideoOptions(((CheckBoxPreference) mPrefInstantVideoUpload).isChecked());
|
||||
|
||||
|
||||
mPrefInstantVideoUpload.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
|
||||
|
||||
@Override
|
||||
|
@ -408,7 +450,7 @@ public class Preferences extends PreferenceActivity {
|
|||
((CheckBoxPreference)mPrefInstantUpload).isChecked());
|
||||
|
||||
/* About App */
|
||||
pAboutApp = (Preference) findPreference("about_app");
|
||||
pAboutApp = findPreference("about_app");
|
||||
if (pAboutApp != null) {
|
||||
pAboutApp.setTitle(String.format(getString(R.string.about_android),
|
||||
getString(R.string.app_name)));
|
||||
|
@ -416,6 +458,7 @@ public class Preferences extends PreferenceActivity {
|
|||
}
|
||||
|
||||
loadInstantUploadPath();
|
||||
loadStoragePath();
|
||||
loadInstantUploadVideoPath();
|
||||
}
|
||||
|
||||
|
@ -488,7 +531,7 @@ public class Preferences extends PreferenceActivity {
|
|||
mPrefInstantUploadCategory.removePreference(mPrefInstantPictureUploadOnlyOnCharging);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void toggleInstantVideoOptions(Boolean value){
|
||||
if (value){
|
||||
mPrefInstantUploadCategory.addPreference(mPrefInstantVideoUploadPathWiFi);
|
||||
|
@ -550,8 +593,7 @@ public class Preferences extends PreferenceActivity {
|
|||
|
||||
if (requestCode == ACTION_SELECT_UPLOAD_PATH && resultCode == RESULT_OK){
|
||||
|
||||
OCFile folderToUpload =
|
||||
(OCFile) data.getParcelableExtra(UploadPathActivity.EXTRA_FOLDER);
|
||||
OCFile folderToUpload = data.getParcelableExtra(UploadPathActivity.EXTRA_FOLDER);
|
||||
|
||||
mUploadPath = folderToUpload.getRemotePath();
|
||||
|
||||
|
@ -562,10 +604,9 @@ public class Preferences extends PreferenceActivity {
|
|||
|
||||
saveInstantUploadPathOnPreferences();
|
||||
|
||||
} else if (requestCode == ACTION_SELECT_UPLOAD_VIDEO_PATH && resultCode == RESULT_OK){
|
||||
} else if (requestCode == ACTION_SELECT_UPLOAD_VIDEO_PATH && resultCode == RESULT_OK) {
|
||||
|
||||
OCFile folderToUploadVideo =
|
||||
(OCFile) data.getParcelableExtra(UploadPathActivity.EXTRA_FOLDER);
|
||||
OCFile folderToUploadVideo = data.getParcelableExtra(UploadPathActivity.EXTRA_FOLDER);
|
||||
|
||||
mUploadVideoPath = folderToUploadVideo.getRemotePath();
|
||||
|
||||
|
@ -599,8 +640,7 @@ public class Preferences extends PreferenceActivity {
|
|||
Toast.makeText(this, R.string.pass_code_removed, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
} else if (requestCode == ACTION_REQUEST_CODE_DAVDROID_SETUP && resultCode == RESULT_OK) {
|
||||
Toast.makeText(this, R.string.prefs_calendar_contacts_sync_setup_successful, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
Toast.makeText(this, R.string.prefs_calendar_contacts_sync_setup_successful, Toast.LENGTH_LONG).show(); }
|
||||
}
|
||||
|
||||
public ActionBar getSupportActionBar() {
|
||||
|
@ -687,10 +727,38 @@ public class Preferences extends PreferenceActivity {
|
|||
private void loadInstantUploadPath() {
|
||||
SharedPreferences appPrefs =
|
||||
PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||
mUploadPath = appPrefs.getString("instant_upload_path", getString(R.string.instant_upload_path));
|
||||
mUploadPath = appPrefs.getString(Keys.INSTANT_UPLOAD_PATH, getString(R.string.instant_upload_path));
|
||||
mPrefInstantUploadPath.setSummary(mUploadPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save storage path
|
||||
*/
|
||||
private void saveStoragePath(String newStoragePath) {
|
||||
SharedPreferences appPrefs =
|
||||
PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||
mStoragePath = newStoragePath;
|
||||
MainApp.setStoragePath(mStoragePath);
|
||||
SharedPreferences.Editor editor = appPrefs.edit();
|
||||
editor.putString(Keys.STORAGE_PATH, mStoragePath);
|
||||
editor.commit();
|
||||
String storageDescription = DataStorageProvider.getInstance().getStorageDescriptionByPath(mStoragePath);
|
||||
mPrefStoragePath.setSummary(storageDescription);
|
||||
mPrefStoragePath.setValue(newStoragePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load storage path set on preferences
|
||||
*/
|
||||
private void loadStoragePath() {
|
||||
SharedPreferences appPrefs =
|
||||
PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||
mStoragePath = appPrefs.getString(Keys.STORAGE_PATH, Environment.getExternalStorageDirectory()
|
||||
.getAbsolutePath());
|
||||
String storageDescription = DataStorageProvider.getInstance().getStorageDescriptionByPath(mStoragePath);
|
||||
mPrefStoragePath.setSummary(storageDescription);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the "Instant Upload Path" on preferences
|
||||
*/
|
||||
|
@ -698,7 +766,7 @@ public class Preferences extends PreferenceActivity {
|
|||
SharedPreferences appPrefs =
|
||||
PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||
SharedPreferences.Editor editor = appPrefs.edit();
|
||||
editor.putString("instant_upload_path", mUploadPath);
|
||||
editor.putString(Keys.INSTANT_UPLOAD_PATH, mUploadPath);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
|
@ -719,7 +787,19 @@ public class Preferences extends PreferenceActivity {
|
|||
SharedPreferences appPrefs =
|
||||
PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||
SharedPreferences.Editor editor = appPrefs.edit();
|
||||
editor.putString("instant_video_upload_path", mUploadVideoPath);
|
||||
editor.putString(Keys.INSTANT_VIDEO_UPLOAD_PATH, mUploadVideoPath);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStorageMigrationFinished(String storagePath, boolean succeed) {
|
||||
if (succeed)
|
||||
saveStoragePath(storagePath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancelMigration() {
|
||||
// Migration was canceled so we don't do anything
|
||||
}
|
||||
|
||||
}
|
||||
|
|
424
src/com/owncloud/android/ui/activity/StorageMigration.java
Normal file
424
src/com/owncloud/android/ui/activity/StorageMigration.java
Normal file
|
@ -0,0 +1,424 @@
|
|||
/**
|
||||
* Nextcloud Android client application
|
||||
*
|
||||
* @author Bartosz Przybylski
|
||||
* Copyright (C) 2016 Bartosz Przybylski
|
||||
* Copyright (C) 2016 Nextcloud
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.owncloud.android.ui.activity;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.app.ProgressDialog;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.os.AsyncTask;
|
||||
import android.view.View;
|
||||
|
||||
import com.owncloud.android.MainApp;
|
||||
import com.owncloud.android.R;
|
||||
import com.owncloud.android.datamodel.FileDataStorageManager;
|
||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||
import com.owncloud.android.utils.FileStorageUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* @author Bartosz Przybylski
|
||||
*/
|
||||
public class StorageMigration {
|
||||
private static final String TAG = StorageMigration.class.getName();
|
||||
|
||||
public interface StorageMigrationProgressListener {
|
||||
void onStorageMigrationFinished(String storagePath, boolean succeed);
|
||||
void onCancelMigration();
|
||||
}
|
||||
|
||||
private ProgressDialog mProgressDialog;
|
||||
private Context mContext;
|
||||
private String mSourceStoragePath;
|
||||
private String mTargetStoragePath;
|
||||
|
||||
private StorageMigrationProgressListener mListener;
|
||||
|
||||
public StorageMigration(Context context, String sourcePath, String targetPath) {
|
||||
mContext = context;
|
||||
mSourceStoragePath = sourcePath;
|
||||
mTargetStoragePath = targetPath;
|
||||
}
|
||||
|
||||
public void setStorageMigrationProgressListener(StorageMigrationProgressListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
public void migrate() {
|
||||
if (storageFolderAlreadyExists())
|
||||
askToOverride();
|
||||
else {
|
||||
ProgressDialog progressDialog = createMigrationProgressDialog();
|
||||
progressDialog.show();
|
||||
new FileMigrationTask(
|
||||
mContext,
|
||||
mSourceStoragePath,
|
||||
mTargetStoragePath,
|
||||
progressDialog,
|
||||
mListener).execute();
|
||||
|
||||
progressDialog.getButton(progressDialog.BUTTON_POSITIVE).setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean storageFolderAlreadyExists() {
|
||||
File f = new File(mTargetStoragePath, MainApp.getDataFolder());
|
||||
return f.exists() && f.isDirectory();
|
||||
}
|
||||
|
||||
private void askToOverride() {
|
||||
|
||||
new AlertDialog.Builder(mContext)
|
||||
.setMessage(R.string.file_migration_directory_already_exists)
|
||||
.setCancelable(true)
|
||||
.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialogInterface) {
|
||||
if (mListener != null)
|
||||
mListener.onCancelMigration();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.common_cancel, new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
if (mListener != null)
|
||||
mListener.onCancelMigration();
|
||||
}
|
||||
})
|
||||
.setNeutralButton(R.string.file_migration_use_data_folder, new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
ProgressDialog progressDialog = createMigrationProgressDialog();
|
||||
progressDialog.show();
|
||||
new StoragePathSwitchTask(
|
||||
mContext,
|
||||
mSourceStoragePath,
|
||||
mTargetStoragePath,
|
||||
progressDialog,
|
||||
mListener).execute();
|
||||
|
||||
progressDialog.getButton(ProgressDialog.BUTTON_POSITIVE).setVisibility(View.GONE);
|
||||
|
||||
}
|
||||
})
|
||||
.setPositiveButton(R.string.file_migration_override_data_folder, new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
ProgressDialog progressDialog = createMigrationProgressDialog();
|
||||
progressDialog.show();
|
||||
new FileMigrationTask(
|
||||
mContext,
|
||||
mSourceStoragePath,
|
||||
mTargetStoragePath,
|
||||
progressDialog,
|
||||
mListener).execute();
|
||||
|
||||
progressDialog.getButton(ProgressDialog.BUTTON_POSITIVE).setVisibility(View.GONE);
|
||||
}
|
||||
})
|
||||
.create()
|
||||
.show();
|
||||
}
|
||||
|
||||
private ProgressDialog createMigrationProgressDialog() {
|
||||
ProgressDialog progressDialog = new ProgressDialog(mContext);
|
||||
progressDialog.setCancelable(false);
|
||||
progressDialog.setTitle(R.string.file_migration_dialog_title);
|
||||
progressDialog.setMessage(mContext.getString(R.string.file_migration_preparing));
|
||||
progressDialog.setButton(
|
||||
ProgressDialog.BUTTON_POSITIVE,
|
||||
mContext.getString(R.string.drawer_close),
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
dialogInterface.dismiss();
|
||||
}
|
||||
});
|
||||
return progressDialog;
|
||||
}
|
||||
|
||||
abstract static private class FileMigrationTaskBase extends AsyncTask<Void, Integer, Integer> {
|
||||
protected String mStorageSource;
|
||||
protected String mStorageTarget;
|
||||
protected Context mContext;
|
||||
protected ProgressDialog mProgressDialog;
|
||||
protected StorageMigrationProgressListener mListener;
|
||||
|
||||
protected String mAuthority;
|
||||
protected Account[] mOcAccounts;
|
||||
|
||||
public FileMigrationTaskBase(Context context,
|
||||
String source,
|
||||
String target,
|
||||
ProgressDialog progressDialog,
|
||||
StorageMigrationProgressListener listener) {
|
||||
mContext = context;
|
||||
mStorageSource = source;
|
||||
mStorageTarget = target;
|
||||
mProgressDialog = progressDialog;
|
||||
mListener = listener;
|
||||
|
||||
mAuthority = mContext.getString(R.string.authority);
|
||||
mOcAccounts = AccountManager.get(mContext).getAccountsByType(MainApp.getAccountType());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate(Integer... progress) {
|
||||
if (progress.length > 1 && progress[0] != 0) {
|
||||
mProgressDialog.setMessage(mContext.getString(progress[0]));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Integer code) {
|
||||
if (code != 0) {
|
||||
mProgressDialog.setMessage(mContext.getString(code));
|
||||
} else {
|
||||
mProgressDialog.setMessage(mContext.getString(R.string.file_migration_ok_finished));
|
||||
}
|
||||
|
||||
boolean succeed = code == 0;
|
||||
if (succeed) {
|
||||
mProgressDialog.hide();
|
||||
} else {
|
||||
mProgressDialog.getButton(ProgressDialog.BUTTON_POSITIVE).setVisibility(View.VISIBLE);
|
||||
mProgressDialog.setIndeterminateDrawable(mContext.getResources().getDrawable(R.drawable.image_fail));
|
||||
}
|
||||
|
||||
if (mListener != null) {
|
||||
mListener.onStorageMigrationFinished(succeed ? mStorageTarget : mStorageSource, succeed);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean[] saveAccountsSyncStatus() {
|
||||
boolean[] syncs = new boolean[mOcAccounts.length];
|
||||
for (int i = 0; i < mOcAccounts.length; ++i) {
|
||||
syncs[i] = ContentResolver.getSyncAutomatically(mOcAccounts[i], mAuthority);
|
||||
}
|
||||
return syncs;
|
||||
}
|
||||
|
||||
protected void stopAccountsSyncing() {
|
||||
for (int i = 0; i < mOcAccounts.length; ++i) {
|
||||
ContentResolver.setSyncAutomatically(mOcAccounts[i], mAuthority, false);
|
||||
}
|
||||
}
|
||||
|
||||
protected void waitForUnfinishedSynchronizations() {
|
||||
for (int i = 0; i < mOcAccounts.length; ++i) {
|
||||
while (ContentResolver.isSyncActive(mOcAccounts[i], mAuthority)) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
Log_OC.w(TAG, "Thread interrupted while waiting for account to end syncing");
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void restoreAccountsSyncStatus(boolean oldSync[]) {
|
||||
for (int i = 0; i < mOcAccounts.length; ++i) {
|
||||
ContentResolver.setSyncAutomatically(mOcAccounts[i], mAuthority, oldSync[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static private class StoragePathSwitchTask extends FileMigrationTaskBase {
|
||||
|
||||
public StoragePathSwitchTask(Context context,
|
||||
String source,
|
||||
String target,
|
||||
ProgressDialog progressDialog,
|
||||
StorageMigrationProgressListener listener) {
|
||||
super(context, source, target, progressDialog, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer doInBackground(Void... voids) {
|
||||
publishProgress(R.string.file_migration_preparing);
|
||||
|
||||
Log_OC.stopLogging();
|
||||
boolean[] syncStates = new boolean[0];
|
||||
try {
|
||||
publishProgress(R.string.file_migration_saving_accounts_configuration);
|
||||
syncStates = saveAccountsSyncStatus();
|
||||
|
||||
publishProgress(R.string.file_migration_waiting_for_unfinished_sync);
|
||||
stopAccountsSyncing();
|
||||
waitForUnfinishedSynchronizations();
|
||||
} finally {
|
||||
publishProgress(R.string.file_migration_restoring_accounts_configuration);
|
||||
restoreAccountsSyncStatus(syncStates);
|
||||
}
|
||||
Log_OC.startLogging(mStorageTarget);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static private class FileMigrationTask extends FileMigrationTaskBase {
|
||||
private class MigrationException extends Exception {
|
||||
private int mResId;
|
||||
|
||||
MigrationException(int resId) {
|
||||
super();
|
||||
this.mResId = resId;
|
||||
}
|
||||
|
||||
int getResId() { return mResId; }
|
||||
}
|
||||
|
||||
public FileMigrationTask(Context context,
|
||||
String source,
|
||||
String target,
|
||||
ProgressDialog progressDialog,
|
||||
StorageMigrationProgressListener listener) {
|
||||
super(context, source, target, progressDialog, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer doInBackground(Void... args) {
|
||||
publishProgress(R.string.file_migration_preparing);
|
||||
Log_OC.stopLogging();
|
||||
|
||||
boolean[] syncState = new boolean[0];
|
||||
|
||||
try {
|
||||
File dstFile = new File(mStorageTarget + File.separator + MainApp.getDataFolder());
|
||||
deleteRecursive(dstFile);
|
||||
dstFile.delete();
|
||||
|
||||
File srcFile = new File(mStorageSource + File.separator + MainApp.getDataFolder());
|
||||
srcFile.mkdirs();
|
||||
|
||||
publishProgress(R.string.file_migration_checking_destination);
|
||||
|
||||
checkDestinationAvailability();
|
||||
|
||||
publishProgress(R.string.file_migration_saving_accounts_configuration);
|
||||
syncState = saveAccountsSyncStatus();
|
||||
|
||||
publishProgress(R.string.file_migration_waiting_for_unfinished_sync);
|
||||
stopAccountsSyncing();
|
||||
waitForUnfinishedSynchronizations();
|
||||
|
||||
publishProgress(R.string.file_migration_migrating);
|
||||
copyFiles();
|
||||
|
||||
publishProgress(R.string.file_migration_updating_index);
|
||||
updateIndex(mContext);
|
||||
|
||||
publishProgress(R.string.file_migration_cleaning);
|
||||
cleanup();
|
||||
|
||||
} catch (MigrationException e) {
|
||||
rollback();
|
||||
Log_OC.startLogging(mStorageSource);
|
||||
return e.getResId();
|
||||
} finally {
|
||||
publishProgress(R.string.file_migration_restoring_accounts_configuration);
|
||||
restoreAccountsSyncStatus(syncState);
|
||||
}
|
||||
|
||||
Log_OC.startLogging(mStorageTarget);
|
||||
publishProgress(R.string.file_migration_ok_finished);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void checkDestinationAvailability() throws MigrationException {
|
||||
File srcFile = new File(mStorageSource);
|
||||
File dstFile = new File(mStorageTarget);
|
||||
|
||||
if (!dstFile.canRead() || !srcFile.canRead())
|
||||
throw new MigrationException(R.string.file_migration_failed_not_readable);
|
||||
|
||||
if (!dstFile.canWrite() || !srcFile.canWrite())
|
||||
throw new MigrationException(R.string.file_migration_failed_not_writable);
|
||||
|
||||
if (new File(dstFile, MainApp.getDataFolder()).exists())
|
||||
throw new MigrationException(R.string.file_migration_failed_dir_already_exists);
|
||||
|
||||
if (dstFile.getFreeSpace() < FileStorageUtils.getFolderSize(new File(srcFile, MainApp.getDataFolder())))
|
||||
throw new MigrationException(R.string.file_migration_failed_not_enough_space);
|
||||
}
|
||||
|
||||
void copyFiles() throws MigrationException {
|
||||
File srcFile = new File(mStorageSource + File.separator + MainApp.getDataFolder());
|
||||
File dstFile = new File(mStorageTarget + File.separator + MainApp.getDataFolder());
|
||||
|
||||
copyDirs(srcFile, dstFile);
|
||||
}
|
||||
|
||||
void copyDirs(File src, File dst) throws MigrationException {
|
||||
if (!dst.mkdirs())
|
||||
throw new MigrationException(R.string.file_migration_failed_while_coping);
|
||||
|
||||
for (File f : src.listFiles()) {
|
||||
if (f.isDirectory())
|
||||
copyDirs(f, new File(dst, f.getName()));
|
||||
else if (!FileStorageUtils.copyFile(f, new File(dst, f.getName())))
|
||||
throw new MigrationException(R.string.file_migration_failed_while_coping);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void updateIndex(Context context) throws MigrationException {
|
||||
FileDataStorageManager manager = new FileDataStorageManager(null, context.getContentResolver());
|
||||
|
||||
try {
|
||||
manager.migrateStoredFiles(mStorageSource, mStorageTarget);
|
||||
} catch (Exception e) {
|
||||
Log_OC.e(TAG,e.getMessage(),e);
|
||||
throw new MigrationException(R.string.file_migration_failed_while_updating_index);
|
||||
}
|
||||
}
|
||||
|
||||
void cleanup() {
|
||||
File srcFile = new File(mStorageSource + File.separator + MainApp.getDataFolder());
|
||||
if (!deleteRecursive(srcFile))
|
||||
Log_OC.w(TAG, "Migration cleanup step failed");
|
||||
srcFile.delete();
|
||||
}
|
||||
|
||||
boolean deleteRecursive(File f) {
|
||||
boolean res = true;
|
||||
if (f.isDirectory())
|
||||
for (File c : f.listFiles())
|
||||
res = deleteRecursive(c) && res;
|
||||
return f.delete() && res;
|
||||
}
|
||||
|
||||
void rollback() {
|
||||
File dstFile = new File(mStorageTarget + File.separator + MainApp.getDataFolder());
|
||||
if (dstFile.exists())
|
||||
if (!dstFile.delete())
|
||||
Log_OC.w(TAG, "Rollback step failed");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -68,7 +68,7 @@ public class UploadFilesActivity extends FileActivity implements
|
|||
private boolean mSelectAll = false;
|
||||
private LocalFileListFragment mFileListFragment;
|
||||
private Button mCancelBtn;
|
||||
private Button mUploadBtn;
|
||||
protected Button mUploadBtn;
|
||||
private Spinner mBehaviourSpinner;
|
||||
private Account mAccountOnCreation;
|
||||
private DialogFragment mCurrentDialog;
|
||||
|
@ -81,7 +81,7 @@ public class UploadFilesActivity extends FileActivity implements
|
|||
public static final int RESULT_OK_AND_DO_NOTHING = 2;
|
||||
public static final int RESULT_OK_AND_DELETE = 3;
|
||||
|
||||
private static final String KEY_DIRECTORY_PATH =
|
||||
public static final String KEY_DIRECTORY_PATH =
|
||||
UploadFilesActivity.class.getCanonicalName() + ".KEY_DIRECTORY_PATH";
|
||||
private static final String KEY_ALL_SELECTED =
|
||||
UploadFilesActivity.class.getCanonicalName() + ".KEY_ALL_SELECTED";
|
||||
|
|
|
@ -21,12 +21,10 @@
|
|||
package com.owncloud.android.utils;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.os.StatFs;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
|
@ -37,6 +35,11 @@ import com.owncloud.android.lib.common.utils.Log_OC;
|
|||
import com.owncloud.android.lib.resources.files.RemoteFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
@ -79,8 +82,11 @@ public class FileStorageUtils {
|
|||
* Get local owncloud storage path for accountName.
|
||||
*/
|
||||
public static final String getSavePath(String accountName) {
|
||||
File sdCard = Environment.getExternalStorageDirectory();
|
||||
return sdCard.getAbsolutePath() + "/" + MainApp.getDataFolder() + "/" + Uri.encode(accountName, "@");
|
||||
return MainApp.getStoragePath()
|
||||
+ File.separator
|
||||
+ MainApp.getDataFolder()
|
||||
+ File.separator
|
||||
+ Uri.encode(accountName, "@");
|
||||
// URL encoding is an 'easy fix' to overcome that NTFS and FAT32 don't allow ":" in file names,
|
||||
// that can be in the accountName since 0.1.190B
|
||||
}
|
||||
|
@ -98,32 +104,30 @@ public class FileStorageUtils {
|
|||
* Get absolute path to tmp folder inside datafolder in sd-card for given accountName.
|
||||
*/
|
||||
public static final String getTemporalPath(String accountName) {
|
||||
File sdCard = Environment.getExternalStorageDirectory();
|
||||
return sdCard.getAbsolutePath() + "/" + MainApp.getDataFolder() + "/tmp/" + Uri.encode(accountName, "@");
|
||||
// URL encoding is an 'easy fix' to overcome that NTFS and FAT32 don't allow ":" in file names,
|
||||
// that can be in the accountName since 0.1.190B
|
||||
return MainApp.getStoragePath()
|
||||
+ File.separator
|
||||
+ MainApp.getDataFolder()
|
||||
+ File.separator
|
||||
+ "tmp"
|
||||
+ File.separator
|
||||
+ Uri.encode(accountName, "@");
|
||||
// URL encoding is an 'easy fix' to overcome that NTFS and FAT32 don't allow ":" in file names,
|
||||
// that can be in the accountName since 0.1.190B
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimistic number of bytes available on sd-card. accountName is ignored.
|
||||
*
|
||||
* @param accountName not used. can thus be null.
|
||||
* @return Optimistic number of available bytes (can be less)
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
public static final long getUsableSpace(String accountName) {
|
||||
File savePath = Environment.getExternalStorageDirectory();
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD) {
|
||||
return savePath.getUsableSpace();
|
||||
|
||||
} else {
|
||||
StatFs stats = new StatFs(savePath.getAbsolutePath());
|
||||
return stats.getAvailableBlocks() * stats.getBlockSize();
|
||||
}
|
||||
|
||||
File savePath = new File(MainApp.getStoragePath());
|
||||
return savePath.getUsableSpace();
|
||||
}
|
||||
|
||||
public static final String getLogPath() {
|
||||
return Environment.getExternalStorageDirectory() + File.separator + MainApp.getDataFolder() + File.separator + "log";
|
||||
return MainApp.getStoragePath() + File.separator + MainApp.getDataFolder() + File.separator + "log";
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -441,13 +445,11 @@ public class FileStorageUtils {
|
|||
public static long getFolderSize(File dir) {
|
||||
if (dir.exists()) {
|
||||
long result = 0;
|
||||
File[] fileList = dir.listFiles();
|
||||
for(int i = 0; i < fileList.length; i++) {
|
||||
if(fileList[i].isDirectory()) {
|
||||
result += getFolderSize(fileList[i]);
|
||||
} else {
|
||||
result += fileList[i].length();
|
||||
}
|
||||
for (File f : dir.listFiles()) {
|
||||
if (f.isDirectory())
|
||||
result += getFolderSize(f);
|
||||
else
|
||||
result += f.length();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -497,4 +499,36 @@ public class FileStorageUtils {
|
|||
}
|
||||
}
|
||||
|
||||
public static boolean copyFile(File src, File target) {
|
||||
boolean ret = true;
|
||||
|
||||
InputStream in = null;
|
||||
OutputStream out = null;
|
||||
|
||||
try {
|
||||
in = new FileInputStream(src);
|
||||
out = new FileOutputStream(target);
|
||||
byte[] buf = new byte[1024];
|
||||
int len;
|
||||
while ((len = in.read(buf)) > 0) {
|
||||
out.write(buf, 0, len);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
ret = false;
|
||||
} finally {
|
||||
if (in != null) try {
|
||||
in.close();
|
||||
} catch (IOException e) {
|
||||
Log_OC.e(TAG, "Error closing input stream during copy", e);
|
||||
}
|
||||
if (out != null) try {
|
||||
out.close();
|
||||
} catch (IOException e) {
|
||||
Log_OC.e(TAG, "Error closing output stream during copy", e);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue