Drafts v2 (#2032)
* cleanup warnings, reorganize some code * move ComposeAutoCompleteAdapter to compose package * composeOptions doesn't need to be a class member * add DraftsActivity and DraftsViewModel * drafts * remove unnecessary Unit in ComposeViewModel * add schema/25.json * fix db migration * drafts * cleanup code * fix compose activity rotation bug * fix media descriptions getting lost when restoring a draft * improve deleting drafts * fix ComposeActivityTest * improve draft layout for almost empty drafts * reformat code * show toast when opening reply to deleted toot * improve item_draft layout
This commit is contained in:
parent
baa915a0a3
commit
940d6d395a
85 changed files with 2032 additions and 381 deletions
|
|
@ -1,33 +1,18 @@
|
|||
package com.keylesspalace.tusky.util;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.keylesspalace.tusky.BuildConfig;
|
||||
import com.keylesspalace.tusky.db.AppDatabase;
|
||||
import com.keylesspalace.tusky.db.TootDao;
|
||||
import com.keylesspalace.tusky.db.TootEntity;
|
||||
import com.keylesspalace.tusky.entity.NewPoll;
|
||||
import com.keylesspalace.tusky.entity.Status;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
|
|
@ -45,61 +30,6 @@ public final class SaveTootHelper {
|
|||
this.context = context;
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
public boolean saveToot(@NonNull String content,
|
||||
@NonNull String contentWarning,
|
||||
@Nullable List<String> savedJsonUrls,
|
||||
@NonNull List<String> mediaUris,
|
||||
@NonNull List<String> mediaDescriptions,
|
||||
int savedTootUid,
|
||||
@Nullable String inReplyToId,
|
||||
@Nullable String replyingStatusContent,
|
||||
@Nullable String replyingStatusAuthorUsername,
|
||||
@NonNull Status.Visibility statusVisibility,
|
||||
@Nullable NewPoll poll) {
|
||||
|
||||
if (TextUtils.isEmpty(content) && mediaUris.isEmpty() && poll == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get any existing file's URIs.
|
||||
|
||||
String mediaUrlsSerialized = null;
|
||||
String mediaDescriptionsSerialized = null;
|
||||
|
||||
if (!ListUtils.isEmpty(mediaUris)) {
|
||||
List<String> savedList = saveMedia(mediaUris, savedJsonUrls);
|
||||
if (!ListUtils.isEmpty(savedList)) {
|
||||
mediaUrlsSerialized = gson.toJson(savedList);
|
||||
if (!ListUtils.isEmpty(savedJsonUrls)) {
|
||||
deleteMedia(setDifference(savedJsonUrls, savedList));
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
mediaDescriptionsSerialized = gson.toJson(mediaDescriptions);
|
||||
} else if (!ListUtils.isEmpty(savedJsonUrls)) {
|
||||
/* If there were URIs in the previous draft, but they've now been removed, those files
|
||||
* can be deleted. */
|
||||
deleteMedia(savedJsonUrls);
|
||||
}
|
||||
final TootEntity toot = new TootEntity(savedTootUid, content, mediaUrlsSerialized, mediaDescriptionsSerialized, contentWarning,
|
||||
inReplyToId,
|
||||
replyingStatusContent,
|
||||
replyingStatusAuthorUsername,
|
||||
statusVisibility,
|
||||
poll);
|
||||
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
tootDao.insertOrReplace(toot);
|
||||
return null;
|
||||
}
|
||||
}.execute();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void deleteDraft(int tootId) {
|
||||
TootEntity item = tootDao.find(tootId);
|
||||
if (item != null) {
|
||||
|
|
@ -124,82 +54,4 @@ public final class SaveTootHelper {
|
|||
tootDao.delete(item.getUid());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private List<String> saveMedia(@NonNull List<String> mediaUris,
|
||||
@Nullable List<String> existingUris) {
|
||||
|
||||
File directory = context.getExternalFilesDir("Tusky");
|
||||
|
||||
if (directory == null || !(directory.exists())) {
|
||||
Log.e(TAG, "Error obtaining directory to save media.");
|
||||
return null;
|
||||
}
|
||||
|
||||
ContentResolver contentResolver = context.getContentResolver();
|
||||
ArrayList<File> filesSoFar = new ArrayList<>();
|
||||
ArrayList<String> results = new ArrayList<>();
|
||||
for (String mediaUri : mediaUris) {
|
||||
/* If the media was already saved in a previous draft, there's no need to save another
|
||||
* copy, just add the existing URI to the results. */
|
||||
if (existingUris != null) {
|
||||
int index = existingUris.indexOf(mediaUri);
|
||||
if (index != -1) {
|
||||
results.add(mediaUri);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Otherwise, save the media.
|
||||
|
||||
Uri uri = Uri.parse(mediaUri);
|
||||
|
||||
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date());
|
||||
|
||||
String mimeType = contentResolver.getType(uri);
|
||||
MimeTypeMap map = MimeTypeMap.getSingleton();
|
||||
String fileExtension = map.getExtensionFromMimeType(mimeType);
|
||||
String filename = String.format("Tusky_Draft_Media_%s.%s", timeStamp, fileExtension);
|
||||
File file = new File(directory, filename);
|
||||
filesSoFar.add(file);
|
||||
boolean copied = IOUtils.copyToFile(contentResolver, uri, file);
|
||||
if (!copied) {
|
||||
/* If any media files were created in prior iterations, delete those before
|
||||
* returning. */
|
||||
for (File earlierFile : filesSoFar) {
|
||||
boolean deleted = earlierFile.delete();
|
||||
if (!deleted) {
|
||||
Log.i(TAG, "Could not delete the file " + earlierFile.toString());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
Uri resultUri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileprovider", file);
|
||||
results.add(resultUri.toString());
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private void deleteMedia(List<String> mediaUris) {
|
||||
for (String uriString : mediaUris) {
|
||||
Uri uri = Uri.parse(uriString);
|
||||
if (context.getContentResolver().delete(uri, null, null) == 0) {
|
||||
Log.e(TAG, String.format("Did not delete file %s.", uriString));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A∖B={x∈A|x∉B}
|
||||
*
|
||||
* @return all elements of set A that are not in set B.
|
||||
*/
|
||||
private static List<String> setDifference(List<String> a, List<String> b) {
|
||||
List<String> c = new ArrayList<>();
|
||||
for (String s : a) {
|
||||
if (!b.contains(s)) {
|
||||
c.add(s);
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue