finalized instance implementation

This commit is contained in:
nuclearfog 2023-04-09 19:56:07 +02:00
parent 5d68a6387e
commit 8bfd045f0a
No known key found for this signature in database
GPG Key ID: 03488A185C476379
11 changed files with 156 additions and 97 deletions

View File

@ -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 + "\"";
}
}

View File

@ -257,7 +257,7 @@ public class TwitterV1 implements Connection {
@Override
public Instance getInformation() {
return new TwitterV1Instance();
return new TwitterV1Instance(settings.getLogin().getHostname());
}

View File

@ -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\"";
}
}

View File

@ -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());
}

View File

@ -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\"";
}
}

View File

@ -14,13 +14,13 @@ import org.nuclearfog.twidda.model.Instance;
*
* @author nuclearfog
*/
public class InstanceLoader extends AsyncExecutor<InstanceLoader.InstanceLoaderParam, Instance> {
public class InstanceLoader extends AsyncExecutor<Void, Instance> {
/**
* 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<InstanceLoader.InstanceLoaderP
@Override
protected Instance doInBackground(@NonNull InstanceLoaderParam param) {
protected Instance doInBackground(@NonNull Void param) {
Instance instance = null;
try {
switch (param.mode) {
case InstanceLoaderParam.LOAD_DB:
instance = db.getInstance(param.domain);
if (instance != null && (System.currentTimeMillis() - instance.getTimestamp()) < MAX_TIME_DIFF)
break;
// fall through
case InstanceLoaderParam.LOAD_ONLINE:
instance = connection.getInformation();
break;
instance = db.getInstance();
if (instance == null || (System.currentTimeMillis() - instance.getTimestamp()) >= 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;
}
}
}

View File

@ -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<Uri> 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
*

View File

@ -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;
}
}

View File

@ -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()) {

View File

@ -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<StatusUpdateResult>, 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> statusUpdateResult = this::onStatusUpdated;
private AsyncCallback<Instance> 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 {

View File

@ -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();