ComposeActivity refactor (#1541)

* Convert ComposeActivity to Kotlin

* More ComposeActivity cleanups

* Move ComposeActivity to it's own package

* Remove ComposeActivity.IntentBuilder

* Re-do part of the media downsizing/uploading

* Add sending of status to ViewModel, draft media descriptions

* Allow uploading video, update description after uploading

* Enable camera, enable upload cancelling

* Cleanup of ComposeActivity

* Extract CaptionDialog, extract ComposeActivity methods

* Fix handling of redrafted media

* Add initial state and media uploading out of Activity

* Change ComposeOptions.mentionedUsernames to be Set rather than List

We probably don't want repeated usernames when we are writing a post
and Set provides such guarantee for free plus it tells it to the
callers. The only disadvantage is lack of order but it shouldn't be a
problem.

* Add combineOptionalLiveData. Add docs.

It it useful for nullable LiveData's. I think we cannot differentiate
between value not being set and value being null so I just added the
variant without null check.

* Add poll support to Compose.

* cleanup code

* move more classes into compose package

* cleanup code

* fix button behavior

* add error handling for media upload

* add caching for instance data again

* merge develop

* fix scheduled toots

* delete unused string

* cleanup ComposeActivity

* fix restoring media from drafts

* make media upload code a little bit clearer

* cleanup autocomplete search code

* avoid duplicate object creation in SavedTootActivity

* perf: avoid unnecessary work when initializing ComposeActivity

* add license header to new files

* use small toot button on bigger displays

* fix ComposeActivityTest

* fix bad merge

* use Singles.zip instead of Single.zip
This commit is contained in:
Ivan Kupalov 2019-12-19 19:09:40 +01:00 committed by Konrad Pozniak
commit 8770fbe986
68 changed files with 3162 additions and 2666 deletions

View file

@ -42,12 +42,13 @@ import androidx.lifecycle.Lifecycle;
import com.keylesspalace.tusky.BaseActivity;
import com.keylesspalace.tusky.BottomSheetActivity;
import com.keylesspalace.tusky.ComposeActivity;
import com.keylesspalace.tusky.MainActivity;
import com.keylesspalace.tusky.R;
import com.keylesspalace.tusky.PostLookupFallbackBehavior;
import com.keylesspalace.tusky.ViewMediaActivity;
import com.keylesspalace.tusky.ViewTagActivity;
import com.keylesspalace.tusky.components.compose.ComposeActivity;
import com.keylesspalace.tusky.components.compose.ComposeActivity.ComposeOptions;
import com.keylesspalace.tusky.components.report.ReportActivity;
import com.keylesspalace.tusky.db.AccountEntity;
import com.keylesspalace.tusky.db.AccountManager;
@ -148,21 +149,22 @@ public abstract class SFragment extends BaseFragment implements Injectable {
mentionedUsernames.add(actionableStatus.getAccount().getUsername());
String loggedInUsername = null;
AccountEntity activeAccount = accountManager.getActiveAccount();
if(activeAccount != null) {
if (activeAccount != null) {
loggedInUsername = activeAccount.getUsername();
}
for (Status.Mention mention : mentions) {
mentionedUsernames.add(mention.getUsername());
}
mentionedUsernames.remove(loggedInUsername);
Intent intent = new ComposeActivity.IntentBuilder()
.inReplyToId(inReplyToId)
.replyVisibility(replyVisibility)
.contentWarning(contentWarning)
.mentionedUsernames(mentionedUsernames)
.replyingStatusAuthor(actionableStatus.getAccount().getLocalUsername())
.replyingStatusContent(actionableStatus.getContent().toString())
.build(getContext());
ComposeOptions composeOptions = new ComposeOptions();
composeOptions.setInReplyToId(inReplyToId);
composeOptions.setReplyVisibility(replyVisibility);
composeOptions.setContentWarning(contentWarning);
composeOptions.setMentionedUsernames(mentionedUsernames);
composeOptions.setReplyingStatusAuthor(actionableStatus.getAccount().getLocalUsername());
composeOptions.setReplyingStatusContent(actionableStatus.getContent().toString());
Intent intent = ComposeActivity.startIntent(getContext(), composeOptions);
getActivity().startActivity(intent);
}
@ -176,7 +178,7 @@ public abstract class SFragment extends BaseFragment implements Injectable {
String loggedInAccountId = null;
AccountEntity activeAccount = accountManager.getActiveAccount();
if(activeAccount != null) {
if (activeAccount != null) {
loggedInAccountId = activeAccount.getAccountId();
}
@ -209,7 +211,7 @@ public abstract class SFragment extends BaseFragment implements Injectable {
Menu menu = popup.getMenu();
MenuItem openAsItem = menu.findItem(R.id.status_open_as);
switch(accounts.size()) {
switch (accounts.size()) {
case 0:
case 1:
openAsItem.setVisible(false);
@ -232,7 +234,8 @@ public abstract class SFragment extends BaseFragment implements Injectable {
switch (item.getItemId()) {
case R.id.status_share_content: {
Status statusToShare = status;
if(statusToShare.getReblog() != null) statusToShare = statusToShare.getReblog();
if (statusToShare.getReblog() != null)
statusToShare = statusToShare.getReblog();
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
@ -357,7 +360,8 @@ public abstract class SFragment extends BaseFragment implements Injectable {
.observeOn(AndroidSchedulers.mainThread())
.as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY)))
.subscribe(
deletedStatus -> {},
deletedStatus -> {
},
error -> {
Log.w("SFragment", "error deleting status", error);
Toast.makeText(getContext(), R.string.error_generic, Toast.LENGTH_SHORT).show();
@ -381,22 +385,22 @@ public abstract class SFragment extends BaseFragment implements Injectable {
.subscribe(deletedStatus -> {
removeItem(position);
if(deletedStatus.isEmpty()) {
if (deletedStatus.isEmpty()) {
deletedStatus = status.toDeletedStatus();
}
ComposeActivity.IntentBuilder intentBuilder = new ComposeActivity.IntentBuilder()
.tootText(deletedStatus.getText())
.inReplyToId(deletedStatus.getInReplyToId())
.visibility(deletedStatus.getVisibility())
.contentWarning(deletedStatus.getSpoilerText())
.mediaAttachments(deletedStatus.getAttachments())
.sensitive(deletedStatus.getSensitive());
if(deletedStatus.getPoll() != null) {
intentBuilder.poll(deletedStatus.getPoll().toNewPoll(deletedStatus.getCreatedAt()));
ComposeOptions composeOptions = new ComposeOptions();
composeOptions.setTootText(deletedStatus.getText());
composeOptions.setInReplyToId(deletedStatus.getInReplyToId());
composeOptions.setVisibility(deletedStatus.getVisibility());
composeOptions.setContentWarning(deletedStatus.getSpoilerText());
composeOptions.setMediaAttachments(deletedStatus.getAttachments());
composeOptions.setSensitive(deletedStatus.getSensitive());
if (deletedStatus.getPoll() != null) {
composeOptions.setPoll(deletedStatus.getPoll().toNewPoll(deletedStatus.getCreatedAt()));
}
Intent intent = intentBuilder.build(getContext());
Intent intent = ComposeActivity
.startIntent(getContext(), composeOptions);
startActivity(intent);
},
error -> {
@ -415,22 +419,22 @@ public abstract class SFragment extends BaseFragment implements Injectable {
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.putExtra(MainActivity.STATUS_URL, statusUrl);
startActivity(intent);
((BaseActivity)getActivity()).finishWithoutSlideOutAnimation();
((BaseActivity) getActivity()).finishWithoutSlideOutAnimation();
}
private void showOpenAsDialog(String statusUrl, CharSequence dialogTitle) {
BaseActivity activity = (BaseActivity)getActivity();
BaseActivity activity = (BaseActivity) getActivity();
activity.showAccountChooserDialog(dialogTitle, false, account -> openAsAccount(statusUrl, account));
}
private void downloadAllMedia(Status status) {
Toast.makeText(getContext(), R.string.downloading_media, Toast.LENGTH_SHORT).show();
for(Attachment attachment: status.getAttachments()) {
for (Attachment attachment : status.getAttachments()) {
String url = attachment.getUrl();
Uri uri = Uri.parse(url);
String filename = uri.getLastPathSegment();
DownloadManager downloadManager = (DownloadManager)getActivity().getSystemService(Context.DOWNLOAD_SERVICE);
DownloadManager downloadManager = (DownloadManager) getActivity().getSystemService(Context.DOWNLOAD_SERVICE);
DownloadManager.Request request = new DownloadManager.Request(uri);
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename);
downloadManager.enqueue(request);
@ -438,8 +442,8 @@ public abstract class SFragment extends BaseFragment implements Injectable {
}
private void requestDownloadAllMedia(Status status) {
String[] permissions = new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE };
((BaseActivity)getActivity()).requestPermissions(permissions, (permissions1, grantResults) -> {
String[] permissions = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};
((BaseActivity) getActivity()).requestPermissions(permissions, (permissions1, grantResults) -> {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
downloadAllMedia(status);
} else {
@ -487,9 +491,9 @@ public abstract class SFragment extends BaseFragment implements Injectable {
@VisibleForTesting
public boolean shouldFilterStatus(Status status) {
if(filterRemoveRegex && status.getPoll() != null) {
for(PollOption option: status.getPoll().getOptions()) {
if(filterRemoveRegexMatcher.reset(option.getTitle()).find()) {
if (filterRemoveRegex && status.getPoll() != null) {
for (PollOption option : status.getPoll().getOptions()) {
if (filterRemoveRegexMatcher.reset(option.getTitle()).find()) {
return true;
}
}

View file

@ -22,7 +22,7 @@ import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment;
import com.keylesspalace.tusky.ComposeActivity;
import com.keylesspalace.tusky.components.compose.ComposeActivity;
import java.util.Calendar;
import java.util.TimeZone;