Merge branch 'develop'

Media3 3.26.0
Thomas 11 months ago
commit a1370693fb

@ -13,8 +13,8 @@ android {
defaultConfig {
minSdk 21
targetSdk 34
versionCode 501
versionName "3.25.0"
versionCode 505
versionName "3.26.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
flavorDimensions "default"

@ -193,7 +193,11 @@ public abstract class PeertubeBaseMainActivity extends BaseActivity implements C
super.onDestroy();
ChromeCasts.unregisterListener(this);
if (manage_chromecast != null) {
try {
unregisterReceiver(manage_chromecast);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
new Thread(() -> {
if (chromeCasts != null && chromeCasts.size() > 0) {
for (ChromeCast cast : chromeCasts) {

@ -1,4 +1,24 @@
[
{
"version": "3.26.0",
"code": "505",
"note": "Added:\n- Android 14 support\n- Automatically split long messages in threads (default: ASK)\n- Links and media are clickable when composing\n- Allow to underline clickable elements (Settings > Timelines - default: disabled)\n- Allow to disable relative date in messages\n- Add a scroll bar for timelines (default: disabled)\n- Add a search bar for custom emojis\n- Links clickable in media descriptions\n\nChanged:\n- Counters close to action buttons\n- Hide emoji picker if the instance has no emoji\n- Followed tags are ordered\n- Account picker when opening with another account\n\nFixed:\n- Avoid error 429 with NTFY\n- Fix custom colors (Android 14)\n- Fix a crash when composing\n- Display issue with followed tags\n- Crashes with profiles\n- Fix an issue with poll and Pleroma\n- Emoji not displayed in the picker\n- Several crashes are fixed"
},
{
"version": "3.25.3",
"code": "504",
"note": "Added:\n- Add a scroll bar for timelines (default: disabled)\n- Add a search bar for custom emojis\n\nFixed:\n- Fix prompt to split asked several times when refusing\n- Crashes with profiles\n- Fix an issue with poll and Pleroma\n- Emoji not displayed in the picker"
},
{
"version": "3.25.2",
"code": "503",
"note": "Added:\n- Allow to underline clickable elements (Settings > Timelines - default: disabled)\n- Allow to disable relative date in messages\n\nChanged:\n- Counters close to action buttons\n- Hide emoji picker if the instance has no emoji\n- Followed tags are ordered\n- Account picker when opening with another account\n\nFixed:\n- Fix a crash when composing\n- Fix an issue with the back button\n- Display issue with followed tags"
},
{
"version": "3.25.1",
"code": "502",
"note": "Fix a crash from release 3.25.0"
},
{
"version": "3.25.0",
"code": "501",

@ -36,6 +36,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.database.MatrixCursor;
import android.graphics.PorterDuff;
import android.graphics.drawable.BitmapDrawable;
@ -214,6 +215,7 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
public static BaseAccount currentAccount;
public static iconLauncher mLauncher = iconLauncher.BUBBLES;
public static boolean headerMenuOpen;
public static int currentNightMode;
Fragment currentFragment;
private AppBarConfiguration mAppBarConfiguration;
private ActivityMainBinding binding;
@ -1859,9 +1861,12 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
@Override
protected void onDestroy() {
try {
unregisterReceiver(broadcast_data);
unregisterReceiver(broadcast_error_message);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
if (networkStateReceiver != null) {
try {
unregisterReceiver(networkStateReceiver);

@ -15,12 +15,16 @@ package app.fedilab.android.activities;
* see <http://www.gnu.org/licenses>. */
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;
import androidx.core.app.ActivityOptionsCompat;
import androidx.lifecycle.ViewModelProvider;
@ -40,6 +44,7 @@ import app.fedilab.android.mastodon.helper.CrossActionHelper;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.helper.MastodonHelper;
import app.fedilab.android.mastodon.viewmodel.mastodon.AccountsVM;
import es.dmoral.toasty.Toasty;
public class AboutActivity extends BaseBarActivity {
@ -58,10 +63,10 @@ public class AboutActivity extends BaseBarActivity {
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
String version = "";
try {
PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
String version = pInfo.versionName;
version = pInfo.versionName;
binding.aboutVersion.setText(getResources().getString(R.string.about_vesrion, version));
} catch (PackageManager.NameNotFoundException ignored) {
}
@ -77,6 +82,20 @@ public class AboutActivity extends BaseBarActivity {
}
binding.aboutSupportPaypal.setOnClickListener(v -> Helper.openBrowser(AboutActivity.this, "https://www.paypal.me/Mastalab"));
String finalVersion = version;
binding.aboutVersionCopy.setOnClickListener(v->{
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
String content = "Fedilab v" + finalVersion + " for " + (BuildConfig.DONATIONS?"FDroid":"Google");
ClipData clip = ClipData.newPlainText(Helper.CLIP_BOARD, content);
if (clipboard != null) {
clipboard.setPrimaryClip(clip);
Toasty.info(AboutActivity.this, getString(R.string.clipboard_version), Toast.LENGTH_LONG).show();
}
});
if (BuildConfig.DONATIONS) {
binding.aboutSupportPaypal.setVisibility(View.VISIBLE);
} else {

@ -15,6 +15,8 @@ package app.fedilab.android.mastodon.activities;
* see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.BaseMainActivity.currentNightMode;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
@ -37,6 +39,7 @@ import org.conscrypt.Conscrypt;
import java.security.Security;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.helper.ThemeHelper;
@ -70,7 +73,7 @@ public class BaseActivity extends AppCompatActivity {
String currentTheme = sharedpreferences.getString(getString(R.string.SET_THEME_BASE), getString(R.string.SET_DEFAULT_THEME));
//Default automatic switch
int currentNightMode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
currentNightMode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
if (currentTheme.equals(getString(R.string.SET_DEFAULT_THEME))) {
switch (currentNightMode) {
case Configuration.UI_MODE_NIGHT_NO -> {

@ -15,6 +15,8 @@ package app.fedilab.android.mastodon.activities;
* see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.BaseMainActivity.currentNightMode;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
@ -65,7 +67,7 @@ public class BaseBarActivity extends AppCompatActivity {
}
String currentTheme = sharedpreferences.getString(getString(R.string.SET_THEME_BASE), getString(R.string.SET_DEFAULT_THEME));
//Default automatic switch
int currentNightMode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
currentNightMode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
if (currentTheme.equals(getString(R.string.SET_DEFAULT_THEME))) {
switch (currentNightMode) {
case Configuration.UI_MODE_NIGHT_NO -> {

@ -15,6 +15,8 @@ package app.fedilab.android.mastodon.activities;
* see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.BaseMainActivity.currentNightMode;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
@ -65,7 +67,7 @@ public class BaseTransparentActivity extends AppCompatActivity {
}
String currentTheme = sharedpreferences.getString(getString(R.string.SET_THEME_BASE), getString(R.string.SET_DEFAULT_THEME));
//Default automatic switch
int currentNightMode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
currentNightMode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
if (currentTheme.equals(getString(R.string.SET_DEFAULT_THEME))) {
switch (currentNightMode) {

@ -195,8 +195,11 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
if (timer != null) {
timer.cancel();
}
try {
unregisterReceiver(imageReceiver);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}

@ -113,6 +113,12 @@ public class ContextActivity extends BaseActivity implements FragmentMastodonCon
}
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.clear();
}
private void loadLocalConversation() {
Bundle bundle = new Bundle();
bundle.putSerializable(Helper.ARG_STATUS, focusedStatus);

@ -32,11 +32,15 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivityFollowedTagsBinding;
import app.fedilab.android.databinding.PopupAddFollowedTagtBinding;
import app.fedilab.android.mastodon.client.entities.api.MastodonList;
import app.fedilab.android.mastodon.client.entities.api.Tag;
import app.fedilab.android.mastodon.client.entities.app.Timeline;
import app.fedilab.android.mastodon.helper.Helper;
@ -74,6 +78,7 @@ public class FollowedTagActivity extends BaseBarActivity implements FollowedTagA
.observe(FollowedTagActivity.this, tags -> {
if (tags != null && tags.tags != null && tags.tags.size() > 0) {
tagList = new ArrayList<>(tags.tags);
sortAsc(tagList);
followedTagAdapter = new FollowedTagAdapter(tagList);
followedTagAdapter.actionOnTag = this;
binding.notContent.setVisibility(View.GONE);
@ -95,6 +100,8 @@ public class FollowedTagActivity extends BaseBarActivity implements FollowedTagA
});
setTitle(R.string.followed_tags);
invalidateOptionsMenu();
} else {
finish();
}
}
});
@ -143,6 +150,11 @@ public class FollowedTagActivity extends BaseBarActivity implements FollowedTagA
dialogBuilder.setView(popupAddFollowedTagtBinding.getRoot());
popupAddFollowedTagtBinding.addTag.setFilters(new InputFilter[]{new InputFilter.LengthFilter(255)});
dialogBuilder.setPositiveButton(R.string.validate, (dialog, id) -> {
String name = Objects.requireNonNull(popupAddFollowedTagtBinding.addTag.getText()).toString().trim();
if(tagList.contains(new Tag(name))) {
Toasty.error(FollowedTagActivity.this, getString(R.string.tag_already_followed), Toasty.LENGTH_LONG).show();
return;
}
if (popupAddFollowedTagtBinding.addTag.getText() != null && popupAddFollowedTagtBinding.addTag.getText().toString().trim().length() > 0) {
tagVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, popupAddFollowedTagtBinding.addTag.getText().toString().trim())
.observe(FollowedTagActivity.this, newTag -> {
@ -159,8 +171,10 @@ public class FollowedTagActivity extends BaseBarActivity implements FollowedTagA
if (newTag != null) {
tagList.add(0, newTag);
followedTagAdapter.notifyItemInserted(0);
sortAsc(tagList);
followedTagAdapter.notifyItemRangeChanged(0, tagList.size());
} else {
Toasty.error(FollowedTagActivity.this, getString(R.string.toast_feature_not_supported), Toasty.LENGTH_LONG).show();
Toasty.error(FollowedTagActivity.this, getString(R.string.not_valid_tag_name), Toasty.LENGTH_LONG).show();
}
});
dialog.dismiss();
@ -175,6 +189,9 @@ public class FollowedTagActivity extends BaseBarActivity implements FollowedTagA
return super.onOptionsItemSelected(item);
}
private void sortAsc(List<Tag> tagList) {
Collections.sort(tagList, (obj1, obj2) -> obj1.name.compareToIgnoreCase(obj2.name));
}
@Override
public boolean onCreateOptionsMenu(@NonNull Menu menu) {

@ -143,6 +143,8 @@ public class MastodonListActivity extends BaseBarActivity implements MastodonLis
});
setTitle(R.string.action_lists);
invalidateOptionsMenu();
} else {
finish();
}
}
});

@ -14,6 +14,8 @@ package app.fedilab.android.mastodon.activities;
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import static android.util.Patterns.WEB_URL;
import android.Manifest;
import android.app.DownloadManager;
import android.content.BroadcastReceiver;
@ -21,12 +23,18 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.text.method.ScrollingMovementMethod;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextPaint;
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
@ -34,6 +42,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
@ -42,11 +51,13 @@ import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter;
import androidx.preference.PreferenceManager;
import androidx.viewpager.widget.ViewPager;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.regex.Matcher;
import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivityMediaPagerBinding;
@ -152,8 +163,8 @@ public class MediaActivity extends BaseTransparentActivity implements OnDownload
});
}
binding.mediaDescription.setMovementMethod(new ScrollingMovementMethod());
binding.mediaDescriptionTranslated.setMovementMethod(new ScrollingMovementMethod());
binding.mediaDescription.setMovementMethod(LinkMovementMethod.getInstance());
binding.mediaDescriptionTranslated.setMovementMethod(LinkMovementMethod.getInstance());
if (description != null && description.trim().length() > 0 && description.trim().compareTo("null") != 0) {
binding.mediaDescription.setText(description);
@ -195,7 +206,7 @@ public class MediaActivity extends BaseTransparentActivity implements OnDownload
}
handler = new Handler();
if (description != null && description.trim().length() > 0 && description.trim().compareTo("null") != 0) {
binding.mediaDescription.setText(description);
binding.mediaDescription.setText(linkify(MediaActivity.this, description), TextView.BufferType.SPANNABLE);
}
binding.translate.setOnClickListener(v -> {
String descriptionToTranslate = attachments.get(position).description;
@ -228,6 +239,40 @@ public class MediaActivity extends BaseTransparentActivity implements OnDownload
setFullscreen(true);
}
private Spannable linkify(Context context, String content) {
if(content == null) {
return new SpannableString("");
}
Matcher matcher = WEB_URL.matcher(content);
Spannable contentSpan = new SpannableString(content);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
boolean underlineLinks = sharedpreferences.getBoolean(context.getString(R.string.SET_UNDERLINE_CLICKABLE), false);
while (matcher.find()) {
int matchStart = matcher.start();
int matchEnd = matcher.end();
String url = content.substring(matchStart, matchEnd);
if (matchStart >= 0 && matchEnd <= content.length() && matchEnd >= matchStart) {
contentSpan.setSpan(new ClickableSpan() {
@Override
public void onClick(@NonNull View textView) {
Helper.openBrowser(context, url);
}
@Override
public void updateDrawState(@NonNull TextPaint ds) {
super.updateDrawState(ds);
if(!underlineLinks) {
ds.setUnderlineText(status != null && status.underlined);
}
}
}, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
}
}
return contentSpan;
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
try {
@ -337,7 +382,7 @@ public class MediaActivity extends BaseTransparentActivity implements OnDownload
if (!fullscreen) {
String description = attachments.get(binding.mediaViewpager.getCurrentItem()).description;
if (description != null && description.trim().length() > 0 && description.trim().compareTo("null") != 0) {
binding.mediaDescription.setText(description);
binding.mediaDescription.setText(linkify(MediaActivity.this, description), TextView.BufferType.SPANNABLE);
if (attachments.get(binding.mediaViewpager.getCurrentItem()).translation != null) {
binding.mediaDescription.setVisibility(View.GONE);
binding.mediaDescriptionTranslated.setText(attachments.get(binding.mediaViewpager.getCurrentItem()).translation);
@ -367,7 +412,11 @@ public class MediaActivity extends BaseTransparentActivity implements OnDownload
@Override
public void onDestroy() {
try {
unregisterReceiver(onDownloadComplete);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
super.onDestroy();
}

@ -234,6 +234,12 @@ public class ProfileActivity extends BaseActivity {
}
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.clear();
}
private void initializeView(Account account) {
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(ProfileActivity.this);
if (account == null) {
@ -1260,7 +1266,11 @@ public class ProfileActivity extends BaseActivity {
scheduledExecutorService.shutdownNow();
scheduledExecutorService = null;
}
try {
unregisterReceiver(broadcast_data);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
super.onDestroy();
}

@ -116,6 +116,8 @@ public class AdminActionActivity extends BaseBarActivity {
setTitle(R.string.administration);
invalidateOptionsMenu();
});
} else {
finish();
}
}
});
@ -340,7 +342,11 @@ public class AdminActionActivity extends BaseBarActivity {
protected void onDestroy() {
super.onDestroy();
if (mReceiver != null) {
try {
unregisterReceiver(mReceiver);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}
}

@ -14,21 +14,40 @@ package app.fedilab.android.mastodon.client.entities.api;
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.BaseMainActivity.emojis;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteBlobTooBigException;
import android.database.sqlite.SQLiteDatabase;
import android.os.Handler;
import android.os.Looper;
import androidx.annotation.NonNull;
import androidx.lifecycle.ViewModelProvider;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken;
import java.io.Serializable;
import java.net.IDN;
import java.util.ArrayList;
import java.util.List;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.mastodon.client.endpoints.MastodonInstanceService;
import app.fedilab.android.mastodon.exception.DBException;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.viewmodel.mastodon.InstancesVM;
import app.fedilab.android.sqlite.Sqlite;
import okhttp3.OkHttpClient;
import retrofit2.Call;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class EmojiInstance implements Serializable {
@ -170,6 +189,17 @@ public class EmojiInstance implements Serializable {
}
}
private MastodonInstanceService init(String instance) {
final OkHttpClient okHttpClient = Helper.myOkHttpClient(context);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://" + (instance != null ? IDN.toASCII(instance, IDN.ALLOW_UNASSIGNED) : null) + "/api/v1/")
.addConverterFactory(GsonConverterFactory.create(Helper.getDateBuilder()))
.client(okHttpClient)
.build();
return retrofit.create(MastodonInstanceService.class);
}
/**
* Returns the emojis for an instance
*
@ -184,10 +214,77 @@ public class EmojiInstance implements Serializable {
Cursor c = db.query(Sqlite.TABLE_EMOJI_INSTANCE, null, Sqlite.COL_INSTANCE + " = '" + instance + "'", null, null, null, null, "1");
return cursorToEmojiList(c);
} catch (Exception e) {
MastodonInstanceService mastodonInstanceService = init(instance);
Call<List<Emoji>> emojiCall = mastodonInstanceService.customEmoji();
if (emojiCall != null) {
try {
Response<List<Emoji>> emojiResponse = emojiCall.execute();
if (emojiResponse.isSuccessful()) {
return emojiResponse.body();
}
} catch (Exception err) {
err.printStackTrace();
}
}
return null;
}
}
public interface EmojiFilteredCallBack{
void get(List<Emoji> emojiList);
}
/**
* Returns the emojis for an instance
*
* @param instance String
* @param filter String
* @param callBack EmojiFilteredCallBack - Get filtered emojis
*
* @return List<Emoji> - List of {@link Emoji}
*/
public void getEmojiListFiltered(@NonNull String instance, @NonNull String filter, EmojiFilteredCallBack callBack) throws DBException {
if (db == null) {
throw new DBException("db is null. Wrong initialization.");
}
new Thread(() -> {
List<Emoji> emojiArrayList= new ArrayList<>();
List<Emoji> emojiFiltered= new ArrayList<>();
if (emojis == null || !emojis.containsKey(BaseMainActivity.currentInstance) || emojis.get(BaseMainActivity.currentInstance) == null) {
try {
Cursor c = db.query(Sqlite.TABLE_EMOJI_INSTANCE, null, Sqlite.COL_INSTANCE + " = '" + instance + "'", null, null, null, null, "1");
emojiArrayList = cursorToEmojiList(c);
} catch (Exception e) {
MastodonInstanceService mastodonInstanceService = init(instance);
Call<List<Emoji>> emojiCall = mastodonInstanceService.customEmoji();
if (emojiCall != null) {
try {
Response<List<Emoji>> emojiResponse = emojiCall.execute();
if (emojiResponse.isSuccessful()) {
emojiArrayList = emojiResponse.body();
}
} catch (Exception err) {
err.printStackTrace();
}
}
}
} else {
emojiArrayList = emojis.get(instance);
}
if(emojiArrayList != null && emojiArrayList.size() > 0 ) {
for(Emoji emoji: emojiArrayList) {
if(emoji.shortcode.contains(filter)) {
emojiFiltered.add(emoji);
}
}
}
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> callBack.get(emojiFiltered);
mainHandler.post(myRunnable);
}).start();
}
/**
* Restore emoji list from db
*

@ -32,6 +32,12 @@ public class Tag implements Serializable {
@SerializedName("following")
public boolean following = false;
public Tag() {}
public Tag(String name) {
this.name = name;
}
public int getWeight() {
int weight = 0;
if (history != null && history.size() > 0) {

@ -14,17 +14,15 @@ package app.fedilab.android.mastodon.helper;
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.mastodon.helper.Helper.mentionLongPattern;
import static app.fedilab.android.mastodon.helper.Helper.mentionPattern;
import static app.fedilab.android.mastodon.helper.Helper.mentionPatternALL;
import static app.fedilab.android.mastodon.helper.MastodonHelper.countWithEmoji;
import android.util.Patterns;
import java.util.ArrayList;
import java.util.regex.Matcher;
import app.fedilab.android.mastodon.ui.drawer.ComposeAdapter;
public class ComposeHelper {

@ -579,6 +579,20 @@ public class Helper {
return df.format(date);
}
/**
* Convert a date in String
*
* @param date Date
* @return String
*/
public static String mediumDateToString(Date date) {
if (date == null) {
date = new Date();
}
DateFormat df = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.getDefault());
return df.format(date);
}
/**
* Convert a date in String -> format yyyy-MM-dd HH:mm:ss
*

@ -16,6 +16,7 @@ package app.fedilab.android.mastodon.helper;
import static app.fedilab.android.BaseMainActivity.currentAccount;
import static app.fedilab.android.BaseMainActivity.currentNightMode;
import android.app.Activity;
import android.content.ClipData;
@ -104,6 +105,7 @@ public class SpannableHelper {
public static final String CLICKABLE_SPAN = "CLICKABLE_SPAN";
private static int linkColor;
private static boolean underlineLinks;
public static Spannable convert(Context context, String text,
Status status, Account account, Announcement announcement,
@ -112,9 +114,9 @@ public class SpannableHelper {
return null;
}
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
int currentNightMode = context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
boolean customLight = sharedpreferences.getBoolean(context.getString(R.string.SET_CUSTOMIZE_LIGHT_COLORS), false);
boolean customDark = sharedpreferences.getBoolean(context.getString(R.string.SET_CUSTOMIZE_DARK_COLORS), false);
underlineLinks = sharedpreferences.getBoolean(context.getString(R.string.SET_UNDERLINE_CLICKABLE), false);
int link_color;
if (currentNightMode == Configuration.UI_MODE_NIGHT_NO && customLight) {
link_color = sharedpreferences.getInt(context.getString(R.string.SET_LIGHT_LINK), -1);
@ -309,7 +311,9 @@ public class SpannableHelper {
@Override
public void updateDrawState(@NonNull TextPaint ds) {
super.updateDrawState(ds);
if(!underlineLinks) {
ds.setUnderlineText(status != null && status.underlined);
}
if (linkColor != -1) {
ds.setColor(linkColor);
}
@ -599,7 +603,9 @@ public class SpannableHelper {
@Override
public void updateDrawState(@NonNull TextPaint ds) {
super.updateDrawState(ds);
if(!underlineLinks) {
ds.setUnderlineText(status != null && status.underlined);
}
if (linkColor != -1) {
ds.setColor(linkColor);
}
@ -744,7 +750,9 @@ public class SpannableHelper {
@Override
public void updateDrawState(@NonNull TextPaint ds) {
super.updateDrawState(ds);
if(!underlineLinks) {
ds.setUnderlineText(status != null && status.underlined);
}
if (linkColor != -1) {
ds.setColor(linkColor);
}
@ -891,7 +899,9 @@ public class SpannableHelper {
@Override
public void updateDrawState(@NonNull TextPaint ds) {
super.updateDrawState(ds);
if(!underlineLinks) {
ds.setUnderlineText(false);
}
if (linkColor != -1) {
ds.setColor(linkColor);
}

@ -16,6 +16,7 @@ package app.fedilab.android.mastodon.helper;
import static android.content.Context.WINDOW_SERVICE;
import static app.fedilab.android.BaseMainActivity.currentInstance;
import static app.fedilab.android.BaseMainActivity.currentNightMode;
import static app.fedilab.android.BaseMainActivity.currentUserID;
import android.app.Activity;
@ -274,7 +275,7 @@ public class ThemeHelper {
public static void switchTo(String themePref) {
if (themes.LIGHT.name().equals(themePref) || themes.SOLARIZED_LIGHT.name().equals(themePref)) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
} else if (themes.DARK.name().equals(themePref) || themes.SOLARIZED_DARK.name().equals(themePref)) {
} else if (themes.DARK.name().equals(themePref) || themes.SOLARIZED_DARK.name().equals(themePref) || themes.DRACULA.name().equals(themePref) || themes.BLACK.name().equals(themePref)) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
@ -286,7 +287,6 @@ public class ThemeHelper {
}
public static void applyThemeColor(Activity activity) {
int currentNightMode = activity.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
final SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(activity);
boolean dynamicColor = sharedpreferences.getBoolean(activity.getString(R.string.SET_DYNAMICCOLOR), false);
boolean customAccentEnabled = sharedpreferences.getBoolean(activity.getString(R.string.SET_CUSTOM_ACCENT) + currentUserID + currentInstance, false);
@ -314,6 +314,8 @@ public class ThemeHelper {
public enum themes {
LIGHT,
DARK,
BLACK,
DRACULA,
SYSTEM,
SOLARIZED_LIGHT,
SOLARIZED_DARK

@ -59,6 +59,7 @@ import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.AppCompatEditText;
import androidx.appcompat.widget.LinearLayoutCompat;
import androidx.appcompat.widget.SearchView;
import androidx.core.app.ActivityCompat;
import androidx.core.app.ActivityOptionsCompat;
import androidx.core.content.ContextCompat;
@ -103,11 +104,13 @@ import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.databinding.ComposeAttachmentItemBinding;
import app.fedilab.android.databinding.ComposePollBinding;
import app.fedilab.android.databinding.ComposePollItemBinding;
import app.fedilab.android.databinding.CustomEmojiPickerBinding;
import app.fedilab.android.databinding.DrawerMediaListBinding;
import app.fedilab.android.databinding.DrawerStatusComposeBinding;
import app.fedilab.android.databinding.DrawerStatusSimpleBinding;
import app.fedilab.android.mastodon.activities.ComposeActivity;
import app.fedilab.android.mastodon.activities.MediaActivity;
import app.fedilab.android.mastodon.activities.SearchResultTabActivity;
import app.fedilab.android.mastodon.client.entities.api.Account;
import app.fedilab.android.mastodon.client.entities.api.Attachment;
import app.fedilab.android.mastodon.client.entities.api.Emoji;
@ -173,6 +176,8 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
private List<Emoji> emojisList = new ArrayList<>();
private boolean unlisted_changed = false;
private RecyclerView mRecyclerView;
private boolean proceedToSplit = false;
private boolean splitChoiceDone = false;
public ComposeAdapter(List<Status> statusList, int statusCount, BaseAccount account, Account mentionedAccount, String visibility, String editMessageId) {
@ -530,7 +535,6 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
TextWatcher textw;
AccountsVM accountsVM = new ViewModelProvider((ViewModelStoreOwner) context).get(AccountsVM.class);
SearchVM searchVM = new ViewModelProvider((ViewModelStoreOwner) context).get(SearchVM.class);
final boolean[] proceedToSplit = {false};
textw = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
@ -545,7 +549,8 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
String defaultFormat = sharedpreferences.getString(context.getString(R.string.SET_THREAD_MESSAGE), context.getString(R.string.DEFAULT_THREAD_VALUE));
//User asked to be prompted for threading long messages
if(defaultFormat.compareToIgnoreCase("ASK") == 0) {
if(defaultFormat.compareToIgnoreCase("ASK") == 0 && !splitChoiceDone) {
splitChoiceDone = true;
AlertDialog.Builder threadConfirm = new MaterialAlertDialogBuilder(context);
threadConfirm.setTitle(context.getString(R.string.thread_long_this_message));
threadConfirm.setMessage(context.getString(R.string.thread_long_message_message));
@ -569,7 +574,7 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
});
threadConfirm.show();
} else if(defaultFormat.compareToIgnoreCase("ENABLE") == 0) { //User wants to automatically thread long messages
proceedToSplit[0] = true;
proceedToSplit = true;
ArrayList<String> splitText = ComposeHelper.splitToots(s.toString(), max_car);
int statusListSize = statusList.size();
int i = 0;
@ -589,7 +594,7 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
@Override
public void afterTextChanged(Editable s) {
String contentString = s.toString();
if(proceedToSplit[0]) {
if(proceedToSplit) {
int max_car = MastodonHelper.getInstanceMaxChars(context);
ArrayList<String> splitText = ComposeHelper.splitToots(contentString, max_car);
contentString = splitText.get(0);
@ -1381,7 +1386,7 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
}
holder.binding.statusContent.setText(
status.getSpanContent(context,
new WeakReference<>(holder.binding.statusContent), () -> notifyItemChanged(position)),
new WeakReference<>(holder.binding.statusContent), () -> mRecyclerView.post(() -> notifyItemChanged(position))),
TextView.BufferType.SPANNABLE);
holder.binding.statusContent.setMovementMethod(LongClickLinkMovementMethod.getInstance());
MastodonHelper.loadPPMastodon(holder.binding.avatar, status.account);
@ -1650,7 +1655,11 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
//Last compose drawer
buttonVisibility(holder);
if (emojis != null && emojis.size() > 0) {
holder.binding.buttonEmoji.setVisibility(View.VISIBLE);
} else {
holder.binding.buttonEmoji.setVisibility(View.GONE);
}
holder.binding.buttonEmoji.setOnClickListener(v -> {
try {
displayEmojiPicker(holder, account.instance);
@ -2113,29 +2122,57 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
* @param holder - view for the message {@link ComposeViewHolder}
*/
private void displayEmojiPicker(ComposeViewHolder holder, String instance) throws DBException {
final AlertDialog.Builder builder = new MaterialAlertDialogBuilder(context);
int paddingPixel = 15;
float density = context.getResources().getDisplayMetrics().density;
int paddingDp = (int) (paddingPixel * density);
builder.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
builder.setTitle(R.string.insert_emoji);
CustomEmojiPickerBinding customEmojiPickerBinding = CustomEmojiPickerBinding.inflate(LayoutInflater.from(context), new LinearLayout(context), false);
if (emojis != null && emojis.size() > 0) {
GridView gridView = new GridView(context);
gridView.setAdapter(new EmojiAdapter(emojis.get(instance)));
gridView.setNumColumns(5);
gridView.setOnItemClickListener((parent, view, position, id) -> {
customEmojiPickerBinding.gridview.setAdapter(new EmojiAdapter(emojis.get(instance)));
customEmojiPickerBinding.gridview.setOnItemClickListener((parent, view, position, id) -> {
holder.binding.content.getText().insert(holder.binding.content.getSelectionStart(), " :" + Objects.requireNonNull(emojis.get(instance)).get(position).shortcode + ": ");
alertDialogEmoji.dismiss();
});
gridView.setPadding(paddingDp, paddingDp, paddingDp, paddingDp);
builder.setView(gridView);
} else {
TextView textView = new TextView(context);
textView.setText(context.getString(R.string.no_emoji));
textView.setPadding(paddingDp, paddingDp, paddingDp, paddingDp);
builder.setView(textView);
}
customEmojiPickerBinding.toolbarSearch.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
InputMethodManager imm = (InputMethodManager) context.getSystemService(INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(customEmojiPickerBinding.toolbarSearch.getWindowToken(), 0);
try {
new EmojiInstance(context).getEmojiListFiltered(instance, query.trim(), emojiList -> {
if (emojiList != null && emojiList.size() > 0) {
customEmojiPickerBinding.gridview.setAdapter(new EmojiAdapter(emojiList));
customEmojiPickerBinding.gridview.setOnItemClickListener((parent, view, position, id) -> {
holder.binding.content.getText().insert(holder.binding.content.getSelectionStart(), " :" + emojiList.get(position).shortcode + ": ");
alertDialogEmoji.dismiss();
});
}
});
} catch (DBException e) {
throw new RuntimeException(e);
}
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
try {
new EmojiInstance(context).getEmojiListFiltered(instance, newText.trim(), emojiList -> {
if (emojiList != null && emojiList.size() > 0) {
customEmojiPickerBinding.gridview.setAdapter(new EmojiAdapter(emojiList));
customEmojiPickerBinding.gridview.setOnItemClickListener((parent, view, position, id) -> {
holder.binding.content.getText().insert(holder.binding.content.getSelectionStart(), " :" + emojiList.get(position).shortcode + ": ");
alertDialogEmoji.dismiss();
});
}
});
} catch (DBException e) {
throw new RuntimeException(e);
}
return false;
}
});
builder.setView(customEmojiPickerBinding.getRoot());
alertDialogEmoji = builder.show();
}

@ -15,6 +15,8 @@ package app.fedilab.android.mastodon.ui.drawer;
* see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.BaseMainActivity.currentNightMode;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
@ -72,7 +74,6 @@ public class ConversationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
public static void applyColorConversation(Context context, ConversationHolder holder) {
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
int currentNightMode = context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
boolean customLight = sharedpreferences.getBoolean(context.getString(R.string.SET_CUSTOMIZE_LIGHT_COLORS), false);
boolean customDark = sharedpreferences.getBoolean(context.getString(R.string.SET_CUSTOMIZE_DARK_COLORS), false);
int theme_icons_color = -1;

@ -15,6 +15,7 @@ package app.fedilab.android.mastodon.ui.drawer;
* see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.BaseMainActivity.currentNightMode;
import static app.fedilab.android.mastodon.ui.drawer.StatusAdapter.statusManagement;
import android.app.Activity;
@ -82,7 +83,6 @@ public class NotificationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
public static void applyColorAccount(Context context, ViewHolderFollow holder) {
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
int currentNightMode = context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
boolean customLight = sharedpreferences.getBoolean(context.getString(R.string.SET_CUSTOMIZE_LIGHT_COLORS), false);
boolean customDark = sharedpreferences.getBoolean(context.getString(R.string.SET_CUSTOMIZE_DARK_COLORS), false);
int theme_icons_color = -1;
@ -343,7 +343,6 @@ public class NotificationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
holderStatus.bindingNotification.status.typeOfNotification.setImageResource(R.drawable.ic_baseline_poll_24);
}
int theme_icons_color = -1;
int currentNightMode = context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
boolean customLight = sharedpreferences.getBoolean(context.getString(R.string.SET_CUSTOMIZE_LIGHT_COLORS), false);
boolean customDark = sharedpreferences.getBoolean(context.getString(R.string.SET_CUSTOMIZE_DARK_COLORS), false);
if (currentNightMode == Configuration.UI_MODE_NIGHT_NO) { //LIGHT THEME

@ -17,6 +17,7 @@ package app.fedilab.android.mastodon.ui.drawer;
import static android.content.Context.INPUT_METHOD_SERVICE;
import static app.fedilab.android.BaseMainActivity.currentAccount;
import static app.fedilab.android.BaseMainActivity.currentNightMode;
import static app.fedilab.android.BaseMainActivity.currentUserID;
import static app.fedilab.android.BaseMainActivity.emojis;
import static app.fedilab.android.BaseMainActivity.regex_home;
@ -57,7 +58,6 @@ import android.os.Looper;
import android.text.Html;
import android.text.SpannableString;
import android.text.TextUtils;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@ -303,51 +303,35 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
boolean remote) {
if (statusReturned == null) {
switch (typeOfAction) {
case BOOKMARK_ACTION:
statusToDeal.bookmarked = true;
break;
case REBLOG_ACTION:
case BOOKMARK_ACTION -> statusToDeal.bookmarked = true;
case REBLOG_ACTION -> {
statusToDeal.reblogged = true;
statusToDeal.reblogs_count++;
break;
case FAVOURITE_ACTION:
}
case FAVOURITE_ACTION -> {
statusToDeal.favourited = true;
statusToDeal.favourites_count++;
break;
case UNBOOKMARK_ACTION:
statusToDeal.bookmarked = false;
break;
case UNREBLOG_ACTION:
}
case UNBOOKMARK_ACTION -> statusToDeal.bookmarked = false;
case UNREBLOG_ACTION -> {
statusToDeal.reblogged = false;
statusToDeal.reblogs_count--;
break;
case UNFAVOURITE_ACTION:
}
case UNFAVOURITE_ACTION -> {
statusToDeal.favourited = false;
statusToDeal.favourites_count--;
break;
}
} else {
boolean isOK = true;
switch (typeOfAction) {
case BOOKMARK_ACTION:
isOK = statusReturned.bookmarked;
break;
case REBLOG_ACTION:
isOK = statusReturned.reblogged;
break;
case FAVOURITE_ACTION:
isOK = statusReturned.favourited;
break;
case UNBOOKMARK_ACTION:
isOK = !statusReturned.bookmarked;
break;
case UNREBLOG_ACTION:
isOK = !statusReturned.reblogged;
break;
case UNFAVOURITE_ACTION:
isOK = !statusReturned.favourited;
break;
}
} else {
boolean isOK = switch (typeOfAction) {
case BOOKMARK_ACTION -> statusReturned.bookmarked;
case REBLOG_ACTION -> statusReturned.reblogged;
case FAVOURITE_ACTION -> statusReturned.favourited;
case UNBOOKMARK_ACTION -> !statusReturned.bookmarked;
case UNREBLOG_ACTION -> !statusReturned.reblogged;
case UNFAVOURITE_ACTION -> !statusReturned.favourited;
default -> true;
};
if (!isOK) {
Toasty.error(context, context.getString(R.string.toast_error), Toasty.LENGTH_SHORT).show();
return;
@ -367,18 +351,10 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
}
} else {
switch (typeOfAction) {
case REBLOG_ACTION:
statusToDeal.reblogs_count++;
break;
case FAVOURITE_ACTION:
statusToDeal.favourites_count++;
break;
case UNREBLOG_ACTION:
statusToDeal.reblogs_count--;
break;
case UNFAVOURITE_ACTION:
statusToDeal.favourites_count--;
break;
case REBLOG_ACTION -> statusToDeal.reblogs_count++;
case FAVOURITE_ACTION -> statusToDeal.favourites_count++;
case UNREBLOG_ACTION -> statusToDeal.reblogs_count--;
case UNFAVOURITE_ACTION -> statusToDeal.favourites_count--;
}
}
}
@ -451,6 +427,7 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
boolean displayReactions = sharedpreferences.getBoolean(context.getString(R.string.SET_DISPLAY_REACTIONS) + MainActivity.currentUserID + MainActivity.currentInstance, true);
boolean compactButtons = sharedpreferences.getBoolean(context.getString(R.string.SET_DISPLAY_COMPACT_ACTION_BUTTON), false);
boolean originalDateForBoost = sharedpreferences.getBoolean(context.getString(R.string.SET_BOOST_ORIGINAL_DATE), true);
boolean relativeDate = sharedpreferences.getBoolean(context.getString(R.string.SET_DISPLAY_RELATIVE_DATE), true);
boolean hideSingleMediaWithCard = sharedpreferences.getBoolean(context.getString(R.string.SET_HIDE_SINGLE_MEDIA_WITH_CARD), false);
boolean autofetch = sharedpreferences.getBoolean(context.getString(R.string.SET_AUTO_FETCH_MISSING_MESSAGES), false);
boolean warnNoMedia = sharedpreferences.getBoolean(context.getString(R.string.SET_MANDATORY_ALT_TEXT_FOR_BOOSTS), true);
@ -769,12 +746,8 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
holder.binding.statusContentMaths.removeAllViews();
MathJaxConfig mathJaxConfig = new MathJaxConfig();
switch (context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) {
case Configuration.UI_MODE_NIGHT_YES:
mathJaxConfig.setTextColor("white");
break;
case Configuration.UI_MODE_NIGHT_NO:
mathJaxConfig.setTextColor("black");
break;
case Configuration.UI_MODE_NIGHT_YES -> mathJaxConfig.setTextColor("white");
case Configuration.UI_MODE_NIGHT_NO -> mathJaxConfig.setTextColor("black");
}
mathJaxConfig.setAutomaticLinebreaks(true);
@ -1020,9 +993,7 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
if (needToWarnForMissingDescription) {
AlertDialog.Builder alt_bld = new MaterialAlertDialogBuilder(context);
alt_bld.setMessage(context.getString(R.string.reblog_missing_description));
alt_bld.setPositiveButton(R.string.yes, (dialog, id) -> {
CrossActionHelper.doCrossAction(context, CrossActionHelper.TypeOfCrossAction.REBLOG_ACTION, null, statusToDeal);
});
alt_bld.setPositiveButton(R.string.yes, (dialog, id) -> CrossActionHelper.doCrossAction(context, CrossActionHelper.TypeOfCrossAction.REBLOG_ACTION, null, statusToDeal));
alt_bld.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss());
AlertDialog alert = alt_bld.create();
alert.show();
@ -1245,21 +1216,21 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
holder.binding.visibility.setContentDescription(context.getString(R.string.v_public));
holder.binding.visibilitySmall.setContentDescription(context.getString(R.string.v_public));
switch (status.visibility) {
case "unlisted":
case "unlisted" -> {
holder.binding.visibility.setContentDescription(context.getString(R.string.v_unlisted));
holder.binding.visibilitySmall.setContentDescription(context.getString(R.string.v_unlisted));
ressource = R.drawable.ic_baseline_lock_open_24;
break;
case "private":
}
case "private" -> {
ressource = R.drawable.ic_baseline_lock_24;
holder.binding.visibility.setContentDescription(context.getString(R.string.v_private));
holder.binding.visibilitySmall.setContentDescription(context.getString(R.string.v_private));
break;
case "direct":
}
case "direct" -> {
ressource = R.drawable.ic_baseline_mail_24;
holder.binding.visibility.setContentDescription(context.getString(R.string.v_direct));
holder.binding.visibilitySmall.setContentDescription(context.getString(R.string.v_direct));
break;
}
}
if (statusToDeal.local_only) {
@ -1309,28 +1280,33 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
holder.binding.editTime.setVisibility(View.GONE);
holder.binding.visibilitySmall.setImageResource(ressource);
if (displayCounters && canBeFederated) {
if (statusToDeal.replies_count > 0 && !(context instanceof ContextActivity)) {
holder.binding.replyCount.setVisibility(View.VISIBLE);
holder.binding.replyCount.setText(String.valueOf(statusToDeal.replies_count));
holder.binding.statusInfo.setVisibility(View.VISIBLE);
holder.binding.dateShort.setVisibility(View.GONE);
holder.binding.visibilitySmall.setVisibility(View.GONE);
holder.binding.reblogsCount.setText(String.valueOf(statusToDeal.reblogs_count));
holder.binding.favoritesCount.setText(String.valueOf(statusToDeal.favourites_count));
if (originalDateForBoost || status.reblog == null) {
holder.binding.time.setText(Helper.dateDiff(context, statusToDeal.created_at));
} else {
holder.binding.time.setText(Helper.dateDiff(context, status.created_at));
holder.binding.replyCount.setVisibility(View.GONE);
}
if (statusToDeal.edited_at != null) {
Drawable img = ContextCompat.getDrawable(context, R.drawable.ic_baseline_mode_edit_message_24);
img.setBounds(0, 0, (int) (Helper.convertDpToPixel(16, context) * scale + 0.5f), (int) (Helper.convertDpToPixel(16, context) * scale + 0.5f));
holder.binding.time.setCompoundDrawables(null, null, img, null);
if(statusToDeal.reblogs_count > 0) {
holder.binding.boostCount.setText(String.valueOf(statusToDeal.reblogs_count));
holder.binding.boostCount.setVisibility(View.VISIBLE);
} else {
holder.binding.time.setCompoundDrawables(null, null, null, null);
holder.binding.boostCount.setVisibility(View.GONE);
}
Helper.absoluteDateTimeReveal(context, holder.binding.time, statusToDeal.created_at, statusToDeal.edited_at);
holder.binding.visibility.setImageResource(ressource);
holder.binding.time.setVisibility(View.VISIBLE);
if(statusToDeal.favourites_count > 0) {
holder.binding.favoriteCount.setText(String.valueOf(statusToDeal.favourites_count));
holder.binding.favoriteCount.setVisibility(View.VISIBLE);
} else {
holder.binding.favoriteCount.setVisibility(View.GONE);
}
} else {
holder.binding.boostCount.setVisibility(View.GONE);
holder.binding.favoriteCount.setVisibility(View.GONE);
if (statusToDeal.replies_count > 0 && !(context instanceof ContextActivity)) {
holder.binding.replyCount.setVisibility(View.VISIBLE);
} else {
holder.binding.replyCount.setVisibility(View.GONE);
}
}
holder.binding.statusInfo.setVisibility(View.GONE);
holder.binding.dateShort.setVisibility(View.VISIBLE);
holder.binding.visibilitySmall.setVisibility(View.VISIBLE);
@ -1341,15 +1317,22 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
} else {
holder.binding.dateShort.setCompoundDrawables(null, null, null, null);
}
if(relativeDate) {
if (originalDateForBoost || status.reblog == null) {
holder.binding.dateShort.setText(Helper.dateDiff(context, statusToDeal.created_at));
} else {
holder.binding.dateShort.setText(Helper.dateDiff(context, status.created_at));
}
} else {
if (originalDateForBoost || status.reblog == null) {
holder.binding.dateShort.setText(Helper.mediumDateToString(statusToDeal.created_at));
} else {
holder.binding.dateShort.setText(Helper.mediumDateToString(status.created_at));
}
}
holder.binding.time.setVisibility(View.GONE);
Helper.absoluteDateTimeReveal(context, holder.binding.dateShort, statusToDeal.created_at, statusToDeal.edited_at);
}
}
//---- SPOILER TEXT -----
if (statusToDeal.spoiler_text != null && !statusToDeal.spoiler_text.trim().isEmpty()) {
@ -1401,20 +1384,16 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
}
//--- BOOST VISIBILITY ---
switch (statusToDeal.visibility) {
case "public":
case "unlisted":
case "public", "unlisted" ->
holder.binding.actionButtonBoost.setVisibility(View.VISIBLE);
break;
case "private":
case "private" -> {
if (status.account.id.compareTo(BaseMainActivity.currentUserID) == 0) {
holder.binding.actionButtonBoost.setVisibility(View.VISIBLE);
} else {
holder.binding.actionButtonBoost.setVisibility(View.GONE);
}
break;
case "direct":
holder.binding.actionButtonBoost.setVisibility(View.GONE);
break;
}
case "direct" -> holder.binding.actionButtonBoost.setVisibility(View.GONE);
}
//--- MAIN CONTENT ---
@ -1802,6 +1781,12 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
// --- POLL ---
if (statusToDeal.poll != null && statusToDeal.poll.options != null) {
int normalize;
if(statusToDeal.poll.multiple && statusToDeal.poll.voters_count > 1) {
normalize = statusToDeal.poll.voters_count;
} else {
normalize = statusToDeal.poll.votes_count;
}
if (statusToDeal.poll.voted || statusToDeal.poll.expired) {
holder.binding.poll.submitVote.setVisibility(View.GONE);
holder.binding.poll.rated.setVisibility(View.VISIBLE);
@ -1820,7 +1805,7 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
}
for (Poll.PollItem pollItem : statusToDeal.poll.options) {
@NonNull LayoutPollItemBinding pollItemBinding = LayoutPollItemBinding.inflate(inflater, holder.binding.poll.rated, true);
double value = Math.round((pollItem.votes_count * 100) / (double) statusToDeal.poll.voters_count);
double value = Math.round((pollItem.votes_count * 100) / (double) normalize);
pollItemBinding.pollItemPercent.setText(String.format("%s %%", (int) value));
pollItemBinding.pollItemText.setText(
pollItem.getSpanTitle(context, statusToDeal,
@ -1973,7 +1958,7 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
}
}));
holder.binding.poll.pollContainer.setVisibility(View.VISIBLE);
String pollInfo = context.getResources().getQuantityString(R.plurals.number_of_voters, statusToDeal.poll.voters_count, statusToDeal.poll.voters_count);
String pollInfo = context.getResources().getQuantityString(R.plurals.number_of_voters, normalize, normalize);
if (statusToDeal.poll.expired) {
pollInfo += " - " + context.getString(R.string.poll_finish_at, MastodonHelper.dateToStringPoll(statusToDeal.poll.expires_at));
} else {
@ -2335,6 +2320,7 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
if (accounts.size() > 1) {
List<app.fedilab.android.mastodon.client.entities.api.Account> accountList = new ArrayList<>();
for (BaseAccount account : accounts) {
account.mastodon_account.acct += "@" + account.instance;
accountList.add(account.mastodon_account);
}
Handler mainHandler = new Handler(Looper.getMainLooper());
@ -2408,11 +2394,7 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
});
popup.show();
});
if (statusToDeal.replies_count > 0 && !(context instanceof ContextActivity)) {
holder.binding.replyCount.setVisibility(View.VISIBLE);
} else {
holder.binding.replyCount.setVisibility(View.GONE);
}
holder.binding.actionButtonReply.setOnLongClickListener(v -> {
CrossActionHelper.doCrossAction(context, CrossActionHelper.TypeOfCrossAction.REPLY_ACTION, null, statusToDeal);
return true;
@ -2773,7 +2755,7 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
if (id != null) {
b.putString(type, id);
}
if (type == ARG_TIMELINE_REFRESH_ALL) {
if (type.equals(ARG_TIMELINE_REFRESH_ALL)) {
b.putBoolean(ARG_TIMELINE_REFRESH_ALL, true);
}
Intent intentBC = new Intent(Helper.RECEIVE_STATUS_ACTION);
@ -2785,7 +2767,6 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
public static void applyColor(Context context, StatusViewHolder holder) {
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
int currentNightMode = context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
boolean customLight = sharedpreferences.getBoolean(context.getString(R.string.SET_CUSTOMIZE_LIGHT_COLORS), false);
boolean customDark = sharedpreferences.getBoolean(context.getString(R.string.SET_CUSTOMIZE_DARK_COLORS), false);
int theme_icons_color = -1;

@ -15,6 +15,7 @@ package app.fedilab.android.mastodon.ui.fragment.admin;
* see <http://www.gnu.org/licenses>. */
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@ -24,6 +25,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@ -63,6 +65,9 @@ public class FragmentAdminAccount extends Fragment {
}
flagLoading = false;
binding = FragmentPaginationBinding.inflate(inflater, container, false);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
boolean displayScrollBar = sharedpreferences.getBoolean(getString(R.string.SET_TIMELINE_SCROLLBAR), false);
binding.recyclerView.setVerticalScrollBarEnabled(displayScrollBar);
return binding.getRoot();
}

@ -16,6 +16,7 @@ package app.fedilab.android.mastodon.ui.fragment.admin;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@ -25,6 +26,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@ -119,7 +121,9 @@ public class FragmentAdminDomain extends Fragment {
);
adminVM = new ViewModelProvider(FragmentAdminDomain.this).get(viewModelKey, AdminVM.class);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
boolean displayScrollBar = sharedpreferences.getBoolean(getString(R.string.SET_TIMELINE_SCROLLBAR), false);
binding.recyclerView.setVerticalScrollBarEnabled(displayScrollBar);
binding.noActionText.setText(R.string.no_blocked_domains);
binding.loader.setVisibility(View.VISIBLE);
binding.recyclerView.setVisibility(View.GONE);

@ -15,6 +15,7 @@ package app.fedilab.android.mastodon.ui.fragment.admin;
* see <http://www.gnu.org/licenses>. */
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@ -24,6 +25,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@ -69,7 +71,9 @@ public class FragmentAdminReport extends Fragment {
}
binding = FragmentPaginationBinding.inflate(inflater, container, false);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
boolean displayScrollBar = sharedpreferences.getBoolean(getString(R.string.SET_TIMELINE_SCROLLBAR), false);
binding.recyclerView.setVerticalScrollBarEnabled(displayScrollBar);
int c1 = ThemeHelper.getAttColor(requireActivity(), R.attr.colorAccent);
binding.swipeContainer.setColorSchemeColors(
c1, c1, c1

@ -14,6 +14,7 @@ package app.fedilab.android.mastodon.ui.fragment.media;
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@ -23,6 +24,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@ -63,6 +65,9 @@ public class FragmentMediaProfile extends Fragment {
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
binding = FragmentPaginationBinding.inflate(inflater, container, false);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
boolean displayScrollBar = sharedpreferences.getBoolean(getString(R.string.SET_TIMELINE_SCROLLBAR), false);
binding.recyclerView.setVerticalScrollBarEnabled(displayScrollBar);
Bundle bundle = this.getArguments();
if (bundle != null) {
accountTimeline = (Account) getArguments().getSerializable(Helper.ARG_ACCOUNT);

@ -26,9 +26,11 @@ import androidx.preference.SwitchPreferenceCompat;
import app.fedilab.android.R;
import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.mastodon.helper.Helper;
public class FragmentTimelinesSettings extends PreferenceFragmentCompat implements SharedPreferences.OnSharedPreferenceChangeListener {
boolean recreate;
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.pref_timelines);
@ -78,7 +80,7 @@ public class FragmentTimelinesSettings extends PreferenceFragmentCompat implemen
boolean checked = sharedpreferences.getBoolean(getString(R.string.SET_PIXELFED_PRESENTATION) + MainActivity.currentUserID + MainActivity.currentInstance, false);
SET_PIXELFED_PRESENTATION.setChecked(checked);
}
recreate = false;
}
@Override
@ -89,6 +91,9 @@ public class FragmentTimelinesSettings extends PreferenceFragmentCompat implemen
if (key.compareToIgnoreCase(getString(R.string.SET_TRANSLATOR)) == 0) {
createPref();
}
if (key.compareToIgnoreCase(getString(R.string.SET_TIMELINE_SCROLLBAR)) == 0) {
recreate = true;
}
if (key.compareToIgnoreCase(getString(R.string.SET_DISPLAY_BOOKMARK)) == 0) {
SwitchPreferenceCompat SET_DISPLAY_BOOKMARK = findPreference(getString(R.string.SET_DISPLAY_BOOKMARK));
if (SET_DISPLAY_BOOKMARK != null) {
@ -124,6 +129,11 @@ public class FragmentTimelinesSettings extends PreferenceFragmentCompat implemen
super.onPause();
getPreferenceScreen().getSharedPreferences()
.unregisterOnSharedPreferenceChangeListener(this);
if (recreate) {
recreate = false;
requireActivity().recreate();
Helper.recreateMainActivity(requireActivity());
}
}

@ -19,6 +19,7 @@ import static app.fedilab.android.BaseMainActivity.currentInstance;
import static app.fedilab.android.BaseMainActivity.currentToken;
import static app.fedilab.android.mastodon.helper.MastodonHelper.ACCOUNTS_PER_CALL;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@ -28,6 +29,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@ -103,6 +105,9 @@ public class FragmentMastodonAccount extends Fragment {
flagLoading = false;
binding = FragmentPaginationBinding.inflate(inflater, container, false);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
boolean displayScrollBar = sharedpreferences.getBoolean(getString(R.string.SET_TIMELINE_SCROLLBAR), false);
binding.recyclerView.setVerticalScrollBarEnabled(displayScrollBar);
return binding.getRoot();
}

@ -14,6 +14,7 @@ package app.fedilab.android.mastodon.ui.fragment.timeline;
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@ -22,6 +23,7 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import java.util.List;
@ -45,6 +47,9 @@ public class FragmentMastodonAnnouncement extends Fragment {
ViewGroup container, Bundle savedInstanceState) {
binding = FragmentPaginationBinding.inflate(inflater, container, false);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
boolean displayScrollBar = sharedpreferences.getBoolean(getString(R.string.SET_TIMELINE_SCROLLBAR), false);
binding.recyclerView.setVerticalScrollBarEnabled(displayScrollBar);
View root = binding.getRoot();
announcementsVM = new ViewModelProvider(FragmentMastodonAnnouncement.this).get(AnnouncementsVM.class);
binding.loader.setVisibility(View.VISIBLE);

@ -20,6 +20,7 @@ import static app.fedilab.android.mastodon.activities.ContextActivity.expand;
import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
@ -31,6 +32,7 @@ import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import java.util.ArrayList;
@ -175,6 +177,9 @@ public class FragmentMastodonContext extends Fragment {
}
binding = FragmentPaginationBinding.inflate(inflater, container, false);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
boolean displayScrollBar = sharedpreferences.getBoolean(getString(R.string.SET_TIMELINE_SCROLLBAR), false);
binding.recyclerView.setVerticalScrollBarEnabled(displayScrollBar);
statusesVM = new ViewModelProvider(FragmentMastodonContext.this).get(StatusesVM.class);
binding.recyclerView.setNestedScrollingEnabled(true);
this.statuses = new ArrayList<>();
@ -295,7 +300,11 @@ public class FragmentMastodonContext extends Fragment {
@Override
public void onDestroyView() {
try {
requireActivity().unregisterReceiver(receive_action);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
super.onDestroyView();
}

@ -82,6 +82,9 @@ public class FragmentMastodonConversation extends Fragment implements Conversati
ViewGroup container, Bundle savedInstanceState) {
flagLoading = false;
binding = FragmentPaginationBinding.inflate(inflater, container, false);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
boolean displayScrollBar = sharedpreferences.getBoolean(getString(R.string.SET_TIMELINE_SCROLLBAR), false);
binding.recyclerView.setVerticalScrollBarEnabled(displayScrollBar);
isViewInitialized = false;
return binding.getRoot();
}

@ -272,7 +272,11 @@ public class FragmentMastodonDirectMessage extends Fragment {
@Override
public void onDestroyView() {
try {
requireActivity().unregisterReceiver(broadcast_data);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
super.onDestroyView();
}

@ -15,6 +15,7 @@ package app.fedilab.android.mastodon.ui.fragment.timeline;
* see <http://www.gnu.org/licenses>. */
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@ -24,6 +25,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@ -52,6 +54,9 @@ public class FragmentMastodonDomainBlock extends Fragment {
ViewGroup container, Bundle savedInstanceState) {
binding = FragmentPaginationBinding.inflate(inflater, container, false);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
boolean displayScrollBar = sharedpreferences.getBoolean(getString(R.string.SET_TIMELINE_SCROLLBAR), false);
binding.recyclerView.setVerticalScrollBarEnabled(displayScrollBar);
return binding.getRoot();
}

@ -201,6 +201,9 @@ public class FragmentMastodonNotification extends Fragment implements Notificati
flagLoading = false;
isViewInitialized = false;
binding = FragmentPaginationBinding.inflate(inflater, container, false);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
boolean displayScrollBar = sharedpreferences.getBoolean(getString(R.string.SET_TIMELINE_SCROLLBAR), false);
binding.recyclerView.setVerticalScrollBarEnabled(displayScrollBar);
View root = binding.getRoot();
if (getArguments() != null) {
notificationType = (NotificationTypeEnum) getArguments().get(Helper.ARG_NOTIFICATION_TYPE);
@ -675,8 +678,13 @@ public class FragmentMastodonNotification extends Fragment implements Notificati
@Override
public void onDestroyView() {
try {
requireActivity().unregisterReceiver(receive_action);
requireActivity().unregisterReceiver(receive_refresh);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
if (isAdded()) {
storeMarker();
}

@ -15,6 +15,7 @@ package app.fedilab.android.mastodon.ui.fragment.timeline;
* see <http://www.gnu.org/licenses>. */
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@ -24,6 +25,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@ -51,6 +53,9 @@ public class FragmentMastodonSuggestion extends Fragment {
ViewGroup container, Bundle savedInstanceState) {
flagLoading = false;
binding = FragmentPaginationBinding.inflate(inflater, container, false);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
boolean displayScrollBar = sharedpreferences.getBoolean(getString(R.string.SET_TIMELINE_SCROLLBAR), false);
binding.recyclerView.setVerticalScrollBarEnabled(displayScrollBar);
return binding.getRoot();
}

@ -15,6 +15,7 @@ package app.fedilab.android.mastodon.ui.fragment.timeline;
* see <http://www.gnu.org/licenses>. */
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@ -24,6 +25,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@ -63,6 +65,9 @@ public class FragmentMastodonTag extends Fragment {
}
binding = FragmentPaginationBinding.inflate(inflater, container, false);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
boolean displayScrollBar = sharedpreferences.getBoolean(getString(R.string.SET_TIMELINE_SCROLLBAR), false);
binding.recyclerView.setVerticalScrollBarEnabled(displayScrollBar);
return binding.getRoot();
}

@ -23,11 +23,9 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -95,7 +93,6 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
@Override
public void onReceive(Context context, Intent intent) {
Bundle b = intent.getExtras();
Log.v(Helper.TAG, "onReceive: " + b);
if (b != null) {
Status receivedStatus = (Status) b.getSerializable(Helper.ARG_STATUS_ACTION);
String delete_statuses_for_user = b.getString(Helper.ARG_STATUS_ACCOUNT_ID_DELETED);
@ -355,9 +352,10 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
lockForResumeCall = 0;
binding.loader.setVisibility(View.VISIBLE);
binding.recyclerView.setVisibility(View.GONE);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
max_id = statusReport != null ? statusReport.id : null;
offset = 0;
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
rememberPosition = sharedpreferences.getBoolean(getString(R.string.SET_REMEMBER_POSITION), true);
//Inner marker are only for pinned timelines and main timelines, they have isViewInitialized set to false
if (max_id == null && !isViewInitialized && rememberPosition) {
@ -459,6 +457,9 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
ContextCompat.registerReceiver(requireActivity(), receive_action, new IntentFilter(Helper.RECEIVE_STATUS_ACTION), ContextCompat.RECEIVER_NOT_EXPORTED);
binding = FragmentPaginationBinding.inflate(inflater, container, false);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
boolean displayScrollBar = sharedpreferences.getBoolean(getString(R.string.SET_TIMELINE_SCROLLBAR), false);
binding.recyclerView.setVerticalScrollBarEnabled(displayScrollBar);
return binding.getRoot();
}
@ -777,27 +778,24 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
}
timelineParams.fetchingMissing = fetchingMissing;
switch (timelineType) {
case LOCAL:
case LOCAL -> {
timelineParams.local = true;
timelineParams.remote = false;
break;
case PUBLIC:
}
case PUBLIC -> {
timelineParams.local = false;
timelineParams.remote = true;
break;
case BUBBLE:
}
case BUBBLE -> {
if (bubbleTimeline != null) {
timelineParams.onlyMedia = bubbleTimeline.only_media;
timelineParams.remote = bubbleTimeline.remote;
timelineParams.replyVisibility = bubbleTimeline.reply_visibility;
timelineParams.excludeVisibilities = bubbleTimeline.exclude_visibilities;
}
break;
case LIST:
timelineParams.listId = list_id;
break;
case ART:
case TAG:
}
case LIST -> timelineParams.listId = list_id;
case ART, TAG -> {
if (tagTimeline == null) {
tagTimeline = new TagTimeline();
tagTimeline.name = search;
@ -810,11 +808,11 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
if (timelineParams.hashtagTrim != null && timelineParams.hashtagTrim.startsWith("#")) {
timelineParams.hashtagTrim = tagTimeline.name.substring(1);
}
break;
case REMOTE:
}
case REMOTE -> {
timelineParams.instance = remoteInstance;
timelineParams.token = null;
break;
}
}
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
boolean useCache = sharedpreferences.getBoolean(getString(R.string.SET_USE_CACHE), true);
@ -1018,15 +1016,13 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
}
});
}
} //GNU TIMELINES
else if (pinnedTimeline != null && pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.GNU) {
}//MISSKEY TIMELINES
else if (pinnedTimeline != null && pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.MISSKEY) {
if (direction == null) {
timelinesVM.getMisskey(remoteInstance, null, MastodonHelper.statusesPerCall(requireActivity()))
.observe(getViewLifecycleOwner(), this::initializeStatusesCommonView);
} else if (direction == DIRECTION.BOTTOM) {
timelinesVM.getMisskey(remoteInstance, max_id, MastodonHelper.statusesPerCall(requireActivity()))
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false, true, fetchStatus));
} else if (direction == DIRECTION.TOP) {

@ -1596,7 +1596,11 @@ public class PeertubeActivity extends BasePeertubeActivity implements CommentLis
private void unregisterReceiver() {
if (mPowerKeyReceiver != null) {
try {
unregisterReceiver(mPowerKeyReceiver);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
mPowerKeyReceiver = null;
}
}

@ -153,7 +153,11 @@ public class PeertubeMainActivity extends PeertubeBaseMainActivity {
if (!keepRemote) {
typeOfConnection = TypeOfConnection.NORMAL;
}
try {
unregisterReceiver(broadcast_data);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}
@SuppressLint("ApplySharedPref")

@ -125,7 +125,11 @@ public class RetrieveInfoService extends Service implements NetworkStateReceiver
super.onDestroy();
if (networkStateReceiver != null) {
networkStateReceiver.removeListener(this);
try {
unregisterReceiver(networkStateReceiver);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}
}

@ -3,7 +3,7 @@
<item android:id="@android:id/background">
<shape>
<corners android:radius="8dp" />
<solid android:color="?secondaryColor" />
<solid android:color="?colorSecondary" />
</shape>
</item>
@ -17,7 +17,7 @@
<scale android:scaleWidth="100%">
<shape>
<corners android:radius="8dp" />
<solid android:color="?primaryColor" />
<solid android:color="?colorPrimary" />
</shape>
</scale>
</item>

@ -35,14 +35,32 @@
android:text="@string/app_name"
android:textSize="20sp" />
<!-- About version -->
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/about_version"
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"
android:gravity="center"
android:orientation="horizontal">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/about_version"
tools:text="x.y.z"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center_horizontal"
android:textSize="16sp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/about_version_copy"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="36dp"
android:layout_height="36dp"
app:iconGravity="textStart"
app:iconPadding="0dp"
android:layout_marginStart="10dp"
android:contentDescription="@string/copy_version"
app:icon="@drawable/ic_copy_link"/>
</androidx.appcompat.widget.LinearLayoutCompat>
<!-- About developer -->
<androidx.appcompat.widget.AppCompatTextView

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:padding="15dp"
android:orientation="vertical">
<androidx.appcompat.widget.SearchView
android:id="@+id/toolbar_search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:iconifiedByDefault="false"
android:layout_gravity="center_vertical"
android:gravity="center" />
<GridView
android:id="@+id/gridview"
android:layout_width="match_parent"
android:numColumns="5"
android:layout_height="0dp"
android:layout_weight="1"/>
</androidx.appcompat.widget.LinearLayoutCompat>

@ -657,33 +657,54 @@
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/reply_count"
android:layout_marginStart="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:text="+"
android:textColor="?colorControlNormal"
android:textSize="12sp"
android:visibility="gone"
tools:ignore="HardcodedText"
tools:visibility="visible" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/action_button_boost_container"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/action_button_quote"
app:layout_constraintStart_toEndOf="@+id/action_button_reply_container"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0">
<com.varunest.sparkbutton.SparkButton
android:id="@+id/action_button_boost"
android:layout_width="48dp"
android:layout_height="48dp"
android:adjustViewBounds="true"
android:layout_gravity="center"
android:contentDescription="@string/reblog_add"
app:activeImage="@drawable/ic_round_repeat_active_24"
app:iconSize="28dp"
app:inactiveImage="@drawable/ic_round_repeat_24"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/action_button_quote"
app:layout_constraintStart_toEndOf="@+id/action_button_reply_container"
app:layout_constraintTop_toTopOf="parent"
app:primaryColor="@color/boost_icon"
app:secondaryColor="@color/boost_icon" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/boost_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:textColor="?colorControlNormal"
android:visibility="gone"
tools:ignore="HardcodedText"
tools:visibility="visible" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/action_button_quote"
android:layout_width="28dp"
@ -697,11 +718,21 @@
android:src="@drawable/ic_baseline_format_quote_24"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/action_button_favorite"
app:layout_constraintStart_toEndOf="@+id/action_button_boost"
app:layout_constraintEnd_toStartOf="@+id/action_button_favorite_container"
app:layout_constraintStart_toEndOf="@+id/action_button_boost_container"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/action_button_favorite_container"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/action_button_bookmark"
app:layout_constraintStart_toEndOf="@+id/action_button_quote"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0">
<com.varunest.sparkbutton.SparkButton
android:id="@+id/action_button_favorite"
android:layout_width="48dp"
@ -712,13 +743,19 @@
app:activeImage="@drawable/ic_round_star_24"
app:animationSpeed="1.5"
app:inactiveImage="@drawable/ic_round_star_border_24"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/action_button_bookmark"
app:layout_constraintStart_toEndOf="@+id/action_button_quote"
app:layout_constraintTop_toTopOf="parent"
app:primaryColor="@color/marked_icon"
app:secondaryColor="@color/marked_icon"
sparkbutton:iconSize="28dp" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/favorite_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:textColor="?colorControlNormal"
android:visibility="gone"
tools:ignore="HardcodedText"
tools:visibility="visible" />
</androidx.appcompat.widget.LinearLayoutCompat>
<com.varunest.sparkbutton.SparkButton
android:id="@+id/action_button_bookmark"
@ -732,7 +769,7 @@
app:inactiveImage="@drawable/ic_round_bookmark_border_24"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/action_button_translate"
app:layout_constraintStart_toEndOf="@+id/action_button_favorite"
app:layout_constraintStart_toEndOf="@+id/action_button_favorite_container"
app:layout_constraintTop_toTopOf="parent"
app:primaryColor="@color/marked_icon"
app:secondaryColor="@color/marked_icon"

@ -38,7 +38,10 @@
android:layout_height="wrap_content"
android:clipToPadding="false"
android:paddingBottom="6dp"
android:scrollbars="none" />
android:fastScrollEnabled="true"
android:scrollbarSize="5dp"
android:scrollbarThumbVertical="?colorPrimary"
android:scrollbars="vertical" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

@ -1068,4 +1068,10 @@
<string name="thumbnail">Vorschau</string>
<string name="thread_long_message_message">Der Beitrag wird in mehrere Antworten aufgeteilt, um die maximale Zeichenanzahl Ihrer Instanz einzuhalten.</string>
<string name="thread_long_this_message">Diese Beiträge in Antworten aufteilen?</string>
<string name="clipboard_version">Informationen wurden in die Zwischenablage kopiert</string>
<string name="set_display_relative_date">Zeige relatives Datum für Beiträge an</string>
<string name="copy_version">Informationen kopieren</string>
<string name="underline_links">Anklickbare Elemente unterstreichen</string>
<string name="tag_already_followed">Du folgst bereits diesem Tag!</string>
<string name="timeline_scrollbar">Bildlaufleiste in der Timeline anzeigen</string>
</resources>

@ -747,7 +747,7 @@
<string name="label_eraser_mode">Modo Borrar</string>
<string name="delete_cache_message">Tes a certeza de querer baleirar a caché\? Se tes borradores que inclúen multimedia, estes perderanse.</string>
<string name="my_account">A miña conta</string>
<string name="set_display_counters_description">Montrará icona con contador para as novas mensaxes nas cronoloxías da lapela</string>
<string name="set_display_counters_description">Mostrará un contador nas cronoloxías das pestanas para as novas mensaxes</string>
<string name="load_settings">Cargar axustes exportados</string>
<string name="push_distributors">Xestor de notificacións</string>
<string name="import_settings">Importar axustes</string>
@ -1062,4 +1062,9 @@
<string name="thumbnail">Miniatura</string>
<string name="thread_long_message_message">A mensaxe vai ser dividida en varias respostas para cumprir co número máximo de caracteres da túa instancia.</string>
<string name="thread_long_this_message">Dividir estas mensaxes nas respostas?</string>
<string name="clipboard_version">Copiouse a información ao portapapeis</string>
<string name="set_display_relative_date">Mostrar a data relativa das mensaxes</string>
<string name="copy_version">Copiar información</string>
<string name="underline_links">Subliñar os elementos clicables</string>
<string name="tag_already_followed">Xa segues ese cancelo!</string>
</resources>

@ -45,6 +45,7 @@
</item>
<item name="android:windowSharedElementExitTransition">@transition/change_image_transform
</item>
<item name="alertDialogTheme">@style/AppThemeAlertDialog</item>
</style>
<style name="AppThemeBar" parent="Theme.Material3.Dark">
@ -81,6 +82,7 @@
<item name="android:windowLightStatusBar" tools:targetApi="m">false</item>
<item name="android:windowLightNavigationBar" tools:targetApi="o_mr1">false</item>
<item name="android:isLightTheme" tools:targetApi="q">false</item>
<item name="alertDialogTheme">@style/AppThemeAlertDialog</item>
</style>
@ -98,6 +100,7 @@
</item>
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
<item name="alertDialogTheme">@style/AppThemeAlertDialog</item>
</style>
<style name="AppThemeAlertDialog" parent="ThemeOverlay.Material3.MaterialAlertDialog">
@ -168,6 +171,7 @@
<item name="colorOnSurfaceInverse">@color/solarized_md_theme_dark_inverseOnSurface</item>
<item name="colorSurfaceInverse">@color/solarized_md_theme_dark_inverseSurface</item>
<item name="colorPrimaryInverse">@color/solarized_md_theme_dark_inversePrimary</item>
<item name="alertDialogTheme">@style/SolarizedAlertDialog</item>
</style>
<style name="SolarizedAppThemeBar" parent="AppThemeBar">
@ -203,6 +207,7 @@
<item name="colorOnSurfaceInverse">@color/solarized_md_theme_dark_inverseOnSurface</item>
<item name="colorSurfaceInverse">@color/solarized_md_theme_dark_inverseSurface</item>
<item name="colorPrimaryInverse">@color/solarized_md_theme_dark_inversePrimary</item>
<item name="alertDialogTheme">@style/SolarizedAlertDialog</item>
</style>
<style name="TransparentSolarized" parent="SolarizedAppThemeBar">
@ -221,6 +226,7 @@
</item>
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
<item name="alertDialogTheme">@style/SolarizedAlertDialog</item>
</style>
<style name="SolarizedAlertDialog" parent="ThemeOverlay.Material3.MaterialAlertDialog">
@ -289,6 +295,7 @@
<item name="colorOnSurfaceInverse">@color/black</item>
<item name="colorSurfaceInverse">@color/white</item>
<item name="colorPrimaryInverse">@color/black</item>
<item name="alertDialogTheme">@style/BlackAlertDialog</item>
</style>
<style name="BlackAppThemeBar" parent="AppThemeBar">
@ -320,6 +327,7 @@
<item name="colorOnSurfaceInverse">@color/black</item>
<item name="colorSurfaceInverse">@color/white</item>
<item name="colorPrimaryInverse">@color/black</item>
<item name="alertDialogTheme">@style/BlackAlertDialog</item>
</style>
<style name="TransparentBlack" parent="BlackAppThemeBar">
@ -337,6 +345,7 @@
</item>
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
<item name="alertDialogTheme">@style/BlackAlertDialog</item>
</style>
<style name="BlackAlertDialog" parent="ThemeOverlay.Material3.MaterialAlertDialog">
@ -402,6 +411,7 @@
<item name="colorOnSurfaceInverse">@color/dracula_comment</item>
<item name="colorSurfaceInverse">@color/dracula_foreground</item>
<item name="colorPrimaryInverse">@color/dracula_comment</item>
<item name="alertDialogTheme">@style/DraculaAlertDialog</item>
</style>
@ -434,6 +444,7 @@
<item name="colorOnSurfaceInverse">@color/dracula_comment</item>
<item name="colorSurfaceInverse">@color/dracula_foreground</item>
<item name="colorPrimaryInverse">@color/dracula_comment</item>
<item name="alertDialogTheme">@style/DraculaAlertDialog</item>
</style>
<style name="TransparentDracula" parent="DraculaAppThemeBar">
@ -452,6 +463,7 @@
</item>
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
<item name="alertDialogTheme">@style/DraculaAlertDialog</item>
</style>
<style name="DraculaAlertDialog" parent="ThemeOverlay.Material3.MaterialAlertDialog">

@ -1066,4 +1066,10 @@
<string name="thumbnail">Küçük resim</string>
<string name="thread_long_message_message">Mesaj, sunucunuzun en fazla karakter sayısına uygun olacak şekilde birkaç yanıta bölünecek.</string>
<string name="thread_long_this_message">Bu mesajlar yanıtlara bölünsün mü?</string>
<string name="clipboard_version">Bilgiler panoya kopyalandı</string>
<string name="set_display_relative_date">Mesajlar için göreceli tarihi görüntüle</string>
<string name="copy_version">Bilgileri kopyala</string>
<string name="underline_links">Tıklanabilir ögelerin altını çiz</string>
<string name="tag_already_followed">Bu etiketi zaten takip ediyorsunuz!</string>
<string name="timeline_scrollbar">Zaman çizelgeleri için kaydırma çubuğu göster</string>
</resources>

@ -27,6 +27,8 @@
<string name="add_account">Add an account</string>
<string name="clipboard">The content of the message has been copied to the clipboard</string>
<string name="clipboard_url">The URL of the message has been copied to the clipboard</string>
<string name="clipboard_version">Information have been copied to the clipboard</string>
<string name="tag_already_followed">You already follow that tag!</string>
<string name="camera">Camera</string>
<string name="delete_all">Delete all</string>
<string name="schedule">Schedule</string>
@ -407,6 +409,7 @@
<string name="action_logout_account">Logout account</string>
<string name="all">All</string>
<string name="copy_link">Copy link</string>
<string name="copy_version">Copy information</string>
<string name="calls_blocked">http calls blocked by the application</string>
<string name="list_of_blocked_domains">List of blocked calls</string>
<string name="submit">Submit</string>
@ -1168,6 +1171,7 @@
<string name="SET_NOTIF_VALIDATION_FAV" translatable="false">SET_NOTIF_VALIDATION_FAV</string>
<string name="SET_DISPLAY_COUNTER_FAV_BOOST" translatable="false">SET_DISPLAY_COUNTER_FAV_BOOST</string>
<string name="SET_DISPLAY_RELATIVE_DATE" translatable="false">SET_DISPLAY_RELATIVE_DATE</string>
<string name="SET_REMOVE_LEFT_MARGIN" translatable="false">SET_REMOVE_LEFT_MARGIN</string>
<string name="SET_PROFILE_REMOTELY" translatable="false">SET_PROFILE_REMOTELY</string>
@ -1186,8 +1190,10 @@
<string name="SET_GROUP_REBLOGS" translatable="false">SET_GROUP_REBLOGS</string>
<string name="SET_BOOST_ORIGINAL_DATE" translatable="false">SET_BOOST_ORIGINAL_DATE</string>
<string name="SET_TIMELINE_SCROLLBAR" translatable="false">SET_TIMELINE_SCROLLBAR</string>
<string name="SET_MARKDOWN_SUPPORT" translatable="false">SET_MARKDOWN_SUPPORT</string>
<string name="SET_TRUNCATE_LINKS" translatable="false">SET_TRUNCATE_LINKS</string>
<string name="SET_UNDERLINE_CLICKABLE" translatable="false">SET_UNDERLINE_CLICKABLE</string>
<string name="SET_TRUNCATE_LINKS_MAX" translatable="false">SET_TRUNCATE_LINKS_MAX</string>
<string name="SET_HIDE_SINGLE_MEDIA_WITH_CARD" translatable="false">SET_HIDE_SINGLE_MEDIA_WITH_CARD</string>
@ -1729,6 +1735,7 @@
<string name="set_timelines_in_a_list_title">Timelines in a list</string>
<string name="set_timelines_in_a_list">When enabled, all pinned timelines will be displayed in a drop-down menu</string>
<string name="set_display_counter">Display counters for messages</string>
<string name="set_display_relative_date">Display relative date for messages</string>
<string name="set_use_cache">Use cache</string>
<string name="set_use_cache_indication">Timelines will be cached so the application will be faster.</string>
<string name="load_media_type_title">Load thumbnails for media</string>
@ -1945,6 +1952,7 @@
<string name="also_followed_by">Followed by:</string>
<string name="Directory">Directory</string>
<string name="boost_original_date">Display original date for boosts</string>
<string name="timeline_scrollbar">Display a scroll bar for timelines</string>
<string name="markdown_support">Markdown support</string>
<string name="set_disable_release_notes">Disable release notes</string>
<string name="set_disable_release_notes_indication">When a new version is published, you will not be alerted inside the app.</string>
@ -1989,6 +1997,7 @@
<string name="toot_error_no_media_description">There are missing media descriptions</string>
<string name="truncate_links">Truncate links</string>
<string name="underline_links">Underline clickable elements</string>
<string name="truncate_links_max">Max chars in links</string>

@ -46,6 +46,7 @@
<item name="android:windowSharedElementExitTransition">@transition/change_image_transform
</item>
<item name="android:statusBarColor">?android:colorBackground</item>
<item name="alertDialogTheme">@style/AppThemeAlertDialog</item>
</style>
<style name="AppThemeBar" parent="Theme.Material3.Light">
@ -82,6 +83,7 @@
<item name="android:windowLightNavigationBar" tools:targetApi="o_mr1">true</item>
<item name="android:isLightTheme" tools:targetApi="q">true</item>
<item name="android:statusBarColor">?android:colorBackground</item>
<item name="alertDialogTheme">@style/AppThemeAlertDialog</item>
</style>
<style name="Transparent" parent="AppThemeBar">
@ -98,6 +100,7 @@
</item>
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
<item name="alertDialogTheme">@style/AppThemeAlertDialog</item>
</style>
<style name="AppThemeAlertDialog" parent="ThemeOverlay.Material3.MaterialAlertDialog">
@ -167,6 +170,7 @@
<item name="colorOnSurfaceInverse">@color/solarized_md_theme_light_inverseOnSurface</item>
<item name="colorSurfaceInverse">@color/solarized_md_theme_light_inverseSurface</item>
<item name="colorPrimaryInverse">@color/solarized_md_theme_light_inversePrimary</item>
<item name="alertDialogTheme">@style/SolarizedAlertDialog</item>
</style>
<style name="TransparentSolarized" parent="SolarizedAppThemeBar">
@ -183,6 +187,7 @@
</item>
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
<item name="alertDialogTheme">@style/SolarizedAlertDialog</item>
</style>
<style name="SolarizedAlertDialog" parent="ThemeOverlay.Material3.MaterialAlertDialog">
@ -254,6 +259,7 @@
<item name="colorOnSurfaceInverse">@color/solarized_md_theme_light_inverseOnSurface</item>
<item name="colorSurfaceInverse">@color/solarized_md_theme_light_inverseSurface</item>
<item name="colorPrimaryInverse">@color/solarized_md_theme_light_inversePrimary</item>
<item name="alertDialogTheme">@style/SolarizedAlertDialog</item>
</style>
<style name="Fedilab.SmallIconButton" parent="Widget.Material3.Button.IconButton">

@ -50,6 +50,14 @@
app:singleLineTitle="false"
app:title="@string/boost_original_date" />
<SwitchPreferenceCompat
android:defaultValue="false"
app:iconSpaceReserved="false"
app:key="@string/SET_TIMELINE_SCROLLBAR"
app:singleLineTitle="false"
app:title="@string/timeline_scrollbar" />
<SwitchPreferenceCompat
android:defaultValue="true"
app:iconSpaceReserved="false"
@ -64,6 +72,13 @@
app:singleLineTitle="false"
app:title="@string/truncate_links" />
<SwitchPreferenceCompat
android:defaultValue="false"
app:iconSpaceReserved="false"
app:key="@string/SET_UNDERLINE_CLICKABLE"
app:singleLineTitle="false"
app:title="@string/underline_links" />
<androidx.preference.SeekBarPreference
android:defaultValue="30"
android:max="150"
@ -198,6 +213,12 @@
app:key="@string/SET_DISPLAY_COUNTER_FAV_BOOST"
app:singleLineTitle="false"
app:title="@string/set_display_counter" />
<SwitchPreferenceCompat
android:defaultValue="true"
app:iconSpaceReserved="false"
app:key="@string/SET_DISPLAY_RELATIVE_DATE"
app:singleLineTitle="false"
app:title="@string/set_display_relative_date" />
<SeekBarPreference
android:defaultValue="0"
android:max="20"

@ -0,0 +1,8 @@
Added:
- Android 14 support
- Automatically split long messages in threads (default: ASK)
- Links and media are clicka ble when composing
Fixed:
- Avoid error 429 with NTFY
- Several crashes are fixed

@ -0,0 +1,14 @@
Added:
- Allow to underline clickable elements (Settings > Timelines - default: disabled)
- Allow to disable relative date in messages
Changed:
- Counters close to action buttons
- Hide emoji picker if the instance has no emoji
- Followed tags are ordered
- Account picker when opening with another account
Fixed:
- Fix a crash when composing
- Fix an issue with the back button
- Display issue with followed tags

@ -0,0 +1,9 @@
Added:
- Add a scroll bar for timelines (default: disabled)
- Add a search bar for custom emojis
Fixed:
- Fix prompt to split asked several times when refusing
- Crashes with profiles
- Fix an issue with poll and Pleroma
- Emoji not displayed in the picker

@ -0,0 +1,25 @@
Added:
- Android 14 support
- Automatically split long messages in threads (default: ASK)
- Links and media are clickable when composing
- Allow to underline clickable elements (Settings > Timelines - default: disabled)
- Allow to disable relative date in messages
- Add a scroll bar for timelines (default: disabled)
- Add a search bar for custom emojis
- Links clickable in media descriptions
Changed:
- Counters close to action buttons
- Hide emoji picker if the instance has no emoji
- Followed tags are ordered
- Account picker when opening with another account
Fixed:
- Avoid error 429 with NTFY
- Fix custom colors (Android 14)
- Fix a crash when composing
- Display issue with followed tags
- Crashes with profiles
- Fix an issue with poll and Pleroma
- Emoji not displayed in the picker
- Several crashes are fixed

@ -0,0 +1,14 @@
Engadido:
- Tres novas iconas para a app (Orgullo, Rosa e Pirata)
- Gardar a posición en conversas remotas
Arranxos:
- Markdown: deixar de xestionar as etiquetas e soporte para texto riscado
- O cursor é máis visible cando se redacta
- Solución ao problema co número máximo de caracteres da instancia
- Pestanas nos perfís
- Nalgúns idiomas non se podía premer nos cancelos
- Problemas coas contas que tiñan moitos seguimentos
- Os fallos cando había varios gif na mesma mensaxe
- Máximo de caracteres nas enquisas
- Outros fallos

@ -0,0 +1,8 @@
Engadido:
- Soporte para Android 14
- División automática de mensaxes longas en fíos (por defecto: ASK)
- As ligazóns son accesibles durante a escritura da mensaxe
Arranxos:
- do erro 429 con NTFY
- varios problemas

@ -0,0 +1,8 @@
Engadido:
- Soporte para Android 14
- División automática de mensaxes longas en fíos (por defecto: ASK)
- As ligazóns son accesibles durante a escritura da mensaxe
Arranxos:
- do erro 429 con NTFY
- varios problemas
Loading…
Cancel
Save