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 |