Compare commits

..

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

1961 changed files with 31843 additions and 102878 deletions

View file

@ -1,41 +0,0 @@
---
name: "Bug"
about: "Something isn't working"
labels:
- Bug
---
<!-- Please, describe the issue here -->
## # Steps for reproducing the issue
<!-- Step, to reproduce it -->
---
<!-- The instance you are using -->
Instance:
<!-- Your social network -->
<!-- Put a x between brackets like: - [x] Mastodon -->
- [ ] Mastodon
- [ ] Pleroma
- [ ] Friendica
- [ ] Pixelfed
<!-- If you know the version of Fedilab that you are using (can be found in about page) -->
Version of Fedilab:
<!-- Your Android version -->
Android version:
<!-- If you read our contributing advice -->
[ ] - I read
the [contributing page](https://codeberg.org/tom79/Fedilab/src/branch/main/CONTRIBUTING.md)

View file

@ -1,26 +0,0 @@
---
name: "Feature"
about: "A new feature or an enhancement to an existing feature"
labels:
- Feature
---
## # Describe the improvement
<!-- Your social network -->
<!-- Put a x between brackets like: - [x] Mastodon -->
- [ ] Mastodon
- [ ] Pleroma
- [ ] Friendica
- [ ] Pixelfed
<!-- Describe the improvement here -->
<!-- If you read our contributing advice -->
[ ] - I read
the [contributing page](https://codeberg.org/tom79/Fedilab/src/branch/main/CONTRIBUTING.md)

2
.gitignore vendored
View file

@ -10,5 +10,3 @@
local.properties local.properties
/cropper/build/ /cropper/build/
/build/ /build/
/app/fdroid/release/
/app/playstore/release/

View file

@ -9,13 +9,3 @@ tools for helping in translations. New translations will be automatically merged
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
exist in the default strings.xml file (res/values/strings.xml). If you add or modify strings of exist in the default strings.xml file (res/values/strings.xml). If you add or modify strings of
other languages, it will interfere with weblate's translations. other languages, it will interfere with weblate's translations.
### Issues and Reports:
Before creating an issue please take a moment and search the repository issues to avoid duplicates.
For bug reports, please provide as much details as possible to better debug the problem. The
important part is how to reproduce the bug and steps to reproduce it.
### Pull Requests
Please target the develop branch and not the main branch.

View file

@ -1,24 +1,23 @@
[![Translation status](https://hosted.weblate.org/widgets/fedilab/-/strings/svg-badge.svg)](https://hosted.weblate.org/engage/fedilab/) [![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) &nbsp;&nbsp;&nbsp;[![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
# Fedilab is a multi-accounts client for Mastodon, Pleroma, Friendica and Pixelfed # Fedilab is a multi-accounts client for Mastodon, Pleroma, Peertube, GNU Social, Friendica and Pixelfed
## Donate ## Donate
[<img alt="Donate using Liberapay" src="https://img.shields.io/liberapay/patrons/tom79.svg?logo=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)
## Download ## 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) [<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) &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://fedilab.app/wiki/home/) [WIKI](https://fedilab.app/wiki/home/)
[Release notes](https://codeberg.org/tom79/Fedilab/tags) [Release notes](https://framagit.org/tom79/fedilab/tags)
Lead developer: [toot.fedilab.app/@apps](https://toot.fedilab.app/@apps) Lead developer: [toot.fedilab.app/@apps](https://toot.fedilab.app/@apps)

View file

@ -1,20 +1,16 @@
import java.util.regex.Matcher
import java.util.regex.Pattern
plugins { plugins {
id 'com.android.application' id 'com.android.application'
id 'kotlin-android' id 'kotlin-android'
id 'androidx.navigation.safeargs.kotlin'
} }
def flavor def flavor
android { android {
compileSdk 33 compileSdk 31
defaultConfig { defaultConfig {
minSdk 21 minSdk 21
targetSdk 33 targetSdk 31
versionCode 477 versionCode 390
versionName "3.17.0" versionName "3.0.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }
flavorDimensions "default" flavorDimensions "default"
@ -23,12 +19,8 @@ android {
minifyEnabled false minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }
debug {
applicationIdSuffix '.debug'
}
} }
compileOptions { compileOptions {
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
} }
@ -63,21 +55,6 @@ android {
java.srcDirs = ['src/main/java', 'src/fdroid/java'] java.srcDirs = ['src/main/java', 'src/fdroid/java']
res.srcDirs = ['src/main/res', 'src/fdroid/res'] res.srcDirs = ['src/main/res', 'src/fdroid/res']
} }
main {
res.srcDirs = [
'src/main/res/layouts/mastodon',
'src/main/res/layouts/peertube',
'src/main/res/layouts',
'src/main/layout',
'src/main/res/drawables/mastodon',
'src/main/res/drawables/peertube',
'src/main/res/drawables',
'src/main/res/menus/mastodon',
'src/main/res/menus/peertube',
'src/main/res/menus',
'src/main/res'
]
}
} }
configurations { configurations {
all { all {
@ -93,140 +70,61 @@ allprojects {
} }
dependencies { dependencies {
implementation project(':autoimageslider') implementation project(':autoimageslider')
implementation 'androidx.appcompat:appcompat:1.6.0' implementation 'androidx.appcompat:appcompat:1.4.2'
implementation 'com.google.android.material:material:1.6.1'
implementation 'com.google.android.material:material:1.7.0'
implementation 'com.jaredrummler:colorpicker:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation "com.google.code.gson:gson:2.9.1" implementation "com.google.code.gson:gson:2.8.6"
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 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'androidx.preference:preference:1.2.0' 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.github.evozi:Cyanea:1.0.7'
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'
implementation "com.github.bumptech.glide:glide:4.14.2" implementation 'org.framagit.tom79:SparkButton:1.0.13'
implementation "com.github.bumptech.glide:okhttp3-integration:4.14.2" implementation "com.github.bumptech.glide:glide:4.12.0"
implementation("com.github.bumptech.glide:recyclerview-integration:4.14.2") { implementation 'com.github.mergehez:ArgPlayer:v3.1'
implementation ("com.github.bumptech.glide:recyclerview-integration:4.12.0") {
// Excludes the support library because it's already included by Glide. // Excludes the support library because it's already included by Glide.
transitive = false transitive = false
} }
implementation "org.jsoup:jsoup:1.15.1"
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 'com.burhanrashid52:photoeditor:1.5.1' implementation 'com.burhanrashid52:photoeditor:1.5.1'
implementation("com.vanniktech:android-image-cropper:4.3.3") implementation project(path: ':cropper')
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:2.23.0' implementation 'com.github.penfeizhou.android.animation:apng:2.22.0'
implementation 'com.google.android.exoplayer:exoplayer:2.18.1' implementation 'com.github.penfeizhou.android.animation:gif:2.22.0'
implementation 'com.google.android.exoplayer:exoplayer:2.16.1'
implementation "androidx.viewpager2:viewpager2:1.0.0" implementation "androidx.viewpager2:viewpager2:1.0.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.7.1" 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.9.6" implementation "ch.acra:acra-mail:5.9.3"
implementation "ch.acra:acra-limiter:5.9.3" implementation "ch.acra:acra-limiter:5.9.3"
implementation "ch.acra:acra-dialog:5.9.6" implementation "ch.acra:acra-dialog:5.9.3"
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.0.1' implementation 'com.github.UnifiedPush:android-connector:2.0.0'
// implementation 'com.github.UnifiedPush:android-foss_embedded_fcm_distributor:1.0.0-beta1' // implementation 'com.github.UnifiedPush:android-foss_embedded_fcm_distributor:1.0.0-beta1'
playstoreImplementation('com.github.UnifiedPush:android-embedded_fcm_distributor:2.1.3') { playstoreImplementation 'com.github.UnifiedPush:android-embedded_fcm_distributor:1.1.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.5.1' implementation 'androidx.lifecycle:lifecycle-livedata:2.4.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.5.1' implementation 'androidx.lifecycle:lifecycle-viewmodel:2.4.1'
implementation 'androidx.navigation:navigation-fragment:2.5.3' implementation 'androidx.navigation:navigation-fragment:2.4.2'
implementation 'androidx.navigation:navigation-ui:2.5.3' implementation 'androidx.navigation:navigation-ui:2.4.2'
testImplementation 'junit:junit:' testImplementation 'junit:junit:'
androidTestImplementation 'androidx.test.ext:junit:1.1.4' androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.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'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
implementation 'androidx.vectordrawable:vectordrawable:1.1.0'
implementation "androidx.fragment:fragment:1.5.5"
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'androidx.browser:browser:1.4.0'
implementation 'androidx.documentfile:documentfile:1.0.1'
implementation 'com.github.amoskorir:avatarimagegenerator:1.5.0'
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.1.0'
implementation 'com.google.android.exoplayer:extension-mediasession:2.18.1'
implementation "com.github.mabbas007:TagsEditText:1.0.5"
implementation "net.gotev:uploadservice:4.7.0"
implementation "net.gotev:uploadservice-okhttp:4.7.0"
implementation 'androidx.media:media:1.6.0'
implementation 'com.github.mancj:MaterialSearchBar:0.8.5'
implementation 'com.github.vkay94:DoubleTapPlayerView:1.0.0'
//************ CAST **************///
//---> Google libs (google_full)
playstoreImplementation "com.google.android.gms:play-services-cast-tv:19.0.1"
playstoreImplementation "com.google.android.gms:play-services-cast:21.0.1"
playstoreImplementation "androidx.mediarouter:mediarouter:1.3.0"
playstoreImplementation 'com.google.android.gms:play-services-cast-framework:21.0.1'
playstoreImplementation "com.google.android.gms:play-services-cast-tv:19.0.1"
playstoreImplementation "com.google.android.gms:play-services-cast:21.0.1"
playstoreImplementation "androidx.mediarouter:mediarouter:1.3.0"
playstoreImplementation 'com.google.android.gms:play-services-cast-framework:21.0.1'
//----> Other flavors
fdroidImplementation 'su.litvak.chromecast:api-v2:0.11.3'
fdroidImplementation 'com.fasterxml.jackson.core:jackson-core:2.12.0'
fdroidImplementation 'org.slf4j:slf4j-simple:1.7.30'
fdroidImplementation 'com.github.evozi:Cyanea:1.0.7'
fdroidImplementation 'su.litvak.chromecast:api-v2:0.11.3'
fdroidImplementation 'com.fasterxml.jackson.core:jackson-core:2.12.0'
fdroidImplementation 'org.slf4j:slf4j-simple:1.7.30'
} }
def getCurrentFlavor() {
Gradle gradle = getGradle()
String tskReqStr = gradle.getStartParameter().getTaskRequests().toString()
println("tskReqStr:" +tskReqStr)
Pattern pattern
if( tskReqStr.contains( "assemble" ) ) // to run ./gradlew assembleRelease to build APK
pattern = Pattern.compile("assemble(\\w+)(Release|Debug)")
else if( tskReqStr.contains( "bundle" ) ) // to run ./gradlew bundleRelease to build .aab
pattern = Pattern.compile("assemble(\\w+)(Release|Debug)")
else
pattern = Pattern.compile("generate(\\w+)(Release|Debug)")
Matcher matcher = pattern.matcher( tskReqStr )
println(tskReqStr)
if( matcher.find() )
return matcher.group(1).toLowerCase()
else
{
println "NO MATCH FOUND"
return ""
}
}
println("Flavor: ${getCurrentFlavor()}")
if ( getCurrentFlavor() == "playstore" ){
apply plugin: 'com.google.gms.google-services'
}

View file

@ -1,15 +1,15 @@
package app.fedilab.android; package app.fedilab.android;
import static org.junit.Assert.assertEquals;
import android.content.Context; import android.content.Context;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/** /**
* Instrumented test, which will execute on an Android device. * Instrumented test, which will execute on an Android device.
* *

View file

@ -1,68 +0,0 @@
{
"project_info": {
"project_number": "479837431022",
"project_id": "pc-api-4835782490875392372-140",
"storage_bucket": "pc-api-4835782490875392372-140.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:479837431022:android:1102a97a55202beb547fff",
"android_client_info": {
"package_name": "app.fedilab.android"
}
},
"oauth_client": [
{
"client_id": "479837431022-mettpakdcso72c35djvikfc57l4i7n53.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyCklTEEgLUxy__0Vzcr5_H179kYPXGjmGo"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "479837431022-mettpakdcso72c35djvikfc57l4i7n53.apps.googleusercontent.com",
"client_type": 3
}
]
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:479837431022:android:529536f519b8f4ce547fff",
"android_client_info": {
"package_name": "app.fedilab.android.debug"
}
},
"oauth_client": [
{
"client_id": "479837431022-mettpakdcso72c35djvikfc57l4i7n53.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyCklTEEgLUxy__0Vzcr5_H179kYPXGjmGo"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "479837431022-mettpakdcso72c35djvikfc57l4i7n53.apps.googleusercontent.com",
"client_type": 3
}
]
}
}
}
],
"configuration_version": "1"
}

View file

@ -1,3 +0,0 @@
<resources>
<string name="app_name" translatable="false">Fedilab dbg</string>
</resources>

View file

@ -1,19 +0,0 @@
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<shortcut
android:shortcutId="compose"
android:enabled="true"
android:icon="@drawable/ic_baseline_add_comment_24"
android:shortcutShortLabel="@string/compose_shortcut_short_label1"
tools:targetApi="n_mr1">
<intent
android:action="app.fedilab.android.shorcut.compose"
android:targetClass="app.fedilab.android.activities.MainActivity"
android:targetPackage="fr.gouv.etalab.mastodon.debug" />
<categories android:name="android.shortcut.conversation" />
<capability-binding android:key="actions.intent.CREATE_MESSAGE" />
</shortcut>
</shortcuts>

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="my_images"
path="Android/data/fr.gouv.etalab.mastodon.debug/files/Pictures" />
<cache-path
name="*"
path="." />
</paths>

View file

@ -1,211 +0,0 @@
package app.fedilab.android.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 static app.fedilab.android.peertube.helper.Helper.CAST_ID;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.webkit.MimeTypeMap;
import androidx.appcompat.app.AlertDialog;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.security.GeneralSecurityException;
import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivityPeertubeBinding;
import app.fedilab.android.mastodon.activities.BaseBarActivity;
import app.fedilab.android.peertube.client.data.VideoData;
import app.fedilab.android.peertube.helper.Helper;
import su.litvak.chromecast.api.v2.ChromeCast;
import su.litvak.chromecast.api.v2.MediaStatus;
import su.litvak.chromecast.api.v2.Status;
public class BasePeertubeActivity extends BaseBarActivity {
protected ActivityPeertubeBinding binding;
protected VideoData.Video peertube;
protected ExoPlayer player;
protected String videoURL;
protected String subtitlesStr;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityPeertubeBinding.inflate(getLayoutInflater());
View view = binding.getRoot();
setContentView(view);
binding.minController.castPlay.setOnClickListener(v -> {
binding.minController.castLoader.setVisibility(View.VISIBLE);
if (PeertubeBaseMainActivity.chromeCast != null) {
new Thread(() -> {
try {
int icon = -1;
if (PeertubeBaseMainActivity.chromeCast.getMediaStatus().playerState == MediaStatus.PlayerState.PLAYING) {
PeertubeBaseMainActivity.chromeCast.pause();
icon = R.drawable.ic_baseline_play_arrow_32;
} else if (PeertubeBaseMainActivity.chromeCast.getMediaStatus().playerState == MediaStatus.PlayerState.PAUSED) {
PeertubeBaseMainActivity.chromeCast.play();
icon = R.drawable.ic_baseline_pause_32;
}
if (icon != -1) {
Handler mainHandler = new Handler(Looper.getMainLooper());
int finalIcon = icon;
Runnable myRunnable = () -> binding.minController.castPlay.setImageResource(finalIcon);
mainHandler.post(myRunnable);
}
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> binding.minController.castLoader.setVisibility(View.GONE);
mainHandler.post(myRunnable);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
});
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_cast) {
if (PeertubeBaseMainActivity.chromeCasts != null && PeertubeBaseMainActivity.chromeCasts.size() > 0) {
String[] chromecast_choice = new String[PeertubeBaseMainActivity.chromeCasts.size()];
AlertDialog.Builder alt_bld = new MaterialAlertDialogBuilder(this);
alt_bld.setTitle(R.string.chromecast_choice);
int i = 0;
for (ChromeCast cc : PeertubeBaseMainActivity.chromeCasts) {
chromecast_choice[i] = cc.getTitle();
i++;
}
i = 0;
for (ChromeCast cc : PeertubeBaseMainActivity.chromeCasts) {
if (PeertubeBaseMainActivity.chromecastActivated && cc.isConnected()) {
break;
}
i++;
}
alt_bld.setSingleChoiceItems(chromecast_choice, i, (dialog, position) -> {
PeertubeBaseMainActivity.chromeCast = PeertubeBaseMainActivity.chromeCasts.get(position);
new Thread(() -> {
if (PeertubeBaseMainActivity.chromeCast != null) {
Intent intentBC = new Intent(Helper.RECEIVE_CAST_SETTINGS);
Bundle b = new Bundle();
if (PeertubeBaseMainActivity.chromecastActivated) {
b.putInt("displayed", 0);
intentBC.putExtras(b);
LocalBroadcastManager.getInstance(BasePeertubeActivity.this).sendBroadcast(intentBC);
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> {
binding.doubleTapPlayerView.setVisibility(View.VISIBLE);
binding.minController.castMiniController.setVisibility(View.GONE);
};
mainHandler.post(myRunnable);
} else {
b.putInt("displayed", 1);
b.putSerializable("castedTube", peertube);
intentBC.putExtras(b);
LocalBroadcastManager.getInstance(BasePeertubeActivity.this).sendBroadcast(intentBC);
try {
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> {
invalidateOptionsMenu();
binding.minController.castLoader.setVisibility(View.VISIBLE);
player.setPlayWhenReady(false);
binding.doubleTapPlayerView.setVisibility(View.GONE);
binding.minController.castMiniController.setVisibility(View.VISIBLE);
dialog.dismiss();
if (videoURL != null) {
if (player != null && player.getCurrentPosition() > 0) {
videoURL += "?start=" + (player.getCurrentPosition() / 1000);
}
}
};
mainHandler.post(myRunnable);
if (!PeertubeBaseMainActivity.chromeCast.isConnected()) {
PeertubeBaseMainActivity.chromeCast.connect();
}
myRunnable = this::invalidateOptionsMenu;
mainHandler.post(myRunnable);
Status status = PeertubeBaseMainActivity.chromeCast.getStatus();
if (PeertubeBaseMainActivity.chromeCast.isAppAvailable(CAST_ID) && !status.isAppRunning(CAST_ID)) {
PeertubeBaseMainActivity.chromeCast.launchApp(CAST_ID);
}
if (videoURL != null) {
String mime = MimeTypeMap.getFileExtensionFromUrl(videoURL);
PeertubeBaseMainActivity.chromeCast.setRequestTimeout(60000);
PeertubeBaseMainActivity.chromeCast.load(peertube.getTitle(), null, videoURL, mime);
PeertubeBaseMainActivity.chromeCast.play();
binding.minController.castPlay.setImageResource(R.drawable.ic_baseline_pause_32);
}
myRunnable = () -> binding.minController.castLoader.setVisibility(View.GONE);
mainHandler.post(myRunnable);
} catch (IOException | GeneralSecurityException e) {
e.printStackTrace();
}
}
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> {
invalidateOptionsMenu();
dialog.dismiss();
};
mainHandler.post(myRunnable);
}
}).start();
});
alt_bld.setPositiveButton(R.string.close, (dialog, id) -> dialog.dismiss());
AlertDialog alert = alt_bld.create();
alert.show();
}
}
return super.onOptionsItemSelected(item);
}
@Override
public boolean onCreateOptionsMenu(@NotNull Menu menu) {
getMenuInflater().inflate(R.menu.video_menu, menu);
MenuItem castItem = menu.findItem(R.id.action_cast);
if (PeertubeBaseMainActivity.chromeCasts != null && PeertubeBaseMainActivity.chromeCasts.size() > 0) {
castItem.setVisible(true);
if (PeertubeBaseMainActivity.chromeCast != null && PeertubeBaseMainActivity.chromeCast.isConnected()) {
castItem.setIcon(R.drawable.ic_baseline_cast_connected_24);
} else {
castItem.setIcon(R.drawable.ic_baseline_cast_24);
}
}
return true;
}
}

View file

@ -1,263 +0,0 @@
package app.fedilab.android.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.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivityMainPeertubeBinding;
import app.fedilab.android.mastodon.activities.BaseActivity;
import app.fedilab.android.peertube.client.data.VideoData;
import app.fedilab.android.peertube.helper.Helper;
import su.litvak.chromecast.api.v2.ChromeCast;
import su.litvak.chromecast.api.v2.ChromeCasts;
import su.litvak.chromecast.api.v2.ChromeCastsListener;
import su.litvak.chromecast.api.v2.MediaStatus;
public abstract class PeertubeBaseMainActivity extends BaseActivity implements ChromeCastsListener {
public static List<ChromeCast> chromeCasts;
public static ChromeCast chromeCast;
public static boolean chromecastActivated = false;
protected ActivityMainPeertubeBinding binding;
private BroadcastReceiver manage_chromecast;
private VideoData.Video castedTube;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainPeertubeBinding.inflate(getLayoutInflater());
View view = binding.getRoot();
setContentView(view);
ChromeCastsListener chromeCastsListener = this;
ChromeCasts.registerListener(chromeCastsListener);
binding.castClose.setOnClickListener(v -> {
Intent intentBC = new Intent(Helper.RECEIVE_CAST_SETTINGS);
Bundle b = new Bundle();
b.putInt("displayed", 0);
intentBC.putExtras(b);
LocalBroadcastManager.getInstance(PeertubeBaseMainActivity.this).sendBroadcast(intentBC);
});
binding.castTogglePlay.setOnClickListener(v -> {
if (chromeCast != null) {
new Thread(() -> {
try {
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> binding.castTogglePlay.setVisibility(View.GONE);
mainHandler.post(myRunnable);
int icon = -1;
if (chromeCast.getMediaStatus().playerState == MediaStatus.PlayerState.PLAYING) {
chromeCast.pause();
icon = R.drawable.ic_baseline_play_arrow_32;
} else if (chromeCast.getMediaStatus().playerState == MediaStatus.PlayerState.PAUSED) {
chromeCast.play();
icon = R.drawable.ic_baseline_pause_32;
}
if (icon != -1) {
int finalIcon = icon;
myRunnable = () -> binding.castTogglePlay.setImageResource(finalIcon);
mainHandler.post(myRunnable);
}
myRunnable = () -> binding.castTogglePlay.setVisibility(View.VISIBLE);
mainHandler.post(myRunnable);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
});
manage_chromecast = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Bundle b = intent.getExtras();
assert b != null;
int state = b.getInt("state_asked", -1);
int displayed = b.getInt("displayed", -1);
castedTube = (VideoData.Video) b.getSerializable("castedTube");
if (state == 1) {
discoverCast();
} else if (state == 0) {
new Thread(() -> {
try {
if (chromeCast != null) {
chromeCast.stopApp();
chromeCast.disconnect();
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
if (displayed == 1) {
chromecastActivated = true;
if (castedTube != null) {
binding.castInfo.setVisibility(View.VISIBLE);
Helper.loadGiF(PeertubeBaseMainActivity.this, castedTube.getThumbnailPath(), binding.castView);
binding.castTitle.setText(castedTube.getTitle());
binding.castDescription.setText(castedTube.getDescription());
}
} else if (displayed == 0) {
chromecastActivated = false;
binding.castInfo.setVisibility(View.GONE);
new Thread(() -> {
try {
if (chromeCast != null) {
chromeCast.stopApp();
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
};
LocalBroadcastManager.getInstance(PeertubeBaseMainActivity.this).registerReceiver(manage_chromecast, new IntentFilter(Helper.RECEIVE_CAST_SETTINGS));
}
@Override
public void newChromeCastDiscovered(ChromeCast chromeCast) {
if (chromeCasts == null) {
chromeCasts = new ArrayList<>();
chromeCasts.add(chromeCast);
} else {
boolean canBeAdded = true;
for (ChromeCast cast : chromeCasts) {
if (cast.getName().compareTo(chromeCast.getName()) == 0) {
canBeAdded = false;
break;
}
}
if (canBeAdded) {
chromeCasts.add(chromeCast);
}
}
try {
if (chromeCast.isAppRunning(Helper.CAST_ID) && chromeCast.getMediaStatus() != null && chromeCast.getMediaStatus().playerState != null) {
if (binding.castInfo.getVisibility() == View.GONE) {
binding.castInfo.setVisibility(View.VISIBLE);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void chromeCastRemoved(ChromeCast chromeCast) {
}
@Override
public void onDestroy() {
super.onDestroy();
ChromeCasts.unregisterListener(this);
if (manage_chromecast != null) {
LocalBroadcastManager.getInstance(PeertubeBaseMainActivity.this).unregisterReceiver(manage_chromecast);
new Thread(() -> {
if (chromeCasts != null && chromeCasts.size() > 0) {
for (ChromeCast cast : chromeCasts) {
try {
cast.stopApp();
cast.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
}
if (chromeCasts != null) {
chromeCasts = null;
}
if (chromeCast != null) {
chromeCast = null;
}
}
//Method for discovering cast devices
public void discoverCast() {
new Thread(() -> {
if (chromeCasts != null) {
for (ChromeCast cast : chromeCasts) {
try {
cast.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
chromeCasts = null;
}
chromeCasts = new ArrayList<>();
try {
List<NetworkInterface> interfaces;
interfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface ni : interfaces) {
if ((!ni.isLoopback()) && ni.isUp() && (ni.getName().equals("wlan0"))) {
Enumeration<InetAddress> inetAddressEnumeration = ni.getInetAddresses();
while (inetAddressEnumeration.hasMoreElements()) {
InetAddress inetAddress = inetAddressEnumeration.nextElement();
ChromeCasts.restartDiscovery(inetAddress);
int tryFind = 0;
while (ChromeCasts.get().isEmpty() && tryFind < 5) {
try {
//noinspection BusyWait
Thread.sleep(1000);
tryFind++;
} catch (InterruptedException ignored) {
}
}
}
}
}
ChromeCasts.stopDiscovery();
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = this::invalidateOptionsMenu;
mainHandler.post(myRunnable);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}

View file

@ -1,52 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/castMiniController"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"
android:visibility="gone">
<ImageView
android:id="@+id/cast_play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/play"
android:src="@drawable/ic_baseline_play_arrow_32"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cast_loader"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<TextView
android:id="@+id/cast_loader_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/please_wait"
android:textColor="?colorAccent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/cast_loader_small"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ProgressBar
android:id="@+id/cast_loader_small"
android:layout_width="wrap_content"
android:layout_height="18dp"
android:layout_gravity="center"
android:layout_marginStart="5dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/cast_loader_text"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_cast"
android:icon="@drawable/ic_baseline_cast_24"
android:title="@string/cast"
android:visible="false"
app:showAsAction="always" />
</menu>

View file

@ -1,19 +0,0 @@
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<shortcut
android:shortcutId="compose"
android:enabled="true"
android:icon="@drawable/ic_baseline_add_comment_24"
android:shortcutShortLabel="@string/compose_shortcut_short_label1"
tools:targetApi="n_mr1">
<intent
android:action="app.fedilab.android.shorcut.compose"
android:targetClass="app.fedilab.android.activities.MainActivity"
android:targetPackage="fr.gouv.etalab.mastodon" />
<categories android:name="android.shortcut.conversation" />
<capability-binding android:key="actions.intent.CREATE_MESSAGE" />
</shortcut>
</shortcuts>

View file

@ -2,7 +2,7 @@
<paths> <paths>
<external-path <external-path
name="my_images" name="my_images"
path="Android/data/fr.gouv.etalab.mastodon/files/Pictures" /> path="Android/data/fr.gouv.etalab.mastodon.test/files/Pictures" />
<cache-path <cache-path
name="*" name="*"

View file

@ -3,9 +3,7 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
package="app.fedilab.android"> package="app.fedilab.android">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission <uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:name="android.permission.WRITE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" /> tools:ignore="ScopedStorage" />
@ -13,171 +11,35 @@
<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-feature
android:name="android.hardware.camera"
android:required="false" />
<queries>
<intent>
<action android:name="android.media.action.IMAGE_CAPTURE" />
</intent>
</queries>
<application <application
android:name="app.fedilab.android.MainApplication" android:name=".MainApplication"
tools:replace="android:allowBackup"
android:allowBackup="false" android:allowBackup="false"
android:configChanges="orientation|screenSize" android:icon="@mipmap/ic_launcher"
android:icon="@mipmap/ic_launcher_bubbles"
android:label="@string/app_name"
android:largeHeap="true"
android:requestLegacyExternalStorage="true"
android:roundIcon="@mipmap/ic_launcher_bubbles_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true"
tools:replace="android:allowBackup"> android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppThemeDark"
>
<activity <activity
android:name=".activities.MainActivity" android:name=".activities.MainActivity"
android:configChanges="orientation|screenSize|keyboardHidden|screenLayout|smallestScreenSize" android:exported="true"
android:exported="true"> >
<intent-filter> <intent-filter>
<action android:name="android.intent.action.SEND" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" /> <category android:name="android.intent.category.LAUNCHER" />
<data android:mimeType="video/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
<data android:mimeType="video/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!-- The app is a good candidate for URL in https://domain.name/@xxxxxx-->
<!-- It should cover every URLs for statuses but some others not related to mastodon matching this scheme -->
<data
android:host="*"
android:pathPrefix="/@"
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:exported="true"
android:name=".activities.LoginActivity" android:name=".activities.LoginActivity"
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
android:exported="true"
android:windowSoftInputMode="stateAlwaysHidden"> android:windowSoftInputMode="stateAlwaysHidden">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" /> <category android:name="android.intent.category.BROWSABLE" />
@ -186,71 +48,58 @@
android:scheme="fedilab" /> android:scheme="fedilab" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity <activity
android:name=".mastodon.activities.StatusHistoryActivity" android:name=".activities.WebviewConnectActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/status_history"
android:theme="@style/AppThemeBar" />
<activity
android:name=".mastodon.activities.ContextActivity"
android:configChanges="keyboardHidden|orientation|screenSize" /> android:configChanges="keyboardHidden|orientation|screenSize" />
<activity <activity
android:name=".mastodon.activities.DraftActivity" android:name=".activities.ContextActivity"
android:configChanges="keyboardHidden|orientation|screenSize" /> android:configChanges="keyboardHidden|orientation|screenSize" />
<activity <activity
android:name=".mastodon.imageeditor.EditImageActivity" android:name=".activities.DraftActivity"
android:configChanges="keyboardHidden|orientation|screenSize" /> android:configChanges="keyboardHidden|orientation|screenSize" />
<activity <activity
android:name=".mastodon.activities.ComposeActivity" android:name=".imageeditor.EditImageActivity"
android:configChanges="keyboardHidden|orientation|screenSize" />
<activity
android:name=".activities.ComposeActivity"
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
android:label="@string/compose" /> android:label="@string/compose" />
<activity <activity
android:name=".mastodon.activities.StatusInfoActivity" android:name=".activities.StatusInfoActivity"
android:configChanges="keyboardHidden|orientation|screenSize" /> android:configChanges="keyboardHidden|orientation|screenSize" />
<activity <activity
android:name=".mastodon.activities.FollowRequestActivity" android:name=".activities.FollowRequestActivity"
android:configChanges="keyboardHidden|orientation|screenSize" /> android:configChanges="keyboardHidden|orientation|screenSize" />
<activity <activity
android:name=".mastodon.activities.ProfileActivity" android:name=".activities.WebviewActivity"
android:configChanges="keyboardHidden|orientation|screenSize" />
<activity
android:name=".activities.ProfileActivity"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/account" /> android:label="@string/account" />
<activity <activity
android:name=".mastodon.activities.admin.AdminAccountActivity" android:name=".activities.AdminAccountActivity"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/account" /> android:label="@string/account" />
<activity <activity
android:name=".mastodon.activities.AccountReportActivity" android:name=".activities.ScheduledActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/account"
android:theme="@style/AppThemeBar" />
<activity
android:name=".mastodon.activities.admin.AdminReportActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/report"
android:theme="@style/AppThemeBar" />
<activity
android:name=".mastodon.activities.ScheduledActivity"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/scheduled" /> android:label="@string/scheduled" />
<activity <activity
android:name="com.canhub.cropper.CropImageActivity" android:name="com.theartofdev.edmodo.cropper.CropImageActivity"
android:theme="@style/Base.Theme.AppCompat" /> android:theme="@style/Base.Theme.AppCompat" />
<service
android:name=".services.PostMessageService"
android:label="@string/post_message" />
<activity <activity
android:name=".mastodon.activities.SearchResultTabActivity" android:name=".activities.SearchResultTabActivity"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/search" android:theme="@style/AppThemeBar"
android:theme="@style/AppThemeBar" /> android:label="@string/search" />
<activity <activity
android:name=".mastodon.activities.TrendsActivity" android:name=".activities.ReorderTimelinesActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/trending"
android:theme="@style/AppThemeBar" />
<activity
android:name=".mastodon.activities.ReorderTimelinesActivity"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/reorder_timelines" android:label="@string/reorder_timelines"
android:theme="@style/AppThemeBar" /> android:theme="@style/AppThemeBar" />
@ -261,86 +110,84 @@
android:theme="@style/AppThemeBar" /> android:theme="@style/AppThemeBar" />
<activity <activity
android:name=".mastodon.activities.admin.AdminDomainBlockActivity" android:name=".activities.PartnerShipActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/blocked_domains"
android:theme="@style/AppThemeBar" />
<activity
android:name=".mastodon.activities.SuggestionActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/Suggestions"
android:theme="@style/AppThemeBar" />
<activity
android:name=".mastodon.activities.DirectoryActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/Directory"
android:theme="@style/AppThemeBar" />
<activity
android:name=".mastodon.activities.PartnerShipActivity"
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 <activity
android:name=".mastodon.activities.ActionActivity" android:name=".activities.ActionActivity"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/interactions" android:label="@string/interactions"
android:theme="@style/AppThemeBar" /> android:theme="@style/AppThemeBar" />
<activity <activity
android:name=".mastodon.activities.admin.AdminActionActivity" android:name=".activities.AdminActionActivity"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/administration" android:label="@string/administration"
android:theme="@style/AppThemeBar" /> android:theme="@style/AppThemeBar" />
<activity <activity
android:name=".mastodon.activities.MastodonListActivity" android:name=".activities.MastodonListActivity"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/action_lists" android:label="@string/action_lists"
android:theme="@style/AppThemeBar" /> android:theme="@style/AppThemeBar" />
<activity <activity
android:name=".mastodon.activities.FollowedTagActivity" android:name=".activities.SettingsActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/followed_tags"
android:theme="@style/AppThemeBar" />
<activity
android:name=".mastodon.activities.SettingsActivity"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/settings" android:label="@string/settings"
android:theme="@style/AppThemeBar" /> android:theme="@style/AppThemeBar" />
<activity <activity
android:name=".mastodon.activities.HashTagActivity" android:name=".activities.InstanceActivity"
android:configChanges="keyboardHidden|orientation|screenSize" />
<activity
android:name=".mastodon.activities.AnnouncementActivity"
android:configChanges="keyboardHidden|orientation|screenSize" />
<activity
android:name=".mastodon.activities.MediaActivity"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
android:theme="@style/Transparent" /> android:label="@string/action_about_instance"
android:theme="@style/DialogDark" />
<activity
android:name=".activities.InstanceProfileActivity"
android:excludeFromRecents="true"
android:theme="@style/DialogDark" />
<activity
android:name=".activities.ProxyActivity"
android:excludeFromRecents="true"
android:theme="@style/DialogDark" />
<activity
android:name=".activities.HashTagActivity"
android:configChanges="keyboardHidden|orientation|screenSize" />
<activity
android:name=".activities.AnnouncementActivity"
android:configChanges="keyboardHidden|orientation|screenSize" />
<activity
android:name=".activities.MediaActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:theme="@style/TransparentDark" />
<activity <activity
android:name=".mastodon.activities.ReportActivity" android:name=".activities.InstanceHealthActivity"
android:theme="@style/AppThemeBar" android:excludeFromRecents="true"
android:theme="@style/DialogDark" />
<activity
android:name=".activities.ReportActivity"
android:theme="@style/AppThemeBarDark"
android:windowSoftInputMode="stateVisible" /> android:windowSoftInputMode="stateVisible" />
<activity <activity
android:name=".mastodon.activities.CustomSharingActivity" android:name=".activities.CustomSharingActivity"
android:label="@string/settings_title_custom_sharing" android:label="@string/settings_title_custom_sharing"
android:theme="@style/AppThemeBar" android:windowSoftInputMode="stateVisible"
android:windowSoftInputMode="stateVisible" /> android:theme="@style/AppThemeBarDark" />
<activity <activity
android:name=".mastodon.activities.FilterActivity" android:name=".activities.FilterActivity"
android:label="@string/filters" android:label="@string/filters"
android:theme="@style/AppThemeBar" android:theme="@style/AppThemeBarDark"
android:windowSoftInputMode="stateVisible" /> android:windowSoftInputMode="stateVisible" />
<activity <activity
android:name=".mastodon.activities.EditProfileActivity" android:name=".activities.EditProfileActivity"
android:label="@string/edit_profile" android:label="@string/edit_profile"
android:theme="@style/AppThemeBar" android:theme="@style/AppThemeBarDark"
android:windowSoftInputMode="stateVisible" /> android:windowSoftInputMode="stateVisible" />
<activity <activity
android:name=".mastodon.activities.CacheActivity" android:name=".activities.CacheActivity"
android:label="@string/action_cache" android:label="@string/action_cache"
android:theme="@style/AppThemeBar" /> android:theme="@style/AppThemeBarDark" />
<provider <provider
android:name="androidx.core.content.FileProvider" android:name="androidx.core.content.FileProvider"
@ -353,7 +200,7 @@
</provider> </provider>
<receiver <receiver
android:name=".mastodon.broadcastreceiver.ToastMessage" android:name=".broadcastreceiver.ToastMessage"
android:exported="false"> android:exported="false">
<intent-filter> <intent-filter>
<action android:name="RECEIVE_TOAST_MESSAGE" /> <action android:name="RECEIVE_TOAST_MESSAGE" />
@ -361,7 +208,7 @@
</receiver> </receiver>
<receiver <receiver
android:name=".mastodon.services.CustomReceiver" android:name=".services.CustomReceiver"
android:enabled="true" android:enabled="true"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
@ -372,105 +219,5 @@
<action android:name="org.unifiedpush.android.connector.REGISTRATION_REFUSED" /> <action android:name="org.unifiedpush.android.connector.REGISTRATION_REFUSED" />
</intent-filter> </intent-filter>
</receiver> </receiver>
<activity
android:name=".peertube.activities.PeertubeMainActivity"
android:configChanges="keyboardHidden|orientation|screenSize" />
<activity
android:name=".peertube.activities.PeertubeActivity"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
android:launchMode="singleTask"
android:resizeableActivity="true"
android:supportsPictureInPicture="true"
tools:targetApi="n" />
<activity
android:name=".peertube.activities.PeertubeEditUploadActivity"
android:configChanges="orientation|screenSize"
android:exported="false"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.ShowChannelActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.ShowAccountActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.AccountActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.MyAccountActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.SearchActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.AllPlaylistsActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.AllLocalPlaylistsActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.PlaylistsActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.VideosTimelineActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.SepiaSearchActivity"
android:configChanges="orientation|screenSize"
android:label="@string/sepia_search"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.ManageInstancesActivity"
android:configChanges="orientation|screenSize"
android:label="@string/instances_picker"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.WebviewActivity"
android:configChanges="keyboardHidden|orientation|screenSize" />
<activity
android:name=".peertube.activities.LoginActivity"
android:configChanges="orientation|screenSize"
android:exported="true"
android:windowSoftInputMode="stateAlwaysHidden">
<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:host="backtotubelab"
android:scheme="tubelab" />
</intent-filter>
</activity>
<activity
android:name=".peertube.activities.SettingsActivity"
android:configChanges="orientation|screenSize"
android:label="@string/settings"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.PeertubeUploadActivity"
android:configChanges="orientation|screenSize"
android:label="@string/upload_video"
android:windowSoftInputMode="stateAlwaysHidden" />
<service
android:name=".peertube.services.RetrieveInfoService"
android:exported="false" />
</application> </application>
</manifest> </manifest>

View file

@ -1,20 +0,0 @@
{
"1": "Music",
"2": "Films",
"3": "Vehicles",
"4": "Art",
"5": "Sports",
"6": "Travels",
"7": "Gaming",
"8": "People",
"9": "Comedy",
"10": "Entertainment",
"11": "News & Politics",
"12": "How To",
"13": "Education",
"14": "Activism",
"15": "Science & Technology",
"16": "Animals",
"17": "Kids",
"18": "Food"
}

View file

@ -1,199 +0,0 @@
{
"aa": "Afar",
"ab": "Abkhazian",
"af": "Afrikaans",
"ak": "Akan",
"am": "Amharic",
"ar": "Arabic",
"an": "Aragonese",
"ase": "American Sign Language",
"as": "Assamese",
"av": "Avaric",
"avk": "Kotava",
"ay": "Aymara",
"az": "Azerbaijani",
"ba": "Bashkir",
"bm": "Bambara",
"be": "Belarusian",
"bn": "Bengali",
"bfi": "British Sign Language",
"bi": "Bislama",
"bo": "Tibetan",
"bs": "Bosnian",
"br": "Breton",
"bg": "Bulgarian",
"bzs": "Brazilian Sign Language",
"ca": "Catalan",
"cs": "Czech",
"ch": "Chamorro",
"ce": "Chechen",
"cv": "Chuvash",
"kw": "Cornish",
"co": "Corsican",
"cr": "Cree",
"cse": "Czech Sign Language",
"csl": "Chinese Sign Language",
"cy": "Welsh",
"da": "Danish",
"de": "German",
"dv": "Dhivehi",
"dsl": "Danish Sign Language",
"dz": "Dzongkha",
"el": "Greek",
"en": "English",
"eo": "Esperanto",
"et": "Estonian",
"eu": "Basque",
"ee": "Ewe",
"fo": "Faroese",
"fa": "Persian",
"fj": "Fijian",
"fi": "Finnish",
"fr": "French",
"fy": "Western Frisian",
"fsl": "French Sign Language",
"ff": "Fulah",
"gd": "Scottish Gaelic",
"ga": "Irish",
"gl": "Galician",
"gv": "Manx",
"gn": "Guarani",
"gsg": "German Sign Language",
"gu": "Gujarati",
"ht": "Haitian",
"ha": "Hausa",
"sh": "Serbo-Croatian",
"he": "Hebrew",
"hz": "Herero",
"hi": "Hindi",
"ho": "Hiri Motu",
"hr": "Croatian",
"hu": "Hungarian",
"hy": "Armenian",
"ig": "Igbo",
"ii": "Sichuan Yi",
"iu": "Inuktitut",
"id": "Indonesian",
"ik": "Inupiaq",
"is": "Icelandic",
"it": "Italian",
"jv": "Javanese",
"jbo": "Lojban",
"ja": "Japanese",
"jsl": "Japanese Sign Language",
"kab": "Kabyle",
"kl": "Kalaallisut",
"kn": "Kannada",
"ks": "Kashmiri",
"ka": "Georgian",
"kr": "Kanuri",
"kk": "Kazakh",
"km": "Khmer",
"ki": "Kikuyu",
"rw": "Kinyarwanda",
"ky": "Kirghiz",
"kv": "Komi",
"kg": "Kongo",
"ko": "Korean",
"kj": "Kuanyama",
"ku": "Kurdish",
"lo": "Lao",
"la": "Latin",
"lv": "Latvian",
"li": "Limburgan",
"ln": "Lingala",
"lt": "Lithuanian",
"lb": "Luxembourgish",
"lu": "Luba-Katanga",
"lg": "Ganda",
"mh": "Marshallese",
"ml": "Malayalam",
"mr": "Marathi",
"mk": "Macedonian",
"mg": "Malagasy",
"mt": "Maltese",
"mn": "Mongolian",
"mi": "Maori",
"ms": "Malay (macrolanguage)",
"my": "Burmese",
"na": "Nauru",
"nv": "Navajo",
"nr": "South Ndebele",
"nd": "North Ndebele",
"ng": "Ndonga",
"ne": "Nepali (macrolanguage)",
"nl": "Dutch",
"nn": "Norwegian Nynorsk",
"nb": "Norwegian Bokmål",
"no": "Norwegian",
"ny": "Nyanja",
"oc": "Occitan",
"oj": "Ojibwa",
"or": "Oriya (macrolanguage)",
"om": "Oromo",
"os": "Ossetian",
"pa": "Panjabi",
"pks": "Pakistan Sign Language",
"pl": "Polish",
"pt": "Portuguese",
"ps": "Pushto",
"qu": "Quechua",
"rm": "Romansh",
"ro": "Romanian",
"rsl": "Russian Sign Language",
"rn": "Rundi",
"ru": "Russian",
"sg": "Sango",
"sdl": "Saudi Arabian Sign Language",
"sfs": "South African Sign Language",
"si": "Sinhala",
"sk": "Slovak",
"sl": "Slovenian",
"se": "Northern Sami",
"sm": "Samoan",
"sn": "Shona",
"sd": "Sindhi",
"so": "Somali",
"st": "Southern Sotho",
"es": "Spanish",
"sq": "Albanian",
"sc": "Sardinian",
"sr": "Serbian",
"ss": "Swati",
"su": "Sundanese",
"sw": "Swahili (macrolanguage)",
"sv": "Swedish",
"swl": "Swedish Sign Language",
"ty": "Tahitian",
"ta": "Tamil",
"tt": "Tatar",
"te": "Telugu",
"tg": "Tajik",
"tl": "Tagalog",
"th": "Thai",
"ti": "Tigrinya",
"tlh": "Klingon",
"to": "Tonga (Tonga Islands)",
"tn": "Tswana",
"ts": "Tsonga",
"tk": "Turkmen",
"tr": "Turkish",
"tw": "Twi",
"ug": "Uighur",
"uk": "Ukrainian",
"ur": "Urdu",
"uz": "Uzbek",
"ve": "Venda",
"vi": "Vietnamese",
"wa": "Walloon",
"wo": "Wolof",
"xh": "Xhosa",
"yi": "Yiddish",
"yo": "Yoruba",
"za": "Zhuang",
"zh": "Chinese",
"zu": "Zulu",
"zxx": "No linguistic content",
"zh-Hans": "Simplified Chinese",
"zh-Hant": "Traditional Chinese"
}

View file

@ -1,730 +0,0 @@
[
{
"code": "aa",
"language": "Afaraf"
},
{
"code": "ab",
"language": "аҧсуа бызшәа"
},
{
"code": "ae",
"language": "avesta"
},
{
"code": "af",
"language": "Afrikaans"
},
{
"code": "ak",
"language": "Akan"
},
{
"code": "am",
"language": "አማርኛ"
},
{
"code": "an",
"language": "aragonés"
},
{
"code": "ar",
"language": "اللغة العربية"
},
{
"code": "as",
"language": "অসমীয়া"
},
{
"code": "av",
"language": "авар мацӀ"
},
{
"code": "ay",
"language": "aymar aru"
},
{
"code": "az",
"language": "azərbaycan dili"
},
{
"code": "ba",
"language": "башҡорт теле"
},
{
"code": "be",
"language": "беларуская мова"
},
{
"code": "bg",
"language": "български език"
},
{
"code": "bh",
"language": "भोजपुरी"
},
{
"code": "bi",
"language": "Bislama"
},
{
"code": "bm",
"language": "bamanankan"
},
{
"code": "bn",
"language": "বাংলা"
},
{
"code": "bo",
"language": "བོད་ཡིག"
},
{
"code": "br",
"language": "brezhoneg"
},
{
"code": "bs",
"language": "bosanski jezik"
},
{
"code": "ca",
"language": "Català"
},
{
"code": "ce",
"language": "нохчийн мотт"
},
{
"code": "ch",
"language": "Chamoru"
},
{
"code": "co",
"language": "corsu"
},
{
"code": "cr",
"language": "ᓀᐦᐃᔭᐍᐏᐣ"
},
{
"code": "cs",
"language": "čeština"
},
{
"code": "cu",
"language": "ѩзыкъ словѣньскъ"
},
{
"code": "cv",
"language": "чӑваш чӗлхи"
},
{
"code": "cy",
"language": "Cymraeg"
},
{
"code": "da",
"language": "dansk"
},
{
"code": "de",
"language": "Deutsch"
},
{
"code": "dv",
"language": "Dhivehi"
},
{
"code": "dz",
"language": "རྫོང་ཁ"
},
{
"code": "ee",
"language": "Eʋegbe"
},
{
"code": "el",
"language": "Ελληνικά"
},
{
"code": "en",
"language": "English"
},
{
"code": "eo",
"language": "Esperanto"
},
{
"code": "es",
"language": "Español"
},
{
"code": "et",
"language": "eesti"
},
{
"code": "eu",
"language": "euskara"
},
{
"code": "fa",
"language": "فارسی"
},
{
"code": "ff",
"language": "Fulfulde"
},
{
"code": "fi",
"language": "suomi"
},
{
"code": "fj",
"language": "Vakaviti"
},
{
"code": "fo",
"language": "føroyskt"
},
{
"code": "fr",
"language": "Français"
},
{
"code": "fy",
"language": "Frysk"
},
{
"code": "ga",
"language": "Gaeilge"
},
{
"code": "gd",
"language": "Gàidhlig"
},
{
"code": "gl",
"language": "galego"
},
{
"code": "gu",
"language": "ગુજરાતી"
},
{
"code": "gv",
"language": "Gaelg"
},
{
"code": "ha",
"language": "هَوُسَ"
},
{
"code": "he",
"language": "עברית"
},
{
"code": "hi",
"language": "हिन्दी"
},
{
"code": "ho",
"language": "Hiri Motu"
},
{
"code": "hr",
"language": "Hrvatski"
},
{
"code": "ht",
"language": "Kreyòl ayisyen"
},
{
"code": "hu",
"language": "magyar"
},
{
"code": "hy",
"language": "Հայերեն"
},
{
"code": "hz",
"language": "Otjiherero"
},
{
"code": "ia",
"language": "Interlingua"
},
{
"code": "id",
"language": "Bahasa Indonesia"
},
{
"code": "ie",
"language": "Interlingue"
},
{
"code": "ig",
"language": "Asụsụ Igbo"
},
{
"code": "ii",
"language": "ꆈꌠ꒿ Nuosuhxop"
},
{
"code": "ik",
"language": "Iñupiaq"
},
{
"code": "io",
"language": "Ido"
},
{
"code": "is",
"language": "Íslenska"
},
{
"code": "it",
"language": "Italiano"
},
{
"code": "iu",
"language": "ᐃᓄᒃᑎᑐᑦ"
},
{
"code": "ja",
"language": "日本語"
},
{
"code": "jv",
"language": "basa Jawa"
},
{
"code": "ka",
"language": "ქართული"
},
{
"code": "kg",
"language": "Kikongo"
},
{
"code": "ki",
"language": "Gĩkũyũ"
},
{
"code": "kj",
"language": "Kuanyama"
},
{
"code": "kk",
"language": "қазақ тілі"
},
{
"code": "kl",
"language": "kalaallisut"
},
{
"code": "km",
"language": "ខេមរភាសា"
},
{
"code": "kn",
"language": "ಕನ್ನಡ"
},
{
"code": "ko",
"language": "한국어"
},
{
"code": "kr",
"language": "Kanuri"
},
{
"code": "ks",
"language": "कश्मीरी"
},
{
"code": "ku",
"language": "Kurmancî"
},
{
"code": "kv",
"language": "коми кыв"
},
{
"code": "kw",
"language": "Kernewek"
},
{
"code": "ky",
"language": "Кыргызча"
},
{
"code": "la",
"language": "latine"
},
{
"code": "lb",
"language": "Lëtzebuergesch"
},
{
"code": "lg",
"language": "Luganda"
},
{
"code": "li",
"language": "Limburgs"
},
{
"code": "ln",
"language": "Lingála"
},
{
"code": "lo",
"language": "ພາສາ"
},
{
"code": "lt",
"language": "lietuvių kalba"
},
{
"code": "lu",
"language": "Tshiluba"
},
{
"code": "lv",
"language": "latviešu valoda"
},
{
"code": "mg",
"language": "fiteny malagasy"
},
{
"code": "mh",
"language": "Kajin M̧ajeļ"
},
{
"code": "mi",
"language": "te reo Māori"
},
{
"code": "mk",
"language": "македонски јазик"
},
{
"code": "ml",
"language": "മലയാളം"
},
{
"code": "mn",
"language": "Монгол хэл"
},
{
"code": "mr",
"language": "मराठी"
},
{
"code": "ms",
"language": "Bahasa Melayu"
},
{
"code": "mt",
"language": "Malti"
},
{
"code": "my",
"language": "ဗမာစာ"
},
{
"code": "na",
"language": "Ekakairũ Naoero"
},
{
"code": "nb",
"language": "Norsk bokmål"
},
{
"code": "nd",
"language": "isiNdebele"
},
{
"code": "ne",
"language": "नेपाली"
},
{
"code": "ng",
"language": "Owambo"
},
{
"code": "nl",
"language": "Nederlands"
},
{
"code": "nn",
"language": "Norsk Nynorsk"
},
{
"code": "no",
"language": "Norsk"
},
{
"code": "nr",
"language": "isiNdebele"
},
{
"code": "nv",
"language": "Diné bizaad"
},
{
"code": "ny",
"language": "chiCheŵa"
},
{
"code": "oc",
"language": "occitan"
},
{
"code": "oj",
"language": "ᐊᓂᔑᓈᐯᒧᐎᓐ"
},
{
"code": "om",
"language": "Afaan Oromoo"
},
{
"code": "or",
"language": "ଓଡ଼ିଆ"
},
{
"code": "os",
"language": "ирон æвзаг"
},
{
"code": "pa",
"language": "ਪੰਜਾਬੀ"
},
{
"code": "pi",
"language": "पाऴि"
},
{
"code": "pl",
"language": "Polski"
},
{
"code": "ps",
"language": "پښتو"
},
{
"code": "pt",
"language": "Português"
},
{
"code": "qu",
"language": "Runa Simi"
},
{
"code": "rm",
"language": "rumantsch grischun"
},
{
"code": "rn",
"language": "Ikirundi"
},
{
"code": "ro",
"language": "Română"
},
{
"code": "ru",
"language": "Русский"
},
{
"code": "rw",
"language": "Ikinyarwanda"
},
{
"code": "sa",
"language": "संस्कृतम्"
},
{
"code": "sc",
"language": "sardu"
},
{
"code": "sd",
"language": "सिन्धी"
},
{
"code": "se",
"language": "Davvisámegiella"
},
{
"code": "sg",
"language": "yângâ tî sängö"
},
{
"code": "si",
"language": "සිංහල"
},
{
"code": "sk",
"language": "slovenčina"
},
{
"code": "sl",
"language": "slovenščina"
},
{
"code": "sn",
"language": "chiShona"
},
{
"code": "so",
"language": "Soomaaliga"
},
{
"code": "sq",
"language": "Shqip"
},
{
"code": "sr",
"language": "српски језик"
},
{
"code": "ss",
"language": "SiSwati"
},
{
"code": "st",
"language": "Sesotho"
},
{
"code": "su",
"language": "Basa Sunda"
},
{
"code": "sv",
"language": "Svenska"
},
{
"code": "sw",
"language": "Kiswahili"
},
{
"code": "ta",
"language": "தமிழ்"
},
{
"code": "te",
"language": "తెలుగు"
},
{
"code": "tg",
"language": "тоҷикӣ"
},
{
"code": "th",
"language": "ไทย"
},
{
"code": "ti",
"language": "ትግርኛ"
},
{
"code": "tk",
"language": "Türkmen"
},
{
"code": "tl",
"language": "Wikang Tagalog"
},
{
"code": "tn",
"language": "Setswana"
},
{
"code": "to",
"language": "faka Tonga"
},
{
"code": "tr",
"language": "Türkçe"
},
{
"code": "ts",
"language": "Xitsonga"
},
{
"code": "tt",
"language": "татар теле"
},
{
"code": "tw",
"language": "Twi"
},
{
"code": "ty",
"language": "Reo Tahiti"
},
{
"code": "ug",
"language": "ئۇيغۇرچە‎"
},
{
"code": "uk",
"language": "Українська"
},
{
"code": "ur",
"language": "اردو"
},
{
"code": "uz",
"language": "Ўзбек"
},
{
"code": "ve",
"language": "Tshivenḓa"
},
{
"code": "vi",
"language": "Tiếng Việt"
},
{
"code": "vo",
"language": "Volapük"
},
{
"code": "wa",
"language": "walon"
},
{
"code": "wo",
"language": "Wollof"
},
{
"code": "xh",
"language": "isiXhosa"
},
{
"code": "yi",
"language": "ייִדיש"
},
{
"code": "yo",
"language": "Yorùbá"
},
{
"code": "za",
"language": "Saɯ cueŋƅ"
},
{
"code": "zh",
"language": "中文"
},
{
"code": "zu",
"language": "isiZulu"
}
]

File diff suppressed because it is too large Load diff

View file

@ -1,392 +0,0 @@
[
{
"version": "3.17.0",
"code": "477",
"note": "Added:\n- Peertube 2FA support\n- Cache home in background (default disabled -> New settings category and per account) / change frequency\n- Auto-fetch missing messages for the Home (default disabled -> in Settings - Timelines)\n- Automatically switch between tabs when searching\n- More deep links detection\n- Allow to group mentions at the top (default: disabled)\n\n\nFixed:\n- Dynamic color for Android 12+\n- Missing media description for previews\n- Fix a crash when replying\n- Fix button size not changed\n- Forward tags in replies\n- Media cannot be downloaded or shared with Android 10\n- Some crashes"
},
{
"version": "3.16.4",
"code": "476",
"note": "Added:\n- Cache home in background (default disabled -> New settings category and per account) / change frequency\n- Auto-fetch missing messages for the Home (default disabled -> in Settings - Timelines)\n- Automatically switch between tabs when searching\n\nFixed:\n- Some crashes"
},
{
"version": "3.16.3",
"code": "475",
"note": "Added:\n- Peertube 2FA support\n\nFixed:\n- Dynamic color for Android 12+"
},
{
"version": "3.16.2",
"code": "474",
"note": "Added:\n- Peertube support\n- Compose shortcut (long press launcher)\n- Long press compose button to write with another account\n- Edit description and focus for media (for the next Mastodon release)\n\nChanged:\n- Cross actions with two accounts display a dialog\n- Order & compact og values when sharing > title - url - content\n- Tap on top message (user info) open threads\n\nFixed:\n- Text cleared when adding a media\n- Fix Maths not working with quotes\n- Fix crashes"
},
{
"version": "3.16.1",
"code": "473",
"note": "Changed:\n- Edit description and focus for media (for the next Mastodon release)\n\nChanged:\n- Peertube: remove role support to avoid crashes with older instances\n\nFixed:\n- Fix some crashes"
},
{
"version": "3.16.0",
"code": "472",
"note": "Changed:\n- Peertube support\n- Compose shortcut\n- Long press compose button to write with another account\n\nChanged:\n- Cross actions with two accounts display a dialog\n- Order & compact og values when sharing > title - url - content\n\nFixed:\n- Text cleared when adding a media\n- Fix crashes"
},
{
"version": "3.15.2",
"code": "471",
"note": "Changed:\n- Add instance name when sharing\n\nFixed:\n- Fix a crash when removing media\n- Other minor fixes"
},
{
"version": "3.15.1",
"code": "470",
"note": "Changed:\n- Material dialogs\n\nFixed:\n- Light theme issues"
},
{
"version": "3.15.0",
"code": "469",
"note": "Added:\n- Maths support (view and compose)\n- Filter DMs in HOME (long press on the tab)\n- Filter languages for users in home timeline (from their profile)\n- Add several targeted languages for translator\n\nChanged:\n- Hide single media with preview is now a setting (default: disabled)\n- Group items in menu of messages\n\nFixed:\n- Cross-actions didn't display account instances"
},
{
"version": "3.14.6",
"code": "468",
"note": "Added:\n- Maths support (view and compose)\n\nChanged:\n- Hide single media with preview is now a setting (default: disabled)"
},
{
"version": "3.14.5",
"code": "467",
"note": "Changed:\n- Allow to swipe media for profiles\n\nFixed:\n- Fix crashes with pinch zoom\n- Copy/Paste in threads\n- Fix crash when checking redirection on http links\n- Display menu in media viewer resets pinch-zoom"
},
{
"version": "3.14.4",
"code": "466",
"note": "Changed:\n- Media viewer (pinch zoom)\n\nFixed:\n- Cross account actions (long press)\n- Boost color\n- Buttons not visible with custom themes.\n- Fix some bad behaviors with media viewer"
},
{
"version": "3.14.3",
"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)"
},
{
"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)"
},
{
"version": "3.13.5",
"code": "459",
"note": "Added:\n- Glitch: Allow to post messages locally (Can be turned off in Settings)\n\nFixed:\n- Crashes"
},
{
"version": "3.13.4",
"code": "458",
"note": "Added:\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\nChanged:\n- Full rework on links in messages (also mentions and tags)\n\nFixed:\n- Spoiler text when editing\n- Fix watermarks"
},
{
"version": "3.13.3",
"code": "457",
"note": "Added:\n- Allow to enable extra features in Settings\n- Customizable settings for extra features\n- Support quotes, reactions with messages\n- Support text format (html, markdown, etc.) when composing\n\nFixed:\n- CW not working with media\n- Media not displayed for older instances\n- Some crashes\n"
},
{
"version": "3.13.2",
"code": "456",
"note": "Changed:\n- Hidden media smaller with preview images\n\nFixed:\n- Issue with Media for Android 11+\n- Crash when not setting a translation key\n- Fix DeepL for API pro\n- Crash when visiting a profile with a lot of media\n- Home muted accounts not working without filters\n- Animated custom emoji not displayed"
},
{
"version": "3.13.1",
"code": "455",
"note": "Added:\n- DeepL translation support free/pro keys\n\nChanged:\n- Hide buttons for media when editing\n\nFixed:\n- GIF loaded as static images\n- Suggested accounts cannot be followed"
},
{
"version": "3.13.0",
"code": "454",
"note": "Added:\n- Post random quotes\n- Group reblogs in home timeline\n- Rename Nitter timelines\n- Android 13 support\n- Pagination with search / trending\n- Allow to remove left margin in messages (default: disabled)\n\nChanged:\n- Display translate button only when language is different\n- Respect blank spaces between words in messages\n- Focus button more accessible when editing media\n- Visual feedback for block on account list\n- Visual changes with compose / top bar\n- Use custom Nitter timeline name in manage timelines\n\nFixed:\n- Behavior with cw toggle\n- Truncated gimini links\n- Nav buttons not visible with media (Light theme)\n- Status bar with Android 5\n- Fix links not clickable\n- Fix deep links\n- Fix remote threads not fetched for some instances\n- Adding description to shared media\n- Open with another accounts\n- Chars size not respected for Android 5-6\n- Wrong instance fetched for instances.social\n- Bouncing Timeline on refresh\n- Links to mentions, tags, urls, not visible.\n- Custom channel sounds not applied\n- users with short username are not linked\n- Fix crashes"
},
{
"version": "3.12.3",
"code": "453",
"note": "Added:\n- Pagination with search / trending\n\nFixed:\n- Long press on Nitter tabs\n- Open with another accounts\n- Chars size not respected for Android 5-6\n- Wrong instance fetched for instances.social"
},
{
"version": "3.12.2",
"code": "452",
"note": "Added:\n- Rename Nitter timelines\n- Android 13 support\n\nChanged:\n- Visual feedback for block on account list\n- Visual changes with compose / top bar\n\nFixed:\n- Nav buttons not visible with media (Light theme)\n- Status bar with Android 5\n- Fix links not clickable\n- Fix deep links\n- Fix remote threads not fetched for some instances\n- Adding description to shared media\n- Fix crashes"
},
{
"version": "3.12.1",
"code": "451",
"note": "Added:\n- Post random quotes\n- Group reblogs in home timeline\n\nChanged:\n- Display translate button only when language is different\n- Respect blank spaces between words in messages\n- Focus button more accessible when editing media\n\nFixed:\n- Behavior with cw toggle\n- Truncated gimini links"
},
{
"version": "3.12.0",
"code": "450",
"note": "Added:\n- Full data import/export feature\n- Android 13 themed icon support\n\nFixed:\n- Fix a regression with filters\n- Fix dark solarized theme\n- Fix hide link previews for CW\n- Fix status bar color for all themes\n- Fix language in compose \"...\"\n- Fix add all home muted accounts from lists\n- Fix top notification badges"
},
{
"version": "3.11.3",
"code": "449",
"note": "Added:\n- Add more targeted languages in picker for translations\n- Add account name in push notifications\n\nFixed:\n- Fix a crash when changing language\n- Fix counter colors\n- Fix default link color\n- Fix a crash when clicking on mentions"
},
{
"version": "3.11.2",
"code": "448",
"note": "Added:\n- Mute/Unmute accounts in the Home timeline from their messages or their profiles\n- Add all users from a list to \"Muted home\" in one click\n- Display/Manage users that are muted for home\n\nFixed:\n- Timeline crashes"
},
{
"version": "3.11.0",
"code": "446",
"note": "Added:\n- Display all messages in threads from remote instances (when possible)\n- Allow to unmute/unfollow/unpin a tag from tag timelines\n- Display most used accounts in header menu for an easy switch\n- Automatically add the tag when composing from a tag timeline\n- Add a translate button at the bottom of messages (default: disabled)\n- Add account role in profiles\n- Translate morse\n\nChanged:\n- Disable animations after a refresh\n\nFixed:\n- Contact not working when composing\n- Status bar for black theme\n- Message duplicated in conversations when edited\n- Color issue on Android 5\n- Several crashes"
},
{
"version": "3.10.2",
"code": "445",
"note": "Added:\n- Allow to unmute/unfollow/unpin a tag from tag timelines\n- Automatically add the tag when composing from a tag timeline\n- Add a translate button at the bottom of messages (default: disabled)\n- Add account role in profiles\n\nFixed:\n- Contact not working when composing\n- Status bar for black theme\n- Message duplicated in conversations when edited\n- Color issue on Android 5"
},
{
"version": "3.10.1",
"code": "444",
"note": "Added:\n- Display all messages in threads from remote instances (when possible)\n* Only public messages for instances using the Mastodon API\n* A dedicated button is displayed at the top right when conditions are filled."
},
{
"version": "3.10.0",
"code": "443",
"note": "Added:\n- Dracula theme\n- Customize message colors\n- Enable/Disable Card presentation\n\nChanged:\n- Colors for some themes\n- Space between buttons\n\nFixed:\n- Animated profile pictures not displayed\n- Mentions broken in profile bio and fields\n- Jumps with fit preview images when scrolling up\n- Fetch more button broken with cache\n- Tag patterns in URL break the link\n- Typo in followed tags"
},
{
"version": "3.9.7",
"code": "442",
"note": "Added:\n- Dracula theme\n\nChanged:\n- Colors for Light/Dark/Black themes\n\nFixed:\n- Animated profile pictures not displayed\n- Mentions broken in profile bio and fields\n- Tag patterns in URL break the link\n- Typo in followed tags"
},
{
"version": "3.9.6",
"code": "441",
"note": "Fixed:\n- Jumps with fit preview images when scrolling up\n- Fetch more button broken with cache"
},
{
"version": "3.9.5",
"code": "440",
"note": "Fixed:\n- Custom emoji are not always displayed\n- Jumps in timeline when using \"fit preview images\"\n- Dark theme: timeline buttons without toggle"
},
{
"version": "3.9.4",
"code": "439",
"note": "Changed:\n- Remove card presentation\n- Link color for black theme\n\nFixed:\n- Crash when changing the theme"
},
{
"version": "3.9.3",
"code": "438",
"note": "Added:\n- New design with 5 themes\n\nChanged:\n- Remove built-in browser support\n- Fit preview image displays images vertically\n- Add counters next to images\n\nFixed:\n- Jumps in timelines\n- Replies to wrong messages with followed instances\n- Bug with delete&redraft with a media\n- List cannot be hidden\n- Some crashes"
},
{
"version": "3.9.1",
"code": "436",
"note": "Changed:\n- Remove built-in browser support\n- More spaces between action buttons in messages\n\nFixed:\n- Text size issue\n- Text overlap\n- Wrong background for solarized black\n- Mix between light and dark theme\n- Save button hidden"
},
{
"version": "3.9.0",
"code": "435",
"note": "Added:\n- Migrate to Material Design 3\n- 5 Themes (Light, Dark, Solarized Light/Dark, Black)\n- Automatically switch between Light/Dark\n- Light and Dark theme can be defined for time-based switch\n- Android 12+: Dynamic color\n\nFixed:\n- Jumps in timelines\n"
},
{
"version": "3.8.1",
"code": "434",
"note": "Added:\n- Mute tags with long press in timelines\n\nChanged:\n- Muted account messages are now removed from cache\n\nFixed:\n- Open with another account\n- Fix jumps in profiles\n- Media not displayed in album -> force indexation\n- Built-in browser does not give admin scope\n- Some crashes"
},
{
"version": "3.8.0",
"code": "433",
"note": "Added:\n- List of blocked domains (allow to unblock)\n- Support gemini links\n- Suggested followers\n- Mod/Adm: Manage instance blocked domains\n- Open messages with another account\n- Allow to disable notifications for admins\n- Sort lists\n\nChanged:\n- Allow search term to be edited\n\nFixed:\n- Drafts deleted with no warning\n- Remove lists from \"Manage timelines\"\n- App crashes when proxy is set\n- Filter not synced after being edited\n- Some crashes / improvements"
},
{
"version": "3.7.5",
"code": "432",
"note": "Added:\n- List of blocked domains (allow to unblock)\n- Support gemini links\n- Suggested followers\n\nChanged:\n- Allow search term to be edited\n\nFixed:\n- Drafts deleted with no warning\n- App crashes when proxy is set\n- Filter not synced after being edited\n- Some crashes"
},
{
"version": "3.7.4",
"code": "431",
"note": "Added:\n- Full support to new filters for Mastodon 4\n- Visit profiles without being authenticated / Allow to display all their messages\n\nChanged:\n- Compose view takes the whole width even in threads\n- Accounts can be timed-mute from their profile\n\nFixed:\n- Draft stored when replying \"no\" or dialog prompted without changes\n- Empty pages when starting the app\n- Saving and sharing media fails on some devices\n- Add support for admin notifications\n- Copying content of a message"
},
{
"version": "3.7.3",
"code": "430",
"note": "Added:\n- Visit profiles without being authenticated / Allow to display all their messages\n\nFixed:\n- Saving media fails on some devices"
},
{
"version": "3.7.2",
"code": "429",
"note": "Added:\n- Full support to new filters for Mastodon 4"
},
{
"version": "3.7.1",
"code": "428",
"note": "Added:\n- Support to open links containing /@display_name/ in their path (works on older devices)\n- Display reply count when counters are enabled\n- Add support for filtering profile messages\n\nChanged:\n- Compose view takes the whole width even in threads\n- Reset push notification marker when clearing cache\n\nFixed:\n- Draft stored when replying \"no\" or dialog prompted without changes\n- Filters not working with tags\n- Add a specific error message for followed tags\n- Empty pages when starting the app"
},
{
"version": "3.7.0",
"code": "427",
"note": "Added:\n- Follow tags (dedicated entry in menu)\n- Reduce the list of languages when composing (Settings > Compose)\n- Language indicator when composing\n- Replies are automatically set to first message language\n- Two new Light themes\n- More moderation features\n- List name can be edited\n\nFixed:\n- Filter not working\n- Crash with trends\n- Issue with themes\n- Some content lost when sending messages (mentions)\n- Fix freezes in timelines\n- Some other fixes"
},
{
"version": "3.6.5",
"code": "426",
"note": "- Two new Light themes\n- More moderation features\n\nFixed:\n- Filter not working\n- Crash with trends\n- Some content lost when sending messages (mentions)\n- Some other fixes"
},
{
"version": "3.6.4",
"code": "425",
"note": "Changed:\n- Tag search ordered by popularity\n\nFixed:\n- Unable to get client ID on some devices\n- Issue with messages/notifications not correctly displayed\n- Notifications not received\n- Friendica: issues with mentions and tags (open browser)\n- Improve sharing behaviour"
},
{
"version": "3.6.3",
"code": "424",
"note": "Fixed:\n- Issue with messages/notifications not correctly displayed\n- Friendica: issues with mentions and tags (open browser)\n- Improve sharing behaviour\n"
},
{
"version": "3.6.2",
"code": "423",
"note": "Fixed:\n- Order of notifications\n- URL when sharing boosted message\n- Blank pages when restarting\n- Fix some crashes"
},
{
"version": "3.6.1",
"code": "422",
"note": "Added:\n- Display client in detailed messages\n- Visual support for quotes starting with \">\"\n- Increase indentations for threads (zero to 20, default 5)\n- Visibility for public replies set to unlisted (can be disabled)\n\nChanged:\n- Reduce title size when text size is increased\n\nFixed:\n- Filters are not applied\n- Blocking an account doesn't remove messages in cache\n- Fix some crashes"
},
{
"version": "3.6.0",
"code": "421",
"note": "Added:\n- Edit messages (if your instance supports that feature)\n- Pin/Unpin messages\n- Set the default language for translations\n- Change app icon (Settings > Interface)\n- Allow to disable \"remember position\" in timelines\n- Allow to disable notification aggregation in settings\n- Icon on media previews if a description is available\n\nChanged:\n- Allow to disable/enable media for notifications\n\nFixed:\n- Post loses \"spoiler message\" when adding a media\n- Camera not working on Android 11\n- Notification aggregation\n- Vibrations when fetching new notifications\n- Fix an issue with media timelines\n- Fix some theme issues\n- Fix an issue with built-in browser & openId\n- Bad behaviours with Art Timelines\n- Some crashes"
},
{
"version": "3.5.3",
"code": "420",
"note": "- Edit messages (if your instance support that feature)\n- Some fixes"
},
{
"version": "3.5.2",
"code": "419",
"note": "Added:\n- Pin/Unpin messages\n- Set the default language for translations\n\nChanged:\n- Allow to disable/enable media for notifications\n\nFixed:\n- Wrong images in notification timeline\n- Double icon bug\n- Fix some crashes"
},
{
"version": "3.5.1",
"code": "418",
"note": "Added:\n- Change app icon (Settings > Interface)\n- Allow to disable \"remember position\" in timelines\n- Allow to disable notification aggregation in settings\n\nChanged:\n- Allow to disable/enable media for notifications\n\nFixed:\n- Post loses \"spoiler message\" when adding a media\n- Camera not working on Android 11\n- Notification aggregation\n- Vibrations when fetching new notifications\n- Fix an issue with media timelines\n- Some crashes"
},
{
"version": "3.5.0",
"code": "417",
"note": "Changed:\n- Swipe between timelines\n- Improve cache\n- Button sizes can be changed in settings\n- French translation\n\nFixed:\n- Pleroma: Emoji reactions\n- Sharing (several fixes)\n- Theme issues\n- Rendering issue for links\n- Notifications not removed from cache\n- Issue with watermarks\n- Pagination with bookmarks/favourites\n- Some crashes"
},
{
"version": "3.4.2",
"code": "415",
"note": "Fixed:\n- Attach media to a reply"
},
{
"version": "3.4.1",
"code": "414",
"note": "Added:\n- Disable counters in settings\nFixed:\n- Duplicated messages from cache\n- Notifications in double\n- Drafts not automatically removed\n- Messages not removed from cache after deletion"
},
{
"version": "3.4.0",
"code": "413",
"note": "Added:\n- New cache mechanism (can be disabled in settings)\n- Set thumbnails load behavior Always/Wifi only /ask\n- Add counters for new messages in timelines\nFixed:\n- Contextual menu not working in threads\n- Tag search issue with Friendica\n- Notifications click open the wrong tab\n- Encoding issue with media descriptions\n- Some other fixes."
},
{
"version": "3.3.2",
"code": "409",
"note": "- Fix an issue with cache and home timeline\n- Nitter timelines use the custom instance from settings\n- Fix Nitter issues (only RT)\n- No longer accepts invalid certificate for onion URLs(Google)\n- Fix some crashes"
},
{
"version": "3.3.1",
"code": "408",
"note": "- Improve speed for Nitter instances\n- Allow to edit Nitter accounts with a long press on tabs\n- Fix pagination issue with pinned timelines\n- Fix some crashes\n- Fix visibility when displaying counters"
},
{
"version": "3.3.0",
"code": "407",
"note": "Added:\n- Settings to set all timelines at the top (default disabled)\n- Settings to display timelines in a list (default disabled)\n- Display counters for fav/reblog in timelines (default disabled)\n\nFixed:\n- Visibility issue when replying\n- Some theme issues when composing\n- Some crashes"
},
{
"version": "3.2.0",
"code": "405",
"note": "Added:\n- Export Settings\n- Propagate manual reordering of lists in timeline to \"Lists\" submenu\n- Allow to change the push distributor in settings\n\nChanged:\n- Improve fit preview images\n- Improve notifications\n- Profile media displayed in a grid\n\nFixed:\n- Some Peertube videos not working\n- Respect the default visibility account when replying\n- Discriminate gif from images\n- App crashes when opening external instance timeline\n- Remove button in thread composer crashes the app\n- Back button opens a lot of old activities before closing the app\n- Problems with sharing\n- Reorder Lists with UI issue on change the visibility\n- Link is not shown correctly in posts from Friendica"
},
{
"version": "3.1.0",
"code": "402",
"note": "Added:\n- New theme: Dark Elephant from S1m\n- Error messages from server side when posting fails\n\nChanged:\n- Fetch more buttons more visible\n\nFixed:\n- Issue when fetching missing messages\n- Some issues with themes\n- Too much lost space with reaction (Pleroma)\n- Delete and redraft crashes\n- Crash when playing a video\n- Other crash fixes"
},
{
"version": "3.0.11",
"code": "401",
"note": "Added:\n- New theme: Dark Elephant from S1m\n- Error messages from server side when posting fails\n- Allow to set the fetch time for delayed notifications\n\nChanged:\n- Fetch more buttons more visible\n\nFixed:\n- Issue when fetching missing messages\n- Some issues with themes\n- Too much lost space with reaction (Pleroma)\n- Delete and redraft crashes\n- Crash when playing a video\n- Other crash fixes"
},
{
"version": "3.0.10",
"code": "400",
"note": "Added:\n- Allow to define the max chars count when not detected (In about the instance)\n- Add emoji one picker when composing (must be enabled in settings)\n- Add release notes with the ability to translate them\n\nFixed:\n- Friendica custom emojis not displayed\n- Long press to store media\n- Some bug fixes"
},
{
"version": "3.0.9",
"code": "399",
"note": "Added:\n- Set compose language (from compose menu -> three vertical dots)\n- Add reactions support for Pleroma\n- Add privacy indicator at the top right\n\nChanged\n- Improve the scrolling behaviour\n- Scroll to top (tab reselection) will fetch new messages and then scroll to top\n\nFixed:\n- Empty tag timelines\n- Remove focus point for fit media preview\n- Fix cannot share with one account\n- Fix black theme\n- Theme cannot be selected\n- Fix some button colors"
},
{
"version": "3.0.8",
"code": "398",
"note": "- Keep improving the scroll behaviour\n- Scroll to top (tab reselection) will fetch new messages and then scroll to top\n- Remove focus point for fit media preview\n- Fix cannot share with one account\n- Fix black theme\n- Fix some button colors"
},
{
"version": "3.0.7",
"code": "397",
"note": "- Fix some bugs reported."
},
{
"version": "3.0.6",
"code": "396",
"note": "Added:\n- Allow to set a focus point on previews (media editor)\n- Respect the focus point with previews\n- Pagination with the fetch more button support reading up or down\n- Add trends\n\nFixed:\n- Only last push notification is displayed (not grouped)\n- Bad behavior with the right/left scroll\n- Fix long profiles not fully displayed\n- Issues with some polls\n- Some crashes\n- Some bad behaviors"
},
{
"version": "3.0.5",
"code": "395",
"note": "- Fix some bugs\n- Allow to share with the app"
},
{
"version": "3.0.4",
"code": "394",
"note": "- Fix crashes for some Pleroma instances"
},
{
"version": "3.0.2",
"code": "393",
"note": "- Some bug fixes\n- Improve pinned timelines"
},
{
"version": "3.0.1",
"code": "391",
"note": "Some quick fixes"
},
{
"version": "3.0.0",
"code": "390",
"note": "New version of Fedilab with new feature.\n- You can now compose threads\n- See the whole thread when replying\n- Cache support\n- New design"
}
]

View file

@ -0,0 +1,13 @@
base_theme,2
author,Fedilab
name,Breeze Dark - Yellow
theme_boost_header_color,-14012878
theme_statuses_color,-14473687
theme_link_color,-12734743
theme_icons_color,-4340793
pref_color_background,-15658735
pref_color_navigation_bar,true
pref_color_status_bar,true
theme_accent,-148405
theme_text_color,-1052431
theme_primary,-13552069
1 base_theme 2
2 author Fedilab
3 name Breeze Dark - Yellow
4 theme_boost_header_color -14012878
5 theme_statuses_color -14473687
6 theme_link_color -12734743
7 theme_icons_color -4340793
8 pref_color_background -15658735
9 pref_color_navigation_bar true
10 pref_color_status_bar true
11 theme_accent -148405
12 theme_text_color -1052431
13 theme_primary -13552069

View file

@ -0,0 +1,15 @@
base_theme,2
author,Roboron
name,Cyberpunk Neon
theme_boost_header_color,-16776697,
theme_text_header_1_line,-1441575,
theme_text_header_2_line,-5242717,
theme_statuses_color,-16181197,
theme_link_color,-1441575,
theme_icons_color,-16138810,
pref_color_background,-16774370,
pref_color_navigation_bar,true,
pref_color_status_bar,true,
theme_accent,-1441575,
theme_text_color,-16138810,
theme_primary,-16774370,
1 base_theme,2
2 author,Roboron
3 name,Cyberpunk Neon
4 theme_boost_header_color,-16776697,
5 theme_text_header_1_line,-1441575,
6 theme_text_header_2_line,-5242717,
7 theme_statuses_color,-16181197,
8 theme_link_color,-1441575,
9 theme_icons_color,-16138810,
10 pref_color_background,-16774370,
11 pref_color_navigation_bar,true,
12 pref_color_status_bar,true,
13 theme_accent,-1441575,
14 theme_text_color,-16138810,
15 theme_primary,-16774370,

View file

@ -0,0 +1,15 @@
base_theme,2
author,Jøta Seth
name,Grey Orange
theme_boost_header_color,-14869219
theme_text_header_1_line,-1
theme_text_header_2_line,-1
theme_statuses_color,-14145496
theme_link_color,-26624
theme_icons_color,-26624
pref_color_background,-13092808
pref_color_navigation_bar,true
pref_color_status_bar,true
theme_accent,-26624
theme_text_color,-1
theme_primary,-14408668
1 base_theme 2
2 author Jøta Seth
3 name Grey Orange
4 theme_boost_header_color -14869219
5 theme_text_header_1_line -1
6 theme_text_header_2_line -1
7 theme_statuses_color -14145496
8 theme_link_color -26624
9 theme_icons_color -26624
10 pref_color_background -13092808
11 pref_color_navigation_bar true
12 pref_color_status_bar true
13 theme_accent -26624
14 theme_text_color -1
15 theme_primary -14408668

View file

@ -0,0 +1,15 @@
base_theme,2
author,@AntoineD@h.kher.nl
name,Gruvbox OLED
theme_boost_header_color,-16777216
theme_text_header_1_line,-265785
theme_text_header_2_line,-6777062
theme_statuses_color,-16777216
theme_link_color,-2647775
theme_icons_color,-7175308
pref_color_background,-16777216
pref_color_navigation_bar,true
pref_color_status_bar,true
theme_accent,-9921174
theme_text_color,-265785
theme_primary,-16777216
1 base_theme 2
2 author @AntoineD@h.kher.nl
3 name Gruvbox OLED
4 theme_boost_header_color -16777216
5 theme_text_header_1_line -265785
6 theme_text_header_2_line -6777062
7 theme_statuses_color -16777216
8 theme_link_color -2647775
9 theme_icons_color -7175308
10 pref_color_background -16777216
11 pref_color_navigation_bar true
12 pref_color_status_bar true
13 theme_accent -9921174
14 theme_text_color -265785
15 theme_primary -16777216

View file

@ -0,0 +1,15 @@
base_theme,2
author,AngryTux
name,Less Angry Orange
theme_boost_header_color,-15855063
theme_text_header_1_line,-2128640
theme_text_header_2_line,-5329234
theme_statuses_color,-1
theme_link_color,-12146699
theme_icons_color,-2128640
pref_color_background,-15987700
pref_color_navigation_bar,true
pref_color_status_bar,true
theme_accent,-3968000
theme_text_color,-197380
theme_primary,-14408668
1 base_theme 2
2 author AngryTux
3 name Less Angry Orange
4 theme_boost_header_color -15855063
5 theme_text_header_1_line -2128640
6 theme_text_header_2_line -5329234
7 theme_statuses_color -1
8 theme_link_color -12146699
9 theme_icons_color -2128640
10 pref_color_background -15987700
11 pref_color_navigation_bar true
12 pref_color_status_bar true
13 theme_accent -3968000
14 theme_text_color -197380
15 theme_primary -14408668

View file

@ -0,0 +1,15 @@
base_theme,2
author,Mondstern
name,Mondstern Fedilab
theme_boost_header_color,-1,
theme_text_header_1_line,-13855804,
theme_text_header_2_line,-16227945,
theme_statuses_color,-14935012,
theme_link_color,-15542685,
theme_icons_color,-10723999,
pref_color_background,-15921907,
pref_color_navigation_bar,false,
pref_color_status_bar,false,
theme_accent,-15542685,
theme_text_color,-1,
theme_primary,-14474461,
1 base_theme,2
2 author,Mondstern
3 name,Mondstern Fedilab
4 theme_boost_header_color,-1,
5 theme_text_header_1_line,-13855804,
6 theme_text_header_2_line,-16227945,
7 theme_statuses_color,-14935012,
8 theme_link_color,-15542685,
9 theme_icons_color,-10723999,
10 pref_color_background,-15921907,
11 pref_color_navigation_bar,false,
12 pref_color_status_bar,false,
13 theme_accent,-15542685,
14 theme_text_color,-1,
15 theme_primary,-14474461,

View file

@ -0,0 +1,13 @@
base_theme,2
author,Fedilab
name,Nocturnal
theme_boost_header_color,-12895429
theme_statuses_color,-13553359
theme_link_color,-16747570
theme_icons_color,-10158118
pref_color_background,-14606047
pref_color_navigation_bar,true
pref_color_status_bar,true
theme_accent,-13136013
theme_text_color,-2236963
theme_primary,-14013910
1 base_theme 2
2 author Fedilab
3 name Nocturnal
4 theme_boost_header_color -12895429
5 theme_statuses_color -13553359
6 theme_link_color -16747570
7 theme_icons_color -10158118
8 pref_color_background -14606047
9 pref_color_navigation_bar true
10 pref_color_status_bar true
11 theme_accent -13136013
12 theme_text_color -2236963
13 theme_primary -14013910

View file

@ -0,0 +1,15 @@
base_theme,2
author,Jøta Seth
name,Photon Dark
theme_boost_header_color,-14145496
theme_text_header_1_line,-1
theme_text_header_2_line,-1
theme_statuses_color,-14935012
theme_link_color,-14059009
theme_icons_color,-9474193
pref_color_background,-15921907
pref_color_navigation_bar,true
pref_color_status_bar,true
theme_accent,-14059009
theme_text_color,-1
theme_primary,-14474461
1 base_theme 2
2 author Jøta Seth
3 name Photon Dark
4 theme_boost_header_color -14145496
5 theme_text_header_1_line -1
6 theme_text_header_2_line -1
7 theme_statuses_color -14935012
8 theme_link_color -14059009
9 theme_icons_color -9474193
10 pref_color_background -15921907
11 pref_color_navigation_bar true
12 pref_color_status_bar true
13 theme_accent -14059009
14 theme_text_color -1
15 theme_primary -14474461

View file

@ -0,0 +1,15 @@
base_theme,2
author,Fedilab
name,Solarized Dark - Purple
theme_boost_header_color,-16506327
theme_text_header_1_line,-1120043
theme_text_header_2_line,-1120043
theme_statuses_color,-16304574
theme_link_color,-14251054
theme_icons_color,-7102047
pref_color_background,-16766154
pref_color_navigation_bar,true
pref_color_status_bar,true
theme_accent,-9670204
theme_text_color,-133405
theme_primary,-16304574
1 base_theme 2
2 author Fedilab
3 name Solarized Dark - Purple
4 theme_boost_header_color -16506327
5 theme_text_header_1_line -1120043
6 theme_text_header_2_line -1120043
7 theme_statuses_color -16304574
8 theme_link_color -14251054
9 theme_icons_color -7102047
10 pref_color_background -16766154
11 pref_color_navigation_bar true
12 pref_color_status_bar true
13 theme_accent -9670204
14 theme_text_color -133405
15 theme_primary -16304574

View file

@ -0,0 +1,47 @@
[
{
"theme_name": "Dark",
"base_theme": "DARK",
"primary": "#FF272727",
"primary_dark": "#FF272727",
"primary_light": "#FFd9e1e8",
"accent": "#FF2b90d9",
"accent_dark": "#FF1b80c9",
"accent_light": "#FF772b90d9",
"background": "#FF121212",
"background_dark": "#FF282c37",
"background_light": "#FF282c37",
"should_tint_statusbar": true,
"should_tint_navbar": true
},
{
"theme_name": "Light",
"base_theme": "LIGHT",
"primary": "#FFFFFF",
"primary_dark": "#FFFFFFFF",
"primary_light": "#FFd9e1e8",
"accent": "#FF2b90d9",
"accent_dark": "#FF1b80c9",
"accent_light": "#FF772b90d9",
"background": "#FFFFFFFF",
"background_dark": "#FFFFFFFF",
"background_light": "#FFFFFFFF",
"should_tint_statusbar": true,
"should_tint_navbar": true
},
{
"theme_name": "Black",
"base_theme": "DARK",
"primary": "#FF000000",
"primary_dark": "#FF000000",
"primary_light": "#FF000000",
"accent": "#FF606984",
"accent_dark": "#FF606984",
"accent_light": "#FF606984",
"background": "#FF000000",
"background_dark": "#FF000000",
"background_light": "#FF000000",
"should_tint_statusbar": true,
"should_tint_navbar": true
}
]

File diff suppressed because it is too large Load diff

View file

@ -15,20 +15,16 @@ package app.fedilab.android;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Build;
import android.os.StrictMode; import android.os.StrictMode;
import android.webkit.WebView;
import androidx.multidex.MultiDex; import androidx.multidex.MultiDex;
import androidx.multidex.MultiDexApplication; import androidx.multidex.MultiDexApplication;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import net.gotev.uploadservice.UploadServiceConfig; import com.jaredrummler.cyanea.Cyanea;
import net.gotev.uploadservice.observer.request.GlobalRequestObserver; import com.jaredrummler.cyanea.prefs.CyaneaTheme;
import org.acra.ACRA; import org.acra.ACRA;
import org.acra.ReportField; import org.acra.ReportField;
@ -37,50 +33,49 @@ import org.acra.config.DialogConfigurationBuilder;
import org.acra.config.MailSenderConfigurationBuilder; import org.acra.config.MailSenderConfigurationBuilder;
import org.acra.data.StringFormat; import org.acra.data.StringFormat;
import java.util.Objects; import java.util.List;
import app.fedilab.android.mastodon.helper.ThemeHelper;
import app.fedilab.android.peertube.services.GlobalUploadObserver;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
public class MainApplication extends MultiDexApplication { public class MainApplication extends MultiDexApplication {
public static String UPLOAD_CHANNEL_ID = "upload_info_peertube";
private WebView webView; private static MainApplication app;
public static MainApplication getApp() {
return app;
}
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
try { app = this;
webView = new WebView(this); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(MainApplication.this);
} catch (Exception ignored) {
Cyanea.init(this, super.getResources());
List<CyaneaTheme> list = CyaneaTheme.Companion.from(getAssets(), "themes/cyanea_themes.json");
boolean custom_theme = sharedpreferences.getBoolean("use_custom_theme", false);
boolean no_theme_set = sharedpreferences.getBoolean("no_theme_set", true);
if (no_theme_set && !custom_theme) {
list.get(0).apply(Cyanea.getInstance());
SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putBoolean("no_theme_set", false);
editor.apply();
} }
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder(); StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build()); StrictMode.setVmPolicy(builder.build());
Toasty.Config.getInstance().apply(); Toasty.Config.getInstance().apply();
if (webView != null) {
try {
webView.destroy();
} catch (Exception ignored) {
} }
}
createNotificationChannel();
UploadServiceConfig.initialize(MainApplication.this, UPLOAD_CHANNEL_ID, true);
new GlobalRequestObserver(this, new GlobalUploadObserver());
}
@Override @Override
protected void attachBaseContext(Context base) { protected void attachBaseContext(Context base) {
super.attachBaseContext(base); super.attachBaseContext(base);
MultiDex.install(MainApplication.this); MultiDex.install(MainApplication.this);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(MainApplication.this); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(MainApplication.this);
boolean send_crash_reports = sharedpreferences.getBoolean(getString(R.string.SET_SEND_CRASH_REPORTS), false); boolean send_crash_reports = sharedpreferences.getBoolean(getString(R.string.SET_SEND_CRASH_REPORTS), true);
String currentTheme = sharedpreferences.getString(getString(R.string.SET_THEME_BASE), getString(R.string.SET_DEFAULT_THEME));
ThemeHelper.switchTo(currentTheme);
if (send_crash_reports) { if (send_crash_reports) {
ACRA.init(this, new CoreConfigurationBuilder() ACRA.init(this, new CoreConfigurationBuilder()
//core configuration: //core configuration:
@ -97,6 +92,7 @@ public class MainApplication extends MultiDexApplication {
.withResIcon(R.mipmap.ic_launcher) .withResIcon(R.mipmap.ic_launcher)
.withText(getString(R.string.crash_title)) .withText(getString(R.string.crash_title))
.withCommentPrompt(getString(R.string.crash_message)) .withCommentPrompt(getString(R.string.crash_message))
.withResTheme(R.style.DialogDark)
.withPositiveButtonText(getString(R.string.send_email)) .withPositiveButtonText(getString(R.string.send_email))
.withNegativeButtonText(getString(R.string.cancel)) .withNegativeButtonText(getString(R.string.cancel))
.build() .build()
@ -112,15 +108,4 @@ public class MainApplication extends MultiDexApplication {
); );
} }
} }
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(UPLOAD_CHANNEL_ID,
getString(R.string.notification_channel_name),
NotificationManager.IMPORTANCE_LOW);
channel.setSound(null, null);
((NotificationManager) Objects.requireNonNull(getSystemService(Context.NOTIFICATION_SERVICE))).createNotificationChannel(channel);
}
}
} }

View file

@ -18,31 +18,31 @@ package app.fedilab.android.activities;
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.graphics.drawable.ColorDrawable;
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 androidx.core.app.ActivityOptionsCompat; import androidx.core.app.ActivityOptionsCompat;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
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.BuildConfig; import app.fedilab.android.BuildConfig;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Account;
import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.databinding.ActivityAboutBinding; import app.fedilab.android.databinding.ActivityAboutBinding;
import app.fedilab.android.mastodon.activities.BaseBarActivity; import app.fedilab.android.helper.CrossActionHelper;
import app.fedilab.android.mastodon.activities.ProfileActivity; import app.fedilab.android.helper.Helper;
import app.fedilab.android.mastodon.client.entities.api.Account; import app.fedilab.android.helper.MastodonHelper;
import app.fedilab.android.mastodon.client.entities.api.Status; import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.mastodon.helper.CrossActionHelper; import app.fedilab.android.viewmodel.mastodon.AccountsVM;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.helper.MastodonHelper;
import app.fedilab.android.mastodon.viewmodel.mastodon.AccountsVM;
public class AboutActivity extends BaseBarActivity { public class AboutActivity extends BaseActivity {
private ActivityAboutBinding binding; private ActivityAboutBinding binding;
@ -51,12 +51,13 @@ public class AboutActivity extends BaseBarActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyThemeBar(this);
binding = ActivityAboutBinding.inflate(getLayoutInflater()); binding = ActivityAboutBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
try { try {
@ -77,12 +78,13 @@ 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"));
binding.accountFollow.setBackgroundTintList(ThemeHelper.getButtonActionColorStateList(AboutActivity.this));
if (BuildConfig.DONATIONS) { if (BuildConfig.DONATIONS) {
binding.aboutSupportPaypal.setVisibility(View.VISIBLE); binding.aboutSupportPaypal.setVisibility(View.VISIBLE);
} else { } else {
binding.aboutSupportPaypal.setVisibility(View.GONE); binding.aboutSupportPaypal.setVisibility(View.GONE);
} }
binding.accountFollow.setIconResource(R.drawable.ic_baseline_person_add_24); binding.accountFollow.setImageResource(R.drawable.ic_baseline_person_add_24);
binding.aboutWebsite.setOnClickListener(v -> Helper.openBrowser(AboutActivity.this, "https://fedilab.app")); binding.aboutWebsite.setOnClickListener(v -> Helper.openBrowser(AboutActivity.this, "https://fedilab.app"));
CrossActionHelper.fetchRemoteAccount(AboutActivity.this, "@apps@toot.fedilab.app", new CrossActionHelper.Callback() { CrossActionHelper.fetchRemoteAccount(AboutActivity.this, "@apps@toot.fedilab.app", new CrossActionHelper.Callback() {
@Override @Override
@ -110,12 +112,12 @@ public class AboutActivity extends BaseBarActivity {
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<>();
ids.add(account.id); ids.add(account.id);
accountsVM.getRelationships(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, ids) accountsVM.getRelationships(MainActivity.currentInstance, MainActivity.currentToken, ids)
.observe(AboutActivity.this, relationShips -> { .observe(AboutActivity.this, relationShips -> {
if (relationShips != null && relationShips.size() > 0) { if (relationShips != null && relationShips.size() > 0) {
if (!relationShips.get(0).following) { if (!relationShips.get(0).following) {
binding.accountFollow.setVisibility(View.VISIBLE); binding.accountFollow.setVisibility(View.VISIBLE);
binding.accountFollow.setOnClickListener(v -> accountsVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, true, false, null) binding.accountFollow.setOnClickListener(v -> accountsVM.follow(MainActivity.currentInstance, MainActivity.currentToken, account.id, true, false)
.observe(AboutActivity.this, relationShip -> binding.accountFollow.setVisibility(View.GONE))); .observe(AboutActivity.this, relationShip -> binding.accountFollow.setVisibility(View.GONE)));
} }
} }

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.activities; package app.fedilab.android.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -14,52 +14,51 @@ 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.graphics.drawable.ColorDrawable;
import android.os.Bundle; import android.os.Bundle;
import android.view.MenuItem; import android.view.MenuItem;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
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 app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.app.Timeline;
import app.fedilab.android.databinding.ActivityActionsBinding; import app.fedilab.android.databinding.ActivityActionsBinding;
import app.fedilab.android.mastodon.client.entities.app.Timeline; import app.fedilab.android.helper.Helper;
import app.fedilab.android.mastodon.helper.Helper; import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.mastodon.helper.ThemeHelper; import app.fedilab.android.ui.fragment.timeline.FragmentMastodonAccount;
import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonAccount; import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTimeline;
import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonDomainBlock;
import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonTimeline;
public class ActionActivity extends BaseBarActivity { public class ActionActivity extends BaseActivity {
private ActivityActionsBinding binding; private ActivityActionsBinding binding;
private boolean canGoBack; private boolean canGoBack;
private FragmentMastodonTimeline fragmentMastodonTimeline; private FragmentMastodonTimeline fragmentMastodonTimeline;
private FragmentMastodonAccount fragmentMastodonAccount; private FragmentMastodonAccount fragmentMastodonAccount;
private FragmentMastodonDomainBlock fragmentMastodonDomainBlock;
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyThemeBar(this);
binding = ActivityActionsBinding.inflate(getLayoutInflater()); binding = ActivityActionsBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
canGoBack = false; canGoBack = false;
binding.favourites.setOnClickListener(v -> displayTimeline(Timeline.TimeLineEnum.FAVOURITE_TIMELINE)); binding.favourites.setOnClickListener(v -> displayTimeline(Timeline.TimeLineEnum.FAVOURITE_TIMELINE));
binding.bookmarks.setOnClickListener(v -> displayTimeline(Timeline.TimeLineEnum.BOOKMARK_TIMELINE)); binding.bookmarks.setOnClickListener(v -> displayTimeline(Timeline.TimeLineEnum.BOOKMARK_TIMELINE));
binding.muted.setOnClickListener(v -> displayTimeline(Timeline.TimeLineEnum.MUTED_TIMELINE)); binding.muted.setOnClickListener(v -> displayTimeline(Timeline.TimeLineEnum.MUTED_TIMELINE));
binding.blocked.setOnClickListener(v -> displayTimeline(Timeline.TimeLineEnum.BLOCKED_TIMELINE)); binding.blocked.setOnClickListener(v -> displayTimeline(Timeline.TimeLineEnum.BLOCKED_TIMELINE));
binding.domainBlock.setOnClickListener(v -> displayTimeline(Timeline.TimeLineEnum.BLOCKED_DOMAIN_TIMELINE));
binding.mutedHome.setOnClickListener(v -> displayTimeline(Timeline.TimeLineEnum.MUTED_TIMELINE_HOME));
} }
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) {
ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> { ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> {
fragmentMastodonAccount = new FragmentMastodonAccount(); fragmentMastodonAccount = new FragmentMastodonAccount();
@ -74,15 +73,6 @@ public class ActionActivity extends BaseBarActivity {
fragmentTransaction.commit(); fragmentTransaction.commit();
}); });
} else if (type == Timeline.TimeLineEnum.BLOCKED_DOMAIN_TIMELINE) {
ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> {
fragmentMastodonDomainBlock = new FragmentMastodonDomainBlock();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction =
fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, fragmentMastodonDomainBlock);
fragmentTransaction.commit();
});
} else { } else {
ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> { ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> {
@ -112,12 +102,6 @@ public class ActionActivity extends BaseBarActivity {
case BOOKMARK_TIMELINE: case BOOKMARK_TIMELINE:
setTitle(R.string.bookmarks); setTitle(R.string.bookmarks);
break; break;
case BLOCKED_DOMAIN_TIMELINE:
setTitle(R.string.blocked_domains);
break;
case MUTED_TIMELINE_HOME:
setTitle(R.string.muted_menu_home);
break;
} }
} }
@ -132,9 +116,6 @@ public class ActionActivity extends BaseBarActivity {
if (fragmentMastodonAccount != null) { if (fragmentMastodonAccount != null) {
fragmentMastodonAccount.onDestroyView(); fragmentMastodonAccount.onDestroyView();
} }
if (fragmentMastodonDomainBlock != null) {
fragmentMastodonDomainBlock.onDestroyView();
}
}); });
setTitle(R.string.interactions); setTitle(R.string.interactions);
} else { } else {

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.activities.admin; package app.fedilab.android.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -19,14 +19,16 @@ import android.content.ClipData;
import android.content.ClipboardManager; import android.content.ClipboardManager;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
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.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.TextView; import android.widget.TextView;
@ -44,8 +46,8 @@ 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 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;
@ -53,140 +55,78 @@ 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;
import app.fedilab.android.activities.MainActivity; import app.fedilab.android.client.entities.api.Account;
import app.fedilab.android.client.entities.api.AdminAccount;
import app.fedilab.android.client.entities.api.Attachment;
import app.fedilab.android.databinding.ActivityAdminAccountBinding; import app.fedilab.android.databinding.ActivityAdminAccountBinding;
import app.fedilab.android.mastodon.activities.BaseActivity; import app.fedilab.android.helper.Helper;
import app.fedilab.android.mastodon.activities.InstanceProfileActivity; import app.fedilab.android.helper.MastodonHelper;
import app.fedilab.android.mastodon.activities.MediaActivity; import app.fedilab.android.helper.SpannableHelper;
import app.fedilab.android.mastodon.client.entities.api.Attachment; import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.mastodon.client.entities.api.admin.AdminAccount; import app.fedilab.android.viewmodel.mastodon.AdminVM;
import app.fedilab.android.mastodon.client.entities.api.admin.AdminIp; import app.fedilab.android.viewmodel.mastodon.NodeInfoVM;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.helper.MastodonHelper;
import app.fedilab.android.mastodon.helper.SpannableHelper;
import app.fedilab.android.mastodon.helper.ThemeHelper;
import app.fedilab.android.mastodon.viewmodel.mastodon.AdminVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.NodeInfoVM;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
public class AdminAccountActivity extends BaseActivity { public class AdminAccountActivity extends BaseActivity {
private AdminAccount adminAccount;
private Account account;
private ScheduledExecutorService scheduledExecutorService; private ScheduledExecutorService scheduledExecutorService;
private ActivityAdminAccountBinding binding; private ActivityAdminAccountBinding binding;
private String account_id;
private AdminVM adminVM;
private AdminAccount adminAccount;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyTheme(this);
binding = ActivityAdminAccountBinding.inflate(getLayoutInflater()); binding = ActivityAdminAccountBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
ActionBar actionBar = getSupportActionBar(); ActionBar actionBar = getSupportActionBar();
Bundle b = getIntent().getExtras(); Bundle b = getIntent().getExtras();
adminAccount = null;
if (b != null) { if (b != null) {
adminAccount = (AdminAccount) b.getSerializable(Helper.ARG_ACCOUNT); adminAccount = (AdminAccount) b.getSerializable(Helper.ARG_ACCOUNT);
account_id = b.getString(Helper.ARG_ACCOUNT_ID, null); if (adminAccount != null) {
account = adminAccount.account;
}
} }
postponeEnterTransition(); postponeEnterTransition();
//Remove title //Remove title
if (actionBar != null) { if (actionBar != null) {
actionBar.setDisplayShowTitleEnabled(false); actionBar.setDisplayShowTitleEnabled(false);
actionBar.setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this);
float scale = sharedpreferences.getFloat(getString(R.string.SET_FONT_SCALE), 1.1f);
binding.title.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18 * 1.1f / scale);
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true);
} }
adminVM = new ViewModelProvider(AdminAccountActivity.this).get(AdminVM.class); binding.toolbar.setPopupTheme(Helper.popupStyle());
if (account_id != null) { if (account != null) {
adminVM.getAccount(MainActivity.currentInstance, MainActivity.currentToken, account_id).observe(this, this::initializeView); new Thread(() -> {
return; account = SpannableHelper.convertAccount(AdminAccountActivity.this, account);
} Handler mainHandler = new Handler(Looper.getMainLooper());
if (adminAccount != null && adminAccount.account != null) { Runnable myRunnable = () -> initializeView(account);
initializeView(adminAccount); mainHandler.post(myRunnable);
}).start();
} else { } else {
Toasty.error(AdminAccountActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show(); Toasty.error(AdminAccountActivity.this, getString(R.string.toast_error_loading_account), Toast.LENGTH_LONG).show();
finish(); finish();
} }
binding.disableAction.setOnClickListener(v -> {
if (adminAccount.disabled) {
adminVM.enable(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, adminAccount.id)
.observe(AdminAccountActivity.this, adminAccountResult -> {
adminAccount.disabled = false;
binding.disableAction.setText(R.string.disable);
binding.disabled.setText(R.string.no);
});
} else {
adminVM.performAction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, adminAccount.id, "disable ", null, null, null, null);
adminAccount.disabled = true;
binding.disableAction.setText(R.string.undisable);
binding.disabled.setText(R.string.yes);
}
});
binding.approveAction.setOnClickListener(v -> {
if (adminAccount.approved) {
adminVM.reject(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, adminAccount.id)
.observe(AdminAccountActivity.this, adminAccountResult -> {
adminAccount = adminAccountResult;
initializeView(adminAccount);
});
} else {
adminVM.approve(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, adminAccount.id);
adminAccount.approved = true;
initializeView(adminAccount);
}
});
binding.silenceAction.setOnClickListener(v -> {
if (adminAccount.silenced) {
adminVM.unsilence(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, adminAccount.id)
.observe(AdminAccountActivity.this, adminAccountResult -> {
adminAccount = adminAccountResult;
initializeView(adminAccount);
});
} else {
adminVM.performAction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, adminAccount.id, "silence", null, null, null, null);
adminAccount.silenced = true;
initializeView(adminAccount);
}
});
binding.suspendAction.setOnClickListener(v -> {
if (adminAccount.suspended) {
adminVM.unsuspend(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, adminAccount.id)
.observe(AdminAccountActivity.this, adminAccountResult -> {
adminAccount = adminAccountResult;
initializeView(adminAccount);
});
} else {
adminVM.performAction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, adminAccount.id, "suspend", null, null, null, null);
adminAccount.suspended = true;
initializeView(adminAccount);
}
});
} }
private void initializeView(AdminAccount adminAccount) { private void initializeView(Account account) {
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(AdminAccountActivity.this); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(AdminAccountActivity.this);
if (adminAccount == null) { if (account == null) {
Toasty.error(AdminAccountActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show(); Toasty.error(AdminAccountActivity.this, getString(R.string.toast_error_loading_account), Toast.LENGTH_LONG).show();
finish(); finish();
return; return;
} }
binding.title.setText(String.format(Locale.getDefault(), "@%s", adminAccount.account.acct)); binding.title.setText(String.format(Locale.getDefault(), "@%s", account.acct));
// MastodonHelper.loadPPMastodon(binding.profilePicture, account); // MastodonHelper.loadPPMastodon(binding.profilePicture, account);
binding.appBar.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> { binding.appBar.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> {
@ -200,18 +140,14 @@ public class AdminAccountActivity extends BaseActivity {
} }
}); });
binding.username.setText(String.format(Locale.getDefault(), "@%s", adminAccount.username)); binding.username.setText(String.format(Locale.getDefault(), "@%s", adminAccount.username));
binding.domain.setText(adminAccount.domain); binding.domain.setText(adminAccount.domain);
binding.email.setText(adminAccount.email); binding.email.setText(adminAccount.email);
StringBuilder lastActive = new StringBuilder(); StringBuilder lastActive = new StringBuilder();
if (adminAccount.ips != null) { if (adminAccount.ips != null) {
int count = 0; for (AdminAccount.IP ip : adminAccount.ips) {
for (AdminIp ip : adminAccount.ips) {
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");
count++;
if (count > 4) {
break;
}
} }
} }
if (lastActive.toString().trim().length() == 0) { if (lastActive.toString().trim().length() == 0) {
@ -231,9 +167,78 @@ public class AdminAccountActivity extends BaseActivity {
binding.silenceAction.setText(adminAccount.silenced ? R.string.unsilence : R.string.silence); binding.silenceAction.setText(adminAccount.silenced ? R.string.unsilence : R.string.silence);
binding.suspendAction.setText(adminAccount.suspended ? R.string.unsuspend : R.string.suspend); binding.suspendAction.setText(adminAccount.suspended ? R.string.unsuspend : R.string.suspend);
AdminVM adminVM = new ViewModelProvider(AdminAccountActivity.this).get(AdminVM.class);
binding.disableAction.setOnClickListener(v -> {
if (adminAccount.disabled) {
adminVM.enable(MainActivity.currentInstance, MainActivity.currentToken, account.id)
.observe(AdminAccountActivity.this, adminAccountResult -> {
adminAccount.disabled = false;
binding.disableAction.setText(R.string.disable);
binding.disabled.setText(R.string.no);
});
} else {
adminVM.performAction(MainActivity.currentInstance, MainActivity.currentToken, account.id, "disable ", null, null, null, null);
adminAccount.disabled = true;
binding.disableAction.setText(R.string.undisable);
binding.disabled.setText(R.string.yes);
}
});
binding.approveAction.setOnClickListener(v -> {
if (adminAccount.approved) {
adminVM.reject(MainActivity.currentInstance, MainActivity.currentToken, account.id)
.observe(AdminAccountActivity.this, adminAccountResult -> {
adminAccount.approved = false;
binding.approveAction.setText(R.string.approve);
binding.approved.setText(R.string.no);
});
} else {
adminVM.approve(MainActivity.currentInstance, MainActivity.currentToken, account.id);
adminAccount.approved = true;
binding.approveAction.setText(R.string.reject);
binding.approved.setText(R.string.yes);
}
});
binding.silenceAction.setOnClickListener(v -> {
if (adminAccount.disabled) {
adminVM.unsilence(MainActivity.currentInstance, MainActivity.currentToken, account.id)
.observe(AdminAccountActivity.this, adminAccountResult -> {
adminAccount.silenced = false;
binding.silenceAction.setText(R.string.silence);
binding.disabled.setText(R.string.no);
});
} else {
adminVM.performAction(MainActivity.currentInstance, MainActivity.currentToken, account.id, "silence", null, null, null, null);
adminAccount.silenced = true;
binding.disableAction.setText(R.string.unsilence);
binding.disabled.setText(R.string.yes);
}
});
binding.suspendAction.setOnClickListener(v -> {
if (adminAccount.disabled) {
adminVM.unsuspend(MainActivity.currentInstance, MainActivity.currentToken, account.id)
.observe(AdminAccountActivity.this, adminAccountResult -> {
adminAccount.suspended = false;
binding.suspendAction.setText(R.string.suspend);
binding.suspended.setText(R.string.no);
});
} else {
adminVM.performAction(MainActivity.currentInstance, MainActivity.currentToken, account.id, "suspend", null, null, null, null);
adminAccount.suspended = true;
binding.disableAction.setText(R.string.unsuspend);
binding.suspended.setText(R.string.yes);
}
});
//Retrieve relationship with the connected account
List<String> accountListToCheck = new ArrayList<>();
accountListToCheck.add(account.id);
//Animate emojis //Animate emojis
if (adminAccount.account.emojis != null && adminAccount.account.emojis.size() > 0) { 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();
@ -244,7 +249,7 @@ public class AdminAccountActivity extends BaseActivity {
//Tablayout for timelines/following/followers //Tablayout for timelines/following/followers
boolean disableGif = sharedpreferences.getBoolean(getString(R.string.SET_DISABLE_GIF), false); boolean disableGif = sharedpreferences.getBoolean(getString(R.string.SET_DISABLE_GIF), false);
String targetedUrl = disableGif ? adminAccount.account.avatar_static : adminAccount.account.avatar; String targetedUrl = disableGif ? account.avatar_static : account.avatar;
Glide.with(AdminAccountActivity.this) Glide.with(AdminAccountActivity.this)
.asDrawable() .asDrawable()
.dontTransform() .dontTransform()
@ -269,10 +274,10 @@ public class AdminAccountActivity extends BaseActivity {
} }
); );
//Load header //Load header
MastodonHelper.loadProfileMediaMastodon(AdminAccountActivity.this, binding.bannerPp, adminAccount.account, MastodonHelper.MediaAccountType.HEADER); MastodonHelper.loadProfileMediaMastodon(binding.bannerPp, account, MastodonHelper.MediaAccountType.HEADER);
//Redraws icon for locked accounts //Redraws icon for locked accounts
final float scale = getResources().getDisplayMetrics().density; final float scale = getResources().getDisplayMetrics().density;
if (adminAccount.account.locked) { if (account.locked) {
Drawable img = ContextCompat.getDrawable(AdminAccountActivity.this, R.drawable.ic_baseline_lock_24); Drawable img = ContextCompat.getDrawable(AdminAccountActivity.this, R.drawable.ic_baseline_lock_24);
assert img != null; assert img != null;
img.setBounds(0, 0, (int) (16 * scale + 0.5f), (int) (16 * scale + 0.5f)); img.setBounds(0, 0, (int) (16 * scale + 0.5f), (int) (16 * scale + 0.5f));
@ -282,41 +287,38 @@ public class AdminAccountActivity extends BaseActivity {
} }
//Peertube account watched by a Mastodon account //Peertube account watched by a Mastodon account
//Bot account //Bot account
if (adminAccount.account.bot) { if (account.bot) {
binding.accountBot.setVisibility(View.VISIBLE); binding.accountBot.setVisibility(View.VISIBLE);
} }
if (adminAccount.account.acct != null) { if (account.acct != null) {
setTitle(adminAccount.account.acct); setTitle(account.acct);
} }
final SpannableString content = new SpannableString(getString(R.string.disclaimer_full)); final SpannableString content = new SpannableString(getString(R.string.disclaimer_full));
content.setSpan(new UnderlineSpan(), 0, content.length(), 0); content.setSpan(new UnderlineSpan(), 0, content.length(), 0);
content.setSpan(new ForegroundColorSpan(ThemeHelper.getAttColor(this, R.attr.colorPrimary)), 0, content.length(), content.setSpan(new ForegroundColorSpan(ContextCompat.getColor(AdminAccountActivity.this, R.color.cyanea_accent_reference)), 0, content.length(),
Spanned.SPAN_INCLUSIVE_EXCLUSIVE); Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//This account was moved to another one //This account was moved to another one
if (adminAccount.account.moved != null) { if (account.moved != null) {
binding.accountMoved.setVisibility(View.VISIBLE); binding.accountMoved.setVisibility(View.VISIBLE);
Drawable imgTravel = ContextCompat.getDrawable(AdminAccountActivity.this, R.drawable.ic_baseline_card_travel_24); Drawable imgTravel = ContextCompat.getDrawable(AdminAccountActivity.this, R.drawable.ic_baseline_card_travel_24);
assert imgTravel != null; assert imgTravel != null;
imgTravel.setBounds(0, 0, (int) (20 * scale + 0.5f), (int) (20 * scale + 0.5f)); imgTravel.setBounds(0, 0, (int) (20 * scale + 0.5f), (int) (20 * scale + 0.5f));
binding.accountMoved.setCompoundDrawables(imgTravel, null, null, null); binding.accountMoved.setCompoundDrawables(imgTravel, null, null, null);
//Retrieves content and make account names clickable //Retrieves content and make account names clickable
SpannableString spannableString = SpannableHelper.moveToText(AdminAccountActivity.this, adminAccount.account); SpannableString spannableString = SpannableHelper.moveToText(AdminAccountActivity.this, account);
binding.accountMoved.setText(spannableString, TextView.BufferType.SPANNABLE); binding.accountMoved.setText(spannableString, TextView.BufferType.SPANNABLE);
binding.accountMoved.setMovementMethod(LinkMovementMethod.getInstance()); binding.accountMoved.setMovementMethod(LinkMovementMethod.getInstance());
} }
binding.accountDn.setText( binding.accountDn.setText(account.span_display_name != null ? account.span_display_name : account.display_name, TextView.BufferType.SPANNABLE);
adminAccount.account.getSpanDisplayName(AdminAccountActivity.this, binding.accountUn.setText(String.format("@%s", account.acct));
new WeakReference<>(binding.accountDn)),
TextView.BufferType.SPANNABLE);
binding.accountUn.setText(String.format("@%s", adminAccount.account.acct));
binding.accountUn.setOnLongClickListener(v -> { binding.accountUn.setOnLongClickListener(v -> {
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
String account_id = adminAccount.account.acct; String account_id = account.acct;
if (account_id.split("@").length == 1) if (account_id.split("@").length == 1)
account_id += "@" + BaseMainActivity.currentInstance; account_id += "@" + BaseMainActivity.currentInstance;
ClipData clip = ClipData.newPlainText("mastodon_account_id", "@" + account_id); ClipData clip = ClipData.newPlainText("mastodon_account_id", "@" + account_id);
@ -326,15 +328,15 @@ public class AdminAccountActivity extends BaseActivity {
return false; return false;
}); });
MastodonHelper.loadPPMastodon(binding.accountPp, adminAccount.account); MastodonHelper.loadPPMastodon(binding.accountPp, 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 b = new Bundle(); Bundle b = new Bundle();
Attachment attachment = new Attachment(); Attachment attachment = new Attachment();
attachment.description = adminAccount.account.acct; attachment.description = account.acct;
attachment.preview_url = adminAccount.account.avatar; attachment.preview_url = account.avatar;
attachment.url = adminAccount.account.avatar; attachment.url = account.avatar;
attachment.remote_url = adminAccount.account.avatar; attachment.remote_url = account.avatar;
attachment.type = "image"; attachment.type = "image";
ArrayList<Attachment> attachments = new ArrayList<>(); ArrayList<Attachment> attachments = new ArrayList<>();
attachments.add(attachment); attachments.add(attachment);
@ -348,10 +350,10 @@ public class AdminAccountActivity extends BaseActivity {
}); });
binding.accountDate.setText(Helper.shortDateToString(adminAccount.created_at)); binding.accountDate.setText(Helper.shortDateToString(account.created_at));
binding.accountDate.setVisibility(View.VISIBLE); binding.accountDate.setVisibility(View.VISIBLE);
String[] accountInstanceArray = adminAccount.account.acct.split("@"); String[] accountInstanceArray = account.acct.split("@");
String accountInstance = BaseMainActivity.currentInstance; String accountInstance = BaseMainActivity.currentInstance;
if (accountInstanceArray.length > 1) { if (accountInstanceArray.length > 1) {
accountInstance = accountInstanceArray[1]; accountInstance = accountInstanceArray[1];
@ -365,11 +367,12 @@ public class AdminAccountActivity extends BaseActivity {
binding.instanceInfo.setVisibility(View.VISIBLE); binding.instanceInfo.setVisibility(View.VISIBLE);
binding.instanceInfo.setOnClickListener(v -> { binding.instanceInfo.setOnClickListener(v -> {
InstanceProfileActivity instanceProfileActivity = new InstanceProfileActivity(); Intent intent = new Intent(AdminAccountActivity.this, InstanceProfileActivity.class);
Bundle b = new Bundle(); Bundle b = new Bundle();
b.putString(Helper.ARG_INSTANCE, finalAccountInstance); b.putString(Helper.ARG_INSTANCE, finalAccountInstance);
instanceProfileActivity.setArguments(b); intent.putExtras(b);
instanceProfileActivity.show(getSupportFragmentManager(), null); startActivity(intent);
}); });
} }
}); });

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.activities.admin; package app.fedilab.android.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -14,14 +14,9 @@ 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.activities.AdminActionActivity.AdminEnum.REPORT;
import static app.fedilab.android.mastodon.activities.admin.AdminActionActivity.AdminEnum.DOMAIN;
import static app.fedilab.android.mastodon.activities.admin.AdminActionActivity.AdminEnum.REPORT;
import android.content.BroadcastReceiver; import android.graphics.drawable.ColorDrawable;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle; import android.os.Bundle;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
@ -29,74 +24,50 @@ import android.view.MenuItem;
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.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivityAdminActionsBinding; import app.fedilab.android.databinding.ActivityAdminActionsBinding;
import app.fedilab.android.databinding.PopupAdminFilterAccountsBinding; 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.helper.Helper;
import app.fedilab.android.mastodon.client.entities.api.admin.AdminDomainBlock; import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.mastodon.helper.Helper; import app.fedilab.android.ui.fragment.admin.FragmentAdminAccount;
import app.fedilab.android.mastodon.helper.ThemeHelper; import app.fedilab.android.ui.fragment.admin.FragmentAdminReport;
import app.fedilab.android.mastodon.ui.fragment.admin.FragmentAdminAccount;
import app.fedilab.android.mastodon.ui.fragment.admin.FragmentAdminDomain;
import app.fedilab.android.mastodon.ui.fragment.admin.FragmentAdminReport;
public class AdminActionActivity extends BaseBarActivity { public class AdminActionActivity extends BaseActivity {
public static Boolean local = true, remote = true, active = true, pending = true, disabled = true, silenced = true, suspended = true, staff = null, orderByMostRecent = true; public static Boolean local = true, remote = true, active = true, pending = true, disabled = true, silenced = true, suspended = true, staff = null, orderByMostRecent = true;
public static Boolean resolved = null, reportLocal = true, reportRemote = true; public static Boolean resolved = false, reportLocal = true, reportRemote = true;
private ActivityAdminActionsBinding binding; private ActivityAdminActionsBinding binding;
private boolean canGoBack; private boolean canGoBack;
private FragmentAdminReport fragmentAdminReport; private FragmentAdminReport fragmentAdminReport;
private FragmentAdminAccount fragmentAdminAccount; private FragmentAdminAccount fragmentAdminAccount;
private FragmentAdminDomain fragmentAdminDomain;
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Bundle b = intent.getExtras();
if (b != null) {
AdminDomainBlock adminDomainBlock = (AdminDomainBlock) b.getSerializable(Helper.ARG_ADMIN_DOMAINBLOCK);
AdminDomainBlock adminDomainBlockDelete = (AdminDomainBlock) b.getSerializable(Helper.ARG_ADMIN_DOMAINBLOCK_DELETE);
if (adminDomainBlock != null && adminDomainBlock.domain != null && fragmentAdminDomain != null) {
fragmentAdminDomain.update(adminDomainBlock);
}
if (adminDomainBlockDelete != null && fragmentAdminDomain != null) {
fragmentAdminDomain.delete(adminDomainBlockDelete);
}
}
}
};
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyThemeBar(this);
binding = ActivityAdminActionsBinding.inflate(getLayoutInflater()); binding = ActivityAdminActionsBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, new IntentFilter(Helper.BROADCAST_DATA));
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
canGoBack = false; canGoBack = false;
binding.reports.setOnClickListener(v -> displayTimeline(REPORT)); binding.reports.setOnClickListener(v -> displayTimeline(REPORT));
binding.accounts.setOnClickListener(v -> displayTimeline(ACCOUNT)); binding.accounts.setOnClickListener(v -> displayTimeline(AdminEnum.ACCOUNT));
binding.domains.setOnClickListener(v -> displayTimeline(DOMAIN));
} }
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, () -> { ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> {
fragmentAdminReport = new FragmentAdminReport(); fragmentAdminReport = new FragmentAdminReport();
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
@ -109,7 +80,9 @@ public class AdminActionActivity extends BaseBarActivity {
fragmentTransaction.replace(R.id.fragment_container, fragmentAdminReport); fragmentTransaction.replace(R.id.fragment_container, fragmentAdminReport);
fragmentTransaction.commit(); fragmentTransaction.commit();
}); });
} else if (type == ACCOUNT) {
} else {
ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> { ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> {
fragmentAdminAccount = new FragmentAdminAccount(); fragmentAdminAccount = new FragmentAdminAccount();
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
@ -122,19 +95,7 @@ public class AdminActionActivity extends BaseBarActivity {
fragmentTransaction.replace(R.id.fragment_container, fragmentAdminAccount); fragmentTransaction.replace(R.id.fragment_container, fragmentAdminAccount);
fragmentTransaction.commit(); fragmentTransaction.commit();
}); });
} else if (type == DOMAIN) {
ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> {
fragmentAdminDomain = new FragmentAdminDomain();
Bundle bundle = new Bundle();
bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, type);
bundle.putString(Helper.ARG_VIEW_MODEL_KEY, "FEDILAB_" + type.getValue());
fragmentAdminDomain.setArguments(bundle);
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction =
fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, fragmentAdminDomain);
fragmentTransaction.commit();
});
} }
switch (type) { switch (type) {
case REPORT: case REPORT:
@ -143,16 +104,13 @@ public class AdminActionActivity extends BaseBarActivity {
case ACCOUNT: case ACCOUNT:
setTitle(R.string.accounts); setTitle(R.string.accounts);
break; break;
case DOMAIN:
setTitle(R.string.domains);
break;
} }
invalidateOptionsMenu(); invalidateOptionsMenu();
} }
@Override @Override
public boolean onCreateOptionsMenu(@NonNull Menu menu) { public boolean onCreateOptionsMenu(@NonNull Menu menu) {
if (canGoBack && fragmentAdminAccount != null) { if (canGoBack) {
getMenuInflater().inflate(R.menu.menu_admin_account, menu); getMenuInflater().inflate(R.menu.menu_admin_account, menu);
} }
return super.onCreateOptionsMenu(menu); return super.onCreateOptionsMenu(menu);
@ -165,7 +123,7 @@ public class AdminActionActivity extends BaseBarActivity {
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))) {
AlertDialog.Builder alertDialogBuilder = new MaterialAlertDialogBuilder(AdminActionActivity.this); AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(AdminActionActivity.this, Helper.dialogStyle());
PopupAdminFilterAccountsBinding binding = PopupAdminFilterAccountsBinding.inflate(getLayoutInflater()); PopupAdminFilterAccountsBinding binding = PopupAdminFilterAccountsBinding.inflate(getLayoutInflater());
alertDialogBuilder.setView(binding.getRoot()); alertDialogBuilder.setView(binding.getRoot());
if (local != null && remote == null) { if (local != null && remote == null) {
@ -197,8 +155,6 @@ public class AdminActionActivity extends BaseBarActivity {
binding.moderationAll.setChecked(true); binding.moderationAll.setChecked(true);
} }
binding.moderation.setOnCheckedChangeListener((group, checkedId) -> { binding.moderation.setOnCheckedChangeListener((group, checkedId) -> {
disabled = null;
silenced = null;
if (checkedId == R.id.moderation_all) { if (checkedId == R.id.moderation_all) {
active = true; active = true;
suspended = true; suspended = true;
@ -259,7 +215,7 @@ public class AdminActionActivity extends BaseBarActivity {
AlertDialog alert = alertDialogBuilder.create(); AlertDialog alert = alertDialogBuilder.create();
alert.show(); alert.show();
} else { } else {
AlertDialog.Builder alertDialogBuilder = new MaterialAlertDialogBuilder(AdminActionActivity.this); AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(AdminActionActivity.this, Helper.dialogStyle());
PopupAdminFilterReportsBinding binding = PopupAdminFilterReportsBinding.inflate(getLayoutInflater()); PopupAdminFilterReportsBinding binding = PopupAdminFilterReportsBinding.inflate(getLayoutInflater());
alertDialogBuilder.setView(binding.getRoot()); alertDialogBuilder.setView(binding.getRoot());
if (resolved == null) { if (resolved == null) {
@ -271,7 +227,7 @@ public class AdminActionActivity extends BaseBarActivity {
if (checkedId == R.id.status_resolved) { if (checkedId == R.id.status_resolved) {
resolved = true; resolved = true;
} else if (checkedId == R.id.status_unresolved) { } else if (checkedId == R.id.status_unresolved) {
resolved = null; resolved = false;
} }
}); });
if (reportLocal != null && reportRemote == null) { if (reportLocal != null && reportRemote == null) {
@ -314,14 +270,6 @@ public class AdminActionActivity extends BaseBarActivity {
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
@Override
protected void onDestroy() {
super.onDestroy();
if (mReceiver != null) {
LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
}
}
@Override @Override
public void onBackPressed() { public void onBackPressed() {
if (canGoBack) { if (canGoBack) {
@ -329,15 +277,9 @@ public class AdminActionActivity extends BaseBarActivity {
ThemeHelper.slideViewsToRight(binding.fragmentContainer, binding.buttonContainer, () -> { ThemeHelper.slideViewsToRight(binding.fragmentContainer, binding.buttonContainer, () -> {
if (fragmentAdminReport != null) { if (fragmentAdminReport != null) {
fragmentAdminReport.onDestroyView(); fragmentAdminReport.onDestroyView();
fragmentAdminReport = null;
} }
if (fragmentAdminAccount != null) { if (fragmentAdminAccount != null) {
fragmentAdminAccount.onDestroyView(); fragmentAdminAccount.onDestroyView();
fragmentAdminAccount = null;
}
if (fragmentAdminDomain != null) {
fragmentAdminDomain.onDestroyView();
fragmentAdminDomain = null;
} }
setTitle(R.string.administration); setTitle(R.string.administration);
invalidateOptionsMenu(); invalidateOptionsMenu();
@ -352,9 +294,8 @@ public class AdminActionActivity extends BaseBarActivity {
@SerializedName("REPORT") @SerializedName("REPORT")
REPORT("REPORT"), REPORT("REPORT"),
@SerializedName("ACCOUNT") @SerializedName("ACCOUNT")
ACCOUNT("ACCOUNT"), ACCOUNT("ACCOUNT");
@SerializedName("DOMAIN")
DOMAIN("DOMAIN");
private final String value; private final String value;
AdminEnum(String value) { AdminEnum(String value) {

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.activities.admin; package app.fedilab.android.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -19,14 +19,16 @@ import android.content.ClipData;
import android.content.ClipboardManager; import android.content.ClipboardManager;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
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.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.TextView; import android.widget.TextView;
@ -44,7 +46,6 @@ 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 java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -54,24 +55,20 @@ 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;
import app.fedilab.android.client.entities.api.Account;
import app.fedilab.android.client.entities.api.AdminAccount;
import app.fedilab.android.client.entities.api.Attachment;
import app.fedilab.android.databinding.ActivityAdminAccountBinding; import app.fedilab.android.databinding.ActivityAdminAccountBinding;
import app.fedilab.android.mastodon.activities.BaseBarActivity; import app.fedilab.android.helper.Helper;
import app.fedilab.android.mastodon.activities.InstanceProfileActivity; import app.fedilab.android.helper.MastodonHelper;
import app.fedilab.android.mastodon.activities.MediaActivity; import app.fedilab.android.helper.SpannableHelper;
import app.fedilab.android.mastodon.client.entities.api.Account; import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.mastodon.client.entities.api.Attachment; import app.fedilab.android.viewmodel.mastodon.AdminVM;
import app.fedilab.android.mastodon.client.entities.api.admin.AdminAccount; import app.fedilab.android.viewmodel.mastodon.NodeInfoVM;
import app.fedilab.android.mastodon.client.entities.api.admin.AdminIp;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.helper.MastodonHelper;
import app.fedilab.android.mastodon.helper.SpannableHelper;
import app.fedilab.android.mastodon.helper.ThemeHelper;
import app.fedilab.android.mastodon.viewmodel.mastodon.AdminVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.NodeInfoVM;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
public class AdminReportActivity extends BaseBarActivity { public class AdminReportActivity extends BaseActivity {
private AdminAccount adminAccount; private AdminAccount adminAccount;
private Account account; private Account account;
@ -82,7 +79,7 @@ public class AdminReportActivity extends BaseBarActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyTheme(this);
binding = ActivityAdminAccountBinding.inflate(getLayoutInflater()); binding = ActivityAdminAccountBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
@ -99,18 +96,25 @@ public class AdminReportActivity extends BaseBarActivity {
//Remove title //Remove title
if (actionBar != null) { if (actionBar != null) {
actionBar.setDisplayShowTitleEnabled(false); actionBar.setDisplayShowTitleEnabled(false);
actionBar.setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this);
float scale = sharedpreferences.getFloat(getString(R.string.SET_FONT_SCALE), 1.1f);
binding.title.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18 * 1.1f / scale);
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true);
} }
binding.toolbar.setPopupTheme(Helper.popupStyle());
if (account != null) { if (account != null) {
initializeView(account); new Thread(() -> {
account = SpannableHelper.convertAccount(AdminReportActivity.this, account);
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> initializeView(account);
mainHandler.post(myRunnable);
}).start();
} else { } else {
Toasty.error(AdminReportActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show(); Toasty.error(AdminReportActivity.this, getString(R.string.toast_error_loading_account), Toast.LENGTH_LONG).show();
finish(); finish();
} }
} }
@ -118,7 +122,7 @@ public class AdminReportActivity extends BaseBarActivity {
private void initializeView(Account account) { private void initializeView(Account account) {
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(AdminReportActivity.this); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(AdminReportActivity.this);
if (account == null) { if (account == null) {
Toasty.error(AdminReportActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show(); Toasty.error(AdminReportActivity.this, getString(R.string.toast_error_loading_account), Toast.LENGTH_LONG).show();
finish(); finish();
return; return;
} }
@ -142,7 +146,7 @@ public class AdminReportActivity extends BaseBarActivity {
binding.email.setText(adminAccount.email); binding.email.setText(adminAccount.email);
StringBuilder lastActive = new StringBuilder(); StringBuilder lastActive = new StringBuilder();
if (adminAccount.ips != null) { if (adminAccount.ips != null) {
for (AdminIp ip : adminAccount.ips) { for (AdminAccount.IP ip : adminAccount.ips) {
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");
} }
} }
@ -167,7 +171,7 @@ public class AdminReportActivity extends BaseBarActivity {
binding.disableAction.setOnClickListener(v -> { binding.disableAction.setOnClickListener(v -> {
if (adminAccount.disabled) { if (adminAccount.disabled) {
adminVM.enable(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id) adminVM.enable(MainActivity.currentInstance, MainActivity.currentToken, account.id)
.observe(AdminReportActivity.this, adminAccountResult -> { .observe(AdminReportActivity.this, adminAccountResult -> {
if (adminAccountResult != null) { if (adminAccountResult != null) {
adminAccount.disabled = false; adminAccount.disabled = false;
@ -179,7 +183,7 @@ public class AdminReportActivity extends BaseBarActivity {
}); });
} else { } else {
adminVM.performAction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, "disable ", null, null, null, null); adminVM.performAction(MainActivity.currentInstance, MainActivity.currentToken, account.id, "disable ", null, null, null, null);
adminAccount.disabled = true; adminAccount.disabled = true;
binding.disableAction.setText(R.string.undisable); binding.disableAction.setText(R.string.undisable);
binding.disabled.setText(R.string.yes); binding.disabled.setText(R.string.yes);
@ -188,7 +192,7 @@ public class AdminReportActivity extends BaseBarActivity {
binding.approveAction.setOnClickListener(v -> { binding.approveAction.setOnClickListener(v -> {
if (adminAccount.approved) { if (adminAccount.approved) {
adminVM.reject(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id) adminVM.reject(MainActivity.currentInstance, MainActivity.currentToken, account.id)
.observe(AdminReportActivity.this, adminAccountResult -> { .observe(AdminReportActivity.this, adminAccountResult -> {
if (adminAccountResult != null) { if (adminAccountResult != null) {
adminAccount.approved = false; adminAccount.approved = false;
@ -200,7 +204,7 @@ public class AdminReportActivity extends BaseBarActivity {
}); });
} else { } else {
adminVM.approve(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id); adminVM.approve(MainActivity.currentInstance, MainActivity.currentToken, account.id);
adminAccount.approved = true; adminAccount.approved = true;
binding.approveAction.setText(R.string.reject); binding.approveAction.setText(R.string.reject);
binding.approved.setText(R.string.yes); binding.approved.setText(R.string.yes);
@ -209,7 +213,7 @@ public class AdminReportActivity extends BaseBarActivity {
binding.silenceAction.setOnClickListener(v -> { binding.silenceAction.setOnClickListener(v -> {
if (adminAccount.disabled) { if (adminAccount.disabled) {
adminVM.unsilence(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id) adminVM.unsilence(MainActivity.currentInstance, MainActivity.currentToken, account.id)
.observe(AdminReportActivity.this, adminAccountResult -> { .observe(AdminReportActivity.this, adminAccountResult -> {
if (adminAccountResult != null) { if (adminAccountResult != null) {
adminAccount.silenced = false; adminAccount.silenced = false;
@ -220,7 +224,7 @@ public class AdminReportActivity extends BaseBarActivity {
} }
}); });
} else { } else {
adminVM.performAction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, "silence", null, null, null, null); adminVM.performAction(MainActivity.currentInstance, MainActivity.currentToken, account.id, "silence", null, null, null, null);
adminAccount.silenced = true; adminAccount.silenced = true;
binding.disableAction.setText(R.string.unsilence); binding.disableAction.setText(R.string.unsilence);
binding.disabled.setText(R.string.yes); binding.disabled.setText(R.string.yes);
@ -229,7 +233,7 @@ public class AdminReportActivity extends BaseBarActivity {
binding.suspendAction.setOnClickListener(v -> { binding.suspendAction.setOnClickListener(v -> {
if (adminAccount.disabled) { if (adminAccount.disabled) {
adminVM.unsuspend(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id) adminVM.unsuspend(MainActivity.currentInstance, MainActivity.currentToken, account.id)
.observe(AdminReportActivity.this, adminAccountResult -> { .observe(AdminReportActivity.this, adminAccountResult -> {
if (adminAccountResult != null) { if (adminAccountResult != null) {
adminAccount.suspended = false; adminAccount.suspended = false;
@ -240,7 +244,7 @@ public class AdminReportActivity extends BaseBarActivity {
} }
}); });
} else { } else {
adminVM.performAction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, "suspend", null, null, null, null); adminVM.performAction(MainActivity.currentInstance, MainActivity.currentToken, account.id, "suspend", null, null, null, null);
adminAccount.suspended = true; adminAccount.suspended = true;
binding.disableAction.setText(R.string.unsuspend); binding.disableAction.setText(R.string.unsuspend);
binding.suspended.setText(R.string.yes); binding.suspended.setText(R.string.yes);
@ -288,7 +292,7 @@ public class AdminReportActivity extends BaseBarActivity {
} }
); );
//Load header //Load header
MastodonHelper.loadProfileMediaMastodon(AdminReportActivity.this, binding.bannerPp, account, MastodonHelper.MediaAccountType.HEADER); MastodonHelper.loadProfileMediaMastodon(binding.bannerPp, account, MastodonHelper.MediaAccountType.HEADER);
//Redraws icon for locked accounts //Redraws icon for locked accounts
final float scale = getResources().getDisplayMetrics().density; final float scale = getResources().getDisplayMetrics().density;
if (account.locked) { if (account.locked) {
@ -311,7 +315,7 @@ public class AdminReportActivity extends BaseBarActivity {
final SpannableString content = new SpannableString(getString(R.string.disclaimer_full)); final SpannableString content = new SpannableString(getString(R.string.disclaimer_full));
content.setSpan(new UnderlineSpan(), 0, content.length(), 0); content.setSpan(new UnderlineSpan(), 0, content.length(), 0);
content.setSpan(new ForegroundColorSpan(ThemeHelper.getAttColor(this, R.attr.colorPrimary)), 0, content.length(), content.setSpan(new ForegroundColorSpan(ContextCompat.getColor(AdminReportActivity.this, R.color.cyanea_accent_reference)), 0, content.length(),
Spanned.SPAN_INCLUSIVE_EXCLUSIVE); Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//This account was moved to another one //This account was moved to another one
@ -327,10 +331,8 @@ public class AdminReportActivity extends BaseBarActivity {
binding.accountMoved.setMovementMethod(LinkMovementMethod.getInstance()); binding.accountMoved.setMovementMethod(LinkMovementMethod.getInstance());
} }
binding.accountDn.setText(
account.getSpanDisplayName(AdminReportActivity.this, binding.accountDn.setText(account.span_display_name != null ? account.span_display_name : account.display_name, TextView.BufferType.SPANNABLE);
new WeakReference<>(binding.accountDn)),
TextView.BufferType.SPANNABLE);
binding.accountUn.setText(String.format("@%s", account.acct)); binding.accountUn.setText(String.format("@%s", account.acct));
binding.accountUn.setOnLongClickListener(v -> { binding.accountUn.setOnLongClickListener(v -> {
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
@ -383,11 +385,12 @@ public class AdminReportActivity extends BaseBarActivity {
binding.instanceInfo.setVisibility(View.VISIBLE); binding.instanceInfo.setVisibility(View.VISIBLE);
binding.instanceInfo.setOnClickListener(v -> { binding.instanceInfo.setOnClickListener(v -> {
InstanceProfileActivity instanceProfileActivity = new InstanceProfileActivity(); Intent intent = new Intent(AdminReportActivity.this, InstanceProfileActivity.class);
Bundle b = new Bundle(); Bundle b = new Bundle();
b.putString(Helper.ARG_INSTANCE, finalAccountInstance); b.putString(Helper.ARG_INSTANCE, finalAccountInstance);
instanceProfileActivity.setArguments(b); intent.putExtras(b);
instanceProfileActivity.show(getSupportFragmentManager(), null); startActivity(intent);
}); });
} }
}); });

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.activities; package app.fedilab.android.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -18,20 +18,20 @@ package app.fedilab.android.mastodon.activities;
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;
import android.content.SharedPreferences; import android.graphics.drawable.ColorDrawable;
import android.os.Bundle; import android.os.Bundle;
import android.util.TypedValue;
import android.view.MenuItem; import android.view.MenuItem;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
import androidx.preference.PreferenceManager; import androidx.core.content.ContextCompat;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.EmojiInstance;
import app.fedilab.android.databinding.ActivityAnnouncementBinding; import app.fedilab.android.databinding.ActivityAnnouncementBinding;
import app.fedilab.android.mastodon.client.entities.api.EmojiInstance; import app.fedilab.android.exception.DBException;
import app.fedilab.android.mastodon.exception.DBException; import app.fedilab.android.helper.Helper;
import app.fedilab.android.mastodon.helper.Helper; import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonAnnouncement; import app.fedilab.android.ui.fragment.timeline.FragmentMastodonAnnouncement;
public class AnnouncementActivity extends BaseActivity { public class AnnouncementActivity extends BaseActivity {
@ -40,7 +40,7 @@ public class AnnouncementActivity extends BaseActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyTheme(this);
ActivityAnnouncementBinding binding = ActivityAnnouncementBinding.inflate(getLayoutInflater()); ActivityAnnouncementBinding binding = ActivityAnnouncementBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
@ -50,15 +50,14 @@ public class AnnouncementActivity extends BaseActivity {
//Remove title //Remove title
if (actionBar != null) { if (actionBar != null) {
actionBar.setDisplayShowTitleEnabled(false); actionBar.setDisplayShowTitleEnabled(false);
actionBar.setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
binding.title.setText(R.string.action_announcements); binding.title.setText(R.string.action_announcements);
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true);
} }
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this);
float scale = sharedpreferences.getFloat(getString(R.string.SET_FONT_SCALE), 1.1f);
binding.title.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18 * 1.1f / scale);
Helper.addFragment(getSupportFragmentManager(), R.id.nav_host_fragment_tags, new FragmentMastodonAnnouncement(), null, null, null); Helper.addFragment(getSupportFragmentManager(), R.id.nav_host_fragment_tags, new FragmentMastodonAnnouncement(), null, null, null);
if (emojis == null || !emojis.containsKey(currentInstance)) { if (emojis == null || !emojis.containsKey(currentInstance)) {
new Thread(() -> { new Thread(() -> {

View file

@ -1,5 +1,5 @@
package app.fedilab.android.activities; package app.fedilab.android.activities;
/* Copyright 2023 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
* *
@ -15,28 +15,32 @@ package app.fedilab.android.activities;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import android.annotation.SuppressLint;
import android.os.Bundle; import android.os.Bundle;
import android.view.View;
import app.fedilab.android.databinding.ActivityMainPeertubeBinding; import androidx.annotation.Nullable;
import app.fedilab.android.mastodon.activities.BaseActivity;
import com.jaredrummler.cyanea.app.CyaneaAppCompatActivity;
import com.vanniktech.emoji.EmojiManager;
import com.vanniktech.emoji.one.EmojiOneProvider;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.helper.ThemeHelper;
public class PeertubeBaseMainActivity extends BaseActivity { @SuppressLint("Registered")
public class BaseActivity extends CyaneaAppCompatActivity {
protected ActivityMainPeertubeBinding binding; static {
Helper.installProvider();
EmojiManager.install(new EmojiOneProvider());
}
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
binding = ActivityMainPeertubeBinding.inflate(getLayoutInflater()); ThemeHelper.adjustFontScale(this, getResources().getConfiguration());
View view = binding.getRoot(); Helper.setLocale(this);
setContentView(view);
}
//Method for discovering cast devices
public void discoverCast() {
} }
} }

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.client.entities.app; package app.fedilab.android.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -14,21 +14,23 @@ 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 com.google.gson.annotations.SerializedName; import android.annotation.SuppressLint;
import java.io.Serializable; import com.jaredrummler.cyanea.app.CyaneaFragmentActivity;
import java.util.List; import com.vanniktech.emoji.EmojiManager;
import com.vanniktech.emoji.one.EmojiOneProvider;
import app.fedilab.android.helper.Helper;
public class Quotes implements Serializable { @SuppressLint("Registered")
public class BaseFragmentActivity extends CyaneaFragmentActivity {
@SerializedName("quotes")
public List<Quote> quotes;
public static class Quote implements Serializable { static {
@SerializedName("author") Helper.installProvider();
public String author; EmojiManager.install(new EmojiOneProvider());
@SerializedName("content")
public String content;
} }
} }

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.activities; package app.fedilab.android.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -15,6 +15,7 @@ package app.fedilab.android.mastodon.activities;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
@ -24,27 +25,24 @@ import android.view.MenuItem;
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.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; 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.client.entities.app.Account;
import app.fedilab.android.client.entities.app.BaseAccount;
import app.fedilab.android.client.entities.app.CacheAccount;
import app.fedilab.android.databinding.ActivityCacheBinding; import app.fedilab.android.databinding.ActivityCacheBinding;
import app.fedilab.android.mastodon.client.entities.app.Account; import app.fedilab.android.helper.CacheHelper;
import app.fedilab.android.mastodon.client.entities.app.BaseAccount; import app.fedilab.android.helper.Helper;
import app.fedilab.android.mastodon.client.entities.app.CacheAccount; import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.mastodon.client.entities.app.StatusCache; import app.fedilab.android.ui.drawer.CacheAdapter;
import app.fedilab.android.mastodon.client.entities.app.StatusDraft;
import app.fedilab.android.mastodon.exception.DBException;
import app.fedilab.android.mastodon.helper.CacheHelper;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.ui.drawer.CacheAdapter;
public class CacheActivity extends BaseBarActivity { public class CacheActivity extends BaseActivity {
private ActivityCacheBinding binding; private ActivityCacheBinding binding;
private List<CacheAccount> cacheAccounts; private List<CacheAccount> cacheAccounts;
@ -53,11 +51,12 @@ public class CacheActivity extends BaseBarActivity {
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyThemeBar(this);
binding = ActivityCacheBinding.inflate(getLayoutInflater()); binding = ActivityCacheBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
CacheHelper.getCacheValues(CacheActivity.this, size -> { CacheHelper.getCacheValues(CacheActivity.this, size -> {
if (size > 0) { if (size > 0) {
@ -68,35 +67,13 @@ public class CacheActivity extends BaseBarActivity {
new Thread(() -> { new Thread(() -> {
List<BaseAccount> accounts; List<BaseAccount> accounts = new Account(CacheActivity.this).getPushNotificationAccounts();
cacheAccounts = new ArrayList<>(); cacheAccounts = new ArrayList<>();
try {
accounts = new Account(CacheActivity.this).getAll();
for (BaseAccount baseAccount : accounts) { for (BaseAccount baseAccount : accounts) {
CacheAccount cacheAccount = new CacheAccount(); CacheAccount cacheAccount = new CacheAccount();
cacheAccount.account = baseAccount; cacheAccount.account = baseAccount;
try {
cacheAccount.home_cache_count = new StatusCache(CacheActivity.this).countHome(baseAccount);
} catch (DBException e) {
e.printStackTrace();
}
try {
cacheAccount.other_cache_count = new StatusCache(CacheActivity.this).countOther(baseAccount);
} catch (DBException e) {
e.printStackTrace();
}
try {
cacheAccount.draft_count = new StatusDraft(CacheActivity.this).count(baseAccount);
} catch (DBException e) {
e.printStackTrace();
}
cacheAccounts.add(cacheAccount); cacheAccounts.add(cacheAccount);
} }
} catch (DBException e) {
e.printStackTrace();
}
Handler mainHandler = new Handler(Looper.getMainLooper()); Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> { Runnable myRunnable = () -> {
cacheAdapter = new CacheAdapter(cacheAccounts); cacheAdapter = new CacheAdapter(cacheAccounts);
@ -121,7 +98,7 @@ public class CacheActivity extends BaseBarActivity {
finish(); finish();
return true; return true;
} else if (item.getItemId() == R.id.action_clear) { } else if (item.getItemId() == R.id.action_clear) {
AlertDialog.Builder deleteConfirm = new MaterialAlertDialogBuilder(CacheActivity.this); AlertDialog.Builder deleteConfirm = new AlertDialog.Builder(CacheActivity.this, Helper.dialogStyle());
deleteConfirm.setTitle(getString(R.string.delete_cache)); deleteConfirm.setTitle(getString(R.string.delete_cache));
deleteConfirm.setMessage(getString(R.string.delete_cache_message)); deleteConfirm.setMessage(getString(R.string.delete_cache_message));
deleteConfirm.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss()); deleteConfirm.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
@ -131,21 +108,7 @@ public class CacheActivity extends BaseBarActivity {
size = size / 1000000.0f; size = size / 1000000.0f;
} }
binding.fileCacheSize.setText(String.format("%s %s", String.format(Locale.getDefault(), "%.2f", size), getString(R.string.cache_units))); binding.fileCacheSize.setText(String.format("%s %s", String.format(Locale.getDefault(), "%.2f", size), getString(R.string.cache_units)));
AlertDialog.Builder restartBuilder = new MaterialAlertDialogBuilder(CacheActivity.this); cacheAdapter.notifyDataSetChanged();
restartBuilder.setMessage(getString(R.string.restart_the_app));
restartBuilder.setNegativeButton(R.string.no, (dialogRestart, whichRestart) -> {
recreate();
dialogRestart.dismiss();
});
restartBuilder.setPositiveButton(R.string.restart, (dialogRestart, whichRestart) -> {
dialogRestart.dismiss();
Helper.restart(CacheActivity.this);
});
AlertDialog alertDialog = restartBuilder.create();
if (!isFinishing()) {
alertDialog.show();
}
})); }));
dialog.dismiss(); dialog.dismiss();
}); });

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.activities; package app.fedilab.android.activities;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -18,6 +18,7 @@ package app.fedilab.android.mastodon.activities;
import static app.fedilab.android.BaseMainActivity.currentAccount; 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;
import static app.fedilab.android.ui.drawer.ComposeAdapter.prepareDraft;
import android.Manifest; import android.Manifest;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
@ -25,8 +26,8 @@ import android.content.BroadcastReceiver;
import android.content.ClipData; import android.content.ClipData;
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.graphics.drawable.ColorDrawable;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
@ -34,7 +35,6 @@ import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
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;
@ -48,15 +48,11 @@ 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.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.work.Data; import androidx.work.Data;
import androidx.work.OneTimeWorkRequest; import androidx.work.OneTimeWorkRequest;
import androidx.work.OutOfQuotaPolicy;
import androidx.work.WorkManager; import androidx.work.WorkManager;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.io.File; import java.io.File;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
@ -64,69 +60,59 @@ 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.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit; 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;
import app.fedilab.android.activities.MainActivity; import app.fedilab.android.client.entities.api.Attachment;
import app.fedilab.android.client.entities.api.Context;
import app.fedilab.android.client.entities.api.EmojiInstance;
import app.fedilab.android.client.entities.api.Mention;
import app.fedilab.android.client.entities.api.ScheduledStatus;
import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.client.entities.app.Account;
import app.fedilab.android.client.entities.app.BaseAccount;
import app.fedilab.android.client.entities.app.StatusDraft;
import app.fedilab.android.databinding.ActivityPaginationBinding; import app.fedilab.android.databinding.ActivityPaginationBinding;
import app.fedilab.android.databinding.PopupContactBinding; import app.fedilab.android.databinding.PopupContactBinding;
import app.fedilab.android.mastodon.client.entities.api.Account; import app.fedilab.android.exception.DBException;
import app.fedilab.android.mastodon.client.entities.api.Attachment; import app.fedilab.android.helper.DividerDecorationSimple;
import app.fedilab.android.mastodon.client.entities.api.Context; import app.fedilab.android.helper.Helper;
import app.fedilab.android.mastodon.client.entities.api.EmojiInstance; import app.fedilab.android.helper.MastodonHelper;
import app.fedilab.android.mastodon.client.entities.api.Instance; import app.fedilab.android.helper.MediaHelper;
import app.fedilab.android.mastodon.client.entities.api.Mention; import app.fedilab.android.helper.SpannableHelper;
import app.fedilab.android.mastodon.client.entities.api.ScheduledStatus; import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.mastodon.client.entities.api.Status; import app.fedilab.android.jobs.ScheduleThreadWorker;
import app.fedilab.android.mastodon.client.entities.app.BaseAccount; import app.fedilab.android.services.PostMessageService;
import app.fedilab.android.mastodon.client.entities.app.StatusDraft; import app.fedilab.android.services.ThreadMessageService;
import app.fedilab.android.mastodon.exception.DBException; import app.fedilab.android.ui.drawer.AccountsReplyAdapter;
import app.fedilab.android.mastodon.helper.DividerDecorationSimple; import app.fedilab.android.ui.drawer.ComposeAdapter;
import app.fedilab.android.mastodon.helper.Helper; import app.fedilab.android.viewmodel.mastodon.AccountsVM;
import app.fedilab.android.mastodon.helper.MastodonHelper; import app.fedilab.android.viewmodel.mastodon.StatusesVM;
import app.fedilab.android.mastodon.helper.MediaHelper;
import app.fedilab.android.mastodon.interfaces.OnDownloadInterface;
import app.fedilab.android.mastodon.jobs.ComposeWorker;
import app.fedilab.android.mastodon.jobs.ScheduleThreadWorker;
import app.fedilab.android.mastodon.services.ThreadMessageService;
import app.fedilab.android.mastodon.ui.drawer.AccountsReplyAdapter;
import app.fedilab.android.mastodon.ui.drawer.ComposeAdapter;
import app.fedilab.android.mastodon.viewmodel.mastodon.AccountsVM;
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 { public class ComposeActivity extends BaseActivity implements ComposeAdapter.ManageDrafts, AccountsReplyAdapter.ActionDone {
public static final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 754; public static final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 754;
public static final int REQUEST_AUDIO_PERMISSION_RESULT = 1653; public static final int REQUEST_AUDIO_PERMISSION_RESULT = 1653;
public static final int PICK_MEDIA = 5700; public static final int PICK_MEDIA = 5700;
public static final int TAKE_PHOTO = 5600; public static final int TAKE_PHOTO = 5600;
private final Timer timer = new Timer();
private List<Status> statusList; private List<Status> statusList;
private Status statusReply, statusMention, statusQuoted; private Status statusReply, statusMention;
private StatusDraft statusDraft; private StatusDraft statusDraft;
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) {
String imgpath = intent.getStringExtra("imgpath"); String imgpath = intent.getStringExtra("imgpath");
float focusX = intent.getFloatExtra("focusX", -2);
float focusY = intent.getFloatExtra("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.size() > 0) { 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.equalsIgnoreCase(imgpath)) {
if (focusX != -2) {
attachment.focus = focusX + "," + focusY;
}
composeAdapter.notifyItemChanged(position); composeAdapter.notifyItemChanged(position);
break; break;
} }
@ -137,71 +123,224 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
} }
} }
}; };
private boolean promptSaveDraft;
private boolean restoredDraft;
private List<Attachment> sharedAttachments;
private ActivityPaginationBinding binding; private ActivityPaginationBinding binding;
private BaseAccount account; private BaseAccount account;
private String instance, token; private String instance, token;
private Uri photoFileUri; private Uri photoFileUri;
private ScheduledStatus scheduledStatus; private ScheduledStatus scheduledStatus;
private String visibility; private String visibility;
private Account accountMention; private app.fedilab.android.client.entities.api.Account accountMention;
private String statusReplyId; private String statusReplyId;
private Account mentionBooster; private app.fedilab.android.client.entities.api.Account mentionBooster;
private String sharedSubject, sharedContent, sharedTitle, sharedDescription, shareURL, sharedUrlMedia;
private String editMessageId;
private static int visibilityToNumber(String visibility) { @Override
switch (visibility) { protected void onCreate(Bundle savedInstanceState) {
case "public": super.onCreate(savedInstanceState);
return 3; ThemeHelper.applyTheme(this);
case "unlisted": binding = ActivityPaginationBinding.inflate(getLayoutInflater());
return 2; setContentView(binding.getRoot());
case "private": setSupportActionBar(binding.toolbar);
return 1; ActionBar actionBar = getSupportActionBar();
case "direct": //Remove title
return 0; if (actionBar != null) {
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
return 3; if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
} }
statusList = new ArrayList<>();
Bundle b = getIntent().getExtras();
if (b != null) {
statusReply = (Status) b.getSerializable(Helper.ARG_STATUS_REPLY);
statusDraft = (StatusDraft) b.getSerializable(Helper.ARG_STATUS_DRAFT);
scheduledStatus = (ScheduledStatus) b.getSerializable(Helper.ARG_STATUS_SCHEDULED);
statusReplyId = b.getString(Helper.ARG_STATUS_REPLY_ID);
statusMention = (Status) b.getSerializable(Helper.ARG_STATUS_MENTION);
account = (Account) b.getSerializable(Helper.ARG_ACCOUNT);
instance = b.getString(Helper.ARG_INSTANCE, null);
token = b.getString(Helper.ARG_TOKEN, null);
visibility = b.getString(Helper.ARG_VISIBILITY, null);
mentionBooster = (app.fedilab.android.client.entities.api.Account) b.getSerializable(Helper.ARG_MENTION_BOOSTER);
accountMention = (app.fedilab.android.client.entities.api.Account) b.getSerializable(Helper.ARG_ACCOUNT_MENTION);
}
binding.toolbar.setPopupTheme(Helper.popupStyle());
//Edit a scheduled status from server
if (scheduledStatus != null) {
statusDraft = new StatusDraft();
List<Status> statuses = new ArrayList<>();
Status status = new Status();
status.text = scheduledStatus.params.text;
status.in_reply_to_id = scheduledStatus.params.in_reply_to_id;
status.poll = scheduledStatus.params.poll;
private static String visibilityToString(int visibility) { if (scheduledStatus.params.media_ids != null && scheduledStatus.params.media_ids.size() > 0) {
switch (visibility) { status.media_attachments = new ArrayList<>();
case 3: new Thread(() -> {
return "public"; StatusesVM statusesVM = new ViewModelProvider(ComposeActivity.this).get(StatusesVM.class);
case 2: for (String attachmentId : scheduledStatus.params.media_ids) {
return "unlisted"; statusesVM.getAttachment(instance, token, attachmentId)
case 1: .observe(ComposeActivity.this, attachment -> status.media_attachments.add(attachment));
return "private";
case 0:
return "direct";
} }
return "public"; }).start();
} }
status.sensitive = scheduledStatus.params.sensitive;
status.spoiler_text = scheduledStatus.params.spoiler_text;
status.visibility = scheduledStatus.params.visibility;
statusDraft.statusDraftList = statuses;
}
if (account == null) {
account = currentAccount;
}
if (account == null) {
Toasty.error(ComposeActivity.this, getString(R.string.toast_error), Toasty.LENGTH_SHORT).show();
finish();
return;
}
if (instance == null) {
instance = account.instance;
}
if (token == null) {
token = account.token;
}
if (emojis == null || !emojis.containsKey(currentInstance)) {
new Thread(() -> {
try {
emojis.put(currentInstance, new EmojiInstance(ComposeActivity.this).getEmojiList(currentInstance));
} catch (DBException e) {
e.printStackTrace();
}
}).start();
}
StatusesVM statusesVM = new ViewModelProvider(ComposeActivity.this).get(StatusesVM.class);
//Empty compose
List<Status> statusDraftList = new ArrayList<>();
Status status = new Status();
statusDraftList.add(status);
public static String getVisibility(String defaultVisibility) { if (statusReplyId != null && statusDraft != null) {//Delete and redraft
int tootVisibility = visibilityToNumber(defaultVisibility); statusesVM.getStatus(currentInstance, BaseMainActivity.currentToken, statusReplyId)
if (currentAccount != null && currentAccount.mastodon_account != null && currentAccount.mastodon_account.source != null) { .observe(ComposeActivity.this, status1 -> {
int userVisibility = visibilityToNumber(currentAccount.mastodon_account.source.privacy); if (status1 != null) {
if (tootVisibility > userVisibility) { statusesVM.getContext(currentInstance, BaseMainActivity.currentToken, statusReplyId)
return visibilityToString(userVisibility); .observe(ComposeActivity.this, statusContext -> {
if (statusContext != null) {
initializeContextRedraftView(statusContext, status1);
} else { } else {
return visibilityToString(tootVisibility); Helper.sendToastMessage(getApplication(), Helper.RECEIVE_TOAST_TYPE_ERROR, getString(R.string.toast_error));
}
});
} else {
Helper.sendToastMessage(getApplication(), Helper.RECEIVE_TOAST_TYPE_ERROR, getString(R.string.toast_error));
}
});
} else if (statusDraft != null) {//Restore a draft with all messages
new Thread(() -> {
if (statusDraft.statusReplyList != null) {
statusDraft.statusReplyList = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusDraft.statusReplyList);
}
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> {
if (statusDraft.statusReplyList != null) {
statusList.addAll(statusDraft.statusReplyList);
binding.recyclerView.addItemDecoration(new DividerDecorationSimple(ComposeActivity.this, statusList));
}
int statusCount = statusList.size();
statusList.addAll(statusDraft.statusDraftList);
composeAdapter = new ComposeAdapter(statusList, statusCount, account, accountMention, visibility);
composeAdapter.manageDrafts = this;
LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this);
binding.recyclerView.setLayoutManager(mLayoutManager);
binding.recyclerView.setAdapter(composeAdapter);
binding.recyclerView.scrollToPosition(composeAdapter.getItemCount() - 1);
};
mainHandler.post(myRunnable);
}).start();
} else if (statusReply != null) {
new Thread(() -> {
statusReply = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusReply);
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> {
statusList.add(statusReply);
int statusCount = statusList.size();
statusDraftList.get(0).in_reply_to_id = statusReply.id;
//We change order for mentions
//At first place the account that has been mentioned if it's not our
statusDraftList.get(0).mentions = new ArrayList<>();
if (!statusReply.account.acct.equalsIgnoreCase(currentAccount.mastodon_account.acct)) {
Mention mention = new Mention();
mention.acct = "@" + statusReply.account.acct;
mention.url = statusReply.account.url;
mention.username = statusReply.account.username;
statusDraftList.get(0).mentions.add(mention);
}
//There are other mentions to
if (statusReply.mentions != null && statusReply.mentions.size() > 0) {
for (Mention mentionTmp : statusReply.mentions) {
if (!mentionTmp.acct.equalsIgnoreCase(statusReply.account.acct) && !mentionTmp.acct.equalsIgnoreCase(currentAccount.mastodon_account.acct)) {
statusDraftList.get(0).mentions.add(mentionTmp);
} }
} }
return defaultVisibility; }
if (mentionBooster != null) {
Mention mention = new Mention();
mention.acct = mentionBooster.acct;
mention.url = mentionBooster.url;
mention.username = mentionBooster.username;
boolean present = false;
for (Mention mentionTmp : statusDraftList.get(0).mentions) {
if (mentionTmp.acct.equalsIgnoreCase(mentionBooster.acct)) {
present = true;
break;
}
}
if (!present) {
statusDraftList.get(0).mentions.add(mention);
}
}
if (statusReply.spoiler_text != null) {
statusDraftList.get(0).spoiler_text = statusReply.spoiler_text;
}
//StatusDraftList at this point should only have one element
statusList.addAll(statusDraftList);
composeAdapter = new ComposeAdapter(statusList, statusCount, account, accountMention, visibility);
composeAdapter.manageDrafts = this;
LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this);
binding.recyclerView.setLayoutManager(mLayoutManager);
binding.recyclerView.setAdapter(composeAdapter);
statusesVM.getContext(currentInstance, BaseMainActivity.currentToken, statusReply.id)
.observe(ComposeActivity.this, this::initializeContextView);
};
mainHandler.post(myRunnable);
}).start();
} else {
//Compose without replying
statusList.addAll(statusDraftList);
composeAdapter = new ComposeAdapter(statusList, 0, account, accountMention, visibility);
composeAdapter.manageDrafts = this;
LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this);
binding.recyclerView.setLayoutManager(mLayoutManager);
binding.recyclerView.setAdapter(composeAdapter);
if (statusMention != null) {
composeAdapter.loadMentions(statusMention);
}
}
MastodonHelper.loadPPMastodon(binding.profilePicture, account.mastodon_account);
LocalBroadcastManager.getInstance(this)
.registerReceiver(imageReceiver,
new IntentFilter(Helper.INTENT_SEND_MODIFIED_IMAGE));
} }
@Override @Override
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
if (timer != null) {
timer.cancel();
}
LocalBroadcastManager.getInstance(this) LocalBroadcastManager.getInstance(this)
.unregisterReceiver(imageReceiver); .unregisterReceiver(imageReceiver);
} }
@Override @Override
@ -211,11 +350,10 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
private void storeDraftWarning() { private void storeDraftWarning() {
if (statusDraft == null) { if (statusDraft == null) {
statusDraft = ComposeAdapter.prepareDraft(statusList, composeAdapter, account.instance, account.user_id); statusDraft = prepareDraft(statusList, composeAdapter, account.instance, account.user_id);
} }
if (canBeSent(statusDraft)) { if (canBeSent(statusDraft)) {
if (promptSaveDraft) { AlertDialog.Builder alt_bld = new AlertDialog.Builder(ComposeActivity.this, Helper.dialogStyle());
AlertDialog.Builder alt_bld = new MaterialAlertDialogBuilder(ComposeActivity.this);
alt_bld.setMessage(R.string.save_draft); alt_bld.setMessage(R.string.save_draft);
alt_bld.setPositiveButton(R.string.save, (dialog, id) -> { alt_bld.setPositiveButton(R.string.save, (dialog, id) -> {
dialog.dismiss(); dialog.dismiss();
@ -224,26 +362,11 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
}); });
alt_bld.setNegativeButton(R.string.no, (dialog, id) -> { alt_bld.setNegativeButton(R.string.no, (dialog, id) -> {
try {
new StatusDraft(ComposeActivity.this).removeDraft(statusDraft);
} catch (DBException e) {
e.printStackTrace();
}
dialog.dismiss(); dialog.dismiss();
finish(); finish();
}); });
AlertDialog alert = alt_bld.create(); AlertDialog alert = alt_bld.create();
alert.show(); alert.show();
} else {
if (!restoredDraft) {
try {
new StatusDraft(ComposeActivity.this).removeDraft(statusDraft);
} catch (DBException e) {
e.printStackTrace();
}
}
finish();
}
} else { } else {
finish(); finish();
} }
@ -263,8 +386,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
statusList.addAll(0, context.ancestors); statusList.addAll(0, context.ancestors);
composeAdapter.setStatusCount(context.ancestors.size() + 1); composeAdapter.setStatusCount(context.ancestors.size() + 1);
composeAdapter.notifyItemRangeInserted(0, context.ancestors.size()); composeAdapter.notifyItemRangeInserted(0, context.ancestors.size());
composeAdapter.notifyItemChanged(context.ancestors.size() + 1);
composeAdapter.notifyItemRangeChanged(0, statusList.size());
if (binding.recyclerView.getItemDecorationCount() > 0) { if (binding.recyclerView.getItemDecorationCount() > 0) {
for (int i = 0; i < binding.recyclerView.getItemDecorationCount(); i++) { for (int i = 0; i < binding.recyclerView.getItemDecorationCount(); i++) {
binding.recyclerView.removeItemDecorationAt(i); binding.recyclerView.removeItemDecorationAt(i);
@ -274,6 +396,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
binding.recyclerView.scrollToPosition(composeAdapter.getItemCount() - 1); binding.recyclerView.scrollToPosition(composeAdapter.getItemCount() - 1);
} }
/** /**
* Intialize the common view for the context * Intialize the common view for the context
* *
@ -289,8 +412,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
statusList.addAll(0, context.ancestors); statusList.addAll(0, context.ancestors);
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);
composeAdapter.promptDraftListener = this;
composeAdapter.manageDrafts = this; composeAdapter.manageDrafts = this;
LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this); LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this);
binding.recyclerView.setLayoutManager(mLayoutManager); binding.recyclerView.setLayoutManager(mLayoutManager);
@ -300,6 +422,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
binding.recyclerView.scrollToPosition(composeAdapter.getItemCount() - 1); binding.recyclerView.scrollToPosition(composeAdapter.getItemCount() - 1);
} }
@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.
@ -307,6 +430,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
return true; return true;
} }
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
@ -316,7 +440,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
} else if (item.getItemId() == R.id.action_photo_camera) { } else if (item.getItemId() == R.id.action_photo_camera) {
photoFileUri = MediaHelper.dispatchTakePictureIntent(ComposeActivity.this); photoFileUri = MediaHelper.dispatchTakePictureIntent(ComposeActivity.this);
} else if (item.getItemId() == R.id.action_contacts) { } else if (item.getItemId() == R.id.action_contacts) {
AlertDialog.Builder builderSingle = new MaterialAlertDialogBuilder(ComposeActivity.this); AlertDialog.Builder builderSingle = new AlertDialog.Builder(ComposeActivity.this, Helper.dialogStyle());
builderSingle.setTitle(getString(R.string.select_accounts)); builderSingle.setTitle(getString(R.string.select_accounts));
PopupContactBinding popupContactBinding = PopupContactBinding.inflate(getLayoutInflater(), new LinearLayout(ComposeActivity.this), false); PopupContactBinding popupContactBinding = PopupContactBinding.inflate(getLayoutInflater(), new LinearLayout(ComposeActivity.this), false);
@ -392,7 +516,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
} }
} else if (item.getItemId() == R.id.action_schedule) { } else if (item.getItemId() == R.id.action_schedule) {
if (statusDraft == null) { if (statusDraft == null) {
statusDraft = ComposeAdapter.prepareDraft(statusList, composeAdapter, account.instance, account.user_id); statusDraft = prepareDraft(statusList, composeAdapter, account.instance, account.user_id);
} }
if (canBeSent(statusDraft)) { if (canBeSent(statusDraft)) {
MediaHelper.scheduleMessage(ComposeActivity.this, date -> storeDraft(true, date)); MediaHelper.scheduleMessage(ComposeActivity.this, date -> storeDraft(true, date));
@ -403,22 +527,23 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
return true; return true;
} }
private void onRetrieveContact(PopupContactBinding popupContactBinding, List<Account> accounts) {
popupContactBinding.loader.setVisibility(View.GONE); private void onRetrieveContact(PopupContactBinding binding, List<app.fedilab.android.client.entities.api.Account> accounts) {
binding.loader.setVisibility(View.GONE);
if (accounts == null) { if (accounts == null) {
accounts = new ArrayList<>(); accounts = new ArrayList<>();
} }
List<Boolean> checkedValues = new ArrayList<>(); List<Boolean> checkedValues = new ArrayList<>();
List<Account> contacts = new ArrayList<>(accounts); List<app.fedilab.android.client.entities.api.Account> contacts = new ArrayList<>(accounts);
for (Account account : contacts) { for (app.fedilab.android.client.entities.api.Account account : contacts) {
checkedValues.add(composeAdapter.getLastComposeContent().contains("@" + account.acct)); checkedValues.add(composeAdapter.getLastComposeContent().contains("@" + account.acct));
} }
AccountsReplyAdapter contactAdapter = new AccountsReplyAdapter(contacts, checkedValues); AccountsReplyAdapter contactAdapter = new AccountsReplyAdapter(contacts, checkedValues);
contactAdapter.actionDone = ComposeActivity.this; binding.lvAccountsSearch.setAdapter(contactAdapter);
popupContactBinding.lvAccountsSearch.setAdapter(contactAdapter); binding.lvAccountsSearch.setLayoutManager(new LinearLayoutManager(ComposeActivity.this));
popupContactBinding.lvAccountsSearch.setLayoutManager(new LinearLayoutManager(ComposeActivity.this));
} }
@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);
@ -441,327 +566,16 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
} }
} }
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityPaginationBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar);
promptSaveDraft = false;
restoredDraft = false;
ActionBar actionBar = getSupportActionBar();
//Remove title
if (actionBar != null) {
actionBar.setDisplayShowTitleEnabled(false);
}
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
}
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this);
float scale = sharedpreferences.getFloat(getString(R.string.SET_FONT_SCALE), 1.1f);
binding.title.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18 * 1.1f / scale);
statusList = new ArrayList<>();
Bundle b = getIntent().getExtras();
if (b != null) {
statusReply = (Status) b.getSerializable(Helper.ARG_STATUS_REPLY);
statusQuoted = (Status) b.getSerializable(Helper.ARG_QUOTED_MESSAGE);
statusDraft = (StatusDraft) b.getSerializable(Helper.ARG_STATUS_DRAFT);
scheduledStatus = (ScheduledStatus) b.getSerializable(Helper.ARG_STATUS_SCHEDULED);
statusReplyId = b.getString(Helper.ARG_STATUS_REPLY_ID);
statusMention = (Status) b.getSerializable(Helper.ARG_STATUS_MENTION);
account = (BaseAccount) b.getSerializable(Helper.ARG_ACCOUNT);
editMessageId = b.getString(Helper.ARG_EDIT_STATUS_ID, null);
instance = b.getString(Helper.ARG_INSTANCE, null);
token = b.getString(Helper.ARG_TOKEN, null);
visibility = b.getString(Helper.ARG_VISIBILITY, null);
if (visibility == null && statusReply != null) {
visibility = getVisibility(statusReply.visibility);
} else if (visibility == null && currentAccount != null && currentAccount.mastodon_account != null && currentAccount.mastodon_account.source != null) {
visibility = currentAccount.mastodon_account.source.privacy;
}
mentionBooster = (Account) b.getSerializable(Helper.ARG_MENTION_BOOSTER);
accountMention = (Account) b.getSerializable(Helper.ARG_ACCOUNT_MENTION);
//Shared elements
sharedAttachments = (ArrayList<Attachment>) b.getSerializable(Helper.ARG_MEDIA_ATTACHMENTS);
sharedUrlMedia = b.getString(Helper.ARG_SHARE_URL_MEDIA);
sharedSubject = b.getString(Helper.ARG_SHARE_SUBJECT, null);
sharedContent = b.getString(Helper.ARG_SHARE_CONTENT, null);
sharedTitle = b.getString(Helper.ARG_SHARE_TITLE, null);
sharedDescription = b.getString(Helper.ARG_SHARE_DESCRIPTION, null);
shareURL = b.getString(Helper.ARG_SHARE_URL, null);
}
if (sharedContent != null && shareURL != null && sharedContent.compareTo(shareURL) == 0) {
sharedContent = "";
}
if (sharedTitle != null && sharedSubject != null && sharedSubject.length() > sharedTitle.length()) {
sharedTitle = sharedSubject;
}
//Edit a scheduled status from server
if (scheduledStatus != null) {
statusDraft = new StatusDraft();
List<Status> statuses = new ArrayList<>();
Status status = new Status();
status.id = Helper.generateIdString();
status.text = scheduledStatus.params.text;
status.in_reply_to_id = scheduledStatus.params.in_reply_to_id;
status.poll = scheduledStatus.params.poll;
if (scheduledStatus.params.media_ids != null && scheduledStatus.params.media_ids.size() > 0) {
status.media_attachments = new ArrayList<>();
new Thread(() -> {
StatusesVM statusesVM = new ViewModelProvider(ComposeActivity.this).get(StatusesVM.class);
for (String attachmentId : scheduledStatus.params.media_ids) {
statusesVM.getAttachment(instance, token, attachmentId)
.observe(ComposeActivity.this, attachment -> status.media_attachments.add(attachment));
}
}).start();
}
status.sensitive = scheduledStatus.params.sensitive;
status.spoiler_text = scheduledStatus.params.spoiler_text;
status.visibility = scheduledStatus.params.visibility;
statusDraft.statusDraftList = statuses;
}
if (account == null) {
account = currentAccount;
}
if (account == null) {
Toasty.error(ComposeActivity.this, getString(R.string.toast_error), Toasty.LENGTH_SHORT).show();
finish();
return;
}
if (instance == null) {
instance = account.instance;
}
if (token == null) {
token = account.token;
}
if (emojis == null || !emojis.containsKey(instance)) {
new Thread(() -> {
try {
emojis.put(instance, new EmojiInstance(ComposeActivity.this).getEmojiList(instance));
} catch (DBException e) {
e.printStackTrace();
}
}).start();
}
if (MainActivity.instanceInfo == null) {
String instanceInfo = sharedpreferences.getString(getString(R.string.INSTANCE_INFO) + instance, null);
if (instanceInfo != null) {
MainActivity.instanceInfo = Instance.restore(instanceInfo);
}
}
StatusesVM statusesVM = new ViewModelProvider(ComposeActivity.this).get(StatusesVM.class);
//Empty compose
List<Status> statusDraftList = new ArrayList<>();
Status status = new Status();
status.id = Helper.generateIdString();
if (statusQuoted != null) {
status.quote_id = statusQuoted.id;
}
statusDraftList.add(status);
if (statusReplyId != null && statusDraft != null) {//Delete and redraft
statusesVM.getStatus(currentInstance, BaseMainActivity.currentToken, statusReplyId)
.observe(ComposeActivity.this, status1 -> {
if (status1 != null) {
statusesVM.getContext(currentInstance, BaseMainActivity.currentToken, statusReplyId)
.observe(ComposeActivity.this, statusContext -> {
if (statusContext != null) {
initializeContextRedraftView(statusContext, status1);
} else {
Helper.sendToastMessage(getApplication(), Helper.RECEIVE_TOAST_TYPE_ERROR, getString(R.string.toast_error));
}
});
} else {
Helper.sendToastMessage(getApplication(), Helper.RECEIVE_TOAST_TYPE_ERROR, getString(R.string.toast_error));
}
});
} else if (statusDraft != null) {//Restore a draft with all messages
restoredDraft = true;
if (statusDraft.statusReplyList != null) {
statusList.addAll(statusDraft.statusReplyList);
binding.recyclerView.addItemDecoration(new DividerDecorationSimple(ComposeActivity.this, statusList));
}
int statusCount = statusList.size();
statusList.addAll(statusDraft.statusDraftList);
composeAdapter = new ComposeAdapter(statusList, statusCount, account, accountMention, visibility, editMessageId);
composeAdapter.manageDrafts = this;
composeAdapter.promptDraftListener = this;
LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this);
binding.recyclerView.setLayoutManager(mLayoutManager);
binding.recyclerView.setAdapter(composeAdapter);
binding.recyclerView.scrollToPosition(composeAdapter.getItemCount() - 1);
} else if (statusReply != null) {
statusList.add(statusReply);
int statusCount = statusList.size();
statusDraftList.get(0).in_reply_to_id = statusReply.id;
//We change order for mentions
//At first place the account that has been mentioned if it's not our
statusDraftList.get(0).mentions = new ArrayList<>();
if (statusReply.account.acct != null && currentAccount.mastodon_account != null && !statusReply.account.acct.equalsIgnoreCase(currentAccount.mastodon_account.acct)) {
Mention mention = new Mention();
mention.acct = "@" + statusReply.account.acct;
mention.url = statusReply.account.url;
mention.username = statusReply.account.username;
statusDraftList.get(0).mentions.add(mention);
}
//There are other mentions to
if (statusReply.mentions != null && statusReply.mentions.size() > 0) {
for (Mention mentionTmp : statusReply.mentions) {
if (statusReply.account.acct != null && !mentionTmp.acct.equalsIgnoreCase(statusReply.account.acct) && currentAccount.mastodon_account != null && !mentionTmp.acct.equalsIgnoreCase(currentAccount.mastodon_account.acct)) {
statusDraftList.get(0).mentions.add(mentionTmp);
}
}
}
if (mentionBooster != null) {
Mention mention = new Mention();
mention.acct = mentionBooster.acct;
mention.url = mentionBooster.url;
mention.username = mentionBooster.username;
boolean present = false;
for (Mention mentionTmp : statusDraftList.get(0).mentions) {
if (mentionTmp.acct.equalsIgnoreCase(mentionBooster.acct)) {
present = true;
break;
}
}
if (!present) {
statusDraftList.get(0).mentions.add(mention);
}
}
if (statusReply.spoiler_text != null) {
statusDraftList.get(0).spoiler_text = statusReply.spoiler_text;
if (statusReply.spoiler_text.trim().length() > 0) {
statusDraftList.get(0).spoilerChecked = true;
}
}
if (statusReply.language != null && !statusReply.language.isEmpty()) {
Set<String> storedLanguages = sharedpreferences.getStringSet(getString(R.string.SET_SELECTED_LANGUAGE), null);
if (storedLanguages == null || storedLanguages.size() == 0) {
statusDraftList.get(0).language = statusReply.language;
} else {
if (storedLanguages.contains(statusReply.language)) {
statusDraftList.get(0).language = statusReply.language;
} else {
String currentCode = sharedpreferences.getString(getString(R.string.SET_COMPOSE_LANGUAGE) + account.user_id + account.instance, Locale.getDefault().getLanguage());
if (currentCode.isEmpty()) {
currentCode = "EN";
}
statusDraftList.get(0).language = currentCode;
}
}
}
//StatusDraftList at this point should only have one element
statusList.addAll(statusDraftList);
composeAdapter = new ComposeAdapter(statusList, statusCount, account, accountMention, visibility, editMessageId);
composeAdapter.manageDrafts = this;
composeAdapter.promptDraftListener = this;
LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this);
binding.recyclerView.setLayoutManager(mLayoutManager);
binding.recyclerView.setAdapter(composeAdapter);
statusesVM.getContext(currentInstance, BaseMainActivity.currentToken, statusReply.id)
.observe(ComposeActivity.this, this::initializeContextView);
} else if (statusQuoted != null) {
statusList.add(statusQuoted);
int statusCount = statusList.size();
statusDraftList.get(0).quote_id = statusQuoted.id;
//StatusDraftList at this point should only have one element
statusList.addAll(statusDraftList);
composeAdapter = new ComposeAdapter(statusList, statusCount, account, accountMention, visibility, editMessageId);
composeAdapter.manageDrafts = this;
composeAdapter.promptDraftListener = this;
LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this);
binding.recyclerView.setLayoutManager(mLayoutManager);
binding.recyclerView.setAdapter(composeAdapter);
} else {
//Compose without replying
statusList.addAll(statusDraftList);
composeAdapter = new ComposeAdapter(statusList, 0, account, accountMention, visibility, editMessageId);
composeAdapter.manageDrafts = this;
composeAdapter.promptDraftListener = this;
LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this);
binding.recyclerView.setLayoutManager(mLayoutManager);
binding.recyclerView.setAdapter(composeAdapter);
if (statusMention != null) {
composeAdapter.loadMentions(statusMention);
}
}
MastodonHelper.loadPPMastodon(binding.profilePicture, account.mastodon_account);
LocalBroadcastManager.getInstance(this)
.registerReceiver(imageReceiver,
new IntentFilter(Helper.INTENT_SEND_MODIFIED_IMAGE));
if (timer != null) {
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
if (promptSaveDraft) {
storeDraft(false);
}
}
}, 0, 10000);
}
if (sharedAttachments != null && sharedAttachments.size() > 0) {
for (Attachment attachment : sharedAttachments) {
composeAdapter.addAttachment(-1, attachment);
}
} /*else if (sharedUri != null && !sharedUri.toString().startsWith("http")) {
List<Uri> uris = new ArrayList<>();
uris.add(sharedUri);
Helper.createAttachmentFromUri(ComposeActivity.this, uris, attachments -> {
for(Attachment attachment: attachments) {
composeAdapter.addAttachment(-1, attachment);
}
});
} */ else if (shareURL != null) {
Helper.download(ComposeActivity.this, sharedUrlMedia, new OnDownloadInterface() {
@Override
public void onDownloaded(String saveFilePath, String downloadUrl, Error error) {
composeAdapter.addSharing(shareURL, sharedTitle, sharedDescription, sharedSubject, sharedContent, saveFilePath);
}
@Override
public void onUpdateProgress(int progress) {
}
});
} else {
if (composeAdapter != null) {
composeAdapter.addSharing(null, null, sharedDescription, null, sharedContent, null);
}
}
}
@Override @Override
public void onItemDraftAdded(int position) { public void onItemDraftAdded(int position) {
Status status = new Status(); Status status = new Status();
status.mentions = statusList.get(position).mentions;
status.id = Helper.generateIdString(); status.visibility = statusList.get(position).visibility;
status.mentions = statusList.get(position - 1).mentions; status.spoiler_text = statusList.get(position).spoiler_text;
status.visibility = statusList.get(position - 1).visibility; status.sensitive = statusList.get(position).sensitive;
final SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(ComposeActivity.this);
boolean unlistedReplies = sharedpreferences.getBoolean(getString(R.string.SET_UNLISTED_REPLIES), true);
if (status.visibility.equalsIgnoreCase("public") && unlistedReplies) {
status.visibility = "unlisted";
}
status.spoiler_text = statusList.get(position - 1).spoiler_text;
status.sensitive = statusList.get(position - 1).sensitive;
statusList.add(status); statusList.add(status);
composeAdapter.notifyItemInserted(position); composeAdapter.notifyItemInserted(position + 1);
composeAdapter.notifyItemRangeChanged(0, statusList.size()); binding.recyclerView.smoothScrollToPosition(position + 1);
binding.recyclerView.smoothScrollToPosition(statusList.size());
} }
@Override @Override
@ -788,17 +602,15 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
private void storeDraft(boolean sendMessage, String scheduledDate) { private void storeDraft(boolean sendMessage, String scheduledDate) {
new Thread(() -> { new Thread(() -> {
//Collect all statusCompose //Collect all statusCompose
List<Status> statusDrafts = new ArrayList<>(); List<Status> statusDrafts = new ArrayList<>();
List<Status> statusReplies = new ArrayList<>(); List<Status> statusReplies = new ArrayList<>();
for (Status status : statusList) { for (Status status : statusList) {
if (status.id == null || status.id.startsWith("@fedilab_compose_")) { if (status.id == null) {
statusDrafts.add(status); statusDrafts.add(status);
} else { } else {
statusReplies.add(status); statusReplies.add(status);
} }
} }
if (statusDraft == null) { if (statusDraft == null) {
statusDraft = new StatusDraft(ComposeActivity.this); statusDraft = new StatusDraft(ComposeActivity.this);
@ -808,20 +620,10 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
WorkManager.getInstance(ComposeActivity.this).cancelWorkById(statusDraft.workerUuid); WorkManager.getInstance(ComposeActivity.this).cancelWorkById(statusDraft.workerUuid);
} }
} }
if (statusReplies.size() > 0) { statusDraft.statusReplyList = statusReplies;
statusDraft.statusReplyList = new ArrayList<>(); statusDraft.statusDraftList = statusDrafts;
statusDraft.statusReplyList.addAll(statusReplies);
}
if (statusDrafts.size() > 0) {
statusDraft.statusDraftList = new ArrayList<>();
statusDraft.statusDraftList.addAll(statusDrafts);
}
if (statusDraft.instance == null) {
statusDraft.instance = account.instance; statusDraft.instance = account.instance;
}
if (statusDraft.user_id == null) {
statusDraft.user_id = account.user_id; statusDraft.user_id = account.user_id;
}
if (!canBeSent(statusDraft)) { if (!canBeSent(statusDraft)) {
return; return;
@ -862,14 +664,8 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
.addTag(Helper.WORKER_SCHEDULED_STATUSES) .addTag(Helper.WORKER_SCHEDULED_STATUSES)
.setInitialDelay(delayToPass, TimeUnit.MILLISECONDS) .setInitialDelay(delayToPass, TimeUnit.MILLISECONDS)
.build(); .build();
WorkManager.getInstance(ComposeActivity.this).enqueue(oneTimeWorkRequest);
statusDraft.workerUuid = oneTimeWorkRequest.getId(); statusDraft.workerUuid = oneTimeWorkRequest.getId();
statusDraft.scheduled_at = date; statusDraft.scheduled_at = date;
try {
new StatusDraft(ComposeActivity.this).updateStatusDraft(statusDraft);
} catch (DBException e) {
e.printStackTrace();
}
Handler mainHandler = new Handler(Looper.getMainLooper()); Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> { Runnable myRunnable = () -> {
Toasty.info(ComposeActivity.this, getString(R.string.toot_scheduled), Toasty.LENGTH_LONG).show(); Toasty.info(ComposeActivity.this, getString(R.string.toot_scheduled), Toasty.LENGTH_LONG).show();
@ -886,21 +682,18 @@ 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) {
Data inputData = new Data.Builder() Intent intent = new Intent(ComposeActivity.this, PostMessageService.class);
.putString(Helper.ARG_STATUS_DRAFT_ID, String.valueOf(statusDraft.id)) intent.putExtra(Helper.ARG_STATUS_DRAFT, statusDraft);
.putString(Helper.ARG_INSTANCE, instance) intent.putExtra(Helper.ARG_INSTANCE, instance);
.putString(Helper.ARG_TOKEN, token) intent.putExtra(Helper.ARG_TOKEN, token);
.putString(Helper.ARG_EDIT_STATUS_ID, editMessageId) intent.putExtra(Helper.ARG_SCHEDULED_DATE, scheduledDate);
.putString(Helper.ARG_USER_ID, account.user_id) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
.putString(Helper.ARG_SCHEDULED_DATE, scheduledDate).build(); startForegroundService(intent);
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(ComposeWorker.class)
.setInputData(inputData)
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
.build();
WorkManager.getInstance(ComposeActivity.this).enqueue(request);
} else { } else {
new ThreadMessageService(ComposeActivity.this, instance, account.user_id, token, statusDraft, scheduledDate, editMessageId); startService(intent);
}
} else {
new ThreadMessageService(ComposeActivity.this, instance, token, statusDraft, scheduledDate);
} }
finish(); finish();
} }
@ -910,14 +703,10 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
private boolean canBeSent(StatusDraft statusDraft) { private boolean canBeSent(StatusDraft statusDraft) {
if (statusDraft == null) { if (statusDraft == null || statusDraft.statusDraftList == null || statusDraft.statusDraftList.size() == 0) {
return false; return false;
} }
List<Status> statuses = statusDraft.statusDraftList; Status statusCheck = statusDraft.statusDraftList.get(0);
if (statuses == null || statuses.size() == 0) {
return false;
}
Status statusCheck = statuses.get(0);
if (statusCheck == null) { if (statusCheck == null) {
return false; return false;
} }
@ -933,11 +722,6 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
composeAdapter.updateContent(isChecked, acct); composeAdapter.updateContent(isChecked, acct);
} }
@Override
public void promptDraft() {
promptSaveDraft = true;
}
public enum mediaType { public enum mediaType {
PHOTO, PHOTO,

View file

@ -0,0 +1,176 @@
package app.fedilab.android.activities;
/* Copyright 2021 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.BaseMainActivity.currentAccount;
import static app.fedilab.android.ui.drawer.StatusAdapter.sendAction;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.Menu;
import android.view.MenuItem;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.PreferenceManager;
import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.client.entities.app.QuickLoad;
import app.fedilab.android.client.entities.app.StatusCache;
import app.fedilab.android.databinding.ActivityConversationBinding;
import app.fedilab.android.exception.DBException;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.helper.MastodonHelper;
import app.fedilab.android.helper.SpannableHelper;
import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonContext;
import app.fedilab.android.viewmodel.mastodon.StatusesVM;
public class ContextActivity extends BaseActivity {
public static boolean expand;
public static boolean displayCW;
public static Resources.Theme theme;
Fragment currentFragment;
private Status focusedStatus;
private ActivityConversationBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ThemeHelper.applyTheme(this);
binding = ActivityConversationBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar);
ActionBar actionBar = getSupportActionBar();
//Remove title
if (actionBar != null) {
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
}
binding.title.setText(R.string.context_conversation);
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
}
binding.toolbar.setPopupTheme(Helper.popupStyle());
Bundle b = getIntent().getExtras();
final SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(ContextActivity.this);
displayCW = sharedpreferences.getBoolean(getString(R.string.SET_EXPAND_CW), false);
focusedStatus = null; // or other values
if (b != null)
focusedStatus = (Status) b.getSerializable(Helper.ARG_STATUS);
if (focusedStatus == null) {
finish();
return;
}
MastodonHelper.loadPPMastodon(binding.profilePicture, currentAccount.mastodon_account);
Bundle bundle = new Bundle();
new Thread(() -> {
focusedStatus = SpannableHelper.convertStatus(getApplication().getApplicationContext(), focusedStatus);
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> {
bundle.putSerializable(Helper.ARG_STATUS, focusedStatus);
currentFragment = Helper.addFragment(getSupportFragmentManager(), R.id.nav_host_fragment_content_main, new FragmentMastodonContext(), bundle, null, null);
};
mainHandler.post(myRunnable);
}).start();
StatusesVM timelinesVM = new ViewModelProvider(ContextActivity.this).get(StatusesVM.class);
timelinesVM.getStatus(MainActivity.currentInstance, MainActivity.currentToken, focusedStatus.id).observe(ContextActivity.this, status -> {
if (status != null) {
StatusCache statusCache = new StatusCache();
statusCache.instance = MainActivity.currentInstance;
statusCache.user_id = MainActivity.currentUserID;
statusCache.status = status;
statusCache.status_id = status.id;
//Update cache
new Thread(() -> {
try {
new StatusCache(getApplication()).updateIfExists(statusCache);
new QuickLoad(getApplication().getApplicationContext()).updateStatus(currentAccount, status);
Handler mainHandler = new Handler(Looper.getMainLooper());
//Update UI
Runnable myRunnable = () -> sendAction(ContextActivity.this, Helper.ARG_STATUS_ACTION, status, null);
mainHandler.post(myRunnable);
} catch (DBException e) {
e.printStackTrace();
}
}).start();
}
});
}
@Override
public boolean onCreateOptionsMenu(@NonNull Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_context, menu);
MenuItem itemExpand = menu.findItem(R.id.action_expand);
if (expand) {
itemExpand.setIcon(R.drawable.ic_baseline_expand_less_24);
} else {
itemExpand.setIcon(R.drawable.ic_baseline_expand_more_24);
}
MenuItem itemDisplayCW = menu.findItem(R.id.action_show_cw);
if (displayCW) {
itemDisplayCW.setIcon(R.drawable.ic_baseline_remove_red_eye_24);
} else {
itemDisplayCW.setIcon(R.drawable.ic_outline_remove_red_eye_24);
}
return true;
}
public void setCurrentFragment(FragmentMastodonContext fragmentMastodonContext) {
currentFragment = fragmentMastodonContext;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
return true;
} else if (item.getItemId() == R.id.action_expand) {
expand = !expand;
if (currentFragment != null && currentFragment instanceof FragmentMastodonContext) {
((FragmentMastodonContext) currentFragment).redraw();
}
invalidateOptionsMenu();
} else if (item.getItemId() == R.id.action_show_cw) {
displayCW = !displayCW;
if (currentFragment != null && currentFragment instanceof FragmentMastodonContext) {
((FragmentMastodonContext) currentFragment).refresh();
}
invalidateOptionsMenu();
}
return true;
}
@Override
protected void onDestroy() {
super.onDestroy();
binding = null;
currentFragment = null;
}
}

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.activities; package app.fedilab.android.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -17,6 +17,7 @@ package app.fedilab.android.mastodon.activities;
import static app.fedilab.android.BaseMainActivity.currentAccount; import static app.fedilab.android.BaseMainActivity.currentAccount;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.graphics.drawable.ColorDrawable;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
@ -25,21 +26,23 @@ import android.text.TextUtils;
import android.view.MenuItem; import android.view.MenuItem;
import android.widget.Toast; import android.widget.Toast;
import androidx.core.content.ContextCompat;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Attachment;
import app.fedilab.android.client.entities.api.Emoji;
import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.client.entities.api.Tag;
import app.fedilab.android.databinding.ActivityCustomSharingBinding; import app.fedilab.android.databinding.ActivityCustomSharingBinding;
import app.fedilab.android.mastodon.client.entities.api.Attachment; import app.fedilab.android.helper.Helper;
import app.fedilab.android.mastodon.client.entities.api.Emoji; import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.mastodon.client.entities.api.Status; import app.fedilab.android.helper.customsharing.CustomSharingAsyncTask;
import app.fedilab.android.mastodon.client.entities.api.Tag; import app.fedilab.android.helper.customsharing.CustomSharingResponse;
import app.fedilab.android.mastodon.helper.Helper; import app.fedilab.android.helper.customsharing.OnCustomSharingInterface;
import app.fedilab.android.mastodon.helper.customsharing.CustomSharingAsyncTask;
import app.fedilab.android.mastodon.helper.customsharing.CustomSharingResponse;
import app.fedilab.android.mastodon.helper.customsharing.OnCustomSharingInterface;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
@ -48,7 +51,7 @@ import es.dmoral.toasty.Toasty;
* Share status metadata to remote content aggregators * Share status metadata to remote content aggregators
*/ */
public class CustomSharingActivity extends BaseBarActivity implements OnCustomSharingInterface { public class CustomSharingActivity extends BaseActivity implements OnCustomSharingInterface {
private String title, keywords, custom_sharing_url, encodedCustomSharingURL; private String title, keywords, custom_sharing_url, encodedCustomSharingURL;
private String bundle_url; private String bundle_url;
@ -63,13 +66,14 @@ public class CustomSharingActivity extends BaseBarActivity implements OnCustomSh
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyThemeBar(this);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(CustomSharingActivity.this); 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);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
Bundle b = getIntent().getExtras(); Bundle b = getIntent().getExtras();
status = null; status = null;

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.activities; package app.fedilab.android.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -16,11 +16,10 @@ package app.fedilab.android.mastodon.activities;
import static app.fedilab.android.BaseMainActivity.currentAccount; import static app.fedilab.android.BaseMainActivity.currentAccount;
import android.content.SharedPreferences; import android.graphics.drawable.ColorDrawable;
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.util.TypedValue;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
@ -29,24 +28,24 @@ 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.core.content.ContextCompat;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Attachment;
import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.client.entities.app.StatusDraft;
import app.fedilab.android.databinding.ActivityDraftsBinding; import app.fedilab.android.databinding.ActivityDraftsBinding;
import app.fedilab.android.mastodon.client.entities.api.Attachment; import app.fedilab.android.exception.DBException;
import app.fedilab.android.mastodon.client.entities.api.Status; import app.fedilab.android.helper.Helper;
import app.fedilab.android.mastodon.client.entities.app.StatusDraft; import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.mastodon.exception.DBException; import app.fedilab.android.ui.drawer.StatusDraftAdapter;
import app.fedilab.android.mastodon.ui.drawer.StatusDraftAdapter; import app.fedilab.android.viewmodel.mastodon.TimelinesVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.TimelinesVM;
public class DraftActivity extends BaseActivity implements StatusDraftAdapter.DraftActions { public class DraftActivity extends BaseActivity implements StatusDraftAdapter.DraftActions {
@ -60,20 +59,21 @@ public class DraftActivity extends BaseActivity implements StatusDraftAdapter.Dr
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyTheme(this);
binding = ActivityDraftsBinding.inflate(getLayoutInflater()); binding = ActivityDraftsBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
ActionBar actionBar = getSupportActionBar(); ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
}
//Remove title //Remove title
if (actionBar != null) { if (actionBar != null) {
actionBar.setDisplayShowTitleEnabled(false); actionBar.setDisplayShowTitleEnabled(false);
} }
binding.toolbar.setPopupTheme(Helper.popupStyle());
binding.title.setText(R.string.drafts); binding.title.setText(R.string.drafts);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this);
float scale = sharedpreferences.getFloat(getString(R.string.SET_FONT_SCALE), 1.1f);
binding.title.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18 * 1.1f / scale);
binding.loader.setVisibility(View.VISIBLE); binding.loader.setVisibility(View.VISIBLE);
binding.lvStatus.setVisibility(View.GONE); binding.lvStatus.setVisibility(View.GONE);
binding.noAction.setVisibility(View.GONE); binding.noAction.setVisibility(View.GONE);
@ -99,7 +99,7 @@ public class DraftActivity extends BaseActivity implements StatusDraftAdapter.Dr
finish(); finish();
return true; return true;
} else if (item.getItemId() == R.id.action_delete) { } else if (item.getItemId() == R.id.action_delete) {
AlertDialog.Builder unfollowConfirm = new MaterialAlertDialogBuilder(DraftActivity.this); AlertDialog.Builder unfollowConfirm = new AlertDialog.Builder(DraftActivity.this, Helper.dialogStyle());
unfollowConfirm.setTitle(getString(R.string.delete_all)); unfollowConfirm.setTitle(getString(R.string.delete_all));
unfollowConfirm.setMessage(getString(R.string.remove_draft)); unfollowConfirm.setMessage(getString(R.string.remove_draft));
unfollowConfirm.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss()); unfollowConfirm.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
@ -205,6 +205,12 @@ public class DraftActivity extends BaseActivity implements StatusDraftAdapter.Dr
} }
} }
@Override
public void onDestroy() {
super.onDestroy();
binding.lvStatus.setAdapter(null);
binding = null;
}
@Override @Override
public void onAllDeleted() { public void onAllDeleted() {

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.activities; package app.fedilab.android.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -18,7 +18,7 @@ import static app.fedilab.android.BaseMainActivity.currentAccount;
import static app.fedilab.android.BaseMainActivity.instanceInfo; import static app.fedilab.android.BaseMainActivity.instanceInfo;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.graphics.drawable.ColorDrawable;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.provider.MediaStore; import android.provider.MediaStore;
@ -30,11 +30,11 @@ import android.view.View;
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.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.localbroadcastmanager.content.LocalBroadcastManager; 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.textfield.TextInputEditText; import com.google.android.material.textfield.TextInputEditText;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
@ -43,17 +43,18 @@ import java.util.Locale;
import app.fedilab.android.BaseMainActivity; import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Account;
import app.fedilab.android.client.entities.api.Field;
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.exception.DBException;
import app.fedilab.android.mastodon.client.entities.api.Field; import app.fedilab.android.helper.Helper;
import app.fedilab.android.mastodon.exception.DBException; import app.fedilab.android.helper.MastodonHelper;
import app.fedilab.android.mastodon.helper.Helper; import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.mastodon.helper.MastodonHelper; import app.fedilab.android.viewmodel.mastodon.AccountsVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.AccountsVM;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
public class EditProfileActivity extends BaseBarActivity { public class EditProfileActivity extends BaseActivity {
public static final int PICK_MEDIA_AVATAR = 5705; public static final int PICK_MEDIA_AVATAR = 5705;
public static final int PICK_MEDIA_HEADER = 5706; public static final int PICK_MEDIA_HEADER = 5706;
@ -63,11 +64,12 @@ public class EditProfileActivity extends BaseBarActivity {
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyThemeBar(this);
binding = ActivityEditProfileBinding.inflate(getLayoutInflater()); binding = ActivityEditProfileBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
new ViewModelProvider(EditProfileActivity.this).get(AccountsVM.class).getConnectedAccount(BaseMainActivity.currentInstance, BaseMainActivity.currentToken) new ViewModelProvider(EditProfileActivity.this).get(AccountsVM.class).getConnectedAccount(BaseMainActivity.currentInstance, BaseMainActivity.currentToken)
@ -85,7 +87,7 @@ public class EditProfileActivity extends BaseBarActivity {
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
private void initializeView() { private void initializeView() {
//Hydrate values //Hydrate values
MastodonHelper.loadProfileMediaMastodon(EditProfileActivity.this, binding.bannerPp, currentAccount.mastodon_account, MastodonHelper.MediaAccountType.HEADER); MastodonHelper.loadProfileMediaMastodon(binding.bannerPp, currentAccount.mastodon_account, MastodonHelper.MediaAccountType.HEADER);
MastodonHelper.loadPPMastodon(binding.accountPp, currentAccount.mastodon_account); MastodonHelper.loadPPMastodon(binding.accountPp, currentAccount.mastodon_account);
binding.displayName.setText(currentAccount.mastodon_account.display_name); binding.displayName.setText(currentAccount.mastodon_account.display_name);
binding.acct.setText(String.format(Locale.getDefault(), "%s@%s", currentAccount.mastodon_account.acct, BaseMainActivity.currentInstance)); binding.acct.setText(String.format(Locale.getDefault(), "%s@%s", currentAccount.mastodon_account.acct, BaseMainActivity.currentInstance));
@ -136,7 +138,7 @@ public class EditProfileActivity extends BaseBarActivity {
value = Html.fromHtml(field.value).toString(); value = Html.fromHtml(field.value).toString();
fieldItemBinding.value.setText(value); fieldItemBinding.value.setText(value);
fieldItemBinding.remove.setOnClickListener(v -> { fieldItemBinding.remove.setOnClickListener(v -> {
AlertDialog.Builder deleteConfirm = new MaterialAlertDialogBuilder(EditProfileActivity.this); AlertDialog.Builder deleteConfirm = new AlertDialog.Builder(EditProfileActivity.this, Helper.dialogStyle());
deleteConfirm.setTitle(getString(R.string.delete_field)); deleteConfirm.setTitle(getString(R.string.delete_field));
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());
@ -158,7 +160,7 @@ public class EditProfileActivity extends BaseBarActivity {
binding.addField.setOnClickListener(view -> { binding.addField.setOnClickListener(view -> {
AccountFieldItemBinding fieldItemBinding = AccountFieldItemBinding.inflate(getLayoutInflater()); AccountFieldItemBinding fieldItemBinding = AccountFieldItemBinding.inflate(getLayoutInflater());
fieldItemBinding.remove.setOnClickListener(v -> { fieldItemBinding.remove.setOnClickListener(v -> {
AlertDialog.Builder deleteConfirm = new MaterialAlertDialogBuilder(EditProfileActivity.this); AlertDialog.Builder deleteConfirm = new AlertDialog.Builder(EditProfileActivity.this, Helper.dialogStyle());
deleteConfirm.setTitle(getString(R.string.delete_field)); deleteConfirm.setTitle(getString(R.string.delete_field));
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());
@ -178,8 +180,6 @@ public class EditProfileActivity extends BaseBarActivity {
binding.addField.setVisibility(View.GONE); binding.addField.setVisibility(View.GONE);
} }
}); });
//Actions with the activity //Actions with the activity
accountsVM = new ViewModelProvider(EditProfileActivity.this).get(AccountsVM.class); 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));
@ -191,15 +191,13 @@ public class EditProfileActivity extends BaseBarActivity {
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);
if (requestCode == PICK_MEDIA_AVATAR && resultCode == RESULT_OK) { if (requestCode == PICK_MEDIA_AVATAR && resultCode == RESULT_OK) {
Uri uri = data.getData();
if (uri != null) {
binding.avatarProgress.setVisibility(View.VISIBLE); binding.avatarProgress.setVisibility(View.VISIBLE);
Glide.with(EditProfileActivity.this) Glide.with(EditProfileActivity.this)
.asDrawable() .asDrawable()
.load(uri) .load(data.getData())
.thumbnail(0.1f) .thumbnail(0.1f)
.into(binding.accountPp); .into(binding.accountPp);
accountsVM.updateProfilePicture(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, uri, AccountsVM.UpdateMediaType.AVATAR) accountsVM.updateProfilePicture(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, data.getData(), AccountsVM.UpdateMediaType.AVATAR)
.observe(EditProfileActivity.this, account -> { .observe(EditProfileActivity.this, account -> {
if (account != null) { if (account != null) {
sendBroadCast(account); sendBroadCast(account);
@ -208,7 +206,7 @@ public class EditProfileActivity extends BaseBarActivity {
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(currentAccount); new app.fedilab.android.client.entities.app.Account(EditProfileActivity.this).insertOrUpdate(currentAccount);
} catch (DBException e) { } catch (DBException e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -217,9 +215,6 @@ public class EditProfileActivity extends BaseBarActivity {
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));
} }
}); });
} else {
Toasty.error(EditProfileActivity.this, getString(R.string.toast_error), Toasty.LENGTH_LONG).show();
}
} else if (requestCode == PICK_MEDIA_HEADER && resultCode == RESULT_OK) { } else if (requestCode == PICK_MEDIA_HEADER && resultCode == RESULT_OK) {
Glide.with(EditProfileActivity.this) Glide.with(EditProfileActivity.this)
.asDrawable() .asDrawable()
@ -235,7 +230,7 @@ public class EditProfileActivity extends BaseBarActivity {
currentAccount.mastodon_account = account; currentAccount.mastodon_account = account;
new Thread(() -> { new Thread(() -> {
try { try {
new app.fedilab.android.mastodon.client.entities.app.Account(EditProfileActivity.this).insertOrUpdate(currentAccount); new app.fedilab.android.client.entities.app.Account(EditProfileActivity.this).insertOrUpdate(currentAccount);
} catch (DBException e) { } catch (DBException e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -329,7 +324,7 @@ public class EditProfileActivity extends BaseBarActivity {
currentAccount.mastodon_account = account; currentAccount.mastodon_account = account;
new Thread(() -> { new Thread(() -> {
try { try {
new app.fedilab.android.mastodon.client.entities.app.Account(EditProfileActivity.this).insertOrUpdate(currentAccount); new app.fedilab.android.client.entities.app.Account(EditProfileActivity.this).insertOrUpdate(currentAccount);
sendBroadCast(account); sendBroadCast(account);
} catch (DBException e) { } catch (DBException e) {
e.printStackTrace(); e.printStackTrace();

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.activities; package app.fedilab.android.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -15,6 +15,7 @@ package app.fedilab.android.mastodon.activities;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import android.content.Context; import android.content.Context;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.MenuItem; import android.view.MenuItem;
@ -27,28 +28,27 @@ import android.widget.Button;
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.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelStoreOwner; import androidx.lifecycle.ViewModelStoreOwner;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects; 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.client.entities.api.Filter;
import app.fedilab.android.databinding.ActivityFiltersBinding; import app.fedilab.android.databinding.ActivityFiltersBinding;
import app.fedilab.android.databinding.PopupAddFilterBinding; import app.fedilab.android.databinding.PopupAddFilterBinding;
import app.fedilab.android.mastodon.client.entities.api.Filter; import app.fedilab.android.helper.Helper;
import app.fedilab.android.mastodon.ui.drawer.FilterAdapter; import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.mastodon.ui.drawer.KeywordAdapter; import app.fedilab.android.ui.drawer.FilterAdapter;
import app.fedilab.android.mastodon.viewmodel.mastodon.FiltersVM; import app.fedilab.android.viewmodel.mastodon.AccountsVM;
public class FilterActivity extends BaseBarActivity implements FilterAdapter.Delete { public class FilterActivity extends BaseActivity implements FilterAdapter.Delete {
private ActivityFiltersBinding binding; private ActivityFiltersBinding binding;
private List<Filter> filterList; private List<Filter> filterList;
@ -62,15 +62,14 @@ public class FilterActivity extends BaseBarActivity implements FilterAdapter.Del
* @param listener - {@link FilterAdapter.FilterAction} * @param listener - {@link FilterAdapter.FilterAction}
*/ */
public static void addEditFilter(Context context, Filter filter, FilterAdapter.FilterAction listener) { public static void addEditFilter(Context context, Filter filter, FilterAdapter.FilterAction listener) {
AlertDialog.Builder dialogBuilder = new MaterialAlertDialogBuilder(context); AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context, Helper.dialogStyle());
PopupAddFilterBinding popupAddFilterBinding = PopupAddFilterBinding.inflate(LayoutInflater.from(context)); PopupAddFilterBinding popupAddFilterBinding = PopupAddFilterBinding.inflate(LayoutInflater.from(context));
FiltersVM filtersVM = new ViewModelProvider((ViewModelStoreOwner) context).get(FiltersVM.class); AccountsVM accountsVM = new ViewModelProvider((ViewModelStoreOwner) context).get(AccountsVM.class);
dialogBuilder.setView(popupAddFilterBinding.getRoot()); dialogBuilder.setView(popupAddFilterBinding.getRoot());
ArrayAdapter<CharSequence> adapterResize = ArrayAdapter.createFromResource(Objects.requireNonNull(context), ArrayAdapter<CharSequence> adapterResize = ArrayAdapter.createFromResource(Objects.requireNonNull(context),
R.array.filter_expire, android.R.layout.simple_spinner_dropdown_item); R.array.filter_expire, android.R.layout.simple_spinner_dropdown_item);
popupAddFilterBinding.filterExpire.setAdapter(adapterResize); popupAddFilterBinding.filterExpire.setAdapter(adapterResize);
final int[] expire = {-1}; final int[] expire = {-1};
Filter.FilterParams filterParams = new Filter.FilterParams();
popupAddFilterBinding.filterExpire.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { popupAddFilterBinding.filterExpire.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override @Override
public void onItemSelected(AdapterView<?> parent1, View view, int position1, long id) { public void onItemSelected(AdapterView<?> parent1, View view, int position1, long id) {
@ -79,21 +78,18 @@ public class FilterActivity extends BaseBarActivity implements FilterAdapter.Del
expire[0] = -1; expire[0] = -1;
break; break;
case 1: case 1:
expire[0] = 1800;
break;
case 2:
expire[0] = 3600; expire[0] = 3600;
break; break;
case 3: case 2:
expire[0] = 21600; expire[0] = 21600;
break; break;
case 4: case 3:
expire[0] = 43200; expire[0] = 43200;
break; break;
case 5: case 4:
expire[0] = 86400; expire[0] = 86400;
break; break;
case 6: case 5:
expire[0] = 604800; expire[0] = 604800;
break; break;
} }
@ -103,29 +99,9 @@ public class FilterActivity extends BaseBarActivity implements FilterAdapter.Del
public void onNothingSelected(AdapterView<?> parent1) { public void onNothingSelected(AdapterView<?> parent1) {
} }
}); });
if (filter != null) { if (filter != null) {
popupAddFilterBinding.addPhrase.setText(filter.phrase);
filterParams.filter_action = filter.filter_action; if (filter.context != null)
filterParams.title = filter.title;
// filterParams.expires_in = filter.expires_at;
filterParams.context = filter.context;
filterParams.id = filter.id;
if (filter.keywords != null && filter.keywords.size() > 0) {
filterParams.keywords = new ArrayList<>();
for (Filter.KeywordsAttributes keywordsAttributes : filter.keywords) {
Filter.KeywordsParams keywordsParams = new Filter.KeywordsParams();
keywordsParams._destroy = null;
keywordsParams.id = keywordsAttributes.id;
keywordsParams.keyword = keywordsAttributes.keyword;
keywordsParams.whole_word = keywordsAttributes.whole_word;
filterParams.keywords.add(keywordsParams);
}
}
popupAddFilterBinding.addTitle.setText(filter.title);
if (filter.context != null) {
for (String val : filter.context) { for (String val : filter.context) {
switch (val) { switch (val) {
case "home": case "home":
@ -140,91 +116,48 @@ public class FilterActivity extends BaseBarActivity implements FilterAdapter.Del
case "thread": case "thread":
popupAddFilterBinding.contextConversation.setChecked(true); popupAddFilterBinding.contextConversation.setChecked(true);
break; break;
case "account":
popupAddFilterBinding.contextProfiles.setChecked(true);
break;
} }
} }
} popupAddFilterBinding.contextWholeWord.setChecked(filter.whole_word);
if (filter.filter_action.equalsIgnoreCase("warn")) { popupAddFilterBinding.contextDrop.setChecked(filter.irreversible);
popupAddFilterBinding.actionHide.setChecked(true);
popupAddFilterBinding.actionRemove.setChecked(false);
} else {
popupAddFilterBinding.actionHide.setChecked(false);
popupAddFilterBinding.actionRemove.setChecked(true);
}
}
if (filterParams.keywords == null) {
filterParams.keywords = new ArrayList<>();
} }
KeywordAdapter keywordAdapter = new KeywordAdapter(filterParams.keywords);
popupAddFilterBinding.lvKeywords.setAdapter(keywordAdapter);
popupAddFilterBinding.lvKeywords.setNestedScrollingEnabled(false);
popupAddFilterBinding.lvKeywords.setLayoutManager(new LinearLayoutManager(context));
popupAddFilterBinding.addKeyword.setOnClickListener(v -> {
Filter.KeywordsParams keywordsParams = new Filter.KeywordsParams();
keywordsParams.whole_word = true;
filterParams.keywords.add(keywordsParams);
keywordAdapter.notifyItemInserted(filterParams.keywords.size() - 1);
});
popupAddFilterBinding.actionRemove.setOnClickListener(v -> {
popupAddFilterBinding.actionHide.setChecked(false);
popupAddFilterBinding.actionRemove.setChecked(true);
});
popupAddFilterBinding.actionHide.setOnClickListener(v -> {
popupAddFilterBinding.actionRemove.setChecked(false);
popupAddFilterBinding.actionHide.setChecked(true);
});
AlertDialog alertDialog = dialogBuilder.setPositiveButton(R.string.validate, null) AlertDialog alertDialog = dialogBuilder.setPositiveButton(R.string.validate, null)
.setNegativeButton(R.string.cancel, null).create(); .setNegativeButton(R.string.cancel, null).create();
alertDialog.setOnShowListener(dialogInterface -> { alertDialog.setOnShowListener(dialogInterface -> {
Button button = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE); Button button = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
button.setOnClickListener(view -> { button.setOnClickListener(view -> {
if (popupAddFilterBinding.addPhrase.getText() == null || popupAddFilterBinding.addPhrase.getText().toString().trim().length() == 0) {
boolean canBeSent = true; popupAddFilterBinding.addPhrase.setError(context.getString(R.string.cannot_be_empty));
for (int i = 0; i < filterParams.keywords.size(); i++) { return;
if (filterParams.keywords.get(i).keyword == null || (filterParams.keywords.get(i).keyword.trim().isEmpty() && filterParams.keywords.get(i)._destroy != null && !filterParams.keywords.get(i)._destroy)) {
canBeSent = false;
} }
} if (!popupAddFilterBinding.contextConversation.isChecked() && !popupAddFilterBinding.contextHome.isChecked() && !popupAddFilterBinding.contextPublic.isChecked() && !popupAddFilterBinding.contextNotification.isChecked()) {
if (popupAddFilterBinding.addTitle.getText().toString().trim().isEmpty()) {
popupAddFilterBinding.addTitle.setError(context.getString(R.string.cannot_be_empty));
canBeSent = false;
}
if (!popupAddFilterBinding.contextConversation.isChecked() && !popupAddFilterBinding.contextHome.isChecked() && !popupAddFilterBinding.contextPublic.isChecked() && !popupAddFilterBinding.contextNotification.isChecked() && !popupAddFilterBinding.contextProfiles.isChecked()) {
popupAddFilterBinding.contextDescription.setError(context.getString(R.string.cannot_be_empty)); popupAddFilterBinding.contextDescription.setError(context.getString(R.string.cannot_be_empty));
canBeSent = false; return;
} }
if (canBeSent) { if (popupAddFilterBinding.addPhrase.getText() != null && popupAddFilterBinding.addPhrase.getText().toString().trim().length() > 0) {
filterParams.context = new ArrayList<>(); Filter filterSent = new Filter();
ArrayList<String> contextFilter = new ArrayList<>();
if (popupAddFilterBinding.contextHome.isChecked()) if (popupAddFilterBinding.contextHome.isChecked())
filterParams.context.add("home"); contextFilter.add("home");
if (popupAddFilterBinding.contextPublic.isChecked()) if (popupAddFilterBinding.contextPublic.isChecked())
filterParams.context.add("public"); contextFilter.add("public");
if (popupAddFilterBinding.contextNotification.isChecked()) if (popupAddFilterBinding.contextNotification.isChecked())
filterParams.context.add("notifications"); contextFilter.add("notifications");
if (popupAddFilterBinding.contextConversation.isChecked()) if (popupAddFilterBinding.contextConversation.isChecked())
filterParams.context.add("thread"); contextFilter.add("thread");
if (popupAddFilterBinding.contextProfiles.isChecked()) filterSent.context = contextFilter;
filterParams.context.add("account"); filterSent.expires_at_sent = expire[0];
if (expire[0] != -1) { filterSent.phrase = popupAddFilterBinding.addPhrase.getText().toString();
filterParams.expires_in = (long) expire[0]; filterSent.whole_word = popupAddFilterBinding.contextWholeWord.isChecked();
} else { filterSent.irreversible = popupAddFilterBinding.contextDrop.isChecked();
filterParams.expires_in = null; if (filter != null) {
} accountsVM.editFilter(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, filter.id, filterSent.phrase, filterSent.context, filterSent.irreversible, filterSent.whole_word, filterSent.expires_at_sent)
filterParams.title = popupAddFilterBinding.addTitle.getText().toString().trim();
filterParams.filter_action = popupAddFilterBinding.actionRemove.isChecked() ? "hide" : "warn";
if (filterParams.id != null) {
filtersVM.editFilter(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, filterParams)
.observe((LifecycleOwner) context, listener::callback); .observe((LifecycleOwner) context, listener::callback);
} else { } else {
filtersVM.addFilter(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, filterParams) accountsVM.addFilter(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, filterSent.phrase, filterSent.context, filterSent.irreversible, filterSent.whole_word, filterSent.expires_at_sent)
.observe((LifecycleOwner) context, listener::callback); .observe((LifecycleOwner) context, listener::callback);
} }
alertDialog.dismiss(); alertDialog.dismiss();
@ -236,8 +169,8 @@ public class FilterActivity extends BaseBarActivity implements FilterAdapter.Del
alertDialog.setTitle(context.getString(R.string.action_update_filter)); alertDialog.setTitle(context.getString(R.string.action_update_filter));
alertDialog.setOnDismissListener(dialogInterface -> { alertDialog.setOnDismissListener(dialogInterface -> {
//Hide keyboard //Hide keyboard
InputMethodManager imm = (InputMethodManager) context.getSystemService(INPUT_METHOD_SERVICE); InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(popupAddFilterBinding.addTitle.getWindowToken(), 0); imm.hideSoftInputFromWindow(popupAddFilterBinding.addPhrase.getWindowToken(), 0);
}); });
if (alertDialog.getWindow() != null) { if (alertDialog.getWindow() != null) {
alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
@ -249,16 +182,17 @@ public class FilterActivity extends BaseBarActivity implements FilterAdapter.Del
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyThemeBar(this);
binding = ActivityFiltersBinding.inflate(getLayoutInflater()); binding = ActivityFiltersBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
filterList = new ArrayList<>(); filterList = new ArrayList<>();
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
FiltersVM filtersVM = new ViewModelProvider(FilterActivity.this).get(FiltersVM.class); AccountsVM accountsVM = new ViewModelProvider(FilterActivity.this).get(AccountsVM.class);
filtersVM.getFilters(BaseMainActivity.currentInstance, BaseMainActivity.currentToken) accountsVM.getFilters(BaseMainActivity.currentInstance, BaseMainActivity.currentToken)
.observe(FilterActivity.this, filters -> { .observe(FilterActivity.this, filters -> {
BaseMainActivity.mainFilters = filters; BaseMainActivity.mainFilters = filters;
if (filters != null && filters.size() > 0) { if (filters != null && filters.size() > 0) {
@ -275,11 +209,7 @@ public class FilterActivity extends BaseBarActivity implements FilterAdapter.Del
binding.addFilter.setOnClickListener(v -> addEditFilter(FilterActivity.this, null, filter -> { binding.addFilter.setOnClickListener(v -> addEditFilter(FilterActivity.this, null, filter -> {
if (filter != null) { if (filter != null) {
if (MainActivity.mainFilters == null) {
MainActivity.mainFilters = new ArrayList<>();
}
filterList.add(0, filter); filterList.add(0, filter);
MainActivity.mainFilters.add(filter);
if (filterAdapter != null) { if (filterAdapter != null) {
filterAdapter.notifyItemInserted(0); filterAdapter.notifyItemInserted(0);
} else { } else {

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.activities; package app.fedilab.android.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -15,11 +15,13 @@ package app.fedilab.android.mastodon.activities;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import android.graphics.drawable.ColorDrawable;
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 androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
@ -29,12 +31,13 @@ import java.util.List;
import app.fedilab.android.BaseMainActivity; import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Account;
import app.fedilab.android.client.entities.api.Accounts;
import app.fedilab.android.databinding.ActivityStatusInfoBinding; import app.fedilab.android.databinding.ActivityStatusInfoBinding;
import app.fedilab.android.mastodon.client.entities.api.Account; import app.fedilab.android.helper.MastodonHelper;
import app.fedilab.android.mastodon.client.entities.api.Accounts; import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.mastodon.helper.MastodonHelper; import app.fedilab.android.ui.drawer.AccountFollowRequestAdapter;
import app.fedilab.android.mastodon.ui.drawer.AccountFollowRequestAdapter; import app.fedilab.android.viewmodel.mastodon.AccountsVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.AccountsVM;
public class FollowRequestActivity extends BaseActivity { public class FollowRequestActivity extends BaseActivity {
@ -48,13 +51,14 @@ public class FollowRequestActivity extends BaseActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyTheme(this);
binding = ActivityStatusInfoBinding.inflate(getLayoutInflater()); binding = ActivityStatusInfoBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
accountList = new ArrayList<>(); accountList = new ArrayList<>();
flagLoading = false; flagLoading = false;

View file

@ -0,0 +1,170 @@
package app.fedilab.android.activities;
/* 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.BaseMainActivity.currentAccount;
import android.content.Intent;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.Menu;
import android.view.MenuItem;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.core.content.ContextCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import java.util.ArrayList;
import java.util.List;
import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.client.entities.app.Pinned;
import app.fedilab.android.client.entities.app.PinnedTimeline;
import app.fedilab.android.client.entities.app.StatusDraft;
import app.fedilab.android.client.entities.app.TagTimeline;
import app.fedilab.android.client.entities.app.Timeline;
import app.fedilab.android.databinding.ActivityHashtagBinding;
import app.fedilab.android.exception.DBException;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTimeline;
import es.dmoral.toasty.Toasty;
public class HashTagActivity extends BaseActivity {
public static int position;
private String tag;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ThemeHelper.applyTheme(this);
ActivityHashtagBinding binding = ActivityHashtagBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
Bundle b = getIntent().getExtras();
if (b != null) {
tag = b.getString(Helper.ARG_SEARCH_KEYWORD, null);
}
if (tag == null)
finish();
setSupportActionBar(binding.toolbar);
ActionBar actionBar = getSupportActionBar();
//Remove title
if (actionBar != null) {
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
}
binding.title.setText(tag);
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
}
Bundle bundle = new Bundle();
bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.TAG);
bundle.putString(Helper.ARG_SEARCH_KEYWORD, tag);
Helper.addFragment(getSupportFragmentManager(), R.id.nav_host_fragment_tags, new FragmentMastodonTimeline(), bundle, null, null);
binding.toolbar.setPopupTheme(Helper.popupStyle());
binding.compose.setOnClickListener(v -> {
Intent intentToot = new Intent(HashTagActivity.this, ComposeActivity.class);
StatusDraft statusDraft = new StatusDraft();
Status status = new Status();
status.text = "#" + tag;
List<Status> statuses = new ArrayList<>();
statuses.add(status);
statusDraft.statusDraftList = statuses;
Bundle _b = new Bundle();
_b.putSerializable(Helper.ARG_TAG_TIMELINE, statusDraft);
intentToot.putExtras(_b);
startActivity(intentToot);
});
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
return true;
} else if (item.getItemId() == R.id.action_add_timeline) {
new Thread(() -> {
try {
Pinned pinned = new Pinned(HashTagActivity.this).getPinned(currentAccount);
boolean canBeAdded = true;
boolean update = true;
if (pinned == null) {
pinned = new Pinned();
pinned.pinnedTimelines = new ArrayList<>();
update = false;
} else {
for (PinnedTimeline pinnedTimeline : pinned.pinnedTimelines) {
if (pinnedTimeline.type == Timeline.TimeLineEnum.TAG) {
if (pinnedTimeline.tagTimeline.name.compareTo(tag.trim()) == 0) {
canBeAdded = false;
}
}
}
}
if (!canBeAdded) {
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> Toasty.warning(HashTagActivity.this, getString(R.string.tags_already_stored), Toasty.LENGTH_SHORT).show();
mainHandler.post(myRunnable);
return;
}
PinnedTimeline pinnedTimeline = new PinnedTimeline();
pinnedTimeline.type = Timeline.TimeLineEnum.TAG;
pinnedTimeline.position = pinned.pinnedTimelines.size();
pinnedTimeline.displayed = true;
TagTimeline tagTimeline = new TagTimeline();
tagTimeline.name = tag.trim();
tagTimeline.isNSFW = false;
tagTimeline.isART = false;
pinnedTimeline.tagTimeline = tagTimeline;
pinned.pinnedTimelines.add(pinnedTimeline);
if (update) {
new Pinned(HashTagActivity.this).updatePinned(pinned);
} else {
new Pinned(HashTagActivity.this).insertPinned(pinned);
}
Bundle b = new Bundle();
b.putBoolean(Helper.RECEIVE_REDRAW_TOPBAR, true);
Intent intentBD = new Intent(Helper.BROADCAST_DATA);
intentBD.putExtras(b);
LocalBroadcastManager.getInstance(HashTagActivity.this).sendBroadcast(intentBD);
} catch (DBException e) {
e.printStackTrace();
}
}).start();
}
return super.onOptionsItemSelected(item);
}
@Override
public boolean onCreateOptionsMenu(@NonNull Menu menu) {
getMenuInflater().inflate(R.menu.menu_reorder, menu);
return super.onCreateOptionsMenu(menu);
}
}

View file

@ -0,0 +1,109 @@
package app.fedilab.android.activities;
/* 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.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.text.Html;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import androidx.lifecycle.ViewModelProvider;
import com.bumptech.glide.Glide;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Instance;
import app.fedilab.android.databinding.ActivityInstanceBinding;
import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.viewmodel.mastodon.InstancesVM;
import es.dmoral.toasty.Toasty;
public class InstanceActivity extends BaseActivity {
ActivityInstanceBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ThemeHelper.applyThemeDialog(this);
binding = ActivityInstanceBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
getWindow().setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
if (getSupportActionBar() != null)
getSupportActionBar().hide();
binding.close.setOnClickListener(view -> finish());
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
}
InstancesVM instancesVM = new ViewModelProvider(InstanceActivity.this).get(InstancesVM.class);
instancesVM.getInstance(BaseMainActivity.currentInstance).observe(InstanceActivity.this, instanceInfo -> {
binding.instanceContainer.setVisibility(View.VISIBLE);
binding.loader.setVisibility(View.GONE);
if (instanceInfo == null || instanceInfo.info == null) {
Toasty.error(InstanceActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
return;
}
Instance instance = instanceInfo.info;
binding.instanceTitle.setText(instance.title);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
binding.instanceDescription.setText(Html.fromHtml(instance.description, Html.FROM_HTML_MODE_LEGACY));
else
binding.instanceDescription.setText(Html.fromHtml(instance.description));
if (instance.description == null || instance.description.trim().length() == 0)
binding.instanceDescription.setText(getString(R.string.instance_no_description));
binding.instanceVersion.setText(instance.version);
binding.instanceUri.setText(instance.uri);
if (instance.email == null) {
binding.instanceContact.hide();
}
Glide.with(InstanceActivity.this)
.asBitmap()
.load(instance.thumbnail)
.into(binding.backGroundImage);
binding.instanceContact.setOnClickListener(v -> {
Intent emailIntent = new Intent(Intent.ACTION_SENDTO, Uri.fromParts("mailto", instance.email, null));
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "[Mastodon] - " + instance.uri);
startActivity(Intent.createChooser(emailIntent, getString(R.string.send_email)));
});
});
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
}

View file

@ -0,0 +1,114 @@
package app.fedilab.android.activities;
/* 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.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.text.SpannableString;
import android.text.style.UnderlineSpan;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.ViewModelProvider;
import com.bumptech.glide.Glide;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.client.entities.app.InstanceSocial;
import app.fedilab.android.databinding.ActivityInstanceSocialBinding;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.viewmodel.mastodon.InstanceSocialVM;
public class InstanceHealthActivity extends BaseActivity {
private ActivityInstanceSocialBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ThemeHelper.applyThemeDialog(this);
binding = ActivityInstanceSocialBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
getWindow().setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
if (getSupportActionBar() != null)
getSupportActionBar().hide();
binding.close.setOnClickListener(view -> finish());
SpannableString content = new SpannableString(binding.refInstance.getText().toString());
content.setSpan(new UnderlineSpan(), 0, content.length(), 0);
binding.refInstance.setText(content);
binding.refInstance.setOnClickListener(view -> {
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://instances.social"));
startActivity(browserIntent);
});
checkInstance();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
private void checkInstance() {
InstanceSocialVM instanceSocialVM = new ViewModelProvider(InstanceHealthActivity.this).get(InstanceSocialVM.class);
instanceSocialVM.getInstances(BaseMainActivity.currentInstance.trim()).observe(InstanceHealthActivity.this, instanceSocialList -> {
if (instanceSocialList != null && instanceSocialList.instances.size() > 0) {
InstanceSocial.Instance instanceSocial = instanceSocialList.instances.get(0);
if (instanceSocial.thumbnail != null && !instanceSocial.thumbnail.equals("null"))
Glide.with(InstanceHealthActivity.this)
.asBitmap()
.load(instanceSocial.thumbnail)
.into(binding.backGroundImage);
binding.name.setText(instanceSocial.name);
if (instanceSocial.up) {
binding.up.setText(R.string.is_up);
binding.up.setTextColor(ContextCompat.getColor(InstanceHealthActivity.this, R.color.green_1));
} else {
binding.up.setText(R.string.is_down);
binding.up.setTextColor(ContextCompat.getColor(InstanceHealthActivity.this, R.color.red_1));
}
binding.uptime.setText(getString(R.string.instance_health_uptime, (instanceSocial.uptime * 100)));
if (instanceSocial.checked_at != null)
binding.checkedAt.setText(getString(R.string.instance_health_checkedat, Helper.dateToString(instanceSocial.checked_at)));
binding.values.setText(getString(R.string.instance_health_indication, instanceSocial.version, Helper.withSuffix(instanceSocial.active_users), Helper.withSuffix(instanceSocial.statuses)));
binding.instanceContainer.setVisibility(View.VISIBLE);
} else {
binding.instanceContainer.setVisibility(View.VISIBLE);
binding.mainContainer.setVisibility(View.GONE);
binding.noInstance.setVisibility(View.VISIBLE);
}
binding.loader.setVisibility(View.GONE);
});
}
}

View file

@ -0,0 +1,127 @@
package app.fedilab.android.activities;
/* 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 androidx.core.text.HtmlCompat.FROM_HTML_MODE_LEGACY;
import android.os.Build;
import android.os.Bundle;
import android.text.Html;
import android.text.SpannableString;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import java.util.ArrayList;
import java.util.List;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Account;
import app.fedilab.android.databinding.ActivityInstanceProfileBinding;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.ui.drawer.AccountAdapter;
import app.fedilab.android.viewmodel.mastodon.NodeInfoVM;
import app.fedilab.android.viewmodel.mastodon.SearchVM;
import es.dmoral.toasty.Toasty;
public class InstanceProfileActivity extends BaseActivity {
private String instance;
private ActivityInstanceProfileBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ThemeHelper.applyThemeDialog(this);
binding = ActivityInstanceProfileBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
getWindow().setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
Bundle b = getIntent().getExtras();
if (getSupportActionBar() != null)
getSupportActionBar().hide();
if (b != null)
instance = b.getString(Helper.ARG_INSTANCE, null);
if (instance == null) {
finish();
}
Button close = findViewById(R.id.close);
close.setOnClickListener(view -> finish());
NodeInfoVM nodeInfoVM = new ViewModelProvider(InstanceProfileActivity.this).get(NodeInfoVM.class);
nodeInfoVM.getNodeInfo(instance).observe(InstanceProfileActivity.this, nodeInfo -> {
if (nodeInfo == null) {
Toasty.error(InstanceProfileActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
finish();
return;
}
binding.name.setText(nodeInfo.metadata != null ? nodeInfo.metadata.nodeName : instance);
SpannableString descriptionSpan;
if (nodeInfo.metadata != null && nodeInfo.metadata.nodeDescription != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
descriptionSpan = new SpannableString(Html.fromHtml(nodeInfo.metadata.nodeDescription, FROM_HTML_MODE_LEGACY));
else
descriptionSpan = new SpannableString(Html.fromHtml(nodeInfo.metadata.nodeDescription));
binding.description.setText(descriptionSpan, TextView.BufferType.SPANNABLE);
}
binding.userCount.setText(Helper.withSuffix((nodeInfo.usage.users.total)));
binding.statusCount.setText(Helper.withSuffix(((nodeInfo.usage.localPosts))));
String softwareStr = nodeInfo.software.name + " - ";
binding.software.setText(softwareStr);
binding.version.setText(nodeInfo.software.version);
if (nodeInfo.metadata != null && nodeInfo.metadata.staffAccounts != null && nodeInfo.metadata.staffAccounts.size() > 0) {
SearchVM searchVM = new ViewModelProvider(InstanceProfileActivity.this).get(SearchVM.class);
List<Account> accounts = new ArrayList<>();
for (String accountURL : nodeInfo.metadata.staffAccounts) {
searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, accountURL, null, "accounts", false, true, false, 0, null, null, 1)
.observe(InstanceProfileActivity.this, results -> {
if (results.accounts != null && results.accounts.size() > 0) {
accounts.add(results.accounts.get(0));
}
if (accounts.size() == nodeInfo.metadata.staffAccounts.size()) {
AccountAdapter accountsListAdapter = new AccountAdapter(accounts);
binding.lvAccounts.setAdapter(accountsListAdapter);
final LinearLayoutManager mLayoutManager;
mLayoutManager = new LinearLayoutManager(InstanceProfileActivity.this);
binding.lvAccounts.setLayoutManager(mLayoutManager);
}
});
}
}
binding.instanceContainer.setVisibility(View.VISIBLE);
binding.loader.setVisibility(View.GONE);
});
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
}

View file

@ -15,15 +15,11 @@ package app.fedilab.android.activities;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.mastodon.helper.Helper.PREF_USER_SOFTWARE; import static app.fedilab.android.helper.MastodonHelper.REDIRECT_CONTENT_WEB;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.widget.FrameLayout; import android.widget.FrameLayout;
@ -36,18 +32,14 @@ import org.jetbrains.annotations.NotNull;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.mastodon.activities.BaseActivity; import app.fedilab.android.client.entities.app.Account;
import app.fedilab.android.mastodon.activities.ProxyActivity; import app.fedilab.android.helper.Helper;
import app.fedilab.android.mastodon.client.entities.app.Account; import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.mastodon.exception.DBException; import app.fedilab.android.ui.fragment.login.FragmentLoginMain;
import app.fedilab.android.mastodon.helper.Helper; import app.fedilab.android.viewmodel.mastodon.AccountsVM;
import app.fedilab.android.mastodon.helper.MastodonHelper; import app.fedilab.android.viewmodel.mastodon.AdminVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.AccountsVM; import app.fedilab.android.viewmodel.mastodon.OauthVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.AdminVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.OauthVM;
import app.fedilab.android.ui.fragment.FragmentLoginMain;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
@ -56,40 +48,12 @@ public class LoginActivity extends BaseActivity {
public static Account.API apiLogin; public static Account.API apiLogin;
public static String currentInstanceLogin, client_idLogin, client_secretLogin, softwareLogin; public static String currentInstanceLogin, client_idLogin, client_secretLogin, softwareLogin;
public static boolean requestedAdmin; private final int PICK_IMPORT = 5557;
private boolean requestedAdmin;
@SuppressLint("ApplySharedPref")
public void proceedLogin(Activity activity, Account account) {
new Thread(() -> {
try {
//update the database
new Account(activity).insertOrUpdate(account);
Handler mainHandler = new Handler(Looper.getMainLooper());
BaseMainActivity.currentToken = account.token;
BaseMainActivity.currentUserID = account.user_id;
BaseMainActivity.api = Account.API.MASTODON;
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(activity);
SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putString(Helper.PREF_USER_TOKEN, account.token);
editor.putString(PREF_USER_SOFTWARE, BaseMainActivity.api.name());
editor.commit();
//The user is now authenticated, it will be redirected to MainActivity
Runnable myRunnable = () -> {
Intent mainActivity = new Intent(activity, MainActivity.class);
startActivity(mainActivity);
finish();
};
mainHandler.post(myRunnable);
} catch (DBException e) {
e.printStackTrace();
}
}).start();
}
private void manageItent(Intent intent) { private void manageItent(Intent intent) {
if (intent != null && intent.getData() != null && intent.getData().toString().contains(MastodonHelper.REDIRECT_CONTENT_WEB + "?code=")) { if (intent != null && intent.getData() != null && intent.getData().toString().contains(REDIRECT_CONTENT_WEB + "?code=")) {
String url = intent.getData().toString(); String url = intent.getData().toString();
Matcher matcher = Helper.codePattern.matcher(url); Matcher matcher = Helper.codePattern.matcher(url);
if (!matcher.find()) { if (!matcher.find()) {
@ -103,7 +67,6 @@ public class LoginActivity extends BaseActivity {
String scope = requestedAdmin ? Helper.OAUTH_SCOPES_ADMIN : Helper.OAUTH_SCOPES; String scope = requestedAdmin ? Helper.OAUTH_SCOPES_ADMIN : Helper.OAUTH_SCOPES;
oauthVM.createToken(currentInstanceLogin, "authorization_code", client_idLogin, client_secretLogin, Helper.REDIRECT_CONTENT_WEB, scope, code) oauthVM.createToken(currentInstanceLogin, "authorization_code", client_idLogin, client_secretLogin, Helper.REDIRECT_CONTENT_WEB, scope, code)
.observe(LoginActivity.this, tokenObj -> { .observe(LoginActivity.this, tokenObj -> {
if (tokenObj != null) {
Account account = new Account(); Account account = new Account();
account.client_id = client_idLogin; account.client_id = client_idLogin;
account.client_secret = client_secretLogin; account.client_secret = client_secretLogin;
@ -114,7 +77,6 @@ public class LoginActivity extends BaseActivity {
//API call to retrieve account information for the new token //API call to retrieve account information for the new token
AccountsVM accountsVM = new ViewModelProvider(LoginActivity.this).get(AccountsVM.class); AccountsVM accountsVM = new ViewModelProvider(LoginActivity.this).get(AccountsVM.class);
accountsVM.getConnectedAccount(currentInstanceLogin, account.token).observe(LoginActivity.this, mastodonAccount -> { accountsVM.getConnectedAccount(currentInstanceLogin, account.token).observe(LoginActivity.this, mastodonAccount -> {
if (mastodonAccount != null) {
account.mastodon_account = mastodonAccount; account.mastodon_account = mastodonAccount;
account.user_id = mastodonAccount.id; account.user_id = mastodonAccount.id;
//We check if user have really moderator rights //We check if user have really moderator rights
@ -122,23 +84,16 @@ public class LoginActivity extends BaseActivity {
AdminVM adminVM = new ViewModelProvider(LoginActivity.this).get(AdminVM.class); AdminVM adminVM = new ViewModelProvider(LoginActivity.this).get(AdminVM.class);
adminVM.getAccount(account.instance, account.token, account.user_id).observe(LoginActivity.this, adminAccount -> { adminVM.getAccount(account.instance, account.token, account.user_id).observe(LoginActivity.this, adminAccount -> {
account.admin = adminAccount != null; account.admin = adminAccount != null;
proceedLogin(LoginActivity.this, account); WebviewConnectActivity.proceedLogin(LoginActivity.this, account);
}); });
} else { } else {
proceedLogin(LoginActivity.this, account); WebviewConnectActivity.proceedLogin(LoginActivity.this, account);
}
});
});
} }
} else {
Toasty.error(LoginActivity.this, getString(R.string.toast_token), Toast.LENGTH_LONG).show();
} }
});
} else {
Toasty.error(LoginActivity.this, getString(R.string.toast_token), Toast.LENGTH_LONG).show();
}
});
}
}
@Override @Override
protected void onNewIntent(Intent intent) { protected void onNewIntent(Intent intent) {
@ -149,18 +104,24 @@ public class LoginActivity extends BaseActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyTheme(this);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(LoginActivity.this); 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);
requestedAdmin = false;
//The activity handles a redirect URI, it will extract token code and will proceed to authentication //The activity handles a redirect URI, it will extract token code and will proceed to authentication
//That happens when the user wants to use an external browser //That happens when the user wants to use an external browser
manageItent(getIntent()); manageItent(getIntent());
} }
public boolean requestedAdmin() {
return requestedAdmin;
}
public boolean setAdmin(boolean askAdmin) {
return requestedAdmin = askAdmin;
}
@Override @Override
protected void onResume() { protected void onResume() {
@ -172,6 +133,9 @@ public class LoginActivity extends BaseActivity {
public boolean onCreateOptionsMenu(@NotNull Menu menu) { public boolean onCreateOptionsMenu(@NotNull 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.main_login, menu); getMenuInflater().inflate(R.menu.main_login, menu);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(LoginActivity.this);
boolean embedded_browser = sharedpreferences.getBoolean(getString(R.string.SET_EMBEDDED_BROWSER), true);
menu.findItem(R.id.action_custom_tabs).setChecked(!embedded_browser);
return true; return true;
} }
@ -181,8 +145,17 @@ public class LoginActivity extends BaseActivity {
// automatically handle clicks on the Home/Up button, so long // automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml. // as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId(); int id = item.getItemId();
if (id == R.id.action_proxy) { if (id == R.id.action_proxy) {
(new ProxyActivity()).show(getSupportFragmentManager(), null); Intent intent = new Intent(LoginActivity.this, ProxyActivity.class);
startActivity(intent);
} else if (id == R.id.action_custom_tabs) {
item.setChecked(!item.isChecked());
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(LoginActivity.this);
SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putBoolean(getString(R.string.SET_EMBEDDED_BROWSER), !item.isChecked());
editor.apply();
return false;
} else if (id == R.id.action_request_admin) { } else if (id == R.id.action_request_admin) {
item.setChecked(!item.isChecked()); item.setChecked(!item.isChecked());
requestedAdmin = item.isChecked(); requestedAdmin = item.isChecked();
@ -191,5 +164,20 @@ public class LoginActivity extends BaseActivity {
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PICK_IMPORT && resultCode == RESULT_OK) {
if (data == null || data.getData() == null) {
Toasty.error(LoginActivity.this, getString(R.string.toot_select_file_error), Toast.LENGTH_LONG).show();
return;
}
// String filename = Helper.getFilePathFromURI(LoginActivity.this, data.getData());
// Sqlite.importDB(LoginActivity.this, filename);
} else {
Toasty.error(LoginActivity.this, getString(R.string.toot_select_file_error), Toast.LENGTH_LONG).show();
}
}
} }

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.activities; package app.fedilab.android.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -16,6 +16,7 @@ package app.fedilab.android.mastodon.activities;
import android.content.Intent; import android.content.Intent;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable; import android.text.Editable;
import android.text.InputFilter; import android.text.InputFilter;
@ -27,6 +28,7 @@ import android.view.View;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
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.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
@ -34,36 +36,28 @@ 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;
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.List;
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.client.entities.api.Account;
import app.fedilab.android.client.entities.api.MastodonList;
import app.fedilab.android.client.entities.app.Timeline;
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.helper.Helper;
import app.fedilab.android.mastodon.client.entities.api.MastodonList; import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.mastodon.client.entities.app.Pinned; import app.fedilab.android.ui.drawer.AccountListAdapter;
import app.fedilab.android.mastodon.client.entities.app.PinnedTimeline; import app.fedilab.android.ui.drawer.MastodonListAdapter;
import app.fedilab.android.mastodon.client.entities.app.Timeline; import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTimeline;
import app.fedilab.android.mastodon.exception.DBException; import app.fedilab.android.viewmodel.mastodon.AccountsVM;
import app.fedilab.android.mastodon.helper.Helper; import app.fedilab.android.viewmodel.mastodon.TimelinesVM;
import app.fedilab.android.mastodon.helper.ThemeHelper;
import app.fedilab.android.mastodon.ui.drawer.AccountListAdapter;
import app.fedilab.android.mastodon.ui.drawer.MastodonListAdapter;
import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonTimeline;
import app.fedilab.android.mastodon.viewmodel.mastodon.AccountsVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.ReorderVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.TimelinesVM;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
public class MastodonListActivity extends BaseBarActivity implements MastodonListAdapter.ActionOnList { public class MastodonListActivity extends BaseActivity implements MastodonListAdapter.ActionOnList {
AccountListAdapter accountsInListAdapter; AccountListAdapter accountsInListAdapter;
@ -78,44 +72,27 @@ public class MastodonListActivity extends BaseBarActivity implements MastodonLis
private boolean flagLoading; private boolean flagLoading;
private String max_id; private String max_id;
private FragmentMastodonTimeline fragmentMastodonTimeline; private FragmentMastodonTimeline fragmentMastodonTimeline;
private boolean orderASC;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyThemeBar(this);
binding = ActivityListBinding.inflate(getLayoutInflater()); binding = ActivityListBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
canGoBack = false; canGoBack = false;
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
flagLoading = false; flagLoading = false;
orderASC = true;
max_id = null; max_id = null;
accountsVM = new ViewModelProvider(MastodonListActivity.this).get(AccountsVM.class); accountsVM = new ViewModelProvider(MastodonListActivity.this).get(AccountsVM.class);
timelinesVM = new ViewModelProvider(MastodonListActivity.this).get(TimelinesVM.class); timelinesVM = new ViewModelProvider(MastodonListActivity.this).get(TimelinesVM.class);
timelinesVM.getLists(BaseMainActivity.currentInstance, BaseMainActivity.currentToken) timelinesVM.getLists(BaseMainActivity.currentInstance, BaseMainActivity.currentToken)
.observe(MastodonListActivity.this, mastodonLists -> { .observe(MastodonListActivity.this, mastodonLists -> {
ReorderVM reorderVM = new ViewModelProvider(MastodonListActivity.this).get(ReorderVM.class);
reorderVM.getPinned().observe(MastodonListActivity.this, pinned -> {
if (mastodonLists != null && mastodonLists.size() > 0) { if (mastodonLists != null && mastodonLists.size() > 0) {
mastodonListList = new ArrayList<>(mastodonLists); mastodonListList = new ArrayList<>(mastodonLists);
if (pinned != null) {
if (pinned.pinnedTimelines != null && pinned.pinnedTimelines.size() > 0) {
for (PinnedTimeline pinnedTimeline : pinned.pinnedTimelines) {
if (pinnedTimeline.type == Timeline.TimeLineEnum.LIST) {
for (MastodonList mastodonList : mastodonLists) {
if (mastodonList.id.equalsIgnoreCase(pinnedTimeline.mastodonList.id)) {
mastodonList.position = pinnedTimeline.position;
}
}
}
}
}
}
sortAsc(mastodonListList);
mastodonListAdapter = new MastodonListAdapter(mastodonListList); mastodonListAdapter = new MastodonListAdapter(mastodonListList);
mastodonListAdapter.actionOnList = this; mastodonListAdapter.actionOnList = this;
binding.notContent.setVisibility(View.GONE); binding.notContent.setVisibility(View.GONE);
@ -125,20 +102,6 @@ public class MastodonListActivity extends BaseBarActivity implements MastodonLis
binding.notContent.setVisibility(View.VISIBLE); binding.notContent.setVisibility(View.VISIBLE);
} }
}); });
});
}
private void sortAsc(List<MastodonList> mastodonLists) {
Collections.sort(mastodonLists, (obj1, obj2) -> obj1.title.compareToIgnoreCase(obj2.title));
orderASC = true;
invalidateOptionsMenu();
}
private void sortDesc(List<MastodonList> mastodonLists) {
Collections.sort(mastodonLists, (obj1, obj2) -> obj2.title.compareToIgnoreCase(obj1.title));
orderASC = false;
invalidateOptionsMenu();
} }
@Override @Override
@ -146,22 +109,8 @@ public class MastodonListActivity extends BaseBarActivity implements MastodonLis
if (item.getItemId() == android.R.id.home) { if (item.getItemId() == android.R.id.home) {
onBackPressed(); onBackPressed();
return true; return true;
} else if (item.getItemId() == R.id.action_user_mute_home) {
AlertDialog.Builder dialogBuilder = new MaterialAlertDialogBuilder(MastodonListActivity.this);
dialogBuilder.setTitle(R.string.put_all_accounts_in_home_muted);
dialogBuilder.setPositiveButton(R.string.mute_them_all, (dialog, id) -> {
timelinesVM.getAccountsInList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, mastodonList.id, null, null, 0)
.observe(MastodonListActivity.this, accounts -> {
if (accounts != null && accounts.size() > 0) {
accountsVM.muteAccountsHome(MainActivity.currentAccount, accounts);
}
});
dialog.dismiss();
});
dialogBuilder.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss());
dialogBuilder.show();
} else if (item.getItemId() == R.id.action_manage_users) { } else if (item.getItemId() == R.id.action_manage_users) {
AlertDialog.Builder dialogBuilder = new MaterialAlertDialogBuilder(MastodonListActivity.this); AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(MastodonListActivity.this, Helper.dialogStyle());
PopupManageAccountsListBinding popupManageAccountsListBinding = PopupManageAccountsListBinding.inflate(getLayoutInflater()); PopupManageAccountsListBinding popupManageAccountsListBinding = PopupManageAccountsListBinding.inflate(getLayoutInflater());
dialogBuilder.setView(popupManageAccountsListBinding.getRoot()); dialogBuilder.setView(popupManageAccountsListBinding.getRoot());
popupManageAccountsListBinding.loader.setVisibility(View.VISIBLE); popupManageAccountsListBinding.loader.setVisibility(View.VISIBLE);
@ -258,7 +207,7 @@ public class MastodonListActivity extends BaseBarActivity implements MastodonLis
dialogBuilder.setPositiveButton(R.string.close, (dialog, id) -> dialog.dismiss()); dialogBuilder.setPositiveButton(R.string.close, (dialog, id) -> dialog.dismiss());
dialogBuilder.create().show(); dialogBuilder.create().show();
} else if (item.getItemId() == R.id.action_delete && mastodonList != null) { } else if (item.getItemId() == R.id.action_delete && mastodonList != null) {
AlertDialog.Builder alt_bld = new MaterialAlertDialogBuilder(MastodonListActivity.this); AlertDialog.Builder alt_bld = new AlertDialog.Builder(MastodonListActivity.this, Helper.dialogStyle());
alt_bld.setTitle(R.string.action_lists_delete); alt_bld.setTitle(R.string.action_lists_delete);
alt_bld.setMessage(R.string.action_lists_confirm_delete); alt_bld.setMessage(R.string.action_lists_confirm_delete);
alt_bld.setPositiveButton(R.string.delete, (dialog, id) -> { alt_bld.setPositiveButton(R.string.delete, (dialog, id) -> {
@ -296,7 +245,7 @@ public class MastodonListActivity extends BaseBarActivity implements MastodonLis
AlertDialog alert = alt_bld.create(); AlertDialog alert = alt_bld.create();
alert.show(); alert.show();
} else if (item.getItemId() == R.id.action_add_list) { } else if (item.getItemId() == R.id.action_add_list) {
AlertDialog.Builder dialogBuilder = new MaterialAlertDialogBuilder(MastodonListActivity.this); AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(MastodonListActivity.this, Helper.dialogStyle());
PopupAddListBinding popupAddListBinding = PopupAddListBinding.inflate(getLayoutInflater()); PopupAddListBinding popupAddListBinding = PopupAddListBinding.inflate(getLayoutInflater());
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)});
@ -309,13 +258,6 @@ public class MastodonListActivity extends BaseBarActivity implements MastodonLis
} }
if (newMastodonList != null) { if (newMastodonList != null) {
mastodonListList.add(0, newMastodonList); mastodonListList.add(0, newMastodonList);
if (mastodonListAdapter == null) {
mastodonListAdapter = new MastodonListAdapter(mastodonListList);
mastodonListAdapter.actionOnList = MastodonListActivity.this;
binding.notContent.setVisibility(View.GONE);
binding.recyclerView.setAdapter(mastodonListAdapter);
binding.recyclerView.setLayoutManager(new LinearLayoutManager(MastodonListActivity.this));
}
mastodonListAdapter.notifyItemInserted(0); mastodonListAdapter.notifyItemInserted(0);
} 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();
@ -335,83 +277,6 @@ public class MastodonListActivity extends BaseBarActivity implements MastodonLis
}); });
dialogBuilder.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss()); dialogBuilder.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss());
dialogBuilder.create().show(); dialogBuilder.create().show();
} else if (item.getItemId() == R.id.action_edit) {
AlertDialog.Builder dialogBuilder = new MaterialAlertDialogBuilder(MastodonListActivity.this);
PopupAddListBinding popupAddListBinding = PopupAddListBinding.inflate(getLayoutInflater());
dialogBuilder.setView(popupAddListBinding.getRoot());
popupAddListBinding.addList.setFilters(new InputFilter[]{new InputFilter.LengthFilter(255)});
popupAddListBinding.addList.setText(mastodonList.title);
popupAddListBinding.addList.setSelection(popupAddListBinding.addList.getText().length());
dialogBuilder.setPositiveButton(R.string.validate, (dialog, id) -> {
if (popupAddListBinding.addList.getText() != null && popupAddListBinding.addList.getText().toString().trim().length() > 0) {
timelinesVM.updateList(
BaseMainActivity.currentInstance, BaseMainActivity.currentToken, mastodonList.id,
popupAddListBinding.addList.getText().toString().trim(), null)
.observe(MastodonListActivity.this, newMastodonList -> {
if (mastodonListList != null && newMastodonList != null) {
int position = 0;
for (MastodonList mastodonList : mastodonListList) {
if (newMastodonList.id.equalsIgnoreCase(mastodonList.id)) {
ReorderVM reorderVM = new ViewModelProvider(MastodonListActivity.this).get(ReorderVM.class);
int finalPosition = position;
reorderVM.getAllPinned().observe(MastodonListActivity.this, pinned -> {
if (pinned != null) {
if (pinned.pinnedTimelines != null) {
for (PinnedTimeline pinnedTimeline : pinned.pinnedTimelines) {
if (pinnedTimeline.mastodonList != null) {
if (pinnedTimeline.mastodonList.id.equalsIgnoreCase(newMastodonList.id)) {
if (!newMastodonList.title.equalsIgnoreCase(pinnedTimeline.mastodonList.title)) {
pinnedTimeline.mastodonList.title = newMastodonList.title;
setTitle(newMastodonList.title);
mastodonListList.get(finalPosition).title = newMastodonList.title;
mastodonListAdapter.notifyItemChanged(finalPosition);
new Thread(() -> {
try {
new Pinned(MastodonListActivity.this).updatePinned(pinned);
Bundle b = new Bundle();
b.putBoolean(Helper.RECEIVE_REDRAW_TOPBAR, true);
Intent intentBD = new Intent(Helper.BROADCAST_DATA);
intentBD.putExtras(b);
LocalBroadcastManager.getInstance(MastodonListActivity.this).sendBroadcast(intentBD);
} catch (DBException e) {
e.printStackTrace();
}
}).start();
}
break;
}
}
}
}
}
});
}
position++;
}
} else {
Toasty.error(MastodonListActivity.this, getString(R.string.toast_error), Toasty.LENGTH_LONG).show();
}
});
dialog.dismiss();
} else {
popupAddListBinding.addList.setError(getString(R.string.not_valid_list_name));
}
});
dialogBuilder.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss());
dialogBuilder.create().show();
} else if (item.getItemId() == R.id.action_order) {
if (mastodonListList != null && mastodonListList.size() > 0 && mastodonListAdapter != null) {
if (orderASC) {
sortDesc(mastodonListList);
} else {
sortAsc(mastodonListList);
}
invalidateOptionsMenu();
mastodonListAdapter.notifyItemRangeChanged(0, mastodonListList.size());
}
} }
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
@ -441,14 +306,6 @@ public class MastodonListActivity extends BaseBarActivity implements MastodonLis
public boolean onCreateOptionsMenu(@NonNull Menu menu) { public boolean onCreateOptionsMenu(@NonNull Menu menu) {
if (!canGoBack) { if (!canGoBack) {
getMenuInflater().inflate(R.menu.menu_main_list, menu); getMenuInflater().inflate(R.menu.menu_main_list, menu);
MenuItem order = menu.findItem(R.id.action_order);
if (order != null) {
if (orderASC) {
order.setIcon(R.drawable.ic_baseline_filter_asc_24);
} else {
order.setIcon(R.drawable.ic_baseline_filter_desc_24);
}
}
} else { } else {
getMenuInflater().inflate(R.menu.menu_list, menu); getMenuInflater().inflate(R.menu.menu_list, menu);
} }

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.activities; package app.fedilab.android.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -22,44 +22,41 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.Point;
import android.graphics.drawable.ColorDrawable;
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.view.Display;
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.Window; import android.view.Window;
import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentStatePagerAdapter; import androidx.viewpager2.adapter.FragmentStateAdapter;
import androidx.viewpager.widget.ViewPager; import androidx.viewpager2.widget.ViewPager2;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Attachment;
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.helper.Helper;
import app.fedilab.android.mastodon.client.entities.api.Status; import app.fedilab.android.helper.MediaHelper;
import app.fedilab.android.mastodon.helper.Helper; import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.mastodon.helper.MediaHelper; import app.fedilab.android.interfaces.OnDownloadInterface;
import app.fedilab.android.mastodon.helper.TranslateHelper; import app.fedilab.android.ui.fragment.media.FragmentMedia;
import app.fedilab.android.mastodon.interfaces.OnDownloadInterface;
import app.fedilab.android.mastodon.ui.fragment.media.FragmentMedia;
import app.fedilab.android.mastodon.ui.fragment.media.FragmentMediaProfile;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
public class MediaActivity extends BaseTransparentActivity implements OnDownloadInterface { public class MediaActivity extends BaseActivity implements OnDownloadInterface {
int flags; int flags;
private ArrayList<Attachment> attachments; private ArrayList<Attachment> attachments;
@ -71,7 +68,7 @@ public class MediaActivity extends BaseTransparentActivity implements OnDownload
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(Context.DOWNLOAD_SERVICE);
assert manager != null; 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);
@ -93,15 +90,15 @@ public class MediaActivity extends BaseTransparentActivity implements OnDownload
}; };
private boolean fullscreen; private boolean fullscreen;
private Handler handler; private Handler handler;
private int minTouch, maxTouch;
private float startX;
private float startY;
private ActivityMediaPagerBinding binding; private ActivityMediaPagerBinding binding;
private FragmentMedia mCurrentFragment;
private Status status;
private boolean mediaFromProfile;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
getWindow().requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY); getWindow().requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
ThemeHelper.applyThemeBar(this);
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ActivityCompat.postponeEnterTransition(MediaActivity.this); ActivityCompat.postponeEnterTransition(MediaActivity.this);
binding = ActivityMediaPagerBinding.inflate(getLayoutInflater()); binding = ActivityMediaPagerBinding.inflate(getLayoutInflater());
@ -114,16 +111,11 @@ public class MediaActivity extends BaseTransparentActivity implements OnDownload
if (b != null) { if (b != null) {
mediaPosition = b.getInt(Helper.ARG_MEDIA_POSITION, 1); mediaPosition = b.getInt(Helper.ARG_MEDIA_POSITION, 1);
attachments = (ArrayList<Attachment>) b.getSerializable(Helper.ARG_MEDIA_ARRAY); attachments = (ArrayList<Attachment>) b.getSerializable(Helper.ARG_MEDIA_ARRAY);
mediaFromProfile = b.getBoolean(Helper.ARG_MEDIA_ARRAY_PROFILE, false);
status = (Status) b.getSerializable(Helper.ARG_STATUS);
}
if (mediaFromProfile && FragmentMediaProfile.mediaAttachmentProfile != null) {
attachments = new ArrayList<>();
attachments.addAll(FragmentMediaProfile.mediaAttachmentProfile);
} }
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
if (attachments == null || attachments.size() == 0) if (attachments == null || attachments.size() == 0)
@ -131,55 +123,27 @@ public class MediaActivity extends BaseTransparentActivity implements OnDownload
setTitle(""); setTitle("");
ScreenSlidePagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager()); ScreenSlidePagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(MediaActivity.this);
binding.mediaViewpager.setAdapter(mPagerAdapter); binding.mediaViewpager.setAdapter(mPagerAdapter);
binding.mediaViewpager.setSaveEnabled(false);
binding.mediaViewpager.setCurrentItem(mediaPosition - 1); binding.mediaViewpager.setCurrentItem(mediaPosition - 1);
binding.haulerView.setOnDragDismissedListener(dragDirection -> ActivityCompat.finishAfterTransition(MediaActivity.this));
registerReceiver(onDownloadComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)); 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) {
binding.originalMessage.setOnClickListener(v -> {
Intent intentContext = new Intent(MediaActivity.this, ContextActivity.class);
intentContext.putExtra(Helper.ARG_STATUS, attachments.get(mediaPosition - 1).status);
intentContext.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intentContext);
});
}
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(description); binding.mediaDescription.setText(description);
binding.translate.setOnClickListener(v -> {
String descriptionToTranslate = attachments.get(mediaPosition - 1).description;
TranslateHelper.translate(MediaActivity.this, descriptionToTranslate, translated -> {
if (translated != null) {
attachments.get(mediaPosition - 1).translation = translated;
binding.mediaDescriptionTranslated.setText(translated);
binding.mediaDescriptionTranslated.setVisibility(View.VISIBLE);
binding.mediaDescription.setVisibility(View.GONE);
} else {
Toasty.error(MediaActivity.this, getString(R.string.toast_error_translate), Toast.LENGTH_LONG).show();
}
});
});
if (attachments.get(mediaPosition - 1).translation != null) {
binding.mediaDescription.setVisibility(View.GONE);
binding.mediaDescriptionTranslated.setText(attachments.get(mediaPosition - 1).translation);
binding.mediaDescriptionTranslated.setVisibility(View.VISIBLE);
} else {
binding.mediaDescription.setVisibility(View.VISIBLE);
binding.mediaDescriptionTranslated.setVisibility(View.GONE);
}
}
binding.mediaViewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
public void onPageScrollStateChanged(int state) {
} }
binding.mediaViewpager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels);
} }
@Override
public void onPageSelected(int position) { public void onPageSelected(int position) {
mediaPosition = position; super.onPageSelected(position);
String description = attachments.get(position).description; String description = attachments.get(position).description;
if (handler != null) { if (handler != null) {
handler.removeCallbacksAndMessages(null); handler.removeCallbacksAndMessages(null);
@ -188,51 +152,24 @@ public class MediaActivity extends BaseTransparentActivity implements OnDownload
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(description); binding.mediaDescription.setText(description);
} }
binding.translate.setOnClickListener(v -> {
String descriptionToTranslate = attachments.get(position).description;
TranslateHelper.translate(MediaActivity.this, descriptionToTranslate, translated -> {
if (translated != null) {
attachments.get(position).translation = translated;
binding.mediaDescriptionTranslated.setText(translated);
binding.mediaDescriptionTranslated.setVisibility(View.VISIBLE);
binding.mediaDescription.setVisibility(View.GONE);
} else {
Toasty.error(MediaActivity.this, getString(R.string.toast_error_translate), Toast.LENGTH_LONG).show();
}
});
});
if (!fullscreen) {
if (attachments.get(position).translation != null) {
binding.mediaDescription.setVisibility(View.GONE);
binding.mediaDescriptionTranslated.setText(attachments.get(position).translation);
binding.mediaDescriptionTranslated.setVisibility(View.VISIBLE);
} else {
binding.mediaDescription.setVisibility(View.VISIBLE);
binding.mediaDescriptionTranslated.setVisibility(View.GONE);
}
} else {
binding.mediaDescription.setVisibility(View.GONE);
binding.mediaDescriptionTranslated.setVisibility(View.GONE);
}
}
});
setFullscreen(true);
} }
@Override @Override
public boolean dispatchTouchEvent(MotionEvent event) { public void onPageScrollStateChanged(int state) {
try { super.onPageScrollStateChanged(state);
return super.dispatchTouchEvent(event);
} catch (IllegalArgumentException ex) {
ex.printStackTrace();
} }
return false; });
setFullscreen(true);
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int screenHeight = size.y;
minTouch = (int) (screenHeight * 0.1);
maxTouch = (int) (screenHeight * 0.9);
} }
public void toogleFullScreen() {
fullscreen = !fullscreen;
setFullscreen(fullscreen);
}
@Override @Override
public boolean onCreateOptionsMenu(@NonNull Menu menu) { public boolean onCreateOptionsMenu(@NonNull Menu menu) {
@ -250,7 +187,6 @@ public class MediaActivity extends BaseTransparentActivity implements OnDownload
int position = binding.mediaViewpager.getCurrentItem(); int position = binding.mediaViewpager.getCurrentItem();
Attachment attachment = attachments.get(position); Attachment attachment = attachments.get(position);
if (Build.VERSION.SDK_INT >= 23) { if (Build.VERSION.SDK_INT >= 23) {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
if (ContextCompat.checkSelfPermission(MediaActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(MediaActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { if (ContextCompat.checkSelfPermission(MediaActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(MediaActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MediaActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, Helper.EXTERNAL_STORAGE_REQUEST_CODE_MEDIA_SAVE); ActivityCompat.requestPermissions(MediaActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, Helper.EXTERNAL_STORAGE_REQUEST_CODE_MEDIA_SAVE);
} else { } else {
@ -261,14 +197,6 @@ public class MediaActivity extends BaseTransparentActivity implements OnDownload
downloadID = -1; downloadID = -1;
} }
} }
} else {
if (attachment.type.compareTo("image") == 0) {
MediaHelper.manageMove(MediaActivity.this, attachment.url, false);
} else {
MediaHelper.manageDownloadsNoPopup(MediaActivity.this, attachment.url);
downloadID = -1;
}
}
} else { } else {
if (attachment.type.compareToIgnoreCase("image") == 0) { if (attachment.type.compareToIgnoreCase("image") == 0) {
MediaHelper.manageMove(MediaActivity.this, attachment.url, false); MediaHelper.manageMove(MediaActivity.this, attachment.url, false);
@ -322,40 +250,52 @@ public class MediaActivity extends BaseTransparentActivity implements OnDownload
} }
} }
private void toggleScreenContain(boolean fullscreen) { @Override
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startX = event.getX();
startY = event.getY();
break;
case MotionEvent.ACTION_UP:
float endX = event.getX();
float endY = event.getY();
if (endY > minTouch && endY < maxTouch && isAClick(startX, endX, startY, endY)) {
setFullscreen(!fullscreen);
if (!fullscreen) { if (!fullscreen) {
String description = attachments.get(binding.mediaViewpager.getCurrentItem()).description; String description = attachments.get(binding.mediaViewpager.getCurrentItem()).description;
if (handler != null) {
handler.removeCallbacksAndMessages(null);
}
handler = new Handler();
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(description); binding.mediaDescription.setText(description);
if (attachments.get(binding.mediaViewpager.getCurrentItem()).translation != null) {
binding.mediaDescription.setVisibility(View.GONE);
binding.mediaDescriptionTranslated.setText(attachments.get(binding.mediaViewpager.getCurrentItem()).translation);
binding.mediaDescriptionTranslated.setVisibility(View.VISIBLE);
} else {
binding.mediaDescription.setVisibility(View.VISIBLE);
binding.mediaDescriptionTranslated.setVisibility(View.GONE);
} }
} else {
binding.translate.setVisibility(View.GONE);
if (status != null) {
binding.originalMessage.setVisibility(View.VISIBLE);
} else {
binding.originalMessage.setVisibility(View.INVISIBLE);
} }
binding.mediaDescriptionTranslated.setVisibility(View.GONE);
binding.mediaDescription.setVisibility(View.GONE);
} }
} else { break;
binding.originalMessage.setVisibility(View.INVISIBLE);
binding.translate.setVisibility(View.GONE);
binding.mediaDescriptionTranslated.setVisibility(View.GONE);
binding.mediaDescription.setVisibility(View.GONE);
} }
try {
return super.dispatchTouchEvent(event);
} catch (IllegalArgumentException ex) {
ex.printStackTrace();
}
return false;
} }
private boolean isAClick(float startX, float endX, float startY, float endY) {
float differenceX = Math.abs(startX - endX);
float differenceY = Math.abs(startY - endY);
int CLICK_ACTION_THRESHOLD = 200;
return !(differenceX > CLICK_ACTION_THRESHOLD/* =5 */ || differenceY > CLICK_ACTION_THRESHOLD);
}
@Override @Override
public void onDestroy() { public void onDestroy() {
binding = null;
unregisterReceiver(onDownloadComplete); unregisterReceiver(onDownloadComplete);
super.onDestroy(); super.onDestroy();
} }
@ -375,15 +315,6 @@ public class MediaActivity extends BaseTransparentActivity implements OnDownload
super.onPostResume(); super.onPostResume();
} }
// Shows the system bars by removing all the flags
// except for the ones that make the content appear under the system bars.
private void showSystemUI() {
View decorView = getWindow().getDecorView();
decorView.setSystemUiVisibility(flags |
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
}
public boolean getFullScreen() { public boolean getFullScreen() {
return this.fullscreen; return this.fullscreen;
@ -394,17 +325,10 @@ public class MediaActivity extends BaseTransparentActivity implements OnDownload
if (!fullscreen) { if (!fullscreen) {
showSystemUI(); showSystemUI();
binding.mediaDescription.setVisibility(View.VISIBLE); binding.mediaDescription.setVisibility(View.VISIBLE);
binding.translate.setVisibility(View.VISIBLE);
if (mediaFromProfile) {
binding.originalMessage.setVisibility(View.VISIBLE);
}
} else { } else {
hideSystemUI();
binding.mediaDescription.setVisibility(View.GONE); binding.mediaDescription.setVisibility(View.GONE);
binding.translate.setVisibility(View.GONE); hideSystemUI();
binding.originalMessage.setVisibility(View.INVISIBLE);
} }
toggleScreenContain(fullscreen);
} }
private void hideSystemUI() { private void hideSystemUI() {
@ -424,22 +348,28 @@ public class MediaActivity extends BaseTransparentActivity implements OnDownload
| View.SYSTEM_UI_FLAG_FULLSCREEN); | View.SYSTEM_UI_FLAG_FULLSCREEN);
} }
public FragmentMedia getCurrentFragment() { // Shows the system bars by removing all the flags
return mCurrentFragment; // except for the ones that make the content appear under the system bars.
private void showSystemUI() {
View decorView = getWindow().getDecorView();
decorView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
} }
/** /**
* Media Pager * Media Pager
*/ */
@SuppressWarnings("deprecation") private class ScreenSlidePagerAdapter extends FragmentStateAdapter {
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
ScreenSlidePagerAdapter(FragmentManager fm) { ScreenSlidePagerAdapter(FragmentActivity fa) {
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); super(fa);
} }
@NotNull @NonNull
@Override @Override
public Fragment getItem(int position) { public Fragment createFragment(int position) {
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
FragmentMedia mediaSliderFragment = new FragmentMedia(); FragmentMedia mediaSliderFragment = new FragmentMedia();
bundle.putInt(Helper.ARG_MEDIA_POSITION, position); bundle.putInt(Helper.ARG_MEDIA_POSITION, position);
@ -449,15 +379,7 @@ public class MediaActivity extends BaseTransparentActivity implements OnDownload
} }
@Override @Override
public void setPrimaryItem(@NotNull ViewGroup container, int position, @NotNull Object object) { public int getItemCount() {
if (getCurrentFragment() != object) {
mCurrentFragment = ((FragmentMedia) object);
}
super.setPrimaryItem(container, position, object);
}
@Override
public int getCount() {
return attachments.size(); return attachments.size();
} }
} }

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.activities; package app.fedilab.android.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -16,6 +16,7 @@ package app.fedilab.android.mastodon.activities;
import android.content.Intent; import android.content.Intent;
import android.graphics.drawable.ColorDrawable;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.text.method.LinkMovementMethod; import android.text.method.LinkMovementMethod;
@ -24,34 +25,36 @@ import android.view.View;
import android.widget.TextView; import android.widget.TextView;
import androidx.core.app.ActivityOptionsCompat; import androidx.core.app.ActivityOptionsCompat;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
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.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Account;
import app.fedilab.android.client.entities.api.Status;
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.helper.CrossActionHelper;
import app.fedilab.android.mastodon.client.entities.api.Status; import app.fedilab.android.helper.Helper;
import app.fedilab.android.mastodon.helper.CrossActionHelper; import app.fedilab.android.helper.MastodonHelper;
import app.fedilab.android.mastodon.helper.Helper; import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.mastodon.helper.MastodonHelper; import app.fedilab.android.viewmodel.mastodon.AccountsVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.AccountsVM;
public class PartnerShipActivity extends BaseBarActivity { public class PartnerShipActivity extends BaseActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyThemeBar(this);
ActivityPartnershipBinding binding = ActivityPartnershipBinding.inflate(getLayoutInflater()); ActivityPartnershipBinding binding = ActivityPartnershipBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
TextView about_partnership = findViewById(R.id.about_partnership); TextView about_partnership = findViewById(R.id.about_partnership);
@ -61,8 +64,9 @@ public class PartnerShipActivity extends BaseBarActivity {
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://masto.host")); Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://masto.host"));
startActivity(browserIntent); startActivity(browserIntent);
}); });
binding.accountFollow.setBackgroundTintList(ThemeHelper.getButtonActionColorStateList(PartnerShipActivity.this));
setTitle(R.string.action_partnership); setTitle(R.string.action_partnership);
binding.accountFollow.setIconResource(R.drawable.ic_baseline_person_add_24); binding.accountFollow.setImageResource(R.drawable.ic_baseline_person_add_24);
CrossActionHelper.fetchRemoteAccount(PartnerShipActivity.this, "@mastohost@mastodon.social", new CrossActionHelper.Callback() { CrossActionHelper.fetchRemoteAccount(PartnerShipActivity.this, "@mastohost@mastodon.social", new CrossActionHelper.Callback() {
@Override @Override
public void federatedStatus(Status status) { public void federatedStatus(Status status) {
@ -88,12 +92,12 @@ public class PartnerShipActivity extends BaseBarActivity {
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<>();
ids.add(account.id); ids.add(account.id);
accountsVM.getRelationships(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, ids) accountsVM.getRelationships(MainActivity.currentInstance, MainActivity.currentToken, ids)
.observe(PartnerShipActivity.this, relationShips -> { .observe(PartnerShipActivity.this, relationShips -> {
if (relationShips != null && relationShips.size() > 0) { if (relationShips != null && relationShips.size() > 0) {
if (!relationShips.get(0).following) { if (!relationShips.get(0).following) {
binding.accountFollow.setVisibility(View.VISIBLE); binding.accountFollow.setVisibility(View.VISIBLE);
binding.accountFollow.setOnClickListener(v -> accountsVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, true, false, null) binding.accountFollow.setOnClickListener(v -> accountsVM.follow(MainActivity.currentInstance, MainActivity.currentToken, account.id, true, false)
.observe(PartnerShipActivity.this, relationShip -> binding.accountFollow.setVisibility(View.GONE))); .observe(PartnerShipActivity.this, relationShip -> binding.accountFollow.setVisibility(View.GONE)));
} }
} }

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.activities; package app.fedilab.android.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -25,16 +25,19 @@ 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.drawable.Animatable; import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.Html;
import android.text.Spannable;
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.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
@ -49,7 +52,6 @@ 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.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;
@ -57,14 +59,13 @@ 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;
import androidx.viewpager2.widget.ViewPager2;
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.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.tabs.TabLayout; import com.google.android.material.tabs.TabLayout;
import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
@ -76,35 +77,30 @@ 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;
import app.fedilab.android.activities.MainActivity; import app.fedilab.android.client.entities.api.Account;
import app.fedilab.android.client.entities.api.Attachment;
import app.fedilab.android.client.entities.api.Field;
import app.fedilab.android.client.entities.api.IdentityProof;
import app.fedilab.android.client.entities.api.MastodonList;
import app.fedilab.android.client.entities.api.RelationShip;
import app.fedilab.android.client.entities.app.Pinned;
import app.fedilab.android.client.entities.app.PinnedTimeline;
import app.fedilab.android.client.entities.app.RemoteInstance;
import app.fedilab.android.client.entities.app.Timeline;
import app.fedilab.android.client.entities.app.WellKnownNodeinfo;
import app.fedilab.android.databinding.ActivityProfileBinding; import app.fedilab.android.databinding.ActivityProfileBinding;
import app.fedilab.android.databinding.NotificationsRelatedAccountsBinding; import app.fedilab.android.exception.DBException;
import app.fedilab.android.mastodon.client.entities.api.Account; import app.fedilab.android.helper.CrossActionHelper;
import app.fedilab.android.mastodon.client.entities.api.Attachment; import app.fedilab.android.helper.Helper;
import app.fedilab.android.mastodon.client.entities.api.FamiliarFollowers; import app.fedilab.android.helper.MastodonHelper;
import app.fedilab.android.mastodon.client.entities.api.Field; import app.fedilab.android.helper.SpannableHelper;
import app.fedilab.android.mastodon.client.entities.api.IdentityProof; import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.mastodon.client.entities.api.MastodonList; import app.fedilab.android.ui.drawer.IdentityProofsAdapter;
import app.fedilab.android.mastodon.client.entities.api.RelationShip; import app.fedilab.android.ui.pageadapter.FedilabProfileTLPageAdapter;
import app.fedilab.android.mastodon.client.entities.app.Languages; import app.fedilab.android.viewmodel.mastodon.AccountsVM;
import app.fedilab.android.mastodon.client.entities.app.Pinned; import app.fedilab.android.viewmodel.mastodon.NodeInfoVM;
import app.fedilab.android.mastodon.client.entities.app.PinnedTimeline; import app.fedilab.android.viewmodel.mastodon.ReorderVM;
import app.fedilab.android.mastodon.client.entities.app.RemoteInstance; import app.fedilab.android.viewmodel.mastodon.TimelinesVM;
import app.fedilab.android.mastodon.client.entities.app.Timeline;
import app.fedilab.android.mastodon.client.entities.app.WellKnownNodeinfo;
import app.fedilab.android.mastodon.exception.DBException;
import app.fedilab.android.mastodon.helper.CrossActionHelper;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.helper.MastodonHelper;
import app.fedilab.android.mastodon.helper.SpannableHelper;
import app.fedilab.android.mastodon.helper.ThemeHelper;
import app.fedilab.android.mastodon.ui.drawer.FieldAdapter;
import app.fedilab.android.mastodon.ui.drawer.IdentityProofsAdapter;
import app.fedilab.android.mastodon.ui.pageadapter.FedilabProfileTLPageAdapter;
import app.fedilab.android.mastodon.viewmodel.mastodon.AccountsVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.NodeInfoVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.ReorderVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.TimelinesVM;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
@ -112,7 +108,6 @@ public class ProfileActivity extends BaseActivity {
private RelationShip relationship; private RelationShip relationship;
private FamiliarFollowers familiarFollowers;
private Account account; private Account account;
private ScheduledExecutorService scheduledExecutorService; private ScheduledExecutorService scheduledExecutorService;
private action doAction; private action doAction;
@ -123,7 +118,7 @@ public class ProfileActivity extends BaseActivity {
private String account_id; private String account_id;
private String mention_str; private String mention_str;
private WellKnownNodeinfo.NodeInfo nodeInfo; private WellKnownNodeinfo.NodeInfo nodeInfo;
private boolean checkRemotely;
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) {
@ -138,46 +133,47 @@ public class ProfileActivity extends BaseActivity {
} }
} }
}; };
private boolean homeMuted;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyTheme(this);
binding = ActivityProfileBinding.inflate(getLayoutInflater()); binding = ActivityProfileBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
ActionBar actionBar = getSupportActionBar(); ActionBar actionBar = getSupportActionBar();
Bundle b = getIntent().getExtras(); Bundle b = getIntent().getExtras();
binding.accountFollow.setEnabled(false); binding.accountFollow.setEnabled(false);
checkRemotely = false;
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this);
homeMuted = false;
if (b != null) { if (b != null) {
account = (Account) b.getSerializable(Helper.ARG_ACCOUNT); account = (Account) b.getSerializable(Helper.ARG_ACCOUNT);
account_id = b.getString(Helper.ARG_USER_ID, null); account_id = b.getString(Helper.ARG_USER_ID, null);
mention_str = b.getString(Helper.ARG_MENTION, null); mention_str = b.getString(Helper.ARG_MENTION, null);
checkRemotely = b.getBoolean(Helper.ARG_CHECK_REMOTELY, false);
} }
if (!checkRemotely) {
checkRemotely = sharedpreferences.getBoolean(getString(R.string.SET_PROFILE_REMOTELY), false);
} postponeEnterTransition();
ActivityCompat.postponeEnterTransition(ProfileActivity.this);
//Remove title //Remove title
if (actionBar != null) { if (actionBar != null) {
actionBar.setDisplayShowTitleEnabled(false); actionBar.setDisplayShowTitleEnabled(false);
actionBar.setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true);
} }
binding.toolbar.setPopupTheme(Helper.popupStyle());
float scale = sharedpreferences.getFloat(getString(R.string.SET_FONT_SCALE), 1.1f);
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);
if (account != null) { if (account != null) {
initializeView(account); new Thread(() -> {
account = SpannableHelper.convertAccount(ProfileActivity.this, account);
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> initializeView(account);
mainHandler.post(myRunnable);
}).start();
} else if (account_id != null) { } else if (account_id != null) {
accountsVM.getAccount(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account_id).observe(ProfileActivity.this, fetchedAccount -> { accountsVM.getAccount(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account_id).observe(ProfileActivity.this, fetchedAccount -> {
account = fetchedAccount; account = fetchedAccount;
@ -189,47 +185,21 @@ public class ProfileActivity extends BaseActivity {
account = accounts.get(0); account = accounts.get(0);
initializeView(account); initializeView(account);
} else { } else {
Toasty.error(ProfileActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show(); Toasty.error(ProfileActivity.this, getString(R.string.toast_error_loading_account), Toast.LENGTH_LONG).show();
finish(); finish();
} }
}); });
} else { } else {
Toasty.error(ProfileActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show(); Toasty.error(ProfileActivity.this, getString(R.string.toast_error_loading_account), Toast.LENGTH_LONG).show();
finish(); finish();
} }
//Check if account is homeMuted
accountsVM.isMuted(currentAccount, account).observe(this, result -> homeMuted = result != null && result);
LocalBroadcastManager.getInstance(ProfileActivity.this).registerReceiver(broadcast_data, new IntentFilter(Helper.BROADCAST_DATA)); LocalBroadcastManager.getInstance(ProfileActivity.this).registerReceiver(broadcast_data, new IntentFilter(Helper.BROADCAST_DATA));
} }
private void updateViewWithNewData(Account account) {
if (account != null) {
if (account.role != null && account.role.highlighted) {
binding.accountRole.setText(account.role.name);
binding.accountRole.setVisibility(View.VISIBLE);
}
if (binding.accountTabLayout.getTabCount() > 2) {
TabLayout.Tab statusTab = binding.accountTabLayout.getTabAt(0);
TabLayout.Tab followingTab = binding.accountTabLayout.getTabAt(1);
TabLayout.Tab followerTab = binding.accountTabLayout.getTabAt(2);
if (statusTab != null) {
statusTab.setText(getString(R.string.status_cnt, Helper.withSuffix(account.statuses_count)));
}
if (followingTab != null) {
followingTab.setText(getString(R.string.following_cnt, Helper.withSuffix(account.following_count)));
}
if (followerTab != null) {
followerTab.setText(getString(R.string.followers_cnt, Helper.withSuffix(account.followers_count)));
}
}
}
}
private void initializeView(Account account) { private void initializeView(Account account) {
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(ProfileActivity.this); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(ProfileActivity.this);
if (account == null) { if (account == null) {
Toasty.error(ProfileActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show(); Toasty.error(ProfileActivity.this, getString(R.string.toast_error_loading_account), Toast.LENGTH_LONG).show();
finish(); finish();
return; return;
} }
@ -262,13 +232,6 @@ public class ProfileActivity extends BaseActivity {
updateAccount(); updateAccount();
} }
}); });
accountsVM.getFamiliarFollowers(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, accountListToCheck).observe(ProfileActivity.this, familiarFollowersList -> {
if (familiarFollowersList != null && familiarFollowersList.size() > 0) {
this.familiarFollowers = familiarFollowersList.get(0);
updateAccount();
}
});
//Retrieve identity proofs //Retrieve identity proofs
accountsVM.getIdentityProofs(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id).observe(ProfileActivity.this, identityProofs -> { accountsVM.getIdentityProofs(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id).observe(ProfileActivity.this, identityProofs -> {
this.identityProofList = identityProofs; this.identityProofList = identityProofs;
@ -285,13 +248,19 @@ public class ProfileActivity extends BaseActivity {
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(ProfileActivity.this, account);
binding.accountTabLayout.addTab(binding.accountTabLayout.newTab().setText(getString(R.string.status_cnt, Helper.withSuffix(account.statuses_count)))); binding.accountTabLayout.addTab(binding.accountTabLayout.newTab().setText(getString(R.string.status_cnt, Helper.withSuffix(account.statuses_count))));
binding.accountTabLayout.addTab(binding.accountTabLayout.newTab().setText(getString(R.string.following_cnt, Helper.withSuffix(account.following_count)))); binding.accountTabLayout.addTab(binding.accountTabLayout.newTab().setText(getString(R.string.following_cnt, Helper.withSuffix(account.following_count))));
binding.accountTabLayout.addTab(binding.accountTabLayout.newTab().setText(getString(R.string.followers_cnt, Helper.withSuffix(account.followers_count)))); binding.accountTabLayout.addTab(binding.accountTabLayout.newTab().setText(getString(R.string.followers_cnt, Helper.withSuffix(account.followers_count))));
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.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels);
binding.accountTabLayout.selectTab(binding.accountTabLayout.getTabAt(position));
}
});
binding.accountTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { binding.accountTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override @Override
public void onTabSelected(TabLayout.Tab tab) { public void onTabSelected(TabLayout.Tab tab) {
@ -300,15 +269,18 @@ public class ProfileActivity extends BaseActivity {
@Override @Override
public void onTabUnselected(TabLayout.Tab tab) { public void onTabUnselected(TabLayout.Tab tab) {
} }
@Override @Override
public void onTabReselected(TabLayout.Tab tab) { public void onTabReselected(TabLayout.Tab tab) {
} }
}); });
binding.accountTabLayout.setTabTextColors(ThemeHelper.getAttColor(ProfileActivity.this, R.attr.mTextColor), ContextCompat.getColor(ProfileActivity.this, R.color.cyanea_accent_dark_reference));
binding.accountTabLayout.setTabIconTint(ThemeHelper.getColorStateList(ProfileActivity.this));
boolean disableGif = sharedpreferences.getBoolean(getString(R.string.SET_DISABLE_GIF), false); boolean disableGif = sharedpreferences.getBoolean(getString(R.string.SET_DISABLE_GIF), false);
String targetedUrl = disableGif ? account.avatar_static : account.avatar; String targetedUrl = disableGif ? account.avatar_static : account.avatar;
// MastodonHelper.loadPPMastodon(binding.accountPp, account);
Glide.with(ProfileActivity.this) Glide.with(ProfileActivity.this)
.asDrawable() .asDrawable()
.dontTransform() .dontTransform()
@ -317,32 +289,23 @@ public class ProfileActivity extends BaseActivity {
@Override @Override
public void onResourceReady(@NonNull final Drawable resource, Transition<? super Drawable> transition) { public void onResourceReady(@NonNull final Drawable resource, Transition<? super Drawable> transition) {
binding.profilePicture.setImageDrawable(resource); binding.profilePicture.setImageDrawable(resource);
binding.accountPp.setImageDrawable(resource); startPostponedEnterTransition();
if (resource instanceof Animatable) {
binding.profilePicture.animate();
binding.accountPp.animate();
((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); startPostponedEnterTransition();
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.accountPp.setImageResource(R.drawable.ic_person);
ActivityCompat.startPostponedEnterTransition(ProfileActivity.this);
} }
} }
); );
//Load header //Load header
MastodonHelper.loadProfileMediaMastodon(ProfileActivity.this, binding.bannerPp, account, MastodonHelper.MediaAccountType.HEADER); MastodonHelper.loadProfileMediaMastodon(binding.bannerPp, account, MastodonHelper.MediaAccountType.HEADER);
//Redraws icon for locked accounts //Redraws icon for locked accounts
final float scale = getResources().getDisplayMetrics().density; final float scale = getResources().getDisplayMetrics().density;
if (account.locked) { if (account.locked) {
@ -365,7 +328,7 @@ public class ProfileActivity extends BaseActivity {
final SpannableString content = new SpannableString(getString(R.string.disclaimer_full)); final SpannableString content = new SpannableString(getString(R.string.disclaimer_full));
content.setSpan(new UnderlineSpan(), 0, content.length(), 0); content.setSpan(new UnderlineSpan(), 0, content.length(), 0);
content.setSpan(new ForegroundColorSpan(ThemeHelper.getAttColor(this, R.attr.colorPrimary)), 0, content.length(), content.setSpan(new ForegroundColorSpan(ContextCompat.getColor(ProfileActivity.this, R.color.cyanea_accent_reference)), 0, content.length(),
Spanned.SPAN_INCLUSIVE_EXCLUSIVE); Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
binding.warningMessage.setText(content); binding.warningMessage.setText(content);
binding.warningMessage.setOnClickListener(view -> { binding.warningMessage.setOnClickListener(view -> {
@ -392,37 +355,58 @@ public class ProfileActivity extends BaseActivity {
binding.accountMoved.setText(spannableString, TextView.BufferType.SPANNABLE); binding.accountMoved.setText(spannableString, TextView.BufferType.SPANNABLE);
binding.accountMoved.setMovementMethod(LinkMovementMethod.getInstance()); binding.accountMoved.setMovementMethod(LinkMovementMethod.getInstance());
} }
if (account.acct != null && account.acct.contains("@")) if (account.acct.contains("@"))
binding.warningContainer.setVisibility(View.VISIBLE); binding.warningMessage.setVisibility(View.VISIBLE);
else else
binding.warningContainer.setVisibility(View.GONE); binding.warningMessage.setVisibility(View.GONE);
if (checkRemotely) {
binding.openRemoteProfile.setVisibility(View.GONE);
}
binding.openRemoteProfile.setOnClickListener(v -> {
Intent intent = new Intent(ProfileActivity.this, ProfileActivity.class);
Bundle b = new Bundle();
b.putSerializable(Helper.ARG_ACCOUNT, account);
b.putSerializable(Helper.ARG_CHECK_REMOTELY, true);
intent.putExtras(b);
ActivityOptionsCompat options = ActivityOptionsCompat
.makeSceneTransitionAnimation(ProfileActivity.this, binding.profilePicture, getString(R.string.activity_porfile_pp));
startActivity(intent, options.toBundle());
finish();
});
//Fields for profile //Fields for profile
List<Field> fields = account.fields; List<Field> fields = account.fields;
if (fields != null && fields.size() > 0) { if (fields != null && fields.size() > 0) {
FieldAdapter fieldAdapter = new FieldAdapter(fields, account); for (int i = 0; i < fields.size(); i++) {
binding.fieldsContainer.setAdapter(fieldAdapter); LinearLayout field;
binding.fieldsContainer.setLayoutManager(new LinearLayoutManager(ProfileActivity.this)); TextView labelView;
TextView valueView;
switch (i) {
case 1:
field = binding.field1;
labelView = binding.label1;
valueView = binding.value1;
break;
case 2:
field = binding.field2;
labelView = binding.label2;
valueView = binding.value2;
break;
case 3:
field = binding.field3;
labelView = binding.label3;
valueView = binding.value3;
break;
default:
field = binding.field4;
labelView = binding.label4;
valueView = binding.value4;
break;
} }
binding.accountDn.setText( field.setVisibility(View.VISIBLE);
account.getSpanDisplayName(ProfileActivity.this, if (fields.get(i).verified_at != null) {
new WeakReference<>(binding.accountDn)), valueView.setCompoundDrawablesWithIntrinsicBounds(null, null, ContextCompat.getDrawable(ProfileActivity.this, R.drawable.ic_baseline_verified_24), null);
TextView.BufferType.SPANNABLE); fields.get(i).value_span.setSpan(new ForegroundColorSpan(ContextCompat.getColor(ProfileActivity.this, R.color.verified_text)), 0, fields.get(i).value_span.toString().length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
valueView.setText(fields.get(i).value_span != null ? fields.get(i).value_span : fields.get(i).value, TextView.BufferType.SPANNABLE);
valueView.setMovementMethod(LinkMovementMethod.getInstance());
labelView.setText(fields.get(i).name);
}
binding.fieldsContainer.setVisibility(View.VISIBLE);
}
if (account.span_display_name == null && account.display_name == null) {
binding.accountDn.setText(account.username);
} else {
binding.accountDn.setText(account.span_display_name != null ? account.span_display_name : account.display_name, TextView.BufferType.SPANNABLE);
}
binding.accountUn.setText(String.format("@%s", account.acct)); binding.accountUn.setText(String.format("@%s", account.acct));
binding.accountUn.setOnLongClickListener(v -> { binding.accountUn.setOnLongClickListener(v -> {
@ -436,13 +420,15 @@ public class ProfileActivity extends BaseActivity {
clipboard.setPrimaryClip(clip); clipboard.setPrimaryClip(clip);
return false; return false;
}); });
binding.accountNote.setText(
account.getSpanNote(ProfileActivity.this, if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
new WeakReference<>(binding.accountNote)), binding.accountNote.setText(account.span_note != null ? account.span_note : new SpannableString(Html.fromHtml(account.note, Html.FROM_HTML_MODE_COMPACT)), TextView.BufferType.SPANNABLE);
TextView.BufferType.SPANNABLE); else
binding.accountNote.setText(account.span_note != null ? account.span_note : new SpannableString(Html.fromHtml(account.note)), TextView.BufferType.SPANNABLE);
binding.accountNote.setMovementMethod(LinkMovementMethod.getInstance()); binding.accountNote.setMovementMethod(LinkMovementMethod.getInstance());
MastodonHelper.loadPPMastodon(binding.accountPp, account);
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 b = new Bundle(); Bundle b = new Bundle();
@ -464,12 +450,23 @@ public class ProfileActivity extends BaseActivity {
}); });
binding.accountNotification.setOnClickListener(v -> {
if (relationship != null && relationship.followed_by) {
relationship.notifying = !relationship.notifying;
accountsVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, relationship.showing_reblogs, relationship.notifying)
.observe(ProfileActivity.this, relationShip -> {
this.relationship = relationShip;
updateAccount();
});
}
});
binding.accountFollow.setOnClickListener(v -> { binding.accountFollow.setOnClickListener(v -> {
if (doAction == action.NOTHING) { if (doAction == action.NOTHING) {
Toasty.info(ProfileActivity.this, getString(R.string.nothing_to_do), Toast.LENGTH_LONG).show(); Toasty.info(ProfileActivity.this, getString(R.string.nothing_to_do), Toast.LENGTH_LONG).show();
} else if (doAction == action.FOLLOW) { } else if (doAction == action.FOLLOW) {
binding.accountFollow.setEnabled(false); binding.accountFollow.setEnabled(false);
accountsVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, true, false, null) accountsVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, true, false)
.observe(ProfileActivity.this, relationShip -> { .observe(ProfileActivity.this, relationShip -> {
this.relationship = relationShip; this.relationship = relationShip;
updateAccount(); updateAccount();
@ -477,7 +474,7 @@ public class ProfileActivity extends BaseActivity {
} else if (doAction == action.UNFOLLOW) { } else if (doAction == action.UNFOLLOW) {
boolean confirm_unfollow = sharedpreferences.getBoolean(getString(R.string.SET_UNFOLLOW_VALIDATION), true); boolean confirm_unfollow = sharedpreferences.getBoolean(getString(R.string.SET_UNFOLLOW_VALIDATION), true);
if (confirm_unfollow) { if (confirm_unfollow) {
AlertDialog.Builder unfollowConfirm = new MaterialAlertDialogBuilder(ProfileActivity.this); AlertDialog.Builder unfollowConfirm = new AlertDialog.Builder(ProfileActivity.this, Helper.dialogStyle());
unfollowConfirm.setTitle(getString(R.string.unfollow_confirm)); unfollowConfirm.setTitle(getString(R.string.unfollow_confirm));
unfollowConfirm.setMessage(account.acct); unfollowConfirm.setMessage(account.acct);
unfollowConfirm.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss()); unfollowConfirm.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
@ -510,7 +507,7 @@ public class ProfileActivity extends BaseActivity {
}); });
} }
}); });
binding.accountFollow.setOnLongClickListener(v -> { binding.accountNotification.setOnLongClickListener(v -> {
CrossActionHelper.doCrossAction(ProfileActivity.this, CrossActionHelper.TypeOfCrossAction.FOLLOW_ACTION, account, null); CrossActionHelper.doCrossAction(ProfileActivity.this, CrossActionHelper.TypeOfCrossAction.FOLLOW_ACTION, account, null);
return false; return false;
}); });
@ -533,19 +530,16 @@ public class ProfileActivity extends BaseActivity {
binding.instanceInfo.setVisibility(View.VISIBLE); binding.instanceInfo.setVisibility(View.VISIBLE);
binding.instanceInfo.setOnClickListener(v -> { binding.instanceInfo.setOnClickListener(v -> {
InstanceProfileActivity instanceProfileActivity = new InstanceProfileActivity(); Intent intent = new Intent(ProfileActivity.this, InstanceProfileActivity.class);
Bundle b = new Bundle(); Bundle b = new Bundle();
b.putString(Helper.ARG_INSTANCE, finalAccountInstance); b.putString(Helper.ARG_INSTANCE, finalAccountInstance);
instanceProfileActivity.setArguments(b); intent.putExtras(b);
instanceProfileActivity.show(getSupportFragmentManager(), null); startActivity(intent);
}); });
} }
}); });
if (accountInstance != null && !accountInstance.equalsIgnoreCase(MainActivity.currentInstance)) {
accountsVM.lookUpAccount(accountInstance, account.username).observe(ProfileActivity.this, this::updateViewWithNewData);
} else if (accountInstance != null && accountInstance.equalsIgnoreCase(MainActivity.currentInstance)) {
updateViewWithNewData(account);
}
} }
@ -553,10 +547,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 (currentAccount == null || account == null) {
return;
}
//The value for account is from same server so id can be used
if (account.id.equals(currentAccount.user_id)) {
binding.accountFollow.setVisibility(View.GONE);
binding.headerEditProfile.setVisibility(View.VISIBLE);
binding.headerEditProfile.bringToFront();
}
//Manage indentity proofs if not yet displayed //Manage indentity proofs if not yet displayed
if (identityProofList != null && identityProofList.size() > 0) { if (identityProofList != null && identityProofList.size() > 0) {
@ -565,7 +562,7 @@ public class ProfileActivity extends BaseActivity {
//Recyclerview for identity proof has not been inflated yet //Recyclerview for identity proof has not been inflated yet
if (identityProofsRecycler == null) { if (identityProofsRecycler == null) {
identity_proofs_indicator.setOnClickListener(v -> { identity_proofs_indicator.setOnClickListener(v -> {
AlertDialog.Builder builder = new MaterialAlertDialogBuilder(ProfileActivity.this); AlertDialog.Builder builder = new AlertDialog.Builder(ProfileActivity.this, Helper.dialogStyle());
identityProofsRecycler = new RecyclerView(ProfileActivity.this); identityProofsRecycler = new RecyclerView(ProfileActivity.this);
LinearLayoutManager mLayoutManager = new LinearLayoutManager(ProfileActivity.this); LinearLayoutManager mLayoutManager = new LinearLayoutManager(ProfileActivity.this);
identityProofsRecycler.setLayoutManager(mLayoutManager); identityProofsRecycler.setLayoutManager(mLayoutManager);
@ -579,33 +576,12 @@ public class ProfileActivity extends BaseActivity {
}); });
} }
} }
binding.accountFollow.setBackgroundTintList(ThemeHelper.getButtonActionColorStateList(ProfileActivity.this));
if (familiarFollowers != null && familiarFollowers.accounts != null && familiarFollowers.accounts.size() > 0) {
binding.relatedAccounts.removeAllViews();
for (Account account : familiarFollowers.accounts) {
NotificationsRelatedAccountsBinding notificationsRelatedAccountsBinding = NotificationsRelatedAccountsBinding.inflate(LayoutInflater.from(ProfileActivity.this));
MastodonHelper.loadProfileMediaMastodonRound(ProfileActivity.this, notificationsRelatedAccountsBinding.profilePicture, account);
notificationsRelatedAccountsBinding.acc.setText(account.username);
notificationsRelatedAccountsBinding.relatedAccountContainer.setOnClickListener(v -> {
Intent intent = new Intent(ProfileActivity.this, ProfileActivity.class);
Bundle b = new Bundle();
b.putSerializable(Helper.ARG_ACCOUNT, account);
intent.putExtras(b);
ActivityOptionsCompat options = ActivityOptionsCompat
.makeSceneTransitionAnimation(ProfileActivity.this, notificationsRelatedAccountsBinding.profilePicture, getString(R.string.activity_porfile_pp));
// start the new activity
startActivity(intent, options.toBundle());
});
binding.relatedAccounts.addView(notificationsRelatedAccountsBinding.getRoot());
}
binding.familiarFollowers.setVisibility(View.VISIBLE);
}
binding.accountFollow.setEnabled(true); binding.accountFollow.setEnabled(true);
//Visibility depending of the relationship //Visibility depending of the relationship
if (relationship != null) { if (relationship != null) {
if (relationship.blocked_by) { if (relationship.blocked_by) {
binding.accountFollow.setIconResource(R.drawable.ic_baseline_person_add_24); binding.accountFollow.setImageResource(R.drawable.ic_baseline_person_add_24);
binding.accountFollow.setVisibility(View.VISIBLE); binding.accountFollow.setVisibility(View.VISIBLE);
binding.accountFollow.setEnabled(false); binding.accountFollow.setEnabled(false);
binding.accountFollow.setContentDescription(getString(R.string.action_disabled)); binding.accountFollow.setContentDescription(getString(R.string.action_disabled));
@ -613,7 +589,7 @@ public class ProfileActivity extends BaseActivity {
if (relationship.requested) { if (relationship.requested) {
binding.accountFollowRequest.setVisibility(View.VISIBLE); binding.accountFollowRequest.setVisibility(View.VISIBLE);
binding.accountFollow.setIconResource(R.drawable.ic_baseline_hourglass_full_24); binding.accountFollow.setImageResource(R.drawable.ic_baseline_hourglass_full_24);
binding.accountFollow.setVisibility(View.VISIBLE); binding.accountFollow.setVisibility(View.VISIBLE);
binding.accountFollow.setContentDescription(getString(R.string.follow_request)); binding.accountFollow.setContentDescription(getString(R.string.follow_request));
doAction = action.UNFOLLOW; doAction = action.UNFOLLOW;
@ -624,50 +600,33 @@ public class ProfileActivity extends BaseActivity {
binding.accountFollowedBy.setVisibility(View.GONE); binding.accountFollowedBy.setVisibility(View.GONE);
} }
if (relationship.following) { if (relationship.following) {
binding.accountFollow.setIconResource(R.drawable.ic_baseline_person_remove_24); binding.accountFollow.setImageResource(R.drawable.ic_baseline_person_remove_24);
binding.accountFollow.setBackgroundTintList(ColorStateList.valueOf(ThemeHelper.getAttColor(this, R.attr.colorError))); binding.accountFollow.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor(ProfileActivity.this, R.color.red_1)));
doAction = action.UNFOLLOW; doAction = action.UNFOLLOW;
binding.accountFollow.setContentDescription(getString(R.string.action_unfollow)); binding.accountFollow.setContentDescription(getString(R.string.action_unfollow));
binding.accountFollow.setVisibility(View.VISIBLE); binding.accountFollow.setVisibility(View.VISIBLE);
} else if (relationship.blocking) { } else if (relationship.blocking) {
binding.accountFollow.setBackgroundTintList(ColorStateList.valueOf(ThemeHelper.getAttColor(this, R.attr.colorError))); binding.accountFollow.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor(ProfileActivity.this, R.color.red_1)));
binding.accountFollow.setIconResource(R.drawable.ic_baseline_lock_open_24); binding.accountFollow.setImageResource(R.drawable.ic_baseline_lock_open_24);
doAction = action.UNBLOCK; doAction = action.UNBLOCK;
binding.accountFollow.setVisibility(View.VISIBLE); binding.accountFollow.setVisibility(View.VISIBLE);
binding.accountFollow.setContentDescription(getString(R.string.action_unblock)); binding.accountFollow.setContentDescription(getString(R.string.action_unblock));
} else { } else {
binding.accountFollow.setIconResource(R.drawable.ic_baseline_person_add_24); binding.accountFollow.setImageResource(R.drawable.ic_baseline_person_add_24);
doAction = action.FOLLOW; doAction = action.FOLLOW;
binding.accountFollow.setVisibility(View.VISIBLE); binding.accountFollow.setVisibility(View.VISIBLE);
binding.accountFollow.setContentDescription(getString(R.string.action_follow)); binding.accountFollow.setContentDescription(getString(R.string.action_follow));
} }
//The value for account is from same server so id can be used
if (account.id.equals(currentAccount.user_id)) {
binding.accountFollow.setVisibility(View.GONE);
binding.headerEditProfile.setVisibility(View.VISIBLE);
binding.headerEditProfile.bringToFront();
}
if (!relationship.following) { if (!relationship.following) {
binding.accountNotification.setVisibility(View.GONE); binding.accountNotification.setVisibility(View.GONE);
} else { } else {
binding.accountNotification.setVisibility(View.VISIBLE); binding.accountNotification.setVisibility(View.VISIBLE);
} }
if (relationship.notifying) { if (relationship.notifying) {
binding.accountNotification.setIconResource(R.drawable.ic_baseline_notifications_active_24); binding.accountNotification.setImageResource(R.drawable.ic_baseline_notifications_active_24);
} else { } else {
binding.accountNotification.setIconResource(R.drawable.ic_baseline_notifications_off_24); binding.accountNotification.setImageResource(R.drawable.ic_baseline_notifications_off_24);
} }
binding.accountNotification.setOnClickListener(v -> {
if (relationship != null && relationship.following) {
relationship.notifying = !relationship.notifying;
accountsVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, relationship.showing_reblogs, relationship.notifying, relationship.languages)
.observe(ProfileActivity.this, relationShip -> {
this.relationship = relationShip;
updateAccount();
});
}
});
//Account note //Account note
if (relationship.note == null || relationship.note.trim().isEmpty()) { if (relationship.note == null || relationship.note.trim().isEmpty()) {
binding.personalNote.setText(R.string.note_for_account); binding.personalNote.setText(R.string.note_for_account);
@ -675,7 +634,7 @@ public class ProfileActivity extends BaseActivity {
binding.personalNote.setText(relationship.note); binding.personalNote.setText(relationship.note);
} }
binding.personalNote.setOnClickListener(view -> { binding.personalNote.setOnClickListener(view -> {
AlertDialog.Builder builderInner = new MaterialAlertDialogBuilder(ProfileActivity.this); AlertDialog.Builder builderInner = new AlertDialog.Builder(ProfileActivity.this, Helper.dialogStyle());
builderInner.setTitle(R.string.note_for_account); builderInner.setTitle(R.string.note_for_account);
EditText input = new EditText(ProfileActivity.this); EditText input = new EditText(ProfileActivity.this);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
@ -718,7 +677,6 @@ public class ProfileActivity extends BaseActivity {
menu.findItem(R.id.action_block).setVisible(false); menu.findItem(R.id.action_block).setVisible(false);
menu.findItem(R.id.action_report).setVisible(false); menu.findItem(R.id.action_report).setVisible(false);
menu.findItem(R.id.action_mute).setVisible(false); menu.findItem(R.id.action_mute).setVisible(false);
menu.findItem(R.id.action_timed_mute).setVisible(false);
menu.findItem(R.id.action_mention).setVisible(false); menu.findItem(R.id.action_mention).setVisible(false);
menu.findItem(R.id.action_follow_instance).setVisible(false); menu.findItem(R.id.action_follow_instance).setVisible(false);
menu.findItem(R.id.action_block_instance).setVisible(false); menu.findItem(R.id.action_block_instance).setVisible(false);
@ -726,13 +684,9 @@ public class ProfileActivity extends BaseActivity {
menu.findItem(R.id.action_endorse).setVisible(false); menu.findItem(R.id.action_endorse).setVisible(false);
menu.findItem(R.id.action_direct_message).setVisible(false); menu.findItem(R.id.action_direct_message).setVisible(false);
menu.findItem(R.id.action_add_to_list).setVisible(false); menu.findItem(R.id.action_add_to_list).setVisible(false);
menu.findItem(R.id.action_mute_home).setVisible(false);
menu.findItem(R.id.action_subscribed_language).setVisible(false);
} else { } else {
menu.findItem(R.id.action_block).setVisible(true); menu.findItem(R.id.action_block).setVisible(true);
menu.findItem(R.id.action_mute).setVisible(true); menu.findItem(R.id.action_mute).setVisible(true);
menu.findItem(R.id.action_mute_home).setVisible(true);
menu.findItem(R.id.action_timed_mute).setVisible(true);
menu.findItem(R.id.action_mention).setVisible(true); menu.findItem(R.id.action_mention).setVisible(true);
} }
//Update menu title depending of relationship //Update menu title depending of relationship
@ -740,8 +694,6 @@ public class ProfileActivity extends BaseActivity {
if (!relationship.following) { if (!relationship.following) {
menu.findItem(R.id.action_hide_boost).setVisible(false); menu.findItem(R.id.action_hide_boost).setVisible(false);
menu.findItem(R.id.action_endorse).setVisible(false); menu.findItem(R.id.action_endorse).setVisible(false);
menu.findItem(R.id.action_mute_home).setVisible(false);
menu.findItem(R.id.action_subscribed_language).setVisible(false);
} }
if (relationship.blocking) { if (relationship.blocking) {
menu.findItem(R.id.action_block).setTitle(R.string.action_unblock); menu.findItem(R.id.action_block).setTitle(R.string.action_unblock);
@ -759,11 +711,6 @@ public class ProfileActivity extends BaseActivity {
} else { } else {
menu.findItem(R.id.action_hide_boost).setTitle(getString(R.string.show_boost, account.username)); menu.findItem(R.id.action_hide_boost).setTitle(getString(R.string.show_boost, account.username));
} }
if (homeMuted) {
menu.findItem(R.id.action_mute_home).setTitle(getString(R.string.unmute_home));
} else {
menu.findItem(R.id.action_mute_home).setTitle(getString(R.string.mute_home));
}
} }
} }
return true; return true;
@ -772,12 +719,10 @@ public class ProfileActivity extends BaseActivity {
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
int itemId = item.getItemId(); int itemId = item.getItemId();
String[] splitAcct = null; String[] splitAcct = account.acct.split("@");
if (account != null && account.acct != null) {
splitAcct = account.acct.split("@");
}
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(ProfileActivity.this); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(ProfileActivity.this);
final boolean isOwner = account != null && account.id != null && BaseMainActivity.currentUserID != null && account.id.compareToIgnoreCase(BaseMainActivity.currentUserID) == 0; AlertDialog.Builder builderInner;
final boolean isOwner = account.id.compareToIgnoreCase(BaseMainActivity.currentUserID) == 0;
final String[] stringArrayConf; final String[] stringArrayConf;
if (isOwner) { if (isOwner) {
stringArrayConf = getResources().getStringArray(R.array.more_action_owner_confirm); stringArrayConf = getResources().getStringArray(R.array.more_action_owner_confirm);
@ -788,7 +733,7 @@ public class ProfileActivity extends BaseActivity {
if (itemId == android.R.id.home) { if (itemId == android.R.id.home) {
finish(); finish();
return true; return true;
} else if (itemId == R.id.action_follow_instance && splitAcct != null) { } else if (itemId == R.id.action_follow_instance) {
String finalInstanceName = splitAcct[1]; String finalInstanceName = splitAcct[1];
ReorderVM reorderVM = new ViewModelProvider(ProfileActivity.this).get(ReorderVM.class); ReorderVM reorderVM = new ViewModelProvider(ProfileActivity.this).get(ReorderVM.class);
//Get pinned instances //Get pinned instances
@ -836,10 +781,6 @@ public class ProfileActivity extends BaseActivity {
pinnedTimeline.type = Timeline.TimeLineEnum.REMOTE; pinnedTimeline.type = Timeline.TimeLineEnum.REMOTE;
pinnedTimeline.position = pinned.pinnedTimelines.size(); pinnedTimeline.position = pinned.pinnedTimelines.size();
pinned.pinnedTimelines.add(pinnedTimeline); pinned.pinnedTimelines.add(pinnedTimeline);
if (pinned.instance == null || pinned.user_id == null) {
pinned.instance = MainActivity.currentInstance;
pinned.user_id = MainActivity.currentUserID;
}
Pinned finalPinned = pinned; Pinned finalPinned = pinned;
boolean finalPresent = present; boolean finalPresent = present;
new Thread(() -> { new Thread(() -> {
@ -864,7 +805,7 @@ public class ProfileActivity extends BaseActivity {
}); });
return true; return true;
} else if (itemId == R.id.action_filter) { } else if (itemId == R.id.action_filter) {
AlertDialog.Builder filterTagDialog = new MaterialAlertDialogBuilder(ProfileActivity.this); AlertDialog.Builder filterTagDialog = new AlertDialog.Builder(ProfileActivity.this, Helper.dialogStyle());
Set<String> featuredTagsSet = sharedpreferences.getStringSet(getString(R.string.SET_FEATURED_TAGS), null); Set<String> featuredTagsSet = sharedpreferences.getStringSet(getString(R.string.SET_FEATURED_TAGS), null);
List<String> tags = new ArrayList<>(); List<String> tags = new ArrayList<>();
if (featuredTagsSet != null) { if (featuredTagsSet != null) {
@ -905,70 +846,13 @@ public class ProfileActivity extends BaseActivity {
.observe(ProfileActivity.this, relationShip -> this.relationship = relationShip); .observe(ProfileActivity.this, relationShip -> this.relationship = relationShip);
} }
return true; return true;
} else if (itemId == R.id.action_subscribed_language) {
if (relationship != null) {
List<String> subscribedLanguages = relationship.languages;
Set<String> storedLanguages = sharedpreferences.getStringSet(getString(R.string.SET_SELECTED_LANGUAGE), null);
List<Languages.Language> languages = Languages.get(ProfileActivity.this);
if (languages == null) {
return true;
}
String[] codesArr;
String[] languagesArr;
boolean[] presentArr;
if (storedLanguages != null && storedLanguages.size() > 0) {
int i = 0;
codesArr = new String[storedLanguages.size()];
languagesArr = new String[storedLanguages.size()];
presentArr = new boolean[storedLanguages.size()];
for (String code : storedLanguages) {
for (Languages.Language language : languages) {
if (language.code.equalsIgnoreCase(code)) {
languagesArr[i] = language.language;
}
}
codesArr[i] = code;
presentArr[i] = subscribedLanguages != null && subscribedLanguages.contains(code);
i++;
}
} else {
codesArr = new String[languages.size()];
presentArr = new boolean[languages.size()];
languagesArr = new String[languages.size()];
int i = 0;
for (Languages.Language language : languages) {
codesArr[i] = language.code;
languagesArr[i] = language.language;
if (subscribedLanguages != null && subscribedLanguages.contains(language.code)) {
presentArr[i] = true;
}
i++;
}
}
AlertDialog.Builder builder = new MaterialAlertDialogBuilder(ProfileActivity.this);
builder.setTitle(getString(R.string.filter_languages));
builder.setMultiChoiceItems(languagesArr, presentArr, (dialog, which, isChecked) -> {
List<String> languagesFilter = new ArrayList<>();
for (int i = 0; i < codesArr.length; i++) {
if (presentArr[i]) {
languagesFilter.add(codesArr[i]);
}
}
accountsVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, relationship.showing_reblogs, relationship.notifying, languagesFilter)
.observe(ProfileActivity.this, relationShip -> this.relationship = relationShip);
});
builder.setNegativeButton(R.string.close, (dialog, which) -> dialog.dismiss());
builder.create().show();
}
return true;
} else if (itemId == R.id.action_hide_boost) { } else if (itemId == R.id.action_hide_boost) {
if (relationship != null) if (relationship != null)
if (relationship.showing_reblogs) { if (relationship.showing_reblogs) {
accountsVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, false, relationship.notifying, relationship.languages) accountsVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, false, relationship.notifying)
.observe(ProfileActivity.this, relationShip -> this.relationship = relationShip); .observe(ProfileActivity.this, relationShip -> this.relationship = relationShip);
} else { } else {
accountsVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, true, relationship.notifying, relationship.languages) accountsVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, true, relationship.notifying)
.observe(ProfileActivity.this, relationShip -> this.relationship = relationShip); .observe(ProfileActivity.this, relationShip -> this.relationship = relationShip);
} }
return true; return true;
@ -990,7 +874,7 @@ public class ProfileActivity extends BaseActivity {
} }
accountsVM.getListContainingAccount(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id) accountsVM.getListContainingAccount(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id)
.observe(ProfileActivity.this, mastodonListUserIs -> { .observe(ProfileActivity.this, mastodonListUserIs -> {
AlertDialog.Builder builderSingle = new MaterialAlertDialogBuilder(ProfileActivity.this); AlertDialog.Builder builderSingle = new AlertDialog.Builder(ProfileActivity.this, Helper.dialogStyle());
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());
String[] listsId = new String[mastodonLists.size()]; String[] listsId = new String[mastodonLists.size()];
@ -1012,32 +896,20 @@ public class ProfileActivity extends BaseActivity {
i++; i++;
} }
builderSingle.setMultiChoiceItems(listsArray, presentArray, (dialog, which, isChecked) -> { builderSingle.setMultiChoiceItems(listsArray, presentArray, (dialog, which, isChecked) -> {
if (relationship == null || !relationship.following) { if (!relationship.following) {
accountsVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, true, false, null) accountsVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, true, false)
.observe(ProfileActivity.this, newRelationShip -> { .observe(ProfileActivity.this, newRelationShip -> {
if (newRelationShip != null) {
relationship = newRelationShip; relationship = newRelationShip;
updateAccount(); updateAccount();
if (isChecked) { if (isChecked) {
timelinesVM.addAccountsList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, listsId[which], userIds).observe(ProfileActivity.this, success -> { timelinesVM.addAccountsList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, listsId[which], userIds);
if (success == null || !success) {
Toasty.error(ProfileActivity.this, getString(R.string.toast_error_add_to_list), Toast.LENGTH_LONG).show();
}
});
} else { } else {
timelinesVM.deleteAccountsList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, listsId[which], userIds); timelinesVM.deleteAccountsList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, listsId[which], userIds);
} }
} else {
Toasty.error(ProfileActivity.this, getString(R.string.toast_error_add_to_list), Toast.LENGTH_LONG).show();
}
}); });
} else { } else {
if (isChecked) { if (isChecked) {
timelinesVM.addAccountsList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, listsId[which], userIds).observe(ProfileActivity.this, success -> { timelinesVM.addAccountsList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, listsId[which], userIds);
if (success == null || !success) {
Toasty.error(ProfileActivity.this, getString(R.string.toast_error_add_to_list), Toast.LENGTH_LONG).show();
}
});
} else { } else {
timelinesVM.deleteAccountsList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, listsId[which], userIds); timelinesVM.deleteAccountsList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, listsId[which], userIds);
} }
@ -1066,71 +938,17 @@ public class ProfileActivity extends BaseActivity {
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;
if (relationship != null) {
String target;
if (item.getItemId() == R.id.action_block_instance) {
target = account.acct.split("@")[1];
} else {
target = account.id;
}
if (relationship.muting) { if (relationship.muting) {
accountsVM.unmute(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, target) builderInner = new AlertDialog.Builder(ProfileActivity.this, Helper.dialogStyle());
.observe(ProfileActivity.this, relationShip -> { builderInner.setTitle(stringArrayConf[4]);
this.relationship = relationShip; doActionAccount = action.UNMUTE;
updateAccount();
});
return true;
}
builderInner = new MaterialAlertDialogBuilder(ProfileActivity.this);
builderInner.setTitle(stringArrayConf[0]);
builderInner.setNeutralButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
builderInner.setNegativeButton(R.string.keep_notifications, (dialog, which) -> accountsVM.mute(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, target, false, 0)
.observe(ProfileActivity.this, relationShip -> {
this.relationship = relationShip;
updateAccount();
}));
builderInner.setPositiveButton(R.string.action_mute, (dialog, which) -> {
accountsVM.mute(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, target, true, 0)
.observe(ProfileActivity.this, relationShip -> {
this.relationship = relationShip;
updateAccount();
});
dialog.dismiss();
});
builderInner.show();
}
} else if (itemId == R.id.action_mute_home) {
AlertDialog.Builder builderInner = new MaterialAlertDialogBuilder(ProfileActivity.this);
builderInner.setMessage(account.acct);
builderInner.setNeutralButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
if (homeMuted) {
builderInner.setTitle(R.string.unmute_home);
builderInner.setPositiveButton(R.string.action_unmute, (dialog, which) -> accountsVM.unmuteHome(currentAccount, account)
.observe(ProfileActivity.this, account -> {
homeMuted = false;
invalidateOptionsMenu();
Toasty.info(ProfileActivity.this, getString(R.string.toast_unmute), Toasty.LENGTH_LONG).show();
}));
} else { } else {
builderInner.setTitle(R.string.mute_home); builderInner = new AlertDialog.Builder(ProfileActivity.this, Helper.dialogStyle());
builderInner.setPositiveButton(R.string.action_mute, (dialog, which) -> accountsVM.muteHome(currentAccount, account) builderInner.setTitle(stringArrayConf[0]);
.observe(ProfileActivity.this, account -> { doActionAccount = action.MUTE;
homeMuted = true;
invalidateOptionsMenu();
Toasty.info(ProfileActivity.this, getString(R.string.toast_mute), Toasty.LENGTH_LONG).show();
}));
} }
builderInner.show();
} else if (itemId == R.id.action_timed_mute) {
MastodonHelper.scheduleBoost(ProfileActivity.this, MastodonHelper.ScheduleType.TIMED_MUTED, null, account, rs -> {
this.relationship = rs;
updateAccount();
});
return true;
} else if (itemId == R.id.action_report) { } else if (itemId == R.id.action_report) {
AlertDialog.Builder builderInner = new MaterialAlertDialogBuilder(ProfileActivity.this); builderInner = new AlertDialog.Builder(ProfileActivity.this, Helper.dialogStyle());
builderInner.setTitle(R.string.report_account); builderInner.setTitle(R.string.report_account);
//Text for report //Text for report
EditText input = new EditText(ProfileActivity.this); EditText input = new EditText(ProfileActivity.this);
@ -1150,8 +968,7 @@ public class ProfileActivity extends BaseActivity {
builderInner.show(); 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); builderInner = new AlertDialog.Builder(ProfileActivity.this, Helper.dialogStyle());
if (relationship != null) {
if (relationship.blocking) { if (relationship.blocking) {
builderInner.setTitle(stringArrayConf[5]); builderInner.setTitle(stringArrayConf[5]);
doActionAccount = action.UNBLOCK; doActionAccount = action.UNBLOCK;
@ -1159,8 +976,13 @@ public class ProfileActivity extends BaseActivity {
builderInner.setTitle(stringArrayConf[1]); builderInner.setTitle(stringArrayConf[1]);
doActionAccount = action.BLOCK; doActionAccount = action.BLOCK;
} }
} else if (itemId == R.id.action_block_instance) {
builderInner = new AlertDialog.Builder(ProfileActivity.this, Helper.dialogStyle());
doActionAccount = action.BLOCK_DOMAIN;
String domain = account.acct.split("@")[1];
builderInner.setMessage(getString(R.string.block_domain_confirm_message, domain));
} else { } else {
doActionAccount = action.NOTHING; return true;
} }
builderInner.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss()); builderInner.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
builderInner.setPositiveButton(R.string.yes, (dialog, which) -> { builderInner.setPositiveButton(R.string.yes, (dialog, which) -> {
@ -1171,6 +993,20 @@ public class ProfileActivity extends BaseActivity {
target = account.id; target = account.id;
} }
switch (doActionAccount) { switch (doActionAccount) {
case MUTE:
accountsVM.mute(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, target, true, 0)
.observe(ProfileActivity.this, relationShip -> {
this.relationship = relationShip;
updateAccount();
});
break;
case UNMUTE:
accountsVM.unmute(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, target)
.observe(ProfileActivity.this, relationShip -> {
this.relationship = relationShip;
updateAccount();
});
break;
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 -> {
@ -1185,29 +1021,13 @@ public class ProfileActivity extends BaseActivity {
updateAccount(); updateAccount();
}); });
break; break;
} case BLOCK_DOMAIN:
dialog.dismiss();
});
builderInner.show();
} else if (itemId == R.id.action_block_instance) {
AlertDialog.Builder builderInner = new MaterialAlertDialogBuilder(ProfileActivity.this);
String domain = account.acct.split("@")[1];
builderInner.setMessage(getString(R.string.block_domain_confirm_message, domain));
builderInner.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
builderInner.setPositiveButton(R.string.yes, (dialog, which) -> {
String target;
if (item.getItemId() == R.id.action_block_instance) {
target = account.acct.split("@")[1];
} else {
target = account.id;
}
accountsVM.addDomainBlocks(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, target); accountsVM.addDomainBlocks(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, target);
break;
}
dialog.dismiss(); dialog.dismiss();
}); });
builderInner.show(); builderInner.show();
} else {
return true;
}
return true; return true;
} }

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.activities; package app.fedilab.android.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -15,44 +15,39 @@ package app.fedilab.android.mastodon.activities;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import android.app.Dialog;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.Window; import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivityProxyBinding; import app.fedilab.android.databinding.ActivityProxyBinding;
public class ProxyActivity extends DialogFragment { public class ProxyActivity extends BaseActivity {
private ActivityProxyBinding binding; private ActivityProxyBinding binding;
private int position; private int position;
@NonNull
@Override @Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(ProxyActivity.this);
binding = ActivityProxyBinding.inflate(getLayoutInflater()); binding = ActivityProxyBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
MaterialAlertDialogBuilder materialAlertDialogBuilder = new MaterialAlertDialogBuilder(requireContext()); getWindow().setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
materialAlertDialogBuilder.setView(binding.getRoot()); if (getSupportActionBar() != null)
getSupportActionBar().hide();
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
//Enable proxy //Enable proxy
boolean enable_proxy = sharedpreferences.getBoolean(getString(R.string.SET_PROXY_ENABLED), false); boolean enable_proxy = sharedpreferences.getBoolean(getString(R.string.SET_PROXY_ENABLED), false);
binding.enableProxy.setChecked(enable_proxy); binding.enableProxy.setChecked(enable_proxy);
position = sharedpreferences.getInt(getString(R.string.SET_PROXY_TYPE), 0); position = 0;
String hostVal = sharedpreferences.getString(getString(R.string.SET_PROXY_HOST), "127.0.0.1"); String hostVal = sharedpreferences.getString(getString(R.string.SET_PROXY_HOST), "127.0.0.1");
int portVal = sharedpreferences.getInt(getString(R.string.SET_PROXY_PORT), 8118); int portVal = sharedpreferences.getInt(getString(R.string.SET_PROXY_PORT), 8118);
final String login = sharedpreferences.getString(getString(R.string.SET_PROXY_LOGIN), null); final String login = sharedpreferences.getString(getString(R.string.SET_PROXY_LOGIN), null);
@ -67,17 +62,22 @@ public class ProxyActivity extends DialogFragment {
if (pwd != null && binding.proxyPassword.length() > 0) { if (pwd != null && binding.proxyPassword.length() > 0) {
binding.proxyPassword.setText(pwd); binding.proxyPassword.setText(pwd);
} }
if (position == 1) binding.protocol.check(R.id.protocol_socks); ArrayAdapter<CharSequence> adapterTrans = ArrayAdapter.createFromResource(ProxyActivity.this,
binding.protocol.addOnButtonCheckedListener((group, checkedId, isChecked) -> { R.array.proxy_type_choice, android.R.layout.simple_spinner_dropdown_item);
if (isChecked) { binding.type.setAdapter(adapterTrans);
if (checkedId == R.id.protocol_http) binding.type.setSelection(sharedpreferences.getInt(getString(R.string.SET_PROXY_TYPE), 0), false);
position = 0; binding.type.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
else @Override
position = 1; public void onItemSelected(AdapterView<?> parent, View view, int p, long id) {
position = p;
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
} }
}); });
binding.setProxySave.setOnClickListener(view -> {
materialAlertDialogBuilder.setPositiveButton(R.string.save, (dialog1, which) -> {
String hostVal1 = binding.host.getText().toString().trim(); String hostVal1 = binding.host.getText().toString().trim();
String portVal1 = binding.port.getText().toString().trim(); String portVal1 = binding.port.getText().toString().trim();
String proxy_loginVal = binding.proxyLogin.getText().toString().trim(); String proxy_loginVal = binding.proxyLogin.getText().toString().trim();
@ -91,24 +91,18 @@ public class ProxyActivity extends DialogFragment {
editor.putString(getString(R.string.SET_PROXY_LOGIN), proxy_loginVal); editor.putString(getString(R.string.SET_PROXY_LOGIN), proxy_loginVal);
editor.putString(getString(R.string.SET_PROXY_PASSWORD), proxy_passwordVal); editor.putString(getString(R.string.SET_PROXY_PASSWORD), proxy_passwordVal);
editor.apply(); editor.apply();
finish();
}); });
materialAlertDialogBuilder.setNeutralButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
Dialog dialog = materialAlertDialogBuilder.create();
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
return dialog;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return super.onCreateView(inflater, container, savedInstanceState);
} }
@Override @Override
public void onDestroyView() { public boolean onOptionsItemSelected(MenuItem item) {
super.onDestroyView(); if (item.getItemId() == android.R.id.home) {
binding = null; finish();
return true;
} }
return super.onOptionsItemSelected(item);
}
} }

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.activities; package app.fedilab.android.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -15,14 +15,18 @@ package app.fedilab.android.mastodon.activities;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.helper.PinnedTimelineHelper.sortMenuItem;
import static app.fedilab.android.helper.PinnedTimelineHelper.sortPositionAsc;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.graphics.drawable.ColorDrawable;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.text.Editable; import android.text.Editable;
import android.text.InputFilter;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager; import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
@ -30,49 +34,47 @@ import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.snackbar.Snackbar;
import java.io.IOException; 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.R; import app.fedilab.android.R;
import app.fedilab.android.activities.MainActivity; import app.fedilab.android.client.entities.app.BottomMenu;
import app.fedilab.android.client.entities.app.InstanceSocial;
import app.fedilab.android.client.entities.app.Pinned;
import app.fedilab.android.client.entities.app.PinnedTimeline;
import app.fedilab.android.client.entities.app.RemoteInstance;
import app.fedilab.android.client.entities.app.Timeline;
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.exception.DBException;
import app.fedilab.android.mastodon.client.entities.app.InstanceSocial; import app.fedilab.android.helper.Helper;
import app.fedilab.android.mastodon.client.entities.app.Pinned; import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.mastodon.client.entities.app.PinnedTimeline; import app.fedilab.android.helper.itemtouchhelper.OnStartDragListener;
import app.fedilab.android.mastodon.client.entities.app.RemoteInstance; import app.fedilab.android.helper.itemtouchhelper.OnUndoListener;
import app.fedilab.android.mastodon.client.entities.app.Timeline; import app.fedilab.android.helper.itemtouchhelper.SimpleItemTouchHelperCallback;
import app.fedilab.android.mastodon.exception.DBException; import app.fedilab.android.ui.drawer.ReorderBottomMenuAdapter;
import app.fedilab.android.mastodon.helper.Helper; import app.fedilab.android.ui.drawer.ReorderTabAdapter;
import app.fedilab.android.mastodon.helper.PinnedTimelineHelper; import app.fedilab.android.viewmodel.mastodon.InstanceSocialVM;
import app.fedilab.android.mastodon.helper.itemtouchhelper.OnStartDragListener; import app.fedilab.android.viewmodel.mastodon.ReorderVM;
import app.fedilab.android.mastodon.helper.itemtouchhelper.SimpleItemTouchHelperCallback;
import app.fedilab.android.mastodon.ui.drawer.ReorderBottomMenuAdapter;
import app.fedilab.android.mastodon.ui.drawer.ReorderTabAdapter;
import app.fedilab.android.mastodon.viewmodel.mastodon.InstanceSocialVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.ReorderVM;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
import okhttp3.Call; import okhttp3.Call;
import okhttp3.Callback; import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response; import okhttp3.Response;
public class ReorderTimelinesActivity extends BaseBarActivity implements OnStartDragListener { public class ReorderTimelinesActivity extends BaseActivity implements OnStartDragListener, OnUndoListener {
private ItemTouchHelper touchHelper; private ItemTouchHelper touchHelper;
@ -85,7 +87,6 @@ public class ReorderTimelinesActivity extends BaseBarActivity implements OnStart
private ActivityReorderTabsBinding binding; private ActivityReorderTabsBinding binding;
private boolean changes; private boolean changes;
private boolean bottomChanges; private boolean bottomChanges;
private boolean update;
public void setChanges(boolean changes) { public void setChanges(boolean changes) {
this.changes = changes; this.changes = changes;
@ -98,34 +99,26 @@ public class ReorderTimelinesActivity extends BaseBarActivity implements OnStart
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyThemeBar(this);
binding = ActivityReorderTabsBinding.inflate(getLayoutInflater()); binding = ActivityReorderTabsBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
searchInstanceRunning = false; searchInstanceRunning = false;
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(ReorderTimelinesActivity.this);
boolean singleBar = sharedpreferences.getBoolean(getString(R.string.SET_USE_SINGLE_TOPBAR), false);
if (singleBar) {
binding.titleBottom.setVisibility(View.GONE);
binding.lvReorderBottom.setVisibility(View.GONE);
binding.titleTop.setVisibility(View.GONE);
}
changes = false; changes = false;
bottomChanges = false; bottomChanges = false;
ReorderVM reorderVM = new ViewModelProvider(ReorderTimelinesActivity.this).get(ReorderVM.class); ReorderVM reorderVM = new ViewModelProvider(ReorderTimelinesActivity.this).get(ReorderVM.class);
reorderVM.getPinned().observe(ReorderTimelinesActivity.this, _pinned -> { reorderVM.getPinned().observe(ReorderTimelinesActivity.this, _pinned -> {
update = true;
this.pinned = _pinned; this.pinned = _pinned;
if (this.pinned == null) { if (this.pinned == null) {
this.pinned = new Pinned(); this.pinned = new Pinned();
this.pinned.pinnedTimelines = new ArrayList<>(); this.pinned.pinnedTimelines = new ArrayList<>();
update = false;
} }
PinnedTimelineHelper.sortPositionAsc(this.pinned.pinnedTimelines); sortPositionAsc(this.pinned.pinnedTimelines);
reorderTabAdapter = new ReorderTabAdapter(this.pinned, ReorderTimelinesActivity.this); reorderTabAdapter = new ReorderTabAdapter(this.pinned, ReorderTimelinesActivity.this, ReorderTimelinesActivity.this);
ItemTouchHelper.Callback callback = ItemTouchHelper.Callback callback =
new SimpleItemTouchHelperCallback(reorderTabAdapter); new SimpleItemTouchHelperCallback(reorderTabAdapter);
touchHelper = new ItemTouchHelper(callback); touchHelper = new ItemTouchHelper(callback);
@ -140,7 +133,7 @@ public class ReorderTimelinesActivity extends BaseBarActivity implements OnStart
this.bottomMenu = new BottomMenu(getApplicationContext()).defaultBottomMenu(); this.bottomMenu = new BottomMenu(getApplicationContext()).defaultBottomMenu();
this.bottomMenu.bottom_menu = new ArrayList<>(); this.bottomMenu.bottom_menu = new ArrayList<>();
} }
PinnedTimelineHelper.sortMenuItem(this.bottomMenu.bottom_menu); sortMenuItem(this.bottomMenu.bottom_menu);
reorderBottomMenuAdapter = new ReorderBottomMenuAdapter(this.bottomMenu, ReorderTimelinesActivity.this); reorderBottomMenuAdapter = new ReorderBottomMenuAdapter(this.bottomMenu, ReorderTimelinesActivity.this);
ItemTouchHelper.Callback callback = ItemTouchHelper.Callback callback =
new SimpleItemTouchHelperCallback(reorderBottomMenuAdapter); new SimpleItemTouchHelperCallback(reorderBottomMenuAdapter);
@ -160,6 +153,7 @@ public class ReorderTimelinesActivity extends BaseBarActivity implements OnStart
} else if (item.getItemId() == R.id.action_add_timeline) { } else if (item.getItemId() == R.id.action_add_timeline) {
addInstance(); addInstance();
} }
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
@ -171,44 +165,31 @@ public class ReorderTimelinesActivity extends BaseBarActivity implements OnStart
} }
private void addInstance() { private void addInstance() {
AlertDialog.Builder dialogBuilder = new MaterialAlertDialogBuilder(ReorderTimelinesActivity.this); AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(ReorderTimelinesActivity.this, Helper.dialogStyle());
PopupSearchInstanceBinding popupSearchInstanceBinding = PopupSearchInstanceBinding.inflate(getLayoutInflater()); PopupSearchInstanceBinding popupSearchInstanceBinding = PopupSearchInstanceBinding.inflate(getLayoutInflater());
dialogBuilder.setView(popupSearchInstanceBinding.getRoot()); dialogBuilder.setView(popupSearchInstanceBinding.getRoot());
TextWatcher textWatcher = autoComplete(popupSearchInstanceBinding);
popupSearchInstanceBinding.searchInstance.addTextChangedListener(textWatcher);
popupSearchInstanceBinding.setAttachmentGroup.setOnCheckedChangeListener((group, checkedId) -> { popupSearchInstanceBinding.setAttachmentGroup.setOnCheckedChangeListener((group, checkedId) -> {
if (checkedId == R.id.twitter_accounts) { if (checkedId == R.id.twitter_accounts) {
popupSearchInstanceBinding.searchInstance.setHint(R.string.list_of_twitter_accounts); popupSearchInstanceBinding.searchInstance.setHint(R.string.list_of_twitter_accounts);
popupSearchInstanceBinding.searchInstance.removeTextChangedListener(textWatcher);
} else { } else {
popupSearchInstanceBinding.searchInstance.setHint(R.string.instance); popupSearchInstanceBinding.searchInstance.setHint(R.string.instance);
popupSearchInstanceBinding.searchInstance.removeTextChangedListener(textWatcher);
popupSearchInstanceBinding.searchInstance.addTextChangedListener(textWatcher);
} }
}); });
popupSearchInstanceBinding.searchInstance.setFilters(new InputFilter[]{new InputFilter.LengthFilter(60)});
dialogBuilder.setPositiveButton(R.string.validate, (dialog, id) -> { dialogBuilder.setPositiveButton(R.string.validate, (dialog, id) -> {
String instanceName = popupSearchInstanceBinding.searchInstance.getText().toString().trim().replace("@", ""); String instanceName = popupSearchInstanceBinding.searchInstance.getText().toString().trim().replace("@", "");
new Thread(() -> { new Thread(() -> {
String url = null; String url = null;
boolean getCall = true; if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.mastodon_instance)
RequestBody formBody = new FormBody.Builder()
.build();
if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.mastodon_instance) {
url = "https://" + instanceName + "/api/v1/timelines/public?local=true"; url = "https://" + instanceName + "/api/v1/timelines/public?local=true";
} 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/v1/timelines/public"; 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;
} 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) {
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 + "/" + instanceName.replaceAll("[ ]+", ",").replaceAll("\\s", "") + "/rss";
} }
OkHttpClient client = new OkHttpClient.Builder() OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS) .connectTimeout(10, TimeUnit.SECONDS)
@ -217,16 +198,9 @@ public class ReorderTimelinesActivity extends BaseBarActivity implements OnStart
.readTimeout(10, TimeUnit.SECONDS).build(); .readTimeout(10, TimeUnit.SECONDS).build();
Request request; Request request;
if (url != null) { if (url != null) {
if (getCall) {
request = new Request.Builder() request = new Request.Builder()
.url(url) .url(url)
.build(); .build();
} else {
request = new Request.Builder()
.url(url)
.post(formBody)
.build();
}
client.newCall(request).enqueue(new Callback() { client.newCall(request).enqueue(new Callback() {
@Override @Override
public void onFailure(@NonNull Call call, @NonNull IOException e) { public void onFailure(@NonNull Call call, @NonNull IOException e) {
@ -262,29 +236,13 @@ public class ReorderTimelinesActivity extends BaseBarActivity implements OnStart
pinnedTimeline.type = Timeline.TimeLineEnum.REMOTE; pinnedTimeline.type = Timeline.TimeLineEnum.REMOTE;
pinnedTimeline.position = pinned.pinnedTimelines.size(); pinnedTimeline.position = pinned.pinnedTimelines.size();
pinned.pinnedTimelines.add(pinnedTimeline); pinned.pinnedTimelines.add(pinnedTimeline);
if (pinned.user_id == null || pinned.instance == null) {
pinned.user_id = MainActivity.currentUserID;
pinned.instance = MainActivity.currentInstance;
}
if (update) {
try { try {
new Pinned(ReorderTimelinesActivity.this).updatePinned(pinned); new Pinned(ReorderTimelinesActivity.this).updatePinned(pinned);
} catch (DBException e) { changes = true;
e.printStackTrace();
}
} else {
try {
new Pinned(ReorderTimelinesActivity.this).insertPinned(pinned);
} catch (DBException e) {
e.printStackTrace();
}
}
reorderTabAdapter.notifyItemInserted(pinned.pinnedTimelines.size()); reorderTabAdapter.notifyItemInserted(pinned.pinnedTimelines.size());
Bundle b = new Bundle(); } catch (DBException e) {
b.putBoolean(Helper.RECEIVE_REDRAW_TOPBAR, true); e.printStackTrace();
Intent intentBD = new Intent(Helper.BROADCAST_DATA); }
intentBD.putExtras(b);
LocalBroadcastManager.getInstance(ReorderTimelinesActivity.this).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());
@ -309,11 +267,7 @@ public class ReorderTimelinesActivity extends BaseBarActivity implements OnStart
popupSearchInstanceBinding.searchInstance.setOnItemClickListener((parent, view1, position, id) -> oldSearch = parent.getItemAtPosition(position).toString().trim()); popupSearchInstanceBinding.searchInstance.setOnItemClickListener((parent, view1, position, id) -> oldSearch = parent.getItemAtPosition(position).toString().trim());
popupSearchInstanceBinding.searchInstance.addTextChangedListener(new TextWatcher() {
}
private TextWatcher autoComplete(PopupSearchInstanceBinding popupSearchInstanceBinding) {
return new TextWatcher() {
@Override @Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { public void beforeTextChanged(CharSequence s, int start, int count, int after) {
} }
@ -360,7 +314,8 @@ public class ReorderTimelinesActivity extends BaseBarActivity implements OnStart
} }
} }
} }
}; });
} }
@Override @Override
@ -389,6 +344,51 @@ public class ReorderTimelinesActivity extends BaseBarActivity implements OnStart
} }
@Override
public void onUndo(PinnedTimeline pinnedTimeline, int position) {
String text = "";
switch (pinnedTimeline.type) {
case TAG:
text = getString(R.string.reorder_tag_removed);
break;
case REMOTE:
text = getString(R.string.reorder_instance_removed);
break;
case LIST:
text = getString(R.string.reorder_list_deleted);
break;
}
Runnable runnable = () -> {
//change position of pinned that are after the removed item
for (int i = pinnedTimeline.position + 1; i < pinned.pinnedTimelines.size(); i++) {
pinned.pinnedTimelines.get(i).position -= 1;
}
pinned.pinnedTimelines.remove(pinnedTimeline);
reorderTabAdapter.notifyItemRemoved(position);
try {
new Pinned(ReorderTimelinesActivity.this).updatePinned(pinned);
changes = true;
} catch (DBException e) {
e.printStackTrace();
}
};
Handler handler = new Handler();
handler.postDelayed(runnable, 4000);
Snackbar.make(binding.getRoot(), text, 4000)
.setAction(getString(R.string.undo), view -> {
pinned.pinnedTimelines.add(position, pinnedTimeline);
reorderTabAdapter.notifyItemInserted(position);
handler.removeCallbacks(runnable);
})
.setTextColor(ThemeHelper.getAttColor(ReorderTimelinesActivity.this, R.attr.mTextColor))
.setActionTextColor(ContextCompat.getColor(ReorderTimelinesActivity.this, R.color.cyanea_accent_reference))
.setBackgroundTint(ContextCompat.getColor(ReorderTimelinesActivity.this, R.color.cyanea_primary_dark_reference))
.show();
}
@Override @Override
public void onStop() { public void onStop() {
super.onStop(); super.onStop();

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.activities; package app.fedilab.android.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -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 android.graphics.drawable.ColorDrawable;
import android.os.Bundle; import android.os.Bundle;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
@ -21,6 +22,7 @@ import android.widget.RadioButton;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.widget.LinearLayoutCompat; import androidx.appcompat.widget.LinearLayoutCompat;
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.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
@ -31,19 +33,20 @@ import java.util.List;
import app.fedilab.android.BaseMainActivity; import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Account;
import app.fedilab.android.client.entities.api.RelationShip;
import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.client.entities.app.Timeline;
import app.fedilab.android.databinding.ActivityReportBinding; import app.fedilab.android.databinding.ActivityReportBinding;
import app.fedilab.android.mastodon.client.entities.api.Account; import app.fedilab.android.helper.Helper;
import app.fedilab.android.mastodon.client.entities.api.RelationShip; import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.mastodon.client.entities.api.Status; import app.fedilab.android.ui.drawer.RulesAdapter;
import app.fedilab.android.mastodon.client.entities.app.Timeline; import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTimeline;
import app.fedilab.android.mastodon.helper.Helper; import app.fedilab.android.viewmodel.mastodon.AccountsVM;
import app.fedilab.android.mastodon.ui.drawer.RulesAdapter;
import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonTimeline;
import app.fedilab.android.mastodon.viewmodel.mastodon.AccountsVM;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
public class ReportActivity extends BaseBarActivity { public class ReportActivity extends BaseActivity {
private ActivityReportBinding binding; private ActivityReportBinding binding;
private Status status; private Status status;
@ -61,13 +64,14 @@ public class ReportActivity extends BaseBarActivity {
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyThemeBar(this);
binding = ActivityReportBinding.inflate(getLayoutInflater()); binding = ActivityReportBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
Bundle b = getIntent().getExtras(); Bundle b = getIntent().getExtras();

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.activities; package app.fedilab.android.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -16,21 +16,21 @@ package app.fedilab.android.mastodon.activities;
import static app.fedilab.android.BaseMainActivity.currentAccount; import static app.fedilab.android.BaseMainActivity.currentAccount;
import android.content.SharedPreferences; import android.graphics.drawable.ColorDrawable;
import android.os.Bundle; import android.os.Bundle;
import android.util.TypedValue;
import android.view.MenuItem; import android.view.MenuItem;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
import androidx.preference.PreferenceManager; import androidx.core.content.ContextCompat;
import com.google.android.material.tabs.TabLayout; 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.MastodonHelper; import app.fedilab.android.helper.MastodonHelper;
import app.fedilab.android.mastodon.ui.pageadapter.FedilabScheduledPageAdapter; import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.ui.pageadapter.FedilabScheduledPageAdapter;
public class ScheduledActivity extends BaseActivity { public class ScheduledActivity extends BaseActivity {
@ -39,7 +39,7 @@ public class ScheduledActivity extends BaseActivity {
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyTheme(this);
binding = ActivityScheduledBinding.inflate(getLayoutInflater()); binding = ActivityScheduledBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
@ -47,24 +47,23 @@ public class ScheduledActivity extends BaseActivity {
//Remove title //Remove title
if (actionBar != null) { if (actionBar != null) {
actionBar.setDisplayShowTitleEnabled(false); actionBar.setDisplayShowTitleEnabled(false);
actionBar.setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true);
} }
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this);
float scale = sharedpreferences.getFloat(getString(R.string.SET_FONT_SCALE), 1.1f);
binding.title.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18 * 1.1f / scale);
MastodonHelper.loadPPMastodon(binding.profilePicture, currentAccount.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)));
binding.scheduleTablayout.addTab(binding.scheduleTablayout.newTab().setText(getString(R.string.reblog))); binding.scheduleTablayout.addTab(binding.scheduleTablayout.newTab().setText(getString(R.string.reblog)));
binding.scheduleViewpager.setAdapter(new FedilabScheduledPageAdapter(getSupportFragmentManager())); binding.scheduleViewpager.setAdapter(new FedilabScheduledPageAdapter(ScheduledActivity.this));
binding.scheduleViewpager.setOffscreenPageLimit(3); binding.scheduleViewpager.setOffscreenPageLimit(3);
binding.scheduleViewpager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(binding.scheduleTablayout)); binding.scheduleTablayout.setTabTextColors(ThemeHelper.getAttColor(ScheduledActivity.this, R.attr.mTextColor), ContextCompat.getColor(ScheduledActivity.this, R.color.cyanea_accent_dark_reference));
binding.scheduleTablayout.setTabIconTint(ThemeHelper.getColorStateList(ScheduledActivity.this));
binding.scheduleTablayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { binding.scheduleTablayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override @Override
@ -74,10 +73,12 @@ public class ScheduledActivity extends BaseActivity {
@Override @Override
public void onTabUnselected(TabLayout.Tab tab) { public void onTabUnselected(TabLayout.Tab tab) {
} }
@Override @Override
public void onTabReselected(TabLayout.Tab tab) { public void onTabReselected(TabLayout.Tab tab) {
} }
}); });
} }

View file

@ -0,0 +1,241 @@
package app.fedilab.android.activities;
/* 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.app.SearchManager;
import android.content.Context;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.inputmethod.InputMethodManager;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.SearchView;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import androidx.viewpager2.widget.ViewPager2;
import com.google.android.material.tabs.TabLayout;
import org.jetbrains.annotations.NotNull;
import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivitySearchResultTabsBinding;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonAccount;
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTag;
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTimeline;
import es.dmoral.toasty.Toasty;
public class SearchResultTabActivity extends BaseActivity {
private String search;
private ActivitySearchResultTabsBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ThemeHelper.applyThemeBar(this);
binding = ActivitySearchResultTabsBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
Bundle b = getIntent().getExtras();
if (b != null) {
search = b.getString(Helper.ARG_SEARCH_KEYWORD, null);
}
if (search == null) {
Toasty.error(SearchResultTabActivity.this, getString(R.string.toast_error_search), Toast.LENGTH_LONG).show();
finish();
return;
}
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
}
setTitle(search);
binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.tags)));
binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.accounts)));
binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.toots)));
binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.action_cache)));
binding.searchTabLayout.setTabTextColors(ThemeHelper.getAttColor(SearchResultTabActivity.this, R.attr.mTextColor), ContextCompat.getColor(SearchResultTabActivity.this, R.color.cyanea_accent_dark_reference));
binding.searchTabLayout.setTabIconTint(ThemeHelper.getColorStateList(SearchResultTabActivity.this));
binding.searchTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
binding.searchViewpager.setCurrentItem(tab.getPosition());
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
Fragment fragment;
if (binding.searchViewpager.getAdapter() != null) {
fragment = (Fragment) getSupportFragmentManager().findFragmentByTag("f" + binding.searchViewpager.getCurrentItem());
if (fragment instanceof FragmentMastodonAccount) {
FragmentMastodonAccount fragmentMastodonAccount = ((FragmentMastodonAccount) fragment);
fragmentMastodonAccount.scrollToTop();
} else if (fragment instanceof FragmentMastodonTimeline) {
FragmentMastodonTimeline fragmentMastodonTimeline = ((FragmentMastodonTimeline) fragment);
fragmentMastodonTimeline.scrollToTop();
} else if (fragment instanceof FragmentMastodonTag) {
FragmentMastodonTag fragmentMastodonTag = ((FragmentMastodonTag) fragment);
fragmentMastodonTag.scrollToTop();
}
}
}
});
}
@Override
public boolean onCreateOptionsMenu(@NonNull Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_search, menu);
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
searchView.setIconifiedByDefault(false);
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
assert imm != null;
imm.hideSoftInputFromWindow(binding.searchTabLayout.getWindowToken(), 0);
query = query.replaceAll("^#+", "");
search = query.trim();
ScreenSlidePagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(SearchResultTabActivity.this);
binding.searchViewpager.setAdapter(mPagerAdapter);
searchView.clearFocus();
setTitle(search);
searchView.setIconified(true);
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
return false;
}
});
searchView.setOnCloseListener(() -> {
setTitle(search);
return false;
});
searchView.setOnSearchClickListener(v -> {
searchView.setQuery(search, false);
searchView.setIconified(false);
});
ScreenSlidePagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(SearchResultTabActivity.this);
binding.searchViewpager.setAdapter(mPagerAdapter);
binding.searchViewpager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels);
binding.searchTabLayout.selectTab(binding.searchTabLayout.getTabAt(position));
}
@Override
public void onPageSelected(int position) {
super.onPageSelected(position);
TabLayout.Tab tab = binding.searchTabLayout.getTabAt(position);
if (tab != null)
tab.select();
}
@Override
public void onPageScrollStateChanged(int state) {
super.onPageScrollStateChanged(state);
}
});
return true;
}
@Override
public boolean onOptionsItemSelected(@NotNull MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
return true;
} else if (item.getItemId() == R.id.action_search) {
return true;
}
return super.onOptionsItemSelected(item);
}
/**
* Pager adapter for the 4 fragments
*/
private class ScreenSlidePagerAdapter extends FragmentStateAdapter {
ScreenSlidePagerAdapter(FragmentActivity fa) {
super(fa);
}
@NonNull
@Override
public Fragment createFragment(int position) {
Bundle bundle = new Bundle();
switch (position) {
case 0:
FragmentMastodonTag fragmentMastodonTag = new FragmentMastodonTag();
bundle.putString(Helper.ARG_SEARCH_KEYWORD, search);
fragmentMastodonTag.setArguments(bundle);
return fragmentMastodonTag;
case 1:
FragmentMastodonAccount fragmentMastodonAccount = new FragmentMastodonAccount();
bundle.putString(Helper.ARG_SEARCH_KEYWORD, search);
fragmentMastodonAccount.setArguments(bundle);
return fragmentMastodonAccount;
case 2:
FragmentMastodonTimeline fragmentMastodonTimeline = new FragmentMastodonTimeline();
bundle.putString(Helper.ARG_SEARCH_KEYWORD, search);
fragmentMastodonTimeline.setArguments(bundle);
return fragmentMastodonTimeline;
default:
fragmentMastodonTimeline = new FragmentMastodonTimeline();
bundle.putString(Helper.ARG_SEARCH_KEYWORD_CACHE, search);
fragmentMastodonTimeline.setArguments(bundle);
return fragmentMastodonTimeline;
}
}
@Override
public int getItemCount() {
return 4;
}
}
}

View file

@ -0,0 +1,208 @@
package app.fedilab.android.activities;
/* 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.BaseMainActivity.currentAccount;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.View;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import com.google.gson.annotations.SerializedName;
import java.util.Locale;
import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivitySettingsBinding;
import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.ui.fragment.settings.FragmentComposeSettings;
import app.fedilab.android.ui.fragment.settings.FragmentInterfaceSettings;
import app.fedilab.android.ui.fragment.settings.FragmentLanguageSettings;
import app.fedilab.android.ui.fragment.settings.FragmentNotificationsSettings;
import app.fedilab.android.ui.fragment.settings.FragmentPrivacySettings;
import app.fedilab.android.ui.fragment.settings.FragmentThemingSettings;
import app.fedilab.android.ui.fragment.settings.FragmentTimelinesSettings;
public class SettingsActivity extends BaseActivity {
private ActivitySettingsBinding binding;
private boolean canGoBack;
private Fragment currentFragment;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ThemeHelper.applyThemeBar(this);
binding = ActivitySettingsBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
}
canGoBack = false;
binding.setTimelines.setOnClickListener(v -> displaySettings(SettingsEnum.TIMELINES));
binding.setNotifications.setOnClickListener(v -> displaySettings(SettingsEnum.NOTIFICATIONS));
binding.setInterface.setOnClickListener(v -> displaySettings(SettingsEnum.INTERFACE));
binding.setCompose.setOnClickListener(v -> displaySettings(SettingsEnum.COMPOSE));
binding.setPrivacy.setOnClickListener(v -> displaySettings(SettingsEnum.PRIVACY));
binding.setTheming.setOnClickListener(v -> displaySettings(SettingsEnum.THEMING));
binding.setAdministration.setOnClickListener(v -> displaySettings(SettingsEnum.ADMINISTRATION));
binding.setLanguage.setOnClickListener(v -> displaySettings(SettingsEnum.LANGUAGE));
if (currentAccount.admin) {
binding.setAdministration.setVisibility(View.VISIBLE);
} else {
binding.setAdministration.setVisibility(View.GONE);
}
}
public void displaySettings(SettingsEnum settingsEnum) {
ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction =
fragmentManager.beginTransaction();
String category = "";
switch (settingsEnum) {
case TIMELINES:
FragmentTimelinesSettings fragmentTimelinesSettings = new FragmentTimelinesSettings();
fragmentTransaction.replace(R.id.fragment_container, fragmentTimelinesSettings);
currentFragment = fragmentTimelinesSettings;
category = getString(R.string.settings_category_label_timelines);
break;
case NOTIFICATIONS:
FragmentNotificationsSettings fragmentNotificationsSettings = new FragmentNotificationsSettings();
fragmentTransaction.replace(R.id.fragment_container, fragmentNotificationsSettings);
currentFragment = fragmentNotificationsSettings;
category = getString(R.string.notifications);
break;
case INTERFACE:
FragmentInterfaceSettings fragmentInterfaceSettings = new FragmentInterfaceSettings();
fragmentTransaction.replace(R.id.fragment_container, fragmentInterfaceSettings);
currentFragment = fragmentInterfaceSettings;
category = getString(R.string.settings_category_label_interface);
break;
case COMPOSE:
FragmentComposeSettings fragmentComposeSettings = new FragmentComposeSettings();
fragmentTransaction.replace(R.id.fragment_container, fragmentComposeSettings);
currentFragment = fragmentComposeSettings;
category = getString(R.string.compose);
break;
case PRIVACY:
FragmentPrivacySettings fragmentPrivacySettings = new FragmentPrivacySettings();
fragmentTransaction.replace(R.id.fragment_container, fragmentPrivacySettings);
currentFragment = fragmentPrivacySettings;
category = getString(R.string.action_privacy);
break;
case THEMING:
FragmentThemingSettings fragmentThemingSettings = new FragmentThemingSettings();
fragmentTransaction.replace(R.id.fragment_container, fragmentThemingSettings);
currentFragment = fragmentThemingSettings;
category = getString(R.string.theming);
break;
case LANGUAGE:
FragmentLanguageSettings fragmentLanguageSettings = new FragmentLanguageSettings();
fragmentTransaction.replace(R.id.fragment_container, fragmentLanguageSettings);
currentFragment = fragmentLanguageSettings;
category = getString(R.string.languages);
break;
}
String title = String.format(Locale.getDefault(), "%s - %s", getString(R.string.settings), category);
setTitle(title);
canGoBack = true;
fragmentTransaction.commit();
});
}
@Override
public void onBackPressed() {
if (canGoBack) {
canGoBack = false;
ThemeHelper.slideViewsToRight(binding.fragmentContainer, binding.buttonContainer, () -> {
if (currentFragment != null) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction =
fragmentManager.beginTransaction();
fragmentTransaction.remove(currentFragment).commit();
}
});
setTitle(R.string.settings);
} else {
super.onBackPressed();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (currentFragment != null) {
currentFragment.onDestroy();
}
binding = null;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
public enum SettingsEnum {
@SerializedName("TIMELINES")
TIMELINES("TIMELINES"),
@SerializedName("NOTIFICATIONS")
NOTIFICATIONS("NOTIFICATIONS"),
@SerializedName("INTERFACE")
INTERFACE("INTERFACE"),
@SerializedName("COMPOSE")
COMPOSE("COMPOSE"),
@SerializedName("PRIVACY")
PRIVACY("PRIVACY"),
@SerializedName("THEMING")
THEMING("THEMING"),
@SerializedName("ADMINISTRATION")
ADMINISTRATION("ADMINISTRATION"),
@SerializedName("LANGUAGE")
LANGUAGE("LANGUAGE");
private final String value;
SettingsEnum(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
}

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.activities; package app.fedilab.android.activities;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -15,11 +15,13 @@ package app.fedilab.android.mastodon.activities;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import android.graphics.drawable.ColorDrawable;
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 androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
@ -29,15 +31,14 @@ import java.util.List;
import app.fedilab.android.BaseMainActivity; import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Account;
import app.fedilab.android.client.entities.api.Accounts;
import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.databinding.ActivityStatusInfoBinding; import app.fedilab.android.databinding.ActivityStatusInfoBinding;
import app.fedilab.android.mastodon.client.entities.api.Account; import app.fedilab.android.helper.Helper;
import app.fedilab.android.mastodon.client.entities.api.Accounts; import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.mastodon.client.entities.api.RelationShip; import app.fedilab.android.ui.drawer.AccountAdapter;
import app.fedilab.android.mastodon.client.entities.api.Status; import app.fedilab.android.viewmodel.mastodon.StatusesVM;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.ui.drawer.AccountAdapter;
import app.fedilab.android.mastodon.viewmodel.mastodon.AccountsVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.StatusesVM;
public class StatusInfoActivity extends BaseActivity { public class StatusInfoActivity extends BaseActivity {
@ -53,13 +54,14 @@ public class StatusInfoActivity extends BaseActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyTheme(this);
binding = ActivityStatusInfoBinding.inflate(getLayoutInflater()); binding = ActivityStatusInfoBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
accountList = new ArrayList<>(); accountList = new ArrayList<>();
Bundle b = getIntent().getExtras(); Bundle b = getIntent().getExtras();
@ -113,8 +115,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) {
int position = this.accountList.size();
fetchRelationShip(accounts.accounts, position);
int startId = 0; int startId = 0;
//There are some statuses present in the timeline //There are some statuses present in the timeline
if (accountList.size() > 0) { if (accountList.size() > 0) {
@ -127,27 +127,6 @@ public class StatusInfoActivity extends BaseActivity {
} }
} }
private void fetchRelationShip(List<Account> accounts, int position) {
List<String> ids = new ArrayList<>();
for (Account account : accounts) {
ids.add(account.id);
}
AccountsVM accountsVM = new ViewModelProvider(StatusInfoActivity.this).get(AccountsVM.class);
accountsVM.getRelationships(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, ids)
.observe(StatusInfoActivity.this, relationShips -> {
if (relationShips != null) {
for (RelationShip relationShip : relationShips) {
for (Account account : accounts) {
if (account.id.compareToIgnoreCase(relationShip.id) == 0) {
account.relationShip = relationShip;
}
}
}
accountAdapter.notifyItemRangeChanged(position, accounts.size());
}
});
}
@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) {

View file

@ -1,5 +1,5 @@
package app.fedilab.android.peertube.activities; package app.fedilab.android.activities;
/* Copyright 2023 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
* *
@ -18,43 +18,69 @@ import android.Manifest;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.drawable.ColorDrawable;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
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 android.view.WindowManager; import android.view.WindowManager;
import android.webkit.WebView; import android.widget.ArrayAdapter;
import android.widget.FrameLayout;
import android.widget.Toast; import android.widget.Toast;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.mastodon.activities.BaseBarActivity; import app.fedilab.android.databinding.ActivityWebviewBinding;
import app.fedilab.android.peertube.helper.Helper; import app.fedilab.android.helper.Helper;
import app.fedilab.android.peertube.webview.MastalabWebChromeClient; import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.peertube.webview.MastalabWebViewClient; import app.fedilab.android.sqlite.Sqlite;
import app.fedilab.android.webview.CustomWebview;
import app.fedilab.android.webview.FedilabWebChromeClient;
import app.fedilab.android.webview.FedilabWebViewClient;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
public class WebviewActivity extends BaseBarActivity { public class WebviewActivity extends BaseActivity {
public static List<String> trackingDomains;
private String url; private String url;
private boolean peertubeLink; private boolean peertubeLink;
private WebView webView; private CustomWebview webView;
private FedilabWebViewClient FedilabWebViewClient;
private ActivityWebviewBinding binding;
@SuppressLint("SetJavaScriptEnabled") @SuppressLint("SetJavaScriptEnabled")
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_webview_peertube); ThemeHelper.applyTheme(this);
binding = ActivityWebviewBinding.inflate(getLayoutInflater());
View view = binding.getRoot();
setContentView(view);
setSupportActionBar(binding.toolbar);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayShowTitleEnabled(false);
}
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
}
Bundle b = getIntent().getExtras(); Bundle b = getIntent().getExtras();
if (b != null) { if (b != null) {
url = b.getString("url", null); url = b.getString("url", null);
@ -67,16 +93,15 @@ public class WebviewActivity extends BaseBarActivity {
webView = Helper.initializeWebview(WebviewActivity.this, R.id.webview, null); webView = Helper.initializeWebview(WebviewActivity.this, R.id.webview, null);
setTitle(""); setTitle("");
FrameLayout webview_container = findViewById(R.id.webview_container);
final ViewGroup videoLayout = findViewById(R.id.videoLayout); // Your own view, read class comments
webView.getSettings().setJavaScriptEnabled(true); webView.getSettings().setJavaScriptEnabled(true);
MastalabWebChromeClient mastalabWebChromeClient = new MastalabWebChromeClient(WebviewActivity.this, webView, webview_container, videoLayout); FedilabWebChromeClient FedilabWebChromeClient = new FedilabWebChromeClient(WebviewActivity.this, webView, binding.webviewContainer, binding.videoLayout);
mastalabWebChromeClient.setOnToggledFullscreen(fullscreen -> { FedilabWebChromeClient.setOnToggledFullscreen(fullscreen -> {
if (fullscreen) { if (fullscreen) {
videoLayout.setVisibility(View.VISIBLE); binding.videoLayout.setVisibility(View.VISIBLE);
WindowManager.LayoutParams attrs = getWindow().getAttributes(); WindowManager.LayoutParams attrs = getWindow().getAttributes();
attrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN; attrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
attrs.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; attrs.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
@ -88,12 +113,12 @@ public class WebviewActivity extends BaseBarActivity {
attrs.flags &= ~WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; attrs.flags &= ~WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
getWindow().setAttributes(attrs); getWindow().setAttributes(attrs);
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
videoLayout.setVisibility(View.GONE); binding.videoLayout.setVisibility(View.GONE);
} }
}); });
webView.setWebChromeClient(mastalabWebChromeClient); webView.setWebChromeClient(FedilabWebChromeClient);
MastalabWebViewClient mastalabWebViewClient = new MastalabWebViewClient(WebviewActivity.this); FedilabWebViewClient = new FedilabWebViewClient(WebviewActivity.this);
webView.setWebViewClient(mastalabWebViewClient); webView.setWebViewClient(FedilabWebViewClient);
webView.setDownloadListener((url, userAgent, contentDisposition, mimetype, contentLength) -> { webView.setDownloadListener((url, userAgent, contentDisposition, mimetype, contentLength) -> {
if (Build.VERSION.SDK_INT >= 23) { if (Build.VERSION.SDK_INT >= 23) {
@ -108,12 +133,47 @@ public class WebviewActivity extends BaseBarActivity {
}); });
if (!url.toLowerCase().startsWith("http://") && !url.toLowerCase().startsWith("https://")) if (!url.toLowerCase().startsWith("http://") && !url.toLowerCase().startsWith("https://"))
url = "http://" + url; url = "http://" + url;
if (trackingDomains == null) {
AsyncTask.execute(() -> {
SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
// trackingDomains = new DomainBlockDAO(WebviewActivity.this, db).getAll();
if (trackingDomains == null)
trackingDomains = new ArrayList<>();
// Get a handler that can be used to post to the main thread
Handler mainHandler = new Handler(getMainLooper());
Runnable myRunnable = () -> webView.loadUrl(url);
mainHandler.post(myRunnable);
});
} else
webView.loadUrl(url); webView.loadUrl(url);
} }
/* public void setCount(Context context, String count) {
if (defaultMenu != null && !peertubeLink) {
MenuItem menuItem = defaultMenu.findItem(R.id.action_block);
LayerDrawable icon = (LayerDrawable) menuItem.getIcon();
CountDrawable badge;
// Reuse drawable if possible
Drawable reuse = icon.findDrawableByLayerId(R.id.ic_block_count);
if (reuse instanceof CountDrawable) {
badge = (CountDrawable) reuse;
} else {
badge = new CountDrawable(context);
}
badge.setCount(count);
icon.mutate();
icon.setDrawableByLayerId(R.id.ic_block_count, badge);
}
}*/
@Override @Override
public boolean onPrepareOptionsMenu(Menu menu) { public boolean onPrepareOptionsMenu(Menu menu) {
return super.onPrepareOptionsMenu(menu); return super.onPrepareOptionsMenu(menu);
} }
@ -122,6 +182,7 @@ public class WebviewActivity extends BaseBarActivity {
getMenuInflater().inflate(R.menu.main_webview, menu); getMenuInflater().inflate(R.menu.main_webview, menu);
if (peertubeLink) { if (peertubeLink) {
menu.findItem(R.id.action_go).setVisible(false); menu.findItem(R.id.action_go).setVisible(false);
menu.findItem(R.id.action_block).setVisible(false);
} }
return true; return true;
} }
@ -131,6 +192,27 @@ public class WebviewActivity extends BaseBarActivity {
int itemId = item.getItemId(); int itemId = item.getItemId();
if (itemId == android.R.id.home) { if (itemId == android.R.id.home) {
finish(); finish();
return true;
} else if (itemId == R.id.action_block) {
List<String> domains = FedilabWebViewClient.getDomains();
final ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(WebviewActivity.this, R.layout.domains_blocked);
arrayAdapter.addAll(domains);
AlertDialog.Builder builder = new AlertDialog.Builder(WebviewActivity.this, Helper.dialogStyle());
builder.setTitle(R.string.list_of_blocked_domains);
builder.setNegativeButton(R.string.close, (dialog, which) -> dialog.dismiss());
builder.setAdapter(arrayAdapter, (dialog, which) -> {
String strName = arrayAdapter.getItem(which);
assert strName != null;
Toasty.info(WebviewActivity.this, strName, Toast.LENGTH_LONG).show();
});
builder.show();
return true; return true;
} else if (itemId == R.id.action_go) { } else if (itemId == R.id.action_go) {
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));

View file

@ -0,0 +1,259 @@
/* Copyright 2021 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>. */
package app.fedilab.android.activities;
import static app.fedilab.android.activities.LoginActivity.apiLogin;
import static app.fedilab.android.activities.LoginActivity.client_idLogin;
import static app.fedilab.android.activities.LoginActivity.client_secretLogin;
import static app.fedilab.android.activities.LoginActivity.currentInstanceLogin;
import static app.fedilab.android.activities.LoginActivity.softwareLogin;
import static app.fedilab.android.helper.Helper.PREF_USER_TOKEN;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ProgressBar;
import androidx.appcompat.app.AlertDialog;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.PreferenceManager;
import java.util.regex.Matcher;
import app.fedilab.android.R;
import app.fedilab.android.client.entities.app.Account;
import app.fedilab.android.databinding.ActivityWebviewConnectBinding;
import app.fedilab.android.exception.DBException;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.viewmodel.mastodon.AccountsVM;
import app.fedilab.android.viewmodel.mastodon.AdminVM;
import app.fedilab.android.viewmodel.mastodon.OauthVM;
public class WebviewConnectActivity extends BaseActivity {
private ActivityWebviewConnectBinding binding;
private AlertDialog alert;
private String login_url;
private boolean requestedAdmin;
@SuppressWarnings("deprecation")
public static void clearCookies(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
CookieManager.getInstance().removeAllCookies(null);
CookieManager.getInstance().flush();
} else {
CookieSyncManager cookieSyncMngr = CookieSyncManager.createInstance(context);
cookieSyncMngr.startSync();
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.removeAllCookie();
cookieManager.removeSessionCookie();
cookieSyncMngr.stopSync();
cookieSyncMngr.sync();
}
}
@SuppressLint("ApplySharedPref")
public static void proceedLogin(Activity activity, Account account) {
new Thread(() -> {
try {
//update the database
new Account(activity).insertOrUpdate(account);
Handler mainHandler = new Handler(Looper.getMainLooper());
MainActivity.currentToken = account.token;
MainActivity.currentUserID = account.user_id;
MainActivity.api = Account.API.MASTODON;
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(activity);
SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putString(PREF_USER_TOKEN, account.token);
editor.commit();
//The user is now authenticated, it will be redirected to MainActivity
Runnable myRunnable = () -> {
Intent mainActivity = new Intent(activity, MainActivity.class);
activity.startActivity(mainActivity);
activity.finish();
};
mainHandler.post(myRunnable);
} catch (DBException e) {
e.printStackTrace();
}
}).start();
}
@SuppressLint("SetJavaScriptEnabled")
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ThemeHelper.applyTheme(this);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(WebviewConnectActivity.this);
binding = ActivityWebviewConnectBinding.inflate(getLayoutInflater());
View rootView = binding.getRoot();
setContentView(rootView);
Bundle b = getIntent().getExtras();
if (b != null) {
login_url = b.getString("login_url");
requestedAdmin = b.getBoolean("requestedAdmin", false);
}
if (login_url == null)
finish();
clearCookies(WebviewConnectActivity.this);
binding.webviewConnect.getSettings().setJavaScriptEnabled(true);
String user_agent = sharedpreferences.getString(getString(R.string.SET_CUSTOM_USER_AGENT), Helper.USER_AGENT);
binding.webviewConnect.getSettings().setUserAgentString(user_agent);
CookieManager.getInstance().setAcceptThirdPartyCookies(binding.webviewConnect, true);
final ProgressBar pbar = findViewById(R.id.progress_bar);
binding.webviewConnect.setWebChromeClient(new WebChromeClient() {
@Override
public void onProgressChanged(WebView view, int progress) {
if (progress < 100 && pbar.getVisibility() == ProgressBar.GONE) {
pbar.setVisibility(ProgressBar.VISIBLE);
}
pbar.setProgress(progress);
if (progress == 100) {
pbar.setVisibility(ProgressBar.GONE);
}
}
});
binding.webviewConnect.setWebViewClient(new WebViewClient() {
/* @Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
String x_xsrf_token = null;
String x_csrf_token = null;
if (request.getUrl().toString().contains("accounts/verify_credentials")) {
String cookies = CookieManager.getInstance().getCookie(request.getUrl().toString());
Map<String, String> requestHeaders = request.getRequestHeaders();
Iterator<Map.Entry<String, String>> it = requestHeaders.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> pair = it.next();
if (pair.getKey().compareTo("X-XSRF-TOKEN") == 0) {
x_xsrf_token = pair.getValue();
}
if (pair.getKey().compareTo("X-CSRF-TOKEN") == 0) {
x_csrf_token = pair.getValue();
}
it.remove();
}
if (x_xsrf_token != null && x_csrf_token != null) {
String finalX_xsrf_token = x_xsrf_token;
String finalX_csrf_token = x_csrf_token;
new Handler(Looper.getMainLooper()).post(() -> {
view.stopLoading();
SharedPreferences sharedpreferences1 = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedpreferences1.edit();
String token = "X-XSRF-TOKEN= " + finalX_xsrf_token + ";X-CSRF-TOKEN= " + finalX_csrf_token + "|" + cookies;
editor.putString(Helper.PREF_KEY_OAUTH_TOKEN, token);
editor.commit();
view.setVisibility(View.GONE);
//Update the account with the token;
new UpdateAccountInfoAsyncTask(WebviewConnectActivity.this, token, clientId, clientSecret, null, instance, social);
});
}
}
return super.shouldInterceptRequest(view, request);
}*/
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
super.shouldOverrideUrlLoading(view, url);
if (url.contains(Helper.REDIRECT_CONTENT_WEB)) {
Matcher matcher = Helper.codePattern.matcher(url);
if (!matcher.find()) {
return false;
}
String code = matcher.group(1);
OauthVM oauthVM = new ViewModelProvider(WebviewConnectActivity.this).get(OauthVM.class);
//API call to get the user token
String scope = requestedAdmin ? Helper.OAUTH_SCOPES_ADMIN : Helper.OAUTH_SCOPES;
oauthVM.createToken(currentInstanceLogin, "authorization_code", client_idLogin, client_secretLogin, Helper.REDIRECT_CONTENT_WEB, scope, code)
.observe(WebviewConnectActivity.this, tokenObj -> {
Account account = new Account();
account.client_id = client_idLogin;
account.client_secret = client_secretLogin;
account.token = tokenObj.token_type + " " + tokenObj.access_token;
account.api = apiLogin;
account.software = softwareLogin;
account.instance = currentInstanceLogin;
//API call to retrieve account information for the new token
AccountsVM accountsVM = new ViewModelProvider(WebviewConnectActivity.this).get(AccountsVM.class);
accountsVM.getConnectedAccount(currentInstanceLogin, account.token).observe(WebviewConnectActivity.this, mastodonAccount -> {
account.mastodon_account = mastodonAccount;
account.user_id = mastodonAccount.id;
//We check if user have really moderator rights
if (requestedAdmin) {
AdminVM adminVM = new ViewModelProvider(WebviewConnectActivity.this).get(AdminVM.class);
adminVM.getAccount(account.instance, account.token, account.user_id).observe(WebviewConnectActivity.this, adminAccount -> {
account.admin = adminAccount != null;
proceedLogin(WebviewConnectActivity.this, account);
});
} else {
proceedLogin(WebviewConnectActivity.this, account);
}
});
});
return true;
} else {
return false;
}
}
});
binding.webviewConnect.loadUrl(login_url);
}
@Override
public void onBackPressed() {
if (binding.webviewConnect.canGoBack()) {
binding.webviewConnect.goBack();
} else {
super.onBackPressed();
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (alert != null) {
alert.dismiss();
alert = null;
}
binding.webviewConnect.destroy();
}
}

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.broadcastreceiver; package app.fedilab.android.broadcastreceiver;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.broadcastreceiver; package app.fedilab.android.broadcastreceiver;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -14,12 +14,15 @@ package app.fedilab.android.mastodon.broadcastreceiver;
* 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.helper.Helper.RECEIVE_TOAST_CONTENT;
import static app.fedilab.android.helper.Helper.RECEIVE_TOAST_TYPE;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import app.fedilab.android.mastodon.helper.Helper; import app.fedilab.android.helper.Helper;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
public class ToastMessage extends BroadcastReceiver { public class ToastMessage extends BroadcastReceiver {
@ -27,8 +30,8 @@ public class ToastMessage extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
Bundle b = intent.getExtras(); Bundle b = intent.getExtras();
if (b != null) { if (b != null) {
String type = b.getString(Helper.RECEIVE_TOAST_TYPE, null); String type = b.getString(RECEIVE_TOAST_TYPE, null);
String content = b.getString(Helper.RECEIVE_TOAST_CONTENT, null); String content = b.getString(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:

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.client; package app.fedilab.android.client;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -15,7 +15,7 @@ package app.fedilab.android.mastodon.client;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import app.fedilab.android.mastodon.client.entities.app.WellKnownNodeinfo; import app.fedilab.android.client.entities.app.WellKnownNodeinfo;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.GET; import retrofit2.http.GET;
import retrofit2.http.Path; import retrofit2.http.Path;

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.client.endpoints; package app.fedilab.android.client.endpoints;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -17,7 +17,7 @@ package app.fedilab.android.mastodon.client.endpoints;
import java.util.List; import java.util.List;
import app.fedilab.android.mastodon.client.entities.api.JoinMastodonInstance; import app.fedilab.android.client.entities.api.JoinMastodonInstance;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.GET; import retrofit2.http.GET;
import retrofit2.http.Query; import retrofit2.http.Query;

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.client.endpoints; package app.fedilab.android.client.endpoints;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -17,18 +17,17 @@ package app.fedilab.android.mastodon.client.endpoints;
import java.util.List; import java.util.List;
import app.fedilab.android.mastodon.client.entities.api.Account; import app.fedilab.android.client.entities.api.Account;
import app.fedilab.android.mastodon.client.entities.api.FamiliarFollowers; import app.fedilab.android.client.entities.api.FeaturedTag;
import app.fedilab.android.mastodon.client.entities.api.FeaturedTag; import app.fedilab.android.client.entities.api.Filter;
import app.fedilab.android.mastodon.client.entities.api.IdentityProof; import app.fedilab.android.client.entities.api.IdentityProof;
import app.fedilab.android.mastodon.client.entities.api.MastodonList; import app.fedilab.android.client.entities.api.MastodonList;
import app.fedilab.android.mastodon.client.entities.api.Preferences; import app.fedilab.android.client.entities.api.Preferences;
import app.fedilab.android.mastodon.client.entities.api.RelationShip; import app.fedilab.android.client.entities.api.RelationShip;
import app.fedilab.android.mastodon.client.entities.api.Report; import app.fedilab.android.client.entities.api.Report;
import app.fedilab.android.mastodon.client.entities.api.Status; import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.mastodon.client.entities.api.Suggestion; import app.fedilab.android.client.entities.api.Tag;
import app.fedilab.android.mastodon.client.entities.api.Tag; import app.fedilab.android.client.entities.api.Token;
import app.fedilab.android.mastodon.client.entities.api.Token;
import okhttp3.MultipartBody; import okhttp3.MultipartBody;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.Body; import retrofit2.http.Body;
@ -41,6 +40,7 @@ import retrofit2.http.Headers;
import retrofit2.http.Multipart; import retrofit2.http.Multipart;
import retrofit2.http.PATCH; import retrofit2.http.PATCH;
import retrofit2.http.POST; import retrofit2.http.POST;
import retrofit2.http.PUT;
import retrofit2.http.Part; import retrofit2.http.Part;
import retrofit2.http.Path; import retrofit2.http.Path;
import retrofit2.http.Query; import retrofit2.http.Query;
@ -96,7 +96,7 @@ public interface MastodonAccountsService {
@Field("source[privacy]") String privacy, @Field("source[privacy]") String privacy,
@Field("source[sensitive]") Boolean sensitive, @Field("source[sensitive]") Boolean sensitive,
@Field("source[language]") String language, @Field("source[language]") String language,
@Field("fields_attributes") List<app.fedilab.android.mastodon.client.entities.api.Field.FieldParams> fields @Field("fields_attributes") List<app.fedilab.android.client.entities.api.Field.FieldParams> fields
); );
//Get Account //Get Account
@ -106,12 +106,6 @@ public interface MastodonAccountsService {
@Path("id") String id @Path("id") String id
); );
//Get Account
@GET("accounts/lookup")
Call<Account> lookUpAccount(
@Query("acct") String acct
);
//Get Account statuses //Get Account statuses
@GET("accounts/{id}/statuses") @GET("accounts/{id}/statuses")
Call<List<Status>> getAccountStatuses( Call<List<Status>> getAccountStatuses(
@ -159,7 +153,6 @@ public interface MastodonAccountsService {
@Path("id") String id @Path("id") String id
); );
//Get Identity proofs //Get Identity proofs
@GET("accounts/{id}/identity_proofs") @GET("accounts/{id}/identity_proofs")
Call<List<IdentityProof>> getIdentityProofs( Call<List<IdentityProof>> getIdentityProofs(
@ -174,8 +167,7 @@ public interface MastodonAccountsService {
@Header("Authorization") String app_token, @Header("Authorization") String app_token,
@Path("id") String id, @Path("id") String id,
@Field("reblogs") boolean reblogs, @Field("reblogs") boolean reblogs,
@Field("notify") boolean notify, @Field("notify") boolean notify
@Field("languages[]") List<String> languages
); );
//Follow account //Follow account
@ -255,13 +247,6 @@ public interface MastodonAccountsService {
@Query("id[]") List<String> ids @Query("id[]") List<String> ids
); );
//Get familiar followers
@GET("accounts/familiar_followers ")
Call<List<FamiliarFollowers>> getFamiliarFollowers(
@Header("Authorization") String token,
@Query("id[]") List<String> ids
);
//Get search //Get search
@GET("accounts/search") @GET("accounts/search")
Call<List<Account>> searchAccounts( Call<List<Account>> searchAccounts(
@ -331,9 +316,51 @@ public interface MastodonAccountsService {
@DELETE("domain_blocks") @DELETE("domain_blocks")
Call<Void> removeDomainBlocks( Call<Void> removeDomainBlocks(
@Header("Authorization") String token, @Header("Authorization") String token,
@Query("domain") String domain @Field("domain") String domain
); );
//Get filters
@GET("filters")
Call<List<Filter>> getFilters(
@Header("Authorization") String token);
//Get a filter with its id
@GET("filters/{id}")
Call<Filter> getFilter(
@Header("Authorization") String token,
@Path("id") String id);
//Add a filter
@FormUrlEncoded
@POST("filters")
Call<Filter> addFilter(
@Header("Authorization") String token,
@Field("phrase") String phrase,
@Field("context[]") List<String> context,
@Field("irreversible") boolean irreversible,
@Field("whole_word") boolean whole_word,
@Field("expires_in") long expires_in
);
//Edit a filter
@FormUrlEncoded
@PUT("filters/{id}")
Call<Filter> editFilter(
@Header("Authorization") String token,
@Path("id") String id,
@Field("phrase") String phrase,
@Field("context[]") List<String> context,
@Field("irreversible") boolean irreversible,
@Field("whole_word") boolean whole_word,
@Field("expires_in") long expires_in
);
//Remove a filter
@DELETE("filters/{id}")
Call<Void> removeFilter(
@Header("Authorization") String token,
@Path("id") String id
);
//Post a report //Post a report
@Headers({"Accept: application/json"}) @Headers({"Accept: application/json"})
@ -407,7 +434,7 @@ public interface MastodonAccountsService {
//Get user suggestions //Get user suggestions
@GET("suggestions") @GET("suggestions")
Call<List<Suggestion>> getSuggestions( Call<List<Account>> getSuggestions(
@Header("Authorization") String token, @Header("Authorization") String token,
@Query("limit") String limit @Query("limit") String limit
); );
@ -418,15 +445,4 @@ public interface MastodonAccountsService {
@Header("Authorization") String token, @Header("Authorization") String token,
@Path("account_id") String account_id @Path("account_id") String account_id
); );
//Get user suggestions
@GET("directory")
Call<List<Account>> getDirectory(
@Header("Authorization") String token,
@Query("offset") Integer offset,
@Query("limit") Integer limit,
@Query("order") String order,
@Query("local") Boolean local
);
} }

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.client.endpoints; package app.fedilab.android.client.endpoints;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -17,17 +17,14 @@ package app.fedilab.android.mastodon.client.endpoints;
import java.util.List; import java.util.List;
import app.fedilab.android.mastodon.client.entities.api.admin.AdminAccount; import app.fedilab.android.client.entities.api.AdminAccount;
import app.fedilab.android.mastodon.client.entities.api.admin.AdminDomainBlock; import app.fedilab.android.client.entities.api.AdminReport;
import app.fedilab.android.mastodon.client.entities.api.admin.AdminReport;
import retrofit2.Call; import retrofit2.Call;
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.POST; import retrofit2.http.POST;
import retrofit2.http.PUT;
import retrofit2.http.Path; import retrofit2.http.Path;
import retrofit2.http.Query; import retrofit2.http.Query;
@ -60,7 +57,6 @@ public interface MastodonAdminService {
@Path("id") String id @Path("id") String id
); );
@FormUrlEncoded
@POST("admin/accounts/{account_id}/action") @POST("admin/accounts/{account_id}/action")
Call<Void> performAction( Call<Void> performAction(
@Header("Authorization") String app_token, @Header("Authorization") String app_token,
@ -72,35 +68,35 @@ public interface MastodonAdminService {
@Field("send_email_notification") Boolean send_email_notification @Field("send_email_notification") Boolean send_email_notification
); );
@FormUrlEncoded
@POST("admin/accounts/{account_id}/approve") @POST("admin/accounts/{account_id}/approve")
Call<AdminAccount> approve( Call<AdminAccount> approve(
@Header("Authorization") String app_token, @Header("Authorization") String app_token,
@Path("account_id") String account_id @Path("account_id") String account_id
); );
@FormUrlEncoded
@POST("admin/accounts/{account_id}/reject") @POST("admin/accounts/{account_id}/reject")
Call<AdminAccount> reject( Call<AdminAccount> reject(
@Header("Authorization") String app_token, @Header("Authorization") String app_token,
@Path("account_id") String account_id @Path("account_id") String account_id
); );
@FormUrlEncoded
@POST("admin/accounts/{account_id}/enable") @POST("admin/accounts/{account_id}/enable")
Call<AdminAccount> enable( Call<AdminAccount> enable(
@Header("Authorization") String app_token, @Header("Authorization") String app_token,
@Path("account_id") String account_id @Path("account_id") String account_id
); );
@FormUrlEncoded
@POST("admin/accounts/{account_id}/unsilence") @POST("admin/accounts/{account_id}/unsilence")
Call<AdminAccount> unsilence( Call<AdminAccount> unsilence(
@Header("Authorization") String app_token, @Header("Authorization") String app_token,
@Path("account_id") String account_id @Path("account_id") String account_id
); );
@FormUrlEncoded
@POST("admin/accounts/{account_id}/unsuspend") @POST("admin/accounts/{account_id}/unsuspend")
Call<AdminAccount> unsuspend( Call<AdminAccount> unsuspend(
@Header("Authorization") String app_token, @Header("Authorization") String app_token,
@ -118,115 +114,38 @@ public interface MastodonAdminService {
@Query("limit") int limit @Query("limit") int limit
); );
//***************** ADMIN REPORTS ************** @FormUrlEncoded
@GET("admin/reports/{id}") @GET("admin/reports/{id}")
Call<AdminReport> getReport( Call<AdminReport> getReport(
@Header("Authorization") String token, @Header("Authorization") String token,
@Path("id") String id @Path("id") String id
); );
@FormUrlEncoded
@POST("admin/reports/{id}/assign_to_self") @POST("admin/reports/{id}/assign_to_self")
Call<AdminReport> assignToSelf( Call<AdminReport> assignToSelf(
@Header("Authorization") String app_token, @Header("Authorization") String app_token,
@Path("id") String id @Path("id") String id
); );
@FormUrlEncoded
@POST("admin/reports/{id}/unassign") @POST("admin/reports/{id}/unassign")
Call<AdminReport> unassign( Call<AdminReport> unassign(
@Header("Authorization") String app_token, @Header("Authorization") String app_token,
@Path("id") String id @Path("id") String id
); );
@FormUrlEncoded
@POST("admin/reports/{id}/resolve") @POST("admin/reports/{id}/resolve")
Call<AdminReport> resolved( Call<AdminReport> resolved(
@Header("Authorization") String app_token, @Header("Authorization") String app_token,
@Path("id") String id @Path("id") String id
); );
@FormUrlEncoded
@POST("admin/reports/{id}/reopen") @POST("admin/reports/{id}/reopen")
Call<AdminReport> reopen( Call<AdminReport> reopen(
@Header("Authorization") String app_token, @Header("Authorization") String app_token,
@Path("id") String id @Path("id") String id
); );
//*************** ADMIN DOMAINS ****************
@GET("admin/domain_blocks")
Call<List<AdminDomainBlock>> getDomainBlocks(
@Header("Authorization") String token,
@Query("max_id") String max_id,
@Query("limit") int limit
);
@GET("admin/domain_allows")
Call<List<AdminDomainBlock>> getDomainAllows(
@Header("Authorization") String token,
@Query("max_id") String max_id,
@Query("limit") int limit
);
@GET("admin/domain_blocks/{id}")
Call<AdminDomainBlock> getDomainBlock(
@Header("Authorization") String token,
@Path("id") String id
);
@GET("admin/domain_allows/{id}")
Call<AdminDomainBlock> getDomainAllow(
@Header("Authorization") String token,
@Path("id") String id
);
@FormUrlEncoded
@POST("admin/domain_blocks")
Call<AdminDomainBlock> blockDomain(
@Header("Authorization") String app_token,
@Field("domain") String domain,
@Field("severity") String severity,
@Field("reject_media") Boolean reject_media,
@Field("reject_reports") Boolean reject_reports,
@Field("private_comment") String private_comment,
@Field("public_comment") String public_comment,
@Field("obfuscate") Boolean obfuscate
);
@FormUrlEncoded
@POST("admin/domain_allows")
Call<AdminDomainBlock> allowDomain(
@Header("Authorization") String app_token,
@Path("domain") String domain
);
@FormUrlEncoded
@PUT("admin/domain_blocks/{id}")
Call<AdminDomainBlock> updateBlockDomain(
@Header("Authorization") String app_token,
@Path("id") String id,
@Field("severity") String severity,
@Field("reject_media") Boolean reject_media,
@Field("reject_reports") Boolean reject_reports,
@Field("private_comment") String private_comment,
@Field("public_comment") String public_comment,
@Field("obfuscate") Boolean obfuscate
);
@DELETE("admin/domain_blocks/{id}")
Call<Void> deleteBlockDomain(
@Header("Authorization") String app_token,
@Path("id") String id
);
@DELETE("admin/domain_allows/{id}")
Call<Void> deleteAllowDomain(
@Header("Authorization") String app_token,
@Path("id") String id
);
} }

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.client.endpoints; package app.fedilab.android.client.endpoints;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -17,7 +17,7 @@ package app.fedilab.android.mastodon.client.endpoints;
import java.util.List; import java.util.List;
import app.fedilab.android.mastodon.client.entities.api.Announcement; import app.fedilab.android.client.entities.api.Announcement;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.DELETE; import retrofit2.http.DELETE;
import retrofit2.http.FormUrlEncoded; import retrofit2.http.FormUrlEncoded;

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.client.endpoints; package app.fedilab.android.client.endpoints;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -15,8 +15,8 @@ package app.fedilab.android.mastodon.client.endpoints;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import app.fedilab.android.mastodon.client.entities.api.App; import app.fedilab.android.client.entities.api.App;
import app.fedilab.android.mastodon.client.entities.api.Token; import app.fedilab.android.client.entities.api.Token;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.Field; import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded; import retrofit2.http.FormUrlEncoded;

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.client.endpoints; package app.fedilab.android.client.endpoints;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -17,11 +17,11 @@ package app.fedilab.android.mastodon.client.endpoints;
import java.util.List; import java.util.List;
import app.fedilab.android.mastodon.client.entities.api.Account; import app.fedilab.android.client.entities.api.Account;
import app.fedilab.android.mastodon.client.entities.api.Activity; import app.fedilab.android.client.entities.api.Activity;
import app.fedilab.android.mastodon.client.entities.api.Emoji; import app.fedilab.android.client.entities.api.Emoji;
import app.fedilab.android.mastodon.client.entities.api.Instance; import app.fedilab.android.client.entities.api.Instance;
import app.fedilab.android.mastodon.client.entities.api.Tag; import app.fedilab.android.client.entities.api.Tag;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.GET; import retrofit2.http.GET;
import retrofit2.http.Query; import retrofit2.http.Query;

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.client.endpoints; package app.fedilab.android.client.endpoints;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -16,8 +16,8 @@ package app.fedilab.android.mastodon.client.endpoints;
import java.util.List; import java.util.List;
import app.fedilab.android.mastodon.client.entities.api.Notification; import app.fedilab.android.client.entities.api.Notification;
import app.fedilab.android.mastodon.client.entities.api.PushSubscription; import app.fedilab.android.client.entities.api.PushSubscription;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.DELETE; import retrofit2.http.DELETE;
import retrofit2.http.Field; import retrofit2.http.Field;
@ -71,12 +71,7 @@ public interface MastodonNotificationsService {
@Field("data[alerts][favourite]") boolean favourite, @Field("data[alerts][favourite]") boolean favourite,
@Field("data[alerts][reblog]") boolean reblog, @Field("data[alerts][reblog]") boolean reblog,
@Field("data[alerts][mention]") boolean mention, @Field("data[alerts][mention]") boolean mention,
@Field("data[alerts][poll]") boolean poll, @Field("data[alerts][poll]") boolean poll
@Field("data[alerts][status]") boolean status,
@Field("data[alerts][update]") boolean update,
@Field("data[alerts][admin.sign_up]") boolean admin_sign_up,
@Field("data[alerts][admin.report]") boolean admin_report
); );
@GET("push/subscription") @GET("push/subscription")

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.client.endpoints; package app.fedilab.android.client.endpoints;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -15,7 +15,7 @@ package app.fedilab.android.mastodon.client.endpoints;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import app.fedilab.android.mastodon.client.entities.api.Oembed; import app.fedilab.android.client.entities.api.Oembed;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.GET; import retrofit2.http.GET;
import retrofit2.http.Query; import retrofit2.http.Query;

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.client.endpoints; package app.fedilab.android.client.endpoints;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -15,7 +15,7 @@ package app.fedilab.android.mastodon.client.endpoints;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import app.fedilab.android.mastodon.client.entities.api.Results; import app.fedilab.android.client.entities.api.Results;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.GET; import retrofit2.http.GET;
import retrofit2.http.Header; import retrofit2.http.Header;

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.client.endpoints; package app.fedilab.android.client.endpoints;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -17,16 +17,14 @@ package app.fedilab.android.mastodon.client.endpoints;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import app.fedilab.android.mastodon.client.entities.api.Account; import app.fedilab.android.client.entities.api.Account;
import app.fedilab.android.mastodon.client.entities.api.Attachment; import app.fedilab.android.client.entities.api.Attachment;
import app.fedilab.android.mastodon.client.entities.api.Card; import app.fedilab.android.client.entities.api.Card;
import app.fedilab.android.mastodon.client.entities.api.Context; import app.fedilab.android.client.entities.api.Context;
import app.fedilab.android.mastodon.client.entities.api.Poll; import app.fedilab.android.client.entities.api.Poll;
import app.fedilab.android.mastodon.client.entities.api.ScheduledStatus; import app.fedilab.android.client.entities.api.ScheduledStatus;
import app.fedilab.android.mastodon.client.entities.api.Status; import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.mastodon.client.entities.api.StatusSource;
import okhttp3.MultipartBody; import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.DELETE; import retrofit2.http.DELETE;
import retrofit2.http.Field; import retrofit2.http.Field;
@ -59,42 +57,7 @@ public interface MastodonStatusesService {
@Field("sensitive") Boolean sensitive, @Field("sensitive") Boolean sensitive,
@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("quote_id") String quote_id,
@Field("content_type") String content_type
);
@GET("statuses/{id}/source")
Call<StatusSource> getStatusSource(
@Header("Authorization") String token,
@Path("id") String id);
@GET("statuses/{id}/history")
Call<List<Status>> getStatusHistory(
@Header("Authorization") String token,
@Path("id") String id);
//Post a status
@FormUrlEncoded
@PUT("statuses/{id}")
Call<Status> updateStatus(
@Header("Idempotency-Key") String idempotency_Key,
@Header("Authorization") String token,
@Path("id") String id,
@Field("status") String status,
@Field("media_ids[]") List<String> media_ids,
@Field("poll[options][]") List<String> poll_options,
@Field("poll[expires_in]") Integer poll_expire_in,
@Field("poll[multiple]") Boolean poll_multiple,
@Field("poll[hide_totals]") Boolean poll_hide_totals,
@Field("in_reply_to_id") String in_reply_to_id,
@Field("sensitive") Boolean sensitive,
@Field("spoiler_text") String spoiler_text,
@Field("visibility") String visibility,
@Field("language") String language,
@Field("media_attributes[id][]") List<String> media_id,
@Field("media_attributes[description][]") List<String> media_description,
@Field("media_attributes[focus][]") List<String> focus
); );
//Post a scheduled status //Post a scheduled status
@ -254,8 +217,8 @@ public interface MastodonStatusesService {
@Header("Authorization") String token, @Header("Authorization") String token,
@Part MultipartBody.Part file, @Part MultipartBody.Part file,
@Part MultipartBody.Part thumbnail, @Part MultipartBody.Part thumbnail,
@Part("description") RequestBody description, @Part("description") String description,
@Part("focus") RequestBody focus @Part("focus") String focus
); );
//Edit a Media //Edit a Media
@ -318,18 +281,4 @@ public interface MastodonStatusesService {
@Header("Authorization") String token, @Header("Authorization") String token,
@Path("id") String id @Path("id") String id
); );
@POST("statuses/{id}/react/{name}")
Call<Void> addReaction(
@Header("Authorization") String app_token,
@Path("id") String id,
@Path("name") String name
);
@POST("statuses/{id}/unreact/{name}")
Call<Void> removeReaction(
@Header("Authorization") String app_token,
@Path("id") String id,
@Path("name") String name
);
} }

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.client.endpoints; package app.fedilab.android.client.endpoints;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -16,23 +16,17 @@ package app.fedilab.android.mastodon.client.endpoints;
import java.util.List; import java.util.List;
import app.fedilab.android.mastodon.client.entities.api.Account; import app.fedilab.android.client.entities.api.Account;
import app.fedilab.android.mastodon.client.entities.api.Conversation; import app.fedilab.android.client.entities.api.Conversation;
import app.fedilab.android.mastodon.client.entities.api.Marker; import app.fedilab.android.client.entities.api.Marker;
import app.fedilab.android.mastodon.client.entities.api.MastodonList; import app.fedilab.android.client.entities.api.MastodonList;
import app.fedilab.android.mastodon.client.entities.api.Status; import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.mastodon.client.entities.api.Tag;
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.peertube.PeertubeVideo;
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.POST; import retrofit2.http.POST;
import retrofit2.http.PUT; import retrofit2.http.PUT;
import retrofit2.http.Path; import retrofit2.http.Path;
@ -53,46 +47,20 @@ public interface MastodonTimelinesService {
@Query("limit") Integer limit @Query("limit") Integer limit
); );
@GET("timelines/bubble")
Call<List<Status>> getBubble(
@Header("Authorization") String token,
@Query("only_media") Boolean only_media,
@Query("remote") Boolean remote,
@Query("with_muted") Boolean with_muted,
@Query("exclude_visibilities") List<String> exclude_visibilities,
@Query("reply_visibility") String reply_visibility,
@Query("max_id") String max_id,
@Query("since_id") String since_id,
@Query("min_id") String min_id,
@Query("limit") Integer limit
);
@GET("trends/statuses")
Call<List<Status>> getStatusTrends(
@Header("Authorization") String token,
@Query("offset") String offset,
@Query("limit") Integer limit);
@GET("trends/tags")
Call<List<Tag>> getTagTrends(@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(
@Header("Authorization") String token, @Header("Authorization") String token,
@Path("hashtag") String hashtag, @Path("hashtag") String hashtag,
@Query("local") Boolean local, @Query("local") boolean local,
@Query("only_media") Boolean only_media, @Query("only_media") boolean only_media,
@Query("all[]") List<String> all, @Query("all[]") List<String> all,
@Query("any[]") List<String> any, @Query("any[]") List<String> any,
@Query("none[]") List<String> none, @Query("none[]") List<String> none,
@Query("max_id") String max_id, @Query("max_id") String max_id,
@Query("since_id") String since_id, @Query("since_id") String since_id,
@Query("min_id") String min_id, @Query("min_id") String min_id,
@Query("limit") Integer limit @Query("limit") int limit
); );
//Home timeline //Home timeline
@ -102,8 +70,8 @@ public interface MastodonTimelinesService {
@Query("max_id") String max_id, @Query("max_id") String max_id,
@Query("since_id") String since_id, @Query("since_id") String since_id,
@Query("min_id") String min_id, @Query("min_id") String min_id,
@Query("limit") Integer limit, @Query("limit") int limit,
@Query("local") Boolean local @Query("local") boolean local
); );
//List timeline //List timeline
@ -114,7 +82,7 @@ public interface MastodonTimelinesService {
@Query("max_id") String max_id, @Query("max_id") String max_id,
@Query("since_id") String since_id, @Query("since_id") String since_id,
@Query("min_id") String min_id, @Query("min_id") String min_id,
@Query("limit") Integer limit @Query("limit") int limit
); );
//get conversations //get conversations
@ -223,50 +191,4 @@ public interface MastodonTimelinesService {
@Field("home[last_read_id]") String home_last_read_id, @Field("home[last_read_id]") String home_last_read_id,
@Field("notifications[last_read_id]") String notifications_last_read_id @Field("notifications[last_read_id]") String notifications_last_read_id
); );
@Headers({"Accept: application/json"})
@POST("api/notes")
Call<List<MisskeyNote>> getMisskey(@Body MisskeyNote.MisskeyParams params);
//Public timelines for Misskey
@FormUrlEncoded
@POST("api/notes")
Call<List<MisskeyNote>> getMisskey(
@Field("local") boolean local,
@Field("file") boolean file,
@Field("poll") boolean poll,
@Field("remote") boolean remote,
@Field("reply") boolean reply,
@Field("untilId") String max_id,
@Field("since_id") String since_id,
@Field("limit") Integer limit
);
@GET("api/v1/videos")
Call<PeertubeVideo> getPeertube(
@Query("start") String start,
@Query("filter") String filter,
@Query("sort") String sort,
@Query("count") int count
);
@Headers({"Accept: text/html,application/xhtml+xml,application/xml"})
@GET("{names}/rss")
Call<Nitter> getNitter(
@Path("names") String id,
@Query("max_position") String max_position
);
@Headers({"Accept: text/html,application/xhtml+xml,application/xml"})
@GET("{account}/rss")
Call<Nitter> getNitterAccount(
@Path("account") String account
);
@GET("api/v1/videos/{id}")
Call<PeertubeVideo.Video> getPeertubeVideo(
@Path("id") String id
);
} }

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.client.endpoints; package app.fedilab.android.client.endpoints;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.client.entities.api; package app.fedilab.android.client.entities.api;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -14,23 +14,15 @@ 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 android.app.Activity;
import android.content.Context;
import android.text.Spannable; import android.text.Spannable;
import android.view.View;
import androidx.annotation.Nullable;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import java.io.Serializable; import java.io.Serializable;
import java.lang.ref.WeakReference;
import java.util.Date; import java.util.Date;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import app.fedilab.android.mastodon.helper.SpannableHelper;
public class Account implements Serializable { public class Account implements Serializable {
@SerializedName("id") @SerializedName("id")
@ -75,70 +67,18 @@ public class Account implements Serializable {
public List<Field> fields; public List<Field> fields;
@SerializedName("suspended") @SerializedName("suspended")
public boolean suspended; public boolean suspended;
@SerializedName("limited")
public boolean limited;
@SerializedName("discoverable") @SerializedName("discoverable")
public boolean discoverable; public boolean discoverable;
@SerializedName("group")
public boolean group;
@SerializedName("mute_expires_at") @SerializedName("mute_expires_at")
public Date mute_expires_at; public Date mute_expires_at;
@SerializedName("moved") @SerializedName("moved")
public Account moved; public Account moved;
@SerializedName("role")
public Role role; //Some extra spannable element - They will be filled automatically when fetching the account
public transient Spannable span_display_name;
public transient Spannable span_note;
public transient RelationShip relationShip; public transient RelationShip relationShip;
public synchronized Spannable getSpanDisplayName(Context context, WeakReference<View> viewWeakReference) {
if (display_name == null || display_name.isEmpty()) {
display_name = username;
}
return SpannableHelper.convert(context, display_name, null, this, null, viewWeakReference);
}
public synchronized Spannable getSpanDisplayName(Activity activity, WeakReference<View> viewWeakReference) {
if (display_name == null || display_name.isEmpty()) {
display_name = username;
}
return SpannableHelper.convertEmoji(activity, display_name, this, viewWeakReference);
}
public synchronized Spannable getSpanDisplayNameTitle(Context context, WeakReference<View> viewWeakReference, String title) {
return SpannableHelper.convert(context, title, null, this, null, viewWeakReference);
}
public synchronized Spannable getSpanNote(Context context, WeakReference<View> viewWeakReference) {
return SpannableHelper.convert(context, note, null, this, null, viewWeakReference);
}
@Override
public boolean equals(@Nullable Object obj) {
boolean same = false;
if (obj instanceof Account) {
same = this.id.equals(((Account) obj).id);
}
return same;
}
public static class Role implements Serializable {
@SerializedName("id")
public String id;
@SerializedName("name")
public String name;
@SerializedName("color")
public String color;
@SerializedName("position")
public int position;
@SerializedName("permissions")
public int permissions;
@SerializedName("highlighted")
public boolean highlighted;
@SerializedName("created_at")
public Date created_at;
@SerializedName("updated_at")
public Date updated_at;
}
public static class AccountParams implements Serializable { public static class AccountParams implements Serializable {
@SerializedName("discoverable") @SerializedName("discoverable")
public boolean discoverable; public boolean discoverable;

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.client.entities.api; package app.fedilab.android.client.entities.api;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.client.entities.api; package app.fedilab.android.client.entities.api;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.client.entities.api.admin; package app.fedilab.android.client.entities.api;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -18,39 +18,10 @@ import com.google.gson.annotations.SerializedName;
import java.io.Serializable; import java.io.Serializable;
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;
public class AdminAccount implements Serializable { public class AdminAccount implements Serializable {
public static LinkedHashMap<Integer, String> permissions;
static {
permissions = new LinkedHashMap<>();
permissions.put(1, "Administrator");
permissions.put(2, "Devops");
permissions.put(4, "View Audit Log");
permissions.put(8, "View Dashboard");
permissions.put(10, "Manage Reports");
permissions.put(20, "Manage Federation");
permissions.put(40, "Manage Settings");
permissions.put(80, "Manage Blocks");
permissions.put(100, "Manage Taxonomies");
permissions.put(200, "Manage Appeals");
permissions.put(400, "Manage Users");
permissions.put(800, "Manage Invites");
permissions.put(1000, "Manage Rules");
permissions.put(2000, "Manage Announcements");
permissions.put(4000, "Manage Custom Emojis");
permissions.put(8000, "Manage Webhooks");
permissions.put(10000, "Invite Users");
permissions.put(20000, "Manage Roles");
permissions.put(40000, "Manage User Access");
permissions.put(80000, "Delete User Data");
}
@SerializedName("id") @SerializedName("id")
public String id; public String id;
@SerializedName("username") @SerializedName("username")
@ -62,21 +33,25 @@ public class AdminAccount implements Serializable {
@SerializedName("email") @SerializedName("email")
public String email; public String email;
@SerializedName("ip") @SerializedName("ip")
public String ip; public IP ip;
@SerializedName("ips")
public List<IP> ips;
@SerializedName("locale")
public String locale;
@SerializedName("invite_request")
public String invite_request;
@SerializedName("role") @SerializedName("role")
public Role role; public String role;
@SerializedName("confirmed") @SerializedName("confirmed")
public boolean confirmed; public boolean confirmed;
@SerializedName("suspended")
public boolean suspended;
@SerializedName("silenced")
public boolean silenced;
@SerializedName("disabled")
public boolean disabled;
@SerializedName("approved") @SerializedName("approved")
public boolean approved; public boolean approved;
@SerializedName("ips") @SerializedName("disabled")
public List<AdminIp> ips; public boolean disabled;
@SerializedName("silenced")
public boolean silenced;
@SerializedName("suspended")
public boolean suspended;
@SerializedName("account") @SerializedName("account")
public Account account; public Account account;
@SerializedName("created_by_application_id") @SerializedName("created_by_application_id")
@ -85,28 +60,12 @@ public class AdminAccount implements Serializable {
public String invited_by_account_id; public String invited_by_account_id;
@SerializedName("locale") public static class IP implements Serializable {
public String locale;
@SerializedName("invite_request")
public String invite_request;
public static class Role implements Serializable {
@SerializedName("ip") @SerializedName("ip")
public String ip; public String ip;
@SerializedName("name") @SerializedName("used_at")
public String name; public Date used_at;
@SerializedName("color") @SerializedName("user_id")
public String color; public String user_id;
@SerializedName("position")
public long position;
@SerializedName("permissions")
public int permissions;
@SerializedName("highlighted")
public boolean highlighted;
@SerializedName("created_at")
public Date created_at;
@SerializedName("updated_at")
public Date updated_at;
} }
} }

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.client.entities.api.admin; package app.fedilab.android.client.entities.api;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -17,8 +17,6 @@ package app.fedilab.android.mastodon.client.entities.api.admin;
import java.util.List; import java.util.List;
import app.fedilab.android.mastodon.client.entities.api.Pagination;
public class AdminAccounts { public class AdminAccounts {
public Pagination pagination = new Pagination(); public Pagination pagination = new Pagination();

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.client.entities.api.admin; package app.fedilab.android.client.entities.api;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -20,37 +20,30 @@ import java.io.Serializable;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import app.fedilab.android.mastodon.client.entities.api.Instance;
import app.fedilab.android.mastodon.client.entities.api.Status;
public class AdminReport implements Serializable { public class AdminReport implements Serializable {
@SerializedName("id") @SerializedName("id")
public String id; public String id;
@SerializedName("account")
public Account account;
@SerializedName("action_taken") @SerializedName("action_taken")
public Boolean action_taken; public String action_taken;
@SerializedName("action_taken_at") @SerializedName("action_taken_by_account")
public Date action_taken_at; public String action_taken_by_account;
@SerializedName("assigned_account")
public Account assigned_account;
@SerializedName("category") @SerializedName("category")
public String category; public String category;
@SerializedName("comment") @SerializedName("comment")
public String comment; public String comment;
@SerializedName("forwarded")
public boolean forwarded;
@SerializedName("created_at") @SerializedName("created_at")
public Date created_at; public Date created_at;
@SerializedName("updated_at")
public Date updated_at;
@SerializedName("account")
public AdminAccount account;
@SerializedName("target_account") @SerializedName("target_account")
public AdminAccount target_account; public Account target_account;
@SerializedName("assigned_account")
public AdminAccount assigned_account;
@SerializedName("action_taken_by_account")
public AdminAccount action_taken_by_account;
@SerializedName("statuses") @SerializedName("statuses")
public List<Status> statuses; public List<Status> statuses;
@SerializedName("rules") @SerializedName("rules")
public List<Instance.Rule> rules; public List<Instance.Rule> rules;
@SerializedName("updated_at")
public Date updated_at;
} }

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.client.entities.api.admin; package app.fedilab.android.client.entities.api;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -17,8 +17,6 @@ package app.fedilab.android.mastodon.client.entities.api.admin;
import java.util.List; import java.util.List;
import app.fedilab.android.mastodon.client.entities.api.Pagination;
public class AdminReports { public class AdminReports {
public Pagination pagination = new Pagination(); public Pagination pagination = new Pagination();

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.client.entities.api; package app.fedilab.android.client.entities.api;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -14,18 +14,13 @@ 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 android.content.Context;
import android.text.Spannable; import android.text.Spannable;
import android.view.View;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
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.SpannableHelper;
public class Announcement { public class Announcement {
@SerializedName("id") @SerializedName("id")
public String id; public String id;
@ -54,9 +49,6 @@ public class Announcement {
@SerializedName("reactions") @SerializedName("reactions")
public List<Reaction> reactions; public List<Reaction> reactions;
//Some extra spannable element - They will be filled automatically when fetching the status
public synchronized Spannable getSpanContent(Context context, WeakReference<View> viewWeakReference) { public transient Spannable span_content;
return SpannableHelper.convert(context, content, null, null, this, viewWeakReference);
}
} }

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.client.entities.api; package app.fedilab.android.client.entities.api;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.client.entities.api; package app.fedilab.android.client.entities.api;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -14,8 +14,6 @@ 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 androidx.annotation.Nullable;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import java.io.Serializable; import java.io.Serializable;
@ -45,52 +43,5 @@ public class Attachment implements Serializable {
public long size; public long size;
@SerializedName("local_path") @SerializedName("local_path")
public String local_path; public String local_path;
@SerializedName("meta")
public Meta meta;
@SerializedName("sensitive")
public boolean sensitive = false;
public String peertubeHost = null;
public String peertubeId = null;
public String focus = null;
public String translation = null;
public transient Status status = null;
@Override
public boolean equals(@Nullable Object obj) {
boolean same = false;
if (obj instanceof Attachment && ((Attachment) obj).id != null && this.id != null) {
same = this.id.equals(((Attachment) obj).id);
return same;
} else return super.equals(obj);
}
public static class Meta implements Serializable {
@SerializedName("focus")
public Focus focus;
@SerializedName("original")
public MediaData original;
@SerializedName("small")
public MediaData small;
}
public static class Focus implements Serializable {
@SerializedName("x")
public float x;
@SerializedName("y")
public float y;
}
public static class MediaData implements Serializable {
@SerializedName("width")
public int width;
@SerializedName("height")
public int height;
@SerializedName("size")
public String size;
@SerializedName("aspect")
public float aspect;
}
} }

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.client.entities.api; package app.fedilab.android.client.entities.api;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.client.entities.api; package app.fedilab.android.client.entities.api;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab

View file

@ -1,5 +1,10 @@
package app.fedilab.android.mastodon.client.entities.api; package app.fedilab.android.client.entities.api;
/* Copyright 2022 Thomas Schneider
import com.google.gson.annotations.SerializedName;
import java.util.List;
/* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
* *
@ -13,16 +18,13 @@ 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>. */
public class Conversation {
import com.google.gson.annotations.SerializedName;
import java.io.Serializable;
import java.util.List;
public class FamiliarFollowers implements Serializable {
@SerializedName("id") @SerializedName("id")
public String id; public String id;
@SerializedName("unread")
public boolean unread;
@SerializedName("accounts") @SerializedName("accounts")
public List<Account> accounts; public List<Account> accounts;
@SerializedName("last_status")
public Status last_status;
} }

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.client.entities.api; package app.fedilab.android.client.entities.api;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.client.entities.api; package app.fedilab.android.client.entities.api;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab

View file

@ -1,4 +1,4 @@
package app.fedilab.android.mastodon.client.entities.api; package app.fedilab.android.client.entities.api;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -27,7 +27,7 @@ import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import app.fedilab.android.mastodon.exception.DBException; import app.fedilab.android.exception.DBException;
import app.fedilab.android.sqlite.Sqlite; import app.fedilab.android.sqlite.Sqlite;

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