diff --git a/AndroidManifest.xml b/AndroidManifest.xml index eb352ad22c5..7ad2cfa6615 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -129,17 +129,29 @@ android:name=".providers.FileContentProvider" android:authorities="@string/authority" android:enabled="true" - android:exported="false" + android:exported="true" android:label="@string/sync_string_files" android:syncable="true" /> + + + + + + + + + true + \ No newline at end of file diff --git a/res/values/bools.xml b/res/values/bools.xml index c2ca6732c83..e070bbcfc07 100644 --- a/res/values/bools.xml +++ b/res/values/bools.xml @@ -19,4 +19,5 @@ false + false \ No newline at end of file diff --git a/res/values/setup.xml b/res/values/setup.xml index 02127007563..02334f9b379 100644 --- a/res/values/setup.xml +++ b/res/values/setup.xml @@ -4,6 +4,7 @@ ownCloud owncloud org.owncloud + org.owncloud.documents owncloud.db ownCloud owncloud diff --git a/src/com/owncloud/android/authentication/AccountUtils.java b/src/com/owncloud/android/authentication/AccountUtils.java index 7bf180213c8..28a6ecd894e 100644 --- a/src/com/owncloud/android/authentication/AccountUtils.java +++ b/src/com/owncloud/android/authentication/AccountUtils.java @@ -56,8 +56,7 @@ public class AccountUtils { * account). If none is available and valid, returns null. */ public static Account getCurrentOwnCloudAccount(Context context) { - Account[] ocAccounts = AccountManager.get(context).getAccountsByType( - MainApp.getAccountType()); + Account[] ocAccounts = getAccounts(context); Account defaultAccount = null; SharedPreferences appPreferences = PreferenceManager @@ -83,10 +82,14 @@ public static Account getCurrentOwnCloudAccount(Context context) { return defaultAccount; } + public static Account[] getAccounts(Context context) { + AccountManager accountManager = AccountManager.get(context); + return accountManager.getAccountsByType(MainApp.getAccountType()); + } + public static boolean exists(Account account, Context context) { - Account[] ocAccounts = AccountManager.get(context).getAccountsByType( - MainApp.getAccountType()); + Account[] ocAccounts = getAccounts(context); if (account != null && account.name != null) { int lastAtPos = account.name.lastIndexOf("@"); @@ -128,10 +131,8 @@ public static Account getOwnCloudAccountByName(Context context, String accountNa public static boolean setCurrentOwnCloudAccount(Context context, String accountName) { boolean result = false; if (accountName != null) { - Account[] ocAccounts = AccountManager.get(context).getAccountsByType( - MainApp.getAccountType()); boolean found; - for (Account account : ocAccounts) { + for (Account account : getAccounts(context)) { found = (account.name.equals(accountName)); if (found) { SharedPreferences.Editor appPrefs = PreferenceManager diff --git a/src/com/owncloud/android/providers/DocumentsStorageProvider.java b/src/com/owncloud/android/providers/DocumentsStorageProvider.java new file mode 100644 index 00000000000..85942dd5a84 --- /dev/null +++ b/src/com/owncloud/android/providers/DocumentsStorageProvider.java @@ -0,0 +1,208 @@ +/** + * ownCloud Android client application + * + * @author Bartosz Przybylski + * Copyright (C) 2015 Bartosz Przybylski + * Copyright (C) 2015 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.owncloud.android.providers; + +import android.accounts.Account; +import android.annotation.TargetApi; +import android.content.ContentResolver; +import android.content.Intent; +import android.content.res.AssetFileDescriptor; +import android.database.Cursor; +import android.graphics.Point; +import android.os.Build; +import android.os.CancellationSignal; +import android.os.ParcelFileDescriptor; +import android.provider.DocumentsProvider; + +import com.owncloud.android.authentication.AccountUtils; +import com.owncloud.android.datamodel.FileDataStorageManager; +import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.files.services.FileDownloader; +import com.owncloud.android.providers.cursors.FileCursor; +import com.owncloud.android.providers.cursors.RootCursor; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.HashMap; +import java.util.Map; +import java.util.Vector; + +@TargetApi(Build.VERSION_CODES.KITKAT) +public class DocumentsStorageProvider extends DocumentsProvider { + + private FileDataStorageManager mCurrentStorageManager = null; + private static Map mRootIdToStorageManager; + + @Override + public Cursor queryRoots(String[] projection) throws FileNotFoundException { + initiateStorageMap(); + + final RootCursor result = new RootCursor(projection); + + for (Account account : AccountUtils.getAccounts(getContext())) + result.addRoot(account, getContext()); + + return result; + } + + @Override + public Cursor queryDocument(String documentId, String[] projection) throws FileNotFoundException { + final long docId = Long.parseLong(documentId); + updateCurrentStorageManagerIfNeeded(docId); + + final FileCursor result = new FileCursor(projection); + result.addFile(mCurrentStorageManager.getFileById(docId)); + + return result; + } + + @Override + public Cursor queryChildDocuments(String parentDocumentId, String[] projection, String sortOrder) + throws FileNotFoundException { + + final long folderId = Long.parseLong(parentDocumentId); + updateCurrentStorageManagerIfNeeded(folderId); + + final FileCursor result = new FileCursor(projection); + + final OCFile browsedDir = mCurrentStorageManager.getFileById(folderId); + for (OCFile file : mCurrentStorageManager.getFolderContent(browsedDir)) + result.addFile(file); + + return result; + } + + @Override + public ParcelFileDescriptor openDocument(String documentId, String mode, CancellationSignal cancellationSignal) + throws FileNotFoundException { + final long docId = Long.parseLong(documentId); + updateCurrentStorageManagerIfNeeded(docId); + + OCFile file = mCurrentStorageManager.getFileById(docId); + + if (!file.isDown()) { + + Intent i = new Intent(getContext(), FileDownloader.class); + i.putExtra(FileDownloader.EXTRA_ACCOUNT, mCurrentStorageManager.getAccount()); + i.putExtra(FileDownloader.EXTRA_FILE, file); + getContext().startService(i); + + do { + if (!waitOrGetCancelled(cancellationSignal)) + return null; + file = mCurrentStorageManager.getFileById(docId); + + } while (!file.isDown()); + } + + return ParcelFileDescriptor.open( + new File(file.getStoragePath()), ParcelFileDescriptor.MODE_READ_ONLY); + } + + @Override + public boolean onCreate() { + return true; + } + + @Override + public AssetFileDescriptor openDocumentThumbnail(String documentId, Point sizeHint, CancellationSignal signal) throws FileNotFoundException { + long docId = Long.parseLong(documentId); + updateCurrentStorageManagerIfNeeded(docId); + + OCFile file = mCurrentStorageManager.getFileById(docId); + + File realFile = new File(file.getStoragePath()); + + return new AssetFileDescriptor( + ParcelFileDescriptor.open(realFile, ParcelFileDescriptor.MODE_READ_ONLY), + 0, + AssetFileDescriptor.UNKNOWN_LENGTH); + } + + @Override + public Cursor querySearchDocuments(String rootId, String query, String[] projection) throws FileNotFoundException { + updateCurrentStorageManagerIfNeeded(rootId); + + OCFile root = mCurrentStorageManager.getFileByPath("/"); + FileCursor result = new FileCursor(projection); + + for (OCFile f : findFiles(root, query)) + result.addFile(f); + + return result; + } + + private void updateCurrentStorageManagerIfNeeded(long docId) { + if (mCurrentStorageManager == null || + (mRootIdToStorageManager.containsKey(docId) && + mCurrentStorageManager != mRootIdToStorageManager.get(docId))) { + mCurrentStorageManager = mRootIdToStorageManager.get(docId); + } + } + + private void updateCurrentStorageManagerIfNeeded(String rootId) { + for (FileDataStorageManager data : mRootIdToStorageManager.values()) + if (data.getAccount().name.equals(rootId)) + mCurrentStorageManager = data; + } + + private void initiateStorageMap() { + + mRootIdToStorageManager = new HashMap(); + + ContentResolver contentResolver = getContext().getContentResolver(); + + for (Account account : AccountUtils.getAccounts(getContext())) { + final FileDataStorageManager storageManager = + new FileDataStorageManager(account, contentResolver); + final OCFile rootDir = storageManager.getFileByPath("/"); + mRootIdToStorageManager.put(rootDir.getFileId(), storageManager); + } + + } + + private boolean waitOrGetCancelled(CancellationSignal cancellationSignal) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + return false; + } + + if (cancellationSignal != null && cancellationSignal.isCanceled()) + return false; + + return true; + } + + Vector findFiles(OCFile root, String query) { + Vector result = new Vector(); + for (OCFile f : mCurrentStorageManager.getFolderContent(root)) { + if (f.isFolder()) { + result.addAll(findFiles(f, query)); + } else { + if (f.getFileName().contains(query)) + result.add(f); + } + } + return result; + } +} diff --git a/src/com/owncloud/android/providers/cursors/FileCursor.java b/src/com/owncloud/android/providers/cursors/FileCursor.java new file mode 100644 index 00000000000..9d4b9d8cc0d --- /dev/null +++ b/src/com/owncloud/android/providers/cursors/FileCursor.java @@ -0,0 +1,61 @@ +/** + * ownCloud Android client application + * + * @author Bartosz Przybylski + * Copyright (C) 2015 Bartosz Przybylski + * Copyright (C) 2015 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.owncloud.android.providers.cursors; + +import android.annotation.TargetApi; +import android.database.MatrixCursor; +import android.os.Build; +import android.provider.DocumentsContract.Document; + +import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.utils.MimetypeIconUtil; + +@TargetApi(Build.VERSION_CODES.KITKAT) +public class FileCursor extends MatrixCursor { + + private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] { + Document.COLUMN_DOCUMENT_ID, Document.COLUMN_DISPLAY_NAME, + Document.COLUMN_MIME_TYPE, Document.COLUMN_SIZE, + Document.COLUMN_FLAGS, Document.COLUMN_LAST_MODIFIED + }; + + public FileCursor(String[] projection) { + super(projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION); + } + + public void addFile(OCFile file) { + if (file == null) return; + + final int iconRes = MimetypeIconUtil.getFileTypeIconId(file.getMimetype(), file.getFileName()); + final String mimeType = file.isFolder() ? Document.MIME_TYPE_DIR : file.getMimetype(); + final String imagePath = file.isImage() && file.isDown() ? file.getStoragePath() : null; + int flags = imagePath != null ? Document.FLAG_SUPPORTS_THUMBNAIL : 0; + + newRow().add(Document.COLUMN_DOCUMENT_ID, Long.toString(file.getFileId())) + .add(Document.COLUMN_DISPLAY_NAME, file.getFileName()) + .add(Document.COLUMN_LAST_MODIFIED, file.getModificationTimestamp()) + .add(Document.COLUMN_SIZE, file.getFileLength()) + .add(Document.COLUMN_FLAGS, flags) + .add(Document.COLUMN_ICON, iconRes) + .add(Document.COLUMN_MIME_TYPE, mimeType); + } +} diff --git a/src/com/owncloud/android/providers/cursors/RootCursor.java b/src/com/owncloud/android/providers/cursors/RootCursor.java new file mode 100644 index 00000000000..cbf63d96bf8 --- /dev/null +++ b/src/com/owncloud/android/providers/cursors/RootCursor.java @@ -0,0 +1,62 @@ +/** + * ownCloud Android client application + * + * @author Bartosz Przybylski + * Copyright (C) 2015 Bartosz Przybylski + * Copyright (C) 2015 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.owncloud.android.providers.cursors; + +import android.accounts.Account; +import android.annotation.TargetApi; +import android.content.Context; +import android.database.MatrixCursor; +import android.os.Build; +import android.provider.DocumentsContract.Root; + +import com.owncloud.android.R; +import com.owncloud.android.datamodel.FileDataStorageManager; +import com.owncloud.android.datamodel.OCFile; + + +@TargetApi(Build.VERSION_CODES.KITKAT) +public class RootCursor extends MatrixCursor { + + private static final String[] DEFAULT_ROOT_PROJECTION = new String[] { + Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE, + Root.COLUMN_DOCUMENT_ID, Root.COLUMN_AVAILABLE_BYTES, Root.COLUMN_SUMMARY, + Root.COLUMN_FLAGS + }; + + public RootCursor(String[] projection) { + super(projection != null ? projection : DEFAULT_ROOT_PROJECTION); + } + + public void addRoot(Account account, Context context) { + final FileDataStorageManager manager = + new FileDataStorageManager(account, context.getContentResolver()); + final OCFile mainDir = manager.getFileByPath("/"); + newRow().add(Root.COLUMN_ROOT_ID, account.name) + .add(Root.COLUMN_DOCUMENT_ID, mainDir.getFileId()) + .add(Root.COLUMN_SUMMARY, account.name) + .add(Root.COLUMN_TITLE, context.getString(R.string.app_name)) + .add(Root.COLUMN_ICON, R.drawable.icon) + .add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_SEARCH); + + } + +}