From 8bfd045f0a5b5b81a934018b841a30f450150c37 Mon Sep 17 00:00:00 2001 From: nuclearfog Date: Sun, 9 Apr 2023 19:56:07 +0200 Subject: [PATCH] finalized instance implementation --- .../api/mastodon/impl/MastodonInstance.java | 23 ++++++- .../backend/api/twitter/v1/TwitterV1.java | 2 +- .../twitter/v1/impl/TwitterV1Instance.java | 32 +++++++++- .../backend/api/twitter/v2/TwitterV2.java | 3 +- .../twitter/v2/impl/TwitterV2Instance.java | 29 ++++++++- .../twidda/backend/async/InstanceLoader.java | 37 +++-------- .../twidda/backend/helper/StatusUpdate.java | 25 +++++--- .../twidda/config/Configuration.java | 30 --------- .../twidda/database/AppDatabase.java | 5 +- .../twidda/ui/activities/StatusEditor.java | 61 ++++++++++++------- .../twidda/ui/activities/VideoViewer.java | 6 ++ 11 files changed, 156 insertions(+), 97 deletions(-) diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/api/mastodon/impl/MastodonInstance.java b/app/src/main/java/org/nuclearfog/twidda/backend/api/mastodon/impl/MastodonInstance.java index 8b2696ba..47843917 100644 --- a/app/src/main/java/org/nuclearfog/twidda/backend/api/mastodon/impl/MastodonInstance.java +++ b/app/src/main/java/org/nuclearfog/twidda/backend/api/mastodon/impl/MastodonInstance.java @@ -1,5 +1,8 @@ package org.nuclearfog.twidda.backend.api.mastodon.impl; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -50,7 +53,7 @@ public class MastodonInstance implements Instance { version = json.getString("version"); maxHashtagFeature = accounts.getInt("max_featured_tags"); maxCharacters = statuses.getInt("max_characters"); - maxImages = media.getInt("max_media_attachments"); + maxImages = statuses.getInt("max_media_attachments"); maxImageSize = media.getInt("image_size_limit"); maxVideoSize = media.getInt("video_size_limit"); maxPollOptions = polls.getInt("max_options"); @@ -192,4 +195,22 @@ public class MastodonInstance implements Instance { public boolean isTranslationSupported() { return translationSupported; } + + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) + return true; + if (!(obj instanceof MastodonInstance)) + return false; + MastodonInstance instance = (MastodonInstance) obj; + return instance.domain.equals(domain) && instance.timestamp == timestamp; + } + + + @NonNull + @Override + public String toString() { + return "domain=\"" + domain + " \" version=\"" + version + "\""; + } } \ No newline at end of file diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/api/twitter/v1/TwitterV1.java b/app/src/main/java/org/nuclearfog/twidda/backend/api/twitter/v1/TwitterV1.java index c19cf017..5c488d78 100644 --- a/app/src/main/java/org/nuclearfog/twidda/backend/api/twitter/v1/TwitterV1.java +++ b/app/src/main/java/org/nuclearfog/twidda/backend/api/twitter/v1/TwitterV1.java @@ -257,7 +257,7 @@ public class TwitterV1 implements Connection { @Override public Instance getInformation() { - return new TwitterV1Instance(); + return new TwitterV1Instance(settings.getLogin().getHostname()); } diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/api/twitter/v1/impl/TwitterV1Instance.java b/app/src/main/java/org/nuclearfog/twidda/backend/api/twitter/v1/impl/TwitterV1Instance.java index e2461977..c9e9e4c9 100644 --- a/app/src/main/java/org/nuclearfog/twidda/backend/api/twitter/v1/impl/TwitterV1Instance.java +++ b/app/src/main/java/org/nuclearfog/twidda/backend/api/twitter/v1/impl/TwitterV1Instance.java @@ -1,5 +1,8 @@ package org.nuclearfog.twidda.backend.api.twitter.v1.impl; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import org.nuclearfog.twidda.model.Instance; /** @@ -12,6 +15,15 @@ public class TwitterV1Instance implements Instance { private static final long serialVersionUID = 6248302391974167770L; + private String hostname; + + /** + * @param hostname currently used hostname + */ + public TwitterV1Instance(String hostname) { + this.hostname = hostname; + } + @Override public String getTitle() { @@ -21,7 +33,7 @@ public class TwitterV1Instance implements Instance { @Override public String getDomain() { - return "https://twitter.com"; + return hostname; } @@ -137,4 +149,22 @@ public class TwitterV1Instance implements Instance { public boolean isTranslationSupported() { return false; } + + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) + return true; + if (!(obj instanceof TwitterV1Instance)) + return false; + TwitterV1Instance instance = (TwitterV1Instance) obj; + return instance.hostname.equals(hostname); + } + + + @NonNull + @Override + public String toString() { + return "domain=\"" + getDomain() + " \" version=\"2.0\""; + } } \ No newline at end of file diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/api/twitter/v2/TwitterV2.java b/app/src/main/java/org/nuclearfog/twidda/backend/api/twitter/v2/TwitterV2.java index 8409c6d3..5b6a4a92 100644 --- a/app/src/main/java/org/nuclearfog/twidda/backend/api/twitter/v2/TwitterV2.java +++ b/app/src/main/java/org/nuclearfog/twidda/backend/api/twitter/v2/TwitterV2.java @@ -7,7 +7,6 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.nuclearfog.twidda.BuildConfig; -import org.nuclearfog.twidda.backend.api.ConnectionException; import org.nuclearfog.twidda.backend.api.twitter.TwitterException; import org.nuclearfog.twidda.backend.api.twitter.v1.TwitterV1; import org.nuclearfog.twidda.backend.api.twitter.v2.impl.AccountV2; @@ -68,7 +67,7 @@ public class TwitterV2 extends TwitterV1 { @Override public Instance getInformation() { - return new TwitterV2Instance(); + return new TwitterV2Instance(settings.getLogin().getHostname()); } diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/api/twitter/v2/impl/TwitterV2Instance.java b/app/src/main/java/org/nuclearfog/twidda/backend/api/twitter/v2/impl/TwitterV2Instance.java index ee6bd0f6..9c6db0d1 100644 --- a/app/src/main/java/org/nuclearfog/twidda/backend/api/twitter/v2/impl/TwitterV2Instance.java +++ b/app/src/main/java/org/nuclearfog/twidda/backend/api/twitter/v2/impl/TwitterV2Instance.java @@ -1,7 +1,9 @@ package org.nuclearfog.twidda.backend.api.twitter.v2.impl; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import org.nuclearfog.twidda.backend.api.twitter.v1.impl.TwitterV1Instance; -import org.nuclearfog.twidda.model.Instance; /** * Twitter API v2.0 configuration @@ -13,9 +15,34 @@ public class TwitterV2Instance extends TwitterV1Instance { private static final long serialVersionUID = 5979539035652732059L; + /** + * @param hostname currently used hostname + */ + public TwitterV2Instance(String hostname) { + super(hostname); + } + @Override public String getVersion() { return "2.0"; } + + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) + return true; + if (!(obj instanceof TwitterV2Instance)) + return false; + TwitterV2Instance instance = (TwitterV2Instance) obj; + return instance.getDomain().equals(getDomain()); + } + + + @NonNull + @Override + public String toString() { + return "domain=\"" + getDomain() + " \" version=\"2.0\""; + } } \ No newline at end of file diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/async/InstanceLoader.java b/app/src/main/java/org/nuclearfog/twidda/backend/async/InstanceLoader.java index 5857f878..0a05acd4 100644 --- a/app/src/main/java/org/nuclearfog/twidda/backend/async/InstanceLoader.java +++ b/app/src/main/java/org/nuclearfog/twidda/backend/async/InstanceLoader.java @@ -14,13 +14,13 @@ import org.nuclearfog.twidda.model.Instance; * * @author nuclearfog */ -public class InstanceLoader extends AsyncExecutor { +public class InstanceLoader extends AsyncExecutor { /** * time difference to update instance information * if database instance is older than this time, an update will be triggered */ - private static final long MAX_TIME_DIFF = 1000 * 60 * 60 * 24 * 2; + private static final long MAX_TIME_DIFF = 172800000L; private Connection connection; private AppDatabase db; @@ -35,40 +35,17 @@ public class InstanceLoader extends AsyncExecutor= MAX_TIME_DIFF) { + instance = connection.getInformation(); + db.saveInstance(instance); } } catch (Exception exception) { exception.printStackTrace(); } return instance; } - - /** - * - */ - public static class InstanceLoaderParam { - - public static final int LOAD_DB = 1; - public static final int LOAD_ONLINE = 2; - - final int mode; - final String domain; - - public InstanceLoaderParam(int mode, String domain) { - this.domain = domain; - this.mode = mode; - } - } } \ No newline at end of file diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/helper/StatusUpdate.java b/app/src/main/java/org/nuclearfog/twidda/backend/helper/StatusUpdate.java index 163f82f0..e5c4f76b 100644 --- a/app/src/main/java/org/nuclearfog/twidda/backend/helper/StatusUpdate.java +++ b/app/src/main/java/org/nuclearfog/twidda/backend/helper/StatusUpdate.java @@ -9,8 +9,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.documentfile.provider.DocumentFile; -import org.nuclearfog.twidda.config.Configuration; -import org.nuclearfog.twidda.config.GlobalSettings; +import org.nuclearfog.twidda.model.Instance; import java.io.InputStream; import java.util.ArrayList; @@ -73,6 +72,8 @@ public class StatusUpdate { private PollUpdate poll; @Nullable private LocationUpdate location; + @Nullable + private Instance instance; private int attachment = EMPTY; private List mediaUris = new ArrayList<>(5); @@ -107,8 +108,7 @@ public class StatusUpdate { */ public int addMedia(Context context, Uri mediaUri) { String mime = context.getContentResolver().getType(mediaUri); - Configuration configuration = GlobalSettings.getInstance(context).getLogin().getConfiguration(); - if (mime == null) { + if (mime == null || instance == null) { return MEDIA_ERROR; } // check if file is a 'gif' image @@ -121,7 +121,7 @@ public class StatusUpdate { DocumentFile file = DocumentFile.fromSingleUri(context, mediaUri); if (file != null && file.length() > 0) { mediaUris.add(mediaUri); - if (mediaUris.size() == configuration.getGifLimit()) { + if (mediaUris.size() == instance.getGifLimit()) { attachmentLimitReached = true; } return MEDIA_GIF; @@ -140,7 +140,7 @@ public class StatusUpdate { DocumentFile file = DocumentFile.fromSingleUri(context, mediaUri); if (file != null && file.length() > 0) { mediaUris.add(mediaUri); - if (mediaUris.size() == configuration.getImageLimit()) { + if (mediaUris.size() == instance.getImageLimit()) { attachmentLimitReached = true; } return MEDIA_IMAGE; @@ -158,7 +158,7 @@ public class StatusUpdate { DocumentFile file = DocumentFile.fromSingleUri(context, mediaUri); if (file != null && file.length() > 0) { mediaUris.add(mediaUri); - if (mediaUris.size() == configuration.getVideoLimit()) { + if (mediaUris.size() == instance.getVideoLimit()) { attachmentLimitReached = true; } return MEDIA_VIDEO; @@ -204,17 +204,28 @@ public class StatusUpdate { } /** + * set spoiler flag */ public void setSpoiler(boolean spoiler) { this.spoiler = spoiler; } /** + * set sensitive flag */ public void setSensitive(boolean sensitive) { this.sensitive = sensitive; } + /** + * set instance imformation such as status limitations + * + * @param instance instance imformation + */ + public void setInstanceInformation(Instance instance) { + this.instance = instance; + } + /** * get ID of the replied status * diff --git a/app/src/main/java/org/nuclearfog/twidda/config/Configuration.java b/app/src/main/java/org/nuclearfog/twidda/config/Configuration.java index 6aa16a4f..ac43737b 100644 --- a/app/src/main/java/org/nuclearfog/twidda/config/Configuration.java +++ b/app/src/main/java/org/nuclearfog/twidda/config/Configuration.java @@ -41,9 +41,6 @@ public enum Configuration { private final boolean statusSpoilerSupported; private final boolean statusVisibilitySupported; private final boolean directMessageSupported; - private final int maxImages; - private final int maxGifs; - private final int maxVideos; /** * @param accountType account login type, see {@link Account} @@ -64,9 +61,6 @@ public enum Configuration { statusSpoilerSupported = false; statusVisibilitySupported = false; directMessageSupported = true; - maxImages = 4; - maxGifs = 1; - maxVideos = 1; break; default: @@ -82,9 +76,6 @@ public enum Configuration { statusSpoilerSupported = true; statusVisibilitySupported = true; directMessageSupported = false; - maxImages = 4; - maxGifs = 1; - maxVideos = 1; break; } } @@ -172,25 +163,4 @@ public enum Configuration { public boolean directmessageSupported() { return directMessageSupported; } - - /** - * @return image limit for posts - */ - public int getImageLimit() { - return maxImages; - } - - /** - * @return video limit for posts - */ - public int getVideoLimit() { - return maxVideos; - } - - /** - * @return gif limit for posts - */ - public int getGifLimit() { - return maxGifs; - } } \ No newline at end of file diff --git a/app/src/main/java/org/nuclearfog/twidda/database/AppDatabase.java b/app/src/main/java/org/nuclearfog/twidda/database/AppDatabase.java index 73563c17..23450dfb 100644 --- a/app/src/main/java/org/nuclearfog/twidda/database/AppDatabase.java +++ b/app/src/main/java/org/nuclearfog/twidda/database/AppDatabase.java @@ -915,14 +915,13 @@ public class AppDatabase { /** * get a single instance of a domain * - * @param domain domain name of the instance * @return instance or null if not found */ @Nullable - public Instance getInstance(String domain) { + public Instance getInstance() { synchronized (LOCK) { SQLiteDatabase db = adapter.getDbRead(); - String[] args = {domain}; + String[] args = {settings.getLogin().getHostname()}; Instance result = null; Cursor cursor = db.query(InstanceTable.NAME, DatabaseInstance.COLUMNS, INSTANCE_SELECTION, args, null, null, null); if (cursor.moveToFirst()) { diff --git a/app/src/main/java/org/nuclearfog/twidda/ui/activities/StatusEditor.java b/app/src/main/java/org/nuclearfog/twidda/ui/activities/StatusEditor.java index 55027708..a267db3f 100644 --- a/app/src/main/java/org/nuclearfog/twidda/ui/activities/StatusEditor.java +++ b/app/src/main/java/org/nuclearfog/twidda/ui/activities/StatusEditor.java @@ -22,6 +22,7 @@ import androidx.recyclerview.widget.RecyclerView; import org.nuclearfog.twidda.R; import org.nuclearfog.twidda.backend.async.AsyncExecutor.AsyncCallback; +import org.nuclearfog.twidda.backend.async.InstanceLoader; import org.nuclearfog.twidda.backend.async.StatusUpdater; import org.nuclearfog.twidda.backend.async.StatusUpdater.StatusUpdateResult; import org.nuclearfog.twidda.backend.helper.PollUpdate; @@ -29,6 +30,7 @@ import org.nuclearfog.twidda.backend.helper.StatusUpdate; import org.nuclearfog.twidda.backend.utils.AppStyles; import org.nuclearfog.twidda.backend.utils.ErrorHandler; import org.nuclearfog.twidda.config.GlobalSettings; +import org.nuclearfog.twidda.model.Instance; import org.nuclearfog.twidda.ui.adapter.IconAdapter; import org.nuclearfog.twidda.ui.adapter.IconAdapter.OnMediaClickListener; import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog; @@ -45,7 +47,7 @@ import org.nuclearfog.twidda.ui.dialogs.StatusPreferenceDialog; * @author nuclearfog */ public class StatusEditor extends MediaActivity implements OnClickListener, OnProgressStopListener, OnConfirmListener, - OnMediaClickListener, AsyncCallback, TextWatcher, PollUpdateCallback { + OnMediaClickListener, TextWatcher, PollUpdateCallback { /** * key to add a statusd ID to reply @@ -59,14 +61,18 @@ public class StatusEditor extends MediaActivity implements OnClickListener, OnPr */ public static final String KEY_STATUS_EDITOR_TEXT = "status_text"; + private AsyncCallback statusUpdateResult = this::onStatusUpdated; + private AsyncCallback instanceResult = this::onInstanceResult; + private View mediaBtn; private View locationBtn; private View pollBtn; private View locationPending; - private StatusUpdater uploaderAsync; - private GlobalSettings settings; + private StatusUpdater statusUpdater; + private InstanceLoader instanceLoader; + private GlobalSettings settings; private ConfirmDialog confirmDialog; private ProgressDialog loadingCircle; private PollDialog pollDialog; @@ -98,7 +104,8 @@ public class StatusEditor extends MediaActivity implements OnClickListener, OnPr mediaBtn = findViewById(R.id.popup_status_add_media); locationPending = findViewById(R.id.popup_status_location_loading); - uploaderAsync = new StatusUpdater(this); + instanceLoader = new InstanceLoader(this); + statusUpdater = new StatusUpdater(this); settings = GlobalSettings.getInstance(this); loadingCircle = new ProgressDialog(this); confirmDialog = new ConfirmDialog(this); @@ -120,6 +127,8 @@ public class StatusEditor extends MediaActivity implements OnClickListener, OnPr iconList.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, true)); iconList.setAdapter(adapter); + instanceLoader.execute(null, instanceResult); + statusText.addTextChangedListener(this); closeButton.setOnClickListener(this); preference.setOnClickListener(this); @@ -151,7 +160,8 @@ public class StatusEditor extends MediaActivity implements OnClickListener, OnPr @Override protected void onDestroy() { loadingCircle.dismiss(); - uploaderAsync.cancel(); + statusUpdater.cancel(); + instanceLoader.cancel(); super.onDestroy(); } @@ -175,7 +185,7 @@ public class StatusEditor extends MediaActivity implements OnClickListener, OnPr Toast.makeText(getApplicationContext(), R.string.info_location_pending, Toast.LENGTH_SHORT).show(); } // check if gps locating is not pending - else if (uploaderAsync.isIdle()) { + else if (statusUpdater.isIdle()) { updateStatus(); } } @@ -223,6 +233,7 @@ public class StatusEditor extends MediaActivity implements OnClickListener, OnPr @Override public void afterTextChanged(Editable s) { statusUpdate.addText(s.toString()); + // todo add character limit check } @@ -272,7 +283,7 @@ public class StatusEditor extends MediaActivity implements OnClickListener, OnPr @Override public void stopProgress() { - uploaderAsync.cancel(); + statusUpdater.cancel(); } @@ -310,19 +321,6 @@ public class StatusEditor extends MediaActivity implements OnClickListener, OnPr } - @Override - public void onResult(@NonNull StatusUpdateResult result) { - if (result.success) { - Toast.makeText(getApplicationContext(), R.string.info_status_sent, Toast.LENGTH_LONG).show(); - finish(); - } else { - String message = ErrorHandler.getErrorMessage(this, result.exception); - confirmDialog.show(ConfirmDialog.STATUS_EDITOR_ERROR, message); - loadingCircle.dismiss(); - } - } - - @Override public void onPollUpdate(@Nullable PollUpdate update) { statusUpdate.addPoll(update); @@ -339,6 +337,27 @@ public class StatusEditor extends MediaActivity implements OnClickListener, OnPr } } + /** + * called when the status was successfully updated + */ + private void onStatusUpdated(@NonNull StatusUpdateResult result) { + if (result.success) { + Toast.makeText(getApplicationContext(), R.string.info_status_sent, Toast.LENGTH_LONG).show(); + finish(); + } else { + String message = ErrorHandler.getErrorMessage(this, result.exception); + confirmDialog.show(ConfirmDialog.STATUS_EDITOR_ERROR, message); + loadingCircle.dismiss(); + } + } + + /** + * set instance information such as upload limits + */ + private void onInstanceResult(Instance instance) { + statusUpdate.setInstanceInformation(instance); + } + /** * start uploading status and media files */ @@ -346,7 +365,7 @@ public class StatusEditor extends MediaActivity implements OnClickListener, OnPr // first initialize filestreams of the media files if (statusUpdate.prepare(getContentResolver())) { // send status - uploaderAsync.execute(statusUpdate, this); + statusUpdater.execute(statusUpdate, statusUpdateResult); // show progress dialog loadingCircle.show(); } else { diff --git a/app/src/main/java/org/nuclearfog/twidda/ui/activities/VideoViewer.java b/app/src/main/java/org/nuclearfog/twidda/ui/activities/VideoViewer.java index 9c5cdf57..25e951f2 100644 --- a/app/src/main/java/org/nuclearfog/twidda/ui/activities/VideoViewer.java +++ b/app/src/main/java/org/nuclearfog/twidda/ui/activities/VideoViewer.java @@ -24,8 +24,10 @@ import android.content.DialogInterface.OnDismissListener; import android.content.Intent; import android.graphics.PixelFormat; import android.location.Location; +import android.media.AudioManager; import android.media.MediaPlayer; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.os.Parcelable; import android.view.MotionEvent; @@ -321,6 +323,10 @@ public class VideoViewer extends MediaActivity implements OnSeekBarChangeListene } // setup video looping for gif else { + // disable audiofocus for gif + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){ + videoView.setAudioFocusRequest(AudioManager.AUDIOFOCUS_NONE); + } loadingCircle.setVisibility(INVISIBLE); mp.setLooping(true); mp.start();