Skip to content

Instantly share code, notes, and snippets.

@jcgruenhage
Created November 19, 2016 14:43
Show Gist options
  • Save jcgruenhage/7de5065b5a9774e9fadb3670a953f669 to your computer and use it in GitHub Desktop.
Save jcgruenhage/7de5065b5a9774e9fadb3670a953f669 to your computer and use it in GitHub Desktop.
DocumentsProvider with Subtree selection. Read only. Not sure if writing in the subtree would work just by implementing writing in general.
/**
* nextCloud Android client application
*
* @author Bartosz Przybylski
* Copyright (C) 2016 Bartosz Przybylski <bart.p.pl@gmail.com>
*
* 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 org.nextcloud.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.DocumentsContract;
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 org.nextcloud.providers.cursors.FileCursor;
import org.nextcloud.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 {
@Override
public boolean isChildDocument(String parentDocumentId, String documentId) {
Cursor childDocuments = null;
try {
childDocuments = queryChildDocuments(parentDocumentId, null, null);
} catch (FileNotFoundException pE) {
pE.printStackTrace();
}
if (childDocuments == null) return false;
int columnId = childDocuments.getColumnIndex(DocumentsContract.Document.COLUMN_DOCUMENT_ID);
boolean notFound = true;
while (notFound) {
if (childDocuments.getString(columnId) == documentId) notFound = false;
if (!childDocuments.moveToNext()) break;
}
return !notFound;
}
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);
OCFile file = mCurrentStorageManager.getFileById(docId);
if (file != null)
result.addFile(file);
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, false)) {
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, false)) {
if (f.isFolder()) {
result.addAll(findFiles(f, query));
} else if (f.getFileName().contains(query)) {
result.add(f);
}
}
return result;
}
}
@jcgruenhage
Copy link
Author

RootCursor also needs a little change, the addRoot method has to be this:

    public void addRoot(Account account, Context context) {
        final FileDataStorageManager manager =
                new FileDataStorageManager(account, context.getContentResolver());
        final OCFile mainDir = manager.getFileByPath("/");
        RowBuilder newRow = 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.mipmap.ic_launcher)
                .add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_SEARCH);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            newRow.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_IS_CHILD);
        }
    }

@ayush19283
Copy link

ayush19283 commented Apr 21, 2024

Is the alternate of Storage access framework (open_action_document_tree) intent @daimajia @weidongjian @KunMinX
Please help I am getting error (no intent found to handle action open document tree). Is there any alternate of it?

@jcgruenhage
Copy link
Author

@ayush19283 sorry, can't help you. This code is ancient and I haven't touched the SAF (or android software development in general) since.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment