* 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:
Konrad Pozniak 2021-01-21 18:57:09 +01:00 committed by GitHub
commit 940d6d395a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
85 changed files with 2032 additions and 381 deletions

View file

@ -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));
}
}
}
/**
* AB={xA|xB}
*
* @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;
}
}
}