Skip to content

Commit

Permalink
#296: Add documents provider
Browse files Browse the repository at this point in the history
  • Loading branch information
przybylski authored and jabarros committed Jun 29, 2016
1 parent 05566df commit 74ee655
Show file tree
Hide file tree
Showing 8 changed files with 377 additions and 9 deletions.
16 changes: 14 additions & 2 deletions AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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" />

<provider
android:name=".providers.UsersAndGroupsSearchProvider"
android:authorities="com.owncloud.android.providers.UsersAndGroupsSearchProvider"
android:authorities=".providers.UsersAndGroupsSearchProvider"
android:enabled="true"
android:exported="false"
android:label="@string/search_users_and_groups_hint" />

<provider
android:authorities="@string/document_provider_authority"
android:name=".providers.DocumentsStorageProvider"
android:exported="true"
android:grantUriPermissions="true"
android:permission="android.permission.MANAGE_DOCUMENTS"
android:enabled="@bool/atLeastKitKat">
<intent-filter>
<action android:name="android.content.action.DOCUMENTS_PROVIDER" />
</intent-filter>
</provider>

<activity
android:name=".authentication.AuthenticatorActivity"
android:exported="true"
Expand Down
22 changes: 22 additions & 0 deletions res/values-v19/bools.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
ownCloud Android client application
Copyright (C) 2015 ownCloud Inc.
Copyright (C) 2015 Bartosz Przybylski
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 <http://www.gnu.org/licenses/>.
-->
<resources>
<bool name="atLeastKitKat">true</bool>
</resources>
1 change: 1 addition & 0 deletions res/values/bools.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@
<!-- Default boolean values -->
<resources>
<bool name="large_land_layout">false</bool>
<bool name="atLeastKitKat">false</bool>
</resources>
1 change: 1 addition & 0 deletions res/values/setup.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<string name="app_name">ownCloud</string>
<string name="account_type">owncloud</string> <!-- better if was a domain name; but changing it now would require migrate accounts when the app is updated -->
<string name="authority">org.owncloud</string> <!-- better if was the app package with ".provider" appended ; it identifies the provider -->
<string name="document_provider_authority">org.owncloud.documents</string>
<string name ="db_file">owncloud.db</string>
<string name ="db_name">ownCloud</string>
<string name ="data_folder">owncloud</string>
Expand Down
15 changes: 8 additions & 7 deletions src/com/owncloud/android/authentication/AccountUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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("@");
Expand Down Expand Up @@ -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
Expand Down
208 changes: 208 additions & 0 deletions src/com/owncloud/android/providers/DocumentsStorageProvider.java
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*
*/

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<Long, FileDataStorageManager> 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<Long, FileDataStorageManager>();

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<OCFile> findFiles(OCFile root, String query) {
Vector<OCFile> result = new Vector<OCFile>();
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;
}
}
61 changes: 61 additions & 0 deletions src/com/owncloud/android/providers/cursors/FileCursor.java
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*
*/

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);
}
}
Loading

0 comments on commit 74ee655

Please sign in to comment.