forked from mirrors/Fedilab
first commit
This commit is contained in:
commit
0f855c5ac2
710 changed files with 112474 additions and 0 deletions
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
*.iml
|
||||||
|
.gradle
|
||||||
|
/local.properties
|
||||||
|
/.idea/*
|
||||||
|
.DS_Store
|
||||||
|
/build
|
||||||
|
/captures
|
||||||
|
.externalNativeBuild
|
||||||
|
.cxx
|
||||||
|
local.properties
|
1
app/.gitignore
vendored
Normal file
1
app/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/build
|
122
app/build.gradle
Normal file
122
app/build.gradle
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
plugins {
|
||||||
|
id 'com.android.application'
|
||||||
|
}
|
||||||
|
def flavor
|
||||||
|
android {
|
||||||
|
compileSdk 31
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdk 21
|
||||||
|
targetSdk 31
|
||||||
|
versionCode 1
|
||||||
|
versionName "1.0"
|
||||||
|
|
||||||
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
}
|
||||||
|
flavorDimensions "default"
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
productFlavors {
|
||||||
|
fdroid {
|
||||||
|
applicationId "fr.gouv.etalab.mastodon"
|
||||||
|
buildConfigField "boolean", "DONATIONS", "true"
|
||||||
|
buildConfigField "boolean", "push", "false"
|
||||||
|
flavor = "fdroid"
|
||||||
|
}
|
||||||
|
playstore {
|
||||||
|
applicationId "app.fedilab.android"
|
||||||
|
buildConfigField "boolean", "DONATIONS", "false"
|
||||||
|
buildConfigField "boolean", "push", "true"
|
||||||
|
flavor = "playstore"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lintOptions {
|
||||||
|
checkReleaseBuilds false
|
||||||
|
abortOnError false
|
||||||
|
}
|
||||||
|
buildFeatures {
|
||||||
|
viewBinding true
|
||||||
|
}
|
||||||
|
sourceSets {
|
||||||
|
playstore {
|
||||||
|
manifest.srcFile "src/playstore/AndroidManifest.xml"
|
||||||
|
java.srcDirs = ['src/main/java', 'src/playstore/java']
|
||||||
|
res.srcDirs = ['src/main/res', 'src/playstore/res']
|
||||||
|
}
|
||||||
|
fdroid {
|
||||||
|
java.srcDirs = ['src/main/java', 'src/fdroid/java']
|
||||||
|
res.srcDirs = ['src/main/res', 'src/fdroid/res']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
configurations {
|
||||||
|
all {
|
||||||
|
exclude group: 'androidx.lifecycle', module: 'lifecycle-viewmodel-ktx'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
|
maven { url "https://jitpack.io" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
implementation project(':autoimageslider')
|
||||||
|
implementation 'androidx.appcompat:appcompat:1.4.1'
|
||||||
|
implementation 'com.google.android.material:material:1.5.0'
|
||||||
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
|
||||||
|
implementation "com.google.code.gson:gson:2.8.6"
|
||||||
|
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
||||||
|
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
|
||||||
|
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
||||||
|
implementation 'androidx.preference:preference:1.2.0'
|
||||||
|
implementation 'com.github.evozi:Cyanea:1.0.7'
|
||||||
|
implementation 'com.vanniktech:emoji-one:0.6.0'
|
||||||
|
implementation 'com.github.GrenderG:Toasty:1.5.2'
|
||||||
|
implementation 'org.framagit.tom79:SparkButton:1.0.13'
|
||||||
|
implementation "com.github.bumptech.glide:glide:4.12.0"
|
||||||
|
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.
|
||||||
|
transitive = false
|
||||||
|
}
|
||||||
|
implementation project(path: ':mytransl')
|
||||||
|
implementation project(path: ':ratethisapp')
|
||||||
|
annotationProcessor "com.github.bumptech.glide:compiler:4.12.0"
|
||||||
|
implementation 'jp.wasabeef:glide-transformations:4.3.0'
|
||||||
|
implementation 'com.github.penfeizhou.android.animation:apng:2.17.0'
|
||||||
|
implementation 'com.github.penfeizhou.android.animation:gif:2.17.0'
|
||||||
|
implementation 'com.google.android.exoplayer:exoplayer:2.16.1'
|
||||||
|
implementation 'com.github.piasy:rxandroidaudio:1.7.0'
|
||||||
|
implementation 'com.github.piasy:AudioProcessor:1.7.0'
|
||||||
|
implementation "androidx.work:work-runtime:2.7.1"
|
||||||
|
implementation 'app.futured.hauler:hauler:5.0.0'
|
||||||
|
implementation "com.github.chrisbanes:PhotoView:2.3.0"
|
||||||
|
implementation "ch.acra:acra-mail:5.5.0"
|
||||||
|
implementation "ch.acra:acra-limiter:5.5.0"
|
||||||
|
implementation "ch.acra:acra-notification:5.5.0"
|
||||||
|
|
||||||
|
implementation "com.madgag.spongycastle:bctls-jdk15on:1.58.0.0"
|
||||||
|
implementation 'com.github.UnifiedPush:android-connector:2.0.0'
|
||||||
|
// implementation 'com.github.UnifiedPush:android-foss_embedded_fcm_distributor:1.0.0-beta1'
|
||||||
|
playstoreImplementation 'com.github.UnifiedPush:android-embedded_fcm_distributor:1.1.0'
|
||||||
|
implementation 'com.burhanrashid52:photoeditor:1.5.1'
|
||||||
|
implementation 'androidx.multidex:multidex:2.0.1'
|
||||||
|
implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
|
||||||
|
implementation 'androidx.lifecycle:lifecycle-livedata:2.4.1'
|
||||||
|
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.4.1'
|
||||||
|
implementation 'androidx.navigation:navigation-fragment:2.4.1'
|
||||||
|
implementation 'androidx.navigation:navigation-ui:2.4.1'
|
||||||
|
testImplementation 'junit:junit:'
|
||||||
|
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||||
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||||
|
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1'
|
||||||
|
}
|
21
app/proguard-rules.pro
vendored
Normal file
21
app/proguard-rules.pro
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
|
@ -0,0 +1,26 @@
|
||||||
|
package app.fedilab.android;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry;
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instrumented test, which will execute on an Android device.
|
||||||
|
*
|
||||||
|
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class ExampleInstrumentedTest {
|
||||||
|
@Test
|
||||||
|
public void useAppContext() {
|
||||||
|
// Context of the app under test.
|
||||||
|
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||||
|
assertEquals("app.fedilab.android", appContext.getPackageName());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/* 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>. */
|
||||||
|
package app.fedilab.android.activities;
|
||||||
|
|
||||||
|
import app.fedilab.android.BaseMainActivity;
|
||||||
|
|
||||||
|
public class MainActivity extends BaseMainActivity {
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void rateThisApp() {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
11
app/src/fdroid/res/xml/file_paths.xml
Normal file
11
app/src/fdroid/res/xml/file_paths.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<paths>
|
||||||
|
<external-path
|
||||||
|
name="my_images"
|
||||||
|
path="Android/data/fr.gouv.etalab.mastodon/files/Pictures" />
|
||||||
|
|
||||||
|
<cache-path
|
||||||
|
name="*"
|
||||||
|
path="." />
|
||||||
|
|
||||||
|
</paths>
|
187
app/src/main/AndroidManifest.xml
Normal file
187
app/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
package="app.fedilab.android">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission
|
||||||
|
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||||
|
tools:ignore="ScopedStorage" />
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:name=".MainApplication"
|
||||||
|
tools:replace="android:allowBackup"
|
||||||
|
android:allowBackup="false"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
|
android:supportsRtl="true"
|
||||||
|
android:theme="@style/AppTheme"
|
||||||
|
>
|
||||||
|
<activity
|
||||||
|
android:name=".activities.MainActivity"
|
||||||
|
android:exported="true"
|
||||||
|
>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:exported="true"
|
||||||
|
android:name=".activities.LoginActivity"
|
||||||
|
android:configChanges="orientation|screenSize"
|
||||||
|
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="backtofedilab"
|
||||||
|
android:scheme="fedilab" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<activity
|
||||||
|
android:name=".activities.WebviewConnectActivity"
|
||||||
|
android:configChanges="keyboardHidden|orientation|screenSize" />
|
||||||
|
<activity
|
||||||
|
android:name=".activities.ContextActivity"
|
||||||
|
android:configChanges="keyboardHidden|orientation|screenSize" />
|
||||||
|
<activity
|
||||||
|
android:name=".activities.DraftActivity"
|
||||||
|
android:configChanges="keyboardHidden|orientation|screenSize" />
|
||||||
|
<activity
|
||||||
|
android:name=".activities.ComposeActivity"
|
||||||
|
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||||
|
android:windowSoftInputMode="stateVisible"
|
||||||
|
android:label="@string/compose" />
|
||||||
|
<activity
|
||||||
|
android:name=".activities.StatusInfoActivity"
|
||||||
|
android:configChanges="keyboardHidden|orientation|screenSize" />
|
||||||
|
<activity
|
||||||
|
android:name=".activities.WebviewActivity"
|
||||||
|
android:configChanges="keyboardHidden|orientation|screenSize" />
|
||||||
|
<activity
|
||||||
|
android:name=".activities.ProfileActivity"
|
||||||
|
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||||
|
android:label="@string/account" />
|
||||||
|
<activity
|
||||||
|
android:name=".activities.ScheduledActivity"
|
||||||
|
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||||
|
android:label="@string/scheduled" />
|
||||||
|
|
||||||
|
<service
|
||||||
|
android:name=".services.PostMessageService"
|
||||||
|
android:label="@string/post_message" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".activities.SearchResultTabActivity"
|
||||||
|
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||||
|
android:theme="@style/AppThemeBar"
|
||||||
|
android:label="@string/search" />
|
||||||
|
<activity
|
||||||
|
android:name=".activities.ReorderTimelinesActivity"
|
||||||
|
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||||
|
android:label="@string/reorder_timelines"
|
||||||
|
android:theme="@style/AppThemeBar" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".activities.ActionActivity"
|
||||||
|
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||||
|
android:label="@string/interactions"
|
||||||
|
android:theme="@style/AppThemeBar" />
|
||||||
|
<activity
|
||||||
|
android:name=".activities.MastodonListActivity"
|
||||||
|
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||||
|
android:label="@string/action_lists"
|
||||||
|
android:theme="@style/AppThemeBar" />
|
||||||
|
<activity
|
||||||
|
android:name=".activities.SettingsActivity"
|
||||||
|
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||||
|
android:label="@string/settings"
|
||||||
|
android:theme="@style/AppThemeBar" />
|
||||||
|
<activity
|
||||||
|
android:name=".activities.InstanceActivity"
|
||||||
|
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||||
|
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.MediaActivity"
|
||||||
|
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||||
|
android:theme="@style/TransparentDark" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".activities.InstanceHealthActivity"
|
||||||
|
android:excludeFromRecents="true"
|
||||||
|
android:theme="@style/DialogDark" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".activities.ReportActivity"
|
||||||
|
android:theme="@style/AppThemeBarDark"
|
||||||
|
android:windowSoftInputMode="stateVisible" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".activities.CustomSharingActivity"
|
||||||
|
android:label="@string/settings_title_custom_sharing"
|
||||||
|
android:windowSoftInputMode="stateVisible"
|
||||||
|
android:theme="@style/AppThemeBarDark" />
|
||||||
|
<activity
|
||||||
|
android:name=".activities.FilterActivity"
|
||||||
|
android:label="@string/filters"
|
||||||
|
android:theme="@style/AppThemeBarDark"
|
||||||
|
android:windowSoftInputMode="stateVisible" />
|
||||||
|
<activity
|
||||||
|
android:name=".activities.EditProfileActivity"
|
||||||
|
android:label="@string/edit_profile"
|
||||||
|
android:theme="@style/AppThemeBarDark"
|
||||||
|
android:windowSoftInputMode="stateVisible" />
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:name="androidx.core.content.FileProvider"
|
||||||
|
android:authorities="${applicationId}.fileProvider"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/file_paths" />
|
||||||
|
</provider>
|
||||||
|
|
||||||
|
<receiver
|
||||||
|
android:name=".broadcastreceiver.ToastMessage"
|
||||||
|
android:exported="false">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="RECEIVE_TOAST_MESSAGE" />
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
|
||||||
|
<receiver
|
||||||
|
android:name=".services.CustomReceiver"
|
||||||
|
android:enabled="true"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="org.unifiedpush.android.connector.MESSAGE" />
|
||||||
|
<action android:name="org.unifiedpush.android.connector.UNREGISTERED" />
|
||||||
|
<action android:name="org.unifiedpush.android.connector.NEW_ENDPOINT" />
|
||||||
|
<action android:name="org.unifiedpush.android.connector.REGISTRATION_FAILED" />
|
||||||
|
<action android:name="org.unifiedpush.android.connector.REGISTRATION_REFUSED" />
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
</application>
|
||||||
|
</manifest>
|
|
@ -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
|
|
|
@ -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,
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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,
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
47
app/src/main/assets/themes/cyanea_themes.json
Normal file
47
app/src/main/assets/themes/cyanea_themes.json
Normal 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
|
||||||
|
}
|
||||||
|
]
|
BIN
app/src/main/ic_launcher-playstore.png
Normal file
BIN
app/src/main/ic_launcher-playstore.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
677
app/src/main/java/app/fedilab/android/BaseMainActivity.java
Normal file
677
app/src/main/java/app/fedilab/android/BaseMainActivity.java
Normal file
|
@ -0,0 +1,677 @@
|
||||||
|
package app.fedilab.android;
|
||||||
|
/* 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.status.DISCONNECTED;
|
||||||
|
import static app.fedilab.android.BaseMainActivity.status.UNKNOWN;
|
||||||
|
import static app.fedilab.android.helper.Helper.PREF_USER_TOKEN;
|
||||||
|
import static app.fedilab.android.helper.Helper.deleteDir;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.graphics.PorterDuff;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.view.ContextThemeWrapper;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.SubMenu;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.ActionBar;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.appcompat.widget.PopupMenu;
|
||||||
|
import androidx.appcompat.widget.SearchView;
|
||||||
|
import androidx.core.app.ActivityOptionsCompat;
|
||||||
|
import androidx.core.view.GravityCompat;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||||
|
import androidx.navigation.NavController;
|
||||||
|
import androidx.navigation.Navigation;
|
||||||
|
import androidx.navigation.ui.AppBarConfiguration;
|
||||||
|
import androidx.navigation.ui.NavigationUI;
|
||||||
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
|
import com.bumptech.glide.load.resource.gif.GifDrawable;
|
||||||
|
import com.bumptech.glide.request.target.CustomTarget;
|
||||||
|
import com.bumptech.glide.request.transition.Transition;
|
||||||
|
import com.jaredrummler.cyanea.Cyanea;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import app.fedilab.android.activities.ActionActivity;
|
||||||
|
import app.fedilab.android.activities.BaseActivity;
|
||||||
|
import app.fedilab.android.activities.ComposeActivity;
|
||||||
|
import app.fedilab.android.activities.DraftActivity;
|
||||||
|
import app.fedilab.android.activities.FilterActivity;
|
||||||
|
import app.fedilab.android.activities.InstanceActivity;
|
||||||
|
import app.fedilab.android.activities.InstanceHealthActivity;
|
||||||
|
import app.fedilab.android.activities.LoginActivity;
|
||||||
|
import app.fedilab.android.activities.MainActivity;
|
||||||
|
import app.fedilab.android.activities.MastodonListActivity;
|
||||||
|
import app.fedilab.android.activities.ProfileActivity;
|
||||||
|
import app.fedilab.android.activities.ProxyActivity;
|
||||||
|
import app.fedilab.android.activities.ReorderTimelinesActivity;
|
||||||
|
import app.fedilab.android.activities.ScheduledActivity;
|
||||||
|
import app.fedilab.android.activities.SearchResultTabActivity;
|
||||||
|
import app.fedilab.android.activities.SettingsActivity;
|
||||||
|
import app.fedilab.android.broadcastreceiver.NetworkStateReceiver;
|
||||||
|
import app.fedilab.android.client.entities.Account;
|
||||||
|
import app.fedilab.android.client.entities.Pinned;
|
||||||
|
import app.fedilab.android.client.entities.app.PinnedTimeline;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Filter;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Instance;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.MastodonList;
|
||||||
|
import app.fedilab.android.databinding.ActivityMainBinding;
|
||||||
|
import app.fedilab.android.databinding.NavHeaderMainBinding;
|
||||||
|
import app.fedilab.android.exception.DBException;
|
||||||
|
import app.fedilab.android.helper.Helper;
|
||||||
|
import app.fedilab.android.helper.PinnedTimelineHelper;
|
||||||
|
import app.fedilab.android.helper.PushHelper;
|
||||||
|
import app.fedilab.android.helper.ThemeHelper;
|
||||||
|
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTimeline;
|
||||||
|
import app.fedilab.android.viewmodel.mastodon.AccountsVM;
|
||||||
|
import app.fedilab.android.viewmodel.mastodon.InstancesVM;
|
||||||
|
import app.fedilab.android.viewmodel.mastodon.TimelinesVM;
|
||||||
|
import app.fedilab.android.viewmodel.mastodon.TopBarVM;
|
||||||
|
import es.dmoral.toasty.Toasty;
|
||||||
|
|
||||||
|
public abstract class BaseMainActivity extends BaseActivity implements NetworkStateReceiver.NetworkStateReceiverListener {
|
||||||
|
|
||||||
|
public static String currentInstance, currentToken, currentUserID, client_id, client_secret, software;
|
||||||
|
public static Account.API api;
|
||||||
|
public static boolean admin;
|
||||||
|
public static WeakReference<Account> accountWeakReference;
|
||||||
|
public static HashMap<Integer, Fragment> mPageReferenceMap;
|
||||||
|
public static status networkAvailable = UNKNOWN;
|
||||||
|
public static Instance instanceInfo;
|
||||||
|
public static List<Filter> mainFilters;
|
||||||
|
public static boolean filterFetched;
|
||||||
|
Fragment currentFragment;
|
||||||
|
private Account account;
|
||||||
|
private AppBarConfiguration mAppBarConfiguration;
|
||||||
|
private ActivityMainBinding binding;
|
||||||
|
private Pinned pinned;
|
||||||
|
|
||||||
|
private final BroadcastReceiver broadcast_data = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
Bundle b = intent.getExtras();
|
||||||
|
if (b != null) {
|
||||||
|
if (b.getBoolean(Helper.RECEIVE_REDRAW_TOPBAR, false)) {
|
||||||
|
List<MastodonList> mastodonLists = (List<MastodonList>) b.getSerializable(Helper.RECEIVE_MASTODON_LIST);
|
||||||
|
redrawPinned(mastodonLists);
|
||||||
|
} else if (b.getBoolean(Helper.RECEIVE_RECREATE_ACTIVITY, false)) {
|
||||||
|
Cyanea.getInstance().edit().apply().recreate(BaseMainActivity.this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private NetworkStateReceiver networkStateReceiver;
|
||||||
|
private boolean headerMenuOpen;
|
||||||
|
|
||||||
|
protected abstract void rateThisApp();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onNewIntent(Intent intent) {
|
||||||
|
super.onNewIntent(intent);
|
||||||
|
mamageNewIntent(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ApplySharedPref")
|
||||||
|
private void mamageNewIntent(Intent intent) {
|
||||||
|
if (intent == null)
|
||||||
|
return;
|
||||||
|
String action = intent.getAction();
|
||||||
|
String type = intent.getType();
|
||||||
|
Bundle extras = intent.getExtras();
|
||||||
|
String userIdIntent, instanceIntent;
|
||||||
|
if (extras != null && extras.containsKey(Helper.INTENT_ACTION)) {
|
||||||
|
userIdIntent = extras.getString(Helper.PREF_KEY_ID); //Id of the account in the intent
|
||||||
|
instanceIntent = extras.getString(Helper.PREF_INSTANCE);
|
||||||
|
if (extras.getInt(Helper.INTENT_ACTION) == Helper.NOTIFICATION_INTENT) {
|
||||||
|
try {
|
||||||
|
Account account = new Account(BaseMainActivity.this).getUniqAccount(userIdIntent, instanceIntent);
|
||||||
|
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(BaseMainActivity.this);
|
||||||
|
headerMenuOpen = false;
|
||||||
|
Toasty.info(BaseMainActivity.this, getString(R.string.toast_account_changed, "@" + account.mastodon_account.acct + "@" + account.instance), Toasty.LENGTH_LONG).show();
|
||||||
|
BaseMainActivity.currentToken = account.token;
|
||||||
|
BaseMainActivity.currentUserID = account.user_id;
|
||||||
|
api = account.api;
|
||||||
|
SharedPreferences.Editor editor = sharedpreferences.edit();
|
||||||
|
editor.putString(PREF_USER_TOKEN, account.token);
|
||||||
|
editor.commit();
|
||||||
|
Intent mainActivity = new Intent(this, MainActivity.class);
|
||||||
|
startActivity(mainActivity);
|
||||||
|
intent.removeExtra(Helper.INTENT_ACTION);
|
||||||
|
finish();
|
||||||
|
} catch (DBException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(BaseMainActivity.this);
|
||||||
|
ThemeHelper.applyTheme(this);
|
||||||
|
if (!Helper.isLoggedIn(BaseMainActivity.this)) {
|
||||||
|
//It is not, the user is redirected to the login page
|
||||||
|
Intent myIntent = new Intent(BaseMainActivity.this, LoginActivity.class);
|
||||||
|
startActivity(myIntent);
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
BaseMainActivity.currentToken = sharedpreferences.getString(Helper.PREF_USER_TOKEN, null);
|
||||||
|
}
|
||||||
|
mamageNewIntent(getIntent());
|
||||||
|
ThemeHelper.initiliazeColors(BaseMainActivity.this);
|
||||||
|
filterFetched = false;
|
||||||
|
networkStateReceiver = new NetworkStateReceiver();
|
||||||
|
networkStateReceiver.addListener(this);
|
||||||
|
binding = ActivityMainBinding.inflate(getLayoutInflater());
|
||||||
|
setContentView(binding.getRoot());
|
||||||
|
setSupportActionBar(binding.toolbar);
|
||||||
|
ActionBar actionBar = getSupportActionBar();
|
||||||
|
//Remove title
|
||||||
|
if (actionBar != null) {
|
||||||
|
actionBar.setDisplayShowTitleEnabled(false);
|
||||||
|
}
|
||||||
|
rateThisApp();
|
||||||
|
SharedPreferences cyneaPref = getSharedPreferences("com.jaredrummler.cyanea", Context.MODE_PRIVATE);
|
||||||
|
binding.tabLayout.setTabTextColors(ThemeHelper.getAttColor(BaseMainActivity.this, R.attr.mTextColor), cyneaPref.getInt("theme_accent", -1));
|
||||||
|
binding.tabLayout.setTabIconTint(ThemeHelper.getColorStateList(BaseMainActivity.this));
|
||||||
|
binding.compose.setOnClickListener(v -> startActivity(new Intent(this, ComposeActivity.class)));
|
||||||
|
headerMenuOpen = false;
|
||||||
|
binding.bottomNavView.inflateMenu(R.menu.bottom_nav_menu);
|
||||||
|
binding.bottomNavView.setOnItemSelectedListener(item -> {
|
||||||
|
int itemId = item.getItemId();
|
||||||
|
if (itemId == R.id.nav_home) {
|
||||||
|
binding.viewPager.setCurrentItem(0);
|
||||||
|
} else if (itemId == R.id.nav_local) {
|
||||||
|
binding.viewPager.setCurrentItem(1);
|
||||||
|
} else if (itemId == R.id.nav_public) {
|
||||||
|
binding.viewPager.setCurrentItem(2);
|
||||||
|
} else if (itemId == R.id.nav_notifications) {
|
||||||
|
binding.viewPager.setCurrentItem(3);
|
||||||
|
} else if (itemId == R.id.nav_privates) {
|
||||||
|
binding.viewPager.setCurrentItem(4);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Passing each menu ID as a set of Ids because each
|
||||||
|
// menu should be considered as top level destinations.
|
||||||
|
mAppBarConfiguration = new AppBarConfiguration.Builder()
|
||||||
|
.setOpenableLayout(binding.drawerLayout)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
NavHeaderMainBinding headerMainBinding = NavHeaderMainBinding.inflate(getLayoutInflater());
|
||||||
|
binding.navView.addHeaderView(headerMainBinding.getRoot());
|
||||||
|
binding.navView.setNavigationItemSelectedListener(menuItem -> {
|
||||||
|
int id = menuItem.getItemId();
|
||||||
|
if (id == R.id.nav_drafts) {
|
||||||
|
Intent intent = new Intent(this, DraftActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
|
} else if (id == R.id.nav_reorder) {
|
||||||
|
Intent intent = new Intent(this, ReorderTimelinesActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
|
} else if (id == R.id.nav_interactions) {
|
||||||
|
Intent intent = new Intent(this, ActionActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
|
} else if (id == R.id.nav_filter) {
|
||||||
|
Intent intent = new Intent(this, FilterActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
|
} else if (id == R.id.nav_list) {
|
||||||
|
Intent intent = new Intent(this, MastodonListActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
|
} else if (id == R.id.nav_settings) {
|
||||||
|
Intent intent = new Intent(this, SettingsActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
|
} else if (id == R.id.nav_scheduled) {
|
||||||
|
Intent intent = new Intent(this, ScheduledActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
binding.drawerLayout.close();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
headerMainBinding.instanceInfoContainer.setOnClickListener(v -> startActivity(new Intent(BaseMainActivity.this, InstanceHealthActivity.class)));
|
||||||
|
headerMainBinding.accountProfilePicture.setOnClickListener(v -> {
|
||||||
|
Intent intent = new Intent(BaseMainActivity.this, ProfileActivity.class);
|
||||||
|
Bundle b = new Bundle();
|
||||||
|
b.putSerializable(Helper.ARG_ACCOUNT, account.mastodon_account);
|
||||||
|
intent.putExtras(b);
|
||||||
|
ActivityOptionsCompat options = ActivityOptionsCompat
|
||||||
|
.makeSceneTransitionAnimation(BaseMainActivity.this, headerMainBinding.instanceInfoContainer, getString(R.string.activity_porfile_pp));
|
||||||
|
startActivity(intent, options.toBundle());
|
||||||
|
});
|
||||||
|
headerMainBinding.changeAccount.setOnClickListener(v -> {
|
||||||
|
headerMenuOpen = !headerMenuOpen;
|
||||||
|
if (headerMenuOpen) {
|
||||||
|
headerMainBinding.ownerAccounts.setImageResource(R.drawable.ic_baseline_arrow_drop_up_24);
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
List<Account> accounts = new Account(BaseMainActivity.this).getAll();
|
||||||
|
Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||||
|
Runnable myRunnable = () -> {
|
||||||
|
binding.navView.getMenu().clear();
|
||||||
|
binding.navView.inflateMenu(R.menu.menu_accounts);
|
||||||
|
headerMenuOpen = true;
|
||||||
|
|
||||||
|
Menu mainMenu = binding.navView.getMenu();
|
||||||
|
SubMenu currentSubmenu = null;
|
||||||
|
String lastInstance = "";
|
||||||
|
if (accounts != null) {
|
||||||
|
for (final Account account : accounts) {
|
||||||
|
if (!currentToken.equalsIgnoreCase(account.token)) {
|
||||||
|
if (!lastInstance.trim().equalsIgnoreCase(account.instance.trim())) {
|
||||||
|
lastInstance = account.instance.toUpperCase();
|
||||||
|
currentSubmenu = mainMenu.addSubMenu(account.instance.toUpperCase());
|
||||||
|
}
|
||||||
|
if (currentSubmenu == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final MenuItem item = currentSubmenu.add("@" + account.mastodon_account.acct);
|
||||||
|
item.setIcon(R.drawable.ic_person);
|
||||||
|
boolean disableGif = sharedpreferences.getBoolean(getString(R.string.SET_DISABLE_GIF), false);
|
||||||
|
String url = !disableGif ? account.mastodon_account.avatar : account.mastodon_account.avatar_static;
|
||||||
|
if (url.startsWith("/")) {
|
||||||
|
url = "https://" + account.instance + account.mastodon_account.avatar;
|
||||||
|
}
|
||||||
|
if (!this.isDestroyed() && !this.isFinishing()) {
|
||||||
|
if (url.contains(".gif")) {
|
||||||
|
Glide.with(BaseMainActivity.this)
|
||||||
|
.asGif()
|
||||||
|
.load(url)
|
||||||
|
.into(new CustomTarget<GifDrawable>() {
|
||||||
|
@Override
|
||||||
|
public void onResourceReady(@NonNull GifDrawable resource, Transition<? super GifDrawable> transition) {
|
||||||
|
item.setIcon(resource);
|
||||||
|
item.getIcon().setColorFilter(0xFFFFFFFF, PorterDuff.Mode.MULTIPLY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoadCleared(@Nullable Drawable placeholder) {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Glide.with(BaseMainActivity.this)
|
||||||
|
.asDrawable()
|
||||||
|
.load(url)
|
||||||
|
.into(new CustomTarget<Drawable>() {
|
||||||
|
@Override
|
||||||
|
public void onResourceReady(@NonNull Drawable resource, Transition<? super Drawable> transition) {
|
||||||
|
item.setIcon(resource);
|
||||||
|
item.getIcon().setColorFilter(0xFFFFFFFF, PorterDuff.Mode.MULTIPLY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoadCleared(@Nullable Drawable placeholder) {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
item.setOnMenuItemClickListener(item1 -> {
|
||||||
|
if (!this.isFinishing()) {
|
||||||
|
headerMenuOpen = false;
|
||||||
|
Toasty.info(BaseMainActivity.this, getString(R.string.toast_account_changed, "@" + account.mastodon_account.acct + "@" + account.instance), Toasty.LENGTH_LONG).show();
|
||||||
|
BaseMainActivity.currentToken = account.token;
|
||||||
|
BaseMainActivity.currentUserID = account.user_id;
|
||||||
|
api = account.api;
|
||||||
|
SharedPreferences.Editor editor = sharedpreferences.edit();
|
||||||
|
editor.putString(PREF_USER_TOKEN, account.token);
|
||||||
|
editor.commit();
|
||||||
|
//The user is now aut
|
||||||
|
//The user is now authenticated, it will be redirected to MainActivity
|
||||||
|
Intent mainActivity = new Intent(this, MainActivity.class);
|
||||||
|
startActivity(mainActivity);
|
||||||
|
finish();
|
||||||
|
headerMainBinding.ownerAccounts.setImageResource(R.drawable.ic_baseline_arrow_drop_down_24);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
currentSubmenu = mainMenu.addSubMenu("");
|
||||||
|
MenuItem addItem = currentSubmenu.add(R.string.add_account);
|
||||||
|
addItem.setIcon(R.drawable.ic_baseline_person_add_24);
|
||||||
|
addItem.setOnMenuItemClickListener(item -> {
|
||||||
|
Intent intent = new Intent(BaseMainActivity.this, LoginActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
mainHandler.post(myRunnable);
|
||||||
|
} catch (DBException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
} else {
|
||||||
|
binding.navView.getMenu().clear();
|
||||||
|
binding.navView.inflateMenu(R.menu.activity_main_drawer);
|
||||||
|
headerMainBinding.ownerAccounts.setImageResource(R.drawable.ic_baseline_arrow_drop_down_24);
|
||||||
|
headerMenuOpen = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
headerMainBinding.headerOptionInfo.setOnClickListener(v -> {
|
||||||
|
PopupMenu popup = new PopupMenu(new ContextThemeWrapper(BaseMainActivity.this, Helper.popupStyle()), headerMainBinding.headerOptionInfo);
|
||||||
|
popup.getMenuInflater()
|
||||||
|
.inflate(R.menu.main, popup.getMenu());
|
||||||
|
|
||||||
|
popup.setOnMenuItemClickListener(item -> {
|
||||||
|
int itemId = item.getItemId();
|
||||||
|
if (itemId == R.id.action_logout_account) {
|
||||||
|
AlertDialog.Builder alt_bld = new AlertDialog.Builder(BaseMainActivity.this, Helper.dialogStyle());
|
||||||
|
alt_bld.setTitle(R.string.action_logout);
|
||||||
|
alt_bld.setMessage(getString(R.string.logout_account_confirmation, account.mastodon_account.username, account.instance));
|
||||||
|
alt_bld.setPositiveButton(R.string.action_logout, (dialog, id) -> {
|
||||||
|
dialog.dismiss();
|
||||||
|
try {
|
||||||
|
Helper.removeAccount(BaseMainActivity.this, null);
|
||||||
|
} catch (DBException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
alt_bld.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss());
|
||||||
|
AlertDialog alert = alt_bld.create();
|
||||||
|
alert.show();
|
||||||
|
return true;
|
||||||
|
} else if (itemId == R.id.action_about_instance) {
|
||||||
|
Intent intent = new Intent(BaseMainActivity.this, InstanceActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
|
return true;
|
||||||
|
} else if (itemId == R.id.action_cache) {
|
||||||
|
new Helper.CacheTask(BaseMainActivity.this);
|
||||||
|
return true;
|
||||||
|
} else if (itemId == R.id.action_proxy) {
|
||||||
|
Intent intent = new Intent(BaseMainActivity.this, ProxyActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
popup.show();
|
||||||
|
});
|
||||||
|
account = null;
|
||||||
|
//Update account details
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
account = new Account(BaseMainActivity.this).getConnectedAccount();
|
||||||
|
} catch (DBException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||||
|
Runnable myRunnable = () -> {
|
||||||
|
if (account == null) {
|
||||||
|
//It is not, the user is redirected to the login page
|
||||||
|
Intent myIntent = new Intent(BaseMainActivity.this, LoginActivity.class);
|
||||||
|
startActivity(myIntent);
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
currentInstance = account.instance;
|
||||||
|
currentUserID = account.user_id;
|
||||||
|
accountWeakReference = new WeakReference<>(account);
|
||||||
|
binding.profilePicture.setOnClickListener(v -> binding.drawerLayout.openDrawer(GravityCompat.START));
|
||||||
|
Helper.loadPP(binding.profilePicture, account);
|
||||||
|
headerMainBinding.accountAcc.setText(String.format("%s@%s", account.mastodon_account.username, account.instance));
|
||||||
|
if (account.mastodon_account.display_name.isEmpty()) {
|
||||||
|
account.mastodon_account.display_name = account.mastodon_account.acct;
|
||||||
|
}
|
||||||
|
headerMainBinding.accountName.setText(account.mastodon_account.display_name);
|
||||||
|
|
||||||
|
Helper.loadPP(headerMainBinding.accountProfilePicture, account);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some general data are loaded when the app starts such;
|
||||||
|
* - Instance info (for limits)
|
||||||
|
* - Emoji for picker
|
||||||
|
* - Filters for timelines
|
||||||
|
* - Pinned timelines (in app feature)
|
||||||
|
*/
|
||||||
|
|
||||||
|
//Update emoji in db for the current instance
|
||||||
|
new ViewModelProvider(BaseMainActivity.this).get(InstancesVM.class).getEmoji(currentInstance);
|
||||||
|
//Retrieve instance info
|
||||||
|
new ViewModelProvider(BaseMainActivity.this).get(InstancesVM.class).getInstance(currentInstance)
|
||||||
|
.observe(BaseMainActivity.this, instance -> instanceInfo = instance.info);
|
||||||
|
//Retrieve filters
|
||||||
|
new ViewModelProvider(BaseMainActivity.this).get(AccountsVM.class).getFilters(currentInstance, currentToken)
|
||||||
|
.observe(BaseMainActivity.this, filters -> mainFilters = filters);
|
||||||
|
new ViewModelProvider(BaseMainActivity.this).get(AccountsVM.class).getConnectedAccount(currentInstance, currentToken)
|
||||||
|
.observe(BaseMainActivity.this, account1 -> {
|
||||||
|
BaseMainActivity.accountWeakReference.get().mastodon_account = account1;
|
||||||
|
});
|
||||||
|
//Update pinned timelines
|
||||||
|
new ViewModelProvider(BaseMainActivity.this).get(TopBarVM.class).getDBPinned()
|
||||||
|
.observe(this, pinned -> {
|
||||||
|
this.pinned = pinned;
|
||||||
|
//First it's taken from db (last stored values)
|
||||||
|
PinnedTimelineHelper.redrawTopBarPinned(BaseMainActivity.this, binding, pinned, null);
|
||||||
|
//Fetch remote lists for the authenticated account and update them
|
||||||
|
new ViewModelProvider(BaseMainActivity.this).get(TimelinesVM.class).getLists(currentInstance, currentToken)
|
||||||
|
.observe(this, mastodonLists -> PinnedTimelineHelper.redrawTopBarPinned(BaseMainActivity.this, binding, pinned, mastodonLists));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
mainHandler.post(myRunnable);
|
||||||
|
}).start();
|
||||||
|
//Toolbar search
|
||||||
|
binding.toolbarSearch.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onQueryTextSubmit(String query) {
|
||||||
|
//Hide keyboard
|
||||||
|
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
|
||||||
|
imm.hideSoftInputFromWindow(binding.toolbarSearch.getWindowToken(), 0);
|
||||||
|
query = query.replaceAll("^#+", "");
|
||||||
|
Intent intent;
|
||||||
|
intent = new Intent(BaseMainActivity.this, SearchResultTabActivity.class);
|
||||||
|
intent.putExtra(Helper.ARG_SEARCH_KEYWORD, query);
|
||||||
|
startActivity(intent);
|
||||||
|
binding.toolbarSearch.setQuery("", false);
|
||||||
|
binding.toolbarSearch.setIconified(true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onQueryTextChange(String newText) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
binding.toolbarSearch.setOnCloseListener(() -> {
|
||||||
|
binding.tabLayout.setVisibility(View.VISIBLE);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
PushHelper.startStreaming(BaseMainActivity.this);
|
||||||
|
|
||||||
|
binding.toolbarSearch.setOnSearchClickListener(v -> binding.tabLayout.setVisibility(View.VISIBLE));
|
||||||
|
//For receiving data from other activities
|
||||||
|
LocalBroadcastManager.getInstance(BaseMainActivity.this).registerReceiver(broadcast_data, new IntentFilter(Helper.BROADCAST_DATA));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refreshFragment() {
|
||||||
|
if (binding.viewPager.getAdapter() != null) {
|
||||||
|
binding.viewPager.getAdapter().notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
LocalBroadcastManager.getInstance(BaseMainActivity.this).unregisterReceiver(broadcast_data);
|
||||||
|
if (networkStateReceiver != null) {
|
||||||
|
try {
|
||||||
|
unregisterReceiver(networkStateReceiver);
|
||||||
|
} catch (IllegalArgumentException illegalArgumentException) {
|
||||||
|
illegalArgumentException.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(BaseMainActivity.this);
|
||||||
|
boolean clearCacheExit = sharedpreferences.getBoolean(getString(R.string.SET_CLEAR_CACHE_EXIT), false);
|
||||||
|
//Clear cache when leaving - Default = false
|
||||||
|
if (clearCacheExit) {
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
if (getCacheDir().getParentFile() != null) {
|
||||||
|
String path = getCacheDir().getParentFile().getPath();
|
||||||
|
File dir = new File(path);
|
||||||
|
if (dir.isDirectory()) {
|
||||||
|
deleteDir(dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void redrawPinned(List<MastodonList> mastodonLists) {
|
||||||
|
int currentItem = binding.viewPager.getCurrentItem();
|
||||||
|
new ViewModelProvider(BaseMainActivity.this).get(TopBarVM.class).getDBPinned()
|
||||||
|
.observe(this, pinned -> {
|
||||||
|
this.pinned = pinned;
|
||||||
|
//First it's taken from db (last stored values)
|
||||||
|
PinnedTimelineHelper.redrawTopBarPinned(BaseMainActivity.this, binding, pinned, mastodonLists);
|
||||||
|
binding.viewPager.setCurrentItem(currentItem);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBackPressed() {
|
||||||
|
if (binding.drawerLayout.isDrawerOpen(GravityCompat.START)) {
|
||||||
|
binding.drawerLayout.closeDrawer(GravityCompat.START);
|
||||||
|
} else {
|
||||||
|
int fragments = getSupportFragmentManager().getBackStackEntryCount();
|
||||||
|
if (fragments == 1) {
|
||||||
|
finish();
|
||||||
|
} else if (getSupportFragmentManager().getBackStackEntryCount() > 1) {
|
||||||
|
getSupportFragmentManager().popBackStack();
|
||||||
|
List<Fragment> fragmentList = getSupportFragmentManager().getFragments();
|
||||||
|
for (Fragment fragment : fragmentList) {
|
||||||
|
if (fragment != null && fragment.isVisible()) {
|
||||||
|
|
||||||
|
if (fragment instanceof FragmentMastodonTimeline) {
|
||||||
|
currentFragment = fragment;
|
||||||
|
getSupportFragmentManager().beginTransaction().show(currentFragment).commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
super.onBackPressed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getFloatingVisibility() {
|
||||||
|
return binding.compose.getVisibility() == View.VISIBLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void manageFloatingButton(boolean display) {
|
||||||
|
if (display) {
|
||||||
|
binding.compose.show();
|
||||||
|
} else {
|
||||||
|
binding.compose.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @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.main, menu);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (item.getItemId() == R.id.action_logout) {
|
||||||
|
AlertDialog.Builder alt_bld = new AlertDialog.Builder(BaseMainActivity.this, Helper.dialogStyle());
|
||||||
|
alt_bld.setTitle(R.string.action_logout);
|
||||||
|
alt_bld.setMessage(getString(R.string.logout_account_confirmation, account.mastodon_account.username, account.instance));
|
||||||
|
alt_bld.setPositiveButton(R.string.action_logout, (dialog, id) -> {
|
||||||
|
dialog.dismiss();
|
||||||
|
try {
|
||||||
|
Helper.removeAccount(BaseMainActivity.this, null);
|
||||||
|
} catch (DBException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
alt_bld.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss());
|
||||||
|
AlertDialog alert = alt_bld.create();
|
||||||
|
alert.show();
|
||||||
|
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onSupportNavigateUp() {
|
||||||
|
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
|
||||||
|
//unselect all tag elements
|
||||||
|
for (PinnedTimeline pinnedTimeline : pinned.pinnedTimelines) {
|
||||||
|
pinnedTimeline.isSelected = false;
|
||||||
|
}
|
||||||
|
return NavigationUI.navigateUp(navController, mAppBarConfiguration)
|
||||||
|
|| super.onSupportNavigateUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void networkAvailable() {
|
||||||
|
networkAvailable = status.CONNECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void networkUnavailable() {
|
||||||
|
networkAvailable = DISCONNECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public enum status {
|
||||||
|
UNKNOWN,
|
||||||
|
CONNECTED,
|
||||||
|
DISCONNECTED
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package app.fedilab.android;
|
||||||
|
/* 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 app.fedilab.android.client.entities.InstanceSocial;
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.http.GET;
|
||||||
|
import retrofit2.http.Header;
|
||||||
|
import retrofit2.http.Query;
|
||||||
|
|
||||||
|
public interface InstancesSocialService {
|
||||||
|
|
||||||
|
@GET("instances/search?name=true")
|
||||||
|
Call<InstanceSocial> getInstances(@Header("Authorization") String token, @Query("q") String search);
|
||||||
|
|
||||||
|
}
|
90
app/src/main/java/app/fedilab/android/MainApplication.java
Normal file
90
app/src/main/java/app/fedilab/android/MainApplication.java
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
package app.fedilab.android;
|
||||||
|
/* 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 android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.StrictMode;
|
||||||
|
|
||||||
|
import androidx.multidex.MultiDex;
|
||||||
|
import androidx.multidex.MultiDexApplication;
|
||||||
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import com.jaredrummler.cyanea.Cyanea;
|
||||||
|
import com.jaredrummler.cyanea.prefs.CyaneaTheme;
|
||||||
|
|
||||||
|
import org.acra.ACRA;
|
||||||
|
import org.acra.annotation.AcraNotification;
|
||||||
|
import org.acra.config.CoreConfigurationBuilder;
|
||||||
|
import org.acra.config.LimiterConfigurationBuilder;
|
||||||
|
import org.acra.config.MailSenderConfigurationBuilder;
|
||||||
|
import org.acra.data.StringFormat;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import es.dmoral.toasty.Toasty;
|
||||||
|
|
||||||
|
|
||||||
|
@AcraNotification(
|
||||||
|
resIcon = R.mipmap.ic_launcher, resTitle = R.string.crash_title, resChannelName = R.string.set_crash_reports, resText = R.string.crash_message)
|
||||||
|
|
||||||
|
public class MainApplication extends MultiDexApplication {
|
||||||
|
|
||||||
|
|
||||||
|
private static MainApplication app;
|
||||||
|
|
||||||
|
public static MainApplication getApp() {
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
app = this;
|
||||||
|
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(MainApplication.this);
|
||||||
|
|
||||||
|
|
||||||
|
Cyanea.init(this, super.getResources());
|
||||||
|
List<CyaneaTheme> list = CyaneaTheme.Companion.from(getAssets(), "themes/cyanea_themes.json");
|
||||||
|
int theme = sharedpreferences.getInt(getString(R.string.SET_THEME), 0);
|
||||||
|
boolean custom_theme = sharedpreferences.getBoolean("use_custom_theme", false);
|
||||||
|
if (!custom_theme) {
|
||||||
|
list.get(theme).apply(Cyanea.getInstance());
|
||||||
|
}
|
||||||
|
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
|
||||||
|
StrictMode.setVmPolicy(builder.build());
|
||||||
|
|
||||||
|
|
||||||
|
boolean send_crash_reports = sharedpreferences.getBoolean(getString(R.string.SET_SEND_CRASH_REPORTS), false);
|
||||||
|
if (send_crash_reports) {
|
||||||
|
CoreConfigurationBuilder ACRABuilder = new CoreConfigurationBuilder(this);
|
||||||
|
ACRABuilder.setBuildConfigClass(BuildConfig.class).setReportFormat(StringFormat.KEY_VALUE_LIST);
|
||||||
|
int versionCode = BuildConfig.VERSION_CODE;
|
||||||
|
ACRABuilder.getPluginConfigurationBuilder(MailSenderConfigurationBuilder.class).setReportAsFile(true).setMailTo("hello@fedilab.app").setSubject("[Fedilab] - Crash Report " + versionCode).setEnabled(true);
|
||||||
|
ACRABuilder.getPluginConfigurationBuilder(LimiterConfigurationBuilder.class).setEnabled(true);
|
||||||
|
ACRA.init(this, ACRABuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
Toasty.Config.getInstance().apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void attachBaseContext(Context base) {
|
||||||
|
super.attachBaseContext(base);
|
||||||
|
MultiDex.install(MainApplication.this);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,129 @@
|
||||||
|
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.os.Bundle;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
import androidx.fragment.app.FragmentTransaction;
|
||||||
|
|
||||||
|
import app.fedilab.android.R;
|
||||||
|
import app.fedilab.android.client.entities.Timeline;
|
||||||
|
import app.fedilab.android.databinding.ActivityActionsBinding;
|
||||||
|
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.FragmentMastodonTimeline;
|
||||||
|
|
||||||
|
public class ActionActivity extends BaseActivity {
|
||||||
|
|
||||||
|
private ActivityActionsBinding binding;
|
||||||
|
private boolean canGoBack;
|
||||||
|
private FragmentMastodonTimeline fragmentMastodonTimeline;
|
||||||
|
private FragmentMastodonAccount fragmentMastodonAccount;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
ThemeHelper.applyThemeBar(this);
|
||||||
|
binding = ActivityActionsBinding.inflate(getLayoutInflater());
|
||||||
|
setContentView(binding.getRoot());
|
||||||
|
|
||||||
|
if (getSupportActionBar() != null)
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
canGoBack = false;
|
||||||
|
binding.favourites.setOnClickListener(v -> displayTimeline(Timeline.TimeLineEnum.FAVOURITE_TIMELINE));
|
||||||
|
binding.bookmarks.setOnClickListener(v -> displayTimeline(Timeline.TimeLineEnum.BOOKMARK_TIMELINE));
|
||||||
|
binding.muted.setOnClickListener(v -> displayTimeline(Timeline.TimeLineEnum.MUTED_TIMELINE));
|
||||||
|
binding.blocked.setOnClickListener(v -> displayTimeline(Timeline.TimeLineEnum.BLOCKED_TIMELINE));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void displayTimeline(Timeline.TimeLineEnum type) {
|
||||||
|
canGoBack = true;
|
||||||
|
if (type == Timeline.TimeLineEnum.MUTED_TIMELINE || type == Timeline.TimeLineEnum.BLOCKED_TIMELINE) {
|
||||||
|
|
||||||
|
ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> {
|
||||||
|
fragmentMastodonAccount = new FragmentMastodonAccount();
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, type);
|
||||||
|
fragmentMastodonAccount.setArguments(bundle);
|
||||||
|
FragmentManager fragmentManager = getSupportFragmentManager();
|
||||||
|
FragmentTransaction fragmentTransaction =
|
||||||
|
fragmentManager.beginTransaction();
|
||||||
|
fragmentTransaction.replace(R.id.fragment_container, fragmentMastodonAccount);
|
||||||
|
fragmentTransaction.commit();
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> {
|
||||||
|
fragmentMastodonTimeline = new FragmentMastodonTimeline();
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, type);
|
||||||
|
fragmentMastodonTimeline.setArguments(bundle);
|
||||||
|
FragmentManager fragmentManager = getSupportFragmentManager();
|
||||||
|
FragmentTransaction fragmentTransaction =
|
||||||
|
fragmentManager.beginTransaction();
|
||||||
|
fragmentTransaction.replace(R.id.fragment_container, fragmentMastodonTimeline);
|
||||||
|
fragmentTransaction.commit();
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
switch (type) {
|
||||||
|
case MUTED_TIMELINE:
|
||||||
|
setTitle(R.string.muted_menu);
|
||||||
|
break;
|
||||||
|
case FAVOURITE_TIMELINE:
|
||||||
|
setTitle(R.string.favourite);
|
||||||
|
break;
|
||||||
|
case BLOCKED_TIMELINE:
|
||||||
|
setTitle(R.string.blocked_menu);
|
||||||
|
break;
|
||||||
|
case BOOKMARK_TIMELINE:
|
||||||
|
setTitle(R.string.bookmarks);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBackPressed() {
|
||||||
|
if (canGoBack) {
|
||||||
|
canGoBack = false;
|
||||||
|
ThemeHelper.slideViewsToRight(binding.fragmentContainer, binding.buttonContainer, () -> {
|
||||||
|
if (fragmentMastodonTimeline != null) {
|
||||||
|
fragmentMastodonTimeline.onDestroyView();
|
||||||
|
}
|
||||||
|
if (fragmentMastodonAccount != null) {
|
||||||
|
fragmentMastodonAccount.onDestroyView();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setTitle(R.string.interactions);
|
||||||
|
} else {
|
||||||
|
super.onBackPressed();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (item.getItemId() == android.R.id.home) {
|
||||||
|
onBackPressed();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
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 android.annotation.SuppressLint;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.jaredrummler.cyanea.app.CyaneaAppCompatActivity;
|
||||||
|
import com.vanniktech.emoji.EmojiManager;
|
||||||
|
import com.vanniktech.emoji.one.EmojiOneProvider;
|
||||||
|
|
||||||
|
import app.fedilab.android.helper.Helper;
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressLint("Registered")
|
||||||
|
public class BaseActivity extends CyaneaAppCompatActivity {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
Helper.setLocale(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static {
|
||||||
|
Helper.installProvider();
|
||||||
|
EmojiManager.install(new EmojiOneProvider());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
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.annotation.SuppressLint;
|
||||||
|
|
||||||
|
import com.jaredrummler.cyanea.app.CyaneaFragmentActivity;
|
||||||
|
import com.vanniktech.emoji.EmojiManager;
|
||||||
|
import com.vanniktech.emoji.one.EmojiOneProvider;
|
||||||
|
|
||||||
|
import app.fedilab.android.helper.Helper;
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressLint("Registered")
|
||||||
|
public class BaseFragmentActivity extends CyaneaFragmentActivity {
|
||||||
|
|
||||||
|
|
||||||
|
static {
|
||||||
|
Helper.installProvider();
|
||||||
|
EmojiManager.install(new EmojiOneProvider());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,599 @@
|
||||||
|
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.ui.drawer.ComposeAdapter.prepareDraft;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.ClipData;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.ActionBar;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.work.Data;
|
||||||
|
import androidx.work.OneTimeWorkRequest;
|
||||||
|
import androidx.work.WorkManager;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import app.fedilab.android.BaseMainActivity;
|
||||||
|
import app.fedilab.android.R;
|
||||||
|
import app.fedilab.android.client.entities.Account;
|
||||||
|
import app.fedilab.android.client.entities.StatusDraft;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Context;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Mention;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.ScheduledStatus;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Status;
|
||||||
|
import app.fedilab.android.databinding.ActivityPaginationBinding;
|
||||||
|
import app.fedilab.android.databinding.PopupContactBinding;
|
||||||
|
import app.fedilab.android.exception.DBException;
|
||||||
|
import app.fedilab.android.helper.DividerDecorationSimple;
|
||||||
|
import app.fedilab.android.helper.Helper;
|
||||||
|
import app.fedilab.android.helper.MastodonHelper;
|
||||||
|
import app.fedilab.android.helper.MediaHelper;
|
||||||
|
import app.fedilab.android.helper.SpannableHelper;
|
||||||
|
import app.fedilab.android.helper.ThemeHelper;
|
||||||
|
import app.fedilab.android.jobs.ScheduleThreadWorker;
|
||||||
|
import app.fedilab.android.services.PostMessageService;
|
||||||
|
import app.fedilab.android.ui.drawer.AccountsReplyAdapter;
|
||||||
|
import app.fedilab.android.ui.drawer.ComposeAdapter;
|
||||||
|
import app.fedilab.android.viewmodel.mastodon.AccountsVM;
|
||||||
|
import app.fedilab.android.viewmodel.mastodon.StatusesVM;
|
||||||
|
import es.dmoral.toasty.Toasty;
|
||||||
|
|
||||||
|
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 REQUEST_AUDIO_PERMISSION_RESULT = 1653;
|
||||||
|
public static final int PICK_MEDIA = 5700;
|
||||||
|
public static final int TAKE_PHOTO = 5600;
|
||||||
|
|
||||||
|
private List<Status> statusList;
|
||||||
|
private Status statusReply, statusMention;
|
||||||
|
private StatusDraft statusDraft;
|
||||||
|
private ComposeAdapter composeAdapter;
|
||||||
|
private ActivityPaginationBinding binding;
|
||||||
|
private Account account;
|
||||||
|
private String instance, token;
|
||||||
|
private Uri photoFileUri;
|
||||||
|
private ScheduledStatus scheduledStatus;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
ThemeHelper.applyTheme(this);
|
||||||
|
binding = ActivityPaginationBinding.inflate(getLayoutInflater());
|
||||||
|
setContentView(binding.getRoot());
|
||||||
|
setSupportActionBar(binding.toolbar);
|
||||||
|
ActionBar actionBar = getSupportActionBar();
|
||||||
|
//Remove title
|
||||||
|
if (actionBar != null) {
|
||||||
|
actionBar.setDisplayShowTitleEnabled(false);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
statusMention = (Status) b.getSerializable(Helper.ARG_STATUS_MENTION);
|
||||||
|
account = (Account) b.getSerializable(Helper.ARG_ACCOUNT);
|
||||||
|
instance = b.getString(Helper.ARG_INSTANCE, BaseMainActivity.currentInstance);
|
||||||
|
token = b.getString(Helper.ARG_TOKEN, BaseMainActivity.currentToken);
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
|
||||||
|
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 (instance == null) {
|
||||||
|
instance = BaseMainActivity.currentInstance;
|
||||||
|
}
|
||||||
|
if (token == null) {
|
||||||
|
token = BaseMainActivity.currentToken;
|
||||||
|
}
|
||||||
|
if (account == null) {
|
||||||
|
account = BaseMainActivity.accountWeakReference.get();
|
||||||
|
}
|
||||||
|
StatusesVM statusesVM = new ViewModelProvider(ComposeActivity.this).get(StatusesVM.class);
|
||||||
|
//Empty compose
|
||||||
|
List<Status> statusDraftList = new ArrayList<>();
|
||||||
|
Status status = new Status();
|
||||||
|
statusDraftList.add(status);
|
||||||
|
|
||||||
|
//Restore a draft with all messages
|
||||||
|
if (statusDraft != null && statusDraft.statusReplyList != null) {
|
||||||
|
new Thread(() -> {
|
||||||
|
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);
|
||||||
|
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;
|
||||||
|
statusDraftList.get(0).mentions = statusReply.mentions;
|
||||||
|
if (statusDraftList.get(0).mentions == null) {
|
||||||
|
statusDraftList.get(0).mentions = new ArrayList<>();
|
||||||
|
}
|
||||||
|
//We will add the mentioned account in mention if not the current user nor if it is already mentioned
|
||||||
|
if (statusReply.account != null && statusReply.account.acct != null && !statusReply.account.id.equals(BaseMainActivity.currentUserID)) {
|
||||||
|
boolean canBeAdded = true;
|
||||||
|
for (Mention mention : statusDraftList.get(0).mentions) {
|
||||||
|
if (mention.acct.compareToIgnoreCase(statusReply.account.acct) == 0) {
|
||||||
|
mention.id = null;
|
||||||
|
canBeAdded = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (canBeAdded) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//StatusDraftList at this point should only have one element
|
||||||
|
statusList.addAll(statusDraftList);
|
||||||
|
composeAdapter = new ComposeAdapter(statusList, statusCount, account);
|
||||||
|
composeAdapter.manageDrafts = this;
|
||||||
|
LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this);
|
||||||
|
binding.recyclerView.setLayoutManager(mLayoutManager);
|
||||||
|
binding.recyclerView.setAdapter(composeAdapter);
|
||||||
|
statusesVM.getContext(BaseMainActivity.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);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBackPressed() {
|
||||||
|
storeDraftWarning();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void storeDraftWarning() {
|
||||||
|
if (statusDraft == null) {
|
||||||
|
statusDraft = prepareDraft(statusList, composeAdapter, account.instance, account.user_id);
|
||||||
|
}
|
||||||
|
if (canBeSent(statusDraft)) {
|
||||||
|
AlertDialog.Builder alt_bld = new AlertDialog.Builder(ComposeActivity.this, Helper.dialogStyle());
|
||||||
|
alt_bld.setMessage(R.string.save_draft);
|
||||||
|
alt_bld.setPositiveButton(R.string.save, (dialog, id) -> {
|
||||||
|
dialog.dismiss();
|
||||||
|
storeDraft(false);
|
||||||
|
finish();
|
||||||
|
|
||||||
|
});
|
||||||
|
alt_bld.setNegativeButton(R.string.no, (dialog, id) -> {
|
||||||
|
dialog.dismiss();
|
||||||
|
finish();
|
||||||
|
});
|
||||||
|
AlertDialog alert = alt_bld.create();
|
||||||
|
alert.show();
|
||||||
|
} else {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Intialize the common view for the context
|
||||||
|
*
|
||||||
|
* @param context {@link Context}
|
||||||
|
*/
|
||||||
|
private void initializeContextView(final Context context) {
|
||||||
|
|
||||||
|
if (context == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//Build the array of statuses
|
||||||
|
statusList.addAll(0, context.ancestors);
|
||||||
|
composeAdapter.setStatusCount(context.ancestors.size() + 1);
|
||||||
|
composeAdapter.notifyItemRangeInserted(0, context.ancestors.size());
|
||||||
|
if (binding.recyclerView.getItemDecorationCount() > 0) {
|
||||||
|
for (int i = 0; i < binding.recyclerView.getItemDecorationCount(); i++) {
|
||||||
|
binding.recyclerView.removeItemDecorationAt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.recyclerView.addItemDecoration(new DividerDecorationSimple(ComposeActivity.this, statusList));
|
||||||
|
binding.recyclerView.scrollToPosition(composeAdapter.getItemCount() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@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_compose, menu);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (item.getItemId() == android.R.id.home) {
|
||||||
|
storeDraftWarning();
|
||||||
|
return true;
|
||||||
|
} else if (item.getItemId() == R.id.action_photo_camera) {
|
||||||
|
photoFileUri = MediaHelper.dispatchTakePictureIntent(ComposeActivity.this);
|
||||||
|
} else if (item.getItemId() == R.id.action_contacts) {
|
||||||
|
AlertDialog.Builder builderSingle = new AlertDialog.Builder(ComposeActivity.this, Helper.dialogStyle());
|
||||||
|
|
||||||
|
builderSingle.setTitle(getString(R.string.select_accounts));
|
||||||
|
PopupContactBinding popupContactBinding = PopupContactBinding.inflate(getLayoutInflater(), new LinearLayout(ComposeActivity.this), false);
|
||||||
|
popupContactBinding.loader.setVisibility(View.VISIBLE);
|
||||||
|
AccountsVM accountsVM = new ViewModelProvider(ComposeActivity.this).get(AccountsVM.class);
|
||||||
|
accountsVM.searchAccounts(instance, token, "", 10, false, true)
|
||||||
|
.observe(ComposeActivity.this, accounts -> onRetrieveContact(popupContactBinding, accounts));
|
||||||
|
|
||||||
|
popupContactBinding.searchAccount.addTextChangedListener(new TextWatcher() {
|
||||||
|
@Override
|
||||||
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
|
if (count > 0) {
|
||||||
|
popupContactBinding.searchAccount.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_baseline_close_24, 0);
|
||||||
|
} else {
|
||||||
|
popupContactBinding.searchAccount.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_baseline_search_24, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable s) {
|
||||||
|
if (s != null && s.length() > 0) {
|
||||||
|
accountsVM.searchAccounts(instance, token, s.toString().trim(), 10, false, true)
|
||||||
|
.observe(ComposeActivity.this, accounts -> onRetrieveContact(popupContactBinding, accounts));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
popupContactBinding.searchAccount.setOnTouchListener((v, event) -> {
|
||||||
|
final int DRAWABLE_RIGHT = 2;
|
||||||
|
if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||||
|
if (popupContactBinding.searchAccount.length() > 0 && event.getRawX() >= (popupContactBinding.searchAccount.getRight() - popupContactBinding.searchAccount.getCompoundDrawables()[DRAWABLE_RIGHT].getBounds().width())) {
|
||||||
|
popupContactBinding.searchAccount.setText("");
|
||||||
|
accountsVM.searchAccounts(instance, token, "", 10, false, true)
|
||||||
|
.observe(ComposeActivity.this, accounts -> onRetrieveContact(popupContactBinding, accounts));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
builderSingle.setView(popupContactBinding.getRoot());
|
||||||
|
builderSingle.setNegativeButton(R.string.validate, (dialog, which) -> {
|
||||||
|
dialog.dismiss();
|
||||||
|
composeAdapter.putCursor();
|
||||||
|
});
|
||||||
|
builderSingle.show();
|
||||||
|
} else if (item.getItemId() == R.id.action_microphone) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) ==
|
||||||
|
PackageManager.PERMISSION_GRANTED) {
|
||||||
|
MediaHelper.recordAudio(ComposeActivity.this, file -> {
|
||||||
|
List<Uri> uris = new ArrayList<>();
|
||||||
|
uris.add(Uri.fromFile(new File(file)));
|
||||||
|
composeAdapter.addAttachment(-1, uris);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (shouldShowRequestPermissionRationale(Manifest.permission.RECORD_AUDIO)) {
|
||||||
|
Toast.makeText(this,
|
||||||
|
getString(R.string.audio), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO
|
||||||
|
}, REQUEST_AUDIO_PERMISSION_RESULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
MediaHelper.recordAudio(ComposeActivity.this, file -> {
|
||||||
|
List<Uri> uris = new ArrayList<>();
|
||||||
|
uris.add(Uri.fromFile(new File(file)));
|
||||||
|
composeAdapter.addAttachment(-1, uris);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (item.getItemId() == R.id.action_schedule) {
|
||||||
|
if (statusDraft == null) {
|
||||||
|
statusDraft = prepareDraft(statusList, composeAdapter, account.instance, account.user_id);
|
||||||
|
}
|
||||||
|
if (canBeSent(statusDraft)) {
|
||||||
|
MediaHelper.scheduleMessage(ComposeActivity.this, date -> storeDraft(true, date));
|
||||||
|
} else {
|
||||||
|
Toasty.info(ComposeActivity.this, getString(R.string.toot_error_no_content), Toasty.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void onRetrieveContact(PopupContactBinding binding, List<app.fedilab.android.client.mastodon.entities.Account> accounts) {
|
||||||
|
binding.loader.setVisibility(View.GONE);
|
||||||
|
if (accounts == null) {
|
||||||
|
accounts = new ArrayList<>();
|
||||||
|
}
|
||||||
|
List<Boolean> checkedValues = new ArrayList<>();
|
||||||
|
List<app.fedilab.android.client.mastodon.entities.Account> contacts = new ArrayList<>(accounts);
|
||||||
|
for (app.fedilab.android.client.mastodon.entities.Account account : contacts) {
|
||||||
|
checkedValues.add(composeAdapter.getLastComposeContent().contains("@" + account.acct));
|
||||||
|
}
|
||||||
|
AccountsReplyAdapter contactAdapter = new AccountsReplyAdapter(contacts, checkedValues);
|
||||||
|
binding.lvAccountsSearch.setAdapter(contactAdapter);
|
||||||
|
binding.lvAccountsSearch.setLayoutManager(new LinearLayoutManager(ComposeActivity.this));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
List<Uri> uris = new ArrayList<>();
|
||||||
|
if (requestCode >= PICK_MEDIA && resultCode == RESULT_OK) {
|
||||||
|
ClipData clipData = data.getClipData();
|
||||||
|
int position = requestCode - PICK_MEDIA;
|
||||||
|
if (clipData != null) {
|
||||||
|
for (int i = 0; i < clipData.getItemCount(); i++) {
|
||||||
|
ClipData.Item item = clipData.getItemAt(i);
|
||||||
|
uris.add(item.getUri());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uris.add(data.getData());
|
||||||
|
}
|
||||||
|
composeAdapter.addAttachment(position, uris);
|
||||||
|
} else if (requestCode == TAKE_PHOTO && resultCode == RESULT_OK) {
|
||||||
|
uris.add(photoFileUri);
|
||||||
|
composeAdapter.addAttachment(-1, uris);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemDraftAdded(int position) {
|
||||||
|
Status status = new Status();
|
||||||
|
status.mentions = statusList.get(position).mentions;
|
||||||
|
status.visibility = statusList.get(position).visibility;
|
||||||
|
status.spoiler_text = statusList.get(position).spoiler_text;
|
||||||
|
status.sensitive = statusList.get(position).sensitive;
|
||||||
|
statusList.add(status);
|
||||||
|
composeAdapter.notifyItemInserted(position + 1);
|
||||||
|
binding.recyclerView.smoothScrollToPosition(position + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemDraftDeleted(Status status, int position) {
|
||||||
|
statusList.remove(status);
|
||||||
|
composeAdapter.notifyItemRemoved(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSubmit(StatusDraft draft) {
|
||||||
|
//Store in drafts
|
||||||
|
if (statusDraft == null) {
|
||||||
|
statusDraft = draft;
|
||||||
|
} else {
|
||||||
|
statusDraft.statusDraftList = draft.statusDraftList;
|
||||||
|
}
|
||||||
|
storeDraft(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void storeDraft(boolean sendMessage) {
|
||||||
|
storeDraft(sendMessage, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void storeDraft(boolean sendMessage, String scheduledDate) {
|
||||||
|
new Thread(() -> {
|
||||||
|
//Collect all statusCompose
|
||||||
|
List<Status> statusDrafts = new ArrayList<>();
|
||||||
|
List<Status> statusReplies = new ArrayList<>();
|
||||||
|
for (Status status : statusList) {
|
||||||
|
if (status.id == null) {
|
||||||
|
statusDrafts.add(status);
|
||||||
|
} else {
|
||||||
|
statusReplies.add(status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (statusDraft == null) {
|
||||||
|
statusDraft = new StatusDraft(ComposeActivity.this);
|
||||||
|
} else {
|
||||||
|
//Draft previously and date is changed
|
||||||
|
if (statusDraft.scheduled_at != null && scheduledDate != null && statusDraft.workerUuid != null) {
|
||||||
|
WorkManager.getInstance(ComposeActivity.this).cancelWorkById(statusDraft.workerUuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
statusDraft.statusReplyList = statusReplies;
|
||||||
|
statusDraft.statusDraftList = statusDrafts;
|
||||||
|
statusDraft.instance = account.instance;
|
||||||
|
statusDraft.user_id = account.user_id;
|
||||||
|
if (!canBeSent(statusDraft)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (statusDraft.id > 0) {
|
||||||
|
try {
|
||||||
|
new StatusDraft(ComposeActivity.this).updateStatusDraft(statusDraft);
|
||||||
|
} catch (DBException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
statusDraft.id = new StatusDraft(ComposeActivity.this).insertStatusDraft(statusDraft);
|
||||||
|
} catch (DBException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Only one single message scheduled
|
||||||
|
if (sendMessage && scheduledDate != null && statusDraft.statusDraftList.size() > 1) {
|
||||||
|
//Schedule a thread
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat(Helper.SCHEDULE_DATE_FORMAT, Locale.getDefault());
|
||||||
|
Date date;
|
||||||
|
try {
|
||||||
|
date = sdf.parse(scheduledDate);
|
||||||
|
long delayToPass = 0;
|
||||||
|
if (date != null) {
|
||||||
|
delayToPass = (date.getTime() - new Date().getTime());
|
||||||
|
}
|
||||||
|
Data inputData = new Data.Builder()
|
||||||
|
.putString(Helper.ARG_INSTANCE, BaseMainActivity.currentInstance)
|
||||||
|
.putString(Helper.ARG_TOKEN, BaseMainActivity.currentToken)
|
||||||
|
.putString(Helper.ARG_USER_ID, BaseMainActivity.currentUserID)
|
||||||
|
.putLong(Helper.ARG_STATUS_DRAFT_ID, statusDraft.id)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
OneTimeWorkRequest oneTimeWorkRequest = new OneTimeWorkRequest.Builder(ScheduleThreadWorker.class)
|
||||||
|
.setInputData(inputData)
|
||||||
|
.addTag(Helper.WORKER_SCHEDULED_STATUSES)
|
||||||
|
.setInitialDelay(delayToPass, TimeUnit.MILLISECONDS)
|
||||||
|
.build();
|
||||||
|
statusDraft.workerUuid = oneTimeWorkRequest.getId();
|
||||||
|
statusDraft.scheduled_at = date;
|
||||||
|
Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||||
|
Runnable myRunnable = () -> {
|
||||||
|
Toasty.info(ComposeActivity.this, getString(R.string.toot_scheduled), Toasty.LENGTH_LONG).show();
|
||||||
|
finish();
|
||||||
|
};
|
||||||
|
mainHandler.post(myRunnable);
|
||||||
|
} catch (ParseException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (sendMessage) {
|
||||||
|
Intent intent = new Intent(ComposeActivity.this, PostMessageService.class);
|
||||||
|
intent.putExtra(Helper.ARG_STATUS_DRAFT, statusDraft);
|
||||||
|
intent.putExtra(Helper.ARG_INSTANCE, instance);
|
||||||
|
intent.putExtra(Helper.ARG_TOKEN, token);
|
||||||
|
intent.putExtra(Helper.ARG_SCHEDULED_DATE, scheduledDate);
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
startForegroundService(intent);
|
||||||
|
} else {
|
||||||
|
startService(intent);
|
||||||
|
}
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean canBeSent(StatusDraft statusDraft) {
|
||||||
|
if (statusDraft == null || statusDraft.statusDraftList == null || statusDraft.statusDraftList.size() == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Status statusCheck = statusDraft.statusDraftList.get(0);
|
||||||
|
if (statusCheck == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (statusCheck.text != null && statusCheck.text.trim().length() != 0)
|
||||||
|
|| (statusCheck.media_attachments != null && statusCheck.media_attachments.size() != 0)
|
||||||
|
|| statusCheck.poll != null
|
||||||
|
|| (statusCheck.spoiler_text != null && statusCheck.spoiler_text.trim().length() != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onContactClick(boolean isChecked, String acct) {
|
||||||
|
composeAdapter.updateContent(isChecked, acct);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public enum mediaType {
|
||||||
|
PHOTO,
|
||||||
|
VIDEO,
|
||||||
|
AUDIO,
|
||||||
|
ALL
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,143 @@
|
||||||
|
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 android.content.SharedPreferences;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
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.fragment.app.Fragment;
|
||||||
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import app.fedilab.android.BaseMainActivity;
|
||||||
|
import app.fedilab.android.R;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Status;
|
||||||
|
import app.fedilab.android.databinding.ActivityConversationBinding;
|
||||||
|
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;
|
||||||
|
|
||||||
|
public class ContextActivity extends BaseActivity {
|
||||||
|
|
||||||
|
public static boolean expand;
|
||||||
|
public static boolean displayCW;
|
||||||
|
Fragment currentFragment;
|
||||||
|
private Status focusedStatus;
|
||||||
|
private ActivityConversationBinding binding;
|
||||||
|
public static Resources.Theme theme;
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
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, BaseMainActivity.accountWeakReference.get().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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,250 @@
|
||||||
|
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.SharedPreferences;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.text.Html;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import app.fedilab.android.BaseMainActivity;
|
||||||
|
import app.fedilab.android.R;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Attachment;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Emoji;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Status;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Tag;
|
||||||
|
import app.fedilab.android.databinding.ActivityCustomSharingBinding;
|
||||||
|
import app.fedilab.android.helper.Helper;
|
||||||
|
import app.fedilab.android.helper.ThemeHelper;
|
||||||
|
import app.fedilab.android.helper.customsharing.CustomSharingAsyncTask;
|
||||||
|
import app.fedilab.android.helper.customsharing.CustomSharingResponse;
|
||||||
|
import app.fedilab.android.helper.customsharing.OnCustomSharingInterface;
|
||||||
|
import es.dmoral.toasty.Toasty;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Curtis on 13/02/2019.
|
||||||
|
* Share status metadata to remote content aggregators
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class CustomSharingActivity extends BaseActivity implements OnCustomSharingInterface {
|
||||||
|
|
||||||
|
private String title, keywords, custom_sharing_url, encodedCustomSharingURL;
|
||||||
|
private String bundle_url;
|
||||||
|
private String bundle_source;
|
||||||
|
private String bundle_id;
|
||||||
|
private String bundle_content;
|
||||||
|
private String bundle_thumbnailurl;
|
||||||
|
private String bundle_creator;
|
||||||
|
private ActivityCustomSharingBinding binding;
|
||||||
|
private Status status;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
ThemeHelper.applyThemeBar(this);
|
||||||
|
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(CustomSharingActivity.this);
|
||||||
|
binding = ActivityCustomSharingBinding.inflate(getLayoutInflater());
|
||||||
|
setContentView(binding.getRoot());
|
||||||
|
if (getSupportActionBar() != null) {
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||||
|
}
|
||||||
|
Bundle b = getIntent().getExtras();
|
||||||
|
status = null;
|
||||||
|
if (b != null) {
|
||||||
|
status = (Status) b.getSerializable(Helper.ARG_STATUS);
|
||||||
|
}
|
||||||
|
if (status == null) {
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bundle_creator = status.account.acct;
|
||||||
|
bundle_url = status.url;
|
||||||
|
bundle_id = status.uri;
|
||||||
|
bundle_source = status.account.url;
|
||||||
|
String bundle_tags = getTagsString();
|
||||||
|
bundle_content = formatedContent(status.content, status.emojis);
|
||||||
|
if (status.card != null && status.card.image != null) {
|
||||||
|
bundle_thumbnailurl = status.card.image;
|
||||||
|
} else if (status.media_attachments != null && status.media_attachments.size() > 0) {
|
||||||
|
List<Attachment> mediaAttachments = status.media_attachments;
|
||||||
|
Attachment firstAttachment = mediaAttachments.get(0);
|
||||||
|
bundle_thumbnailurl = firstAttachment.preview_url;
|
||||||
|
} else {
|
||||||
|
bundle_thumbnailurl = status.account.avatar;
|
||||||
|
}
|
||||||
|
if (!bundle_creator.contains("@")) {
|
||||||
|
bundle_creator = bundle_creator + "@" + BaseMainActivity.accountWeakReference.get().instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.setCustomSharingTitle.setEllipsize(TextUtils.TruncateAt.END);
|
||||||
|
//set text on title, description, and keywords
|
||||||
|
String[] lines = bundle_content.split("\n");
|
||||||
|
//Remove tags in title
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||||
|
lines[0] = Html.fromHtml(lines[0], Html.FROM_HTML_MODE_LEGACY).toString();
|
||||||
|
else
|
||||||
|
lines[0] = Html.fromHtml(lines[0]).toString();
|
||||||
|
String newTitle;
|
||||||
|
if (lines[0].length() > 60) {
|
||||||
|
newTitle = lines[0].substring(0, 60) + '…';
|
||||||
|
} else {
|
||||||
|
newTitle = lines[0];
|
||||||
|
}
|
||||||
|
binding.setCustomSharingTitle.setText(newTitle);
|
||||||
|
String newDescription;
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||||
|
newDescription = Html.fromHtml(bundle_content, Html.FROM_HTML_MODE_LEGACY).toString();
|
||||||
|
else
|
||||||
|
newDescription = Html.fromHtml(bundle_content).toString();
|
||||||
|
|
||||||
|
binding.setCustomSharingDescription.setText(newDescription);
|
||||||
|
binding.setCustomSharingKeywords.setText(bundle_tags);
|
||||||
|
binding.setCustomSharingSave.setOnClickListener(v -> {
|
||||||
|
// obtain title, description, keywords
|
||||||
|
title = binding.setCustomSharingTitle.getText().toString();
|
||||||
|
keywords = binding.setCustomSharingKeywords.getText().toString();
|
||||||
|
CharSequence comma_only = ",";
|
||||||
|
CharSequence space_only = " ";
|
||||||
|
CharSequence double_space = " ";
|
||||||
|
keywords = keywords.replace(comma_only, space_only);
|
||||||
|
keywords = keywords.replace(double_space, space_only);
|
||||||
|
// Create encodedCustomSharingURL
|
||||||
|
custom_sharing_url = sharedpreferences.getString(getString(R.string.SET_CUSTOM_SHARING_URL),
|
||||||
|
"http://example.net/add?token=YOUR_TOKEN&url=${url}&title=${title}" +
|
||||||
|
"&source=${source}&id=${id}&description=${description}&keywords=${keywords}&creator=${creator}&thumbnailurl=${thumbnailurl}");
|
||||||
|
encodedCustomSharingURL = encodeCustomSharingURL();
|
||||||
|
new CustomSharingAsyncTask(CustomSharingActivity.this, encodedCustomSharingURL, CustomSharingActivity.this);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getTagsString() {
|
||||||
|
//iterate through tags and create comma delimited string of tag names
|
||||||
|
StringBuilder tag_names = new StringBuilder();
|
||||||
|
for (Tag t : status.tags) {
|
||||||
|
if (tag_names.toString().equals("")) {
|
||||||
|
tag_names = new StringBuilder(t.name);
|
||||||
|
} else {
|
||||||
|
tag_names.append(", ").append(t.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tag_names.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (item.getItemId() == android.R.id.home) {
|
||||||
|
finish();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCustomSharing(CustomSharingResponse customSharingResponse) {
|
||||||
|
binding.setCustomSharingSave.setEnabled(true);
|
||||||
|
if (customSharingResponse.getError() != null) {
|
||||||
|
Toasty.error(CustomSharingActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String response = customSharingResponse.getResponse();
|
||||||
|
Toasty.success(CustomSharingActivity.this, response, Toast.LENGTH_LONG).show();
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String encodeCustomSharingURL() {
|
||||||
|
Uri uri = Uri.parse(custom_sharing_url);
|
||||||
|
String protocol = uri.getScheme();
|
||||||
|
String server = uri.getAuthority();
|
||||||
|
String path = uri.getPath();
|
||||||
|
if (path != null) {
|
||||||
|
path = path.replaceAll("/", "");
|
||||||
|
}
|
||||||
|
Uri.Builder builder = new Uri.Builder();
|
||||||
|
builder.scheme(protocol)
|
||||||
|
.authority(server)
|
||||||
|
.appendPath(path);
|
||||||
|
Set<String> args = uri.getQueryParameterNames();
|
||||||
|
boolean paramFound;
|
||||||
|
for (String param_name : args) {
|
||||||
|
paramFound = false;
|
||||||
|
String param_value = uri.getQueryParameter(param_name);
|
||||||
|
if (param_value != null)
|
||||||
|
switch (param_value) {
|
||||||
|
case "${url}":
|
||||||
|
paramFound = true;
|
||||||
|
builder.appendQueryParameter(param_name, bundle_url);
|
||||||
|
break;
|
||||||
|
case "${title}":
|
||||||
|
paramFound = true;
|
||||||
|
builder.appendQueryParameter(param_name, title);
|
||||||
|
break;
|
||||||
|
case "${source}":
|
||||||
|
paramFound = true;
|
||||||
|
builder.appendQueryParameter(param_name, bundle_source);
|
||||||
|
break;
|
||||||
|
case "${id}":
|
||||||
|
paramFound = true;
|
||||||
|
builder.appendQueryParameter(param_name, bundle_id);
|
||||||
|
break;
|
||||||
|
case "${description}":
|
||||||
|
paramFound = true;
|
||||||
|
builder.appendQueryParameter(param_name, bundle_content);
|
||||||
|
break;
|
||||||
|
case "${keywords}":
|
||||||
|
paramFound = true;
|
||||||
|
builder.appendQueryParameter(param_name, keywords);
|
||||||
|
break;
|
||||||
|
case "${creator}":
|
||||||
|
paramFound = true;
|
||||||
|
builder.appendQueryParameter(param_name, bundle_creator);
|
||||||
|
break;
|
||||||
|
case "${thumbnailurl}":
|
||||||
|
paramFound = true;
|
||||||
|
builder.appendQueryParameter(param_name, bundle_thumbnailurl);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!paramFound) {
|
||||||
|
builder.appendQueryParameter(param_name, param_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder.build().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String formatedContent(String content, List<Emoji> emojis) {
|
||||||
|
//Avoid null content
|
||||||
|
if (content == null)
|
||||||
|
return "";
|
||||||
|
if (emojis == null || emojis.size() == 0)
|
||||||
|
return content;
|
||||||
|
for (Emoji emoji : emojis) {
|
||||||
|
content = content.replaceAll(":" + emoji.shortcode + ":", "<img src='" + emoji.url + "' width=20 alt='" + emoji.shortcode + "'/>");
|
||||||
|
}
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,213 @@
|
||||||
|
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.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.ActionBar;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import app.fedilab.android.BaseMainActivity;
|
||||||
|
import app.fedilab.android.R;
|
||||||
|
import app.fedilab.android.client.entities.StatusDraft;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Attachment;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Status;
|
||||||
|
import app.fedilab.android.databinding.ActivityDraftsBinding;
|
||||||
|
import app.fedilab.android.exception.DBException;
|
||||||
|
import app.fedilab.android.helper.Helper;
|
||||||
|
import app.fedilab.android.helper.ThemeHelper;
|
||||||
|
import app.fedilab.android.ui.drawer.StatusDraftAdapter;
|
||||||
|
import app.fedilab.android.viewmodel.mastodon.TimelinesVM;
|
||||||
|
|
||||||
|
public class DraftActivity extends BaseActivity implements StatusDraftAdapter.DraftActions {
|
||||||
|
|
||||||
|
|
||||||
|
private ActivityDraftsBinding binding;
|
||||||
|
private List<StatusDraft> statusDrafts;
|
||||||
|
private StatusDraftAdapter statusDraftAdapter;
|
||||||
|
private TimelinesVM timelinesVM;
|
||||||
|
private LinearLayoutManager mLayoutManager;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
ThemeHelper.applyTheme(this);
|
||||||
|
binding = ActivityDraftsBinding.inflate(getLayoutInflater());
|
||||||
|
setContentView(binding.getRoot());
|
||||||
|
setSupportActionBar(binding.toolbar);
|
||||||
|
ActionBar actionBar = getSupportActionBar();
|
||||||
|
//Remove title
|
||||||
|
if (actionBar != null) {
|
||||||
|
actionBar.setDisplayShowTitleEnabled(false);
|
||||||
|
}
|
||||||
|
binding.toolbar.setPopupTheme(Helper.popupStyle());
|
||||||
|
binding.title.setText(R.string.drafts);
|
||||||
|
binding.loader.setVisibility(View.VISIBLE);
|
||||||
|
binding.lvStatus.setVisibility(View.GONE);
|
||||||
|
binding.noAction.setVisibility(View.GONE);
|
||||||
|
if (getSupportActionBar() != null) {
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||||
|
}
|
||||||
|
timelinesVM = new ViewModelProvider(DraftActivity.this).get(TimelinesVM.class);
|
||||||
|
timelinesVM.getDrafts(BaseMainActivity.accountWeakReference.get())
|
||||||
|
.observe(DraftActivity.this, this::initializeDraftView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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_draft, menu);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (item.getItemId() == android.R.id.home) {
|
||||||
|
finish();
|
||||||
|
return true;
|
||||||
|
} else if (item.getItemId() == R.id.action_delete) {
|
||||||
|
AlertDialog.Builder unfollowConfirm = new AlertDialog.Builder(DraftActivity.this, Helper.dialogStyle());
|
||||||
|
unfollowConfirm.setTitle(getString(R.string.delete_all));
|
||||||
|
unfollowConfirm.setMessage(getString(R.string.remove_draft));
|
||||||
|
unfollowConfirm.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
|
||||||
|
unfollowConfirm.setPositiveButton(R.string.delete, (dialog, which) -> {
|
||||||
|
new Thread(() -> {
|
||||||
|
if (statusDrafts != null) {
|
||||||
|
for (StatusDraft statusDraft : statusDrafts) {
|
||||||
|
//Check if there are media in the drafts
|
||||||
|
List<Attachment> attachments = new ArrayList<>();
|
||||||
|
if (statusDraft.statusDraftList != null) {
|
||||||
|
for (Status drafts : statusDraft.statusDraftList) {
|
||||||
|
if (drafts.media_attachments != null && drafts.media_attachments.size() > 0) {
|
||||||
|
attachments.addAll(drafts.media_attachments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//If there are media, we need to remove them first.
|
||||||
|
if (attachments.size() > 0) {
|
||||||
|
for (Attachment attachment : attachments) {
|
||||||
|
if (attachment.local_path != null) {
|
||||||
|
File fileToDelete = new File(attachment.local_path);
|
||||||
|
if (fileToDelete.exists()) {
|
||||||
|
//noinspection ResultOfMethodCallIgnored
|
||||||
|
fileToDelete.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
//Delete the draft
|
||||||
|
new StatusDraft(DraftActivity.this).removeAllDraft();
|
||||||
|
Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||||
|
Runnable myRunnable = () -> statusDraftAdapter.draftActions.onAllDeleted();
|
||||||
|
mainHandler.post(myRunnable);
|
||||||
|
} catch (DBException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
dialog.dismiss();
|
||||||
|
});
|
||||||
|
unfollowConfirm.show();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Intialize the view for drafts
|
||||||
|
*
|
||||||
|
* @param statusDrafts {@link List<StatusDraft>}
|
||||||
|
*/
|
||||||
|
private void initializeDraftView(List<StatusDraft> statusDrafts) {
|
||||||
|
if (statusDrafts == null) {
|
||||||
|
statusDrafts = new ArrayList<>();
|
||||||
|
}
|
||||||
|
binding.loader.setVisibility(View.GONE);
|
||||||
|
if (statusDrafts.size() > 0) {
|
||||||
|
binding.lvStatus.setVisibility(View.VISIBLE);
|
||||||
|
this.statusDrafts = statusDrafts;
|
||||||
|
statusDraftAdapter = new StatusDraftAdapter(this.statusDrafts);
|
||||||
|
statusDraftAdapter.draftActions = this;
|
||||||
|
mLayoutManager = new LinearLayoutManager(DraftActivity.this);
|
||||||
|
binding.lvStatus.setLayoutManager(mLayoutManager);
|
||||||
|
binding.lvStatus.setAdapter(statusDraftAdapter);
|
||||||
|
} else {
|
||||||
|
binding.noAction.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
//We need to check if drafts changed (ie when coming back from the compose activity)
|
||||||
|
if (statusDrafts != null && timelinesVM != null) {
|
||||||
|
timelinesVM.getDrafts(BaseMainActivity.accountWeakReference.get())
|
||||||
|
.observe(DraftActivity.this, this::updateDrafts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateDrafts(List<StatusDraft> statusDrafts) {
|
||||||
|
if (statusDrafts == null) {
|
||||||
|
statusDrafts = new ArrayList<>();
|
||||||
|
}
|
||||||
|
int currentPosition = mLayoutManager.findFirstVisibleItemPosition();
|
||||||
|
if (statusDrafts.size() > 0) {
|
||||||
|
int count = this.statusDrafts.size();
|
||||||
|
this.statusDrafts.clear();
|
||||||
|
this.statusDrafts = new ArrayList<>();
|
||||||
|
statusDraftAdapter.notifyItemRangeRemoved(0, count);
|
||||||
|
this.statusDrafts = statusDrafts;
|
||||||
|
statusDraftAdapter = new StatusDraftAdapter(this.statusDrafts);
|
||||||
|
statusDraftAdapter.draftActions = this;
|
||||||
|
mLayoutManager = new LinearLayoutManager(DraftActivity.this);
|
||||||
|
binding.lvStatus.setLayoutManager(mLayoutManager);
|
||||||
|
binding.lvStatus.setAdapter(statusDraftAdapter);
|
||||||
|
if (currentPosition < this.statusDrafts.size()) {
|
||||||
|
binding.lvStatus.scrollToPosition(currentPosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
binding.lvStatus.setAdapter(null);
|
||||||
|
binding = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAllDeleted() {
|
||||||
|
binding.lvStatus.setVisibility(View.GONE);
|
||||||
|
binding.noAction.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,291 @@
|
||||||
|
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.instanceInfo;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
import android.text.Html;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||||
|
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
|
import com.google.android.material.textfield.TextInputEditText;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import app.fedilab.android.BaseMainActivity;
|
||||||
|
import app.fedilab.android.R;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Account;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Field;
|
||||||
|
import app.fedilab.android.databinding.AccountFieldItemBinding;
|
||||||
|
import app.fedilab.android.databinding.ActivityEditProfileBinding;
|
||||||
|
import app.fedilab.android.helper.Helper;
|
||||||
|
import app.fedilab.android.helper.MastodonHelper;
|
||||||
|
import app.fedilab.android.helper.ThemeHelper;
|
||||||
|
import app.fedilab.android.viewmodel.mastodon.AccountsVM;
|
||||||
|
import es.dmoral.toasty.Toasty;
|
||||||
|
|
||||||
|
public class EditProfileActivity extends BaseActivity {
|
||||||
|
|
||||||
|
public static final int PICK_MEDIA_AVATAR = 5705;
|
||||||
|
public static final int PICK_MEDIA_HEADER = 5706;
|
||||||
|
private ActivityEditProfileBinding binding;
|
||||||
|
private AccountsVM accountsVM;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
ThemeHelper.applyThemeBar(this);
|
||||||
|
binding = ActivityEditProfileBinding.inflate(getLayoutInflater());
|
||||||
|
setContentView(binding.getRoot());
|
||||||
|
if (getSupportActionBar() != null)
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
|
||||||
|
new ViewModelProvider(EditProfileActivity.this).get(AccountsVM.class).getConnectedAccount(BaseMainActivity.currentInstance, BaseMainActivity.currentToken)
|
||||||
|
.observe(EditProfileActivity.this, account -> {
|
||||||
|
BaseMainActivity.accountWeakReference.get().mastodon_account = account;
|
||||||
|
initializeView();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
private void initializeView() {
|
||||||
|
//Hydrate values
|
||||||
|
MastodonHelper.loadProfileMediaMastodon(binding.bannerPp, BaseMainActivity.accountWeakReference.get().mastodon_account, MastodonHelper.MediaAccountType.HEADER);
|
||||||
|
MastodonHelper.loadPPMastodon(binding.accountPp, BaseMainActivity.accountWeakReference.get().mastodon_account);
|
||||||
|
binding.displayName.setText(BaseMainActivity.accountWeakReference.get().mastodon_account.display_name);
|
||||||
|
binding.acct.setText(String.format(Locale.getDefault(), "%s@%s", BaseMainActivity.accountWeakReference.get().mastodon_account.acct, BaseMainActivity.currentInstance));
|
||||||
|
String bio;
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||||
|
bio = Html.fromHtml(BaseMainActivity.accountWeakReference.get().mastodon_account.note, Html.FROM_HTML_MODE_LEGACY).toString();
|
||||||
|
else
|
||||||
|
bio = Html.fromHtml(BaseMainActivity.accountWeakReference.get().mastodon_account.note).toString();
|
||||||
|
binding.bio.setText(bio);
|
||||||
|
binding.sensitive.setChecked(BaseMainActivity.accountWeakReference.get().mastodon_account.source.sensitive);
|
||||||
|
binding.bot.setChecked(BaseMainActivity.accountWeakReference.get().mastodon_account.bot);
|
||||||
|
binding.discoverable.setChecked(BaseMainActivity.accountWeakReference.get().mastodon_account.discoverable);
|
||||||
|
switch (BaseMainActivity.accountWeakReference.get().mastodon_account.source.privacy) {
|
||||||
|
case "public":
|
||||||
|
binding.visibilityPublic.setChecked(true);
|
||||||
|
break;
|
||||||
|
case "unlisted":
|
||||||
|
binding.visibilityUnlisted.setChecked(true);
|
||||||
|
break;
|
||||||
|
case "private":
|
||||||
|
binding.visibilityPrivate.setChecked(true);
|
||||||
|
break;
|
||||||
|
case "direct":
|
||||||
|
binding.visibilityDirect.setChecked(true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (BaseMainActivity.accountWeakReference.get().mastodon_account.locked) {
|
||||||
|
binding.locked.setChecked(true);
|
||||||
|
} else {
|
||||||
|
binding.unlocked.setChecked(true);
|
||||||
|
}
|
||||||
|
List<Field> fields = BaseMainActivity.accountWeakReference.get().mastodon_account.fields;
|
||||||
|
if (fields != null && fields.size() > 0) {
|
||||||
|
for (Field field : fields) {
|
||||||
|
AccountFieldItemBinding fieldItemBinding = AccountFieldItemBinding.inflate(getLayoutInflater());
|
||||||
|
fieldItemBinding.name.setText(field.name);
|
||||||
|
String value;
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||||
|
value = Html.fromHtml(field.value, Html.FROM_HTML_MODE_LEGACY).toString();
|
||||||
|
else
|
||||||
|
value = Html.fromHtml(field.value).toString();
|
||||||
|
fieldItemBinding.value.setText(value);
|
||||||
|
fieldItemBinding.remove.setOnClickListener(v -> {
|
||||||
|
AlertDialog.Builder deleteConfirm = new AlertDialog.Builder(EditProfileActivity.this, Helper.dialogStyle());
|
||||||
|
deleteConfirm.setTitle(getString(R.string.delete_field));
|
||||||
|
deleteConfirm.setMessage(getString(R.string.delete_field_confirm));
|
||||||
|
deleteConfirm.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
|
||||||
|
deleteConfirm.setPositiveButton(R.string.delete, (dialog, which) -> {
|
||||||
|
binding.fieldsContainer.removeView(fieldItemBinding.getRoot());
|
||||||
|
if (binding.fieldsContainer.getChildCount() < 4) {
|
||||||
|
binding.fieldsContainer.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
binding.fieldsContainer.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
dialog.dismiss();
|
||||||
|
});
|
||||||
|
deleteConfirm.create().show();
|
||||||
|
});
|
||||||
|
binding.fieldsContainer.addView(fieldItemBinding.getRoot());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
binding.addField.setOnClickListener(view -> {
|
||||||
|
AccountFieldItemBinding fieldItemBinding = AccountFieldItemBinding.inflate(getLayoutInflater());
|
||||||
|
fieldItemBinding.remove.setOnClickListener(v -> {
|
||||||
|
AlertDialog.Builder deleteConfirm = new AlertDialog.Builder(EditProfileActivity.this, Helper.dialogStyle());
|
||||||
|
deleteConfirm.setTitle(getString(R.string.delete_field));
|
||||||
|
deleteConfirm.setMessage(getString(R.string.delete_field_confirm));
|
||||||
|
deleteConfirm.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
|
||||||
|
deleteConfirm.setPositiveButton(R.string.delete, (dialog, which) -> {
|
||||||
|
binding.fieldsContainer.removeView(fieldItemBinding.getRoot());
|
||||||
|
if (binding.fieldsContainer.getChildCount() < 4) {
|
||||||
|
binding.fieldsContainer.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
binding.fieldsContainer.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
dialog.dismiss();
|
||||||
|
});
|
||||||
|
deleteConfirm.create().show();
|
||||||
|
});
|
||||||
|
binding.fieldsContainer.addView(fieldItemBinding.getRoot());
|
||||||
|
if (binding.fieldsContainer.getChildCount() >= 4) {
|
||||||
|
binding.addField.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//Actions with the activity
|
||||||
|
accountsVM = new ViewModelProvider(EditProfileActivity.this).get(AccountsVM.class);
|
||||||
|
binding.headerSelect.setOnClickListener(view -> startActivityForResult(prepareIntent(), EditProfileActivity.PICK_MEDIA_AVATAR));
|
||||||
|
|
||||||
|
binding.avatarSelect.setOnClickListener(view -> startActivityForResult(prepareIntent(), EditProfileActivity.PICK_MEDIA_HEADER));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
if (requestCode == PICK_MEDIA_AVATAR && resultCode == RESULT_OK) {
|
||||||
|
binding.avatarProgress.setVisibility(View.VISIBLE);
|
||||||
|
Glide.with(EditProfileActivity.this)
|
||||||
|
.asDrawable()
|
||||||
|
.load(data.getData())
|
||||||
|
.thumbnail(0.1f)
|
||||||
|
.into(binding.accountPp);
|
||||||
|
accountsVM.updateProfilePicture(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, data.getData(), AccountsVM.UpdateMediaType.AVATAR)
|
||||||
|
.observe(EditProfileActivity.this, account -> {
|
||||||
|
sendBroadCast(account);
|
||||||
|
binding.avatarProgress.setVisibility(View.GONE);
|
||||||
|
BaseMainActivity.accountWeakReference.get().mastodon_account = account;
|
||||||
|
});
|
||||||
|
} else if (requestCode == PICK_MEDIA_HEADER && resultCode == RESULT_OK) {
|
||||||
|
Glide.with(EditProfileActivity.this)
|
||||||
|
.asDrawable()
|
||||||
|
.load(data.getData())
|
||||||
|
.thumbnail(0.1f)
|
||||||
|
.into(binding.bannerPp);
|
||||||
|
binding.headerProgress.setVisibility(View.VISIBLE);
|
||||||
|
accountsVM.updateProfilePicture(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, data.getData(), AccountsVM.UpdateMediaType.HEADER)
|
||||||
|
.observe(EditProfileActivity.this, account -> {
|
||||||
|
sendBroadCast(account);
|
||||||
|
binding.headerProgress.setVisibility(View.GONE);
|
||||||
|
BaseMainActivity.accountWeakReference.get().mastodon_account = account;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendBroadCast(Account account) {
|
||||||
|
Bundle b = new Bundle();
|
||||||
|
b.putBoolean(Helper.RECEIVE_REDRAW_PROFILE, true);
|
||||||
|
b.putSerializable(Helper.ARG_ACCOUNT, account);
|
||||||
|
Intent intentBD = new Intent(Helper.BROADCAST_DATA);
|
||||||
|
intentBD.putExtras(b);
|
||||||
|
LocalBroadcastManager.getInstance(EditProfileActivity.this).sendBroadcast(intentBD);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Intent prepareIntent() {
|
||||||
|
Intent intent;
|
||||||
|
intent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||||
|
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||||
|
intent.setType("*/*");
|
||||||
|
String[] mimetypes;
|
||||||
|
long max_size = -1;
|
||||||
|
if (instanceInfo.getMimeTypeImage().size() > 0) {
|
||||||
|
mimetypes = instanceInfo.getMimeTypeImage().toArray(new String[0]);
|
||||||
|
max_size = instanceInfo.configuration.media_attachments.image_size_limit;
|
||||||
|
} else {
|
||||||
|
mimetypes = new String[]{"image/*"};
|
||||||
|
}
|
||||||
|
if (max_size > 0) {
|
||||||
|
intent.putExtra(MediaStore.EXTRA_SIZE_LIMIT, max_size);
|
||||||
|
}
|
||||||
|
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimetypes);
|
||||||
|
return intent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(@NonNull Menu menu) {
|
||||||
|
getMenuInflater().inflate(R.menu.menu_edit_profile, menu);
|
||||||
|
return super.onCreateOptionsMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getPrivacy() {
|
||||||
|
if (binding.visibilityPublic.isChecked()) {
|
||||||
|
return "public";
|
||||||
|
} else if (binding.visibilityUnlisted.isChecked()) {
|
||||||
|
return "unlisted";
|
||||||
|
} else if (binding.visibilityPrivate.isChecked()) {
|
||||||
|
return "private";
|
||||||
|
} else if (binding.visibilityDirect.isChecked()) {
|
||||||
|
return "direct";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
LinkedHashMap<Integer, Field.FieldParams> getFields() {
|
||||||
|
LinkedHashMap<Integer, Field.FieldParams> fields = new LinkedHashMap<>();
|
||||||
|
for (int i = 0; i < binding.fieldsContainer.getChildCount(); i++) {
|
||||||
|
Field.FieldParams field = new Field.FieldParams();
|
||||||
|
field.name = ((TextInputEditText) binding.fieldsContainer.getChildAt(i).findViewById(R.id.name)).getText().toString().trim();
|
||||||
|
field.value = ((TextInputEditText) binding.fieldsContainer.getChildAt(i).findViewById(R.id.value)).getText().toString().trim();
|
||||||
|
fields.put(i, field);
|
||||||
|
}
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (item.getItemId() == android.R.id.home) {
|
||||||
|
finish();
|
||||||
|
return true;
|
||||||
|
} else if (item.getItemId() == R.id.action_save) {
|
||||||
|
accountsVM.updateCredentials(BaseMainActivity.currentInstance, BaseMainActivity.currentToken,
|
||||||
|
binding.discoverable.isChecked(),
|
||||||
|
binding.bot.isChecked(),
|
||||||
|
binding.displayName.getText().toString().trim(),
|
||||||
|
binding.bio.getText().toString(),
|
||||||
|
binding.locked.isChecked(),
|
||||||
|
getPrivacy(),
|
||||||
|
binding.sensitive.isChecked(),
|
||||||
|
null,
|
||||||
|
getFields()
|
||||||
|
)
|
||||||
|
.observe(EditProfileActivity.this, account -> {
|
||||||
|
BaseMainActivity.accountWeakReference.get().mastodon_account = account;
|
||||||
|
sendBroadCast(account);
|
||||||
|
Toasty.success(EditProfileActivity.this, getString(R.string.profiled_updated), Toasty.LENGTH_LONG).show();
|
||||||
|
finish();
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,238 @@
|
||||||
|
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.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.Button;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.lifecycle.LifecycleOwner;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
import androidx.lifecycle.ViewModelStoreOwner;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import app.fedilab.android.BaseMainActivity;
|
||||||
|
import app.fedilab.android.R;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Filter;
|
||||||
|
import app.fedilab.android.databinding.ActivityFiltersBinding;
|
||||||
|
import app.fedilab.android.databinding.PopupAddFilterBinding;
|
||||||
|
import app.fedilab.android.helper.Helper;
|
||||||
|
import app.fedilab.android.helper.ThemeHelper;
|
||||||
|
import app.fedilab.android.ui.drawer.FilterAdapter;
|
||||||
|
import app.fedilab.android.viewmodel.mastodon.AccountsVM;
|
||||||
|
|
||||||
|
public class FilterActivity extends BaseActivity implements FilterAdapter.Delete {
|
||||||
|
|
||||||
|
private ActivityFiltersBinding binding;
|
||||||
|
private List<Filter> filterList;
|
||||||
|
private FilterAdapter filterAdapter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method that allows to add or edit filter depending if Filter passing into params is null (null = insertion)
|
||||||
|
*
|
||||||
|
* @param context - Context
|
||||||
|
* @param filter - {@link Filter}
|
||||||
|
* @param listener - {@link FilterAdapter.FilterAction}
|
||||||
|
*/
|
||||||
|
public static void addEditFilter(Context context, Filter filter, FilterAdapter.FilterAction listener) {
|
||||||
|
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context, Helper.dialogStyle());
|
||||||
|
PopupAddFilterBinding popupAddFilterBinding = PopupAddFilterBinding.inflate(LayoutInflater.from(context));
|
||||||
|
AccountsVM accountsVM = new ViewModelProvider((ViewModelStoreOwner) context).get(AccountsVM.class);
|
||||||
|
dialogBuilder.setView(popupAddFilterBinding.getRoot());
|
||||||
|
ArrayAdapter<CharSequence> adapterResize = ArrayAdapter.createFromResource(Objects.requireNonNull(context),
|
||||||
|
R.array.filter_expire, android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
popupAddFilterBinding.filterExpire.setAdapter(adapterResize);
|
||||||
|
final int[] expire = {-1};
|
||||||
|
popupAddFilterBinding.filterExpire.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||||
|
@Override
|
||||||
|
public void onItemSelected(AdapterView<?> parent1, View view, int position1, long id) {
|
||||||
|
switch (position1) {
|
||||||
|
case 0:
|
||||||
|
expire[0] = -1;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
expire[0] = 3600;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
expire[0] = 21600;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
expire[0] = 43200;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
expire[0] = 86400;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
expire[0] = 604800;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNothingSelected(AdapterView<?> parent1) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (filter != null) {
|
||||||
|
popupAddFilterBinding.addPhrase.setText(filter.phrase);
|
||||||
|
if (filter.context != null)
|
||||||
|
for (String val : filter.context) {
|
||||||
|
switch (val) {
|
||||||
|
case "home":
|
||||||
|
popupAddFilterBinding.contextHome.setChecked(true);
|
||||||
|
break;
|
||||||
|
case "public":
|
||||||
|
popupAddFilterBinding.contextPublic.setChecked(true);
|
||||||
|
break;
|
||||||
|
case "notifications":
|
||||||
|
popupAddFilterBinding.contextNotification.setChecked(true);
|
||||||
|
break;
|
||||||
|
case "thread":
|
||||||
|
popupAddFilterBinding.contextConversation.setChecked(true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
popupAddFilterBinding.contextWholeWord.setChecked(filter.whole_word);
|
||||||
|
popupAddFilterBinding.contextDrop.setChecked(filter.irreversible);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AlertDialog alertDialog = dialogBuilder.setPositiveButton(R.string.validate, null)
|
||||||
|
.setNegativeButton(R.string.cancel, null).create();
|
||||||
|
alertDialog.setOnShowListener(dialogInterface -> {
|
||||||
|
|
||||||
|
Button button = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
|
||||||
|
button.setOnClickListener(view -> {
|
||||||
|
if (popupAddFilterBinding.addPhrase.getText() == null || popupAddFilterBinding.addPhrase.getText().toString().trim().length() == 0) {
|
||||||
|
popupAddFilterBinding.addPhrase.setError(context.getString(R.string.cannot_be_empty));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!popupAddFilterBinding.contextConversation.isChecked() && !popupAddFilterBinding.contextHome.isChecked() && !popupAddFilterBinding.contextPublic.isChecked() && !popupAddFilterBinding.contextNotification.isChecked()) {
|
||||||
|
popupAddFilterBinding.contextDescription.setError(context.getString(R.string.cannot_be_empty));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (popupAddFilterBinding.addPhrase.getText() != null && popupAddFilterBinding.addPhrase.getText().toString().trim().length() > 0) {
|
||||||
|
Filter filterSent = new Filter();
|
||||||
|
ArrayList<String> contextFilter = new ArrayList<>();
|
||||||
|
if (popupAddFilterBinding.contextHome.isChecked())
|
||||||
|
contextFilter.add("home");
|
||||||
|
if (popupAddFilterBinding.contextPublic.isChecked())
|
||||||
|
contextFilter.add("public");
|
||||||
|
if (popupAddFilterBinding.contextNotification.isChecked())
|
||||||
|
contextFilter.add("notifications");
|
||||||
|
if (popupAddFilterBinding.contextConversation.isChecked())
|
||||||
|
contextFilter.add("thread");
|
||||||
|
filterSent.context = contextFilter;
|
||||||
|
filterSent.expires_at_sent = expire[0];
|
||||||
|
filterSent.phrase = popupAddFilterBinding.addPhrase.getText().toString();
|
||||||
|
filterSent.whole_word = popupAddFilterBinding.contextWholeWord.isChecked();
|
||||||
|
filterSent.irreversible = popupAddFilterBinding.contextDrop.isChecked();
|
||||||
|
if (filter != null) {
|
||||||
|
accountsVM.editFilter(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, filter.id, filterSent.phrase, filterSent.context, filterSent.irreversible, filterSent.whole_word, filterSent.expires_at_sent)
|
||||||
|
.observe((LifecycleOwner) context, listener::callback);
|
||||||
|
} else {
|
||||||
|
accountsVM.addFilter(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, filterSent.phrase, filterSent.context, filterSent.irreversible, filterSent.whole_word, filterSent.expires_at_sent)
|
||||||
|
.observe((LifecycleOwner) context, listener::callback);
|
||||||
|
}
|
||||||
|
alertDialog.dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Button buttonCancel = alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE);
|
||||||
|
buttonCancel.setOnClickListener(view -> alertDialog.dismiss());
|
||||||
|
});
|
||||||
|
alertDialog.setTitle(context.getString(R.string.action_update_filter));
|
||||||
|
alertDialog.setOnDismissListener(dialogInterface -> {
|
||||||
|
//Hide keyboard
|
||||||
|
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
|
imm.hideSoftInputFromWindow(popupAddFilterBinding.addPhrase.getWindowToken(), 0);
|
||||||
|
});
|
||||||
|
if (alertDialog.getWindow() != null) {
|
||||||
|
alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
|
||||||
|
}
|
||||||
|
alertDialog.show();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
ThemeHelper.applyThemeBar(this);
|
||||||
|
binding = ActivityFiltersBinding.inflate(getLayoutInflater());
|
||||||
|
setContentView(binding.getRoot());
|
||||||
|
filterList = new ArrayList<>();
|
||||||
|
if (getSupportActionBar() != null)
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
|
||||||
|
AccountsVM accountsVM = new ViewModelProvider(FilterActivity.this).get(AccountsVM.class);
|
||||||
|
accountsVM.getFilters(BaseMainActivity.currentInstance, BaseMainActivity.currentToken)
|
||||||
|
.observe(FilterActivity.this, filters -> {
|
||||||
|
BaseMainActivity.mainFilters = filters;
|
||||||
|
if (filters != null && filters.size() > 0) {
|
||||||
|
filterList.addAll(filters);
|
||||||
|
filterAdapter = new FilterAdapter(filterList);
|
||||||
|
filterAdapter.delete = this;
|
||||||
|
binding.lvFilters.setAdapter(filterAdapter);
|
||||||
|
binding.lvFilters.setLayoutManager(new LinearLayoutManager(FilterActivity.this));
|
||||||
|
} else {
|
||||||
|
binding.lvFilters.setVisibility(View.GONE);
|
||||||
|
binding.noAction.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
binding.addFilter.setOnClickListener(v -> addEditFilter(FilterActivity.this, null, filter -> {
|
||||||
|
if (filter != null) {
|
||||||
|
filterList.add(0, filter);
|
||||||
|
if (filterAdapter != null) {
|
||||||
|
filterAdapter.notifyItemInserted(0);
|
||||||
|
} else {
|
||||||
|
filterAdapter = new FilterAdapter(filterList);
|
||||||
|
filterAdapter.delete = FilterActivity.this;
|
||||||
|
binding.lvFilters.setAdapter(filterAdapter);
|
||||||
|
binding.lvFilters.setLayoutManager(new LinearLayoutManager(FilterActivity.this));
|
||||||
|
}
|
||||||
|
binding.lvFilters.setVisibility(View.VISIBLE);
|
||||||
|
binding.noAction.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (item.getItemId() == android.R.id.home) {
|
||||||
|
finish();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void allFiltersDeleted() {
|
||||||
|
binding.lvFilters.setVisibility(View.GONE);
|
||||||
|
binding.noAction.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,162 @@
|
||||||
|
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.os.Bundle;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.ActionBar;
|
||||||
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import app.fedilab.android.BaseMainActivity;
|
||||||
|
import app.fedilab.android.R;
|
||||||
|
import app.fedilab.android.client.entities.Pinned;
|
||||||
|
import app.fedilab.android.client.entities.StatusDraft;
|
||||||
|
import app.fedilab.android.client.entities.Timeline;
|
||||||
|
import app.fedilab.android.client.entities.app.PinnedTimeline;
|
||||||
|
import app.fedilab.android.client.entities.app.TagTimeline;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Status;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
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(BaseMainActivity.accountWeakReference.get());
|
||||||
|
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) {
|
||||||
|
Toasty.warning(HashTagActivity.this, getString(R.string.tags_already_stored), Toasty.LENGTH_SHORT).show();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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.mastodon.entities.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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.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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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.mastodon.entities.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,222 @@
|
||||||
|
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.admin;
|
||||||
|
import static app.fedilab.android.BaseMainActivity.api;
|
||||||
|
import static app.fedilab.android.BaseMainActivity.client_id;
|
||||||
|
import static app.fedilab.android.BaseMainActivity.client_secret;
|
||||||
|
import static app.fedilab.android.BaseMainActivity.currentInstance;
|
||||||
|
import static app.fedilab.android.BaseMainActivity.software;
|
||||||
|
import static app.fedilab.android.helper.Helper.PREF_USER_TOKEN;
|
||||||
|
import static app.fedilab.android.helper.MastodonHelper.REDIRECT_CONTENT_WEB;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
import androidx.navigation.fragment.NavHostFragment;
|
||||||
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import app.fedilab.android.BaseMainActivity;
|
||||||
|
import app.fedilab.android.R;
|
||||||
|
import app.fedilab.android.client.entities.Account;
|
||||||
|
import app.fedilab.android.client.entities.WellKnownNodeinfo;
|
||||||
|
import app.fedilab.android.exception.DBException;
|
||||||
|
import app.fedilab.android.helper.Helper;
|
||||||
|
import app.fedilab.android.helper.ThemeHelper;
|
||||||
|
import app.fedilab.android.ui.fragment.login.FragmentLoginMain;
|
||||||
|
import app.fedilab.android.viewmodel.mastodon.AccountsVM;
|
||||||
|
import app.fedilab.android.viewmodel.mastodon.OauthVM;
|
||||||
|
import es.dmoral.toasty.Toasty;
|
||||||
|
|
||||||
|
|
||||||
|
public class LoginActivity extends BaseActivity {
|
||||||
|
|
||||||
|
|
||||||
|
private final int PICK_IMPORT = 5557;
|
||||||
|
private String oldSearch;
|
||||||
|
private String autofilledInstance;
|
||||||
|
private WellKnownNodeinfo.NodeInfo nodeInfo;
|
||||||
|
private NavHostFragment host;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
ThemeHelper.applyTheme(this);
|
||||||
|
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(LoginActivity.this);
|
||||||
|
setContentView(new FrameLayout(this));
|
||||||
|
|
||||||
|
Helper.addFragment(getSupportFragmentManager(), android.R.id.content, new FragmentLoginMain(), null, null, null);
|
||||||
|
|
||||||
|
Bundle b = getIntent().getExtras();
|
||||||
|
if (b != null) {
|
||||||
|
autofilledInstance = b.getString("instance", null);
|
||||||
|
admin = b.getBoolean("admin", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//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
|
||||||
|
if (getIntent() != null && getIntent().getData() != null && getIntent().getData().toString().contains(REDIRECT_CONTENT_WEB + "?code=")) {
|
||||||
|
String url = getIntent().getData().toString();
|
||||||
|
String[] val = url.split("code=");
|
||||||
|
if (val.length < 2) {
|
||||||
|
Toasty.error(LoginActivity.this, getString(R.string.toast_code_error), Toast.LENGTH_LONG).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String code = val[1];
|
||||||
|
OauthVM oauthVM = new ViewModelProvider(LoginActivity.this).get(OauthVM.class);
|
||||||
|
//We are dealing with a Mastodon API
|
||||||
|
if (api == Account.API.MASTODON) {
|
||||||
|
//API call to get the user token
|
||||||
|
oauthVM.createToken(currentInstance, "authorization_code", client_id, client_secret, Helper.REDIRECT_CONTENT_WEB, Helper.OAUTH_SCOPES, code)
|
||||||
|
.observe(LoginActivity.this, tokenObj -> {
|
||||||
|
Account account = new Account();
|
||||||
|
account.client_id = BaseMainActivity.client_id;
|
||||||
|
account.client_secret = BaseMainActivity.client_secret;
|
||||||
|
account.token = tokenObj.token_type + " " + tokenObj.access_token;
|
||||||
|
account.api = api;
|
||||||
|
account.software = software;
|
||||||
|
account.instance = currentInstance;
|
||||||
|
//API call to retrieve account information for the new token
|
||||||
|
AccountsVM accountsVM = new ViewModelProvider(LoginActivity.this).get(AccountsVM.class);
|
||||||
|
accountsVM.getConnectedAccount(currentInstance, account.token).observe(LoginActivity.this, mastodonAccount -> {
|
||||||
|
account.mastodon_account = mastodonAccount;
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
account.user_id = mastodonAccount.id;
|
||||||
|
//update the database
|
||||||
|
new Account(LoginActivity.this).insertOrUpdate(account);
|
||||||
|
|
||||||
|
BaseMainActivity.currentToken = account.token;
|
||||||
|
BaseMainActivity.currentUserID = account.user_id;
|
||||||
|
api = Account.API.MASTODON;
|
||||||
|
SharedPreferences.Editor editor = sharedpreferences.edit();
|
||||||
|
editor.putString(PREF_USER_TOKEN, account.token);
|
||||||
|
editor.commit();
|
||||||
|
//The user is now aut
|
||||||
|
//The user is now authenticated, it will be redirected to MainActivity
|
||||||
|
Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||||
|
Runnable myRunnable = () -> {
|
||||||
|
Intent mainActivity = new Intent(LoginActivity.this, BaseMainActivity.class);
|
||||||
|
startActivity(mainActivity);
|
||||||
|
finish();
|
||||||
|
};
|
||||||
|
mainHandler.post(myRunnable);
|
||||||
|
} catch (DBException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(@NotNull Menu menu) {
|
||||||
|
// Inflate the menu; this adds items to the action bar if it is present.
|
||||||
|
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);
|
||||||
|
/* boolean security_provider = sharedpreferences.getBoolean(getString(R.string.SET_SECURITY_PROVIDER), true);
|
||||||
|
menu.findItem(R.id.action_provider).setChecked(security_provider);*/
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(@NotNull MenuItem item) {
|
||||||
|
// Handle action bar item clicks here. The action bar will
|
||||||
|
// automatically handle clicks on the Home/Up button, so long
|
||||||
|
// as you specify a parent activity in AndroidManifest.xml.
|
||||||
|
int id = item.getItemId();
|
||||||
|
|
||||||
|
//noinspection SimplifiableIfStatement
|
||||||
|
if (id == R.id.action_about) {
|
||||||
|
// Intent intent = new Intent(LoginActivity.this, AboutActivity.class);
|
||||||
|
// startActivity(intent);
|
||||||
|
} else if (id == R.id.action_privacy) {
|
||||||
|
// Intent intent = new Intent(LoginActivity.this, PrivacyActivity.class);
|
||||||
|
// startActivity(intent);
|
||||||
|
} else if (id == R.id.action_proxy) {
|
||||||
|
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_provider) {
|
||||||
|
/* item.setChecked(!item.isChecked());
|
||||||
|
SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE);
|
||||||
|
SharedPreferences.Editor editor = sharedpreferences.edit();
|
||||||
|
editor.putBoolean(getString(R.string.SET_SECURITY_PROVIDER), item.isChecked());
|
||||||
|
editor.apply();*/
|
||||||
|
return false;
|
||||||
|
} else if (id == R.id.action_import_data) {
|
||||||
|
/* if (ContextCompat.checkSelfPermission(LoginActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) !=
|
||||||
|
PackageManager.PERMISSION_GRANTED) {
|
||||||
|
ActivityCompat.requestPermissions(LoginActivity.this,
|
||||||
|
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
|
||||||
|
TootActivity.MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);
|
||||||
|
return true;
|
||||||
|
}*/
|
||||||
|
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||||
|
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||||
|
intent.setType("*/*");
|
||||||
|
String[] mimetypes = {"*/*"};
|
||||||
|
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimetypes);
|
||||||
|
startActivityForResult(intent, PICK_IMPORT);
|
||||||
|
}
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,323 @@
|
||||||
|
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.os.Bundle;
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.InputFilter;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
import androidx.fragment.app.FragmentTransaction;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import app.fedilab.android.BaseMainActivity;
|
||||||
|
import app.fedilab.android.R;
|
||||||
|
import app.fedilab.android.client.entities.Timeline;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Account;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.MastodonList;
|
||||||
|
import app.fedilab.android.databinding.ActivityListBinding;
|
||||||
|
import app.fedilab.android.databinding.PopupAddListBinding;
|
||||||
|
import app.fedilab.android.databinding.PopupManageAccountsListBinding;
|
||||||
|
import app.fedilab.android.helper.Helper;
|
||||||
|
import app.fedilab.android.helper.ThemeHelper;
|
||||||
|
import app.fedilab.android.ui.drawer.AccountListAdapter;
|
||||||
|
import app.fedilab.android.ui.drawer.MastodonListAdapter;
|
||||||
|
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTimeline;
|
||||||
|
import app.fedilab.android.viewmodel.mastodon.AccountsVM;
|
||||||
|
import app.fedilab.android.viewmodel.mastodon.TimelinesVM;
|
||||||
|
import es.dmoral.toasty.Toasty;
|
||||||
|
|
||||||
|
|
||||||
|
public class MastodonListActivity extends BaseActivity implements MastodonListAdapter.ActionOnList {
|
||||||
|
|
||||||
|
|
||||||
|
AccountListAdapter accountsInListAdapter;
|
||||||
|
private ActivityListBinding binding;
|
||||||
|
private boolean canGoBack;
|
||||||
|
private TimelinesVM timelinesVM;
|
||||||
|
private MastodonList mastodonList;
|
||||||
|
private ArrayList<MastodonList> mastodonListList;
|
||||||
|
private MastodonListAdapter mastodonListAdapter;
|
||||||
|
private AccountsVM accountsVM;
|
||||||
|
private List<Account> accountsInList;
|
||||||
|
private boolean flagLoading;
|
||||||
|
private String max_id;
|
||||||
|
private FragmentMastodonTimeline fragmentMastodonTimeline;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
ThemeHelper.applyThemeBar(this);
|
||||||
|
binding = ActivityListBinding.inflate(getLayoutInflater());
|
||||||
|
setContentView(binding.getRoot());
|
||||||
|
canGoBack = false;
|
||||||
|
if (getSupportActionBar() != null) {
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||||
|
}
|
||||||
|
flagLoading = false;
|
||||||
|
max_id = null;
|
||||||
|
accountsVM = new ViewModelProvider(MastodonListActivity.this).get(AccountsVM.class);
|
||||||
|
timelinesVM = new ViewModelProvider(MastodonListActivity.this).get(TimelinesVM.class);
|
||||||
|
timelinesVM.getLists(BaseMainActivity.currentInstance, BaseMainActivity.currentToken)
|
||||||
|
.observe(MastodonListActivity.this, mastodonLists -> {
|
||||||
|
if (mastodonLists != null && mastodonLists.size() > 0) {
|
||||||
|
mastodonListList = new ArrayList<>(mastodonLists);
|
||||||
|
mastodonListAdapter = new MastodonListAdapter(mastodonListList);
|
||||||
|
mastodonListAdapter.actionOnList = this;
|
||||||
|
binding.notContent.setVisibility(View.GONE);
|
||||||
|
binding.recyclerView.setAdapter(mastodonListAdapter);
|
||||||
|
binding.recyclerView.setLayoutManager(new LinearLayoutManager(MastodonListActivity.this));
|
||||||
|
} else {
|
||||||
|
binding.notContent.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (item.getItemId() == android.R.id.home) {
|
||||||
|
onBackPressed();
|
||||||
|
return true;
|
||||||
|
} else if (item.getItemId() == R.id.action_manage_users) {
|
||||||
|
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(MastodonListActivity.this, Helper.dialogStyle());
|
||||||
|
PopupManageAccountsListBinding popupManageAccountsListBinding = PopupManageAccountsListBinding.inflate(getLayoutInflater());
|
||||||
|
dialogBuilder.setView(popupManageAccountsListBinding.getRoot());
|
||||||
|
popupManageAccountsListBinding.loader.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
popupManageAccountsListBinding.searchAccount.setOnTouchListener((v, event) -> {
|
||||||
|
final int DRAWABLE_RIGHT = 2;
|
||||||
|
if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||||
|
if (popupManageAccountsListBinding.searchAccount.length() > 0 && event.getRawX() >= (popupManageAccountsListBinding.searchAccount.getRight() - popupManageAccountsListBinding.searchAccount.getCompoundDrawables()[DRAWABLE_RIGHT].getBounds().width())) {
|
||||||
|
popupManageAccountsListBinding.searchAccount.setText("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
timelinesVM.getAccountsInList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, mastodonList.id, null, null, 10)
|
||||||
|
.observe(MastodonListActivity.this, accounts -> {
|
||||||
|
popupManageAccountsListBinding.loader.setVisibility(View.GONE);
|
||||||
|
accountsInList = accounts;
|
||||||
|
if (accountsInList == null) {
|
||||||
|
accountsInList = new ArrayList<>();
|
||||||
|
}
|
||||||
|
if (accountsInList.size() > 0) {
|
||||||
|
max_id = accountsInList.get(accountsInList.size() - 1).id;
|
||||||
|
popupManageAccountsListBinding.noContent.setVisibility(View.GONE);
|
||||||
|
popupManageAccountsListBinding.lvAccountsCurrent.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
popupManageAccountsListBinding.noContent.setVisibility(View.VISIBLE);
|
||||||
|
popupManageAccountsListBinding.lvAccountsCurrent.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
accountsInListAdapter = new AccountListAdapter(mastodonList, accountsInList, null);
|
||||||
|
popupManageAccountsListBinding.lvAccountsCurrent.setAdapter(accountsInListAdapter);
|
||||||
|
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(MastodonListActivity.this);
|
||||||
|
popupManageAccountsListBinding.lvAccountsCurrent.setLayoutManager(linearLayoutManager);
|
||||||
|
popupManageAccountsListBinding.lvAccountsCurrent.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||||
|
@Override
|
||||||
|
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
|
||||||
|
int firstVisibleItem = linearLayoutManager.findFirstVisibleItemPosition();
|
||||||
|
if (dy > 0) {
|
||||||
|
int visibleItemCount = linearLayoutManager.getChildCount();
|
||||||
|
int totalItemCount = linearLayoutManager.getItemCount();
|
||||||
|
if (firstVisibleItem + visibleItemCount == totalItemCount) {
|
||||||
|
if (!flagLoading) {
|
||||||
|
flagLoading = true;
|
||||||
|
timelinesVM.getAccountsInList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, mastodonList.id, max_id, null, 10)
|
||||||
|
.observe(MastodonListActivity.this, accounts -> {
|
||||||
|
if (accounts != null && accounts.size() > 0) {
|
||||||
|
int position = accountsInList.size();
|
||||||
|
max_id = accountsInList.get(accounts.size() - 1).id;
|
||||||
|
accountsInList.addAll(accounts);
|
||||||
|
accountsInListAdapter.notifyItemRangeChanged(position, accounts.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
popupManageAccountsListBinding.searchAccount.addTextChangedListener(new TextWatcher() {
|
||||||
|
@Override
|
||||||
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
|
if (count > 0) {
|
||||||
|
popupManageAccountsListBinding.searchAccount.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_baseline_close_24, 0);
|
||||||
|
} else {
|
||||||
|
popupManageAccountsListBinding.searchAccount.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_baseline_search_24, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable s) {
|
||||||
|
if (s != null && s.length() > 0) {
|
||||||
|
accountsVM.searchAccounts(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, s.toString(), 20, true, true)
|
||||||
|
.observe(MastodonListActivity.this, accounts -> {
|
||||||
|
popupManageAccountsListBinding.lvAccountsSearch.setVisibility(View.VISIBLE);
|
||||||
|
popupManageAccountsListBinding.lvAccountsCurrent.setVisibility(View.GONE);
|
||||||
|
AccountListAdapter accountListAdapter = new AccountListAdapter(mastodonList, accountsInList, accounts);
|
||||||
|
popupManageAccountsListBinding.lvAccountsSearch.setAdapter(accountListAdapter);
|
||||||
|
popupManageAccountsListBinding.lvAccountsSearch.setLayoutManager(new LinearLayoutManager(MastodonListActivity.this));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
popupManageAccountsListBinding.lvAccountsSearch.setVisibility(View.GONE);
|
||||||
|
popupManageAccountsListBinding.lvAccountsCurrent.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
dialogBuilder.setPositiveButton(R.string.close, (dialog, id) -> dialog.dismiss());
|
||||||
|
dialogBuilder.create().show();
|
||||||
|
} else if (item.getItemId() == R.id.action_delete && mastodonList != null) {
|
||||||
|
AlertDialog.Builder alt_bld = new AlertDialog.Builder(MastodonListActivity.this, Helper.dialogStyle());
|
||||||
|
alt_bld.setTitle(R.string.action_lists_delete);
|
||||||
|
alt_bld.setMessage(R.string.action_lists_confirm_delete);
|
||||||
|
alt_bld.setPositiveButton(R.string.delete, (dialog, id) -> {
|
||||||
|
timelinesVM.deleteList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, mastodonList.id);
|
||||||
|
int position = 0;
|
||||||
|
for (MastodonList mastodonListTmp : mastodonListList) {
|
||||||
|
if (mastodonListTmp.id.equalsIgnoreCase(mastodonList.id)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
position++;
|
||||||
|
}
|
||||||
|
mastodonListList.remove(position);
|
||||||
|
mastodonListAdapter.notifyItemRemoved(position);
|
||||||
|
ThemeHelper.slideViewsToRight(binding.fragmentContainer, binding.recyclerView, () -> {
|
||||||
|
if (fragmentMastodonTimeline != null) {
|
||||||
|
fragmentMastodonTimeline.onDestroyView();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (mastodonListList.size() == 0) {
|
||||||
|
binding.notContent.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
binding.notContent.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
Bundle b = new Bundle();
|
||||||
|
b.putBoolean(Helper.RECEIVE_REDRAW_TOPBAR, true);
|
||||||
|
Intent intentBD = new Intent(Helper.BROADCAST_DATA);
|
||||||
|
b.putSerializable(Helper.RECEIVE_MASTODON_LIST, mastodonListList);
|
||||||
|
intentBD.putExtras(b);
|
||||||
|
LocalBroadcastManager.getInstance(MastodonListActivity.this).sendBroadcast(intentBD);
|
||||||
|
});
|
||||||
|
alt_bld.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss());
|
||||||
|
AlertDialog alert = alt_bld.create();
|
||||||
|
alert.show();
|
||||||
|
} else if (item.getItemId() == R.id.action_add_list) {
|
||||||
|
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(MastodonListActivity.this, Helper.dialogStyle());
|
||||||
|
PopupAddListBinding popupAddListBinding = PopupAddListBinding.inflate(getLayoutInflater());
|
||||||
|
dialogBuilder.setView(popupAddListBinding.getRoot());
|
||||||
|
popupAddListBinding.addList.setFilters(new InputFilter[]{new InputFilter.LengthFilter(255)});
|
||||||
|
dialogBuilder.setPositiveButton(R.string.validate, (dialog, id) -> {
|
||||||
|
if (popupAddListBinding.addList.getText() != null && popupAddListBinding.addList.getText().toString().trim().length() > 0) {
|
||||||
|
timelinesVM.createList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, popupAddListBinding.addList.getText().toString().trim(), null)
|
||||||
|
.observe(MastodonListActivity.this, newMastodonList -> {
|
||||||
|
if (newMastodonList != null) {
|
||||||
|
mastodonListList.add(0, newMastodonList);
|
||||||
|
mastodonListAdapter.notifyItemInserted(0);
|
||||||
|
} else {
|
||||||
|
Toasty.error(MastodonListActivity.this, getString(R.string.toast_error), Toasty.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
Bundle b = new Bundle();
|
||||||
|
b.putBoolean(Helper.RECEIVE_REDRAW_TOPBAR, true);
|
||||||
|
Intent intentBD = new Intent(Helper.BROADCAST_DATA);
|
||||||
|
b.putSerializable(Helper.RECEIVE_MASTODON_LIST, mastodonListList);
|
||||||
|
intentBD.putExtras(b);
|
||||||
|
LocalBroadcastManager.getInstance(MastodonListActivity.this).sendBroadcast(intentBD);
|
||||||
|
});
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void click(MastodonList mastodonList) {
|
||||||
|
|
||||||
|
this.mastodonList = mastodonList;
|
||||||
|
canGoBack = true;
|
||||||
|
ThemeHelper.slideViewsToLeft(binding.recyclerView, binding.fragmentContainer, () -> {
|
||||||
|
fragmentMastodonTimeline = new FragmentMastodonTimeline();
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putSerializable(Helper.ARG_LIST_ID, mastodonList.id);
|
||||||
|
bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.LIST);
|
||||||
|
setTitle(mastodonList.title);
|
||||||
|
fragmentMastodonTimeline.setArguments(bundle);
|
||||||
|
FragmentManager fragmentManager = getSupportFragmentManager();
|
||||||
|
FragmentTransaction fragmentTransaction =
|
||||||
|
fragmentManager.beginTransaction();
|
||||||
|
fragmentTransaction.replace(R.id.fragment_container, fragmentMastodonTimeline);
|
||||||
|
fragmentTransaction.commit();
|
||||||
|
});
|
||||||
|
invalidateOptionsMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(@NonNull Menu menu) {
|
||||||
|
if (binding != null) {
|
||||||
|
if (binding.recyclerView.getVisibility() == View.VISIBLE) {
|
||||||
|
getMenuInflater().inflate(R.menu.menu_main_list, menu);
|
||||||
|
} else {
|
||||||
|
getMenuInflater().inflate(R.menu.menu_list, menu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBackPressed() {
|
||||||
|
if (canGoBack) {
|
||||||
|
canGoBack = false;
|
||||||
|
ThemeHelper.slideViewsToRight(binding.fragmentContainer, binding.recyclerView, () -> {
|
||||||
|
if (fragmentMastodonTimeline != null) {
|
||||||
|
fragmentMastodonTimeline.onDestroyView();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setTitle(R.string.action_lists);
|
||||||
|
invalidateOptionsMenu();
|
||||||
|
} else {
|
||||||
|
super.onBackPressed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,437 @@
|
||||||
|
/* 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>. */
|
||||||
|
package app.fedilab.android.activities;
|
||||||
|
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.app.DownloadManager;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.graphics.Point;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.view.Display;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.Window;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.core.app.ActivityCompat;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
import androidx.fragment.app.FragmentStatePagerAdapter;
|
||||||
|
import androidx.preference.PreferenceManager;
|
||||||
|
import androidx.viewpager.widget.PagerAdapter;
|
||||||
|
import androidx.viewpager.widget.ViewPager;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import app.fedilab.android.R;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Attachment;
|
||||||
|
import app.fedilab.android.databinding.ActivityMediaPagerBinding;
|
||||||
|
import app.fedilab.android.helper.Helper;
|
||||||
|
import app.fedilab.android.helper.MediaHelper;
|
||||||
|
import app.fedilab.android.helper.ThemeHelper;
|
||||||
|
import app.fedilab.android.interfaces.OnDownloadInterface;
|
||||||
|
import app.fedilab.android.ui.fragment.media.FragmentMedia;
|
||||||
|
import es.dmoral.toasty.Toasty;
|
||||||
|
|
||||||
|
|
||||||
|
public class MediaActivity extends BaseActivity implements OnDownloadInterface {
|
||||||
|
|
||||||
|
int flags;
|
||||||
|
private ArrayList<Attachment> attachments;
|
||||||
|
private int mediaPosition;
|
||||||
|
private long downloadID;
|
||||||
|
|
||||||
|
private final BroadcastReceiver onDownloadComplete = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
|
||||||
|
if (downloadID == id) {
|
||||||
|
DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
|
||||||
|
assert manager != null;
|
||||||
|
Uri uri = manager.getUriForDownloadedFile(downloadID);
|
||||||
|
Intent shareIntent = new Intent(Intent.ACTION_SEND);
|
||||||
|
shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
|
||||||
|
ContentResolver cR = context.getContentResolver();
|
||||||
|
if (cR != null && uri != null) {
|
||||||
|
shareIntent.setType(cR.getType(uri));
|
||||||
|
try {
|
||||||
|
startActivity(shareIntent);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Toasty.error(context, context.getString(R.string.toast_error), Toasty.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Toasty.success(context, context.getString(R.string.save_over), Toasty.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private boolean fullscreen;
|
||||||
|
private Handler handler;
|
||||||
|
private int minTouch, maxTouch;
|
||||||
|
private float startX;
|
||||||
|
private float startY;
|
||||||
|
private FragmentMedia mCurrentFragment;
|
||||||
|
private ActivityMediaPagerBinding binding;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
getWindow().requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
|
||||||
|
ThemeHelper.applyThemeBar(this);
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
ActivityCompat.postponeEnterTransition(MediaActivity.this);
|
||||||
|
binding = ActivityMediaPagerBinding.inflate(getLayoutInflater());
|
||||||
|
setContentView(binding.getRoot());
|
||||||
|
|
||||||
|
|
||||||
|
fullscreen = false;
|
||||||
|
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(MediaActivity.this);
|
||||||
|
final int med_desc_timeout = sharedpreferences.getInt(getString(R.string.SET_MED_DESC_TIMEOUT), 3) * 1000;
|
||||||
|
flags = getWindow().getDecorView().getSystemUiVisibility();
|
||||||
|
Bundle b = getIntent().getExtras();
|
||||||
|
if (b != null) {
|
||||||
|
mediaPosition = b.getInt(Helper.ARG_MEDIA_POSITION, 1);
|
||||||
|
attachments = (ArrayList<Attachment>) b.getSerializable(Helper.ARG_MEDIA_ARRAY);
|
||||||
|
}
|
||||||
|
if (getSupportActionBar() != null) {
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attachments == null || attachments.size() == 0)
|
||||||
|
finish();
|
||||||
|
|
||||||
|
setTitle("");
|
||||||
|
|
||||||
|
PagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
|
||||||
|
binding.mediaViewpager.setAdapter(mPagerAdapter);
|
||||||
|
|
||||||
|
binding.mediaViewpager.setCurrentItem(mediaPosition - 1);
|
||||||
|
binding.mediaViewpager.setOffscreenPageLimit(0);
|
||||||
|
binding.haulerView.setOnDragDismissedListener(dragDirection -> ActivityCompat.finishAfterTransition(MediaActivity.this));
|
||||||
|
registerReceiver(onDownloadComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
|
||||||
|
String description = attachments.get(mediaPosition - 1).description;
|
||||||
|
handler = new Handler();
|
||||||
|
if (description != null && description.trim().length() > 0 && description.trim().compareTo("null") != 0) {
|
||||||
|
binding.mediaDescription.setText(description);
|
||||||
|
binding.mediaDescription.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
handler.postDelayed(() -> {
|
||||||
|
if (binding != null && !binding.mediaDescription.hasSelection()) {
|
||||||
|
binding.mediaDescription.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}, med_desc_timeout);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (!binding.mediaDescription.hasSelection()) {
|
||||||
|
binding.mediaDescription.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.mediaViewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
|
||||||
|
public void onPageScrollStateChanged(int state) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onPageSelected(int position) {
|
||||||
|
String description = attachments.get(position).description;
|
||||||
|
if (handler != null) {
|
||||||
|
handler.removeCallbacksAndMessages(null);
|
||||||
|
}
|
||||||
|
handler = new Handler();
|
||||||
|
if (description != null && description.trim().length() > 0 && description.trim().compareTo("null") != 0) {
|
||||||
|
binding.mediaDescription.setText(description);
|
||||||
|
binding.mediaDescription.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
handler.postDelayed(() -> {
|
||||||
|
if (binding != null && !binding.mediaDescription.hasSelection()) {
|
||||||
|
binding.mediaDescription.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}, med_desc_timeout);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (!binding.mediaDescription.hasSelection()) {
|
||||||
|
binding.mediaDescription.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(@NonNull Menu menu) {
|
||||||
|
getMenuInflater().inflate(R.menu.menu_media, menu);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (item.getItemId() == android.R.id.home) {
|
||||||
|
finish();
|
||||||
|
ActivityCompat.finishAfterTransition(MediaActivity.this);
|
||||||
|
return true;
|
||||||
|
} else if (item.getItemId() == R.id.action_save) {
|
||||||
|
int position = binding.mediaViewpager.getCurrentItem();
|
||||||
|
Attachment attachment = attachments.get(position);
|
||||||
|
if (Build.VERSION.SDK_INT >= 23) {
|
||||||
|
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);
|
||||||
|
} else {
|
||||||
|
if (attachment.type.compareTo("image") == 0) {
|
||||||
|
MediaHelper.manageMove(MediaActivity.this, attachment.url, false);
|
||||||
|
} else {
|
||||||
|
MediaHelper.manageDownloadsNoPopup(MediaActivity.this, attachment.url);
|
||||||
|
downloadID = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (attachment.type.compareToIgnoreCase("image") == 0) {
|
||||||
|
MediaHelper.manageMove(MediaActivity.this, attachment.url, false);
|
||||||
|
} else {
|
||||||
|
MediaHelper.manageDownloadsNoPopup(MediaActivity.this, attachment.url);
|
||||||
|
downloadID = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (item.getItemId() == R.id.action_share) {
|
||||||
|
int position = binding.mediaViewpager.getCurrentItem();
|
||||||
|
Attachment attachment = attachments.get(position);
|
||||||
|
if (attachment.type.compareTo("image") == 0) {
|
||||||
|
MediaHelper.manageMove(MediaActivity.this, attachment.url, true);
|
||||||
|
} else if (attachment.type.equalsIgnoreCase("video") || attachment.type.equalsIgnoreCase("audio") || attachment.type.equalsIgnoreCase("gifv")) {
|
||||||
|
downloadID = MediaHelper.manageDownloadsNoPopup(MediaActivity.this, attachment.url);
|
||||||
|
} else {
|
||||||
|
if (Build.VERSION.SDK_INT >= 23) {
|
||||||
|
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_SHARE);
|
||||||
|
} else {
|
||||||
|
downloadID = MediaHelper.manageDownloadsNoPopup(MediaActivity.this, attachment.url);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
downloadID = MediaHelper.manageDownloadsNoPopup(MediaActivity.this, attachment.url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
|
if (requestCode == Helper.EXTERNAL_STORAGE_REQUEST_CODE_MEDIA_SAVE) {
|
||||||
|
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
int position = binding.mediaViewpager.getCurrentItem();
|
||||||
|
Attachment attachment = attachments.get(position);
|
||||||
|
if (attachment.type.compareToIgnoreCase("image") == 0) {
|
||||||
|
MediaHelper.manageMove(MediaActivity.this, attachment.url, false);
|
||||||
|
} else {
|
||||||
|
MediaHelper.manageDownloadsNoPopup(MediaActivity.this, attachment.url);
|
||||||
|
downloadID = -1;
|
||||||
|
}
|
||||||
|
} else { /*Todo: Toast "Storage Permission Required" */ }
|
||||||
|
} else if (requestCode == Helper.EXTERNAL_STORAGE_REQUEST_CODE_MEDIA_SHARE) {
|
||||||
|
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
int position = binding.mediaViewpager.getCurrentItem();
|
||||||
|
Attachment attachment = attachments.get(position);
|
||||||
|
downloadID = MediaHelper.manageDownloadsNoPopup(MediaActivity.this, attachment.url);
|
||||||
|
} else { /*Todo: Toast "Storage Permission Required" */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean dispatchTouchEvent(MotionEvent event) {
|
||||||
|
|
||||||
|
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(MediaActivity.this);
|
||||||
|
final int med_desc_timeout = sharedpreferences.getInt(getString(R.string.SET_MED_DESC_TIMEOUT), 3) * 1000;
|
||||||
|
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) {
|
||||||
|
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) {
|
||||||
|
binding.mediaDescription.setText(description);
|
||||||
|
binding.mediaDescription.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
handler.postDelayed(() -> {
|
||||||
|
if (binding != null && !binding.mediaDescription.hasSelection()) {
|
||||||
|
binding.mediaDescription.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}, med_desc_timeout);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (!binding.mediaDescription.hasSelection()) {
|
||||||
|
binding.mediaDescription.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
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
|
||||||
|
public void onDestroy() {
|
||||||
|
binding = null;
|
||||||
|
unregisterReceiver(onDownloadComplete);
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public FragmentMedia getCurrentFragment() {
|
||||||
|
return mCurrentFragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDownloaded(String saveFilePath, String downloadUrl, Error error) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUpdateProgress(int progress) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostResume() {
|
||||||
|
super.onPostResume();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean getFullScreen() {
|
||||||
|
return this.fullscreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFullscreen(boolean fullscreen) {
|
||||||
|
this.fullscreen = fullscreen;
|
||||||
|
if (!fullscreen) {
|
||||||
|
showSystemUI();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
hideSystemUI();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hideSystemUI() {
|
||||||
|
// Enables regular immersive mode.
|
||||||
|
// For "lean back" mode, remove SYSTEM_UI_FLAG_IMMERSIVE.
|
||||||
|
// Or for "sticky immersive," replace it with SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||||
|
View decorView = getWindow().getDecorView();
|
||||||
|
decorView.setSystemUiVisibility(
|
||||||
|
View.SYSTEM_UI_FLAG_IMMERSIVE
|
||||||
|
// Set the content to appear under the system bars so that the
|
||||||
|
// content doesn't resize when the system bars hide and show.
|
||||||
|
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||||
|
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||||
|
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||||
|
// Hide the nav bar and status bar
|
||||||
|
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||||
|
| View.SYSTEM_UI_FLAG_FULLSCREEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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(
|
||||||
|
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||||
|
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Media Pager
|
||||||
|
*/
|
||||||
|
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
|
||||||
|
|
||||||
|
ScreenSlidePagerAdapter(FragmentManager fm) {
|
||||||
|
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Fragment getItem(int position) {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
FragmentMedia mediaSliderFragment = new FragmentMedia();
|
||||||
|
bundle.putInt(Helper.ARG_MEDIA_POSITION, position);
|
||||||
|
bundle.putSerializable(Helper.ARG_MEDIA_ATTACHMENT, attachments.get(position));
|
||||||
|
mediaSliderFragment.setArguments(bundle);
|
||||||
|
return mediaSliderFragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPrimaryItem(@NotNull ViewGroup container, int position, @NotNull Object object) {
|
||||||
|
if (getCurrentFragment() != object) {
|
||||||
|
mCurrentFragment = ((FragmentMedia) object);
|
||||||
|
}
|
||||||
|
super.setPrimaryItem(container, position, object);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
return attachments.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,108 @@
|
||||||
|
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.SharedPreferences;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
|
||||||
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import app.fedilab.android.R;
|
||||||
|
import app.fedilab.android.databinding.ActivityProxyBinding;
|
||||||
|
|
||||||
|
|
||||||
|
public class ProxyActivity extends BaseActivity {
|
||||||
|
|
||||||
|
private ActivityProxyBinding binding;
|
||||||
|
private int position;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(ProxyActivity.this);
|
||||||
|
binding = ActivityProxyBinding.inflate(getLayoutInflater());
|
||||||
|
setContentView(binding.getRoot());
|
||||||
|
getWindow().setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
if (getSupportActionBar() != null)
|
||||||
|
getSupportActionBar().hide();
|
||||||
|
|
||||||
|
//Enable proxy
|
||||||
|
boolean enable_proxy = sharedpreferences.getBoolean(getString(R.string.SET_PROXY_ENABLED), false);
|
||||||
|
binding.enableProxy.setChecked(enable_proxy);
|
||||||
|
position = 0;
|
||||||
|
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);
|
||||||
|
final String login = sharedpreferences.getString(getString(R.string.SET_PROXY_LOGIN), null);
|
||||||
|
final String pwd = sharedpreferences.getString(getString(R.string.SET_PROXY_PASSWORD), null);
|
||||||
|
if (hostVal.length() > 0) {
|
||||||
|
binding.host.setText(hostVal);
|
||||||
|
}
|
||||||
|
binding.port.setText(String.valueOf(portVal));
|
||||||
|
if (login != null && login.length() > 0) {
|
||||||
|
binding.proxyLogin.setText(login);
|
||||||
|
}
|
||||||
|
if (pwd != null && binding.proxyPassword.length() > 0) {
|
||||||
|
binding.proxyPassword.setText(pwd);
|
||||||
|
}
|
||||||
|
ArrayAdapter<CharSequence> adapterTrans = ArrayAdapter.createFromResource(ProxyActivity.this,
|
||||||
|
R.array.proxy_type_choice, android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
binding.type.setAdapter(adapterTrans);
|
||||||
|
binding.type.setSelection(sharedpreferences.getInt(getString(R.string.SET_PROXY_TYPE), 0), false);
|
||||||
|
binding.type.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||||
|
@Override
|
||||||
|
public void onItemSelected(AdapterView<?> parent, View view, int p, long id) {
|
||||||
|
position = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNothingSelected(AdapterView<?> parent) {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
binding.setProxySave.setOnClickListener(view -> {
|
||||||
|
String hostVal1 = binding.host.getText().toString().trim();
|
||||||
|
String portVal1 = binding.port.getText().toString().trim();
|
||||||
|
String proxy_loginVal = binding.proxyLogin.getText().toString().trim();
|
||||||
|
String proxy_passwordVal = binding.proxyPassword.getText().toString().trim();
|
||||||
|
SharedPreferences.Editor editor = sharedpreferences.edit();
|
||||||
|
editor.putBoolean(getString(R.string.SET_PROXY_ENABLED), binding.enableProxy.isChecked());
|
||||||
|
editor.putInt(getString(R.string.SET_PROXY_TYPE), position);
|
||||||
|
editor.putString(getString(R.string.SET_PROXY_HOST), hostVal1);
|
||||||
|
if (portVal1.matches("\\d+"))
|
||||||
|
editor.putInt(getString(R.string.SET_PROXY_PORT), Integer.parseInt(portVal1));
|
||||||
|
editor.putString(getString(R.string.SET_PROXY_LOGIN), proxy_loginVal);
|
||||||
|
editor.putString(getString(R.string.SET_PROXY_PASSWORD), proxy_passwordVal);
|
||||||
|
editor.apply();
|
||||||
|
finish();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (item.getItemId() == android.R.id.home) {
|
||||||
|
finish();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,352 @@
|
||||||
|
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.helper.PinnedTimelineHelper.sortPositionAsc;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.InputFilter;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import app.fedilab.android.R;
|
||||||
|
import app.fedilab.android.client.entities.InstanceSocial;
|
||||||
|
import app.fedilab.android.client.entities.Pinned;
|
||||||
|
import app.fedilab.android.client.entities.Timeline;
|
||||||
|
import app.fedilab.android.client.entities.app.PinnedTimeline;
|
||||||
|
import app.fedilab.android.client.entities.app.RemoteInstance;
|
||||||
|
import app.fedilab.android.databinding.ActivityReorderTabsBinding;
|
||||||
|
import app.fedilab.android.databinding.PopupSearchInstanceBinding;
|
||||||
|
import app.fedilab.android.exception.DBException;
|
||||||
|
import app.fedilab.android.helper.Helper;
|
||||||
|
import app.fedilab.android.helper.ThemeHelper;
|
||||||
|
import app.fedilab.android.helper.itemtouchhelper.OnStartDragListener;
|
||||||
|
import app.fedilab.android.helper.itemtouchhelper.OnUndoListener;
|
||||||
|
import app.fedilab.android.helper.itemtouchhelper.SimpleItemTouchHelperCallback;
|
||||||
|
import app.fedilab.android.ui.drawer.ReorderTabAdapter;
|
||||||
|
import app.fedilab.android.viewmodel.mastodon.InstanceSocialVM;
|
||||||
|
import app.fedilab.android.viewmodel.mastodon.ReorderVM;
|
||||||
|
import es.dmoral.toasty.Toasty;
|
||||||
|
import okhttp3.Call;
|
||||||
|
import okhttp3.Callback;
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
|
import okhttp3.Request;
|
||||||
|
import okhttp3.Response;
|
||||||
|
|
||||||
|
|
||||||
|
public class ReorderTimelinesActivity extends BaseActivity implements OnStartDragListener, OnUndoListener {
|
||||||
|
|
||||||
|
|
||||||
|
private ItemTouchHelper touchHelper;
|
||||||
|
private ReorderTabAdapter adapter;
|
||||||
|
private boolean searchInstanceRunning;
|
||||||
|
private String oldSearch;
|
||||||
|
private Pinned pinned;
|
||||||
|
private ActivityReorderTabsBinding binding;
|
||||||
|
private boolean changes;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
ThemeHelper.applyThemeBar(this);
|
||||||
|
binding = ActivityReorderTabsBinding.inflate(getLayoutInflater());
|
||||||
|
setContentView(binding.getRoot());
|
||||||
|
searchInstanceRunning = false;
|
||||||
|
if (getSupportActionBar() != null)
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
|
||||||
|
changes = false;
|
||||||
|
ReorderVM reorderVM = new ViewModelProvider(ReorderTimelinesActivity.this).get(ReorderVM.class);
|
||||||
|
reorderVM.getPinned().observe(ReorderTimelinesActivity.this, _pinned -> {
|
||||||
|
this.pinned = _pinned;
|
||||||
|
if (this.pinned == null) {
|
||||||
|
this.pinned = new Pinned();
|
||||||
|
this.pinned.pinnedTimelines = new ArrayList<>();
|
||||||
|
}
|
||||||
|
sortPositionAsc(this.pinned.pinnedTimelines);
|
||||||
|
adapter = new ReorderTabAdapter(this.pinned, ReorderTimelinesActivity.this, ReorderTimelinesActivity.this);
|
||||||
|
ItemTouchHelper.Callback callback =
|
||||||
|
new SimpleItemTouchHelperCallback(adapter);
|
||||||
|
touchHelper = new ItemTouchHelper(callback);
|
||||||
|
touchHelper.attachToRecyclerView(binding.lvReorderTabs);
|
||||||
|
binding.lvReorderTabs.setAdapter(adapter);
|
||||||
|
LinearLayoutManager mLayoutManager = new LinearLayoutManager(ReorderTimelinesActivity.this);
|
||||||
|
binding.lvReorderTabs.setLayoutManager(mLayoutManager);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@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) {
|
||||||
|
addInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(@NonNull Menu menu) {
|
||||||
|
getMenuInflater().inflate(R.menu.menu_reorder, menu);
|
||||||
|
return super.onCreateOptionsMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addInstance() {
|
||||||
|
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(ReorderTimelinesActivity.this, Helper.dialogStyle());
|
||||||
|
PopupSearchInstanceBinding popupSearchInstanceBinding = PopupSearchInstanceBinding.inflate(getLayoutInflater());
|
||||||
|
dialogBuilder.setView(popupSearchInstanceBinding.getRoot());
|
||||||
|
popupSearchInstanceBinding.setAttachmentGroup.setOnCheckedChangeListener((group, checkedId) -> {
|
||||||
|
if (checkedId == R.id.twitter_accounts) {
|
||||||
|
popupSearchInstanceBinding.searchInstance.setHint(R.string.list_of_twitter_accounts);
|
||||||
|
} else {
|
||||||
|
popupSearchInstanceBinding.searchInstance.setHint(R.string.instance);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
popupSearchInstanceBinding.searchInstance.setFilters(new InputFilter[]{new InputFilter.LengthFilter(60)});
|
||||||
|
dialogBuilder.setPositiveButton(R.string.validate, (dialog, id) -> {
|
||||||
|
String instanceName = popupSearchInstanceBinding.searchInstance.getText().toString().trim().replace("@", "");
|
||||||
|
new Thread(() -> {
|
||||||
|
String url = null;
|
||||||
|
if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.mastodon_instance)
|
||||||
|
url = "https://" + instanceName + "/api/v1/timelines/public?local=true";
|
||||||
|
else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.peertube_instance)
|
||||||
|
url = "https://" + instanceName + "/api/v1/videos/";
|
||||||
|
else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.pixelfed_instance) {
|
||||||
|
url = "https://" + instanceName + "/api/v1/timelines/public";
|
||||||
|
} else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.misskey_instance) {
|
||||||
|
url = "https://" + instanceName + "/api/notes/local-timeline";
|
||||||
|
} else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.gnu_instance) {
|
||||||
|
url = "https://" + instanceName + "/api/statuses/public_timeline.json";
|
||||||
|
}
|
||||||
|
OkHttpClient client = new OkHttpClient.Builder()
|
||||||
|
.connectTimeout(10, TimeUnit.SECONDS)
|
||||||
|
.writeTimeout(10, TimeUnit.SECONDS)
|
||||||
|
.proxy(Helper.getProxy(getApplication().getApplicationContext()))
|
||||||
|
.readTimeout(10, TimeUnit.SECONDS).build();
|
||||||
|
Request request;
|
||||||
|
if (url != null) {
|
||||||
|
request = new Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.build();
|
||||||
|
client.newCall(request).enqueue(new Callback() {
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Call call, @NonNull IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
runOnUiThread(() -> Toasty.warning(ReorderTimelinesActivity.this, getString(R.string.toast_instance_unavailable), Toast.LENGTH_LONG).show());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Call call, @NonNull final Response response) {
|
||||||
|
if (!response.isSuccessful()) {
|
||||||
|
runOnUiThread(() -> {
|
||||||
|
dialog.dismiss();
|
||||||
|
RemoteInstance.InstanceType instanceType = null;
|
||||||
|
if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.mastodon_instance) {
|
||||||
|
instanceType = RemoteInstance.InstanceType.MASTODON;
|
||||||
|
} else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.peertube_instance) {
|
||||||
|
instanceType = RemoteInstance.InstanceType.PEERTUBE;
|
||||||
|
} else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.pixelfed_instance) {
|
||||||
|
instanceType = RemoteInstance.InstanceType.PIXELFED;
|
||||||
|
} else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.misskey_instance) {
|
||||||
|
instanceType = RemoteInstance.InstanceType.MISSKEY;
|
||||||
|
} else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.gnu_instance) {
|
||||||
|
instanceType = RemoteInstance.InstanceType.GNU;
|
||||||
|
} else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.twitter_accounts) {
|
||||||
|
instanceType = RemoteInstance.InstanceType.NITTER;
|
||||||
|
}
|
||||||
|
RemoteInstance remoteInstance = new RemoteInstance();
|
||||||
|
remoteInstance.type = instanceType;
|
||||||
|
remoteInstance.host = instanceName;
|
||||||
|
PinnedTimeline pinnedTimeline = new PinnedTimeline();
|
||||||
|
pinnedTimeline.remoteInstance = remoteInstance;
|
||||||
|
pinnedTimeline.displayed = true;
|
||||||
|
pinnedTimeline.type = Timeline.TimeLineEnum.REMOTE;
|
||||||
|
pinnedTimeline.position = pinned.pinnedTimelines.size();
|
||||||
|
pinned.pinnedTimelines.add(pinnedTimeline);
|
||||||
|
try {
|
||||||
|
new Pinned(ReorderTimelinesActivity.this).updatePinned(pinned);
|
||||||
|
changes = true;
|
||||||
|
adapter.notifyItemInserted(pinned.pinnedTimelines.size());
|
||||||
|
} catch (DBException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
runOnUiThread(() -> Toasty.warning(ReorderTimelinesActivity.this, getString(R.string.toast_instance_unavailable), Toast.LENGTH_LONG).show());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
runOnUiThread(() -> Toasty.warning(ReorderTimelinesActivity.this, getString(R.string.toast_instance_unavailable), Toast.LENGTH_LONG).show());
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
});
|
||||||
|
AlertDialog alertDialog = dialogBuilder.create();
|
||||||
|
alertDialog.setOnDismissListener(dialogInterface -> {
|
||||||
|
//Hide keyboard
|
||||||
|
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
|
||||||
|
assert imm != null;
|
||||||
|
imm.hideSoftInputFromWindow(popupSearchInstanceBinding.searchInstance.getWindowToken(), 0);
|
||||||
|
});
|
||||||
|
if (alertDialog.getWindow() != null)
|
||||||
|
alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
|
||||||
|
alertDialog.show();
|
||||||
|
|
||||||
|
popupSearchInstanceBinding.searchInstance.setOnItemClickListener((parent, view1, position, id) -> oldSearch = parent.getItemAtPosition(position).toString().trim());
|
||||||
|
|
||||||
|
popupSearchInstanceBinding.searchInstance.addTextChangedListener(new TextWatcher() {
|
||||||
|
@Override
|
||||||
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable s) {
|
||||||
|
|
||||||
|
if (s.length() > 2 && !searchInstanceRunning) {
|
||||||
|
String query = s.toString().trim();
|
||||||
|
if (query.startsWith("http://")) {
|
||||||
|
query = query.replace("http://", "");
|
||||||
|
}
|
||||||
|
if (query.startsWith("https://")) {
|
||||||
|
query = query.replace("https://", "");
|
||||||
|
}
|
||||||
|
if (oldSearch == null || !oldSearch.equals(s.toString().trim())) {
|
||||||
|
searchInstanceRunning = true;
|
||||||
|
InstanceSocialVM instanceSocialVM = new ViewModelProvider(ReorderTimelinesActivity.this).get(InstanceSocialVM.class);
|
||||||
|
instanceSocialVM.getInstances(query).observe(ReorderTimelinesActivity.this, instanceSocialList -> {
|
||||||
|
popupSearchInstanceBinding.searchInstance.setAdapter(null);
|
||||||
|
String[] instances = new String[instanceSocialList.instances.size()];
|
||||||
|
int j = 0;
|
||||||
|
for (InstanceSocial.Instance instance : instanceSocialList.instances) {
|
||||||
|
instances[j] = instance.name;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
ArrayAdapter<String> arrayAdapter =
|
||||||
|
new ArrayAdapter<>(ReorderTimelinesActivity.this, android.R.layout.simple_list_item_1, instances);
|
||||||
|
popupSearchInstanceBinding.searchInstance.setAdapter(arrayAdapter);
|
||||||
|
if (popupSearchInstanceBinding.searchInstance.hasFocus() && !isFinishing())
|
||||||
|
popupSearchInstanceBinding.searchInstance.showDropDown();
|
||||||
|
if (oldSearch != null && oldSearch.equals(popupSearchInstanceBinding.searchInstance.getText().toString())) {
|
||||||
|
popupSearchInstanceBinding.searchInstance.dismissDropDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
oldSearch = s.toString().trim();
|
||||||
|
searchInstanceRunning = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
if (changes) {
|
||||||
|
//Update menu
|
||||||
|
Bundle b = new Bundle();
|
||||||
|
b.putBoolean(Helper.RECEIVE_REDRAW_TOPBAR, true);
|
||||||
|
Intent intentBD = new Intent(Helper.BROADCAST_DATA);
|
||||||
|
intentBD.putExtras(b);
|
||||||
|
LocalBroadcastManager.getInstance(ReorderTimelinesActivity.this).sendBroadcast(intentBD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStartDrag(RecyclerView.ViewHolder viewHolder) {
|
||||||
|
touchHelper.startDrag(viewHolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUndo(PinnedTimeline pinnedTimeline, int position) {
|
||||||
|
binding.undoContainer.setVisibility(View.VISIBLE);
|
||||||
|
switch (pinnedTimeline.type) {
|
||||||
|
case TAG:
|
||||||
|
binding.undoMessage.setText(R.string.reorder_tag_removed);
|
||||||
|
break;
|
||||||
|
case REMOTE:
|
||||||
|
binding.undoMessage.setText(R.string.reorder_instance_removed);
|
||||||
|
break;
|
||||||
|
case LIST:
|
||||||
|
binding.undoMessage.setText(R.string.reorder_list_deleted);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
binding.undoAction.setPaintFlags(binding.undoAction.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
|
||||||
|
Runnable runnable = () -> {
|
||||||
|
binding.undoContainer.setVisibility(View.GONE);
|
||||||
|
//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);
|
||||||
|
adapter.notifyItemRemoved(position);
|
||||||
|
try {
|
||||||
|
new Pinned(ReorderTimelinesActivity.this).updatePinned(pinned);
|
||||||
|
changes = true;
|
||||||
|
} catch (DBException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Handler handler = new Handler();
|
||||||
|
handler.postDelayed(runnable, 4000);
|
||||||
|
binding.undoAction.setOnClickListener(v -> {
|
||||||
|
pinned.pinnedTimelines.add(position, pinnedTimeline);
|
||||||
|
adapter.notifyItemInserted(position);
|
||||||
|
binding.undoContainer.setVisibility(View.GONE);
|
||||||
|
handler.removeCallbacks(runnable);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,327 @@
|
||||||
|
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.os.Bundle;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.RadioButton;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.widget.LinearLayoutCompat;
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
import androidx.fragment.app.FragmentTransaction;
|
||||||
|
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.Timeline;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Account;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.RelationShip;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Status;
|
||||||
|
import app.fedilab.android.databinding.ActivityReportBinding;
|
||||||
|
import app.fedilab.android.helper.Helper;
|
||||||
|
import app.fedilab.android.helper.ThemeHelper;
|
||||||
|
import app.fedilab.android.ui.drawer.RulesAdapter;
|
||||||
|
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTimeline;
|
||||||
|
import app.fedilab.android.viewmodel.mastodon.AccountsVM;
|
||||||
|
import es.dmoral.toasty.Toasty;
|
||||||
|
|
||||||
|
|
||||||
|
public class ReportActivity extends BaseActivity {
|
||||||
|
|
||||||
|
private ActivityReportBinding binding;
|
||||||
|
private Status status;
|
||||||
|
private Account account;
|
||||||
|
private AccountsVM accountsVM;
|
||||||
|
private RelationShip relationShip;
|
||||||
|
private List<String> statusIds;
|
||||||
|
private List<String> ruleIds;
|
||||||
|
private String comment;
|
||||||
|
private boolean forward;
|
||||||
|
private FragmentMastodonTimeline fragment;
|
||||||
|
private RulesAdapter rulesAdapter;
|
||||||
|
private String category;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
ThemeHelper.applyThemeBar(this);
|
||||||
|
binding = ActivityReportBinding.inflate(getLayoutInflater());
|
||||||
|
|
||||||
|
setContentView(binding.getRoot());
|
||||||
|
|
||||||
|
if (getSupportActionBar() != null)
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
|
||||||
|
Bundle b = getIntent().getExtras();
|
||||||
|
if (b != null) {
|
||||||
|
status = (Status) b.getSerializable(Helper.ARG_STATUS);
|
||||||
|
account = (Account) b.getSerializable(Helper.ARG_ACCOUNT);
|
||||||
|
}
|
||||||
|
if (account == null && status != null) {
|
||||||
|
account = status.account;
|
||||||
|
}
|
||||||
|
//The entry view
|
||||||
|
show(binding.screenReason);
|
||||||
|
|
||||||
|
setTitle(getString(R.string.report_title, "@" + account.acct));
|
||||||
|
|
||||||
|
binding.val1.setOnClickListener(v -> {
|
||||||
|
binding.actionButton.setEnabled(true);
|
||||||
|
setChecked(binding.val1);
|
||||||
|
});
|
||||||
|
binding.val2.setOnClickListener(v -> {
|
||||||
|
binding.actionButton.setEnabled(true);
|
||||||
|
setChecked(binding.val2);
|
||||||
|
});
|
||||||
|
binding.val3.setOnClickListener(v -> {
|
||||||
|
binding.actionButton.setEnabled(true);
|
||||||
|
setChecked(binding.val3);
|
||||||
|
});
|
||||||
|
binding.val4.setOnClickListener(v -> {
|
||||||
|
binding.actionButton.setEnabled(true);
|
||||||
|
setChecked(binding.val4);
|
||||||
|
});
|
||||||
|
|
||||||
|
binding.val1Container.setOnClickListener(v -> {
|
||||||
|
binding.actionButton.setEnabled(true);
|
||||||
|
setChecked(binding.val1);
|
||||||
|
});
|
||||||
|
binding.val2Container.setOnClickListener(v -> {
|
||||||
|
binding.actionButton.setEnabled(true);
|
||||||
|
setChecked(binding.val2);
|
||||||
|
});
|
||||||
|
binding.val3Container.setOnClickListener(v -> {
|
||||||
|
binding.actionButton.setEnabled(true);
|
||||||
|
setChecked(binding.val3);
|
||||||
|
});
|
||||||
|
binding.val4Container.setOnClickListener(v -> {
|
||||||
|
binding.actionButton.setEnabled(true);
|
||||||
|
setChecked(binding.val4);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
accountsVM = new ViewModelProvider(ReportActivity.this).get(AccountsVM.class);
|
||||||
|
binding.actionButton.setOnClickListener(v -> {
|
||||||
|
if (binding.screenReason.getVisibility() == View.VISIBLE) {
|
||||||
|
if (binding.val1.isChecked()) {
|
||||||
|
show(binding.screenIdontlike);
|
||||||
|
switchToIDontLike();
|
||||||
|
} else if (binding.val2.isChecked()) {
|
||||||
|
show(binding.screenSpam);
|
||||||
|
switchToSpam();
|
||||||
|
} else if (binding.val3.isChecked()) {
|
||||||
|
show(binding.screenViolateRules);
|
||||||
|
switchToRules();
|
||||||
|
} else if (binding.val4.isChecked()) {
|
||||||
|
show(binding.screenSomethingElse);
|
||||||
|
switchToSomethingElse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (item.getItemId() == android.R.id.home) {
|
||||||
|
finish();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void switchToRules() {
|
||||||
|
rulesAdapter = new RulesAdapter(BaseMainActivity.instanceInfo.rules);
|
||||||
|
binding.lvVr.setAdapter(rulesAdapter);
|
||||||
|
binding.lvVr.setLayoutManager(new LinearLayoutManager(ReportActivity.this));
|
||||||
|
binding.actionButton.setText(R.string.next);
|
||||||
|
binding.actionButton.setOnClickListener(v -> {
|
||||||
|
category = "violation";
|
||||||
|
show(binding.screenSomethingElse);
|
||||||
|
switchToSomethingElse();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void switchToIDontLike() {
|
||||||
|
List<String> ids = new ArrayList<>();
|
||||||
|
ids.add(account.id);
|
||||||
|
binding.unfollowTitle.setText(getString(R.string.report_1_unfollow_title, "@" + account.acct));
|
||||||
|
binding.muteTitle.setText(getString(R.string.report_1_mute_title, "@" + account.acct));
|
||||||
|
binding.blockTitle.setText(getString(R.string.report_1_block_title, "@" + account.acct));
|
||||||
|
accountsVM.getRelationships(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, ids)
|
||||||
|
.observe(ReportActivity.this, relationShips -> {
|
||||||
|
if (relationShips != null && relationShips.size() > 0) {
|
||||||
|
relationShip = relationShips.get(0);
|
||||||
|
if (relationShip.following) {
|
||||||
|
binding.groupUnfollow.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
binding.groupUnfollow.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
if (relationShip.blocking) {
|
||||||
|
binding.groupBlock.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
binding.groupBlock.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
if (relationShip.muting) {
|
||||||
|
binding.groupMute.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
binding.groupMute.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.actionUnfollow.setOnClickListener(v -> accountsVM.unfollow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id)
|
||||||
|
.observe(ReportActivity.this, rsUnfollow -> {
|
||||||
|
if (rsUnfollow != null) {
|
||||||
|
relationShip = rsUnfollow;
|
||||||
|
Toasty.info(ReportActivity.this, getString(R.string.toast_unfollow), Toasty.LENGTH_LONG).show();
|
||||||
|
binding.groupUnfollow.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
binding.actionMute.setOnClickListener(v -> accountsVM.mute(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, null, null)
|
||||||
|
.observe(ReportActivity.this, rsMute -> {
|
||||||
|
if (rsMute != null) {
|
||||||
|
relationShip = rsMute;
|
||||||
|
Toasty.info(ReportActivity.this, getString(R.string.toast_mute), Toasty.LENGTH_LONG).show();
|
||||||
|
binding.groupMute.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
binding.actionBlock.setOnClickListener(v -> accountsVM.block(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id)
|
||||||
|
.observe(ReportActivity.this, rsBlock -> {
|
||||||
|
if (rsBlock != null) {
|
||||||
|
relationShip = rsBlock;
|
||||||
|
Toasty.info(ReportActivity.this, getString(R.string.toast_block), Toasty.LENGTH_LONG).show();
|
||||||
|
binding.groupBlock.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
binding.actionButton.setText(R.string.done);
|
||||||
|
binding.actionButton.setOnClickListener(v -> finish());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void switchToSpam() {
|
||||||
|
|
||||||
|
fragment = new FragmentMastodonTimeline();
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.ACCOUNT_TIMELINE);
|
||||||
|
bundle.putSerializable(Helper.ARG_ACCOUNT, account);
|
||||||
|
//Set to display statuses with less options
|
||||||
|
bundle.putBoolean(Helper.ARG_MINIFIED, true);
|
||||||
|
if (status != null) {
|
||||||
|
status.isChecked = true;
|
||||||
|
bundle.putSerializable(Helper.ARG_STATUS_REPORT, status);
|
||||||
|
}
|
||||||
|
fragment.setArguments(bundle);
|
||||||
|
FragmentManager fragmentManager = getSupportFragmentManager();
|
||||||
|
FragmentTransaction fragmentTransaction =
|
||||||
|
fragmentManager.beginTransaction();
|
||||||
|
fragmentTransaction.replace(R.id.fram_spam_container, fragment);
|
||||||
|
fragmentTransaction.commit();
|
||||||
|
|
||||||
|
binding.actionButton.setText(R.string.next);
|
||||||
|
binding.actionButton.setOnClickListener(v -> {
|
||||||
|
category = "spam";
|
||||||
|
switchToMoreInfo();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void switchToSomethingElse() {
|
||||||
|
|
||||||
|
fragment = new FragmentMastodonTimeline();
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.ACCOUNT_TIMELINE);
|
||||||
|
bundle.putSerializable(Helper.ARG_ACCOUNT, account);
|
||||||
|
//Set to display statuses with less options
|
||||||
|
bundle.putBoolean(Helper.ARG_MINIFIED, true);
|
||||||
|
if (status != null) {
|
||||||
|
status.isChecked = true;
|
||||||
|
bundle.putSerializable(Helper.ARG_STATUS_REPORT, status);
|
||||||
|
}
|
||||||
|
fragment.setArguments(bundle);
|
||||||
|
FragmentManager fragmentManager = getSupportFragmentManager();
|
||||||
|
FragmentTransaction fragmentTransaction =
|
||||||
|
fragmentManager.beginTransaction();
|
||||||
|
fragmentTransaction.replace(R.id.fram_se_container, fragment);
|
||||||
|
fragmentTransaction.commit();
|
||||||
|
|
||||||
|
binding.actionButton.setText(R.string.next);
|
||||||
|
binding.actionButton.setOnClickListener(v -> {
|
||||||
|
if (category == null) {
|
||||||
|
category = "other";
|
||||||
|
}
|
||||||
|
switchToMoreInfo();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void switchToMoreInfo() {
|
||||||
|
|
||||||
|
show(binding.screenMoreDetails);
|
||||||
|
String[] domains = account.acct.split("@");
|
||||||
|
if (domains.length > 1) {
|
||||||
|
binding.forward.setOnCheckedChangeListener((compoundButton, checked) -> forward = checked);
|
||||||
|
binding.forward.setText(getString(R.string.report_more_forward, domains[1]));
|
||||||
|
} else {
|
||||||
|
forward = false;
|
||||||
|
binding.forwardBlock.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
binding.actionButton.setText(R.string.done);
|
||||||
|
binding.actionButton.setOnClickListener(v -> {
|
||||||
|
if (rulesAdapter != null) {
|
||||||
|
ruleIds = rulesAdapter.getChecked();
|
||||||
|
}
|
||||||
|
if (fragment != null) {
|
||||||
|
statusIds = fragment.getCheckedStatusesId();
|
||||||
|
}
|
||||||
|
comment = binding.reportMessage.getText().toString();
|
||||||
|
binding.actionButton.setEnabled(false);
|
||||||
|
accountsVM.report(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, category, statusIds, ruleIds, comment, forward)
|
||||||
|
.observe(ReportActivity.this, report -> {
|
||||||
|
Toasty.success(ReportActivity.this, R.string.report_sent, Toasty.LENGTH_LONG).show();
|
||||||
|
finish();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide all views and display the wanted one
|
||||||
|
*
|
||||||
|
* @param linearLayoutCompat LinearLayoutCompat - One of the report page
|
||||||
|
*/
|
||||||
|
private void show(LinearLayoutCompat linearLayoutCompat) {
|
||||||
|
binding.screenReason.setVisibility(View.GONE);
|
||||||
|
binding.screenIdontlike.setVisibility(View.GONE);
|
||||||
|
binding.screenSpam.setVisibility(View.GONE);
|
||||||
|
binding.screenViolateRules.setVisibility(View.GONE);
|
||||||
|
binding.screenSomethingElse.setVisibility(View.GONE);
|
||||||
|
binding.screenMoreDetails.setVisibility(View.GONE);
|
||||||
|
linearLayoutCompat.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setChecked(RadioButton radioButton) {
|
||||||
|
binding.val1.setChecked(false);
|
||||||
|
binding.val2.setChecked(false);
|
||||||
|
binding.val3.setChecked(false);
|
||||||
|
binding.val4.setChecked(false);
|
||||||
|
radioButton.setChecked(true);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
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.os.Bundle;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.ActionBar;
|
||||||
|
|
||||||
|
import com.google.android.material.tabs.TabLayout;
|
||||||
|
|
||||||
|
import app.fedilab.android.BaseMainActivity;
|
||||||
|
import app.fedilab.android.R;
|
||||||
|
import app.fedilab.android.databinding.ActivityScheduledBinding;
|
||||||
|
import app.fedilab.android.helper.MastodonHelper;
|
||||||
|
import app.fedilab.android.helper.ThemeHelper;
|
||||||
|
import app.fedilab.android.ui.pageadapter.FedilabScheduledPageAdapter;
|
||||||
|
|
||||||
|
public class ScheduledActivity extends BaseActivity {
|
||||||
|
|
||||||
|
private ActivityScheduledBinding binding;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
ThemeHelper.applyTheme(this);
|
||||||
|
binding = ActivityScheduledBinding.inflate(getLayoutInflater());
|
||||||
|
setContentView(binding.getRoot());
|
||||||
|
setSupportActionBar(binding.toolbar);
|
||||||
|
ActionBar actionBar = getSupportActionBar();
|
||||||
|
//Remove title
|
||||||
|
if (actionBar != null) {
|
||||||
|
actionBar.setDisplayShowTitleEnabled(false);
|
||||||
|
}
|
||||||
|
if (getSupportActionBar() != null) {
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
MastodonHelper.loadPPMastodon(binding.profilePicture, BaseMainActivity.accountWeakReference.get().mastodon_account);
|
||||||
|
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_client)));
|
||||||
|
binding.scheduleTablayout.addTab(binding.scheduleTablayout.newTab().setText(getString(R.string.reblog)));
|
||||||
|
|
||||||
|
binding.scheduleViewpager.setAdapter(new FedilabScheduledPageAdapter(getSupportFragmentManager()));
|
||||||
|
binding.scheduleViewpager.setOffscreenPageLimit(3);
|
||||||
|
binding.scheduleViewpager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(binding.scheduleTablayout));
|
||||||
|
binding.scheduleTablayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
|
||||||
|
@Override
|
||||||
|
public void onTabSelected(TabLayout.Tab tab) {
|
||||||
|
binding.scheduleViewpager.setCurrentItem(tab.getPosition());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTabUnselected(TabLayout.Tab tab) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTabReselected(TabLayout.Tab tab) {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (item.getItemId() == android.R.id.home) {
|
||||||
|
finish();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,240 @@
|
||||||
|
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.os.Bundle;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
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.FragmentManager;
|
||||||
|
import androidx.fragment.app.FragmentStatePagerAdapter;
|
||||||
|
import androidx.viewpager.widget.PagerAdapter;
|
||||||
|
import androidx.viewpager.widget.ViewPager;
|
||||||
|
|
||||||
|
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);
|
||||||
|
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) binding.searchViewpager.getAdapter().instantiateItem(binding.searchViewpager, tab.getPosition());
|
||||||
|
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();
|
||||||
|
PagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
PagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
|
||||||
|
binding.searchViewpager.setAdapter(mPagerAdapter);
|
||||||
|
|
||||||
|
binding.searchViewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPageSelected(int position) {
|
||||||
|
TabLayout.Tab tab = binding.searchTabLayout.getTabAt(position);
|
||||||
|
if (tab != null)
|
||||||
|
tab.select();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPageScrollStateChanged(int 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 FragmentStatePagerAdapter {
|
||||||
|
|
||||||
|
ScreenSlidePagerAdapter(FragmentManager fm) {
|
||||||
|
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Fragment getItem(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 void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,203 @@
|
||||||
|
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.os.Bundle;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
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.FragmentAdministrationSettings;
|
||||||
|
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);
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ADMINISTRATION:
|
||||||
|
FragmentAdministrationSettings fragmentAdministrationSettings = new FragmentAdministrationSettings();
|
||||||
|
fragmentTransaction.replace(R.id.fragment_container, fragmentAdministrationSettings);
|
||||||
|
currentFragment = fragmentAdministrationSettings;
|
||||||
|
category = getString(R.string.administration);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,140 @@
|
||||||
|
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 android.os.Bundle;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import app.fedilab.android.BaseMainActivity;
|
||||||
|
import app.fedilab.android.R;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Account;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Accounts;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Status;
|
||||||
|
import app.fedilab.android.databinding.ActivityStatusInfoBinding;
|
||||||
|
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.StatusesVM;
|
||||||
|
|
||||||
|
|
||||||
|
public class StatusInfoActivity extends BaseActivity {
|
||||||
|
|
||||||
|
private ActivityStatusInfoBinding binding;
|
||||||
|
private List<Account> accountList;
|
||||||
|
private AccountAdapter accountAdapter;
|
||||||
|
private String max_id;
|
||||||
|
private typeOfInfo type;
|
||||||
|
private boolean flagLoading;
|
||||||
|
private Status status;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
ThemeHelper.applyTheme(this);
|
||||||
|
binding = ActivityStatusInfoBinding.inflate(getLayoutInflater());
|
||||||
|
setContentView(binding.getRoot());
|
||||||
|
setSupportActionBar(binding.toolbar);
|
||||||
|
if (getSupportActionBar() != null) {
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||||
|
}
|
||||||
|
accountList = new ArrayList<>();
|
||||||
|
Bundle b = getIntent().getExtras();
|
||||||
|
if (b != null) {
|
||||||
|
type = (typeOfInfo) b.getSerializable(Helper.ARG_TYPE_OF_INFO);
|
||||||
|
status = (Status) b.getSerializable(Helper.ARG_STATUS);
|
||||||
|
}
|
||||||
|
if (type == null || status == null) {
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
flagLoading = false;
|
||||||
|
max_id = null;
|
||||||
|
binding.title.setText(type == typeOfInfo.BOOSTED_BY ? R.string.boosted_by : R.string.favourited_by);
|
||||||
|
StatusesVM statusesVM = new ViewModelProvider(StatusInfoActivity.this).get(StatusesVM.class);
|
||||||
|
accountAdapter = new AccountAdapter(accountList);
|
||||||
|
LinearLayoutManager mLayoutManager = new LinearLayoutManager(StatusInfoActivity.this);
|
||||||
|
binding.lvAccounts.setLayoutManager(mLayoutManager);
|
||||||
|
binding.lvAccounts.setAdapter(accountAdapter);
|
||||||
|
|
||||||
|
binding.lvAccounts.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||||
|
@Override
|
||||||
|
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
|
||||||
|
int firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition();
|
||||||
|
if (dy > 0) {
|
||||||
|
int visibleItemCount = mLayoutManager.getChildCount();
|
||||||
|
int totalItemCount = mLayoutManager.getItemCount();
|
||||||
|
if (firstVisibleItem + visibleItemCount == totalItemCount) {
|
||||||
|
if (!flagLoading) {
|
||||||
|
flagLoading = true;
|
||||||
|
binding.loadingNextAccounts.setVisibility(View.VISIBLE);
|
||||||
|
if (type == typeOfInfo.BOOSTED_BY) {
|
||||||
|
statusesVM.rebloggedBy(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, status.id, max_id, null, null).observe(StatusInfoActivity.this, accounts -> manageView(accounts));
|
||||||
|
} else if (type == typeOfInfo.LIKED_BY) {
|
||||||
|
statusesVM.favouritedBy(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, status.id, max_id, null, null).observe(StatusInfoActivity.this, accounts -> manageView(accounts));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
binding.loadingNextAccounts.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (type == typeOfInfo.BOOSTED_BY) {
|
||||||
|
statusesVM.rebloggedBy(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, status.id, null, null, null).observe(StatusInfoActivity.this, this::manageView);
|
||||||
|
} else if (type == typeOfInfo.LIKED_BY) {
|
||||||
|
statusesVM.favouritedBy(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, status.id, null, null, null).observe(StatusInfoActivity.this, this::manageView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void manageView(Accounts accounts) {
|
||||||
|
flagLoading = false;
|
||||||
|
binding.loadingNextAccounts.setVisibility(View.GONE);
|
||||||
|
if (accountList != null && accounts != null && accounts.accounts != null) {
|
||||||
|
int startId = 0;
|
||||||
|
//There are some statuses present in the timeline
|
||||||
|
if (accountList.size() > 0) {
|
||||||
|
startId = accountList.size();
|
||||||
|
}
|
||||||
|
accountList.addAll(accounts.accounts);
|
||||||
|
max_id = accounts.pagination.min_id;
|
||||||
|
accountAdapter.notifyItemRangeInserted(startId, accounts.accounts.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (item.getItemId() == android.R.id.home) {
|
||||||
|
finish();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum typeOfInfo {
|
||||||
|
LIKED_BY,
|
||||||
|
BOOSTED_BY
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,257 @@
|
||||||
|
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 android.Manifest;
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.core.app.ActivityCompat;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import app.fedilab.android.R;
|
||||||
|
import app.fedilab.android.databinding.ActivityWebviewBinding;
|
||||||
|
import app.fedilab.android.helper.Helper;
|
||||||
|
import app.fedilab.android.helper.ThemeHelper;
|
||||||
|
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;
|
||||||
|
|
||||||
|
|
||||||
|
public class WebviewActivity extends BaseActivity {
|
||||||
|
|
||||||
|
public static List<String> trackingDomains;
|
||||||
|
private String url;
|
||||||
|
private String peertubeLinkToFetch;
|
||||||
|
private boolean peertubeLink;
|
||||||
|
private CustomWebview webView;
|
||||||
|
private Menu defaultMenu;
|
||||||
|
private FedilabWebViewClient FedilabWebViewClient;
|
||||||
|
private ActivityWebviewBinding binding;
|
||||||
|
|
||||||
|
@SuppressLint("SetJavaScriptEnabled")
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
ThemeHelper.applyTheme(this);
|
||||||
|
binding = ActivityWebviewBinding.inflate(getLayoutInflater());
|
||||||
|
View view = binding.getRoot();
|
||||||
|
setContentView(view);
|
||||||
|
|
||||||
|
Bundle b = getIntent().getExtras();
|
||||||
|
if (b != null) {
|
||||||
|
url = b.getString("url", null);
|
||||||
|
peertubeLinkToFetch = b.getString("peertubeLinkToFetch", null);
|
||||||
|
peertubeLink = b.getBoolean("peertubeLink", false);
|
||||||
|
}
|
||||||
|
if (url == null)
|
||||||
|
finish();
|
||||||
|
if (getSupportActionBar() != null)
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
|
||||||
|
webView = Helper.initializeWebview(WebviewActivity.this, R.id.webview, null);
|
||||||
|
setTitle("");
|
||||||
|
|
||||||
|
webView.getSettings().setJavaScriptEnabled(true);
|
||||||
|
|
||||||
|
|
||||||
|
FedilabWebChromeClient FedilabWebChromeClient = new FedilabWebChromeClient(WebviewActivity.this, webView, binding.webviewContainer, binding.videoLayout);
|
||||||
|
FedilabWebChromeClient.setOnToggledFullscreen(fullscreen -> {
|
||||||
|
|
||||||
|
if (fullscreen) {
|
||||||
|
binding.videoLayout.setVisibility(View.VISIBLE);
|
||||||
|
WindowManager.LayoutParams attrs = getWindow().getAttributes();
|
||||||
|
attrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
|
||||||
|
attrs.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
|
||||||
|
getWindow().setAttributes(attrs);
|
||||||
|
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
|
||||||
|
} else {
|
||||||
|
WindowManager.LayoutParams attrs = getWindow().getAttributes();
|
||||||
|
attrs.flags &= ~WindowManager.LayoutParams.FLAG_FULLSCREEN;
|
||||||
|
attrs.flags &= ~WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
|
||||||
|
getWindow().setAttributes(attrs);
|
||||||
|
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
|
||||||
|
binding.videoLayout.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
webView.setWebChromeClient(FedilabWebChromeClient);
|
||||||
|
FedilabWebViewClient = new FedilabWebViewClient(WebviewActivity.this);
|
||||||
|
webView.setWebViewClient(FedilabWebViewClient);
|
||||||
|
webView.setDownloadListener((url, userAgent, contentDisposition, mimetype, contentLength) -> {
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= 23) {
|
||||||
|
if (ContextCompat.checkSelfPermission(WebviewActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(WebviewActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
ActivityCompat.requestPermissions(WebviewActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, Helper.EXTERNAL_STORAGE_REQUEST_CODE);
|
||||||
|
} else {
|
||||||
|
Helper.manageDownloads(WebviewActivity.this, url);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Helper.manageDownloads(WebviewActivity.this, url);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!url.toLowerCase().startsWith("http://") && !url.toLowerCase().startsWith("https://"))
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||||
|
/* if (!peertubeLink)
|
||||||
|
setCount(WebviewActivity.this, "0");*/
|
||||||
|
defaultMenu = menu;
|
||||||
|
return super.onPrepareOptionsMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(@NotNull Menu menu) {
|
||||||
|
getMenuInflater().inflate(R.menu.main_webview, menu);
|
||||||
|
defaultMenu = menu;
|
||||||
|
if (peertubeLink) {
|
||||||
|
menu.findItem(R.id.action_go).setVisible(false);
|
||||||
|
menu.findItem(R.id.action_block).setVisible(false);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
int itemId = item.getItemId();
|
||||||
|
if (itemId == android.R.id.home) {
|
||||||
|
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;
|
||||||
|
} else if (itemId == R.id.action_go) {
|
||||||
|
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
||||||
|
try {
|
||||||
|
startActivity(browserIntent);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Toasty.error(WebviewActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUrl(String newUrl) {
|
||||||
|
this.url = newUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
if (webView != null)
|
||||||
|
webView.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
if (webView != null)
|
||||||
|
webView.onResume();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBackPressed() {
|
||||||
|
if (webView.canGoBack()) {
|
||||||
|
webView.goBack();
|
||||||
|
} else {
|
||||||
|
super.onBackPressed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
if (webView != null)
|
||||||
|
webView.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,264 @@
|
||||||
|
/* 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.BaseMainActivity.api;
|
||||||
|
import static app.fedilab.android.BaseMainActivity.currentInstance;
|
||||||
|
import static app.fedilab.android.BaseMainActivity.software;
|
||||||
|
import static app.fedilab.android.helper.Helper.PREF_USER_TOKEN;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.graphics.drawable.ColorDrawable;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.webkit.CookieManager;
|
||||||
|
import android.webkit.CookieSyncManager;
|
||||||
|
import android.webkit.WebChromeClient;
|
||||||
|
import android.webkit.WebView;
|
||||||
|
import android.webkit.WebViewClient;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.ActionBar;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import app.fedilab.android.BaseMainActivity;
|
||||||
|
import app.fedilab.android.R;
|
||||||
|
import app.fedilab.android.client.entities.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.OauthVM;
|
||||||
|
import es.dmoral.toasty.Toasty;
|
||||||
|
|
||||||
|
|
||||||
|
public class WebviewConnectActivity extends BaseActivity {
|
||||||
|
|
||||||
|
|
||||||
|
private ActivityWebviewConnectBinding binding;
|
||||||
|
private AlertDialog alert;
|
||||||
|
private String login_url;
|
||||||
|
|
||||||
|
@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("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");
|
||||||
|
}
|
||||||
|
if (login_url == null)
|
||||||
|
finish();
|
||||||
|
ActionBar actionBar = getSupportActionBar();
|
||||||
|
if (actionBar != null) {
|
||||||
|
LayoutInflater inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||||
|
assert inflater != null;
|
||||||
|
View view = inflater.inflate(R.layout.simple_bar, new LinearLayout(WebviewConnectActivity.this), false);
|
||||||
|
view.setBackground(new ColorDrawable(ContextCompat.getColor(WebviewConnectActivity.this, R.color.cyanea_primary)));
|
||||||
|
actionBar.setCustomView(view, new ActionBar.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
|
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
|
||||||
|
ImageView toolbar_close = actionBar.getCustomView().findViewById(R.id.toolbar_close);
|
||||||
|
TextView toolbar_title = actionBar.getCustomView().findViewById(R.id.toolbar_title);
|
||||||
|
toolbar_close.setOnClickListener(v -> finish());
|
||||||
|
toolbar_title.setText(R.string.add_account);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
String[] val = url.split("code=");
|
||||||
|
if (val.length < 2) {
|
||||||
|
Toasty.error(WebviewConnectActivity.this, getString(R.string.toast_code_error), Toast.LENGTH_LONG).show();
|
||||||
|
Intent myIntent = new Intent(WebviewConnectActivity.this, LoginActivity.class);
|
||||||
|
startActivity(myIntent);
|
||||||
|
finish();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String code = val[1];
|
||||||
|
OauthVM oauthVM = new ViewModelProvider(WebviewConnectActivity.this).get(OauthVM.class);
|
||||||
|
//API call to get the user token
|
||||||
|
oauthVM.createToken(currentInstance, "authorization_code", BaseMainActivity.client_id, BaseMainActivity.client_secret, Helper.REDIRECT_CONTENT_WEB, Helper.OAUTH_SCOPES, code)
|
||||||
|
.observe(WebviewConnectActivity.this, tokenObj -> {
|
||||||
|
Account account = new Account();
|
||||||
|
account.client_id = BaseMainActivity.client_id;
|
||||||
|
account.client_secret = BaseMainActivity.client_secret;
|
||||||
|
account.token = tokenObj.token_type + " " + tokenObj.access_token;
|
||||||
|
account.api = api;
|
||||||
|
account.software = software;
|
||||||
|
account.instance = currentInstance;
|
||||||
|
//API call to retrieve account information for the new token
|
||||||
|
AccountsVM accountsVM = new ViewModelProvider(WebviewConnectActivity.this).get(AccountsVM.class);
|
||||||
|
accountsVM.getConnectedAccount(currentInstance, account.token).observe(WebviewConnectActivity.this, mastodonAccount -> {
|
||||||
|
account.mastodon_account = mastodonAccount;
|
||||||
|
account.user_id = mastodonAccount.id;
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
//update the database
|
||||||
|
new Account(WebviewConnectActivity.this).insertOrUpdate(account);
|
||||||
|
Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||||
|
BaseMainActivity.currentToken = account.token;
|
||||||
|
BaseMainActivity.currentUserID = account.user_id;
|
||||||
|
api = Account.API.MASTODON;
|
||||||
|
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(WebviewConnectActivity.this);
|
||||||
|
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(WebviewConnectActivity.this, BaseMainActivity.class);
|
||||||
|
startActivity(mainActivity);
|
||||||
|
finish();
|
||||||
|
};
|
||||||
|
mainHandler.post(myRunnable);
|
||||||
|
} catch (DBException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
package app.fedilab.android.broadcastreceiver;
|
||||||
|
/* 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 android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.ConnectivityManager;
|
||||||
|
import android.net.NetworkInfo;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Original work from https://stackoverflow.com/a/25873554
|
||||||
|
*/
|
||||||
|
public class NetworkStateReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
|
protected Set<NetworkStateReceiverListener> listeners;
|
||||||
|
protected Boolean connected;
|
||||||
|
|
||||||
|
public NetworkStateReceiver() {
|
||||||
|
listeners = new HashSet<>();
|
||||||
|
connected = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
if (intent == null || intent.getExtras() == null)
|
||||||
|
return;
|
||||||
|
ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||||
|
NetworkInfo ni = manager.getActiveNetworkInfo();
|
||||||
|
if (ni != null && ni.getState() == NetworkInfo.State.CONNECTED) {
|
||||||
|
connected = true;
|
||||||
|
} else if (intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, Boolean.FALSE)) {
|
||||||
|
connected = false;
|
||||||
|
}
|
||||||
|
notifyStateToAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void notifyStateToAll() {
|
||||||
|
for (NetworkStateReceiverListener listener : listeners)
|
||||||
|
notifyState(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyState(NetworkStateReceiverListener listener) {
|
||||||
|
if (connected == null || listener == null)
|
||||||
|
return;
|
||||||
|
if (connected)
|
||||||
|
listener.networkAvailable();
|
||||||
|
else
|
||||||
|
listener.networkUnavailable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addListener(NetworkStateReceiverListener l) {
|
||||||
|
listeners.add(l);
|
||||||
|
notifyState(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeListener(NetworkStateReceiverListener l) {
|
||||||
|
listeners.remove(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface NetworkStateReceiverListener {
|
||||||
|
void networkAvailable();
|
||||||
|
|
||||||
|
void networkUnavailable();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
package app.fedilab.android.broadcastreceiver;
|
||||||
|
/* 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.helper.Helper.RECEIVE_TOAST_CONTENT;
|
||||||
|
import static app.fedilab.android.helper.Helper.RECEIVE_TOAST_TYPE;
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import app.fedilab.android.helper.Helper;
|
||||||
|
import es.dmoral.toasty.Toasty;
|
||||||
|
|
||||||
|
public class ToastMessage extends BroadcastReceiver {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
Bundle b = intent.getExtras();
|
||||||
|
if (b != null) {
|
||||||
|
String type = b.getString(RECEIVE_TOAST_TYPE, null);
|
||||||
|
String content = b.getString(RECEIVE_TOAST_CONTENT, null);
|
||||||
|
if (type != null && content != null) {
|
||||||
|
switch (type) {
|
||||||
|
case Helper.RECEIVE_TOAST_TYPE_ERROR:
|
||||||
|
Toasty.error(context, content, Toasty.LENGTH_SHORT).show();
|
||||||
|
break;
|
||||||
|
case Helper.RECEIVE_TOAST_TYPE_WARNING:
|
||||||
|
Toasty.warning(context, content, Toasty.LENGTH_SHORT).show();
|
||||||
|
break;
|
||||||
|
case Helper.RECEIVE_TOAST_TYPE_INFO:
|
||||||
|
Toasty.info(context, content, Toasty.LENGTH_SHORT).show();
|
||||||
|
break;
|
||||||
|
case Helper.RECEIVE_TOAST_TYPE_SUCCESS:
|
||||||
|
Toasty.success(context, content, Toasty.LENGTH_SHORT).show();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package app.fedilab.android.client;
|
||||||
|
/* 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 app.fedilab.android.client.entities.WellKnownNodeinfo;
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.http.GET;
|
||||||
|
import retrofit2.http.Path;
|
||||||
|
|
||||||
|
public interface NodeInfoService {
|
||||||
|
|
||||||
|
@GET(".well-known/nodeinfo")
|
||||||
|
Call<WellKnownNodeinfo> getWellKnownNodeinfoLinks();
|
||||||
|
|
||||||
|
@GET("{nodeInfoPath}")
|
||||||
|
Call<WellKnownNodeinfo.NodeInfo> getNodeinfo(
|
||||||
|
@Path(value = "nodeInfoPath", encoded = true) String nodeInfoPath
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,441 @@
|
||||||
|
package app.fedilab.android.client.entities;
|
||||||
|
/* 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 android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import app.fedilab.android.BaseMainActivity;
|
||||||
|
import app.fedilab.android.exception.DBException;
|
||||||
|
import app.fedilab.android.helper.Helper;
|
||||||
|
import app.fedilab.android.sqlite.Sqlite;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that manages Accounts from database
|
||||||
|
* Accounts details are serialized and can be for different softwares
|
||||||
|
* The type of the software is stored in api field
|
||||||
|
*/
|
||||||
|
public class Account implements Serializable {
|
||||||
|
|
||||||
|
|
||||||
|
private final SQLiteDatabase db;
|
||||||
|
@SerializedName("user_id")
|
||||||
|
public String user_id;
|
||||||
|
@SerializedName("instance")
|
||||||
|
public String instance;
|
||||||
|
@SerializedName("api")
|
||||||
|
public API api;
|
||||||
|
@SerializedName("software")
|
||||||
|
public String software;
|
||||||
|
@SerializedName("token")
|
||||||
|
public String token;
|
||||||
|
@SerializedName("refresh_token")
|
||||||
|
public String refresh_token;
|
||||||
|
@SerializedName("token_validity")
|
||||||
|
public long token_validity;
|
||||||
|
@SerializedName("client_id")
|
||||||
|
public String client_id;
|
||||||
|
@SerializedName("client_secret")
|
||||||
|
public String client_secret;
|
||||||
|
@SerializedName("created_at")
|
||||||
|
public Date created_at;
|
||||||
|
@SerializedName("updated_at")
|
||||||
|
public Date updated_at;
|
||||||
|
@SerializedName("mastodon_account")
|
||||||
|
public app.fedilab.android.client.mastodon.entities.Account mastodon_account;
|
||||||
|
|
||||||
|
private transient Context context;
|
||||||
|
|
||||||
|
public Account() {
|
||||||
|
db = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account(Context context) {
|
||||||
|
//Creation of the DB with tables
|
||||||
|
this.context = context;
|
||||||
|
this.db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialized a Mastodon Account class
|
||||||
|
*
|
||||||
|
* @param mastodon_account {@link app.fedilab.android.client.mastodon.entities.Account} to serialize
|
||||||
|
* @return String serialized account
|
||||||
|
*/
|
||||||
|
public static String mastodonAccountToStringStorage(app.fedilab.android.client.mastodon.entities.Account mastodon_account) {
|
||||||
|
Gson gson = new Gson();
|
||||||
|
try {
|
||||||
|
return gson.toJson(mastodon_account);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all Account in db
|
||||||
|
*
|
||||||
|
* @return Account List<Account>
|
||||||
|
*/
|
||||||
|
public List<Account> getPushNotificationAccounts() {
|
||||||
|
|
||||||
|
try {
|
||||||
|
Cursor c = db.query(Sqlite.TABLE_USER_ACCOUNT, null, "(" + Sqlite.COL_API + " = 'MASTODON' OR " + Sqlite.COL_API + " = 'PLEROMA') AND " + Sqlite.COL_TOKEN + " IS NOT NULL", null, null, null, Sqlite.COL_INSTANCE + " ASC", null);
|
||||||
|
return cursorToListUserWithOwner(c);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unserialized a Mastodon Account
|
||||||
|
*
|
||||||
|
* @param serializedAccount String serialized account
|
||||||
|
* @return {@link app.fedilab.android.client.mastodon.entities.Account}
|
||||||
|
*/
|
||||||
|
public static app.fedilab.android.client.mastodon.entities.Account restoreAccountFromString(String serializedAccount) {
|
||||||
|
Gson gson = new Gson();
|
||||||
|
try {
|
||||||
|
return gson.fromJson(serializedAccount, app.fedilab.android.client.mastodon.entities.Account.class);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert or update a user
|
||||||
|
*
|
||||||
|
* @param account {@link Account}
|
||||||
|
* @return long - db id
|
||||||
|
* @throws DBException exception with database
|
||||||
|
*/
|
||||||
|
public long insertOrUpdate(Account account) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
boolean exists = accountExist(account);
|
||||||
|
long idReturned;
|
||||||
|
if (exists) {
|
||||||
|
idReturned = updateAccount(account);
|
||||||
|
} else {
|
||||||
|
idReturned = insertAccount(account);
|
||||||
|
}
|
||||||
|
return idReturned;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert an account in db
|
||||||
|
*
|
||||||
|
* @param account {@link Account}
|
||||||
|
* @return long - db id
|
||||||
|
* @throws DBException exception with database
|
||||||
|
*/
|
||||||
|
private long insertAccount(Account account) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(Sqlite.COL_APP_CLIENT_ID, account.client_id);
|
||||||
|
values.put(Sqlite.COL_APP_CLIENT_SECRET, account.client_secret);
|
||||||
|
values.put(Sqlite.COL_USER_ID, account.user_id);
|
||||||
|
values.put(Sqlite.COL_INSTANCE, account.instance);
|
||||||
|
values.put(Sqlite.COL_API, account.api.name());
|
||||||
|
values.put(Sqlite.COL_SOFTWARE, account.software);
|
||||||
|
values.put(Sqlite.COL_TOKEN_VALIDITY, account.token_validity);
|
||||||
|
values.put(Sqlite.COL_TOKEN, account.token);
|
||||||
|
values.put(Sqlite.COL_REFRESH_TOKEN, account.refresh_token);
|
||||||
|
if (account.mastodon_account != null) {
|
||||||
|
values.put(Sqlite.COL_ACCOUNT, mastodonAccountToStringStorage(account.mastodon_account));
|
||||||
|
}
|
||||||
|
values.put(Sqlite.COL_CREATED_AT, Helper.dateToString(new Date()));
|
||||||
|
values.put(Sqlite.COL_UPDATED_AT, Helper.dateToString(new Date()));
|
||||||
|
//Inserts token
|
||||||
|
try {
|
||||||
|
return db.insertOrThrow(Sqlite.TABLE_USER_ACCOUNT, null, values);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update an account in db
|
||||||
|
*
|
||||||
|
* @param account {@link Account}
|
||||||
|
* @return long - db id
|
||||||
|
* @throws DBException exception with database
|
||||||
|
*/
|
||||||
|
private long updateAccount(Account account) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
//Can be null if only retrieving account details - IE : not the whole authentication process
|
||||||
|
if (account.client_id != null) {
|
||||||
|
values.put(Sqlite.COL_APP_CLIENT_ID, account.client_id);
|
||||||
|
values.put(Sqlite.COL_APP_CLIENT_SECRET, account.client_secret);
|
||||||
|
values.put(Sqlite.COL_API, account.api.name());
|
||||||
|
values.put(Sqlite.COL_SOFTWARE, account.software);
|
||||||
|
values.put(Sqlite.COL_TOKEN_VALIDITY, account.token_validity);
|
||||||
|
values.put(Sqlite.COL_TOKEN, account.token);
|
||||||
|
values.put(Sqlite.COL_REFRESH_TOKEN, account.refresh_token);
|
||||||
|
}
|
||||||
|
if (account.mastodon_account != null) {
|
||||||
|
values.put(Sqlite.COL_ACCOUNT, mastodonAccountToStringStorage(account.mastodon_account));
|
||||||
|
}
|
||||||
|
values.put(Sqlite.COL_UPDATED_AT, Helper.dateToString(new Date()));
|
||||||
|
//Inserts token
|
||||||
|
try {
|
||||||
|
return db.update(Sqlite.TABLE_USER_ACCOUNT,
|
||||||
|
values, Sqlite.COL_USER_ID + " = ? AND " + Sqlite.COL_INSTANCE + " =?",
|
||||||
|
new String[]{account.user_id, account.instance});
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a user exists in db
|
||||||
|
*
|
||||||
|
* @param account Account {@link Account}
|
||||||
|
* @return boolean - user exists
|
||||||
|
* @throws DBException Exception
|
||||||
|
*/
|
||||||
|
public boolean accountExist(Account account) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
Cursor mCount = db.rawQuery("select count(*) from " + Sqlite.TABLE_USER_ACCOUNT
|
||||||
|
+ " where " + Sqlite.COL_USER_ID + " = '" + account.user_id + "' AND " + Sqlite.COL_INSTANCE + " = '" + account.instance + "'", null);
|
||||||
|
mCount.moveToFirst();
|
||||||
|
int count = mCount.getInt(0);
|
||||||
|
mCount.close();
|
||||||
|
return (count > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an Account by token
|
||||||
|
*
|
||||||
|
* @param userId String
|
||||||
|
* @param instance String
|
||||||
|
* @return Account {@link Account}
|
||||||
|
*/
|
||||||
|
public Account getUniqAccount(String userId, String instance) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Cursor c = db.query(Sqlite.TABLE_USER_ACCOUNT, null, Sqlite.COL_USER_ID + " = \"" + userId + "\" AND " + Sqlite.COL_INSTANCE + " = \"" + instance + "\"", null, null, null, null, "1");
|
||||||
|
return cursorToUser(c);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns authenticated Account
|
||||||
|
*
|
||||||
|
* @return Account {@link Account}
|
||||||
|
*/
|
||||||
|
public Account getConnectedAccount() throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Cursor c = db.query(Sqlite.TABLE_USER_ACCOUNT, null, Sqlite.COL_TOKEN + " = '" + BaseMainActivity.currentToken + "'", null, null, null, null, "1");
|
||||||
|
return cursorToUser(c);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all accounts that allows cross-account actions
|
||||||
|
*
|
||||||
|
* @return Account List<{@link Account}>
|
||||||
|
*/
|
||||||
|
public List<Account> getCrossAccounts() throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Cursor c = db.query(Sqlite.TABLE_USER_ACCOUNT, null, Sqlite.COL_API + " = 'MASTODON'", null, null, null, null, null);
|
||||||
|
return cursorToListUser(c);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all accounts
|
||||||
|
*
|
||||||
|
* @return Account List<{@link Account}>
|
||||||
|
*/
|
||||||
|
public List<Account> getAll() throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Cursor c = db.query(Sqlite.TABLE_USER_ACCOUNT, null, null, null, null, null, null, null);
|
||||||
|
return cursorToListUser(c);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns last used account
|
||||||
|
*
|
||||||
|
* @return Account {@link Account}
|
||||||
|
*/
|
||||||
|
public Account getLastUsedAccount() throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Cursor c = db.query(Sqlite.TABLE_USER_ACCOUNT, null, null, null, null, null, Sqlite.COL_UPDATED_AT + " DESC", "1");
|
||||||
|
return cursorToUser(c);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an account from db
|
||||||
|
*
|
||||||
|
* @param account {@link Account}
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public int removeUser(Account account) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
return db.delete(Sqlite.TABLE_USER_ACCOUNT, Sqlite.COL_USER_ID + " = '" + account.user_id +
|
||||||
|
"' AND " + Sqlite.COL_INSTANCE + " = '" + account.instance + "'", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private List<Account> cursorToListUser(Cursor c) {
|
||||||
|
//No element found
|
||||||
|
if (c.getCount() == 0) {
|
||||||
|
c.close();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List<Account> accountList = new ArrayList<>();
|
||||||
|
while (c.moveToNext()) {
|
||||||
|
Account account = convertCursorToAccount(c);
|
||||||
|
//We don't add in the list the current connected account
|
||||||
|
if (!account.token.equalsIgnoreCase(BaseMainActivity.currentToken)) {
|
||||||
|
accountList.add(account);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Close the cursor
|
||||||
|
c.close();
|
||||||
|
return accountList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Account> cursorToListUserWithOwner(Cursor c) {
|
||||||
|
//No element found
|
||||||
|
if (c.getCount() == 0) {
|
||||||
|
c.close();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List<Account> accountList = new ArrayList<>();
|
||||||
|
while (c.moveToNext()) {
|
||||||
|
Account account = convertCursorToAccount(c);
|
||||||
|
//We don't add in the list the current connected account
|
||||||
|
accountList.add(account);
|
||||||
|
}
|
||||||
|
//Close the cursor
|
||||||
|
c.close();
|
||||||
|
return accountList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Method to hydrate an Account from database
|
||||||
|
* @param c Cursor
|
||||||
|
* @return Account {@link Account}
|
||||||
|
*/
|
||||||
|
private Account cursorToUser(Cursor c) {
|
||||||
|
//No element found
|
||||||
|
if (c.getCount() == 0) {
|
||||||
|
c.close();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
//Take the first element
|
||||||
|
c.moveToFirst();
|
||||||
|
//New user
|
||||||
|
Account account = convertCursorToAccount(c);
|
||||||
|
//Close the cursor
|
||||||
|
c.close();
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read cursor and hydrate without closing it
|
||||||
|
*
|
||||||
|
* @param c - Cursor
|
||||||
|
* @return Account
|
||||||
|
*/
|
||||||
|
private Account convertCursorToAccount(Cursor c) {
|
||||||
|
Account account = new Account();
|
||||||
|
account.user_id = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_USER_ID));
|
||||||
|
account.client_id = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_APP_CLIENT_ID));
|
||||||
|
account.client_secret = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_APP_CLIENT_SECRET));
|
||||||
|
account.token = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_TOKEN));
|
||||||
|
account.instance = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_INSTANCE));
|
||||||
|
account.refresh_token = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_REFRESH_TOKEN));
|
||||||
|
account.token_validity = c.getInt(c.getColumnIndexOrThrow(Sqlite.COL_TOKEN_VALIDITY));
|
||||||
|
account.created_at = Helper.stringToDate(context, c.getString(c.getColumnIndexOrThrow(Sqlite.COL_CREATED_AT)));
|
||||||
|
account.updated_at = Helper.stringToDate(context, c.getString(c.getColumnIndexOrThrow(Sqlite.COL_UPDATED_AT)));
|
||||||
|
account.software = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_SOFTWARE));
|
||||||
|
String apiStr = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_API));
|
||||||
|
API api = null;
|
||||||
|
switch (apiStr) {
|
||||||
|
case "MASTODON":
|
||||||
|
api = API.MASTODON;
|
||||||
|
break;
|
||||||
|
case "PEERTUBE":
|
||||||
|
api = API.PEERTUBE;
|
||||||
|
break;
|
||||||
|
case "PIXELFED":
|
||||||
|
api = API.PIXELFED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
account.api = api;
|
||||||
|
if (api == API.MASTODON) {
|
||||||
|
account.mastodon_account = restoreAccountFromString(c.getString(c.getColumnIndexOrThrow(Sqlite.COL_ACCOUNT)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum API {
|
||||||
|
MASTODON,
|
||||||
|
PEERTUBE,
|
||||||
|
PIXELFED
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
package app.fedilab.android.client.entities;
|
||||||
|
/* 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 com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class InstanceSocial {
|
||||||
|
|
||||||
|
@SerializedName("instances")
|
||||||
|
public List<Instance> instances;
|
||||||
|
|
||||||
|
public static class Instance {
|
||||||
|
@SerializedName("name")
|
||||||
|
public String name;
|
||||||
|
@SerializedName("added_at")
|
||||||
|
public Date added_at;
|
||||||
|
@SerializedName("updated_at")
|
||||||
|
public Date updated_at;
|
||||||
|
@SerializedName("checked_at")
|
||||||
|
public Date checked_at;
|
||||||
|
@SerializedName("uptime")
|
||||||
|
public float uptime;
|
||||||
|
@SerializedName("up")
|
||||||
|
public boolean up;
|
||||||
|
@SerializedName("version")
|
||||||
|
public String version;
|
||||||
|
@SerializedName("thumbnail")
|
||||||
|
public String thumbnail;
|
||||||
|
@SerializedName("dead")
|
||||||
|
public boolean dead;
|
||||||
|
@SerializedName("active_users")
|
||||||
|
public int active_users;
|
||||||
|
@SerializedName("statuses")
|
||||||
|
public int statuses;
|
||||||
|
@SerializedName("email")
|
||||||
|
public String email;
|
||||||
|
@SerializedName("admin")
|
||||||
|
public String admin;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,212 @@
|
||||||
|
package app.fedilab.android.client.entities;
|
||||||
|
/* 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.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import app.fedilab.android.BaseMainActivity;
|
||||||
|
import app.fedilab.android.client.entities.app.PinnedTimeline;
|
||||||
|
import app.fedilab.android.exception.DBException;
|
||||||
|
import app.fedilab.android.helper.Helper;
|
||||||
|
import app.fedilab.android.sqlite.Sqlite;
|
||||||
|
|
||||||
|
|
||||||
|
public class Pinned implements Serializable {
|
||||||
|
private final SQLiteDatabase db;
|
||||||
|
|
||||||
|
@SerializedName("id")
|
||||||
|
public long id = -1;
|
||||||
|
@SerializedName("instance")
|
||||||
|
public String instance;
|
||||||
|
@SerializedName("user_id")
|
||||||
|
public String user_id;
|
||||||
|
@SerializedName("pinnedTimelines")
|
||||||
|
public List<PinnedTimeline> pinnedTimelines;
|
||||||
|
@SerializedName("created_at")
|
||||||
|
public Date created_ad;
|
||||||
|
@SerializedName("updated_at")
|
||||||
|
public Date updated_at;
|
||||||
|
private Context context;
|
||||||
|
|
||||||
|
public Pinned() {
|
||||||
|
db = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Pinned(Context context) {
|
||||||
|
//Creation of the DB with tables
|
||||||
|
this.context = context;
|
||||||
|
this.db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialized a list of PinnedTimeline class
|
||||||
|
*
|
||||||
|
* @param pinnedTimelines List of {@link PinnedTimeline} to serialize
|
||||||
|
* @return String serialized pinnedTimelines list
|
||||||
|
*/
|
||||||
|
public static String mastodonPinnedTimelinesToStringStorage(List<PinnedTimeline> pinnedTimelines) {
|
||||||
|
Gson gson = new Gson();
|
||||||
|
try {
|
||||||
|
return gson.toJson(pinnedTimelines);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unserialized a PinnedTimeline List
|
||||||
|
*
|
||||||
|
* @param serializedPinnedTimelines String serialized PinnedTimeline list
|
||||||
|
* @return List of {@link PinnedTimeline}
|
||||||
|
*/
|
||||||
|
public static List<PinnedTimeline> restorePinnedTimelinesFromString(String serializedPinnedTimelines) {
|
||||||
|
Gson gson = new Gson();
|
||||||
|
try {
|
||||||
|
return gson.fromJson(serializedPinnedTimelines, new TypeToken<List<PinnedTimeline>>() {
|
||||||
|
}.getType());
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert pinnedTimeline in db
|
||||||
|
*
|
||||||
|
* @param pinned {@link Pinned}
|
||||||
|
* @return long - db id
|
||||||
|
* @throws DBException exception with database
|
||||||
|
*/
|
||||||
|
public long insertPinned(Pinned pinned) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(Sqlite.COL_INSTANCE, BaseMainActivity.currentInstance);
|
||||||
|
values.put(Sqlite.COL_USER_ID, BaseMainActivity.currentUserID);
|
||||||
|
values.put(Sqlite.COL_PINNED_TIMELINES, mastodonPinnedTimelinesToStringStorage(pinned.pinnedTimelines));
|
||||||
|
values.put(Sqlite.COL_CREATED_AT, Helper.dateToString(new Date()));
|
||||||
|
//Inserts pinned
|
||||||
|
try {
|
||||||
|
return db.insertOrThrow(Sqlite.TABLE_PINNED_TIMELINES, null, values);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* update pinned in db
|
||||||
|
*
|
||||||
|
* @param pinned {@link Pinned}
|
||||||
|
* @return long - db id
|
||||||
|
* @throws DBException exception with database
|
||||||
|
*/
|
||||||
|
public long updatePinned(Pinned pinned) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(Sqlite.COL_PINNED_TIMELINES, mastodonPinnedTimelinesToStringStorage(pinned.pinnedTimelines));
|
||||||
|
values.put(Sqlite.COL_UPDATED_AT, Helper.dateToString(new Date()));
|
||||||
|
//Inserts token
|
||||||
|
try {
|
||||||
|
return db.update(Sqlite.TABLE_PINNED_TIMELINES,
|
||||||
|
values, Sqlite.COL_INSTANCE + " = ? AND " + Sqlite.COL_USER_ID + " = ?",
|
||||||
|
new String[]{pinned.instance, pinned.user_id});
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the pinned timeline for an account
|
||||||
|
*
|
||||||
|
* @param account Account
|
||||||
|
* @return Pinned - {@link Pinned}
|
||||||
|
*/
|
||||||
|
public Pinned getPinned(Account account) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Cursor c = db.query(Sqlite.TABLE_PINNED_TIMELINES, null, Sqlite.COL_INSTANCE + " = '" + account.instance + "' AND " + Sqlite.COL_USER_ID + " = '" + account.user_id + "'", null, null, null, Sqlite.COL_UPDATED_AT + " DESC", "1");
|
||||||
|
return cursorToPined(c);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean pinnedExist(Pinned pinned) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
Cursor mCount = db.rawQuery("select count(*) from " + Sqlite.TABLE_PINNED_TIMELINES
|
||||||
|
+ " where " + Sqlite.COL_INSTANCE + " = '" + pinned.instance + "' AND " + Sqlite.COL_USER_ID + " = '" + pinned.user_id + "'", null);
|
||||||
|
mCount.moveToFirst();
|
||||||
|
int count = mCount.getInt(0);
|
||||||
|
mCount.close();
|
||||||
|
return (count > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore pinned from db
|
||||||
|
*
|
||||||
|
* @param c Cursor
|
||||||
|
* @return Pinned
|
||||||
|
*/
|
||||||
|
private Pinned cursorToPined(Cursor c) {
|
||||||
|
//No element found
|
||||||
|
if (c.getCount() == 0) {
|
||||||
|
c.close();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
//Take the first element
|
||||||
|
c.moveToFirst();
|
||||||
|
Pinned pinned = convertCursorToStatusDraft(c);
|
||||||
|
//Close the cursor
|
||||||
|
c.close();
|
||||||
|
return pinned;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read cursor and hydrate without closing it
|
||||||
|
*
|
||||||
|
* @param c - Cursor
|
||||||
|
* @return Timeline
|
||||||
|
*/
|
||||||
|
private Pinned convertCursorToStatusDraft(Cursor c) {
|
||||||
|
Pinned pinned = new Pinned();
|
||||||
|
pinned.id = c.getInt(c.getColumnIndexOrThrow(Sqlite.COL_ID));
|
||||||
|
pinned.instance = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_INSTANCE));
|
||||||
|
pinned.user_id = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_USER_ID));
|
||||||
|
pinned.pinnedTimelines = restorePinnedTimelinesFromString(c.getString(c.getColumnIndexOrThrow(Sqlite.COL_PINNED_TIMELINES)));
|
||||||
|
pinned.created_ad = Helper.stringToDate(context, c.getString(c.getColumnIndexOrThrow(Sqlite.COL_CREATED_AT)));
|
||||||
|
pinned.updated_at = Helper.stringToDate(context, c.getString(c.getColumnIndexOrThrow(Sqlite.COL_UPDATED_AT)));
|
||||||
|
return pinned;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package app.fedilab.android.client.entities;
|
||||||
|
/* 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 com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class PostState implements Serializable {
|
||||||
|
|
||||||
|
@SerializedName("number_of_posts")
|
||||||
|
public int number_of_posts;
|
||||||
|
@SerializedName("posts_successfully_sent")
|
||||||
|
public int posts_successfully_sent;
|
||||||
|
@SerializedName("posts")
|
||||||
|
public List<Post> posts;
|
||||||
|
|
||||||
|
public static class Post implements Serializable {
|
||||||
|
@SerializedName("id")
|
||||||
|
public String id;
|
||||||
|
@SerializedName("in_reply_to_id")
|
||||||
|
public String in_reply_to_id;
|
||||||
|
@SerializedName("number_of_media")
|
||||||
|
public int number_of_media;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,225 @@
|
||||||
|
package app.fedilab.android.client.entities;
|
||||||
|
/* 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.client.entities.StatusCache.mastodonStatusToStringStorage;
|
||||||
|
import static app.fedilab.android.client.entities.StatusCache.restoreStatusFromString;
|
||||||
|
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import app.fedilab.android.BaseMainActivity;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Status;
|
||||||
|
import app.fedilab.android.exception.DBException;
|
||||||
|
import app.fedilab.android.helper.Helper;
|
||||||
|
import app.fedilab.android.sqlite.Sqlite;
|
||||||
|
|
||||||
|
public class ScheduledBoost implements Serializable {
|
||||||
|
|
||||||
|
private transient final SQLiteDatabase db;
|
||||||
|
@SerializedName("id")
|
||||||
|
public long id = -1;
|
||||||
|
@SerializedName("instance")
|
||||||
|
public String instance;
|
||||||
|
@SerializedName("userId")
|
||||||
|
public String userId;
|
||||||
|
@SerializedName("statusId")
|
||||||
|
public String statusId;
|
||||||
|
@SerializedName("status")
|
||||||
|
public Status status;
|
||||||
|
@SerializedName("reblogged")
|
||||||
|
public int reblogged;
|
||||||
|
@SerializedName("workerUuid")
|
||||||
|
public UUID workerUuid;
|
||||||
|
@SerializedName("scheduledAt")
|
||||||
|
public Date scheduledAt;
|
||||||
|
|
||||||
|
private Context context;
|
||||||
|
|
||||||
|
public ScheduledBoost() {
|
||||||
|
db = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScheduledBoost(Context context) {
|
||||||
|
//Creation of the DB with tables
|
||||||
|
this.db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialized a UUID
|
||||||
|
*
|
||||||
|
* @param uuid {@link UUID} to serialize
|
||||||
|
* @return String serialized uuid
|
||||||
|
*/
|
||||||
|
public static String uuidToStringStorage(UUID uuid) {
|
||||||
|
Gson gson = new Gson();
|
||||||
|
try {
|
||||||
|
return gson.toJson(uuid);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unserialized a UUID
|
||||||
|
*
|
||||||
|
* @param serializedUUID String serialized UUID
|
||||||
|
* @return {@link UUID}
|
||||||
|
*/
|
||||||
|
public static UUID restoreUuidFromString(String serializedUUID) {
|
||||||
|
Gson gson = new Gson();
|
||||||
|
try {
|
||||||
|
return gson.fromJson(serializedUUID, UUID.class);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* * Remove a scheduled boost
|
||||||
|
*
|
||||||
|
* @param instance - String
|
||||||
|
* @param userId - String
|
||||||
|
* @param statusId - String
|
||||||
|
* @return long
|
||||||
|
* @throws DBException exception
|
||||||
|
*/
|
||||||
|
public int removeScheduled(String instance, String userId, String statusId) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
return db.delete(Sqlite.TABLE_SCHEDULE_BOOST, Sqlite.COL_INSTANCE + " = '" + instance + "' AND "
|
||||||
|
+ Sqlite.COL_USER_ID + " = '" + userId + "' AND "
|
||||||
|
+ Sqlite.COL_STATUS_ID + " = '" + statusId + "'"
|
||||||
|
, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert scheduled boost in db
|
||||||
|
*
|
||||||
|
* @param scheduledBoost - ScheduledBoost
|
||||||
|
* @return long - db id
|
||||||
|
* @throws DBException exception with database
|
||||||
|
*/
|
||||||
|
public long insertScheduledBoost(ScheduledBoost scheduledBoost) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(Sqlite.COL_INSTANCE, BaseMainActivity.currentInstance);
|
||||||
|
values.put(Sqlite.COL_USER_ID, BaseMainActivity.currentUserID);
|
||||||
|
values.put(Sqlite.COL_STATUS_ID, scheduledBoost.statusId);
|
||||||
|
values.put(Sqlite.COL_WORKER_UUID, uuidToStringStorage(scheduledBoost.workerUuid));
|
||||||
|
values.put(Sqlite.COL_STATUS, mastodonStatusToStringStorage(scheduledBoost.status));
|
||||||
|
values.put(Sqlite.COL_SCHEDULED_AT, Helper.dateToString(scheduledBoost.scheduledAt));
|
||||||
|
values.put(Sqlite.COL_REBLOGGED, 0);
|
||||||
|
//Inserts scheduled
|
||||||
|
try {
|
||||||
|
return db.insertOrThrow(Sqlite.TABLE_SCHEDULE_BOOST, null, values);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* * Remove a scheduled boost
|
||||||
|
*
|
||||||
|
* @param scheduledBoost - ScheduledBoost
|
||||||
|
* @return long
|
||||||
|
* @throws DBException exception
|
||||||
|
*/
|
||||||
|
public int removeScheduled(ScheduledBoost scheduledBoost) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
return db.delete(Sqlite.TABLE_SCHEDULE_BOOST, Sqlite.COL_INSTANCE + " = '" + scheduledBoost.instance + "' AND "
|
||||||
|
+ Sqlite.COL_USER_ID + " = '" + scheduledBoost.userId + "' AND "
|
||||||
|
+ Sqlite.COL_STATUS_ID + " = '" + scheduledBoost.statusId + "'"
|
||||||
|
, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the ScheduledBoost for an account that has been scheduled by the client
|
||||||
|
*
|
||||||
|
* @param account Account
|
||||||
|
* @return List<ScheduledBoost> - List of {@link ScheduledBoost}
|
||||||
|
*/
|
||||||
|
public List<ScheduledBoost> getScheduled(Account account) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Cursor c = db.query(Sqlite.TABLE_SCHEDULE_BOOST, null, Sqlite.COL_INSTANCE + " = '" + account.instance + "' AND " + Sqlite.COL_USER_ID + " = '" + account.user_id + "' AND " + Sqlite.COL_WORKER_UUID + " != ''", null, null, null, Sqlite.COL_ID + " DESC", null);
|
||||||
|
return cursorToScheduledBoost(c);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore scheduledBoost list from db
|
||||||
|
*
|
||||||
|
* @param c Cursor
|
||||||
|
* @return List<ScheduledBoost>
|
||||||
|
*/
|
||||||
|
private List<ScheduledBoost> cursorToScheduledBoost(Cursor c) {
|
||||||
|
//No element found
|
||||||
|
if (c.getCount() == 0) {
|
||||||
|
c.close();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List<ScheduledBoost> scheduledBoosts = new ArrayList<>();
|
||||||
|
while (c.moveToNext()) {
|
||||||
|
ScheduledBoost scheduledBoost = convertCursorToScheduledBoost(c);
|
||||||
|
scheduledBoosts.add(scheduledBoost);
|
||||||
|
}
|
||||||
|
//Close the cursor
|
||||||
|
c.close();
|
||||||
|
return scheduledBoosts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read cursor and hydrate without closing it
|
||||||
|
*
|
||||||
|
* @param c - Cursor
|
||||||
|
* @return Timeline
|
||||||
|
*/
|
||||||
|
private ScheduledBoost convertCursorToScheduledBoost(Cursor c) {
|
||||||
|
ScheduledBoost scheduledBoost = new ScheduledBoost();
|
||||||
|
scheduledBoost.id = c.getInt(c.getColumnIndexOrThrow(Sqlite.COL_ID));
|
||||||
|
scheduledBoost.instance = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_INSTANCE));
|
||||||
|
scheduledBoost.userId = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_USER_ID));
|
||||||
|
scheduledBoost.statusId = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_STATUS_ID));
|
||||||
|
scheduledBoost.reblogged = c.getInt(c.getColumnIndexOrThrow(Sqlite.COL_REBLOGGED));
|
||||||
|
scheduledBoost.status = restoreStatusFromString(c.getString(c.getColumnIndexOrThrow(Sqlite.COL_STATUS)));
|
||||||
|
scheduledBoost.scheduledAt = Helper.stringToDate(context, c.getString(c.getColumnIndexOrThrow(Sqlite.COL_SCHEDULED_AT)));
|
||||||
|
scheduledBoost.workerUuid = ScheduledBoost.restoreUuidFromString(c.getString(c.getColumnIndexOrThrow(Sqlite.COL_WORKER_UUID)));
|
||||||
|
return scheduledBoost;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,327 @@
|
||||||
|
package app.fedilab.android.client.entities;
|
||||||
|
/* 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 android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Pagination;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Status;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Statuses;
|
||||||
|
import app.fedilab.android.exception.DBException;
|
||||||
|
import app.fedilab.android.helper.Helper;
|
||||||
|
import app.fedilab.android.helper.MastodonHelper;
|
||||||
|
import app.fedilab.android.sqlite.Sqlite;
|
||||||
|
|
||||||
|
public class StatusCache {
|
||||||
|
|
||||||
|
private final SQLiteDatabase db;
|
||||||
|
@SerializedName("id")
|
||||||
|
public long id;
|
||||||
|
@SerializedName("user_id")
|
||||||
|
public String user_id;
|
||||||
|
@SerializedName("instance")
|
||||||
|
public String instance;
|
||||||
|
@SerializedName("type")
|
||||||
|
public CacheEnum type;
|
||||||
|
@SerializedName("status_id")
|
||||||
|
public String status_id;
|
||||||
|
@SerializedName("status")
|
||||||
|
public Status status;
|
||||||
|
@SerializedName("created_at")
|
||||||
|
public Date created_at;
|
||||||
|
@SerializedName("updated_at")
|
||||||
|
public Date updated_at;
|
||||||
|
private Context context;
|
||||||
|
|
||||||
|
public StatusCache() {
|
||||||
|
db = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StatusCache(Context context) {
|
||||||
|
//Creation of the DB with tables
|
||||||
|
this.context = context;
|
||||||
|
this.db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialized a Status class
|
||||||
|
*
|
||||||
|
* @param mastodon_status {@link Status} to serialize
|
||||||
|
* @return String serialized status
|
||||||
|
*/
|
||||||
|
public static String mastodonStatusToStringStorage(Status mastodon_status) {
|
||||||
|
Gson gson = new Gson();
|
||||||
|
try {
|
||||||
|
return gson.toJson(mastodon_status);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unserialized a Mastodon Status
|
||||||
|
*
|
||||||
|
* @param serializedStatus String serialized status
|
||||||
|
* @return {@link Status}
|
||||||
|
*/
|
||||||
|
public static Status restoreStatusFromString(String serializedStatus) {
|
||||||
|
Gson gson = new Gson();
|
||||||
|
try {
|
||||||
|
return gson.fromJson(serializedStatus, Status.class);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert or update a status
|
||||||
|
*
|
||||||
|
* @param statusCache {@link StatusCache}
|
||||||
|
* @return long - db id
|
||||||
|
* @throws DBException exception with database
|
||||||
|
*/
|
||||||
|
public long insertOrUpdate(StatusCache statusCache) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
boolean exists = statusExist(statusCache);
|
||||||
|
long idReturned;
|
||||||
|
if (exists) {
|
||||||
|
idReturned = updateStatus(statusCache);
|
||||||
|
} else {
|
||||||
|
idReturned = insertStatus(statusCache);
|
||||||
|
}
|
||||||
|
return idReturned;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a status exists in db
|
||||||
|
*
|
||||||
|
* @param statusCache Status {@link StatusCache}
|
||||||
|
* @return boolean - StatusCache exists
|
||||||
|
* @throws DBException Exception
|
||||||
|
*/
|
||||||
|
public boolean statusExist(StatusCache statusCache) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
Cursor mCount = db.rawQuery("select count(*) from " + Sqlite.TABLE_STATUS_CACHE
|
||||||
|
+ " where " + Sqlite.COL_STATUS_ID + " = '" + statusCache.status_id + "'"
|
||||||
|
+ " AND " + Sqlite.COL_INSTANCE + " = '" + statusCache.instance + "'"
|
||||||
|
+ " AND " + Sqlite.COL_USER_ID + "= '" + statusCache.user_id + "'", null);
|
||||||
|
mCount.moveToFirst();
|
||||||
|
int count = mCount.getInt(0);
|
||||||
|
mCount.close();
|
||||||
|
return (count > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert a status in db
|
||||||
|
*
|
||||||
|
* @param statusCache {@link StatusCache}
|
||||||
|
* @return long - db id
|
||||||
|
* @throws DBException exception with database
|
||||||
|
*/
|
||||||
|
private long insertStatus(StatusCache statusCache) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(Sqlite.COL_USER_ID, statusCache.user_id);
|
||||||
|
values.put(Sqlite.COL_INSTANCE, statusCache.instance);
|
||||||
|
values.put(Sqlite.COL_TYPE, statusCache.type.getValue());
|
||||||
|
values.put(Sqlite.COL_STATUS_ID, statusCache.status_id);
|
||||||
|
values.put(Sqlite.COL_STATUS, mastodonStatusToStringStorage(statusCache.status));
|
||||||
|
values.put(Sqlite.COL_CREATED_AT, Helper.dateToString(new Date()));
|
||||||
|
//Inserts token
|
||||||
|
try {
|
||||||
|
return db.insertOrThrow(Sqlite.TABLE_STATUS_CACHE, null, values);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a status in db
|
||||||
|
*
|
||||||
|
* @param statusCache {@link StatusCache}
|
||||||
|
* @return long - db id
|
||||||
|
* @throws DBException exception with database
|
||||||
|
*/
|
||||||
|
private long updateStatus(StatusCache statusCache) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(Sqlite.COL_USER_ID, statusCache.user_id);
|
||||||
|
values.put(Sqlite.COL_TYPE, statusCache.type.getValue());
|
||||||
|
values.put(Sqlite.COL_STATUS_ID, statusCache.status_id);
|
||||||
|
values.put(Sqlite.COL_STATUS, mastodonStatusToStringStorage(statusCache.status));
|
||||||
|
values.put(Sqlite.COL_UPDATED_AT, Helper.dateToString(new Date()));
|
||||||
|
//Inserts token
|
||||||
|
try {
|
||||||
|
return db.update(Sqlite.TABLE_STATUS_CACHE,
|
||||||
|
values, Sqlite.COL_STATUS_ID + " = ? AND " + Sqlite.COL_INSTANCE + " =?",
|
||||||
|
new String[]{statusCache.status_id, statusCache.instance});
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get paginated statuses from db
|
||||||
|
*
|
||||||
|
* @param type CacheEnum - not used yet but will allow to extend cache to other timelines
|
||||||
|
* @param instance String - instance
|
||||||
|
* @param user_id String - us
|
||||||
|
* @param max_id String - status having max id
|
||||||
|
* @param min_id String - status having min id
|
||||||
|
* @return Statuses
|
||||||
|
* @throws DBException - throws a db exception
|
||||||
|
*/
|
||||||
|
public Statuses geStatuses(CacheEnum type, String instance, String user_id, String max_id, String min_id) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
String selection = Sqlite.COL_INSTANCE + "='" + instance + "' AND " + Sqlite.COL_USER_ID + "= '" + user_id + "'";
|
||||||
|
String limit = String.valueOf(MastodonHelper.statusesPerCall(context));
|
||||||
|
if (max_id == null && min_id != null) {
|
||||||
|
selection += "AND " + Sqlite.COL_STATUS_ID + " >= '" + min_id + "'";
|
||||||
|
} else if (max_id != null && min_id == null) {
|
||||||
|
selection += "AND " + Sqlite.COL_STATUS_ID + " <= '" + max_id + "'";
|
||||||
|
} else if (max_id != null) {
|
||||||
|
selection += "AND " + Sqlite.COL_STATUS_ID + " >= '" + min_id + "' AND " + Sqlite.COL_STATUS_ID + " <= '" + max_id + "'";
|
||||||
|
limit = null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Cursor c = db.query(Sqlite.TABLE_STATUS_CACHE, null, selection, null, null, null, Sqlite.COL_STATUS_ID + " DESC", limit);
|
||||||
|
return createStatusReply(cursorToListOfStatuses(c));
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param type CacheEnum - not used yet but will allow to extend cache to other timelines
|
||||||
|
* @param instance String - instance
|
||||||
|
* @param user_id String - us
|
||||||
|
* @param search String search
|
||||||
|
* @return - List<Status>
|
||||||
|
* @throws DBException exception
|
||||||
|
*/
|
||||||
|
public List<Status> searchStatus(CacheEnum type, String instance, String user_id, String search) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
String selection = Sqlite.COL_INSTANCE + "='" + instance
|
||||||
|
+ "' AND " + Sqlite.COL_USER_ID + "= '" + user_id + "'";
|
||||||
|
List<Status> reply = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
Cursor c = db.query(Sqlite.TABLE_STATUS_CACHE, null, selection, null, null, null, Sqlite.COL_STATUS_ID + " DESC", "");
|
||||||
|
List<Status> statuses = cursorToListOfStatuses(c);
|
||||||
|
if (statuses != null && statuses.size() > 0) {
|
||||||
|
for (Status status : statuses) {
|
||||||
|
if (status.content.toLowerCase().contains(search.trim().toLowerCase())) {
|
||||||
|
reply.add(status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a cursor to list of statuses
|
||||||
|
*
|
||||||
|
* @param c Cursor
|
||||||
|
* @return List<Status>
|
||||||
|
*/
|
||||||
|
private List<Status> cursorToListOfStatuses(Cursor c) {
|
||||||
|
//No element found
|
||||||
|
if (c.getCount() == 0) {
|
||||||
|
c.close();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List<Status> statusList = new ArrayList<>();
|
||||||
|
while (c.moveToNext()) {
|
||||||
|
Status status = convertCursorToStatus(c);
|
||||||
|
statusList.add(status);
|
||||||
|
}
|
||||||
|
//Close the cursor
|
||||||
|
c.close();
|
||||||
|
return statusList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a reply from db in the same way than API call
|
||||||
|
*
|
||||||
|
* @param statusList List<Status>
|
||||||
|
* @return Statuses (with pagination)
|
||||||
|
*/
|
||||||
|
private Statuses createStatusReply(List<Status> statusList) {
|
||||||
|
Statuses statuses = new Statuses();
|
||||||
|
statuses.statuses = statusList;
|
||||||
|
Pagination pagination = new Pagination();
|
||||||
|
if (statusList != null && statusList.size() > 0) {
|
||||||
|
pagination.max_id = statusList.get(0).id;
|
||||||
|
pagination.min_id = statusList.get(statusList.size() - 1).id;
|
||||||
|
}
|
||||||
|
statuses.pagination = pagination;
|
||||||
|
return statuses;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read cursor and hydrate without closing it
|
||||||
|
*
|
||||||
|
* @param c - Cursor
|
||||||
|
* @return Status
|
||||||
|
*/
|
||||||
|
private Status convertCursorToStatus(Cursor c) {
|
||||||
|
String serializedStatus = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_STATUS));
|
||||||
|
return restoreStatusFromString(serializedStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum CacheEnum {
|
||||||
|
@SerializedName("HOME")
|
||||||
|
HOME("HOME");
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
CacheEnum(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,374 @@
|
||||||
|
package app.fedilab.android.client.entities;
|
||||||
|
/* 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 android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import app.fedilab.android.BaseMainActivity;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Status;
|
||||||
|
import app.fedilab.android.exception.DBException;
|
||||||
|
import app.fedilab.android.helper.Helper;
|
||||||
|
import app.fedilab.android.sqlite.Sqlite;
|
||||||
|
|
||||||
|
|
||||||
|
public class StatusDraft implements Serializable {
|
||||||
|
|
||||||
|
|
||||||
|
private transient final SQLiteDatabase db;
|
||||||
|
@SerializedName("id")
|
||||||
|
public long id = -1;
|
||||||
|
@SerializedName("instance")
|
||||||
|
public String instance;
|
||||||
|
@SerializedName("user_id")
|
||||||
|
public String user_id;
|
||||||
|
@SerializedName("statusDraftList")
|
||||||
|
public List<Status> statusDraftList;
|
||||||
|
@SerializedName("statusReplyList")
|
||||||
|
public List<Status> statusReplyList;
|
||||||
|
@SerializedName("state")
|
||||||
|
public PostState state;
|
||||||
|
@SerializedName("created_at")
|
||||||
|
public Date created_ad;
|
||||||
|
@SerializedName("updated_at")
|
||||||
|
public Date updated_at;
|
||||||
|
@SerializedName("worker_uuid")
|
||||||
|
public UUID workerUuid;
|
||||||
|
@SerializedName("scheduled_at")
|
||||||
|
public Date scheduled_at;
|
||||||
|
|
||||||
|
private transient Context context;
|
||||||
|
|
||||||
|
public StatusDraft() {
|
||||||
|
db = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StatusDraft(Context context) {
|
||||||
|
//Creation of the DB with tables
|
||||||
|
this.context = context;
|
||||||
|
this.db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialized a list of Status class
|
||||||
|
*
|
||||||
|
* @param statuses List of {@link Status} to serialize
|
||||||
|
* @return String serialized emoji list
|
||||||
|
*/
|
||||||
|
public static String mastodonStatusListToStringStorage(List<Status> statuses) {
|
||||||
|
Gson gson = new Gson();
|
||||||
|
try {
|
||||||
|
return gson.toJson(statuses);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unserialized a Status List
|
||||||
|
*
|
||||||
|
* @param serializedStatusList String serialized Status list
|
||||||
|
* @return List of {@link Status}
|
||||||
|
*/
|
||||||
|
public static List<Status> restoreStatusListFromString(String serializedStatusList) {
|
||||||
|
Gson gson = new Gson();
|
||||||
|
try {
|
||||||
|
return gson.fromJson(serializedStatusList, new TypeToken<List<Status>>() {
|
||||||
|
}.getType());
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialized a list of Status class
|
||||||
|
*
|
||||||
|
* @param postState {@link PostState} to serialize
|
||||||
|
* @return String serialized PostState list
|
||||||
|
*/
|
||||||
|
public static String postStateToStringStorage(PostState postState) {
|
||||||
|
Gson gson = new Gson();
|
||||||
|
try {
|
||||||
|
return gson.toJson(postState);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unserialized a PostState
|
||||||
|
*
|
||||||
|
* @param serializedPostState String serialized PostState
|
||||||
|
* @return {@link PostState}
|
||||||
|
*/
|
||||||
|
public static PostState restorePostStateFromString(String serializedPostState) {
|
||||||
|
Gson gson = new Gson();
|
||||||
|
try {
|
||||||
|
return gson.fromJson(serializedPostState, PostState.class);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert statusDraft in db
|
||||||
|
*
|
||||||
|
* @param statusDraft {@link StatusDraft}
|
||||||
|
* @return long - db id
|
||||||
|
* @throws DBException exception with database
|
||||||
|
*/
|
||||||
|
public long insertStatusDraft(StatusDraft statusDraft) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(Sqlite.COL_INSTANCE, statusDraft.instance);
|
||||||
|
values.put(Sqlite.COL_USER_ID, statusDraft.user_id);
|
||||||
|
values.put(Sqlite.COL_DRAFTS, mastodonStatusListToStringStorage(statusDraft.statusDraftList));
|
||||||
|
values.put(Sqlite.COL_REPLIES, mastodonStatusListToStringStorage(statusDraft.statusReplyList));
|
||||||
|
values.put(Sqlite.COL_CREATED_AT, Helper.dateToString(new Date()));
|
||||||
|
if (statusDraft.workerUuid != null) {
|
||||||
|
values.put(Sqlite.COL_WORKER_UUID, ScheduledBoost.uuidToStringStorage(statusDraft.workerUuid));
|
||||||
|
values.put(Sqlite.COL_SCHEDULED_AT, Helper.dateToString(statusDraft.scheduled_at));
|
||||||
|
}
|
||||||
|
//Inserts drafts
|
||||||
|
try {
|
||||||
|
return db.insertOrThrow(Sqlite.TABLE_STATUS_DRAFT, null, values);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a draft from db
|
||||||
|
*
|
||||||
|
* @param statusDraft {@link StatusDraft}
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public int removeDraft(StatusDraft statusDraft) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
return db.delete(Sqlite.TABLE_STATUS_DRAFT, Sqlite.COL_ID + " = '" + statusDraft.id + "'", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all drafts for an account from db
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public int removeAllDraft() throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
return db.delete(Sqlite.TABLE_STATUS_DRAFT, Sqlite.COL_USER_ID + " = '" + BaseMainActivity.currentUserID + "' AND " + Sqlite.COL_INSTANCE + " = '" + BaseMainActivity.currentInstance + "'", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* update statusDraft in db
|
||||||
|
*
|
||||||
|
* @param statusDraft {@link StatusDraft}
|
||||||
|
* @return long - db id
|
||||||
|
* @throws DBException exception with database
|
||||||
|
*/
|
||||||
|
public long updateStatusDraft(StatusDraft statusDraft) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(Sqlite.COL_DRAFTS, mastodonStatusListToStringStorage(statusDraft.statusDraftList));
|
||||||
|
values.put(Sqlite.COL_REPLIES, mastodonStatusListToStringStorage(statusDraft.statusReplyList));
|
||||||
|
values.put(Sqlite.COL_UPDATED_AT, Helper.dateToString(new Date()));
|
||||||
|
if (statusDraft.workerUuid != null) {
|
||||||
|
values.put(Sqlite.COL_WORKER_UUID, ScheduledBoost.uuidToStringStorage(statusDraft.workerUuid));
|
||||||
|
values.put(Sqlite.COL_SCHEDULED_AT, Helper.dateToString(statusDraft.scheduled_at));
|
||||||
|
}
|
||||||
|
//Inserts token
|
||||||
|
try {
|
||||||
|
return db.update(Sqlite.TABLE_STATUS_DRAFT,
|
||||||
|
values, Sqlite.COL_ID + " = ?",
|
||||||
|
new String[]{String.valueOf(statusDraft.id)});
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* remove schedule statusDraft in db
|
||||||
|
*
|
||||||
|
* @param statusDraft {@link StatusDraft}
|
||||||
|
* @return long - db id
|
||||||
|
* @throws DBException exception with database
|
||||||
|
*/
|
||||||
|
public long removeScheduled(StatusDraft statusDraft) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.putNull(Sqlite.COL_WORKER_UUID);
|
||||||
|
values.putNull(Sqlite.COL_SCHEDULED_AT);
|
||||||
|
//Inserts token
|
||||||
|
try {
|
||||||
|
return db.update(Sqlite.TABLE_STATUS_DRAFT,
|
||||||
|
values, Sqlite.COL_ID + " = ?",
|
||||||
|
new String[]{String.valueOf(statusDraft.id)});
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* update statusDraft in db
|
||||||
|
*
|
||||||
|
* @param statusDraft {@link StatusDraft}
|
||||||
|
* @return long - db id
|
||||||
|
* @throws DBException exception with database
|
||||||
|
*/
|
||||||
|
public long updatePostState(StatusDraft statusDraft) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(Sqlite.COL_STATE, postStateToStringStorage(statusDraft.state));
|
||||||
|
values.put(Sqlite.COL_UPDATED_AT, Helper.dateToString(new Date()));
|
||||||
|
//Inserts token
|
||||||
|
try {
|
||||||
|
return db.update(Sqlite.TABLE_STATUS_DRAFT,
|
||||||
|
values, Sqlite.COL_ID + " = ?",
|
||||||
|
new String[]{String.valueOf(statusDraft.id)});
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the StatusDraft for an account
|
||||||
|
*
|
||||||
|
* @param account Account
|
||||||
|
* @return List<StatusDraft> - List of {@link StatusDraft}
|
||||||
|
*/
|
||||||
|
public List<StatusDraft> geStatusDraftList(Account account) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Cursor c = db.query(Sqlite.TABLE_STATUS_DRAFT, null, Sqlite.COL_INSTANCE + " = '" + account.instance + "' AND " + Sqlite.COL_USER_ID + " = '" + account.user_id + "'", null, null, null, Sqlite.COL_UPDATED_AT + " ASC", null);
|
||||||
|
return cursorToStatusDraftList(c);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the StatusDraft for an account that has been scheduled by the client
|
||||||
|
*
|
||||||
|
* @param account Account
|
||||||
|
* @return List<StatusDraft> - List of {@link StatusDraft}
|
||||||
|
*/
|
||||||
|
public List<StatusDraft> geStatusDraftScheduledList(Account account) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Cursor c = db.query(Sqlite.TABLE_STATUS_DRAFT, null, Sqlite.COL_INSTANCE + " = '" + account.instance + "' AND " + Sqlite.COL_USER_ID + " = '" + account.user_id + "' AND " + Sqlite.COL_WORKER_UUID + " != ''", null, null, null, Sqlite.COL_UPDATED_AT + " ASC", null);
|
||||||
|
return cursorToStatusDraftList(c);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the StatusDraft for an account
|
||||||
|
*
|
||||||
|
* @param draftId String
|
||||||
|
* @return StatusDraft - {@link StatusDraft}
|
||||||
|
*/
|
||||||
|
public StatusDraft geStatusDraft(String draftId) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Cursor c = db.query(Sqlite.TABLE_STATUS_DRAFT, null, Sqlite.COL_ID + " = '" + draftId + "'", null, null, null, null, "1");
|
||||||
|
return convertCursorToStatusDraft(c);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore statusDraft list from db
|
||||||
|
*
|
||||||
|
* @param c Cursor
|
||||||
|
* @return List<Emoji>
|
||||||
|
*/
|
||||||
|
private List<StatusDraft> cursorToStatusDraftList(Cursor c) {
|
||||||
|
//No element found
|
||||||
|
if (c.getCount() == 0) {
|
||||||
|
c.close();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List<StatusDraft> statusDrafts = new ArrayList<>();
|
||||||
|
while (c.moveToNext()) {
|
||||||
|
StatusDraft statusDraft = convertCursorToStatusDraft(c);
|
||||||
|
statusDrafts.add(statusDraft);
|
||||||
|
}
|
||||||
|
//Close the cursor
|
||||||
|
c.close();
|
||||||
|
return statusDrafts;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read cursor and hydrate without closing it
|
||||||
|
*
|
||||||
|
* @param c - Cursor
|
||||||
|
* @return Timeline
|
||||||
|
*/
|
||||||
|
private StatusDraft convertCursorToStatusDraft(Cursor c) {
|
||||||
|
StatusDraft statusDraft = new StatusDraft();
|
||||||
|
statusDraft.id = c.getInt(c.getColumnIndexOrThrow(Sqlite.COL_ID));
|
||||||
|
statusDraft.instance = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_INSTANCE));
|
||||||
|
statusDraft.user_id = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_USER_ID));
|
||||||
|
statusDraft.statusReplyList = restoreStatusListFromString(c.getString(c.getColumnIndexOrThrow(Sqlite.COL_REPLIES)));
|
||||||
|
statusDraft.statusDraftList = restoreStatusListFromString(c.getString(c.getColumnIndexOrThrow(Sqlite.COL_DRAFTS)));
|
||||||
|
statusDraft.state = restorePostStateFromString(c.getString(c.getColumnIndexOrThrow(Sqlite.COL_STATE)));
|
||||||
|
statusDraft.created_ad = Helper.stringToDate(context, c.getString(c.getColumnIndexOrThrow(Sqlite.COL_CREATED_AT)));
|
||||||
|
statusDraft.updated_at = Helper.stringToDate(context, c.getString(c.getColumnIndexOrThrow(Sqlite.COL_UPDATED_AT)));
|
||||||
|
statusDraft.workerUuid = ScheduledBoost.restoreUuidFromString(c.getString(c.getColumnIndexOrThrow(Sqlite.COL_WORKER_UUID)));
|
||||||
|
statusDraft.scheduled_at = Helper.stringToDate(context, c.getString(c.getColumnIndexOrThrow(Sqlite.COL_SCHEDULED_AT)));
|
||||||
|
return statusDraft;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,419 @@
|
||||||
|
package app.fedilab.android.client.entities;
|
||||||
|
/* 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 android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import app.fedilab.android.exception.DBException;
|
||||||
|
import app.fedilab.android.sqlite.Sqlite;
|
||||||
|
|
||||||
|
public class Timeline {
|
||||||
|
|
||||||
|
private final SQLiteDatabase db;
|
||||||
|
@SerializedName("id")
|
||||||
|
public long id;
|
||||||
|
@SerializedName("user_id")
|
||||||
|
public String user_id;
|
||||||
|
@SerializedName("instance")
|
||||||
|
public String instance;
|
||||||
|
@SerializedName("position")
|
||||||
|
public int position;
|
||||||
|
@SerializedName("type")
|
||||||
|
public TimeLineEnum type;
|
||||||
|
@SerializedName("remote_instance")
|
||||||
|
public String remote_instance;
|
||||||
|
@SerializedName("displayed")
|
||||||
|
public boolean displayed;
|
||||||
|
@SerializedName("timelineOptions")
|
||||||
|
public TimelineOptions timelineOptions;
|
||||||
|
private Context context;
|
||||||
|
|
||||||
|
public Timeline() {
|
||||||
|
db = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Timeline(Context context) {
|
||||||
|
//Creation of the DB with tables
|
||||||
|
this.context = context;
|
||||||
|
this.db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialized a TimelineOptions class
|
||||||
|
*
|
||||||
|
* @param timelineOptions {@link TimelineOptions} to serialize
|
||||||
|
* @return String serialized timeline options
|
||||||
|
*/
|
||||||
|
public static String timelineOptionsToStringStorage(TimelineOptions timelineOptions) {
|
||||||
|
Gson gson = new Gson();
|
||||||
|
try {
|
||||||
|
return gson.toJson(timelineOptions);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unserialized a TimelineOptions
|
||||||
|
*
|
||||||
|
* @param serializedTimelineOptionsString serialized timeline options
|
||||||
|
* @return {@link TimelineOptions}
|
||||||
|
*/
|
||||||
|
public static TimelineOptions restoreTimelineOptionsFromString(String serializedTimelineOptionsString) {
|
||||||
|
Gson gson = new Gson();
|
||||||
|
try {
|
||||||
|
return gson.fromJson(serializedTimelineOptionsString, TimelineOptions.class);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert a timeline
|
||||||
|
*
|
||||||
|
* @param timeline {@link Timeline}
|
||||||
|
* @return long - db id
|
||||||
|
* @throws DBException exception with database
|
||||||
|
*/
|
||||||
|
public long insert(Timeline timeline) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
if (!canBeModified(timeline)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(Sqlite.COL_POSITION, countEntries());
|
||||||
|
values.put(Sqlite.COL_USER_ID, timeline.user_id);
|
||||||
|
values.put(Sqlite.COL_INSTANCE, timeline.instance);
|
||||||
|
values.put(Sqlite.COL_TYPE, timeline.type.getValue());
|
||||||
|
values.put(Sqlite.COL_REMOTE_INSTANCE, timeline.remote_instance);
|
||||||
|
values.put(Sqlite.COL_DISPLAYED, timeline.displayed);
|
||||||
|
if (timeline.timelineOptions != null) {
|
||||||
|
values.put(Sqlite.COL_TIMELINE_OPTION, timelineOptionsToStringStorage(timeline.timelineOptions));
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return db.insertOrThrow(Sqlite.TABLE_TIMELINES, null, values);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean canBeModified(Timeline timeline) {
|
||||||
|
return timeline.type != TimeLineEnum.HOME && timeline.type != TimeLineEnum.DIRECT && timeline.type != TimeLineEnum.LOCAL && timeline.type != TimeLineEnum.PUBLIC && timeline.type != TimeLineEnum.NOTIFICATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* update a timeline
|
||||||
|
*
|
||||||
|
* @param timeline {@link Timeline}
|
||||||
|
* @return long - db id
|
||||||
|
* @throws DBException exception with database
|
||||||
|
*/
|
||||||
|
public long update(Timeline timeline) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(Sqlite.COL_POSITION, timeline.position);
|
||||||
|
values.put(Sqlite.COL_DISPLAYED, timeline.displayed);
|
||||||
|
if (timeline.timelineOptions != null && canBeModified(timeline)) {
|
||||||
|
values.put(Sqlite.COL_TIMELINE_OPTION, timelineOptionsToStringStorage(timeline.timelineOptions));
|
||||||
|
}
|
||||||
|
reorderUpdatePosition(timeline);
|
||||||
|
try {
|
||||||
|
return db.update(Sqlite.TABLE_TIMELINES,
|
||||||
|
values, Sqlite.COL_ID + " = ?",
|
||||||
|
new String[]{String.valueOf(timeline.id)});
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a timeline from db
|
||||||
|
*
|
||||||
|
* @param timeline {@link Timeline}
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public int remove(Timeline timeline) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
if (!canBeModified(timeline)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
reorderDeletePosition(timeline);
|
||||||
|
return db.delete(Sqlite.TABLE_TIMELINES, Sqlite.COL_ID + " = '" + timeline.id + "'", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all timelines between two position (positions included)
|
||||||
|
*
|
||||||
|
* @return List<Timelines> timelines
|
||||||
|
*/
|
||||||
|
public List<Timeline> getTimelineBetweenPosition(int min, int max) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
if (min > max) {
|
||||||
|
int _t = min;
|
||||||
|
min = max;
|
||||||
|
max = min;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Cursor c = db.query(Sqlite.TABLE_TIMELINES, null, Sqlite.COL_POSITION + " >= '" + min + "' AND " + Sqlite.COL_POSITION + " <= '" + max + "'", null, null, null, Sqlite.COL_POSITION + " ASC", null);
|
||||||
|
return cursorToListTimelines(c);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all timelines after a position (position included)
|
||||||
|
*
|
||||||
|
* @return List<Timelines> timelines
|
||||||
|
*/
|
||||||
|
public List<Timeline> getTimelineAfterPosition(int position) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Cursor c = db.query(Sqlite.TABLE_TIMELINES, null, Sqlite.COL_POSITION + " > '" + position + "'", null, null, null, Sqlite.COL_POSITION + " ASC", null);
|
||||||
|
return cursorToListTimelines(c);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reorder each position after moving an element
|
||||||
|
*
|
||||||
|
* @param _mTimeline Timeline
|
||||||
|
* @throws DBException - db exception
|
||||||
|
*/
|
||||||
|
public void reorderUpdatePosition(Timeline _mTimeline) throws DBException {
|
||||||
|
Timeline previousPosition = getTimeline(_mTimeline.id);
|
||||||
|
List<Timeline> timelines = getTimelineBetweenPosition(_mTimeline.position, previousPosition.position);
|
||||||
|
if (previousPosition.position > _mTimeline.position) {
|
||||||
|
for (int i = _mTimeline.position; i < timelines.size(); i++) {
|
||||||
|
Timeline timeline = timelines.get(i);
|
||||||
|
timeline.position++;
|
||||||
|
update(timeline);
|
||||||
|
}
|
||||||
|
} else if (previousPosition.position < _mTimeline.position) {
|
||||||
|
for (int i = previousPosition.position + 1; i <= timelines.size(); i++) {
|
||||||
|
Timeline timeline = timelines.get(i);
|
||||||
|
timeline.position--;
|
||||||
|
update(timeline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reorder each position after deleting an element
|
||||||
|
*
|
||||||
|
* @param _mTimeline Timeline
|
||||||
|
* @throws DBException - db exception
|
||||||
|
*/
|
||||||
|
public void reorderDeletePosition(Timeline _mTimeline) throws DBException {
|
||||||
|
List<Timeline> timelines = getTimelineAfterPosition(_mTimeline.position);
|
||||||
|
for (Timeline timeline : timelines) {
|
||||||
|
timeline.position--;
|
||||||
|
update(timeline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all timelines
|
||||||
|
*
|
||||||
|
* @return List<Timelines> timelines
|
||||||
|
*/
|
||||||
|
public List<Timeline> getTimelines() throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Cursor c = db.query(Sqlite.TABLE_TIMELINES, null, null, null, null, null, Sqlite.COL_POSITION + " ASC", null);
|
||||||
|
return cursorToListTimelines(c);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a timeline
|
||||||
|
*
|
||||||
|
* @return Timelines timeline
|
||||||
|
*/
|
||||||
|
public Timeline getTimeline(long id) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Cursor c = db.query(Sqlite.TABLE_TIMELINES, null, Sqlite.COL_ID + "='" + id + "'", null, null, null, Sqlite.COL_POSITION + " ASC", null);
|
||||||
|
return cursorToTimeline(c);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Timeline> cursorToListTimelines(Cursor c) {
|
||||||
|
//No element found
|
||||||
|
if (c.getCount() == 0) {
|
||||||
|
c.close();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List<Timeline> timelineList = new ArrayList<>();
|
||||||
|
while (c.moveToNext()) {
|
||||||
|
Timeline timeline = convertCursorToTimeLine(c);
|
||||||
|
timelineList.add(timeline);
|
||||||
|
}
|
||||||
|
//Close the cursor
|
||||||
|
c.close();
|
||||||
|
return timelineList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Timeline cursorToTimeline(Cursor c) {
|
||||||
|
//No element found
|
||||||
|
if (c.getCount() == 0) {
|
||||||
|
c.close();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
//Take the first element
|
||||||
|
c.moveToFirst();
|
||||||
|
Timeline timeline = convertCursorToTimeLine(c);
|
||||||
|
//Close the cursor
|
||||||
|
c.close();
|
||||||
|
return timeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read cursor and hydrate without closing it
|
||||||
|
*
|
||||||
|
* @param c - Cursor
|
||||||
|
* @return Timeline
|
||||||
|
*/
|
||||||
|
private Timeline convertCursorToTimeLine(Cursor c) {
|
||||||
|
Timeline timeline = new Timeline();
|
||||||
|
timeline.id = c.getInt(c.getColumnIndexOrThrow(Sqlite.COL_ID));
|
||||||
|
timeline.timelineOptions = restoreTimelineOptionsFromString(c.getString(c.getColumnIndexOrThrow(Sqlite.COL_TIMELINE_OPTION)));
|
||||||
|
timeline.type = TimeLineEnum.valueOf(c.getString(c.getColumnIndexOrThrow(Sqlite.COL_TYPE)));
|
||||||
|
timeline.displayed = c.getInt(c.getColumnIndexOrThrow(Sqlite.COL_TIMELINE_OPTION)) == 1;
|
||||||
|
timeline.instance = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_INSTANCE));
|
||||||
|
timeline.user_id = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_USER_ID));
|
||||||
|
timeline.remote_instance = c.getString(c.getColumnIndexOrThrow(Sqlite.COL_REMOTE_INSTANCE));
|
||||||
|
timeline.position = c.getInt(c.getColumnIndexOrThrow(Sqlite.COL_POSITION));
|
||||||
|
return timeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count entry in db
|
||||||
|
*
|
||||||
|
* @return int - number of timelines recorded in db
|
||||||
|
* @throws DBException Exception
|
||||||
|
*/
|
||||||
|
public int countEntries() throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
Cursor mCount = db.rawQuery("select count(*) from " + Sqlite.TABLE_TIMELINES, null);
|
||||||
|
mCount.moveToFirst();
|
||||||
|
int count = mCount.getInt(0);
|
||||||
|
mCount.close();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public enum TimeLineEnum {
|
||||||
|
@SerializedName("HOME")
|
||||||
|
HOME("HOME"),
|
||||||
|
@SerializedName("DIRECT")
|
||||||
|
DIRECT("DIRECT"),
|
||||||
|
@SerializedName("NOTIFICATION")
|
||||||
|
NOTIFICATION("NOTIFICATION"),
|
||||||
|
@SerializedName("LOCAL")
|
||||||
|
LOCAL("LOCAL"),
|
||||||
|
@SerializedName("PUBLIC")
|
||||||
|
PUBLIC("PUBLIC"),
|
||||||
|
@SerializedName("TAG")
|
||||||
|
TAG("TAG"),
|
||||||
|
@SerializedName("LIST")
|
||||||
|
LIST("LIST"),
|
||||||
|
@SerializedName("REMOTE")
|
||||||
|
REMOTE("REMOTE"),
|
||||||
|
@SerializedName("ACCOUNT_TIMELINE")
|
||||||
|
ACCOUNT_TIMELINE("ACCOUNT_TIMELINE"),
|
||||||
|
@SerializedName("MUTED_TIMELINE")
|
||||||
|
MUTED_TIMELINE("MUTED_TIMELINE"),
|
||||||
|
@SerializedName("BOOKMARK_TIMELINE")
|
||||||
|
BOOKMARK_TIMELINE("BOOKMARK_TIMELINE"),
|
||||||
|
@SerializedName("BLOCKED_TIMELINE")
|
||||||
|
BLOCKED_TIMELINE("BLOCKED_TIMELINE"),
|
||||||
|
@SerializedName("FAVOURITE_TIMELINE")
|
||||||
|
FAVOURITE_TIMELINE("FAVOURITE_TIMELINE"),
|
||||||
|
@SerializedName("REBLOG_TIMELINE")
|
||||||
|
REBLOG_TIMELINE("REBLOG_TIMELINE"),
|
||||||
|
@SerializedName("SCHEDULED_TOOT_SERVER")
|
||||||
|
SCHEDULED_TOOT_SERVER("SCHEDULED_TOOT_SERVER"),
|
||||||
|
@SerializedName("SCHEDULED_TOOT_CLIENT")
|
||||||
|
SCHEDULED_TOOT_CLIENT("SCHEDULED_TOOT_CLIENT"),
|
||||||
|
@SerializedName("SCHEDULED_BOOST")
|
||||||
|
SCHEDULED_BOOST("SCHEDULED_BOOST");
|
||||||
|
|
||||||
|
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
TimeLineEnum(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TimelineOptions {
|
||||||
|
@SerializedName("all")
|
||||||
|
public List<String> all;
|
||||||
|
@SerializedName("any")
|
||||||
|
public List<String> any;
|
||||||
|
@SerializedName("none")
|
||||||
|
public List<String> none;
|
||||||
|
@SerializedName("data")
|
||||||
|
public List<String> data;
|
||||||
|
@SerializedName("media_only")
|
||||||
|
public boolean media_only;
|
||||||
|
@SerializedName("sensitive")
|
||||||
|
public boolean sensitive;
|
||||||
|
@SerializedName("list_id")
|
||||||
|
public String list_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
package app.fedilab.android.client.entities;
|
||||||
|
/* 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 com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class WellKnownNodeinfo {
|
||||||
|
|
||||||
|
@SerializedName("links")
|
||||||
|
public List<NodeInfoLinks> links;
|
||||||
|
|
||||||
|
public static class NodeInfoLinks {
|
||||||
|
@SerializedName("reel")
|
||||||
|
public String reel;
|
||||||
|
@SerializedName("href")
|
||||||
|
public String href;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class NodeInfo {
|
||||||
|
@SerializedName("version")
|
||||||
|
public String version;
|
||||||
|
@SerializedName("software")
|
||||||
|
public Software software;
|
||||||
|
@SerializedName("usage")
|
||||||
|
public Usage usage;
|
||||||
|
@SerializedName("metadata")
|
||||||
|
public Metadata metadata;
|
||||||
|
@SerializedName("openRegistrations")
|
||||||
|
public boolean openRegistrations;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Software {
|
||||||
|
@SerializedName("name")
|
||||||
|
public String name;
|
||||||
|
@SerializedName("version")
|
||||||
|
public String version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Usage {
|
||||||
|
@SerializedName("users")
|
||||||
|
public Users users;
|
||||||
|
@SerializedName("localPosts")
|
||||||
|
public int localPosts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Users {
|
||||||
|
@SerializedName("total")
|
||||||
|
public int total;
|
||||||
|
@SerializedName("activeMonth")
|
||||||
|
public int activeMonth;
|
||||||
|
@SerializedName("activeHalfyear")
|
||||||
|
public int activeHalfyear;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Metadata {
|
||||||
|
@SerializedName("nodeName")
|
||||||
|
public String nodeName;
|
||||||
|
@SerializedName("nodeDescription")
|
||||||
|
public String nodeDescription;
|
||||||
|
@SerializedName("staffAccounts")
|
||||||
|
public List<String> staffAccounts;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package app.fedilab.android.client.entities.app;
|
||||||
|
/* 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 com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import app.fedilab.android.client.entities.Timeline;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.MastodonList;
|
||||||
|
|
||||||
|
public class PinnedTimeline implements Serializable {
|
||||||
|
|
||||||
|
@SerializedName("id")
|
||||||
|
public int id;
|
||||||
|
@SerializedName("userId")
|
||||||
|
public String userId;
|
||||||
|
@SerializedName("instance")
|
||||||
|
public String instance;
|
||||||
|
@SerializedName("position")
|
||||||
|
public int position;
|
||||||
|
@SerializedName("displayed")
|
||||||
|
public boolean displayed = true;
|
||||||
|
@SerializedName("type")
|
||||||
|
public Timeline.TimeLineEnum type;
|
||||||
|
@SerializedName("remoteInstance")
|
||||||
|
public RemoteInstance remoteInstance;
|
||||||
|
@SerializedName("tagTimeline")
|
||||||
|
public TagTimeline tagTimeline;
|
||||||
|
@SerializedName("mastodonList")
|
||||||
|
public MastodonList mastodonList;
|
||||||
|
@SerializedName("currentFilter")
|
||||||
|
public String currentFilter;
|
||||||
|
|
||||||
|
|
||||||
|
public transient boolean isSelected = false;
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
package app.fedilab.android.client.entities.app;
|
||||||
|
/* 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 com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class RemoteInstance implements Serializable {
|
||||||
|
|
||||||
|
@SerializedName("dbID")
|
||||||
|
public long dbID;
|
||||||
|
@SerializedName("id")
|
||||||
|
public String id;
|
||||||
|
@SerializedName("host")
|
||||||
|
public String host;
|
||||||
|
@SerializedName("type")
|
||||||
|
public InstanceType type;
|
||||||
|
@SerializedName("tags")
|
||||||
|
public List<String> tags;
|
||||||
|
@SerializedName("filteredWith")
|
||||||
|
public String filteredWith;
|
||||||
|
|
||||||
|
|
||||||
|
public enum InstanceType {
|
||||||
|
@SerializedName("MASTODON")
|
||||||
|
MASTODON("MASTODON"),
|
||||||
|
@SerializedName("PIXELFED")
|
||||||
|
PIXELFED("PIXELFED"),
|
||||||
|
@SerializedName("PEERTUBE")
|
||||||
|
PEERTUBE("PEERTUBE"),
|
||||||
|
@SerializedName("NITTER")
|
||||||
|
NITTER("NITTER"),
|
||||||
|
@SerializedName("MISSKEY")
|
||||||
|
MISSKEY("MISSKEY"),
|
||||||
|
@SerializedName("GNU")
|
||||||
|
GNU("GNU");
|
||||||
|
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
InstanceType(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package app.fedilab.android.client.entities.app;
|
||||||
|
/* 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 com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class TagTimeline implements Serializable {
|
||||||
|
@SerializedName("id")
|
||||||
|
public int id;
|
||||||
|
@SerializedName("name")
|
||||||
|
public String name;
|
||||||
|
@SerializedName("displayName")
|
||||||
|
public String displayName;
|
||||||
|
@SerializedName("isART")
|
||||||
|
public boolean isART;
|
||||||
|
@SerializedName("isNSFW")
|
||||||
|
public boolean isNSFW;
|
||||||
|
@SerializedName("any")
|
||||||
|
public List<String> any;
|
||||||
|
@SerializedName("all")
|
||||||
|
public List<String> all;
|
||||||
|
@SerializedName("none")
|
||||||
|
public List<String> none;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package app.fedilab.android.client.mastodon;
|
||||||
|
/* 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 java.util.List;
|
||||||
|
|
||||||
|
import app.fedilab.android.client.mastodon.entities.JoinMastodonInstance;
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.http.GET;
|
||||||
|
import retrofit2.http.Query;
|
||||||
|
|
||||||
|
public interface JoinMastodonService {
|
||||||
|
|
||||||
|
|
||||||
|
@GET("servers")
|
||||||
|
Call<List<JoinMastodonInstance>> getInstances(
|
||||||
|
@Query("category") String category
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,447 @@
|
||||||
|
package app.fedilab.android.client.mastodon;
|
||||||
|
/* 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 java.util.List;
|
||||||
|
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Account;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.FeaturedTag;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Filter;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.IdentityProof;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.MastodonList;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Preferences;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.RelationShip;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Report;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Status;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Tag;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Token;
|
||||||
|
import okhttp3.MultipartBody;
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.http.Body;
|
||||||
|
import retrofit2.http.DELETE;
|
||||||
|
import retrofit2.http.Field;
|
||||||
|
import retrofit2.http.FormUrlEncoded;
|
||||||
|
import retrofit2.http.GET;
|
||||||
|
import retrofit2.http.Header;
|
||||||
|
import retrofit2.http.Headers;
|
||||||
|
import retrofit2.http.Multipart;
|
||||||
|
import retrofit2.http.PATCH;
|
||||||
|
import retrofit2.http.POST;
|
||||||
|
import retrofit2.http.PUT;
|
||||||
|
import retrofit2.http.Part;
|
||||||
|
import retrofit2.http.Path;
|
||||||
|
import retrofit2.http.Query;
|
||||||
|
|
||||||
|
public interface MastodonAccountsService {
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Accounts
|
||||||
|
*/
|
||||||
|
//Register account
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("accounts")
|
||||||
|
Call<Token> registerAccount(
|
||||||
|
@Header("Authorization") String app_token,
|
||||||
|
@Field("username") String username,
|
||||||
|
@Field("email") String email,
|
||||||
|
@Field("password") String password,
|
||||||
|
@Field("agreement") boolean agreement,
|
||||||
|
@Field("locale") String locale,
|
||||||
|
@Field("reason") String reason);
|
||||||
|
|
||||||
|
//Info about the connected account
|
||||||
|
@GET("accounts/verify_credentials")
|
||||||
|
Call<Account> verify_credentials(
|
||||||
|
@Header("Authorization") String token);
|
||||||
|
|
||||||
|
@Multipart
|
||||||
|
@PATCH("accounts/update_credentials")
|
||||||
|
Call<Account> update_media(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Part MultipartBody.Part avatar,
|
||||||
|
@Part MultipartBody.Part header
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
@Headers({"Accept: application/json"})
|
||||||
|
@PATCH("accounts/update_credentials")
|
||||||
|
Call<Account> update_credentials(
|
||||||
|
@Header("Authorization") String token, @Body Account.AccountParams accountParams
|
||||||
|
);
|
||||||
|
|
||||||
|
@FormUrlEncoded
|
||||||
|
@PATCH("accounts/update_credentials")
|
||||||
|
Call<Account> update_credentials(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Field("discoverable") Boolean discoverable,
|
||||||
|
@Field("bot") Boolean bot,
|
||||||
|
@Field("display_name") String display_name,
|
||||||
|
@Field("note") String note,
|
||||||
|
@Field("locked") Boolean locked,
|
||||||
|
@Field("source[privacy]") String privacy,
|
||||||
|
@Field("source[sensitive]") Boolean sensitive,
|
||||||
|
@Field("source[language]") String language,
|
||||||
|
@Field("fields_attributes") List<app.fedilab.android.client.mastodon.entities.Field.FieldParams> fields
|
||||||
|
);
|
||||||
|
|
||||||
|
//Get Account
|
||||||
|
@GET("accounts/{id}")
|
||||||
|
Call<Account> getAccount(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Get Account statuses
|
||||||
|
@GET("accounts/{id}/statuses")
|
||||||
|
Call<List<Status>> getAccountStatuses(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id,
|
||||||
|
@Query("max_id") String max_id,
|
||||||
|
@Query("since_id") String since_id,
|
||||||
|
@Query("min_id") String min_id,
|
||||||
|
@Query("exclude_replies") Boolean exclude_replies,
|
||||||
|
@Query("exclude_reblogs") Boolean exclude_reblogs,
|
||||||
|
@Query("only_media") Boolean only_media,
|
||||||
|
@Query("pinned") Boolean pinned,
|
||||||
|
@Query("limit") int limit
|
||||||
|
);
|
||||||
|
|
||||||
|
//Get Account followers
|
||||||
|
@GET("accounts/{id}/followers")
|
||||||
|
Call<List<Account>> getAccountFollowers(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id,
|
||||||
|
@Query("max_id") String max_id,
|
||||||
|
@Query("since_id") String since_id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Get Account following
|
||||||
|
@GET("accounts/{id}/following")
|
||||||
|
Call<List<Account>> getAccountFollowing(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id,
|
||||||
|
@Query("max_id") String max_id,
|
||||||
|
@Query("since_id") String since_id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Get Account featured tags
|
||||||
|
@GET("accounts/{id}/featured_tags")
|
||||||
|
Call<List<FeaturedTag>> getAccountFeaturedTags(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Lists containing this account
|
||||||
|
@GET("accounts/{id}/lists")
|
||||||
|
Call<List<MastodonList>> getListContainingAccount(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Get Identity proofs
|
||||||
|
@GET("accounts/{id}/identity_proofs")
|
||||||
|
Call<List<IdentityProof>> getIdentityProofs(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Follow account
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("accounts/{id}/follow")
|
||||||
|
Call<RelationShip> follow(
|
||||||
|
@Header("Authorization") String app_token,
|
||||||
|
@Path("id") String id,
|
||||||
|
@Field("reblogs") boolean reblogs,
|
||||||
|
@Field("notify") boolean notify
|
||||||
|
);
|
||||||
|
|
||||||
|
//Follow account
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("accounts/{id}/note")
|
||||||
|
Call<RelationShip> note(
|
||||||
|
@Header("Authorization") String app_token,
|
||||||
|
@Path("id") String id,
|
||||||
|
@Field("comment") boolean comment
|
||||||
|
);
|
||||||
|
|
||||||
|
//Unfollow account
|
||||||
|
@POST("accounts/{id}/unfollow")
|
||||||
|
Call<RelationShip> unfollow(
|
||||||
|
@Header("Authorization") String app_token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Block account
|
||||||
|
@POST("accounts/{id}/block")
|
||||||
|
Call<RelationShip> block(
|
||||||
|
@Header("Authorization") String app_token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Unblock account
|
||||||
|
@POST("accounts/{id}/unblock")
|
||||||
|
Call<RelationShip> unblock(
|
||||||
|
@Header("Authorization") String app_token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Mute account
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("accounts/{id}/mute")
|
||||||
|
Call<RelationShip> mute(
|
||||||
|
@Header("Authorization") String app_token,
|
||||||
|
@Path("id") String id,
|
||||||
|
@Field("notifications") Boolean notifications,
|
||||||
|
@Field("duration") Integer duration
|
||||||
|
);
|
||||||
|
|
||||||
|
//Unmute account
|
||||||
|
@POST("accounts/{id}/unmute")
|
||||||
|
Call<RelationShip> unmute(
|
||||||
|
@Header("Authorization") String app_token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Feature on profile
|
||||||
|
@POST("accounts/{id}/pin")
|
||||||
|
Call<RelationShip> endorse(
|
||||||
|
@Header("Authorization") String app_token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Unfeature account
|
||||||
|
@POST("accounts/{id}/unpin")
|
||||||
|
Call<RelationShip> unendorse(
|
||||||
|
@Header("Authorization") String app_token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
//User note
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("accounts/{id}/note")
|
||||||
|
Call<RelationShip> note(
|
||||||
|
@Header("Authorization") String app_token,
|
||||||
|
@Path("id") String id,
|
||||||
|
@Field("comment") String comment
|
||||||
|
);
|
||||||
|
|
||||||
|
//Get relationships
|
||||||
|
@GET("accounts/relationships")
|
||||||
|
Call<List<RelationShip>> getRelationships(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Query("id[]") List<String> ids
|
||||||
|
);
|
||||||
|
|
||||||
|
//Get search
|
||||||
|
@GET("accounts/search")
|
||||||
|
Call<List<Account>> searchAccounts(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Query("q") String q,
|
||||||
|
@Query("limit") int limit,
|
||||||
|
@Query("resolve") boolean resolve,
|
||||||
|
@Query("following") boolean following
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
//Bookmarks
|
||||||
|
@GET("bookmarks")
|
||||||
|
Call<List<Status>> getBookmarks(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Query("limit") String limit,
|
||||||
|
@Query("max_id") String max_id,
|
||||||
|
@Query("since_id") String since_id,
|
||||||
|
@Query("min_id") String min_id
|
||||||
|
);
|
||||||
|
|
||||||
|
//favourites
|
||||||
|
@GET("favourites")
|
||||||
|
Call<List<Status>> getFavourites(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Query("limit") String limit,
|
||||||
|
@Query("min_id") String min_id,
|
||||||
|
@Query("max_id") String max_id
|
||||||
|
);
|
||||||
|
|
||||||
|
//muted users
|
||||||
|
@GET("mutes")
|
||||||
|
Call<List<Account>> getMutes(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Query("limit") String limit,
|
||||||
|
@Query("max_id") String max_id,
|
||||||
|
@Query("since_id") String since_id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Blocked users
|
||||||
|
@GET("blocks")
|
||||||
|
Call<List<Account>> getBlocks(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Query("limit") String limit,
|
||||||
|
@Query("max_id") String max_id,
|
||||||
|
@Query("since_id") String since_id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Get blocked domains
|
||||||
|
@GET("domain_blocks")
|
||||||
|
Call<List<String>> getDomainBlocks(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Query("limit") String limit,
|
||||||
|
@Query("max_id") String max_id,
|
||||||
|
@Query("since_id") String since_id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Add a blocked domains
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("domain_blocks")
|
||||||
|
Call<Void> addDomainBlock(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Field("domain") String domain
|
||||||
|
);
|
||||||
|
|
||||||
|
//Remove a blocked domains
|
||||||
|
@DELETE("domain_blocks")
|
||||||
|
Call<Void> removeDomainBlocks(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@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
|
||||||
|
@Headers({"Accept: application/json"})
|
||||||
|
@POST("reports")
|
||||||
|
Call<Report> report(
|
||||||
|
@Header("Authorization") String token, @Body Report.ReportParams params
|
||||||
|
);
|
||||||
|
|
||||||
|
//Get follow request
|
||||||
|
@GET("follow_requests")
|
||||||
|
Call<List<Account>> getFollowRequests(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("limit") String limit);
|
||||||
|
|
||||||
|
//Accept follow request
|
||||||
|
@POST("follow_requests/{id}/authorize")
|
||||||
|
Call<RelationShip> acceptFollow(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Reject follow request
|
||||||
|
@POST("follow_requests/{id}/reject")
|
||||||
|
Call<RelationShip> rejectFollow(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Accounts that the user is currently featuring on their profile.
|
||||||
|
@GET("endorsements")
|
||||||
|
Call<List<Account>> getEndorsements(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Query("limit") String limit,
|
||||||
|
@Query("max_id") String max_id,
|
||||||
|
@Query("since_id") String since_id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Feature tags
|
||||||
|
@GET("featured_tags")
|
||||||
|
Call<List<FeaturedTag>> getFeaturedTags(
|
||||||
|
@Header("Authorization") String token
|
||||||
|
);
|
||||||
|
|
||||||
|
//Add a feature tags
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("featured_tags")
|
||||||
|
Call<FeaturedTag> addFeaturedTag(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Field("name") String name
|
||||||
|
);
|
||||||
|
|
||||||
|
//Remove a feature tags
|
||||||
|
@DELETE("featured_tags/{id}")
|
||||||
|
Call<Void> removeFeaturedTag(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Feature tags suggestions
|
||||||
|
@GET("featured_tags/suggestions")
|
||||||
|
Call<List<Tag>> getFeaturedTagsSuggestions(
|
||||||
|
@Header("Authorization") String token
|
||||||
|
);
|
||||||
|
|
||||||
|
//Get user preferences
|
||||||
|
@GET("preferences")
|
||||||
|
Call<Preferences> getPreferences(
|
||||||
|
@Header("Authorization") String token
|
||||||
|
);
|
||||||
|
|
||||||
|
//Get user suggestions
|
||||||
|
@GET("suggestions")
|
||||||
|
Call<List<Account>> getSuggestions(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Query("limit") String limit
|
||||||
|
);
|
||||||
|
|
||||||
|
//Remove a user suggestion
|
||||||
|
@DELETE("suggestions/{account_id}")
|
||||||
|
Call<Void> removeSuggestion(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("account_id") String account_id
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,149 @@
|
||||||
|
package app.fedilab.android.client.mastodon;
|
||||||
|
/* 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 java.util.List;
|
||||||
|
|
||||||
|
import app.fedilab.android.client.mastodon.entities.AdminAccount;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.AdminReport;
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.http.Field;
|
||||||
|
import retrofit2.http.FormUrlEncoded;
|
||||||
|
import retrofit2.http.GET;
|
||||||
|
import retrofit2.http.Header;
|
||||||
|
import retrofit2.http.POST;
|
||||||
|
import retrofit2.http.Path;
|
||||||
|
import retrofit2.http.Query;
|
||||||
|
|
||||||
|
public interface MastodonAdminService {
|
||||||
|
|
||||||
|
@GET("/admin/accounts")
|
||||||
|
Call<List<AdminAccount>> getAccounts(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Query("local") boolean local,
|
||||||
|
@Query("remote") boolean remote,
|
||||||
|
@Query("by_domain") String by_domain,
|
||||||
|
@Query("active") boolean active,
|
||||||
|
@Query("pending") boolean pending,
|
||||||
|
@Query("disabled") boolean disabled,
|
||||||
|
@Query("silenced") boolean silenced,
|
||||||
|
@Query("suspended") boolean suspended,
|
||||||
|
@Query("username") String username,
|
||||||
|
@Query("display_name") String display_name,
|
||||||
|
@Query("email") String email,
|
||||||
|
@Query("ip") String ip,
|
||||||
|
@Query("staff") boolean staff,
|
||||||
|
@Query("max_id") String max_id,
|
||||||
|
@Query("since_id") String since_id,
|
||||||
|
@Query("limit") int limit
|
||||||
|
);
|
||||||
|
|
||||||
|
@GET("/admin/accounts/{id}")
|
||||||
|
Call<AdminAccount> getAccount(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
@POST("/admin/accounts/{account_id}/action")
|
||||||
|
Call<Void> performAction(
|
||||||
|
@Header("Authorization") String app_token,
|
||||||
|
@Path("account_id") String account_id,
|
||||||
|
@Field("type") String type,
|
||||||
|
@Field("report_id") String report_id,
|
||||||
|
@Field("warning_preset_id") String warning_preset_id,
|
||||||
|
@Field("text") String text,
|
||||||
|
@Field("send_email_notification") boolean send_email_notification
|
||||||
|
);
|
||||||
|
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("/admin/accounts/{account_id}/approve")
|
||||||
|
Call<AdminAccount> approve(
|
||||||
|
@Header("Authorization") String app_token,
|
||||||
|
@Path("account_id") String account_id
|
||||||
|
);
|
||||||
|
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("/admin/accounts/{account_id}/reject")
|
||||||
|
Call<AdminAccount> reject(
|
||||||
|
@Header("Authorization") String app_token,
|
||||||
|
@Path("account_id") String account_id
|
||||||
|
);
|
||||||
|
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("/admin/accounts/{account_id}/enable")
|
||||||
|
Call<AdminAccount> enable(
|
||||||
|
@Header("Authorization") String app_token,
|
||||||
|
@Path("account_id") String account_id
|
||||||
|
);
|
||||||
|
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("/admin/accounts/{account_id}/unsilence")
|
||||||
|
Call<AdminAccount> unsilence(
|
||||||
|
@Header("Authorization") String app_token,
|
||||||
|
@Path("account_id") String account_id
|
||||||
|
);
|
||||||
|
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("/admin/accounts/{account_id}/unsuspend")
|
||||||
|
Call<AdminAccount> unsuspend(
|
||||||
|
@Header("Authorization") String app_token,
|
||||||
|
@Path("account_id") String account_id
|
||||||
|
);
|
||||||
|
|
||||||
|
@FormUrlEncoded
|
||||||
|
@GET("/admin/reports")
|
||||||
|
Call<List<AdminReport>> getReports(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Field("resolved") boolean resolved,
|
||||||
|
@Field("account_id") String account_id,
|
||||||
|
@Field("target_account_id") String target_account_id
|
||||||
|
);
|
||||||
|
|
||||||
|
@FormUrlEncoded
|
||||||
|
@GET("/admin/reports/{id}")
|
||||||
|
Call<AdminReport> getReport(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("/admin/reports/{id}/assign_to_self")
|
||||||
|
Call<AdminReport> assignToSelf(
|
||||||
|
@Header("Authorization") String app_token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("/admin/reports/{id}/unassign")
|
||||||
|
Call<AdminReport> unassign(
|
||||||
|
@Header("Authorization") String app_token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("/admin/reports/{id}/resolve")
|
||||||
|
Call<AdminReport> resolved(
|
||||||
|
@Header("Authorization") String app_token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("/admin/reports/{id}/reopen")
|
||||||
|
Call<AdminReport> reopen(
|
||||||
|
@Header("Authorization") String app_token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
package app.fedilab.android.client.mastodon;
|
||||||
|
/* 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 java.util.List;
|
||||||
|
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Announcement;
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.http.DELETE;
|
||||||
|
import retrofit2.http.FormUrlEncoded;
|
||||||
|
import retrofit2.http.GET;
|
||||||
|
import retrofit2.http.Header;
|
||||||
|
import retrofit2.http.POST;
|
||||||
|
import retrofit2.http.PUT;
|
||||||
|
import retrofit2.http.Path;
|
||||||
|
import retrofit2.http.Query;
|
||||||
|
|
||||||
|
public interface MastodonAnnouncementsService {
|
||||||
|
|
||||||
|
@GET("/announcements")
|
||||||
|
Call<List<Announcement>> getAnnouncements(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Query("with_dismissed") boolean with_dismissed
|
||||||
|
);
|
||||||
|
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("/announcements/{id}/dismiss")
|
||||||
|
Call<Void> dismiss(
|
||||||
|
@Header("Authorization") String app_token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
@FormUrlEncoded
|
||||||
|
@PUT("/announcements/{id}/reactions/{name}")
|
||||||
|
Call<Void> addReaction(
|
||||||
|
@Header("Authorization") String app_token,
|
||||||
|
@Path("id") String id,
|
||||||
|
@Path("name") String name
|
||||||
|
);
|
||||||
|
|
||||||
|
@DELETE("/announcements/{id}/reactions/{name}")
|
||||||
|
Call<Void> removeReaction(
|
||||||
|
@Header("Authorization") String app_token,
|
||||||
|
@Path("id") String id,
|
||||||
|
@Path("name") String name
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
package app.fedilab.android.client.mastodon;
|
||||||
|
/* 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 app.fedilab.android.client.mastodon.entities.App;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Token;
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.http.Field;
|
||||||
|
import retrofit2.http.FormUrlEncoded;
|
||||||
|
import retrofit2.http.GET;
|
||||||
|
import retrofit2.http.Header;
|
||||||
|
import retrofit2.http.POST;
|
||||||
|
|
||||||
|
public interface MastodonAppsService {
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* OAUTH - TOKEN
|
||||||
|
*/
|
||||||
|
//Create app
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("apps")
|
||||||
|
Call<App> createApp(
|
||||||
|
@Field("client_name") String client_name,
|
||||||
|
@Field("redirect_uris") String redirect_uris,
|
||||||
|
@Field("scopes") String scopes,
|
||||||
|
@Field("website") String website);
|
||||||
|
|
||||||
|
@GET("apps/verify_credentials")
|
||||||
|
Call<App> verifyCredentials(
|
||||||
|
@Header("Authorization") String app_token);
|
||||||
|
|
||||||
|
|
||||||
|
//Create token
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("oauth/token")
|
||||||
|
Call<Token> createToken(
|
||||||
|
@Field("grant_type") String grant_type,
|
||||||
|
@Field("client_id") String client_id,
|
||||||
|
@Field("client_secret") String client_secret,
|
||||||
|
@Field("redirect_uri") String redirect_uri,
|
||||||
|
@Field("scope") String scope,
|
||||||
|
@Field("code") String code);
|
||||||
|
|
||||||
|
//Revoke token
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("oauth/revoke")
|
||||||
|
Call<Void> revokeToken(
|
||||||
|
@Field("client_id") String client_id,
|
||||||
|
@Field("client_secret") String client_secret,
|
||||||
|
@Field("token") String token);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
package app.fedilab.android.client.mastodon;
|
||||||
|
/* 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 java.util.List;
|
||||||
|
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Account;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Activity;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Emoji;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Instance;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Tag;
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.http.GET;
|
||||||
|
import retrofit2.http.Query;
|
||||||
|
|
||||||
|
|
||||||
|
public interface MastodonInstanceService {
|
||||||
|
|
||||||
|
@GET("instance")
|
||||||
|
Call<Instance> instance();
|
||||||
|
|
||||||
|
@GET("instance/peers")
|
||||||
|
Call<List<String>> connectedInstance();
|
||||||
|
|
||||||
|
@GET("instance/activity")
|
||||||
|
Call<List<Activity>> weeklyActivity();
|
||||||
|
|
||||||
|
@GET("trends")
|
||||||
|
Call<List<Tag>> trends();
|
||||||
|
|
||||||
|
@GET("directory")
|
||||||
|
Call<List<Account>> directory(
|
||||||
|
@Query("offset") int offset,
|
||||||
|
@Query("limit") int limit,
|
||||||
|
@Query("order") String order,
|
||||||
|
@Query("local") boolean local
|
||||||
|
);
|
||||||
|
|
||||||
|
@GET("custom_emojis")
|
||||||
|
Call<List<Emoji>> customEmoji();
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
package app.fedilab.android.client.mastodon;
|
||||||
|
/* 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 java.util.List;
|
||||||
|
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Notification;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.PushSubscription;
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.http.DELETE;
|
||||||
|
import retrofit2.http.Field;
|
||||||
|
import retrofit2.http.FormUrlEncoded;
|
||||||
|
import retrofit2.http.GET;
|
||||||
|
import retrofit2.http.Header;
|
||||||
|
import retrofit2.http.POST;
|
||||||
|
import retrofit2.http.PUT;
|
||||||
|
import retrofit2.http.Path;
|
||||||
|
import retrofit2.http.Query;
|
||||||
|
|
||||||
|
public interface MastodonNotificationsService {
|
||||||
|
|
||||||
|
@GET("notifications")
|
||||||
|
Call<List<Notification>> getNotifications(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Query("exclude_types[]") List<String> exclude_types,
|
||||||
|
@Query("account_id") String account_id,
|
||||||
|
@Query("max_id") String max_id,
|
||||||
|
@Query("since_id") String since_id,
|
||||||
|
@Query("min_id") String min_id,
|
||||||
|
@Query("limit") int limit
|
||||||
|
);
|
||||||
|
|
||||||
|
@GET("notifications/{id}")
|
||||||
|
Call<Notification> getNotification(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
@POST("notifications/clear")
|
||||||
|
Call<Void> clearAllNotifications(
|
||||||
|
@Header("Authorization") String token
|
||||||
|
);
|
||||||
|
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("notifications/{id}/dismiss")
|
||||||
|
Call<Void> dismissNotification(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("push/subscription")
|
||||||
|
Call<PushSubscription> pushSubscription(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Field("subscription[endpoint]") String endpoint,
|
||||||
|
@Field("subscription[keys][p256dh]") String keys_p256dh,
|
||||||
|
@Field("subscription[keys][auth]") String keys_auth,
|
||||||
|
@Field("data[alerts][follow]") boolean follow,
|
||||||
|
@Field("data[alerts][favourite]") boolean favourite,
|
||||||
|
@Field("data[alerts][reblog]") boolean reblog,
|
||||||
|
@Field("data[alerts][mention]") boolean mention,
|
||||||
|
@Field("data[alerts][poll]") boolean poll
|
||||||
|
);
|
||||||
|
|
||||||
|
@GET("push/subscription")
|
||||||
|
Call<PushSubscription> getPushSubscription(
|
||||||
|
@Header("Authorization") String token
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
@FormUrlEncoded
|
||||||
|
@PUT("push/subscription")
|
||||||
|
Call<PushSubscription> updatePushSubscription(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Field("data[alerts][follow]") boolean follow,
|
||||||
|
@Field("data[alerts][favourite]") boolean favourite,
|
||||||
|
@Field("data[alerts][reblog]") boolean reblog,
|
||||||
|
@Field("data[alerts][mention]") boolean mention,
|
||||||
|
@Field("data[alerts][poll]") boolean poll
|
||||||
|
);
|
||||||
|
|
||||||
|
@DELETE("push/subscription")
|
||||||
|
Call<Void> deletePushsubscription(
|
||||||
|
@Header("Authorization") String token
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package app.fedilab.android.client.mastodon;
|
||||||
|
/* 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 app.fedilab.android.client.mastodon.entities.Oembed;
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.http.GET;
|
||||||
|
import retrofit2.http.Query;
|
||||||
|
|
||||||
|
public interface MastodonOembedService {
|
||||||
|
|
||||||
|
@GET("/oembed")
|
||||||
|
Call<Oembed> oembed(
|
||||||
|
@Query("url") String url,
|
||||||
|
@Query("maxwidth") int maxwidth,
|
||||||
|
@Query("maxheight") int maxheight
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package app.fedilab.android.client.mastodon;
|
||||||
|
/* 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 app.fedilab.android.client.mastodon.entities.Results;
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.http.GET;
|
||||||
|
import retrofit2.http.Header;
|
||||||
|
import retrofit2.http.Query;
|
||||||
|
|
||||||
|
public interface MastodonSearchService {
|
||||||
|
//API V2
|
||||||
|
@GET("search")
|
||||||
|
Call<Results> search(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Query("q") String q,
|
||||||
|
@Query("account_id") String account_id,
|
||||||
|
@Query("type") String type,
|
||||||
|
@Query("exclude_unreviewed") boolean exclude_unreviewed,
|
||||||
|
@Query("resolve") boolean resolve,
|
||||||
|
@Query("following") boolean following,
|
||||||
|
@Query("offset") int offset,
|
||||||
|
@Query("max_id") String max_id,
|
||||||
|
@Query("min_id") String min_id,
|
||||||
|
@Query("limit") int limit
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,284 @@
|
||||||
|
package app.fedilab.android.client.mastodon;
|
||||||
|
/* 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 java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Account;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Attachment;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Card;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Context;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Poll;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.ScheduledStatus;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Status;
|
||||||
|
import okhttp3.MultipartBody;
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.http.DELETE;
|
||||||
|
import retrofit2.http.Field;
|
||||||
|
import retrofit2.http.FormUrlEncoded;
|
||||||
|
import retrofit2.http.GET;
|
||||||
|
import retrofit2.http.Header;
|
||||||
|
import retrofit2.http.Multipart;
|
||||||
|
import retrofit2.http.POST;
|
||||||
|
import retrofit2.http.PUT;
|
||||||
|
import retrofit2.http.Part;
|
||||||
|
import retrofit2.http.Path;
|
||||||
|
import retrofit2.http.Query;
|
||||||
|
|
||||||
|
|
||||||
|
public interface MastodonStatusesService {
|
||||||
|
|
||||||
|
//Post a status
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("statuses")
|
||||||
|
Call<Status> createStatus(
|
||||||
|
@Header("Idempotency-Key") String idempotency_Key,
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@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
|
||||||
|
);
|
||||||
|
|
||||||
|
//Post a scheduled status
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("statuses")
|
||||||
|
Call<ScheduledStatus> createScheduledStatus(
|
||||||
|
@Header("Idempotency-Key") String idempotency_Key,
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@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("scheduled_at") String scheduled_at,
|
||||||
|
@Field("language") String language
|
||||||
|
);
|
||||||
|
|
||||||
|
//Get a specific status
|
||||||
|
@GET("statuses/{id}")
|
||||||
|
Call<Status> getStatus(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Delete a specific status
|
||||||
|
@DELETE("statuses/{id}")
|
||||||
|
Call<Status> deleteStatus(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Get parent and child statuses
|
||||||
|
@GET("statuses/{id}/context")
|
||||||
|
Call<Context> getContext(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Get reblogged by
|
||||||
|
@GET("statuses/{id}/reblogged_by")
|
||||||
|
Call<List<Account>> getRebloggedBy(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id,
|
||||||
|
@Query("max_id") String max_id,
|
||||||
|
@Query("since_id") String since_id,
|
||||||
|
@Query("min_id") String min_id,
|
||||||
|
@Query("limit") int limit
|
||||||
|
);
|
||||||
|
|
||||||
|
//Get favourited by
|
||||||
|
@GET("statuses/{id}/favourited_by")
|
||||||
|
Call<List<Account>> getFavourited(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id,
|
||||||
|
@Query("max_id") String max_id,
|
||||||
|
@Query("since_id") String since_id,
|
||||||
|
@Query("min_id") String min_id,
|
||||||
|
@Query("limit") int limit
|
||||||
|
);
|
||||||
|
|
||||||
|
//Add status to favourites
|
||||||
|
@POST("statuses/{id}/favourite")
|
||||||
|
Call<Status> favourites(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Remove status from favourites
|
||||||
|
@POST("statuses/{id}/unfavourite")
|
||||||
|
Call<Status> unFavourite(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Reblog a status
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("statuses/{id}/reblog")
|
||||||
|
Call<Status> reblog(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id,
|
||||||
|
@Field("visibility") String visibility
|
||||||
|
);
|
||||||
|
|
||||||
|
//Unreblog a status
|
||||||
|
@POST("statuses/{id}/unreblog")
|
||||||
|
Call<Status> unReblog(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Bookmark a status
|
||||||
|
@POST("statuses/{id}/bookmark")
|
||||||
|
Call<Status> bookmark(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Unbookmark a status
|
||||||
|
@POST("statuses/{id}/unbookmark")
|
||||||
|
Call<Status> unBookmark(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Mute a conversation
|
||||||
|
@POST("statuses/{id}/mute")
|
||||||
|
Call<Status> muteConversation(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
//UnMute a conversation
|
||||||
|
@POST("statuses/{id}/unmute")
|
||||||
|
Call<Status> unMuteConversation(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Pin a status
|
||||||
|
@POST("statuses/{id}/pin")
|
||||||
|
Call<Status> pin(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
//UNPin a status
|
||||||
|
@POST("statuses/{id}/unpin")
|
||||||
|
Call<Status> unPin(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Get reblogged by
|
||||||
|
@GET("statuses/{id}/card")
|
||||||
|
Call<Card> getCard(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
//Get a Media
|
||||||
|
@GET("media/{id}")
|
||||||
|
Call<Attachment> getMedia(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Upload a Media
|
||||||
|
@Multipart
|
||||||
|
@POST("media")
|
||||||
|
Call<Attachment> postMedia(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Part MultipartBody.Part file,
|
||||||
|
@Part MultipartBody.Part thumbnail,
|
||||||
|
@Part("description") String description,
|
||||||
|
@Part("focus") String focus
|
||||||
|
);
|
||||||
|
|
||||||
|
//Edit a Media
|
||||||
|
@Multipart
|
||||||
|
@PUT("media/{id}")
|
||||||
|
Call<Attachment> updateMedia(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id,
|
||||||
|
@Part MultipartBody.Part file,
|
||||||
|
@Part MultipartBody.Part thumbnail,
|
||||||
|
@Part("description") String description,
|
||||||
|
@Part("focus") String focus
|
||||||
|
);
|
||||||
|
|
||||||
|
//Get a Poll
|
||||||
|
@GET("polls/{id}")
|
||||||
|
Call<Poll> getPoll(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Vote on a Poll
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("polls/{id}/votes")
|
||||||
|
Call<Poll> votePoll(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id,
|
||||||
|
@Field("choices[]") int[] choices
|
||||||
|
);
|
||||||
|
|
||||||
|
//Get scheduled statuses
|
||||||
|
@GET("scheduled_statuses")
|
||||||
|
Call<List<ScheduledStatus>> getScheduledStatuses(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Query("max_id") String max_id,
|
||||||
|
@Query("since_id") String since_id,
|
||||||
|
@Query("min_id") String min_id,
|
||||||
|
@Query("limit") int limit
|
||||||
|
);
|
||||||
|
|
||||||
|
//Get scheduled status
|
||||||
|
@GET("scheduled_statuses/{id}")
|
||||||
|
Call<ScheduledStatus> getScheduledStatus(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Schedule a status
|
||||||
|
@FormUrlEncoded
|
||||||
|
@PUT("scheduled_statuses/{id}")
|
||||||
|
Call<ScheduledStatus> updateScheduleStatus(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id,
|
||||||
|
@Field("scheduled_at") Date scheduled_at
|
||||||
|
);
|
||||||
|
|
||||||
|
//Delete a scheduled status
|
||||||
|
@DELETE("scheduled_statuses/{id}")
|
||||||
|
Call<Void> deleteScheduledStatus(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,194 @@
|
||||||
|
package app.fedilab.android.client.mastodon;
|
||||||
|
/* 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 java.util.List;
|
||||||
|
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Account;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Conversation;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Marker;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.MastodonList;
|
||||||
|
import app.fedilab.android.client.mastodon.entities.Status;
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.http.DELETE;
|
||||||
|
import retrofit2.http.Field;
|
||||||
|
import retrofit2.http.FormUrlEncoded;
|
||||||
|
import retrofit2.http.GET;
|
||||||
|
import retrofit2.http.Header;
|
||||||
|
import retrofit2.http.POST;
|
||||||
|
import retrofit2.http.PUT;
|
||||||
|
import retrofit2.http.Path;
|
||||||
|
import retrofit2.http.Query;
|
||||||
|
|
||||||
|
public interface MastodonTimelinesService {
|
||||||
|
|
||||||
|
//Public timelines
|
||||||
|
@GET("timelines/public")
|
||||||
|
Call<List<Status>> getPublic(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Query("local") boolean local,
|
||||||
|
@Query("remote") boolean remote,
|
||||||
|
@Query("only_media") boolean only_media,
|
||||||
|
@Query("max_id") String max_id,
|
||||||
|
@Query("since_id") String since_id,
|
||||||
|
@Query("min_id") String min_id,
|
||||||
|
@Query("limit") int limit
|
||||||
|
);
|
||||||
|
|
||||||
|
//Public Tags timelines
|
||||||
|
@GET("timelines/tag/{hashtag}")
|
||||||
|
Call<List<Status>> getHashTag(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("hashtag") String hashtag,
|
||||||
|
@Query("local") boolean local,
|
||||||
|
@Query("only_media") boolean only_media,
|
||||||
|
@Query("all[]") List<String> all,
|
||||||
|
@Query("any[]") List<String> any,
|
||||||
|
@Query("none[]") List<String> none,
|
||||||
|
@Query("max_id") String max_id,
|
||||||
|
@Query("since_id") String since_id,
|
||||||
|
@Query("min_id") String min_id,
|
||||||
|
@Query("limit") int limit
|
||||||
|
);
|
||||||
|
|
||||||
|
//Home timeline
|
||||||
|
@GET("timelines/home")
|
||||||
|
Call<List<Status>> getHome(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Query("max_id") String max_id,
|
||||||
|
@Query("since_id") String since_id,
|
||||||
|
@Query("min_id") String min_id,
|
||||||
|
@Query("limit") int limit,
|
||||||
|
@Query("local") boolean local
|
||||||
|
);
|
||||||
|
|
||||||
|
//List timeline
|
||||||
|
@GET("timelines/list/{list_id}")
|
||||||
|
Call<List<Status>> getList(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("list_id") String list_id,
|
||||||
|
@Query("max_id") String max_id,
|
||||||
|
@Query("since_id") String since_id,
|
||||||
|
@Query("min_id") String min_id,
|
||||||
|
@Query("limit") int limit
|
||||||
|
);
|
||||||
|
|
||||||
|
//get conversations
|
||||||
|
@GET("conversations")
|
||||||
|
Call<List<Conversation>> getConversations(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Query("max_id") String max_id,
|
||||||
|
@Query("since_id") String since_id,
|
||||||
|
@Query("min_id") String min_id,
|
||||||
|
@Query("limit") int limit
|
||||||
|
);
|
||||||
|
|
||||||
|
//Delete a conversation
|
||||||
|
@DELETE("conversations/{id}")
|
||||||
|
Call<Void> deleteConversation(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Mark a conversation as read
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("conversations/{id}/read")
|
||||||
|
Call<Status> markReadConversation(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Show user list
|
||||||
|
@GET("lists")
|
||||||
|
Call<List<MastodonList>> getLists(
|
||||||
|
@Header("Authorization") String token
|
||||||
|
);
|
||||||
|
|
||||||
|
//Get Single list
|
||||||
|
@GET("lists/{id}")
|
||||||
|
Call<MastodonList> getList(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Create a user list
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("lists")
|
||||||
|
Call<MastodonList> createList(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Field("title") String title,
|
||||||
|
@Field("replies_policy") String replies_policy
|
||||||
|
);
|
||||||
|
|
||||||
|
//Update a list
|
||||||
|
@FormUrlEncoded
|
||||||
|
@PUT("lists/{id}")
|
||||||
|
Call<MastodonList> updateList(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id,
|
||||||
|
@Field("title") String title,
|
||||||
|
@Field("replies_policy") String replies_policy
|
||||||
|
);
|
||||||
|
|
||||||
|
//Delete a conversation
|
||||||
|
@DELETE("lists/{id}")
|
||||||
|
Call<Void> deleteList(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id
|
||||||
|
);
|
||||||
|
|
||||||
|
//Get accounts in a list
|
||||||
|
@GET("lists/{id}/accounts")
|
||||||
|
Call<List<Account>> getAccountsInList(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id,
|
||||||
|
@Query("max_id") String max_id,
|
||||||
|
@Query("since_id") String since_id,
|
||||||
|
@Query("limit") int limit
|
||||||
|
);
|
||||||
|
|
||||||
|
//Add account in a list
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("lists/{id}/accounts")
|
||||||
|
Call<Void> addAccountsList(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id,
|
||||||
|
@Field("account_ids[]") List<String> account_ids
|
||||||
|
);
|
||||||
|
|
||||||
|
//Delete accounts in a list
|
||||||
|
@DELETE("lists/{id}/accounts")
|
||||||
|
Call<Void> deleteAccountsList(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Path("id") String id,
|
||||||
|
@Query("account_ids[]") List<String> account_ids
|
||||||
|
);
|
||||||
|
|
||||||
|
//Get a marker
|
||||||
|
@GET("markers")
|
||||||
|
Call<Marker> getMarker(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Query("timeline") List<String> timeline
|
||||||
|
);
|
||||||
|
|
||||||
|
//Save marker
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("markers")
|
||||||
|
Call<Void> addMarker(
|
||||||
|
@Header("Authorization") String token,
|
||||||
|
@Field("home[last_read_id]") String home_last_read_id,
|
||||||
|
@Field("notifications[last_read_id]") String notifications_last_read_id
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,111 @@
|
||||||
|
package app.fedilab.android.client.mastodon;
|
||||||
|
/* 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 android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import okhttp3.MediaType;
|
||||||
|
import okhttp3.RequestBody;
|
||||||
|
import okio.BufferedSink;
|
||||||
|
|
||||||
|
public class ProgressRequestBody extends RequestBody {
|
||||||
|
|
||||||
|
private static final int SEGMENT_SIZE = 2048;
|
||||||
|
private final File mFile;
|
||||||
|
private final String mContentType;
|
||||||
|
private final ProgressListener mListener;
|
||||||
|
private final long mTotalToUpload;
|
||||||
|
private long mLastTotalUploaded;
|
||||||
|
|
||||||
|
public ProgressRequestBody(File file, String content_type, long lastTotalUploaded, long totalToUpload, ProgressListener listener) {
|
||||||
|
mFile = file;
|
||||||
|
mContentType = content_type;
|
||||||
|
mListener = listener;
|
||||||
|
mTotalToUpload = totalToUpload;
|
||||||
|
mLastTotalUploaded = lastTotalUploaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MediaType contentType() {
|
||||||
|
return MediaType.parse(mContentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long contentLength() {
|
||||||
|
return mFile.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeTo(@NonNull BufferedSink sink) throws IOException {
|
||||||
|
byte[] buffer = new byte[SEGMENT_SIZE];
|
||||||
|
FileInputStream in = new FileInputStream(mFile);
|
||||||
|
try {
|
||||||
|
int read;
|
||||||
|
Handler handler = new Handler(Looper.getMainLooper());
|
||||||
|
while ((read = in.read(buffer)) != -1) {
|
||||||
|
// update progress on UI thread
|
||||||
|
handler.post(new ProgressUpdater(mLastTotalUploaded, mTotalToUpload));
|
||||||
|
mLastTotalUploaded += read;
|
||||||
|
sink.write(buffer, 0, read);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
in.close();
|
||||||
|
}
|
||||||
|
/* Source source = null;
|
||||||
|
try {
|
||||||
|
source = Okio.source(mFile);
|
||||||
|
|
||||||
|
long read;
|
||||||
|
|
||||||
|
while ((read = source.read(bufferedSink.getBuffer(), SEGMENT_SIZE)) != -1) {
|
||||||
|
mLastTotalUploaded += read;
|
||||||
|
bufferedSink.flush();
|
||||||
|
mListener.onProgressUpdate((int)(100 * mLastTotalUploaded / mTotalToUpload));
|
||||||
|
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
Util.closeQuietly(source);
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ProgressListener {
|
||||||
|
void onProgressUpdate(int percentage);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ProgressUpdater implements Runnable {
|
||||||
|
private final long mUploaded;
|
||||||
|
private final long mTotal;
|
||||||
|
|
||||||
|
public ProgressUpdater(long uploaded, long total) {
|
||||||
|
mUploaded = uploaded;
|
||||||
|
mTotal = total;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
mListener.onProgressUpdate((int) (100 * mUploaded / mTotal));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
package app.fedilab.android.client.mastodon.entities;
|
||||||
|
/* 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 android.text.Spannable;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Account implements Serializable {
|
||||||
|
|
||||||
|
@SerializedName("id")
|
||||||
|
public String id;
|
||||||
|
@SerializedName("username")
|
||||||
|
public String username;
|
||||||
|
@SerializedName("acct")
|
||||||
|
public String acct;
|
||||||
|
@SerializedName("display_name")
|
||||||
|
public String display_name;
|
||||||
|
@SerializedName("locked")
|
||||||
|
public boolean locked;
|
||||||
|
@SerializedName("bot")
|
||||||
|
public boolean bot;
|
||||||
|
@SerializedName("created_at")
|
||||||
|
public Date created_at;
|
||||||
|
@SerializedName("note")
|
||||||
|
public String note;
|
||||||
|
@SerializedName("url")
|
||||||
|
public String url;
|
||||||
|
@SerializedName("avatar")
|
||||||
|
public String avatar;
|
||||||
|
@SerializedName("avatar_static")
|
||||||
|
public String avatar_static;
|
||||||
|
@SerializedName("header")
|
||||||
|
public String header;
|
||||||
|
@SerializedName("header_static")
|
||||||
|
public String header_static;
|
||||||
|
@SerializedName("followers_count")
|
||||||
|
public int followers_count;
|
||||||
|
@SerializedName("following_count")
|
||||||
|
public int following_count;
|
||||||
|
@SerializedName("statuses_count")
|
||||||
|
public int statuses_count;
|
||||||
|
@SerializedName("last_status_at")
|
||||||
|
public Date last_status_at;
|
||||||
|
@SerializedName("source")
|
||||||
|
public Source source;
|
||||||
|
@SerializedName("emojis")
|
||||||
|
public List<Emoji> emojis;
|
||||||
|
@SerializedName("fields")
|
||||||
|
public List<Field> fields;
|
||||||
|
@SerializedName("suspended")
|
||||||
|
public boolean suspended;
|
||||||
|
@SerializedName("discoverable")
|
||||||
|
public boolean discoverable;
|
||||||
|
@SerializedName("mute_expires_at")
|
||||||
|
public Date mute_expires_at;
|
||||||
|
@SerializedName("moved")
|
||||||
|
public Account moved;
|
||||||
|
|
||||||
|
|
||||||
|
//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 static class AccountParams implements Serializable {
|
||||||
|
@SerializedName("discoverable")
|
||||||
|
public boolean discoverable;
|
||||||
|
@SerializedName("bot")
|
||||||
|
public boolean bot;
|
||||||
|
@SerializedName("display_name")
|
||||||
|
public String display_name;
|
||||||
|
@SerializedName("note")
|
||||||
|
public String note;
|
||||||
|
@SerializedName("locked")
|
||||||
|
public boolean locked;
|
||||||
|
@SerializedName("source")
|
||||||
|
public Source.SourceParams source;
|
||||||
|
@SerializedName("fields_attributes")
|
||||||
|
public LinkedHashMap<Integer, Field.FieldParams> fields;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package app.fedilab.android.client.mastodon.entities;
|
||||||
|
/* 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 java.util.List;
|
||||||
|
|
||||||
|
public class Accounts {
|
||||||
|
public Pagination pagination = new Pagination();
|
||||||
|
public List<Account> accounts;
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package app.fedilab.android.client.mastodon.entities;
|
||||||
|
/* 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 com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class Activity {
|
||||||
|
@SerializedName("week")
|
||||||
|
public String week;
|
||||||
|
@SerializedName("statuses")
|
||||||
|
public String statuses;
|
||||||
|
@SerializedName("logins")
|
||||||
|
public String logins;
|
||||||
|
@SerializedName("registrations")
|
||||||
|
public String registrations;
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package app.fedilab.android.client.mastodon.entities;
|
||||||
|
/* 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 com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public class AdminAccount {
|
||||||
|
|
||||||
|
@SerializedName("id")
|
||||||
|
public String id;
|
||||||
|
@SerializedName("username")
|
||||||
|
public String username;
|
||||||
|
@SerializedName("domain")
|
||||||
|
public String domain;
|
||||||
|
@SerializedName("created_at")
|
||||||
|
public Date created_at;
|
||||||
|
@SerializedName("email")
|
||||||
|
public String email;
|
||||||
|
@SerializedName("ip")
|
||||||
|
public String ip;
|
||||||
|
@SerializedName("locale")
|
||||||
|
public String locale;
|
||||||
|
@SerializedName("invite_request")
|
||||||
|
public String invite_request;
|
||||||
|
@SerializedName("role")
|
||||||
|
public String role;
|
||||||
|
@SerializedName("confirmed")
|
||||||
|
public boolean confirmed;
|
||||||
|
@SerializedName("approved")
|
||||||
|
public boolean approved;
|
||||||
|
@SerializedName("disabled")
|
||||||
|
public boolean disabled;
|
||||||
|
@SerializedName("silenced")
|
||||||
|
public boolean silenced;
|
||||||
|
@SerializedName("suspended")
|
||||||
|
public boolean suspended;
|
||||||
|
@SerializedName("account")
|
||||||
|
public Account account;
|
||||||
|
@SerializedName("created_by_application_id")
|
||||||
|
public String created_by_application_id;
|
||||||
|
@SerializedName("invited_by_account_id")
|
||||||
|
public String invited_by_account_id;
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
package app.fedilab.android.client.mastodon.entities;
|
||||||
|
/* 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 com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class AdminReport {
|
||||||
|
|
||||||
|
@SerializedName("id")
|
||||||
|
public String id;
|
||||||
|
@SerializedName("action_taken")
|
||||||
|
public String action_taken;
|
||||||
|
@SerializedName("comment")
|
||||||
|
public String comment;
|
||||||
|
@SerializedName("created_at")
|
||||||
|
public Date created_at;
|
||||||
|
@SerializedName("updated_at")
|
||||||
|
public Date updated_at;
|
||||||
|
@SerializedName("account")
|
||||||
|
public Account account;
|
||||||
|
@SerializedName("target_account")
|
||||||
|
public Account target_account;
|
||||||
|
@SerializedName("assigned_account")
|
||||||
|
public Account assigned_account;
|
||||||
|
@SerializedName("action_taken_by_account")
|
||||||
|
public String action_taken_by_account;
|
||||||
|
@SerializedName("statuses")
|
||||||
|
public List<Status> statuses;
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package app.fedilab.android.client.mastodon.entities;
|
||||||
|
/* 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 com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Announcement {
|
||||||
|
@SerializedName("id")
|
||||||
|
public String id;
|
||||||
|
@SerializedName("content")
|
||||||
|
public String content;
|
||||||
|
@SerializedName("starts_at")
|
||||||
|
public Date starts_at;
|
||||||
|
@SerializedName("ends_at")
|
||||||
|
public Date ends_at;
|
||||||
|
@SerializedName("all_day")
|
||||||
|
public boolean all_day;
|
||||||
|
@SerializedName("published_at")
|
||||||
|
public Date published_at;
|
||||||
|
@SerializedName("updated_at")
|
||||||
|
public Date updated_at;
|
||||||
|
@SerializedName("read")
|
||||||
|
public boolean read;
|
||||||
|
@SerializedName("mentions")
|
||||||
|
public List<Mention> mentions;
|
||||||
|
@SerializedName("statuses")
|
||||||
|
public List<Status> statuses;
|
||||||
|
@SerializedName("tags")
|
||||||
|
public List<Tag> tags;
|
||||||
|
@SerializedName("emojis")
|
||||||
|
public List<Emoji> emojis;
|
||||||
|
@SerializedName("reactions")
|
||||||
|
public List<Reaction> reactions;
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package app.fedilab.android.client.mastodon.entities;
|
||||||
|
/* 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 com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public class App implements Serializable {
|
||||||
|
|
||||||
|
@SerializedName("id")
|
||||||
|
public String id;
|
||||||
|
@SerializedName("name")
|
||||||
|
public String name;
|
||||||
|
@SerializedName("website")
|
||||||
|
public String website;
|
||||||
|
@SerializedName("redirect_uri")
|
||||||
|
public String redirect_uri;
|
||||||
|
@SerializedName("client_id")
|
||||||
|
public String client_id;
|
||||||
|
@SerializedName("client_secret")
|
||||||
|
public String client_secret;
|
||||||
|
@SerializedName("vapid_key")
|
||||||
|
public String vapid_key;
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package app.fedilab.android.client.mastodon.entities;
|
||||||
|
/* 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 com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public class Attachment implements Serializable {
|
||||||
|
@SerializedName("id")
|
||||||
|
public String id;
|
||||||
|
@SerializedName("type")
|
||||||
|
public String type;
|
||||||
|
@SerializedName("url")
|
||||||
|
public String url;
|
||||||
|
@SerializedName("preview_url")
|
||||||
|
public String preview_url;
|
||||||
|
@SerializedName("remote_url")
|
||||||
|
public String remote_url;
|
||||||
|
@SerializedName("text_url")
|
||||||
|
public String text_url;
|
||||||
|
@SerializedName("description")
|
||||||
|
public String description;
|
||||||
|
@SerializedName("blurhash")
|
||||||
|
public String blurhash;
|
||||||
|
@SerializedName("mimeType")
|
||||||
|
public String mimeType;
|
||||||
|
@SerializedName("filename")
|
||||||
|
public String filename;
|
||||||
|
@SerializedName("size")
|
||||||
|
public long size;
|
||||||
|
@SerializedName("local_path")
|
||||||
|
public String local_path;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package app.fedilab.android.client.mastodon.entities;
|
||||||
|
/* 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 com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public class Card implements Serializable {
|
||||||
|
@SerializedName("url")
|
||||||
|
public String url;
|
||||||
|
@SerializedName("title")
|
||||||
|
public String title;
|
||||||
|
@SerializedName("description")
|
||||||
|
public String description;
|
||||||
|
@SerializedName("type")
|
||||||
|
public String type;
|
||||||
|
@SerializedName("author_name")
|
||||||
|
public String author_name;
|
||||||
|
@SerializedName("author_url")
|
||||||
|
public String author_url;
|
||||||
|
@SerializedName("provider_name")
|
||||||
|
public String provider_name;
|
||||||
|
@SerializedName("provider_url")
|
||||||
|
public String provider_url;
|
||||||
|
@SerializedName("html")
|
||||||
|
public String html;
|
||||||
|
@SerializedName("width")
|
||||||
|
public int width;
|
||||||
|
@SerializedName("height")
|
||||||
|
public int height;
|
||||||
|
@SerializedName("image")
|
||||||
|
public String image;
|
||||||
|
@SerializedName("embed_url")
|
||||||
|
public String embed_url;
|
||||||
|
@SerializedName("blurhash")
|
||||||
|
public String blurhash;
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package app.fedilab.android.client.mastodon.entities;
|
||||||
|
/* 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 com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Context {
|
||||||
|
@SerializedName("ancestors")
|
||||||
|
public List<Status> ancestors;
|
||||||
|
@SerializedName("descendants")
|
||||||
|
public List<Status> descendants;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package app.fedilab.android.client.mastodon.entities;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/* 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>. */
|
||||||
|
public class Conversation {
|
||||||
|
@SerializedName("id")
|
||||||
|
public String id;
|
||||||
|
@SerializedName("unread")
|
||||||
|
public boolean unread;
|
||||||
|
@SerializedName("accounts")
|
||||||
|
public List<Account> accounts;
|
||||||
|
@SerializedName("last_status")
|
||||||
|
public Status last_status;
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package app.fedilab.android.client.mastodon.entities;
|
||||||
|
/* 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 java.util.List;
|
||||||
|
|
||||||
|
public class Conversations {
|
||||||
|
public Pagination pagination = new Pagination();
|
||||||
|
public List<Conversation> conversations;
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package app.fedilab.android.client.mastodon.entities;
|
||||||
|
/* 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 com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public class Emoji implements Serializable {
|
||||||
|
@SerializedName("shortcode")
|
||||||
|
public String shortcode;
|
||||||
|
@SerializedName("url")
|
||||||
|
public String url;
|
||||||
|
@SerializedName("static_url")
|
||||||
|
public String static_url;
|
||||||
|
@SerializedName("visible_in_picker")
|
||||||
|
public boolean visible_in_picker;
|
||||||
|
@SerializedName("category")
|
||||||
|
public String category;
|
||||||
|
}
|
|
@ -0,0 +1,217 @@
|
||||||
|
package app.fedilab.android.client.mastodon.entities;
|
||||||
|
/* 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 android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import app.fedilab.android.exception.DBException;
|
||||||
|
import app.fedilab.android.sqlite.Sqlite;
|
||||||
|
|
||||||
|
|
||||||
|
public class EmojiInstance implements Serializable {
|
||||||
|
private final SQLiteDatabase db;
|
||||||
|
@SerializedName("instance")
|
||||||
|
public String instance;
|
||||||
|
@SerializedName("emojiList")
|
||||||
|
public List<Emoji> emojiList;
|
||||||
|
private Context context;
|
||||||
|
|
||||||
|
public EmojiInstance() {
|
||||||
|
db = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EmojiInstance(Context context) {
|
||||||
|
//Creation of the DB with tables
|
||||||
|
this.context = context;
|
||||||
|
this.db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialized a list of Emoji class
|
||||||
|
*
|
||||||
|
* @param emojis List of {@link Emoji} to serialize
|
||||||
|
* @return String serialized emoji list
|
||||||
|
*/
|
||||||
|
public static String mastodonEmojiListToStringStorage(List<Emoji> emojis) {
|
||||||
|
Gson gson = new Gson();
|
||||||
|
try {
|
||||||
|
return gson.toJson(emojis);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unserialized a Emoji List
|
||||||
|
*
|
||||||
|
* @param serializedEmojiList String serialized account
|
||||||
|
* @return List of {@link Emoji}
|
||||||
|
*/
|
||||||
|
public static List<Emoji> restoreEmojiListFromString(String serializedEmojiList) {
|
||||||
|
Gson gson = new Gson();
|
||||||
|
try {
|
||||||
|
return gson.fromJson(serializedEmojiList, new TypeToken<List<Emoji>>() {
|
||||||
|
}.getType());
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert or update emoji
|
||||||
|
*
|
||||||
|
* @param emojiInstance {@link EmojiInstance}
|
||||||
|
* @return long - db id
|
||||||
|
* @throws DBException exception with database
|
||||||
|
*/
|
||||||
|
public long insertOrUpdate(EmojiInstance emojiInstance) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
if (emojiInstance == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
boolean exists = emojiInstanceExist(emojiInstance);
|
||||||
|
long idReturned;
|
||||||
|
if (exists) {
|
||||||
|
idReturned = updateEmojiInstance(emojiInstance);
|
||||||
|
} else {
|
||||||
|
idReturned = insertEmojiInstance(emojiInstance);
|
||||||
|
}
|
||||||
|
return idReturned;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if emojis exists in db
|
||||||
|
*
|
||||||
|
* @param emojiInstance EmojiInstance {@link EmojiInstance}
|
||||||
|
* @return boolean - emojiInstance exists
|
||||||
|
* @throws DBException Exception
|
||||||
|
*/
|
||||||
|
public boolean emojiInstanceExist(EmojiInstance emojiInstance) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
Cursor mCount = db.rawQuery("select count(*) from " + Sqlite.TABLE_EMOJI_INSTANCE
|
||||||
|
+ " where " + Sqlite.COL_INSTANCE + " = '" + emojiInstance.instance + "'", null);
|
||||||
|
mCount.moveToFirst();
|
||||||
|
int count = mCount.getInt(0);
|
||||||
|
mCount.close();
|
||||||
|
return (count > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert emojis in db
|
||||||
|
*
|
||||||
|
* @param emojiInstance {@link EmojiInstance}
|
||||||
|
* @return long - db id
|
||||||
|
* @throws DBException exception with database
|
||||||
|
*/
|
||||||
|
private long insertEmojiInstance(EmojiInstance emojiInstance) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(Sqlite.COL_INSTANCE, emojiInstance.instance);
|
||||||
|
values.put(Sqlite.COL_EMOJI_LIST, mastodonEmojiListToStringStorage(emojiInstance.emojiList));
|
||||||
|
//Inserts token
|
||||||
|
try {
|
||||||
|
return db.insertOrThrow(Sqlite.TABLE_EMOJI_INSTANCE, null, values);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* update emojis in db
|
||||||
|
*
|
||||||
|
* @param emojiInstance {@link EmojiInstance}
|
||||||
|
* @return long - db id
|
||||||
|
* @throws DBException exception with database
|
||||||
|
*/
|
||||||
|
private long updateEmojiInstance(EmojiInstance emojiInstance) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(Sqlite.COL_EMOJI_LIST, mastodonEmojiListToStringStorage(emojiInstance.emojiList));
|
||||||
|
//Inserts token
|
||||||
|
try {
|
||||||
|
return db.update(Sqlite.TABLE_EMOJI_INSTANCE,
|
||||||
|
values, Sqlite.COL_INSTANCE + " = ?",
|
||||||
|
new String[]{emojiInstance.instance});
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the emojis for an instance
|
||||||
|
*
|
||||||
|
* @param instance String
|
||||||
|
* @return List<Emoji> - List of {@link Emoji}
|
||||||
|
*/
|
||||||
|
public List<Emoji> getEmojiList(String instance) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Cursor c = db.query(Sqlite.TABLE_EMOJI_INSTANCE, null, Sqlite.COL_INSTANCE + " = '" + instance + "'", null, null, null, null, "1");
|
||||||
|
return cursorToEmojiList(c);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore emoji list from db
|
||||||
|
*
|
||||||
|
* @param c Cursor
|
||||||
|
* @return List<Emoji>
|
||||||
|
*/
|
||||||
|
private List<Emoji> cursorToEmojiList(Cursor c) {
|
||||||
|
//No element found
|
||||||
|
if (c.getCount() == 0) {
|
||||||
|
c.close();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
//Take the first element
|
||||||
|
c.moveToFirst();
|
||||||
|
List<Emoji> emojiList = restoreEmojiListFromString(c.getString(c.getColumnIndexOrThrow(Sqlite.COL_EMOJI_LIST)));
|
||||||
|
c.close();
|
||||||
|
List<Emoji> filteredEmojis = new ArrayList<>();
|
||||||
|
if (emojiList != null) {
|
||||||
|
for (Emoji emoji : emojiList) {
|
||||||
|
if (emoji.visible_in_picker) {
|
||||||
|
filteredEmojis.add(emoji);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filteredEmojis;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package app.fedilab.android.client.mastodon.entities;
|
||||||
|
/* 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 com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class Error {
|
||||||
|
@SerializedName("code")
|
||||||
|
public int code;
|
||||||
|
@SerializedName("error")
|
||||||
|
public String error;
|
||||||
|
@SerializedName("error_description")
|
||||||
|
public String error_description;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package app.fedilab.android.client.mastodon.entities;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/* 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>. */
|
||||||
|
public class FeaturedTag {
|
||||||
|
@SerializedName("id")
|
||||||
|
public String id;
|
||||||
|
@SerializedName("name")
|
||||||
|
public String name;
|
||||||
|
@SerializedName("url")
|
||||||
|
public String url;
|
||||||
|
@SerializedName("statuses_count")
|
||||||
|
public int statuses_count;
|
||||||
|
@SerializedName("last_status_at")
|
||||||
|
public Date last_status_at;
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
package app.fedilab.android.client.mastodon.entities;
|
||||||
|
/* 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 android.text.Spannable;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public class Field implements Serializable {
|
||||||
|
@SerializedName("name")
|
||||||
|
public String name;
|
||||||
|
@SerializedName("value")
|
||||||
|
public String value;
|
||||||
|
@SerializedName("verified_at")
|
||||||
|
public Date verified_at;
|
||||||
|
|
||||||
|
//Some extra spannable element - They will be filled automatically when fetching the account
|
||||||
|
public transient Spannable value_span;
|
||||||
|
|
||||||
|
public static class FieldParams implements Serializable {
|
||||||
|
@SerializedName("name")
|
||||||
|
public String name;
|
||||||
|
@SerializedName("value")
|
||||||
|
public String value;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package app.fedilab.android.client.mastodon.entities;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/* 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>. */
|
||||||
|
|
||||||
|
public class Filter {
|
||||||
|
@SerializedName("id")
|
||||||
|
public String id;
|
||||||
|
@SerializedName("phrase")
|
||||||
|
public String phrase;
|
||||||
|
@SerializedName("context")
|
||||||
|
public List<String> context;
|
||||||
|
@SerializedName("whole_word")
|
||||||
|
public boolean whole_word;
|
||||||
|
@SerializedName("expires_at")
|
||||||
|
public Date expires_at;
|
||||||
|
@SerializedName("expires_at_sent")
|
||||||
|
public long expires_at_sent;
|
||||||
|
@SerializedName("irreversible")
|
||||||
|
public boolean irreversible;
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package app.fedilab.android.client.mastodon.entities;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
/* 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>. */
|
||||||
|
public class History {
|
||||||
|
@SerializedName("day")
|
||||||
|
public String day;
|
||||||
|
@SerializedName("uses")
|
||||||
|
public String uses;
|
||||||
|
@SerializedName("accounts")
|
||||||
|
public String accounts;
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package app.fedilab.android.client.mastodon.entities;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/* 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>. */
|
||||||
|
public class IdentityProof {
|
||||||
|
@SerializedName("provider")
|
||||||
|
public String provider;
|
||||||
|
@SerializedName("provider_username")
|
||||||
|
public String provider_username;
|
||||||
|
@SerializedName("updated_at")
|
||||||
|
public Date updated_at;
|
||||||
|
@SerializedName("proof_url")
|
||||||
|
public String proof_url;
|
||||||
|
@SerializedName("profile_url")
|
||||||
|
public String profile_url;
|
||||||
|
}
|
|
@ -0,0 +1,178 @@
|
||||||
|
package app.fedilab.android.client.mastodon.entities;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/* 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>. */
|
||||||
|
|
||||||
|
public class Instance implements Serializable {
|
||||||
|
|
||||||
|
@SerializedName("uri")
|
||||||
|
public String uri;
|
||||||
|
@SerializedName("title")
|
||||||
|
public String title;
|
||||||
|
@SerializedName("short_description")
|
||||||
|
public String short_description;
|
||||||
|
@SerializedName("description")
|
||||||
|
public String description;
|
||||||
|
@SerializedName("email")
|
||||||
|
public String email;
|
||||||
|
@SerializedName("version")
|
||||||
|
public String version;
|
||||||
|
@SerializedName("languages")
|
||||||
|
public List<String> languages;
|
||||||
|
@SerializedName("registrations")
|
||||||
|
public boolean registrations;
|
||||||
|
@SerializedName("rules")
|
||||||
|
public List<Rule> rules;
|
||||||
|
@SerializedName("approval_required")
|
||||||
|
public boolean approval_required;
|
||||||
|
@SerializedName("invites_enabled")
|
||||||
|
public boolean invites_enabled;
|
||||||
|
@SerializedName("stats")
|
||||||
|
public Stats stats;
|
||||||
|
@SerializedName("urls")
|
||||||
|
public Urls urls;
|
||||||
|
@SerializedName("thumbnail")
|
||||||
|
public String thumbnail;
|
||||||
|
@SerializedName("contact_account")
|
||||||
|
public Account contact_account;
|
||||||
|
@SerializedName("configuration")
|
||||||
|
public Configuration configuration;
|
||||||
|
|
||||||
|
|
||||||
|
public List<String> getMimeTypeAudio() {
|
||||||
|
List<String> mimeTypes = new ArrayList<>();
|
||||||
|
if (configuration == null || configuration.media_attachments == null) {
|
||||||
|
return mimeTypes;
|
||||||
|
}
|
||||||
|
for (String mimeType : configuration.media_attachments.supported_mime_types) {
|
||||||
|
if (mimeType.startsWith("audio")) {
|
||||||
|
mimeTypes.add(mimeType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mimeTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getMimeTypeVideo() {
|
||||||
|
List<String> mimeTypes = new ArrayList<>();
|
||||||
|
if (configuration == null || configuration.media_attachments == null) {
|
||||||
|
return mimeTypes;
|
||||||
|
}
|
||||||
|
for (String mimeType : configuration.media_attachments.supported_mime_types) {
|
||||||
|
if (mimeType.startsWith("video")) {
|
||||||
|
mimeTypes.add(mimeType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mimeTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public List<String> getMimeTypeImage() {
|
||||||
|
List<String> mimeTypes = new ArrayList<>();
|
||||||
|
if (configuration == null || configuration.media_attachments == null) {
|
||||||
|
return mimeTypes;
|
||||||
|
}
|
||||||
|
for (String mimeType : configuration.media_attachments.supported_mime_types) {
|
||||||
|
if (mimeType.startsWith("image")) {
|
||||||
|
mimeTypes.add(mimeType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mimeTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getMimeTypeOther() {
|
||||||
|
List<String> mimeTypes = new ArrayList<>();
|
||||||
|
if (configuration == null || configuration.media_attachments == null) {
|
||||||
|
return mimeTypes;
|
||||||
|
}
|
||||||
|
for (String mimeType : configuration.media_attachments.supported_mime_types) {
|
||||||
|
if (!mimeType.startsWith("image") && !mimeType.startsWith("video") && !mimeType.startsWith("audio")) {
|
||||||
|
mimeTypes.add(mimeType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mimeTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Configuration implements Serializable {
|
||||||
|
@SerializedName("statuses")
|
||||||
|
public StatusesConf statusesConf;
|
||||||
|
@SerializedName("polls")
|
||||||
|
public PollsConf pollsConf;
|
||||||
|
@SerializedName("media_attachments")
|
||||||
|
public MediaConf media_attachments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class StatusesConf implements Serializable {
|
||||||
|
@SerializedName("max_characters")
|
||||||
|
public int max_characters = 500;
|
||||||
|
@SerializedName("max_media_attachments")
|
||||||
|
public int max_media_attachments = 4;
|
||||||
|
@SerializedName("characters_reserved_per_url")
|
||||||
|
public int characters_reserved_per_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class MediaConf implements Serializable {
|
||||||
|
@SerializedName("supported_mime_types")
|
||||||
|
public List<String> supported_mime_types;
|
||||||
|
@SerializedName("image_size_limit")
|
||||||
|
public int image_size_limit;
|
||||||
|
@SerializedName("image_matrix_limit")
|
||||||
|
public int image_matrix_limit;
|
||||||
|
@SerializedName("video_size_limit")
|
||||||
|
public int video_size_limit;
|
||||||
|
@SerializedName("video_frame_rate_limit")
|
||||||
|
public int video_frame_rate_limit;
|
||||||
|
@SerializedName("video_matrix_limit")
|
||||||
|
public int video_matrix_limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PollsConf implements Serializable {
|
||||||
|
@SerializedName("min_expiration")
|
||||||
|
public int min_expiration;
|
||||||
|
@SerializedName("max_options")
|
||||||
|
public int max_options = 4;
|
||||||
|
@SerializedName("max_option_chars")
|
||||||
|
public int max_option_chars = 25;
|
||||||
|
@SerializedName("max_expiration")
|
||||||
|
public int max_expiration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Stats implements Serializable {
|
||||||
|
@SerializedName("user_count")
|
||||||
|
public int user_count;
|
||||||
|
@SerializedName("status_count")
|
||||||
|
public int status_count;
|
||||||
|
@SerializedName("domain_count")
|
||||||
|
public int domain_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Urls implements Serializable {
|
||||||
|
@SerializedName("streaming_api")
|
||||||
|
public String streaming_api;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Rule implements Serializable {
|
||||||
|
@SerializedName("id")
|
||||||
|
public String id;
|
||||||
|
@SerializedName("text")
|
||||||
|
public String text;
|
||||||
|
public transient boolean isChecked = false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,204 @@
|
||||||
|
package app.fedilab.android.client.mastodon.entities;
|
||||||
|
/* 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 android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import app.fedilab.android.exception.DBException;
|
||||||
|
import app.fedilab.android.sqlite.Sqlite;
|
||||||
|
|
||||||
|
|
||||||
|
public class InstanceInfo implements Serializable {
|
||||||
|
|
||||||
|
private final SQLiteDatabase db;
|
||||||
|
@SerializedName("instance")
|
||||||
|
public String instance;
|
||||||
|
@SerializedName("info")
|
||||||
|
public Instance info;
|
||||||
|
|
||||||
|
public InstanceInfo() {
|
||||||
|
db = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InstanceInfo(Context context) {
|
||||||
|
//Creation of the DB with tables
|
||||||
|
this.db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialized a list of Emoji class
|
||||||
|
*
|
||||||
|
* @param instance {@link Instance} to serialize
|
||||||
|
* @return String serialized instance
|
||||||
|
*/
|
||||||
|
public static String instanceInfoToStringStorage(Instance instance) {
|
||||||
|
Gson gson = new Gson();
|
||||||
|
try {
|
||||||
|
return gson.toJson(instance);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unserialized an instance
|
||||||
|
*
|
||||||
|
* @param serializedInstanceInfo String serialized instance
|
||||||
|
* @return {@link Instance}
|
||||||
|
*/
|
||||||
|
public static Instance restoreInstanceInfoFromString(String serializedInstanceInfo) {
|
||||||
|
Gson gson = new Gson();
|
||||||
|
try {
|
||||||
|
return gson.fromJson(serializedInstanceInfo, Instance.class);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert or update instance
|
||||||
|
*
|
||||||
|
* @param instanceInfo {@link InstanceInfo}
|
||||||
|
* @return long - db id
|
||||||
|
* @throws DBException exception with database
|
||||||
|
*/
|
||||||
|
public long insertOrUpdate(InstanceInfo instanceInfo) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
if (instanceInfo == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
boolean exists = instanceInfoExist(instanceInfo);
|
||||||
|
long idReturned;
|
||||||
|
if (exists) {
|
||||||
|
idReturned = updateInstanceInfo(instanceInfo);
|
||||||
|
} else {
|
||||||
|
idReturned = insertInstanceInfo(instanceInfo);
|
||||||
|
}
|
||||||
|
return idReturned;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if instanceInfo exists in db
|
||||||
|
*
|
||||||
|
* @param instanceInfo InstanceInfo {@link InstanceInfo}
|
||||||
|
* @return boolean - instanceInfo exists
|
||||||
|
* @throws DBException Exception
|
||||||
|
*/
|
||||||
|
public boolean instanceInfoExist(InstanceInfo instanceInfo) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
Cursor mCount = db.rawQuery("select count(*) from " + Sqlite.TABLE_INSTANCE_INFO
|
||||||
|
+ " where " + Sqlite.COL_INSTANCE + " = '" + instanceInfo.instance + "'", null);
|
||||||
|
mCount.moveToFirst();
|
||||||
|
int count = mCount.getInt(0);
|
||||||
|
mCount.close();
|
||||||
|
return (count > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert instanceInfo in db
|
||||||
|
*
|
||||||
|
* @param instanceInfo {@link InstanceInfo}
|
||||||
|
* @return long - db id
|
||||||
|
* @throws DBException exception with database
|
||||||
|
*/
|
||||||
|
private long insertInstanceInfo(InstanceInfo instanceInfo) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(Sqlite.COL_INSTANCE, instanceInfo.instance);
|
||||||
|
values.put(Sqlite.COL_INFO, instanceInfoToStringStorage(instanceInfo.info));
|
||||||
|
//Inserts instance
|
||||||
|
try {
|
||||||
|
return db.insertOrThrow(Sqlite.TABLE_INSTANCE_INFO, null, values);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* update instanceInfo in db
|
||||||
|
*
|
||||||
|
* @param instanceInfo {@link InstanceInfo}
|
||||||
|
* @return long - db id
|
||||||
|
* @throws DBException exception with database
|
||||||
|
*/
|
||||||
|
private long updateInstanceInfo(InstanceInfo instanceInfo) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(Sqlite.COL_INFO, instanceInfoToStringStorage(instanceInfo.info));
|
||||||
|
//Inserts token
|
||||||
|
try {
|
||||||
|
return db.update(Sqlite.TABLE_INSTANCE_INFO,
|
||||||
|
values, Sqlite.COL_INSTANCE + " = ?",
|
||||||
|
new String[]{instanceInfo.instance});
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the info for an instance
|
||||||
|
*
|
||||||
|
* @param instance String
|
||||||
|
* @return InstanceInfo - {@link InstanceInfo}
|
||||||
|
*/
|
||||||
|
public Instance getInstanceInfo(String instance) throws DBException {
|
||||||
|
if (db == null) {
|
||||||
|
throw new DBException("db is null. Wrong initialization.");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Cursor c = db.query(Sqlite.TABLE_INSTANCE_INFO, null, Sqlite.COL_INSTANCE + " = '" + instance + "'", null, null, null, null, "1");
|
||||||
|
return cursorToInstanceInfo(c);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore instanceInfo from db
|
||||||
|
*
|
||||||
|
* @param c Cursor
|
||||||
|
* @return Instance
|
||||||
|
*/
|
||||||
|
private Instance cursorToInstanceInfo(Cursor c) {
|
||||||
|
//No element found
|
||||||
|
if (c.getCount() == 0) {
|
||||||
|
c.close();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
//Take the first element
|
||||||
|
c.moveToFirst();
|
||||||
|
Instance instance = restoreInstanceInfoFromString(c.getString(c.getColumnIndexOrThrow(Sqlite.COL_INFO)));
|
||||||
|
c.close();
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
package app.fedilab.android.client.mastodon.entities;
|
||||||
|
/* 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 com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class JoinMastodonInstance {
|
||||||
|
@SerializedName("domain")
|
||||||
|
public String domain;
|
||||||
|
@SerializedName("version")
|
||||||
|
public String version;
|
||||||
|
@SerializedName("description")
|
||||||
|
public String description;
|
||||||
|
@SerializedName("languages")
|
||||||
|
public List<String> languages;
|
||||||
|
@SerializedName("categories")
|
||||||
|
public List<String> categories;
|
||||||
|
@SerializedName("proxied_thumbnail")
|
||||||
|
public String proxied_thumbnail;
|
||||||
|
@SerializedName("total_users")
|
||||||
|
public int total_users;
|
||||||
|
@SerializedName("last_week_users")
|
||||||
|
public int last_week_users;
|
||||||
|
@SerializedName("approval_required")
|
||||||
|
public boolean approval_required;
|
||||||
|
@SerializedName("language")
|
||||||
|
public String language;
|
||||||
|
@SerializedName("general")
|
||||||
|
public String general;
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package app.fedilab.android.client.mastodon.entities;
|
||||||
|
/* 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 com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public class Marker {
|
||||||
|
|
||||||
|
@SerializedName("home")
|
||||||
|
public MarkerContent home;
|
||||||
|
@SerializedName("notifications")
|
||||||
|
public MarkerContent notifications;
|
||||||
|
|
||||||
|
|
||||||
|
public static class MarkerContent {
|
||||||
|
@SerializedName("last_read_id")
|
||||||
|
public String last_read_id;
|
||||||
|
@SerializedName("version")
|
||||||
|
public int version;
|
||||||
|
@SerializedName("updated_at")
|
||||||
|
public Date updated_at;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package app.fedilab.android.client.mastodon.entities;
|
||||||
|
/* 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 com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public class MastodonList implements Serializable {
|
||||||
|
|
||||||
|
@SerializedName("id")
|
||||||
|
public String id;
|
||||||
|
@SerializedName("title")
|
||||||
|
public String title;
|
||||||
|
@SerializedName("replies_policy")
|
||||||
|
public String replies_policy;
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue