Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support org attachments #795

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import com.orgzly.R
import com.orgzly.android.AppIntent
import com.orgzly.android.OrgzlyTest
import com.orgzly.android.espresso.util.EspressoUtils.*
import com.orgzly.android.prefs.AppPreferences
import com.orgzly.android.ui.share.ShareActivity
import org.hamcrest.Matchers.startsWith
import org.junit.Assert.assertTrue
Expand Down Expand Up @@ -156,17 +157,25 @@ class ShareActivityTest : OrgzlyTest() {
extraStreamUri = "content://uri")

onView(withId(R.id.title_view)).check(matches(withText("content://uri")))
onView(withId(R.id.content_view)).check(matches(withText("Cannot find image using this URI.")))
onView(withId(R.id.content_view)).check(matches(withText("content://uri\n" +
"\n" +
"Cannot determine fileName to this content.")))

onView(withId(R.id.done)).perform(click()); // Note done
}

@Test
fun testNoMatchingType() {
startActivityWithIntent(action = Intent.ACTION_SEND, type = "application/octet-stream")
fun testFileCopy() {
AppPreferences.attachMethod(context, ShareActivity.ATTACH_METHOD_COPY_DIR);
startActivityWithIntent(
action = Intent.ACTION_SEND,
type = "application/pdf",
extraStreamUri = "content://uri")

onView(withId(R.id.title_view)).check(matches(withText("")))
onSnackbar().check(matches(withText(context.getString(R.string.share_type_not_supported, "application/octet-stream"))))
onView(withId(R.id.title_view)).check(matches(withText("content://uri")))
onView(withId(R.id.content_view)).check(matches(withText("content://uri\n\nCannot determine fileName to this content.")))

onView(withId(R.id.done)).perform(click()) // Note done
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.orgzly.android.util

import android.text.style.URLSpan
import com.orgzly.android.ui.views.style.AttachmentLinkSpan
import com.orgzly.android.ui.views.style.FileLinkSpan
import com.orgzly.android.ui.views.style.FileOrNotLinkSpan
import com.orgzly.android.ui.views.style.IdLinkSpan
Expand Down Expand Up @@ -67,6 +69,10 @@ class OrgFormatterLinkTest(private val param: Parameter) : OrgFormatterTest() {
Parameter("[[file:orgzly-tests/document.txt]]", "file:orgzly-tests/document.txt", listOf(Span(0, 30, FileLinkSpan::class.java))),
Parameter("[[file:orgzly-tests/document.txt][Document]]", "Document", listOf(Span(0, 8, FileLinkSpan::class.java))),

Parameter("attachment:orgzly-tests/document.txt", "attachment:orgzly-tests/document.txt", listOf(Span(0, 36, AttachmentLinkSpan::class.java))),
Parameter("[[attachment:orgzly-tests/document.txt]]", "attachment:orgzly-tests/document.txt", listOf(Span(0, 36, AttachmentLinkSpan::class.java))),
Parameter("[[attachment:orgzly-tests/document.txt][Document]]", "Document", listOf(Span(0, 8, AttachmentLinkSpan::class.java))),

Parameter("id:45DFE015-255E-4B86-B957-F7FD77364DCA", "id:45DFE015-255E-4B86-B957-F7FD77364DCA", listOf(Span(0, 39, IdLinkSpan::class.java))),
Parameter("[[id:45DFE015-255E-4B86-B957-F7FD77364DCA]]", "id:45DFE015-255E-4B86-B957-F7FD77364DCA", listOf(Span(0, 39, IdLinkSpan::class.java))),
Parameter("id:foo", "id:foo", listOf(Span(0, 6, IdLinkSpan::class.java))),
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
<data android:mimeType="*/*" />
</intent-filter>

<meta-data
Expand Down
39 changes: 38 additions & 1 deletion app/src/main/java/com/orgzly/android/data/DataRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import android.media.MediaScannerConnection
import android.net.Uri
import android.os.Handler
import android.text.TextUtils
import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Transformations
Expand Down Expand Up @@ -1314,7 +1315,7 @@ class DataRepository @Inject constructor(
fun createNoteFromNotification(title: String) {
val book = getTargetBook(context)

val notePayload = NoteBuilder.newPayload(context, title, "")
val notePayload = NoteBuilder.newPayload(context, title, "", null)

createNote(notePayload, NotePlace(book.book.id))
}
Expand Down Expand Up @@ -1572,6 +1573,42 @@ class DataRepository @Inject constructor(
}
}

/**
* Store the attachment content, in the repo for [bookId].
*
* @throws IOException
*/
@Throws(IOException::class)
fun storeAttachment(bookId: Long, notePayload: NotePayload) {
// Get the fileName from the provider.
// TODO provide a way to customize the fileName
val uri = notePayload.attachmentUri!!
val documentFile: DocumentFile = DocumentFile.fromSingleUri(context, uri)
?: throw IOException("Cannot get the fileName for Uri $uri")
val fileName = documentFile.name

val attachDir = notePayload.attachDir(context)

val book = getBookView(bookId)
?: throw IOException(resources.getString(R.string.book_does_not_exist_anymore))

// Not quite sure what repo to use.
val repoEntity = book.linkRepo ?: defaultRepoForSavingBook()
val repo = getRepoInstance(repoEntity.id, repoEntity.type, repoEntity.url)

val tempFile: File
// Get the InputStream of the content and write it to a File.
context.contentResolver.openInputStream(uri).use { inputStream ->
tempFile = getTempBookFile()
MiscUtils.writeStreamToFile(inputStream, tempFile)
LogUtils.d(TAG, "Wrote to file $tempFile")
}

repo.storeFile(tempFile, attachDir, fileName)
LogUtils.d(TAG, "Stored file to repo")
tempFile.delete()
}

/**
* Loads book from resource.
*/
Expand Down
23 changes: 23 additions & 0 deletions app/src/main/java/com/orgzly/android/prefs/AppPreferences.java
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,29 @@ public static String fileRelativeRoot(Context context) {
);
}

public static String attachMethod(Context context) {
return getDefaultSharedPreferences(context).getString(
context.getResources().getString(R.string.pref_key_attach_method),
context.getResources().getString(R.string.pref_default_attach_method));
}

public static void attachMethod(Context context, String value) {
String key = context.getResources().getString(R.string.pref_key_attach_method);
getDefaultSharedPreferences(context).edit().putString(key, value).apply();
}

/**
* When attachMethod is `link`, this pref is not used for saving attachment.
* When attachMethod is `copy_dir`, this pref is the target for saving attachment.
* When attachMethod is `copy_id`, this pref is used as a prefix for saving attachment, used
* together with ID subdirectory.
*/
public static String attachDirDefaultPath(Context context) {
return getDefaultSharedPreferences(context).getString(
context.getResources().getString(R.string.pref_key_attach_dir_default_path),
"data");
}

/*
* Note's metadata visibility
*/
Expand Down
32 changes: 30 additions & 2 deletions app/src/main/java/com/orgzly/android/repos/ContentRepo.java
Original file line number Diff line number Diff line change
Expand Up @@ -128,18 +128,25 @@ public VersionedRook retrieveBook(String fileName, File destinationFile) throws

@Override
public VersionedRook storeBook(File file, String fileName) throws IOException {
return storeFile(file, "", fileName);
}

@Override
public VersionedRook storeFile(File file, String pathInRepo, String fileName) throws IOException {
if (!file.exists()) {
throw new FileNotFoundException("File " + file + " does not exist");
}

DocumentFile documentFile = createRecursive(repoDocumentFile, pathInRepo);

/* Delete existing file. */
DocumentFile existingFile = repoDocumentFile.findFile(fileName);
DocumentFile existingFile = documentFile.findFile(fileName);
if (existingFile != null) {
existingFile.delete();
}

/* Create new file. */
DocumentFile destinationFile = repoDocumentFile.createFile("text/*", fileName);
DocumentFile destinationFile = documentFile.createFile("text/*", fileName);

if (destinationFile == null) {
throw new IOException("Failed creating " + fileName + " in " + repoUri);
Expand All @@ -163,6 +170,27 @@ public VersionedRook storeBook(File file, String fileName) throws IOException {
return new VersionedRook(repoId, RepoType.DOCUMENT, getUri(), uri, rev, mtime);
}

private DocumentFile createRecursive(DocumentFile parent, String path) {
if (".".equals(path) || "".equals(path)) {
return parent;
}
int l = path.lastIndexOf('/');
DocumentFile p;
if (l >= 0) {
p = createRecursive(parent, path.substring(0, l));
} else {
p = parent;
}
String subdir = path.substring(l+1);
DocumentFile f = p.findFile(subdir);
if (f != null) {
// already exist, return it
return f;
}
// Otherwise, create the directory
return p.createDirectory(subdir);
}

@Override
public VersionedRook renameBook(Uri from, String name) throws IOException {
DocumentFile fromDocFile = DocumentFile.fromSingleUri(context, from);
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/java/com/orgzly/android/repos/DatabaseRepo.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ public VersionedRook storeBook(File file, String fileName) throws IOException {
return dbRepo.createBook(repoId, vrook, content);
}

@Override
public VersionedRook storeFile(File file, String pathInRepo, String fileName) throws IOException {
throw new UnsupportedOperationException();
}

@Override
public VersionedRook renameBook(Uri fromUri, String name) {
Uri toUri = UriUtils.getUriForNewName(fromUri, name);
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/java/com/orgzly/android/repos/DirectoryRepo.java
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,11 @@ public VersionedRook storeBook(File file, String fileName) throws IOException {
return new VersionedRook(repoId, RepoType.DIRECTORY, repoUri, uri, rev, mtime);
}

@Override
public VersionedRook storeFile(File file, String pathInRepo, String fileName) throws IOException {
return storeBook(file, pathInRepo + File.separator + fileName);
}

@Override
public VersionedRook renameBook(Uri fromUri, String name) throws IOException {
String fromFilePath = fromUri.getPath();
Expand Down
9 changes: 6 additions & 3 deletions app/src/main/java/com/orgzly/android/repos/DropboxClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.orgzly.BuildConfig;
import com.orgzly.android.BookName;
import com.orgzly.android.prefs.AppPreferences;
import com.orgzly.android.util.LogUtils;

import java.io.BufferedOutputStream;
import java.io.File;
Expand Down Expand Up @@ -190,7 +191,7 @@ public List<VersionedRook> getBooks(Uri repoUri) throws IOException {
}

} catch (DbxException e) {
e.printStackTrace();
LogUtils.d(TAG, e.toString());

/* If we get NOT_FOUND from Dropbox, just return the empty list. */
if (e instanceof GetMetadataErrorException) {
Expand Down Expand Up @@ -235,6 +236,7 @@ public VersionedRook download(Uri repoUri, String fileName, File localFile) thro
}

} catch (DbxException e) {
LogUtils.d(TAG, e.toString());
if (e.getMessage() != null) {
throw new IOException("Failed downloading Dropbox file " + uri + ": " + e.getMessage());
} else {
Expand Down Expand Up @@ -266,6 +268,7 @@ public VersionedRook upload(File file, Uri repoUri, String fileName) throws IOEx
.uploadAndFinish(in);

} catch (DbxException e) {
LogUtils.d(TAG, e.toString());
if (e.getMessage() != null) {
throw new IOException("Failed overwriting " + bookUri.getPath() + " on Dropbox: " + e.getMessage());
} else {
Expand All @@ -290,7 +293,7 @@ public void delete(String path) throws IOException {
}

} catch (DbxException e) {
e.printStackTrace();
LogUtils.d(TAG, e.toString());

if (e.getMessage() != null) {
throw new IOException("Failed deleting " + path + " on Dropbox: " + e.getMessage());
Expand Down Expand Up @@ -319,7 +322,7 @@ public VersionedRook move(Uri repoUri, Uri from, Uri to) throws IOException {
return new VersionedRook(repoId, RepoType.DROPBOX, repoUri, to, rev, mtime);

} catch (Exception e) {
e.printStackTrace();
LogUtils.d(TAG, e.toString());

if (e.getMessage() != null) { // TODO: Move this throwing to utils
throw new IOException("Failed moving " + from + " to " + to + ": " + e.getMessage(), e);
Expand Down
69 changes: 0 additions & 69 deletions app/src/main/java/com/orgzly/android/repos/DropboxRepo.java

This file was deleted.

Loading