diff --git a/app/build.gradle b/app/build.gradle index 04fc7414..fb85bb8b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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" diff --git a/app/src/fdroid/java/app/fedilab/android/activities/PeertubeBaseMainActivity.java b/app/src/fdroid/java/app/fedilab/android/activities/PeertubeBaseMainActivity.java index 379bc276..595820f8 100644 --- a/app/src/fdroid/java/app/fedilab/android/activities/PeertubeBaseMainActivity.java +++ b/app/src/fdroid/java/app/fedilab/android/activities/PeertubeBaseMainActivity.java @@ -193,7 +193,11 @@ public abstract class PeertubeBaseMainActivity extends BaseActivity implements C super.onDestroy(); ChromeCasts.unregisterListener(this); if (manage_chromecast != null) { - unregisterReceiver(manage_chromecast); + try { + unregisterReceiver(manage_chromecast); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } new Thread(() -> { if (chromeCasts != null && chromeCasts.size() > 0) { for (ChromeCast cast : chromeCasts) { diff --git a/app/src/main/assets/release_notes/notes.json b/app/src/main/assets/release_notes/notes.json index 0dfe79f3..f9fbcfdb 100644 --- a/app/src/main/assets/release_notes/notes.json +++ b/app/src/main/assets/release_notes/notes.json @@ -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", diff --git a/app/src/main/java/app/fedilab/android/BaseMainActivity.java b/app/src/main/java/app/fedilab/android/BaseMainActivity.java index 4cadc14f..850de4de 100644 --- a/app/src/main/java/app/fedilab/android/BaseMainActivity.java +++ b/app/src/main/java/app/fedilab/android/BaseMainActivity.java @@ -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() { - unregisterReceiver(broadcast_data); - unregisterReceiver(broadcast_error_message); - + try { + unregisterReceiver(broadcast_data); + unregisterReceiver(broadcast_error_message); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } if (networkStateReceiver != null) { try { unregisterReceiver(networkStateReceiver); diff --git a/app/src/main/java/app/fedilab/android/activities/AboutActivity.java b/app/src/main/java/app/fedilab/android/activities/AboutActivity.java index 2ea60818..209351fc 100644 --- a/app/src/main/java/app/fedilab/android/activities/AboutActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/AboutActivity.java @@ -15,12 +15,16 @@ package app.fedilab.android.activities; * see . */ +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 { diff --git a/app/src/main/java/app/fedilab/android/mastodon/activities/BaseActivity.java b/app/src/main/java/app/fedilab/android/mastodon/activities/BaseActivity.java index 963f7a33..6ab76c08 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/activities/BaseActivity.java +++ b/app/src/main/java/app/fedilab/android/mastodon/activities/BaseActivity.java @@ -15,6 +15,8 @@ package app.fedilab.android.mastodon.activities; * see . */ +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 -> { diff --git a/app/src/main/java/app/fedilab/android/mastodon/activities/BaseBarActivity.java b/app/src/main/java/app/fedilab/android/mastodon/activities/BaseBarActivity.java index eb50d386..6a1dc0fa 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/activities/BaseBarActivity.java +++ b/app/src/main/java/app/fedilab/android/mastodon/activities/BaseBarActivity.java @@ -15,6 +15,8 @@ package app.fedilab.android.mastodon.activities; * see . */ +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 -> { diff --git a/app/src/main/java/app/fedilab/android/mastodon/activities/BaseTransparentActivity.java b/app/src/main/java/app/fedilab/android/mastodon/activities/BaseTransparentActivity.java index 6da2715e..28f76db5 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/activities/BaseTransparentActivity.java +++ b/app/src/main/java/app/fedilab/android/mastodon/activities/BaseTransparentActivity.java @@ -15,6 +15,8 @@ package app.fedilab.android.mastodon.activities; * see . */ +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) { diff --git a/app/src/main/java/app/fedilab/android/mastodon/activities/ComposeActivity.java b/app/src/main/java/app/fedilab/android/mastodon/activities/ComposeActivity.java index cf9c72f5..6d9c24d5 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/activities/ComposeActivity.java +++ b/app/src/main/java/app/fedilab/android/mastodon/activities/ComposeActivity.java @@ -195,8 +195,11 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana if (timer != null) { timer.cancel(); } - unregisterReceiver(imageReceiver); - + try { + unregisterReceiver(imageReceiver); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } } diff --git a/app/src/main/java/app/fedilab/android/mastodon/activities/ContextActivity.java b/app/src/main/java/app/fedilab/android/mastodon/activities/ContextActivity.java index 7d9390c7..a166587f 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/activities/ContextActivity.java +++ b/app/src/main/java/app/fedilab/android/mastodon/activities/ContextActivity.java @@ -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); diff --git a/app/src/main/java/app/fedilab/android/mastodon/activities/FollowedTagActivity.java b/app/src/main/java/app/fedilab/android/mastodon/activities/FollowedTagActivity.java index 79ef0228..9b75fe47 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/activities/FollowedTagActivity.java +++ b/app/src/main/java/app/fedilab/android/mastodon/activities/FollowedTagActivity.java @@ -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 tagList) { + Collections.sort(tagList, (obj1, obj2) -> obj1.name.compareToIgnoreCase(obj2.name)); + } @Override public boolean onCreateOptionsMenu(@NonNull Menu menu) { diff --git a/app/src/main/java/app/fedilab/android/mastodon/activities/MastodonListActivity.java b/app/src/main/java/app/fedilab/android/mastodon/activities/MastodonListActivity.java index 5d9f3ac9..0be0a8ca 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/activities/MastodonListActivity.java +++ b/app/src/main/java/app/fedilab/android/mastodon/activities/MastodonListActivity.java @@ -143,6 +143,8 @@ public class MastodonListActivity extends BaseBarActivity implements MastodonLis }); setTitle(R.string.action_lists); invalidateOptionsMenu(); + } else { + finish(); } } }); diff --git a/app/src/main/java/app/fedilab/android/mastodon/activities/MediaActivity.java b/app/src/main/java/app/fedilab/android/mastodon/activities/MediaActivity.java index ca082b02..39da1713 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/activities/MediaActivity.java +++ b/app/src/main/java/app/fedilab/android/mastodon/activities/MediaActivity.java @@ -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 . */ +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() { - unregisterReceiver(onDownloadComplete); + try { + unregisterReceiver(onDownloadComplete); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } super.onDestroy(); } diff --git a/app/src/main/java/app/fedilab/android/mastodon/activities/ProfileActivity.java b/app/src/main/java/app/fedilab/android/mastodon/activities/ProfileActivity.java index ddc779bb..336f47c5 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/activities/ProfileActivity.java +++ b/app/src/main/java/app/fedilab/android/mastodon/activities/ProfileActivity.java @@ -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; } - unregisterReceiver(broadcast_data); + try { + unregisterReceiver(broadcast_data); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } super.onDestroy(); } diff --git a/app/src/main/java/app/fedilab/android/mastodon/activities/admin/AdminActionActivity.java b/app/src/main/java/app/fedilab/android/mastodon/activities/admin/AdminActionActivity.java index 005af5d3..b84354b9 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/activities/admin/AdminActionActivity.java +++ b/app/src/main/java/app/fedilab/android/mastodon/activities/admin/AdminActionActivity.java @@ -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) { - unregisterReceiver(mReceiver); + try { + unregisterReceiver(mReceiver); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } } } diff --git a/app/src/main/java/app/fedilab/android/mastodon/client/entities/api/EmojiInstance.java b/app/src/main/java/app/fedilab/android/mastodon/client/entities/api/EmojiInstance.java index 71955a3d..ce672178 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/client/entities/api/EmojiInstance.java +++ b/app/src/main/java/app/fedilab/android/mastodon/client/entities/api/EmojiInstance.java @@ -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 . */ +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> emojiCall = mastodonInstanceService.customEmoji(); + if (emojiCall != null) { + try { + Response> emojiResponse = emojiCall.execute(); + if (emojiResponse.isSuccessful()) { + return emojiResponse.body(); + } + } catch (Exception err) { + err.printStackTrace(); + } + } return null; } } + public interface EmojiFilteredCallBack{ + void get(List emojiList); + } + + /** + * Returns the emojis for an instance + * + * @param instance String + * @param filter String + * @param callBack EmojiFilteredCallBack - Get filtered emojis + * + * @return List - 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 emojiArrayList= new ArrayList<>(); + List 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> emojiCall = mastodonInstanceService.customEmoji(); + if (emojiCall != null) { + try { + Response> 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 * diff --git a/app/src/main/java/app/fedilab/android/mastodon/client/entities/api/Tag.java b/app/src/main/java/app/fedilab/android/mastodon/client/entities/api/Tag.java index a1b9b1b3..5b2a5bbe 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/client/entities/api/Tag.java +++ b/app/src/main/java/app/fedilab/android/mastodon/client/entities/api/Tag.java @@ -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) { diff --git a/app/src/main/java/app/fedilab/android/mastodon/helper/ComposeHelper.java b/app/src/main/java/app/fedilab/android/mastodon/helper/ComposeHelper.java index 230e186f..57de14a3 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/helper/ComposeHelper.java +++ b/app/src/main/java/app/fedilab/android/mastodon/helper/ComposeHelper.java @@ -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 . */ -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 { diff --git a/app/src/main/java/app/fedilab/android/mastodon/helper/Helper.java b/app/src/main/java/app/fedilab/android/mastodon/helper/Helper.java index 940f587d..b2069db7 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/helper/Helper.java +++ b/app/src/main/java/app/fedilab/android/mastodon/helper/Helper.java @@ -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 * diff --git a/app/src/main/java/app/fedilab/android/mastodon/helper/SpannableHelper.java b/app/src/main/java/app/fedilab/android/mastodon/helper/SpannableHelper.java index 6f65eaa6..de58d977 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/helper/SpannableHelper.java +++ b/app/src/main/java/app/fedilab/android/mastodon/helper/SpannableHelper.java @@ -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); - ds.setUnderlineText(status != null && status.underlined); + 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); - ds.setUnderlineText(status != null && status.underlined); + 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); - ds.setUnderlineText(status != null && status.underlined); + 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); - ds.setUnderlineText(false); + if(!underlineLinks) { + ds.setUnderlineText(false); + } if (linkColor != -1) { ds.setColor(linkColor); } diff --git a/app/src/main/java/app/fedilab/android/mastodon/helper/ThemeHelper.java b/app/src/main/java/app/fedilab/android/mastodon/helper/ThemeHelper.java index be9ebd97..51d1b8d0 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/helper/ThemeHelper.java +++ b/app/src/main/java/app/fedilab/android/mastodon/helper/ThemeHelper.java @@ -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 diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/ComposeAdapter.java b/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/ComposeAdapter.java index 34ef0c3f..97bd72f2 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/ComposeAdapter.java +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/ComposeAdapter.java @@ -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 emojisList = new ArrayList<>(); private boolean unlisted_changed = false; private RecyclerView mRecyclerView; + private boolean proceedToSplit = false; + private boolean splitChoiceDone = false; public ComposeAdapter(List statusList, int statusCount, BaseAccount account, Account mentionedAccount, String visibility, String editMessageId) { @@ -530,7 +535,6 @@ public class ComposeAdapter extends RecyclerView.Adapter splitText = ComposeHelper.splitToots(s.toString(), max_car); int statusListSize = statusList.size(); int i = 0; @@ -589,7 +594,7 @@ public class ComposeAdapter extends RecyclerView.Adapter splitText = ComposeHelper.splitToots(contentString, max_car); contentString = splitText.get(0); @@ -1381,7 +1386,7 @@ public class ComposeAdapter extends RecyclerView.Adapter(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 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 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(); } diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/ConversationAdapter.java b/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/ConversationAdapter.java index 3192d286..08a2b11a 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/ConversationAdapter.java +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/ConversationAdapter.java @@ -15,6 +15,8 @@ package app.fedilab.android.mastodon.ui.drawer; * see . */ +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. */ +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 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; - } + 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 } } 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 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 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 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 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,46 +1280,58 @@ public class StatusAdapter extends RecyclerView.Adapter holder.binding.editTime.setVisibility(View.GONE); holder.binding.visibilitySmall.setImageResource(ressource); if (displayCounters && canBeFederated) { - 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)); + if (statusToDeal.replies_count > 0 && !(context instanceof ContextActivity)) { + holder.binding.replyCount.setVisibility(View.VISIBLE); + holder.binding.replyCount.setText(String.valueOf(statusToDeal.replies_count)); } 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); + } + 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); } - Helper.absoluteDateTimeReveal(context, holder.binding.time, statusToDeal.created_at, statusToDeal.edited_at); - holder.binding.visibility.setImageResource(ressource); - holder.binding.time.setVisibility(View.VISIBLE); } else { - holder.binding.statusInfo.setVisibility(View.GONE); - holder.binding.dateShort.setVisibility(View.VISIBLE); - holder.binding.visibilitySmall.setVisibility(View.VISIBLE); - 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.dateShort.setCompoundDrawables(null, null, img, null); + 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.dateShort.setCompoundDrawables(null, null, null, null); + holder.binding.replyCount.setVisibility(View.GONE); } + } + holder.binding.statusInfo.setVisibility(View.GONE); + holder.binding.dateShort.setVisibility(View.VISIBLE); + holder.binding.visibilitySmall.setVisibility(View.VISIBLE); + 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.dateShort.setCompoundDrawables(null, null, img, null); + } 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)); } - holder.binding.time.setVisibility(View.GONE); - Helper.absoluteDateTimeReveal(context, holder.binding.dateShort, statusToDeal.created_at, statusToDeal.edited_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 ----- @@ -1401,20 +1384,16 @@ public class StatusAdapter extends RecyclerView.Adapter } //--- BOOST VISIBILITY --- switch (statusToDeal.visibility) { - case "public": - case "unlisted": - holder.binding.actionButtonBoost.setVisibility(View.VISIBLE); - break; - case "private": + case "public", "unlisted" -> + holder.binding.actionButtonBoost.setVisibility(View.VISIBLE); + 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 // --- 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 } 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 } })); 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 if (accounts.size() > 1) { List 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 }); 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 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 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; diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/admin/FragmentAdminAccount.java b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/admin/FragmentAdminAccount.java index 90e02138..678e8bb4 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/admin/FragmentAdminAccount.java +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/admin/FragmentAdminAccount.java @@ -15,6 +15,7 @@ package app.fedilab.android.mastodon.ui.fragment.admin; * see . */ +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(); } diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/admin/FragmentAdminDomain.java b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/admin/FragmentAdminDomain.java index b5c24ece..a46645a8 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/admin/FragmentAdminDomain.java +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/admin/FragmentAdminDomain.java @@ -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); diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/admin/FragmentAdminReport.java b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/admin/FragmentAdminReport.java index dc3834d8..93f03cd2 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/admin/FragmentAdminReport.java +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/admin/FragmentAdminReport.java @@ -15,6 +15,7 @@ package app.fedilab.android.mastodon.ui.fragment.admin; * see . */ +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 diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/media/FragmentMediaProfile.java b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/media/FragmentMediaProfile.java index c6da7a0b..8d8daca7 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/media/FragmentMediaProfile.java +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/media/FragmentMediaProfile.java @@ -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 . */ +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); diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/settings/FragmentTimelinesSettings.java b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/settings/FragmentTimelinesSettings.java index bc6c01f3..77946049 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/settings/FragmentTimelinesSettings.java +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/settings/FragmentTimelinesSettings.java @@ -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()); + } } diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonAccount.java b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonAccount.java index 55879749..4e26ec8e 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonAccount.java +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonAccount.java @@ -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(); } diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonAnnouncement.java b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonAnnouncement.java index 38701d1d..b19a4e9a 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonAnnouncement.java +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonAnnouncement.java @@ -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 . */ +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); diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonContext.java b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonContext.java index d476ad8d..3adf0c1f 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonContext.java +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonContext.java @@ -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() { - requireActivity().unregisterReceiver(receive_action); + try { + requireActivity().unregisterReceiver(receive_action); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } super.onDestroyView(); } diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonConversation.java b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonConversation.java index 99a22dca..5f70deac 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonConversation.java +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonConversation.java @@ -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(); } diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonDirectMessage.java b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonDirectMessage.java index 91690c06..3c0a6164 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonDirectMessage.java +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonDirectMessage.java @@ -272,7 +272,11 @@ public class FragmentMastodonDirectMessage extends Fragment { @Override public void onDestroyView() { - requireActivity().unregisterReceiver(broadcast_data); + try { + requireActivity().unregisterReceiver(broadcast_data); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } super.onDestroyView(); } diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonDomainBlock.java b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonDomainBlock.java index 9674611c..96704670 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonDomainBlock.java +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonDomainBlock.java @@ -15,6 +15,7 @@ package app.fedilab.android.mastodon.ui.fragment.timeline; * see . */ +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(); } diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonNotification.java b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonNotification.java index a728dbb1..e5f09fa7 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonNotification.java +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonNotification.java @@ -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() { - requireActivity().unregisterReceiver(receive_action); - requireActivity().unregisterReceiver(receive_refresh); + + try { + requireActivity().unregisterReceiver(receive_action); + requireActivity().unregisterReceiver(receive_refresh); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } if (isAdded()) { storeMarker(); } diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonSuggestion.java b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonSuggestion.java index 64eafe2e..8437cc3c 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonSuggestion.java +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonSuggestion.java @@ -15,6 +15,7 @@ package app.fedilab.android.mastodon.ui.fragment.timeline; * see . */ +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(); } diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonTag.java b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonTag.java index 13254a16..d0027c98 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonTag.java +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonTag.java @@ -15,6 +15,7 @@ package app.fedilab.android.mastodon.ui.fragment.timeline; * see . */ +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(); } diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonTimeline.java b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonTimeline.java index a1647ed4..64ee7268 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonTimeline.java +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonTimeline.java @@ -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) { diff --git a/app/src/main/java/app/fedilab/android/peertube/activities/PeertubeActivity.java b/app/src/main/java/app/fedilab/android/peertube/activities/PeertubeActivity.java index ea6b87fc..cab6b84f 100644 --- a/app/src/main/java/app/fedilab/android/peertube/activities/PeertubeActivity.java +++ b/app/src/main/java/app/fedilab/android/peertube/activities/PeertubeActivity.java @@ -1596,7 +1596,11 @@ public class PeertubeActivity extends BasePeertubeActivity implements CommentLis private void unregisterReceiver() { if (mPowerKeyReceiver != null) { - unregisterReceiver(mPowerKeyReceiver); + try { + unregisterReceiver(mPowerKeyReceiver); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } mPowerKeyReceiver = null; } } diff --git a/app/src/main/java/app/fedilab/android/peertube/activities/PeertubeMainActivity.java b/app/src/main/java/app/fedilab/android/peertube/activities/PeertubeMainActivity.java index daf4b4a6..3e56a2b6 100644 --- a/app/src/main/java/app/fedilab/android/peertube/activities/PeertubeMainActivity.java +++ b/app/src/main/java/app/fedilab/android/peertube/activities/PeertubeMainActivity.java @@ -153,7 +153,11 @@ public class PeertubeMainActivity extends PeertubeBaseMainActivity { if (!keepRemote) { typeOfConnection = TypeOfConnection.NORMAL; } - unregisterReceiver(broadcast_data); + try { + unregisterReceiver(broadcast_data); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } } @SuppressLint("ApplySharedPref") diff --git a/app/src/main/java/app/fedilab/android/peertube/services/RetrieveInfoService.java b/app/src/main/java/app/fedilab/android/peertube/services/RetrieveInfoService.java index 2411e4d2..a4b4842c 100644 --- a/app/src/main/java/app/fedilab/android/peertube/services/RetrieveInfoService.java +++ b/app/src/main/java/app/fedilab/android/peertube/services/RetrieveInfoService.java @@ -125,7 +125,11 @@ public class RetrieveInfoService extends Service implements NetworkStateReceiver super.onDestroy(); if (networkStateReceiver != null) { networkStateReceiver.removeListener(this); - unregisterReceiver(networkStateReceiver); + try { + unregisterReceiver(networkStateReceiver); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } } } diff --git a/app/src/main/res/drawables/peertube/drawable/progress_bar.xml b/app/src/main/res/drawables/peertube/drawable/progress_bar.xml index c0a479c8..049d4b60 100644 --- a/app/src/main/res/drawables/peertube/drawable/progress_bar.xml +++ b/app/src/main/res/drawables/peertube/drawable/progress_bar.xml @@ -3,7 +3,7 @@ - + @@ -17,7 +17,7 @@ - + diff --git a/app/src/main/res/layouts/mastodon/layout/activity_about.xml b/app/src/main/res/layouts/mastodon/layout/activity_about.xml index bc940ac2..4d7114ad 100644 --- a/app/src/main/res/layouts/mastodon/layout/activity_about.xml +++ b/app/src/main/res/layouts/mastodon/layout/activity_about.xml @@ -35,14 +35,32 @@ android:text="@string/app_name" android:textSize="20sp" /> - + android:gravity="center" + android:orientation="horizontal"> + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layouts/mastodon/layout/drawer_status.xml b/app/src/main/res/layouts/mastodon/layout/drawer_status.xml index 1f27d35f..d331c73d 100644 --- a/app/src/main/res/layouts/mastodon/layout/drawer_status.xml +++ b/app/src/main/res/layouts/mastodon/layout/drawer_status.xml @@ -657,32 +657,53 @@ - + app:layout_constraintVertical_bias="0.0"> + + + + + + - + app:layout_constraintVertical_bias="0.0"> + + + + android:fastScrollEnabled="true" + android:scrollbarSize="5dp" + android:scrollbarThumbVertical="?colorPrimary" + android:scrollbars="vertical" /> diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 032da256..c82e4c75 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -1068,4 +1068,10 @@ Vorschau Der Beitrag wird in mehrere Antworten aufgeteilt, um die maximale Zeichenanzahl Ihrer Instanz einzuhalten. Diese Beiträge in Antworten aufteilen? + Informationen wurden in die Zwischenablage kopiert + Zeige relatives Datum für Beiträge an + Informationen kopieren + Anklickbare Elemente unterstreichen + Du folgst bereits diesem Tag! + Bildlaufleiste in der Timeline anzeigen \ No newline at end of file diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index cff7941c..e2b37dea 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -747,7 +747,7 @@ Modo Borrar Tes a certeza de querer baleirar a caché\? Se tes borradores que inclúen multimedia, estes perderanse. A miña conta - Montrará icona con contador para as novas mensaxes nas cronoloxías da lapela + Mostrará un contador nas cronoloxías das pestanas para as novas mensaxes Cargar axustes exportados Xestor de notificacións Importar axustes @@ -1062,4 +1062,9 @@ Miniatura A mensaxe vai ser dividida en varias respostas para cumprir co número máximo de caracteres da túa instancia. Dividir estas mensaxes nas respostas? + Copiouse a información ao portapapeis + Mostrar a data relativa das mensaxes + Copiar información + Subliñar os elementos clicables + Xa segues ese cancelo! \ No newline at end of file diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml index a77b7b9a..8344bd2c 100644 --- a/app/src/main/res/values-night/themes.xml +++ b/app/src/main/res/values-night/themes.xml @@ -45,6 +45,7 @@ @transition/change_image_transform + @style/AppThemeAlertDialog @@ -98,6 +100,7 @@ true true + @style/AppThemeAlertDialog @@ -434,6 +444,7 @@ @color/dracula_comment @color/dracula_foreground @color/dracula_comment + @style/DraculaAlertDialog