387 lines
15 KiB
Java
387 lines
15 KiB
Java
/*
|
|
* Nextcloud Android client application
|
|
*
|
|
* @author Andy Scherzinger
|
|
* Copyright (C) 2016 Andy Scherzinger
|
|
* 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.datamodel;
|
|
|
|
import android.accounts.Account;
|
|
import android.content.ContentResolver;
|
|
import android.content.ContentValues;
|
|
import android.content.Context;
|
|
import android.database.Cursor;
|
|
import android.net.Uri;
|
|
|
|
import com.nextcloud.client.core.Clock;
|
|
import com.nextcloud.client.preferences.AppPreferences;
|
|
import com.nextcloud.client.preferences.AppPreferencesImpl;
|
|
import com.owncloud.android.db.ProviderMeta;
|
|
import com.owncloud.android.lib.common.utils.Log_OC;
|
|
|
|
import java.io.File;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Observable;
|
|
|
|
import androidx.annotation.NonNull;
|
|
|
|
import static com.owncloud.android.datamodel.OCFile.PATH_SEPARATOR;
|
|
|
|
/**
|
|
* Database provider for handling the persistence aspects of {@link SyncedFolder}s.
|
|
*/
|
|
public class SyncedFolderProvider extends Observable {
|
|
static private final String TAG = SyncedFolderProvider.class.getSimpleName();
|
|
|
|
private final ContentResolver mContentResolver;
|
|
private final AppPreferences preferences;
|
|
private final Clock clock;
|
|
|
|
/**
|
|
* constructor.
|
|
*
|
|
* @param contentResolver the ContentResolver to work with.
|
|
*/
|
|
public SyncedFolderProvider(ContentResolver contentResolver, AppPreferences preferences, Clock clock) {
|
|
if (contentResolver == null) {
|
|
throw new IllegalArgumentException("Cannot create an instance with a NULL contentResolver");
|
|
}
|
|
mContentResolver = contentResolver;
|
|
this.preferences = preferences;
|
|
this.clock = clock;
|
|
}
|
|
|
|
/**
|
|
* Stores a synced folder object in database.
|
|
*
|
|
* @param syncedFolder synced folder to store
|
|
* @return synced folder id, -1 if the insert process fails.
|
|
*/
|
|
public long storeSyncedFolder(SyncedFolder syncedFolder) {
|
|
Log_OC.v(TAG, "Inserting " + syncedFolder.getLocalPath() + " with enabled=" + syncedFolder.isEnabled());
|
|
|
|
ContentValues cv = createContentValuesFromSyncedFolder(syncedFolder);
|
|
|
|
Uri result = mContentResolver.insert(ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS, cv);
|
|
|
|
if (result != null) {
|
|
return Long.parseLong(result.getPathSegments().get(1));
|
|
} else {
|
|
Log_OC.e(TAG, "Failed to insert item " + syncedFolder.getLocalPath() + " into folder sync db.");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
public int countEnabledSyncedFolders() {
|
|
int count = 0;
|
|
Cursor cursor = mContentResolver.query(
|
|
ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS,
|
|
null,
|
|
ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ENABLED + " = ?",
|
|
new String[]{"1"},
|
|
null
|
|
);
|
|
|
|
if (cursor != null) {
|
|
count = cursor.getCount();
|
|
cursor.close();
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
/**
|
|
* get all synced folder entries.
|
|
*
|
|
* @return all synced folder entries, empty if none have been found
|
|
*/
|
|
public List<SyncedFolder> getSyncedFolders() {
|
|
Cursor cursor = mContentResolver.query(
|
|
ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS,
|
|
null,
|
|
"1=1",
|
|
null,
|
|
null
|
|
);
|
|
|
|
if (cursor != null) {
|
|
List<SyncedFolder> list = new ArrayList<>(cursor.getCount());
|
|
if (cursor.moveToFirst()) {
|
|
do {
|
|
SyncedFolder syncedFolder = createSyncedFolderFromCursor(cursor);
|
|
if (syncedFolder == null) {
|
|
Log_OC.e(TAG, "SyncedFolder could not be created from cursor");
|
|
} else {
|
|
list.add(cursor.getPosition(), syncedFolder);
|
|
}
|
|
} while (cursor.moveToNext());
|
|
|
|
}
|
|
cursor.close();
|
|
return list;
|
|
} else {
|
|
Log_OC.e(TAG, "DB error creating read all cursor for synced folders.");
|
|
}
|
|
|
|
return new ArrayList<>(0);
|
|
}
|
|
|
|
/**
|
|
* Update upload status of file uniquely referenced by id.
|
|
*
|
|
* @param id synced folder id.
|
|
* @param enabled new status.
|
|
* @return the number of rows updated.
|
|
*/
|
|
public int updateSyncedFolderEnabled(long id, Boolean enabled) {
|
|
Log_OC.v(TAG, "Storing synced folder id" + id + " with enabled=" + enabled);
|
|
|
|
int result = 0;
|
|
Cursor cursor = mContentResolver.query(
|
|
ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS,
|
|
null,
|
|
ProviderMeta.ProviderTableMeta._ID + "=?",
|
|
new String[]{String.valueOf(id)},
|
|
null
|
|
);
|
|
|
|
if (cursor != null && cursor.getCount() == 1) {
|
|
while (cursor.moveToNext()) {
|
|
// read sync folder object and update
|
|
SyncedFolder syncedFolder = createSyncedFolderFromCursor(cursor);
|
|
|
|
syncedFolder.setEnabled(enabled, clock.getCurrentTime());
|
|
|
|
// update sync folder object in db
|
|
result = updateSyncFolder(syncedFolder);
|
|
|
|
}
|
|
} else {
|
|
if (cursor == null) {
|
|
Log_OC.e(TAG, "Sync folder db cursor for ID=" + id + " in NULL.");
|
|
} else {
|
|
Log_OC.e(TAG, cursor.getCount() + " items for id=" + id + " available in sync folder database. " +
|
|
"Expected 1. Failed to update sync folder db.");
|
|
}
|
|
}
|
|
|
|
if (cursor != null) {
|
|
cursor.close();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public SyncedFolder findByLocalPathAndAccount(String localPath, Account account) {
|
|
SyncedFolder result = null;
|
|
Cursor cursor = mContentResolver.query(
|
|
ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS,
|
|
null,
|
|
ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_LOCAL_PATH + " LIKE ? AND " +
|
|
ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ACCOUNT + " =? ",
|
|
new String[]{localPath + "%", account.name},
|
|
null
|
|
);
|
|
|
|
if (cursor != null && cursor.getCount() == 1) {
|
|
result = createSyncedFolderFromCursor(cursor);
|
|
} else {
|
|
if (cursor == null) {
|
|
Log_OC.e(TAG, "Sync folder db cursor for local path=" + localPath + " in NULL.");
|
|
} else {
|
|
Log_OC.e(TAG, cursor.getCount() + " items for local path=" + localPath
|
|
+ " available in sync folder db. Expected 1. Failed to update sync folder db.");
|
|
}
|
|
}
|
|
|
|
if (cursor != null) {
|
|
cursor.close();
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
/**
|
|
* Delete all synced folders for an account
|
|
*
|
|
* @param account whose synced folders should be deleted
|
|
*/
|
|
public int deleteSyncFoldersForAccount(Account account) {
|
|
return mContentResolver.delete(
|
|
ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS,
|
|
ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ACCOUNT + " = ?",
|
|
new String[]{String.valueOf(account.name)}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Delete a synced folder from the db
|
|
*
|
|
* @param id for the synced folder.
|
|
*/
|
|
private int deleteSyncFolderWithId(long id) {
|
|
return mContentResolver.delete(
|
|
ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS,
|
|
ProviderMeta.ProviderTableMeta._ID + " = ?",
|
|
new String[]{String.valueOf(id)}
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Try to figure out if a path exists for synced folder, and if not, go one folder back
|
|
* Otherwise, delete the entry
|
|
*
|
|
* @param context the context.
|
|
*/
|
|
public void updateAutoUploadPaths(Context context) {
|
|
List<SyncedFolder> syncedFolders = getSyncedFolders();
|
|
for (SyncedFolder syncedFolder : syncedFolders) {
|
|
if (!new File(syncedFolder.getLocalPath()).exists()) {
|
|
String localPath = syncedFolder.getLocalPath();
|
|
if (localPath.endsWith(PATH_SEPARATOR)) {
|
|
localPath = localPath.substring(0, localPath.lastIndexOf('/'));
|
|
}
|
|
localPath = localPath.substring(0, localPath.lastIndexOf('/'));
|
|
if (new File(localPath).exists()) {
|
|
syncedFolder.setLocalPath(localPath);
|
|
updateSyncFolder(syncedFolder);
|
|
} else {
|
|
deleteSyncFolderWithId(syncedFolder.getId());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (context != null) {
|
|
AppPreferences preferences = AppPreferencesImpl.fromContext(context);
|
|
preferences.setAutoUploadPathsUpdateEnabled(true);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* delete any records of synchronized folders that are not within the given list of ids.
|
|
*
|
|
* @param ids the list of ids to be excluded from deletion.
|
|
* @return number of deleted records.
|
|
*/
|
|
public int deleteSyncedFoldersNotInList(List<Long> ids) {
|
|
int result = mContentResolver.delete(
|
|
ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS,
|
|
ProviderMeta.ProviderTableMeta._ID + " NOT IN (?)",
|
|
new String[]{String.valueOf(ids)}
|
|
);
|
|
|
|
if(result > 0) {
|
|
preferences.setLegacyClean(true);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* delete record of synchronized folder with the given id.
|
|
*/
|
|
public int deleteSyncedFolder(long id) {
|
|
return mContentResolver.delete(
|
|
ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS,
|
|
ProviderMeta.ProviderTableMeta._ID + " = ?",
|
|
new String[]{String.valueOf(id)}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* update given synced folder.
|
|
*
|
|
* @param syncedFolder the synced folder to be updated.
|
|
* @return the number of rows updated.
|
|
*/
|
|
public int updateSyncFolder(SyncedFolder syncedFolder) {
|
|
Log_OC.v(TAG, "Updating " + syncedFolder.getLocalPath() + " with enabled=" + syncedFolder.isEnabled());
|
|
|
|
ContentValues cv = createContentValuesFromSyncedFolder(syncedFolder);
|
|
|
|
return mContentResolver.update(
|
|
ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS,
|
|
cv,
|
|
ProviderMeta.ProviderTableMeta._ID + "=?",
|
|
new String[]{String.valueOf(syncedFolder.getId())}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* maps a cursor into a SyncedFolder object.
|
|
*
|
|
* @param cursor the db cursor
|
|
* @return the mapped SyncedFolder, null if cursor is null
|
|
*/
|
|
private SyncedFolder createSyncedFolderFromCursor(Cursor cursor) {
|
|
SyncedFolder syncedFolder = null;
|
|
if (cursor != null) {
|
|
long id = cursor.getLong(cursor.getColumnIndex(ProviderMeta.ProviderTableMeta._ID));
|
|
String localPath = cursor.getString(cursor.getColumnIndex(
|
|
ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_LOCAL_PATH));
|
|
String remotePath = cursor.getString(cursor.getColumnIndex(
|
|
ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_REMOTE_PATH));
|
|
Boolean wifiOnly = cursor.getInt(cursor.getColumnIndex(
|
|
ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_WIFI_ONLY)) == 1;
|
|
Boolean chargingOnly = cursor.getInt(cursor.getColumnIndex(
|
|
ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_CHARGING_ONLY)) == 1;
|
|
Boolean subfolderByDate = cursor.getInt(cursor.getColumnIndex(
|
|
ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE)) == 1;
|
|
String accountName = cursor.getString(cursor.getColumnIndex(
|
|
ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ACCOUNT));
|
|
Integer uploadAction = cursor.getInt(cursor.getColumnIndex(
|
|
ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_UPLOAD_ACTION));
|
|
Boolean enabled = cursor.getInt(cursor.getColumnIndex(
|
|
ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ENABLED)) == 1;
|
|
long enabledTimestampMs = cursor.getLong(cursor.getColumnIndex(
|
|
ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ENABLED_TIMESTAMP_MS));
|
|
MediaFolderType type = MediaFolderType.getById(cursor.getInt(cursor.getColumnIndex(
|
|
ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_TYPE)));
|
|
|
|
syncedFolder = new SyncedFolder(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate,
|
|
accountName, uploadAction, enabled, enabledTimestampMs, type);
|
|
}
|
|
return syncedFolder;
|
|
}
|
|
|
|
/**
|
|
* create ContentValues object based on given SyncedFolder.
|
|
*
|
|
* @param syncedFolder the synced folder
|
|
* @return the corresponding ContentValues object
|
|
*/
|
|
@NonNull
|
|
private ContentValues createContentValuesFromSyncedFolder(SyncedFolder syncedFolder) {
|
|
ContentValues cv = new ContentValues();
|
|
cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_LOCAL_PATH, syncedFolder.getLocalPath());
|
|
cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_REMOTE_PATH, syncedFolder.getRemotePath());
|
|
cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_WIFI_ONLY, syncedFolder.getWifiOnly());
|
|
cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_CHARGING_ONLY, syncedFolder.getChargingOnly());
|
|
cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ENABLED, syncedFolder.isEnabled());
|
|
cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ENABLED_TIMESTAMP_MS, syncedFolder.getEnabledTimestampMs());
|
|
cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE, syncedFolder.getSubfolderByDate());
|
|
cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ACCOUNT, syncedFolder.getAccount());
|
|
cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_UPLOAD_ACTION, syncedFolder.getUploadAction());
|
|
cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_TYPE, syncedFolder.getType().getId());
|
|
|
|
return cv;
|
|
}
|
|
}
|