Compare commits

..

No commits in common. "main" and "3.21.1" have entirely different histories.
main ... 3.21.1

984 changed files with 8487 additions and 31701 deletions

View file

@ -36,5 +36,6 @@ Android version:
<!-- If you read our contributing advice --> <!-- If you read our contributing advice -->
- [ ] I read the [contributing page](https://codeberg.org/tom79/Fedilab/src/branch/main/CONTRIBUTING.md) [ ] - I read
the [contributing page](https://codeberg.org/tom79/Fedilab/src/branch/main/CONTRIBUTING.md)

View file

@ -22,4 +22,5 @@ labels:
<!-- If you read our contributing advice --> <!-- If you read our contributing advice -->
- [ ] I read the [contributing page](https://codeberg.org/tom79/Fedilab/src/branch/main/CONTRIBUTING.md) [ ] - I read
the [contributing page](https://codeberg.org/tom79/Fedilab/src/branch/main/CONTRIBUTING.md)

View file

@ -3,7 +3,7 @@ CONTRIBUTING
### Localizations: ### Localizations:
Fedilab works only with [Weblate](https://hosted.weblate.org/projects/fedilab), which offers nice Fedilab works only with [Weblate]https://hosted.weblate.org/projects/fedilab), which offers nice
tools for helping in translations. New translations will be automatically merged in a branch. tools for helping in translations. New translations will be automatically merged in a branch.
If you're submiting a merge request and your work adds new strings to the app, make sure they only If you're submiting a merge request and your work adds new strings to the app, make sure they only

View file

@ -1,46 +1,24 @@
[![Translation status](https://hosted.weblate.org/widgets/fedilab/-/strings/svg-badge.svg)](https://hosted.weblate.org/engage/fedilab/)
&nbsp;&nbsp;&nbsp;[![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
<img src="src/fdroid/fastlane/metadata/android/en/images/icon.png" width="100"/> # Fedilab is a multi-accounts client for Mastodon, Pleroma, Friendica and Pixelfed
# Fedilab
A multi-accounts client for Mastodon, Pleroma, Friendica and Pixelfed
[![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg?style=for-the-badge)](https://www.gnu.org/licenses/gpl-3.0)
[![Weblate project translated](https://img.shields.io/weblate/progress/fedilab?server=https%3A%2F%2Fhosted.weblate.org&style=for-the-badge)](https://hosted.weblate.org/engage/fedilab/)
[![F-Droid Version](https://img.shields.io/f-droid/v/fr.gouv.etalab.mastodon?label=F-Droid&style=for-the-badge)](https://f-droid.org/app/fr.gouv.etalab.mastodon)
[![Liberapay patrons](https://img.shields.io/liberapay/patrons/tom79?label=Liberapay&style=for-the-badge)](https://liberapay.com/tom79/donate)
[![Open Collective backers and sponsors](https://img.shields.io/opencollective/all/fedilab?label=Open%20Collective&style=for-the-badge)](https://opencollective.com/fedilab)
## Screenshots
<img src="src/fdroid/fastlane/metadata/android/en/images/phoneScreenshots/1.png" width="150"/>
&nbsp;&nbsp;
<img src="src/fdroid/fastlane/metadata/android/en/images/phoneScreenshots/2.png" width="150"/>
&nbsp;&nbsp;
<img src="src/fdroid/fastlane/metadata/android/en/images/phoneScreenshots/4.png" width="150"/>
&nbsp;&nbsp;
<img src="src/fdroid/fastlane/metadata/android/en/images/phoneScreenshots/7.png" width="150"/>
## Download
[<img alt='Get it on F-Droid' src='./images/get-it-on-fdroid.png' height="80"/>](https://f-droid.org/app/fr.gouv.etalab.mastodon)&nbsp;&nbsp;[<img alt='Get it on Google Play' src='./images/get-it-on-play.png' height="80"/>](https://play.google.com/store/apps/details?id=app.fedilab.android)
## Translate
- [Weblate](https://hosted.weblate.org/engage/fedilab/)
## Donate ## Donate
- [Liberapay](https://liberapay.com/tom79/donate) [<img alt="Donate using Liberapay" src="https://img.shields.io/liberapay/patrons/tom79.svg?logo=liberapay"/>](https://liberapay.com/tom79/donate)
- [Open Collective](https://opencollective.com/fedilab)
- [More...](https://fedilab.app/page/donations/)
## Download
[<img alt='Get it on Google Play' src='./images/get-it-on-play.png' height="80"/>](https://play.google.com/store/apps/details?id=app.fedilab.android)
&nbsp;&nbsp;[<img alt='Get it on F-Droid' src='./images/get-it-on-fdroid.png' height="80"/>](https://f-droid.org/app/fr.gouv.etalab.mastodon)
<img src='https://img.shields.io/f-droid/v/fr.gouv.etalab.mastodon?include_prereleases' />
## Resources ## Resources
- [Wiki](https://wiki.fedilab.app) [WIKI](https://fedilab.app/wiki/home/)
- [Releases + Changelogs](https://codeberg.org/tom79/Fedilab/releases)
## Contact [Release notes](https://codeberg.org/tom79/Fedilab/tags)
- Fedi:<br>[toot.fedilab.app/@apps](https://toot.fedilab.app/@apps)
Lead developer: [toot.fedilab.app/@apps](https://toot.fedilab.app/@apps)

View file

@ -8,13 +8,13 @@ plugins {
} }
def flavor def flavor
android { android {
compileSdk 34 compileSdk 33
defaultConfig { defaultConfig {
minSdk 21 minSdk 21
targetSdk 34 targetSdk 33
versionCode 534 versionCode 488
versionName "3.33.1" versionName "3.21.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }
flavorDimensions "default" flavorDimensions "default"
@ -25,13 +25,12 @@ android {
} }
debug { debug {
applicationIdSuffix '.debug' applicationIdSuffix '.debug'
pseudoLocalesEnabled true
} }
} }
compileOptions { compileOptions {
coreLibraryDesugaringEnabled true coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_17 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_1_8
} }
productFlavors { productFlavors {
fdroid { fdroid {
@ -47,11 +46,16 @@ android {
flavor = "playstore" flavor = "playstore"
} }
} }
lintOptions {
checkReleaseBuilds false
abortOnError false
}
buildFeatures { buildFeatures {
viewBinding true viewBinding true
} }
sourceSets { sourceSets {
playstore { playstore {
manifest.srcFile "src/playstore/AndroidManifest.xml"
java.srcDirs = ['src/main/java', 'src/playstore/java'] java.srcDirs = ['src/main/java', 'src/playstore/java']
res.srcDirs = ['src/main/res', 'src/playstore/res'] res.srcDirs = ['src/main/res', 'src/playstore/res']
} }
@ -80,18 +84,10 @@ android {
} }
} }
configurations { configurations {
cleanedAnnotations
implementation.exclude group: 'org.jetbrains', module: 'annotations'
all { all {
exclude group: 'androidx.lifecycle', module: 'lifecycle-viewmodel-ktx' exclude group: 'androidx.lifecycle', module: 'lifecycle-viewmodel-ktx'
} }
} }
namespace 'app.fedilab.android'
lint {
abortOnError false
checkReleaseBuilds false
}
buildToolsVersion '35.0.0'
} }
allprojects { allprojects {
repositories { repositories {
@ -100,22 +96,20 @@ allprojects {
} }
} }
dependencies { dependencies {
implementation 'org.unifiedpush.android:connector:3.0.9' implementation project(':autoimageslider')
implementation 'androidx.appcompat:appcompat:1.6.1'
playstoreImplementation('org.unifiedpush.android:embedded-fcm-distributor:3.0.0') implementation 'com.google.android.material:material:1.8.0'
implementation 'androidx.appcompat:appcompat:1.7.0' implementation 'com.jaredrummler:colorpicker:1.1.0'
implementation 'com.google.android.material:material:1.10.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation "com.google.code.gson:gson:2.9.1"
implementation 'androidx.constraintlayout:constraintlayout:2.2.0'
implementation "com.google.code.gson:gson:2.10.1"
implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0' implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.retrofit2:converter-simplexml:2.9.0' implementation 'com.squareup.retrofit2:converter-simplexml:2.9.0'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'androidx.preference:preference:1.2.1' implementation 'androidx.preference:preference:1.2.0'
implementation "org.conscrypt:conscrypt-android:2.5.2" implementation "org.conscrypt:conscrypt-android:2.5.2"
implementation 'com.vanniktech:emoji-one:0.6.0' implementation 'com.vanniktech:emoji-one:0.6.0'
implementation 'com.github.GrenderG:Toasty:1.5.2' implementation 'com.github.GrenderG:Toasty:1.5.2'
@ -126,82 +120,76 @@ dependencies {
transitive = false transitive = false
} }
implementation "org.jsoup:jsoup:1.18.1" implementation "org.jsoup:jsoup:1.15.1"
implementation 'com.github.mergehez:ArgPlayer:v3.1'
implementation project(':autoimageslider') implementation 'com.github.mergehez:ArgPlayer:v3.1'
implementation project(path: ':mytransl') implementation project(path: ':mytransl')
implementation project(path: ':ratethisapp') implementation project(path: ':ratethisapp')
implementation project(path: ':sparkbutton') implementation project(path: ':sparkbutton')
implementation project(path: ':colorPicker')
implementation project(path: ':mathjaxandroid')
implementation project(path: ':doubletapplayerview')
implementation 'com.burhanrashid52:photoeditor:1.5.1' implementation 'com.burhanrashid52:photoeditor:1.5.1'
implementation("com.vanniktech:android-image-cropper:4.3.3") implementation("com.vanniktech:android-image-cropper:4.3.3")
implementation project(path: ':mathjaxandroid')
annotationProcessor "com.github.bumptech.glide:compiler:4.12.0" annotationProcessor "com.github.bumptech.glide:compiler:4.12.0"
implementation 'jp.wasabeef:glide-transformations:4.3.0' implementation 'jp.wasabeef:glide-transformations:4.3.0'
implementation 'com.github.penfeizhou.android.animation:glide-plugin:3.0.5' implementation 'com.github.penfeizhou.android.animation:glide-plugin:2.23.0'
implementation 'androidx.media3:media3-exoplayer-hls:1.2.1' implementation 'com.google.android.exoplayer:exoplayer:2.18.4'
implementation "androidx.media3:media3-exoplayer:1.2.1" implementation "androidx.viewpager2:viewpager2:1.0.0"
implementation "androidx.media3:media3-exoplayer-dash:1.2.1"
implementation "androidx.media3:media3-ui:1.2.1"
implementation "androidx.media3:media3-session:1.2.1"
implementation "androidx.viewpager2:viewpager2:1.1.0"
implementation 'com.github.piasy:rxandroidaudio:1.7.0' implementation 'com.github.piasy:rxandroidaudio:1.7.0'
implementation 'com.github.piasy:AudioProcessor:1.7.0' implementation 'com.github.piasy:AudioProcessor:1.7.0'
implementation "androidx.work:work-runtime:2.9.0" implementation "androidx.work:work-runtime:2.7.1"
implementation 'app.futured.hauler:hauler:5.0.0' implementation 'app.futured.hauler:hauler:5.0.0'
implementation "com.github.chrisbanes:PhotoView:2.3.0" implementation "com.github.chrisbanes:PhotoView:2.3.0"
implementation "ch.acra:acra-mail:5.11.3" implementation "ch.acra:acra-mail:5.9.6"
implementation "ch.acra:acra-limiter:5.11.3" implementation "ch.acra:acra-limiter:5.9.3"
implementation "ch.acra:acra-dialog:5.11.3" implementation "ch.acra:acra-dialog:5.9.6"
implementation "com.madgag.spongycastle:bctls-jdk15on:1.58.0.0" implementation "com.madgag.spongycastle:bctls-jdk15on:1.58.0.0"
implementation 'com.github.UnifiedPush:android-connector:2.1.1'
// implementation 'com.github.UnifiedPush:android-foss_embedded_fcm_distributor:1.0.0-beta1'
playstoreImplementation('com.github.UnifiedPush:android-embedded_fcm_distributor:2.2.0') {
exclude group: 'com.google.firebase', module: 'firebase-core'
exclude group: 'com.google.firebase', module: 'firebase-analytics'
exclude group: 'com.google.firebase', module: 'firebase-measurement-connector'
}
implementation 'com.burhanrashid52:photoeditor:1.5.1' implementation 'com.burhanrashid52:photoeditor:1.5.1'
implementation 'androidx.multidex:multidex:2.0.1' implementation 'androidx.multidex:multidex:2.0.1'
implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
implementation 'androidx.lifecycle:lifecycle-livedata:2.8.7' implementation 'androidx.lifecycle:lifecycle-livedata:2.5.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.8.7' implementation 'androidx.lifecycle:lifecycle-viewmodel:2.5.1'
implementation 'androidx.navigation:navigation-fragment:2.8.7' implementation 'androidx.navigation:navigation-fragment:2.5.3'
implementation 'androidx.navigation:navigation-ui:2.8.7' implementation 'androidx.navigation:navigation-ui:2.5.3'
testImplementation 'junit:junit:' testImplementation 'junit:junit:'
androidTestImplementation 'androidx.test.ext:junit:1.2.1' androidTestImplementation 'androidx.test.ext:junit:1.1.4'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0'
// debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1' // debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1'
implementation 'com.r0adkll:slidableactivity:2.1.0' implementation 'com.r0adkll:slidableactivity:2.1.0'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.4' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
implementation 'androidx.vectordrawable:vectordrawable:1.2.0'
implementation "androidx.fragment:fragment:1.8.6" implementation 'androidx.vectordrawable:vectordrawable:1.1.0'
implementation "androidx.fragment:fragment:1.5.5"
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'androidx.browser:browser:1.8.0' implementation 'androidx.browser:browser:1.5.0'
implementation 'androidx.documentfile:documentfile:1.0.1' implementation 'androidx.documentfile:documentfile:1.0.1'
implementation 'com.github.amoskorir:avatarimagegenerator:1.5.0' implementation 'com.github.amoskorir:avatarimagegenerator:1.5.0'
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.1.0'
implementation 'com.google.android.exoplayer:extension-mediasession:2.18.4'
implementation "com.github.mabbas007:TagsEditText:1.0.5" implementation "com.github.mabbas007:TagsEditText:1.0.5"
implementation "net.gotev:uploadservice:4.9.2" implementation "net.gotev:uploadservice:4.7.0"
implementation "net.gotev:uploadservice-okhttp:4.9.2" implementation "net.gotev:uploadservice-okhttp:4.7.0"
implementation 'androidx.media:media:1.7.0' implementation 'androidx.media:media:1.6.0'
implementation 'com.github.mancj:MaterialSearchBar:0.8.5' implementation 'com.github.mancj:MaterialSearchBar:0.8.5'
implementation 'com.github.androidmads:QRGenerator:1.0.1' implementation 'com.github.vkay94:DoubleTapPlayerView:1.0.0'
implementation 'io.noties.markwon:core:4.6.2'
implementation 'io.noties.markwon:ext-tables:4.6.2'
implementation 'io.noties.markwon:syntax-highlight:4.6.2'
implementation 'io.noties.markwon:ext-strikethrough:4.6.2'
implementation 'io.noties.markwon:inline-parser:4.6.2'
annotationProcessor 'io.noties:prism4j-bundler:2.0.0'
//************ CAST **************/// //************ CAST **************///
//---> Google libs (google_full) //---> Google libs (google_full)
playstoreImplementation "com.google.android.gms:play-services-cast-tv:21.1.1" playstoreImplementation "com.google.android.gms:play-services-cast-tv:19.0.1"
playstoreImplementation "com.google.android.gms:play-services-cast:22.0.0" playstoreImplementation "com.google.android.gms:play-services-cast:21.0.1"
playstoreImplementation "androidx.mediarouter:mediarouter:1.7.0" playstoreImplementation "androidx.mediarouter:mediarouter:1.3.0"
playstoreImplementation 'com.google.android.gms:play-services-cast-framework:22.0.0' playstoreImplementation 'com.google.android.gms:play-services-cast-framework:21.0.1'
//----> Other flavors //----> Other flavors

View file

@ -28,10 +28,9 @@ import android.view.View;
import android.webkit.MimeTypeMap; import android.webkit.MimeTypeMap;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.media3.common.Player; import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.media3.exoplayer.ExoPlayer;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -39,7 +38,6 @@ import org.jetbrains.annotations.NotNull;
import java.io.IOException; import java.io.IOException;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import app.fedilab.android.BuildConfig;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivityPeertubeBinding; import app.fedilab.android.databinding.ActivityPeertubeBinding;
import app.fedilab.android.mastodon.activities.BaseBarActivity; import app.fedilab.android.mastodon.activities.BaseBarActivity;
@ -126,8 +124,7 @@ public class BasePeertubeActivity extends BaseBarActivity {
if (PeertubeBaseMainActivity.chromecastActivated) { if (PeertubeBaseMainActivity.chromecastActivated) {
b.putInt("displayed", 0); b.putInt("displayed", 0);
intentBC.putExtras(b); intentBC.putExtras(b);
intentBC.setPackage(BuildConfig.APPLICATION_ID); LocalBroadcastManager.getInstance(BasePeertubeActivity.this).sendBroadcast(intentBC);
sendBroadcast(intentBC);
Handler mainHandler = new Handler(Looper.getMainLooper()); Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> { Runnable myRunnable = () -> {
binding.doubleTapPlayerView.setVisibility(View.VISIBLE); binding.doubleTapPlayerView.setVisibility(View.VISIBLE);
@ -139,8 +136,7 @@ public class BasePeertubeActivity extends BaseBarActivity {
b.putInt("displayed", 1); b.putInt("displayed", 1);
b.putSerializable("castedTube", peertube); b.putSerializable("castedTube", peertube);
intentBC.putExtras(b); intentBC.putExtras(b);
intentBC.setPackage(BuildConfig.APPLICATION_ID); LocalBroadcastManager.getInstance(BasePeertubeActivity.this).sendBroadcast(intentBC);
sendBroadcast(intentBC);
try { try {
Handler mainHandler = new Handler(Looper.getMainLooper()); Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> { Runnable myRunnable = () -> {

View file

@ -19,14 +19,12 @@ import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.view.View; import android.view.View;
import androidx.core.content.ContextCompat; import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
@ -36,7 +34,6 @@ import java.util.Collections;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.List; import java.util.List;
import app.fedilab.android.BuildConfig;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivityMainPeertubeBinding; import app.fedilab.android.databinding.ActivityMainPeertubeBinding;
import app.fedilab.android.mastodon.activities.BaseActivity; import app.fedilab.android.mastodon.activities.BaseActivity;
@ -52,35 +49,34 @@ public abstract class PeertubeBaseMainActivity extends BaseActivity implements C
public static List<ChromeCast> chromeCasts; public static List<ChromeCast> chromeCasts;
public static ChromeCast chromeCast; public static ChromeCast chromeCast;
public static boolean chromecastActivated = false; public static boolean chromecastActivated = false;
protected ActivityMainPeertubeBinding parentBinding; protected ActivityMainPeertubeBinding binding;
private BroadcastReceiver manage_chromecast; private BroadcastReceiver manage_chromecast;
private VideoData.Video castedTube; private VideoData.Video castedTube;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
parentBinding = ActivityMainPeertubeBinding.inflate(getLayoutInflater()); binding = ActivityMainPeertubeBinding.inflate(getLayoutInflater());
View view = parentBinding.getRoot(); View view = binding.getRoot();
setContentView(view); setContentView(view);
ChromeCastsListener chromeCastsListener = this; ChromeCastsListener chromeCastsListener = this;
ChromeCasts.registerListener(chromeCastsListener); ChromeCasts.registerListener(chromeCastsListener);
parentBinding.castClose.setOnClickListener(v -> { binding.castClose.setOnClickListener(v -> {
Intent intentBC = new Intent(Helper.RECEIVE_CAST_SETTINGS); Intent intentBC = new Intent(Helper.RECEIVE_CAST_SETTINGS);
Bundle b = new Bundle(); Bundle b = new Bundle();
b.putInt("displayed", 0); b.putInt("displayed", 0);
intentBC.putExtras(b); intentBC.putExtras(b);
intentBC.setPackage(BuildConfig.APPLICATION_ID); LocalBroadcastManager.getInstance(PeertubeBaseMainActivity.this).sendBroadcast(intentBC);
sendBroadcast(intentBC);
}); });
parentBinding.castTogglePlay.setOnClickListener(v -> { binding.castTogglePlay.setOnClickListener(v -> {
if (chromeCast != null) { if (chromeCast != null) {
new Thread(() -> { new Thread(() -> {
try { try {
Handler mainHandler = new Handler(Looper.getMainLooper()); Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> parentBinding.castTogglePlay.setVisibility(View.GONE); Runnable myRunnable = () -> binding.castTogglePlay.setVisibility(View.GONE);
mainHandler.post(myRunnable); mainHandler.post(myRunnable);
int icon = -1; int icon = -1;
if (chromeCast.getMediaStatus().playerState == MediaStatus.PlayerState.PLAYING) { if (chromeCast.getMediaStatus().playerState == MediaStatus.PlayerState.PLAYING) {
@ -92,10 +88,10 @@ public abstract class PeertubeBaseMainActivity extends BaseActivity implements C
} }
if (icon != -1) { if (icon != -1) {
int finalIcon = icon; int finalIcon = icon;
myRunnable = () -> parentBinding.castTogglePlay.setImageResource(finalIcon); myRunnable = () -> binding.castTogglePlay.setImageResource(finalIcon);
mainHandler.post(myRunnable); mainHandler.post(myRunnable);
} }
myRunnable = () -> parentBinding.castTogglePlay.setVisibility(View.VISIBLE); myRunnable = () -> binding.castTogglePlay.setVisibility(View.VISIBLE);
mainHandler.post(myRunnable); mainHandler.post(myRunnable);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
@ -131,14 +127,14 @@ public abstract class PeertubeBaseMainActivity extends BaseActivity implements C
if (displayed == 1) { if (displayed == 1) {
chromecastActivated = true; chromecastActivated = true;
if (castedTube != null) { if (castedTube != null) {
parentBinding.castInfo.setVisibility(View.VISIBLE); binding.castInfo.setVisibility(View.VISIBLE);
Helper.loadGiF(PeertubeBaseMainActivity.this, castedTube.getThumbnailPath(), parentBinding.castView); Helper.loadGiF(PeertubeBaseMainActivity.this, castedTube.getThumbnailPath(), binding.castView);
parentBinding.castTitle.setText(castedTube.getTitle()); binding.castTitle.setText(castedTube.getTitle());
parentBinding.castDescription.setText(castedTube.getDescription()); binding.castDescription.setText(castedTube.getDescription());
} }
} else if (displayed == 0) { } else if (displayed == 0) {
chromecastActivated = false; chromecastActivated = false;
parentBinding.castInfo.setVisibility(View.GONE); binding.castInfo.setVisibility(View.GONE);
new Thread(() -> { new Thread(() -> {
try { try {
if (chromeCast != null) { if (chromeCast != null) {
@ -151,7 +147,7 @@ public abstract class PeertubeBaseMainActivity extends BaseActivity implements C
} }
} }
}; };
ContextCompat.registerReceiver(PeertubeBaseMainActivity.this, manage_chromecast, new IntentFilter(Helper.RECEIVE_CAST_SETTINGS), ContextCompat.RECEIVER_NOT_EXPORTED); LocalBroadcastManager.getInstance(PeertubeBaseMainActivity.this).registerReceiver(manage_chromecast, new IntentFilter(Helper.RECEIVE_CAST_SETTINGS));
} }
@Override @Override
@ -173,8 +169,8 @@ public abstract class PeertubeBaseMainActivity extends BaseActivity implements C
} }
try { try {
if (chromeCast.isAppRunning(Helper.CAST_ID) && chromeCast.getMediaStatus() != null && chromeCast.getMediaStatus().playerState != null) { if (chromeCast.isAppRunning(Helper.CAST_ID) && chromeCast.getMediaStatus() != null && chromeCast.getMediaStatus().playerState != null) {
if (parentBinding.castInfo.getVisibility() == View.GONE) { if (binding.castInfo.getVisibility() == View.GONE) {
parentBinding.castInfo.setVisibility(View.VISIBLE); binding.castInfo.setVisibility(View.VISIBLE);
} }
} }
} catch (IOException e) { } catch (IOException e) {
@ -193,11 +189,8 @@ public abstract class PeertubeBaseMainActivity extends BaseActivity implements C
super.onDestroy(); super.onDestroy();
ChromeCasts.unregisterListener(this); ChromeCasts.unregisterListener(this);
if (manage_chromecast != null) { if (manage_chromecast != null) {
try { LocalBroadcastManager.getInstance(PeertubeBaseMainActivity.this).unregisterReceiver(manage_chromecast);
unregisterReceiver(manage_chromecast);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
new Thread(() -> { new Thread(() -> {
if (chromeCasts != null && chromeCasts.size() > 0) { if (chromeCasts != null && chromeCasts.size() > 0) {
for (ChromeCast cast : chromeCasts) { for (ChromeCast cast : chromeCasts) {

View file

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools"
package="app.fedilab.android">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
@ -12,7 +13,6 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-feature <uses-feature
android:name="android.hardware.camera" android:name="android.hardware.camera"
@ -24,7 +24,7 @@
</queries> </queries>
<application <application
android:name="app.fedilab.android.MainApplication" android:name="app.fedilab.android.MainApplication"
android:allowBackup="false" android:allowBackup="true"
android:dataExtractionRules="@xml/extraction_rules" android:dataExtractionRules="@xml/extraction_rules"
android:fullBackupContent="@xml/backup_rules" android:fullBackupContent="@xml/backup_rules"
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
@ -72,13 +72,106 @@
android:host="*" android:host="*"
android:pathPrefix="/@" android:pathPrefix="/@"
android:scheme="https" /> android:scheme="https" />
<data
android:host="*"
android:pathPrefix="/notes"
android:scheme="https" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity-alias
android:name=".activities.MainActivity.Bubbles"
android:enabled="true"
android:exported="true"
android:icon="@mipmap/ic_launcher_bubbles"
android:roundIcon="@mipmap/ic_launcher_bubbles_round"
android:targetActivity=".activities.MainActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/compose_shortcuts" />
</activity-alias>
<activity-alias
android:name=".activities.MainActivity.Fediverse"
android:enabled="false"
android:exported="true"
android:icon="@mipmap/ic_launcher_fediverse"
android:roundIcon="@mipmap/ic_launcher_fediverse_round"
android:targetActivity=".activities.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/compose_shortcuts" />
</activity-alias>
<activity-alias
android:name=".activities.MainActivity.Hero"
android:enabled="false"
android:exported="true"
android:icon="@mipmap/ic_launcher_hero"
android:roundIcon="@mipmap/ic_launcher_hero_round"
android:targetActivity=".activities.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/compose_shortcuts" />
</activity-alias>
<activity-alias
android:name=".activities.MainActivity.Atom"
android:enabled="false"
android:exported="true"
android:icon="@mipmap/ic_launcher_atom"
android:roundIcon="@mipmap/ic_launcher_atom_round"
android:targetActivity=".activities.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/compose_shortcuts" />
</activity-alias>
<activity-alias
android:name=".activities.MainActivity.BrainCrash"
android:enabled="false"
android:exported="true"
android:icon="@mipmap/ic_launcher_crash"
android:roundIcon="@mipmap/ic_launcher_crash_round"
android:targetActivity=".activities.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/compose_shortcuts" />
</activity-alias>
<activity-alias
android:name=".activities.MainActivity.Mastalab"
android:enabled="false"
android:exported="true"
android:icon="@mipmap/ic_launcher_mastalab"
android:roundIcon="@mipmap/ic_launcher_mastalab_round"
android:targetActivity=".activities.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/compose_shortcuts" />
</activity-alias>
<activity <activity
android:name=".activities.LoginActivity" android:name=".activities.LoginActivity"
@ -172,10 +265,6 @@
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/action_about" android:label="@string/action_about"
android:theme="@style/AppThemeBar" /> android:theme="@style/AppThemeBar" />
<activity
android:name=".mastodon.activities.TimelineActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:theme="@style/AppThemeBar" />
<activity <activity
android:name=".mastodon.activities.CheckHomeCacheActivity" android:name=".mastodon.activities.CheckHomeCacheActivity"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
@ -234,7 +323,6 @@
android:configChanges="keyboardHidden|orientation|screenSize" /> android:configChanges="keyboardHidden|orientation|screenSize" />
<activity <activity
android:name=".mastodon.activities.MediaActivity" android:name=".mastodon.activities.MediaActivity"
android:hardwareAccelerated="true"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
android:theme="@style/Transparent" /> android:theme="@style/Transparent" />
@ -264,16 +352,6 @@
android:label="@string/action_cache" android:label="@string/action_cache"
android:theme="@style/AppThemeBar" /> android:theme="@style/AppThemeBar" />
<activity android:name=".activities.WebActivityPub"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="web+ap" />
</intent-filter>
</activity>
<provider <provider
android:name="androidx.core.content.FileProvider" android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileProvider" android:authorities="${applicationId}.fileProvider"
@ -292,12 +370,18 @@
</intent-filter> </intent-filter>
</receiver> </receiver>
<service android:name=".mastodon.services.PushServiceImpl" <receiver
android:exported="false"> android:name=".mastodon.services.CustomReceiver"
android:enabled="true"
android:exported="true">
<intent-filter> <intent-filter>
<action android:name="org.unifiedpush.android.connector.PUSH_EVENT"/> <action android:name="org.unifiedpush.android.connector.MESSAGE" />
<action android:name="org.unifiedpush.android.connector.UNREGISTERED" />
<action android:name="org.unifiedpush.android.connector.NEW_ENDPOINT" />
<action android:name="org.unifiedpush.android.connector.REGISTRATION_FAILED" />
<action android:name="org.unifiedpush.android.connector.REGISTRATION_REFUSED" />
</intent-filter> </intent-filter>
</service> </receiver>
<activity <activity
@ -395,366 +479,8 @@
<service <service
android:name=".peertube.services.RetrieveInfoService" android:name=".peertube.services.RetrieveInfoService"
android:foregroundServiceType="dataSync"
android:exported="false" /> android:exported="false" />
<service
android:name="androidx.work.impl.foreground.SystemForegroundService"
android:foregroundServiceType="dataSync"
tools:node="merge" />
<!-- ============ -->
<!-- CUSTOM ICONS -->
<!-- ============ -->
<activity-alias
android:name=".activities.MainActivity.Bubbles"
android:enabled="true"
android:exported="true"
android:icon="@mipmap/ic_launcher_bubbles"
android:roundIcon="@mipmap/ic_launcher_bubbles_round"
android:targetActivity=".activities.MainActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/compose_shortcuts" />
</activity-alias>
<activity-alias
android:name=".activities.MainActivity.BubblesUA"
android:enabled="false"
android:exported="true"
android:icon="@mipmap/ic_launcher_bubbles_ua"
android:roundIcon="@mipmap/ic_launcher_bubbles_ua_round"
android:targetActivity=".activities.MainActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/compose_shortcuts" />
</activity-alias>
<activity-alias
android:name=".activities.MainActivity.BubblesPeaGreen"
android:enabled="false"
android:exported="true"
android:icon="@mipmap/ic_launcher_bubbles_pea_green"
android:roundIcon="@mipmap/ic_launcher_bubbles_pea_green_round"
android:targetActivity=".activities.MainActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/compose_shortcuts" />
</activity-alias>
<activity-alias
android:name=".activities.MainActivity.BubblesPride"
android:enabled="false"
android:exported="true"
android:icon="@mipmap/ic_launcher_bubbles_pride"
android:roundIcon="@mipmap/ic_launcher_bubbles_pride_round"
android:targetActivity=".activities.MainActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/compose_shortcuts" />
</activity-alias>
<activity-alias
android:name=".activities.MainActivity.BubblesPink"
android:enabled="false"
android:exported="true"
android:icon="@mipmap/ic_launcher_bubbles_pink"
android:roundIcon="@mipmap/ic_launcher_bubbles_pink_round"
android:targetActivity=".activities.MainActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/compose_shortcuts" />
</activity-alias>
<activity-alias
android:name=".activities.MainActivity.BubblesPirate"
android:enabled="false"
android:exported="true"
android:icon="@mipmap/ic_launcher_bubbles_pirate"
android:roundIcon="@mipmap/ic_launcher_bubbles_pirate_round"
android:targetActivity=".activities.MainActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/compose_shortcuts" />
</activity-alias>
<activity-alias
android:name=".activities.MainActivity.Fediverse"
android:enabled="false"
android:exported="true"
android:icon="@mipmap/ic_launcher_fediverse"
android:roundIcon="@mipmap/ic_launcher_fediverse_round"
android:targetActivity=".activities.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/compose_shortcuts" />
</activity-alias>
<activity-alias
android:name=".activities.MainActivity.Hero"
android:enabled="false"
android:exported="true"
android:icon="@mipmap/ic_launcher_hero"
android:roundIcon="@mipmap/ic_launcher_hero_round"
android:targetActivity=".activities.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/compose_shortcuts" />
</activity-alias>
<activity-alias
android:name=".activities.MainActivity.Atom"
android:enabled="false"
android:exported="true"
android:icon="@mipmap/ic_launcher_atom"
android:roundIcon="@mipmap/ic_launcher_atom_round"
android:targetActivity=".activities.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/compose_shortcuts" />
</activity-alias>
<activity-alias
android:name=".activities.MainActivity.BrainCrash"
android:enabled="false"
android:exported="true"
android:icon="@mipmap/ic_launcher_crash"
android:roundIcon="@mipmap/ic_launcher_crash_round"
android:targetActivity=".activities.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/compose_shortcuts" />
</activity-alias>
<activity-alias
android:name=".activities.MainActivity.Mastalab"
android:enabled="false"
android:exported="true"
android:icon="@mipmap/ic_launcher_mastalab"
android:roundIcon="@mipmap/ic_launcher_mastalab_round"
android:targetActivity=".activities.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/compose_shortcuts" />
</activity-alias>
<activity-alias
android:name=".activities.MainActivity.Leaf"
android:enabled="false"
android:exported="true"
android:icon="@mipmap/ic_launcher_leaf"
android:roundIcon="@mipmap/ic_launcher_leaf_round"
android:targetActivity=".activities.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/compose_shortcuts" />
</activity-alias>
<activity-alias
android:name=".activities.MainActivity.Offset"
android:enabled="false"
android:exported="true"
android:icon="@mipmap/ic_launcher_offset"
android:roundIcon="@mipmap/ic_launcher_offset_round"
android:targetActivity=".activities.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/compose_shortcuts" />
</activity-alias>
<activity-alias
android:name=".activities.MainActivity.Jungle"
android:enabled="false"
android:exported="true"
android:icon="@mipmap/ic_launcher_jungle"
android:roundIcon="@mipmap/ic_launcher_jungle_round"
android:targetActivity=".activities.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/compose_shortcuts" />
</activity-alias>
<activity-alias
android:name=".activities.MainActivity.Confetti"
android:enabled="false"
android:exported="true"
android:icon="@mipmap/ic_launcher_confetti"
android:roundIcon="@mipmap/ic_launcher_confetti_round"
android:targetActivity=".activities.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/compose_shortcuts" />
</activity-alias>
<activity-alias
android:name=".activities.MainActivity.Spaghetti"
android:enabled="false"
android:exported="true"
android:icon="@mipmap/ic_launcher_spaghetti"
android:roundIcon="@mipmap/ic_launcher_spaghetti_round"
android:targetActivity=".activities.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/compose_shortcuts" />
</activity-alias>
<activity-alias
android:name=".activities.MainActivity.Warm"
android:enabled="false"
android:exported="true"
android:icon="@mipmap/ic_launcher_warm"
android:roundIcon="@mipmap/ic_launcher_warm_round"
android:targetActivity=".activities.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/compose_shortcuts" />
</activity-alias>
<activity-alias
android:name=".activities.MainActivity.Purple1"
android:enabled="false"
android:exported="true"
android:icon="@mipmap/ic_launcher_purple_1"
android:roundIcon="@mipmap/ic_launcher_purple_1_round"
android:targetActivity=".activities.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/compose_shortcuts" />
</activity-alias>
<activity-alias
android:name=".activities.MainActivity.Purple2"
android:enabled="false"
android:exported="true"
android:icon="@mipmap/ic_launcher_purple_2"
android:roundIcon="@mipmap/ic_launcher_purple_2_round"
android:targetActivity=".activities.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/compose_shortcuts" />
</activity-alias>
<activity-alias
android:name=".activities.MainActivity.YellowHeadedRedBubble"
android:enabled="false"
android:exported="true"
android:icon="@mipmap/ic_launcher_yellow_headed_red_bubble"
android:roundIcon="@mipmap/ic_launcher_yellow_headed_red_bubble_round"
android:targetActivity=".activities.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/compose_shortcuts" />
</activity-alias>
<activity-alias
android:name=".activities.MainActivity.Mosaic"
android:enabled="false"
android:exported="true"
android:icon="@mipmap/ic_launcher_mosaic"
android:roundIcon="@mipmap/ic_launcher_mosaic_round"
android:targetActivity=".activities.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/compose_shortcuts" />
</activity-alias>
</application> </application>
</manifest> </manifest>

View file

@ -1,179 +1,4 @@
[ [
{
"version": "3.33.1",
"code": "534",
"note": "Added:\n- Highlight bottom hashtags\n- Support Trending Links\n- Featured tags displayed in profiles\n- Add/Remove featured tags from the profile editor\n\nChanged:\n- Add confirmation dialog when long pressing the boost button\n- Open messages by tapping on Scheduled Boost\n- Improve language picker when filtered with some languages\n\nFixed:\n- Limits number of fetch for filters\n- Pleroma instances cannot select media\n- Wrong messages deleted for scheduled (messages and boosts)\n- Fix a crash with long threads\n- Fix a potential memory issue for not cropped media\n- Fix embedded quotes not displayed\n- Some crashes"
},
{
"version": "3.32.3",
"code": "532",
"note": "Fixed:\n- Polls not displayed\n- Pagination with trends\n- Push notifications not working on some devices"
},
{
"version": "3.32.2",
"code": "531",
"note": "Added:\n- An outline around media\n\nChanged:\n- Make username, display name in nav drawer clickable\n- Gif media not animated by default\n- Disable by default the mention to the booster when replying. Can be enabled in Settings > Compose (per account)\n\nFixed:\n- Wrong preview picture on share from another app\n- Crash when translating with MinT\n- Refresh and pagination broken for the Trending timeline\n- Fix lags / Crashes"
},
{
"version": "3.32.1",
"code": "530",
"note": "Fixed:\n- Fix a crash on some devices\n- Hide quote button\n- Fix a layout issue with pictures in landscape\n- Fix a crash when opening the original message from a picture"
},
{
"version": "3.32.0",
"code": "529",
"note": "Added:\n- Add option to disable auto hiding compose button\n\nChanged:\n- Add more content descriptions for buttons\n- Update some buttons\n- Update navigation drawer header\n- Squeeze action buttons when needed to prevent overlapping\n\nFixed:\n- Fix crash when media are too heavy\n- Some custom emojis in bio do not render\n- Posting messages does not work on some Friendica instances\n- Fix a crash with auto-split messages\n- Fix a crash when opening conversations\n- Fix a background color issue when displaying media"
},
{
"version": "3.31.3",
"code": "528",
"note": "Added:\n- Add new icon launchers (Settings > Interface)\n\nChanged:\n- Make logout/proxy button more visible in main menu\n- Remove permission FOREGROUND_SERVICE\n- Improve a little more media layout with translations\n\nFixed:\n- Fix status bar icons not visible in light theme with custom accent color\n- Reaction buttons not clickable for some instances"
},
{
"version": "3.31.2",
"code": "527",
"note": "Added:\n- Add support to URL scheme \"web+ap\" for opening profiles with the app\n\nChanged:\n- Layout for media descriptions\n\nFixed:\n- Fix a crash when translating media descriptions\n- Handle included twice when replying to a self user's boost\n- Fix color issues when using a custom theme"
},
{
"version": "3.31.1",
"code": "526",
"note": "Added:\n- Add MinT machine translation system support\n- Add support \"instance only\" for GoToSocial\n\nFixed:\n- GIF not displayed in timelines\n- Fix a crash when unpinning timelines\n- Top bar coloring at scroll for conversations\n- Black screen when going back from the Peertube section"
},
{
"version": "3.31.0",
"code": "524",
"note": "Added:\n- Pinned Trending Timeline (can be hidden in Manage Timelines)\n- Add a fallback to the default translator\n- New Pixelfed entry in Settings to disable fullscreen for media\n- Add tooltips for tabs in profiles\n\nChanged:\n- Increase touch area of reply buttons\n- Show a dialog after settings export\n\nFixed:\n- Fix media description not updated when there are several\n- Only a part of DeepL translations are shown\n- Fix Lingva truncated translations\n- Fix a crash when fetching remote profiles\n- Fix a crash with animated emoji\n- Fix a crash when displaying Home cache charts"
},
{
"version": "3.30.1",
"code": "523",
"note": "Added:\n- Follow Twitter tags\n\nChange:\n- Remove automatic backup (Google only)\n\nFixed:\n- Push notifications not working for some devices\n- Filters not applied to media description\n- Fix a crash with animated images in timelines\n- Fix a crash for long threads\n- Fix a crash due to some messages (happened in different timelines)"
},
{
"version": "3.30.0",
"code": "522",
"note": "Added:\n- Allow to follow the discover timelines of Pixelfed instances\n- Keep media proportions in timeline for Pixelfed\n- Add a like button in timelines for Pixelfed\n- Allow to login with a token\n\nChange:\n- Use Pixelfed layout when following Pixelfed instances\n- Allow to scroll buttons for larger screens in conversations\n- Move QR code into header\n\nFixed:\n- Not clickable URLs when Markdown is enabled\n- Nitter to follow Twitter accounts\n- Pixelfed timeline not displayed by default\n- Fix admin reports not accessible\n- Top bar coloring at scroll\n- Drafts not saved when adding/editing media descriptions\n- User search suggestions have duplicates\n- Some minor crashes"
},
{
"version": "3.29.2",
"code": "518",
"note": "Fixed:\n- Fix crash when changing the type of notifications\n- Fix issue with names not displayed fully\n- Fix a crash with notifications and Sharkey\n- Fix a crash when app is back to the foreground\n- Fix xmpp links not opening xmpp clients\n- Fix wrong muted time"
},
{
"version": "3.29.1",
"code": "517",
"note": "Added:\n- Allow to edit scheduled messages from server side\n\nChanged:\n- Order list name alphabetically in profiles\n- Remove registration for Google\n\nFixed:\n- Push notifications\n- Peertube instances picker\n- Edit scheduled threads (local)\n- Instant search of Hashtag repeats results\n- Quotes broken with Markdowns\n- Fix reports crashes after submitting\n- Fix emoji picker when there is no result\n- Fix other crashes"
},
{
"version": "3.28.2",
"code": "515",
"note": "Added:\n- Display a QR code on profiles\n\nFixed:\n- Fix tap on messages in conversations\n- Pronouns taking too much place"
},
{
"version": "3.28.1",
"code": "514",
"note": "Added:\n- Allow to disable pronouns support (default: enabled)\n- Add more support for pronouns (localization in different languages)\n\nFixed:\n- Fix a crash when reporting messages\n- Fix a crash when following tags\n- Fix some display issues\n- Several fixes from the last release (3.28.0)"
},
{
"version": "3.28.0",
"code": "513",
"note": "Added:\n- Pronouns support (Timeline/Compose/Autocomplete)\n\nChanged:\n- Use Media3 library\n\nFixed:\n- Timed mute duration too long\n- Sharing videos only download them\n- Crashes from previous release"
},
{
"version": "3.27.1",
"code": "511",
"note": "Added:\n- Tap on account banners to display them as media\n\nFixed:\n- Position lost when switching between accounts\n- Wrong profiles when enabling remote conversations\n- Peertube local timeline\n- Peertube instances search\n- Crashes of the previous release"
},
{
"version": "3.27.0",
"code": "510",
"note": "Added:\n- Fixed top bar (default: disabled)\n- Usage frequency of tags when composing\n\nChanged:\n- Markdown support disabled by default\n\nFixed:\n- Fix crashes during interactions or when opening a new screen\n- Fix color of dialogs in Settings\n- Some minor crashes"
},
{
"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",
"note": "Added:\n- Android 14 support\n- Automatically split long messages in threads (default: ASK)\n- Links and media are clickable when composing\n\nFixed:\n- Avoid error 429 with NTFY\n- Several crashes are fixed"
},
{
"version": "3.24.1",
"code": "500",
"note": "Added:\n- Three new app icons (Pride, Pink and Pirate)\n- Keep position with remote conversations\n\nFixed:\n- Markdown: stop parsing tags and support strike text\n- Cursor more visible when composing\n- Fix custom instance max char length not working\n- Tabs in profiles\n- Fix not clickable tags for some languages\n- Bug with account having huge amount of followers\n- Crash with several gif in same message\n- Poll max chars\n- Some crashes"
},
{
"version": "3.24.0",
"code": "499",
"note": "Added:\n- Markdown support (can be disabled in Settings > Timelines)\n- Hide / Show Self boosts, self replies and your own messages (Long press the Home tab)\n\nChanged:\n- Full screen size when writing media descriptions.\n- Move media descriptions to the top\n\nFixed:\n- Holes in timelines due to a cache bug\n- Spoiler issue when composing threads\n- CamelCase tags when forwarding them in replies\n- Buttons hidden by keyboard when composing\n- Overlay with menu and buttons when playing videos\n- Clicks on card do not open Mastodon posts inside the app\n- Scrollable bio when editing profiles\n- Crash when adding a user into a list\n- Longer fields when editing bio\n- Crash with Pixelfed\n"
},
{
"version": "3.23.5",
"code": "498",
"note": "Added:\n- Hide / Show Self boosts and self replies (Long press the Home tab)\n\nChanged:\n- Full screen size when writing media descriptions."
},
{
"version": "3.23.4",
"code": "497",
"note": "Fixed:\n\n- Holes in timelines due to a cache bug\n- Fix tags issue with RTL and the markdown format\n- Scrollable bio when editing profiles\n- Crash when adding a user into a list"
},
{
"version": "3.23.3",
"code": "496",
"note": "- Longer fields when editing bio\n- Clicks on card do not open Mastodon posts inside the app\n- Crash with Pixelfed\n- Line breaks with Markdown\n- Side effects with Markdown and some links"
},
{
"version": "3.23.2",
"code": "495",
"note": "Added:\n- Markdown support (can be disabled in Settings > Timelines)"
},
{
"version": "3.23.1",
"code": "494",
"note": "Added:\n- Scrollable media description\n\nFixed:\n- Crashes with profiles"
},
{
"version": "3.23.0",
"code": "493",
"note": "Added:\n- Add preview for app icons\n- Two new app icons\n\nFixed:\n- Fix Nitter feeds\n- Crash with Pixelfed accounts\n- Lingva encoding issue\n- Avoid sleep mode for media activity\n- Videos are played simultaneously\n- Voice messages for Android 10+\n- Punycode not supported for domains"
},
{
"version": "3.22.2",
"code": "492",
"note": "Added:\n- Follow Lemmy instances (from Manage Timelines)\n- View remote conversations (default: disabled - Settings > Interface)\n\nFixed:\n- Add 50 chars max for poll options\n- Too many requests\n- Blank Home page\n- Crashes when visiting profiles\n- Some audio files cannot be uploaded\n- Multiple notifications\n- Fix some other crashes"
},
{
"version": "3.22.1",
"code": "491",
"note": "Added:\n- Follow Lemmy instance (from Manage Timelines)\nFixed:\n- Add 50 chars max for poll options"
},
{
"version": "3.22.0",
"code": "490",
"note": "Fixed:\n- Too many requests\n- Blank Home page\n- Crashes when visiting profiles\n- Some audio files cannot be uploaded"
},
{
"version": "3.21.2",
"code": "489",
"note": "Added:\n- Android 12+ : Customize accent colors for light/dark theme and per account (Settings > Theming > Custom accent color)\n"
},
{ {
"version": "3.21.1", "version": "3.21.1",
"code": "488", "code": "488",
@ -293,6 +118,30 @@
"version": "3.14.3", "version": "3.14.3",
"code": "465", "code": "465",
"note": "Added:\n- Display date of the message instead of the boost (default: disabled)\n- Allow to disable release notes popup in Settings\n\nFixed:\n- Fix timelines slow down and stuttering after some scrolls\n- Fix color issues with follow buttons\n- Fix import from settings (import from login was OK)" "note": "Added:\n- Display date of the message instead of the boost (default: disabled)\n- Allow to disable release notes popup in Settings\n\nFixed:\n- Fix timelines slow down and stuttering after some scrolls\n- Fix color issues with follow buttons\n- Fix import from settings (import from login was OK)"
},
{
"version": "3.14.2",
"code": "464",
"note": "Added:\n- Display familiar followers on profiles\n- Display and filter Instance directory\n\nChanged:\n- Bot and reply icon indicators more visible\n- Single media are hidden with link previews\n\nFixed:\n- Fix a crash with Art timelines\n- Friendica: media cannot be downloaded/shared\n- Fix a crash with pinned timelines"
},
{
"version": "3.14.1",
"code": "463",
"note": "Added:\n- Search bar: display suggestions when starting by \"@\" or \"#\"\n\nChanged:\n- Preload media in timelines to avoid jumps\n- Search: Automatically switch to account tab if no results for tags\n\nFixed:\n- Fix jumps with the fetch more feature\n- Fix videos cannot be saved\n- Tags cannot be pinned when there are no custom tabs\n- PixelFed view: NSFW not honored\n- Fix crashes"
},
{
"version": "3.14.0",
"code": "462",
"note": "Added:\n\n- Add Bubble timeline support in extra-features with filters\n- Allow to display public profiles by default to get all messages (Settings > Interface)\n- Glitch: Allow to post messages locally (Can be turned off in Settings)\n- Pixelfed: Custom layout to display Media fully (Also works for other software when there are media)\n- Allow to align left action buttons in messages\n\nChanged:\n- Full rework on links in messages (also mentions and tags)\n- Add pinned tag in \"any\" to avoid to lose it when renaming timeline\n\nFixed:\n- Links to messages not handled by the app\n- CW when editing a message\n- Fix push notifications with several accounts\n- New messages or edition notifications not pushed\n- Fix quotes with tags/mentions\n- Fix notifications\n- Fix sending multiple media\n- Fix crashes"
},
{
"version": "3.13.7",
"code": "461",
"note": "Added:\n- Pixelfed: Custom layout to display Media fully \n*(Settings > Timelines > Pixelfed Presentation) - Also works for other softwares when there are media\n\nChanged:\n- Add pinned tag in \"any\" to avoid to lose it when renaming timeline\n\nFixed:\n- Fix push notifications with several accounts\n- Fix quotes with tags/mentions\n- Fix notifications\n- Fix sending multiple media\n- Some crashes"
},
{
"version": "3.13.6",
"code": "460",
"note": "Fixed:\n- Cross-compose: Wrong instance emojis\n- Custom emojis not displayed in notifications\n- Fav/Boost markers with shared messages\n- Empty notifications\n- Fix cw removed when replying\n- Fix expand media with fit preview images when sensitive\n- Fix an issue with fetch more displayed too often (cache clear will help or wait new messages)"
} }
] ]

View file

@ -17,7 +17,6 @@ package app.fedilab.android;
import static app.fedilab.android.BaseMainActivity.status.DISCONNECTED; import static app.fedilab.android.BaseMainActivity.status.DISCONNECTED;
import static app.fedilab.android.BaseMainActivity.status.UNKNOWN; import static app.fedilab.android.BaseMainActivity.status.UNKNOWN;
import static app.fedilab.android.mastodon.helper.CacheHelper.deleteDir; import static app.fedilab.android.mastodon.helper.CacheHelper.deleteDir;
import static app.fedilab.android.mastodon.helper.Helper.ARG_REFRESH_NOTFICATION;
import static app.fedilab.android.mastodon.helper.Helper.PREF_USER_ID; import static app.fedilab.android.mastodon.helper.Helper.PREF_USER_ID;
import static app.fedilab.android.mastodon.helper.Helper.PREF_USER_INSTANCE; import static app.fedilab.android.mastodon.helper.Helper.PREF_USER_INSTANCE;
import static app.fedilab.android.mastodon.helper.Helper.PREF_USER_SOFTWARE; import static app.fedilab.android.mastodon.helper.Helper.PREF_USER_SOFTWARE;
@ -71,17 +70,15 @@ import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.PopupMenu; import androidx.appcompat.widget.PopupMenu;
import androidx.appcompat.widget.SearchView; import androidx.appcompat.widget.SearchView;
import androidx.appcompat.widget.Toolbar;
import androidx.appcompat.widget.TooltipCompat;
import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat; import androidx.core.app.ActivityOptionsCompat;
import androidx.core.view.GravityCompat; import androidx.core.view.GravityCompat;
import androidx.cursoradapter.widget.CursorAdapter; import androidx.cursoradapter.widget.CursorAdapter;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction; import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.multidex.BuildConfig; import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.navigation.NavController; import androidx.navigation.NavController;
import androidx.navigation.Navigation; import androidx.navigation.Navigation;
import androidx.navigation.ui.AppBarConfiguration; import androidx.navigation.ui.AppBarConfiguration;
@ -99,7 +96,6 @@ import com.bumptech.glide.request.RequestOptions;
import com.bumptech.glide.request.target.CustomTarget; import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.target.Target; import com.bumptech.glide.request.target.Target;
import com.bumptech.glide.request.transition.Transition; import com.bumptech.glide.request.transition.Transition;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.bottomnavigation.BottomNavigationView; import com.google.android.material.bottomnavigation.BottomNavigationView;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.navigation.NavigationView; import com.google.android.material.navigation.NavigationView;
@ -113,7 +109,6 @@ import org.jsoup.nodes.Element;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.net.IDN;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -164,7 +159,6 @@ import app.fedilab.android.mastodon.client.entities.api.Tag;
import app.fedilab.android.mastodon.client.entities.app.Account; import app.fedilab.android.mastodon.client.entities.app.Account;
import app.fedilab.android.mastodon.client.entities.app.BaseAccount; import app.fedilab.android.mastodon.client.entities.app.BaseAccount;
import app.fedilab.android.mastodon.client.entities.app.BottomMenu; import app.fedilab.android.mastodon.client.entities.app.BottomMenu;
import app.fedilab.android.mastodon.client.entities.app.CachedBundle;
import app.fedilab.android.mastodon.client.entities.app.MutedAccounts; import app.fedilab.android.mastodon.client.entities.app.MutedAccounts;
import app.fedilab.android.mastodon.client.entities.app.Pinned; import app.fedilab.android.mastodon.client.entities.app.Pinned;
import app.fedilab.android.mastodon.client.entities.app.PinnedTimeline; import app.fedilab.android.mastodon.client.entities.app.PinnedTimeline;
@ -178,7 +172,6 @@ import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.helper.MastodonHelper; import app.fedilab.android.mastodon.helper.MastodonHelper;
import app.fedilab.android.mastodon.helper.PinnedTimelineHelper; import app.fedilab.android.mastodon.helper.PinnedTimelineHelper;
import app.fedilab.android.mastodon.helper.PushHelper; import app.fedilab.android.mastodon.helper.PushHelper;
import app.fedilab.android.mastodon.helper.ThemeHelper;
import app.fedilab.android.mastodon.ui.drawer.AccountsSearchTopBarAdapter; import app.fedilab.android.mastodon.ui.drawer.AccountsSearchTopBarAdapter;
import app.fedilab.android.mastodon.ui.drawer.TagSearchTopBarAdapter; import app.fedilab.android.mastodon.ui.drawer.TagSearchTopBarAdapter;
import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonConversation; import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonConversation;
@ -212,25 +205,21 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
public static List<Filter> mainFilters; public static List<Filter> mainFilters;
public static List<app.fedilab.android.mastodon.client.entities.api.Account> filteredAccounts; public static List<app.fedilab.android.mastodon.client.entities.api.Account> filteredAccounts;
public static boolean filterFetched; public static boolean filterFetched;
public static int filterFetchedRetry = 0; public static boolean show_boosts, show_replies, show_dms, show_art_nsfw;
public static boolean show_boosts, show_replies, show_dms, show_art_nsfw, show_self_boosts, show_self_replies, show_my_messages;
public static String regex_home, regex_local, regex_public; public static String regex_home, regex_local, regex_public;
public static BaseAccount currentAccount;
public static iconLauncher mLauncher = iconLauncher.BUBBLES; public static iconLauncher mLauncher = iconLauncher.BUBBLES;
public static boolean headerMenuOpen;
public static int currentNightMode;
Fragment currentFragment; Fragment currentFragment;
private AppBarConfiguration mAppBarConfiguration; private AppBarConfiguration mAppBarConfiguration;
private ActivityMainBinding binding; private ActivityMainBinding binding;
private final BroadcastReceiver broadcast_error_message = new BroadcastReceiver() { private final BroadcastReceiver broadcast_error_message = new BroadcastReceiver() {
@Override @Override
public void onReceive(android.content.Context context, Intent intent) { public void onReceive(android.content.Context context, Intent intent) {
Bundle args = intent.getExtras(); Bundle b = intent.getExtras();
if (args != null) { if (b != null) {
long bundleId = args.getLong(Helper.ARG_INTENT_ID, -1); if (b.getBoolean(Helper.RECEIVE_COMPOSE_ERROR_MESSAGE, false)) {
new CachedBundle(BaseMainActivity.this).getBundle(bundleId, Helper.getCurrentAccount(BaseMainActivity.this), bundle -> { String errorMessage = b.getString(Helper.RECEIVE_ERROR_MESSAGE);
if (bundle.getBoolean(Helper.RECEIVE_COMPOSE_ERROR_MESSAGE, false)) { StatusDraft statusDraft = (StatusDraft) b.getSerializable(Helper.ARG_STATUS_DRAFT);
String errorMessage = bundle.getString(Helper.RECEIVE_ERROR_MESSAGE);
StatusDraft statusDraft = (StatusDraft) bundle.getSerializable(Helper.ARG_STATUS_DRAFT);
Snackbar snackbar = Snackbar.make(binding.getRoot(), errorMessage, 5000); Snackbar snackbar = Snackbar.make(binding.getRoot(), errorMessage, 5000);
View snackbarView = snackbar.getView(); View snackbarView = snackbar.getView();
TextView textView = snackbarView.findViewById(com.google.android.material.R.id.snackbar_text); TextView textView = snackbarView.findViewById(com.google.android.material.R.id.snackbar_text);
@ -238,20 +227,12 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
snackbar snackbar
.setAction(getString(R.string.open_draft), view -> { .setAction(getString(R.string.open_draft), view -> {
Intent intentCompose = new Intent(context, ComposeActivity.class); Intent intentCompose = new Intent(context, ComposeActivity.class);
Bundle args2 = new Bundle(); intentCompose.putExtra(Helper.ARG_STATUS_DRAFT, statusDraft);
args2.putSerializable(Helper.ARG_STATUS_DRAFT, statusDraft);
new CachedBundle(BaseMainActivity.this).insertBundle(args2, Helper.getCurrentAccount(BaseMainActivity.this), bundleId2 -> {
Bundle bundle2 = new Bundle();
bundle2.putLong(Helper.ARG_INTENT_ID, bundleId2);
intentCompose.putExtras(bundle2);
intentCompose.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intentCompose.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intentCompose); context.startActivity(intentCompose);
});
}) })
.show(); .show();
} }
});
} }
} }
}; };
@ -260,16 +241,14 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
private final BroadcastReceiver broadcast_data = new BroadcastReceiver() { private final BroadcastReceiver broadcast_data = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
Bundle args = intent.getExtras(); Bundle b = intent.getExtras();
if (args != null) { if (b != null) {
long bundleId = args.getLong(Helper.ARG_INTENT_ID, -1); if (b.getBoolean(Helper.RECEIVE_REDRAW_TOPBAR, false)) {
new CachedBundle(BaseMainActivity.this).getBundle(bundleId, Helper.getCurrentAccount(BaseMainActivity.this), bundle -> { List<MastodonList> mastodonLists = (List<MastodonList>) b.getSerializable(Helper.RECEIVE_MASTODON_LIST);
if (bundle.getBoolean(Helper.RECEIVE_REDRAW_TOPBAR, false)) {
List<MastodonList> mastodonLists = (List<MastodonList>) bundle.getSerializable(Helper.RECEIVE_MASTODON_LIST);
redrawPinned(mastodonLists); redrawPinned(mastodonLists);
} }
if (bundle.getBoolean(Helper.RECEIVE_REDRAW_BOTTOM, false)) { if (b.getBoolean(Helper.RECEIVE_REDRAW_BOTTOM, false)) {
bottomMenu = new BottomMenu(BaseMainActivity.this).hydrate(Helper.getCurrentAccount(BaseMainActivity.this), binding.bottomNavView); bottomMenu = new BottomMenu(BaseMainActivity.this).hydrate(currentAccount, binding.bottomNavView);
if (bottomMenu != null) { if (bottomMenu != null) {
//ManageClick on bottom menu items //ManageClick on bottom menu items
if (binding.bottomNavView.findViewById(R.id.nav_home) != null) { if (binding.bottomNavView.findViewById(R.id.nav_home) != null) {
@ -312,23 +291,17 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
return true; return true;
}); });
} }
} else if (bundle.getBoolean(Helper.RECEIVE_RECREATE_ACTIVITY, false)) { } else if (b.getBoolean(Helper.RECEIVE_RECREATE_ACTIVITY, false)) {
recreate(); recreate();
} else if (bundle.getBoolean(Helper.RECEIVE_NEW_MESSAGE, false)) { } else if (b.getBoolean(Helper.RECEIVE_NEW_MESSAGE, false)) {
Status statusSent = (Status) bundle.getSerializable(Helper.RECEIVE_STATUS_ACTION); Status statusSent = (Status) b.getSerializable(Helper.RECEIVE_STATUS_ACTION);
String statusEditId = bundle.getString(Helper.ARG_EDIT_STATUS_ID, null); String statusEditId = b.getString(Helper.ARG_EDIT_STATUS_ID, null);
Snackbar.make(binding.displaySnackBar, getString(R.string.message_has_been_sent), Snackbar.LENGTH_LONG) Snackbar.make(binding.displaySnackBar, getString(R.string.message_has_been_sent), Snackbar.LENGTH_LONG)
.setAction(getString(R.string.display), view -> { .setAction(getString(R.string.display), view -> {
Intent intentContext = new Intent(BaseMainActivity.this, ContextActivity.class); Intent intentContext = new Intent(BaseMainActivity.this, ContextActivity.class);
Bundle args2 = new Bundle(); intentContext.putExtra(Helper.ARG_STATUS, statusSent);
args2.putSerializable(Helper.ARG_STATUS, statusSent);
new CachedBundle(BaseMainActivity.this).insertBundle(args2, Helper.getCurrentAccount(BaseMainActivity.this), bundleId2 -> {
Bundle bundle2 = new Bundle();
bundle2.putLong(Helper.ARG_INTENT_ID, bundleId2);
intentContext.putExtras(bundle2);
intentContext.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intentContext.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intentContext); startActivity(intentContext);
});
}) })
.show(); .show();
//The message was edited, we need to update the timeline //The message was edited, we need to update the timeline
@ -350,31 +323,29 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
sendAction(context, Helper.ARG_STATUS_UPDATED, statusSent, null); sendAction(context, Helper.ARG_STATUS_UPDATED, statusSent, null);
} }
} }
});
} }
} }
}; };
private NetworkStateReceiver networkStateReceiver; private NetworkStateReceiver networkStateReceiver;
public static boolean headerMenuOpen;
SharedPreferences sharedpreferences;
public static void fetchRecentAccounts(Activity activity, NavHeaderMainBinding headerMainBinding) { public static void fetchRecentAccounts(Activity activity, NavHeaderMainBinding headerMainBinding) {
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(activity); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(activity);
//Fetch some db values to initialize data //Fetch some db values to initialize data
new Thread(() -> { new Thread(() -> {
try { try {
if (Helper.getCurrentAccount(activity) == null) { if (currentAccount == null) {
if (currentToken == null || currentToken.trim().isEmpty()) { if (currentToken == null || currentToken.trim().isEmpty()) {
currentToken = sharedpreferences.getString(Helper.PREF_USER_TOKEN, null); currentToken = sharedpreferences.getString(Helper.PREF_USER_TOKEN, null);
} }
try { try {
Helper.setCurrentAccount(new Account(activity).getConnectedAccount()); currentAccount = new Account(activity).getConnectedAccount();
} catch (DBException e) { } catch (DBException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
if (Helper.getCurrentAccount(activity) != null) { if (currentAccount != null) {
MutedAccounts mutedAccounts = new MutedAccounts(activity).getMutedAccount(Helper.getCurrentAccount(activity)); MutedAccounts mutedAccounts = new MutedAccounts(activity).getMutedAccount(currentAccount);
if (mutedAccounts != null && mutedAccounts.accounts != null) { if (mutedAccounts != null && mutedAccounts.accounts != null) {
filteredAccounts = mutedAccounts.accounts; filteredAccounts = mutedAccounts.accounts;
} }
@ -382,7 +353,6 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
//Delete cache older than 7 days //Delete cache older than 7 days
new StatusCache(activity).deleteForAllAccountAfter7Days(); new StatusCache(activity).deleteForAllAccountAfter7Days();
new TimelineCacheLogs(activity).deleteForAllAccountAfter7Days(); new TimelineCacheLogs(activity).deleteForAllAccountAfter7Days();
new CachedBundle(activity).deleteOldIntent();
} catch (DBException e) { } catch (DBException e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -465,7 +435,7 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
public static void manageDrawerMenu(Activity activity, NavigationView navigationView, NavHeaderMainBinding headerMainBinding) { public static void manageDrawerMenu(Activity activity, NavigationView navigationView, NavHeaderMainBinding headerMainBinding) {
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(activity); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(activity);
if (headerMenuOpen) { if (headerMenuOpen) {
headerMainBinding.ownerAccounts.setIconResource(R.drawable.ic_baseline_arrow_drop_up_24); headerMainBinding.ownerAccounts.setImageResource(R.drawable.ic_baseline_arrow_drop_up_24);
new Thread(() -> { new Thread(() -> {
try { try {
List<BaseAccount> accounts = new Account(activity).getOtherAccounts(); List<BaseAccount> accounts = new Account(activity).getOtherAccounts();
@ -596,7 +566,7 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
Intent mainActivity = new Intent(activity, MainActivity.class); Intent mainActivity = new Intent(activity, MainActivity.class);
activity.startActivity(mainActivity); activity.startActivity(mainActivity);
activity.finish(); activity.finish();
headerMainBinding.ownerAccounts.setIconResource(R.drawable.ic_accounts); headerMainBinding.ownerAccounts.setImageResource(R.drawable.ic_baseline_arrow_drop_down_24);
return true; return true;
} }
return false; return false;
@ -623,23 +593,30 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
}).start(); }).start();
} else { } else {
navigationView.getMenu().clear(); navigationView.getMenu().clear();
if (Helper.getCurrentAccount(activity).mastodon_account != null) { if (MainActivity.currentAccount.mastodon_account != null) {
navigationView.inflateMenu(R.menu.activity_main_drawer); navigationView.inflateMenu(R.menu.activity_main_drawer);
} else if (Helper.getCurrentAccount(activity).peertube_account != null) { } else if (MainActivity.currentAccount.peertube_account != null) {
navigationView.inflateMenu(R.menu.activity_main_drawer_peertube); navigationView.inflateMenu(R.menu.activity_main_drawer_peertube);
} }
headerMainBinding.ownerAccounts.setIconResource(R.drawable.ic_accounts); headerMainBinding.ownerAccounts.setImageResource(R.drawable.ic_baseline_arrow_drop_down_24);
headerMenuOpen = false; headerMenuOpen = false;
} }
} }
public static void headerLogoutClick(Activity activity, NavHeaderMainBinding headerMainBinding, FragmentManager fragmentManager) { public static void headerOptionInfoClick(Activity activity, NavHeaderMainBinding headerMainBinding, FragmentManager fragmentManager) {
PopupMenu popup = new PopupMenu(activity, headerMainBinding.headerOptionInfo);
popup.getMenuInflater()
.inflate(R.menu.main, popup.getMenu());
popup.setOnMenuItemClickListener(item -> {
int itemId = item.getItemId();
if (itemId == R.id.action_logout_account) {
AlertDialog.Builder alt_bld = new MaterialAlertDialogBuilder(activity); AlertDialog.Builder alt_bld = new MaterialAlertDialogBuilder(activity);
alt_bld.setTitle(R.string.action_logout); alt_bld.setTitle(R.string.action_logout);
if (Helper.getCurrentAccount(activity).mastodon_account != null && Helper.getCurrentAccount(activity).instance != null) { if (currentAccount.mastodon_account != null && currentAccount.instance != null) {
alt_bld.setMessage(activity.getString(R.string.logout_account_confirmation, Helper.getCurrentAccount(activity).mastodon_account.username, Helper.getCurrentAccount(activity).instance)); alt_bld.setMessage(activity.getString(R.string.logout_account_confirmation, currentAccount.mastodon_account.username, currentAccount.instance));
} else if (Helper.getCurrentAccount(activity).peertube_account != null && Helper.getCurrentAccount(activity).instance != null) { } else if (currentAccount.peertube_account != null && currentAccount.instance != null) {
alt_bld.setMessage(activity.getString(R.string.logout_account_confirmation, Helper.getCurrentAccount(activity).peertube_account.getUsername(), Helper.getCurrentAccount(activity).instance)); alt_bld.setMessage(activity.getString(R.string.logout_account_confirmation, currentAccount.peertube_account.getUsername(), currentAccount.instance));
} else { } else {
alt_bld.setMessage(activity.getString(R.string.logout_account_confirmation, "", "")); alt_bld.setMessage(activity.getString(R.string.logout_account_confirmation, "", ""));
} }
@ -654,7 +631,17 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
alt_bld.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss()); alt_bld.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss());
AlertDialog alert = alt_bld.create(); AlertDialog alert = alt_bld.create();
alert.show(); alert.show();
return true;
} else if (itemId == R.id.action_proxy) {
(new ProxyActivity()).show(fragmentManager, null);
return true;
} }
return true;
});
popup.show();
}
protected abstract void rateThisApp();
/** /**
* Open notifications tab when coming from a notification device * Open notifications tab when coming from a notification device
@ -662,37 +649,6 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
* @param intent - Intent intent that will be cancelled * @param intent - Intent intent that will be cancelled
*/ */
private static void openNotifications(Activity activity, Intent intent) { private static void openNotifications(Activity activity, Intent intent) {
if (intent != null && intent.getExtras() != null) {
Bundle args = intent.getExtras();
long bundleId = args.getLong(Helper.ARG_INTENT_ID, -1);
new CachedBundle(activity).getBundle(bundleId, Helper.getCurrentAccount(activity), bundle -> {
app.fedilab.android.mastodon.client.entities.api.Account account = (app.fedilab.android.mastodon.client.entities.api.Account) bundle.getSerializable(Helper.INTENT_TARGETED_ACCOUNT);
Status status = (Status) bundle.getSerializable(Helper.INTENT_TARGETED_STATUS);
if (account != null) {
Intent intentAccount = new Intent(activity, ProfileActivity.class);
Bundle args2 = new Bundle();
args2.putSerializable(Helper.ARG_ACCOUNT, account);
new CachedBundle(activity).insertBundle(args2, Helper.getCurrentAccount(activity), bundleId2 -> {
Bundle bundleCached = new Bundle();
bundleCached.putLong(Helper.ARG_INTENT_ID, bundleId2);
intentAccount.putExtras(bundleCached);
intentAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
activity.startActivity(intentAccount);
});
} else if (status != null) {
Intent intentContext = new Intent(activity, ContextActivity.class);
Bundle args2 = new Bundle();
args2.putSerializable(Helper.ARG_STATUS, status);
new CachedBundle(activity).insertBundle(args2, Helper.getCurrentAccount(activity), bundleId2 -> {
Bundle bundleCached = new Bundle();
bundleCached.putLong(Helper.ARG_INTENT_ID, bundleId2);
intentContext.putExtras(bundleCached);
intentContext.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
activity.startActivity(intentContext);
});
}
});
}
final Handler handler = new Handler(); final Handler handler = new Handler();
handler.postDelayed(() -> { handler.postDelayed(() -> {
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(activity); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(activity);
@ -715,23 +671,11 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
} }
viewPager.setCurrentItem(position); viewPager.setCurrentItem(position);
} }
Bundle args = new Bundle();
args.putBoolean(ARG_REFRESH_NOTFICATION, true);
Intent intentBC = new Intent(Helper.RECEIVE_STATUS_ACTION);
intentBC.setPackage(BuildConfig.APPLICATION_ID);
new CachedBundle(activity).insertBundle(args, Helper.getCurrentAccount(activity), bundleId -> {
Bundle bundle = new Bundle();
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intentBC.putExtras(bundle);
activity.sendBroadcast(intentBC);
});
} }
}, 1000); }, 1000);
intent.removeExtra(Helper.INTENT_ACTION); intent.removeExtra(Helper.INTENT_ACTION);
} }
@SuppressLint("ApplySharedPref") @SuppressLint("ApplySharedPref")
public static void mamageNewIntent(Activity activity, Intent intent) { public static void mamageNewIntent(Activity activity, Intent intent) {
if (intent == null) if (intent == null)
@ -775,7 +719,6 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
Toasty.info(activity, activity.getString(R.string.toast_account_changed, acct), Toasty.LENGTH_LONG).show(); Toasty.info(activity, activity.getString(R.string.toast_account_changed, acct), Toasty.LENGTH_LONG).show();
BaseMainActivity.currentToken = account.token; BaseMainActivity.currentToken = account.token;
BaseMainActivity.currentUserID = account.user_id; BaseMainActivity.currentUserID = account.user_id;
ThemeHelper.applyThemeColor(activity);
api = account.api; api = account.api;
SharedPreferences.Editor editor = sharedpreferences.edit(); SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putString(PREF_USER_TOKEN, account.token); editor.putString(PREF_USER_TOKEN, account.token);
@ -783,10 +726,6 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
editor.commit(); editor.commit();
Intent mainActivity = new Intent(activity, MainActivity.class); Intent mainActivity = new Intent(activity, MainActivity.class);
mainActivity.putExtra(Helper.INTENT_ACTION, Helper.OPEN_NOTIFICATION); mainActivity.putExtra(Helper.INTENT_ACTION, Helper.OPEN_NOTIFICATION);
Bundle bundle = intent.getExtras();
if (bundle != null) {
mainActivity.putExtras(bundle);
}
activity.startActivity(mainActivity); activity.startActivity(mainActivity);
activity.finish(); activity.finish();
} catch (DBException e) { } catch (DBException e) {
@ -796,20 +735,14 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
} else if (extras.getInt(Helper.INTENT_ACTION) == Helper.OPEN_NOTIFICATION) { } else if (extras.getInt(Helper.INTENT_ACTION) == Helper.OPEN_NOTIFICATION) {
openNotifications(activity, intent); openNotifications(activity, intent);
} else if (extras.getInt(Helper.INTENT_ACTION) == Helper.OPEN_WITH_ANOTHER_ACCOUNT) { } else if (extras.getInt(Helper.INTENT_ACTION) == Helper.OPEN_WITH_ANOTHER_ACCOUNT) {
CrossActionHelper.fetchRemoteStatus(activity, Helper.getCurrentAccount(activity), urlOfMessage, new CrossActionHelper.Callback() { CrossActionHelper.fetchRemoteStatus(activity, MainActivity.currentAccount, urlOfMessage, new CrossActionHelper.Callback() {
@Override @Override
public void federatedStatus(Status status) { public void federatedStatus(Status status) {
if (status != null) { if (status != null) {
Intent intent = new Intent(activity, ContextActivity.class); Intent intent = new Intent(activity, ContextActivity.class);
Bundle args = new Bundle(); intent.putExtra(Helper.ARG_STATUS, status);
args.putSerializable(Helper.ARG_STATUS, status);
new CachedBundle(activity).insertBundle(args, Helper.getCurrentAccount(activity), bundleId -> {
Bundle bundle = new Bundle();
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intent.putExtras(bundle);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
activity.startActivity(intent); activity.startActivity(intent);
});
} }
} }
@ -1025,36 +958,30 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
} }
//Here we know that the intent contains a valid URL //Here we know that the intent contains a valid URL
if (!url.contains("medium.com")) { if (!url.contains("medium.com")) {
Pattern link = Pattern.compile("https?://([\\da-z.-]+[à-ü]?\\.[a-z.]{2,10})/(@[@\\w._-]*[0-9]*)(/[0-9]+)?$"); Pattern link = Pattern.compile("https?://([\\da-z.-]+[à-ü]?\\.[a-z.]{2,10})/(@[\\w._-]*[0-9]*)(/[0-9]+)?$");
Matcher matcherLink; Matcher matcherLink;
matcherLink = link.matcher(url); matcherLink = link.matcher(url);
if (matcherLink.find()) { if (matcherLink.find()) {
if (Helper.getCurrentAccount(activity) == null) { if (currentAccount == null) {
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(activity); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(activity);
if (currentToken == null || currentToken.trim().isEmpty()) { if (currentToken == null || currentToken.trim().isEmpty()) {
currentToken = sharedpreferences.getString(Helper.PREF_USER_TOKEN, null); currentToken = sharedpreferences.getString(Helper.PREF_USER_TOKEN, null);
} }
try { try {
Helper.setCurrentAccount(new Account(activity).getConnectedAccount()); currentAccount = new Account(activity).getConnectedAccount();
} catch (DBException e) { } catch (DBException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
if (matcherLink.group(3) != null && Objects.requireNonNull(matcherLink.group(3)).length() > 0) { //It's a toot if (matcherLink.group(3) != null && Objects.requireNonNull(matcherLink.group(3)).length() > 0) { //It's a toot
CrossActionHelper.fetchRemoteStatus(activity, Helper.getCurrentAccount(activity), url, new CrossActionHelper.Callback() { CrossActionHelper.fetchRemoteStatus(activity, currentAccount, url, new CrossActionHelper.Callback() {
@Override @Override
public void federatedStatus(Status status) { public void federatedStatus(Status status) {
if (status != null) { if (status != null) {
Intent intent = new Intent(activity, ContextActivity.class); Intent intent = new Intent(activity, ContextActivity.class);
Bundle args = new Bundle(); intent.putExtra(Helper.ARG_STATUS, status);
args.putSerializable(Helper.ARG_STATUS, status);
new CachedBundle(activity).insertBundle(args, Helper.getCurrentAccount(activity), bundleId -> {
Bundle bundle = new Bundle();
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intent.putExtras(bundle);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
activity.startActivity(intent); activity.startActivity(intent);
});
} else { } else {
Toasty.error(activity, activity.getString(R.string.toast_error), Toasty.LENGTH_SHORT).show(); Toasty.error(activity, activity.getString(R.string.toast_error), Toasty.LENGTH_SHORT).show();
} }
@ -1065,7 +992,7 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
} }
}); });
} else {//It's an account } else {//It's an account
CrossActionHelper.fetchRemoteAccount(activity, Helper.getCurrentAccount(activity), matcherLink.group(2) + "@" + matcherLink.group(1), new CrossActionHelper.Callback() { CrossActionHelper.fetchRemoteAccount(activity, currentAccount, matcherLink.group(2) + "@" + matcherLink.group(1), new CrossActionHelper.Callback() {
@Override @Override
public void federatedStatus(Status status) { public void federatedStatus(Status status) {
} }
@ -1074,15 +1001,11 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
public void federatedAccount(app.fedilab.android.mastodon.client.entities.api.Account account) { public void federatedAccount(app.fedilab.android.mastodon.client.entities.api.Account account) {
if (account != null) { if (account != null) {
Intent intent = new Intent(activity, ProfileActivity.class); Intent intent = new Intent(activity, ProfileActivity.class);
Bundle args = new Bundle(); Bundle b = new Bundle();
args.putSerializable(Helper.ARG_ACCOUNT, account); b.putSerializable(Helper.ARG_ACCOUNT, account);
new CachedBundle(activity).insertBundle(args, Helper.getCurrentAccount(activity), bundleId -> { intent.putExtras(b);
Bundle bundle = new Bundle();
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intent.putExtras(bundle);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
activity.startActivity(intent); activity.startActivity(intent);
});
} else { } else {
Toasty.error(activity, activity.getString(R.string.toast_error), Toasty.LENGTH_SHORT).show(); Toasty.error(activity, activity.getString(R.string.toast_error), Toasty.LENGTH_SHORT).show();
} }
@ -1102,19 +1025,16 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
intent.setFlags(0); intent.setFlags(0);
} }
protected abstract void rateThisApp();
@Override @Override
protected void onNewIntent(Intent intent) { protected void onNewIntent(Intent intent) {
super.onNewIntent(intent); super.onNewIntent(intent);
mamageNewIntent(BaseMainActivity.this, intent); mamageNewIntent(BaseMainActivity.this, intent);
} }
@SuppressLint("UnspecifiedRegisterReceiverFlag")
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
sharedpreferences = PreferenceManager.getDefaultSharedPreferences(BaseMainActivity.this); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(BaseMainActivity.this);
if (!Helper.isLoggedIn(BaseMainActivity.this)) { if (!Helper.isLoggedIn(BaseMainActivity.this)) {
//It is not, the user is redirected to the login page //It is not, the user is redirected to the login page
Intent myIntent = new Intent(BaseMainActivity.this, LoginActivity.class); Intent myIntent = new Intent(BaseMainActivity.this, LoginActivity.class);
@ -1122,7 +1042,7 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
finish(); finish();
return; return;
} else { } else {
BaseMainActivity.currentToken = sharedpreferences.getString(PREF_USER_TOKEN, null); BaseMainActivity.currentToken = sharedpreferences.getString(Helper.PREF_USER_TOKEN, null);
} }
String software = sharedpreferences.getString(PREF_USER_SOFTWARE, null); String software = sharedpreferences.getString(PREF_USER_SOFTWARE, null);
@ -1136,26 +1056,18 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS); permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS);
} }
NavHeaderMainBinding headerMainBinding = NavHeaderMainBinding.inflate(getLayoutInflater()); NavHeaderMainBinding headerMainBinding = NavHeaderMainBinding.inflate(getLayoutInflater());
currentAccount = null;
//Update account details //Update account details
new Thread(() -> { new Thread(() -> {
try { try {
if (currentToken == null) { if (currentToken == null) {
currentToken = sharedpreferences.getString(PREF_USER_TOKEN, null); currentToken = sharedpreferences.getString(PREF_USER_TOKEN, null);
} }
Helper.setCurrentAccount(new Account(BaseMainActivity.this).getConnectedAccount()); currentAccount = new Account(BaseMainActivity.this).getConnectedAccount();
} catch (DBException e) { } catch (DBException e) {
e.printStackTrace(); e.printStackTrace();
} }
//Apply the custom theme if (currentAccount != null && currentAccount.peertube_account != null) {
if (Helper.getCurrentAccount(BaseMainActivity.this) != null && currentInstance == null) {
currentInstance = Helper.getCurrentAccount(BaseMainActivity.this).instance;
currentUserID = Helper.getCurrentAccount(BaseMainActivity.this).user_id;
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = this::recreate;
mainHandler.post(myRunnable);
}
if (Helper.getCurrentAccount(BaseMainActivity.this) != null && Helper.getCurrentAccount(BaseMainActivity.this).peertube_account != null) {
//It is a peertube user //It is a peertube user
Intent intent = getIntent(); Intent intent = getIntent();
Intent myIntent = new Intent(this, PeertubeMainActivity.class); Intent myIntent = new Intent(this, PeertubeMainActivity.class);
@ -1176,10 +1088,15 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
return; return;
} }
//If the attached account is null, the app will fetch remote instance to get up-to-date values //If the attached account is null, the app will fetch remote instance to get up-to-date values
if (Helper.getCurrentAccount(BaseMainActivity.this) != null && Helper.getCurrentAccount(BaseMainActivity.this).mastodon_account == null) { if (currentAccount != null && currentAccount.mastodon_account == null) {
OkHttpClient okHttpClient = Helper.myOkHttpClient(getApplication().getApplicationContext()); OkHttpClient okHttpClient = new OkHttpClient.Builder()
.readTimeout(60, TimeUnit.SECONDS)
.connectTimeout(60, TimeUnit.SECONDS)
.callTimeout(60, TimeUnit.SECONDS)
.proxy(Helper.getProxy(getApplication().getApplicationContext()))
.build();
Retrofit retrofit = new Retrofit.Builder() Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://" + (MainActivity.currentInstance != null ? IDN.toASCII(MainActivity.currentInstance, IDN.ALLOW_UNASSIGNED) : null) + "/api/v1/") .baseUrl("https://" + MainActivity.currentInstance + "/api/v1/")
.addConverterFactory(GsonConverterFactory.create(Helper.getDateBuilder())) .addConverterFactory(GsonConverterFactory.create(Helper.getDateBuilder()))
.client(okHttpClient) .client(okHttpClient)
.build(); .build();
@ -1189,7 +1106,7 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
try { try {
retrofit2.Response<app.fedilab.android.mastodon.client.entities.api.Account> accountResponse = accountCall.execute(); retrofit2.Response<app.fedilab.android.mastodon.client.entities.api.Account> accountResponse = accountCall.execute();
if (accountResponse.isSuccessful()) { if (accountResponse.isSuccessful()) {
Helper.getCurrentAccount(BaseMainActivity.this).mastodon_account = accountResponse.body(); currentAccount.mastodon_account = accountResponse.body();
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@ -1198,9 +1115,9 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
} }
Handler mainHandler = new Handler(Looper.getMainLooper()); Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> { Runnable myRunnable = () -> {
if (Helper.getCurrentAccount(BaseMainActivity.this) == null || (Helper.getCurrentAccount(BaseMainActivity.this).mastodon_account == null && Helper.getCurrentAccount(BaseMainActivity.this).peertube_account == null)) { if (currentAccount == null || (currentAccount.mastodon_account == null && currentAccount.peertube_account == null)) {
//It is not, the user is redirected to the login page //It is not, the user is redirected to the login page
if (Helper.getCurrentAccount(BaseMainActivity.this) != null) { if (currentAccount != null) {
try { try {
Helper.removeAccount(BaseMainActivity.this); Helper.removeAccount(BaseMainActivity.this);
} catch (DBException e) { } catch (DBException e) {
@ -1216,11 +1133,11 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
} }
return; return;
} }
bottomMenu = new BottomMenu(BaseMainActivity.this).hydrate(Helper.getCurrentAccount(BaseMainActivity.this), binding.bottomNavView); bottomMenu = new BottomMenu(BaseMainActivity.this).hydrate(currentAccount, binding.bottomNavView);
if (Helper.getCurrentAccount(BaseMainActivity.this).mastodon_account.locked) { if (currentAccount.mastodon_account.locked) {
binding.navView.getMenu().findItem(R.id.nav_follow_requests).setVisible(true); binding.navView.getMenu().findItem(R.id.nav_follow_requests).setVisible(true);
} }
if (Helper.getCurrentAccount(BaseMainActivity.this).admin) { if (currentAccount.admin) {
binding.navView.getMenu().findItem(R.id.nav_administration).setVisible(true); binding.navView.getMenu().findItem(R.id.nav_administration).setVisible(true);
} }
if (bottomMenu != null) { if (bottomMenu != null) {
@ -1267,14 +1184,11 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
}); });
} }
currentInstance = Helper.getCurrentAccount(BaseMainActivity.this).instance; currentInstance = currentAccount.instance;
currentUserID = Helper.getCurrentAccount(BaseMainActivity.this).user_id; currentUserID = currentAccount.user_id;
show_boosts = sharedpreferences.getBoolean(getString(R.string.SET_SHOW_BOOSTS) + currentUserID + currentInstance, true); show_boosts = sharedpreferences.getBoolean(getString(R.string.SET_SHOW_BOOSTS) + currentUserID + currentInstance, true);
show_my_messages = sharedpreferences.getBoolean(getString(R.string.SET_SHOW_MY_MESSAGES) + currentUserID + currentInstance, true);
show_self_boosts = sharedpreferences.getBoolean(getString(R.string.SET_SHOW_SELF_BOOSTS) + currentUserID + currentInstance, true);
show_replies = sharedpreferences.getBoolean(getString(R.string.SET_SHOW_REPLIES) + currentUserID + currentInstance, true); show_replies = sharedpreferences.getBoolean(getString(R.string.SET_SHOW_REPLIES) + currentUserID + currentInstance, true);
show_self_replies = sharedpreferences.getBoolean(getString(R.string.SET_SHOW_SELF_REPLIES) + currentUserID + currentInstance, true);
show_dms = sharedpreferences.getBoolean(getString(R.string.SET_SHOW_DMS) + currentUserID + currentInstance, true); show_dms = sharedpreferences.getBoolean(getString(R.string.SET_SHOW_DMS) + currentUserID + currentInstance, true);
regex_home = sharedpreferences.getString(getString(R.string.SET_FILTER_REGEX_HOME) + currentUserID + currentInstance, null); regex_home = sharedpreferences.getString(getString(R.string.SET_FILTER_REGEX_HOME) + currentUserID + currentInstance, null);
regex_local = sharedpreferences.getString(getString(R.string.SET_FILTER_REGEX_LOCAL) + currentUserID + currentInstance, null); regex_local = sharedpreferences.getString(getString(R.string.SET_FILTER_REGEX_LOCAL) + currentUserID + currentInstance, null);
@ -1282,22 +1196,22 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
show_art_nsfw = sharedpreferences.getBoolean(getString(R.string.SET_ART_WITH_NSFW) + currentUserID + currentInstance, false); show_art_nsfw = sharedpreferences.getBoolean(getString(R.string.SET_ART_WITH_NSFW) + currentUserID + currentInstance, false);
binding.profilePicture.setOnClickListener(v -> binding.drawerLayout.openDrawer(GravityCompat.START)); binding.profilePicture.setOnClickListener(v -> binding.drawerLayout.openDrawer(GravityCompat.START));
Helper.loadPP(BaseMainActivity.this, binding.profilePicture, Helper.getCurrentAccount(BaseMainActivity.this)); Helper.loadPP(BaseMainActivity.this, binding.profilePicture, currentAccount);
headerMainBinding.accountAcc.setText(String.format("%s@%s", Helper.getCurrentAccount(BaseMainActivity.this).mastodon_account.username, Helper.getCurrentAccount(BaseMainActivity.this).instance)); headerMainBinding.accountAcc.setText(String.format("%s@%s", currentAccount.mastodon_account.username, currentAccount.instance));
if (Helper.getCurrentAccount(BaseMainActivity.this).mastodon_account.display_name == null || Helper.getCurrentAccount(BaseMainActivity.this).mastodon_account.display_name.isEmpty()) { if (currentAccount.mastodon_account.display_name == null || currentAccount.mastodon_account.display_name.isEmpty()) {
Helper.getCurrentAccount(BaseMainActivity.this).mastodon_account.display_name = Helper.getCurrentAccount(BaseMainActivity.this).mastodon_account.acct; currentAccount.mastodon_account.display_name = currentAccount.mastodon_account.acct;
} }
if (!isFinishing()) { if (!isFinishing()) {
headerMainBinding.accountName.setText( headerMainBinding.accountName.setText(
Helper.getCurrentAccount(BaseMainActivity.this).mastodon_account.getSpanDisplayNameEmoji(BaseMainActivity.this, currentAccount.mastodon_account.getSpanDisplayName(BaseMainActivity.this,
new WeakReference<>(headerMainBinding.accountName)), new WeakReference<>(headerMainBinding.accountName)),
TextView.BufferType.SPANNABLE); TextView.BufferType.SPANNABLE);
} }
float scale = sharedpreferences.getFloat(getString(R.string.SET_FONT_SCALE), 1.1f); float scale = sharedpreferences.getFloat(getString(R.string.SET_FONT_SCALE), 1.1f);
headerMainBinding.accountName.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18 * 1.1f / scale); headerMainBinding.accountName.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18 * 1.1f / scale);
headerMainBinding.accountAcc.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18 * 1.1f / scale); headerMainBinding.accountAcc.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18 * 1.1f / scale);
Helper.loadPP(BaseMainActivity.this, headerMainBinding.accountProfilePicture, Helper.getCurrentAccount(BaseMainActivity.this), false); Helper.loadPP(BaseMainActivity.this, headerMainBinding.accountProfilePicture, currentAccount, false);
MastodonHelper.loadProfileMediaMastodon(BaseMainActivity.this, headerMainBinding.backgroundImage, Helper.getCurrentAccount(BaseMainActivity.this).mastodon_account, MastodonHelper.MediaAccountType.HEADER); MastodonHelper.loadProfileMediaMastodon(BaseMainActivity.this, headerMainBinding.backgroundImage, currentAccount.mastodon_account, MastodonHelper.MediaAccountType.HEADER);
headerMainBinding.backgroundImage.setAlpha(0.5f); headerMainBinding.backgroundImage.setAlpha(0.5f);
/* /*
* Some general data are loaded when the app starts such; * Some general data are loaded when the app starts such;
@ -1338,13 +1252,13 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
new ViewModelProvider(BaseMainActivity.this).get(AccountsVM.class).getConnectedAccount(currentInstance, currentToken) new ViewModelProvider(BaseMainActivity.this).get(AccountsVM.class).getConnectedAccount(currentInstance, currentToken)
.observe(BaseMainActivity.this, mastodonAccount -> { .observe(BaseMainActivity.this, mastodonAccount -> {
//Initialize static var //Initialize static var
if (mastodonAccount != null && Helper.getCurrentAccount(BaseMainActivity.this) != null) { if (mastodonAccount != null && currentAccount != null) {
Helper.setCurrentAccountMastodonAccount(BaseMainActivity.this, mastodonAccount); currentAccount.mastodon_account = mastodonAccount;
displayReleaseNotesIfNeeded(BaseMainActivity.this, false); displayReleaseNotesIfNeeded(BaseMainActivity.this, false);
new Thread(() -> { new Thread(() -> {
try { try {
//Update account in db //Update account in db
new Account(BaseMainActivity.this).insertOrUpdate(Helper.getCurrentAccount(BaseMainActivity.this)); new Account(BaseMainActivity.this).insertOrUpdate(currentAccount);
} catch (DBException e) { } catch (DBException e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -1359,10 +1273,8 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
filteredAccounts = new ArrayList<>(); filteredAccounts = new ArrayList<>();
filterFetched = false; filterFetched = false;
filterFetchedRetry = 0;
networkStateReceiver = new NetworkStateReceiver(); networkStateReceiver = new NetworkStateReceiver();
networkStateReceiver.addListener(this); networkStateReceiver.addListener(this);
registerReceiver(networkStateReceiver, new IntentFilter(android.net.ConnectivityManager.CONNECTIVITY_ACTION));
binding = ActivityMainBinding.inflate(getLayoutInflater()); binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
@ -1371,7 +1283,6 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
if (actionBar != null) { if (actionBar != null) {
actionBar.setDisplayShowTitleEnabled(false); actionBar.setDisplayShowTitleEnabled(false);
} }
manageTopBarScrolling(binding.toolbar);
rateThisApp(); rateThisApp();
binding.compose.setOnClickListener(v -> startActivity(new Intent(this, ComposeActivity.class))); binding.compose.setOnClickListener(v -> startActivity(new Intent(this, ComposeActivity.class)));
@ -1452,38 +1363,32 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
bundle.putBoolean(Helper.ARG_PEERTUBE_NAV_REMOTE, true); bundle.putBoolean(Helper.ARG_PEERTUBE_NAV_REMOTE, true);
intent.putExtras(bundle); intent.putExtras(bundle);
startActivity(intent); startActivity(intent);
finish();
} else if (id == R.id.nav_about_instance) { } else if (id == R.id.nav_about_instance) {
(new InstanceActivity()).show(getSupportFragmentManager(), null); (new InstanceActivity()).show(getSupportFragmentManager(), null);
} else if (id == R.id.nav_instance_info) {
(new InstanceHealthActivity()).show(getSupportFragmentManager(), null);
} }
binding.drawerLayout.close(); binding.drawerLayout.close();
return false; return false;
}); });
headerMainBinding.instanceInfo.setOnClickListener(v -> (new InstanceHealthActivity()).show(getSupportFragmentManager(), null));
headerMainBinding.accountProfilePicture.setOnClickListener(v -> { headerMainBinding.accountProfilePicture.setOnClickListener(v -> {
Intent intent = new Intent(BaseMainActivity.this, ProfileActivity.class); Intent intent = new Intent(BaseMainActivity.this, ProfileActivity.class);
Bundle args = new Bundle(); Bundle b = new Bundle();
args.putSerializable(Helper.ARG_ACCOUNT, Helper.getCurrentAccount(BaseMainActivity.this).mastodon_account); b.putSerializable(Helper.ARG_ACCOUNT, currentAccount.mastodon_account);
new CachedBundle(BaseMainActivity.this).insertBundle(args, Helper.getCurrentAccount(BaseMainActivity.this), bundleId -> { intent.putExtras(b);
Bundle bundle = new Bundle(); ActivityOptionsCompat options = ActivityOptionsCompat
bundle.putLong(Helper.ARG_INTENT_ID, bundleId); .makeSceneTransitionAnimation(BaseMainActivity.this, headerMainBinding.instanceInfoContainer, getString(R.string.activity_porfile_pp));
intent.putExtras(bundle); startActivity(intent, options.toBundle());
startActivity(intent);
}); });
}); headerMainBinding.accountAcc.setOnClickListener(v -> headerMainBinding.changeAccount.callOnClick());
headerMainBinding.changeAccount.setOnClickListener(v -> {
TooltipCompat.setTooltipText(headerMainBinding.ownerAccounts, getString(R.string.manage_accounts));
headerMainBinding.accountName.setOnClickListener(v -> headerMainBinding.ownerAccounts.performClick());
headerMainBinding.accountAcc.setOnClickListener(v -> headerMainBinding.ownerAccounts.performClick());
headerMainBinding.ownerAccounts.setOnClickListener(v -> {
headerMenuOpen = !headerMenuOpen; headerMenuOpen = !headerMenuOpen;
manageDrawerMenu(BaseMainActivity.this, binding.navView, headerMainBinding); manageDrawerMenu(BaseMainActivity.this, binding.navView, headerMainBinding);
}); });
TooltipCompat.setTooltipText(headerMainBinding.headerLogout,getString(R.string.action_logout)); headerMainBinding.headerOptionInfo.setOnClickListener(v -> headerOptionInfoClick(BaseMainActivity.this, headerMainBinding, getSupportFragmentManager()));
headerMainBinding.headerLogout.setOnClickListener(v -> headerLogoutClick(BaseMainActivity.this, headerMainBinding, getSupportFragmentManager()));
//Toolbar search //Toolbar search
binding.toolbarSearch.setOnQueryTextListener(new SearchView.OnQueryTextListener() { binding.toolbarSearch.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@ -1519,14 +1424,14 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
int[] to = new int[]{R.id.account_pp, R.id.account_un}; int[] to = new int[]{R.id.account_pp, R.id.account_un};
String searchGroup = matcherMention.group(); String searchGroup = matcherMention.group();
AccountsVM accountsVM = new ViewModelProvider(BaseMainActivity.this).get(AccountsVM.class); AccountsVM accountsVM = new ViewModelProvider(BaseMainActivity.this).get(AccountsVM.class);
accountsVM.searchAccounts(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, searchGroup, 10, false, false) MatrixCursor cursor = new MatrixCursor(new String[]{BaseColumns._ID,
SearchManager.SUGGEST_COLUMN_ICON_1,
SearchManager.SUGGEST_COLUMN_TEXT_1});
accountsVM.searchAccounts(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, searchGroup, 5, false, false)
.observe(BaseMainActivity.this, accounts -> { .observe(BaseMainActivity.this, accounts -> {
if (accounts == null) { if (accounts == null) {
return; return;
} }
MatrixCursor cursor = new MatrixCursor(new String[]{BaseColumns._ID,
SearchManager.SUGGEST_COLUMN_ICON_1,
SearchManager.SUGGEST_COLUMN_TEXT_1});
AccountsSearchTopBarAdapter cursorAdapter = new AccountsSearchTopBarAdapter(BaseMainActivity.this, accounts, R.layout.drawer_account_search, null, from, to, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER); AccountsSearchTopBarAdapter cursorAdapter = new AccountsSearchTopBarAdapter(BaseMainActivity.this, accounts, R.layout.drawer_account_search, null, from, to, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
binding.toolbarSearch.setSuggestionsAdapter(cursorAdapter); binding.toolbarSearch.setSuggestionsAdapter(cursorAdapter);
new Thread(() -> { new Thread(() -> {
@ -1554,7 +1459,8 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
String[] from = new String[]{SearchManager.SUGGEST_COLUMN_TEXT_1}; String[] from = new String[]{SearchManager.SUGGEST_COLUMN_TEXT_1};
int[] to = new int[]{R.id.tag_name}; int[] to = new int[]{R.id.tag_name};
String searchGroup = matcherTag.group(); String searchGroup = matcherTag.group();
MatrixCursor cursor = new MatrixCursor(new String[]{BaseColumns._ID,
SearchManager.SUGGEST_COLUMN_TEXT_1});
searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, searchGroup, null, searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, searchGroup, null,
"hashtags", false, true, false, 0, "hashtags", false, true, false, 0,
null, null, 10).observe(BaseMainActivity.this, null, null, 10).observe(BaseMainActivity.this,
@ -1562,8 +1468,6 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
if (results == null || results.hashtags == null) { if (results == null || results.hashtags == null) {
return; return;
} }
MatrixCursor cursor = new MatrixCursor(new String[]{BaseColumns._ID,
SearchManager.SUGGEST_COLUMN_TEXT_1});
TagSearchTopBarAdapter cursorAdapter = new TagSearchTopBarAdapter(BaseMainActivity.this, results.hashtags, R.layout.drawer_tag_search, null, from, to, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER); TagSearchTopBarAdapter cursorAdapter = new TagSearchTopBarAdapter(BaseMainActivity.this, results.hashtags, R.layout.drawer_tag_search, null, from, to, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
binding.toolbarSearch.setSuggestionsAdapter(cursorAdapter); binding.toolbarSearch.setSuggestionsAdapter(cursorAdapter);
int i = 0; int i = 0;
@ -1585,8 +1489,10 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
binding.toolbarSearch.setOnSearchClickListener(v -> binding.tabLayout.setVisibility(View.VISIBLE)); binding.toolbarSearch.setOnSearchClickListener(v -> binding.tabLayout.setVisibility(View.VISIBLE));
//For receiving data from other activities //For receiving data from other activities
ContextCompat.registerReceiver(BaseMainActivity.this, broadcast_data, new IntentFilter(Helper.BROADCAST_DATA), ContextCompat.RECEIVER_NOT_EXPORTED); LocalBroadcastManager.getInstance(BaseMainActivity.this).registerReceiver(broadcast_data, new IntentFilter(Helper.BROADCAST_DATA));
ContextCompat.registerReceiver(BaseMainActivity.this, broadcast_error_message, new IntentFilter(Helper.INTENT_COMPOSE_ERROR_MESSAGE), ContextCompat.RECEIVER_NOT_EXPORTED); LocalBroadcastManager.getInstance(BaseMainActivity.this)
.registerReceiver(broadcast_error_message,
new IntentFilter(Helper.INTENT_COMPOSE_ERROR_MESSAGE));
if (emojis == null || !emojis.containsKey(BaseMainActivity.currentInstance) || emojis.get(BaseMainActivity.currentInstance) == null) { if (emojis == null || !emojis.containsKey(BaseMainActivity.currentInstance) || emojis.get(BaseMainActivity.currentInstance) == null) {
new Thread(() -> { new Thread(() -> {
try { try {
@ -1599,29 +1505,6 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
fetchRecentAccounts(BaseMainActivity.this, headerMainBinding); fetchRecentAccounts(BaseMainActivity.this, headerMainBinding);
} }
@Override
protected void onResume() {
super.onResume();
if (!sharedpreferences.getBoolean(getString(R.string.SET_AUTO_HIDE_COMPOSE), true) && !getFloatingVisibility())
manageFloatingButton(true);
}
private void manageTopBarScrolling(Toolbar toolbar) {
final boolean topBarScrolling = !sharedpreferences.getBoolean(getString(R.string.SET_DISABLE_TOPBAR_SCROLLING), false);
final AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
int scrollFlags = toolbarLayoutParams.getScrollFlags();
if (topBarScrolling) {
scrollFlags |= AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL;
} else {
scrollFlags &= ~AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL;
}
toolbarLayoutParams.setScrollFlags(scrollFlags);
}
private void manageFilters(int position) { private void manageFilters(int position) {
View view = binding.bottomNavView.findViewById(R.id.nav_home); View view = binding.bottomNavView.findViewById(R.id.nav_home);
boolean showExtendedFilter = true; boolean showExtendedFilter = true;
@ -1637,25 +1520,16 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
.inflate(R.menu.option_filter_toots, popup.getMenu()); .inflate(R.menu.option_filter_toots, popup.getMenu());
Menu menu = popup.getMenu(); Menu menu = popup.getMenu();
final MenuItem itemShowBoosts = menu.findItem(R.id.action_show_boosts); final MenuItem itemShowBoosts = menu.findItem(R.id.action_show_boosts);
final MenuItem itemShowMyMessages = menu.findItem(R.id.action_show_my_messages);
final MenuItem itemShowSelfBoosts = menu.findItem(R.id.action_show_self_boosts);
final MenuItem itemShowDMs = menu.findItem(R.id.action_show_dms); final MenuItem itemShowDMs = menu.findItem(R.id.action_show_dms);
final MenuItem itemShowReplies = menu.findItem(R.id.action_show_replies); final MenuItem itemShowReplies = menu.findItem(R.id.action_show_replies);
final MenuItem itemShowSelfReplies = menu.findItem(R.id.action_show_self_replies);
final MenuItem itemFilter = menu.findItem(R.id.action_filter); final MenuItem itemFilter = menu.findItem(R.id.action_filter);
if (!showExtendedFilter) { if (!showExtendedFilter) {
itemShowBoosts.setVisible(false); itemShowBoosts.setVisible(false);
itemShowReplies.setVisible(false); itemShowReplies.setVisible(false);
itemShowSelfBoosts.setVisible(false);
itemShowSelfReplies.setVisible(false);
itemShowMyMessages.setVisible(false);
itemShowDMs.setVisible(false); itemShowDMs.setVisible(false);
} else { } else {
itemShowBoosts.setVisible(true); itemShowBoosts.setVisible(true);
itemShowReplies.setVisible(true); itemShowReplies.setVisible(true);
itemShowSelfBoosts.setVisible(true);
itemShowSelfReplies.setVisible(true);
itemShowMyMessages.setVisible(true);
itemShowDMs.setVisible(true); itemShowDMs.setVisible(true);
} }
@ -1670,10 +1544,7 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
} }
itemShowBoosts.setChecked(show_boosts); itemShowBoosts.setChecked(show_boosts);
itemShowMyMessages.setChecked(show_my_messages);
itemShowReplies.setChecked(show_replies); itemShowReplies.setChecked(show_replies);
itemShowSelfBoosts.setChecked(show_self_boosts);
itemShowSelfReplies.setChecked(show_self_replies);
itemShowDMs.setChecked(show_dms); itemShowDMs.setChecked(show_dms);
if (show_filtered != null && show_filtered.length() > 0) { if (show_filtered != null && show_filtered.length() > 0) {
itemFilter.setTitle(show_filtered); itemFilter.setTitle(show_filtered);
@ -1682,7 +1553,8 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
if (binding.viewPager.getAdapter() != null) { if (binding.viewPager.getAdapter() != null) {
int tabPosition = binding.tabLayout.getSelectedTabPosition(); int tabPosition = binding.tabLayout.getSelectedTabPosition();
Fragment fragment = (Fragment) binding.viewPager.getAdapter().instantiateItem(binding.viewPager, Math.max(tabPosition, 0)); Fragment fragment = (Fragment) binding.viewPager.getAdapter().instantiateItem(binding.viewPager, Math.max(tabPosition, 0));
if (fragment instanceof FragmentMastodonTimeline fragmentMastodonTimeline && fragment.isVisible()) { if (fragment instanceof FragmentMastodonTimeline && fragment.isVisible()) {
FragmentMastodonTimeline fragmentMastodonTimeline = ((FragmentMastodonTimeline) fragment);
fragmentMastodonTimeline.refreshAllAdapters(); fragmentMastodonTimeline.refreshAllAdapters();
} }
} }
@ -1709,26 +1581,11 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
editor.putBoolean(getString(R.string.SET_SHOW_BOOSTS) + currentUserID + currentInstance, show_boosts); editor.putBoolean(getString(R.string.SET_SHOW_BOOSTS) + currentUserID + currentInstance, show_boosts);
itemShowBoosts.setChecked(show_boosts); itemShowBoosts.setChecked(show_boosts);
editor.apply(); editor.apply();
} else if (itemId == R.id.action_show_my_messages) {
show_my_messages = !show_my_messages;
editor.putBoolean(getString(R.string.SET_SHOW_MY_MESSAGES) + currentUserID + currentInstance, show_my_messages);
itemShowMyMessages.setChecked(show_my_messages);
editor.apply();
} else if (itemId == R.id.action_show_self_boosts) {
show_self_boosts = !show_self_boosts;
editor.putBoolean(getString(R.string.SET_SHOW_SELF_BOOSTS) + currentUserID + currentInstance, show_self_boosts);
itemShowSelfBoosts.setChecked(show_self_boosts);
editor.apply();
} else if (itemId == R.id.action_show_replies) { } else if (itemId == R.id.action_show_replies) {
show_replies = !show_replies; show_replies = !show_replies;
editor.putBoolean(getString(R.string.SET_SHOW_REPLIES) + currentUserID + currentInstance, show_replies); editor.putBoolean(getString(R.string.SET_SHOW_REPLIES) + currentUserID + currentInstance, show_replies);
itemShowReplies.setChecked(show_replies); itemShowReplies.setChecked(show_replies);
editor.apply(); editor.apply();
} else if (itemId == R.id.action_show_self_replies) {
show_self_replies = !show_self_replies;
editor.putBoolean(getString(R.string.SET_SHOW_SELF_REPLIES) + currentUserID + currentInstance, show_self_replies);
itemShowSelfReplies.setChecked(show_self_replies);
editor.apply();
} else if (itemId == R.id.action_show_dms) { } else if (itemId == R.id.action_show_dms) {
show_dms = !show_dms; show_dms = !show_dms;
editor.putBoolean(getString(R.string.SET_SHOW_DMS) + currentUserID + currentInstance, show_dms); editor.putBoolean(getString(R.string.SET_SHOW_DMS) + currentUserID + currentInstance, show_dms);
@ -1936,18 +1793,15 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
@Override @Override
protected void onDestroy() { protected void onDestroy() {
LocalBroadcastManager.getInstance(BaseMainActivity.this).unregisterReceiver(broadcast_data);
try { LocalBroadcastManager.getInstance(BaseMainActivity.this)
unregisterReceiver(broadcast_data); .unregisterReceiver(broadcast_error_message);
unregisterReceiver(broadcast_error_message);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
if (networkStateReceiver != null) { if (networkStateReceiver != null) {
try { try {
networkStateReceiver.removeListener(this);
unregisterReceiver(networkStateReceiver); unregisterReceiver(networkStateReceiver);
} catch (Exception ignored) {} } catch (IllegalArgumentException illegalArgumentException) {
illegalArgumentException.printStackTrace();
}
} }
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(BaseMainActivity.this); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(BaseMainActivity.this);
boolean clearCacheExit = sharedpreferences.getBoolean(getString(R.string.SET_CLEAR_CACHE_EXIT), false); boolean clearCacheExit = sharedpreferences.getBoolean(getString(R.string.SET_CLEAR_CACHE_EXIT), false);
@ -1976,11 +1830,14 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
int position = binding.tabLayout.getSelectedTabPosition(); int position = binding.tabLayout.getSelectedTabPosition();
if (binding.viewPager.getAdapter() != null) { if (binding.viewPager.getAdapter() != null) {
Fragment fragment = (Fragment) binding.viewPager.getAdapter().instantiateItem(binding.viewPager, Math.max(position, 0)); Fragment fragment = (Fragment) binding.viewPager.getAdapter().instantiateItem(binding.viewPager, Math.max(position, 0));
if (fragment instanceof FragmentMastodonTimeline fragmentMastodonTimeline) { if (fragment instanceof FragmentMastodonTimeline) {
FragmentMastodonTimeline fragmentMastodonTimeline = ((FragmentMastodonTimeline) fragment);
fragmentMastodonTimeline.scrollToTop(); fragmentMastodonTimeline.scrollToTop();
} else if (fragment instanceof FragmentMastodonConversation fragmentMastodonConversation) { } else if (fragment instanceof FragmentMastodonConversation) {
FragmentMastodonConversation fragmentMastodonConversation = ((FragmentMastodonConversation) fragment);
fragmentMastodonConversation.scrollToTop(); fragmentMastodonConversation.scrollToTop();
} else if (fragment instanceof FragmentNotificationContainer fragmentNotificationContainer) { } else if (fragment instanceof FragmentNotificationContainer) {
FragmentNotificationContainer fragmentNotificationContainer = ((FragmentNotificationContainer) fragment);
fragmentNotificationContainer.scrollToTop(); fragmentNotificationContainer.scrollToTop();
} }
} }
@ -2029,14 +1886,12 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
} }
public void manageFloatingButton(boolean display) { public void manageFloatingButton(boolean display) {
if (sharedpreferences.getBoolean(getString(R.string.SET_AUTO_HIDE_COMPOSE), true)) {
if (display) { if (display) {
binding.compose.show(); binding.compose.show();
} else { } else {
binding.compose.hide(); binding.compose.hide();
} }
} }
}
@Override @Override
public boolean onSupportNavigateUp() { public boolean onSupportNavigateUp() {
@ -2090,11 +1945,6 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
public enum iconLauncher { public enum iconLauncher {
BUBBLES, BUBBLES,
BUBBLESUA,
BUBBLESPEAGREEN,
BUBBLESPRIDE,
BUBBLESPINK,
BUBBLESPIRATE,
FEDIVERSE, FEDIVERSE,
HERO, HERO,
ATOM, ATOM,

View file

@ -42,9 +42,7 @@ import java.util.Objects;
import app.fedilab.android.mastodon.helper.ThemeHelper; import app.fedilab.android.mastodon.helper.ThemeHelper;
import app.fedilab.android.peertube.services.GlobalUploadObserver; import app.fedilab.android.peertube.services.GlobalUploadObserver;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
import io.noties.prism4j.annotations.PrismBundle;
@PrismBundle(includeAll = true, grammarLocatorClassName = ".MySuperGrammerLocator")
public class MainApplication extends MultiDexApplication { public class MainApplication extends MultiDexApplication {

View file

@ -15,17 +15,14 @@ package app.fedilab.android.activities;
* see <http://www.gnu.org/licenses>. */ * 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.Intent;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Bundle; import android.os.Bundle;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.Toast;
import androidx.core.app.ActivityOptionsCompat;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import java.util.ArrayList; import java.util.ArrayList;
@ -39,12 +36,10 @@ import app.fedilab.android.mastodon.activities.BaseBarActivity;
import app.fedilab.android.mastodon.activities.ProfileActivity; import app.fedilab.android.mastodon.activities.ProfileActivity;
import app.fedilab.android.mastodon.client.entities.api.Account; import app.fedilab.android.mastodon.client.entities.api.Account;
import app.fedilab.android.mastodon.client.entities.api.Status; import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.mastodon.client.entities.app.CachedBundle;
import app.fedilab.android.mastodon.helper.CrossActionHelper; import app.fedilab.android.mastodon.helper.CrossActionHelper;
import app.fedilab.android.mastodon.helper.Helper; import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.helper.MastodonHelper; import app.fedilab.android.mastodon.helper.MastodonHelper;
import app.fedilab.android.mastodon.viewmodel.mastodon.AccountsVM; import app.fedilab.android.mastodon.viewmodel.mastodon.AccountsVM;
import es.dmoral.toasty.Toasty;
public class AboutActivity extends BaseBarActivity { public class AboutActivity extends BaseBarActivity {
@ -63,10 +58,10 @@ public class AboutActivity extends BaseBarActivity {
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
} }
String version = "";
try { try {
PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0); PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
version = pInfo.versionName; String version = pInfo.versionName;
binding.aboutVersion.setText(getResources().getString(R.string.about_vesrion, version)); binding.aboutVersion.setText(getResources().getString(R.string.about_vesrion, version));
} catch (PackageManager.NameNotFoundException ignored) { } catch (PackageManager.NameNotFoundException ignored) {
} }
@ -82,20 +77,6 @@ public class AboutActivity extends BaseBarActivity {
} }
binding.aboutSupportPaypal.setOnClickListener(v -> Helper.openBrowser(AboutActivity.this, "https://www.paypal.me/Mastalab")); 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) { if (BuildConfig.DONATIONS) {
binding.aboutSupportPaypal.setVisibility(View.VISIBLE); binding.aboutSupportPaypal.setVisibility(View.VISIBLE);
} else { } else {
@ -119,14 +100,12 @@ public class AboutActivity extends BaseBarActivity {
binding.accountUn.setText(account.acct); binding.accountUn.setText(account.acct);
binding.accountPp.setOnClickListener(v -> { binding.accountPp.setOnClickListener(v -> {
Intent intent = new Intent(AboutActivity.this, ProfileActivity.class); Intent intent = new Intent(AboutActivity.this, ProfileActivity.class);
Bundle args = new Bundle(); Bundle b = new Bundle();
args.putSerializable(Helper.ARG_ACCOUNT, account); b.putSerializable(Helper.ARG_ACCOUNT, account);
new CachedBundle(AboutActivity.this).insertBundle(args, Helper.getCurrentAccount(AboutActivity.this), bundleId -> { intent.putExtras(b);
Bundle bundle = new Bundle(); ActivityOptionsCompat options = ActivityOptionsCompat
bundle.putLong(Helper.ARG_INTENT_ID, bundleId); .makeSceneTransitionAnimation(AboutActivity.this, binding.accountPp, getString(R.string.activity_porfile_pp));
intent.putExtras(bundle); startActivity(intent, options.toBundle());
startActivity(intent);
});
}); });
AccountsVM accountsVM = new ViewModelProvider(AboutActivity.this).get(AccountsVM.class); AccountsVM accountsVM = new ViewModelProvider(AboutActivity.this).get(AccountsVM.class);
List<String> ids = new ArrayList<>(); List<String> ids = new ArrayList<>();

View file

@ -150,6 +150,7 @@ public class LoginActivity extends BaseActivity {
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(LoginActivity.this);
setContentView(new FrameLayout(this)); setContentView(new FrameLayout(this));
FragmentLoginMain fragmentLoginMain = new FragmentLoginMain(); FragmentLoginMain fragmentLoginMain = new FragmentLoginMain();
Helper.addFragment(getSupportFragmentManager(), android.R.id.content, fragmentLoginMain, null, null, null); Helper.addFragment(getSupportFragmentManager(), android.R.id.content, fragmentLoginMain, null, null, null);

View file

@ -1,102 +0,0 @@
package app.fedilab.android.activities;
/* Copyright 2025 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* 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.Intent;
import android.net.Uri;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import app.fedilab.android.mastodon.activities.ProfileActivity;
import app.fedilab.android.mastodon.client.entities.app.CachedBundle;
import app.fedilab.android.mastodon.helper.Helper;
public class WebActivityPub extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent appIntent = getIntent();
String acct = null;
String intent = null;
if(appIntent == null) {
finish();
return;
}
Uri uri = appIntent.getData();
if(uri == null) {
finish();
return;
}
String scheme = uri.getScheme();
String uriString = uri.toString();
if(!uriString.startsWith(scheme+"://")) {
uriString = uri.toString().replace(scheme+":",scheme+"://");
uri = Uri.parse(uriString);
if(uri == null) {
finish();
return;
}
}
String host = uri.getHost();
String path = uri.getPath();
String query = uri.getQuery();
if(path == null || path.isEmpty()) {
finish();
return;
}
if(query != null) {
String intentPatternString = "intent=(\\w+)";
final Pattern intentPattern = Pattern.compile(intentPatternString, Pattern.CASE_INSENSITIVE);
Matcher matcherIntent = intentPattern.matcher(query);
while (matcherIntent.find()) {
intent = matcherIntent.group(1);
}
}
if(path.startsWith("/@")) {
String[] params = path.split("@");
if(params.length == 2) {
acct = params[1] + "@" + host;
}
} else if(path.split("/").length > 2) {
String[] params = path.split("/");
String root = params[1].toLowerCase();
if (root.equals("users")) {
acct = params[2] + "@" + host;
}
}
if(acct != null) {
Intent intentProfile = new Intent(WebActivityPub.this, ProfileActivity.class);
Bundle args = new Bundle();
args.putString(Helper.ARG_MENTION, acct);
new CachedBundle(WebActivityPub.this).insertBundle(args, Helper.getCurrentAccount(WebActivityPub.this), bundleId -> {
Bundle bundle = new Bundle();
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intentProfile.putExtras(bundle);
intentProfile.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intentProfile);
});
}
finish();
}
}

View file

@ -27,7 +27,6 @@ import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Objects;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.activities.MainActivity; import app.fedilab.android.activities.MainActivity;
@ -35,7 +34,6 @@ import app.fedilab.android.databinding.ActivityAdminReportBinding;
import app.fedilab.android.mastodon.client.entities.api.Status; import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.mastodon.client.entities.api.admin.AdminAccount; import app.fedilab.android.mastodon.client.entities.api.admin.AdminAccount;
import app.fedilab.android.mastodon.client.entities.api.admin.AdminReport; import app.fedilab.android.mastodon.client.entities.api.admin.AdminReport;
import app.fedilab.android.mastodon.client.entities.app.CachedBundle;
import app.fedilab.android.mastodon.helper.Helper; import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.helper.ThemeHelper; import app.fedilab.android.mastodon.helper.ThemeHelper;
import app.fedilab.android.mastodon.ui.drawer.StatusReportAdapter; import app.fedilab.android.mastodon.ui.drawer.StatusReportAdapter;
@ -49,7 +47,6 @@ public class AccountReportActivity extends BaseBarActivity {
private AdminReport report; private AdminReport report;
private ActivityAdminReportBinding binding; private ActivityAdminReportBinding binding;
private AdminVM adminVM; private AdminVM adminVM;
private AdminAccount targeted_account;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -63,22 +60,12 @@ public class AccountReportActivity extends BaseBarActivity {
} }
report = null; report = null;
targeted_account = null; AdminAccount targeted_account = null;
Bundle args = getIntent().getExtras(); Bundle b = getIntent().getExtras();
if (args != null) { if (b != null) {
long bundleId = args.getLong(Helper.ARG_INTENT_ID, -1); account_id = b.getString(Helper.ARG_ACCOUNT_ID, null);
new CachedBundle(AccountReportActivity.this).getBundle(bundleId, Helper.getCurrentAccount(AccountReportActivity.this), this::initializeAfterBundle); targeted_account = (AdminAccount) b.getSerializable(Helper.ARG_ACCOUNT);
} else { report = (AdminReport) b.getSerializable(Helper.ARG_REPORT);
initializeAfterBundle(null);
}
}
private void initializeAfterBundle(Bundle bundle) {
if (bundle != null) {
account_id = bundle.getString(Helper.ARG_ACCOUNT_ID, null);
targeted_account = (AdminAccount) bundle.getSerializable(Helper.ARG_ACCOUNT);
report = (AdminReport) bundle.getSerializable(Helper.ARG_REPORT);
} }
binding.allow.getBackground().setColorFilter(ThemeHelper.getAttColor(this, R.attr.colorPrimary), PorterDuff.Mode.MULTIPLY); binding.allow.getBackground().setColorFilter(ThemeHelper.getAttColor(this, R.attr.colorPrimary), PorterDuff.Mode.MULTIPLY);
@ -115,6 +102,7 @@ public class AccountReportActivity extends BaseBarActivity {
account_id = targeted_account.username; account_id = targeted_account.username;
} }
} }
private void fillReport(AdminAccount accountAdmin, actionType type) { private void fillReport(AdminAccount accountAdmin, actionType type) {
@ -146,12 +134,12 @@ public class AccountReportActivity extends BaseBarActivity {
binding.reject.setOnClickListener(view -> adminVM.reject(MainActivity.currentInstance, MainActivity.currentToken, account_id).observe(this, account -> fillReport(account, actionType.REJECT))); binding.reject.setOnClickListener(view -> adminVM.reject(MainActivity.currentInstance, MainActivity.currentToken, account_id).observe(this, account -> fillReport(account, actionType.REJECT)));
binding.allow.setOnClickListener(view -> adminVM.approve(MainActivity.currentInstance, MainActivity.currentToken, account_id).observe(this, account -> fillReport(account, actionType.APPROVE))); binding.allow.setOnClickListener(view -> adminVM.approve(MainActivity.currentInstance, MainActivity.currentToken, account_id).observe(this, account -> fillReport(account, actionType.APPROVE)));
binding.warn.setOnClickListener(view -> { binding.warn.setOnClickListener(view -> {
adminVM.performAction(MainActivity.currentInstance, MainActivity.currentToken, account_id, "none", null, null, Objects.requireNonNull(binding.comment.getText()).toString().trim(), binding.emailUser.isChecked()); adminVM.performAction(MainActivity.currentInstance, MainActivity.currentToken, account_id, "none", null, null, binding.comment.getText().toString().trim(), binding.emailUser.isChecked());
fillReport(accountAdmin, actionType.NONE); fillReport(accountAdmin, actionType.NONE);
}); });
binding.silence.setOnClickListener(view -> { binding.silence.setOnClickListener(view -> {
if (!accountAdmin.silenced) { if (!accountAdmin.silenced) {
adminVM.performAction(MainActivity.currentInstance, MainActivity.currentToken, account_id, "silence", null, null, Objects.requireNonNull(binding.comment.getText()).toString().trim(), binding.emailUser.isChecked()); adminVM.performAction(MainActivity.currentInstance, MainActivity.currentToken, account_id, "silence", null, null, binding.comment.getText().toString().trim(), binding.emailUser.isChecked());
accountAdmin.silenced = true; accountAdmin.silenced = true;
fillReport(accountAdmin, actionType.SILENCE); fillReport(accountAdmin, actionType.SILENCE);
} else { } else {
@ -160,7 +148,7 @@ public class AccountReportActivity extends BaseBarActivity {
}); });
binding.disable.setOnClickListener(view -> { binding.disable.setOnClickListener(view -> {
if (!accountAdmin.disabled) { if (!accountAdmin.disabled) {
adminVM.performAction(MainActivity.currentInstance, MainActivity.currentToken, account_id, "disable", null, null, Objects.requireNonNull(binding.comment.getText()).toString().trim(), binding.emailUser.isChecked()); adminVM.performAction(MainActivity.currentInstance, MainActivity.currentToken, account_id, "disable", null, null, binding.comment.getText().toString().trim(), binding.emailUser.isChecked());
accountAdmin.disabled = true; accountAdmin.disabled = true;
fillReport(accountAdmin, actionType.DISABLE); fillReport(accountAdmin, actionType.DISABLE);
} else { } else {
@ -170,7 +158,7 @@ public class AccountReportActivity extends BaseBarActivity {
binding.suspend.setOnClickListener(view -> { binding.suspend.setOnClickListener(view -> {
if (!accountAdmin.suspended) { if (!accountAdmin.suspended) {
adminVM.performAction(MainActivity.currentInstance, MainActivity.currentToken, account_id, "suspend", null, null, Objects.requireNonNull(binding.comment.getText()).toString().trim(), binding.emailUser.isChecked()); adminVM.performAction(MainActivity.currentInstance, MainActivity.currentToken, account_id, "suspend", null, null, binding.comment.getText().toString().trim(), binding.emailUser.isChecked());
accountAdmin.suspended = true; accountAdmin.suspended = true;
fillReport(accountAdmin, actionType.SUSPEND); fillReport(accountAdmin, actionType.SUSPEND);
} else { } else {
@ -182,21 +170,35 @@ public class AccountReportActivity extends BaseBarActivity {
if (type != null) { if (type != null) {
String message = null; String message = null;
switch (type) { switch (type) {
case SILENCE -> message = getString(R.string.account_silenced); case SILENCE:
case UNSILENCE -> message = getString(R.string.account_unsilenced); message = getString(R.string.account_silenced);
case DISABLE -> message = getString(R.string.account_disabled); break;
case ENABLE -> message = getString(R.string.account_undisabled); case UNSILENCE:
case SUSPEND -> message = getString(R.string.account_suspended); message = getString(R.string.account_unsilenced);
case UNSUSPEND -> message = getString(R.string.account_unsuspended); break;
case NONE -> message = getString(R.string.account_warned); case DISABLE:
case APPROVE -> { message = getString(R.string.account_disabled);
break;
case ENABLE:
message = getString(R.string.account_undisabled);
break;
case SUSPEND:
message = getString(R.string.account_suspended);
break;
case UNSUSPEND:
message = getString(R.string.account_unsuspended);
break;
case NONE:
message = getString(R.string.account_warned);
break;
case APPROVE:
binding.allowRejectGroup.setVisibility(View.GONE); binding.allowRejectGroup.setVisibility(View.GONE);
message = getString(R.string.account_approved); message = getString(R.string.account_approved);
} break;
case REJECT -> { case REJECT:
binding.allowRejectGroup.setVisibility(View.GONE); binding.allowRejectGroup.setVisibility(View.GONE);
message = getString(R.string.account_rejected); message = getString(R.string.account_rejected);
} break;
} }
if (message != null) { if (message != null) {
Toasty.success(AccountReportActivity.this, message, Toast.LENGTH_LONG).show(); Toasty.success(AccountReportActivity.this, message, Toast.LENGTH_LONG).show();

View file

@ -60,6 +60,8 @@ public class ActionActivity extends BaseBarActivity {
private void displayTimeline(Timeline.TimeLineEnum type) { private void displayTimeline(Timeline.TimeLineEnum type) {
canGoBack = true; canGoBack = true;
if (type == Timeline.TimeLineEnum.MUTED_TIMELINE || type == Timeline.TimeLineEnum.BLOCKED_TIMELINE || type == Timeline.TimeLineEnum.MUTED_TIMELINE_HOME) { if (type == Timeline.TimeLineEnum.MUTED_TIMELINE || type == Timeline.TimeLineEnum.BLOCKED_TIMELINE || type == Timeline.TimeLineEnum.MUTED_TIMELINE_HOME) {
ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> {
fragmentMastodonAccount = new FragmentMastodonAccount(); fragmentMastodonAccount = new FragmentMastodonAccount();
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, type); bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, type);
@ -70,21 +72,20 @@ public class ActionActivity extends BaseBarActivity {
fragmentManager.beginTransaction(); fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, fragmentMastodonAccount); fragmentTransaction.replace(R.id.fragment_container, fragmentMastodonAccount);
fragmentTransaction.commit(); fragmentTransaction.commit();
ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> {
}); });
} else if (type == Timeline.TimeLineEnum.BLOCKED_DOMAIN_TIMELINE) { } else if (type == Timeline.TimeLineEnum.BLOCKED_DOMAIN_TIMELINE) {
ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> {
fragmentMastodonDomainBlock = new FragmentMastodonDomainBlock(); fragmentMastodonDomainBlock = new FragmentMastodonDomainBlock();
FragmentManager fragmentManager = getSupportFragmentManager(); FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = FragmentTransaction fragmentTransaction =
fragmentManager.beginTransaction(); fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, fragmentMastodonDomainBlock); fragmentTransaction.replace(R.id.fragment_container, fragmentMastodonDomainBlock);
fragmentTransaction.commit(); fragmentTransaction.commit();
ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> {
}); });
} else { } else {
ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> {
fragmentMastodonTimeline = new FragmentMastodonTimeline(); fragmentMastodonTimeline = new FragmentMastodonTimeline();
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, type); bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, type);
@ -95,18 +96,28 @@ public class ActionActivity extends BaseBarActivity {
fragmentManager.beginTransaction(); fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, fragmentMastodonTimeline); fragmentTransaction.replace(R.id.fragment_container, fragmentMastodonTimeline);
fragmentTransaction.commit(); fragmentTransaction.commit();
ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> {
}); });
} }
switch (type) { switch (type) {
case MUTED_TIMELINE -> setTitle(R.string.muted_menu); case MUTED_TIMELINE:
case FAVOURITE_TIMELINE -> setTitle(R.string.favourite); setTitle(R.string.muted_menu);
case BLOCKED_TIMELINE -> setTitle(R.string.blocked_menu); break;
case BOOKMARK_TIMELINE -> setTitle(R.string.bookmarks); case FAVOURITE_TIMELINE:
case BLOCKED_DOMAIN_TIMELINE -> setTitle(R.string.blocked_domains); setTitle(R.string.favourite);
case MUTED_TIMELINE_HOME -> setTitle(R.string.muted_menu_home); break;
case BLOCKED_TIMELINE:
setTitle(R.string.blocked_menu);
break;
case BOOKMARK_TIMELINE:
setTitle(R.string.bookmarks);
break;
case BLOCKED_DOMAIN_TIMELINE:
setTitle(R.string.blocked_domains);
break;
case MUTED_TIMELINE_HOME:
setTitle(R.string.muted_menu_home);
break;
} }
} }

View file

@ -15,10 +15,6 @@ package app.fedilab.android.mastodon.activities;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.BaseMainActivity.currentInstance;
import static app.fedilab.android.BaseMainActivity.currentNightMode;
import static app.fedilab.android.BaseMainActivity.currentUserID;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
@ -26,16 +22,15 @@ import android.content.res.Configuration;
import android.graphics.Color; import android.graphics.Color;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.view.View;
import android.view.Window; import android.view.Window;
import android.view.WindowManager; import android.view.WindowManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate; import androidx.appcompat.app.AppCompatDelegate;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import com.google.android.material.color.DynamicColors;
import com.vanniktech.emoji.EmojiManager; import com.vanniktech.emoji.EmojiManager;
import com.vanniktech.emoji.one.EmojiOneProvider; import com.vanniktech.emoji.one.EmojiOneProvider;
@ -57,7 +52,6 @@ public class BaseActivity extends AppCompatActivity {
EmojiManager.install(new EmojiOneProvider()); EmojiManager.install(new EmojiOneProvider());
} }
@SuppressLint("RestrictedApi")
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
@ -75,94 +69,85 @@ public class BaseActivity extends AppCompatActivity {
} }
String currentTheme = sharedpreferences.getString(getString(R.string.SET_THEME_BASE), getString(R.string.SET_DEFAULT_THEME)); String currentTheme = sharedpreferences.getString(getString(R.string.SET_THEME_BASE), getString(R.string.SET_DEFAULT_THEME));
boolean customAccentEnabled = sharedpreferences.getBoolean(getString(R.string.SET_CUSTOM_ACCENT) + currentUserID + currentInstance, false);
//Default automatic switch //Default automatic switch
currentNightMode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
if(customAccentEnabled && currentNightMode == Configuration.UI_MODE_NIGHT_NO) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR|View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
}else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
}
if (currentTheme.equals(getString(R.string.SET_DEFAULT_THEME))) { if (currentTheme.equals(getString(R.string.SET_DEFAULT_THEME))) {
int currentNightMode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
switch (currentNightMode) { switch (currentNightMode) {
case Configuration.UI_MODE_NIGHT_NO -> { case Configuration.UI_MODE_NIGHT_NO:
String defaultLight = sharedpreferences.getString(getString(R.string.SET_THEME_DEFAULT_LIGHT), "LIGHT"); String defaultLight = sharedpreferences.getString(getString(R.string.SET_THEME_DEFAULT_LIGHT), "LIGHT");
switch (defaultLight) { switch (defaultLight) {
case "LIGHT" -> { case "LIGHT":
setTheme(R.style.AppTheme); setTheme(R.style.AppTheme);
currentThemeId = R.style.AppTheme; currentThemeId = R.style.AppTheme;
} break;
case "SOLARIZED_LIGHT" -> { case "SOLARIZED_LIGHT":
setTheme(R.style.SolarizedAppTheme); setTheme(R.style.SolarizedAppTheme);
currentThemeId = R.style.SolarizedAppTheme; currentThemeId = R.style.SolarizedAppTheme;
break;
} }
} break;
} case Configuration.UI_MODE_NIGHT_YES:
case Configuration.UI_MODE_NIGHT_YES -> {
String defaultDark = sharedpreferences.getString(getString(R.string.SET_THEME_DEFAULT_DARK), "DARK"); String defaultDark = sharedpreferences.getString(getString(R.string.SET_THEME_DEFAULT_DARK), "DARK");
switch (defaultDark) { switch (defaultDark) {
case "DARK" -> { case "DARK":
setTheme(R.style.AppTheme); setTheme(R.style.AppTheme);
currentThemeId = R.style.AppTheme; currentThemeId = R.style.AppTheme;
} break;
case "SOLARIZED_DARK" -> { case "SOLARIZED_DARK":
setTheme(R.style.SolarizedAppTheme); setTheme(R.style.SolarizedAppTheme);
currentThemeId = R.style.SolarizedAppTheme; currentThemeId = R.style.SolarizedAppTheme;
} break;
case "BLACK" -> { case "BLACK":
setTheme(R.style.BlackAppTheme); setTheme(R.style.BlackAppTheme);
currentThemeId = R.style.BlackAppTheme; currentThemeId = R.style.BlackAppTheme;
} break;
case "DRACULA" -> { case "DRACULA":
setTheme(R.style.DraculaAppTheme); setTheme(R.style.DraculaAppTheme);
currentThemeId = R.style.DraculaAppTheme; currentThemeId = R.style.DraculaAppTheme;
break;
} }
} break;
}
} }
} else { } else {
switch (currentTheme) { switch (currentTheme) {
case "LIGHT" -> { case "LIGHT":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
setTheme(R.style.AppTheme); setTheme(R.style.AppTheme);
currentThemeId = R.style.AppTheme; currentThemeId = R.style.AppTheme;
} break;
case "DARK" -> { case "DARK":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
setTheme(R.style.AppTheme); setTheme(R.style.AppTheme);
currentThemeId = R.style.AppTheme; currentThemeId = R.style.AppTheme;
} break;
case "SOLARIZED_LIGHT" -> { case "SOLARIZED_LIGHT":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
setTheme(R.style.SolarizedAppTheme); setTheme(R.style.SolarizedAppTheme);
currentThemeId = R.style.SolarizedAppTheme; currentThemeId = R.style.SolarizedAppTheme;
} break;
case "SOLARIZED_DARK" -> { case "SOLARIZED_DARK":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
setTheme(R.style.SolarizedAppTheme); setTheme(R.style.SolarizedAppTheme);
currentThemeId = R.style.SolarizedAppTheme; currentThemeId = R.style.SolarizedAppTheme;
} break;
case "BLACK" -> { case "BLACK":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
setTheme(R.style.BlackAppTheme); setTheme(R.style.BlackAppTheme);
currentThemeId = R.style.BlackAppTheme; currentThemeId = R.style.BlackAppTheme;
} break;
case "DRACULA" -> { case "DRACULA":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
setTheme(R.style.DraculaAppTheme); setTheme(R.style.DraculaAppTheme);
currentThemeId = R.style.DraculaAppTheme; currentThemeId = R.style.DraculaAppTheme;
} break;
} }
} }
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
boolean dynamicColor = sharedpreferences.getBoolean(getString(R.string.SET_DYNAMICCOLOR), false);
ThemeHelper.applyThemeColor(this); if (dynamicColor) {
DynamicColors.applyToActivityIfAvailable(this);
}
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N) { if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N) {
ThemeHelper.adjustFontScale(this, getResources().getConfiguration()); ThemeHelper.adjustFontScale(this, getResources().getConfiguration());
} }
@ -185,11 +170,4 @@ public class BaseActivity extends AppCompatActivity {
} }
super.attachBaseContext(newBase); super.attachBaseContext(newBase);
} }
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.clear();
}
} }

View file

@ -15,10 +15,6 @@ package app.fedilab.android.mastodon.activities;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.BaseMainActivity.currentInstance;
import static app.fedilab.android.BaseMainActivity.currentNightMode;
import static app.fedilab.android.BaseMainActivity.currentUserID;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
@ -26,16 +22,15 @@ import android.content.res.Configuration;
import android.graphics.Color; import android.graphics.Color;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.view.View;
import android.view.Window; import android.view.Window;
import android.view.WindowManager; import android.view.WindowManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate; import androidx.appcompat.app.AppCompatDelegate;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import com.google.android.material.color.DynamicColors;
import com.vanniktech.emoji.EmojiManager; import com.vanniktech.emoji.EmojiManager;
import com.vanniktech.emoji.one.EmojiOneProvider; import com.vanniktech.emoji.one.EmojiOneProvider;
@ -70,67 +65,74 @@ public class BaseBarActivity extends AppCompatActivity {
} }
} }
String currentTheme = sharedpreferences.getString(getString(R.string.SET_THEME_BASE), getString(R.string.SET_DEFAULT_THEME)); String currentTheme = sharedpreferences.getString(getString(R.string.SET_THEME_BASE), getString(R.string.SET_DEFAULT_THEME));
boolean customAccentEnabled = sharedpreferences.getBoolean(getString(R.string.SET_CUSTOM_ACCENT) + currentUserID + currentInstance, false);
//Default automatic switch //Default automatic switch
currentNightMode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
if(customAccentEnabled && currentNightMode == Configuration.UI_MODE_NIGHT_NO) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR|View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
}else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
}
if (currentTheme.equals(getString(R.string.SET_DEFAULT_THEME))) { if (currentTheme.equals(getString(R.string.SET_DEFAULT_THEME))) {
int currentNightMode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
switch (currentNightMode) { switch (currentNightMode) {
case Configuration.UI_MODE_NIGHT_NO -> { case Configuration.UI_MODE_NIGHT_NO:
String defaultLight = sharedpreferences.getString(getString(R.string.SET_THEME_DEFAULT_LIGHT), "LIGHT"); String defaultLight = sharedpreferences.getString(getString(R.string.SET_THEME_DEFAULT_LIGHT), "LIGHT");
switch (defaultLight) { switch (defaultLight) {
case "LIGHT" -> setTheme(R.style.AppThemeBar); case "LIGHT":
case "SOLARIZED_LIGHT" -> setTheme(R.style.SolarizedAppThemeBar); setTheme(R.style.AppThemeBar);
break;
case "SOLARIZED_LIGHT":
setTheme(R.style.SolarizedAppThemeBar);
break;
} }
} break;
case Configuration.UI_MODE_NIGHT_YES -> { case Configuration.UI_MODE_NIGHT_YES:
String defaultDark = sharedpreferences.getString(getString(R.string.SET_THEME_DEFAULT_DARK), "DARK"); String defaultDark = sharedpreferences.getString(getString(R.string.SET_THEME_DEFAULT_DARK), "DARK");
switch (defaultDark) { switch (defaultDark) {
case "DARK" -> setTheme(R.style.AppThemeBar); case "DARK":
case "SOLARIZED_DARK" -> setTheme(R.style.SolarizedAppThemeBar); setTheme(R.style.AppThemeBar);
case "BLACK" -> setTheme(R.style.BlackAppThemeBar); break;
case "DRACULA" -> setTheme(R.style.DraculaAppThemeBar); case "SOLARIZED_DARK":
} setTheme(R.style.SolarizedAppThemeBar);
break;
case "BLACK":
setTheme(R.style.BlackAppThemeBar);
break;
case "DRACULA":
setTheme(R.style.DraculaAppThemeBar);
break;
} }
break;
} }
} else { } else {
switch (currentTheme) { switch (currentTheme) {
case "LIGHT" -> { case "LIGHT":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
setTheme(R.style.AppThemeBar); setTheme(R.style.AppThemeBar);
} break;
case "DARK" -> { case "DARK":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
setTheme(R.style.AppThemeBar); setTheme(R.style.AppThemeBar);
} break;
case "SOLARIZED_LIGHT" -> { case "SOLARIZED_LIGHT":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
setTheme(R.style.SolarizedAppThemeBar); setTheme(R.style.SolarizedAppThemeBar);
} break;
case "SOLARIZED_DARK" -> { case "SOLARIZED_DARK":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
setTheme(R.style.SolarizedAppThemeBar); setTheme(R.style.SolarizedAppThemeBar);
} break;
case "BLACK" -> { case "BLACK":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
setTheme(R.style.BlackAppThemeBar); setTheme(R.style.BlackAppThemeBar);
} break;
case "DRACULA" -> { case "DRACULA":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
setTheme(R.style.DraculaAppThemeBar); setTheme(R.style.DraculaAppThemeBar);
} break;
} }
} }
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyThemeColor(this); boolean dynamicColor = sharedpreferences.getBoolean(getString(R.string.SET_DYNAMICCOLOR), false);
if (dynamicColor) {
DynamicColors.applyToActivityIfAvailable(this);
}
if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
Window window = getWindow(); Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
@ -156,11 +158,4 @@ public class BaseBarActivity extends AppCompatActivity {
super.attachBaseContext(newBase); super.attachBaseContext(newBase);
} }
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.clear();
}
} }

View file

@ -15,8 +15,6 @@ package app.fedilab.android.mastodon.activities;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.BaseMainActivity.currentNightMode;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
@ -32,6 +30,7 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate; import androidx.appcompat.app.AppCompatDelegate;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import com.google.android.material.color.DynamicColors;
import com.vanniktech.emoji.EmojiManager; import com.vanniktech.emoji.EmojiManager;
import com.vanniktech.emoji.one.EmojiOneProvider; import com.vanniktech.emoji.one.EmojiOneProvider;
@ -67,58 +66,73 @@ public class BaseTransparentActivity extends AppCompatActivity {
} }
String currentTheme = sharedpreferences.getString(getString(R.string.SET_THEME_BASE), getString(R.string.SET_DEFAULT_THEME)); String currentTheme = sharedpreferences.getString(getString(R.string.SET_THEME_BASE), getString(R.string.SET_DEFAULT_THEME));
//Default automatic switch //Default automatic switch
currentNightMode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
if (currentTheme.equals(getString(R.string.SET_DEFAULT_THEME))) { if (currentTheme.equals(getString(R.string.SET_DEFAULT_THEME))) {
int currentNightMode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
switch (currentNightMode) { switch (currentNightMode) {
case Configuration.UI_MODE_NIGHT_NO -> { case Configuration.UI_MODE_NIGHT_NO:
String defaultLight = sharedpreferences.getString(getString(R.string.SET_THEME_DEFAULT_LIGHT), "LIGHT"); String defaultLight = sharedpreferences.getString(getString(R.string.SET_THEME_DEFAULT_LIGHT), "LIGHT");
switch (defaultLight) { switch (defaultLight) {
case "LIGHT" -> setTheme(R.style.Transparent); case "LIGHT":
case "SOLARIZED_LIGHT" -> setTheme(R.style.TransparentSolarized); setTheme(R.style.Transparent);
break;
case "SOLARIZED_LIGHT":
setTheme(R.style.TransparentSolarized);
break;
} }
} break;
case Configuration.UI_MODE_NIGHT_YES -> { case Configuration.UI_MODE_NIGHT_YES:
String defaultDark = sharedpreferences.getString(getString(R.string.SET_THEME_DEFAULT_DARK), "DARK"); String defaultDark = sharedpreferences.getString(getString(R.string.SET_THEME_DEFAULT_DARK), "DARK");
switch (defaultDark) { switch (defaultDark) {
case "DARK" -> setTheme(R.style.Transparent); case "DARK":
case "SOLARIZED_DARK" -> setTheme(R.style.TransparentSolarized); setTheme(R.style.Transparent);
case "BLACK" -> setTheme(R.style.TransparentBlack); break;
case "DRACULA" -> setTheme(R.style.TransparentDracula); case "SOLARIZED_DARK":
} setTheme(R.style.TransparentSolarized);
break;
case "BLACK":
setTheme(R.style.TransparentBlack);
break;
case "DRACULA":
setTheme(R.style.TransparentDracula);
break;
} }
break;
} }
} else { } else {
switch (currentTheme) { switch (currentTheme) {
case "LIGHT" -> { case "LIGHT":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
setTheme(R.style.Transparent); setTheme(R.style.Transparent);
} break;
case "DARK" -> { case "DARK":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
setTheme(R.style.Transparent); setTheme(R.style.Transparent);
} break;
case "SOLARIZED_LIGHT" -> { case "SOLARIZED_LIGHT":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
setTheme(R.style.TransparentSolarized); setTheme(R.style.TransparentSolarized);
} break;
case "SOLARIZED_DARK" -> { case "SOLARIZED_DARK":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
setTheme(R.style.TransparentSolarized); setTheme(R.style.TransparentSolarized);
} break;
case "BLACK" -> { case "BLACK":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
setTheme(R.style.TransparentBlack); setTheme(R.style.TransparentBlack);
} break;
case "DRACULA" -> { case "DRACULA":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
setTheme(R.style.TransparentDracula); setTheme(R.style.TransparentDracula);
} break;
} }
} }
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyThemeColor(this); boolean dynamicColor = sharedpreferences.getBoolean(getString(R.string.SET_DYNAMICCOLOR), false);
if (dynamicColor) {
DynamicColors.applyToActivityIfAvailable(this);
}
if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
Window window = getWindow(); Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);

View file

@ -54,12 +54,12 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.databinding.ActivityCheckHomeCachetBinding; import app.fedilab.android.databinding.ActivityCheckHomeCachetBinding;
import app.fedilab.android.mastodon.client.entities.api.Status; import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.mastodon.client.entities.app.StatusCache; import app.fedilab.android.mastodon.client.entities.app.StatusCache;
import app.fedilab.android.mastodon.client.entities.app.TimelineCacheLogs; import app.fedilab.android.mastodon.client.entities.app.TimelineCacheLogs;
import app.fedilab.android.mastodon.exception.DBException; import app.fedilab.android.mastodon.exception.DBException;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.helper.ThemeHelper; import app.fedilab.android.mastodon.helper.ThemeHelper;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
@ -88,7 +88,7 @@ public class CheckHomeCacheActivity extends BaseBarActivity {
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
} }
if (Helper.getCurrentAccount(CheckHomeCacheActivity.this) == null || Helper.getCurrentAccount(CheckHomeCacheActivity.this).mastodon_account == null) { if (MainActivity.currentAccount == null || MainActivity.currentAccount.mastodon_account == null) {
finish(); finish();
return; return;
} }
@ -107,7 +107,7 @@ public class CheckHomeCacheActivity extends BaseBarActivity {
try { try {
if (myRange == range.ALL) { if (myRange == range.ALL) {
if (statuses == null) { if (statuses == null) {
statuses = new StatusCache(this).getHome(Helper.getCurrentAccount(CheckHomeCacheActivity.this)); statuses = new StatusCache(this).getHome(MainActivity.currentAccount);
sortAsc(statuses); sortAsc(statuses);
} }
} else if (myRange == range.DAY) { } else if (myRange == range.DAY) {
@ -202,7 +202,7 @@ public class CheckHomeCacheActivity extends BaseBarActivity {
IndexAxisValueFormatter formatter = new IndexAxisValueFormatter() { IndexAxisValueFormatter formatter = new IndexAxisValueFormatter() {
@Override @Override
public String getFormattedValue(float value) { public String getFormattedValue(float value) {
if (value >= 0 && value < xVals.size()) { if (value < xVals.size()) {
return xVals.get((int) value); return xVals.get((int) value);
} else } else
return ""; return "";
@ -226,7 +226,7 @@ public class CheckHomeCacheActivity extends BaseBarActivity {
Description description = binding.chart.getDescription(); Description description = binding.chart.getDescription();
description.setEnabled(false); description.setEnabled(false);
CustomMarkerView mv = new CustomMarkerView(CheckHomeCacheActivity.this, R.layout.custom_marker_view_layout); CustomMarkerView mv = new CustomMarkerView(CheckHomeCacheActivity.this, R.layout.custom_marker_view_layout);
binding.chart.setMarker(mv); binding.chart.setMarkerView(mv);
binding.chart.invalidate(); binding.chart.invalidate();
}); });
@ -247,7 +247,7 @@ public class CheckHomeCacheActivity extends BaseBarActivity {
try { try {
if (myRange == range.ALL) { if (myRange == range.ALL) {
if (timelineCacheLogsList == null) { if (timelineCacheLogsList == null) {
timelineCacheLogsList = new TimelineCacheLogs(this).getHome(Helper.getCurrentAccount(CheckHomeCacheActivity.this)); timelineCacheLogsList = new TimelineCacheLogs(this).getHome(MainActivity.currentAccount);
} }
} else if (myRange == range.DAY) { } else if (myRange == range.DAY) {
if (timelineCacheLogsDayList == null && timelineCacheLogsList != null) { if (timelineCacheLogsDayList == null && timelineCacheLogsList != null) {
@ -363,7 +363,7 @@ public class CheckHomeCacheActivity extends BaseBarActivity {
IndexAxisValueFormatter formatter = new IndexAxisValueFormatter() { IndexAxisValueFormatter formatter = new IndexAxisValueFormatter() {
@Override @Override
public String getFormattedValue(float value) { public String getFormattedValue(float value) {
if (value >= 0 && value < xVals2.size()) { if (value < xVals2.size()) {
return xVals2.get((int) value); return xVals2.get((int) value);
} else } else
return ""; return "";
@ -391,7 +391,7 @@ public class CheckHomeCacheActivity extends BaseBarActivity {
Description description = binding.chart2.getDescription(); Description description = binding.chart2.getDescription();
description.setEnabled(true); description.setEnabled(true);
CustomMarkerView2 mv = new CustomMarkerView2(CheckHomeCacheActivity.this, R.layout.custom_marker_view_layout); CustomMarkerView2 mv = new CustomMarkerView2(CheckHomeCacheActivity.this, R.layout.custom_marker_view_layout);
binding.chart2.setMarker(mv); binding.chart2.setMarkerView(mv);
binding.chart2.invalidate(); binding.chart2.invalidate();
}); });
@ -404,21 +404,12 @@ public class CheckHomeCacheActivity extends BaseBarActivity {
}).start(); }).start();
} }
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
public enum range { public enum range {
ALL, ALL,
DAY DAY
} }
public static class GraphElement { public static class GraphElement {
String dateLabel; String dateLabel;
int count; int count;
@ -433,6 +424,15 @@ public class CheckHomeCacheActivity extends BaseBarActivity {
} }
} }
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
public class CustomMarkerView extends MarkerView { public class CustomMarkerView extends MarkerView {
private final TextView tvContent; private final TextView tvContent;
@ -450,7 +450,7 @@ public class CheckHomeCacheActivity extends BaseBarActivity {
@Override @Override
public void refreshContent(Entry e, Highlight highlight) { public void refreshContent(Entry e, Highlight highlight) {
if (xVals.size() > (int) e.getX()) { if (xVals.size() > (int) e.getX()) {
tvContent.setText(getString(R.string.messages, (int) e.getY())); tvContent.setText(getString(R.string.messages, (int) e.getY()) + "\r\n" + xVals.get((int) e.getX()));
} }
// this will perform necessary layouting // this will perform necessary layouting
@ -496,7 +496,7 @@ public class CheckHomeCacheActivity extends BaseBarActivity {
text += dateDiffFull(timelineCacheLogsListToAnalyse.get((int) e.getX()).created_at); text += dateDiffFull(timelineCacheLogsListToAnalyse.get((int) e.getX()).created_at);
tvContent.setText(text); tvContent.setText(text);
} else { } else {
tvContent.setText(getString(R.string.messages, (int) e.getY())); tvContent.setText(getString(R.string.messages, (int) e.getY()) + "\r\n" + xVals2.get((int) e.getX()));
} }
} }

View file

@ -15,6 +15,7 @@ package app.fedilab.android.mastodon.activities;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.BaseMainActivity.currentAccount;
import static app.fedilab.android.BaseMainActivity.currentInstance; import static app.fedilab.android.BaseMainActivity.currentInstance;
import static app.fedilab.android.BaseMainActivity.emojis; import static app.fedilab.android.BaseMainActivity.emojis;
@ -32,23 +33,21 @@ import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.text.Editable; import android.text.Editable;
import android.text.InputFilter;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.util.TypedValue; import android.util.TypedValue;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.WindowManager;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.Toast; import android.widget.Toast;
import androidx.activity.OnBackPressedCallback;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.work.Data; import androidx.work.Data;
@ -56,7 +55,6 @@ import androidx.work.OneTimeWorkRequest;
import androidx.work.OutOfQuotaPolicy; import androidx.work.OutOfQuotaPolicy;
import androidx.work.WorkManager; import androidx.work.WorkManager;
import com.bumptech.glide.Glide;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.io.File; import java.io.File;
@ -66,7 +64,6 @@ import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
@ -86,7 +83,6 @@ import app.fedilab.android.mastodon.client.entities.api.Mention;
import app.fedilab.android.mastodon.client.entities.api.ScheduledStatus; import app.fedilab.android.mastodon.client.entities.api.ScheduledStatus;
import app.fedilab.android.mastodon.client.entities.api.Status; import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.mastodon.client.entities.app.BaseAccount; import app.fedilab.android.mastodon.client.entities.app.BaseAccount;
import app.fedilab.android.mastodon.client.entities.app.CachedBundle;
import app.fedilab.android.mastodon.client.entities.app.StatusDraft; import app.fedilab.android.mastodon.client.entities.app.StatusDraft;
import app.fedilab.android.mastodon.exception.DBException; import app.fedilab.android.mastodon.exception.DBException;
import app.fedilab.android.mastodon.helper.DividerDecorationSimple; import app.fedilab.android.mastodon.helper.DividerDecorationSimple;
@ -103,7 +99,7 @@ import app.fedilab.android.mastodon.viewmodel.mastodon.AccountsVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.StatusesVM; import app.fedilab.android.mastodon.viewmodel.mastodon.StatusesVM;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
public class ComposeActivity extends BaseActivity implements ComposeAdapter.ManageDrafts, AccountsReplyAdapter.ActionDone, ComposeAdapter.promptDraftListener, ComposeAdapter.MediaDescriptionCallBack { public class ComposeActivity extends BaseActivity implements ComposeAdapter.ManageDrafts, AccountsReplyAdapter.ActionDone, ComposeAdapter.promptDraftListener {
public static final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 754; public static final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 754;
@ -114,27 +110,23 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
private List<Status> statusList; private List<Status> statusList;
private Status statusReply, statusMention, statusQuoted; private Status statusReply, statusMention, statusQuoted;
private StatusDraft statusDraft; private StatusDraft statusDraft;
private ActionBar actionBar;
private ComposeAdapter composeAdapter; private ComposeAdapter composeAdapter;
private final BroadcastReceiver imageReceiver = new BroadcastReceiver() { private final BroadcastReceiver imageReceiver = new BroadcastReceiver() {
@Override @Override
public void onReceive(android.content.Context context, Intent intent) { public void onReceive(android.content.Context context, Intent intent) {
Bundle args = intent.getExtras(); String imgpath = intent.getStringExtra("imgpath");
if (args != null) { float focusX = intent.getFloatExtra("focusX", -2);
long bundleId = args.getLong(Helper.ARG_INTENT_ID, -1); float focusY = intent.getFloatExtra("focusY", -2);
new CachedBundle(ComposeActivity.this).getBundle(bundleId, Helper.getCurrentAccount(ComposeActivity.this), bundle -> {
String imgpath = bundle.getString("imgpath");
float focusX = bundle.getFloat("focusX", -2);
float focusY = bundle.getFloat("focusY", -2);
if (imgpath != null) { if (imgpath != null) {
int position = 0; int position = 0;
for (Status status : statusList) { for (Status status : statusList) {
if (status.media_attachments != null && !status.media_attachments.isEmpty()) { if (status.media_attachments != null && status.media_attachments.size() > 0) {
for (Attachment attachment : status.media_attachments) { for (Attachment attachment : status.media_attachments) {
if (attachment.local_path != null && attachment.local_path.equalsIgnoreCase(imgpath)) { if (attachment.local_path != null && attachment.local_path.equalsIgnoreCase(imgpath)) {
if (focusX != -2) { if (focusX != -2) {
attachment.focus = focusX + "," + focusY; attachment.focus = focusX + "," + focusY;
} }
composeAdapter.notifyItemChanged(position); composeAdapter.notifyItemChanged(position);
break; break;
} }
@ -143,8 +135,6 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
position++; position++;
} }
} }
});
}
} }
}; };
private boolean promptSaveDraft; private boolean promptSaveDraft;
@ -163,21 +153,31 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
private String editMessageId; private String editMessageId;
private static int visibilityToNumber(String visibility) { private static int visibilityToNumber(String visibility) {
return switch (visibility) { switch (visibility) {
case "unlisted" -> 2; case "public":
case "private" -> 1; return 3;
case "direct" -> 0; case "unlisted":
default -> 3; return 2;
}; case "private":
return 1;
case "direct":
return 0;
}
return 3;
} }
private static String visibilityToString(int visibility) { private static String visibilityToString(int visibility) {
return switch (visibility) { switch (visibility) {
case 2 -> "unlisted"; case 3:
case 1 -> "private"; return "public";
case 0 -> "direct"; case 2:
default -> "public"; return "unlisted";
}; case 1:
return "private";
case 0:
return "direct";
}
return "public";
} }
public static String getVisibility(BaseAccount account, String defaultVisibility) { public static String getVisibility(BaseAccount account, String defaultVisibility) {
@ -199,13 +199,15 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
if (timer != null) { if (timer != null) {
timer.cancel(); timer.cancel();
} }
try { LocalBroadcastManager.getInstance(this)
unregisterReceiver(imageReceiver); .unregisterReceiver(imageReceiver);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
} }
@Override
public void onBackPressed() {
storeDraftWarning();
}
private void storeDraftWarning() { private void storeDraftWarning() {
if (statusDraft == null) { if (statusDraft == null) {
@ -288,7 +290,6 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
statusList.add(initialStatus); statusList.add(initialStatus);
statusList.add(statusDraft.statusDraftList.get(0)); statusList.add(statusDraft.statusDraftList.get(0));
composeAdapter = new ComposeAdapter(statusList, context.ancestors.size(), account, accountMention, visibility, editMessageId); composeAdapter = new ComposeAdapter(statusList, context.ancestors.size(), account, accountMention, visibility, editMessageId);
composeAdapter.mediaDescriptionCallBack = this;
composeAdapter.promptDraftListener = this; composeAdapter.promptDraftListener = this;
composeAdapter.manageDrafts = this; composeAdapter.manageDrafts = this;
LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this); LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this);
@ -303,12 +304,6 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
public boolean onCreateOptionsMenu(@NonNull Menu menu) { public boolean onCreateOptionsMenu(@NonNull Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present. // Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_compose, menu); getMenuInflater().inflate(R.menu.menu_compose, menu);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
MenuItem micro = menu.findItem(R.id.action_microphone);
if (micro != null) {
micro.setVisible(false);
}
}
return true; return true;
} }
@ -474,7 +469,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
promptSaveDraft = false; promptSaveDraft = false;
restoredDraft = false; restoredDraft = false;
actionBar = getSupportActionBar(); ActionBar actionBar = getSupportActionBar();
//Remove title //Remove title
if (actionBar != null) { if (actionBar != null) {
actionBar.setDisplayShowTitleEnabled(false); actionBar.setDisplayShowTitleEnabled(false);
@ -488,22 +483,6 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
binding.title.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18 * 1.1f / scale); binding.title.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18 * 1.1f / scale);
statusList = new ArrayList<>(); statusList = new ArrayList<>();
Bundle b = getIntent().getExtras(); Bundle b = getIntent().getExtras();
if (b != null) {
long bundleId = b.getLong(Helper.ARG_INTENT_ID, -1);
if (bundleId != -1) {
new CachedBundle(ComposeActivity.this).getBundle(bundleId, Helper.getCurrentAccount(ComposeActivity.this), this::initializeAfterBundle);
} else {
initializeAfterBundle(b);
}
} else {
initializeAfterBundle(null);
}
}
private void initializeAfterBundle(Bundle b) {
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this);
new Thread(() -> {
if (b != null) { if (b != null) {
statusReply = (Status) b.getSerializable(Helper.ARG_STATUS_REPLY); statusReply = (Status) b.getSerializable(Helper.ARG_STATUS_REPLY);
statusQuoted = (Status) b.getSerializable(Helper.ARG_QUOTED_MESSAGE); statusQuoted = (Status) b.getSerializable(Helper.ARG_QUOTED_MESSAGE);
@ -513,24 +492,18 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
statusMention = (Status) b.getSerializable(Helper.ARG_STATUS_MENTION); statusMention = (Status) b.getSerializable(Helper.ARG_STATUS_MENTION);
account = (BaseAccount) b.getSerializable(Helper.ARG_ACCOUNT); account = (BaseAccount) b.getSerializable(Helper.ARG_ACCOUNT);
if (account == null) { if (account == null) {
account = Helper.getCurrentAccount(ComposeActivity.this); account = currentAccount;
} }
boolean setMentionBooster = sharedpreferences.getBoolean(getString(R.string.SET_MENTION_BOOSTER) + account.user_id + account.instance, false);
editMessageId = b.getString(Helper.ARG_EDIT_STATUS_ID, null); editMessageId = b.getString(Helper.ARG_EDIT_STATUS_ID, null);
instance = b.getString(Helper.ARG_INSTANCE, null); instance = b.getString(Helper.ARG_INSTANCE, null);
token = b.getString(Helper.ARG_TOKEN, null); token = b.getString(Helper.ARG_TOKEN, null);
visibility = b.getString(Helper.ARG_VISIBILITY, null); visibility = b.getString(Helper.ARG_VISIBILITY, null);
if (visibility == null && statusReply != null) { if (visibility == null && statusReply != null) {
visibility = getVisibility(account, statusReply.visibility); visibility = getVisibility(account, statusReply.visibility);
} else if (visibility == null && Helper.getCurrentAccount(ComposeActivity.this) != null && Helper.getCurrentAccount(ComposeActivity.this).mastodon_account != null && Helper.getCurrentAccount(ComposeActivity.this).mastodon_account.source != null) { } else if (visibility == null && currentAccount != null && currentAccount.mastodon_account != null && currentAccount.mastodon_account.source != null) {
visibility = Helper.getCurrentAccount(ComposeActivity.this).mastodon_account.source.privacy; visibility = currentAccount.mastodon_account.source.privacy;
} }
if(setMentionBooster) {
mentionBooster = (Account) b.getSerializable(Helper.ARG_MENTION_BOOSTER); mentionBooster = (Account) b.getSerializable(Helper.ARG_MENTION_BOOSTER);
} else {
mentionBooster = null;
}
accountMention = (Account) b.getSerializable(Helper.ARG_ACCOUNT_MENTION); accountMention = (Account) b.getSerializable(Helper.ARG_ACCOUNT_MENTION);
//Shared elements //Shared elements
sharedAttachments = (ArrayList<Attachment>) b.getSerializable(Helper.ARG_MEDIA_ATTACHMENTS); sharedAttachments = (ArrayList<Attachment>) b.getSerializable(Helper.ARG_MEDIA_ATTACHMENTS);
@ -541,8 +514,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
sharedDescription = b.getString(Helper.ARG_SHARE_DESCRIPTION, null); sharedDescription = b.getString(Helper.ARG_SHARE_DESCRIPTION, null);
shareURL = b.getString(Helper.ARG_SHARE_URL, null); shareURL = b.getString(Helper.ARG_SHARE_URL, null);
} }
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> {
if (sharedContent != null && shareURL != null && sharedContent.compareTo(shareURL) == 0) { if (sharedContent != null && shareURL != null && sharedContent.compareTo(shareURL) == 0) {
sharedContent = ""; sharedContent = "";
} }
@ -558,22 +530,24 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
status.text = scheduledStatus.params.text; status.text = scheduledStatus.params.text;
status.in_reply_to_id = scheduledStatus.params.in_reply_to_id; status.in_reply_to_id = scheduledStatus.params.in_reply_to_id;
status.poll = scheduledStatus.params.poll; status.poll = scheduledStatus.params.poll;
if (scheduledStatus.params.media_ids != null && !scheduledStatus.params.media_ids.isEmpty()) {
if (scheduledStatus.params.media_ids != null && scheduledStatus.params.media_ids.size() > 0) {
status.media_attachments = new ArrayList<>(); status.media_attachments = new ArrayList<>();
new Thread(() -> {
StatusesVM statusesVM = new ViewModelProvider(ComposeActivity.this).get(StatusesVM.class);
for (String attachmentId : scheduledStatus.params.media_ids) { for (String attachmentId : scheduledStatus.params.media_ids) {
Attachment attachment = new Attachment(); statusesVM.getAttachment(instance, token, attachmentId)
attachment.id = attachmentId; .observe(ComposeActivity.this, attachment -> status.media_attachments.add(attachment));
status.media_attachments.add(attachment);
} }
}).start();
} }
status.sensitive = scheduledStatus.params.sensitive; status.sensitive = scheduledStatus.params.sensitive;
status.spoiler_text = scheduledStatus.params.spoiler_text; status.spoiler_text = scheduledStatus.params.spoiler_text;
status.visibility = scheduledStatus.params.visibility; status.visibility = scheduledStatus.params.visibility;
statuses.add(status);
statusDraft.statusDraftList = statuses; statusDraft.statusDraftList = statuses;
} }
if (account == null) { if (account == null) {
account = Helper.getCurrentAccount(ComposeActivity.this); account = currentAccount;
} }
if (account == null) { if (account == null) {
Toasty.error(ComposeActivity.this, getString(R.string.toast_error), Toasty.LENGTH_SHORT).show(); Toasty.error(ComposeActivity.this, getString(R.string.toast_error), Toasty.LENGTH_SHORT).show();
@ -637,7 +611,6 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
int statusCount = statusList.size(); int statusCount = statusList.size();
statusList.addAll(statusDraft.statusDraftList); statusList.addAll(statusDraft.statusDraftList);
composeAdapter = new ComposeAdapter(statusList, statusCount, account, accountMention, visibility, editMessageId); composeAdapter = new ComposeAdapter(statusList, statusCount, account, accountMention, visibility, editMessageId);
composeAdapter.mediaDescriptionCallBack = this;
composeAdapter.manageDrafts = this; composeAdapter.manageDrafts = this;
composeAdapter.promptDraftListener = this; composeAdapter.promptDraftListener = this;
LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this); LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this);
@ -676,7 +649,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
mention.username = mentionBooster.username; mention.username = mentionBooster.username;
boolean present = false; boolean present = false;
for (Mention mentionTmp : statusDraftList.get(0).mentions) { for (Mention mentionTmp : statusDraftList.get(0).mentions) {
if (mentionTmp.acct.equalsIgnoreCase("@"+mentionBooster.acct)) { if (mentionTmp.acct.equalsIgnoreCase(mentionBooster.acct)) {
present = true; present = true;
break; break;
} }
@ -687,13 +660,13 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
} }
if (statusReply.spoiler_text != null) { if (statusReply.spoiler_text != null) {
statusDraftList.get(0).spoiler_text = statusReply.spoiler_text; statusDraftList.get(0).spoiler_text = statusReply.spoiler_text;
if (!statusReply.spoiler_text.trim().isEmpty()) { if (statusReply.spoiler_text.trim().length() > 0) {
statusDraftList.get(0).spoilerChecked = true; statusDraftList.get(0).spoilerChecked = true;
} }
} }
if (statusReply.language != null && !statusReply.language.isEmpty()) { if (statusReply.language != null && !statusReply.language.isEmpty()) {
Set<String> storedLanguages = sharedpreferences.getStringSet(getString(R.string.SET_SELECTED_LANGUAGE), null); Set<String> storedLanguages = sharedpreferences.getStringSet(getString(R.string.SET_SELECTED_LANGUAGE), null);
if (storedLanguages == null || storedLanguages.isEmpty()) { if (storedLanguages == null || storedLanguages.size() == 0) {
statusDraftList.get(0).language = statusReply.language; statusDraftList.get(0).language = statusReply.language;
} else { } else {
if (storedLanguages.contains(statusReply.language)) { if (storedLanguages.contains(statusReply.language)) {
@ -710,7 +683,6 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
//StatusDraftList at this point should only have one element //StatusDraftList at this point should only have one element
statusList.addAll(statusDraftList); statusList.addAll(statusDraftList);
composeAdapter = new ComposeAdapter(statusList, statusCount, account, accountMention, visibility, editMessageId); composeAdapter = new ComposeAdapter(statusList, statusCount, account, accountMention, visibility, editMessageId);
composeAdapter.mediaDescriptionCallBack = this;
composeAdapter.manageDrafts = this; composeAdapter.manageDrafts = this;
composeAdapter.promptDraftListener = this; composeAdapter.promptDraftListener = this;
LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this); LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this);
@ -725,7 +697,6 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
//StatusDraftList at this point should only have one element //StatusDraftList at this point should only have one element
statusList.addAll(statusDraftList); statusList.addAll(statusDraftList);
composeAdapter = new ComposeAdapter(statusList, statusCount, account, accountMention, visibility, editMessageId); composeAdapter = new ComposeAdapter(statusList, statusCount, account, accountMention, visibility, editMessageId);
composeAdapter.mediaDescriptionCallBack = this;
composeAdapter.manageDrafts = this; composeAdapter.manageDrafts = this;
composeAdapter.promptDraftListener = this; composeAdapter.promptDraftListener = this;
LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this); LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this);
@ -735,7 +706,6 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
//Compose without replying //Compose without replying
statusList.addAll(statusDraftList); statusList.addAll(statusDraftList);
composeAdapter = new ComposeAdapter(statusList, 0, account, accountMention, visibility, editMessageId); composeAdapter = new ComposeAdapter(statusList, 0, account, accountMention, visibility, editMessageId);
composeAdapter.mediaDescriptionCallBack = this;
composeAdapter.manageDrafts = this; composeAdapter.manageDrafts = this;
composeAdapter.promptDraftListener = this; composeAdapter.promptDraftListener = this;
LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this); LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this);
@ -746,7 +716,10 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
} }
} }
MastodonHelper.loadPPMastodon(binding.profilePicture, account.mastodon_account); MastodonHelper.loadPPMastodon(binding.profilePicture, account.mastodon_account);
ContextCompat.registerReceiver(ComposeActivity.this, imageReceiver, new IntentFilter(Helper.INTENT_SEND_MODIFIED_IMAGE), ContextCompat.RECEIVER_NOT_EXPORTED); LocalBroadcastManager.getInstance(this)
.registerReceiver(imageReceiver,
new IntentFilter(Helper.INTENT_SEND_MODIFIED_IMAGE));
if (timer != null) { if (timer != null) {
timer.scheduleAtFixedRate(new TimerTask() { timer.scheduleAtFixedRate(new TimerTask() {
@Override @Override
@ -758,7 +731,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
}, 0, 10000); }, 0, 10000);
} }
if (sharedAttachments != null && !sharedAttachments.isEmpty()) { if (sharedAttachments != null && sharedAttachments.size() > 0) {
for (Attachment attachment : sharedAttachments) { for (Attachment attachment : sharedAttachments) {
composeAdapter.addAttachment(-1, attachment); composeAdapter.addAttachment(-1, attachment);
} }
@ -790,31 +763,15 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
composeAdapter.addSharing(null, null, sharedDescription, null, sharedContent, null); composeAdapter.addSharing(null, null, sharedDescription, null, sharedContent, null);
} }
} }
getOnBackPressedDispatcher().addCallback(new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
if (binding.recyclerView.getVisibility() == View.VISIBLE) {
storeDraftWarning();
} }
}
});
};
mainHandler.post(myRunnable);
}).start();
}
@Override @Override
public void onItemDraftAdded(int position, String initialContent) { public void onItemDraftAdded(int position) {
Status status = new Status(); Status status = new Status();
status.id = Helper.generateIdString(); status.id = Helper.generateIdString();
status.mentions = statusList.get(position - 1).mentions; status.mentions = statusList.get(position - 1).mentions;
status.visibility = statusList.get(position - 1).visibility; status.visibility = statusList.get(position - 1).visibility;
if (initialContent != null) {
status.text = initialContent;
}
final SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(ComposeActivity.this); final SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(ComposeActivity.this);
boolean unlistedReplies = sharedpreferences.getBoolean(getString(R.string.SET_UNLISTED_REPLIES), true); boolean unlistedReplies = sharedpreferences.getBoolean(getString(R.string.SET_UNLISTED_REPLIES), true);
if (status.visibility.equalsIgnoreCase("public") && unlistedReplies) { if (status.visibility.equalsIgnoreCase("public") && unlistedReplies) {
@ -822,7 +779,6 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
} }
status.spoiler_text = statusList.get(position - 1).spoiler_text; status.spoiler_text = statusList.get(position - 1).spoiler_text;
status.sensitive = statusList.get(position - 1).sensitive; status.sensitive = statusList.get(position - 1).sensitive;
status.spoilerChecked = statusList.get(position - 1).spoilerChecked;
statusList.add(status); statusList.add(status);
composeAdapter.notifyItemInserted(position); composeAdapter.notifyItemInserted(position);
composeAdapter.notifyItemRangeChanged(0, statusList.size()); composeAdapter.notifyItemRangeChanged(0, statusList.size());
@ -849,15 +805,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
private void storeDraft(boolean sendMessage) { private void storeDraft(boolean sendMessage) {
String scheduledDate = null; storeDraft(sendMessage, null);
if(scheduledStatus != null && scheduledStatus.scheduled_at != null) {
SimpleDateFormat sdf = new SimpleDateFormat(Helper.SCHEDULE_DATE_FORMAT, Locale.getDefault());
scheduledDate = sdf.format(scheduledStatus.scheduled_at.getTime());
} else if(statusDraft != null && statusDraft.scheduled_at != null) {
SimpleDateFormat sdf = new SimpleDateFormat(Helper.SCHEDULE_DATE_FORMAT, Locale.getDefault());
scheduledDate = sdf.format(statusDraft.scheduled_at.getTime());
}
storeDraft(sendMessage, scheduledDate);
} }
private void storeDraft(boolean sendMessage, String scheduledDate) { private void storeDraft(boolean sendMessage, String scheduledDate) {
@ -879,19 +827,14 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
} else { } else {
//Draft previously and date is changed //Draft previously and date is changed
if (statusDraft.scheduled_at != null && scheduledDate != null && statusDraft.workerUuid != null) { if (statusDraft.scheduled_at != null && scheduledDate != null && statusDraft.workerUuid != null) {
try {
new StatusDraft(ComposeActivity.this).removeScheduled(statusDraft);
WorkManager.getInstance(ComposeActivity.this).cancelWorkById(statusDraft.workerUuid); WorkManager.getInstance(ComposeActivity.this).cancelWorkById(statusDraft.workerUuid);
} catch (DBException e) {
e.printStackTrace();
} }
} }
} if (statusReplies.size() > 0) {
if (!statusReplies.isEmpty()) {
statusDraft.statusReplyList = new ArrayList<>(); statusDraft.statusReplyList = new ArrayList<>();
statusDraft.statusReplyList.addAll(statusReplies); statusDraft.statusReplyList.addAll(statusReplies);
} }
if (!statusDrafts.isEmpty()) { if (statusDrafts.size() > 0) {
statusDraft.statusDraftList = new ArrayList<>(); statusDraft.statusDraftList = new ArrayList<>();
statusDraft.statusDraftList.addAll(statusDrafts); statusDraft.statusDraftList.addAll(statusDrafts);
} }
@ -913,6 +856,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
materialAlertDialogBuilder.setPositiveButton(R.string.send_anyway, (dialog, id) -> { materialAlertDialogBuilder.setPositiveButton(R.string.send_anyway, (dialog, id) -> {
sendMessage(true, scheduledDate); sendMessage(true, scheduledDate);
dialog.dismiss(); dialog.dismiss();
}); });
materialAlertDialogBuilder.setNegativeButton(R.string.add_description, (dialog, id) -> { materialAlertDialogBuilder.setNegativeButton(R.string.add_description, (dialog, id) -> {
composeAdapter.openMissingDescription(); composeAdapter.openMissingDescription();
@ -923,7 +867,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
} else { } else {
Toasty.info(ComposeActivity.this, getString(R.string.toot_error_no_content), Toasty.LENGTH_SHORT).show(); Toasty.info(ComposeActivity.this, getString(R.string.toot_error_no_content), Toasty.LENGTH_SHORT).show();
} }
if (!statusDrafts.isEmpty()) { if (statusDrafts.size() > 0) {
statusDrafts.get(statusDrafts.size() - 1).submitted = false; statusDrafts.get(statusDrafts.size() - 1).submitted = false;
composeAdapter.notifyItemChanged(statusList.size() - 1); composeAdapter.notifyItemChanged(statusList.size() - 1);
} }
@ -996,14 +940,12 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
mediaCount += status.media_attachments != null ? status.media_attachments.size() : 0; mediaCount += status.media_attachments != null ? status.media_attachments.size() : 0;
} }
if (mediaCount > 0) { if (mediaCount > 0) {
String scheduledStatusId = scheduledStatus!=null&&scheduledStatus.id!=null?scheduledStatus.id:null;
Data inputData = new Data.Builder() Data inputData = new Data.Builder()
.putString(Helper.ARG_STATUS_DRAFT_ID, String.valueOf(statusDraft.id)) .putString(Helper.ARG_STATUS_DRAFT_ID, String.valueOf(statusDraft.id))
.putString(Helper.ARG_INSTANCE, instance) .putString(Helper.ARG_INSTANCE, instance)
.putString(Helper.ARG_TOKEN, token) .putString(Helper.ARG_TOKEN, token)
.putString(Helper.ARG_EDIT_STATUS_ID, editMessageId) .putString(Helper.ARG_EDIT_STATUS_ID, editMessageId)
.putString(Helper.ARG_USER_ID, account.user_id) .putString(Helper.ARG_USER_ID, account.user_id)
.putString(Helper.ARG_SCHEDULED_ID, scheduledStatusId)
.putString(Helper.ARG_SCHEDULED_DATE, scheduledDate).build(); .putString(Helper.ARG_SCHEDULED_DATE, scheduledDate).build();
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(ComposeWorker.class) OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(ComposeWorker.class)
.setInputData(inputData) .setInputData(inputData)
@ -1012,8 +954,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
WorkManager.getInstance(ComposeActivity.this).enqueue(request); WorkManager.getInstance(ComposeActivity.this).enqueue(request);
} else { } else {
String scheduledStatusId = scheduledStatus!=null&&scheduledStatus.id!=null?scheduledStatus.id:null; new ThreadMessageService(ComposeActivity.this, instance, account.user_id, token, statusDraft, scheduledDate, editMessageId);
new ThreadMessageService(ComposeActivity.this, instance, account.user_id, token, statusDraft, scheduledDate, editMessageId, scheduledStatusId);
} }
finish(); finish();
} }
@ -1063,40 +1004,6 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
promptSaveDraft = true; promptSaveDraft = true;
} }
@Override
public void click(ComposeAdapter.ComposeViewHolder holder, Attachment attachment, int messagePosition, int mediaPosition) {
binding.description.setVisibility(View.VISIBLE);
actionBar.hide();
binding.recyclerView.setVisibility(View.GONE);
binding.mediaDescription.setText("");
String attachmentPath = attachment.local_path != null && !attachment.local_path.trim().isEmpty() ? attachment.local_path : attachment.preview_url;
Glide.with(binding.mediaPreview.getContext())
.load(attachmentPath)
.into(binding.mediaPreview);
if (attachment.description != null) {
binding.mediaDescription.setText(attachment.description);
binding.mediaDescription.setSelection(Objects.requireNonNull(binding.mediaDescription.getText()).length());
}
binding.mediaDescription.setFilters(new InputFilter[]{new InputFilter.LengthFilter(1500)});
binding.mediaDescription.requestFocus();
Objects.requireNonNull(getWindow()).setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
binding.mediaDescription.requestFocus();
binding.mediaSave.setOnClickListener(v -> {
binding.description.setVisibility(View.GONE);
actionBar.show();
binding.recyclerView.setVisibility(View.VISIBLE);
promptSaveDraft = true;
composeAdapter.openDescriptionActivity(true, Objects.requireNonNull(binding.mediaDescription.getText()).toString().trim(), holder, attachment, messagePosition, mediaPosition);
});
binding.mediaCancel.setOnClickListener(v -> {
binding.description.setVisibility(View.GONE);
actionBar.show();
binding.recyclerView.setVisibility(View.VISIBLE);
composeAdapter.openDescriptionActivity(false, Objects.requireNonNull(binding.mediaDescription.getText()).toString().trim(), holder, attachment, messagePosition, mediaPosition);
});
}
public enum mediaType { public enum mediaType {
PHOTO, PHOTO,

View file

@ -15,7 +15,7 @@ package app.fedilab.android.mastodon.activities;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.BaseMainActivity.currentInstance; import static app.fedilab.android.BaseMainActivity.currentAccount;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
@ -29,13 +29,10 @@ import android.view.MenuItem;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import com.google.android.material.appbar.AppBarLayout;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.util.regex.Matcher; import java.util.regex.Matcher;
@ -46,7 +43,6 @@ import app.fedilab.android.R;
import app.fedilab.android.activities.MainActivity; import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.databinding.ActivityConversationBinding; import app.fedilab.android.databinding.ActivityConversationBinding;
import app.fedilab.android.mastodon.client.entities.api.Status; import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.mastodon.client.entities.app.CachedBundle;
import app.fedilab.android.mastodon.client.entities.app.StatusCache; import app.fedilab.android.mastodon.client.entities.app.StatusCache;
import app.fedilab.android.mastodon.exception.DBException; import app.fedilab.android.mastodon.exception.DBException;
import app.fedilab.android.mastodon.helper.Helper; import app.fedilab.android.mastodon.helper.Helper;
@ -64,16 +60,12 @@ public class ContextActivity extends BaseActivity implements FragmentMastodonCon
Fragment currentFragment; Fragment currentFragment;
private Status firstMessage; private Status firstMessage;
private String remote_instance; private String remote_instance;
private Status focusedStatus;
private String focusedStatusURI;
private boolean checkRemotely;
private ActivityConversationBinding binding;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
binding = ActivityConversationBinding.inflate(getLayoutInflater()); ActivityConversationBinding binding = ActivityConversationBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
ActionBar actionBar = getSupportActionBar(); ActionBar actionBar = getSupportActionBar();
@ -90,75 +82,24 @@ public class ContextActivity extends BaseActivity implements FragmentMastodonCon
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true);
} }
manageTopBarScrolling(binding.toolbar, sharedpreferences); Bundle b = getIntent().getExtras();
displayCW = sharedpreferences.getBoolean(getString(R.string.SET_EXPAND_CW), false); displayCW = sharedpreferences.getBoolean(getString(R.string.SET_EXPAND_CW), false);
focusedStatus = null; // or other values Status focusedStatus = null; // or other values
if (b != null) {
focusedStatus = (Status) b.getSerializable(Helper.ARG_STATUS);
Bundle args = getIntent().getExtras(); remote_instance = b.getString(Helper.ARG_REMOTE_INSTANCE, null);
if (args != null) {
long bundleId = args.getLong(Helper.ARG_INTENT_ID, -1);
new CachedBundle(ContextActivity.this).getBundle(bundleId, Helper.getCurrentAccount(ContextActivity.this), this::initializeAfterBundle);
} else {
initializeAfterBundle(null);
} }
} if (focusedStatus == null || currentAccount == null || currentAccount.mastodon_account == null) {
private void initializeAfterBundle(Bundle bundle) {
if (bundle != null) {
focusedStatus = (Status) bundle.getSerializable(Helper.ARG_STATUS);
remote_instance = bundle.getString(Helper.ARG_REMOTE_INSTANCE, null);
focusedStatusURI = bundle.getString(Helper.ARG_FOCUSED_STATUS_URI, null);
}
if (focusedStatus == null || Helper.getCurrentAccount(ContextActivity.this) == null || Helper.getCurrentAccount(ContextActivity.this).mastodon_account == null) {
finish(); finish();
return; return;
} }
if (focusedStatusURI == null && remote_instance == null) { MastodonHelper.loadPPMastodon(binding.profilePicture, currentAccount.mastodon_account);
focusedStatusURI = focusedStatus.uri;
}
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this);
checkRemotely = sharedpreferences.getBoolean(getString(R.string.SET_CONVERSATION_REMOTELY), false);
if (!checkRemotely) {
loadLocalConversation();
} else {
loadRemotelyConversation(true);
invalidateOptionsMenu();
}
if (Helper.getCurrentAccount(ContextActivity.this) != null) {
MastodonHelper.loadPPMastodon(binding.profilePicture, Helper.getCurrentAccount(ContextActivity.this).mastodon_account);
}
}
private void manageTopBarScrolling(Toolbar toolbar, SharedPreferences sharedpreferences) {
final boolean topBarScrolling = !sharedpreferences.getBoolean(getString(R.string.SET_DISABLE_TOPBAR_SCROLLING), false);
final AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
int scrollFlags = toolbarLayoutParams.getScrollFlags();
if (topBarScrolling) {
scrollFlags |= AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL;
} else {
scrollFlags &= ~AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL;
}
toolbarLayoutParams.setScrollFlags(scrollFlags);
}
private void loadLocalConversation() {
Bundle args = new Bundle();
args.putSerializable(Helper.ARG_STATUS, focusedStatus);
args.putString(Helper.ARG_REMOTE_INSTANCE, remote_instance);
new CachedBundle(ContextActivity.this).insertBundle(args, Helper.getCurrentAccount(ContextActivity.this), bundleId -> {
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.putLong(Helper.ARG_INTENT_ID, bundleId); bundle.putSerializable(Helper.ARG_STATUS, focusedStatus);
bundle.putString(Helper.ARG_REMOTE_INSTANCE, remote_instance);
FragmentMastodonContext fragmentMastodonContext = new FragmentMastodonContext(); FragmentMastodonContext fragmentMastodonContext = new FragmentMastodonContext();
fragmentMastodonContext.firstMessage = this; fragmentMastodonContext.firstMessage = this;
currentFragment = Helper.addFragment(getSupportFragmentManager(), R.id.nav_host_fragment_content_main, fragmentMastodonContext, bundle, null, null); currentFragment = Helper.addFragment(getSupportFragmentManager(), R.id.nav_host_fragment_content_main, fragmentMastodonContext, bundle, null, null);
//Update the status
if (remote_instance == null) { if (remote_instance == null) {
StatusesVM timelinesVM = new ViewModelProvider(ContextActivity.this).get(StatusesVM.class); StatusesVM timelinesVM = new ViewModelProvider(ContextActivity.this).get(StatusesVM.class);
timelinesVM.getStatus(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, focusedStatus.id).observe(ContextActivity.this, status -> { timelinesVM.getStatus(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, focusedStatus.id).observe(ContextActivity.this, status -> {
@ -183,9 +124,9 @@ public class ContextActivity extends BaseActivity implements FragmentMastodonCon
} }
}); });
} }
});
} }
@Override @Override
public boolean onCreateOptionsMenu(@NonNull Menu menu) { public boolean onCreateOptionsMenu(@NonNull Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present. // Inflate the menu; this adds items to the action bar if it is present.
@ -203,7 +144,7 @@ public class ContextActivity extends BaseActivity implements FragmentMastodonCon
itemDisplayCW.setIcon(R.drawable.ic_outline_remove_red_eye_24); itemDisplayCW.setIcon(R.drawable.ic_outline_remove_red_eye_24);
} }
MenuItem action_remote = menu.findItem(R.id.action_remote); MenuItem action_remote = menu.findItem(R.id.action_remote);
if (remote_instance != null || checkRemotely) { if (remote_instance != null) {
action_remote.setVisible(false); action_remote.setVisible(false);
} else { } else {
if (firstMessage != null && !firstMessage.visibility.equalsIgnoreCase("direct") && !firstMessage.visibility.equalsIgnoreCase("private")) { if (firstMessage != null && !firstMessage.visibility.equalsIgnoreCase("direct") && !firstMessage.visibility.equalsIgnoreCase("private")) {
@ -240,67 +181,10 @@ public class ContextActivity extends BaseActivity implements FragmentMastodonCon
} }
invalidateOptionsMenu(); invalidateOptionsMenu();
} else if (item.getItemId() == R.id.action_remote) { } else if (item.getItemId() == R.id.action_remote) {
loadRemotelyConversation(false);
}
return true;
}
private void loadRemotelyConversation(boolean fallback) {
if (fallback) {
StatusesVM statusesVM;
statusesVM = new ViewModelProvider(this).get(StatusesVM.class);
statusesVM.getContext(currentInstance, null, focusedStatus.id)
.observe(this, result -> {
if (result != null && result.ancestors != null && result.ancestors.size() > 0) {
firstMessage = result.ancestors.get(0);
String instance = null;
try {
URL url = new URL(firstMessage.uri);
instance = url.getHost();
} catch (MalformedURLException e) {
e.printStackTrace();
}
if (instance == null) {
loadLocalConversation();
return;
}
Pattern pattern = Helper.statusIdInUrl;
Matcher matcher = pattern.matcher(firstMessage.uri);
String remoteId = null;
if (matcher.find()) {
remoteId = matcher.group(1);
}
if (remoteId == null) {
loadLocalConversation();
return;
}
String finalInstance = instance;
statusesVM.getStatus(instance, null, remoteId).observe(ContextActivity.this, status -> {
if (status != null) {
Bundle args = new Bundle();
args.putSerializable(Helper.ARG_STATUS, status);
args.putString(Helper.ARG_REMOTE_INSTANCE, finalInstance);
args.putString(Helper.ARG_FOCUSED_STATUS_URI, focusedStatusURI);
new CachedBundle(ContextActivity.this).insertBundle(args, Helper.getCurrentAccount(ContextActivity.this), bundleId -> {
Bundle bundle = new Bundle();
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
FragmentMastodonContext fragmentMastodonContext = new FragmentMastodonContext();
fragmentMastodonContext.firstMessage = ContextActivity.this;
currentFragment = Helper.addFragment(getSupportFragmentManager(), R.id.nav_host_fragment_content_main, fragmentMastodonContext, bundle, null, null);
});
} else {
loadLocalConversation();
}
});
} else {
loadLocalConversation();
}
});
} else {
if (firstMessage == null) { if (firstMessage == null) {
Toasty.warning(ContextActivity.this, getString(R.string.toast_try_later), Toasty.LENGTH_SHORT).show(); Toasty.warning(ContextActivity.this, getString(R.string.toast_try_later), Toasty.LENGTH_SHORT).show();
return; return true;
} }
if (firstMessage.account.acct != null) { if (firstMessage.account.acct != null) {
String instance = null; String instance = null;
@ -312,11 +196,11 @@ public class ContextActivity extends BaseActivity implements FragmentMastodonCon
} }
if (instance == null) { if (instance == null) {
Toasty.info(ContextActivity.this, getString(R.string.toast_error_fetch_message), Toasty.LENGTH_SHORT).show(); Toasty.info(ContextActivity.this, getString(R.string.toast_error_fetch_message), Toasty.LENGTH_SHORT).show();
return; return true;
} }
if (instance.equalsIgnoreCase(MainActivity.currentInstance)) { if (instance.equalsIgnoreCase(MainActivity.currentInstance)) {
Toasty.info(ContextActivity.this, getString(R.string.toast_on_your_instance), Toasty.LENGTH_SHORT).show(); Toasty.info(ContextActivity.this, getString(R.string.toast_on_your_instance), Toasty.LENGTH_SHORT).show();
return; return true;
} }
Pattern pattern = Helper.statusIdInUrl; Pattern pattern = Helper.statusIdInUrl;
Matcher matcher = pattern.matcher(firstMessage.uri); Matcher matcher = pattern.matcher(firstMessage.uri);
@ -330,17 +214,10 @@ public class ContextActivity extends BaseActivity implements FragmentMastodonCon
statusesVM.getStatus(instance, null, remoteId).observe(ContextActivity.this, status -> { statusesVM.getStatus(instance, null, remoteId).observe(ContextActivity.this, status -> {
if (status != null) { if (status != null) {
Intent intentContext = new Intent(ContextActivity.this, ContextActivity.class); Intent intentContext = new Intent(ContextActivity.this, ContextActivity.class);
Bundle args = new Bundle(); intentContext.putExtra(Helper.ARG_STATUS, status);
args.putSerializable(Helper.ARG_STATUS, status); intentContext.putExtra(Helper.ARG_REMOTE_INSTANCE, finalInstance);
args.putString(Helper.ARG_FOCUSED_STATUS_URI, focusedStatusURI);
args.putString(Helper.ARG_REMOTE_INSTANCE, finalInstance);
new CachedBundle(ContextActivity.this).insertBundle(args, Helper.getCurrentAccount(ContextActivity.this), bundleId -> {
Bundle bundle = new Bundle();
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intentContext.putExtras(bundle);
intentContext.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intentContext.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intentContext); startActivity(intentContext);
});
} else { } else {
Toasty.warning(ContextActivity.this, getString(R.string.toast_error_fetch_message), Toasty.LENGTH_SHORT).show(); Toasty.warning(ContextActivity.this, getString(R.string.toast_error_fetch_message), Toasty.LENGTH_SHORT).show();
} }
@ -352,6 +229,7 @@ public class ContextActivity extends BaseActivity implements FragmentMastodonCon
Toasty.warning(ContextActivity.this, getString(R.string.toast_error_fetch_message), Toasty.LENGTH_SHORT).show(); Toasty.warning(ContextActivity.this, getString(R.string.toast_error_fetch_message), Toasty.LENGTH_SHORT).show();
} }
} }
return true;
} }
@Override @Override

View file

@ -14,6 +14,7 @@ package app.fedilab.android.mastodon.activities;
* You should have received a copy of the GNU General Public License along with Fedilab; if not, * You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.BaseMainActivity.currentAccount;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.net.Uri; import android.net.Uri;
@ -27,7 +28,6 @@ import android.widget.Toast;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import app.fedilab.android.R; import app.fedilab.android.R;
@ -36,7 +36,6 @@ import app.fedilab.android.mastodon.client.entities.api.Attachment;
import app.fedilab.android.mastodon.client.entities.api.Emoji; import app.fedilab.android.mastodon.client.entities.api.Emoji;
import app.fedilab.android.mastodon.client.entities.api.Status; import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.mastodon.client.entities.api.Tag; import app.fedilab.android.mastodon.client.entities.api.Tag;
import app.fedilab.android.mastodon.client.entities.app.CachedBundle;
import app.fedilab.android.mastodon.helper.Helper; import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.helper.customsharing.CustomSharingAsyncTask; import app.fedilab.android.mastodon.helper.customsharing.CustomSharingAsyncTask;
import app.fedilab.android.mastodon.helper.customsharing.CustomSharingResponse; import app.fedilab.android.mastodon.helper.customsharing.CustomSharingResponse;
@ -65,34 +64,23 @@ public class CustomSharingActivity extends BaseBarActivity implements OnCustomSh
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(CustomSharingActivity.this);
binding = ActivityCustomSharingBinding.inflate(getLayoutInflater()); binding = ActivityCustomSharingBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true);
} }
Bundle args = getIntent().getExtras(); Bundle b = getIntent().getExtras();
status = null; status = null;
if (args != null) { if (b != null) {
long bundleId = args.getLong(Helper.ARG_INTENT_ID, -1); status = (Status) b.getSerializable(Helper.ARG_STATUS);
new CachedBundle(CustomSharingActivity.this).getBundle(bundleId, Helper.getCurrentAccount(CustomSharingActivity.this), this::initializeAfterBundle);
} else {
initializeAfterBundle(null);
}
}
private void initializeAfterBundle(Bundle bundle) {
if (bundle != null) {
status = (Status) bundle.getSerializable(Helper.ARG_STATUS);
} }
if (status == null) { if (status == null) {
finish(); finish();
return; return;
} }
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(CustomSharingActivity.this);
bundle_creator = status.account.acct; bundle_creator = status.account.acct;
bundle_url = status.url; bundle_url = status.url;
bundle_id = status.uri; bundle_id = status.uri;
@ -109,7 +97,7 @@ public class CustomSharingActivity extends BaseBarActivity implements OnCustomSh
bundle_thumbnailurl = status.account.avatar; bundle_thumbnailurl = status.account.avatar;
} }
if (!bundle_creator.contains("@")) { if (!bundle_creator.contains("@")) {
bundle_creator = bundle_creator + "@" + Helper.getCurrentAccount(CustomSharingActivity.this).instance; bundle_creator = bundle_creator + "@" + currentAccount.instance;
} }
binding.setCustomSharingTitle.setEllipsize(TextUtils.TruncateAt.END); binding.setCustomSharingTitle.setEllipsize(TextUtils.TruncateAt.END);
@ -138,7 +126,7 @@ public class CustomSharingActivity extends BaseBarActivity implements OnCustomSh
binding.setCustomSharingSave.setOnClickListener(v -> { binding.setCustomSharingSave.setOnClickListener(v -> {
// obtain title, description, keywords // obtain title, description, keywords
title = binding.setCustomSharingTitle.getText().toString(); title = binding.setCustomSharingTitle.getText().toString();
keywords = Objects.requireNonNull(binding.setCustomSharingKeywords.getText()).toString(); keywords = binding.setCustomSharingKeywords.getText().toString();
CharSequence comma_only = ","; CharSequence comma_only = ",";
CharSequence space_only = " "; CharSequence space_only = " ";
CharSequence double_space = " "; CharSequence double_space = " ";
@ -206,38 +194,38 @@ public class CustomSharingActivity extends BaseBarActivity implements OnCustomSh
String param_value = uri.getQueryParameter(param_name); String param_value = uri.getQueryParameter(param_name);
if (param_value != null) if (param_value != null)
switch (param_value) { switch (param_value) {
case "${url}" -> { case "${url}":
paramFound = true; paramFound = true;
builder.appendQueryParameter(param_name, bundle_url); builder.appendQueryParameter(param_name, bundle_url);
} break;
case "${title}" -> { case "${title}":
paramFound = true; paramFound = true;
builder.appendQueryParameter(param_name, title); builder.appendQueryParameter(param_name, title);
} break;
case "${source}" -> { case "${source}":
paramFound = true; paramFound = true;
builder.appendQueryParameter(param_name, bundle_source); builder.appendQueryParameter(param_name, bundle_source);
} break;
case "${id}" -> { case "${id}":
paramFound = true; paramFound = true;
builder.appendQueryParameter(param_name, bundle_id); builder.appendQueryParameter(param_name, bundle_id);
} break;
case "${description}" -> { case "${description}":
paramFound = true; paramFound = true;
builder.appendQueryParameter(param_name, bundle_content); builder.appendQueryParameter(param_name, bundle_content);
} break;
case "${keywords}" -> { case "${keywords}":
paramFound = true; paramFound = true;
builder.appendQueryParameter(param_name, keywords); builder.appendQueryParameter(param_name, keywords);
} break;
case "${creator}" -> { case "${creator}":
paramFound = true; paramFound = true;
builder.appendQueryParameter(param_name, bundle_creator); builder.appendQueryParameter(param_name, bundle_creator);
} break;
case "${thumbnailurl}" -> { case "${thumbnailurl}":
paramFound = true; paramFound = true;
builder.appendQueryParameter(param_name, bundle_thumbnailurl); builder.appendQueryParameter(param_name, bundle_thumbnailurl);
} break;
} }
if (!paramFound) { if (!paramFound) {
builder.appendQueryParameter(param_name, param_value); builder.appendQueryParameter(param_name, param_value);

View file

@ -15,6 +15,7 @@ package app.fedilab.android.mastodon.activities;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.BaseMainActivity.currentAccount;
import static app.fedilab.android.mastodon.activities.ComposeActivity.PICK_MEDIA; import static app.fedilab.android.mastodon.activities.ComposeActivity.PICK_MEDIA;
import android.content.ClipData; import android.content.ClipData;
@ -38,7 +39,6 @@ import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivityDirectMessageBinding; import app.fedilab.android.databinding.ActivityDirectMessageBinding;
import app.fedilab.android.mastodon.client.entities.api.Status; import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.mastodon.client.entities.app.CachedBundle;
import app.fedilab.android.mastodon.client.entities.app.StatusCache; import app.fedilab.android.mastodon.client.entities.app.StatusCache;
import app.fedilab.android.mastodon.exception.DBException; import app.fedilab.android.mastodon.exception.DBException;
import app.fedilab.android.mastodon.helper.Helper; import app.fedilab.android.mastodon.helper.Helper;
@ -53,6 +53,7 @@ public class DirectMessageActivity extends BaseActivity implements FragmentMasto
public static boolean displayCW; public static boolean displayCW;
FragmentMastodonDirectMessage currentFragment; FragmentMastodonDirectMessage currentFragment;
private Status firstMessage;
private String remote_instance; private String remote_instance;
@Override @Override
@ -71,48 +72,31 @@ public class DirectMessageActivity extends BaseActivity implements FragmentMasto
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this);
float scale = sharedpreferences.getFloat(getString(R.string.SET_FONT_SCALE), 1.1f); float scale = sharedpreferences.getFloat(getString(R.string.SET_FONT_SCALE), 1.1f);
binding.title.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18 * 1.1f / scale); binding.title.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18 * 1.1f / scale);
MastodonHelper.loadPPMastodon(binding.profilePicture, Helper.getCurrentAccount(DirectMessageActivity.this).mastodon_account);
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true);
} }
Bundle args = getIntent().getExtras(); Bundle b = getIntent().getExtras();
displayCW = sharedpreferences.getBoolean(getString(R.string.SET_EXPAND_CW), false); displayCW = sharedpreferences.getBoolean(getString(R.string.SET_EXPAND_CW), false);
if (args != null) {
long bundleId = args.getLong(Helper.ARG_INTENT_ID, -1);
new CachedBundle(DirectMessageActivity.this).getBundle(bundleId, Helper.getCurrentAccount(DirectMessageActivity.this), this::initializeAfterBundle);
} else {
initializeAfterBundle(null);
}
}
private void initializeAfterBundle(Bundle bundle) {
Status focusedStatus = null; // or other values Status focusedStatus = null; // or other values
if (bundle != null) { if (b != null) {
focusedStatus = (Status) bundle.getSerializable(Helper.ARG_STATUS); focusedStatus = (Status) b.getSerializable(Helper.ARG_STATUS);
remote_instance = bundle.getString(Helper.ARG_REMOTE_INSTANCE, null); remote_instance = b.getString(Helper.ARG_REMOTE_INSTANCE, null);
} }
if (focusedStatus == null || currentAccount == null || currentAccount.mastodon_account == null) {
if (focusedStatus == null || Helper.getCurrentAccount(DirectMessageActivity.this) == null || Helper.getCurrentAccount(DirectMessageActivity.this).mastodon_account == null) {
finish(); finish();
return; return;
} }
MastodonHelper.loadPPMastodon(binding.profilePicture, currentAccount.mastodon_account);
Bundle args = new Bundle(); Bundle bundle = new Bundle();
args.putSerializable(Helper.ARG_STATUS, focusedStatus); bundle.putSerializable(Helper.ARG_STATUS, focusedStatus);
args.putString(Helper.ARG_REMOTE_INSTANCE, remote_instance); bundle.putString(Helper.ARG_REMOTE_INSTANCE, remote_instance);
Status finalFocusedStatus = focusedStatus;
new CachedBundle(DirectMessageActivity.this).insertBundle(args, Helper.getCurrentAccount(DirectMessageActivity.this), bundleId -> {
Bundle args2 = new Bundle();
args2.putLong(Helper.ARG_INTENT_ID, bundleId);
FragmentMastodonDirectMessage FragmentMastodonDirectMessage = new FragmentMastodonDirectMessage(); FragmentMastodonDirectMessage FragmentMastodonDirectMessage = new FragmentMastodonDirectMessage();
FragmentMastodonDirectMessage.firstMessage = this; FragmentMastodonDirectMessage.firstMessage = this;
currentFragment = (FragmentMastodonDirectMessage) Helper.addFragment(getSupportFragmentManager(), R.id.nav_host_fragment_content_main, FragmentMastodonDirectMessage, args2, null, null); currentFragment = (FragmentMastodonDirectMessage) Helper.addFragment(getSupportFragmentManager(), R.id.nav_host_fragment_content_main, FragmentMastodonDirectMessage, bundle, null, null);
StatusesVM timelinesVM = new ViewModelProvider(DirectMessageActivity.this).get(StatusesVM.class); StatusesVM timelinesVM = new ViewModelProvider(DirectMessageActivity.this).get(StatusesVM.class);
timelinesVM.getStatus(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, finalFocusedStatus.id).observe(DirectMessageActivity.this, status -> { timelinesVM.getStatus(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, focusedStatus.id).observe(DirectMessageActivity.this, status -> {
if (status != null) { if (status != null) {
StatusCache statusCache = new StatusCache(); StatusCache statusCache = new StatusCache();
statusCache.instance = BaseMainActivity.currentInstance; statusCache.instance = BaseMainActivity.currentInstance;
@ -133,10 +117,9 @@ public class DirectMessageActivity extends BaseActivity implements FragmentMasto
}).start(); }).start();
} }
}); });
});
} }
@Override @Override
public void onActivityResult(int requestCode, int resultCode, Intent data) { public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data); super.onActivityResult(requestCode, resultCode, data);
@ -166,6 +149,7 @@ public class DirectMessageActivity extends BaseActivity implements FragmentMasto
@Override @Override
public void get(Status status) { public void get(Status status) {
firstMessage = status;
invalidateOptionsMenu(); invalidateOptionsMenu();
} }
} }

View file

@ -14,6 +14,7 @@ package app.fedilab.android.mastodon.activities;
* You should have received a copy of the GNU General Public License along with Fedilab; if not, * You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.BaseMainActivity.currentAccount;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
@ -44,7 +45,6 @@ import app.fedilab.android.mastodon.client.entities.api.Attachment;
import app.fedilab.android.mastodon.client.entities.api.Status; import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.mastodon.client.entities.app.StatusDraft; import app.fedilab.android.mastodon.client.entities.app.StatusDraft;
import app.fedilab.android.mastodon.exception.DBException; import app.fedilab.android.mastodon.exception.DBException;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.ui.drawer.StatusDraftAdapter; import app.fedilab.android.mastodon.ui.drawer.StatusDraftAdapter;
import app.fedilab.android.mastodon.viewmodel.mastodon.TimelinesVM; import app.fedilab.android.mastodon.viewmodel.mastodon.TimelinesVM;
@ -82,7 +82,7 @@ public class DraftActivity extends BaseActivity implements StatusDraftAdapter.Dr
getSupportActionBar().setDisplayShowHomeEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true);
} }
timelinesVM = new ViewModelProvider(DraftActivity.this).get(TimelinesVM.class); timelinesVM = new ViewModelProvider(DraftActivity.this).get(TimelinesVM.class);
timelinesVM.getDrafts(Helper.getCurrentAccount(DraftActivity.this)) timelinesVM.getDrafts(currentAccount)
.observe(DraftActivity.this, this::initializeDraftView); .observe(DraftActivity.this, this::initializeDraftView);
} }
@ -178,7 +178,7 @@ public class DraftActivity extends BaseActivity implements StatusDraftAdapter.Dr
super.onResume(); super.onResume();
//We need to check if drafts changed (ie when coming back from the compose activity) //We need to check if drafts changed (ie when coming back from the compose activity)
if (statusDrafts != null && timelinesVM != null) { if (statusDrafts != null && timelinesVM != null) {
timelinesVM.getDrafts(Helper.getCurrentAccount(DraftActivity.this)) timelinesVM.getDrafts(currentAccount)
.observe(DraftActivity.this, this::updateDrafts); .observe(DraftActivity.this, this::updateDrafts);
} }
} }

View file

@ -14,10 +14,9 @@ package app.fedilab.android.mastodon.activities;
* You should have received a copy of the GNU General Public License along with Fedilab; if not, * You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.BaseMainActivity.currentAccount;
import static app.fedilab.android.BaseMainActivity.instanceInfo; import static app.fedilab.android.BaseMainActivity.instanceInfo;
import android.annotation.SuppressLint;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
@ -27,34 +26,27 @@ import android.text.Html;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.textfield.TextInputEditText; import com.google.android.material.textfield.TextInputEditText;
import java.util.ArrayList;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Objects;
import app.fedilab.android.BaseMainActivity; import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.BuildConfig;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.databinding.AccountFeaturedHashtagItemBinding;
import app.fedilab.android.databinding.AccountFieldItemBinding; import app.fedilab.android.databinding.AccountFieldItemBinding;
import app.fedilab.android.databinding.ActivityEditProfileBinding; import app.fedilab.android.databinding.ActivityEditProfileBinding;
import app.fedilab.android.mastodon.client.entities.api.Account; import app.fedilab.android.mastodon.client.entities.api.Account;
import app.fedilab.android.mastodon.client.entities.api.FeaturedTag;
import app.fedilab.android.mastodon.client.entities.api.Field; import app.fedilab.android.mastodon.client.entities.api.Field;
import app.fedilab.android.mastodon.client.entities.api.Tag;
import app.fedilab.android.mastodon.client.entities.app.CachedBundle;
import app.fedilab.android.mastodon.exception.DBException; import app.fedilab.android.mastodon.exception.DBException;
import app.fedilab.android.mastodon.helper.Helper; import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.helper.MastodonHelper; import app.fedilab.android.mastodon.helper.MastodonHelper;
@ -67,10 +59,7 @@ public class EditProfileActivity extends BaseBarActivity {
public static final int PICK_MEDIA_HEADER = 5706; public static final int PICK_MEDIA_HEADER = 5706;
private ActivityEditProfileBinding binding; private ActivityEditProfileBinding binding;
private AccountsVM accountsVM; private AccountsVM accountsVM;
private static final int MAX_FIELDS = 4;
private static final int MAX_FEATURED_HASHTAGS = 10;
@SuppressLint("ClickableViewAccessibility")
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@ -81,132 +70,62 @@ public class EditProfileActivity extends BaseBarActivity {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
} }
binding.scrollContainer.setOnTouchListener((v, event) -> { new ViewModelProvider(EditProfileActivity.this).get(AccountsVM.class).getConnectedAccount(BaseMainActivity.currentInstance, BaseMainActivity.currentToken)
binding.bio.getParent().requestDisallowInterceptTouchEvent(false);
return false;
});
binding.bio.setOnTouchListener((v, event) -> {
binding.bio.getParent().requestDisallowInterceptTouchEvent(true);
return false;
});
accountsVM = new ViewModelProvider(EditProfileActivity.this).get(AccountsVM.class);
accountsVM.getConnectedAccount(BaseMainActivity.currentInstance, BaseMainActivity.currentToken)
.observe(EditProfileActivity.this, account -> { .observe(EditProfileActivity.this, account -> {
if (account != null) { if (account != null) {
Helper.setCurrentAccountMastodonAccount(EditProfileActivity.this, account); currentAccount.mastodon_account = account;
initializeView(); initializeView();
} else { } else {
Helper.sendToastMessage(getApplication(), Helper.RECEIVE_TOAST_TYPE_ERROR, getString(R.string.toast_error)); Helper.sendToastMessage(getApplication(), Helper.RECEIVE_TOAST_TYPE_ERROR, getString(R.string.toast_error));
} }
}); });
} }
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
private void initializeView() { private void initializeView() {
//Hydrate values //Hydrate values
MastodonHelper.loadProfileMediaMastodon(EditProfileActivity.this, binding.bannerPp, Helper.getCurrentAccount(EditProfileActivity.this).mastodon_account, MastodonHelper.MediaAccountType.HEADER); MastodonHelper.loadProfileMediaMastodon(EditProfileActivity.this, binding.bannerPp, currentAccount.mastodon_account, MastodonHelper.MediaAccountType.HEADER);
MastodonHelper.loadPPMastodon(binding.accountPp, Helper.getCurrentAccount(EditProfileActivity.this).mastodon_account); MastodonHelper.loadPPMastodon(binding.accountPp, currentAccount.mastodon_account);
binding.displayName.setText(Helper.getCurrentAccount(EditProfileActivity.this).mastodon_account.display_name); binding.displayName.setText(currentAccount.mastodon_account.display_name);
binding.acct.setText(String.format(Locale.getDefault(), "%s@%s", Helper.getCurrentAccount(EditProfileActivity.this).mastodon_account.acct, BaseMainActivity.currentInstance)); binding.acct.setText(String.format(Locale.getDefault(), "%s@%s", currentAccount.mastodon_account.acct, BaseMainActivity.currentInstance));
String bio; String bio;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
bio = Html.fromHtml(Helper.getCurrentAccount(EditProfileActivity.this).mastodon_account.note, Html.FROM_HTML_MODE_LEGACY).toString(); bio = Html.fromHtml(currentAccount.mastodon_account.note, Html.FROM_HTML_MODE_LEGACY).toString();
else else
bio = Html.fromHtml(Helper.getCurrentAccount(EditProfileActivity.this).mastodon_account.note).toString(); bio = Html.fromHtml(currentAccount.mastodon_account.note).toString();
binding.bio.setText(bio); binding.bio.setText(bio);
if (currentAccount.mastodon_account.source != null) {
accountsVM.getFeaturedTagsSuggestions(BaseMainActivity.currentInstance, BaseMainActivity.currentToken).observe(this, featuredTags -> { binding.sensitive.setChecked(currentAccount.mastodon_account.source.sensitive);
StringBuilder text = new StringBuilder(getString(R.string.no_feature_hashtag_suggestion)); switch (currentAccount.mastodon_account.source.privacy) {
if(featuredTags != null && !featuredTags.isEmpty()) { case "public":
text = new StringBuilder(); binding.visibilityPublic.setChecked(true);
for (Tag tag : featuredTags) { break;
text.append(String.format("#%s ", tag.name)); case "unlisted":
} binding.visibilityUnlisted.setChecked(true);
} break;
binding.featuredHashtagsSuggestions.setText(text); case "private":
}); binding.visibilityPrivate.setChecked(true);
accountsVM.getAccountFeaturedTags(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, Helper.getCurrentAccount(EditProfileActivity.this).mastodon_account.id).observe(this, featuredTags -> { break;
if(featuredTags != null && !featuredTags.isEmpty()) { case "direct":
for (FeaturedTag featuredTag : featuredTags) { binding.visibilityDirect.setChecked(true);
AccountFeaturedHashtagItemBinding featuredHashtagItemBinding = AccountFeaturedHashtagItemBinding.inflate(getLayoutInflater()); break;
featuredHashtagItemBinding.name.setText(featuredTag.name);
featuredHashtagItemBinding.remove.setOnClickListener(v -> {
AlertDialog.Builder deleteConfirm = new MaterialAlertDialogBuilder(EditProfileActivity.this);
deleteConfirm.setTitle(getString(R.string.delete_featured_hashtag));
deleteConfirm.setMessage(getString(R.string.delete_featured_hashtag_confirm));
deleteConfirm.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
deleteConfirm.setPositiveButton(R.string.delete, (dialog, which) -> {
((ViewGroup)featuredHashtagItemBinding.getRoot().getParent()).removeView(featuredHashtagItemBinding.getRoot());
if (binding.featuredHashtagsContainer.getChildCount() >= MAX_FEATURED_HASHTAGS) {
binding.addFeaturedHashtags.setVisibility(View.GONE);
} else {
binding.addFeaturedHashtags.setVisibility(View.VISIBLE);
}
dialog.dismiss();
});
deleteConfirm.create().show();
});
binding.featuredHashtagsContainer.addView(featuredHashtagItemBinding.getRoot());
}
}
binding.addFeaturedHashtags.setOnClickListener(view -> {
AccountFeaturedHashtagItemBinding featuredHashtagItemBinding = AccountFeaturedHashtagItemBinding.inflate(getLayoutInflater());
featuredHashtagItemBinding.remove.setOnClickListener(v -> {
AlertDialog.Builder deleteConfirm = new MaterialAlertDialogBuilder(EditProfileActivity.this);
deleteConfirm.setTitle(getString(R.string.delete_featured_hashtag));
deleteConfirm.setMessage(getString(R.string.delete_featured_hashtag_confirm));
deleteConfirm.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
deleteConfirm.setPositiveButton(R.string.delete, (dialog, which) -> {
((ViewGroup)featuredHashtagItemBinding.getRoot().getParent()).removeView(featuredHashtagItemBinding.getRoot());
if (binding.featuredHashtagsContainer.getChildCount() >= MAX_FEATURED_HASHTAGS) {
binding.addFeaturedHashtags.setVisibility(View.GONE);
} else {
binding.addFeaturedHashtags.setVisibility(View.VISIBLE);
}
dialog.dismiss();
});
deleteConfirm.create().show();
});
binding.featuredHashtagsContainer.addView(featuredHashtagItemBinding.getRoot());
if (binding.featuredHashtagsContainer.getChildCount() >= MAX_FEATURED_HASHTAGS) {
binding.addFeaturedHashtags.setVisibility(View.GONE);
} else {
binding.addFeaturedHashtags.setVisibility(View.VISIBLE);
}
});
});
if (Helper.getCurrentAccount(EditProfileActivity.this).mastodon_account.source != null) {
binding.sensitive.setChecked(Helper.getCurrentAccount(EditProfileActivity.this).mastodon_account.source.sensitive);
switch (Helper.getCurrentAccount(EditProfileActivity.this).mastodon_account.source.privacy) {
case "public" -> binding.visibilityPublic.setChecked(true);
case "unlisted" -> binding.visibilityUnlisted.setChecked(true);
case "private" -> binding.visibilityPrivate.setChecked(true);
case "direct" -> binding.visibilityDirect.setChecked(true);
} }
} else { } else {
binding.sensitive.setVisibility(View.GONE); binding.sensitive.setVisibility(View.GONE);
binding.visibilityGroup.setVisibility(View.GONE); binding.visibilityGroup.setVisibility(View.GONE);
} }
binding.bot.setChecked(Helper.getCurrentAccount(EditProfileActivity.this).mastodon_account.bot); binding.bot.setChecked(currentAccount.mastodon_account.bot);
binding.discoverable.setChecked(Helper.getCurrentAccount(EditProfileActivity.this).mastodon_account.discoverable); binding.discoverable.setChecked(currentAccount.mastodon_account.discoverable);
if (Helper.getCurrentAccount(EditProfileActivity.this).mastodon_account.locked) { if (currentAccount.mastodon_account.locked) {
binding.locked.setChecked(true); binding.locked.setChecked(true);
} else { } else {
binding.unlocked.setChecked(true); binding.unlocked.setChecked(true);
} }
List<Field> fields = Helper.getCurrentAccount(EditProfileActivity.this).mastodon_account.fields; List<Field> fields = currentAccount.mastodon_account.fields;
if (fields != null && !fields.isEmpty()) { if (fields != null && fields.size() > 0) {
for (Field field : fields) { for (Field field : fields) {
AccountFieldItemBinding fieldItemBinding = AccountFieldItemBinding.inflate(getLayoutInflater()); AccountFieldItemBinding fieldItemBinding = AccountFieldItemBinding.inflate(getLayoutInflater());
fieldItemBinding.name.setText(field.name); fieldItemBinding.name.setText(field.name);
@ -222,11 +141,11 @@ public class EditProfileActivity extends BaseBarActivity {
deleteConfirm.setMessage(getString(R.string.delete_field_confirm)); deleteConfirm.setMessage(getString(R.string.delete_field_confirm));
deleteConfirm.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss()); deleteConfirm.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
deleteConfirm.setPositiveButton(R.string.delete, (dialog, which) -> { deleteConfirm.setPositiveButton(R.string.delete, (dialog, which) -> {
((ViewGroup)fieldItemBinding.getRoot().getParent()).removeView(fieldItemBinding.getRoot()); binding.fieldsContainer.removeView(fieldItemBinding.getRoot());
if (binding.fieldsContainer.getChildCount() >= MAX_FIELDS) { if (binding.fieldsContainer.getChildCount() < 4) {
binding.addField.setVisibility(View.GONE); binding.fieldsContainer.setVisibility(View.VISIBLE);
} else { } else {
binding.addField.setVisibility(View.VISIBLE); binding.fieldsContainer.setVisibility(View.GONE);
} }
dialog.dismiss(); dialog.dismiss();
}); });
@ -234,11 +153,7 @@ public class EditProfileActivity extends BaseBarActivity {
}); });
binding.fieldsContainer.addView(fieldItemBinding.getRoot()); binding.fieldsContainer.addView(fieldItemBinding.getRoot());
} }
if (binding.fieldsContainer.getChildCount() >= MAX_FIELDS) {
binding.addField.setVisibility(View.GONE);
} else {
binding.addField.setVisibility(View.VISIBLE);
}
} }
binding.addField.setOnClickListener(view -> { binding.addField.setOnClickListener(view -> {
AccountFieldItemBinding fieldItemBinding = AccountFieldItemBinding.inflate(getLayoutInflater()); AccountFieldItemBinding fieldItemBinding = AccountFieldItemBinding.inflate(getLayoutInflater());
@ -248,24 +163,25 @@ public class EditProfileActivity extends BaseBarActivity {
deleteConfirm.setMessage(getString(R.string.delete_field_confirm)); deleteConfirm.setMessage(getString(R.string.delete_field_confirm));
deleteConfirm.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss()); deleteConfirm.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
deleteConfirm.setPositiveButton(R.string.delete, (dialog, which) -> { deleteConfirm.setPositiveButton(R.string.delete, (dialog, which) -> {
((ViewGroup)fieldItemBinding.getRoot().getParent()).removeView(fieldItemBinding.getRoot()); binding.fieldsContainer.removeView(fieldItemBinding.getRoot());
if (binding.fieldsContainer.getChildCount() >= MAX_FIELDS) { if (binding.fieldsContainer.getChildCount() < 4) {
binding.addField.setVisibility(View.GONE); binding.fieldsContainer.setVisibility(View.VISIBLE);
} else { } else {
binding.addField.setVisibility(View.VISIBLE); binding.fieldsContainer.setVisibility(View.GONE);
} }
dialog.dismiss(); dialog.dismiss();
}); });
deleteConfirm.create().show(); deleteConfirm.create().show();
}); });
binding.fieldsContainer.addView(fieldItemBinding.getRoot()); binding.fieldsContainer.addView(fieldItemBinding.getRoot());
if (binding.fieldsContainer.getChildCount() >= 4) {
if (binding.fieldsContainer.getChildCount() >= MAX_FIELDS) {
binding.addField.setVisibility(View.GONE); binding.addField.setVisibility(View.GONE);
} else {
binding.addField.setVisibility(View.VISIBLE);
} }
}); });
//Actions with the activity
accountsVM = new ViewModelProvider(EditProfileActivity.this).get(AccountsVM.class);
binding.headerSelect.setOnClickListener(view -> startActivityForResult(prepareIntent(), EditProfileActivity.PICK_MEDIA_HEADER)); binding.headerSelect.setOnClickListener(view -> startActivityForResult(prepareIntent(), EditProfileActivity.PICK_MEDIA_HEADER));
binding.avatarSelect.setOnClickListener(view -> startActivityForResult(prepareIntent(), EditProfileActivity.PICK_MEDIA_AVATAR)); binding.avatarSelect.setOnClickListener(view -> startActivityForResult(prepareIntent(), EditProfileActivity.PICK_MEDIA_AVATAR));
@ -288,11 +204,11 @@ public class EditProfileActivity extends BaseBarActivity {
if (account != null) { if (account != null) {
sendBroadCast(account); sendBroadCast(account);
binding.avatarProgress.setVisibility(View.GONE); binding.avatarProgress.setVisibility(View.GONE);
Helper.setCurrentAccountMastodonAccount(EditProfileActivity.this, account); currentAccount.mastodon_account = account;
Helper.recreateMainActivity(EditProfileActivity.this); Helper.recreateMainActivity(EditProfileActivity.this);
new Thread(() -> { new Thread(() -> {
try { try {
new app.fedilab.android.mastodon.client.entities.app.Account(EditProfileActivity.this).insertOrUpdate(Helper.getCurrentAccount(EditProfileActivity.this)); new app.fedilab.android.mastodon.client.entities.app.Account(EditProfileActivity.this).insertOrUpdate(currentAccount);
} catch (DBException e) { } catch (DBException e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -316,10 +232,10 @@ public class EditProfileActivity extends BaseBarActivity {
if (account != null) { if (account != null) {
sendBroadCast(account); sendBroadCast(account);
binding.headerProgress.setVisibility(View.GONE); binding.headerProgress.setVisibility(View.GONE);
Helper.setCurrentAccountMastodonAccount(EditProfileActivity.this, account); currentAccount.mastodon_account = account;
new Thread(() -> { new Thread(() -> {
try { try {
new app.fedilab.android.mastodon.client.entities.app.Account(EditProfileActivity.this).insertOrUpdate(Helper.getCurrentAccount(EditProfileActivity.this)); new app.fedilab.android.mastodon.client.entities.app.Account(EditProfileActivity.this).insertOrUpdate(currentAccount);
} catch (DBException e) { } catch (DBException e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -333,18 +249,12 @@ public class EditProfileActivity extends BaseBarActivity {
} }
private void sendBroadCast(Account account) { private void sendBroadCast(Account account) {
Bundle args = new Bundle(); Bundle b = new Bundle();
args.putBoolean(Helper.RECEIVE_REDRAW_PROFILE, true); b.putBoolean(Helper.RECEIVE_REDRAW_PROFILE, true);
args.putSerializable(Helper.ARG_ACCOUNT, account); b.putSerializable(Helper.ARG_ACCOUNT, account);
new CachedBundle(EditProfileActivity.this).insertBundle(args, Helper.getCurrentAccount(EditProfileActivity.this), bundleId -> {
Bundle bundle = new Bundle();
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
Intent intentBD = new Intent(Helper.BROADCAST_DATA); Intent intentBD = new Intent(Helper.BROADCAST_DATA);
intentBD.putExtras(bundle); intentBD.putExtras(b);
intentBD.setPackage(BuildConfig.APPLICATION_ID); LocalBroadcastManager.getInstance(EditProfileActivity.this).sendBroadcast(intentBD);
sendBroadcast(intentBD);
});
} }
private Intent prepareIntent() { private Intent prepareIntent() {
@ -354,7 +264,7 @@ public class EditProfileActivity extends BaseBarActivity {
intent.setType("*/*"); intent.setType("*/*");
String[] mimetypes; String[] mimetypes;
long max_size = -1; long max_size = -1;
if (!instanceInfo.getMimeTypeImage().isEmpty()) { if (instanceInfo.getMimeTypeImage().size() > 0) {
mimetypes = instanceInfo.getMimeTypeImage().toArray(new String[0]); mimetypes = instanceInfo.getMimeTypeImage().toArray(new String[0]);
max_size = instanceInfo.configuration.media_attachments.image_size_limit; max_size = instanceInfo.configuration.media_attachments.image_size_limit;
} else { } else {
@ -390,23 +300,13 @@ public class EditProfileActivity extends BaseBarActivity {
LinkedHashMap<Integer, Field.FieldParams> fields = new LinkedHashMap<>(); LinkedHashMap<Integer, Field.FieldParams> fields = new LinkedHashMap<>();
for (int i = 0; i < binding.fieldsContainer.getChildCount(); i++) { for (int i = 0; i < binding.fieldsContainer.getChildCount(); i++) {
Field.FieldParams field = new Field.FieldParams(); Field.FieldParams field = new Field.FieldParams();
field.name = Objects.requireNonNull(((TextInputEditText) binding.fieldsContainer.getChildAt(i).findViewById(R.id.name)).getText()).toString().trim(); field.name = ((TextInputEditText) binding.fieldsContainer.getChildAt(i).findViewById(R.id.name)).getText().toString().trim();
field.value = Objects.requireNonNull(((TextInputEditText) binding.fieldsContainer.getChildAt(i).findViewById(R.id.value)).getText()).toString().trim(); field.value = ((TextInputEditText) binding.fieldsContainer.getChildAt(i).findViewById(R.id.value)).getText().toString().trim();
fields.put(i, field); fields.put(i, field);
} }
return fields; return fields;
} }
List<String> getFeaturedHashtags() {
List<String> featuredHashtags = new ArrayList<>();
for (int i = 0; i < binding.featuredHashtagsContainer.getChildCount(); i++) {
String name = Objects.requireNonNull(((TextInputEditText) binding.featuredHashtagsContainer.getChildAt(i).findViewById(R.id.name)).getText()).toString().trim();
featuredHashtags.add(name);
}
return featuredHashtags;
}
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) { if (item.getItemId() == android.R.id.home) {
@ -416,21 +316,20 @@ public class EditProfileActivity extends BaseBarActivity {
accountsVM.updateCredentials(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, accountsVM.updateCredentials(BaseMainActivity.currentInstance, BaseMainActivity.currentToken,
binding.discoverable.isChecked(), binding.discoverable.isChecked(),
binding.bot.isChecked(), binding.bot.isChecked(),
Objects.requireNonNull(binding.displayName.getText()).toString().trim(), binding.displayName.getText().toString().trim(),
Objects.requireNonNull(binding.bio.getText()).toString(), binding.bio.getText().toString(),
binding.locked.isChecked(), binding.locked.isChecked(),
getPrivacy(), getPrivacy(),
binding.sensitive.isChecked(), binding.sensitive.isChecked(),
null, null,
getFields(), getFields()
getFeaturedHashtags()
) )
.observe(EditProfileActivity.this, account -> { .observe(EditProfileActivity.this, account -> {
if (account != null) { if (account != null) {
Helper.setCurrentAccountMastodonAccount(EditProfileActivity.this, account); currentAccount.mastodon_account = account;
new Thread(() -> { new Thread(() -> {
try { try {
new app.fedilab.android.mastodon.client.entities.app.Account(EditProfileActivity.this).insertOrUpdate(Helper.getCurrentAccount(EditProfileActivity.this)); new app.fedilab.android.mastodon.client.entities.app.Account(EditProfileActivity.this).insertOrUpdate(currentAccount);
sendBroadCast(account); sendBroadCast(account);
} catch (DBException e) { } catch (DBException e) {
e.printStackTrace(); e.printStackTrace();

View file

@ -75,13 +75,27 @@ public class FilterActivity extends BaseBarActivity implements FilterAdapter.Del
@Override @Override
public void onItemSelected(AdapterView<?> parent1, View view, int position1, long id) { public void onItemSelected(AdapterView<?> parent1, View view, int position1, long id) {
switch (position1) { switch (position1) {
case 0 -> expire[0] = -1; case 0:
case 1 -> expire[0] = 1800; expire[0] = -1;
case 2 -> expire[0] = 3600; break;
case 3 -> expire[0] = 21600; case 1:
case 4 -> expire[0] = 43200; expire[0] = 1800;
case 5 -> expire[0] = 86400; break;
case 6 -> expire[0] = 604800; case 2:
expire[0] = 3600;
break;
case 3:
expire[0] = 21600;
break;
case 4:
expire[0] = 43200;
break;
case 5:
expire[0] = 86400;
break;
case 6:
expire[0] = 604800;
break;
} }
} }
@ -114,12 +128,21 @@ public class FilterActivity extends BaseBarActivity implements FilterAdapter.Del
if (filter.context != null) { if (filter.context != null) {
for (String val : filter.context) { for (String val : filter.context) {
switch (val) { switch (val) {
case "home" -> popupAddFilterBinding.contextHome.setChecked(true); case "home":
case "public" -> popupAddFilterBinding.contextPublic.setChecked(true); popupAddFilterBinding.contextHome.setChecked(true);
case "notifications" -> break;
case "public":
popupAddFilterBinding.contextPublic.setChecked(true);
break;
case "notifications":
popupAddFilterBinding.contextNotification.setChecked(true); popupAddFilterBinding.contextNotification.setChecked(true);
case "thread" -> popupAddFilterBinding.contextConversation.setChecked(true); break;
case "account" -> popupAddFilterBinding.contextProfiles.setChecked(true); case "thread":
popupAddFilterBinding.contextConversation.setChecked(true);
break;
case "account":
popupAddFilterBinding.contextProfiles.setChecked(true);
break;
} }
} }
} }
@ -170,7 +193,7 @@ public class FilterActivity extends BaseBarActivity implements FilterAdapter.Del
canBeSent = false; canBeSent = false;
} }
} }
if (Objects.requireNonNull(popupAddFilterBinding.addTitle.getText()).toString().trim().isEmpty()) { if (popupAddFilterBinding.addTitle.getText().toString().trim().isEmpty()) {
popupAddFilterBinding.addTitle.setError(context.getString(R.string.cannot_be_empty)); popupAddFilterBinding.addTitle.setError(context.getString(R.string.cannot_be_empty));
canBeSent = false; canBeSent = false;
} }

View file

@ -94,8 +94,11 @@ public class FollowRequestActivity extends BaseActivity {
binding.loadingNextAccounts.setVisibility(View.GONE); binding.loadingNextAccounts.setVisibility(View.GONE);
if (accountList != null && accounts != null && accounts.accounts != null && accounts.accounts.size() > 0) { if (accountList != null && accounts != null && accounts.accounts != null && accounts.accounts.size() > 0) {
int startId = 0;
//There are some statuses present in the timeline //There are some statuses present in the timeline
int startId = accountList.size(); if (accountList.size() > 0) {
startId = accountList.size();
}
flagLoading = accounts.pagination.max_id == null; flagLoading = accounts.pagination.max_id == null;
accountList.addAll(accounts.accounts); accountList.addAll(accounts.accounts);
max_id = accounts.pagination.max_id; max_id = accounts.pagination.max_id;

View file

@ -21,7 +21,6 @@ import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import androidx.activity.OnBackPressedCallback;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
@ -32,9 +31,6 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.ArrayList; 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.BaseMainActivity;
import app.fedilab.android.R; import app.fedilab.android.R;
@ -77,7 +73,6 @@ public class FollowedTagActivity extends BaseBarActivity implements FollowedTagA
.observe(FollowedTagActivity.this, tags -> { .observe(FollowedTagActivity.this, tags -> {
if (tags != null && tags.tags != null && tags.tags.size() > 0) { if (tags != null && tags.tags != null && tags.tags.size() > 0) {
tagList = new ArrayList<>(tags.tags); tagList = new ArrayList<>(tags.tags);
sortAsc(tagList);
followedTagAdapter = new FollowedTagAdapter(tagList); followedTagAdapter = new FollowedTagAdapter(tagList);
followedTagAdapter.actionOnTag = this; followedTagAdapter.actionOnTag = this;
binding.notContent.setVisibility(View.GONE); binding.notContent.setVisibility(View.GONE);
@ -87,29 +82,12 @@ public class FollowedTagActivity extends BaseBarActivity implements FollowedTagA
binding.notContent.setVisibility(View.VISIBLE); binding.notContent.setVisibility(View.VISIBLE);
} }
}); });
getOnBackPressedDispatcher().addCallback(new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
if (canGoBack) {
canGoBack = false;
ThemeHelper.slideViewsToRight(binding.fragmentContainer, binding.recyclerView, () -> {
if (fragmentMastodonTimeline != null) {
fragmentMastodonTimeline.onDestroyView();
}
});
setTitle(R.string.followed_tags);
invalidateOptionsMenu();
} else {
finish();
}
}
});
} }
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) { if (item.getItemId() == android.R.id.home) {
getOnBackPressedDispatcher().onBackPressed(); onBackPressed();
return true; return true;
} else if (item.getItemId() == R.id.action_unfollow && tag != null) { } else if (item.getItemId() == R.id.action_unfollow && tag != null) {
AlertDialog.Builder alt_bld = new MaterialAlertDialogBuilder(FollowedTagActivity.this); AlertDialog.Builder alt_bld = new MaterialAlertDialogBuilder(FollowedTagActivity.this);
@ -149,11 +127,6 @@ public class FollowedTagActivity extends BaseBarActivity implements FollowedTagA
dialogBuilder.setView(popupAddFollowedTagtBinding.getRoot()); dialogBuilder.setView(popupAddFollowedTagtBinding.getRoot());
popupAddFollowedTagtBinding.addTag.setFilters(new InputFilter[]{new InputFilter.LengthFilter(255)}); popupAddFollowedTagtBinding.addTag.setFilters(new InputFilter[]{new InputFilter.LengthFilter(255)});
dialogBuilder.setPositiveButton(R.string.validate, (dialog, id) -> { dialogBuilder.setPositiveButton(R.string.validate, (dialog, id) -> {
String name = Objects.requireNonNull(popupAddFollowedTagtBinding.addTag.getText()).toString().trim();
if (tagList != null && 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) { if (popupAddFollowedTagtBinding.addTag.getText() != null && popupAddFollowedTagtBinding.addTag.getText().toString().trim().length() > 0) {
tagVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, popupAddFollowedTagtBinding.addTag.getText().toString().trim()) tagVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, popupAddFollowedTagtBinding.addTag.getText().toString().trim())
.observe(FollowedTagActivity.this, newTag -> { .observe(FollowedTagActivity.this, newTag -> {
@ -170,10 +143,8 @@ public class FollowedTagActivity extends BaseBarActivity implements FollowedTagA
if (newTag != null) { if (newTag != null) {
tagList.add(0, newTag); tagList.add(0, newTag);
followedTagAdapter.notifyItemInserted(0); followedTagAdapter.notifyItemInserted(0);
sortAsc(tagList);
followedTagAdapter.notifyItemRangeChanged(0, tagList.size());
} else { } else {
Toasty.error(FollowedTagActivity.this, getString(R.string.not_valid_tag_name), Toasty.LENGTH_LONG).show(); Toasty.error(FollowedTagActivity.this, getString(R.string.toast_feature_not_supported), Toasty.LENGTH_LONG).show();
} }
}); });
dialog.dismiss(); dialog.dismiss();
@ -188,9 +159,6 @@ public class FollowedTagActivity extends BaseBarActivity implements FollowedTagA
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
private void sortAsc(List<Tag> tagList) {
Collections.sort(tagList, (obj1, obj2) -> obj1.name.compareToIgnoreCase(obj2.name));
}
@Override @Override
public boolean onCreateOptionsMenu(@NonNull Menu menu) { public boolean onCreateOptionsMenu(@NonNull Menu menu) {
@ -202,11 +170,27 @@ public class FollowedTagActivity extends BaseBarActivity implements FollowedTagA
return true; return true;
} }
@Override
public void onBackPressed() {
if (canGoBack) {
canGoBack = false;
ThemeHelper.slideViewsToRight(binding.fragmentContainer, binding.recyclerView, () -> {
if (fragmentMastodonTimeline != null) {
fragmentMastodonTimeline.onDestroyView();
}
});
setTitle(R.string.followed_tags);
invalidateOptionsMenu();
} else {
super.onBackPressed();
}
}
@Override @Override
public void click(Tag tag) { public void click(Tag tag) {
this.tag = tag; this.tag = tag;
canGoBack = true; canGoBack = true;
ThemeHelper.slideViewsToLeft(binding.recyclerView, binding.fragmentContainer, () -> {
fragmentMastodonTimeline = new FragmentMastodonTimeline(); fragmentMastodonTimeline = new FragmentMastodonTimeline();
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.putSerializable(Helper.ARG_SEARCH_KEYWORD, tag.name); bundle.putSerializable(Helper.ARG_SEARCH_KEYWORD, tag.name);
@ -219,8 +203,6 @@ public class FollowedTagActivity extends BaseBarActivity implements FollowedTagA
fragmentTransaction.replace(R.id.fragment_container, fragmentMastodonTimeline); fragmentTransaction.replace(R.id.fragment_container, fragmentMastodonTimeline);
fragmentTransaction.commit(); fragmentTransaction.commit();
invalidateOptionsMenu(); invalidateOptionsMenu();
ThemeHelper.slideViewsToLeft(binding.recyclerView, binding.fragmentContainer, () -> {
}); });
} }
} }

View file

@ -15,6 +15,8 @@ package app.fedilab.android.mastodon.activities;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.BaseMainActivity.currentAccount;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
@ -26,6 +28,7 @@ import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
@ -33,13 +36,11 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import app.fedilab.android.BaseMainActivity; import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.BuildConfig;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.activities.MainActivity; import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.databinding.ActivityHashtagBinding; import app.fedilab.android.databinding.ActivityHashtagBinding;
import app.fedilab.android.mastodon.client.entities.api.Filter; import app.fedilab.android.mastodon.client.entities.api.Filter;
import app.fedilab.android.mastodon.client.entities.api.Status; import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.mastodon.client.entities.app.CachedBundle;
import app.fedilab.android.mastodon.client.entities.app.Pinned; import app.fedilab.android.mastodon.client.entities.app.Pinned;
import app.fedilab.android.mastodon.client.entities.app.PinnedTimeline; import app.fedilab.android.mastodon.client.entities.app.PinnedTimeline;
import app.fedilab.android.mastodon.client.entities.app.StatusDraft; import app.fedilab.android.mastodon.client.entities.app.StatusDraft;
@ -68,31 +69,20 @@ public class HashTagActivity extends BaseActivity {
private Filter.KeywordsAttributes keyword; private Filter.KeywordsAttributes keyword;
private PinnedTimeline pinnedTimeline; private PinnedTimeline pinnedTimeline;
private Pinned pinned; private Pinned pinned;
private ActivityHashtagBinding binding;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
binding = ActivityHashtagBinding.inflate(getLayoutInflater()); ActivityHashtagBinding binding = ActivityHashtagBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
Bundle args = getIntent().getExtras();
if (args != null) {
long bundleId = args.getLong(Helper.ARG_INTENT_ID, -1);
new CachedBundle(HashTagActivity.this).getBundle(bundleId, Helper.getCurrentAccount(HashTagActivity.this), this::initializeAfterBundle);
} else {
initializeAfterBundle(null);
}
}
private void initializeAfterBundle(Bundle bundle) { setContentView(binding.getRoot());
if (bundle != null) { Bundle b = getIntent().getExtras();
tag = bundle.getString(Helper.ARG_SEARCH_KEYWORD, null); if (b != null) {
tag = b.getString(Helper.ARG_SEARCH_KEYWORD, null);
} }
if (tag == null) { if (tag == null)
finish(); finish();
return;
}
pinnedTag = null; pinnedTag = null;
followedTag = null; followedTag = null;
mutedTag = null; mutedTag = null;
@ -156,10 +146,10 @@ public class HashTagActivity extends BaseActivity {
invalidateOptionsMenu(); invalidateOptionsMenu();
} }
Bundle bundleFragment = new Bundle(); Bundle bundle = new Bundle();
bundleFragment.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.TAG); bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.TAG);
bundleFragment.putString(Helper.ARG_SEARCH_KEYWORD, tag); bundle.putString(Helper.ARG_SEARCH_KEYWORD, tag);
Helper.addFragment(getSupportFragmentManager(), R.id.nav_host_fragment_tags, new FragmentMastodonTimeline(), bundleFragment, null, null); Helper.addFragment(getSupportFragmentManager(), R.id.nav_host_fragment_tags, new FragmentMastodonTimeline(), bundle, null, null);
binding.compose.setOnClickListener(v -> { binding.compose.setOnClickListener(v -> {
Intent intentToot = new Intent(HashTagActivity.this, ComposeActivity.class); Intent intentToot = new Intent(HashTagActivity.this, ComposeActivity.class);
StatusDraft statusDraft = new StatusDraft(); StatusDraft statusDraft = new StatusDraft();
@ -168,15 +158,11 @@ public class HashTagActivity extends BaseActivity {
List<Status> statuses = new ArrayList<>(); List<Status> statuses = new ArrayList<>();
statuses.add(status); statuses.add(status);
statusDraft.statusDraftList = statuses; statusDraft.statusDraftList = statuses;
Bundle args = new Bundle(); Bundle _b = new Bundle();
args.putSerializable(Helper.ARG_STATUS_DRAFT, statusDraft); _b.putSerializable(Helper.ARG_STATUS_DRAFT, statusDraft);
new CachedBundle(HashTagActivity.this).insertBundle(args, Helper.getCurrentAccount(HashTagActivity.this), bundleId -> { intentToot.putExtras(_b);
Bundle bundleCached = new Bundle();
bundleCached.putLong(Helper.ARG_INTENT_ID, bundleId);
intentToot.putExtras(bundleCached);
startActivity(intentToot); startActivity(intentToot);
}); });
});
} }
@Override @Override
@ -202,24 +188,18 @@ public class HashTagActivity extends BaseActivity {
} }
pinnedTag = false; pinnedTag = false;
invalidateOptionsMenu(); invalidateOptionsMenu();
Bundle args = new Bundle(); Bundle b = new Bundle();
args.putBoolean(Helper.RECEIVE_REDRAW_TOPBAR, true); b.putBoolean(Helper.RECEIVE_REDRAW_TOPBAR, true);
Intent intentBD = new Intent(Helper.BROADCAST_DATA); Intent intentBD = new Intent(Helper.BROADCAST_DATA);
new CachedBundle(HashTagActivity.this).insertBundle(args, Helper.getCurrentAccount(HashTagActivity.this), bundleId -> { intentBD.putExtras(b);
Bundle bundle = new Bundle(); LocalBroadcastManager.getInstance(HashTagActivity.this).sendBroadcast(intentBD);
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intentBD.putExtras(bundle);
intentBD.setPackage(BuildConfig.APPLICATION_ID);
sendBroadcast(intentBD);
dialog.dismiss(); dialog.dismiss();
}); });
});
unpinConfirm.show(); unpinConfirm.show();
} else { } else {
new Thread(() -> { new Thread(() -> {
try { try {
Pinned pinned = new Pinned(HashTagActivity.this).getPinned(Helper.getCurrentAccount(HashTagActivity.this)); Pinned pinned = new Pinned(HashTagActivity.this).getPinned(currentAccount);
boolean canBeAdded = true; boolean canBeAdded = true;
boolean update = true; boolean update = true;
if (pinned == null) { if (pinned == null) {
@ -262,16 +242,11 @@ public class HashTagActivity extends BaseActivity {
} else { } else {
new Pinned(HashTagActivity.this).insertPinned(pinned); new Pinned(HashTagActivity.this).insertPinned(pinned);
} }
Bundle args = new Bundle(); Bundle b = new Bundle();
args.putBoolean(Helper.RECEIVE_REDRAW_TOPBAR, true); b.putBoolean(Helper.RECEIVE_REDRAW_TOPBAR, true);
Intent intentBD = new Intent(Helper.BROADCAST_DATA); Intent intentBD = new Intent(Helper.BROADCAST_DATA);
new CachedBundle(HashTagActivity.this).insertBundle(args, Helper.getCurrentAccount(HashTagActivity.this), bundleId -> { intentBD.putExtras(b);
Bundle bundle = new Bundle(); LocalBroadcastManager.getInstance(HashTagActivity.this).sendBroadcast(intentBD);
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intentBD.putExtras(bundle);
intentBD.setPackage(BuildConfig.APPLICATION_ID);
sendBroadcast(intentBD);
});
pinnedTag = true; pinnedTag = true;
invalidateOptionsMenu(); invalidateOptionsMenu();
} catch (DBException e) { } catch (DBException e) {

View file

@ -37,8 +37,6 @@ import androidx.preference.PreferenceManager;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.Objects;
import app.fedilab.android.BaseMainActivity; import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.activities.MainActivity; import app.fedilab.android.activities.MainActivity;
@ -52,6 +50,7 @@ import app.fedilab.android.mastodon.viewmodel.mastodon.InstancesVM;
public class InstanceActivity extends DialogFragment { public class InstanceActivity extends DialogFragment {
ActivityInstanceBinding binding; ActivityInstanceBinding binding;
private boolean applyMaxChar = false;
@NonNull @NonNull
@Override @Override
@ -79,12 +78,10 @@ public class InstanceActivity extends DialogFragment {
binding.about.setOnClickListener(v -> Helper.openBrowser(requireActivity(), "https://" + MainActivity.currentInstance + "/about")); binding.about.setOnClickListener(v -> Helper.openBrowser(requireActivity(), "https://" + MainActivity.currentInstance + "/about"));
binding.privacy.setOnClickListener(v -> Helper.openBrowser(requireActivity(), "https://" + MainActivity.currentInstance + "/privacy-policy")); binding.privacy.setOnClickListener(v -> Helper.openBrowser(requireActivity(), "https://" + MainActivity.currentInstance + "/privacy-policy"));
int maxCharCustom = sharedpreferences.getInt(getString(R.string.SET_MAX_INSTANCE_CHAR) + MainActivity.currentInstance, -1);
if (maxCharCustom != -1) {
binding.maxChar.setText(String.valueOf(maxCharCustom));
}
binding.close.setOnClickListener(view -> { binding.close.setOnClickListener(view -> {
String max_char = Objects.requireNonNull(binding.maxChar.getText()).toString(); if (applyMaxChar) {
String max_char = binding.maxChar.getText().toString();
SharedPreferences.Editor editor = sharedpreferences.edit(); SharedPreferences.Editor editor = sharedpreferences.edit();
if (!max_char.isEmpty()) { if (!max_char.isEmpty()) {
try { try {
@ -92,9 +89,7 @@ public class InstanceActivity extends DialogFragment {
editor.apply(); editor.apply();
} catch (Exception ignored) { } catch (Exception ignored) {
} }
} else { }
editor.putInt(getString(R.string.SET_MAX_INSTANCE_CHAR) + MainActivity.currentInstance, -1);
editor.apply();
} }
requireDialog().dismiss(); requireDialog().dismiss();
} }
@ -112,6 +107,8 @@ public class InstanceActivity extends DialogFragment {
if (val != -1) { if (val != -1) {
binding.maxChar.setText(String.valueOf(val)); binding.maxChar.setText(String.valueOf(val));
} }
applyMaxChar = true;
} else { } else {
Instance instance = instanceInfo.info; Instance instance = instanceInfo.info;

View file

@ -79,34 +79,34 @@ class InstanceHealthActivity : DialogFragment() {
} }
binding.name.text = instance.name binding.name.text = instance.name
if (instance.up) { if (instance.up) {
binding.up.setText(R.string.is_up) binding.up.setText(app.fedilab.android.R.string.is_up)
binding.up.setTextColor( binding.up.setTextColor(
ThemeHelper.getAttColor( ThemeHelper.getAttColor(
requireContext(), requireContext(),
R.attr.colorPrimary app.fedilab.android.R.attr.colorPrimary
) )
) )
} else { } else {
binding.up.setText(R.string.is_down) binding.up.setText(app.fedilab.android.R.string.is_down)
binding.up.setTextColor( binding.up.setTextColor(
ThemeHelper.getAttColor( ThemeHelper.getAttColor(
requireContext(), requireContext(),
R.attr.colorError app.fedilab.android.R.attr.colorError
) )
) )
} }
binding.uptime.text = getString( binding.uptime.text = getString(
R.string.instance_health_uptime, app.fedilab.android.R.string.instance_health_uptime,
instance.uptime * 100 instance.uptime * 100
) )
if (instance.checked_at != null) if (instance.checked_at != null)
binding.checkedAt.text = binding.checkedAt.text =
getString( getString(
R.string.instance_health_checkedat, app.fedilab.android.R.string.instance_health_checkedat,
Helper.dateToString(instance.checked_at) Helper.dateToString(instance.checked_at)
) )
binding.values.text = getString( binding.values.text = getString(
R.string.instance_health_indication, app.fedilab.android.R.string.instance_health_indication,
instance.version, instance.version,
Helper.withSuffix(instance.active_users.toLong()), Helper.withSuffix(instance.active_users.toLong()),
Helper.withSuffix(instance.statuses.toLong()) Helper.withSuffix(instance.statuses.toLong())

View file

@ -15,7 +15,6 @@ package app.fedilab.android.mastodon.activities;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import android.annotation.SuppressLint;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable; import android.text.Editable;
@ -26,12 +25,12 @@ import android.view.MenuItem;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import androidx.activity.OnBackPressedCallback;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction; import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
@ -40,17 +39,15 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects;
import app.fedilab.android.BaseMainActivity; import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.BuildConfig;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.databinding.ActivityListBinding; import app.fedilab.android.databinding.ActivityListBinding;
import app.fedilab.android.databinding.PopupAddListBinding; import app.fedilab.android.databinding.PopupAddListBinding;
import app.fedilab.android.databinding.PopupManageAccountsListBinding; import app.fedilab.android.databinding.PopupManageAccountsListBinding;
import app.fedilab.android.mastodon.client.entities.api.Account; import app.fedilab.android.mastodon.client.entities.api.Account;
import app.fedilab.android.mastodon.client.entities.api.MastodonList; import app.fedilab.android.mastodon.client.entities.api.MastodonList;
import app.fedilab.android.mastodon.client.entities.app.CachedBundle;
import app.fedilab.android.mastodon.client.entities.app.Pinned; import app.fedilab.android.mastodon.client.entities.app.Pinned;
import app.fedilab.android.mastodon.client.entities.app.PinnedTimeline; import app.fedilab.android.mastodon.client.entities.app.PinnedTimeline;
import app.fedilab.android.mastodon.client.entities.app.Timeline; import app.fedilab.android.mastodon.client.entities.app.Timeline;
@ -129,24 +126,6 @@ public class MastodonListActivity extends BaseBarActivity implements MastodonLis
} }
}); });
}); });
getOnBackPressedDispatcher().addCallback(new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
if (canGoBack) {
canGoBack = false;
ThemeHelper.slideViewsToRight(binding.fragmentContainer, binding.recyclerView, () -> {
if (fragmentMastodonTimeline != null) {
fragmentMastodonTimeline.onDestroyView();
}
});
setTitle(R.string.action_lists);
invalidateOptionsMenu();
} else {
finish();
}
}
});
} }
@ -162,11 +141,10 @@ public class MastodonListActivity extends BaseBarActivity implements MastodonLis
invalidateOptionsMenu(); invalidateOptionsMenu();
} }
@SuppressLint("ClickableViewAccessibility")
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) { if (item.getItemId() == android.R.id.home) {
getOnBackPressedDispatcher().onBackPressed(); onBackPressed();
return true; return true;
} else if (item.getItemId() == R.id.action_user_mute_home) { } else if (item.getItemId() == R.id.action_user_mute_home) {
AlertDialog.Builder dialogBuilder = new MaterialAlertDialogBuilder(MastodonListActivity.this); AlertDialog.Builder dialogBuilder = new MaterialAlertDialogBuilder(MastodonListActivity.this);
@ -175,7 +153,7 @@ public class MastodonListActivity extends BaseBarActivity implements MastodonLis
timelinesVM.getAccountsInList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, mastodonList.id, null, null, 0) timelinesVM.getAccountsInList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, mastodonList.id, null, null, 0)
.observe(MastodonListActivity.this, accounts -> { .observe(MastodonListActivity.this, accounts -> {
if (accounts != null && accounts.size() > 0) { if (accounts != null && accounts.size() > 0) {
accountsVM.muteAccountsHome(Helper.getCurrentAccount(MastodonListActivity.this), accounts); accountsVM.muteAccountsHome(MainActivity.currentAccount, accounts);
} }
}); });
dialog.dismiss(); dialog.dismiss();
@ -307,17 +285,12 @@ public class MastodonListActivity extends BaseBarActivity implements MastodonLis
} else { } else {
binding.notContent.setVisibility(View.GONE); binding.notContent.setVisibility(View.GONE);
} }
Bundle args = new Bundle(); Bundle b = new Bundle();
args.putBoolean(Helper.RECEIVE_REDRAW_TOPBAR, true); b.putBoolean(Helper.RECEIVE_REDRAW_TOPBAR, true);
Intent intentBD = new Intent(Helper.BROADCAST_DATA); Intent intentBD = new Intent(Helper.BROADCAST_DATA);
args.putSerializable(Helper.RECEIVE_MASTODON_LIST, mastodonListList); b.putSerializable(Helper.RECEIVE_MASTODON_LIST, mastodonListList);
new CachedBundle(MastodonListActivity.this).insertBundle(args, Helper.getCurrentAccount(MastodonListActivity.this), bundleId -> { intentBD.putExtras(b);
Bundle bundle = new Bundle(); LocalBroadcastManager.getInstance(MastodonListActivity.this).sendBroadcast(intentBD);
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intentBD.putExtras(bundle);
intentBD.setPackage(BuildConfig.APPLICATION_ID);
sendBroadcast(intentBD);
});
}); });
alt_bld.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss()); alt_bld.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss());
AlertDialog alert = alt_bld.create(); AlertDialog alert = alt_bld.create();
@ -347,17 +320,12 @@ public class MastodonListActivity extends BaseBarActivity implements MastodonLis
} else { } else {
Toasty.error(MastodonListActivity.this, getString(R.string.toast_error), Toasty.LENGTH_LONG).show(); Toasty.error(MastodonListActivity.this, getString(R.string.toast_error), Toasty.LENGTH_LONG).show();
} }
Bundle args = new Bundle(); Bundle b = new Bundle();
args.putBoolean(Helper.RECEIVE_REDRAW_TOPBAR, true); b.putBoolean(Helper.RECEIVE_REDRAW_TOPBAR, true);
Intent intentBD = new Intent(Helper.BROADCAST_DATA); Intent intentBD = new Intent(Helper.BROADCAST_DATA);
args.putSerializable(Helper.RECEIVE_MASTODON_LIST, mastodonListList); b.putSerializable(Helper.RECEIVE_MASTODON_LIST, mastodonListList);
new CachedBundle(MastodonListActivity.this).insertBundle(args, Helper.getCurrentAccount(MastodonListActivity.this), bundleId -> { intentBD.putExtras(b);
Bundle bundle = new Bundle(); LocalBroadcastManager.getInstance(MastodonListActivity.this).sendBroadcast(intentBD);
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intentBD.putExtras(bundle);
intentBD.setPackage(BuildConfig.APPLICATION_ID);
sendBroadcast(intentBD);
});
}); });
dialog.dismiss(); dialog.dismiss();
} else { } else {
@ -373,7 +341,7 @@ public class MastodonListActivity extends BaseBarActivity implements MastodonLis
dialogBuilder.setView(popupAddListBinding.getRoot()); dialogBuilder.setView(popupAddListBinding.getRoot());
popupAddListBinding.addList.setFilters(new InputFilter[]{new InputFilter.LengthFilter(255)}); popupAddListBinding.addList.setFilters(new InputFilter[]{new InputFilter.LengthFilter(255)});
popupAddListBinding.addList.setText(mastodonList.title); popupAddListBinding.addList.setText(mastodonList.title);
popupAddListBinding.addList.setSelection(Objects.requireNonNull(popupAddListBinding.addList.getText()).length()); popupAddListBinding.addList.setSelection(popupAddListBinding.addList.getText().length());
dialogBuilder.setPositiveButton(R.string.validate, (dialog, id) -> { dialogBuilder.setPositiveButton(R.string.validate, (dialog, id) -> {
if (popupAddListBinding.addList.getText() != null && popupAddListBinding.addList.getText().toString().trim().length() > 0) { if (popupAddListBinding.addList.getText() != null && popupAddListBinding.addList.getText().toString().trim().length() > 0) {
timelinesVM.updateList( timelinesVM.updateList(
@ -400,18 +368,12 @@ public class MastodonListActivity extends BaseBarActivity implements MastodonLis
new Thread(() -> { new Thread(() -> {
try { try {
new Pinned(MastodonListActivity.this).updatePinned(pinned); new Pinned(MastodonListActivity.this).updatePinned(pinned);
Bundle args = new Bundle(); Bundle b = new Bundle();
args.putBoolean(Helper.RECEIVE_REDRAW_TOPBAR, true); b.putBoolean(Helper.RECEIVE_REDRAW_TOPBAR, true);
Intent intentBD = new Intent(Helper.BROADCAST_DATA); Intent intentBD = new Intent(Helper.BROADCAST_DATA);
new CachedBundle(MastodonListActivity.this).insertBundle(args, Helper.getCurrentAccount(MastodonListActivity.this), bundleId -> { intentBD.putExtras(b);
Bundle bundle = new Bundle(); LocalBroadcastManager.getInstance(MastodonListActivity.this).sendBroadcast(intentBD);
bundle.putLong(Helper.ARG_INTENT_ID, bundleId); } catch (DBException e) {
intentBD.putExtras(bundle);
intentBD.setPackage(BuildConfig.APPLICATION_ID);
sendBroadcast(intentBD);
});
} catch (
DBException e) {
e.printStackTrace(); e.printStackTrace();
} }
}).start(); }).start();
@ -459,6 +421,7 @@ public class MastodonListActivity extends BaseBarActivity implements MastodonLis
this.mastodonList = mastodonList; this.mastodonList = mastodonList;
canGoBack = true; canGoBack = true;
ThemeHelper.slideViewsToLeft(binding.recyclerView, binding.fragmentContainer, () -> {
fragmentMastodonTimeline = new FragmentMastodonTimeline(); fragmentMastodonTimeline = new FragmentMastodonTimeline();
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.putSerializable(Helper.ARG_LIST_ID, mastodonList.id); bundle.putSerializable(Helper.ARG_LIST_ID, mastodonList.id);
@ -471,8 +434,6 @@ public class MastodonListActivity extends BaseBarActivity implements MastodonLis
fragmentTransaction.replace(R.id.fragment_container, fragmentMastodonTimeline); fragmentTransaction.replace(R.id.fragment_container, fragmentMastodonTimeline);
fragmentTransaction.commit(); fragmentTransaction.commit();
invalidateOptionsMenu(); invalidateOptionsMenu();
ThemeHelper.slideViewsToLeft(binding.recyclerView, binding.fragmentContainer, () -> {
}); });
} }
@ -493,4 +454,20 @@ public class MastodonListActivity extends BaseBarActivity implements MastodonLis
} }
return true; return true;
} }
@Override
public void onBackPressed() {
if (canGoBack) {
canGoBack = false;
ThemeHelper.slideViewsToRight(binding.fragmentContainer, binding.recyclerView, () -> {
if (fragmentMastodonTimeline != null) {
fragmentMastodonTimeline.onDestroyView();
}
});
setTitle(R.string.action_lists);
invalidateOptionsMenu();
} else {
super.onBackPressed();
}
}
} }

View file

@ -14,8 +14,6 @@ package app.fedilab.android.mastodon.activities;
* You should have received a copy of the GNU General Public License along with Fedilab; if not, * You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import static android.util.Patterns.WEB_URL;
import android.Manifest; import android.Manifest;
import android.app.DownloadManager; import android.app.DownloadManager;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
@ -23,26 +21,17 @@ import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
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.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.Window; import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -51,19 +40,16 @@ import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter; import androidx.fragment.app.FragmentStatePagerAdapter;
import androidx.preference.PreferenceManager;
import androidx.viewpager.widget.ViewPager; import androidx.viewpager.widget.ViewPager;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.regex.Matcher;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivityMediaPagerBinding; import app.fedilab.android.databinding.ActivityMediaPagerBinding;
import app.fedilab.android.mastodon.client.entities.api.Attachment; import app.fedilab.android.mastodon.client.entities.api.Attachment;
import app.fedilab.android.mastodon.client.entities.api.Status; import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.mastodon.client.entities.app.CachedBundle;
import app.fedilab.android.mastodon.helper.Helper; import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.helper.MediaHelper; import app.fedilab.android.mastodon.helper.MediaHelper;
import app.fedilab.android.mastodon.helper.TranslateHelper; import app.fedilab.android.mastodon.helper.TranslateHelper;
@ -73,7 +59,7 @@ import app.fedilab.android.mastodon.ui.fragment.media.FragmentMediaProfile;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
public class MediaActivity extends BaseBarActivity implements OnDownloadInterface { public class MediaActivity extends BaseTransparentActivity implements OnDownloadInterface {
int flags; int flags;
private ArrayList<Attachment> attachments; private ArrayList<Attachment> attachments;
@ -83,10 +69,10 @@ public class MediaActivity extends BaseBarActivity implements OnDownloadInterfac
private final BroadcastReceiver onDownloadComplete = new BroadcastReceiver() { private final BroadcastReceiver onDownloadComplete = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1); long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
if (downloadID == id) { if (downloadID == id) {
DownloadManager manager = (DownloadManager) context.getSystemService(DOWNLOAD_SERVICE); DownloadManager manager = (DownloadManager) context.getSystemService(DOWNLOAD_SERVICE);
assert manager != null;
Uri uri = manager.getUriForDownloadedFile(downloadID); Uri uri = manager.getUriForDownloadedFile(downloadID);
Intent shareIntent = new Intent(Intent.ACTION_SEND); Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_STREAM, uri); shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
@ -95,8 +81,7 @@ public class MediaActivity extends BaseBarActivity implements OnDownloadInterfac
shareIntent.setType(cR.getType(uri)); shareIntent.setType(cR.getType(uri));
try { try {
startActivity(shareIntent); startActivity(shareIntent);
} catch (Exception e) { } catch (Exception ignored) {
e.printStackTrace();
} }
} else { } else {
Toasty.error(context, context.getString(R.string.toast_error), Toasty.LENGTH_LONG).show(); Toasty.error(context, context.getString(R.string.toast_error), Toasty.LENGTH_LONG).show();
@ -118,7 +103,6 @@ public class MediaActivity extends BaseBarActivity implements OnDownloadInterfac
getWindow().requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY); getWindow().requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
ActivityCompat.postponeEnterTransition(MediaActivity.this); ActivityCompat.postponeEnterTransition(MediaActivity.this);
binding = ActivityMediaPagerBinding.inflate(getLayoutInflater()); binding = ActivityMediaPagerBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
@ -126,29 +110,13 @@ public class MediaActivity extends BaseBarActivity implements OnDownloadInterfac
fullscreen = false; fullscreen = false;
flags = getWindow().getDecorView().getSystemUiVisibility(); flags = getWindow().getDecorView().getSystemUiVisibility();
Bundle args = getIntent().getExtras(); Bundle b = getIntent().getExtras();
if (args != null) { if (b != null) {
long bundleId = args.getLong(Helper.ARG_INTENT_ID, -1); mediaPosition = b.getInt(Helper.ARG_MEDIA_POSITION, 1);
new CachedBundle(MediaActivity.this).getBundle(bundleId, Helper.getCurrentAccount(MediaActivity.this), this::initializeAfterBundle); attachments = (ArrayList<Attachment>) b.getSerializable(Helper.ARG_MEDIA_ARRAY);
} else { mediaFromProfile = b.getBoolean(Helper.ARG_MEDIA_ARRAY_PROFILE, false);
initializeAfterBundle(null); status = (Status) b.getSerializable(Helper.ARG_STATUS);
} }
}
private void initializeAfterBundle(Bundle bundle) {
if (bundle != null) {
mediaPosition = bundle.getInt(Helper.ARG_MEDIA_POSITION, 1);
if(mediaPosition < 1 ) {
mediaPosition = 1;
}
attachments = (ArrayList<Attachment>) bundle.getSerializable(Helper.ARG_MEDIA_ARRAY);
mediaFromProfile = bundle.getBoolean(Helper.ARG_MEDIA_ARRAY_PROFILE, false);
status = (Status) bundle.getSerializable(Helper.ARG_STATUS);
}
if (mediaFromProfile && FragmentMediaProfile.mediaAttachmentProfile != null) { if (mediaFromProfile && FragmentMediaProfile.mediaAttachmentProfile != null) {
attachments = new ArrayList<>(); attachments = new ArrayList<>();
attachments.addAll(FragmentMediaProfile.mediaAttachmentProfile); attachments.addAll(FragmentMediaProfile.mediaAttachmentProfile);
@ -164,45 +132,32 @@ public class MediaActivity extends BaseBarActivity implements OnDownloadInterfac
} }
setTitle(""); setTitle("");
ScreenSlidePagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager()); ScreenSlidePagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
binding.mediaViewpager.setAdapter(mPagerAdapter); binding.mediaViewpager.setAdapter(mPagerAdapter);
binding.mediaViewpager.setSaveEnabled(false); binding.mediaViewpager.setSaveEnabled(false);
binding.mediaViewpager.setCurrentItem(mediaPosition - 1); binding.mediaViewpager.setCurrentItem(mediaPosition - 1);
ContextCompat.registerReceiver(MediaActivity.this, onDownloadComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE), ContextCompat.RECEIVER_EXPORTED); registerReceiver(onDownloadComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
String description = attachments.get(mediaPosition - 1).description; String description = attachments.get(mediaPosition - 1).description;
handler = new Handler(); handler = new Handler();
if (attachments.get(mediaPosition - 1).status != null) { if (attachments.get(mediaPosition - 1).status != null) {
binding.originalMessage.setOnClickListener(v -> { binding.originalMessage.setOnClickListener(v -> {
Intent intentContext = new Intent(MediaActivity.this, ContextActivity.class); Intent intentContext = new Intent(MediaActivity.this, ContextActivity.class);
Bundle args = new Bundle(); intentContext.putExtra(Helper.ARG_STATUS, attachments.get(mediaPosition - 1).status);
args.putSerializable(Helper.ARG_STATUS, attachments.get(mediaPosition - 1).status);
new CachedBundle(MediaActivity.this).insertBundle(args, Helper.getCurrentAccount(MediaActivity.this), bundleId -> {
Bundle bundleCached = new Bundle();
bundleCached.putLong(Helper.ARG_INTENT_ID, bundleId);
intentContext.putExtras(bundleCached);
intentContext.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intentContext.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intentContext); startActivity(intentContext);
}); });
});
} }
if (description != null && description.trim().length() > 0 && description.trim().compareTo("null") != 0) {
binding.mediaDescription.setMovementMethod(LinkMovementMethod.getInstance());
binding.mediaDescriptionTranslated.setMovementMethod(LinkMovementMethod.getInstance());
if (description != null && !description.trim().isEmpty() && description.trim().compareTo("null") != 0) {
binding.mediaDescription.setText(description); binding.mediaDescription.setText(description);
binding.translate.setOnClickListener(v -> { binding.translate.setOnClickListener(v -> {
String descriptionToTranslate = attachments.get(mediaPosition - 1).description; String descriptionToTranslate = attachments.get(mediaPosition - 1).description;
TranslateHelper.translate(MediaActivity.this, descriptionToTranslate, status!=null?status.language:"en", translated -> { TranslateHelper.translate(MediaActivity.this, descriptionToTranslate, translated -> {
if (translated != null) { if (translated != null) {
attachments.get(mediaPosition - 1).translation = translated; attachments.get(mediaPosition - 1).translation = translated;
binding.mediaDescriptionTranslated.setText(translated); binding.mediaDescriptionTranslated.setText(translated);
binding.mediaDescriptionTranslated.setVisibility(View.VISIBLE); binding.mediaDescriptionTranslated.setVisibility(View.VISIBLE);
binding.mediaDescription.setVisibility(View.GONE); binding.mediaDescription.setVisibility(View.GONE);
if (mCurrentFragment != null) {
mCurrentFragment.toggleController(false);
}
binding.translate.setVisibility(View.GONE);
} else { } else {
Toasty.error(MediaActivity.this, getString(R.string.toast_error_translate), Toast.LENGTH_LONG).show(); Toasty.error(MediaActivity.this, getString(R.string.toast_error_translate), Toast.LENGTH_LONG).show();
} }
@ -210,16 +165,10 @@ public class MediaActivity extends BaseBarActivity implements OnDownloadInterfac
}); });
if (attachments.get(mediaPosition - 1).translation != null) { if (attachments.get(mediaPosition - 1).translation != null) {
binding.mediaDescription.setVisibility(View.GONE); binding.mediaDescription.setVisibility(View.GONE);
if (mCurrentFragment != null) {
mCurrentFragment.toggleController(false);
}
binding.mediaDescriptionTranslated.setText(attachments.get(mediaPosition - 1).translation); binding.mediaDescriptionTranslated.setText(attachments.get(mediaPosition - 1).translation);
binding.mediaDescriptionTranslated.setVisibility(View.VISIBLE); binding.mediaDescriptionTranslated.setVisibility(View.VISIBLE);
} else { } else {
binding.mediaDescription.setVisibility(View.VISIBLE); binding.mediaDescription.setVisibility(View.VISIBLE);
if (mCurrentFragment != null) {
mCurrentFragment.toggleController(true);
}
binding.mediaDescriptionTranslated.setVisibility(View.GONE); binding.mediaDescriptionTranslated.setVisibility(View.GONE);
} }
} }
@ -233,29 +182,22 @@ public class MediaActivity extends BaseBarActivity implements OnDownloadInterfac
public void onPageSelected(int position) { public void onPageSelected(int position) {
mediaPosition = position; mediaPosition = position;
if(mediaPosition < 1 ) {
mediaPosition = 1;
}
String description = attachments.get(position).description; String description = attachments.get(position).description;
if (handler != null) { if (handler != null) {
handler.removeCallbacksAndMessages(null); handler.removeCallbacksAndMessages(null);
} }
handler = new Handler(); handler = new Handler();
if (description != null && !description.trim().isEmpty() && description.trim().compareTo("null") != 0) { if (description != null && description.trim().length() > 0 && description.trim().compareTo("null") != 0) {
binding.mediaDescription.setText(linkify(MediaActivity.this, description), TextView.BufferType.SPANNABLE); binding.mediaDescription.setText(description);
} }
binding.translate.setOnClickListener(v -> { binding.translate.setOnClickListener(v -> {
String descriptionToTranslate = attachments.get(position).description; String descriptionToTranslate = attachments.get(position).description;
TranslateHelper.translate(MediaActivity.this, descriptionToTranslate, status!=null?status.language:"en", translated -> { TranslateHelper.translate(MediaActivity.this, descriptionToTranslate, translated -> {
if (translated != null) { if (translated != null) {
attachments.get(position).translation = translated; attachments.get(position).translation = translated;
binding.mediaDescriptionTranslated.setText(translated); binding.mediaDescriptionTranslated.setText(translated);
binding.mediaDescriptionTranslated.setVisibility(View.VISIBLE); binding.mediaDescriptionTranslated.setVisibility(View.VISIBLE);
binding.mediaDescription.setVisibility(View.GONE); binding.mediaDescription.setVisibility(View.GONE);
if (mCurrentFragment != null) {
mCurrentFragment.toggleController(true);
}
binding.translate.setVisibility(View.GONE);
} else { } else {
Toasty.error(MediaActivity.this, getString(R.string.toast_error_translate), Toast.LENGTH_LONG).show(); Toasty.error(MediaActivity.this, getString(R.string.toast_error_translate), Toast.LENGTH_LONG).show();
} }
@ -264,23 +206,14 @@ public class MediaActivity extends BaseBarActivity implements OnDownloadInterfac
if (!fullscreen) { if (!fullscreen) {
if (attachments.get(position).translation != null) { if (attachments.get(position).translation != null) {
binding.mediaDescription.setVisibility(View.GONE); binding.mediaDescription.setVisibility(View.GONE);
if (mCurrentFragment != null) {
mCurrentFragment.toggleController(false);
}
binding.mediaDescriptionTranslated.setText(attachments.get(position).translation); binding.mediaDescriptionTranslated.setText(attachments.get(position).translation);
binding.mediaDescriptionTranslated.setVisibility(View.VISIBLE); binding.mediaDescriptionTranslated.setVisibility(View.VISIBLE);
} else { } else {
binding.mediaDescription.setVisibility(View.VISIBLE); binding.mediaDescription.setVisibility(View.VISIBLE);
if (mCurrentFragment != null) {
mCurrentFragment.toggleController(true);
}
binding.mediaDescriptionTranslated.setVisibility(View.GONE); binding.mediaDescriptionTranslated.setVisibility(View.GONE);
} }
} else { } else {
binding.mediaDescription.setVisibility(View.GONE); binding.mediaDescription.setVisibility(View.GONE);
if (mCurrentFragment != null) {
mCurrentFragment.toggleController(false);
}
binding.mediaDescriptionTranslated.setVisibility(View.GONE); binding.mediaDescriptionTranslated.setVisibility(View.GONE);
} }
} }
@ -288,41 +221,6 @@ public class MediaActivity extends BaseBarActivity implements OnDownloadInterfac
setFullscreen(true); 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 @Override
public boolean dispatchTouchEvent(MotionEvent event) { public boolean dispatchTouchEvent(MotionEvent event) {
try { try {
@ -348,10 +246,7 @@ public class MediaActivity extends BaseBarActivity implements OnDownloadInterfac
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) { if (item.getItemId() == android.R.id.home) {
finish(); finish();
try {
ActivityCompat.finishAfterTransition(MediaActivity.this); ActivityCompat.finishAfterTransition(MediaActivity.this);
} catch (Exception ignored) {
}
return true; return true;
} else if (item.getItemId() == R.id.action_save) { } else if (item.getItemId() == R.id.action_save) {
int position = binding.mediaViewpager.getCurrentItem(); int position = binding.mediaViewpager.getCurrentItem();
@ -433,7 +328,7 @@ public class MediaActivity extends BaseBarActivity implements OnDownloadInterfac
if (!fullscreen) { if (!fullscreen) {
String description = attachments.get(binding.mediaViewpager.getCurrentItem()).description; String description = attachments.get(binding.mediaViewpager.getCurrentItem()).description;
if (description != null && description.trim().length() > 0 && description.trim().compareTo("null") != 0) { if (description != null && description.trim().length() > 0 && description.trim().compareTo("null") != 0) {
binding.mediaDescription.setText(linkify(MediaActivity.this, description), TextView.BufferType.SPANNABLE); binding.mediaDescription.setText(description);
if (attachments.get(binding.mediaViewpager.getCurrentItem()).translation != null) { if (attachments.get(binding.mediaViewpager.getCurrentItem()).translation != null) {
binding.mediaDescription.setVisibility(View.GONE); binding.mediaDescription.setVisibility(View.GONE);
binding.mediaDescriptionTranslated.setText(attachments.get(binding.mediaViewpager.getCurrentItem()).translation); binding.mediaDescriptionTranslated.setText(attachments.get(binding.mediaViewpager.getCurrentItem()).translation);
@ -463,16 +358,13 @@ public class MediaActivity extends BaseBarActivity implements OnDownloadInterfac
@Override @Override
public void onDestroy() { public void onDestroy() {
try {
unregisterReceiver(onDownloadComplete); unregisterReceiver(onDownloadComplete);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
super.onDestroy(); super.onDestroy();
} }
@Override @Override
public void onDownloaded(String saveFilePath, String downloadUrl, Error error) { public void onDownloaded(String saveFilePath, String downloadUrl, Error error) {
} }
@Override @Override
@ -503,22 +395,14 @@ public class MediaActivity extends BaseBarActivity implements OnDownloadInterfac
this.fullscreen = fullscreen; this.fullscreen = fullscreen;
if (!fullscreen) { if (!fullscreen) {
showSystemUI(); showSystemUI();
binding.descriptionContainer.setVisibility(View.VISIBLE);
binding.mediaDescription.setVisibility(View.VISIBLE); binding.mediaDescription.setVisibility(View.VISIBLE);
if (mCurrentFragment != null) {
mCurrentFragment.toggleController(true);
}
binding.translate.setVisibility(View.VISIBLE); binding.translate.setVisibility(View.VISIBLE);
if (mediaFromProfile) { if (mediaFromProfile) {
binding.originalMessage.setVisibility(View.VISIBLE); binding.originalMessage.setVisibility(View.VISIBLE);
} }
} else { } else {
hideSystemUI(); hideSystemUI();
binding.descriptionContainer.setVisibility(View.GONE);
binding.mediaDescription.setVisibility(View.GONE); binding.mediaDescription.setVisibility(View.GONE);
if (mCurrentFragment != null) {
mCurrentFragment.toggleController(false);
}
binding.translate.setVisibility(View.GONE); binding.translate.setVisibility(View.GONE);
binding.originalMessage.setVisibility(View.INVISIBLE); binding.originalMessage.setVisibility(View.INVISIBLE);
} }

View file

@ -23,6 +23,7 @@ import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.TextView; import android.widget.TextView;
import androidx.core.app.ActivityOptionsCompat;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import java.util.ArrayList; import java.util.ArrayList;
@ -33,7 +34,6 @@ import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivityPartnershipBinding; import app.fedilab.android.databinding.ActivityPartnershipBinding;
import app.fedilab.android.mastodon.client.entities.api.Account; import app.fedilab.android.mastodon.client.entities.api.Account;
import app.fedilab.android.mastodon.client.entities.api.Status; import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.mastodon.client.entities.app.CachedBundle;
import app.fedilab.android.mastodon.helper.CrossActionHelper; import app.fedilab.android.mastodon.helper.CrossActionHelper;
import app.fedilab.android.mastodon.helper.Helper; import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.helper.MastodonHelper; import app.fedilab.android.mastodon.helper.MastodonHelper;
@ -78,14 +78,12 @@ public class PartnerShipActivity extends BaseBarActivity {
binding.accountUn.setText(account.acct); binding.accountUn.setText(account.acct);
binding.accountPp.setOnClickListener(v -> { binding.accountPp.setOnClickListener(v -> {
Intent intent = new Intent(PartnerShipActivity.this, ProfileActivity.class); Intent intent = new Intent(PartnerShipActivity.this, ProfileActivity.class);
Bundle args = new Bundle(); Bundle b = new Bundle();
args.putSerializable(Helper.ARG_ACCOUNT, account); b.putSerializable(Helper.ARG_ACCOUNT, account);
new CachedBundle(PartnerShipActivity.this).insertBundle(args, Helper.getCurrentAccount(PartnerShipActivity.this), bundleId -> { intent.putExtras(b);
Bundle bundle = new Bundle(); ActivityOptionsCompat options = ActivityOptionsCompat
bundle.putLong(Helper.ARG_INTENT_ID, bundleId); .makeSceneTransitionAnimation(PartnerShipActivity.this, binding.accountPp, getString(R.string.activity_porfile_pp));
intent.putExtras(bundle); startActivity(intent, options.toBundle());
startActivity(intent);
});
}); });
AccountsVM accountsVM = new ViewModelProvider(PartnerShipActivity.this).get(AccountsVM.class); AccountsVM accountsVM = new ViewModelProvider(PartnerShipActivity.this).get(AccountsVM.class);
List<String> ids = new ArrayList<>(); List<String> ids = new ArrayList<>();

View file

@ -15,9 +15,7 @@ package app.fedilab.android.mastodon.activities;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.BaseMainActivity.currentAccount;
import static app.fedilab.android.mastodon.helper.LogoHelper.getMainLogo;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.ClipData; import android.content.ClipData;
@ -27,20 +25,15 @@ import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.res.ColorStateList; import android.content.res.ColorStateList;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Animatable; import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.Spanned; import android.text.Spanned;
import android.text.method.LinkMovementMethod; import android.text.method.LinkMovementMethod;
import android.text.style.ForegroundColorSpan; import android.text.style.ForegroundColorSpan;
import android.text.style.UnderlineSpan; import android.text.style.UnderlineSpan;
import android.util.TypedValue; import android.util.TypedValue;
import android.view.Gravity;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
@ -48,7 +41,6 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.ViewTreeObserver; import android.view.ViewTreeObserver;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import android.webkit.URLUtil;
import android.widget.EditText; import android.widget.EditText;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
@ -59,10 +51,11 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.TooltipCompat; import androidx.core.app.ActivityCompat;
import androidx.core.app.ActivityOptionsCompat; import androidx.core.app.ActivityOptionsCompat;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
@ -70,41 +63,31 @@ import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.bumptech.glide.request.target.CustomTarget; import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.transition.Transition; import com.bumptech.glide.request.transition.Transition;
import com.google.android.material.chip.Chip;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.tabs.TabLayout; import com.google.android.material.tabs.TabLayout;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Set; import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import androidmads.library.qrgenearator.QRGContents;
import androidmads.library.qrgenearator.QRGEncoder;
import app.fedilab.android.BaseMainActivity; import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.BuildConfig;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.activities.MainActivity; import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.databinding.ActivityProfileBinding; import app.fedilab.android.databinding.ActivityProfileBinding;
import app.fedilab.android.databinding.NotificationsRelatedAccountsBinding; import app.fedilab.android.databinding.NotificationsRelatedAccountsBinding;
import app.fedilab.android.databinding.PopupQrcodeBinding;
import app.fedilab.android.databinding.TabProfileCustomViewBinding;
import app.fedilab.android.mastodon.client.entities.api.Account; 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.Attachment;
import app.fedilab.android.mastodon.client.entities.api.FamiliarFollowers; import app.fedilab.android.mastodon.client.entities.api.FamiliarFollowers;
import app.fedilab.android.mastodon.client.entities.api.FeaturedTag;
import app.fedilab.android.mastodon.client.entities.api.Field; import app.fedilab.android.mastodon.client.entities.api.Field;
import app.fedilab.android.mastodon.client.entities.api.IdentityProof; import app.fedilab.android.mastodon.client.entities.api.IdentityProof;
import app.fedilab.android.mastodon.client.entities.api.MastodonList; import app.fedilab.android.mastodon.client.entities.api.MastodonList;
import app.fedilab.android.mastodon.client.entities.api.RelationShip; import app.fedilab.android.mastodon.client.entities.api.RelationShip;
import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.mastodon.client.entities.app.CachedBundle;
import app.fedilab.android.mastodon.client.entities.app.Languages; import app.fedilab.android.mastodon.client.entities.app.Languages;
import app.fedilab.android.mastodon.client.entities.app.Pinned; import app.fedilab.android.mastodon.client.entities.app.Pinned;
import app.fedilab.android.mastodon.client.entities.app.PinnedTimeline; import app.fedilab.android.mastodon.client.entities.app.PinnedTimeline;
@ -119,7 +102,6 @@ import app.fedilab.android.mastodon.helper.SpannableHelper;
import app.fedilab.android.mastodon.helper.ThemeHelper; import app.fedilab.android.mastodon.helper.ThemeHelper;
import app.fedilab.android.mastodon.ui.drawer.FieldAdapter; import app.fedilab.android.mastodon.ui.drawer.FieldAdapter;
import app.fedilab.android.mastodon.ui.drawer.IdentityProofsAdapter; import app.fedilab.android.mastodon.ui.drawer.IdentityProofsAdapter;
import app.fedilab.android.mastodon.ui.drawer.StatusAdapter;
import app.fedilab.android.mastodon.ui.pageadapter.FedilabProfileTLPageAdapter; import app.fedilab.android.mastodon.ui.pageadapter.FedilabProfileTLPageAdapter;
import app.fedilab.android.mastodon.viewmodel.mastodon.AccountsVM; import app.fedilab.android.mastodon.viewmodel.mastodon.AccountsVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.NodeInfoVM; import app.fedilab.android.mastodon.viewmodel.mastodon.NodeInfoVM;
@ -134,6 +116,7 @@ public class ProfileActivity extends BaseActivity {
private RelationShip relationship; private RelationShip relationship;
private FamiliarFollowers familiarFollowers; private FamiliarFollowers familiarFollowers;
private Account account; private Account account;
private ScheduledExecutorService scheduledExecutorService;
private action doAction; private action doAction;
private AccountsVM accountsVM; private AccountsVM accountsVM;
private RecyclerView identityProofsRecycler; private RecyclerView identityProofsRecycler;
@ -148,17 +131,14 @@ public class ProfileActivity extends BaseActivity {
private final BroadcastReceiver broadcast_data = new BroadcastReceiver() { private final BroadcastReceiver broadcast_data = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
Bundle args = intent.getExtras(); Bundle b = intent.getExtras();
if (args != null) { if (b != null) {
long bundleId = args.getLong(Helper.ARG_INTENT_ID, -1); Account accountReceived = (Account) b.getSerializable(Helper.ARG_ACCOUNT);
new CachedBundle(ProfileActivity.this).getBundle(bundleId, Helper.getCurrentAccount(ProfileActivity.this), bundle -> { if (b.getBoolean(Helper.RECEIVE_REDRAW_PROFILE, false) && accountReceived != null) {
Account accountReceived = (Account) bundle.getSerializable(Helper.ARG_ACCOUNT);
if (bundle.getBoolean(Helper.RECEIVE_REDRAW_PROFILE, false) && accountReceived != null) {
if (account != null && accountReceived.id != null && account.id != null && accountReceived.id.equalsIgnoreCase(account.id)) { if (account != null && accountReceived.id != null && account.id != null && accountReceived.id.equalsIgnoreCase(account.id)) {
initializeView(accountReceived); initializeView(accountReceived);
} }
} }
});
} }
} }
}; };
@ -172,14 +152,21 @@ public class ProfileActivity extends BaseActivity {
setContentView(binding.getRoot()); setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
ActionBar actionBar = getSupportActionBar(); ActionBar actionBar = getSupportActionBar();
Bundle args = getIntent().getExtras(); Bundle b = getIntent().getExtras();
binding.accountFollow.setEnabled(false); binding.accountFollow.setEnabled(false);
checkRemotely = false; checkRemotely = false;
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this);
homeMuted = false;
if (b != null) {
account = (Account) b.getSerializable(Helper.ARG_ACCOUNT);
account_id = b.getString(Helper.ARG_USER_ID, null);
mention_str = b.getString(Helper.ARG_MENTION, null);
checkRemotely = b.getBoolean(Helper.ARG_CHECK_REMOTELY, false);
}
if (!checkRemotely) { if (!checkRemotely) {
checkRemotely = sharedpreferences.getBoolean(getString(R.string.SET_PROFILE_REMOTELY), false); checkRemotely = sharedpreferences.getBoolean(getString(R.string.SET_PROFILE_REMOTELY), false);
} }
ActivityCompat.postponeEnterTransition(ProfileActivity.this);
//Remove title //Remove title
if (actionBar != null) { if (actionBar != null) {
actionBar.setDisplayShowTitleEnabled(false); actionBar.setDisplayShowTitleEnabled(false);
@ -193,22 +180,6 @@ public class ProfileActivity extends BaseActivity {
float scale = sharedpreferences.getFloat(getString(R.string.SET_FONT_SCALE), 1.1f); float scale = sharedpreferences.getFloat(getString(R.string.SET_FONT_SCALE), 1.1f);
binding.title.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18 * 1.1f / scale); binding.title.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18 * 1.1f / scale);
accountsVM = new ViewModelProvider(ProfileActivity.this).get(AccountsVM.class); accountsVM = new ViewModelProvider(ProfileActivity.this).get(AccountsVM.class);
homeMuted = false;
if (args != null) {
long bundleId = args.getLong(Helper.ARG_INTENT_ID, -1);
new CachedBundle(ProfileActivity.this).getBundle(bundleId, Helper.getCurrentAccount(ProfileActivity.this), this::initializeAfterBundle);
} else {
initializeAfterBundle(null);
}
}
private void initializeAfterBundle(Bundle bundle) {
if (bundle != null) {
account = (Account) bundle.getSerializable(Helper.ARG_ACCOUNT);
account_id = bundle.getString(Helper.ARG_USER_ID, null);
mention_str = bundle.getString(Helper.ARG_MENTION, null);
checkRemotely = bundle.getBoolean(Helper.ARG_CHECK_REMOTELY, false);
}
if (account != null) { if (account != null) {
initializeView(account); initializeView(account);
} else if (account_id != null) { } else if (account_id != null) {
@ -218,7 +189,7 @@ public class ProfileActivity extends BaseActivity {
}); });
} else if (mention_str != null) { } else if (mention_str != null) {
accountsVM.searchAccounts(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, mention_str, 1, true, false).observe(ProfileActivity.this, accounts -> { accountsVM.searchAccounts(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, mention_str, 1, true, false).observe(ProfileActivity.this, accounts -> {
if (accounts != null && !accounts.isEmpty()) { if (accounts != null && accounts.size() > 0) {
account = accounts.get(0); account = accounts.get(0);
initializeView(account); initializeView(account);
} else { } else {
@ -231,49 +202,8 @@ public class ProfileActivity extends BaseActivity {
finish(); finish();
} }
//Check if account is homeMuted //Check if account is homeMuted
accountsVM.isMuted(Helper.getCurrentAccount(ProfileActivity.this), account).observe(this, result -> homeMuted = result != null && result); accountsVM.isMuted(currentAccount, account).observe(this, result -> homeMuted = result != null && result);
ContextCompat.registerReceiver(ProfileActivity.this, broadcast_data, new IntentFilter(Helper.BROADCAST_DATA), ContextCompat.RECEIVER_NOT_EXPORTED); LocalBroadcastManager.getInstance(ProfileActivity.this).registerReceiver(broadcast_data, new IntentFilter(Helper.BROADCAST_DATA));
//Search for featured tags
accountsVM.getAccountFeaturedTags(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account!=null?account.id:account_id).observe(this, featuredTags -> {
if(featuredTags != null && !featuredTags.isEmpty()) {
binding.featuredHashtagsContainer.setVisibility(View.VISIBLE);
binding.featuredHashtags.removeAllViews();
for(FeaturedTag featuredTag: featuredTags) {
if(featuredTag.statuses_count > 0 ) {
Chip chip = new Chip(ProfileActivity.this);
chip.setClickable(true);
chip.setEnsureMinTouchTargetSize(false);
chip.setText(String.format("#%s", featuredTag.name));
chip.setTextColor(ThemeHelper.getAttColor(ProfileActivity.this, R.attr.colorPrimary));
chip.setOnClickListener(v -> {
Bundle args = new Bundle();
args.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.ACCOUNT_TIMELINE);
if (account != null) {
args.putSerializable(Helper.ARG_CACHED_ACCOUNT_ID, account.id);
}
args.putString(Helper.ARG_TAGGED, featuredTag.name);
args.putBoolean(Helper.ARG_SHOW_PINNED, false);
args.putBoolean(Helper.ARG_SHOW_REPLIES, false);
args.putBoolean(Helper.ARG_SHOW_REBLOGS, false);
args.putBoolean(Helper.ARG_CHECK_REMOTELY, checkRemotely);
Intent intent = new Intent(ProfileActivity.this, TimelineActivity.class);
new CachedBundle(ProfileActivity.this).insertBundle(args, Helper.getCurrentAccount(ProfileActivity.this), bundleId -> {
Bundle _bundle = new Bundle();
_bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intent.putExtras(_bundle);
startActivity(intent);
});
});
binding.featuredHashtags.addView(chip);
}
}
} else {
binding.featuredHashtagsContainer.setVisibility(View.GONE);
}
});
} }
@ -289,15 +219,12 @@ public class ProfileActivity extends BaseActivity {
TabLayout.Tab followerTab = binding.accountTabLayout.getTabAt(2); TabLayout.Tab followerTab = binding.accountTabLayout.getTabAt(2);
if (statusTab != null) { if (statusTab != null) {
statusTab.setText(getString(R.string.status_cnt, Helper.withSuffix(account.statuses_count))); statusTab.setText(getString(R.string.status_cnt, Helper.withSuffix(account.statuses_count)));
TooltipCompat.setTooltipText(statusTab.view, String.valueOf(account.statuses_count));
} }
if (followingTab != null) { if (followingTab != null) {
followingTab.setText(getString(R.string.following_cnt, Helper.withSuffix(account.following_count))); followingTab.setText(getString(R.string.following_cnt, Helper.withSuffix(account.following_count)));
TooltipCompat.setTooltipText(followingTab.view, String.valueOf(account.following_count));
} }
if (followerTab != null) { if (followerTab != null) {
followerTab.setText(getString(R.string.followers_cnt, Helper.withSuffix(account.followers_count))); followerTab.setText(getString(R.string.followers_cnt, Helper.withSuffix(account.followers_count)));
TooltipCompat.setTooltipText(followerTab.view, String.valueOf(account.followers_count));
} }
} }
@ -329,64 +256,19 @@ public class ProfileActivity extends BaseActivity {
} }
}); });
binding.qrCodeGenerator.setVisibility(View.VISIBLE);
binding.qrCodeGenerator.setOnClickListener(v->{
QRGEncoder qrgEncoder = new QRGEncoder(account.url, null, QRGContents.Type.TEXT, 400);
Drawable logoDrawable = ContextCompat.getDrawable(ProfileActivity.this, R.drawable.fedilab_logo_bubbles);
if (logoDrawable != null) {
Bitmap bitmap = qrgEncoder.getBitmap();
MaterialAlertDialogBuilder alertadd = new MaterialAlertDialogBuilder(ProfileActivity.this);
PopupQrcodeBinding popupQrcodeBinding = PopupQrcodeBinding.inflate(getLayoutInflater());
popupQrcodeBinding.qrcodeImage.setImageBitmap(bitmap);
alertadd.setView(popupQrcodeBinding.getRoot());
alertadd.setNeutralButton(R.string.close, (dialog, which) -> dialog.dismiss());
alertadd.setPositiveButton(R.string.save, (dlg, which) -> {
File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
File targeted_folder = new File(path, getString(R.string.app_name));
if (!targeted_folder.exists()) {
boolean created = targeted_folder.mkdir();
if (!created) {
Toasty.error(ProfileActivity.this, getString(R.string.toast_error), Toasty.LENGTH_SHORT).show();
return;
}
}
String fileName = URLUtil.guessFileName(account.url, null, null);
if (fileName.endsWith(".bin")) {
fileName = fileName.replace(".bin", ".png");
}
fileName = fileName.replaceAll("@","");
File backupFile = new File(targeted_folder.getAbsolutePath() + "/" + fileName);
try (FileOutputStream out = new FileOutputStream(backupFile)) {
final Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
Uri uri = Uri.fromFile(backupFile);
intent.setDataAndType(uri, "image/jpeg");
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
Helper.notify_user(ProfileActivity.this, Helper.getCurrentAccount(ProfileActivity.this), intent, BitmapFactory.decodeResource(getResources(),
getMainLogo(ProfileActivity.this)), Helper.NotifType.STORE, getString(R.string.save_over), getString(R.string.download_from, fileName));
Toasty.success(ProfileActivity.this, getString(R.string.save_over), Toasty.LENGTH_LONG).show();
} catch (IOException e) {
e.printStackTrace();
}
});
alertadd.show();
}
});
//Retrieve relationship with the connected account //Retrieve relationship with the connected account
List<String> accountListToCheck = new ArrayList<>(); List<String> accountListToCheck = new ArrayList<>();
accountListToCheck.add(account.id); accountListToCheck.add(account.id);
//Retrieve relation ship //Retrieve relation ship
accountsVM.getRelationships(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, accountListToCheck).observe(ProfileActivity.this, relationShips -> { accountsVM.getRelationships(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, accountListToCheck).observe(ProfileActivity.this, relationShips -> {
if (relationShips != null && !relationShips.isEmpty()) { if (relationShips != null && relationShips.size() > 0) {
this.relationship = relationShips.get(0); this.relationship = relationShips.get(0);
updateAccount(); updateAccount();
} }
}); });
accountsVM.getFamiliarFollowers(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, accountListToCheck).observe(ProfileActivity.this, familiarFollowersList -> { accountsVM.getFamiliarFollowers(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, accountListToCheck).observe(ProfileActivity.this, familiarFollowersList -> {
if (familiarFollowersList != null && !familiarFollowersList.isEmpty()) { if (familiarFollowersList != null && familiarFollowersList.size() > 0) {
this.familiarFollowers = familiarFollowersList.get(0); this.familiarFollowers = familiarFollowersList.get(0);
updateAccount(); updateAccount();
} }
@ -397,32 +279,21 @@ public class ProfileActivity extends BaseActivity {
this.identityProofList = identityProofs; this.identityProofList = identityProofs;
updateAccount(); updateAccount();
}); });
//Animate emojis
if (account.emojis != null && account.emojis.size() > 0) {
boolean disableAnimatedEmoji = sharedpreferences.getBoolean(getString(R.string.SET_DISABLE_ANIMATED_EMOJI), false);
if (!disableAnimatedEmoji) {
scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
scheduledExecutorService.scheduleAtFixedRate(() -> binding.accountDn.invalidate(), 0, 130, TimeUnit.MILLISECONDS);
}
}
binding.accountTabLayout.clearOnTabSelectedListeners(); binding.accountTabLayout.clearOnTabSelectedListeners();
binding.accountTabLayout.removeAllTabs(); binding.accountTabLayout.removeAllTabs();
//Tablayout for timelines/following/followers //Tablayout for timelines/following/followers
FedilabProfileTLPageAdapter fedilabProfileTLPageAdapter = new FedilabProfileTLPageAdapter(getSupportFragmentManager(), account, checkRemotely); FedilabProfileTLPageAdapter fedilabProfileTLPageAdapter = new FedilabProfileTLPageAdapter(getSupportFragmentManager(), account, checkRemotely);
TabProfileCustomViewBinding tabMessagesView = TabProfileCustomViewBinding.inflate(getLayoutInflater()); binding.accountTabLayout.addTab(binding.accountTabLayout.newTab().setText(getString(R.string.status_cnt, Helper.withSuffix(account.statuses_count))));
TabProfileCustomViewBinding tabFollowingView = TabProfileCustomViewBinding.inflate(getLayoutInflater()); binding.accountTabLayout.addTab(binding.accountTabLayout.newTab().setText(getString(R.string.following_cnt, Helper.withSuffix(account.following_count))));
TabProfileCustomViewBinding tabFollowersView = TabProfileCustomViewBinding.inflate(getLayoutInflater()); binding.accountTabLayout.addTab(binding.accountTabLayout.newTab().setText(getString(R.string.followers_cnt, Helper.withSuffix(account.followers_count))));
tabMessagesView.title.setText(getString(R.string.toots));
tabMessagesView.count.setText(Helper.withSuffix(account.statuses_count));
tabFollowingView.title.setText(getString(R.string.following));
tabFollowingView.count.setText(Helper.withSuffix(account.following_count));
tabFollowersView.title.setText(getString(R.string.followers));
tabFollowersView.count.setText(Helper.withSuffix(account.followers_count));
TabLayout.Tab tabMessages = binding.accountTabLayout.newTab();
TabLayout.Tab tabFollowing = binding.accountTabLayout.newTab();
TabLayout.Tab tabFollowers = binding.accountTabLayout.newTab();
tabMessages.setCustomView(tabMessagesView.getRoot());
tabFollowing.setCustomView(tabFollowingView.getRoot());
tabFollowers.setCustomView(tabFollowersView.getRoot());
binding.accountTabLayout.addTab(tabMessages);
binding.accountTabLayout.addTab(tabFollowing);
binding.accountTabLayout.addTab(tabFollowers);
binding.accountViewpager.setAdapter(fedilabProfileTLPageAdapter); binding.accountViewpager.setAdapter(fedilabProfileTLPageAdapter);
binding.accountViewpager.setOffscreenPageLimit(3); binding.accountViewpager.setOffscreenPageLimit(3);
binding.accountViewpager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(binding.accountTabLayout)); binding.accountViewpager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(binding.accountTabLayout));
@ -470,18 +341,21 @@ public class ProfileActivity extends BaseActivity {
binding.accountPp.animate(); binding.accountPp.animate();
((Animatable) resource).start(); ((Animatable) resource).start();
} }
ActivityCompat.startPostponedEnterTransition(ProfileActivity.this);
} }
@Override @Override
public void onLoadFailed(@Nullable Drawable errorDrawable) { public void onLoadFailed(@Nullable Drawable errorDrawable) {
binding.profilePicture.setImageResource(R.drawable.ic_person); binding.profilePicture.setImageResource(R.drawable.ic_person);
binding.accountPp.setImageResource(R.drawable.ic_person); binding.accountPp.setImageResource(R.drawable.ic_person);
ActivityCompat.startPostponedEnterTransition(ProfileActivity.this);
} }
@Override @Override
public void onLoadCleared(@Nullable Drawable placeholder) { public void onLoadCleared(@Nullable Drawable placeholder) {
binding.profilePicture.setImageResource(R.drawable.ic_person); binding.profilePicture.setImageResource(R.drawable.ic_person);
binding.accountPp.setImageResource(R.drawable.ic_person); binding.accountPp.setImageResource(R.drawable.ic_person);
ActivityCompat.startPostponedEnterTransition(ProfileActivity.this);
} }
} }
); );
@ -545,27 +419,25 @@ public class ProfileActivity extends BaseActivity {
} }
binding.openRemoteProfile.setOnClickListener(v -> { binding.openRemoteProfile.setOnClickListener(v -> {
Intent intent = new Intent(ProfileActivity.this, ProfileActivity.class); Intent intent = new Intent(ProfileActivity.this, ProfileActivity.class);
Bundle args = new Bundle(); Bundle b = new Bundle();
args.putSerializable(Helper.ARG_ACCOUNT, account); b.putSerializable(Helper.ARG_ACCOUNT, account);
args.putSerializable(Helper.ARG_CHECK_REMOTELY, true); b.putSerializable(Helper.ARG_CHECK_REMOTELY, true);
new CachedBundle(ProfileActivity.this).insertBundle(args, Helper.getCurrentAccount(ProfileActivity.this), bundleId -> { intent.putExtras(b);
Bundle bundle = new Bundle(); ActivityOptionsCompat options = ActivityOptionsCompat
bundle.putLong(Helper.ARG_INTENT_ID, bundleId); .makeSceneTransitionAnimation(ProfileActivity.this, binding.profilePicture, getString(R.string.activity_porfile_pp));
intent.putExtras(bundle); startActivity(intent, options.toBundle());
startActivity(intent);
finish(); finish();
}); });
});
//Fields for profile //Fields for profile
List<Field> fields = account.fields; List<Field> fields = account.fields;
if (fields != null && !fields.isEmpty()) { if (fields != null && fields.size() > 0) {
FieldAdapter fieldAdapter = new FieldAdapter(fields, account); FieldAdapter fieldAdapter = new FieldAdapter(fields, account);
binding.fieldsContainer.setAdapter(fieldAdapter); binding.fieldsContainer.setAdapter(fieldAdapter);
binding.fieldsContainer.setLayoutManager(new LinearLayoutManager(ProfileActivity.this)); binding.fieldsContainer.setLayoutManager(new LinearLayoutManager(ProfileActivity.this));
} }
binding.accountDn.setText( binding.accountDn.setText(
account.getSpanDisplayNameEmoji(ProfileActivity.this, account.getSpanDisplayName(ProfileActivity.this,
new WeakReference<>(binding.accountDn)), new WeakReference<>(binding.accountDn)),
TextView.BufferType.SPANNABLE); TextView.BufferType.SPANNABLE);
@ -583,45 +455,14 @@ public class ProfileActivity extends BaseActivity {
}); });
binding.accountNote.setText( binding.accountNote.setText(
account.getSpanNote(ProfileActivity.this, account.getSpanNote(ProfileActivity.this,
new WeakReference<>(binding.accountNote), () -> { new WeakReference<>(binding.accountNote)),
//TODO: replace this hack
binding.accountNote.setText(
account.getSpanNote(ProfileActivity.this,
new WeakReference<>(binding.accountNote)), TextView.BufferType.SPANNABLE);
}),
TextView.BufferType.SPANNABLE); TextView.BufferType.SPANNABLE);
binding.accountNote.setMovementMethod(LinkMovementMethod.getInstance()); binding.accountNote.setMovementMethod(LinkMovementMethod.getInstance());
binding.bannerPp.setOnClickListener(v -> {
Intent intent = new Intent(ProfileActivity.this, MediaActivity.class);
Bundle args = new Bundle();
Attachment attachment = new Attachment();
attachment.description = account.acct;
attachment.preview_url = account.header;
attachment.url = account.header;
attachment.remote_url = account.header;
attachment.type = "image";
ArrayList<Attachment> attachments = new ArrayList<>();
attachments.add(attachment);
args.putSerializable(Helper.ARG_MEDIA_ARRAY, attachments);
args.putInt(Helper.ARG_MEDIA_POSITION, 1);
new CachedBundle(ProfileActivity.this).insertBundle(args, Helper.getCurrentAccount(ProfileActivity.this), bundleId -> {
Bundle bundle = new Bundle();
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intent.putExtras(bundle);
ActivityOptionsCompat options = ActivityOptionsCompat
.makeSceneTransitionAnimation(ProfileActivity.this, binding.accountPp, attachment.url);
// start the new activity
startActivity(intent, options.toBundle());
});
});
binding.accountPp.setOnClickListener(v -> { binding.accountPp.setOnClickListener(v -> {
Intent intent = new Intent(ProfileActivity.this, MediaActivity.class); Intent intent = new Intent(ProfileActivity.this, MediaActivity.class);
Bundle args = new Bundle(); Bundle b = new Bundle();
Attachment attachment = new Attachment(); Attachment attachment = new Attachment();
attachment.description = account.acct; attachment.description = account.acct;
attachment.preview_url = account.avatar; attachment.preview_url = account.avatar;
@ -630,18 +471,14 @@ public class ProfileActivity extends BaseActivity {
attachment.type = "image"; attachment.type = "image";
ArrayList<Attachment> attachments = new ArrayList<>(); ArrayList<Attachment> attachments = new ArrayList<>();
attachments.add(attachment); attachments.add(attachment);
args.putSerializable(Helper.ARG_MEDIA_ARRAY, attachments); b.putSerializable(Helper.ARG_MEDIA_ARRAY, attachments);
args.putInt(Helper.ARG_MEDIA_POSITION, 1); b.putInt(Helper.ARG_MEDIA_POSITION, 1);
new CachedBundle(ProfileActivity.this).insertBundle(args, Helper.getCurrentAccount(ProfileActivity.this), bundleId -> { intent.putExtras(b);
Bundle bundle = new Bundle();
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intent.putExtras(bundle);
ActivityOptionsCompat options = ActivityOptionsCompat ActivityOptionsCompat options = ActivityOptionsCompat
.makeSceneTransitionAnimation(ProfileActivity.this, binding.accountPp, attachment.url); .makeSceneTransitionAnimation(ProfileActivity.this, binding.accountPp, attachment.url);
// start the new activity // start the new activity
startActivity(intent, options.toBundle()); startActivity(intent, options.toBundle());
}); });
});
binding.accountFollow.setOnClickListener(v -> { binding.accountFollow.setOnClickListener(v -> {
@ -733,13 +570,13 @@ public class ProfileActivity extends BaseActivity {
* This methode is called to update the view once an action has been performed * This methode is called to update the view once an action has been performed
*/ */
private void updateAccount() { private void updateAccount() {
if (Helper.getCurrentAccount(ProfileActivity.this) == null || account == null) { if (currentAccount == null || account == null) {
return; return;
} }
//Manage indentity proofs if not yet displayed //Manage indentity proofs if not yet displayed
if (identityProofList != null && !identityProofList.isEmpty()) { if (identityProofList != null && identityProofList.size() > 0) {
ImageView identity_proofs_indicator = findViewById(R.id.identity_proofs_indicator); ImageView identity_proofs_indicator = findViewById(R.id.identity_proofs_indicator);
identity_proofs_indicator.setVisibility(View.VISIBLE); identity_proofs_indicator.setVisibility(View.VISIBLE);
//Recyclerview for identity proof has not been inflated yet //Recyclerview for identity proof has not been inflated yet
@ -760,7 +597,7 @@ public class ProfileActivity extends BaseActivity {
} }
} }
if (familiarFollowers != null && familiarFollowers.accounts != null && !familiarFollowers.accounts.isEmpty()) { if (familiarFollowers != null && familiarFollowers.accounts != null && familiarFollowers.accounts.size() > 0) {
binding.relatedAccounts.removeAllViews(); binding.relatedAccounts.removeAllViews();
for (Account account : familiarFollowers.accounts) { for (Account account : familiarFollowers.accounts) {
NotificationsRelatedAccountsBinding notificationsRelatedAccountsBinding = NotificationsRelatedAccountsBinding.inflate(LayoutInflater.from(ProfileActivity.this)); NotificationsRelatedAccountsBinding notificationsRelatedAccountsBinding = NotificationsRelatedAccountsBinding.inflate(LayoutInflater.from(ProfileActivity.this));
@ -768,15 +605,13 @@ public class ProfileActivity extends BaseActivity {
notificationsRelatedAccountsBinding.acc.setText(account.username); notificationsRelatedAccountsBinding.acc.setText(account.username);
notificationsRelatedAccountsBinding.relatedAccountContainer.setOnClickListener(v -> { notificationsRelatedAccountsBinding.relatedAccountContainer.setOnClickListener(v -> {
Intent intent = new Intent(ProfileActivity.this, ProfileActivity.class); Intent intent = new Intent(ProfileActivity.this, ProfileActivity.class);
Bundle args = new Bundle(); Bundle b = new Bundle();
args.putSerializable(Helper.ARG_ACCOUNT, account); b.putSerializable(Helper.ARG_ACCOUNT, account);
new CachedBundle(ProfileActivity.this).insertBundle(args, Helper.getCurrentAccount(ProfileActivity.this), bundleId -> { intent.putExtras(b);
Bundle bundle = new Bundle(); ActivityOptionsCompat options = ActivityOptionsCompat
bundle.putLong(Helper.ARG_INTENT_ID, bundleId); .makeSceneTransitionAnimation(ProfileActivity.this, notificationsRelatedAccountsBinding.profilePicture, getString(R.string.activity_porfile_pp));
intent.putExtras(bundle); // start the new activity
startActivity(intent); startActivity(intent, options.toBundle());
});
}); });
binding.relatedAccounts.addView(notificationsRelatedAccountsBinding.getRoot()); binding.relatedAccounts.addView(notificationsRelatedAccountsBinding.getRoot());
} }
@ -833,7 +668,7 @@ public class ProfileActivity extends BaseActivity {
//The value for account is from same server so id can be used //The value for account is from same server so id can be used
if (account.id.equals(Helper.getCurrentAccount(ProfileActivity.this).user_id)) { if (account.id.equals(currentAccount.user_id)) {
binding.accountFollow.setVisibility(View.GONE); binding.accountFollow.setVisibility(View.GONE);
binding.headerEditProfile.setVisibility(View.VISIBLE); binding.headerEditProfile.setVisibility(View.VISIBLE);
binding.headerEditProfile.bringToFront(); binding.headerEditProfile.bringToFront();
@ -1009,8 +844,6 @@ public class ProfileActivity extends BaseActivity {
instanceType = RemoteInstance.InstanceType.PIXELFED; instanceType = RemoteInstance.InstanceType.PIXELFED;
} else if (nodeInfo.software.name.compareToIgnoreCase("misskey") == 0) { } else if (nodeInfo.software.name.compareToIgnoreCase("misskey") == 0) {
instanceType = RemoteInstance.InstanceType.MISSKEY; instanceType = RemoteInstance.InstanceType.MISSKEY;
} else if (nodeInfo.software.name.compareToIgnoreCase("lemmy") == 0) {
instanceType = RemoteInstance.InstanceType.LEMMY;
} else if (nodeInfo.software.name.compareToIgnoreCase("gnu") == 0) { } else if (nodeInfo.software.name.compareToIgnoreCase("gnu") == 0) {
instanceType = RemoteInstance.InstanceType.GNU; instanceType = RemoteInstance.InstanceType.GNU;
} else { } else {
@ -1042,16 +875,11 @@ public class ProfileActivity extends BaseActivity {
new Pinned(ProfileActivity.this).insertPinned(finalPinned); new Pinned(ProfileActivity.this).insertPinned(finalPinned);
} }
runOnUiThread(() -> { runOnUiThread(() -> {
Bundle args = new Bundle(); Bundle b = new Bundle();
args.putBoolean(Helper.RECEIVE_REDRAW_TOPBAR, true); b.putBoolean(Helper.RECEIVE_REDRAW_TOPBAR, true);
Intent intentBD = new Intent(Helper.BROADCAST_DATA); Intent intentBD = new Intent(Helper.BROADCAST_DATA);
new CachedBundle(ProfileActivity.this).insertBundle(args, Helper.getCurrentAccount(ProfileActivity.this), bundleId -> { intentBD.putExtras(b);
Bundle bundle = new Bundle(); LocalBroadcastManager.getInstance(ProfileActivity.this).sendBroadcast(intentBD);
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intentBD.putExtras(bundle);
intentBD.setPackage(BuildConfig.APPLICATION_ID);
sendBroadcast(intentBD);
});
}); });
} catch (DBException e) { } catch (DBException e) {
e.printStackTrace(); e.printStackTrace();
@ -1113,7 +941,7 @@ public class ProfileActivity extends BaseActivity {
String[] codesArr; String[] codesArr;
String[] languagesArr; String[] languagesArr;
boolean[] presentArr; boolean[] presentArr;
if (storedLanguages != null && !storedLanguages.isEmpty()) { if (storedLanguages != null && storedLanguages.size() > 0) {
int i = 0; int i = 0;
codesArr = new String[storedLanguages.size()]; codesArr = new String[storedLanguages.size()];
languagesArr = new String[storedLanguages.size()]; languagesArr = new String[storedLanguages.size()];
@ -1171,30 +999,22 @@ public class ProfileActivity extends BaseActivity {
return true; return true;
} else if (itemId == R.id.action_direct_message) { } else if (itemId == R.id.action_direct_message) {
Intent intent = new Intent(ProfileActivity.this, ComposeActivity.class); Intent intent = new Intent(ProfileActivity.this, ComposeActivity.class);
Bundle args = new Bundle(); Bundle b = new Bundle();
args.putSerializable(Helper.ARG_ACCOUNT_MENTION, account); b.putSerializable(Helper.ARG_ACCOUNT_MENTION, account);
args.putString(Helper.ARG_VISIBILITY, "direct"); b.putString(Helper.ARG_VISIBILITY, "direct");
new CachedBundle(ProfileActivity.this).insertBundle(args, Helper.getCurrentAccount(ProfileActivity.this), bundleId -> { intent.putExtras(b);
Bundle bundle = new Bundle();
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intent.putExtras(bundle);
startActivity(intent); startActivity(intent);
});
return true; return true;
} else if (itemId == R.id.action_add_to_list) { } else if (itemId == R.id.action_add_to_list) {
TimelinesVM timelinesVM = new ViewModelProvider(ProfileActivity.this).get(TimelinesVM.class); TimelinesVM timelinesVM = new ViewModelProvider(ProfileActivity.this).get(TimelinesVM.class);
timelinesVM.getLists(BaseMainActivity.currentInstance, BaseMainActivity.currentToken) timelinesVM.getLists(BaseMainActivity.currentInstance, BaseMainActivity.currentToken)
.observe(ProfileActivity.this, mastodonLists -> { .observe(ProfileActivity.this, mastodonLists -> {
if (mastodonLists == null || mastodonLists.isEmpty()) { if (mastodonLists == null || mastodonLists.size() == 0) {
Toasty.info(ProfileActivity.this, getString(R.string.action_lists_empty), Toast.LENGTH_SHORT).show(); Toasty.info(ProfileActivity.this, getString(R.string.action_lists_empty), Toast.LENGTH_SHORT).show();
return; return;
} }
accountsVM.getListContainingAccount(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id) accountsVM.getListContainingAccount(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id)
.observe(ProfileActivity.this, mastodonListUserIs -> { .observe(ProfileActivity.this, mastodonListUserIs -> {
if (mastodonListUserIs == null) {
mastodonListUserIs = new ArrayList<>();
}
Collections.sort(mastodonLists, (obj1, obj2) -> obj1.title.compareToIgnoreCase(obj2.title));
AlertDialog.Builder builderSingle = new MaterialAlertDialogBuilder(ProfileActivity.this); AlertDialog.Builder builderSingle = new MaterialAlertDialogBuilder(ProfileActivity.this);
builderSingle.setTitle(getString(R.string.action_lists_add_to)); builderSingle.setTitle(getString(R.string.action_lists_add_to));
builderSingle.setPositiveButton(R.string.close, (dialog, which) -> dialog.dismiss()); builderSingle.setPositiveButton(R.string.close, (dialog, which) -> dialog.dismiss());
@ -1263,15 +1083,12 @@ public class ProfileActivity extends BaseActivity {
return true; return true;
} else if (itemId == R.id.action_mention) { } else if (itemId == R.id.action_mention) {
Intent intent; Intent intent;
Bundle b;
intent = new Intent(ProfileActivity.this, ComposeActivity.class); intent = new Intent(ProfileActivity.this, ComposeActivity.class);
Bundle args = new Bundle(); b = new Bundle();
args.putSerializable(Helper.ARG_ACCOUNT_MENTION, account); b.putSerializable(Helper.ARG_ACCOUNT_MENTION, account);
new CachedBundle(ProfileActivity.this).insertBundle(args, Helper.getCurrentAccount(ProfileActivity.this), bundleId -> { intent.putExtras(b);
Bundle bundle = new Bundle();
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intent.putExtras(bundle);
startActivity(intent); startActivity(intent);
});
return true; return true;
} else if (itemId == R.id.action_mute) { } else if (itemId == R.id.action_mute) {
AlertDialog.Builder builderInner; AlertDialog.Builder builderInner;
@ -1315,7 +1132,7 @@ public class ProfileActivity extends BaseActivity {
builderInner.setNeutralButton(R.string.cancel, (dialog, which) -> dialog.dismiss()); builderInner.setNeutralButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
if (homeMuted) { if (homeMuted) {
builderInner.setTitle(R.string.unmute_home); builderInner.setTitle(R.string.unmute_home);
builderInner.setPositiveButton(R.string.action_unmute, (dialog, which) -> accountsVM.unmuteHome(Helper.getCurrentAccount(ProfileActivity.this), account) builderInner.setPositiveButton(R.string.action_unmute, (dialog, which) -> accountsVM.unmuteHome(currentAccount, account)
.observe(ProfileActivity.this, account -> { .observe(ProfileActivity.this, account -> {
homeMuted = false; homeMuted = false;
invalidateOptionsMenu(); invalidateOptionsMenu();
@ -1323,7 +1140,7 @@ public class ProfileActivity extends BaseActivity {
})); }));
} else { } else {
builderInner.setTitle(R.string.mute_home); builderInner.setTitle(R.string.mute_home);
builderInner.setPositiveButton(R.string.action_mute, (dialog, which) -> accountsVM.muteHome(Helper.getCurrentAccount(ProfileActivity.this), account) builderInner.setPositiveButton(R.string.action_mute, (dialog, which) -> accountsVM.muteHome(currentAccount, account)
.observe(ProfileActivity.this, account -> { .observe(ProfileActivity.this, account -> {
homeMuted = true; homeMuted = true;
invalidateOptionsMenu(); invalidateOptionsMenu();
@ -1338,15 +1155,24 @@ public class ProfileActivity extends BaseActivity {
}); });
return true; return true;
} else if (itemId == R.id.action_report) { } else if (itemId == R.id.action_report) {
Intent intent = new Intent(ProfileActivity.this, ReportActivity.class); AlertDialog.Builder builderInner = new MaterialAlertDialogBuilder(ProfileActivity.this);
Bundle args = new Bundle(); builderInner.setTitle(R.string.report_account);
args.putSerializable(Helper.ARG_ACCOUNT, account); //Text for report
new CachedBundle(ProfileActivity.this).insertBundle(args, Helper.getCurrentAccount(ProfileActivity.this), bundleId -> { EditText input = new EditText(ProfileActivity.this);
Bundle bundle = new Bundle(); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
bundle.putLong(Helper.ARG_INTENT_ID, bundleId); LinearLayout.LayoutParams.MATCH_PARENT,
intent.putExtras(bundle); LinearLayout.LayoutParams.WRAP_CONTENT);
startActivity(intent); input.setLayoutParams(lp);
builderInner.setView(input);
builderInner.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
builderInner.setPositiveButton(R.string.yes, (dialog, which) -> {
String comment = null;
if (input.getText() != null)
comment = input.getText().toString();
accountsVM.report(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, null, null, null, comment, false);
dialog.dismiss();
}); });
builderInner.show();
return true; return true;
} else if (itemId == R.id.action_block) { } else if (itemId == R.id.action_block) {
AlertDialog.Builder builderInner = new MaterialAlertDialogBuilder(ProfileActivity.this); AlertDialog.Builder builderInner = new MaterialAlertDialogBuilder(ProfileActivity.this);
@ -1370,18 +1196,20 @@ public class ProfileActivity extends BaseActivity {
target = account.id; target = account.id;
} }
switch (doActionAccount) { switch (doActionAccount) {
case BLOCK -> case BLOCK:
accountsVM.block(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, target) accountsVM.block(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, target)
.observe(ProfileActivity.this, relationShip -> { .observe(ProfileActivity.this, relationShip -> {
this.relationship = relationShip; this.relationship = relationShip;
updateAccount(); updateAccount();
}); });
case UNBLOCK -> break;
case UNBLOCK:
accountsVM.unblock(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, target) accountsVM.unblock(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, target)
.observe(ProfileActivity.this, relationShip -> { .observe(ProfileActivity.this, relationShip -> {
this.relationship = relationShip; this.relationship = relationShip;
updateAccount(); updateAccount();
}); });
break;
} }
dialog.dismiss(); dialog.dismiss();
}); });
@ -1410,11 +1238,11 @@ public class ProfileActivity extends BaseActivity {
@Override @Override
public void onDestroy() { public void onDestroy() {
try { if (scheduledExecutorService != null) {
unregisterReceiver(broadcast_data); scheduledExecutorService.shutdownNow();
} catch (IllegalArgumentException e) { scheduledExecutorService = null;
e.printStackTrace();
} }
LocalBroadcastManager.getInstance(ProfileActivity.this).unregisterReceiver(broadcast_data);
super.onDestroy(); super.onDestroy();
} }

View file

@ -31,6 +31,7 @@ import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
@ -42,13 +43,11 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import app.fedilab.android.BuildConfig;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.activities.MainActivity; import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.databinding.ActivityReorderTabsBinding; import app.fedilab.android.databinding.ActivityReorderTabsBinding;
import app.fedilab.android.databinding.PopupSearchInstanceBinding; import app.fedilab.android.databinding.PopupSearchInstanceBinding;
import app.fedilab.android.mastodon.client.entities.app.BottomMenu; import app.fedilab.android.mastodon.client.entities.app.BottomMenu;
import app.fedilab.android.mastodon.client.entities.app.CachedBundle;
import app.fedilab.android.mastodon.client.entities.app.InstanceSocial; import app.fedilab.android.mastodon.client.entities.app.InstanceSocial;
import app.fedilab.android.mastodon.client.entities.app.Pinned; import app.fedilab.android.mastodon.client.entities.app.Pinned;
import app.fedilab.android.mastodon.client.entities.app.PinnedTimeline; import app.fedilab.android.mastodon.client.entities.app.PinnedTimeline;
@ -200,22 +199,16 @@ public class ReorderTimelinesActivity extends BaseBarActivity implements OnStart
} else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.peertube_instance) { } else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.peertube_instance) {
url = "https://" + instanceName + "/api/v1/videos/"; url = "https://" + instanceName + "/api/v1/videos/";
} else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.pixelfed_instance) { } else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.pixelfed_instance) {
url = "https://" + instanceName + "/api/pixelfed/v2/discover/posts/trending?range=daily"; url = "https://" + instanceName + "/api/v1/timelines/public";
} else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.misskey_instance) { } else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.misskey_instance) {
url = "https://" + instanceName + "/api/notes/local-timeline"; url = "https://" + instanceName + "/api/notes/local-timeline";
getCall = false; getCall = false;
} else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.lemmy_instance) {
url = "https://" + instanceName + "/api/v3/post/list";
} else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.gnu_instance) { } else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.gnu_instance) {
url = "https://" + instanceName + "/api/statuses/public_timeline.json"; url = "https://" + instanceName + "/api/statuses/public_timeline.json";
} else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.twitter_accounts) { } else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.twitter_accounts) {
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(ReorderTimelinesActivity.this); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(ReorderTimelinesActivity.this);
String nitterHost = sharedpreferences.getString(getString(R.string.SET_NITTER_HOST), getString(R.string.DEFAULT_NITTER_HOST)).toLowerCase(); String nitterHost = sharedpreferences.getString(getString(R.string.SET_NITTER_HOST), getString(R.string.DEFAULT_NITTER_HOST)).toLowerCase();
url = "https://" + nitterHost + "/" + instanceName.replaceAll("[ ]+", ",").replaceAll("\\s", "") + "/with_replies/rss"; url = "https://" + nitterHost + "/" + instanceName.replaceAll("[ ]+", ",").replaceAll("\\s", "") + "/rss";
}else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.twitter_tags) {
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(ReorderTimelinesActivity.this);
String nitterHost = sharedpreferences.getString(getString(R.string.SET_NITTER_HOST), getString(R.string.DEFAULT_NITTER_HOST)).toLowerCase();
url = "https://" + nitterHost + "/search?f=tweets&q=" + instanceName.replaceAll("[ ]+", "+or+").replaceAll("\\s", "") + "&e-nativeretweets=on";
} }
OkHttpClient client = new OkHttpClient.Builder() OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS) .connectTimeout(10, TimeUnit.SECONDS)
@ -255,14 +248,10 @@ public class ReorderTimelinesActivity extends BaseBarActivity implements OnStart
instanceType = RemoteInstance.InstanceType.PIXELFED; instanceType = RemoteInstance.InstanceType.PIXELFED;
} else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.misskey_instance) { } else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.misskey_instance) {
instanceType = RemoteInstance.InstanceType.MISSKEY; instanceType = RemoteInstance.InstanceType.MISSKEY;
} else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.lemmy_instance) {
instanceType = RemoteInstance.InstanceType.LEMMY;
} else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.gnu_instance) { } else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.gnu_instance) {
instanceType = RemoteInstance.InstanceType.GNU; instanceType = RemoteInstance.InstanceType.GNU;
} else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.twitter_accounts) { } else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.twitter_accounts) {
instanceType = RemoteInstance.InstanceType.NITTER; instanceType = RemoteInstance.InstanceType.NITTER;
} else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.twitter_tags) {
instanceType = RemoteInstance.InstanceType.NITTER_TAG;
} }
RemoteInstance remoteInstance = new RemoteInstance(); RemoteInstance remoteInstance = new RemoteInstance();
remoteInstance.type = instanceType; remoteInstance.type = instanceType;
@ -291,16 +280,11 @@ public class ReorderTimelinesActivity extends BaseBarActivity implements OnStart
} }
} }
reorderTabAdapter.notifyItemInserted(pinned.pinnedTimelines.size()); reorderTabAdapter.notifyItemInserted(pinned.pinnedTimelines.size());
Bundle args = new Bundle(); Bundle b = new Bundle();
args.putBoolean(Helper.RECEIVE_REDRAW_TOPBAR, true); b.putBoolean(Helper.RECEIVE_REDRAW_TOPBAR, true);
Intent intentBD = new Intent(Helper.BROADCAST_DATA); Intent intentBD = new Intent(Helper.BROADCAST_DATA);
new CachedBundle(ReorderTimelinesActivity.this).insertBundle(args, Helper.getCurrentAccount(ReorderTimelinesActivity.this), bundleId -> { intentBD.putExtras(b);
Bundle bundle = new Bundle(); LocalBroadcastManager.getInstance(ReorderTimelinesActivity.this).sendBroadcast(intentBD);
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intentBD.putExtras(bundle);
intentBD.setPackage(BuildConfig.APPLICATION_ID);
sendBroadcast(intentBD);
});
}); });
} else { } else {
runOnUiThread(() -> Toasty.warning(ReorderTimelinesActivity.this, getString(R.string.toast_instance_unavailable), Toast.LENGTH_LONG).show()); runOnUiThread(() -> Toasty.warning(ReorderTimelinesActivity.this, getString(R.string.toast_instance_unavailable), Toast.LENGTH_LONG).show());
@ -384,29 +368,18 @@ public class ReorderTimelinesActivity extends BaseBarActivity implements OnStart
super.onPause(); super.onPause();
if (changes) { if (changes) {
//Update menu //Update menu
Bundle args = new Bundle(); Bundle b = new Bundle();
args.putBoolean(Helper.RECEIVE_REDRAW_TOPBAR, true); b.putBoolean(Helper.RECEIVE_REDRAW_TOPBAR, true);
Intent intentBD = new Intent(Helper.BROADCAST_DATA); Intent intentBD = new Intent(Helper.BROADCAST_DATA);
new CachedBundle(ReorderTimelinesActivity.this).insertBundle(args, Helper.getCurrentAccount(ReorderTimelinesActivity.this), bundleId -> { intentBD.putExtras(b);
Bundle bundle = new Bundle(); LocalBroadcastManager.getInstance(ReorderTimelinesActivity.this).sendBroadcast(intentBD);
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intentBD.putExtras(bundle);
intentBD.setPackage(BuildConfig.APPLICATION_ID);
sendBroadcast(intentBD);
});
} }
if (bottomChanges) { if (bottomChanges) {
Bundle args = new Bundle(); Bundle b = new Bundle();
args.putBoolean(Helper.RECEIVE_REDRAW_BOTTOM, true); b.putBoolean(Helper.RECEIVE_REDRAW_BOTTOM, true);
Intent intentBD = new Intent(Helper.BROADCAST_DATA); Intent intentBD = new Intent(Helper.BROADCAST_DATA);
new CachedBundle(ReorderTimelinesActivity.this).insertBundle(args, Helper.getCurrentAccount(ReorderTimelinesActivity.this), bundleId -> { intentBD.putExtras(b);
Bundle bundle = new Bundle(); LocalBroadcastManager.getInstance(ReorderTimelinesActivity.this).sendBroadcast(intentBD);
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intentBD.putExtras(bundle);
intentBD.setPackage(BuildConfig.APPLICATION_ID);
sendBroadcast(intentBD);
});
} }
} }

View file

@ -14,7 +14,6 @@ package app.fedilab.android.mastodon.activities;
* You should have received a copy of the GNU General Public License along with Fedilab; if not, * You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import android.os.Bundle; import android.os.Bundle;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
@ -22,12 +21,13 @@ import android.widget.RadioButton;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.widget.LinearLayoutCompat; import androidx.appcompat.widget.LinearLayoutCompat;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects;
import app.fedilab.android.BaseMainActivity; import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R; import app.fedilab.android.R;
@ -35,7 +35,6 @@ import app.fedilab.android.databinding.ActivityReportBinding;
import app.fedilab.android.mastodon.client.entities.api.Account; import app.fedilab.android.mastodon.client.entities.api.Account;
import app.fedilab.android.mastodon.client.entities.api.RelationShip; import app.fedilab.android.mastodon.client.entities.api.RelationShip;
import app.fedilab.android.mastodon.client.entities.api.Status; import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.mastodon.client.entities.app.CachedBundle;
import app.fedilab.android.mastodon.client.entities.app.Timeline; import app.fedilab.android.mastodon.client.entities.app.Timeline;
import app.fedilab.android.mastodon.helper.Helper; import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.ui.drawer.RulesAdapter; import app.fedilab.android.mastodon.ui.drawer.RulesAdapter;
@ -70,21 +69,11 @@ public class ReportActivity extends BaseBarActivity {
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
} }
Bundle args = getIntent().getExtras();
if (args != null) {
long bundleId = args.getLong(Helper.ARG_INTENT_ID, -1);
if (bundleId != -1) {
new CachedBundle(ReportActivity.this).getBundle(bundleId, Helper.getCurrentAccount(ReportActivity.this), this::initializeAfterBundle);
} else {
initializeAfterBundle(args);
}
}
}
private void initializeAfterBundle(Bundle bundle) { Bundle b = getIntent().getExtras();
if (bundle != null) { if (b != null) {
status = (Status) bundle.getSerializable(Helper.ARG_STATUS); status = (Status) b.getSerializable(Helper.ARG_STATUS);
account = (Account) bundle.getSerializable(Helper.ARG_ACCOUNT); account = (Account) b.getSerializable(Helper.ARG_ACCOUNT);
} }
if (account == null && status != null) { if (account == null && status != null) {
account = status.account; account = status.account;
@ -149,6 +138,7 @@ public class ReportActivity extends BaseBarActivity {
}); });
} }
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) { if (item.getItemId() == android.R.id.home) {
@ -231,27 +221,21 @@ public class ReportActivity extends BaseBarActivity {
private void switchToSpam() { private void switchToSpam() {
fragment = new FragmentMastodonTimeline(); fragment = new FragmentMastodonTimeline();
Bundle args = new Bundle(); Bundle bundle = new Bundle();
args.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.ACCOUNT_TIMELINE); bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.ACCOUNT_TIMELINE);
args.putBoolean(Helper.ARG_SHOW_PINNED, false); bundle.putSerializable(Helper.ARG_ACCOUNT, account);
args.putBoolean(Helper.ARG_SHOW_REPLIES, true);
args.putBoolean(Helper.ARG_SHOW_REBLOGS, false);
args.putBoolean(Helper.ARG_CHECK_REMOTELY, false);
args.putString(Helper.ARG_VIEW_MODEL_KEY, "FEDILAB_REPORT_" + account.acct);
if (account != null) {
args.putSerializable(Helper.ARG_CACHED_ACCOUNT_ID, account.id);
}
//Set to display statuses with less options //Set to display statuses with less options
args.putBoolean(Helper.ARG_MINIFIED, true); bundle.putBoolean(Helper.ARG_MINIFIED, true);
if (status != null) { if (status != null) {
status.isChecked = true; status.isChecked = true;
args.putSerializable(Helper.ARG_STATUS_REPORT, status); bundle.putSerializable(Helper.ARG_STATUS_REPORT, status);
} }
new CachedBundle(ReportActivity.this).insertBundle(args, Helper.getCurrentAccount(ReportActivity.this), bundleId -> { fragment.setArguments(bundle);
Bundle bundle = new Bundle(); FragmentManager fragmentManager = getSupportFragmentManager();
bundle.putLong(Helper.ARG_INTENT_ID, bundleId); FragmentTransaction fragmentTransaction =
Helper.addFragment(getSupportFragmentManager(), R.id.fram_spam_container, fragment, bundle, null, null); fragmentManager.beginTransaction();
}); fragmentTransaction.replace(R.id.fram_spam_container, fragment);
fragmentTransaction.commit();
binding.actionButton.setText(R.string.next); binding.actionButton.setText(R.string.next);
binding.actionButton.setOnClickListener(v -> { binding.actionButton.setOnClickListener(v -> {
@ -263,27 +247,22 @@ public class ReportActivity extends BaseBarActivity {
private void switchToSomethingElse() { private void switchToSomethingElse() {
fragment = new FragmentMastodonTimeline(); fragment = new FragmentMastodonTimeline();
Bundle args = new Bundle(); Bundle bundle = new Bundle();
args.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.ACCOUNT_TIMELINE); bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.ACCOUNT_TIMELINE);
args.putBoolean(Helper.ARG_SHOW_PINNED, false); bundle.putSerializable(Helper.ARG_ACCOUNT, account);
args.putBoolean(Helper.ARG_SHOW_REPLIES, true);
args.putBoolean(Helper.ARG_SHOW_REBLOGS, false);
args.putBoolean(Helper.ARG_CHECK_REMOTELY, false);
args.putString(Helper.ARG_VIEW_MODEL_KEY, "FEDILAB_REPORT_" + account.acct);
if (account != null) {
args.putSerializable(Helper.ARG_CACHED_ACCOUNT_ID, account.id);
}
//Set to display statuses with less options //Set to display statuses with less options
args.putBoolean(Helper.ARG_MINIFIED, true); bundle.putBoolean(Helper.ARG_MINIFIED, true);
if (status != null) { if (status != null) {
status.isChecked = true; status.isChecked = true;
args.putSerializable(Helper.ARG_STATUS_REPORT, status); bundle.putSerializable(Helper.ARG_STATUS_REPORT, status);
} }
new CachedBundle(ReportActivity.this).insertBundle(args, Helper.getCurrentAccount(ReportActivity.this), bundleId -> { fragment.setArguments(bundle);
Bundle bundle = new Bundle(); FragmentManager fragmentManager = getSupportFragmentManager();
bundle.putLong(Helper.ARG_INTENT_ID, bundleId); FragmentTransaction fragmentTransaction =
Helper.addFragment(getSupportFragmentManager(), R.id.fram_se_container, fragment, bundle, null, null); fragmentManager.beginTransaction();
}); fragmentTransaction.replace(R.id.fram_se_container, fragment);
fragmentTransaction.commit();
binding.actionButton.setText(R.string.next); binding.actionButton.setText(R.string.next);
binding.actionButton.setOnClickListener(v -> { binding.actionButton.setOnClickListener(v -> {
if (category == null) { if (category == null) {
@ -312,7 +291,7 @@ public class ReportActivity extends BaseBarActivity {
if (fragment != null) { if (fragment != null) {
statusIds = fragment.getCheckedStatusesId(); statusIds = fragment.getCheckedStatusesId();
} }
comment = Objects.requireNonNull(binding.reportMessage.getText()).toString(); comment = binding.reportMessage.getText().toString();
binding.actionButton.setEnabled(false); binding.actionButton.setEnabled(false);
accountsVM.report(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, category, statusIds, ruleIds, comment, forward) accountsVM.report(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, category, statusIds, ruleIds, comment, forward)
.observe(ReportActivity.this, report -> { .observe(ReportActivity.this, report -> {

View file

@ -14,6 +14,7 @@ package app.fedilab.android.mastodon.activities;
* You should have received a copy of the GNU General Public License along with Fedilab; if not, * You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.BaseMainActivity.currentAccount;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
@ -28,7 +29,6 @@ import com.google.android.material.tabs.TabLayout;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivityScheduledBinding; import app.fedilab.android.databinding.ActivityScheduledBinding;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.helper.MastodonHelper; import app.fedilab.android.mastodon.helper.MastodonHelper;
import app.fedilab.android.mastodon.ui.pageadapter.FedilabScheduledPageAdapter; import app.fedilab.android.mastodon.ui.pageadapter.FedilabScheduledPageAdapter;
@ -55,7 +55,7 @@ public class ScheduledActivity extends BaseActivity {
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this);
float scale = sharedpreferences.getFloat(getString(R.string.SET_FONT_SCALE), 1.1f); float scale = sharedpreferences.getFloat(getString(R.string.SET_FONT_SCALE), 1.1f);
binding.title.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18 * 1.1f / scale); binding.title.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18 * 1.1f / scale);
MastodonHelper.loadPPMastodon(binding.profilePicture, Helper.getCurrentAccount(ScheduledActivity.this).mastodon_account); MastodonHelper.loadPPMastodon(binding.profilePicture, currentAccount.mastodon_account);
binding.title.setText(R.string.scheduled); binding.title.setText(R.string.scheduled);
binding.scheduleTablayout.addTab(binding.scheduleTablayout.newTab().setText(getString(R.string.toots_server))); binding.scheduleTablayout.addTab(binding.scheduleTablayout.newTab().setText(getString(R.string.toots_server)));
binding.scheduleTablayout.addTab(binding.scheduleTablayout.newTab().setText(getString(R.string.toots_client))); binding.scheduleTablayout.addTab(binding.scheduleTablayout.newTab().setText(getString(R.string.toots_client)));

View file

@ -67,10 +67,10 @@ import es.dmoral.toasty.Toasty;
public class SearchResultTabActivity extends BaseBarActivity { public class SearchResultTabActivity extends BaseBarActivity {
public Boolean tagEmpty, accountEmpty;
private String search; private String search;
private ActivitySearchResultTabsBinding binding; private ActivitySearchResultTabsBinding binding;
private TabLayout.Tab initial; private TabLayout.Tab initial;
public Boolean tagEmpty, accountEmpty;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -82,6 +82,7 @@ public class SearchResultTabActivity extends BaseBarActivity {
Bundle b = getIntent().getExtras(); Bundle b = getIntent().getExtras();
if (b != null) { if (b != null) {
search = b.getString(Helper.ARG_SEARCH_KEYWORD, null); search = b.getString(Helper.ARG_SEARCH_KEYWORD, null);
} }
if (search == null) { if (search == null) {
Toasty.error(SearchResultTabActivity.this, getString(R.string.toast_error_search), Toast.LENGTH_LONG).show(); Toasty.error(SearchResultTabActivity.this, getString(R.string.toast_error_search), Toast.LENGTH_LONG).show();
@ -113,11 +114,14 @@ public class SearchResultTabActivity extends BaseBarActivity {
Fragment fragment; Fragment fragment;
if (binding.searchViewpager.getAdapter() != null) { if (binding.searchViewpager.getAdapter() != null) {
fragment = (Fragment) binding.searchViewpager.getAdapter().instantiateItem(binding.searchViewpager, tab.getPosition()); fragment = (Fragment) binding.searchViewpager.getAdapter().instantiateItem(binding.searchViewpager, tab.getPosition());
if (fragment instanceof FragmentMastodonAccount fragmentMastodonAccount) { if (fragment instanceof FragmentMastodonAccount) {
FragmentMastodonAccount fragmentMastodonAccount = ((FragmentMastodonAccount) fragment);
fragmentMastodonAccount.scrollToTop(); fragmentMastodonAccount.scrollToTop();
} else if (fragment instanceof FragmentMastodonTimeline fragmentMastodonTimeline) { } else if (fragment instanceof FragmentMastodonTimeline) {
FragmentMastodonTimeline fragmentMastodonTimeline = ((FragmentMastodonTimeline) fragment);
fragmentMastodonTimeline.scrollToTop(); fragmentMastodonTimeline.scrollToTop();
} else if (fragment instanceof FragmentMastodonTag fragmentMastodonTag) { } else if (fragment instanceof FragmentMastodonTag) {
FragmentMastodonTag fragmentMastodonTag = ((FragmentMastodonTag) fragment);
fragmentMastodonTag.scrollToTop(); fragmentMastodonTag.scrollToTop();
} }
} }
@ -132,9 +136,6 @@ public class SearchResultTabActivity extends BaseBarActivity {
inflater.inflate(R.menu.menu_search, menu); inflater.inflate(R.menu.menu_search, menu);
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE); SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView(); SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
if (searchView == null) {
return true;
}
if (search != null) { if (search != null) {
searchView.setQuery(search, false); searchView.setQuery(search, false);
} }
@ -301,34 +302,29 @@ public class SearchResultTabActivity extends BaseBarActivity {
@Override @Override
public Fragment getItem(int position) { public Fragment getItem(int position) {
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
FragmentMastodonTimeline fragmentMastodonTimeline;
switch (position) { switch (position) {
case 0 -> { case 0:
FragmentMastodonTag fragmentMastodonTag = new FragmentMastodonTag(); FragmentMastodonTag fragmentMastodonTag = new FragmentMastodonTag();
bundle.putString(Helper.ARG_SEARCH_KEYWORD, search); bundle.putString(Helper.ARG_SEARCH_KEYWORD, search);
fragmentMastodonTag.setArguments(bundle); fragmentMastodonTag.setArguments(bundle);
return fragmentMastodonTag; return fragmentMastodonTag;
} case 1:
case 1 -> {
FragmentMastodonAccount fragmentMastodonAccount = new FragmentMastodonAccount(); FragmentMastodonAccount fragmentMastodonAccount = new FragmentMastodonAccount();
bundle.putString(Helper.ARG_SEARCH_KEYWORD, search); bundle.putString(Helper.ARG_SEARCH_KEYWORD, search);
fragmentMastodonAccount.setArguments(bundle); fragmentMastodonAccount.setArguments(bundle);
return fragmentMastodonAccount; return fragmentMastodonAccount;
} case 2:
case 2 -> { FragmentMastodonTimeline fragmentMastodonTimeline = new FragmentMastodonTimeline();
fragmentMastodonTimeline = new FragmentMastodonTimeline();
bundle.putString(Helper.ARG_SEARCH_KEYWORD, search); bundle.putString(Helper.ARG_SEARCH_KEYWORD, search);
fragmentMastodonTimeline.setArguments(bundle); fragmentMastodonTimeline.setArguments(bundle);
return fragmentMastodonTimeline; return fragmentMastodonTimeline;
} default:
default -> {
fragmentMastodonTimeline = new FragmentMastodonTimeline(); fragmentMastodonTimeline = new FragmentMastodonTimeline();
bundle.putString(Helper.ARG_SEARCH_KEYWORD_CACHE, search); bundle.putString(Helper.ARG_SEARCH_KEYWORD_CACHE, search);
fragmentMastodonTimeline.setArguments(bundle); fragmentMastodonTimeline.setArguments(bundle);
return fragmentMastodonTimeline; return fragmentMastodonTimeline;
} }
} }
}
@Override @Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) { public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {

View file

@ -38,7 +38,6 @@ import app.fedilab.android.mastodon.client.entities.api.Account;
import app.fedilab.android.mastodon.client.entities.api.Accounts; import app.fedilab.android.mastodon.client.entities.api.Accounts;
import app.fedilab.android.mastodon.client.entities.api.RelationShip; import app.fedilab.android.mastodon.client.entities.api.RelationShip;
import app.fedilab.android.mastodon.client.entities.api.Status; import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.mastodon.client.entities.app.CachedBundle;
import app.fedilab.android.mastodon.helper.Helper; import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.ui.drawer.AccountAdapter; import app.fedilab.android.mastodon.ui.drawer.AccountAdapter;
import app.fedilab.android.mastodon.viewmodel.mastodon.AccountsVM; import app.fedilab.android.mastodon.viewmodel.mastodon.AccountsVM;
@ -71,23 +70,12 @@ public class StatusInfoActivity extends BaseActivity {
} }
accountList = new ArrayList<>(); accountList = new ArrayList<>();
checkRemotely = false; checkRemotely = false;
Bundle args = getIntent().getExtras(); Bundle b = getIntent().getExtras();
if (args != null) { if (b != null) {
long bundleId = args.getLong(Helper.ARG_INTENT_ID, -1); type = (typeOfInfo) b.getSerializable(Helper.ARG_TYPE_OF_INFO);
new CachedBundle(StatusInfoActivity.this).getBundle(bundleId, Helper.getCurrentAccount(StatusInfoActivity.this), this::initializeAfterBundle); status = (Status) b.getSerializable(Helper.ARG_STATUS);
} else { checkRemotely = b.getBoolean(Helper.ARG_CHECK_REMOTELY, false);
initializeAfterBundle(null);
} }
}
private void initializeAfterBundle(Bundle bundle) {
if (bundle != null) {
type = (typeOfInfo) bundle.getSerializable(Helper.ARG_TYPE_OF_INFO);
status = (Status) bundle.getSerializable(Helper.ARG_STATUS);
checkRemotely = bundle.getBoolean(Helper.ARG_CHECK_REMOTELY, false);
}
if (type == null || status == null) { if (type == null || status == null) {
finish(); finish();
return; return;
@ -150,7 +138,6 @@ public class StatusInfoActivity extends BaseActivity {
} }
} }
private void manageView(Accounts accounts) { private void manageView(Accounts accounts) {
binding.loadingNextAccounts.setVisibility(View.GONE); binding.loadingNextAccounts.setVisibility(View.GONE);
if (accountList != null && accounts != null && accounts.accounts != null) { if (accountList != null && accounts != null && accounts.accounts != null) {
@ -158,8 +145,11 @@ public class StatusInfoActivity extends BaseActivity {
if (!checkRemotely) { if (!checkRemotely) {
fetchRelationShip(accounts.accounts, position); fetchRelationShip(accounts.accounts, position);
} }
int startId = 0;
//There are some statuses present in the timeline //There are some statuses present in the timeline
int startId = accountList.size(); if (accountList.size() > 0) {
startId = accountList.size();
}
accountList.addAll(accounts.accounts); accountList.addAll(accounts.accounts);
max_id = accounts.pagination.max_id; max_id = accounts.pagination.max_id;
flagLoading = accounts.pagination.max_id == null; flagLoading = accounts.pagination.max_id == null;

View file

@ -1,106 +0,0 @@
package app.fedilab.android.mastodon.activities;
/* Copyright 2023 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* 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.os.Bundle;
import android.view.MenuItem;
import org.jetbrains.annotations.NotNull;
import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivityTimelineBinding;
import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.mastodon.client.entities.app.CachedBundle;
import app.fedilab.android.mastodon.client.entities.app.PinnedTimeline;
import app.fedilab.android.mastodon.client.entities.app.Timeline;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonTimeline;
public class TimelineActivity extends BaseBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
app.fedilab.android.databinding.ActivityTimelineBinding binding = ActivityTimelineBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
Bundle args = getIntent().getExtras();
if (args != null) {
long bundleId = args.getLong(Helper.ARG_INTENT_ID, -1);
new CachedBundle(TimelineActivity.this).getBundle(bundleId, Helper.getCurrentAccount(TimelineActivity.this), this::initializeAfterBundle);
} else {
initializeAfterBundle(null);
}
}
private void initializeAfterBundle(Bundle bundle) {
Timeline.TimeLineEnum timelineType = null;
String lemmy_post_id = null;
PinnedTimeline pinnedTimeline = null;
String tagged = null;
String timelineAccountId = null;
Status status = null;
if (bundle != null) {
timelineType = (Timeline.TimeLineEnum) bundle.get(Helper.ARG_TIMELINE_TYPE);
lemmy_post_id = bundle.getString(Helper.ARG_LEMMY_POST_ID, null);
pinnedTimeline = (PinnedTimeline) bundle.getSerializable(Helper.ARG_REMOTE_INSTANCE);
status = (Status) bundle.getSerializable(Helper.ARG_STATUS);
tagged = bundle.getString(Helper.ARG_TAGGED, null);
timelineAccountId = bundle.getString(Helper.ARG_CACHED_ACCOUNT_ID, null);
}
if (pinnedTimeline != null && pinnedTimeline.remoteInstance != null) {
setTitle(pinnedTimeline.remoteInstance.host);
}
if(tagged != null) {
setTitle(String.format("#%s",tagged));
}
FragmentMastodonTimeline fragmentMastodonTimeline = new FragmentMastodonTimeline();
Bundle args = new Bundle();
args.putSerializable(Helper.ARG_TIMELINE_TYPE, timelineType);
args.putSerializable(Helper.ARG_REMOTE_INSTANCE, pinnedTimeline);
args.putSerializable(Helper.ARG_LEMMY_POST_ID, lemmy_post_id);
args.putSerializable(Helper.ARG_TAGGED, tagged);
args.putSerializable(Helper.ARG_CACHED_ACCOUNT_ID, timelineAccountId);
if (status != null) {
args.putSerializable(Helper.ARG_STATUS, status);
}
new CachedBundle(TimelineActivity.this).insertBundle(args, Helper.getCurrentAccount(TimelineActivity.this), bundleId -> {
Bundle bundle1 = new Bundle();
bundle1.putLong(Helper.ARG_INTENT_ID, bundleId);
fragmentMastodonTimeline.setArguments(bundle1);
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container_view, fragmentMastodonTimeline).commit();
});
}
@Override
public boolean onOptionsItemSelected(@NotNull MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
}

View file

@ -33,7 +33,6 @@ import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivityTrendsBinding; import app.fedilab.android.databinding.ActivityTrendsBinding;
import app.fedilab.android.mastodon.client.entities.app.Timeline; import app.fedilab.android.mastodon.client.entities.app.Timeline;
import app.fedilab.android.mastodon.helper.Helper; import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonLink;
import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonTag; import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonTag;
import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonTimeline; import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonTimeline;
@ -57,7 +56,6 @@ public class TrendsActivity extends BaseBarActivity {
binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.tags))); binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.tags)));
binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.toots))); binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.toots)));
binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.links)));
binding.searchTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { binding.searchTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override @Override
public void onTabSelected(TabLayout.Tab tab) { public void onTabSelected(TabLayout.Tab tab) {
@ -73,12 +71,12 @@ public class TrendsActivity extends BaseBarActivity {
Fragment fragment; Fragment fragment;
if (binding.trendsViewpager.getAdapter() != null) { if (binding.trendsViewpager.getAdapter() != null) {
fragment = (Fragment) binding.trendsViewpager.getAdapter().instantiateItem(binding.trendsViewpager, tab.getPosition()); fragment = (Fragment) binding.trendsViewpager.getAdapter().instantiateItem(binding.trendsViewpager, tab.getPosition());
if (fragment instanceof FragmentMastodonTimeline fragmentMastodonTimeline) { if (fragment instanceof FragmentMastodonTimeline) {
FragmentMastodonTimeline fragmentMastodonTimeline = ((FragmentMastodonTimeline) fragment);
fragmentMastodonTimeline.scrollToTop(); fragmentMastodonTimeline.scrollToTop();
} else if (fragment instanceof FragmentMastodonTag fragmentMastodonTag) { } else if (fragment instanceof FragmentMastodonTag) {
FragmentMastodonTag fragmentMastodonTag = ((FragmentMastodonTag) fragment);
fragmentMastodonTag.scrollToTop(); fragmentMastodonTag.scrollToTop();
}else if (fragment instanceof FragmentMastodonLink fragmentMastodonLink) {
fragmentMastodonLink.scrollToTop();
} }
} }
} }
@ -133,17 +131,11 @@ public class TrendsActivity extends BaseBarActivity {
bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.TREND_TAG); bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.TREND_TAG);
fragmentMastodonTag.setArguments(bundle); fragmentMastodonTag.setArguments(bundle);
return fragmentMastodonTag; return fragmentMastodonTag;
} else if(position == 1) { }
FragmentMastodonTimeline fragmentMastodonTimeline = new FragmentMastodonTimeline(); FragmentMastodonTimeline fragmentMastodonTimeline = new FragmentMastodonTimeline();
bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.TREND_MESSAGE); bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.TREND_MESSAGE);
fragmentMastodonTimeline.setArguments(bundle); fragmentMastodonTimeline.setArguments(bundle);
return fragmentMastodonTimeline; return fragmentMastodonTimeline;
} else {
FragmentMastodonLink fragmentMastodonLink = new FragmentMastodonLink();
bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.TREND_LINK);
fragmentMastodonLink.setArguments(bundle);
return fragmentMastodonLink;
}
} }
@Override @Override
@ -152,7 +144,7 @@ public class TrendsActivity extends BaseBarActivity {
@Override @Override
public int getCount() { public int getCount() {
return 3; return 2;
} }
} }
} }

View file

@ -61,7 +61,6 @@ import app.fedilab.android.mastodon.activities.MediaActivity;
import app.fedilab.android.mastodon.client.entities.api.Attachment; import app.fedilab.android.mastodon.client.entities.api.Attachment;
import app.fedilab.android.mastodon.client.entities.api.admin.AdminAccount; import app.fedilab.android.mastodon.client.entities.api.admin.AdminAccount;
import app.fedilab.android.mastodon.client.entities.api.admin.AdminIp; import app.fedilab.android.mastodon.client.entities.api.admin.AdminIp;
import app.fedilab.android.mastodon.client.entities.app.CachedBundle;
import app.fedilab.android.mastodon.helper.Helper; import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.helper.MastodonHelper; import app.fedilab.android.mastodon.helper.MastodonHelper;
import app.fedilab.android.mastodon.helper.SpannableHelper; import app.fedilab.android.mastodon.helper.SpannableHelper;
@ -88,32 +87,18 @@ public class AdminAccountActivity extends BaseActivity {
setContentView(binding.getRoot()); setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
ActionBar actionBar = getSupportActionBar(); ActionBar actionBar = getSupportActionBar();
Bundle args = getIntent().getExtras(); Bundle b = getIntent().getExtras();
adminAccount = null; adminAccount = null;
if (b != null) {
adminAccount = (AdminAccount) b.getSerializable(Helper.ARG_ACCOUNT);
account_id = b.getString(Helper.ARG_ACCOUNT_ID, null);
}
postponeEnterTransition(); postponeEnterTransition();
//Remove title //Remove title
if (actionBar != null) { if (actionBar != null) {
actionBar.setDisplayShowTitleEnabled(false); actionBar.setDisplayShowTitleEnabled(false);
} }
if (args != null) {
long bundleId = args.getLong(Helper.ARG_INTENT_ID, -1);
new CachedBundle(AdminAccountActivity.this).getBundle(bundleId, Helper.getCurrentAccount(AdminAccountActivity.this), this::initializeAfterBundle);
} else {
initializeAfterBundle(null);
}
}
private void initializeAfterBundle(Bundle bundle) {
if (bundle != null) {
adminAccount = (AdminAccount) bundle.getSerializable(Helper.ARG_ACCOUNT);
account_id = bundle.getString(Helper.ARG_ACCOUNT_ID, null);
}
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this);
float scale = sharedpreferences.getFloat(getString(R.string.SET_FONT_SCALE), 1.1f); float scale = sharedpreferences.getFloat(getString(R.string.SET_FONT_SCALE), 1.1f);
binding.title.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18 * 1.1f / scale); binding.title.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18 * 1.1f / scale);
@ -190,6 +175,8 @@ public class AdminAccountActivity extends BaseActivity {
initializeView(adminAccount); initializeView(adminAccount);
} }
}); });
} }
private void initializeView(AdminAccount adminAccount) { private void initializeView(AdminAccount adminAccount) {
@ -323,7 +310,7 @@ public class AdminAccountActivity extends BaseActivity {
binding.accountDn.setText( binding.accountDn.setText(
adminAccount.account.getSpanDisplayNameEmoji(AdminAccountActivity.this, adminAccount.account.getSpanDisplayName(AdminAccountActivity.this,
new WeakReference<>(binding.accountDn)), new WeakReference<>(binding.accountDn)),
TextView.BufferType.SPANNABLE); TextView.BufferType.SPANNABLE);
binding.accountUn.setText(String.format("@%s", adminAccount.account.acct)); binding.accountUn.setText(String.format("@%s", adminAccount.account.acct));
@ -342,7 +329,7 @@ public class AdminAccountActivity extends BaseActivity {
MastodonHelper.loadPPMastodon(binding.accountPp, adminAccount.account); MastodonHelper.loadPPMastodon(binding.accountPp, adminAccount.account);
binding.accountPp.setOnClickListener(v -> { binding.accountPp.setOnClickListener(v -> {
Intent intent = new Intent(AdminAccountActivity.this, MediaActivity.class); Intent intent = new Intent(AdminAccountActivity.this, MediaActivity.class);
Bundle args = new Bundle(); Bundle b = new Bundle();
Attachment attachment = new Attachment(); Attachment attachment = new Attachment();
attachment.description = adminAccount.account.acct; attachment.description = adminAccount.account.acct;
attachment.preview_url = adminAccount.account.avatar; attachment.preview_url = adminAccount.account.avatar;
@ -351,18 +338,14 @@ public class AdminAccountActivity extends BaseActivity {
attachment.type = "image"; attachment.type = "image";
ArrayList<Attachment> attachments = new ArrayList<>(); ArrayList<Attachment> attachments = new ArrayList<>();
attachments.add(attachment); attachments.add(attachment);
args.putSerializable(Helper.ARG_MEDIA_ARRAY, attachments); b.putSerializable(Helper.ARG_MEDIA_ARRAY, attachments);
args.putInt(Helper.ARG_MEDIA_POSITION, 1); b.putInt(Helper.ARG_MEDIA_POSITION, 1);
new CachedBundle(AdminAccountActivity.this).insertBundle(args, Helper.getCurrentAccount(AdminAccountActivity.this), bundleId -> { intent.putExtras(b);
Bundle bundle = new Bundle();
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intent.putExtras(bundle);
ActivityOptionsCompat options = ActivityOptionsCompat ActivityOptionsCompat options = ActivityOptionsCompat
.makeSceneTransitionAnimation(AdminAccountActivity.this, binding.accountPp, attachment.url); .makeSceneTransitionAnimation(AdminAccountActivity.this, binding.accountPp, attachment.url);
// start the new activity // start the new activity
startActivity(intent, options.toBundle()); startActivity(intent, options.toBundle());
}); });
});
binding.accountDate.setText(Helper.shortDateToString(adminAccount.created_at)); binding.accountDate.setText(Helper.shortDateToString(adminAccount.created_at));

View file

@ -14,7 +14,6 @@ package app.fedilab.android.mastodon.activities.admin;
* You should have received a copy of the GNU General Public License along with Fedilab; if not, * You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.mastodon.activities.admin.AdminActionActivity.AdminEnum.ACCOUNT; import static app.fedilab.android.mastodon.activities.admin.AdminActionActivity.AdminEnum.ACCOUNT;
import static app.fedilab.android.mastodon.activities.admin.AdminActionActivity.AdminEnum.DOMAIN; import static app.fedilab.android.mastodon.activities.admin.AdminActionActivity.AdminEnum.DOMAIN;
import static app.fedilab.android.mastodon.activities.admin.AdminActionActivity.AdminEnum.REPORT; import static app.fedilab.android.mastodon.activities.admin.AdminActionActivity.AdminEnum.REPORT;
@ -27,13 +26,12 @@ import android.os.Bundle;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import androidx.activity.OnBackPressedCallback;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction; import androidx.fragment.app.FragmentTransaction;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
@ -44,7 +42,6 @@ import app.fedilab.android.databinding.PopupAdminFilterAccountsBinding;
import app.fedilab.android.databinding.PopupAdminFilterReportsBinding; import app.fedilab.android.databinding.PopupAdminFilterReportsBinding;
import app.fedilab.android.mastodon.activities.BaseBarActivity; import app.fedilab.android.mastodon.activities.BaseBarActivity;
import app.fedilab.android.mastodon.client.entities.api.admin.AdminDomainBlock; import app.fedilab.android.mastodon.client.entities.api.admin.AdminDomainBlock;
import app.fedilab.android.mastodon.client.entities.app.CachedBundle;
import app.fedilab.android.mastodon.helper.Helper; import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.helper.ThemeHelper; import app.fedilab.android.mastodon.helper.ThemeHelper;
import app.fedilab.android.mastodon.ui.fragment.admin.FragmentAdminAccount; import app.fedilab.android.mastodon.ui.fragment.admin.FragmentAdminAccount;
@ -65,21 +62,18 @@ public class AdminActionActivity extends BaseBarActivity {
private final BroadcastReceiver mReceiver = new BroadcastReceiver() { private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
Bundle args = intent.getExtras(); Bundle b = intent.getExtras();
if (args != null) { if (b != null) {
long bundleId = args.getLong(Helper.ARG_INTENT_ID, -1); AdminDomainBlock adminDomainBlock = (AdminDomainBlock) b.getSerializable(Helper.ARG_ADMIN_DOMAINBLOCK);
new CachedBundle(AdminActionActivity.this).getBundle(bundleId, Helper.getCurrentAccount(AdminActionActivity.this), bundle -> { AdminDomainBlock adminDomainBlockDelete = (AdminDomainBlock) b.getSerializable(Helper.ARG_ADMIN_DOMAINBLOCK_DELETE);
AdminDomainBlock adminDomainBlock = (AdminDomainBlock) bundle.getSerializable(Helper.ARG_ADMIN_DOMAINBLOCK);
AdminDomainBlock adminDomainBlockDelete = (AdminDomainBlock) bundle.getSerializable(Helper.ARG_ADMIN_DOMAINBLOCK_DELETE);
if (adminDomainBlock != null && adminDomainBlock.domain != null && fragmentAdminDomain != null) { if (adminDomainBlock != null && adminDomainBlock.domain != null && fragmentAdminDomain != null) {
fragmentAdminDomain.update(adminDomainBlock); fragmentAdminDomain.update(adminDomainBlock);
} }
if (adminDomainBlockDelete != null && fragmentAdminDomain != null) { if (adminDomainBlockDelete != null && fragmentAdminDomain != null) {
fragmentAdminDomain.delete(adminDomainBlockDelete); fragmentAdminDomain.delete(adminDomainBlockDelete);
} }
});
} }
} }
}; };
@ -89,9 +83,7 @@ public class AdminActionActivity extends BaseBarActivity {
binding = ActivityAdminActionsBinding.inflate(getLayoutInflater()); binding = ActivityAdminActionsBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, new IntentFilter(Helper.BROADCAST_DATA));
ContextCompat.registerReceiver(AdminActionActivity.this, mReceiver, new IntentFilter(Helper.BROADCAST_DATA), ContextCompat.RECEIVER_NOT_EXPORTED);
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
} }
@ -100,38 +92,12 @@ public class AdminActionActivity extends BaseBarActivity {
binding.accounts.setOnClickListener(v -> displayTimeline(ACCOUNT)); binding.accounts.setOnClickListener(v -> displayTimeline(ACCOUNT));
binding.domains.setOnClickListener(v -> displayTimeline(DOMAIN)); binding.domains.setOnClickListener(v -> displayTimeline(DOMAIN));
getOnBackPressedDispatcher().addCallback(new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
if (canGoBack) {
canGoBack = false;
ThemeHelper.slideViewsToRight(binding.fragmentContainer, binding.buttonContainer, () -> {
if (fragmentAdminReport != null) {
fragmentAdminReport.onDestroyView();
fragmentAdminReport = null;
}
if (fragmentAdminAccount != null) {
fragmentAdminAccount.onDestroyView();
fragmentAdminAccount = null;
}
if (fragmentAdminDomain != null) {
fragmentAdminDomain.onDestroyView();
fragmentAdminDomain = null;
}
setTitle(R.string.administration);
invalidateOptionsMenu();
});
} else {
finish();
}
}
});
} }
private void displayTimeline(AdminEnum type) { private void displayTimeline(AdminEnum type) {
canGoBack = true; canGoBack = true;
if (type == REPORT) { if (type == REPORT) {
ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> {
fragmentAdminReport = new FragmentAdminReport(); fragmentAdminReport = new FragmentAdminReport();
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, type); bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, type);
@ -142,10 +108,9 @@ public class AdminActionActivity extends BaseBarActivity {
fragmentManager.beginTransaction(); fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, fragmentAdminReport); fragmentTransaction.replace(R.id.fragment_container, fragmentAdminReport);
fragmentTransaction.commit(); fragmentTransaction.commit();
ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> {
}); });
} else if (type == ACCOUNT) { } else if (type == ACCOUNT) {
ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> {
fragmentAdminAccount = new FragmentAdminAccount(); fragmentAdminAccount = new FragmentAdminAccount();
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, type); bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, type);
@ -156,10 +121,9 @@ public class AdminActionActivity extends BaseBarActivity {
fragmentManager.beginTransaction(); fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, fragmentAdminAccount); fragmentTransaction.replace(R.id.fragment_container, fragmentAdminAccount);
fragmentTransaction.commit(); fragmentTransaction.commit();
ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> {
}); });
} else if (type == DOMAIN) { } else if (type == DOMAIN) {
ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> {
fragmentAdminDomain = new FragmentAdminDomain(); fragmentAdminDomain = new FragmentAdminDomain();
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, type); bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, type);
@ -170,14 +134,18 @@ public class AdminActionActivity extends BaseBarActivity {
fragmentManager.beginTransaction(); fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, fragmentAdminDomain); fragmentTransaction.replace(R.id.fragment_container, fragmentAdminDomain);
fragmentTransaction.commit(); fragmentTransaction.commit();
ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> {
}); });
} }
switch (type) { switch (type) {
case REPORT -> setTitle(R.string.reports); case REPORT:
case ACCOUNT -> setTitle(R.string.accounts); setTitle(R.string.reports);
case DOMAIN -> setTitle(R.string.domains); break;
case ACCOUNT:
setTitle(R.string.accounts);
break;
case DOMAIN:
setTitle(R.string.domains);
break;
} }
invalidateOptionsMenu(); invalidateOptionsMenu();
} }
@ -193,7 +161,7 @@ public class AdminActionActivity extends BaseBarActivity {
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) { if (item.getItemId() == android.R.id.home) {
getOnBackPressedDispatcher().onBackPressed(); onBackPressed();
return true; return true;
} else if (item.getItemId() == R.id.action_filter) { } else if (item.getItemId() == R.id.action_filter) {
if (getTitle().toString().equalsIgnoreCase(getString(R.string.accounts))) { if (getTitle().toString().equalsIgnoreCase(getString(R.string.accounts))) {
@ -350,14 +318,35 @@ public class AdminActionActivity extends BaseBarActivity {
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
if (mReceiver != null) { if (mReceiver != null) {
try { LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
unregisterReceiver(mReceiver);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
} }
} }
@Override
public void onBackPressed() {
if (canGoBack) {
canGoBack = false;
ThemeHelper.slideViewsToRight(binding.fragmentContainer, binding.buttonContainer, () -> {
if (fragmentAdminReport != null) {
fragmentAdminReport.onDestroyView();
fragmentAdminReport = null;
}
if (fragmentAdminAccount != null) {
fragmentAdminAccount.onDestroyView();
fragmentAdminAccount = null;
}
if (fragmentAdminDomain != null) {
fragmentAdminDomain.onDestroyView();
fragmentAdminDomain = null;
}
setTitle(R.string.administration);
invalidateOptionsMenu();
});
} else {
super.onBackPressed();
}
}
public enum AdminEnum { public enum AdminEnum {
@SerializedName("REPORT") @SerializedName("REPORT")

View file

@ -26,18 +26,15 @@ import android.widget.ArrayAdapter;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.Objects;
import app.fedilab.android.BuildConfig;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.activities.MainActivity; import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.databinding.ActivityAdminDomainblockBinding; import app.fedilab.android.databinding.ActivityAdminDomainblockBinding;
import app.fedilab.android.mastodon.activities.BaseBarActivity; import app.fedilab.android.mastodon.activities.BaseBarActivity;
import app.fedilab.android.mastodon.client.entities.api.admin.AdminDomainBlock; import app.fedilab.android.mastodon.client.entities.api.admin.AdminDomainBlock;
import app.fedilab.android.mastodon.client.entities.app.CachedBundle;
import app.fedilab.android.mastodon.helper.Helper; import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.viewmodel.mastodon.AdminVM; import app.fedilab.android.mastodon.viewmodel.mastodon.AdminVM;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
@ -100,9 +97,9 @@ public class AdminDomainBlockActivity extends BaseBarActivity {
binding.rejectReports.setOnCheckedChangeListener((compoundButton, checked) -> adminDomainBlock.reject_reports = checked); binding.rejectReports.setOnCheckedChangeListener((compoundButton, checked) -> adminDomainBlock.reject_reports = checked);
adminVM = new ViewModelProvider(AdminDomainBlockActivity.this).get(AdminVM.class); adminVM = new ViewModelProvider(AdminDomainBlockActivity.this).get(AdminVM.class);
binding.saveChanges.setOnClickListener(v -> { binding.saveChanges.setOnClickListener(v -> {
adminDomainBlock.domain = Objects.requireNonNull(binding.domain.getText()).toString().trim(); adminDomainBlock.domain = binding.domain.getText().toString().trim();
adminDomainBlock.public_comment = Objects.requireNonNull(binding.publicComment.getText()).toString().trim(); adminDomainBlock.public_comment = binding.publicComment.getText().toString().trim();
adminDomainBlock.private_comment = Objects.requireNonNull(binding.privateComment.getText()).toString().trim(); adminDomainBlock.private_comment = binding.privateComment.getText().toString().trim();
adminVM.createOrUpdateDomainBlock(MainActivity.currentInstance, MainActivity.currentToken, adminDomainBlock) adminVM.createOrUpdateDomainBlock(MainActivity.currentInstance, MainActivity.currentToken, adminDomainBlock)
.observe(AdminDomainBlockActivity.this, adminDomainBlockResult -> { .observe(AdminDomainBlockActivity.this, adminDomainBlockResult -> {
if (adminDomainBlockResult != null) { if (adminDomainBlockResult != null) {
@ -110,17 +107,9 @@ public class AdminDomainBlockActivity extends BaseBarActivity {
} else { } else {
Toasty.error(AdminDomainBlockActivity.this, getString(R.string.toast_error), Toasty.LENGTH_SHORT).show(); Toasty.error(AdminDomainBlockActivity.this, getString(R.string.toast_error), Toasty.LENGTH_SHORT).show();
} }
Intent intent = new Intent(Helper.BROADCAST_DATA); Intent intent = new Intent(Helper.BROADCAST_DATA).putExtra(Helper.ARG_ADMIN_DOMAINBLOCK, adminDomainBlockResult);
Bundle args = new Bundle(); LocalBroadcastManager.getInstance(AdminDomainBlockActivity.this).sendBroadcast(intent);
args.putSerializable(Helper.ARG_ADMIN_DOMAINBLOCK, adminDomainBlockResult);
new CachedBundle(AdminDomainBlockActivity.this).insertBundle(args, Helper.getCurrentAccount(AdminDomainBlockActivity.this), bundleId -> {
Bundle bundle = new Bundle();
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intent.putExtras(bundle);
intent.setPackage(BuildConfig.APPLICATION_ID);
sendBroadcast(intent);
finish(); finish();
});
} }
); );
}); });
@ -146,18 +135,9 @@ public class AdminDomainBlockActivity extends BaseBarActivity {
.setPositiveButton(R.string.unblock_domain, (dialog, which) -> { .setPositiveButton(R.string.unblock_domain, (dialog, which) -> {
adminVM.deleteDomain(MainActivity.currentInstance, MainActivity.currentToken, adminDomainBlock.id) adminVM.deleteDomain(MainActivity.currentInstance, MainActivity.currentToken, adminDomainBlock.id)
.observe(AdminDomainBlockActivity.this, adminDomainBlockResult -> { .observe(AdminDomainBlockActivity.this, adminDomainBlockResult -> {
Intent intent = new Intent(Helper.BROADCAST_DATA); Intent intent = new Intent(Helper.BROADCAST_DATA).putExtra(Helper.ARG_ADMIN_DOMAINBLOCK_DELETE, adminDomainBlock);
Bundle args = new Bundle(); LocalBroadcastManager.getInstance(AdminDomainBlockActivity.this).sendBroadcast(intent);
args.putSerializable(Helper.ARG_ADMIN_DOMAINBLOCK_DELETE, adminDomainBlock);
new CachedBundle(AdminDomainBlockActivity.this).insertBundle(args, Helper.getCurrentAccount(AdminDomainBlockActivity.this), bundleId -> {
Bundle bundle = new Bundle();
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intent.putExtras(bundle);
intent.setPackage(BuildConfig.APPLICATION_ID);
sendBroadcast(intent);
finish(); finish();
});
} }
); );
dialog.dismiss(); dialog.dismiss();

View file

@ -46,6 +46,7 @@ import com.bumptech.glide.request.transition.Transition;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
@ -61,7 +62,6 @@ 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.Attachment;
import app.fedilab.android.mastodon.client.entities.api.admin.AdminAccount; import app.fedilab.android.mastodon.client.entities.api.admin.AdminAccount;
import app.fedilab.android.mastodon.client.entities.api.admin.AdminIp; import app.fedilab.android.mastodon.client.entities.api.admin.AdminIp;
import app.fedilab.android.mastodon.client.entities.app.CachedBundle;
import app.fedilab.android.mastodon.helper.Helper; import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.helper.MastodonHelper; import app.fedilab.android.mastodon.helper.MastodonHelper;
import app.fedilab.android.mastodon.helper.SpannableHelper; import app.fedilab.android.mastodon.helper.SpannableHelper;
@ -87,8 +87,19 @@ public class AdminReportActivity extends BaseBarActivity {
setContentView(binding.getRoot()); setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
ActionBar actionBar = getSupportActionBar(); ActionBar actionBar = getSupportActionBar();
Bundle args = getIntent().getExtras(); Bundle b = getIntent().getExtras();
if (b != null) {
adminAccount = (AdminAccount) b.getSerializable(Helper.ARG_ACCOUNT);
if (adminAccount != null) {
account = adminAccount.account;
}
}
postponeEnterTransition(); postponeEnterTransition();
//Remove title
if (actionBar != null) {
actionBar.setDisplayShowTitleEnabled(false);
}
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this);
float scale = sharedpreferences.getFloat(getString(R.string.SET_FONT_SCALE), 1.1f); float scale = sharedpreferences.getFloat(getString(R.string.SET_FONT_SCALE), 1.1f);
binding.title.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18 * 1.1f / scale); binding.title.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18 * 1.1f / scale);
@ -96,28 +107,6 @@ public class AdminReportActivity extends BaseBarActivity {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true);
} }
//Remove title
if (actionBar != null) {
actionBar.setDisplayShowTitleEnabled(false);
}
if (args != null) {
long bundleId = args.getLong(Helper.ARG_INTENT_ID, -1);
new CachedBundle(AdminReportActivity.this).getBundle(bundleId, Helper.getCurrentAccount(AdminReportActivity.this), this::initializeAfterBundle);
} else {
initializeAfterBundle(null);
}
}
private void initializeAfterBundle(Bundle bundle) {
if (bundle != null) {
adminAccount = (AdminAccount) bundle.getSerializable(Helper.ARG_ACCOUNT);
if (adminAccount != null) {
account = adminAccount.account;
}
}
if (account != null) { if (account != null) {
initializeView(account); initializeView(account);
} else { } else {
@ -157,10 +146,10 @@ public class AdminReportActivity extends BaseBarActivity {
lastActive.append(Helper.shortDateToString(ip.used_at)).append(" - ").append(ip.ip).append("\r\n"); lastActive.append(Helper.shortDateToString(ip.used_at)).append(" - ").append(ip.ip).append("\r\n");
} }
} }
if (lastActive.toString().trim().isEmpty()) { if (lastActive.toString().trim().length() == 0) {
binding.lastActiveContainer.setVisibility(View.GONE); binding.lastActiveContainer.setVisibility(View.GONE);
} }
if (adminAccount.email == null || adminAccount.email.trim().isEmpty()) { if (adminAccount.email == null || adminAccount.email.trim().length() == 0) {
binding.emailContainer.setVisibility(View.GONE); binding.emailContainer.setVisibility(View.GONE);
} }
binding.lastActive.setText(lastActive.toString()); binding.lastActive.setText(lastActive.toString());
@ -259,8 +248,11 @@ public class AdminReportActivity extends BaseBarActivity {
}); });
//Retrieve relationship with the connected account
List<String> accountListToCheck = new ArrayList<>();
accountListToCheck.add(account.id);
//Animate emojis //Animate emojis
if (account.emojis != null && !account.emojis.isEmpty()) { if (account.emojis != null && account.emojis.size() > 0) {
boolean disableAnimatedEmoji = sharedpreferences.getBoolean(getString(R.string.SET_DISABLE_ANIMATED_EMOJI), false); boolean disableAnimatedEmoji = sharedpreferences.getBoolean(getString(R.string.SET_DISABLE_ANIMATED_EMOJI), false);
if (!disableAnimatedEmoji) { if (!disableAnimatedEmoji) {
scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
@ -336,7 +328,7 @@ public class AdminReportActivity extends BaseBarActivity {
} }
binding.accountDn.setText( binding.accountDn.setText(
account.getSpanDisplayNameEmoji(AdminReportActivity.this, account.getSpanDisplayName(AdminReportActivity.this,
new WeakReference<>(binding.accountDn)), new WeakReference<>(binding.accountDn)),
TextView.BufferType.SPANNABLE); TextView.BufferType.SPANNABLE);
binding.accountUn.setText(String.format("@%s", account.acct)); binding.accountUn.setText(String.format("@%s", account.acct));
@ -355,7 +347,7 @@ public class AdminReportActivity extends BaseBarActivity {
MastodonHelper.loadPPMastodon(binding.accountPp, account); MastodonHelper.loadPPMastodon(binding.accountPp, account);
binding.accountPp.setOnClickListener(v -> { binding.accountPp.setOnClickListener(v -> {
Intent intent = new Intent(AdminReportActivity.this, MediaActivity.class); Intent intent = new Intent(AdminReportActivity.this, MediaActivity.class);
Bundle args = new Bundle(); Bundle b = new Bundle();
Attachment attachment = new Attachment(); Attachment attachment = new Attachment();
attachment.description = account.acct; attachment.description = account.acct;
attachment.preview_url = account.avatar; attachment.preview_url = account.avatar;
@ -364,17 +356,14 @@ public class AdminReportActivity extends BaseBarActivity {
attachment.type = "image"; attachment.type = "image";
ArrayList<Attachment> attachments = new ArrayList<>(); ArrayList<Attachment> attachments = new ArrayList<>();
attachments.add(attachment); attachments.add(attachment);
args.putSerializable(Helper.ARG_MEDIA_ARRAY, attachments); b.putSerializable(Helper.ARG_MEDIA_ARRAY, attachments);
args.putInt(Helper.ARG_MEDIA_POSITION, 1); b.putInt(Helper.ARG_MEDIA_POSITION, 1);
new CachedBundle(AdminReportActivity.this).insertBundle(args, Helper.getCurrentAccount(AdminReportActivity.this), bundleId -> { intent.putExtras(b);
Bundle bundle = new Bundle();
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intent.putExtras(bundle);
ActivityOptionsCompat options = ActivityOptionsCompat ActivityOptionsCompat options = ActivityOptionsCompat
.makeSceneTransitionAnimation(AdminReportActivity.this, binding.accountPp, attachment.url); .makeSceneTransitionAnimation(AdminReportActivity.this, binding.accountPp, attachment.url);
// start the new activity
startActivity(intent, options.toBundle()); startActivity(intent, options.toBundle());
}); });
});
binding.accountDate.setText(Helper.shortDateToString(account.created_at)); binding.accountDate.setText(Helper.shortDateToString(account.created_at));

View file

@ -25,7 +25,7 @@ import java.util.Set;
/** /**
* Original work from <a href="https://stackoverflow.com/a/25873554">stackoverflow</a> * Original work from https://stackoverflow.com/a/25873554
*/ */
public class NetworkStateReceiver extends BroadcastReceiver { public class NetworkStateReceiver extends BroadcastReceiver {

View file

@ -31,14 +31,18 @@ public class ToastMessage extends BroadcastReceiver {
String content = b.getString(Helper.RECEIVE_TOAST_CONTENT, null); String content = b.getString(Helper.RECEIVE_TOAST_CONTENT, null);
if (type != null && content != null) { if (type != null && content != null) {
switch (type) { switch (type) {
case Helper.RECEIVE_TOAST_TYPE_ERROR -> case Helper.RECEIVE_TOAST_TYPE_ERROR:
Toasty.error(context, content, Toasty.LENGTH_SHORT).show(); Toasty.error(context, content, Toasty.LENGTH_SHORT).show();
case Helper.RECEIVE_TOAST_TYPE_WARNING -> break;
case Helper.RECEIVE_TOAST_TYPE_WARNING:
Toasty.warning(context, content, Toasty.LENGTH_SHORT).show(); Toasty.warning(context, content, Toasty.LENGTH_SHORT).show();
case Helper.RECEIVE_TOAST_TYPE_INFO -> break;
case Helper.RECEIVE_TOAST_TYPE_INFO:
Toasty.info(context, content, Toasty.LENGTH_SHORT).show(); Toasty.info(context, content, Toasty.LENGTH_SHORT).show();
case Helper.RECEIVE_TOAST_TYPE_SUCCESS -> break;
case Helper.RECEIVE_TOAST_TYPE_SUCCESS:
Toasty.success(context, content, Toasty.LENGTH_SHORT).show(); Toasty.success(context, content, Toasty.LENGTH_SHORT).show();
break;
} }
} }
} }

View file

@ -124,7 +124,6 @@ public interface MastodonAccountsService {
@Query("exclude_reblogs") Boolean exclude_reblogs, @Query("exclude_reblogs") Boolean exclude_reblogs,
@Query("only_media") Boolean only_media, @Query("only_media") Boolean only_media,
@Query("pinned") Boolean pinned, @Query("pinned") Boolean pinned,
@Query("tagged") String tagged,
@Query("limit") int limit @Query("limit") int limit
); );

View file

@ -21,7 +21,6 @@ import app.fedilab.android.mastodon.client.entities.api.Account;
import app.fedilab.android.mastodon.client.entities.api.Activity; import app.fedilab.android.mastodon.client.entities.api.Activity;
import app.fedilab.android.mastodon.client.entities.api.Emoji; import app.fedilab.android.mastodon.client.entities.api.Emoji;
import app.fedilab.android.mastodon.client.entities.api.Instance; import app.fedilab.android.mastodon.client.entities.api.Instance;
import app.fedilab.android.mastodon.client.entities.api.InstanceV2;
import app.fedilab.android.mastodon.client.entities.api.Tag; import app.fedilab.android.mastodon.client.entities.api.Tag;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.GET; import retrofit2.http.GET;
@ -33,9 +32,6 @@ public interface MastodonInstanceService {
@GET("instance") @GET("instance")
Call<Instance> instance(); Call<Instance> instance();
@GET("instance")
Call<InstanceV2> instanceV2();
@GET("instance/peers") @GET("instance/peers")
Call<List<String>> connectedInstance(); Call<List<String>> connectedInstance();

View file

@ -67,7 +67,6 @@ public interface MastodonNotificationsService {
@Field("subscription[endpoint]") String endpoint, @Field("subscription[endpoint]") String endpoint,
@Field("subscription[keys][p256dh]") String keys_p256dh, @Field("subscription[keys][p256dh]") String keys_p256dh,
@Field("subscription[keys][auth]") String keys_auth, @Field("subscription[keys][auth]") String keys_auth,
@Field("subscription[standard]") boolean standard,
@Field("data[alerts][follow]") boolean follow, @Field("data[alerts][follow]") boolean follow,
@Field("data[alerts][favourite]") boolean favourite, @Field("data[alerts][favourite]") boolean favourite,
@Field("data[alerts][reblog]") boolean reblog, @Field("data[alerts][reblog]") boolean reblog,

View file

@ -15,7 +15,6 @@ package app.fedilab.android.mastodon.client.endpoints;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import java.util.Date; import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import app.fedilab.android.mastodon.client.entities.api.Account; import app.fedilab.android.mastodon.client.entities.api.Account;
@ -26,17 +25,14 @@ import app.fedilab.android.mastodon.client.entities.api.Poll;
import app.fedilab.android.mastodon.client.entities.api.ScheduledStatus; import app.fedilab.android.mastodon.client.entities.api.ScheduledStatus;
import app.fedilab.android.mastodon.client.entities.api.Status; import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.mastodon.client.entities.api.StatusSource; import app.fedilab.android.mastodon.client.entities.api.StatusSource;
import app.fedilab.android.mastodon.client.entities.api.params.StatusParams;
import okhttp3.MultipartBody; import okhttp3.MultipartBody;
import okhttp3.RequestBody; import okhttp3.RequestBody;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.DELETE; import retrofit2.http.DELETE;
import retrofit2.http.Field; import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded; import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET; import retrofit2.http.GET;
import retrofit2.http.Header; import retrofit2.http.Header;
import retrofit2.http.Headers;
import retrofit2.http.Multipart; import retrofit2.http.Multipart;
import retrofit2.http.POST; import retrofit2.http.POST;
import retrofit2.http.PUT; import retrofit2.http.PUT;
@ -65,8 +61,7 @@ public interface MastodonStatusesService {
@Field("visibility") String visibility, @Field("visibility") String visibility,
@Field("language") String language, @Field("language") String language,
@Field("quote_id") String quote_id, @Field("quote_id") String quote_id,
@Field("content_type") String content_type, @Field("content_type") String content_type
@Field("local_only") Boolean local_only
); );
@GET("statuses/{id}/source") @GET("statuses/{id}/source")
@ -79,16 +74,6 @@ public interface MastodonStatusesService {
@Header("Authorization") String token, @Header("Authorization") String token,
@Path("id") String id); @Path("id") String id);
@Headers({"Accept: application/json"})
@PUT("statuses/{id}")
Call<Status> updateStatus(
@Header("Idempotency-Key") String idempotency_Key,
@Header("Authorization") String token,
@Path("id") String id,
@Body StatusParams statusParams
);
//Post a status //Post a status
@FormUrlEncoded @FormUrlEncoded
@PUT("statuses/{id}") @PUT("statuses/{id}")
@ -107,9 +92,9 @@ public interface MastodonStatusesService {
@Field("spoiler_text") String spoiler_text, @Field("spoiler_text") String spoiler_text,
@Field("visibility") String visibility, @Field("visibility") String visibility,
@Field("language") String language, @Field("language") String language,
@Field("media_attributes[]") LinkedHashMap<String, String> media_id, @Field("media_attributes[][id]") List<String> media_id,
@Field("media_attributes[]") LinkedHashMap<String, String> media_description, @Field("media_attributes[][description]") List<String> media_description,
@Field("media_attributes[]") LinkedHashMap<String, String> focus @Field("media_attributes[][focus]") List<String> focus
); );
//Post a scheduled status //Post a scheduled status

View file

@ -18,12 +18,10 @@ import java.util.List;
import app.fedilab.android.mastodon.client.entities.api.Account; import app.fedilab.android.mastodon.client.entities.api.Account;
import app.fedilab.android.mastodon.client.entities.api.Conversation; import app.fedilab.android.mastodon.client.entities.api.Conversation;
import app.fedilab.android.mastodon.client.entities.api.Link;
import app.fedilab.android.mastodon.client.entities.api.Marker; import app.fedilab.android.mastodon.client.entities.api.Marker;
import app.fedilab.android.mastodon.client.entities.api.MastodonList; import app.fedilab.android.mastodon.client.entities.api.MastodonList;
import app.fedilab.android.mastodon.client.entities.api.Status; import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.mastodon.client.entities.api.Tag; import app.fedilab.android.mastodon.client.entities.api.Tag;
import app.fedilab.android.mastodon.client.entities.lemmy.LemmyPost;
import app.fedilab.android.mastodon.client.entities.misskey.MisskeyNote; import app.fedilab.android.mastodon.client.entities.misskey.MisskeyNote;
import app.fedilab.android.mastodon.client.entities.nitter.Nitter; import app.fedilab.android.mastodon.client.entities.nitter.Nitter;
import app.fedilab.android.mastodon.client.entities.peertube.PeertubeVideo; import app.fedilab.android.mastodon.client.entities.peertube.PeertubeVideo;
@ -81,11 +79,6 @@ public interface MastodonTimelinesService {
@Query("offset") Integer offset, @Query("offset") Integer offset,
@Query("limit") Integer limit); @Query("limit") Integer limit);
@GET("trends/links")
Call<List<Link>> getLinkTrends(@Header("Authorization") String token,
@Query("offset") Integer offset,
@Query("limit") Integer limit);
//Public Tags timelines //Public Tags timelines
@GET("timelines/tag/{hashtag}") @GET("timelines/tag/{hashtag}")
Call<List<Status>> getHashTag( Call<List<Status>> getHashTag(
@ -237,20 +230,6 @@ public interface MastodonTimelinesService {
Call<List<MisskeyNote>> getMisskey(@Body MisskeyNote.MisskeyParams params); Call<List<MisskeyNote>> getMisskey(@Body MisskeyNote.MisskeyParams params);
@GET("discover/posts/trending")
Call<List<Status>> getPixelDiscoverTrending(
@Query("range") String range
);
@GET("api/v3/post/list?sort=New")
Call<LemmyPost.LemmyPosts> getLemmyMain(@Query("limit") Integer limit,
@Query("page") String page);
@GET("api/v3/comment/list")
Call<LemmyPost.LemmyComments> getLemmyThread(@Query("post_id") String post_id,
@Query("limit") Integer limit,
@Query("page") String page);
//Public timelines for Misskey //Public timelines for Misskey
@FormUrlEncoded @FormUrlEncoded
@POST("api/notes") @POST("api/notes")
@ -274,14 +253,14 @@ public interface MastodonTimelinesService {
); );
@Headers({"Accept: text/html,application/xhtml+xml,application/xml"}) @Headers({"Accept: text/html,application/xhtml+xml,application/xml"})
@GET("{names}/with_replies/rss") @GET("{names}/rss")
Call<Nitter> getNitter( Call<Nitter> getNitter(
@Path("names") String id, @Path("names") String id,
@Query("max_position") String max_position @Query("max_position") String max_position
); );
@Headers({"Accept: text/html,application/xhtml+xml,application/xml"}) @Headers({"Accept: text/html,application/xhtml+xml,application/xml"})
@GET("{account}/with_replies/rss") @GET("{account}/rss")
Call<Nitter> getNitterAccount( Call<Nitter> getNitterAccount(
@Path("account") String account @Path("account") String account
); );

View file

@ -1,32 +0,0 @@
package app.fedilab.android.mastodon.client.endpoints;
/* Copyright 2025 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import java.util.List;
import app.fedilab.android.mastodon.client.entities.api.Status;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;
public interface PixelfedTimelinesService {
//Public timelines
@GET("discover/posts/trending")
Call<List<Status>> getTrending(
@Query("range") String range
);
}

View file

@ -60,11 +60,11 @@ public class Account implements Serializable {
@SerializedName("header_static") @SerializedName("header_static")
public String header_static; public String header_static;
@SerializedName("followers_count") @SerializedName("followers_count")
public long followers_count; public int followers_count;
@SerializedName("following_count") @SerializedName("following_count")
public long following_count; public int following_count;
@SerializedName("statuses_count") @SerializedName("statuses_count")
public long statuses_count; public int statuses_count;
@SerializedName("last_status_at") @SerializedName("last_status_at")
public Date last_status_at; public Date last_status_at;
@SerializedName("source") @SerializedName("source")
@ -88,17 +88,15 @@ public class Account implements Serializable {
@SerializedName("role") @SerializedName("role")
public Role role; public Role role;
public transient RelationShip relationShip; public transient RelationShip relationShip;
public transient String pronouns = null;
public synchronized Spannable getSpanDisplayName(Context context, WeakReference<View> viewWeakReference) { public synchronized Spannable getSpanDisplayName(Context context, WeakReference<View> viewWeakReference) {
if (display_name == null || display_name.isEmpty()) { if (display_name == null || display_name.isEmpty()) {
display_name = username; display_name = username;
} }
return SpannableHelper.convert(context, display_name, null, this, null, viewWeakReference, null, true, false); return SpannableHelper.convert(context, display_name, null, this, null, viewWeakReference);
} }
public synchronized Spannable getSpanDisplayNameEmoji(Activity activity, WeakReference<View> viewWeakReference) { public synchronized Spannable getSpanDisplayName(Activity activity, WeakReference<View> viewWeakReference) {
if (display_name == null || display_name.isEmpty()) { if (display_name == null || display_name.isEmpty()) {
display_name = username; display_name = username;
} }
@ -106,14 +104,11 @@ public class Account implements Serializable {
} }
public synchronized Spannable getSpanDisplayNameTitle(Context context, WeakReference<View> viewWeakReference, String title) { public synchronized Spannable getSpanDisplayNameTitle(Context context, WeakReference<View> viewWeakReference, String title) {
return SpannableHelper.convert(context, title, null, this, null, viewWeakReference, null, true, false); return SpannableHelper.convert(context, title, null, this, null, viewWeakReference);
} }
public synchronized Spannable getSpanNote(Context context, WeakReference<View> viewWeakReference) { public synchronized Spannable getSpanNote(Context context, WeakReference<View> viewWeakReference) {
return SpannableHelper.convert(context, note, null, this, null, viewWeakReference, null, true, false); return SpannableHelper.convert(context, note, null, this, null, viewWeakReference);
}
public synchronized Spannable getSpanNote(Context context, WeakReference<View> viewWeakReference, Status.Callback callback) {
return SpannableHelper.convert(context, note, null, this, null, viewWeakReference, callback, true, false);
} }
@Override @Override

View file

@ -56,7 +56,7 @@ public class Announcement {
public synchronized Spannable getSpanContent(Context context, WeakReference<View> viewWeakReference) { public synchronized Spannable getSpanContent(Context context, WeakReference<View> viewWeakReference) {
return SpannableHelper.convert(context, content, null, null, this, viewWeakReference, null, true, false); return SpannableHelper.convert(context, content, null, null, this, viewWeakReference);
} }
} }

View file

@ -14,36 +14,21 @@ 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, * You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.BaseMainActivity.emojis;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.os.Handler;
import android.os.Looper;
import androidx.annotation.NonNull;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import java.io.Serializable; import java.io.Serializable;
import java.net.IDN;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; 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.exception.DBException;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.sqlite.Sqlite; 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 { public class EmojiInstance implements Serializable {
@ -185,17 +170,6 @@ 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 * Returns the emojis for an instance
* *
@ -210,72 +184,10 @@ public class EmojiInstance implements Serializable {
Cursor c = db.query(Sqlite.TABLE_EMOJI_INSTANCE, null, Sqlite.COL_INSTANCE + " = '" + instance + "'", null, null, null, null, "1"); Cursor c = db.query(Sqlite.TABLE_EMOJI_INSTANCE, null, Sqlite.COL_INSTANCE + " = '" + instance + "'", null, null, null, null, "1");
return cursorToEmojiList(c); return cursorToEmojiList(c);
} catch (Exception e) { } 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; return null;
} }
} }
/**
* 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 ) {
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 * Restore emoji list from db
* *
@ -302,8 +214,4 @@ public class EmojiInstance implements Serializable {
} }
return filteredEmojis; return filteredEmojis;
} }
public interface EmojiFilteredCallBack {
void get(List<Emoji> emojiList);
}
} }

View file

@ -47,7 +47,7 @@ public class Field implements Serializable {
if (verified_at != null && value != null) { if (verified_at != null && value != null) {
value_span = new ForegroundColorSpan(ContextCompat.getColor(context, R.color.verified_text)); value_span = new ForegroundColorSpan(ContextCompat.getColor(context, R.color.verified_text));
} }
Spannable spannable = SpannableHelper.convert(context, value, null, account, null, viewWeakReference, null, true, false); Spannable spannable = SpannableHelper.convert(context, value, null, account, null, viewWeakReference);
if (value_span != null && spannable != null) { if (value_span != null && spannable != null) {
spannable.setSpan(value_span, 0, spannable.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); spannable.setSpan(value_span, 0, spannable.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
} }
@ -57,7 +57,7 @@ public class Field implements Serializable {
public synchronized Spannable getLabelSpan(Context context, Account account, WeakReference<View> viewWeakReference) { public synchronized Spannable getLabelSpan(Context context, Account account, WeakReference<View> viewWeakReference) {
Spannable spannable = SpannableHelper.convert(context, name, null, account, null, viewWeakReference, null, true, false); Spannable spannable = SpannableHelper.convert(context, name, null, account, null, viewWeakReference);
if (name_span != null && spannable != null) { if (name_span != null && spannable != null) {
spannable.setSpan(name_span, 0, spannable.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); spannable.setSpan(name_span, 0, spannable.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
} }

View file

@ -2,8 +2,6 @@ package app.fedilab.android.mastodon.client.entities.api;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import java.io.Serializable;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -18,7 +16,7 @@ import java.io.Serializable;
* *
* You should have received a copy of the GNU General Public License along with Fedilab; if not, * You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
public class History implements Serializable { public class History {
@SerializedName("day") @SerializedName("day")
public String day; public String day;
@SerializedName("uses") @SerializedName("uses")

View file

@ -169,8 +169,8 @@ public class Instance implements Serializable {
public int min_expiration; public int min_expiration;
@SerializedName("max_options") @SerializedName("max_options")
public int max_options = 4; public int max_options = 4;
@SerializedName("max_characters_per_option") @SerializedName("max_option_chars")
public int max_option_chars = 50; public int max_option_chars = 25;
@SerializedName("max_expiration") @SerializedName("max_expiration")
public int max_expiration; public int max_expiration;
} }

View file

@ -1,70 +0,0 @@
package app.fedilab.android.mastodon.client.entities.api;
import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/* Copyright 2025 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
public class InstanceV2 implements Serializable {
@SerializedName("domain")
public String domain;
@SerializedName("title")
public String title;
@SerializedName("version")
public String version;
@SerializedName("source_url")
public String sourceUrl;
@SerializedName("description")
public String description;
@SerializedName("configuration")
public Configuration configuration;
public static String serialize(InstanceV2 instance) {
Gson gson = new Gson();
try {
return gson.toJson(instance);
} catch (Exception e) {
return null;
}
}
public static InstanceV2 restore(String serialized) {
Gson gson = new Gson();
try {
return gson.fromJson(serialized, InstanceV2.class);
} catch (Exception e) {
return null;
}
}
public static class Configuration implements Serializable {
@SerializedName("vapid")
public VapId vapId;
}
public static class VapId implements Serializable {
@SerializedName("public_key")
public String publicKey;
}
}

View file

@ -1,52 +0,0 @@
package app.fedilab.android.mastodon.client.entities.api;
/* Copyright 2025 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import com.google.gson.annotations.SerializedName;
import java.io.Serializable;
import java.util.List;
public class Link implements Serializable {
@SerializedName("url")
public String url;
@SerializedName("title")
public String title;
@SerializedName("description")
public String description;
@SerializedName("type")
public String type;
@SerializedName("author_name")
public String author_name;
@SerializedName("author_url")
public String author_url;
@SerializedName("provider_name")
public String provider_name;
@SerializedName("provider_url")
public String provider_url;
@SerializedName("html")
public String html;
@SerializedName("width")
public int width;
@SerializedName("height")
public int height;
@SerializedName("image")
public String image;
@SerializedName("embed_url")
public String embed_url;
@SerializedName("blurhash")
public String blurhash;
@SerializedName("history")
public List<History> history;
}

View file

@ -61,7 +61,7 @@ public class Poll implements Serializable {
public transient Spannable span_title; public transient Spannable span_title;
public Spannable getSpanTitle(Context context, Status status, WeakReference<View> viewWeakReference) { public Spannable getSpanTitle(Context context, Status status, WeakReference<View> viewWeakReference) {
span_title = SpannableHelper.convert(context, title, status, null, null, viewWeakReference, null, false, false); span_title = SpannableHelper.convert(context, title, status, null, null, viewWeakReference, null, false);
return span_title; return span_title;
} }
} }

View file

@ -22,8 +22,6 @@ public class PushSubscription {
public String id; public String id;
@SerializedName("endpoint") @SerializedName("endpoint")
public String endpoint; public String endpoint;
@SerializedName("standard")
public String standard;
@SerializedName("policy") @SerializedName("policy")
public String policy; public String policy;
@SerializedName("alerts") @SerializedName("alerts")

View file

@ -16,7 +16,6 @@ package app.fedilab.android.mastodon.client.entities.api;
import android.content.Context; import android.content.Context;
import android.text.Spannable; import android.text.Spannable;
import android.util.Log;
import android.view.View; import android.view.View;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -29,7 +28,6 @@ import java.lang.ref.WeakReference;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.helper.SpannableHelper; import app.fedilab.android.mastodon.helper.SpannableHelper;
import de.timfreiheit.mathjax.android.MathJaxView; import de.timfreiheit.mathjax.android.MathJaxView;
@ -64,11 +62,11 @@ public class Status implements Serializable, Cloneable {
@SerializedName("url") @SerializedName("url")
public String url; public String url;
@SerializedName("replies_count") @SerializedName("replies_count")
public long replies_count; public int replies_count;
@SerializedName("reblogs_count") @SerializedName("reblogs_count")
public long reblogs_count; public int reblogs_count;
@SerializedName("favourites_count") @SerializedName("favourites_count")
public long favourites_count; public int favourites_count;
@SerializedName("favourited") @SerializedName("favourited")
public boolean favourited; public boolean favourited;
@SerializedName("reblogged") @SerializedName("reblogged")
@ -114,8 +112,6 @@ public class Status implements Serializable, Cloneable {
@SerializedName("reactions") @SerializedName("reactions")
public List<Reaction> reactions; public List<Reaction> reactions;
public String attachedNotification = null;
public int gifPosition = 0;
public transient boolean isFetchMore = false; public transient boolean isFetchMore = false;
public transient boolean isFetching = false; public transient boolean isFetching = false;
@ -137,19 +133,12 @@ public class Status implements Serializable, Cloneable {
public transient boolean setCursorToEnd = false; public transient boolean setCursorToEnd = false;
public transient int cursorPosition = 0; public transient int cursorPosition = 0;
public transient boolean submitted = false; public transient boolean submitted = false;
public transient boolean underlined = false;
public boolean spoilerChecked = false; public boolean spoilerChecked = false;
public Filter filteredByApp; public Filter filteredByApp;
public transient Spannable contentSpan; public transient Spannable contentSpan;
public transient String[] bottomTags;
public transient Spannable contentSpoilerSpan; public transient Spannable contentSpoilerSpan;
public transient Spannable contentTranslateSpan; public transient Spannable contentTranslateSpan;
public transient MathJaxView mathJaxView; public transient MathJaxView mathJaxView;
public String lemmy_post_id;
public transient String pronouns = null;
@Override @Override
public boolean equals(@Nullable Object obj) { public boolean equals(@Nullable Object obj) {
@ -160,30 +149,23 @@ public class Status implements Serializable, Cloneable {
return same; return same;
} }
public synchronized Spannable getSpanContent(Context context, boolean checkRemotely, WeakReference<View> viewWeakReference, Callback callback) { public synchronized Spannable getSpanContent(Context context, WeakReference<View> viewWeakReference, Callback callback) {
if (contentSpan == null) { if (contentSpan == null) {
contentSpan = SpannableHelper.convert(context, content, this, null, null, checkRemotely, viewWeakReference, callback, true, true); contentSpan = SpannableHelper.convert(context, content, this, null, null, viewWeakReference, callback);
} }
return contentSpan; return contentSpan;
} }
public synchronized String[] getBottomTags() {
if(bottomTags == null) {
bottomTags = SpannableHelper.hasBottomTags(content);
}
return bottomTags;
}
public synchronized Spannable getSpanSpoiler(Context context, WeakReference<View> viewWeakReference, Callback callback) { public synchronized Spannable getSpanSpoiler(Context context, WeakReference<View> viewWeakReference, Callback callback) {
if (contentSpoilerSpan == null) { if (contentSpoilerSpan == null) {
contentSpoilerSpan = SpannableHelper.convert(context, spoiler_text, this, null, null, viewWeakReference, callback, true, false); contentSpoilerSpan = SpannableHelper.convert(context, spoiler_text, this, null, null, viewWeakReference, callback);
} }
return contentSpoilerSpan; return contentSpoilerSpan;
} }
public synchronized Spannable getSpanTranslate(Context context, WeakReference<View> viewWeakReference, Callback callback) { public synchronized Spannable getSpanTranslate(Context context, WeakReference<View> viewWeakReference, Callback callback) {
if (contentTranslateSpan == null) { if (contentTranslateSpan == null) {
contentTranslateSpan = SpannableHelper.convert(context, translationContent, this, null, null, viewWeakReference, callback, true, true); contentTranslateSpan = SpannableHelper.convert(context, translationContent, this, null, null, viewWeakReference, callback);
} }
return contentTranslateSpan; return contentTranslateSpan;
} }

View file

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

View file

@ -1,65 +0,0 @@
package app.fedilab.android.mastodon.client.entities.api.params;
/* Copyright 2025 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import com.google.gson.annotations.SerializedName;
import java.io.Serializable;
import java.util.List;
public class StatusParams implements Serializable {
@SerializedName("id")
public String id;
@SerializedName("status")
public String status;
@SerializedName("media_ids")
public List<String> media_ids;
@SerializedName("poll")
public PollParams pollParams;
@SerializedName("in_reply_to_id")
public String in_reply_to_id;
@SerializedName("sensitive")
public Boolean sensitive;
@SerializedName("spoiler_text")
public String spoiler_text;
@SerializedName("visibility")
public String visibility;
@SerializedName("language")
public String language;
@SerializedName("media_attributes")
public List<MediaParams> media_attributes;
public static class PollParams implements Serializable{
@SerializedName("options")
public List<String> poll_options;
@SerializedName("expires_in")
public Integer poll_expire_in;
@SerializedName("multiple")
public Boolean poll_multiple;
@SerializedName("hide_totals")
public Boolean poll_hide_totals;
}
public static class MediaParams implements Serializable {
@SerializedName("id")
public String id;
@SerializedName("description")
public String description;
@SerializedName("focus")
public String focus;
}
}

View file

@ -1,433 +0,0 @@
package app.fedilab.android.mastodon.client.entities.app;
/* Copyright 2024 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* 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.PREF_USER_ID;
import static app.fedilab.android.mastodon.helper.Helper.PREF_USER_INSTANCE;
import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Parcel;
import android.util.Base64;
import androidx.preference.PreferenceManager;
import com.google.gson.annotations.SerializedName;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Calendar;
import java.util.Date;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import app.fedilab.android.mastodon.client.entities.api.Account;
import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.mastodon.exception.DBException;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.sqlite.Sqlite;
/**
* Class that manages Bundle of Intent from database
*/
public class CachedBundle {
public String id;
public Bundle bundle;
public CacheType cacheType;
public String instance;
public String user_id;
public String target_id;
public Date created_at;
private SQLiteDatabase db;
private transient Context context;
public CachedBundle() {
}
public CachedBundle(Context context) {
//Creation of the DB with tables
this.context = context;
this.db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
}
public void insertAccountBundle(Account account, BaseAccount currentUser) throws DBException {
if (db == null) {
throw new DBException("db is null. Wrong initialization.");
}
ContentValues valuesAccount = new ContentValues();
Bundle bundleAccount = new Bundle();
if (account != null) {
bundleAccount.putSerializable(Helper.ARG_ACCOUNT, account);
valuesAccount.put(Sqlite.COL_BUNDLE, serializeBundle(bundleAccount));
valuesAccount.put(Sqlite.COL_CREATED_AT, Helper.dateToString(new Date()));
valuesAccount.put(Sqlite.COL_TARGET_ID, account.id);
valuesAccount.put(Sqlite.COL_USER_ID, currentUser.user_id);
valuesAccount.put(Sqlite.COL_INSTANCE, currentUser.instance);
valuesAccount.put(Sqlite.COL_TYPE, CacheType.ACCOUNT.getValue());
removeIntent(currentUser, account.id);
db.insertOrThrow(Sqlite.TABLE_INTENT, null, valuesAccount);
}
}
/**
* Insert a bundle in db
*
* @param bundle {@link Bundle}
* @return long - db id
* @throws DBException exception with database
*/
private long insertIntent(Bundle bundle, BaseAccount currentUser) throws DBException {
if (db == null) {
throw new DBException("db is null. Wrong initialization.");
}
ContentValues values = new ContentValues();
values.put(Sqlite.COL_BUNDLE, serializeBundle(bundle));
values.put(Sqlite.COL_CREATED_AT, Helper.dateToString(new Date()));
values.put(Sqlite.COL_TYPE, CacheType.ARGS.getValue());
if (bundle.containsKey(Helper.ARG_ACCOUNT) && currentUser != null) {
ContentValues valuesAccount = new ContentValues();
Bundle bundleAccount = new Bundle();
Account account = null;
try {
account = (Account) bundle.getSerializable(Helper.ARG_ACCOUNT);
} catch (ClassCastException ignored) {
}
if (account != null) {
bundleAccount.putSerializable(Helper.ARG_ACCOUNT, account);
valuesAccount.put(Sqlite.COL_BUNDLE, serializeBundle(bundleAccount));
valuesAccount.put(Sqlite.COL_CREATED_AT, Helper.dateToString(new Date()));
valuesAccount.put(Sqlite.COL_TARGET_ID, account.id);
valuesAccount.put(Sqlite.COL_USER_ID, currentUser.user_id);
valuesAccount.put(Sqlite.COL_INSTANCE, currentUser.instance);
valuesAccount.put(Sqlite.COL_TYPE, CacheType.ACCOUNT.getValue());
removeIntent(currentUser, account.id);
db.insertOrThrow(Sqlite.TABLE_INTENT, null, valuesAccount);
}
}
if (bundle.containsKey(Helper.ARG_STATUS) && currentUser != null) {
ContentValues valuesAccount = new ContentValues();
Bundle bundleStatus = new Bundle();
Status status = null;
try {
status = (Status) bundle.getSerializable(Helper.ARG_STATUS);
} catch (ClassCastException ignored) {
}
if (status != null) {
bundleStatus.putSerializable(Helper.ARG_STATUS, status);
valuesAccount.put(Sqlite.COL_BUNDLE, serializeBundle(bundleStatus));
valuesAccount.put(Sqlite.COL_CREATED_AT, Helper.dateToString(new Date()));
valuesAccount.put(Sqlite.COL_TARGET_ID, status.id);
valuesAccount.put(Sqlite.COL_USER_ID, currentUser.user_id);
valuesAccount.put(Sqlite.COL_INSTANCE, currentUser.instance);
valuesAccount.put(Sqlite.COL_TYPE, CacheType.STATUS.getValue());
removeIntent(currentUser, status.id);
db.insertOrThrow(Sqlite.TABLE_INTENT, null, valuesAccount);
}
}
try {
return db.insertOrThrow(Sqlite.TABLE_INTENT, null, values);
} catch (Exception e) {
e.printStackTrace();
return -1;
}
}
public void getBundle(long id, BaseAccount Account, BundleCallback callback) {
new Thread(() -> {
Bundle bundle = null;
try {
CachedBundle cachedBundle = getCachedBundle(String.valueOf(id));
if (cachedBundle != null) {
bundle = cachedBundle.bundle;
if (bundle != null && bundle.containsKey(Helper.ARG_CACHED_ACCOUNT_ID)) {
Account cachedAccount = getCachedAccount(Account, bundle.getString(Helper.ARG_CACHED_ACCOUNT_ID));
if (cachedAccount != null) {
bundle.putSerializable(Helper.ARG_ACCOUNT, cachedAccount);
}
}
if (bundle != null && bundle.containsKey(Helper.ARG_CACHED_STATUS_ID)) {
Status cachedStatus = getCachedStatus(Account, bundle.getString(Helper.ARG_CACHED_STATUS_ID));
if (cachedStatus != null) {
bundle.putSerializable(Helper.ARG_STATUS, cachedStatus);
}
}
}
} catch (DBException ignored) {
}
if (bundle == null) {
bundle = new Bundle();
}
Handler mainHandler = new Handler(Looper.getMainLooper());
Bundle finalBundle = bundle;
Runnable myRunnable = () -> callback.get(finalBundle);
mainHandler.post(myRunnable);
}).start();
}
public void insertBundle(Bundle bundle, BaseAccount Account, BundleInsertCallback callback) {
new Thread(() -> {
long dbBundleId = -1;
try {
dbBundleId = insertIntent(bundle, Account);
} catch (DBException ignored) {
}
Handler mainHandler = new Handler(Looper.getMainLooper());
long finalDbBundleId = dbBundleId;
Runnable myRunnable = () -> callback.inserted(finalDbBundleId);
mainHandler.post(myRunnable);
}).start();
}
/**
* Returns a bundle by targeted account id
*
* @param target_id String
* @return Account {@link Account}
*/
public Account getCachedAccount(BaseAccount account, String target_id) throws DBException {
if (db == null) {
throw new DBException("db is null. Wrong initialization.");
}
if (target_id == null) {
return null;
}
if (account == null) {
account = new BaseAccount();
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
account.user_id = sharedpreferences.getString(PREF_USER_ID, null);
account.instance = sharedpreferences.getString(PREF_USER_INSTANCE, null);
}
try {
Cursor c = db.query(Sqlite.TABLE_INTENT, null, Sqlite.COL_USER_ID + " = '" + account.user_id + "' AND "
+ Sqlite.COL_INSTANCE + " = '" + account.instance + "' AND "
+ Sqlite.COL_TYPE + " = '" + CacheType.ACCOUNT.getValue() + "' AND "
+ Sqlite.COL_TARGET_ID + " = '" + target_id + "'", null, null, null, null, "1");
CachedBundle cachedBundle = cursorToCachedBundle(c);
if (cachedBundle != null && cachedBundle.bundle.containsKey(Helper.ARG_ACCOUNT)) {
return (Account) cachedBundle.bundle.getSerializable(Helper.ARG_ACCOUNT);
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
return null;
}
/**
* Returns a bundle by targeted status id
*
* @param target_id String
* @return Status {@link Status}
*/
private Status getCachedStatus(BaseAccount account, String target_id) throws DBException {
if (db == null) {
throw new DBException("db is null. Wrong initialization.");
}
if (target_id == null) {
return null;
}
if (account == null) {
account = new BaseAccount();
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
account.user_id = sharedpreferences.getString(PREF_USER_ID, null);
account.instance = sharedpreferences.getString(PREF_USER_INSTANCE, null);
}
try {
Cursor c = db.query(Sqlite.TABLE_INTENT, null, Sqlite.COL_USER_ID + " = '" + account.user_id + "' AND "
+ Sqlite.COL_INSTANCE + " = '" + account.instance + "' AND "
+ Sqlite.COL_TYPE + " = '" + CacheType.STATUS.getValue() + "' AND "
+ Sqlite.COL_TARGET_ID + " = '" + target_id + "'", null, null, null, null, "1");
CachedBundle cachedBundle = cursorToCachedBundle(c);
if (cachedBundle != null && cachedBundle.bundle.containsKey(Helper.ARG_STATUS)) {
return (Status) cachedBundle.bundle.getSerializable(Helper.ARG_STATUS);
}
} catch (Exception e) {
return null;
}
return null;
}
/**
* Returns a bundle by its ID
*
* @param id String
* @return CachedBundle {@link CachedBundle}
*/
private CachedBundle getCachedBundle(String id) throws DBException {
if (db == null) {
throw new DBException("db is null. Wrong initialization.");
}
try {
Cursor c = db.query(Sqlite.TABLE_INTENT, null, Sqlite.COL_ID + " = \"" + id + "\"", null, null, null, null, "1");
return cursorToCachedBundle(c);
} catch (Exception e) {
return null;
}
}
/**
* Remove a bundle from db
*/
public void deleteOldIntent() throws DBException {
if (db == null) {
throw new DBException("db is null. Wrong initialization.");
}
Calendar cal = Calendar.getInstance();
cal.setTime(new Date());
cal.add(Calendar.DATE, -1);
Date date = cal.getTime();
String dateStr = Helper.dateToString(date);
try {
db.delete(Sqlite.TABLE_INTENT, Sqlite.COL_CREATED_AT + " < ?", new String[]{dateStr});
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Remove a bundle from db
*/
private void removeIntent(BaseAccount account, String target_id) throws DBException {
if (db == null) {
throw new DBException("db is null. Wrong initialization.");
}
if (account == null || target_id == null) {
return;
}
db.delete(Sqlite.TABLE_INTENT, Sqlite.COL_USER_ID + " = '" + account.user_id + "' AND "
+ Sqlite.COL_INSTANCE + " = '" + account.instance + "' AND "
+ Sqlite.COL_TARGET_ID + " = '" + target_id + "'", null);
}
/***
* Method to hydrate an CachedBundle from database
* @param c Cursor
* @return CachedBundle {@link CachedBundle}
*/
private CachedBundle cursorToCachedBundle(Cursor c) {
//No element found
if (c.getCount() == 0) {
c.close();
return null;
}
//Take the first element
c.moveToFirst();
//New user
CachedBundle account = convertCursorToCachedBundle(c);
//Close the cursor
c.close();
return account;
}
/**
* Read cursor and hydrate without closing it
*
* @param c - Cursor
* @return Account
*/
private CachedBundle convertCursorToCachedBundle(Cursor c) {
CachedBundle cachedBundle = new CachedBundle();
cachedBundle.id = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_ID));
cachedBundle.bundle = deserializeBundle(c.getString(c.getColumnIndexOrThrow(Sqlite.COL_BUNDLE)));
cachedBundle.user_id = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_USER_ID));
cachedBundle.instance = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_INSTANCE));
cachedBundle.target_id = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_TARGET_ID));
cachedBundle.cacheType = CacheType.valueOf(c.getString(c.getColumnIndexOrThrow(Sqlite.COL_TYPE)));
cachedBundle.created_at = Helper.stringToDate(context, c.getString(c.getColumnIndexOrThrow(Sqlite.COL_CREATED_AT)));
return cachedBundle;
}
private String serializeBundle(final Bundle bundle) {
String base64 = null;
final Parcel parcel = Parcel.obtain();
try {
parcel.writeBundle(bundle);
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
final GZIPOutputStream zos = new GZIPOutputStream(new BufferedOutputStream(bos));
zos.write(parcel.marshall());
zos.close();
base64 = Base64.encodeToString(bos.toByteArray(), 0);
} catch (IOException e) {
e.printStackTrace();
} finally {
parcel.recycle();
}
return base64;
}
private Bundle deserializeBundle(final String base64) {
Bundle bundle = null;
final Parcel parcel = Parcel.obtain();
try {
final ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();
final byte[] buffer = new byte[1024];
final GZIPInputStream zis = new GZIPInputStream(new ByteArrayInputStream(Base64.decode(base64, 0)));
int len;
while ((len = zis.read(buffer)) != -1) {
byteBuffer.write(buffer, 0, len);
}
zis.close();
parcel.unmarshall(byteBuffer.toByteArray(), 0, byteBuffer.size());
parcel.setDataPosition(0);
bundle = parcel.readBundle(getClass().getClassLoader());
} catch (IOException e) {
e.printStackTrace();
} finally {
parcel.recycle();
}
return bundle;
}
public enum CacheType {
@SerializedName("ARGS")
ARGS("ARGS"),
@SerializedName("ACCOUNT")
ACCOUNT("ACCOUNT"),
@SerializedName("STATUS")
STATUS("STATUS");
private final String value;
CacheType(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
public interface BundleCallback {
void get(Bundle bundle);
}
public interface BundleInsertCallback {
void inserted(long bundleId);
}
}

View file

@ -1,53 +0,0 @@
package app.fedilab.android.mastodon.client.entities.app;
/* Copyright 2023 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* 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.text.style.URLSpan;
import java.util.List;
public class MarkdownConverter {
public List<MarkdownItem> markdownItems;
public MarkdownItem getByPosition(int position) {
if (markdownItems != null && !markdownItems.isEmpty() && position < markdownItems.size()) {
for (MarkdownItem markdownItem : markdownItems) {
if (markdownItem.position == position) {
return markdownItem;
}
}
}
return null;
}
public static class MarkdownItem {
public String code;
public int position;
public URLSpan urlSpan;
public int regexPosition(List<MarkdownItem> markdownItems) {
int position = 0;
int loopedPosition = 0;
for (MarkdownItem markdownItem : markdownItems) {
if (markdownItem.code.equals(code) && loopedPosition <= this.position) {
position++;
}
loopedPosition++;
}
return position;
}
}
}

View file

@ -46,12 +46,8 @@ public class RemoteInstance implements Serializable {
PEERTUBE("PEERTUBE"), PEERTUBE("PEERTUBE"),
@SerializedName("NITTER") @SerializedName("NITTER")
NITTER("NITTER"), NITTER("NITTER"),
@SerializedName("NITTER_TAG")
NITTER_TAG("NITTER_TAG"),
@SerializedName("MISSKEY") @SerializedName("MISSKEY")
MISSKEY("MISSKEY"), MISSKEY("MISSKEY"),
@SerializedName("LEMMY")
LEMMY("LEMMY"),
@SerializedName("GNU") @SerializedName("GNU")
GNU("GNU"); GNU("GNU");

View file

@ -14,8 +14,6 @@ package app.fedilab.android.mastodon.client.entities.app;
* You should have received a copy of the GNU General Public License along with Fedilab; if not, * You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.mastodon.helper.Helper.TAG;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
@ -291,6 +289,7 @@ public class StatusCache {
if (statusCache.type != null) { if (statusCache.type != null) {
query += " AND " + Sqlite.COL_TYPE + " = '" + statusCache.type.getValue() + "'"; query += " AND " + Sqlite.COL_TYPE + " = '" + statusCache.type.getValue() + "'";
} }
Cursor mCount = db.rawQuery(query, null); Cursor mCount = db.rawQuery(query, null);
mCount.moveToFirst(); mCount.moveToFirst();
int count = mCount.getInt(0); int count = mCount.getInt(0);
@ -353,11 +352,7 @@ public class StatusCache {
values.put(Sqlite.COL_STATUS, mastodonStatusToStringStorage(statusCache.status)); values.put(Sqlite.COL_STATUS, mastodonStatusToStringStorage(statusCache.status));
} }
if (statusCache.notification != null) { if (statusCache.notification != null) {
Notification currentNotification = getCachedNotification(statusCache); values.put(Sqlite.COL_STATUS, mastodonNotificationToStringStorage(statusCache.notification));
if(currentNotification != null && currentNotification.status != null) {
currentNotification.status = statusCache.notification.status;
values.put(Sqlite.COL_STATUS, mastodonNotificationToStringStorage(currentNotification));
}
} }
if (statusCache.conversation != null) { if (statusCache.conversation != null) {
values.put(Sqlite.COL_STATUS, mastodonConversationToStringStorage(statusCache.conversation)); values.put(Sqlite.COL_STATUS, mastodonConversationToStringStorage(statusCache.conversation));
@ -560,28 +555,15 @@ public class StatusCache {
String selection = Sqlite.COL_INSTANCE + "='" + instance + "' AND " + Sqlite.COL_USER_ID + "= '" + user_id + "' AND " + Sqlite.COL_TYPE + "= '" + Timeline.TimeLineEnum.NOTIFICATION.getValue() + "' "; String selection = Sqlite.COL_INSTANCE + "='" + instance + "' AND " + Sqlite.COL_USER_ID + "= '" + user_id + "' AND " + Sqlite.COL_TYPE + "= '" + Timeline.TimeLineEnum.NOTIFICATION.getValue() + "' ";
String limit = String.valueOf(MastodonHelper.statusesPerCall(context)); String limit = String.valueOf(MastodonHelper.statusesPerCall(context));
if (min_id != null) { if (min_id != null) {
if (Helper.isNumeric(min_id)) {
selection += "AND " + Sqlite.COL_STATUS_ID + " > cast(" + min_id + " as int) ";
} else {
selection += "AND " + Sqlite.COL_STATUS_ID + " > '" + min_id + "' "; selection += "AND " + Sqlite.COL_STATUS_ID + " > '" + min_id + "' ";
}
order = " ASC"; order = " ASC";
} else if (max_id != null) { } else if (max_id != null) {
if (Helper.isNumeric(max_id)) {
selection += "AND " + Sqlite.COL_STATUS_ID + " < cast(" + max_id + " as int) ";
} else {
selection += "AND " + Sqlite.COL_STATUS_ID + " < '" + max_id + "' "; selection += "AND " + Sqlite.COL_STATUS_ID + " < '" + max_id + "' ";
}
} else if (since_id != null) { } else if (since_id != null) {
if (Helper.isNumeric(since_id)) {
selection += "AND " + Sqlite.COL_STATUS_ID + " > cast(" + since_id + " as int) ";
} else {
selection += "AND " + Sqlite.COL_STATUS_ID + " > '" + since_id + "' "; selection += "AND " + Sqlite.COL_STATUS_ID + " > '" + since_id + "' ";
}
limit = null; limit = null;
} }
if (exclude_type != null && exclude_type.size() > 0) { if (exclude_type != null && exclude_type.size() > 0) {
StringBuilder exclude = new StringBuilder(); StringBuilder exclude = new StringBuilder();
for (String excluded : exclude_type) { for (String excluded : exclude_type) {
@ -599,15 +581,6 @@ public class StatusCache {
} }
} }
public Notification getCachedNotification(StatusCache statusCache) throws DBException {
if (db == null) {
throw new DBException("db is null. Wrong initialization.");
}
Cursor c = db.query(Sqlite.TABLE_STATUS_CACHE, null, Sqlite.COL_STATUS_ID + " = ? AND " + Sqlite.COL_USER_ID + " = ? AND " + Sqlite.COL_INSTANCE + " =?",
new String[]{statusCache.status_id, statusCache.user_id, statusCache.instance}, null, null, null, "1");
c.moveToFirst();
return convertCursorToNotification(c);
}
/** /**
* Get paginated conversations from db * Get paginated conversations from db
@ -627,25 +600,12 @@ public class StatusCache {
String selection = Sqlite.COL_INSTANCE + "='" + instance + "' AND " + Sqlite.COL_USER_ID + "= '" + user_id + "' AND " + Sqlite.COL_TYPE + "= '" + Timeline.TimeLineEnum.CONVERSATION.getValue() + "' "; String selection = Sqlite.COL_INSTANCE + "='" + instance + "' AND " + Sqlite.COL_USER_ID + "= '" + user_id + "' AND " + Sqlite.COL_TYPE + "= '" + Timeline.TimeLineEnum.CONVERSATION.getValue() + "' ";
String limit = String.valueOf(MastodonHelper.statusesPerCall(context)); String limit = String.valueOf(MastodonHelper.statusesPerCall(context));
if (min_id != null) { if (min_id != null) {
if (Helper.isNumeric(min_id)) {
selection += "AND " + Sqlite.COL_STATUS_ID + " > cast(" + min_id + " as int) ";
} else {
selection += "AND " + Sqlite.COL_STATUS_ID + " > '" + min_id + "' "; selection += "AND " + Sqlite.COL_STATUS_ID + " > '" + min_id + "' ";
}
order = " ASC"; order = " ASC";
} else if (max_id != null) { } else if (max_id != null) {
if (Helper.isNumeric(max_id)) {
selection += "AND " + Sqlite.COL_STATUS_ID + " < cast(" + max_id + " as int) ";
} else {
selection += "AND " + Sqlite.COL_STATUS_ID + " < '" + max_id + "' "; selection += "AND " + Sqlite.COL_STATUS_ID + " < '" + max_id + "' ";
}
} else if (since_id != null) { } else if (since_id != null) {
if (Helper.isNumeric(since_id)) {
selection += "AND " + Sqlite.COL_STATUS_ID + " > cast(" + since_id + " as int) ";
} else {
selection += "AND " + Sqlite.COL_STATUS_ID + " > '" + since_id + "' "; selection += "AND " + Sqlite.COL_STATUS_ID + " > '" + since_id + "' ";
}
limit = null; limit = null;
} }
try { try {
@ -676,25 +636,12 @@ public class StatusCache {
String selection = Sqlite.COL_INSTANCE + "='" + instance + "' AND " + Sqlite.COL_USER_ID + "= '" + user_id + "' AND " + Sqlite.COL_SLUG + "= '" + slug + "' "; String selection = Sqlite.COL_INSTANCE + "='" + instance + "' AND " + Sqlite.COL_USER_ID + "= '" + user_id + "' AND " + Sqlite.COL_SLUG + "= '" + slug + "' ";
String limit = String.valueOf(MastodonHelper.statusesPerCall(context)); String limit = String.valueOf(MastodonHelper.statusesPerCall(context));
if (min_id != null) { if (min_id != null) {
if (Helper.isNumeric(min_id)) {
selection += "AND " + Sqlite.COL_STATUS_ID + " > cast(" + min_id + " as int) ";
} else {
selection += "AND " + Sqlite.COL_STATUS_ID + " > '" + min_id + "' "; selection += "AND " + Sqlite.COL_STATUS_ID + " > '" + min_id + "' ";
}
order = " ASC"; order = " ASC";
} else if (max_id != null) { } else if (max_id != null) {
if (Helper.isNumeric(max_id)) {
selection += "AND " + Sqlite.COL_STATUS_ID + " < cast(" + max_id + " as int) ";
} else {
selection += "AND " + Sqlite.COL_STATUS_ID + " < '" + max_id + "' "; selection += "AND " + Sqlite.COL_STATUS_ID + " < '" + max_id + "' ";
}
} else if (since_id != null) { } else if (since_id != null) {
if (Helper.isNumeric(since_id)) {
selection += "AND " + Sqlite.COL_STATUS_ID + " > cast(" + since_id + " as int) ";
} else {
selection += "AND " + Sqlite.COL_STATUS_ID + " > '" + since_id + "' "; selection += "AND " + Sqlite.COL_STATUS_ID + " > '" + since_id + "' ";
}
limit = null; limit = null;
} }
try { try {

View file

@ -378,8 +378,6 @@ public class Timeline {
REMOTE("REMOTE"), REMOTE("REMOTE"),
@SerializedName("TREND_TAG") @SerializedName("TREND_TAG")
TREND_TAG("TREND_TAG"), TREND_TAG("TREND_TAG"),
@SerializedName("TREND_LINK")
TREND_LINK("TREND_LINK"),
@SerializedName("TREND_MESSAGE") @SerializedName("TREND_MESSAGE")
TREND_MESSAGE("TREND_MESSAGE"), TREND_MESSAGE("TREND_MESSAGE"),
@SerializedName("ACCOUNT_SUGGESTION") @SerializedName("ACCOUNT_SUGGESTION")

View file

@ -1,268 +0,0 @@
package app.fedilab.android.mastodon.client.entities.lemmy;
/* Copyright 2023 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import com.google.gson.annotations.SerializedName;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
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.Status;
public class LemmyPost implements Serializable {
@SerializedName("post")
public Post post;
@SerializedName("comment")
public Comment comment;
@SerializedName("creator")
public Creator creator;
/*@SerializedName("community")
public Community community;*/
@SerializedName("counts")
public Counts counts;
@SerializedName("creator_banned_from_community")
public boolean creator_banned_from_community;
@SerializedName("saved")
public boolean saved;
@SerializedName("read")
public boolean read;
@SerializedName("creator_blocked")
public boolean creator_blocked;
@SerializedName("unread_comments")
public int unread_comments;
public static Status convert(LemmyPost lemmyPost, String instance) {
Status status = new Status();
status.id = lemmyPost.comment == null ? lemmyPost.post.id : lemmyPost.comment.id;
if (lemmyPost.comment != null) {
status.in_reply_to_id = lemmyPost.comment.post_id;
status.lemmy_post_id = null;
} else {
status.lemmy_post_id = lemmyPost.post.id;
}
status.content = lemmyPost.comment == null ? lemmyPost.post.name : lemmyPost.comment.content;
status.visibility = "public";
status.created_at = lemmyPost.comment == null ? lemmyPost.post.published : lemmyPost.comment.published;
status.url = lemmyPost.comment == null ? lemmyPost.post.ap_id : lemmyPost.comment.ap_id;
status.uri = lemmyPost.comment == null ? lemmyPost.post.ap_id : lemmyPost.comment.ap_id;
Account account = new Account();
account.id = lemmyPost.creator.id;
account.acct = lemmyPost.creator.name + "@" + instance;
account.username = "@" + lemmyPost.creator.name;
account.display_name = lemmyPost.creator.name;
account.avatar = lemmyPost.creator.avatar;
account.avatar_static = lemmyPost.creator.avatar;
status.account = account;
if (lemmyPost.comment == null && lemmyPost.post.thumbnail_url != null) {
List<Attachment> attachmentList = new ArrayList<>();
Attachment attachment = new Attachment();
attachment.type = "image";
attachment.url = lemmyPost.post.thumbnail_url;
attachment.preview_url = lemmyPost.post.thumbnail_url;
if (lemmyPost.post.nsfw) {
status.sensitive = true;
}
attachmentList.add(attachment);
status.media_attachments = attachmentList;
} else if (lemmyPost.comment != null && lemmyPost.comment.thumbnail_url != null) {
List<Attachment> attachmentList = new ArrayList<>();
Attachment attachment = new Attachment();
attachment.type = "image";
attachment.url = lemmyPost.comment.thumbnail_url;
attachment.preview_url = lemmyPost.comment.thumbnail_url;
if (lemmyPost.post.nsfw) {
status.sensitive = true;
}
attachmentList.add(attachment);
status.media_attachments = attachmentList;
}
return status;
}
public static class LemmyPosts {
@SerializedName("posts")
public List<LemmyPost> posts;
}
public static class LemmyComments {
@SerializedName("comments")
public List<LemmyPost> comments;
}
public static class Post implements Serializable {
@SerializedName("id")
public String id;
@SerializedName("name")
public String name;
@SerializedName("body")
public String body;
@SerializedName("creator_id")
public String creator_id;
@SerializedName("community_id")
public String community_id;
@SerializedName("removed")
public boolean removed;
@SerializedName("locked")
public boolean locked;
@SerializedName("published")
public Date published;
@SerializedName("updated")
public Date updated;
@SerializedName("deleted")
public boolean deleted;
@SerializedName("nsfw")
public boolean nsfw;
@SerializedName("thumbnail_url")
public String thumbnail_url;
@SerializedName("ap_id")
public String ap_id;
@SerializedName("local")
public boolean local;
@SerializedName("language_id")
public String language_id;
@SerializedName("featured_community")
public boolean featured_community;
@SerializedName("featured_local")
public boolean featured_local;
}
public static class Comment implements Serializable {
@SerializedName("id")
public String id;
@SerializedName("creator_id")
public String creator_id;
@SerializedName("post_id")
public String post_id;
@SerializedName("content")
public String content;
@SerializedName("removed")
public boolean removed;
@SerializedName("published")
public Date published;
@SerializedName("thumbnail_url")
public String thumbnail_url;
@SerializedName("deleted")
public boolean deleted;
@SerializedName("ap_id")
public String ap_id;
@SerializedName("local")
public boolean local;
@SerializedName("path")
public String path;
@SerializedName("distinguished")
public boolean distinguished;
@SerializedName("language_id")
public String language_id;
}
public static class Community implements Serializable {
@SerializedName("id")
public String id;
@SerializedName("name")
public String name;
@SerializedName("title")
public String title;
@SerializedName("description")
public String description;
@SerializedName("removed")
public boolean removed;
@SerializedName("published")
public Date published;
@SerializedName("updated")
public Date updated;
@SerializedName("deleted")
public boolean deleted;
@SerializedName("nsfw")
public boolean nsfw;
@SerializedName("actor_id")
public String actor_id;
@SerializedName("local")
public boolean local;
@SerializedName("icon")
public String icon;
@SerializedName("hidden")
public boolean hidden;
@SerializedName("posting_restricted_to_mods")
public boolean posting_restricted_to_mods;
@SerializedName("instance_id")
public String instance_id;
}
public static class Creator implements Serializable {
@SerializedName("id")
public String id;
@SerializedName("name")
public String name;
@SerializedName("avatar")
public String avatar;
@SerializedName("banned")
public boolean banned;
@SerializedName("published")
public Date published;
@SerializedName("actor_id")
public String actor_id;
@SerializedName("bio")
public String bio;
@SerializedName("local")
public boolean local;
@SerializedName("deleted")
public boolean deleted;
@SerializedName("matrix_user_id")
public String matrix_user_id;
@SerializedName("admin")
public boolean admin;
@SerializedName("bot_account")
public boolean bot_account;
@SerializedName("instance_id")
public String instance_id;
}
public static class Counts implements Serializable {
@SerializedName("id")
public String id;
@SerializedName("post_id")
public String post_id;
@SerializedName("comments")
public int comments;
@SerializedName("score")
public int score;
@SerializedName("upvotes")
public int upvotes;
@SerializedName("downvotes")
public int downvotes;
@SerializedName("published")
public Date published;
@SerializedName("newest_comment_time_necro")
public Date newest_comment_time_necro;
@SerializedName("newest_comment_time")
public Date newest_comment_time;
@SerializedName("featured_local")
public boolean featured_local;
@SerializedName("hot_rank")
public int hot_rank;
@SerializedName("hot_rank_active")
public int hot_rank_active;
}
}

View file

@ -14,12 +14,10 @@ package app.fedilab.android.mastodon.client.entities.nitter;
* You should have received a copy of the GNU General Public License along with Fedilab; if not, * You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import android.content.Context; import android.content.Context;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import org.jsoup.select.Elements;
import org.simpleframework.xml.Element; import org.simpleframework.xml.Element;
import org.simpleframework.xml.ElementList; import org.simpleframework.xml.ElementList;
import org.simpleframework.xml.Namespace; import org.simpleframework.xml.Namespace;
@ -27,11 +25,10 @@ import org.simpleframework.xml.Path;
import org.simpleframework.xml.Root; import org.simpleframework.xml.Root;
import java.io.Serializable; import java.io.Serializable;
import java.net.IDN;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -64,9 +61,14 @@ public class Nitter implements Serializable {
public static MastodonTimelinesService initInstanceXMLOnly(Context context, String instance) { public static MastodonTimelinesService initInstanceXMLOnly(Context context, String instance) {
OkHttpClient okHttpClient = Helper.myOkHttpClient(context); OkHttpClient okHttpClient = new OkHttpClient.Builder()
.readTimeout(60, TimeUnit.SECONDS)
.connectTimeout(60, TimeUnit.SECONDS)
.callTimeout(60, TimeUnit.SECONDS)
.proxy(Helper.getProxy(context))
.build();
Retrofit retrofit = new Retrofit.Builder() Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://" + (instance != null ? IDN.toASCII(instance, IDN.ALLOW_UNASSIGNED) : null)) .baseUrl("https://" + instance)
.addConverterFactory(SimpleXmlConverterFactory.create()) .addConverterFactory(SimpleXmlConverterFactory.create())
.client(okHttpClient) .client(okHttpClient)
.build(); .build();
@ -85,8 +87,7 @@ public class Nitter implements Serializable {
status.text = feedItem.title; status.text = feedItem.title;
status.content = status.content.replaceAll("<img [^>]*src=\"[^\"]+\"[^>]*>", ""); status.content = status.content.replaceAll("<img [^>]*src=\"[^\"]+\"[^>]*>", "");
status.visibility = "public"; status.visibility = "public";
String dateFormat = "E', 'dd' 'MMM' 'yyyy' 'hh:m:s' GMT'"; status.created_at = Helper.stringToDateWithFormat(context, feedItem.pubDate, "EEE, dd MMM yyyy HH:mm:ss zzz");
status.created_at = Helper.stringToDateWithFormat(context, feedItem.pubDate, dateFormat);
status.uri = feedItem.guid; status.uri = feedItem.guid;
status.url = feedItem.link; status.url = feedItem.link;
if (!accounts.containsKey(feedItem.creator)) { if (!accounts.containsKey(feedItem.creator)) {
@ -181,83 +182,4 @@ public class Nitter implements Serializable {
} }
public static Status nitterHTMLParser(Context context, org.jsoup.nodes.Element timelineItem, String nitterInstance) {
if(timelineItem == null) {
return null;
}
Status status = new Status();
Account account = new Account();
String fedilabInstance = "nitter.fedilab.app";
org.jsoup.nodes.Element messageLink;
if(timelineItem.select(".quote-text").html().isEmpty()) {
status.content = timelineItem.select(".tweet-content").html();
status.text = timelineItem.select(".tweet-content").text();
status.url = "https://"+ nitterInstance +timelineItem.select(".tweet-link").attr("href");
messageLink = timelineItem.select(".tweet-link").first();
} else {
status.content = timelineItem.select(".quote-text").html();
status.text = timelineItem.select(".quote-text").text();
status.url = "https://"+ nitterInstance +timelineItem.select(".quote-link").attr("href");
messageLink = timelineItem.select(".quote-link").first();
}
status.uri = status.url;
String status_id = String.valueOf(ThreadLocalRandom.current().nextLong(10,10000000));;
if(messageLink != null){
String[] splitLink = messageLink.attr("href").split("/");
status_id = splitLink[splitLink.length-1];
}
String pubDate = timelineItem.select(".tweet-date").select("a").attr("title");
org.jsoup.nodes.Element nameElement = timelineItem.select(".fullname").first();
String name = nameElement!= null?nameElement.text():"";
org.jsoup.nodes.Element userNameElement = timelineItem.select(".username").first();
String userName = userNameElement!= null?userNameElement.text().replace("@",""):"";
String avatar = "https://" + fedilabInstance + timelineItem.select(".avatar").attr("src");
account.id = userName;
account.acct = userName;
if(timelineItem.select(".replying-to").html().isEmpty()) {
account.username = userName;
account.display_name = name;
} else {
account.display_name = timelineItem.select(".fullname").text() +"&nbsp;" +timelineItem.select(".replying-to").text();
}
account.avatar = avatar;
account.avatar_static = avatar;
account.url = "https://"+ nitterInstance +"/" + userName;
status.id = status_id;
status.account = account;
Elements imageElements = timelineItem.select(".attachments").select("img");
Elements videoElements = timelineItem.select(".attachments").select("video");
ArrayList<Attachment> attachmentList = new ArrayList<>();
for(org.jsoup.nodes.Element imageElement: imageElements) {
Attachment attachment = new Attachment();
attachment.type = "image";
attachment.url = "https://"+fedilabInstance+imageElement.attr("src");
attachment.preview_url = "https://"+fedilabInstance+imageElement.attr("src");
attachment.id = imageElement.attr("src");
attachmentList.add(attachment);
}
for(org.jsoup.nodes.Element videoElement: videoElements) {
Attachment attachment = new Attachment();
attachment.type = "video";
attachment.url = "https://"+fedilabInstance+videoElement.child(0).attr("src");
attachment.preview_url = "https://"+fedilabInstance+videoElement.attr("poster");
attachment.id = videoElement.attr("poster");
attachmentList.add(attachment);
}
status.visibility = "public";
status.media_attachments = attachmentList;
String dateFormat = "MMM d', 'yyyy' · 'h:m a' UTC'";
status.created_at = Helper.stringToDateWithFormat(context, pubDate, dateFormat);
return status;
}
} }

View file

@ -18,23 +18,24 @@ import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.media3.database.ExoDatabaseProvider;
import androidx.media3.datasource.DataSource;
import androidx.media3.datasource.DefaultDataSourceFactory;
import androidx.media3.datasource.DefaultHttpDataSource;
import androidx.media3.datasource.FileDataSource;
import androidx.media3.datasource.cache.CacheDataSink;
import androidx.media3.datasource.cache.CacheDataSource;
import androidx.media3.datasource.cache.LeastRecentlyUsedCacheEvictor;
import androidx.media3.datasource.cache.SimpleCache;
import androidx.media3.exoplayer.upstream.DefaultBandwidthMeter;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import com.google.android.exoplayer2.database.ExoDatabaseProvider;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource;
import com.google.android.exoplayer2.upstream.FileDataSource;
import com.google.android.exoplayer2.upstream.cache.CacheDataSink;
import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor;
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
import java.io.File; import java.io.File;
import app.fedilab.android.R; import app.fedilab.android.R;
@androidx.annotation.OptIn(markerClass = androidx.media3.common.util.UnstableApi.class)
public class CacheDataSourceFactory implements DataSource.Factory { public class CacheDataSourceFactory implements DataSource.Factory {
private static SimpleCache sDownloadCache; private static SimpleCache sDownloadCache;

View file

@ -1,121 +0,0 @@
package app.fedilab.android.mastodon.helper;
/* Copyright 2023 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* 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.mentionPatternALL;
import android.util.Patterns;
import java.util.ArrayList;
import java.util.regex.Matcher;
public class ComposeHelper {
/**
* Allows to split the toot by dot "." for sentences - adds number at the end automatically
*
* @param content String initial content
* @param maxChars int the max chars per toot
* @return ArrayList<String> split toot
*/
public static ArrayList<String> splitToots(String content, int maxChars) {
String[] splitContent = content.split("\\s");
ArrayList<String> mentions = new ArrayList<>();
int mentionLength;
StringBuilder mentionString = new StringBuilder();
Matcher matcher = mentionPatternALL.matcher(content);
while (matcher.find()) {
String mentionLong = matcher.group(1);
if (mentionLong != null) {
if (!mentions.contains(mentionLong)) {
mentions.add(mentionLong);
}
}
String mentionShort = matcher.group(2);
if (mentionShort != null) {
if (!mentions.contains(mentionShort)) {
mentions.add(mentionShort);
}
}
}
for (String mention : mentions) {
mentionString.append(mention).append(" ");
}
mentionLength = countLength(mentionString.toString()) + 1;
int maxCharsPerMessage = maxChars - mentionLength;
int totalCurrent = 0;
ArrayList<String> reply = new ArrayList<>();
int index = 0;
for (String s : splitContent) {
if ((totalCurrent + s.length() + 1) < maxCharsPerMessage) {
totalCurrent += (s.length() + 1);
} else {
if (content.length() > totalCurrent && totalCurrent > 0) {
String tempContent = content.substring(0, (totalCurrent));
content = content.substring(totalCurrent);
reply.add(index, tempContent);
index++;
totalCurrent = s.length() + 1;
}
}
}
if (totalCurrent > 0) {
reply.add(index, content);
}
if (reply.size() > 1) {
int i = 0;
for (String r : reply) {
if (mentions.size() > 0) {
String tmpMention = mentionString.toString();
for (String mention : mentions) {
if (r.contains(mention)) {
tmpMention = tmpMention.replace(mention, "");
}
}
reply.set(i, r + " " + tmpMention);
} else {
reply.set(i, r);
}
i++;
}
}
return reply;
}
/***
* Returns the length used when composing a toot
* @param mentions String containing mentions
* @return int - characters used
*/
public static int countLength(String mentions) {
String contentCount = mentions;
contentCount = contentCount.replaceAll("(?i)(^|[^/\\w])@(([a-z0-9_]+)@[a-z0-9.-]+[a-z0-9]+)", "$1@$3");
Matcher matcherALink = Patterns.WEB_URL.matcher(contentCount);
while (matcherALink.find()) {
final String url = matcherALink.group(1);
if (url != null) {
contentCount = contentCount.replace(url, "abcdefghijklmnopkrstuvw");
}
}
return contentCount.length();
}
}

View file

@ -14,7 +14,6 @@ package app.fedilab.android.mastodon.helper;
* You should have received a copy of the GNU General Public License along with Fedilab; if not, * You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
@ -32,9 +31,9 @@ import androidx.preference.PreferenceManager;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.io.IOException; import java.io.IOException;
import java.net.IDN;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit;
import app.fedilab.android.BaseMainActivity; import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R; import app.fedilab.android.R;
@ -45,7 +44,6 @@ import app.fedilab.android.mastodon.client.entities.api.Results;
import app.fedilab.android.mastodon.client.entities.api.Status; import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.mastodon.client.entities.app.Account; import app.fedilab.android.mastodon.client.entities.app.Account;
import app.fedilab.android.mastodon.client.entities.app.BaseAccount; import app.fedilab.android.mastodon.client.entities.app.BaseAccount;
import app.fedilab.android.mastodon.client.entities.app.CachedBundle;
import app.fedilab.android.mastodon.exception.DBException; import app.fedilab.android.mastodon.exception.DBException;
import app.fedilab.android.mastodon.ui.drawer.AccountsSearchAdapter; import app.fedilab.android.mastodon.ui.drawer.AccountsSearchAdapter;
import app.fedilab.android.mastodon.viewmodel.mastodon.AccountsVM; import app.fedilab.android.mastodon.viewmodel.mastodon.AccountsVM;
@ -73,30 +71,10 @@ public class CrossActionHelper {
final SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context); final SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
new Thread(() -> { new Thread(() -> {
try { try {
boolean confirmFav = sharedpreferences.getBoolean(context.getString(R.string.SET_NOTIF_VALIDATION_FAV), false);
boolean confirmBoost = sharedpreferences.getBoolean(context.getString(R.string.SET_NOTIF_VALIDATION), true);
List<BaseAccount> accounts = new Account(context).getCrossAccounts(); List<BaseAccount> accounts = new Account(context).getCrossAccounts();
if (accounts.size() == 1) { if (accounts.size() == 1) {
Handler mainHandler = new Handler(Looper.getMainLooper()); Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> { Runnable myRunnable = () -> fetchRemote(context, actionType, accounts.get(0), targetedAccount, targetedStatus);
if ((actionType == TypeOfCrossAction.REBLOG_ACTION && confirmBoost) || (actionType == TypeOfCrossAction.FAVOURITE_ACTION && confirmFav)) {
AlertDialog.Builder alt_bld = new MaterialAlertDialogBuilder(context);
if (actionType == TypeOfCrossAction.REBLOG_ACTION) {
alt_bld.setMessage(context.getString(R.string.reblog_add));
} else {
alt_bld.setMessage(context.getString(R.string.favourite_add));
}
alt_bld.setPositiveButton(R.string.yes, (dia, id) -> {
fetchRemote(context, actionType, accounts.get(0), targetedAccount, targetedStatus);
dia.dismiss();
});
alt_bld.setNegativeButton(R.string.cancel, (dia, id) -> dia.dismiss());
AlertDialog alert = alt_bld.create();
alert.show();
} else {
fetchRemote(context, actionType, accounts.get(0), targetedAccount, targetedStatus);
}
};
mainHandler.post(myRunnable); mainHandler.post(myRunnable);
} else { } else {
List<app.fedilab.android.mastodon.client.entities.api.Account> accountList = new ArrayList<>(); List<app.fedilab.android.mastodon.client.entities.api.Account> accountList = new ArrayList<>();
@ -117,7 +95,8 @@ public class CrossActionHelper {
} }
builderSingle.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss()); builderSingle.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
builderSingle.setAdapter(accountsSearchAdapter, (dialog, which) -> { builderSingle.setAdapter(accountsSearchAdapter, (dialog, which) -> {
boolean confirmFav = sharedpreferences.getBoolean(context.getString(R.string.SET_NOTIF_VALIDATION_FAV), false);
boolean confirmBoost = sharedpreferences.getBoolean(context.getString(R.string.SET_NOTIF_VALIDATION), true);
BaseAccount selectedAccount = accountArray[which]; BaseAccount selectedAccount = accountArray[which];
if ((actionType == TypeOfCrossAction.REBLOG_ACTION && confirmBoost) || (actionType == TypeOfCrossAction.FAVOURITE_ACTION && confirmFav)) { if ((actionType == TypeOfCrossAction.REBLOG_ACTION && confirmBoost) || (actionType == TypeOfCrossAction.FAVOURITE_ACTION && confirmFav)) {
AlertDialog.Builder alt_bld = new MaterialAlertDialogBuilder(context); AlertDialog.Builder alt_bld = new MaterialAlertDialogBuilder(context);
@ -128,9 +107,9 @@ public class CrossActionHelper {
} }
alt_bld.setPositiveButton(R.string.yes, (dia, id) -> { alt_bld.setPositiveButton(R.string.yes, (dia, id) -> {
fetchRemote(context, actionType, selectedAccount, targetedAccount, targetedStatus); fetchRemote(context, actionType, selectedAccount, targetedAccount, targetedStatus);
dia.dismiss(); dialog.dismiss();
}); });
alt_bld.setNegativeButton(R.string.cancel, (dia, id) -> dia.dismiss()); alt_bld.setNegativeButton(R.string.cancel, (dia, id) -> dialog.dismiss());
AlertDialog alert = alt_bld.create(); AlertDialog alert = alt_bld.create();
alert.show(); alert.show();
} else { } else {
@ -167,7 +146,7 @@ public class CrossActionHelper {
} }
searchVM.search(ownerAccount.instance, ownerAccount.token, search, null, "accounts", false, true, false, 0, null, null, 1) searchVM.search(ownerAccount.instance, ownerAccount.token, search, null, "accounts", false, true, false, 0, null, null, 1)
.observe((LifecycleOwner) context, results -> { .observe((LifecycleOwner) context, results -> {
if (results != null && results.accounts != null && !results.accounts.isEmpty()) { if (results.accounts != null && results.accounts.size() > 0) {
app.fedilab.android.mastodon.client.entities.api.Account account = results.accounts.get(0); app.fedilab.android.mastodon.client.entities.api.Account account = results.accounts.get(0);
applyAction(context, actionType, ownerAccount, account, null); applyAction(context, actionType, ownerAccount, account, null);
} else { } else {
@ -177,7 +156,7 @@ public class CrossActionHelper {
} else if (targetedStatus != null) { } else if (targetedStatus != null) {
searchVM.search(ownerAccount.instance, ownerAccount.token, targetedStatus.uri, null, "statuses", false, true, false, 0, null, null, 1) searchVM.search(ownerAccount.instance, ownerAccount.token, targetedStatus.uri, null, "statuses", false, true, false, 0, null, null, 1)
.observe((LifecycleOwner) context, results -> { .observe((LifecycleOwner) context, results -> {
if (results != null && results.statuses != null && !results.statuses.isEmpty()) { if (results != null && results.statuses != null && results.statuses.size() > 0) {
Status status = results.statuses.get(0); Status status = results.statuses.get(0);
applyAction(context, actionType, ownerAccount, null, status); applyAction(context, actionType, ownerAccount, null, status);
} else { } else {
@ -204,97 +183,89 @@ public class CrossActionHelper {
statusesVM = new ViewModelProvider((ViewModelStoreOwner) context).get("crossactions", StatusesVM.class); statusesVM = new ViewModelProvider((ViewModelStoreOwner) context).get("crossactions", StatusesVM.class);
} }
switch (actionType) { switch (actionType) {
case MUTE_ACTION -> { case MUTE_ACTION:
assert accountsVM != null; assert accountsVM != null;
accountsVM.mute(ownerAccount.instance, ownerAccount.token, targetedAccount.id, true, 0) accountsVM.mute(ownerAccount.instance, ownerAccount.token, targetedAccount.id, true, 0)
.observe((LifecycleOwner) context, relationShip -> Toasty.info(context, context.getString(R.string.toast_mute), Toasty.LENGTH_SHORT).show()); .observe((LifecycleOwner) context, relationShip -> Toasty.info(context, context.getString(R.string.toast_mute), Toasty.LENGTH_SHORT).show());
} break;
case UNMUTE_ACTION -> { case UNMUTE_ACTION:
assert accountsVM != null; assert accountsVM != null;
accountsVM.unmute(ownerAccount.instance, ownerAccount.token, targetedAccount.id) accountsVM.unmute(ownerAccount.instance, ownerAccount.token, targetedAccount.id)
.observe((LifecycleOwner) context, relationShip -> Toasty.info(context, context.getString(R.string.toast_unmute), Toasty.LENGTH_SHORT).show()); .observe((LifecycleOwner) context, relationShip -> Toasty.info(context, context.getString(R.string.toast_unmute), Toasty.LENGTH_SHORT).show());
} break;
case BLOCK_ACTION -> { case BLOCK_ACTION:
assert accountsVM != null; assert accountsVM != null;
accountsVM.block(ownerAccount.instance, ownerAccount.token, targetedAccount.id) accountsVM.block(ownerAccount.instance, ownerAccount.token, targetedAccount.id)
.observe((LifecycleOwner) context, relationShip -> Toasty.info(context, context.getString(R.string.toast_block), Toasty.LENGTH_SHORT).show()); .observe((LifecycleOwner) context, relationShip -> Toasty.info(context, context.getString(R.string.toast_block), Toasty.LENGTH_SHORT).show());
} break;
case UNBLOCK_ACTION -> { case UNBLOCK_ACTION:
assert accountsVM != null; assert accountsVM != null;
accountsVM.unblock(ownerAccount.instance, ownerAccount.token, targetedAccount.id) accountsVM.unblock(ownerAccount.instance, ownerAccount.token, targetedAccount.id)
.observe((LifecycleOwner) context, relationShip -> Toasty.info(context, context.getString(R.string.toast_unblock), Toasty.LENGTH_SHORT).show()); .observe((LifecycleOwner) context, relationShip -> Toasty.info(context, context.getString(R.string.toast_unblock), Toasty.LENGTH_SHORT).show());
} break;
case FOLLOW_ACTION -> { case FOLLOW_ACTION:
assert accountsVM != null; assert accountsVM != null;
accountsVM.follow(ownerAccount.instance, ownerAccount.token, targetedAccount.id, true, false, null) accountsVM.follow(ownerAccount.instance, ownerAccount.token, targetedAccount.id, true, false, null)
.observe((LifecycleOwner) context, relationShip -> Toasty.info(context, context.getString(R.string.toast_follow), Toasty.LENGTH_SHORT).show()); .observe((LifecycleOwner) context, relationShip -> Toasty.info(context, context.getString(R.string.toast_follow), Toasty.LENGTH_SHORT).show());
} break;
case UNFOLLOW_ACTION -> { case UNFOLLOW_ACTION:
assert accountsVM != null; assert accountsVM != null;
accountsVM.unfollow(ownerAccount.instance, ownerAccount.token, targetedAccount.id) accountsVM.unfollow(ownerAccount.instance, ownerAccount.token, targetedAccount.id)
.observe((LifecycleOwner) context, relationShip -> Toasty.info(context, context.getString(R.string.toast_unfollow), Toasty.LENGTH_SHORT).show()); .observe((LifecycleOwner) context, relationShip -> Toasty.info(context, context.getString(R.string.toast_unfollow), Toasty.LENGTH_SHORT).show());
} break;
case FAVOURITE_ACTION -> { case FAVOURITE_ACTION:
assert statusesVM != null; assert statusesVM != null;
statusesVM.favourite(ownerAccount.instance, ownerAccount.token, targetedStatus.id) statusesVM.favourite(ownerAccount.instance, ownerAccount.token, targetedStatus.id)
.observe((LifecycleOwner) context, status -> Toasty.info(context, context.getString(R.string.toast_favourite), Toasty.LENGTH_SHORT).show()); .observe((LifecycleOwner) context, status -> Toasty.info(context, context.getString(R.string.toast_favourite), Toasty.LENGTH_SHORT).show());
} break;
case UNFAVOURITE_ACTION -> { case UNFAVOURITE_ACTION:
assert statusesVM != null; assert statusesVM != null;
statusesVM.unFavourite(ownerAccount.instance, ownerAccount.token, targetedStatus.id) statusesVM.unFavourite(ownerAccount.instance, ownerAccount.token, targetedStatus.id)
.observe((LifecycleOwner) context, status -> Toasty.info(context, context.getString(R.string.toast_unfavourite), Toasty.LENGTH_SHORT).show()); .observe((LifecycleOwner) context, status -> Toasty.info(context, context.getString(R.string.toast_unfavourite), Toasty.LENGTH_SHORT).show());
} break;
case BOOKMARK_ACTION -> { case BOOKMARK_ACTION:
assert statusesVM != null; assert statusesVM != null;
statusesVM.bookmark(ownerAccount.instance, ownerAccount.token, targetedStatus.id) statusesVM.bookmark(ownerAccount.instance, ownerAccount.token, targetedStatus.id)
.observe((LifecycleOwner) context, status -> Toasty.info(context, context.getString(R.string.toast_bookmark), Toasty.LENGTH_SHORT).show()); .observe((LifecycleOwner) context, status -> Toasty.info(context, context.getString(R.string.toast_bookmark), Toasty.LENGTH_SHORT).show());
} break;
case UNBOOKMARK_ACTION -> { case UNBOOKMARK_ACTION:
assert statusesVM != null; assert statusesVM != null;
statusesVM.unBookmark(ownerAccount.instance, ownerAccount.token, targetedStatus.id) statusesVM.unBookmark(ownerAccount.instance, ownerAccount.token, targetedStatus.id)
.observe((LifecycleOwner) context, status -> Toasty.info(context, context.getString(R.string.toast_unbookmark), Toasty.LENGTH_SHORT).show()); .observe((LifecycleOwner) context, status -> Toasty.info(context, context.getString(R.string.toast_unbookmark), Toasty.LENGTH_SHORT).show());
} break;
case REBLOG_ACTION -> { case REBLOG_ACTION:
assert statusesVM != null; assert statusesVM != null;
statusesVM.reblog(ownerAccount.instance, ownerAccount.token, targetedStatus.id, null) statusesVM.reblog(ownerAccount.instance, ownerAccount.token, targetedStatus.id, null)
.observe((LifecycleOwner) context, status -> Toasty.info(context, context.getString(R.string.toast_reblog), Toasty.LENGTH_SHORT).show()); .observe((LifecycleOwner) context, status -> Toasty.info(context, context.getString(R.string.toast_reblog), Toasty.LENGTH_SHORT).show());
} break;
case UNREBLOG_ACTION -> { case UNREBLOG_ACTION:
assert statusesVM != null; assert statusesVM != null;
statusesVM.unReblog(ownerAccount.instance, ownerAccount.token, targetedStatus.id) statusesVM.unReblog(ownerAccount.instance, ownerAccount.token, targetedStatus.id)
.observe((LifecycleOwner) context, status -> Toasty.info(context, context.getString(R.string.toast_unreblog), Toasty.LENGTH_SHORT).show()); .observe((LifecycleOwner) context, status -> Toasty.info(context, context.getString(R.string.toast_unreblog), Toasty.LENGTH_SHORT).show());
} break;
case REPLY_ACTION -> { case REPLY_ACTION:
Intent intent = new Intent(context, ComposeActivity.class); Intent intent = new Intent(context, ComposeActivity.class);
Bundle args = new Bundle(); intent.putExtra(Helper.ARG_STATUS_REPLY, targetedStatus);
args.putSerializable(Helper.ARG_STATUS_REPLY, targetedStatus); intent.putExtra(Helper.ARG_ACCOUNT, ownerAccount);
args.putSerializable(Helper.ARG_ACCOUNT, ownerAccount);
new CachedBundle(context).insertBundle(args, Helper.getCurrentAccount(context), bundleId -> {
Bundle bundle = new Bundle();
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intent.putExtras(bundle);
context.startActivity(intent); context.startActivity(intent);
}); break;
} case COMPOSE:
case COMPOSE -> {
Intent intentCompose = new Intent(context, ComposeActivity.class); Intent intentCompose = new Intent(context, ComposeActivity.class);
Bundle args = new Bundle(); intentCompose.putExtra(Helper.ARG_ACCOUNT, ownerAccount);
args.putSerializable(Helper.ARG_ACCOUNT, ownerAccount);
new CachedBundle(context).insertBundle(args, Helper.getCurrentAccount(context), bundleId -> {
Bundle bundle = new Bundle();
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intentCompose.putExtras(bundle);
context.startActivity(intentCompose); context.startActivity(intentCompose);
}); break;
}
} }
} }
private static MastodonSearchService init(Context context, String instance) { private static MastodonSearchService init(Context context, @NonNull String instance) {
final OkHttpClient okHttpClient = Helper.myOkHttpClient(context); final OkHttpClient okHttpClient = new OkHttpClient.Builder()
.readTimeout(60, TimeUnit.SECONDS)
.connectTimeout(60, TimeUnit.SECONDS)
.proxy(Helper.getProxy(context))
.build();
Retrofit retrofit = new Retrofit.Builder() Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://" + (instance != null ? IDN.toASCII(instance, IDN.ALLOW_UNASSIGNED) : null) + "/api/v2/") .baseUrl("https://" + instance + "/api/v2/")
.addConverterFactory(GsonConverterFactory.create(Helper.getDateBuilder())) .addConverterFactory(GsonConverterFactory.create(Helper.getDateBuilder()))
.client(okHttpClient) .client(okHttpClient)
.build(); .build();
@ -463,16 +434,11 @@ public class CrossActionHelper {
final BaseAccount account = accountArray[which]; final BaseAccount account = accountArray[which];
Intent intentToot = new Intent(context, ComposeActivity.class); Intent intentToot = new Intent(context, ComposeActivity.class);
bundle.putSerializable(Helper.ARG_ACCOUNT, account); bundle.putSerializable(Helper.ARG_ACCOUNT, account);
new CachedBundle(context).insertBundle(bundle, Helper.getCurrentAccount(context), bundleId -> { intentToot.putExtras(bundle);
Bundle bundleCached = new Bundle();
bundleCached.putLong(Helper.ARG_INTENT_ID, bundleId);
intentToot.putExtras(bundleCached);
context.startActivity(intentToot); context.startActivity(intentToot);
((BaseActivity) context).finish(); ((BaseActivity) context).finish();
dialog.dismiss(); dialog.dismiss();
}); });
});
builderSingle.show(); builderSingle.show();
} }
@ -542,7 +508,7 @@ public class CrossActionHelper {
} }
} }
} }
} catch (Exception e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }

View file

@ -30,8 +30,8 @@ import app.fedilab.android.mastodon.client.entities.api.Status;
public class CustomEmoji extends ReplacementSpan { public class CustomEmoji extends ReplacementSpan {
private final WeakReference<View> viewWeakReference;
private float scale; private float scale;
private final WeakReference<View> viewWeakReference;
private Drawable imageDrawable; private Drawable imageDrawable;
private boolean callbackCalled; private boolean callbackCalled;
@ -47,7 +47,7 @@ public class CustomEmoji extends ReplacementSpan {
} }
public SpannableStringBuilder makeEmoji(SpannableStringBuilder content, List<Emoji> emojiList, boolean animate, Status.Callback callback) { public SpannableStringBuilder makeEmoji(SpannableStringBuilder content, List<Emoji> emojiList, boolean animate, Status.Callback callback) {
if (emojiList != null && !emojiList.isEmpty()) { if (emojiList != null && emojiList.size() > 0) {
int count = 1; int count = 1;
for (Emoji emoji : emojiList) { for (Emoji emoji : emojiList) {
Matcher matcher = Pattern.compile(":" + emoji.shortcode + ":", Pattern.LITERAL) Matcher matcher = Pattern.compile(":" + emoji.shortcode + ":", Pattern.LITERAL)
@ -68,6 +68,13 @@ public class CustomEmoji extends ReplacementSpan {
@Override @Override
public int getSize(@NonNull Paint paint, CharSequence charSequence, int i, int i1, @Nullable Paint.FontMetricsInt fontMetricsInt) { public int getSize(@NonNull Paint paint, CharSequence charSequence, int i, int i1, @Nullable Paint.FontMetricsInt fontMetricsInt) {
if (fontMetricsInt != null) {
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
fontMetricsInt.top = (int) fontMetrics.top;
fontMetricsInt.ascent = (int) fontMetrics.ascent;
fontMetricsInt.descent = (int) fontMetrics.descent;
fontMetricsInt.bottom = (int) fontMetrics.bottom;
}
return (int) (paint.getTextSize() * scale); return (int) (paint.getTextSize() * scale);
} }
@ -78,7 +85,7 @@ public class CustomEmoji extends ReplacementSpan {
int emojiSize = (int) (paint.getTextSize() * scale); int emojiSize = (int) (paint.getTextSize() * scale);
imageDrawable.setBounds(0, 0, emojiSize, emojiSize); imageDrawable.setBounds(0, 0, emojiSize, emojiSize);
int transY = bottom - imageDrawable.getBounds().bottom; int transY = bottom - imageDrawable.getBounds().bottom;
transY -= (int) (paint.getFontMetrics().descent / 2); transY -= paint.getFontMetrics().descent / 2;
canvas.translate(x, (float) transY); canvas.translate(x, (float) transY);
imageDrawable.draw(canvas); imageDrawable.draw(canvas);
canvas.restore(); canvas.restore();
@ -86,50 +93,40 @@ public class CustomEmoji extends ReplacementSpan {
} }
public Target<Drawable> getTarget(boolean animate, Status.Callback callback) { public Target<Drawable> getTarget(boolean animate, Status.Callback callback) {
return new CustomTarget<>() { return new CustomTarget<Drawable>() {
@Override
public void onStart() {
if (imageDrawable instanceof Animatable) {
((Animatable) imageDrawable).start();
}
}
@Override
public void onStop() {
if (imageDrawable instanceof Animatable) {
((Animatable) imageDrawable).stop();
}
}
@Override @Override
public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) { public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
View view = viewWeakReference.get(); View view = viewWeakReference.get();
if (animate && resource instanceof Animatable) { if (animate && resource instanceof Animatable) {
Drawable.Callback drawableCallBack = resource.getCallback();
resource.setCallback(new Drawable.Callback() { resource.setCallback(new Drawable.Callback() {
@Override @Override
public void invalidateDrawable(@NonNull Drawable drawable) { public void invalidateDrawable(@NonNull Drawable drawable) {
if(view != null) { if (drawableCallBack != null) {
view.invalidate(); drawableCallBack.invalidateDrawable(drawable);
} }
view.invalidate();
} }
@Override @Override
public void scheduleDrawable(@NonNull Drawable drawable, @NonNull Runnable runnable, long l) { public void scheduleDrawable(@NonNull Drawable drawable, @NonNull Runnable runnable, long l) {
view.postDelayed(runnable, l); if (drawableCallBack != null) {
drawableCallBack.scheduleDrawable(drawable, runnable, l);
}
} }
@Override @Override
public void unscheduleDrawable(@NonNull Drawable drawable, @NonNull Runnable runnable) { public void unscheduleDrawable(@NonNull Drawable drawable, @NonNull Runnable runnable) {
view.removeCallbacks(runnable); if (drawableCallBack != null) {
drawableCallBack.unscheduleDrawable(drawable, runnable);
}
} }
}); });
((Animatable) resource).start(); ((Animatable) resource).start();
} }
imageDrawable = resource; imageDrawable = resource;
if(view != null) { if (view != null) {
view.invalidate(); view.invalidate();
} }
if (callback != null && !callbackCalled) { if (callback != null && !callbackCalled) {
@ -140,17 +137,6 @@ public class CustomEmoji extends ReplacementSpan {
@Override @Override
public void onLoadCleared(@Nullable Drawable placeholder) { public void onLoadCleared(@Nullable Drawable placeholder) {
View view = viewWeakReference.get();
if (imageDrawable != null) {
if (imageDrawable instanceof Animatable) {
((Animatable) imageDrawable).stop();
imageDrawable.setCallback(null);
}
}
imageDrawable = null;
if(view != null) {
view.invalidate();
}
} }
}; };
} }

View file

@ -69,6 +69,7 @@ public class DividerDecoration extends RecyclerView.ItemDecoration {
StatusAdapter statusAdapter = ((StatusAdapter) parent.getAdapter()); StatusAdapter statusAdapter = ((StatusAdapter) parent.getAdapter());
if (statusAdapter != null && statusAdapter.getItemCount() > position && position > 0) { if (statusAdapter != null && statusAdapter.getItemCount() > position && position > 0) {
Status status = statusAdapter.getItem(position); Status status = statusAdapter.getItem(position);
int start = (int) Helper.convertDpToPixel( int start = (int) Helper.convertDpToPixel(
6 * fontScale * CommentDecorationHelper.getIndentation(status.in_reply_to_id, statusList, indentationMax), 6 * fontScale * CommentDecorationHelper.getIndentation(status.in_reply_to_id, statusList, indentationMax),
_mContext); _mContext);
@ -94,6 +95,7 @@ public class DividerDecoration extends RecyclerView.ItemDecoration {
int indentation = Math.min( int indentation = Math.min(
CommentDecorationHelper.getIndentation(status.in_reply_to_id, statusList, indentationMax), CommentDecorationHelper.getIndentation(status.in_reply_to_id, statusList, indentationMax),
indentationMax); indentationMax);
if (indentation > 0) { if (indentation > 0) {
Paint paint = new Paint(); Paint paint = new Paint();
paint.setDither(false); paint.setDither(false);
@ -107,7 +109,12 @@ public class DividerDecoration extends RecyclerView.ItemDecoration {
startPx = c.getWidth() - startPx; startPx = c.getWidth() - startPx;
float bottomPx = view.getBottom(); float bottomPx = view.getBottom();
int color = colorList.get(j%(colorList.size()-1)); int color;
if (j >= colorList.size()) {
color = colorList.get(j - colorList.size());
} else {
color = colorList.get(j);
}
paint.setColor(ResourcesCompat.getColor(_mContext.getResources(), color, _mContext.getTheme())); paint.setColor(ResourcesCompat.getColor(_mContext.getResources(), color, _mContext.getTheme()));
if (j == indentationMax - 1) { if (j == indentationMax - 1) {
paint.setPathEffect(new DashPathEffect( paint.setPathEffect(new DashPathEffect(
@ -118,7 +125,12 @@ public class DividerDecoration extends RecyclerView.ItemDecoration {
c.drawLine(startPx, view.getTop() - margin, startPx, bottomPx, paint); c.drawLine(startPx, view.getTop() - margin, startPx, bottomPx, paint);
} }
int color = colorList.get(indentation%colorList.size()-1); int color;
if (indentation - 1 >= colorList.size()) {
color = colorList.get(indentation - 1 - colorList.size());
} else {
color = colorList.get(indentation - 1);
}
paint.setColor(ResourcesCompat.getColor(_mContext.getResources(), color, _mContext.getTheme())); paint.setColor(ResourcesCompat.getColor(_mContext.getResources(), color, _mContext.getTheme()));
float startDp = 6 * fontScale * (indentation - 1) + 6 * fontScale; float startDp = 6 * fontScale * (indentation - 1) + 6 * fontScale;

View file

@ -127,7 +127,12 @@ public class DividerDecorationSimple extends RecyclerView.ItemDecoration {
c.drawLine(startPx, view.getTop() - margin, startPx, bottomPx, paint); c.drawLine(startPx, view.getTop() - margin, startPx, bottomPx, paint);
} }
int color = colorList.get(indentation%colorList.size()-1); int color;
if (indentation - 1 >= colorList.size()) {
color = colorList.get(indentation - 1 - colorList.size());
} else {
color = colorList.get(indentation - 1);
}
paint.setColor(ResourcesCompat.getColor(_mContext.getResources(), color, _mContext.getTheme())); paint.setColor(ResourcesCompat.getColor(_mContext.getResources(), color, _mContext.getTheme()));
float startDp = 6 * fontScale * (indentation - 1) + 6 * fontScale; float startDp = 6 * fontScale * (indentation - 1) + 6 * fontScale;

View file

@ -0,0 +1,289 @@
package app.fedilab.android.mastodon.helper;
/* Copyright 2022 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* 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.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Base64;
import androidx.preference.PreferenceManager;
import org.spongycastle.asn1.ASN1ObjectIdentifier;
import org.spongycastle.asn1.x9.ECNamedCurveTable;
import org.spongycastle.asn1.x9.X9ECParameters;
import org.spongycastle.crypto.params.ECNamedDomainParameters;
import org.spongycastle.crypto.params.ECPrivateKeyParameters;
import org.spongycastle.crypto.params.ECPublicKeyParameters;
import org.spongycastle.jce.spec.ECNamedCurveSpec;
import org.spongycastle.jce.spec.ECParameterSpec;
import org.spongycastle.jce.spec.ECPrivateKeySpec;
import org.spongycastle.jce.spec.ECPublicKeySpec;
import org.spongycastle.math.ec.ECCurve;
import org.spongycastle.math.ec.ECPoint;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECGenParameterSpec;
import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
public class ECDH {
public static final String kp_public = "kp_public";
public static final String peer_public = "peer_public";
public static final String PROVIDER = org.spongycastle.jce.provider.BouncyCastleProvider.PROVIDER_NAME;
public static final String kp_private = "kp_private";
public static final String KEGEN_ALG = "ECDH";
public static final String name = "prime256v1";
private static final String kp_public_affine_x = "kp_public_affine_x";
private static final String kp_public_affine_y = "kp_public_affine_y";
private static ECDH instance;
static {
Security.addProvider(new org.spongycastle.jce.provider.BouncyCastleProvider());
}
public final KeyFactory kf;
private final KeyPairGenerator kpg;
private final String slug;
public ECDH(String slug) throws Exception {
if (slug == null) {
throw new Exception("slug cannot be null");
}
try {
kf = KeyFactory.getInstance(KEGEN_ALG, PROVIDER);
kpg = KeyPairGenerator.getInstance(KEGEN_ALG, PROVIDER);
this.slug = slug;
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
public static synchronized ECDH getInstance(String slug) throws Exception {
if (instance == null) {
instance = new ECDH(slug);
}
return instance;
}
public static String base64Encode(byte[] b) {
return Base64.encodeToString(
b, Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP);
}
static byte[] base64Decode(String str) {
return Base64.decode(str, Base64.URL_SAFE);
}
synchronized KeyPair generateKeyPair()
throws Exception {
ECGenParameterSpec ecParamSpec = new ECGenParameterSpec(name);
kpg.initialize(ecParamSpec);
return kpg.generateKeyPair();
}
private byte[] generateSecret(PrivateKey myPrivKey, PublicKey otherPubKey) throws Exception {
KeyAgreement keyAgreement = KeyAgreement.getInstance(KEGEN_ALG);
keyAgreement.init(myPrivKey);
keyAgreement.doPhase(otherPubKey, true);
return keyAgreement.generateSecret();
}
synchronized KeyPair readKeyPair(Context context)
throws Exception {
return new KeyPair(readMyPublicKey(context), readMyPrivateKey(context));
}
@SuppressLint("ApplySharedPref")
public KeyPair newPair(Context context) {
SharedPreferences.Editor prefsEditor = PreferenceManager
.getDefaultSharedPreferences(context).edit();
KeyPair kp;
try {
kp = generateKeyPair();
} catch (Exception e) {
e.printStackTrace();
return null;
}
ECPublicKey key = (ECPublicKey) kp.getPublic();
byte[] x = key.getW().getAffineX().toByteArray();
byte[] y = key.getW().getAffineY().toByteArray();
BigInteger xbi = new BigInteger(1, x);
BigInteger ybi = new BigInteger(1, y);
X9ECParameters x9 = ECNamedCurveTable.getByName(name);
ASN1ObjectIdentifier oid = ECNamedCurveTable.getOID(name);
ECCurve curve = x9.getCurve();
ECPoint point = curve.createPoint(xbi, ybi);
ECNamedDomainParameters dParams = new ECNamedDomainParameters(oid,
x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed());
ECPublicKeyParameters pubKey = new ECPublicKeyParameters(point, dParams);
ECPrivateKeyParameters privateKey = new ECPrivateKeyParameters(new BigInteger(kp.getPrivate().getEncoded()), pubKey.getParameters());
byte[] privateKeyBytes = privateKey.getD().toByteArray();
String keyString = base64Encode(pubKey.getQ().getEncoded(false));
String keypString = base64Encode(privateKeyBytes);
prefsEditor.putString(kp_public + slug, keyString);
prefsEditor.putString(kp_public_affine_x + slug, key.getW().getAffineX().toString());
prefsEditor.putString(kp_public_affine_y + slug, key.getW().getAffineY().toString());
prefsEditor.putString(kp_private + slug, keypString);
prefsEditor.commit();
return kp;
}
synchronized PublicKey readMyPublicKey(Context context) throws Exception {
X9ECParameters x9 = ECNamedCurveTable.getByName(name);
ASN1ObjectIdentifier oid = ECNamedCurveTable.getOID(name);
SharedPreferences prefs = PreferenceManager
.getDefaultSharedPreferences(context);
BigInteger xbi = new BigInteger(prefs.getString(kp_public_affine_x + slug, "0"));
BigInteger ybi = new BigInteger(prefs.getString(kp_public_affine_y + slug, "0"));
ECNamedDomainParameters dParams = new ECNamedDomainParameters(oid,
x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed());
ECNamedCurveSpec ecNamedCurveSpec = new ECNamedCurveSpec(name, dParams.getCurve(), dParams.getG(), dParams.getN());
java.security.spec.ECPoint w = new java.security.spec.ECPoint(xbi, ybi);
return kf.generatePublic(new java.security.spec.ECPublicKeySpec(w, ecNamedCurveSpec));
}
public String uncryptMessage(Context context, String cyphered) {
byte[] privateKey = getSharedSecret(context);
try {
Cipher outCipher = Cipher.getInstance("ECIES", PROVIDER);
PrivateKey ddd = readPrivateKey(privateKey);
outCipher.init(Cipher.DECRYPT_MODE, readPrivateKey(privateKey));
byte[] plaintext = outCipher.doFinal(base64Decode(cyphered));
return new String(plaintext);
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
public PublicKey readPublicKey(String keyStr) throws Exception {
ECParameterSpec parameterSpec = org.spongycastle.jce.ECNamedCurveTable.getParameterSpec(name);
ECCurve curve = parameterSpec.getCurve();
ECPoint point = curve.decodePoint(base64Decode(keyStr));
ECPublicKeySpec pubSpec = new ECPublicKeySpec(point, parameterSpec);
return kf.generatePublic(pubSpec);
}
public PrivateKey readPrivateKey(byte[] key) throws Exception {
ECParameterSpec parameterSpec = org.spongycastle.jce.ECNamedCurveTable.getParameterSpec(name);
ECPrivateKeySpec pubSpec = new ECPrivateKeySpec(new BigInteger(1, key), parameterSpec);
return kf.generatePrivate(pubSpec);
}
synchronized PrivateKey readMyPrivateKey(Context context) throws Exception {
X9ECParameters x9 = ECNamedCurveTable.getByName(name);
ASN1ObjectIdentifier oid = ECNamedCurveTable.getOID(name);
SharedPreferences prefs = PreferenceManager
.getDefaultSharedPreferences(context);
BigInteger ybi = new BigInteger(prefs.getString(kp_public_affine_y + slug, "0"));
ECNamedDomainParameters dParams = new ECNamedDomainParameters(oid,
x9.getCurve(), x9.getG(), x9.getN(), x9.getH(), x9.getSeed());
ECNamedCurveSpec ecNamedCurveSpec = new ECNamedCurveSpec(name, dParams.getCurve(), dParams.getG(), dParams.getN());
return kf.generatePrivate(new java.security.spec.ECPrivateKeySpec(ybi, ecNamedCurveSpec));
}
private synchronized KeyPair getPair(Context context) {
SharedPreferences prefs = PreferenceManager
.getDefaultSharedPreferences(context);
String strPub = prefs.getString(kp_public + slug, "");
String strPriv = prefs.getString(kp_private + slug, "");
if (strPub.trim().isEmpty() || strPriv.trim().isEmpty()) {
return newPair(context);
}
try {
return readKeyPair(context);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
PublicKey getServerKey(Context context) throws Exception {
SharedPreferences prefs = PreferenceManager
.getDefaultSharedPreferences(context);
String serverKey = prefs.getString(peer_public + slug, "");
return readPublicKey(serverKey);
}
@SuppressWarnings({"unused", "RedundantSuppression"})
public byte[] getSharedSecret(Context context) {
try {
KeyPair keyPair = getPair(context);
if (keyPair != null) {
return generateSecret(keyPair.getPrivate(), getServerKey(context));
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
return null;
}
public String getPublicKey(Context context) {
SharedPreferences prefs = PreferenceManager
.getDefaultSharedPreferences(context);
return prefs.getString(kp_public + slug, "");
}
@SuppressLint("ApplySharedPref")
public void saveServerKey(Context context, String strPeerPublic) {
SharedPreferences.Editor prefsEditor = PreferenceManager
.getDefaultSharedPreferences(context).edit();
prefsEditor.putString(peer_public + slug, strPeerPublic);
prefsEditor.commit();
}
}

View file

@ -0,0 +1,237 @@
package app.fedilab.android.mastodon.helper;
/* Copyright 2022 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* 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.client.entities.app.StatusCache.restoreNotificationFromString;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Base64;
import androidx.preference.PreferenceManager;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyAgreement;
import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import app.fedilab.android.mastodon.client.entities.api.Notification;
public class ECDHFedilab {
public static final String kp_public = "kp_public";
public static final String peer_public = "peer_public";
public static final String name = "prime256v1";
private static final byte[] P256_HEAD = new byte[]{(byte) 0x30, (byte) 0x59, (byte) 0x30, (byte) 0x13, (byte) 0x06, (byte) 0x07, (byte) 0x2a,
(byte) 0x86, (byte) 0x48, (byte) 0xce, (byte) 0x3d, (byte) 0x02, (byte) 0x01, (byte) 0x06, (byte) 0x08, (byte) 0x2a, (byte) 0x86,
(byte) 0x48, (byte) 0xce, (byte) 0x3d, (byte) 0x03, (byte) 0x01, (byte) 0x07, (byte) 0x03, (byte) 0x42, (byte) 0x00};
static {
Security.addProvider(new org.spongycastle.jce.provider.BouncyCastleProvider());
}
private final KeyPairGenerator kpg;
private final PublicKey publicKey;
private final String encodedPublicKey;
private final byte[] authKey;
private final String slug;
private final String pushPublicKey;
private final String encodedAuthKey;
private final String pushAccountID;
private final String pushPrivateKey;
PrivateKey privateKey;
private String pushPrivateKe;
public ECDHFedilab(Context context, String slug) throws Exception {
if (slug == null) {
throw new Exception("slug cannot be null");
}
try {
kpg = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec spec = new ECGenParameterSpec("prime256v1");
kpg.initialize(spec);
KeyPair keyPair = kpg.generateKeyPair();
publicKey = keyPair.getPublic();
privateKey = keyPair.getPrivate();
encodedPublicKey = Base64.encodeToString(serializeRawPublicKey(publicKey), Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING);
authKey = new byte[16];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(authKey);
byte[] randomAccountID = new byte[16];
secureRandom.nextBytes(randomAccountID);
pushPrivateKey = Base64.encodeToString(privateKey.getEncoded(), Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING);
pushPublicKey = Base64.encodeToString(publicKey.getEncoded(), Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING);
encodedAuthKey = Base64.encodeToString(authKey, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING);
pushAccountID = Base64.encodeToString(randomAccountID, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING);
SharedPreferences.Editor prefsEditor = PreferenceManager
.getDefaultSharedPreferences(context).edit();
prefsEditor.putString("pushPrivateKey" + slug, pushPrivateKey);
prefsEditor.putString("pushPublicKey" + slug, pushPublicKey);
prefsEditor.putString("encodedAuthKey" + slug, encodedAuthKey);
prefsEditor.putString("pushAccountID" + slug, pushAccountID);
prefsEditor.apply();
this.slug = slug;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
public static String getServerKey(Context context, String slug) {
SharedPreferences sharedPreferences = PreferenceManager
.getDefaultSharedPreferences(context);
return sharedPreferences.getString("server_key" + slug, null);
}
private static byte[] serializeRawPublicKey(PublicKey key) {
ECPoint point = ((ECPublicKey) key).getW();
byte[] x = point.getAffineX().toByteArray();
byte[] y = point.getAffineY().toByteArray();
if (x.length > 32)
x = Arrays.copyOfRange(x, x.length - 32, x.length);
if (y.length > 32)
y = Arrays.copyOfRange(y, y.length - 32, y.length);
byte[] result = new byte[65];
result[0] = 4;
System.arraycopy(x, 0, result, 1 + (32 - x.length), x.length);
System.arraycopy(y, 0, result, result.length - y.length, y.length);
return result;
}
public static Notification decryptNotification(Context context, String slug, byte[] messageEncrypted) {
SharedPreferences sharedPreferences = PreferenceManager
.getDefaultSharedPreferences(context);
String pushPrivateKey = sharedPreferences.getString("pushPrivateKey" + slug, null);
String pushPublicKey = sharedPreferences.getString("pushPublicKey" + slug, null);
String encodedAuthKey = sharedPreferences.getString("encodedAuthKey" + slug, null);
sharedPreferences.getString("pushAccountID" + slug, null);
PublicKey serverKey = null;
serverKey = deserializeRawPublicKey(Base64.decode(getServerKey(context, slug), Base64.URL_SAFE));
PrivateKey privateKey;
PublicKey publicKey;
byte[] authKey;
try {
KeyFactory kf = KeyFactory.getInstance("EC");
privateKey = kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.decode(pushPrivateKey, Base64.URL_SAFE)));
publicKey = kf.generatePublic(new X509EncodedKeySpec(Base64.decode(pushPublicKey, Base64.URL_SAFE)));
authKey = Base64.decode(encodedAuthKey, Base64.URL_SAFE);
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
e.printStackTrace();
return null;
}
byte[] sharedSecret;
try {
KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH");
keyAgreement.init(privateKey);
keyAgreement.doPhase(serverKey, true);
sharedSecret = keyAgreement.generateSecret();
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
e.printStackTrace();
return null;
}
byte[] secondSaltInfo = "Content-Encoding: auth\0".getBytes(StandardCharsets.UTF_8);
byte[] deriveKey;
try {
deriveKey = deriveKey(authKey, sharedSecret, secondSaltInfo, 32);
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
e.printStackTrace();
return null;
}
String decryptedStr;
try {
SecretKeySpec aesKey = new SecretKeySpec(deriveKey, "AES");
byte[] iv = Arrays.copyOfRange(messageEncrypted, 0, 12);
byte[] ciphertext = Arrays.copyOfRange(messageEncrypted, 12, messageEncrypted.length); // Separate ciphertext (the MAC is implicitly separated from the ciphertext)
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec gCMParameterSpec = new GCMParameterSpec(128, iv);
cipher.init(Cipher.DECRYPT_MODE, aesKey, gCMParameterSpec);
byte[] decrypted = cipher.doFinal(ciphertext);
decryptedStr = new String(decrypted, 2, decrypted.length - 2, StandardCharsets.UTF_8);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidAlgorithmParameterException | InvalidKeyException | BadPaddingException | IllegalBlockSizeException e) {
e.printStackTrace();
return null;
}
return restoreNotificationFromString(decryptedStr);
}
protected static PublicKey deserializeRawPublicKey(byte[] rawBytes) {
if (rawBytes.length != 65 && rawBytes.length != 64)
return null;
try {
KeyFactory kf = KeyFactory.getInstance("EC");
ByteArrayOutputStream os = new ByteArrayOutputStream();
os.write(P256_HEAD);
if (rawBytes.length == 64)
os.write(4);
os.write(rawBytes);
return kf.generatePublic(new X509EncodedKeySpec(os.toByteArray()));
} catch (NoSuchAlgorithmException | InvalidKeySpecException | IOException e) {
e.printStackTrace();
}
return null;
}
private static byte[] deriveKey(byte[] firstSalt, byte[] secondSalt, byte[] info, int length) throws NoSuchAlgorithmException, InvalidKeyException {
Mac hmacContext = Mac.getInstance("HmacSHA256");
hmacContext.init(new SecretKeySpec(firstSalt, "HmacSHA256"));
byte[] hmac = hmacContext.doFinal(secondSalt);
hmacContext.init(new SecretKeySpec(hmac, "HmacSHA256"));
hmacContext.update(info);
byte[] result = hmacContext.doFinal(new byte[]{1});
return result.length <= length ? result : Arrays.copyOfRange(result, 0, length);
}
public String getPublicKey() {
return this.encodedPublicKey;
}
public String getAuthKey() {
return this.encodedAuthKey;
}
}

View file

@ -15,7 +15,7 @@ package app.fedilab.android.mastodon.helper;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import static android.content.Context.DOWNLOAD_SERVICE; import static android.content.Context.DOWNLOAD_SERVICE;
import static app.fedilab.android.BaseMainActivity.networkAvailable; import static app.fedilab.android.BaseMainActivity.currentAccount;
import static app.fedilab.android.mastodon.activities.BaseActivity.currentThemeId; import static app.fedilab.android.mastodon.activities.BaseActivity.currentThemeId;
import static app.fedilab.android.mastodon.helper.LogoHelper.getNotificationIcon; import static app.fedilab.android.mastodon.helper.LogoHelper.getNotificationIcon;
import static app.fedilab.android.mastodon.helper.ThemeHelper.fetchAccentColor; import static app.fedilab.android.mastodon.helper.ThemeHelper.fetchAccentColor;
@ -34,7 +34,6 @@ import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo;
import android.content.res.ColorStateList;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.content.res.Resources; import android.content.res.Resources;
import android.database.Cursor; import android.database.Cursor;
@ -59,7 +58,6 @@ import android.os.Looper;
import android.os.Parcelable; import android.os.Parcelable;
import android.provider.MediaStore; import android.provider.MediaStore;
import android.provider.OpenableColumns; import android.provider.OpenableColumns;
import android.text.Html;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.TypedValue; import android.util.TypedValue;
@ -80,6 +78,7 @@ import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.browser.customtabs.CustomTabColorSchemeParams; import androidx.browser.customtabs.CustomTabColorSchemeParams;
import androidx.browser.customtabs.CustomTabsIntent; import androidx.browser.customtabs.CustomTabsIntent;
import androidx.core.app.ActivityOptionsCompat;
import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat; import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
@ -90,6 +89,7 @@ import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelStoreOwner; import androidx.lifecycle.ViewModelStoreOwner;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
@ -100,7 +100,6 @@ import com.bumptech.glide.load.resource.bitmap.CenterCrop;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners; import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.load.resource.gif.GifDrawable; import com.bumptech.glide.load.resource.gif.GifDrawable;
import com.bumptech.glide.request.RequestOptions; import com.bumptech.glide.request.RequestOptions;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
@ -141,7 +140,6 @@ import java.util.Random;
import java.util.Set; import java.util.Set;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -158,7 +156,6 @@ import app.fedilab.android.mastodon.client.entities.api.Attachment;
import app.fedilab.android.mastodon.client.entities.api.Status; import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.mastodon.client.entities.app.Account; import app.fedilab.android.mastodon.client.entities.app.Account;
import app.fedilab.android.mastodon.client.entities.app.BaseAccount; import app.fedilab.android.mastodon.client.entities.app.BaseAccount;
import app.fedilab.android.mastodon.client.entities.app.CachedBundle;
import app.fedilab.android.mastodon.client.entities.app.ReleaseNote; import app.fedilab.android.mastodon.client.entities.app.ReleaseNote;
import app.fedilab.android.mastodon.client.entities.app.Timeline; import app.fedilab.android.mastodon.client.entities.app.Timeline;
import app.fedilab.android.mastodon.exception.DBException; import app.fedilab.android.mastodon.exception.DBException;
@ -168,12 +165,9 @@ import app.fedilab.android.mastodon.viewmodel.mastodon.AccountsVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.OauthVM; import app.fedilab.android.mastodon.viewmodel.mastodon.OauthVM;
import app.fedilab.android.mastodon.watermark.androidwm.WatermarkBuilder; import app.fedilab.android.mastodon.watermark.androidwm.WatermarkBuilder;
import app.fedilab.android.mastodon.watermark.androidwm.bean.WatermarkText; import app.fedilab.android.mastodon.watermark.androidwm.bean.WatermarkText;
import app.fedilab.android.peertube.client.data.AccountData;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
import okhttp3.MediaType; import okhttp3.MediaType;
import okhttp3.MultipartBody; import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody; import okhttp3.RequestBody;
public class Helper { public class Helper {
@ -200,8 +194,6 @@ public class Helper {
public static final String RECEIVE_REDRAW_BOTTOM = "RECEIVE_REDRAW_BOTTOM"; public static final String RECEIVE_REDRAW_BOTTOM = "RECEIVE_REDRAW_BOTTOM";
public static final String RECEIVE_STATUS_ACTION = "RECEIVE_STATUS_ACTION"; public static final String RECEIVE_STATUS_ACTION = "RECEIVE_STATUS_ACTION";
public static final String RECEIVE_REFRESH_NOTIFICATIONS_ACTION = "RECEIVE_REFRESH_NOTIFICATIONS_ACTION";
public static final String RECEIVE_ERROR_MESSAGE = "RECEIVE_ERROR_MESSAGE"; public static final String RECEIVE_ERROR_MESSAGE = "RECEIVE_ERROR_MESSAGE";
public static final String RECEIVE_RECREATE_ACTIVITY = "RECEIVE_RECREATE_ACTIVITY"; public static final String RECEIVE_RECREATE_ACTIVITY = "RECEIVE_RECREATE_ACTIVITY";
@ -213,8 +205,6 @@ public class Helper {
public static final String RECEIVE_REDRAW_PROFILE = "RECEIVE_REDRAW_PROFILE"; public static final String RECEIVE_REDRAW_PROFILE = "RECEIVE_REDRAW_PROFILE";
public static final String ARG_TIMELINE_TYPE = "ARG_TIMELINE_TYPE"; public static final String ARG_TIMELINE_TYPE = "ARG_TIMELINE_TYPE";
public static final String ARG_INTENT_ID = "ARG_INTENT_ID";
public static final String ARG_PEERTUBE_NAV_REMOTE = "ARG_PEERTUBE_NAV_REMOTE"; public static final String ARG_PEERTUBE_NAV_REMOTE = "ARG_PEERTUBE_NAV_REMOTE";
public static final String ARG_REMOTE_INSTANCE_STRING = "ARG_REMOTE_INSTANCE_STRING"; public static final String ARG_REMOTE_INSTANCE_STRING = "ARG_REMOTE_INSTANCE_STRING";
@ -222,8 +212,6 @@ public class Helper {
public static final String ARG_NOTIFICATION_TYPE = "ARG_NOTIFICATION_TYPE"; public static final String ARG_NOTIFICATION_TYPE = "ARG_NOTIFICATION_TYPE";
public static final String ARG_EXCLUDED_NOTIFICATION_TYPE = "ARG_EXCLUDED_NOTIFICATION_TYPE"; public static final String ARG_EXCLUDED_NOTIFICATION_TYPE = "ARG_EXCLUDED_NOTIFICATION_TYPE";
public static final String ARG_STATUS = "ARG_STATUS"; public static final String ARG_STATUS = "ARG_STATUS";
public static final String ARG_QR_CODE_URL = "ARG_QR_CODE_URL";
public static final String ARG_FOCUSED_STATUS_URI = "ARG_FOCUSED_STATUS_URI";
public static final String ARG_TIMELINE_REFRESH_ALL = "ARG_TIMELINE_REFRESH_ALL"; public static final String ARG_TIMELINE_REFRESH_ALL = "ARG_TIMELINE_REFRESH_ALL";
public static final String ARG_REFRESH_NOTFICATION = "ARG_REFRESH_NOTFICATION"; public static final String ARG_REFRESH_NOTFICATION = "ARG_REFRESH_NOTFICATION";
public static final String ARG_STATUS_DELETED = "ARG_STATUS_DELETED"; public static final String ARG_STATUS_DELETED = "ARG_STATUS_DELETED";
@ -247,8 +235,6 @@ public class Helper {
public static final String ARG_STATUS_REPLY_ID = "ARG_STATUS_REPLY_ID"; public static final String ARG_STATUS_REPLY_ID = "ARG_STATUS_REPLY_ID";
public static final String ARG_ACCOUNT = "ARG_ACCOUNT"; public static final String ARG_ACCOUNT = "ARG_ACCOUNT";
public static final String ARG_ACCOUNT_ID = "ARG_ACCOUNT_ID"; public static final String ARG_ACCOUNT_ID = "ARG_ACCOUNT_ID";
public static final String ARG_CACHED_ACCOUNT_ID = "ARG_CACHED_ACCOUNT_ID";
public static final String ARG_CACHED_STATUS_ID = "ARG_CACHED_STATUS_ID";
public static final String ARG_ADMIN_DOMAINBLOCK = "ARG_ADMIN_DOMAINBLOCK"; public static final String ARG_ADMIN_DOMAINBLOCK = "ARG_ADMIN_DOMAINBLOCK";
public static final String ARG_ADMIN_DOMAINBLOCK_DELETE = "ARG_ADMIN_DOMAINBLOCK_DELETE"; public static final String ARG_ADMIN_DOMAINBLOCK_DELETE = "ARG_ADMIN_DOMAINBLOCK_DELETE";
public static final String FEDILAB_MUTED_HASHTAGS = "Fedilab muted hashtags"; public static final String FEDILAB_MUTED_HASHTAGS = "Fedilab muted hashtags";
@ -271,8 +257,6 @@ public class Helper {
public static final String ARG_STATUS_ID = "ARG_STATUS_ID"; public static final String ARG_STATUS_ID = "ARG_STATUS_ID";
public static final String ARG_WORK_ID = "ARG_WORK_ID"; public static final String ARG_WORK_ID = "ARG_WORK_ID";
public static final String ARG_LIST_ID = "ARG_LIST_ID"; public static final String ARG_LIST_ID = "ARG_LIST_ID";
public static final String ARG_LEMMY_POST_ID = "ARG_LEMMY_POST_ID";
public static final String ARG_SEARCH_KEYWORD = "ARG_SEARCH_KEYWORD"; public static final String ARG_SEARCH_KEYWORD = "ARG_SEARCH_KEYWORD";
public static final String ARG_DIRECTORY_ORDER = "ARG_DIRECTORY_ORDER"; public static final String ARG_DIRECTORY_ORDER = "ARG_DIRECTORY_ORDER";
public static final String ARG_DIRECTORY_LOCAL = "ARG_DIRECTORY_LOCAL"; public static final String ARG_DIRECTORY_LOCAL = "ARG_DIRECTORY_LOCAL";
@ -288,7 +272,6 @@ public class Helper {
public static final String ARG_SHOW_REBLOGS = "ARG_SHOW_REBLOGS"; public static final String ARG_SHOW_REBLOGS = "ARG_SHOW_REBLOGS";
public static final String ARG_INITIALIZE_VIEW = "ARG_INITIALIZE_VIEW"; public static final String ARG_INITIALIZE_VIEW = "ARG_INITIALIZE_VIEW";
public static final String ARG_SHOW_PINNED = "ARG_SHOW_PINNED"; public static final String ARG_SHOW_PINNED = "ARG_SHOW_PINNED";
public static final String ARG_TAGGED = "ARG_TAGGED";
public static final String ARG_SHOW_MEDIA_ONY = "ARG_SHOW_MEDIA_ONY"; public static final String ARG_SHOW_MEDIA_ONY = "ARG_SHOW_MEDIA_ONY";
public static final String ARG_MENTION = "ARG_MENTION"; public static final String ARG_MENTION = "ARG_MENTION";
public static final String ARG_CHECK_REMOTELY = "ARG_CHECK_REMOTELY"; public static final String ARG_CHECK_REMOTELY = "ARG_CHECK_REMOTELY";
@ -297,7 +280,6 @@ public class Helper {
public static final String ARG_MEDIA_ARRAY_PROFILE = "ARG_MEDIA_ARRAY_PROFILE"; public static final String ARG_MEDIA_ARRAY_PROFILE = "ARG_MEDIA_ARRAY_PROFILE";
public static final String ARG_VISIBILITY = "ARG_VISIBILITY"; public static final String ARG_VISIBILITY = "ARG_VISIBILITY";
public static final String ARG_SCHEDULED_DATE = "ARG_SCHEDULED_DATE"; public static final String ARG_SCHEDULED_DATE = "ARG_SCHEDULED_DATE";
public static final String ARG_SCHEDULED_ID = "ARG_SCHEDULED_ID";
public static final String WORKER_REFRESH_NOTIFICATION = "WORKER_REFRESH_NOTIFICATION"; public static final String WORKER_REFRESH_NOTIFICATION = "WORKER_REFRESH_NOTIFICATION";
public static final String WORKER_REFRESH_HOME = "WORKER_REFRESH_HOME"; public static final String WORKER_REFRESH_HOME = "WORKER_REFRESH_HOME";
@ -337,7 +319,6 @@ public class Helper {
public static final int OPEN_NOTIFICATION = 2; public static final int OPEN_NOTIFICATION = 2;
public static final int OPEN_WITH_ANOTHER_ACCOUNT = 3; public static final int OPEN_WITH_ANOTHER_ACCOUNT = 3;
public static final String INTENT_TARGETED_ACCOUNT = "INTENT_TARGETED_ACCOUNT"; public static final String INTENT_TARGETED_ACCOUNT = "INTENT_TARGETED_ACCOUNT";
public static final String INTENT_TARGETED_STATUS = "INTENT_TARGETED_STATUS";
public static final String INTENT_SEND_MODIFIED_IMAGE = "INTENT_SEND_MODIFIED_IMAGE"; public static final String INTENT_SEND_MODIFIED_IMAGE = "INTENT_SEND_MODIFIED_IMAGE";
public static final String INTENT_COMPOSE_ERROR_MESSAGE = "INTENT_COMPOSE_ERROR_MESSAGE"; public static final String INTENT_COMPOSE_ERROR_MESSAGE = "INTENT_COMPOSE_ERROR_MESSAGE";
public static final String TEMP_MEDIA_DIRECTORY = "TEMP_MEDIA_DIRECTORY"; public static final String TEMP_MEDIA_DIRECTORY = "TEMP_MEDIA_DIRECTORY";
@ -356,9 +337,6 @@ public class Helper {
public static final Pattern groupPattern = Pattern.compile("(![\\w_]+)"); public static final Pattern groupPattern = Pattern.compile("(![\\w_]+)");
public static final Pattern mentionPattern = Pattern.compile("(@[\\w_.-]?[\\w]+)"); public static final Pattern mentionPattern = Pattern.compile("(@[\\w_.-]?[\\w]+)");
public static final Pattern mentionLongPattern = Pattern.compile("(@[\\w_.-]+@[a-zA-Z0-9][a-zA-Z0-9.-]{1,61}[a-zA-Z0-9](?:\\.[a-zA-Z]{2,})+)"); public static final Pattern mentionLongPattern = Pattern.compile("(@[\\w_.-]+@[a-zA-Z0-9][a-zA-Z0-9.-]{1,61}[a-zA-Z0-9](?:\\.[a-zA-Z]{2,})+)");
public static final Pattern mentionPatternALL = Pattern.compile("(@[\\w_.-]+@[a-zA-Z0-9][a-zA-Z0-9.-]{1,61}[a-zA-Z0-9](?:\\.[a-zA-Z]{2,})+)|(@[\\w_.-]?[\\w]+)");
public static final Pattern mathsPattern = Pattern.compile("\\\\\\(|\\\\\\["); public static final Pattern mathsPattern = Pattern.compile("\\\\\\(|\\\\\\[");
public static final Pattern mathsComposePattern = Pattern.compile("\\\\\\(.*\\\\\\)|\\\\\\[.*\\\\\\]"); public static final Pattern mathsComposePattern = Pattern.compile("\\\\\\(.*\\\\\\)|\\\\\\[.*\\\\\\]");
public static final Pattern twitterPattern = Pattern.compile("((@[\\w]+)@twitter\\.com)"); public static final Pattern twitterPattern = Pattern.compile("((@[\\w]+)@twitter\\.com)");
@ -430,7 +408,6 @@ public class Helper {
private static int notificationId = 1; private static int notificationId = 1;
//Allow to store in shared preference first visible fragment when the app starts //Allow to store in shared preference first visible fragment when the app starts
private static String slugOfFirstFragment; private static String slugOfFirstFragment;
private static BaseAccount baseAccount;
static { static {
LinkedHashMap<PatternType, Pattern> aMap = new LinkedHashMap<>(); LinkedHashMap<PatternType, Pattern> aMap = new LinkedHashMap<>();
@ -441,6 +418,7 @@ public class Helper {
patternHashMap = Collections.unmodifiableMap(aMap); patternHashMap = Collections.unmodifiableMap(aMap);
} }
/** /**
* Manage downloads with URLs * Manage downloads with URLs
* *
@ -484,6 +462,8 @@ public class Helper {
} }
} }
/*** /***
* Check if the user is connected to Internet * Check if the user is connected to Internet
* @return boolean * @return boolean
@ -588,20 +568,6 @@ public class Helper {
return df.format(date); 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 * Convert a date in String -> format yyyy-MM-dd HH:mm:ss
* *
@ -649,13 +615,19 @@ public class Helper {
*/ */
public static Date stringToDateWithFormat(Context context, String stringDate, String format) { public static Date stringToDateWithFormat(Context context, String stringDate, String format) {
if (stringDate == null) if (stringDate == null)
return new Date(); return null;
SimpleDateFormat dateFormat = new SimpleDateFormat(format, Locale.US); Locale userLocale;
Date date = new Date(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
userLocale = context.getResources().getConfiguration().getLocales().get(0);
} else {
userLocale = context.getResources().getConfiguration().locale;
}
SimpleDateFormat dateFormat = new SimpleDateFormat(format, userLocale);
Date date = null;
try { try {
date = dateFormat.parse(stringDate); date = dateFormat.parse(stringDate);
} catch (java.text.ParseException ignored) { } catch (java.text.ParseException ignored) {
ignored.printStackTrace();
} }
return date; return date;
} }
@ -683,10 +655,6 @@ public class Helper {
if (url == null) { if (url == null) {
return; return;
} }
/*if(networkAvailable == BaseMainActivity.status.DISCONNECTED){
Toasty.warning(context, context.getString(R.string.toast_error_internet), Toast.LENGTH_LONG).show();
return;
}*/
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
boolean customTab = sharedpreferences.getBoolean(context.getString(R.string.SET_CUSTOM_TABS), true); boolean customTab = sharedpreferences.getBoolean(context.getString(R.string.SET_CUSTOM_TABS), true);
if (customTab) { if (customTab) {
@ -697,38 +665,28 @@ public class Helper {
.build(); .build();
builder.setDefaultColorSchemeParams(defaultColors); builder.setDefaultColorSchemeParams(defaultColors);
CustomTabsIntent customTabsIntent = builder.build(); CustomTabsIntent customTabsIntent = builder.build();
url = url.replace("HTTPS://", "https://");
url = url.replace("HTTP://", "http://");
try { try {
customTabsIntent.launchUrl(context, Uri.parse(url).normalizeScheme()); customTabsIntent.launchUrl(context, Uri.parse(url));
} catch (Exception e) { } catch (Exception e) {
if(url.toLowerCase().startsWith("xmpp:")) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setData(Uri.parse("https://xmpp.link/#" + url.replace("xmpp:","")).normalizeScheme());
context.startActivity(intent);
} else {
Toasty.error(context, context.getString(R.string.toast_error), Toast.LENGTH_LONG).show(); Toasty.error(context, context.getString(R.string.toast_error), Toast.LENGTH_LONG).show();
} }
}
} else { } else {
Intent intent = new Intent(Intent.ACTION_VIEW); Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (!url.toLowerCase().startsWith("http://") && !url.toLowerCase().startsWith("https://") && !url.toLowerCase().startsWith("gemini://") && !url.toLowerCase().startsWith("xmpp:")) { if (!url.toLowerCase().startsWith("http://") && !url.toLowerCase().startsWith("https://") && !url.toLowerCase().startsWith("gemini://")) {
url = "http://" + url; url = "http://" + url;
} }
intent.setData(Uri.parse(url).normalizeScheme()); intent.setData(Uri.parse(url));
try { try {
context.startActivity(intent); context.startActivity(intent);
} catch (Exception e) { } catch (Exception e) {
if(url.toLowerCase().startsWith("xmpp:")) {
intent.setData(Uri.parse("https://xmpp.link/#" + url.replace("xmpp:","")).normalizeScheme());
context.startActivity(intent);
} else {
Toasty.error(context, context.getString(R.string.toast_error), Toast.LENGTH_LONG).show(); Toasty.error(context, context.getString(R.string.toast_error), Toast.LENGTH_LONG).show();
} }
} }
} }
}
/** /**
* Transform URLs to privacy frontend * Transform URLs to privacy frontend
@ -862,7 +820,7 @@ public class Helper {
@SuppressLint("DefaultLocale") @SuppressLint("DefaultLocale")
public static String withSuffix(long count) { public static String withSuffix(long count) {
if (count < 1000) return String.valueOf(count); if (count < 1000) return "" + count;
int exp = (int) (Math.log(count) / Math.log(1000)); int exp = (int) (Math.log(count) / Math.log(1000));
Locale locale = null; Locale locale = null;
try { try {
@ -888,17 +846,12 @@ public class Helper {
*/ */
public static void sendToastMessage(Context context, String type, String content) { public static void sendToastMessage(Context context, String type, String content) {
Intent intentBC = new Intent(context, ToastMessage.class); Intent intentBC = new Intent(context, ToastMessage.class);
Bundle args = new Bundle(); Bundle b = new Bundle();
args.putString(RECEIVE_TOAST_TYPE, type); b.putString(RECEIVE_TOAST_TYPE, type);
args.putString(RECEIVE_TOAST_CONTENT, content); b.putString(RECEIVE_TOAST_CONTENT, content);
intentBC.setAction(Helper.RECEIVE_TOAST_MESSAGE); intentBC.setAction(Helper.RECEIVE_TOAST_MESSAGE);
new CachedBundle(context).insertBundle(args, Helper.getCurrentAccount(context), bundleId -> { intentBC.putExtras(b);
Bundle bundle = new Bundle();
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intentBC.putExtras(bundle);
intentBC.setPackage(BuildConfig.APPLICATION_ID);
context.sendBroadcast(intentBC); context.sendBroadcast(intentBC);
});
} }
/** /**
@ -919,25 +872,19 @@ public class Helper {
ft.setCustomAnimations(R.anim.enter, R.anim.exit, R.anim.pop_enter, R.anim.pop_exit); ft.setCustomAnimations(R.anim.enter, R.anim.exit, R.anim.pop_enter, R.anim.pop_exit);
Fragment _fragment = fragmentManager.findFragmentByTag(tag); Fragment _fragment = fragmentManager.findFragmentByTag(tag);
if (_fragment != null && _fragment.isAdded()) { if (_fragment != null && _fragment.isAdded()) {
ft.show(_fragment).commitAllowingStateLoss(); ft.show(_fragment).commit();
fragment = _fragment; fragment = _fragment;
} else { } else {
if (args != null) fragment.setArguments(args); if (args != null) fragment.setArguments(args);
ft = fragmentManager.beginTransaction();
ft.add(containerViewId, fragment, tag); ft.add(containerViewId, fragment, tag);
if (backStackName != null) { if (backStackName != null) ft.addToBackStack(backStackName);
try { ft.commit();
ft.addToBackStack(backStackName);
}catch (Exception ignored){}
}
if (!fragmentManager.isDestroyed()) {
ft.commitAllowingStateLoss();
}
} }
fragmentManager.executePendingTransactions(); fragmentManager.executePendingTransactions();
return fragment; return fragment;
} }
/** /**
* Load a media into a view * Load a media into a view
* *
@ -980,11 +927,11 @@ public class Helper {
OauthVM oauthVM = new ViewModelProvider((ViewModelStoreOwner) activity).get(OauthVM.class); OauthVM oauthVM = new ViewModelProvider((ViewModelStoreOwner) activity).get(OauthVM.class);
if (Helper.getCurrentAccount(activity) != null) { if (currentAccount != null) {
//Revoke the token //Revoke the token
oauthVM.revokeToken(Helper.getCurrentAccount(activity).instance, Helper.getCurrentAccount(activity).token, Helper.getCurrentAccount(activity).client_id, Helper.getCurrentAccount(activity).client_secret); oauthVM.revokeToken(currentAccount.instance, currentAccount.token, currentAccount.client_id, currentAccount.client_secret);
//Log out the current user //Log out the current user
accountDB.removeUser(Helper.getCurrentAccount(activity)); accountDB.removeUser(currentAccount);
} }
BaseAccount newAccount = accountDB.getLastUsedAccount(); BaseAccount newAccount = accountDB.getLastUsedAccount();
SharedPreferences.Editor editor = sharedpreferences.edit(); SharedPreferences.Editor editor = sharedpreferences.edit();
@ -998,7 +945,7 @@ public class Helper {
activity.startActivity(loginActivity); activity.startActivity(loginActivity);
activity.finish(); activity.finish();
} else { } else {
Helper.setCurrentAccount(newAccount); currentAccount = newAccount;
editor.putString(PREF_USER_TOKEN, newAccount.token); editor.putString(PREF_USER_TOKEN, newAccount.token);
editor.putString(PREF_USER_SOFTWARE, newAccount.software); editor.putString(PREF_USER_SOFTWARE, newAccount.software);
editor.putString(PREF_USER_INSTANCE, newAccount.instance); editor.putString(PREF_USER_INSTANCE, newAccount.instance);
@ -1024,7 +971,8 @@ public class Helper {
if (context == null) { if (context == null) {
return false; return false;
} }
if (context instanceof Activity activity) { if (context instanceof Activity) {
final Activity activity = (Activity) context;
return !activity.isDestroyed() && !activity.isFinishing(); return !activity.isDestroyed() && !activity.isFinishing();
} }
return true; return true;
@ -1160,13 +1108,11 @@ public class Helper {
.toSquare() .toSquare()
.setBackgroundColor(fetchAccentColor(activity)) .setBackgroundColor(fetchAccentColor(activity))
.build(); .build();
if (Helper.isValidContextForGlide(activity)) {
Glide.with(activity) Glide.with(activity)
.asDrawable() .asDrawable()
.load(avatar) .load(avatar)
.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10))) .apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10)))
.into(view); .into(view);
}
return; return;
} }
} }
@ -1332,9 +1278,9 @@ public class Helper {
MimeTypeMap mime = MimeTypeMap.getSingleton(); MimeTypeMap mime = MimeTypeMap.getSingleton();
String extension = mime.getExtensionFromMimeType(cR.getType(uri)); String extension = mime.getExtensionFromMimeType(cR.getType(uri));
if (uri.toString().endsWith("fedilab_recorded_audio.ogg")) { if (uri.toString().endsWith("fedilab_recorded_audio.m4a")) {
extension = "ogg"; extension = ".m4a";
attachment.mimeType = "audio/ogg"; attachment.mimeType = "audio/mp4";
} }
SimpleDateFormat formatter = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_" + counter, Locale.getDefault()); SimpleDateFormat formatter = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_" + counter, Locale.getDefault());
counter++; counter++;
@ -1386,6 +1332,7 @@ public class Helper {
}).start(); }).start();
} }
public static void createFileFromUri(Context context, Uri uri, OnFileCopied callBack) { public static void createFileFromUri(Context context, Uri uri, OnFileCopied callBack) {
new Thread(() -> { new Thread(() -> {
InputStream selectedFileInputStream; InputStream selectedFileInputStream;
@ -1464,24 +1411,6 @@ public class Helper {
imageView.setColorFilter(color); imageView.setColorFilter(color);
} }
/**
* change color of a drawable
*
* @param materialButton {@link MaterialButton}
* @param hexaColor example 0xffff00
*/
public static void changeDrawableColor(Context context, MaterialButton materialButton, int hexaColor) {
if (materialButton == null)
return;
int color;
try {
color = context.getResources().getColor(hexaColor);
} catch (Resources.NotFoundException e) {
color = hexaColor;
}
materialButton.setIconTint(ColorStateList.valueOf(color));
}
/** /**
* change color of a drawable * change color of a drawable
* *
@ -1559,16 +1488,11 @@ public class Helper {
* @param activity - Activity * @param activity - Activity
*/ */
public static void recreateMainActivity(Activity activity) { public static void recreateMainActivity(Activity activity) {
Bundle args = new Bundle(); Bundle b = new Bundle();
args.putBoolean(Helper.RECEIVE_RECREATE_ACTIVITY, true); b.putBoolean(Helper.RECEIVE_RECREATE_ACTIVITY, true);
Intent intentBD = new Intent(Helper.BROADCAST_DATA); Intent intentBD = new Intent(Helper.BROADCAST_DATA);
new CachedBundle(activity).insertBundle(args, Helper.getCurrentAccount(activity), bundleId -> { intentBD.putExtras(b);
Bundle bundle = new Bundle(); LocalBroadcastManager.getInstance(activity).sendBroadcast(intentBD);
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intentBD.putExtras(bundle);
intentBD.setPackage(BuildConfig.APPLICATION_ID);
activity.sendBroadcast(intentBD);
});
} }
public static void showKeyboard(Context context, View view) { public static void showKeyboard(Context context, View view) {
@ -1605,51 +1529,50 @@ public class Helper {
String channelTitle; String channelTitle;
switch (notifType) { switch (notifType) {
case FAV -> { case FAV:
channelId = "channel_favourite"; channelId = "channel_favourite";
channelTitle = context.getString(R.string.channel_notif_fav); channelTitle = context.getString(R.string.channel_notif_fav);
} break;
case FOLLLOW -> { case FOLLLOW:
channelId = "channel_follow"; channelId = "channel_follow";
channelTitle = context.getString(R.string.channel_notif_follow); channelTitle = context.getString(R.string.channel_notif_follow);
} break;
case MENTION -> { case MENTION:
channelId = "channel_mention"; channelId = "channel_mention";
channelTitle = context.getString(R.string.channel_notif_mention); channelTitle = context.getString(R.string.channel_notif_mention);
} break;
case POLL -> { case POLL:
channelId = "channel_poll"; channelId = "channel_poll";
channelTitle = context.getString(R.string.channel_notif_poll); channelTitle = context.getString(R.string.channel_notif_poll);
} break;
case BACKUP -> { case BACKUP:
channelId = "channel_backup"; channelId = "channel_backup";
channelTitle = context.getString(R.string.channel_notif_backup); channelTitle = context.getString(R.string.channel_notif_backup);
} break;
case STORE -> { case STORE:
channelId = "channel_media"; channelId = "channel_media";
channelTitle = context.getString(R.string.channel_notif_media); channelTitle = context.getString(R.string.channel_notif_media);
} break;
case TOOT -> { case TOOT:
channelId = "channel_status"; channelId = "channel_status";
channelTitle = context.getString(R.string.channel_notif_status); channelTitle = context.getString(R.string.channel_notif_status);
} break;
case UPDATE -> { case UPDATE:
channelId = "channel_update"; channelId = "channel_update";
channelTitle = context.getString(R.string.channel_notif_update); channelTitle = context.getString(R.string.channel_notif_update);
} break;
case SIGN_UP -> { case SIGN_UP:
channelId = "channel_signup"; channelId = "channel_signup";
channelTitle = context.getString(R.string.channel_notif_signup); channelTitle = context.getString(R.string.channel_notif_signup);
} break;
case REPORT -> { case REPORT:
channelId = "channel_report"; channelId = "channel_report";
channelTitle = context.getString(R.string.channel_notif_report); channelTitle = context.getString(R.string.channel_notif_report);
} break;
default -> { default:
channelId = "channel_boost"; channelId = "channel_boost";
channelTitle = context.getString(R.string.channel_notif_boost); channelTitle = context.getString(R.string.channel_notif_boost);
} }
}
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, channelId) NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, channelId)
.setSmallIcon(getNotificationIcon(context)).setTicker(message); .setSmallIcon(getNotificationIcon(context)).setTicker(message);
/* if (notifType == NotifType.MENTION) { /* if (notifType == NotifType.MENTION) {
@ -1657,25 +1580,31 @@ public class Helper {
message = message.substring(0, 499) + ""; message = message.substring(0, 499) + "";
} }
}*/ }*/
notificationBuilder.setGroup(account.mastodon_account != null ? account.mastodon_account.username + "@" + account.instance : "@" + account.instance) notificationBuilder.setGroup(account.mastodon_account != null ? account.mastodon_account.username + "@" + account.instance : "" + "@" + account.instance)
.setContentIntent(pIntent) .setContentIntent(pIntent)
.setContentText(message); .setContentText(message);
int ledColour = Color.BLUE; int ledColour = Color.BLUE;
int prefColor; int prefColor;
prefColor = Integer.parseInt(sharedpreferences.getString(context.getString(R.string.SET_LED_COLOUR_VAL_N), String.valueOf(LED_COLOUR))); prefColor = Integer.parseInt(sharedpreferences.getString(context.getString(R.string.SET_LED_COLOUR_VAL_N), String.valueOf(LED_COLOUR)));
switch (prefColor) { switch (prefColor) {
case 1 -> // CYAN case 1: // CYAN
ledColour = Color.CYAN; ledColour = Color.CYAN;
case 2 -> // MAGENTA break;
case 2: // MAGENTA
ledColour = Color.MAGENTA; ledColour = Color.MAGENTA;
case 3 -> // GREEN break;
case 3: // GREEN
ledColour = Color.GREEN; ledColour = Color.GREEN;
case 4 -> // RED break;
case 4: // RED
ledColour = Color.RED; ledColour = Color.RED;
case 5 -> // YELLOW break;
case 5: // YELLOW
ledColour = Color.YELLOW; ledColour = Color.YELLOW;
case 6 -> // WHITE break;
case 6: // WHITE
ledColour = Color.WHITE; ledColour = Color.WHITE;
break;
} }
@ -1719,7 +1648,7 @@ public class Helper {
.setLargeIcon(icon) .setLargeIcon(icon)
.setSmallIcon(getNotificationIcon(context)) .setSmallIcon(getNotificationIcon(context))
.setStyle(new NotificationCompat.BigTextStyle().bigText(message)) .setStyle(new NotificationCompat.BigTextStyle().bigText(message))
.setGroup(account.mastodon_account != null ? account.mastodon_account.username + "@" + account.instance : "@" + account.instance) .setGroup(account.mastodon_account != null ? account.mastodon_account.username + "@" + account.instance : "" + "@" + account.instance)
.setGroupSummary(true) .setGroupSummary(true)
.build(); .build();
@ -1729,6 +1658,7 @@ public class Helper {
} }
} }
public static String dateDiffFull(Date dateToot) { public static String dateDiffFull(Date dateToot) {
SimpleDateFormat df = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.MEDIUM, Locale.getDefault()); SimpleDateFormat df = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.MEDIUM, Locale.getDefault());
try { try {
@ -1738,6 +1668,7 @@ public class Helper {
} }
} }
public static String dateDiffFullShort(Date dateToot) { public static String dateDiffFullShort(Date dateToot) {
SimpleDateFormat df = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.getDefault()); SimpleDateFormat df = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.getDefault());
try { try {
@ -1849,6 +1780,7 @@ public class Helper {
if (responseCode == HttpURLConnection.HTTP_OK) { if (responseCode == HttpURLConnection.HTTP_OK) {
String fileName = ""; String fileName = "";
String disposition = httpURLConnection.getHeaderField("Content-Disposition"); String disposition = httpURLConnection.getHeaderField("Content-Disposition");
if (disposition != null) { if (disposition != null) {
// extracts file name from header field // extracts file name from header field
int index = disposition.indexOf("filename="); int index = disposition.indexOf("filename=");
@ -1858,13 +1790,8 @@ public class Helper {
} }
} else { } else {
// extracts file name from URL // extracts file name from URL
try {
URL downLoadUrlTmp = new URL(downloadUrl);
fileName = downLoadUrlTmp.getPath().replace("/","_");
}catch (Exception exception) {
fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/") + 1); fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/") + 1);
} }
}
fileName = FileNameCleaner.cleanFileName(fileName); fileName = FileNameCleaner.cleanFileName(fileName);
// opens input stream from the HTTP connection // opens input stream from the HTTP connection
InputStream inputStream = httpURLConnection.getInputStream(); InputStream inputStream = httpURLConnection.getInputStream();
@ -1970,14 +1897,12 @@ public class Helper {
binding.accountUn.setText(account.acct); binding.accountUn.setText(account.acct);
binding.accountPp.setOnClickListener(v -> { binding.accountPp.setOnClickListener(v -> {
Intent intent = new Intent(activity, ProfileActivity.class); Intent intent = new Intent(activity, ProfileActivity.class);
Bundle args = new Bundle(); Bundle b = new Bundle();
args.putSerializable(Helper.ARG_ACCOUNT, account); b.putSerializable(Helper.ARG_ACCOUNT, account);
new CachedBundle(activity).insertBundle(args, Helper.getCurrentAccount(activity), bundleId -> { intent.putExtras(b);
Bundle bundle = new Bundle(); ActivityOptionsCompat options = ActivityOptionsCompat
bundle.putLong(Helper.ARG_INTENT_ID, bundleId); .makeSceneTransitionAnimation(activity, binding.accountPp, activity.getString(R.string.activity_porfile_pp));
intent.putExtras(bundle); activity.startActivity(intent, options.toBundle());
activity.startActivity(intent);
});
}); });
AccountsVM accountsVM = new ViewModelProvider((ViewModelStoreOwner) activity).get(AccountsVM.class); AccountsVM accountsVM = new ViewModelProvider((ViewModelStoreOwner) activity).get(AccountsVM.class);
@ -2060,6 +1985,7 @@ public class Helper {
Runtime.getRuntime().exit(0); Runtime.getRuntime().exit(0);
} }
public static void forwardToBrowser(Activity activity, Intent i) { public static void forwardToBrowser(Activity activity, Intent i) {
Intent intent = new Intent(); Intent intent = new Intent();
intent.setAction(android.content.Intent.ACTION_VIEW); intent.setAction(android.content.Intent.ACTION_VIEW);
@ -2084,6 +2010,7 @@ public class Helper {
} }
} }
public static int dialogStyle() { public static int dialogStyle() {
if (R.style.AppThemeBar == currentThemeId || R.style.AppTheme == currentThemeId) { if (R.style.AppThemeBar == currentThemeId || R.style.AppTheme == currentThemeId) {
return R.style.AppThemeAlertDialog; return R.style.AppThemeAlertDialog;
@ -2112,83 +2039,6 @@ public class Helper {
} }
} }
public static BaseAccount getCurrentAccount(Context context) {
if (baseAccount == null && context != null) {
baseAccount = new BaseAccount();
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
baseAccount.user_id = sharedpreferences.getString(PREF_USER_ID, null);
baseAccount.instance = sharedpreferences.getString(PREF_USER_INSTANCE, null);
baseAccount.token = sharedpreferences.getString(PREF_USER_TOKEN, null);
}
return baseAccount;
}
public static void setCurrentAccount(BaseAccount newBaseAccount) {
baseAccount = newBaseAccount;
}
public static void setCurrentAccountMastodonAccount(Context context, app.fedilab.android.mastodon.client.entities.api.Account newAccount) {
BaseAccount tempBaseAccount = getCurrentAccount(context);
tempBaseAccount.mastodon_account = newAccount;
setCurrentAccount(tempBaseAccount);
}
public static void setCurrentAccountPeertubeAccount(Context context, AccountData.PeertubeAccount newAccount) {
BaseAccount tempBaseAccount = getCurrentAccount(context);
tempBaseAccount.peertube_account = newAccount;
setCurrentAccount(tempBaseAccount);
}
public static boolean isNumeric(String str) {
try {
Double.parseDouble(str);
return true;
} catch (NumberFormatException e) {
return false;
}
}
public static OkHttpClient myOkHttpClient(Context context) {
return new OkHttpClient.Builder()
.addInterceptor(chain -> {
Request originalRequest = chain.request();
Request requestWithUserAgent = originalRequest.newBuilder()
.header("User-Agent", context.getString(R.string.app_name) + "/" + BuildConfig.VERSION_NAME + "/" + BuildConfig.VERSION_CODE)
.build();
return chain.proceed(requestWithUserAgent);
})
.readTimeout(60, TimeUnit.SECONDS)
.connectTimeout(60, TimeUnit.SECONDS)
.callTimeout(60, TimeUnit.SECONDS)
.proxy(Helper.getProxy(context))
.build();
}
public static OkHttpClient myPostOkHttpClient(Context context) {
return new OkHttpClient.Builder()
.addInterceptor(chain -> {
Request originalRequest = chain.request();
Request requestWithUserAgent = originalRequest.newBuilder()
.header("User-Agent", context.getString(R.string.app_name) + "/" + BuildConfig.VERSION_NAME + "/" + BuildConfig.VERSION_CODE)
.build();
return chain.proceed(requestWithUserAgent);
})
.readTimeout(120, TimeUnit.SECONDS)
.connectTimeout(120, TimeUnit.SECONDS)
.callTimeout(120, TimeUnit.SECONDS)
.proxy(Helper.getProxy(context))
.build();
}
public static String parseHtml(String html) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
return Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY).toString();
} else {
//noinspection deprecation
return Html.fromHtml(html).toString();
}
}
//Enum that described actions to replace inside a toot content //Enum that described actions to replace inside a toot content
public enum PatternType { public enum PatternType {
MENTION, MENTION,

View file

@ -1,209 +0,0 @@
package app.fedilab.android.mastodon.helper;
/* Copyright 2023 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* 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.app.AlertDialog;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.preference.PreferenceManager;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.RadioButton;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.ListPreference;
import java.util.ArrayList;
import java.util.List;
import app.fedilab.android.R;
public class ImageListPreference extends ListPreference {
private static final int DEFAULT_TINT = 0;
private static final int DEFAULT_BACKGROUND_TINT = 0xFFFFFFFF;
private final List<Integer> mImages;
private int mErrorResource;
private int mTintColor;
private int mBackgroundColor;
private boolean mUseCard;
private int mCustomItemLayout;
public ImageListPreference(Context context, AttributeSet attrs) {
super(context, attrs);
mImages = new ArrayList<>();
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ImageListPreference);
try {
int entryImagesArrayResource = array.getResourceId(R.styleable.ImageListPreference_ilp_entryImages, 0);
String tintKey = array.getNonResourceString(R.styleable.ImageListPreference_ilp_tintKey);
String backgroundKey = array.getNonResourceString(R.styleable.ImageListPreference_ilp_backgroundTint);
mTintColor = array.getColor(R.styleable.ImageListPreference_ilp_tint, DEFAULT_TINT);
mBackgroundColor = array.getColor(R.styleable.ImageListPreference_ilp_backgroundTint, 0);
mErrorResource = array.getResourceId(R.styleable.ImageListPreference_ilp_errorImage, 0);
mUseCard = array.getBoolean(R.styleable.ImageListPreference_ilp_useCard, false);
mCustomItemLayout = array.getResourceId(R.styleable.ImageListPreference_ilp_itemLayout, 0);
if (tintKey != null) {
mTintColor = sharedPreferences.getInt(tintKey, mTintColor);
}
if (backgroundKey != null) {
mBackgroundColor = sharedPreferences.getInt(backgroundKey, mBackgroundColor);
}
TypedArray images = context.getResources().obtainTypedArray(entryImagesArrayResource);
for (int i = 0; i < images.length(); i++) {
mImages.add(images.getResourceId(i, 0));
}
images.recycle();
} catch (Exception e) {
e.printStackTrace();
} finally {
array.recycle();
}
}
@Override
protected void onClick() {
List<ImageListItem> items = new ArrayList<>();
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
String launcher = sharedpreferences.getString(getContext().getString(R.string.SET_LOGO_LAUNCHER), "Bubbles");
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
int length = getEntries().length;
for (int i = 0; i < length; i++) {
int resource = 0;
if (mImages.size() > i) {
resource = mImages.get(i);
}
items.add(new ImageListItem(getEntries()[i], resource, String.valueOf(getEntryValues()[i]).equalsIgnoreCase(launcher)));
}
int layout = R.layout.imagelistpreference_item;
if (mUseCard) {
layout = R.layout.imagelistpreference_item_card;
}
if (mCustomItemLayout != 0) {
layout = mCustomItemLayout;
}
ListAdapter adapter = new ImageListPreferenceAdapter(getContext(), layout, items);
builder.setAdapter(adapter, (dialogInterface, which) -> {
SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putString(getContext().getString(R.string.SET_LOGO_LAUNCHER), String.valueOf(getEntryValues()[which]));
editor.commit();
});
builder.create().show();
}
private static class ImageListItem {
private final int resource;
private final boolean isChecked;
private final String name;
ImageListItem(CharSequence name, int resource, boolean isChecked) {
this(name.toString(), resource, isChecked);
}
ImageListItem(String name, int resource, boolean isChecked) {
this.name = name;
this.resource = resource;
this.isChecked = isChecked;
}
}
private static class ViewHolder {
ImageView iconImage;
TextView iconName;
RadioButton radioButton;
}
private class ImageListPreferenceAdapter extends ArrayAdapter<ImageListItem> {
private final List<ImageListItem> mItems;
private final int mLayoutResource;
ImageListPreferenceAdapter(Context context, int layoutResource, List<ImageListItem> items) {
super(context, layoutResource, items);
mLayoutResource = layoutResource;
mItems = items;
}
@Override
public @NonNull
View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) getContext().
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
try {
assert inflater != null;
convertView = inflater.inflate(mLayoutResource, parent, false);
holder = new ViewHolder();
holder.iconName = convertView.findViewById(R.id.imagelistpreference_text);
holder.iconImage = convertView.findViewById(R.id.imagelistpreference_image);
holder.radioButton = convertView.findViewById(R.id.imagelistpreference_radio);
convertView.setTag(holder);
} catch (Exception e) {
e.printStackTrace();
return super.getView(position, null, parent);
}
} else {
holder = (ViewHolder) convertView.getTag();
}
if (holder == null) {
return super.getView(position, convertView, parent);
}
ImageListItem item = mItems.get(position);
holder.iconName.setText(item.name);
if (item.resource != 0) {
holder.iconImage.setImageResource(item.resource);
} else {
holder.iconImage.setImageResource(mErrorResource);
}
if (mTintColor != 0) {
holder.iconImage.setColorFilter(mTintColor);
}
if (mBackgroundColor != 0) {
holder.iconImage.setBackgroundColor(mBackgroundColor);
}
holder.radioButton.setChecked(item.isChecked);
return convertView;
}
}
}

View file

@ -60,16 +60,6 @@ public class LogoHelper {
return R.drawable.fedilab_logo_crash; return R.drawable.fedilab_logo_crash;
case "Mastalab": case "Mastalab":
return R.drawable.fedilab_logo_mastalab; return R.drawable.fedilab_logo_mastalab;
case "BubblesUA":
return R.drawable.fedilab_logo_bubbles_ua;
case "BubblesPeaGreen":
return R.drawable.fedilab_logo_bubbles_pea_green;
case "BubblesPride":
return R.drawable.fedilab_logo_bubbles_pride;
case "BubblesPink":
return R.drawable.fedilab_logo_bubbles_pink;
case "BubblesPirate":
return R.drawable.fedilab_logo_bubbles_pirate;
default: default:
return R.drawable.fedilab_logo_bubbles; return R.drawable.fedilab_logo_bubbles;
} }
@ -88,16 +78,6 @@ public class LogoHelper {
return R.drawable.fedilab_logo_crash; return R.drawable.fedilab_logo_crash;
case "Mastalab": case "Mastalab":
return R.drawable.fedilab_logo_mastalab; return R.drawable.fedilab_logo_mastalab;
case "BubblesUA":
return R.drawable.fedilab_logo_bubbles_ua;
case "BubblesPeaGreen":
return R.drawable.fedilab_logo_bubbles_pea_green;
case "BubblesPride":
return R.drawable.fedilab_logo_bubbles_pride;
case "BubblesPink":
return R.drawable.fedilab_logo_bubbles_pink;
case "BubblesPirate":
return R.drawable.fedilab_logo_bubbles_pirate;
default: default:
return R.drawable.fedilab_logo_bubbles; return R.drawable.fedilab_logo_bubbles;
} }
@ -120,21 +100,6 @@ public class LogoHelper {
case "Mastalab": case "Mastalab":
mLauncher = BaseMainActivity.iconLauncher.MASTALAB; mLauncher = BaseMainActivity.iconLauncher.MASTALAB;
break; break;
case "BubblesUA":
mLauncher = BaseMainActivity.iconLauncher.BUBBLESUA;
break;
case "BubblesPeaGreen":
mLauncher = BaseMainActivity.iconLauncher.BUBBLESPEAGREEN;
break;
case "BubblesPride":
mLauncher = BaseMainActivity.iconLauncher.BUBBLESPRIDE;
break;
case "BubblesPink":
mLauncher = BaseMainActivity.iconLauncher.BUBBLESPINK;
break;
case "BubblesPirate":
mLauncher = BaseMainActivity.iconLauncher.BUBBLESPIRATE;
break;
default: default:
mLauncher = BaseMainActivity.iconLauncher.BUBBLES; mLauncher = BaseMainActivity.iconLauncher.BUBBLES;
} }

View file

@ -239,9 +239,9 @@ public class MastodonHelper {
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
boolean disableGif = sharedpreferences.getBoolean(context.getString(R.string.SET_DISABLE_GIF), false); boolean disableGif = sharedpreferences.getBoolean(context.getString(R.string.SET_DISABLE_GIF), false);
@DrawableRes int placeholder = type == MediaAccountType.AVATAR ? R.drawable.ic_person : R.drawable.default_banner; @DrawableRes int placeholder = type == MediaAccountType.AVATAR ? R.drawable.ic_person : R.drawable.default_banner;
if (Helper.isValidContextForGlide(activity != null ? activity.getApplicationContext() : context.getApplicationContext())) { if (Helper.isValidContextForGlide(activity != null ? activity : context)) {
if (account == null) { if (account == null) {
Glide.with(activity != null ? activity.getApplicationContext() : context.getApplicationContext()) Glide.with(activity != null ? activity : context)
.asDrawable() .asDrawable()
.load(placeholder) .load(placeholder)
.thumbnail(0.1f) .thumbnail(0.1f)
@ -253,7 +253,7 @@ public class MastodonHelper {
if (targetedUrl != null) { if (targetedUrl != null) {
if (disableGif || (!targetedUrl.endsWith(".gif"))) { if (disableGif || (!targetedUrl.endsWith(".gif"))) {
try { try {
Glide.with(activity != null ? activity.getApplicationContext() : context.getApplicationContext()) Glide.with(activity != null ? activity : context)
.asDrawable() .asDrawable()
.load(targetedUrl) .load(targetedUrl)
.thumbnail(0.1f) .thumbnail(0.1f)
@ -263,7 +263,7 @@ public class MastodonHelper {
e.printStackTrace(); e.printStackTrace();
} }
} else { } else {
Glide.with(activity != null ? activity.getApplicationContext() : context.getApplicationContext()) Glide.with(activity != null ? activity : context)
.asGif() .asGif()
.load(targetedUrl) .load(targetedUrl)
.thumbnail(0.1f) .thumbnail(0.1f)
@ -271,7 +271,7 @@ public class MastodonHelper {
.into(view); .into(view);
} }
} else { } else {
Glide.with(activity != null ? activity.getApplicationContext() : context.getApplicationContext()) Glide.with(activity != null ? activity : context)
.asDrawable() .asDrawable()
.load(placeholder) .load(placeholder)
.thumbnail(0.1f) .thumbnail(0.1f)
@ -397,7 +397,7 @@ public class MastodonHelper {
* @param text String - The current text * @param text String - The current text
* @return int - Number of characters used by emoji * @return int - Number of characters used by emoji
*/ */
static int countWithEmoji(String text) { private static int countWithEmoji(String text) {
int emojiCount = 0; int emojiCount = 0;
for (int i = 0; i < text.length(); i++) { for (int i = 0; i < text.length(); i++) {
int type = Character.getType(text.charAt(i)); int type = Character.getType(text.charAt(i));
@ -499,7 +499,7 @@ public class MastodonHelper {
accountId = account.id; accountId = account.id;
acct = account.acct; acct = account.acct;
} }
accountsVM.mute(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, accountId, true, (int) delayToPass / 1000) accountsVM.mute(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, accountId, true, (int) delayToPass)
.observe((LifecycleOwner) context, relationShip -> { .observe((LifecycleOwner) context, relationShip -> {
if (listener != null) { if (listener != null) {
listener.onTimedMute(relationShip); listener.onTimedMute(relationShip);
@ -553,13 +553,13 @@ public class MastodonHelper {
public static int getInstanceMaxChars(Context context) { public static int getInstanceMaxChars(Context context) {
int max_car; int max_car;
if (instanceInfo != null) {
max_car = instanceInfo.max_toot_chars != null ? Integer.parseInt(instanceInfo.max_toot_chars) : instanceInfo.configuration.statusesConf.max_characters;
} else {
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
int val = sharedpreferences.getInt(context.getString(R.string.SET_MAX_INSTANCE_CHAR) + MainActivity.currentInstance, -1); int val = sharedpreferences.getInt(context.getString(R.string.SET_MAX_INSTANCE_CHAR) + MainActivity.currentInstance, -1);
if (val != -1) { if (val != -1) {
return val; return val;
} else {
if (instanceInfo != null) {
max_car = instanceInfo.max_toot_chars != null && instanceInfo.max_toot_chars.matches("\\d+") ? Integer.parseInt(instanceInfo.max_toot_chars) : instanceInfo.configuration.statusesConf.max_characters;
} else { } else {
max_car = 500; max_car = 500;
} }

View file

@ -15,6 +15,7 @@ package app.fedilab.android.mastodon.helper;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import static android.content.Context.DOWNLOAD_SERVICE; import static android.content.Context.DOWNLOAD_SERVICE;
import static app.fedilab.android.BaseMainActivity.currentAccount;
import static app.fedilab.android.mastodon.helper.LogoHelper.getMainLogo; import static app.fedilab.android.mastodon.helper.LogoHelper.getMainLogo;
import android.app.Activity; import android.app.Activity;
@ -25,7 +26,6 @@ import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.graphics.Matrix; import android.graphics.Matrix;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.media.MediaRecorder; import android.media.MediaRecorder;
import android.media.MediaScannerConnection; import android.media.MediaScannerConnection;
@ -172,7 +172,7 @@ public class MediaHelper {
intent.setDataAndType(uri, mime); intent.setDataAndType(uri, mime);
MediaScannerConnection.scanFile(context, new String[]{backupFile.getAbsolutePath()}, null, null); MediaScannerConnection.scanFile(context, new String[]{backupFile.getAbsolutePath()}, null, null);
if (!share) { if (!share) {
Helper.notify_user(context, Helper.getCurrentAccount(context), intent, BitmapFactory.decodeResource(context.getResources(), Helper.notify_user(context, currentAccount, intent, BitmapFactory.decodeResource(context.getResources(),
getMainLogo(context)), Helper.NotifType.STORE, context.getString(R.string.save_over), context.getString(R.string.download_from, fileName)); getMainLogo(context)), Helper.NotifType.STORE, context.getString(R.string.save_over), context.getString(R.string.download_from, fileName));
Toasty.success(context, context.getString(R.string.save_over), Toasty.LENGTH_LONG).show(); Toasty.success(context, context.getString(R.string.save_over), Toasty.LENGTH_LONG).show();
} else { } else {
@ -267,32 +267,6 @@ public class MediaHelper {
); );
} }
public static Drawable rescaleImageIfNeeded(Activity activity, Drawable image) {
if (!(image instanceof BitmapDrawable)) {
return image;
}
int maxSize = 2000;
int width = image.getIntrinsicWidth();
int height = image.getIntrinsicHeight();
float scaleFactor;
if(width > maxSize || height > maxSize) {
if(width >= height) {
scaleFactor = (float) maxSize / width;
} else {
scaleFactor = (float) maxSize / height;
}
} else {
return image;
}
Bitmap b = ((BitmapDrawable)image).getBitmap();
int sizeX = Math.round(image.getIntrinsicWidth() * scaleFactor);
int sizeY = Math.round(image.getIntrinsicHeight() * scaleFactor);
Bitmap bitmapResized = Bitmap.createScaledBitmap(b, sizeX, sizeY, false);
image = new BitmapDrawable(activity.getResources(), bitmapResized);
return image;
}
/** /**
* Record media * Record media
* *
@ -300,8 +274,7 @@ public class MediaHelper {
* @param listener ActionRecord * @param listener ActionRecord
*/ */
public static void recordAudio(Activity activity, ActionRecord listener) { public static void recordAudio(Activity activity, ActionRecord listener) {
//String filePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath() + "/fedilab_recorded_audio.ogg"; String filePath = activity.getCacheDir() + "/fedilab_recorded_audio.m4a";
String filePath = activity.getCacheDir() + "/fedilab_recorded_audio.ogg";
AudioRecorder mAudioRecorder = AudioRecorder.getInstance(); AudioRecorder mAudioRecorder = AudioRecorder.getInstance();
File mAudioFile = new File(filePath); File mAudioFile = new File(filePath);
PopupRecordBinding binding = PopupRecordBinding.inflate(activity.getLayoutInflater()); PopupRecordBinding binding = PopupRecordBinding.inflate(activity.getLayoutInflater());
@ -333,18 +306,16 @@ public class MediaHelper {
binding.counter.setText(String.format(Locale.getDefault(), "%s:%s", minutes, seconds)); binding.counter.setText(String.format(Locale.getDefault(), "%s:%s", minutes, seconds));
}); });
} }
}, 0, 1000); }, 1000, 1000);
binding.record.setOnClickListener(v -> { binding.record.setOnClickListener(v -> {
mAudioRecorder.stopRecord(); mAudioRecorder.stopRecord();
timer.cancel(); timer.cancel();
alert.dismiss(); alert.dismiss();
listener.onRecorded(filePath); listener.onRecorded(filePath);
}); });
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
mAudioRecorder.prepareRecord(MediaRecorder.AudioSource.MIC, mAudioRecorder.prepareRecord(MediaRecorder.AudioSource.MIC,
MediaRecorder.OutputFormat.OGG, MediaRecorder.AudioEncoder.OPUS, 48000, 384000, MediaRecorder.OutputFormat.MPEG_4, MediaRecorder.AudioEncoder.AAC,
mAudioFile); mAudioFile);
}
mAudioRecorder.startRecord(); mAudioRecorder.startRecord();
} }

View file

@ -24,7 +24,6 @@ import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Build; import android.os.Build;
import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.text.Html; import android.text.Html;
@ -35,15 +34,17 @@ import androidx.annotation.Nullable;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.engine.GlideException;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.target.CustomTarget; import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.target.Target;
import com.bumptech.glide.request.transition.Transition; import com.bumptech.glide.request.transition.Transition;
import java.net.IDN;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import app.fedilab.android.BaseMainActivity; import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R; import app.fedilab.android.R;
@ -51,10 +52,8 @@ import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.mastodon.client.endpoints.MastodonNotificationsService; import app.fedilab.android.mastodon.client.endpoints.MastodonNotificationsService;
import app.fedilab.android.mastodon.client.entities.api.Notification; import app.fedilab.android.mastodon.client.entities.api.Notification;
import app.fedilab.android.mastodon.client.entities.api.Notifications; import app.fedilab.android.mastodon.client.entities.api.Notifications;
import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.mastodon.client.entities.app.Account; import app.fedilab.android.mastodon.client.entities.app.Account;
import app.fedilab.android.mastodon.client.entities.app.BaseAccount; import app.fedilab.android.mastodon.client.entities.app.BaseAccount;
import app.fedilab.android.mastodon.client.entities.app.CachedBundle;
import app.fedilab.android.mastodon.exception.DBException; import app.fedilab.android.mastodon.exception.DBException;
import app.fedilab.android.mastodon.ui.drawer.StatusAdapter; import app.fedilab.android.mastodon.ui.drawer.StatusAdapter;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
@ -66,21 +65,9 @@ import retrofit2.converter.gson.GsonConverterFactory;
public class NotificationsHelper { public class NotificationsHelper {
private static final HashMap<String, ReentrantLock> lockMap = new HashMap<>(); public static HashMap<String, String> since_ids = new HashMap<>();
public static HashMap<String, List<String>> pushed_notifications = new HashMap<>();
private static ReentrantLock getLock(String slug) { public static void task(Context context, String slug) throws DBException {
synchronized (lockMap) {
if (lockMap.containsKey(slug)) {
return lockMap.get(slug);
}
ReentrantLock lock = new ReentrantLock();
lockMap.put(slug, lock);
return lock;
}
}
public static synchronized void task(Context context, String slug) throws DBException {
SharedPreferences prefs = PreferenceManager SharedPreferences prefs = PreferenceManager
.getDefaultSharedPreferences(context); .getDefaultSharedPreferences(context);
@ -89,6 +76,13 @@ public class NotificationsHelper {
if (accountDb == null) { if (accountDb == null) {
return; return;
} }
String last_notifid;
if (since_ids.containsKey(slug)) {
last_notifid = since_ids.get(slug);
} else {
last_notifid = prefs.getString(context.getString(R.string.LAST_NOTIFICATION_ID) + slug, null);
since_ids.put(slug, last_notifid);
}
//Check which notifications the user wants to see //Check which notifications the user wants to see
boolean notif_follow = prefs.getBoolean(context.getString(R.string.SET_NOTIF_FOLLOW), true); boolean notif_follow = prefs.getBoolean(context.getString(R.string.SET_NOTIF_FOLLOW), true);
@ -102,20 +96,19 @@ public class NotificationsHelper {
boolean notif_report = prefs.getBoolean(context.getString(R.string.SET_NOTIF_ADMIN_REPORT), true); boolean notif_report = prefs.getBoolean(context.getString(R.string.SET_NOTIF_ADMIN_REPORT), true);
//User disagree with all notifications //User disagree with all notifications
if (!notif_follow && !notif_fav && !notif_mention && !notif_share && !notif_poll && !notif_status && !notif_updates && !notif_signup && !notif_report) { if (!notif_follow && !notif_fav && !notif_mention && !notif_share && !notif_poll && !notif_status && !notif_updates && !notif_signup && !notif_report)
return; //Nothing is done return; //Nothing is done
}
new Thread(() -> {
ReentrantLock lock = getLock(slug);
// fetch if we get the lock, or ignore, another thread is doing the job
if (lock.tryLock()) {
try {
MastodonNotificationsService mastodonNotificationsService = init(context, slugArray[1]); MastodonNotificationsService mastodonNotificationsService = init(context, slugArray[1]);
String finalLast_notifid = last_notifid;
new Thread(() -> {
Notifications notifications = new Notifications(); Notifications notifications = new Notifications();
Call<List<Notification>> notificationsCall; Call<List<Notification>> notificationsCall;
String last_notif_id = prefs.getString(context.getString(R.string.LAST_NOTIFICATION_ID) + slug, null); if (finalLast_notifid != null) {
notificationsCall = mastodonNotificationsService.getNotifications(accountDb.token, null, null, null, last_notif_id, null, 30); notificationsCall = mastodonNotificationsService.getNotifications(accountDb.token, null, null, null, finalLast_notifid, null, 30);
} else {
notificationsCall = mastodonNotificationsService.getNotifications(accountDb.token, null, null, null, null, null, 5);
}
if (notificationsCall != null) { if (notificationsCall != null) {
try { try {
Response<List<Notification>> notificationsResponse = notificationsCall.execute(); Response<List<Notification>> notificationsResponse = notificationsCall.execute();
@ -123,10 +116,7 @@ public class NotificationsHelper {
notifications.notifications = notificationsResponse.body(); notifications.notifications = notificationsResponse.body();
if (notifications.notifications != null) { if (notifications.notifications != null) {
if (notifications.notifications.size() > 0) { if (notifications.notifications.size() > 0) {
prefs.edit().putString( since_ids.put(slug, notifications.notifications.get(0).id);
context.getString(R.string.LAST_NOTIFICATION_ID) + slug,
notifications.notifications.get(0).id
).apply();
} }
} }
notifications.pagination = MastodonHelper.getPagination(notificationsResponse.headers()); notifications.pagination = MastodonHelper.getPagination(notificationsResponse.headers());
@ -134,22 +124,16 @@ public class NotificationsHelper {
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
}
Handler mainHandler = new Handler(Looper.getMainLooper()); Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> onRetrieveNotifications(context, notifications, accountDb, last_notif_id); Runnable myRunnable = () -> onRetrieveNotifications(context, notifications, accountDb);
mainHandler.post(myRunnable); mainHandler.post(myRunnable);
}
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}).start(); }).start();
} }
private static MastodonNotificationsService init(Context context, String instance) { private static MastodonNotificationsService init(Context context, @NonNull String instance) {
final OkHttpClient okHttpClient = new OkHttpClient.Builder() final OkHttpClient okHttpClient = new OkHttpClient.Builder()
.readTimeout(60, TimeUnit.SECONDS) .readTimeout(60, TimeUnit.SECONDS)
@ -157,14 +141,14 @@ public class NotificationsHelper {
.proxy(Helper.getProxy(context)) .proxy(Helper.getProxy(context))
.build(); .build();
Retrofit retrofit = new Retrofit.Builder() Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://" + (instance != null ? IDN.toASCII(instance, IDN.ALLOW_UNASSIGNED) : null) + "/api/v1/") .baseUrl("https://" + instance + "/api/v1/")
.addConverterFactory(GsonConverterFactory.create(Helper.getDateBuilder())) .addConverterFactory(GsonConverterFactory.create(Helper.getDateBuilder()))
.client(okHttpClient) .client(okHttpClient)
.build(); .build();
return retrofit.create(MastodonNotificationsService.class); return retrofit.create(MastodonNotificationsService.class);
} }
public static void onRetrieveNotifications(Context context, Notifications newNotifications, final BaseAccount account, String max_id) { public static void onRetrieveNotifications(Context context, Notifications newNotifications, final BaseAccount account) {
if (newNotifications == null || newNotifications.notifications == null || newNotifications.notifications.size() == 0 || account == null) { if (newNotifications == null || newNotifications.notifications == null || newNotifications.notifications.size() == 0 || account == null) {
return; return;
} }
@ -183,6 +167,8 @@ public class NotificationsHelper {
boolean notif_signup = prefs.getBoolean(context.getString(R.string.SET_NOTIF_ADMIN_SIGNUP), true); boolean notif_signup = prefs.getBoolean(context.getString(R.string.SET_NOTIF_ADMIN_SIGNUP), true);
boolean notif_report = prefs.getBoolean(context.getString(R.string.SET_NOTIF_ADMIN_REPORT), true); boolean notif_report = prefs.getBoolean(context.getString(R.string.SET_NOTIF_ADMIN_REPORT), true);
final String max_id = prefs.getString(context.getString(R.string.LAST_NOTIFICATION_ID) + key, null);
final List<Notification> notifications = new ArrayList<>(); final List<Notification> notifications = new ArrayList<>();
int pos = 0; int pos = 0;
for (Notification notif : notificationsReceived) { for (Notification notif : notificationsReceived) {
@ -198,24 +184,10 @@ public class NotificationsHelper {
//No previous notifications in cache, so no notification will be sent //No previous notifications in cache, so no notification will be sent
for (Notification notification : notifications) { for (Notification notification : notifications) {
List<String> notificationIDList;
if (pushed_notifications.containsKey(key)) {
notificationIDList = pushed_notifications.get(key);
if (notificationIDList != null && notificationIDList.contains(notification.id)) {
continue;
}
}
notificationIDList = pushed_notifications.get(key);
if (notificationIDList == null) {
notificationIDList = new ArrayList<>();
}
notificationIDList.add(notification.id);
pushed_notifications.put(key, notificationIDList);
String notificationUrl; String notificationUrl;
String title = null; String title = null;
String message = null; String message = null;
app.fedilab.android.mastodon.client.entities.api.Account targeted_account = null; String targeted_account = null;
Status targeted_status = null;
Helper.NotifType notifType = Helper.NotifType.MENTION; Helper.NotifType notifType = Helper.NotifType.MENTION;
switch (notification.type) { switch (notification.type) {
case "mention": case "mention":
@ -236,7 +208,6 @@ public class NotificationsHelper {
else else
message = new SpannableString(Html.fromHtml(notification.status.content)).toString(); message = new SpannableString(Html.fromHtml(notification.status.content)).toString();
} }
targeted_status = notification.status;
} }
} }
break; break;
@ -259,7 +230,6 @@ public class NotificationsHelper {
else else
message = new SpannableString(Html.fromHtml(notification.status.content)).toString(); message = new SpannableString(Html.fromHtml(notification.status.content)).toString();
} }
targeted_status = notification.status;
} }
} }
break; break;
@ -315,7 +285,7 @@ public class NotificationsHelper {
message = String.format("%s %s", notification.account.display_name, context.getString(R.string.notif_follow_request)); message = String.format("%s %s", notification.account.display_name, context.getString(R.string.notif_follow_request));
else else
message = String.format("@%s %s", notification.account.acct, context.getString(R.string.notif_follow_request)); message = String.format("@%s %s", notification.account.acct, context.getString(R.string.notif_follow_request));
targeted_account = notification.account; targeted_account = notification.account.id;
} }
break; break;
case "follow": case "follow":
@ -326,7 +296,7 @@ public class NotificationsHelper {
message = String.format("%s %s", notification.account.display_name, context.getString(R.string.notif_follow)); message = String.format("%s %s", notification.account.display_name, context.getString(R.string.notif_follow));
else else
message = String.format("@%s %s", notification.account.acct, context.getString(R.string.notif_follow)); message = String.format("@%s %s", notification.account.acct, context.getString(R.string.notif_follow));
targeted_account = notification.account; targeted_account = notification.account.id;
} }
break; break;
case "poll": case "poll":
@ -367,7 +337,6 @@ public class NotificationsHelper {
else else
message = new SpannableString(Html.fromHtml(notification.status.content)).toString(); message = new SpannableString(Html.fromHtml(notification.status.content)).toString();
} }
targeted_status = notification.status;
} }
} }
break; break;
@ -379,7 +348,7 @@ public class NotificationsHelper {
message = String.format("%s %s", notification.account.display_name, context.getString(R.string.notif_signed_up)); message = String.format("%s %s", notification.account.display_name, context.getString(R.string.notif_signed_up));
else else
message = String.format("@%s %s", notification.account.acct, context.getString(R.string.notif_signed_up)); message = String.format("@%s %s", notification.account.acct, context.getString(R.string.notif_signed_up));
targeted_account = notification.account; targeted_account = notification.account.id;
} }
break; break;
case "admin.report": case "admin.report":
@ -390,7 +359,7 @@ public class NotificationsHelper {
message = String.format("%s %s", notification.account.display_name, context.getString(R.string.notif_reported)); message = String.format("%s %s", notification.account.display_name, context.getString(R.string.notif_reported));
else else
message = String.format("@%s %s", notification.account.acct, context.getString(R.string.notif_reported)); message = String.format("@%s %s", notification.account.acct, context.getString(R.string.notif_reported));
targeted_account = notification.account; targeted_account = notification.account.id;
} }
break; break;
default: default:
@ -399,39 +368,53 @@ public class NotificationsHelper {
//Some others notification //Some others notification
final Intent intent = new Intent(context, MainActivity.class); final Intent intent = new Intent(context, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
Bundle args = new Bundle();
intent.putExtra(Helper.INTENT_ACTION, Helper.NOTIFICATION_INTENT); intent.putExtra(Helper.INTENT_ACTION, Helper.NOTIFICATION_INTENT);
intent.putExtra(Helper.PREF_USER_ID, account.user_id); intent.putExtra(Helper.PREF_USER_ID, account.user_id);
if (targeted_account != null) { if (targeted_account != null)
args.putSerializable(Helper.INTENT_TARGETED_ACCOUNT, targeted_account); intent.putExtra(Helper.INTENT_TARGETED_ACCOUNT, targeted_account);
} else if (targeted_status != null) {
args.putSerializable(Helper.INTENT_TARGETED_STATUS, targeted_status);
}
String finalMessage1 = message;
String finalTitle1 = title;
Helper.NotifType finalNotifType1 = notifType;
new CachedBundle(context).insertBundle(args, account, bundleId -> {
Bundle bundle = new Bundle();
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intent.putExtras(bundle);
intent.putExtra(Helper.PREF_USER_INSTANCE, account.instance); intent.putExtra(Helper.PREF_USER_INSTANCE, account.instance);
notificationUrl = notification.account.avatar;
Handler mainHandler = new Handler(Looper.getMainLooper()); Handler mainHandler = new Handler(Looper.getMainLooper());
final String finalNotificationUrl = notification.account.avatar; final String finalNotificationUrl = notificationUrl;
StatusAdapter.sendAction(context, Helper.RECEIVE_REFRESH_NOTIFICATIONS_ACTION, null, null); Helper.NotifType finalNotifType = notifType;
String finalMessage = message;
String finalTitle = title;
StatusAdapter.sendAction(context, Helper.ARG_REFRESH_NOTFICATION, null, null);
Runnable myRunnable = () -> Glide.with(context) Runnable myRunnable = () -> Glide.with(context)
.asBitmap() .asBitmap()
.load(finalNotificationUrl != null ? finalNotificationUrl : R.drawable.fedilab_logo_bubbles) .load(finalNotificationUrl != null ? finalNotificationUrl : R.drawable.fedilab_logo_bubbles)
.into(new CustomTarget<Bitmap>() { .listener(new RequestListener<Bitmap>() {
@Override @Override
public void onResourceReady(@NonNull Bitmap resource, Transition<? super Bitmap> transition) { public boolean onResourceReady(Bitmap resource, Object model, Target<Bitmap> target, DataSource dataSource, boolean isFirstResource) {
Helper.notify_user(context, account, intent, resource, finalNotifType1, finalTitle1, finalMessage1); return false;
} }
@Override @Override
public void onLoadFailed(@Nullable Drawable errorDrawable) { public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) {
super.onLoadFailed(errorDrawable); String lastNotif = prefs.getString(context.getString(R.string.LAST_NOTIFICATION_ID) + account.user_id + "@" + account.instance, null);
if (lastNotif == null || Helper.compareTo(notification.id, lastNotif) > 0) {
SharedPreferences.Editor editor = prefs.edit();
since_ids.put(account.user_id + "@" + account.instance, lastNotif);
editor.putString(context.getString(R.string.LAST_NOTIFICATION_ID) + account.user_id + "@" + account.instance, notifications.get(0).id);
editor.apply();
Helper.notify_user(context, account, intent, BitmapFactory.decodeResource(context.getResources(), Helper.notify_user(context, account, intent, BitmapFactory.decodeResource(context.getResources(),
getMainLogo(context)), finalNotifType1, finalTitle1, finalMessage1); getMainLogo(context)), finalNotifType, finalTitle, finalMessage);
}
return false;
}
})
.into(new CustomTarget<Bitmap>() {
@Override
public void onResourceReady(@NonNull Bitmap resource, Transition<? super Bitmap> transition) {
String lastNotif = prefs.getString(context.getString(R.string.LAST_NOTIFICATION_ID) + account.user_id + "@" + account.instance, null);
if (lastNotif == null || Helper.compareTo(notification.id, lastNotif) > 0) {
SharedPreferences.Editor editor = prefs.edit();
editor.putString(context.getString(R.string.LAST_NOTIFICATION_ID) + account.user_id + "@" + account.instance, notifications.get(0).id);
editor.apply();
since_ids.put(account.user_id + "@" + account.instance, lastNotif);
Helper.notify_user(context, account, intent, resource, finalNotifType, finalTitle, finalMessage);
}
} }
@Override @Override
@ -440,7 +423,7 @@ public class NotificationsHelper {
} }
}); });
mainHandler.post(myRunnable); mainHandler.post(myRunnable);
});
} }
} }

View file

@ -15,14 +15,12 @@ package app.fedilab.android.mastodon.helper;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.BaseMainActivity.currentAccount;
import static app.fedilab.android.BaseMainActivity.currentInstance; import static app.fedilab.android.BaseMainActivity.currentInstance;
import static app.fedilab.android.BaseMainActivity.currentUserID; import static app.fedilab.android.BaseMainActivity.currentUserID;
import static app.fedilab.android.BaseMainActivity.show_boosts; import static app.fedilab.android.BaseMainActivity.show_boosts;
import static app.fedilab.android.BaseMainActivity.show_dms; import static app.fedilab.android.BaseMainActivity.show_dms;
import static app.fedilab.android.BaseMainActivity.show_my_messages;
import static app.fedilab.android.BaseMainActivity.show_replies; import static app.fedilab.android.BaseMainActivity.show_replies;
import static app.fedilab.android.BaseMainActivity.show_self_boosts;
import static app.fedilab.android.BaseMainActivity.show_self_replies;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.SharedPreferences; import android.content.SharedPreferences;
@ -56,7 +54,6 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import app.fedilab.android.BaseMainActivity; import app.fedilab.android.BaseMainActivity;
@ -184,18 +181,15 @@ public class PinnedTimelineHelper {
} }
} }
} }
if (extraFeatures) {
//Create other default timelines
try { try {
Pinned pinnedAll = new Pinned(activity).getAllPinned(Helper.getCurrentAccount(activity)); Pinned pinnedAll = new Pinned(activity).getAllPinned(currentAccount);
if (pinnedAll == null) { if (pinnedAll == null) {
pinnedAll = new Pinned(); pinnedAll = new Pinned();
pinnedAll.user_id = currentUserID; pinnedAll.user_id = currentUserID;
pinnedAll.instance = currentInstance; pinnedAll.instance = currentInstance;
pinnedAll.pinnedTimelines = new ArrayList<>(); pinnedAll.pinnedTimelines = new ArrayList<>();
} }
if (extraFeatures) {
//Bubble timeline
boolean createDefaultBubbleAtTop = true; boolean createDefaultBubbleAtTop = true;
for (PinnedTimeline pinnedTimeline : pinnedAll.pinnedTimelines) { for (PinnedTimeline pinnedTimeline : pinnedAll.pinnedTimelines) {
if (pinnedTimeline.type == Timeline.TimeLineEnum.BUBBLE) { if (pinnedTimeline.type == Timeline.TimeLineEnum.BUBBLE) {
@ -215,33 +209,10 @@ public class PinnedTimelineHelper {
new Pinned(activity).insertPinned(pinned); new Pinned(activity).insertPinned(pinned);
} }
} }
}
//Trend timeline
boolean createDefaultTrendAtTop = true;
for (PinnedTimeline pinnedTimeline : pinnedAll.pinnedTimelines) {
if (pinnedTimeline.type == Timeline.TimeLineEnum.TREND_MESSAGE) {
createDefaultTrendAtTop = false;
break;
}
}
if (createDefaultTrendAtTop) {
PinnedTimeline pinnedTimelineBubble = new PinnedTimeline();
pinnedTimelineBubble.type = Timeline.TimeLineEnum.TREND_MESSAGE;
pinnedTimelineBubble.position = pinnedAll.pinnedTimelines != null ? pinnedAll.pinnedTimelines.size() : 0;
pinned.pinnedTimelines.add(pinnedTimelineBubble);
boolean exist = new Pinned(activity).pinnedExist(pinned);
if (exist) {
new Pinned(activity).updatePinned(pinned);
} else {
new Pinned(activity).insertPinned(pinned);
}
}
} catch (DBException e) { } catch (DBException e) {
e.printStackTrace(); e.printStackTrace();
} }
}
sortPositionAsc(pinnedTimelines); sortPositionAsc(pinnedTimelines);
//Check if changes occurred, if mastodonLists is null it does need, because it is the first call to draw pinned //Check if changes occurred, if mastodonLists is null it does need, because it is the first call to draw pinned
@ -288,7 +259,7 @@ public class PinnedTimelineHelper {
for (MastodonList mastodonList : mastodonLists) { for (MastodonList mastodonList : mastodonLists) {
boolean present = false; boolean present = false;
try { try {
Pinned pinnedAll = new Pinned(activity).getAllPinned(Helper.getCurrentAccount(activity)); Pinned pinnedAll = new Pinned(activity).getAllPinned(currentAccount);
if (pinnedAll == null) { if (pinnedAll == null) {
pinnedAll = pinned; pinnedAll = pinned;
} }
@ -329,18 +300,12 @@ public class PinnedTimelineHelper {
} }
//Pinned tab position will start after BOTTOM_TIMELINE_COUNT (ie 5) //Pinned tab position will start after BOTTOM_TIMELINE_COUNT (ie 5)
activityMainBinding.tabLayout.removeAllTabs(); activityMainBinding.tabLayout.removeAllTabs();
if(activityMainBinding.viewPager.getAdapter() != null) {
activityMainBinding.viewPager.getAdapter().notifyDataSetChanged();
}
int toRemove = FedilabPageAdapter.BOTTOM_TIMELINE_COUNT; int toRemove = FedilabPageAdapter.BOTTOM_TIMELINE_COUNT;
if (!singleBar) { if (!singleBar) {
//Small hack to hide first tabs (they represent the item of the bottom menu) //Small hack to hide first tabs (they represent the item of the bottom menu)
toRemove = itemToRemoveInBottomMenu(activity); toRemove = itemToRemoveInBottomMenu(activity);
for (int i = 0; i < (FedilabPageAdapter.BOTTOM_TIMELINE_COUNT - toRemove); i++) { for (int i = 0; i < (FedilabPageAdapter.BOTTOM_TIMELINE_COUNT - toRemove); i++) {
activityMainBinding.tabLayout.addTab(activityMainBinding.tabLayout.newTab(), false); activityMainBinding.tabLayout.addTab(activityMainBinding.tabLayout.newTab(), false);
if(activityMainBinding.viewPager.getAdapter() != null) {
activityMainBinding.viewPager.getAdapter().notifyDataSetChanged();
}
((ViewGroup) activityMainBinding.tabLayout.getChildAt(0)).getChildAt(i).setVisibility(View.GONE); ((ViewGroup) activityMainBinding.tabLayout.getChildAt(0)).getChildAt(i).setVisibility(View.GONE);
} }
} }
@ -374,10 +339,10 @@ public class PinnedTimelineHelper {
break; break;
case REMOTE: case REMOTE:
name = pinnedTimeline.remoteInstance.host; name = pinnedTimeline.remoteInstance.host;
if (pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.NITTER || pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.NITTER_TAG) { if (pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.NITTER) {
String remoteInstance = sharedpreferences.getString(activity.getString(R.string.SET_NITTER_HOST), activity.getString(R.string.DEFAULT_NITTER_HOST)).toLowerCase(); String remoteInstance = sharedpreferences.getString(activity.getString(R.string.SET_NITTER_HOST), activity.getString(R.string.DEFAULT_NITTER_HOST)).toLowerCase();
//Custom name for Nitter instances //Custom name for Nitter instances
if (pinnedTimeline.remoteInstance.displayName != null && !pinnedTimeline.remoteInstance.displayName.trim().isEmpty()) { if (pinnedTimeline.remoteInstance.displayName != null && pinnedTimeline.remoteInstance.displayName.trim().length() > 0) {
name = pinnedTimeline.remoteInstance.displayName; name = pinnedTimeline.remoteInstance.displayName;
} }
ident = "@R@" + remoteInstance; ident = "@R@" + remoteInstance;
@ -405,13 +370,10 @@ public class PinnedTimelineHelper {
case MASTODON: case MASTODON:
tabCustomViewBinding.icon.setImageResource(R.drawable.mastodon_icon_item); tabCustomViewBinding.icon.setImageResource(R.drawable.mastodon_icon_item);
break; break;
case LEMMY:
tabCustomViewBinding.icon.setImageResource(R.drawable.lemmy);
break;
case MISSKEY: case MISSKEY:
tabCustomViewBinding.icon.setImageResource(R.drawable.misskey); tabCustomViewBinding.icon.setImageResource(R.drawable.misskey);
break; break;
case NITTER_TAG:
case NITTER: case NITTER:
tabCustomViewBinding.icon.setImageResource(R.drawable.nitter); tabCustomViewBinding.icon.setImageResource(R.drawable.nitter);
break; break;
@ -446,9 +408,6 @@ public class PinnedTimelineHelper {
case BUBBLE: case BUBBLE:
tabCustomDefaultViewBinding.icon.setImageResource(R.drawable.ic_baseline_bubble_chart_24); tabCustomDefaultViewBinding.icon.setImageResource(R.drawable.ic_baseline_bubble_chart_24);
break; break;
case TREND_MESSAGE:
tabCustomDefaultViewBinding.icon.setImageResource(R.drawable.baseline_moving_24);
break;
} }
tab.setCustomView(tabCustomDefaultViewBinding.getRoot()); tab.setCustomView(tabCustomDefaultViewBinding.getRoot());
} }
@ -456,9 +415,6 @@ public class PinnedTimelineHelper {
String slug = pinnedTimeline.type.getValue() + (ident != null ? "|" + ident : ""); String slug = pinnedTimeline.type.getValue() + (ident != null ? "|" + ident : "");
tab.setTag(slug); tab.setTag(slug);
activityMainBinding.tabLayout.addTab(tab, false); activityMainBinding.tabLayout.addTab(tab, false);
if(activityMainBinding.viewPager.getAdapter() != null){
activityMainBinding.viewPager.getAdapter().notifyDataSetChanged();
}
pinnedTimelineVisibleList.add(pinnedTimeline); pinnedTimelineVisibleList.add(pinnedTimeline);
} }
} }
@ -505,16 +461,12 @@ public class PinnedTimelineHelper {
case MISSKEY: case MISSKEY:
item.setIcon(R.drawable.misskey); item.setIcon(R.drawable.misskey);
break; break;
case LEMMY:
item.setIcon(R.drawable.lemmy);
break;
case PIXELFED: case PIXELFED:
item.setIcon(R.drawable.pixelfed); item.setIcon(R.drawable.pixelfed);
break; break;
case NITTER_TAG:
case NITTER: case NITTER:
item.setIcon(R.drawable.nitter); item.setIcon(R.drawable.nitter);
if (pinnedTimeline.remoteInstance.displayName != null && !pinnedTimeline.remoteInstance.displayName.trim().isEmpty()) { if (pinnedTimeline.remoteInstance.displayName != null && pinnedTimeline.remoteInstance.displayName.trim().length() > 0) {
item.setTitle(pinnedTimeline.remoteInstance.displayName); item.setTitle(pinnedTimeline.remoteInstance.displayName);
} else { } else {
item.setTitle(pinnedTimeline.remoteInstance.host); item.setTitle(pinnedTimeline.remoteInstance.host);
@ -566,7 +518,7 @@ public class PinnedTimelineHelper {
bubbleClick(activity, finalPinned, v, activityMainBinding, finalI, activityMainBinding.tabLayout.getTabAt(finalI).getTag().toString()); bubbleClick(activity, finalPinned, v, activityMainBinding, finalI, activityMainBinding.tabLayout.getTabAt(finalI).getTag().toString());
break; break;
case REMOTE: case REMOTE:
if (pinnedTimelineVisibleList.get(position).remoteInstance.type != RemoteInstance.InstanceType.NITTER && pinnedTimelineVisibleList.get(position).remoteInstance.type != RemoteInstance.InstanceType.NITTER_TAG) { if (pinnedTimelineVisibleList.get(position).remoteInstance.type != RemoteInstance.InstanceType.NITTER) {
instanceClick(activity, finalPinned, v, activityMainBinding, finalI, activityMainBinding.tabLayout.getTabAt(finalI).getTag().toString()); instanceClick(activity, finalPinned, v, activityMainBinding, finalI, activityMainBinding.tabLayout.getTabAt(finalI).getTag().toString());
} else { } else {
nitterClick(activity, finalPinned, v, activityMainBinding, finalI, activityMainBinding.tabLayout.getTabAt(finalI).getTag().toString()); nitterClick(activity, finalPinned, v, activityMainBinding, finalI, activityMainBinding.tabLayout.getTabAt(finalI).getTag().toString());
@ -574,7 +526,6 @@ public class PinnedTimelineHelper {
break; break;
case HOME: case HOME:
case LOCAL: case LOCAL:
case TREND_MESSAGE:
case PUBLIC: case PUBLIC:
defaultClick(activity, pinnedTimelineVisibleList.get(position).type, v, activityMainBinding, finalI); defaultClick(activity, pinnedTimelineVisibleList.get(position).type, v, activityMainBinding, finalI);
break; break;
@ -588,7 +539,6 @@ public class PinnedTimelineHelper {
activityMainBinding.tabLayout.clearOnTabSelectedListeners(); activityMainBinding.tabLayout.clearOnTabSelectedListeners();
FedilabPageAdapter fedilabPageAdapter = new FedilabPageAdapter(activity, activity.getSupportFragmentManager(), pinned, bottomMenu); FedilabPageAdapter fedilabPageAdapter = new FedilabPageAdapter(activity, activity.getSupportFragmentManager(), pinned, bottomMenu);
activityMainBinding.viewPager.setAdapter(fedilabPageAdapter); activityMainBinding.viewPager.setAdapter(fedilabPageAdapter);
activityMainBinding.viewPager.setOffscreenPageLimit(tabStrip.getChildCount()); activityMainBinding.viewPager.setOffscreenPageLimit(tabStrip.getChildCount());
activityMainBinding.viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(activityMainBinding.tabLayout)); activityMainBinding.viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(activityMainBinding.tabLayout));
if (!singleBar) { if (!singleBar) {
@ -678,25 +628,16 @@ public class PinnedTimelineHelper {
.inflate(R.menu.option_filter_toots, popup.getMenu()); .inflate(R.menu.option_filter_toots, popup.getMenu());
Menu menu = popup.getMenu(); Menu menu = popup.getMenu();
final MenuItem itemShowBoosts = menu.findItem(R.id.action_show_boosts); final MenuItem itemShowBoosts = menu.findItem(R.id.action_show_boosts);
final MenuItem itemShowMyMessages = menu.findItem(R.id.action_show_my_messages);
final MenuItem itemShowSelfBoosts = menu.findItem(R.id.action_show_self_boosts);
final MenuItem itemShowReplies = menu.findItem(R.id.action_show_replies); final MenuItem itemShowReplies = menu.findItem(R.id.action_show_replies);
final MenuItem itemShowSelfReplies = menu.findItem(R.id.action_show_self_replies);
final MenuItem itemShowDMs = menu.findItem(R.id.action_show_dms); final MenuItem itemShowDMs = menu.findItem(R.id.action_show_dms);
final MenuItem itemFilter = menu.findItem(R.id.action_filter); final MenuItem itemFilter = menu.findItem(R.id.action_filter);
if (!showExtendedFilter) { if (!showExtendedFilter) {
itemShowBoosts.setVisible(false); itemShowBoosts.setVisible(false);
itemShowReplies.setVisible(false); itemShowReplies.setVisible(false);
itemShowSelfBoosts.setVisible(false);
itemShowSelfReplies.setVisible(false);
itemShowMyMessages.setVisible(false);
itemShowDMs.setVisible(false); itemShowDMs.setVisible(false);
} else { } else {
itemShowBoosts.setVisible(true); itemShowBoosts.setVisible(true);
itemShowReplies.setVisible(true); itemShowReplies.setVisible(true);
itemShowSelfBoosts.setVisible(true);
itemShowSelfReplies.setVisible(true);
itemShowMyMessages.setVisible(true);
itemShowDMs.setVisible(true); itemShowDMs.setVisible(true);
} }
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(activity); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(activity);
@ -710,10 +651,7 @@ public class PinnedTimelineHelper {
} }
itemShowBoosts.setChecked(show_boosts); itemShowBoosts.setChecked(show_boosts);
itemShowMyMessages.setChecked(show_my_messages);
itemShowReplies.setChecked(show_replies); itemShowReplies.setChecked(show_replies);
itemShowSelfBoosts.setChecked(show_self_boosts);
itemShowSelfReplies.setChecked(show_self_replies);
itemShowDMs.setChecked(show_dms); itemShowDMs.setChecked(show_dms);
if (show_filtered != null && show_filtered.length() > 0) { if (show_filtered != null && show_filtered.length() > 0) {
itemFilter.setTitle(show_filtered); itemFilter.setTitle(show_filtered);
@ -721,7 +659,8 @@ public class PinnedTimelineHelper {
popup.setOnDismissListener(menu1 -> { popup.setOnDismissListener(menu1 -> {
if (activityMainBinding.viewPager.getAdapter() != null && activityMainBinding.tabLayout.getSelectedTabPosition() != -1) { if (activityMainBinding.viewPager.getAdapter() != null && activityMainBinding.tabLayout.getSelectedTabPosition() != -1) {
Fragment fragment = (Fragment) activityMainBinding.viewPager.getAdapter().instantiateItem(activityMainBinding.viewPager, activityMainBinding.tabLayout.getSelectedTabPosition()); Fragment fragment = (Fragment) activityMainBinding.viewPager.getAdapter().instantiateItem(activityMainBinding.viewPager, activityMainBinding.tabLayout.getSelectedTabPosition());
if (fragment instanceof FragmentMastodonTimeline fragmentMastodonTimeline && fragment.isVisible()) { if (fragment instanceof FragmentMastodonTimeline && fragment.isVisible()) {
FragmentMastodonTimeline fragmentMastodonTimeline = ((FragmentMastodonTimeline) fragment);
fragmentMastodonTimeline.refreshAllAdapters(); fragmentMastodonTimeline.refreshAllAdapters();
} }
} }
@ -748,26 +687,11 @@ public class PinnedTimelineHelper {
editor.putBoolean(activity.getString(R.string.SET_SHOW_BOOSTS) + currentUserID + currentInstance, show_boosts); editor.putBoolean(activity.getString(R.string.SET_SHOW_BOOSTS) + currentUserID + currentInstance, show_boosts);
itemShowBoosts.setChecked(show_boosts); itemShowBoosts.setChecked(show_boosts);
editor.apply(); editor.apply();
} else if (itemId == R.id.action_show_my_messages) {
show_my_messages = !show_my_messages;
editor.putBoolean(activity.getString(R.string.SET_SHOW_MY_MESSAGES) + currentUserID + currentInstance, show_my_messages);
itemShowMyMessages.setChecked(show_my_messages);
editor.apply();
} else if (itemId == R.id.action_show_self_boosts) {
show_self_boosts = !show_self_boosts;
editor.putBoolean(activity.getString(R.string.SET_SHOW_SELF_BOOSTS) + currentUserID + currentInstance, show_self_boosts);
itemShowSelfBoosts.setChecked(show_self_boosts);
editor.apply();
} else if (itemId == R.id.action_show_replies) { } else if (itemId == R.id.action_show_replies) {
show_replies = !show_replies; show_replies = !show_replies;
editor.putBoolean(activity.getString(R.string.SET_SHOW_REPLIES) + currentUserID + currentInstance, show_replies); editor.putBoolean(activity.getString(R.string.SET_SHOW_REPLIES) + currentUserID + currentInstance, show_replies);
itemShowReplies.setChecked(show_replies); itemShowReplies.setChecked(show_replies);
editor.apply(); editor.apply();
} else if (itemId == R.id.action_show_self_replies) {
show_self_replies = !show_self_replies;
editor.putBoolean(activity.getString(R.string.SET_SHOW_SELF_REPLIES) + currentUserID + currentInstance, show_self_replies);
itemShowSelfReplies.setChecked(show_self_replies);
editor.apply();
} else if (itemId == R.id.action_show_dms) { } else if (itemId == R.id.action_show_dms) {
show_dms = !show_dms; show_dms = !show_dms;
editor.putBoolean(activity.getString(R.string.SET_SHOW_DMS) + currentUserID + currentInstance, show_dms); editor.putBoolean(activity.getString(R.string.SET_SHOW_DMS) + currentUserID + currentInstance, show_dms);
@ -835,7 +759,7 @@ public class PinnedTimelineHelper {
int toRemove = 0; int toRemove = 0;
try { try {
//If some menu items have been hidden we should not create tab for them //If some menu items have been hidden we should not create tab for them
bottomMenuDb = new BottomMenu(activity).getAllBottomMenu(Helper.getCurrentAccount(activity)); bottomMenuDb = new BottomMenu(activity).getAllBottomMenu(currentAccount);
if (bottomMenuDb != null) { if (bottomMenuDb != null) {
List<BottomMenu.MenuItem> menuItemList = bottomMenuDb.bottom_menu; List<BottomMenu.MenuItem> menuItemList = bottomMenuDb.bottom_menu;
if (menuItemList != null) { if (menuItemList != null) {
@ -1570,12 +1494,6 @@ public class PinnedTimelineHelper {
PopupMenu popup = new PopupMenu(activity, view); PopupMenu popup = new PopupMenu(activity, view);
popup.getMenuInflater() popup.getMenuInflater()
.inflate(R.menu.option_nitter_timeline, popup.getMenu()); .inflate(R.menu.option_nitter_timeline, popup.getMenu());
if(remoteInstance.type == RemoteInstance.InstanceType.NITTER_TAG) {
MenuItem item = popup.getMenu().findItem(R.id.action_nitter_manage_accounts);
if(item != null) {
item.setTitle(R.string.manage_tags);
}
}
int finalOffSetPosition = offSetPosition; int finalOffSetPosition = offSetPosition;
popup.setOnMenuItemClickListener(item -> { popup.setOnMenuItemClickListener(item -> {
int itemId = item.getItemId(); int itemId = item.getItemId();
@ -1595,7 +1513,7 @@ public class PinnedTimelineHelper {
} }
dialogBuilder.setPositiveButton(R.string.validate, (dialog, id) -> { dialogBuilder.setPositiveButton(R.string.validate, (dialog, id) -> {
String values = editTextName.getText().toString(); String values = editTextName.getText().toString();
if (values.trim().isEmpty()) { if (values.trim().length() == 0) {
values = remoteInstance.displayName; values = remoteInstance.displayName;
} }
remoteInstance.displayName = values; remoteInstance.displayName = values;

View file

@ -1,46 +0,0 @@
package app.fedilab.android.mastodon.helper;
/* Copyright 2024 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import java.util.ArrayList;
import java.util.List;
public class PronounsHelper {
public static List<String> pronouns = new ArrayList<>() {
{
add("pronoun");
add("pronouns");
add("pronoms");
add("pronom");
add("pronombres");
add("לשון פנייה");
add("כינויי גוף");
add("pronomen");
add("pronomina");
add("称谓");
add("pronomes");
add("pronome");
add("pronomi");
add("pronomoj");
add("zaimki");
add("voornaamwoorden");
add("riochdairean");
add("כינויי גוף");
add("personlig pronomen");
add("人称");
add("人稱");
}
};
}

View file

@ -35,26 +35,20 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import org.unifiedpush.android.connector.UnifiedPush; import org.unifiedpush.android.connector.UnifiedPush;
import java.net.IDN; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.mastodon.client.endpoints.MastodonInstanceService;
import app.fedilab.android.mastodon.client.entities.api.InstanceV2;
import app.fedilab.android.mastodon.client.entities.app.Account; import app.fedilab.android.mastodon.client.entities.app.Account;
import app.fedilab.android.mastodon.client.entities.app.BaseAccount; import app.fedilab.android.mastodon.client.entities.app.BaseAccount;
import app.fedilab.android.mastodon.jobs.NotificationsWorker; import app.fedilab.android.mastodon.jobs.NotificationsWorker;
import okhttp3.OkHttpClient;
import retrofit2.Call;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class PushHelper { public class PushHelper {
public static void startStreaming(Context context) { public static void startStreaming(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
String typeOfNotification = prefs.getString(context.getString(R.string.SET_NOTIFICATION_TYPE), "PUSH_NOTIFICATIONS"); String typeOfNotification = prefs.getString(context.getString(R.string.SET_NOTIFICATION_TYPE), "PUSH_NOTIFICATIONS");
switch (typeOfNotification) { switch (typeOfNotification) {
@ -63,12 +57,12 @@ public class PushHelper {
List<BaseAccount> accounts = new Account(context).getPushNotificationAccounts(); List<BaseAccount> accounts = new Account(context).getPushNotificationAccounts();
Handler mainHandler = new Handler(Looper.getMainLooper()); Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> { Runnable myRunnable = () -> {
List<String> distributors = UnifiedPush.getDistributors(context); List<String> distributors = UnifiedPush.getDistributors(context, new ArrayList<>());
if (distributors.isEmpty()) { if (distributors.size() == 0) {
AlertDialog.Builder alert = new MaterialAlertDialogBuilder(context); AlertDialog.Builder alert = new MaterialAlertDialogBuilder(context);
alert.setTitle(R.string.no_distributors_found); alert.setTitle(R.string.no_distributors_found);
final TextView message = new TextView(context); final TextView message = new TextView(context);
String link = "https://fedilab.app/docs/fedilab/faq/"; String link = "https://fedilab.app/wiki/features/push-notifications/";
final SpannableString s = final SpannableString s =
new SpannableString(context.getString(R.string.no_distributors_explanation, link)); new SpannableString(context.getString(R.string.no_distributors_explanation, link));
Linkify.addLinks(s, Linkify.WEB_URLS); Linkify.addLinks(s, Linkify.WEB_URLS);
@ -100,10 +94,8 @@ public class PushHelper {
WorkManager.getInstance(context).cancelAllWorkByTag(Helper.WORKER_REFRESH_NOTIFICATION); WorkManager.getInstance(context).cancelAllWorkByTag(Helper.WORKER_REFRESH_NOTIFICATION);
new Thread(() -> { new Thread(() -> {
List<BaseAccount> accounts = new Account(context).getPushNotificationAccounts(); List<BaseAccount> accounts = new Account(context).getPushNotificationAccounts();
if(accounts != null) {
for (BaseAccount account : accounts) { for (BaseAccount account : accounts) {
((Activity) context).runOnUiThread(() -> UnifiedPush.unregister(context, account.user_id + "@" + account.instance)); ((Activity) context).runOnUiThread(() -> UnifiedPush.unregisterApp(context, account.user_id + "@" + account.instance));
}
} }
}).start(); }).start();
break; break;
@ -116,7 +108,7 @@ public class PushHelper {
List<BaseAccount> accounts = new Account(context).getPushNotificationAccounts(); List<BaseAccount> accounts = new Account(context).getPushNotificationAccounts();
if (accounts != null) { if (accounts != null) {
for (BaseAccount account : accounts) { for (BaseAccount account : accounts) {
((Activity) context).runOnUiThread(() -> UnifiedPush.unregister(context, account.user_id + "@" + account.instance)); ((Activity) context).runOnUiThread(() -> UnifiedPush.unregisterApp(context, account.user_id + "@" + account.instance));
} }
} }
}).start(); }).start();
@ -133,49 +125,28 @@ public class PushHelper {
if (accounts == null) { if (accounts == null) {
return; return;
} }
List<String> distributors = UnifiedPush.getDistributors(context); List<String> distributors = UnifiedPush.getDistributors(context, new ArrayList<>());
if (!distributors.isEmpty()) { if (distributors.size() == 1 || !UnifiedPush.getDistributor(context).isEmpty()) {
if (distributors.size() == 1) { if (distributors.size() == 1) {
UnifiedPush.saveDistributor(context, distributors.get(0)); UnifiedPush.saveDistributor(context, distributors.get(0));
} }
final OkHttpClient okHttpClient = Helper.myOkHttpClient(context.getApplicationContext());
for (BaseAccount account : accounts) { for (BaseAccount account : accounts) {
UnifiedPush.registerApp(context, account.user_id + "@" + account.instance, new ArrayList<>(), "");
new Thread(()->{
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://" + (account.instance != null ? IDN.toASCII(account.instance, IDN.ALLOW_UNASSIGNED) : null) + "/api/v2/")
.addConverterFactory(GsonConverterFactory.create(Helper.getDateBuilder()))
.client(okHttpClient)
.build();
MastodonInstanceService mastodonInstanceService = retrofit.create(MastodonInstanceService.class);
Call<InstanceV2> instanceV2Call = mastodonInstanceService.instanceV2();
String vapid = null;
if (instanceV2Call != null) {
try {
Response<InstanceV2> instanceResponse = instanceV2Call.execute();
if (instanceResponse.isSuccessful()) {
InstanceV2 instanceV2 = instanceResponse.body();
if (instanceV2 != null && instanceV2.configuration.vapId != null) {
vapid = instanceV2.configuration.vapId.publicKey;
} }
return;
} }
} catch (Exception e) { AlertDialog.Builder alert = new MaterialAlertDialogBuilder(context);
e.printStackTrace(); alert.setTitle(R.string.select_distributors);
} String[] distributorsStr = distributors.toArray(new String[0]);
} alert.setSingleChoiceItems(distributorsStr, -1, (dialog, item) -> {
Handler mainHandler = new Handler(Looper.getMainLooper()); String distributor = distributorsStr[item];
String finalVapid = vapid!=null?vapid.replaceAll("=",""):null; UnifiedPush.saveDistributor(context, distributor);
Runnable myRunnable = () -> { for (BaseAccount account : accounts) {
try { UnifiedPush.registerApp(context, account.user_id + "@" + account.instance, new ArrayList<>(), "");
UnifiedPush.register(context, account.user_id + "@" + account.instance, null, finalVapid);
}catch (Exception e){
e.printStackTrace();
}
};
mainHandler.post(myRunnable);
}).start();
}
} }
dialog.dismiss();
});
alert.show();
} }
} }

View file

@ -15,19 +15,15 @@ package app.fedilab.android.mastodon.helper;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import org.unifiedpush.android.connector.data.PushEndpoint; import java.util.concurrent.TimeUnit;
import java.net.IDN;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.mastodon.client.endpoints.MastodonNotificationsService; import app.fedilab.android.mastodon.client.endpoints.MastodonNotificationsService;
@ -44,31 +40,23 @@ import retrofit2.converter.gson.GsonConverterFactory;
public class PushNotifications { public class PushNotifications {
public static void unregisterPushNotifications(Context context, String slug) {
new Thread(() -> {
String[] slugArray = slug.split("@");
BaseAccount accountDb = null;
try {
accountDb = new Account(context).getUniqAccount(slugArray[0], slugArray[1]);
} catch (DBException e) {
e.printStackTrace();
}
if (accountDb == null) { public static void registerPushNotifications(Context context, String endpoint, String slug) {
return;
}
MastodonNotificationsService mastodonNotificationsService = init(context, accountDb.instance);
mastodonNotificationsService.deletePushsubscription(accountDb.token);
});
}
public static void registerPushNotifications(Context context, PushEndpoint pushEndpoint, String slug) {
SharedPreferences prefs = PreferenceManager SharedPreferences prefs = PreferenceManager
.getDefaultSharedPreferences(context); .getDefaultSharedPreferences(context);
ECDHFedilab ecdh = null;
try {
ecdh = new ECDHFedilab(context, slug);
} catch (Exception e) {
e.printStackTrace();
}
if (ecdh == null) {
return;
}
String pubKey = pushEndpoint.getPubKeySet().getPubKey(); String pubKey = ecdh.getPublicKey();
String auth =pushEndpoint.getPubKeySet().getAuth(); String auth = ecdh.getAuthKey();
boolean notif_follow = prefs.getBoolean(context.getString(R.string.SET_NOTIF_FOLLOW), true); boolean notif_follow = prefs.getBoolean(context.getString(R.string.SET_NOTIF_FOLLOW), true);
@ -96,10 +84,9 @@ public class PushNotifications {
PushSubscription pushSubscription; PushSubscription pushSubscription;
Call<PushSubscription> pushSubscriptionCall = mastodonNotificationsService.pushSubscription( Call<PushSubscription> pushSubscriptionCall = mastodonNotificationsService.pushSubscription(
accountDb.token, accountDb.token,
pushEndpoint.getUrl(), endpoint,
pubKey, pubKey,
auth, auth,
true,
notif_follow, notif_follow,
notif_fav, notif_fav,
notif_share, notif_share,
@ -114,7 +101,6 @@ public class PushNotifications {
Response<PushSubscription> pushSubscriptionResponse = pushSubscriptionCall.execute(); Response<PushSubscription> pushSubscriptionResponse = pushSubscriptionCall.execute();
if (pushSubscriptionResponse.isSuccessful()) { if (pushSubscriptionResponse.isSuccessful()) {
pushSubscription = pushSubscriptionResponse.body(); pushSubscription = pushSubscriptionResponse.body();
if (pushSubscription != null) { if (pushSubscription != null) {
pushSubscription.server_key = pushSubscription.server_key.replace('/', '_'); pushSubscription.server_key = pushSubscription.server_key.replace('/', '_');
pushSubscription.server_key = pushSubscription.server_key.replace('+', '-'); pushSubscription.server_key = pushSubscription.server_key.replace('+', '-');
@ -144,10 +130,14 @@ public class PushNotifications {
slug + "/unifiedpush.connector", null); slug + "/unifiedpush.connector", null);
} }
private static MastodonNotificationsService init(@NonNull Context context, String instance) { private static MastodonNotificationsService init(@NonNull Context context, @NonNull String instance) {
final OkHttpClient okHttpClient = Helper.myOkHttpClient(context); final OkHttpClient okHttpClient = new OkHttpClient.Builder()
.readTimeout(60, TimeUnit.SECONDS)
.connectTimeout(60, TimeUnit.SECONDS)
.proxy(Helper.getProxy(context.getApplicationContext()))
.build();
Retrofit retrofit = new Retrofit.Builder() Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://" + (instance != null ? IDN.toASCII(instance, IDN.ALLOW_UNASSIGNED) : null) + "/api/v1/") .baseUrl("https://" + instance + "/api/v1/")
.addConverterFactory(GsonConverterFactory.create(Helper.getDateBuilder())) .addConverterFactory(GsonConverterFactory.create(Helper.getDateBuilder()))
.client(okHttpClient) .client(okHttpClient)
.build(); .build();

View file

@ -15,7 +15,7 @@ package app.fedilab.android.mastodon.helper;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.BaseMainActivity.currentNightMode; import static app.fedilab.android.BaseMainActivity.currentAccount;
import android.app.Activity; import android.app.Activity;
import android.content.ClipData; import android.content.ClipData;
@ -71,7 +71,6 @@ import java.util.regex.Pattern;
import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.HttpsURLConnection;
import app.fedilab.android.BaseMainActivity; import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.MySuperGrammerLocator;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.activities.MainActivity; import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.databinding.PopupLinksBinding; import app.fedilab.android.databinding.PopupLinksBinding;
@ -85,66 +84,36 @@ import app.fedilab.android.mastodon.client.entities.api.Emoji;
import app.fedilab.android.mastodon.client.entities.api.Filter; import app.fedilab.android.mastodon.client.entities.api.Filter;
import app.fedilab.android.mastodon.client.entities.api.Mention; import app.fedilab.android.mastodon.client.entities.api.Mention;
import app.fedilab.android.mastodon.client.entities.api.Status; import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.mastodon.client.entities.app.CachedBundle;
import app.fedilab.android.mastodon.client.entities.app.MarkdownConverter;
import app.fedilab.android.mastodon.ui.drawer.StatusAdapter; import app.fedilab.android.mastodon.ui.drawer.StatusAdapter;
import app.fedilab.android.mastodon.viewmodel.mastodon.FiltersVM; import app.fedilab.android.mastodon.viewmodel.mastodon.FiltersVM;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
import io.noties.markwon.AbstractMarkwonPlugin;
import io.noties.markwon.Markwon;
import io.noties.markwon.SoftBreakAddsNewLinePlugin;
import io.noties.markwon.ext.strikethrough.StrikethroughPlugin;
import io.noties.markwon.ext.tables.TablePlugin;
import io.noties.markwon.inlineparser.HtmlInlineProcessor;
import io.noties.markwon.inlineparser.MarkwonInlineParserPlugin;
import io.noties.markwon.syntax.Prism4jThemeDefault;
import io.noties.markwon.syntax.SyntaxHighlightPlugin;
import io.noties.prism4j.Prism4j;
public class SpannableHelper { public class SpannableHelper {
public static final String CLICKABLE_SPAN = "CLICKABLE_SPAN"; public static final String CLICKABLE_SPAN = "CLICKABLE_SPAN";
private static int linkColor; private static int linkColor;
private static boolean underlineLinks;
private static final String patternBottomTags = "\\n{2,}((#[\\w_À-ú-]+)(\\s*| *))+$"; public static Spannable convert(Context context, String text,
Status status, Account account, Announcement announcement, WeakReference<View> viewWeakReference) {
public static String[] hasBottomTags(String text) { return convert(context, text, status, account, announcement, viewWeakReference, null, true);
if (text == null) {
return new String[]{};
}
SpannableString initialContent;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
initialContent = new SpannableString(Html.fromHtml(text, Html.FROM_HTML_MODE_LEGACY));
} else {
initialContent = new SpannableString(Html.fromHtml(text));
}
final Pattern bottomTagsPattern = Pattern.compile(patternBottomTags, Pattern.CASE_INSENSITIVE);
Matcher matcherBottomTags = bottomTagsPattern.matcher(initialContent);
String[] tags = new String[]{};
while (matcherBottomTags.find()) {
String stringTags = Objects.requireNonNull(matcherBottomTags.group()).trim();
tags = stringTags.split("\\s");
}
return tags;
} }
public static Spannable convert(Context context, String text, public static Spannable convert(Context context, String text,
Status status, Account account, Announcement announcement, Status status, Account account, Announcement announcement,
WeakReference<View> viewWeakReference, Status.Callback callback, boolean convertHtml, boolean convertMarkdown) { WeakReference<View> viewWeakReference, Status.Callback callback) {
return convert(context, text, status, account, announcement, false, viewWeakReference, callback, convertHtml, convertMarkdown); return convert(context, text, status, account, announcement, viewWeakReference, callback, true);
} }
public static Spannable convert(Context context, String text, public static Spannable convert(Context context, String text,
Status status, Account account, Announcement announcement, boolean checkRemotely, Status status, Account account, Announcement announcement,
WeakReference<View> viewWeakReference, Status.Callback callback, boolean convertHtml, boolean convertMarkdown) { WeakReference<View> viewWeakReference, Status.Callback callback, boolean convertHtml) {
if (text == null) { if (text == null) {
return null; return null;
} }
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context); 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 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); 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; int link_color;
if (currentNightMode == Configuration.UI_MODE_NIGHT_NO && customLight) { if (currentNightMode == Configuration.UI_MODE_NIGHT_NO && customLight) {
link_color = sharedpreferences.getInt(context.getString(R.string.SET_LIGHT_LINK), -1); link_color = sharedpreferences.getInt(context.getString(R.string.SET_LIGHT_LINK), -1);
@ -162,18 +131,11 @@ public class SpannableHelper {
if (linkColor == 0) { if (linkColor == 0) {
linkColor = -1; linkColor = -1;
} }
if (status != null && status.underlined) {
linkColor = -1;
}
List<Mention> mentions = new ArrayList<>(); List<Mention> mentions = new ArrayList<>();
if (status != null && status.mentions != null) { if (status != null && status.mentions != null) {
mentions.addAll(status.mentions); mentions.addAll(status.mentions);
} }
boolean markdownSupport = sharedpreferences.getBoolean(context.getString(R.string.SET_MARKDOWN_SUPPORT), false);
if(!markdownSupport) {
text = text.replaceAll("((<\\s?p\\s?>|<\\s?br\\s?/?>)&gt;(((?!(<\\s?br\\s?/?>|<\\s?/s?p\\s?>)).)*))", "$2<blockquote>$3</blockquote>"); text = text.replaceAll("((<\\s?p\\s?>|<\\s?br\\s?/?>)&gt;(((?!(<\\s?br\\s?/?>|<\\s?/s?p\\s?>)).)*))", "$2<blockquote>$3</blockquote>");
}
text = text.trim().replaceAll("\\s{3}", "&nbsp;&nbsp;&nbsp;"); text = text.trim().replaceAll("\\s{3}", "&nbsp;&nbsp;&nbsp;");
text = text.trim().replaceAll("\\s{2}", "&nbsp;&nbsp;"); text = text.trim().replaceAll("\\s{2}", "&nbsp;&nbsp;");
SpannableString initialContent; SpannableString initialContent;
@ -186,73 +148,8 @@ public class SpannableHelper {
initialContent = new SpannableString(text); initialContent = new SpannableString(text);
} }
//Get all links //Get all links
SpannableStringBuilder content; SpannableStringBuilder content = new SpannableStringBuilder(initialContent);
if (markdownSupport && convertMarkdown) {
MarkdownConverter markdownConverter = new MarkdownConverter();
markdownConverter.markdownItems = new ArrayList<>();
int next;
int position = 0;
for (int i = 0; i < initialContent.length(); i = next) {
// find the next span transition
next = initialContent.nextSpanTransition(i, initialContent.length(), URLSpan.class);
MarkdownConverter.MarkdownItem markdownItem = new MarkdownConverter.MarkdownItem();
markdownItem.code = initialContent.subSequence(i, next).toString();
markdownItem.position = position;
// get all spans in this range
URLSpan[] spans = initialContent.getSpans(i, next, URLSpan.class);
if (spans != null && spans.length > 0) {
markdownItem.urlSpan = spans[0];
}
if (!markdownItem.code.trim().isEmpty()) {
markdownConverter.markdownItems.add(markdownItem);
position++;
}
}
final Markwon markwon = Markwon.builder(context)
.usePlugin(TablePlugin.create(context))
.usePlugin(SoftBreakAddsNewLinePlugin.create())
.usePlugin(SyntaxHighlightPlugin.create(new Prism4j(new MySuperGrammerLocator()), Prism4jThemeDefault.create()))
.usePlugin(StrikethroughPlugin.create())
.usePlugin(MarkwonInlineParserPlugin.create())
.usePlugin(new AbstractMarkwonPlugin() {
@Override
public void configure(@NonNull Registry registry) {
registry.require(MarkwonInlineParserPlugin.class, plugin -> plugin.factoryBuilder()
.excludeInlineProcessor(HtmlInlineProcessor.class));
}
})
.build();
final Spanned markdown = markwon.toMarkdown(initialContent.toString());
content = new SpannableStringBuilder(markdown);
position = 0;
for (MarkdownConverter.MarkdownItem markdownItem : markdownConverter.markdownItems) {
String sb = Pattern.compile("\\A[\\p{L}0-9_]").matcher(markdownItem.code.trim()).find() ? "\\b" : "";
String eb = Pattern.compile("[\\p{L}0-9_]\\z").matcher(markdownItem.code.trim()).find() ? "\\b" : "\\B";
Pattern p = Pattern.compile(sb + "(" + Pattern.quote(markdownItem.code.trim()) + ")" + eb, Pattern.UNICODE_CASE);
Matcher m = p.matcher(content);
int fetchPosition = 1;
while (m.find()) {
int regexPosition = markdownItem.regexPosition(markdownConverter.markdownItems);
if (regexPosition == fetchPosition) {
content.setSpan(markdownItem.urlSpan, m.start(), m.end(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
}
fetchPosition++;
}
position++;
}
} else {
content = new SpannableStringBuilder(initialContent);
}
URLSpan[] urls = content.getSpans(0, (content.length() - 1), URLSpan.class); URLSpan[] urls = content.getSpans(0, (content.length() - 1), URLSpan.class);
//Loop through links //Loop through links
for (URLSpan span : urls) { for (URLSpan span : urls) {
@ -262,7 +159,6 @@ public class SpannableHelper {
if (start < 0 || end > content.length()) { if (start < 0 || end > content.length()) {
continue; continue;
} }
boolean sameContent = (content.subSequence(start,end).toString().trim().equals(url.trim()));
content.removeSpan(span); content.removeSpan(span);
//Get the matching word associated to the URL //Get the matching word associated to the URL
String word = content.subSequence(start, end).toString(); String word = content.subSequence(start, end).toString();
@ -312,59 +208,40 @@ public class SpannableHelper {
public void onClick(@NonNull View textView) { public void onClick(@NonNull View textView) {
textView.setTag(CLICKABLE_SPAN); textView.setTag(CLICKABLE_SPAN);
Intent intent; Intent intent;
Bundle args; Bundle b;
if (word.startsWith("#")) { if (word.startsWith("#")) {
intent = new Intent(context, HashTagActivity.class); intent = new Intent(context, HashTagActivity.class);
args = new Bundle(); b = new Bundle();
args.putString(Helper.ARG_SEARCH_KEYWORD, word.trim()); b.putString(Helper.ARG_SEARCH_KEYWORD, word.trim());
new CachedBundle(context).insertBundle(args, Helper.getCurrentAccount(context), bundleId -> { intent.putExtras(b);
Bundle bundle = new Bundle();
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intent.putExtras(bundle);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent); context.startActivity(intent);
});
} else if (word.startsWith("@")) { } else if (word.startsWith("@")) {
intent = new Intent(context, ProfileActivity.class); intent = new Intent(context, ProfileActivity.class);
args = new Bundle(); b = new Bundle();
Mention targetedMention = null; Mention targetedMention = null;
String acct = null;
for (Mention mention : mentions) { for (Mention mention : mentions) {
if (word.compareToIgnoreCase("@" + mention.username) == 0) { if (word.compareToIgnoreCase("@" + mention.username) == 0) {
if (!checkRemotely) {
targetedMention = mention; targetedMention = mention;
} else {
acct = mention.acct;
}
break; break;
} }
} }
if (targetedMention != null) { if (targetedMention != null) {
args.putString(Helper.ARG_USER_ID, targetedMention.id); b.putString(Helper.ARG_USER_ID, targetedMention.id);
} else if (acct != null) {
args.putString(Helper.ARG_MENTION, acct);
} else { } else {
args.putString(Helper.ARG_MENTION, word); b.putString(Helper.ARG_MENTION, word);
} }
new CachedBundle(context).insertBundle(args, Helper.getCurrentAccount(context), bundleId -> { intent.putExtras(b);
Bundle bundle = new Bundle();
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intent.putExtras(bundle);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent); context.startActivity(intent);
});
} }
} }
@Override @Override
public void updateDrawState(@NonNull TextPaint ds) { public void updateDrawState(@NonNull TextPaint ds) {
super.updateDrawState(ds); super.updateDrawState(ds);
if (!underlineLinks) { ds.setUnderlineText(false);
ds.setUnderlineText(status != null && status.underlined);
}
if (linkColor != -1) { if (linkColor != -1) {
ds.setColor(linkColor); ds.setColor(linkColor);
} }
@ -372,11 +249,11 @@ public class SpannableHelper {
}, start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); }, start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
} else { } else {
makeLinks(context, status, content, url, start, end, sameContent); makeLinks(context, content, url, start, end);
}
} }
replaceQuoteSpans(context, content); replaceQuoteSpans(context, content);
emails(context, content, status); emails(context, content);
}
Pattern imgPattern = Pattern.compile("<img [^>]*src=\"([^\"]+)\"[^>]*>"); Pattern imgPattern = Pattern.compile("<img [^>]*src=\"([^\"]+)\"[^>]*>");
Matcher matcherImg = imgPattern.matcher(text); Matcher matcherImg = imgPattern.matcher(text);
@ -402,7 +279,7 @@ public class SpannableHelper {
CustomEmoji customEmoji = new CustomEmoji(new WeakReference<>(view)); CustomEmoji customEmoji = new CustomEmoji(new WeakReference<>(view));
content = customEmoji.makeEmoji(content, emojiList, animate, callback); content = customEmoji.makeEmoji(content, emojiList, animate, callback);
if (!imagesToReplace.isEmpty()) { if (imagesToReplace.size() > 0) {
for (Map.Entry<String, String> entry : imagesToReplace.entrySet()) { for (Map.Entry<String, String> entry : imagesToReplace.entrySet()) {
String key = entry.getKey(); String key = entry.getKey();
String url = entry.getValue(); String url = entry.getValue();
@ -417,33 +294,19 @@ public class SpannableHelper {
} }
} }
}
boolean underlineBottomHashTags = sharedpreferences.getBoolean(context.getString(R.string.SET_UNDERLINE_BOTTOM_HASHTAGS), true);
if(underlineBottomHashTags) {
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder();
final Pattern bottomTagsPattern = Pattern.compile(patternBottomTags, Pattern.CASE_INSENSITIVE);
Matcher matcherBottomTags = bottomTagsPattern.matcher(content);
int length = 0;
while (matcherBottomTags.find()) {
length = Objects.requireNonNull(matcherBottomTags.group()).length();
}
spannableStringBuilder.append(content,0, content.length()-length);
return trimSpannable(spannableStringBuilder);
} }
return trimSpannable(new SpannableStringBuilder(content)); return trimSpannable(new SpannableStringBuilder(content));
} }
private static void makeLinks(Context context, Status status, SpannableStringBuilder content, String url, int start, int end, boolean sameContent) { private static void makeLinks(Context context, SpannableStringBuilder content, String url, int start, int end) {
String newUrl = url; String newUrl = url;
boolean validUrl = URLUtil.isValidUrl(url) && sameContent; boolean validUrl = URLUtil.isValidUrl(url) && url.length() == (end - start);
if (validUrl) { if (validUrl) {
newUrl = Helper.transformURL(context, url); newUrl = Helper.transformURL(context, url);
} }
//If URL has been transformed //If URL has been transformed
if (validUrl && newUrl.compareTo(url) != 0) { if (validUrl && newUrl.compareTo(url) != 0) {
content.replace(start, end, newUrl); content.replace(start, end, newUrl);
@ -662,23 +525,6 @@ public class SpannableHelper {
public void onClick(@NonNull View textView) { public void onClick(@NonNull View textView) {
textView.setTag(CLICKABLE_SPAN); textView.setTag(CLICKABLE_SPAN);
linkClickAction(context, finalUrl);
}
@Override
public void updateDrawState(@NonNull TextPaint ds) {
super.updateDrawState(ds);
if (!underlineLinks) {
ds.setUnderlineText(status != null && status.underlined);
}
if (linkColor != -1) {
ds.setColor(linkColor);
}
}
}, start, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
}
public static void linkClickAction(Context context, String finalUrl) {
Pattern link = Pattern.compile("https?://([\\da-z.-]+\\.[a-z.]{2,10})/(@[\\w._-]*[0-9]*)(/[0-9]+)?$"); Pattern link = Pattern.compile("https?://([\\da-z.-]+\\.[a-z.]{2,10})/(@[\\w._-]*[0-9]*)(/[0-9]+)?$");
Matcher matcherLink = link.matcher(finalUrl); Matcher matcherLink = link.matcher(finalUrl);
Pattern linkLong = Pattern.compile("https?://([\\da-z.-]+\\.[a-z.]{2,10})/(@[\\w_.-]+@[a-zA-Z0-9][a-zA-Z0-9.-]{1,61}[a-zA-Z0-9](?:\\.[a-zA-Z]{2,})+)(/[0-9]+)?$"); Pattern linkLong = Pattern.compile("https?://([\\da-z.-]+\\.[a-z.]{2,10})/(@[\\w_.-]+@[a-zA-Z0-9][a-zA-Z0-9.-]{1,61}[a-zA-Z0-9](?:\\.[a-zA-Z]{2,})+)(/[0-9]+)?$");
@ -686,20 +532,14 @@ public class SpannableHelper {
Pattern userWithoutAt = Pattern.compile("https?://([\\da-z.-]+\\.[a-z.]{2,10})/(users/([\\w._-]*[0-9]*))/statuses/([0-9]+)"); Pattern userWithoutAt = Pattern.compile("https?://([\\da-z.-]+\\.[a-z.]{2,10})/(users/([\\w._-]*[0-9]*))/statuses/([0-9]+)");
Matcher matcherUserWithoutAt = userWithoutAt.matcher(finalUrl); Matcher matcherUserWithoutAt = userWithoutAt.matcher(finalUrl);
if (matcherLink.find() && !finalUrl.contains("medium.com")) { if (matcherLink.find() && !finalUrl.contains("medium.com")) {
if (matcherLink.group(3) != null && !Objects.requireNonNull(matcherLink.group(3)).isEmpty()) { //It's a toot if (matcherLink.group(3) != null && Objects.requireNonNull(matcherLink.group(3)).length() > 0) { //It's a toot
CrossActionHelper.fetchRemoteStatus(context, Helper.getCurrentAccount(context), finalUrl, new CrossActionHelper.Callback() { CrossActionHelper.fetchRemoteStatus(context, currentAccount, finalUrl, new CrossActionHelper.Callback() {
@Override @Override
public void federatedStatus(Status status) { public void federatedStatus(Status status) {
Intent intent = new Intent(context, ContextActivity.class); Intent intent = new Intent(context, ContextActivity.class);
Bundle args = new Bundle(); intent.putExtra(Helper.ARG_STATUS, status);
args.putSerializable(Helper.ARG_STATUS, status);
new CachedBundle(context).insertBundle(args, Helper.getCurrentAccount(context), bundleId -> {
Bundle bundle = new Bundle();
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intent.putExtras(bundle);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent); context.startActivity(intent);
});
} }
@Override @Override
@ -707,7 +547,7 @@ public class SpannableHelper {
} }
}); });
} else {//It's an account } else {//It's an account
CrossActionHelper.fetchRemoteAccount(context, Helper.getCurrentAccount(context), matcherLink.group(2) + "@" + matcherLink.group(1), new CrossActionHelper.Callback() { CrossActionHelper.fetchRemoteAccount(context, currentAccount, matcherLink.group(2) + "@" + matcherLink.group(1), new CrossActionHelper.Callback() {
@Override @Override
public void federatedStatus(Status status) { public void federatedStatus(Status status) {
} }
@ -715,33 +555,23 @@ public class SpannableHelper {
@Override @Override
public void federatedAccount(Account account) { public void federatedAccount(Account account) {
Intent intent = new Intent(context, ProfileActivity.class); Intent intent = new Intent(context, ProfileActivity.class);
Bundle args = new Bundle(); Bundle b = new Bundle();
args.putSerializable(Helper.ARG_ACCOUNT, account); b.putSerializable(Helper.ARG_ACCOUNT, account);
new CachedBundle(context).insertBundle(args, Helper.getCurrentAccount(context), bundleId -> { intent.putExtras(b);
Bundle bundle = new Bundle();
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intent.putExtras(bundle);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent); context.startActivity(intent);
});
} }
}); });
} }
} else if (matcherLinkLong.find() && !finalUrl.contains("medium.com")) { } else if (matcherLinkLong.find() && !finalUrl.contains("medium.com")) {
if (matcherLinkLong.group(3) != null && !Objects.requireNonNull(matcherLinkLong.group(3)).isEmpty()) { //It's a toot if (matcherLinkLong.group(3) != null && Objects.requireNonNull(matcherLinkLong.group(3)).length() > 0) { //It's a toot
CrossActionHelper.fetchRemoteStatus(context, Helper.getCurrentAccount(context), finalUrl, new CrossActionHelper.Callback() { CrossActionHelper.fetchRemoteStatus(context, currentAccount, finalUrl, new CrossActionHelper.Callback() {
@Override @Override
public void federatedStatus(Status status) { public void federatedStatus(Status status) {
Intent intent = new Intent(context, ContextActivity.class); Intent intent = new Intent(context, ContextActivity.class);
Bundle args = new Bundle(); intent.putExtra(Helper.ARG_STATUS, status);
args.putSerializable(Helper.ARG_STATUS, status);
new CachedBundle(context).insertBundle(args, Helper.getCurrentAccount(context), bundleId -> {
Bundle bundle = new Bundle();
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intent.putExtras(bundle);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent); context.startActivity(intent);
});
} }
@Override @Override
@ -749,7 +579,7 @@ public class SpannableHelper {
} }
}); });
} else if (matcherLinkLong.group(2) != null) {//It's an account } else if (matcherLinkLong.group(2) != null) {//It's an account
CrossActionHelper.fetchRemoteAccount(context, Helper.getCurrentAccount(context), matcherLinkLong.group(2), new CrossActionHelper.Callback() { CrossActionHelper.fetchRemoteAccount(context, currentAccount, matcherLinkLong.group(2), new CrossActionHelper.Callback() {
@Override @Override
public void federatedStatus(Status status) { public void federatedStatus(Status status) {
} }
@ -757,33 +587,23 @@ public class SpannableHelper {
@Override @Override
public void federatedAccount(Account account) { public void federatedAccount(Account account) {
Intent intent = new Intent(context, ProfileActivity.class); Intent intent = new Intent(context, ProfileActivity.class);
Bundle args = new Bundle(); Bundle b = new Bundle();
args.putSerializable(Helper.ARG_ACCOUNT, account); b.putSerializable(Helper.ARG_ACCOUNT, account);
new CachedBundle(context).insertBundle(args, Helper.getCurrentAccount(context), bundleId -> { intent.putExtras(b);
Bundle bundle = new Bundle();
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intent.putExtras(bundle);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent); context.startActivity(intent);
});
} }
}); });
} }
} else if (matcherUserWithoutAt.find() && !finalUrl.contains("medium.com")) { } else if (matcherUserWithoutAt.find() && !finalUrl.contains("medium.com")) {
if (matcherUserWithoutAt.group(4) != null && !Objects.requireNonNull(matcherUserWithoutAt.group(4)).isEmpty()) { //It's a toot if (matcherUserWithoutAt.group(4) != null && Objects.requireNonNull(matcherUserWithoutAt.group(4)).length() > 0) { //It's a toot
CrossActionHelper.fetchRemoteStatus(context, Helper.getCurrentAccount(context), finalUrl, new CrossActionHelper.Callback() { CrossActionHelper.fetchRemoteStatus(context, currentAccount, finalUrl, new CrossActionHelper.Callback() {
@Override @Override
public void federatedStatus(Status status) { public void federatedStatus(Status status) {
Intent intent = new Intent(context, ContextActivity.class); Intent intent = new Intent(context, ContextActivity.class);
Bundle args = new Bundle(); intent.putExtra(Helper.ARG_STATUS, status);
args.putSerializable(Helper.ARG_STATUS, status);
new CachedBundle(context).insertBundle(args, Helper.getCurrentAccount(context), bundleId -> {
Bundle bundle = new Bundle();
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intent.putExtras(bundle);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent); context.startActivity(intent);
});
} }
@Override @Override
@ -791,7 +611,7 @@ public class SpannableHelper {
} }
}); });
} else {//It's an account } else {//It's an account
CrossActionHelper.fetchRemoteAccount(context, Helper.getCurrentAccount(context), matcherUserWithoutAt.group(3) + "@" + matcherUserWithoutAt.group(1), new CrossActionHelper.Callback() { CrossActionHelper.fetchRemoteAccount(context, currentAccount, matcherUserWithoutAt.group(3) + "@" + matcherUserWithoutAt.group(1), new CrossActionHelper.Callback() {
@Override @Override
public void federatedStatus(Status status) { public void federatedStatus(Status status) {
} }
@ -799,15 +619,11 @@ public class SpannableHelper {
@Override @Override
public void federatedAccount(Account account) { public void federatedAccount(Account account) {
Intent intent = new Intent(context, ProfileActivity.class); Intent intent = new Intent(context, ProfileActivity.class);
Bundle args = new Bundle(); Bundle b = new Bundle();
args.putSerializable(Helper.ARG_ACCOUNT, account); b.putSerializable(Helper.ARG_ACCOUNT, account);
new CachedBundle(context).insertBundle(args, Helper.getCurrentAccount(context), bundleId -> { intent.putExtras(b);
Bundle bundle = new Bundle();
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intent.putExtras(bundle);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent); context.startActivity(intent);
});
} }
}); });
} }
@ -816,7 +632,19 @@ public class SpannableHelper {
} }
} }
private static void emails(Context context, Spannable content, Status status) { @Override
public void updateDrawState(@NonNull TextPaint ds) {
super.updateDrawState(ds);
ds.setUnderlineText(false);
if (linkColor != -1) {
ds.setColor(linkColor);
}
}
}, start, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
}
private static void emails(Context context, Spannable content) {
// --- For all patterns defined in Helper class --- // --- For all patterns defined in Helper class ---
Pattern pattern = Helper.emailPattern; Pattern pattern = Helper.emailPattern;
Matcher matcher = pattern.matcher(content); Matcher matcher = pattern.matcher(content);
@ -845,9 +673,7 @@ public class SpannableHelper {
@Override @Override
public void updateDrawState(@NonNull TextPaint ds) { public void updateDrawState(@NonNull TextPaint ds) {
super.updateDrawState(ds); super.updateDrawState(ds);
if (!underlineLinks) { ds.setUnderlineText(false);
ds.setUnderlineText(status != null && status.underlined);
}
if (linkColor != -1) { if (linkColor != -1) {
ds.setColor(linkColor); ds.setColor(linkColor);
} }
@ -985,22 +811,16 @@ public class SpannableHelper {
@Override @Override
public void onClick(@NonNull View textView) { public void onClick(@NonNull View textView) {
Intent intent = new Intent(context, ProfileActivity.class); Intent intent = new Intent(context, ProfileActivity.class);
Bundle args = new Bundle(); Bundle b = new Bundle();
args.putSerializable(Helper.ARG_ACCOUNT, account.moved); b.putSerializable(Helper.ARG_ACCOUNT, account.moved);
new CachedBundle(context).insertBundle(args, Helper.getCurrentAccount(context), bundleId -> { intent.putExtras(b);
Bundle bundle = new Bundle();
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
intent.putExtras(bundle);
context.startActivity(intent); context.startActivity(intent);
});
} }
@Override @Override
public void updateDrawState(@NonNull TextPaint ds) { public void updateDrawState(@NonNull TextPaint ds) {
super.updateDrawState(ds); super.updateDrawState(ds);
if (!underlineLinks) {
ds.setUnderlineText(false); ds.setUnderlineText(false);
}
if (linkColor != -1) { if (linkColor != -1) {
ds.setColor(linkColor); ds.setColor(linkColor);
} }

View file

@ -15,9 +15,6 @@ package app.fedilab.android.mastodon.helper;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import static android.content.Context.WINDOW_SERVICE; 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; import android.app.Activity;
import android.content.Context; import android.content.Context;
@ -26,8 +23,6 @@ import android.content.res.ColorStateList;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.content.res.Resources; import android.content.res.Resources;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.os.Build; import android.os.Build;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.TypedValue; import android.util.TypedValue;
@ -42,9 +37,6 @@ import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import com.google.android.material.color.DynamicColors;
import com.google.android.material.color.DynamicColorsOptions;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -176,11 +168,13 @@ public class ThemeHelper {
String[] list; String[] list;
try { try {
list = context.getAssets().list("themes/contributors"); list = context.getAssets().list("themes/contributors");
if (list.length > 0) {
for (String file : list) { for (String file : list) {
InputStream is = context.getAssets().open("themes/contributors/" + file); InputStream is = context.getAssets().open("themes/contributors/" + file);
LinkedHashMap<String, String> data = readCSVFile(is); LinkedHashMap<String, String> data = readCSVFile(is);
linkedHashMaps.add(data); linkedHashMaps.add(data);
} }
}
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -273,7 +267,7 @@ public class ThemeHelper {
public static void switchTo(String themePref) { public static void switchTo(String themePref) {
if (themes.LIGHT.name().equals(themePref) || themes.SOLARIZED_LIGHT.name().equals(themePref)) { if (themes.LIGHT.name().equals(themePref) || themes.SOLARIZED_LIGHT.name().equals(themePref)) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
} else if (themes.DARK.name().equals(themePref) || themes.SOLARIZED_DARK.name().equals(themePref) || themes.DRACULA.name().equals(themePref) || themes.BLACK.name().equals(themePref)) { } else if (themes.DARK.name().equals(themePref) || themes.SOLARIZED_DARK.name().equals(themePref)) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
} else { } else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
@ -284,36 +278,9 @@ public class ThemeHelper {
} }
} }
public static void applyThemeColor(Activity activity) {
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);
int customAccentLight = sharedpreferences.getInt(activity.getString(R.string.SET_CUSTOM_ACCENT_LIGHT_VALUE) + currentUserID + currentInstance, -1);
int customAccentDark = sharedpreferences.getInt(activity.getString(R.string.SET_CUSTOM_ACCENT_DARK_VALUE) + currentUserID + currentInstance, -1);
if (customAccentEnabled) {
Bitmap bmp = Bitmap.createBitmap(50, 50, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bmp);
//Light theme enabled
if (currentNightMode == Configuration.UI_MODE_NIGHT_NO && customAccentLight != -1) {
canvas.drawColor(customAccentLight);
} else if (customAccentDark != -1) {
canvas.drawColor(customAccentDark);
}
DynamicColorsOptions.Builder builder = new DynamicColorsOptions.Builder();
builder.setContentBasedSource(bmp);
builder.setThemeOverlay(R.style.ThemeOverlay_Material3_DynamicColors_DayNight);
DynamicColorsOptions dynamicColorsOptions = builder.build();
DynamicColors.applyToActivityIfAvailable(activity, dynamicColorsOptions);
} else if (dynamicColor) {
DynamicColors.applyToActivityIfAvailable(activity);
}
}
public enum themes { public enum themes {
LIGHT, LIGHT,
DARK, DARK,
BLACK,
DRACULA,
SYSTEM, SYSTEM,
SOLARIZED_LIGHT, SOLARIZED_LIGHT,
SOLARIZED_DARK SOLARIZED_DARK

View file

@ -29,10 +29,10 @@ import androidx.lifecycle.ViewModelStoreOwner;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import java.io.IOException; import java.io.IOException;
import java.net.IDN;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -41,7 +41,6 @@ import app.fedilab.android.R;
import app.fedilab.android.activities.MainActivity; import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.mastodon.client.endpoints.MastodonFiltersService; import app.fedilab.android.mastodon.client.endpoints.MastodonFiltersService;
import app.fedilab.android.mastodon.client.entities.api.Account; 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.Filter; import app.fedilab.android.mastodon.client.entities.api.Filter;
import app.fedilab.android.mastodon.client.entities.api.Notification; import app.fedilab.android.mastodon.client.entities.api.Notification;
import app.fedilab.android.mastodon.client.entities.api.Status; import app.fedilab.android.mastodon.client.entities.api.Status;
@ -56,9 +55,13 @@ import retrofit2.converter.gson.GsonConverterFactory;
public class TimelineHelper { public class TimelineHelper {
private static MastodonFiltersService initv2(Context context) { private static MastodonFiltersService initv2(Context context) {
OkHttpClient okHttpClient = Helper.myOkHttpClient(context); OkHttpClient okHttpClient = new OkHttpClient.Builder()
.readTimeout(60, TimeUnit.SECONDS)
.connectTimeout(60, TimeUnit.SECONDS)
.proxy(Helper.getProxy(context))
.build();
Retrofit retrofit = new Retrofit.Builder() Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://" + (MainActivity.currentInstance != null ? IDN.toASCII(MainActivity.currentInstance, IDN.ALLOW_UNASSIGNED) : null) + "/api/v2/") .baseUrl("https://" + BaseMainActivity.currentInstance + "/api/v2/")
.addConverterFactory(GsonConverterFactory.create()) .addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient) .client(okHttpClient)
.build(); .build();
@ -76,7 +79,7 @@ public class TimelineHelper {
*/ */
public static List<Status> filterStatus(Context context, List<Status> statuses, Timeline.TimeLineEnum filterTimeLineType) { public static List<Status> filterStatus(Context context, List<Status> statuses, Timeline.TimeLineEnum filterTimeLineType) {
//A security to make sure filters have been fetched before displaying messages //A security to make sure filters have been fetched before displaying messages
if (!BaseMainActivity.filterFetched && BaseMainActivity.filterFetchedRetry < 3) { if (!BaseMainActivity.filterFetched) {
MastodonFiltersService mastodonFiltersService = initv2(context); MastodonFiltersService mastodonFiltersService = initv2(context);
List<Filter> filterList; List<Filter> filterList;
Call<List<Filter>> getFiltersCall = mastodonFiltersService.getFilters(BaseMainActivity.currentToken); Call<List<Filter>> getFiltersCall = mastodonFiltersService.getFilters(BaseMainActivity.currentToken);
@ -92,11 +95,10 @@ public class TimelineHelper {
e.printStackTrace(); e.printStackTrace();
} }
} }
BaseMainActivity.filterFetchedRetry++;
} }
//If there are filters: //If there are filters:
if (BaseMainActivity.mainFilters != null && !BaseMainActivity.mainFilters.isEmpty() && statuses != null && !statuses.isEmpty()) { if (BaseMainActivity.mainFilters != null && BaseMainActivity.mainFilters.size() > 0 && statuses != null && statuses.size() > 0) {
//Loop through filters //Loop through filters
for (Filter filter : BaseMainActivity.mainFilters) { for (Filter filter : BaseMainActivity.mainFilters) {
@ -115,7 +117,7 @@ public class TimelineHelper {
} else { } else {
if (!filter.context.contains("public")) continue; if (!filter.context.contains("public")) continue;
} }
if (filter.keywords != null && !filter.keywords.isEmpty()) { if (filter.keywords != null && filter.keywords.size() > 0) {
for (Filter.KeywordsAttributes filterKeyword : filter.keywords) { for (Filter.KeywordsAttributes filterKeyword : filter.keywords) {
String sb = Pattern.compile("\\A[A-Za-z0-9_]").matcher(filterKeyword.keyword).find() ? "\\b" : ""; String sb = Pattern.compile("\\A[A-Za-z0-9_]").matcher(filterKeyword.keyword).find() ? "\\b" : "";
String eb = Pattern.compile("[A-Za-z0-9_]\\z").matcher(filterKeyword.keyword).find() ? "\\b" : ""; String eb = Pattern.compile("[A-Za-z0-9_]\\z").matcher(filterKeyword.keyword).find() ? "\\b" : "";
@ -130,14 +132,10 @@ public class TimelineHelper {
continue; continue;
} }
String content; String content;
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
content = Html.fromHtml(status.reblog != null ? status.reblog.content : status.content, Html.FROM_HTML_MODE_LEGACY).toString(); content = Html.fromHtml(status.reblog != null ? status.reblog.content : status.content, Html.FROM_HTML_MODE_LEGACY).toString();
else else
content = Html.fromHtml(status.reblog != null ? status.reblog.content : status.content).toString(); content = Html.fromHtml(status.reblog != null ? status.reblog.content : status.content).toString();
} catch (Exception e) {
content = status.reblog != null ? status.reblog.content : status.content;
}
Matcher m = p.matcher(content); Matcher m = p.matcher(content);
if (m.find()) { if (m.find()) {
status.filteredByApp = filter; status.filteredByApp = filter;
@ -152,17 +150,6 @@ public class TimelineHelper {
Matcher ms = p.matcher(spoilerText); Matcher ms = p.matcher(spoilerText);
if (ms.find()) { if (ms.find()) {
status.filteredByApp = filter; status.filteredByApp = filter;
continue;
}
}
List<Attachment> mediaAttachments = status.reblog != null ? status.reblog.media_attachments : status.media_attachments;
if(mediaAttachments != null && !mediaAttachments.isEmpty()) {
for(Attachment attachment : mediaAttachments) {
if(attachment.description != null) {
Matcher ms = p.matcher(attachment.description );
if (ms.find()) {
status.filteredByApp = filter;
break;
} }
} }
} }
@ -170,15 +157,13 @@ public class TimelineHelper {
} }
} }
} }
} if (statuses != null && statuses.size() > 0) {
}
if (statuses != null && !statuses.isEmpty()) {
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
boolean groupReblogs = sharedpreferences.getBoolean(context.getString(R.string.SET_GROUP_REBLOGS), true); boolean groupReblogs = sharedpreferences.getBoolean(context.getString(R.string.SET_GROUP_REBLOGS), true);
if (filterTimeLineType == Timeline.TimeLineEnum.HOME) { if (filterTimeLineType == Timeline.TimeLineEnum.HOME) {
for (int i = 0; i < statuses.size(); i++) { for (int i = 0; i < statuses.size(); i++) {
if (filteredAccounts != null && !filteredAccounts.isEmpty()) { if (filteredAccounts != null && filteredAccounts.size() > 0) {
for (Account account : filteredAccounts) { for (Account account : filteredAccounts) {
if (account.acct.equals(statuses.get(i).account.acct) || (statuses.get(i).reblog != null && account.acct.equals(statuses.get(i).reblog.account.acct))) { if (account.acct.equals(statuses.get(i).account.acct) || (statuses.get(i).reblog != null && account.acct.equals(statuses.get(i).reblog.account.acct))) {
Filter filterCustom = new Filter(); Filter filterCustom = new Filter();
@ -223,7 +208,7 @@ public class TimelineHelper {
public static List<Notification> filterNotification(Context context, List<Notification> notifications) { public static List<Notification> filterNotification(Context context, List<Notification> notifications) {
//A security to make sure filters have been fetched before displaying messages //A security to make sure filters have been fetched before displaying messages
List<Notification> notificationToRemove = new ArrayList<>(); List<Notification> notificationToRemove = new ArrayList<>();
if (!BaseMainActivity.filterFetched && BaseMainActivity.filterFetchedRetry < 3) { if (!BaseMainActivity.filterFetched) {
try { try {
FiltersVM filtersVM = new ViewModelProvider((ViewModelStoreOwner) context).get(FiltersVM.class); FiltersVM filtersVM = new ViewModelProvider((ViewModelStoreOwner) context).get(FiltersVM.class);
filtersVM.getFilters(BaseMainActivity.currentInstance, BaseMainActivity.currentToken).observe((LifecycleOwner) context, filters -> { filtersVM.getFilters(BaseMainActivity.currentInstance, BaseMainActivity.currentToken).observe((LifecycleOwner) context, filters -> {
@ -233,10 +218,9 @@ public class TimelineHelper {
} catch (Exception e) { } catch (Exception e) {
return notifications; return notifications;
} }
BaseMainActivity.filterFetchedRetry++;
} }
//If there are filters: //If there are filters:
if (BaseMainActivity.mainFilters != null && !BaseMainActivity.mainFilters.isEmpty() && notifications != null && !notifications.isEmpty()) { if (BaseMainActivity.mainFilters != null && BaseMainActivity.mainFilters.size() > 0 && notifications != null && notifications.size() > 0) {
//Loop through filters //Loop through filters
for (Filter filter : BaseMainActivity.mainFilters) { for (Filter filter : BaseMainActivity.mainFilters) {
@ -247,7 +231,7 @@ public class TimelineHelper {
} }
if (!filter.context.contains("notifications")) continue; if (!filter.context.contains("notifications")) continue;
if (filter.keywords != null && !filter.keywords.isEmpty()) { if (filter.keywords != null && filter.keywords.size() > 0) {
for (Filter.KeywordsAttributes filterKeyword : filter.keywords) { for (Filter.KeywordsAttributes filterKeyword : filter.keywords) {
String sb = Pattern.compile("\\A[A-Za-z0-9_]").matcher(filterKeyword.keyword).find() ? "\\b" : ""; String sb = Pattern.compile("\\A[A-Za-z0-9_]").matcher(filterKeyword.keyword).find() ? "\\b" : "";
String eb = Pattern.compile("[A-Za-z0-9_]\\z").matcher(filterKeyword.keyword).find() ? "\\b" : ""; String eb = Pattern.compile("[A-Za-z0-9_]\\z").matcher(filterKeyword.keyword).find() ? "\\b" : "";

View file

@ -38,7 +38,7 @@ import es.dmoral.toasty.Toasty;
public class TranslateHelper { public class TranslateHelper {
public static void translate(Context context, String toTranslate, String languageSource, Translate callback) { public static void translate(Context context, String toTranslate, Translate callback) {
String statusToTranslate; String statusToTranslate;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
statusToTranslate = Html.fromHtml(toTranslate, Html.FROM_HTML_MODE_LEGACY).toString(); statusToTranslate = Html.fromHtml(toTranslate, Html.FROM_HTML_MODE_LEGACY).toString();
@ -51,8 +51,6 @@ public class TranslateHelper {
et = MyTransL.translatorEngine.LIBRETRANSLATE; et = MyTransL.translatorEngine.LIBRETRANSLATE;
} else if (translator.compareToIgnoreCase("LINGVA") == 0) { } else if (translator.compareToIgnoreCase("LINGVA") == 0) {
et = MyTransL.translatorEngine.LINGVA; et = MyTransL.translatorEngine.LINGVA;
} else if (translator.compareToIgnoreCase("MINT") == 0) {
et = MyTransL.translatorEngine.MINT;
} else { } else {
et = MyTransL.translatorEngine.DEEPL; et = MyTransL.translatorEngine.DEEPL;
} }
@ -61,15 +59,7 @@ public class TranslateHelper {
Params params = new Params(); Params params = new Params();
params.setSplit_sentences(false); params.setSplit_sentences(false);
params.setFormat(Params.fType.TEXT); params.setFormat(Params.fType.TEXT);
if(et != MyTransL.translatorEngine.MINT) {
params.setSource_lang("auto"); params.setSource_lang("auto");
} else {
if(languageSource != null && !languageSource.isEmpty()) {
params.setSource_lang(languageSource);
} else {
params.setSource_lang("en");
}
}
if (translator.compareToIgnoreCase("FEDILAB") == 0) { if (translator.compareToIgnoreCase("FEDILAB") == 0) {
myTransL.setLibretranslateDomain("translate.fedilab.app"); myTransL.setLibretranslateDomain("translate.fedilab.app");
} else if (translator.compareToIgnoreCase("LINGVA") == 0) { } else if (translator.compareToIgnoreCase("LINGVA") == 0) {
@ -78,37 +68,19 @@ public class TranslateHelper {
host = context.getString(R.string.SET_TRANSLATOR_HOST_LINGVA); host = context.getString(R.string.SET_TRANSLATOR_HOST_LINGVA);
} }
myTransL.setLingvaDomain(host); myTransL.setLingvaDomain(host);
} else if (translator.compareToIgnoreCase("MINT") == 0) { } else {
String host = sharedpreferences.getString(context.getString(R.string.SET_TRANSLATOR_DOMAIN_MINT), context.getString(R.string.SET_TRANSLATOR_HOST_MINT));
if (host == null || host.trim().isEmpty()) {
host = context.getString(R.string.SET_TRANSLATOR_HOST_MINT);
}
myTransL.setMintDomain(host);
}else {
String translatorVersion = sharedpreferences.getString(context.getString(R.string.SET_TRANSLATOR_VERSION), "PRO"); String translatorVersion = sharedpreferences.getString(context.getString(R.string.SET_TRANSLATOR_VERSION), "PRO");
params.setPro(translatorVersion.equals("PRO")); params.setPro(translatorVersion.equals("PRO"));
String apikey = sharedpreferences.getString(context.getString(R.string.SET_TRANSLATOR_API_KEY), null); String apikey = sharedpreferences.getString(context.getString(R.string.SET_TRANSLATOR_API_KEY), null);
if (apikey != null && !apikey.trim().isEmpty()) { if (apikey != null) {
myTransL.setDeeplAPIKey(apikey.trim()); myTransL.setDeeplAPIKey(apikey.trim());
} else { //Issue with API key (empty or null)
SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putString(context.getString(R.string.SET_TRANSLATOR), "FEDILAB");
editor.commit();
et = MyTransL.translatorEngine.LIBRETRANSLATE;
myTransL.setTranslator(et);
myTransL.setLibretranslateDomain("translate.fedilab.app");
} }
} }
Set<String> translates = sharedpreferences.getStringSet(context.getString(R.string.SET_LIVE_TRANSLATE_MULTIPLE), null); Set<String> translates = sharedpreferences.getStringSet(context.getString(R.string.SET_LIVE_TRANSLATE_MULTIPLE), null);
String translate; String translate;
if (translates == null || translates.size() <= 1) { if (translates == null || translates.size() == 0) {
translate = MyTransL.getLocale(); translate = MyTransL.getLocale();
if (translates != null && translates.size() == 1) {
for (String val : translates) {
translate = val;
}
}
myTransL.translate(statusToTranslate, translate, params, new Results() { myTransL.translate(statusToTranslate, translate, params, new Results() {
@Override @Override
public void onSuccess(com.github.stom79.mytransl.translate.Translate translate) { public void onSuccess(com.github.stom79.mytransl.translate.Translate translate) {

View file

@ -15,6 +15,7 @@ package app.fedilab.android.mastodon.helper;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.BaseMainActivity.currentAccount;
import static app.fedilab.android.mastodon.helper.LogoHelper.getMainLogo; import static app.fedilab.android.mastodon.helper.LogoHelper.getMainLogo;
import static app.fedilab.android.sqlite.Sqlite.DB_NAME; import static app.fedilab.android.sqlite.Sqlite.DB_NAME;
import static app.fedilab.android.sqlite.Sqlite.db; import static app.fedilab.android.sqlite.Sqlite.db;
@ -32,8 +33,6 @@ import android.widget.Toast;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
import java.io.File; import java.io.File;
@ -112,13 +111,7 @@ public class ZipHelper {
Uri uri = Uri.parse("file://" + zipFile); Uri uri = Uri.parse("file://" + zipFile);
intentOpen.setDataAndType(uri, "application/zip"); intentOpen.setDataAndType(uri, "application/zip");
String title = context.getString(R.string.data_export_settings); String title = context.getString(R.string.data_export_settings);
new MaterialAlertDialogBuilder(context) Helper.notify_user(context, currentAccount, intentOpen, BitmapFactory.decodeResource(context.getResources(),
.setTitle(R.string.export_settings)
.setMessage(message + "\n\n" + uri.getPath())
.setIcon(R.drawable.ic_baseline_check_24)
.setNeutralButton(R.string.close, (dialogInterface, i) -> dialogInterface.dismiss())
.show();
Helper.notify_user(context, Helper.getCurrentAccount(context), intentOpen, BitmapFactory.decodeResource(context.getResources(),
getMainLogo(context)), Helper.NotifType.BACKUP, title, message); getMainLogo(context)), Helper.NotifType.BACKUP, title, message);
} }
} }

View file

@ -102,8 +102,9 @@ public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) { public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
// We only want the active item to change // We only want the active item to change
if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) { if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
if (viewHolder instanceof ItemTouchHelperViewHolder itemViewHolder) { if (viewHolder instanceof ItemTouchHelperViewHolder) {
// Let the view holder know that this item is being moved or dragged // Let the view holder know that this item is being moved or dragged
ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder;
itemViewHolder.onItemSelected(); itemViewHolder.onItemSelected();
} }
} }
@ -117,8 +118,9 @@ public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
viewHolder.itemView.setAlpha(ALPHA_FULL); viewHolder.itemView.setAlpha(ALPHA_FULL);
if (viewHolder instanceof ItemTouchHelperViewHolder itemViewHolder) { if (viewHolder instanceof ItemTouchHelperViewHolder) {
// Tell the view holder it's time to restore the idle state // Tell the view holder it's time to restore the idle state
ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder;
itemViewHolder.onItemClear(); itemViewHolder.onItemClear();
} }
} }

View file

@ -73,7 +73,8 @@ public class TimePreferenceDialogFragment extends PreferenceDialogFragmentCompat
// Generate value to save // Generate value to save
String time = hour + ":" + minute; String time = hour + ":" + minute;
DialogPreference preference = getPreference(); DialogPreference preference = getPreference();
if (preference instanceof TimePreference timePreference) { if (preference instanceof TimePreference) {
TimePreference timePreference = ((TimePreference) preference);
if (timePreference.callChangeListener(time)) { if (timePreference.callChangeListener(time)) {
timePreference.setTime(time); timePreference.setTime(time);
} }

Some files were not shown because too many files have changed in this diff Show more