refactor: Start creating core modules (#286)

The existing code base is a single monolithic module. This is relatively
simple to configure, but many of the tasks to compile the module and
produce the final app have to run in series.

This is unnecessarily slow.

This change starts to split the code in to multiple modules, which are:

- :core:account - AccountManager, to break a dependency cycle
- :core:common - low level types or utilities used in many other modules
- :core:database - database types, DAOs, and DI infrastructure
- :core:network - network types, API definitions, and DI infrastructure
- :core:preferences - shared preferences definitions and DI
infrastructure
- :core:testing - fakes and rules used across different modules

Benchmarking with gradle-profiler shows a ~ 17% reduction in incremental
build times after an ABI change. That will improve further as more code
is moved to modules.

The rough mechanics of the changes are:

- Create the modules, and move existing files in to them. This causes a
  lot of churn in import arguments.

- Convert build.gradle files to build.gradle.kts

- Separate out the data required to display a tab (`TabViewData`) from
  the data required to configure a tab (`TabData`) to avoid circular
  dependencies.

- Abstract the repeated build logic shared between the modules in to
  a set of plugins under `build-logic/`, to simplify configuration of
  the application and library builds.

- Be explicit that some nullable types are non-null at time of use.
  Nullable properties in types imported from modules generally can't be
  smart cast to non-null. There's a detailed discussion of why this
restriction exists at
https://discuss.kotlinlang.org/t/what-is-the-reason-behind-smart-cast-being-impossible-to-perform-when-referenced-class-is-in-another-module/2201.

The changes highlight design problems with the current code, including:

- The main application code is too tightly coupled to the network types
- Too many values are declared unnecessarily nullable
- Dependency cycles between code that make modularisation difficult

Future changes will add more modules.

See #291.
This commit is contained in:
Nik Clayton 2023-12-04 16:58:36 +01:00 committed by GitHub
parent a7533b3f08
commit e749b362ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
360 changed files with 3410 additions and 1737 deletions

View File

@ -1,250 +0,0 @@
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.google.ksp)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.parcelize)
alias(libs.plugins.aboutlibraries)
alias(libs.plugins.hilt)
alias(libs.plugins.room)
id "app.pachli.plugins.markdown2resource"
}
apply from: 'gitTools.gradle'
final def gitSha = ext.getGitSha()
final def gitRevCount = ext.getGitRevCount()
// The app name
final def APP_NAME = "Pachli"
// The application id. Must be unique, e.g. based on your domain
final def APP_ID = "app.pachli"
// url of a custom app logo. Recommended size at least 600x600. Keep empty to use the default logo.
final def CUSTOM_LOGO_URL = ""
// e.g. mastodon.social. Keep empty to not suggest any instance on the signup screen
final def CUSTOM_INSTANCE = ""
// link to your support account. Will be linked on the about page when not empty.
final def SUPPORT_ACCOUNT_URL = "https://mastodon.social/@Pachli"
android {
compileSdk 34
namespace "app.pachli"
defaultConfig {
applicationId APP_ID
namespace "app.pachli"
minSdk 23
targetSdk 34
versionCode 8
versionName "2.0.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
resValue "string", "app_name", APP_NAME
buildConfigField("String", "CUSTOM_LOGO_URL", "\"$CUSTOM_LOGO_URL\"")
buildConfigField("String", "CUSTOM_INSTANCE", "\"$CUSTOM_INSTANCE\"")
buildConfigField("String", "SUPPORT_ACCOUNT_URL", "\"$SUPPORT_ACCOUNT_URL\"")
}
buildTypes {
debug {
getIsDefault().set(true)
}
release {
minifyEnabled true
shrinkResources true
proguardFiles 'proguard-rules.pro'
}
}
flavorDimensions += "color"
flavorDimensions += "store"
productFlavors {
blue {
dimension "color"
}
orange {
dimension "color"
resValue "string", "app_name", APP_NAME + " Current"
applicationIdSuffix ".current"
versionNameSuffix "+" + gitSha
}
fdroid {
dimension "store"
}
github {
dimension "store"
}
google {
dimension "store"
}
}
lint {
lintConfig file("lint.xml")
// Regenerate by running `./gradlew updateLintBaselineOrangeDebug`
baseline = file("lint-baseline.xml")
}
buildFeatures {
buildConfig true
resValues true
viewBinding true
}
testOptions {
unitTests {
returnDefaultValues = true
includeAndroidResources = true
}
unitTests.all {
systemProperty 'robolectric.logging.enabled', 'true'
systemProperty 'robolectric.lazyload', 'ON'
}
}
sourceSets {
androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
}
// Exclude unneeded files added by libraries
packagingOptions.resources.excludes += [
'LICENSE_OFL',
'LICENSE_UNICODE',
]
bundle {
language {
// bundle all languages in every apk so the dynamic language switching works
enableSplit = false
}
}
dependenciesInfo {
includeInApk false
includeInBundle false
}
applicationVariants.configureEach { variant ->
tasks.register("printVersionInfo${variant.name.capitalize()}") {
notCompatibleWithConfigurationCache("Should always print the version info")
println variant.versionCode + " " + variant.versionName
}
variant.outputs.configureEach {
// Set the "orange" release versionCode to the number of commits on the
// branch, to ensure the versionCode updates on every release.
if (variant.buildType.name == "release" && variant.flavorName.startsWith("orange")) {
versionCodeOverride = gitRevCount
}
outputFileName = "Pachli_${variant.versionName}_${variant.versionCode}_${gitSha}_" +
"${variant.flavorName}_${buildType.name}.apk"
}
}
}
room {
schemaDirectory("$projectDir/schemas/")
}
ksp {
arg("room.incremental", "true")
arg("room.generateKotlin", "true")
}
configurations {
// JNI-only libraries don't play nicely with Robolectric
// see https://github.com/tuskyapp/Tusky/pull/3367
testImplementation.exclude group: "org.conscrypt", module: "conscrypt-android"
testRuntime.exclude group: "org.conscrypt", module: "conscrypt-android"
}
aboutLibraries {
configPath = "licenses"
includePlatform = false
duplicationMode = com.mikepenz.aboutlibraries.plugin.DuplicateMode.MERGE
prettyPrint = true
}
markdown2resource {
files = [ layout.projectDirectory.file('../PRIVACY.md') ]
}
// library versions are in PROJECT_ROOT/gradle/libs.versions.toml
dependencies {
implementation libs.kotlinx.coroutines.android
implementation libs.kotlinx.coroutines.rx3
implementation libs.bundles.androidx
implementation libs.bundles.room
ksp libs.androidx.room.compiler
implementation libs.android.material
implementation libs.gson
implementation libs.bundles.retrofit
implementation libs.networkresult.calladapter
implementation libs.bundles.okhttp
implementation libs.conscrypt.android
implementation libs.bundles.glide
ksp libs.glide.compiler
implementation libs.bundles.rxjava3
implementation libs.bundles.autodispose
implementation libs.hilt.android
ksp libs.hilt.compiler
implementation libs.sparkbutton
implementation libs.touchimageview
implementation libs.bundles.material.drawer
implementation libs.material.typeface
implementation libs.image.cropper
implementation libs.bundles.filemojicompat
implementation libs.bouncycastle
implementation libs.unified.push
implementation libs.bundles.xmldiff
implementation libs.bundles.aboutlibraries
implementation libs.timber
googleImplementation libs.app.update
googleImplementation libs.app.update.ktx
implementation libs.kotlin.result
implementation libs.semver
testImplementation libs.androidx.test.junit
testImplementation libs.robolectric
testImplementation libs.bundles.mockito
testImplementation libs.mockwebserver
testImplementation libs.androidx.core.testing
testImplementation libs.kotlinx.coroutines.test
testImplementation libs.androidx.work.testing
testImplementation libs.truth
testImplementation libs.turbine
testImplementation libs.androidx.test.core.ktx
testImplementation libs.hilt.android.testing
kspTest libs.hilt.compiler
androidTestImplementation libs.espresso.core
androidTestImplementation libs.androidx.room.testing
androidTestImplementation libs.androidx.test.junit
androidTestImplementation libs.hilt.android.testing
androidTestImplementation libs.androidx.test.core.ktx
lintChecks project(":checks")
}

206
app/build.gradle.kts Normal file
View File

@ -0,0 +1,206 @@
/*
* Copyright 2023 Pachli Association
*
* This file is a part of Pachli.
*
* 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.
*
* Pachli 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 Pachli; if not,
* see <http://www.gnu.org/licenses>.
*/
import com.android.build.gradle.internal.api.ApkVariantOutputImpl
plugins {
alias(libs.plugins.pachli.android.application)
alias(libs.plugins.pachli.android.hilt)
alias(libs.plugins.kotlin.parcelize)
alias(libs.plugins.aboutlibraries)
id("app.pachli.plugins.markdown2resource")
}
apply(from = "gitTools.gradle")
val getGitSha: groovy.lang.Closure<String> by extra
val getGitRevCount: groovy.lang.Closure<Int> by extra
android {
namespace = "app.pachli"
defaultConfig {
applicationId = "app.pachli"
versionCode = 8
versionName = "2.0.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
testInstrumentationRunnerArguments["disableAnalytics"] = "true"
vectorDrawables.useSupportLibrary = true
}
buildTypes {
debug {
isDefault = true
}
release {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
packaging {
resources.excludes.apply {
add("LICENSE_OFL")
add("LICENSE_UNICODE")
}
}
bundle {
language {
// bundle all languages in every apk so the dynamic language switching works
enableSplit = false
}
}
dependenciesInfo {
includeInApk = false
includeInBundle = false
}
testOptions {
unitTests {
isReturnDefaultValues = true
isIncludeAndroidResources = true
}
unitTests.all {
it.systemProperty("robolectric.logging.enabled", "true")
it.systemProperty("robolectric.lazyload", "ON")
}
}
applicationVariants.configureEach {
tasks.register("printVersionInfo${name.replaceFirstChar { it.uppercaseChar() }}") {
notCompatibleWithConfigurationCache("Should always print the version info")
doLast {
println("$versionCode $versionName")
}
}
outputs.configureEach {
this as ApkVariantOutputImpl
// Set the "orange" release versionCode to the number of commits on the
// branch, to ensure the versionCode updates on every release. Include the
// SHA of the current commit to help with troubleshooting bug reports
if (buildType.name == "release" && flavorName.startsWith("orange")) {
versionCodeOverride = getGitRevCount()
versionNameOverride = "$versionName+${getGitSha()}"
}
outputFileName = "Pachli_${versionName}_${versionCode}_${getGitSha()}_${flavorName}_${buildType.name}.apk"
}
}
}
configurations {
// JNI-only libraries don't play nicely with Robolectric
// see https://github.com/tuskyapp/Tusky/pull/3367 and
// https://github.com/google/conscrypt/issues/649
testImplementation {
exclude(group = "org.conscrypt", module = "conscrypt-android")
}
}
aboutLibraries {
configPath = "licenses"
includePlatform = false
duplicationMode = com.mikepenz.aboutlibraries.plugin.DuplicateMode.MERGE
prettyPrint = true
}
markdown2resource {
files.add(layout.projectDirectory.file("../PRIVACY.md"))
}
dependencies {
// CachedTimelineRemoteMediator needs the @Transaction annotation from Room
compileOnly(libs.bundles.room)
testCompileOnly(libs.bundles.room)
implementation(projects.core.accounts)
implementation(projects.core.common)
implementation(projects.core.database)
implementation(projects.core.network)
implementation(projects.core.preferences)
implementation(libs.kotlinx.coroutines.android)
implementation(libs.kotlinx.coroutines.rx3)
implementation(libs.bundles.androidx)
implementation(libs.android.material)
implementation(libs.gson)
implementation(libs.bundles.retrofit)
implementation(libs.networkresult.calladapter)
implementation(libs.bundles.okhttp)
implementation(libs.conscrypt.android)
implementation(libs.bundles.glide)
ksp(libs.glide.compiler)
implementation(libs.bundles.rxjava3)
implementation(libs.bundles.autodispose)
implementation(libs.sparkbutton)
implementation(libs.touchimageview)
implementation(libs.bundles.material.drawer)
implementation(libs.material.typeface)
implementation(libs.image.cropper)
implementation(libs.bundles.filemojicompat)
implementation(libs.bouncycastle)
implementation(libs.unified.push)
implementation(libs.bundles.xmldiff)
implementation(libs.bundles.aboutlibraries)
implementation(libs.timber)
googleImplementation(libs.app.update)
googleImplementation(libs.app.update.ktx)
implementation(libs.kotlin.result)
implementation(libs.semver)
testImplementation(projects.core.testing)
testImplementation(libs.androidx.test.junit)
testImplementation(libs.robolectric)
testImplementation(libs.bundles.mockito)
testImplementation(libs.androidx.core.testing)
testImplementation(libs.kotlinx.coroutines.test)
testImplementation(libs.androidx.work.testing)
testImplementation(libs.truth)
testImplementation(libs.turbine)
testImplementation(libs.androidx.test.core.ktx)
androidTestImplementation(libs.espresso.core)
androidTestImplementation(libs.androidx.room.testing)
androidTestImplementation(libs.androidx.test.junit)
androidTestImplementation(libs.androidx.test.core.ktx)
lintChecks(projects.checks)
}

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<issues format="6" by="lint 8.1.2" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.2)" variant="all" version="8.1.2">
<issues format="6" by="lint 8.2.0" type="baseline" client="gradle" dependencies="false" name="AGP (8.2.0)" variant="all" version="8.2.0">
<issue
id="InvalidPackage"
@ -949,7 +949,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/app/pachli/adapter/TabAdapter.kt"
line="55"
line="54"
column="9"/>
</issue>
@ -960,7 +960,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/app/pachli/adapter/TabAdapter.kt"
line="158"
line="157"
column="13"/>
</issue>
@ -2075,72 +2075,6 @@
column="9"/>
</issue>
<issue
id="SyntheticAccessor"
message="Access to `private` method `isSameDate` of class `Companion` requires synthetic accessor"
errorLine1=" isSameDate(time, now, tz) -> sameDaySdf.format(time)"
errorLine2=" ~~~~~~~~~~">
<location
file="src/main/java/app/pachli/util/AbsoluteTimeFormatter.kt"
line="35"
column="13"/>
</issue>
<issue
id="SyntheticAccessor"
message="Access to `private` method `isSameDate` of class `Companion` requires synthetic accessor"
errorLine1=" isSameDate(time, now, tz) -> sameDaySdf.format(time)"
errorLine2=" ~~~~~~~~~~">
<location
file="src/main/java/app/pachli/util/AbsoluteTimeFormatter.kt"
line="35"
column="13"/>
</issue>
<issue
id="SyntheticAccessor"
message="Access to `private` method `isSameDate` of class `Companion` requires synthetic accessor"
errorLine1=" isSameDate(time, now, tz) -> sameDaySdf.format(time)"
errorLine2=" ~~~~~~~~~~">
<location
file="src/main/java/app/pachli/util/AbsoluteTimeFormatter.kt"
line="35"
column="13"/>
</issue>
<issue
id="SyntheticAccessor"
message="Access to `private` method `isSameYear` of class `Companion` requires synthetic accessor"
errorLine1=" isSameYear(time, now, tz) -> sameYearSdf.format(time)"
errorLine2=" ~~~~~~~~~~">
<location
file="src/main/java/app/pachli/util/AbsoluteTimeFormatter.kt"
line="36"
column="13"/>
</issue>
<issue
id="SyntheticAccessor"
message="Access to `private` method `isSameYear` of class `Companion` requires synthetic accessor"
errorLine1=" isSameYear(time, now, tz) -> sameYearSdf.format(time)"
errorLine2=" ~~~~~~~~~~">
<location
file="src/main/java/app/pachli/util/AbsoluteTimeFormatter.kt"
line="36"
column="13"/>
</issue>
<issue
id="SyntheticAccessor"
message="Access to `private` method `isSameYear` of class `Companion` requires synthetic accessor"
errorLine1=" isSameYear(time, now, tz) -> sameYearSdf.format(time)"
errorLine2=" ~~~~~~~~~~">
<location
file="src/main/java/app/pachli/util/AbsoluteTimeFormatter.kt"
line="36"
column="13"/>
</issue>
<issue
id="SyntheticAccessor"
message="Access to `private` method `getBinding` of class `AccountActivity` requires synthetic accessor"
@ -2581,28 +2515,6 @@
column="21"/>
</issue>
<issue
id="SyntheticAccessor"
message="Access to `private` method `swapHost` of class `Companion` requires synthetic accessor"
errorLine1=" builder.url(swapHost(originalRequest.url, instanceHeader))"
errorLine2=" ~~~~~~~~">
<location
file="src/main/java/app/pachli/network/InstanceSwitchAuthInterceptor.kt"
line="43"
column="29"/>
</issue>
<issue
id="SyntheticAccessor"
message="Access to `private` method `swapHost` of class `Companion` requires synthetic accessor"
errorLine1=" builder.url(swapHost(originalRequest.url, currentAccount.domain))"
errorLine2=" ~~~~~~~~">
<location
file="src/main/java/app/pachli/network/InstanceSwitchAuthInterceptor.kt"
line="52"
column="37"/>
</issue>
<issue
id="SyntheticAccessor"
message="Access to `private` method `getContext` of class `ListStatusAccessibilityDelegate` requires synthetic accessor"
@ -2819,7 +2731,7 @@
errorLine2=" ~~~~~~~~~~~~~~">
<location
file="src/main/java/app/pachli/ListsActivity.kt"
line="269"
line="265"
column="21"/>
</issue>
@ -2830,7 +2742,7 @@
errorLine2=" ~~~~~~">
<location
file="src/main/java/app/pachli/ListsActivity.kt"
line="271"
line="267"
column="21"/>
</issue>
@ -2962,7 +2874,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="244"
line="245"
column="37"/>
</issue>
@ -2973,7 +2885,7 @@
errorLine2=" ~~~~~~~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="248"
line="249"
column="37"/>
</issue>
@ -2984,7 +2896,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="352"
line="353"
column="25"/>
</issue>
@ -2995,7 +2907,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="353"
line="354"
column="29"/>
</issue>
@ -3006,7 +2918,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="355"
line="356"
column="25"/>
</issue>
@ -3017,7 +2929,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="356"
line="357"
column="29"/>
</issue>
@ -3028,7 +2940,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="638"
line="639"
column="17"/>
</issue>
@ -3039,7 +2951,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="641"
line="642"
column="21"/>
</issue>
@ -3050,7 +2962,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="646"
line="647"
column="17"/>
</issue>
@ -3061,7 +2973,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="650"
line="651"
column="21"/>
</issue>
@ -3072,7 +2984,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="655"
line="656"
column="17"/>
</issue>
@ -3083,7 +2995,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="658"
line="659"
column="21"/>
</issue>
@ -3094,7 +3006,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="663"
line="664"
column="17"/>
</issue>
@ -3105,7 +3017,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="666"
line="667"
column="21"/>
</issue>
@ -3116,7 +3028,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="671"
line="672"
column="17"/>
</issue>
@ -3127,7 +3039,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="674"
line="675"
column="21"/>
</issue>
@ -3138,7 +3050,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="678"
line="679"
column="17"/>
</issue>
@ -3149,7 +3061,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="681"
line="682"
column="21"/>
</issue>
@ -3160,7 +3072,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="686"
line="687"
column="17"/>
</issue>
@ -3171,7 +3083,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="689"
line="690"
column="21"/>
</issue>
@ -3182,7 +3094,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="693"
line="694"
column="17"/>
</issue>
@ -3193,7 +3105,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="697"
line="698"
column="21"/>
</issue>
@ -3204,7 +3116,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="706"
line="707"
column="17"/>
</issue>
@ -3215,7 +3127,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="709"
line="710"
column="21"/>
</issue>
@ -3226,7 +3138,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="714"
line="715"
column="17"/>
</issue>
@ -3237,7 +3149,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="717"
line="718"
column="21"/>
</issue>
@ -3248,7 +3160,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="722"
line="723"
column="17"/>
</issue>
@ -3259,7 +3171,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="725"
line="726"
column="21"/>
</issue>
@ -3270,7 +3182,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="730"
line="731"
column="17"/>
</issue>
@ -3281,7 +3193,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="733"
line="734"
column="21"/>
</issue>
@ -3292,7 +3204,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="740"
line="741"
column="21"/>
</issue>
@ -3303,7 +3215,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="743"
line="744"
column="25"/>
</issue>
@ -3314,7 +3226,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="752"
line="753"
column="17"/>
</issue>
@ -3325,7 +3237,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="755"
line="756"
column="21"/>
</issue>
@ -3336,7 +3248,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="768"
line="769"
column="17"/>
</issue>
@ -3347,7 +3259,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="772"
line="773"
column="21"/>
</issue>
@ -3358,7 +3270,7 @@
errorLine2=" ~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="897"
line="898"
column="17"/>
</issue>
@ -3369,7 +3281,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="899"
line="900"
column="17"/>
</issue>
@ -3380,7 +3292,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/app/pachli/MainActivity.kt"
line="910"
line="911"
column="17"/>
</issue>
@ -3611,7 +3523,7 @@
errorLine2=" ~~~~~~~~~">
<location
file="src/main/java/app/pachli/components/notifications/NotificationsFragment.kt"
line="168"
line="169"
column="43"/>
</issue>
@ -3633,7 +3545,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/app/pachli/adapter/StatusBaseViewHolder.kt"
line="811"
line="812"
column="13"/>
</issue>
@ -3644,7 +3556,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/app/pachli/adapter/StatusBaseViewHolder.kt"
line="815"
line="816"
column="13"/>
</issue>
@ -3655,7 +3567,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/app/pachli/adapter/StatusBaseViewHolder.kt"
line="820"
line="821"
column="13"/>
</issue>
@ -3688,7 +3600,7 @@
errorLine2=" ~~~~~~~~~">
<location
file="src/main/java/app/pachli/TabPreferenceActivity.kt"
line="94"
line="96"
column="13"/>
</issue>
@ -3699,7 +3611,7 @@
errorLine2=" ~~~~~~~~">
<location
file="src/main/java/app/pachli/TabPreferenceActivity.kt"
line="143"
line="145"
column="21"/>
</issue>
@ -3710,7 +3622,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/app/pachli/TabPreferenceActivity.kt"
line="153"
line="155"
column="59"/>
</issue>
@ -3721,7 +3633,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/java/app/pachli/TabPreferenceActivity.kt"
line="153"
line="155"
column="59"/>
</issue>
@ -5147,7 +5059,7 @@
errorLine2=" ^">
<location
file="src/main/java/app/pachli/adapter/TabAdapter.kt"
line="92"
line="91"
column="13"/>
</issue>
@ -5158,7 +5070,7 @@
errorLine2=" ^">
<location
file="src/main/java/app/pachli/adapter/TabAdapter.kt"
line="92"
line="91"
column="50"/>
</issue>

View File

@ -4,10 +4,8 @@
-optimizations !code/simplification/cast,!field/*,!class/merging/*
-optimizationpasses 6
-allowaccessmodification
-dontpreverify
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-keepattributes *Annotation*
# For native methods, see http://proguard.sourceforge.net/manual/examples.html#native
@ -40,18 +38,17 @@
# Pachli specific options
# keep members of our model classes, they are used in json de/serialization
-keepclassmembers class app.pachli.entity.* { *; }
-keepclassmembers class app.pachli.network.model.* { *; }
-keepclassmembers class app.pachli.core.network.model.* { *; }
-keep public enum app.pachli.entity.*$** {
-keep public enum app.pachli.core.network.model.*$** {
**[] $VALUES;
public *;
}
-keepclassmembers class app.pachli.components.conversation.ConversationAccountEntity { *; }
-keepclassmembers class app.pachli.db.DraftAttachment { *; }
-keepclassmembers class app.pachli.core.database.model.ConversationAccountEntity { *; }
-keepclassmembers class app.pachli.core.database.model.DraftAttachment { *; }
-keep enum app.pachli.db.DraftAttachment$Type {
-keep enum app.pachli.core.database.model.DraftAttachment$Type {
public *;
}

View File

@ -20,7 +20,7 @@ package app.pachli.updatecheck
import android.content.Intent
import android.net.Uri
import app.pachli.BuildConfig
import app.pachli.util.SharedPreferencesRepository
import app.pachli.core.preferences.SharedPreferencesRepository
import javax.inject.Inject
import javax.inject.Singleton

View File

@ -19,7 +19,7 @@ package app.pachli.updatecheck
import android.content.Intent
import android.net.Uri
import app.pachli.util.SharedPreferencesRepository
import app.pachli.core.preferences.SharedPreferencesRepository
import javax.inject.Inject
class UpdateCheck @Inject constructor(

View File

@ -20,7 +20,7 @@ package app.pachli.updatecheck
import android.content.Intent
import android.net.Uri
import app.pachli.BuildConfig
import app.pachli.util.SharedPreferencesRepository
import app.pachli.core.preferences.SharedPreferencesRepository
import com.google.android.play.core.appupdate.AppUpdateManager
import kotlinx.coroutines.suspendCancellableCoroutine
import javax.inject.Inject

View File

@ -29,13 +29,13 @@ import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.ListAdapter
import app.pachli.core.network.model.TimelineAccount
import app.pachli.core.preferences.PrefKeys
import app.pachli.core.preferences.SharedPreferencesRepository
import app.pachli.databinding.FragmentAccountsInListBinding
import app.pachli.databinding.ItemFollowRequestBinding
import app.pachli.entity.TimelineAccount
import app.pachli.settings.PrefKeys
import app.pachli.util.BindingHolder
import app.pachli.util.Either
import app.pachli.util.SharedPreferencesRepository
import app.pachli.util.emojify
import app.pachli.util.hide
import app.pachli.util.loadAvatar

View File

@ -35,16 +35,16 @@ import androidx.core.content.ContextCompat
import app.pachli.MainActivity.Companion.redirectIntent
import app.pachli.adapter.AccountSelectionAdapter
import app.pachli.components.login.LoginActivity
import app.pachli.db.AccountEntity
import app.pachli.db.AccountManager
import app.pachli.core.accounts.AccountManager
import app.pachli.core.database.model.AccountEntity
import app.pachli.core.preferences.PrefKeys
import app.pachli.core.preferences.PrefKeys.APP_THEME
import app.pachli.core.preferences.SharedPreferencesRepository
import app.pachli.interfaces.AccountSelectionListener
import app.pachli.interfaces.PermissionRequester
import app.pachli.settings.PrefKeys
import app.pachli.settings.PrefKeys.APP_THEME
import app.pachli.util.APP_THEME_DEFAULT
import app.pachli.util.EmbeddedFontFamily
import app.pachli.util.EmbeddedFontFamily.Companion.from
import app.pachli.util.SharedPreferencesRepository
import app.pachli.util.THEME_BLACK
import com.google.android.material.color.MaterialColors
import com.google.android.material.snackbar.Snackbar

View File

@ -26,7 +26,7 @@ import androidx.annotation.VisibleForTesting
import androidx.lifecycle.lifecycleScope
import app.pachli.components.account.AccountActivity
import app.pachli.components.viewthread.ViewThreadActivity
import app.pachli.network.MastodonApi
import app.pachli.core.network.retrofit.MastodonApi
import app.pachli.util.looksLikeMastodonUrl
import app.pachli.util.openLink
import at.connyduck.calladapter.networkresult.fold

View File

@ -36,9 +36,9 @@ import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import app.pachli.core.network.model.MastoList
import app.pachli.databinding.ActivityListsBinding
import app.pachli.databinding.DialogListBinding
import app.pachli.entity.MastoList
import app.pachli.util.hide
import app.pachli.util.show
import app.pachli.util.viewBinding
@ -134,13 +134,9 @@ class ListsActivity : BaseActivity() {
editText.text?.let { editText.setSelection(it.length) }
}
list?.let {
if (it.exclusive == null) {
binding.exclusiveCheckbox.visible(false)
} else {
binding.exclusiveCheckbox.isChecked = it.exclusive
}
}
list?.exclusive?.let {
binding.exclusiveCheckbox.isChecked = isTaskRoot
} ?: binding.exclusiveCheckbox.hide()
}
private fun showListDeleteDialog(list: MastoList) {

View File

@ -77,17 +77,18 @@ import app.pachli.components.preference.PreferencesActivity
import app.pachli.components.scheduled.ScheduledStatusActivity
import app.pachli.components.search.SearchActivity
import app.pachli.components.trending.TrendingActivity
import app.pachli.core.database.model.AccountEntity
import app.pachli.core.database.model.TabKind
import app.pachli.core.network.model.Account
import app.pachli.core.network.model.Notification
import app.pachli.core.preferences.PrefKeys
import app.pachli.databinding.ActivityMainBinding
import app.pachli.db.AccountEntity
import app.pachli.db.DraftsAlert
import app.pachli.entity.Account
import app.pachli.entity.Notification
import app.pachli.interfaces.AccountSelectionListener
import app.pachli.interfaces.ActionButtonActivity
import app.pachli.interfaces.FabFragment
import app.pachli.interfaces.ReselectableFragment
import app.pachli.pager.MainPagerAdapter
import app.pachli.settings.PrefKeys
import app.pachli.updatecheck.UpdateCheck
import app.pachli.updatecheck.UpdateNotificationFrequency
import app.pachli.usecase.DeveloperToolsUseCase
@ -854,7 +855,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
// Save the previous tab so it can be restored later
val previousTab = tabAdapter.tabs.getOrNull(binding.viewPager.currentItem)
val tabs = accountManager.activeAccount!!.tabPreferences
val tabs = accountManager.activeAccount!!.tabPreferences.map { TabViewData.from(it) }
// Detach any existing mediator before changing tab contents and attaching a new mediator
tabLayoutMediator?.detach()
@ -865,8 +866,8 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
tabLayoutMediator = TabLayoutMediator(activeTabLayout, binding.viewPager, true) {
tab: TabLayout.Tab, position: Int ->
tab.icon = AppCompatResources.getDrawable(this@MainActivity, tabs[position].icon)
tab.contentDescription = when (tabs[position].id) {
LIST -> tabs[position].arguments[1]
tab.contentDescription = when (tabs[position].kind) {
TabKind.LIST -> tabs[position].arguments[1]
else -> getString(tabs[position].text)
}
}.also { it.attach() }
@ -876,7 +877,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
// - The previously selected tab (if it hasn't been removed)
// - Left-most tab
val position = if (selectNotificationTab) {
tabs.indexOfFirst { it.id == NOTIFICATIONS }
tabs.indexOfFirst { it.kind == TabKind.NOTIFICATIONS }
} else {
previousTab?.let { tabs.indexOfFirst { it == previousTab } }
}.takeIf { it != -1 } ?: 0

View File

@ -24,13 +24,13 @@ import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import app.pachli.components.notifications.createWorkerNotificationChannel
import app.pachli.settings.NEW_INSTALL_SCHEMA_VERSION
import app.pachli.settings.PrefKeys
import app.pachli.settings.PrefKeys.APP_THEME
import app.pachli.settings.SCHEMA_VERSION
import app.pachli.core.preferences.NEW_INSTALL_SCHEMA_VERSION
import app.pachli.core.preferences.PrefKeys
import app.pachli.core.preferences.PrefKeys.APP_THEME
import app.pachli.core.preferences.SCHEMA_VERSION
import app.pachli.core.preferences.SharedPreferencesRepository
import app.pachli.util.APP_THEME_DEFAULT
import app.pachli.util.LocaleManager
import app.pachli.util.SharedPreferencesRepository
import app.pachli.util.setAppNightMode
import app.pachli.worker.PruneCacheWorker
import app.pachli.worker.WorkerFactory

View File

@ -23,7 +23,7 @@ import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import app.pachli.components.login.LoginActivity
import app.pachli.db.AccountManager
import app.pachli.core.accounts.AccountManager
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject

View File

@ -29,9 +29,9 @@ import app.pachli.appstore.FilterChangedEvent
import app.pachli.components.compose.ComposeActivity
import app.pachli.components.timeline.TimelineFragment
import app.pachli.components.timeline.TimelineKind
import app.pachli.core.network.model.Filter
import app.pachli.core.network.model.FilterV1
import app.pachli.databinding.ActivityStatuslistBinding
import app.pachli.entity.Filter
import app.pachli.entity.FilterV1
import app.pachli.interfaces.ActionButtonActivity
import app.pachli.interfaces.AppBarLayoutHost
import app.pachli.util.unsafeLazy

View File

@ -1,150 +0,0 @@
/*
* Copyright 2019 Conny Duck
*
* This file is a part of Pachli.
*
* 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.
*
* Pachli 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 Pachli; if not,
* see <http://www.gnu.org/licenses>.
*/
package app.pachli
import android.content.Context
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.fragment.app.Fragment
import app.pachli.components.conversation.ConversationsFragment
import app.pachli.components.notifications.NotificationsFragment
import app.pachli.components.timeline.TimelineFragment
import app.pachli.components.timeline.TimelineKind
import app.pachli.components.trending.TrendingLinksFragment
import app.pachli.components.trending.TrendingTagsFragment
import java.util.Objects
/** this would be a good case for a sealed class, but that does not work nice with Room */
const val HOME = "Home"
const val NOTIFICATIONS = "Notifications"
const val LOCAL = "Local"
const val FEDERATED = "Federated"
const val DIRECT = "Direct"
const val TRENDING_TAGS = "TrendingTags"
const val TRENDING_LINKS = "TrendingLinks"
const val TRENDING_STATUSES = "TrendingStatuses"
const val HASHTAG = "Hashtag"
const val LIST = "List"
const val BOOKMARKS = "Bookmarks"
data class TabData(
val id: String,
@StringRes val text: Int,
@DrawableRes val icon: Int,
val fragment: (List<String>) -> Fragment,
val arguments: List<String> = emptyList(),
val title: (Context) -> String = { context -> context.getString(text) },
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as TabData
if (id != other.id) return false
return arguments == other.arguments
}
override fun hashCode() = Objects.hash(id, arguments)
}
fun createTabDataFromId(id: String, arguments: List<String> = emptyList()): TabData {
return when (id) {
HOME -> TabData(
id = HOME,
text = R.string.title_home,
icon = R.drawable.ic_home_24dp,
fragment = { TimelineFragment.newInstance(TimelineKind.Home) },
)
NOTIFICATIONS -> TabData(
id = NOTIFICATIONS,
text = R.string.title_notifications,
icon = R.drawable.ic_notifications_24dp,
fragment = { NotificationsFragment.newInstance() },
)
LOCAL -> TabData(
id = LOCAL,
text = R.string.title_public_local,
icon = R.drawable.ic_local_24dp,
fragment = { TimelineFragment.newInstance(TimelineKind.PublicLocal) },
)
FEDERATED -> TabData(
id = FEDERATED,
text = R.string.title_public_federated,
icon = R.drawable.ic_public_24dp,
fragment = { TimelineFragment.newInstance(TimelineKind.PublicFederated) },
)
DIRECT -> TabData(
id = DIRECT,
text = R.string.title_direct_messages,
icon = R.drawable.ic_reblog_direct_24dp,
fragment = { ConversationsFragment.newInstance() },
)
TRENDING_TAGS -> TabData(
id = TRENDING_TAGS,
text = R.string.title_public_trending_hashtags,
icon = R.drawable.ic_trending_up_24px,
fragment = { TrendingTagsFragment.newInstance() },
)
TRENDING_LINKS -> TabData(
id = TRENDING_LINKS,
text = R.string.title_public_trending_links,
icon = R.drawable.ic_trending_up_24px,
fragment = { TrendingLinksFragment.newInstance() },
)
TRENDING_STATUSES -> TabData(
id = TRENDING_STATUSES,
text = R.string.title_public_trending_statuses,
icon = R.drawable.ic_trending_up_24px,
fragment = { TimelineFragment.newInstance(TimelineKind.TrendingStatuses) },
)
HASHTAG -> TabData(
id = HASHTAG,
text = R.string.hashtags,
icon = R.drawable.ic_hashtag,
fragment = { args -> TimelineFragment.newInstance(TimelineKind.Tag(args)) },
arguments = arguments,
title = { context -> arguments.joinToString(separator = " ") { context.getString(R.string.title_tag, it) } },
)
LIST -> TabData(
id = LIST,
text = R.string.list,
icon = R.drawable.ic_list,
fragment = { args -> TimelineFragment.newInstance(TimelineKind.UserList(args.first(), args.last())) },
arguments = arguments,
title = { arguments.getOrNull(1).orEmpty() },
)
BOOKMARKS -> TabData(
id = BOOKMARKS,
text = R.string.title_bookmarks,
icon = R.drawable.ic_bookmark_active_24dp,
fragment = { TimelineFragment.newInstance(TimelineKind.Bookmarks) },
)
else -> throw IllegalArgumentException("unknown tab type")
}
}
fun defaultTabs(): List<TabData> {
return listOf(
createTabDataFromId(HOME),
createTabDataFromId(NOTIFICATIONS),
createTabDataFromId(LOCAL),
createTabDataFromId(DIRECT),
)
}

View File

@ -42,9 +42,11 @@ import app.pachli.adapter.ItemInteractionListener
import app.pachli.adapter.TabAdapter
import app.pachli.appstore.EventHub
import app.pachli.appstore.MainTabsChangedEvent
import app.pachli.core.database.model.TabData
import app.pachli.core.database.model.TabKind
import app.pachli.core.network.model.MastoList
import app.pachli.core.network.retrofit.MastodonApi
import app.pachli.databinding.ActivityTabPreferenceBinding
import app.pachli.entity.MastoList
import app.pachli.network.MastodonApi
import app.pachli.util.getDimension
import app.pachli.util.hide
import app.pachli.util.show
@ -78,7 +80,7 @@ class TabPreferenceActivity : BaseActivity(), ItemInteractionListener {
private val binding by viewBinding(ActivityTabPreferenceBinding::inflate)
private lateinit var currentTabs: MutableList<TabData>
private lateinit var currentTabs: MutableList<TabViewData>
private lateinit var currentTabsAdapter: TabAdapter
private lateinit var touchHelper: ItemTouchHelper
private lateinit var addTabAdapter: TabAdapter
@ -108,7 +110,7 @@ class TabPreferenceActivity : BaseActivity(), ItemInteractionListener {
setDisplayShowHomeEnabled(true)
}
currentTabs = accountManager.activeAccount?.tabPreferences.orEmpty().toMutableList()
currentTabs = accountManager.activeAccount?.tabPreferences.orEmpty().map { TabViewData.from(it) }.toMutableList()
currentTabsAdapter = TabAdapter(currentTabs, false, this, currentTabs.size <= MIN_TAB_COUNT)
binding.currentTabsRecyclerView.adapter = currentTabsAdapter
binding.currentTabsRecyclerView.layoutManager = LinearLayoutManager(this)
@ -116,7 +118,7 @@ class TabPreferenceActivity : BaseActivity(), ItemInteractionListener {
MaterialDividerItemDecoration(this, MaterialDividerItemDecoration.VERTICAL),
)
addTabAdapter = TabAdapter(listOf(createTabDataFromId(DIRECT)), true, this)
addTabAdapter = TabAdapter(listOf(TabViewData.from(TabData(TabKind.DIRECT))), true, this)
binding.addTabRecyclerView.adapter = addTabAdapter
binding.addTabRecyclerView.layoutManager = LinearLayoutManager(this)
@ -176,15 +178,15 @@ class TabPreferenceActivity : BaseActivity(), ItemInteractionListener {
onBackPressedDispatcher.addCallback(onFabDismissedCallback)
}
override fun onTabAdded(tab: TabData) {
override fun onTabAdded(tab: TabViewData) {
toggleFab(false)
if (tab.id == HASHTAG) {
if (tab.kind == TabKind.HASHTAG) {
showAddHashtagDialog()
return
}
if (tab.id == LIST) {
if (tab.kind == TabKind.LIST) {
showSelectListDialog()
return
}
@ -202,13 +204,13 @@ class TabPreferenceActivity : BaseActivity(), ItemInteractionListener {
saveTabs()
}
override fun onActionChipClicked(tab: TabData, tabPosition: Int) {
override fun onActionChipClicked(tab: TabViewData, tabPosition: Int) {
showAddHashtagDialog(tab, tabPosition)
}
override fun onChipClicked(tab: TabData, tabPosition: Int, chipPosition: Int) {
override fun onChipClicked(tab: TabViewData, tabPosition: Int, chipPosition: Int) {
val newArguments = tab.arguments.filterIndexed { i, _ -> i != chipPosition }
val newTab = tab.copy(arguments = newArguments)
val newTab = tab.copy(tabData = tab.tabData.copy(arguments = newArguments))
currentTabs[tabPosition] = newTab
saveTabs()
@ -233,7 +235,7 @@ class TabPreferenceActivity : BaseActivity(), ItemInteractionListener {
onFabDismissedCallback.isEnabled = expand
}
private fun showAddHashtagDialog(tab: TabData? = null, tabPosition: Int = 0) {
private fun showAddHashtagDialog(tab: TabViewData? = null, tabPosition: Int = 0) {
val frameLayout = FrameLayout(this)
val padding = Utils.dpToPx(this, 8)
frameLayout.updatePadding(left = padding, right = padding)
@ -250,11 +252,11 @@ class TabPreferenceActivity : BaseActivity(), ItemInteractionListener {
.setPositiveButton(R.string.action_save) { _, _ ->
val input = editText.text.toString().trim()
if (tab == null) {
val newTab = createTabDataFromId(HASHTAG, listOf(input))
val newTab = TabViewData.from(TabData(TabKind.HASHTAG, listOf(input)))
currentTabs.add(newTab)
currentTabsAdapter.notifyItemInserted(currentTabs.size - 1)
} else {
val newTab = tab.copy(arguments = tab.arguments + input)
val newTab = tab.copy(tabData = tab.tabData.copy(arguments = tab.arguments + input))
currentTabs[tabPosition] = newTab
currentTabsAdapter.notifyItemChanged(tabPosition)
@ -308,7 +310,7 @@ class TabPreferenceActivity : BaseActivity(), ItemInteractionListener {
.setView(statusLayout)
.setAdapter(adapter) { _, position ->
adapter.getItem(position)?.let { item ->
val newTab = createTabDataFromId(LIST, listOf(item.id, item.title))
val newTab = TabViewData.from(TabData(TabKind.LIST, listOf(item.id, item.title)))
currentTabs.add(newTab)
currentTabsAdapter.notifyItemInserted(currentTabs.size - 1)
updateAvailableTabs()
@ -357,46 +359,47 @@ class TabPreferenceActivity : BaseActivity(), ItemInteractionListener {
}
private fun updateAvailableTabs() {
val addableTabs: MutableList<TabData> = mutableListOf()
val addableTabs: MutableList<TabViewData> = mutableListOf()
val homeTab = createTabDataFromId(HOME)
val homeTab = TabViewData.from(TabData(TabKind.HOME))
if (!currentTabs.contains(homeTab)) {
addableTabs.add(homeTab)
}
val notificationTab = createTabDataFromId(NOTIFICATIONS)
val notificationTab = TabViewData.from(TabData(TabKind.NOTIFICATIONS))
if (!currentTabs.contains(notificationTab)) {
addableTabs.add(notificationTab)
}
val localTab = createTabDataFromId(LOCAL)
val localTab = TabViewData.from(TabData(TabKind.LOCAL))
if (!currentTabs.contains(localTab)) {
addableTabs.add(localTab)
}
val federatedTab = createTabDataFromId(FEDERATED)
val federatedTab = TabViewData.from(TabData(TabKind.FEDERATED))
if (!currentTabs.contains(federatedTab)) {
addableTabs.add(federatedTab)
}
val directMessagesTab = createTabDataFromId(DIRECT)
val directMessagesTab = TabViewData.from(TabData(TabKind.DIRECT))
if (!currentTabs.contains(directMessagesTab)) {
addableTabs.add(directMessagesTab)
}
val trendingTagsTab = createTabDataFromId(TRENDING_TAGS)
val trendingTagsTab = TabViewData.from(TabData(TabKind.TRENDING_TAGS))
if (!currentTabs.contains(trendingTagsTab)) {
addableTabs.add(trendingTagsTab)
}
val trendingLinksTab = createTabDataFromId(TRENDING_LINKS)
val trendingLinksTab = TabViewData.from(TabData(TabKind.TRENDING_LINKS))
if (!currentTabs.contains(trendingLinksTab)) {
addableTabs.add(trendingLinksTab)
}
createTabDataFromId(TRENDING_STATUSES).apply {
currentTabs.contains(this) || addableTabs.add(this)
val trendingStatusesTab = TabViewData.from(TabData(TabKind.TRENDING_STATUSES))
if (!currentTabs.contains(trendingStatusesTab)) {
addableTabs.add(trendingStatusesTab)
}
val bookmarksTab = createTabDataFromId(BOOKMARKS)
val bookmarksTab = TabViewData.from(TabData(TabKind.BOOKMARKS))
if (!currentTabs.contains(trendingTagsTab)) {
addableTabs.add(bookmarksTab)
}
addableTabs.add(createTabDataFromId(HASHTAG))
addableTabs.add(createTabDataFromId(LIST))
addableTabs.add(TabViewData.from(TabData(TabKind.HASHTAG)))
addableTabs.add(TabViewData.from(TabData(TabKind.LIST)))
addTabAdapter.updateData(addableTabs)
@ -414,7 +417,7 @@ class TabPreferenceActivity : BaseActivity(), ItemInteractionListener {
private fun saveTabs() {
accountManager.activeAccount?.let {
lifecycleScope.launch(Dispatchers.IO) {
it.tabPreferences = currentTabs
it.tabPreferences = currentTabs.map { it.tabData }
accountManager.saveAccount(it)
}
}
@ -425,7 +428,7 @@ class TabPreferenceActivity : BaseActivity(), ItemInteractionListener {
super.onPause()
if (tabsChanged) {
lifecycleScope.launch {
eventHub.dispatch(MainTabsChangedEvent(currentTabs))
eventHub.dispatch(MainTabsChangedEvent(currentTabs.map { it.tabData }))
}
}
}

View File

@ -0,0 +1,152 @@
/*
* Copyright 2023 Pachli Association
*
* This file is a part of Pachli.
*
* 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.
*
* Pachli 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 Pachli; if not,
* see <http://www.gnu.org/licenses>.
*/
package app.pachli
import android.content.Context
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.fragment.app.Fragment
import app.pachli.components.conversation.ConversationsFragment
import app.pachli.components.notifications.NotificationsFragment
import app.pachli.components.timeline.TimelineFragment
import app.pachli.components.timeline.TimelineKind
import app.pachli.components.trending.TrendingLinksFragment
import app.pachli.components.trending.TrendingTagsFragment
import app.pachli.core.database.model.TabData
import app.pachli.core.database.model.TabKind
/**
* Wrap a [TabData] with additional information to display a tab with that data.
*
* @param tabData wrapped [TabData]
* @param text text to use for this tab when displayed in lists
* @param icon icon to use when displaying the tab
* @param fragment [Fragment] to display the tab's contents
* @param title title to display in the action bar if this tab is active
*/
data class TabViewData(
val tabData: TabData,
@StringRes val text: Int,
@DrawableRes val icon: Int,
val fragment: (List<String>) -> Fragment,
val title: (Context) -> String = { context -> context.getString(text) },
) {
val kind get() = this.tabData.kind
val arguments get() = this.tabData.arguments
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as TabViewData
if (tabData != other.tabData) return false
return true
}
override fun hashCode() = tabData.hashCode()
companion object {
fun from(tabKind: TabKind) = from(TabData.from(tabKind))
fun from(tabData: TabData) = when (tabData.kind) {
TabKind.HOME -> TabViewData(
tabData = tabData,
text = R.string.title_home,
icon = R.drawable.ic_home_24dp,
fragment = { TimelineFragment.newInstance(TimelineKind.Home) },
)
TabKind.NOTIFICATIONS -> TabViewData(
tabData = tabData,
text = R.string.title_notifications,
icon = R.drawable.ic_notifications_24dp,
fragment = { NotificationsFragment.newInstance() },
)
TabKind.LOCAL -> TabViewData(
tabData = tabData,
text = R.string.title_public_local,
icon = R.drawable.ic_local_24dp,
fragment = { TimelineFragment.newInstance(TimelineKind.PublicLocal) },
)
TabKind.FEDERATED -> TabViewData(
tabData = tabData,
text = R.string.title_public_federated,
icon = R.drawable.ic_public_24dp,
fragment = { TimelineFragment.newInstance(TimelineKind.PublicFederated) },
)
TabKind.DIRECT -> TabViewData(
tabData = tabData,
text = R.string.title_direct_messages,
icon = R.drawable.ic_reblog_direct_24dp,
fragment = { ConversationsFragment.newInstance() },
)
TabKind.TRENDING_TAGS -> TabViewData(
tabData = tabData,
text = R.string.title_public_trending_hashtags,
icon = R.drawable.ic_trending_up_24px,
fragment = { TrendingTagsFragment.newInstance() },
)
TabKind.TRENDING_LINKS -> TabViewData(
tabData = tabData,
text = R.string.title_public_trending_links,
icon = R.drawable.ic_trending_up_24px,
fragment = { TrendingLinksFragment.newInstance() },
)
TabKind.TRENDING_STATUSES -> TabViewData(
tabData = tabData,
text = R.string.title_public_trending_statuses,
icon = R.drawable.ic_trending_up_24px,
fragment = { TimelineFragment.newInstance(TimelineKind.TrendingStatuses) },
)
TabKind.HASHTAG -> TabViewData(
tabData = tabData,
text = R.string.hashtags,
icon = R.drawable.ic_hashtag,
fragment = { args -> TimelineFragment.newInstance(TimelineKind.Tag(args)) },
title = { context ->
tabData.arguments.joinToString(separator = " ") {
context.getString(
R.string.title_tag,
it,
)
}
},
)
TabKind.LIST -> TabViewData(
tabData = tabData,
text = R.string.list,
icon = R.drawable.ic_list,
fragment = { args ->
TimelineFragment.newInstance(
TimelineKind.UserList(args.first(), args.last()),
)
},
title = { tabData.arguments.getOrNull(1).orEmpty() },
)
TabKind.BOOKMARKS -> TabViewData(
tabData = tabData,
text = R.string.title_bookmarks,
icon = R.drawable.ic_bookmark_active_24dp,
fragment = { TimelineFragment.newInstance(TimelineKind.Bookmarks) },
)
else -> throw IllegalArgumentException("unknown tab type: $tabData")
}
}
}

View File

@ -47,8 +47,8 @@ import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import app.pachli.BuildConfig.APPLICATION_ID
import app.pachli.components.viewthread.ViewThreadActivity
import app.pachli.core.network.model.Attachment
import app.pachli.databinding.ActivityViewMediaBinding
import app.pachli.entity.Attachment
import app.pachli.fragment.ViewImageFragment
import app.pachli.fragment.ViewVideoFragment
import app.pachli.pager.ImagePagerAdapter

View File

@ -20,8 +20,8 @@ import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.widget.doAfterTextChanged
import androidx.recyclerview.widget.RecyclerView
import app.pachli.core.network.model.StringField
import app.pachli.databinding.ItemEditFieldBinding
import app.pachli.entity.StringField
import app.pachli.util.BindingHolder
import app.pachli.util.fixTextSelection

View File

@ -23,9 +23,9 @@ import android.view.ViewGroup
import android.widget.ArrayAdapter
import androidx.preference.PreferenceManager
import app.pachli.R
import app.pachli.core.database.model.AccountEntity
import app.pachli.core.preferences.PrefKeys
import app.pachli.databinding.ItemAutocompleteAccountBinding
import app.pachli.db.AccountEntity
import app.pachli.settings.PrefKeys
import app.pachli.util.emojify
import app.pachli.util.loadAvatar

View File

@ -19,8 +19,8 @@ package app.pachli.adapter
import androidx.recyclerview.widget.RecyclerView
import app.pachli.R
import app.pachli.core.network.model.TimelineAccount
import app.pachli.databinding.ItemAccountBinding
import app.pachli.entity.TimelineAccount
import app.pachli.interfaces.AccountActionListener
import app.pachli.interfaces.LinkListener
import app.pachli.util.emojify

View File

@ -20,8 +20,8 @@ import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.appcompat.widget.TooltipCompat
import androidx.recyclerview.widget.RecyclerView
import app.pachli.core.network.model.Emoji
import app.pachli.databinding.ItemEmojiButtonBinding
import app.pachli.entity.Emoji
import app.pachli.util.BindingHolder
import com.bumptech.glide.Glide
import java.util.Locale
@ -32,7 +32,7 @@ class EmojiAdapter(
private val animate: Boolean,
) : RecyclerView.Adapter<BindingHolder<ItemEmojiButtonBinding>>() {
private val emojiList: List<Emoji> = emojiList.filter { emoji -> emoji.visibleInPicker == null || emoji.visibleInPicker }
private val emojiList: List<Emoji> = emojiList.filter { emoji -> emoji.visibleInPicker == null || emoji.visibleInPicker!! }
.sortedBy { it.shortcode.lowercase(Locale.ROOT) }
override fun getItemCount() = emojiList.size

View File

@ -19,8 +19,8 @@ package app.pachli.adapter
import android.view.View
import app.pachli.R
import app.pachli.core.network.model.Filter
import app.pachli.databinding.ItemStatusWrapperBinding
import app.pachli.entity.Filter
import app.pachli.interfaces.StatusActionListener
import app.pachli.util.StatusDisplayOptions
import app.pachli.viewdata.StatusViewData

View File

@ -24,18 +24,18 @@ import android.text.style.StyleSpan
import androidx.recyclerview.widget.RecyclerView
import app.pachli.R
import app.pachli.components.notifications.NotificationsPagingAdapter
import app.pachli.core.common.string.unicodeWrap
import app.pachli.core.network.model.TimelineAccount
import app.pachli.core.network.parseAsMastodonHtml
import app.pachli.databinding.ItemFollowRequestBinding
import app.pachli.entity.TimelineAccount
import app.pachli.interfaces.AccountActionListener
import app.pachli.interfaces.LinkListener
import app.pachli.util.StatusDisplayOptions
import app.pachli.util.emojify
import app.pachli.util.hide
import app.pachli.util.loadAvatar
import app.pachli.util.parseAsMastodonHtml
import app.pachli.util.setClickableText
import app.pachli.util.show
import app.pachli.util.unicodeWrap
import app.pachli.util.visible
import app.pachli.viewdata.NotificationViewData

View File

@ -20,8 +20,8 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import app.pachli.core.network.model.Emoji
import app.pachli.databinding.ItemPollBinding
import app.pachli.entity.Emoji
import app.pachli.util.BindingHolder
import app.pachli.util.emojify
import app.pachli.util.visible

View File

@ -23,14 +23,14 @@ import androidx.recyclerview.widget.RecyclerView
import app.pachli.R
import app.pachli.components.notifications.NotificationActionListener
import app.pachli.components.notifications.NotificationsPagingAdapter
import app.pachli.core.common.string.unicodeWrap
import app.pachli.core.network.model.Report
import app.pachli.core.network.model.TimelineAccount
import app.pachli.databinding.ItemReportNotificationBinding
import app.pachli.entity.Report
import app.pachli.entity.TimelineAccount
import app.pachli.util.StatusDisplayOptions
import app.pachli.util.emojify
import app.pachli.util.getRelativeTimeSpanString
import app.pachli.util.loadAvatar
import app.pachli.util.unicodeWrap
import app.pachli.viewdata.NotificationViewData
import at.connyduck.sparkbutton.helpers.Utils
import java.util.Date

View File

@ -21,24 +21,26 @@ import androidx.core.text.HtmlCompat
import androidx.recyclerview.widget.RecyclerView
import app.pachli.R
import app.pachli.ViewMediaActivity.Companion.newSingleImageIntent
import app.pachli.entity.Attachment
import app.pachli.entity.Emoji
import app.pachli.entity.PreviewCardKind
import app.pachli.entity.Status
import app.pachli.entity.description
import app.pachli.core.common.util.AbsoluteTimeFormatter
import app.pachli.core.common.util.formatNumber
import app.pachli.core.database.model.TranslationState
import app.pachli.core.network.model.Attachment
import app.pachli.core.network.model.Emoji
import app.pachli.core.network.model.PreviewCardKind
import app.pachli.core.network.model.Status
import app.pachli.interfaces.StatusActionListener
import app.pachli.util.AbsoluteTimeFormatter
import app.pachli.util.CardViewMode
import app.pachli.util.CompositeWithOpaqueBackground
import app.pachli.util.StatusDisplayOptions
import app.pachli.util.aspectRatios
import app.pachli.util.decodeBlurHash
import app.pachli.util.description
import app.pachli.util.emojify
import app.pachli.util.expandTouchSizeToFillRow
import app.pachli.util.formatNumber
import app.pachli.util.getFormattedDescription
import app.pachli.util.getRelativeTimeSpanString
import app.pachli.util.hide
import app.pachli.util.iconResource
import app.pachli.util.loadAvatar
import app.pachli.util.makeIcon
import app.pachli.util.setClickableMentions
@ -50,7 +52,6 @@ import app.pachli.view.PollView
import app.pachli.view.PreviewCardView
import app.pachli.viewdata.PollViewData.Companion.from
import app.pachli.viewdata.StatusViewData
import app.pachli.viewdata.TranslationState
import at.connyduck.sparkbutton.SparkButton
import at.connyduck.sparkbutton.helpers.Utils
import com.bumptech.glide.Glide

View File

@ -10,14 +10,14 @@ import android.view.View
import androidx.recyclerview.widget.RecyclerView
import app.pachli.R
import app.pachli.databinding.ItemStatusDetailedBinding
import app.pachli.entity.description
import app.pachli.entity.icon
import app.pachli.interfaces.StatusActionListener
import app.pachli.util.CardViewMode
import app.pachli.util.NoUnderlineURLSpan
import app.pachli.util.StatusDisplayOptions
import app.pachli.util.createClickableText
import app.pachli.util.description
import app.pachli.util.hide
import app.pachli.util.icon
import app.pachli.util.show
import app.pachli.viewdata.StatusViewData
import java.text.DateFormat

View File

@ -21,17 +21,17 @@ import android.text.TextUtils
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import app.pachli.R
import app.pachli.core.common.string.unicodeWrap
import app.pachli.core.common.util.formatNumber
import app.pachli.core.network.model.Emoji
import app.pachli.core.network.model.Filter
import app.pachli.databinding.ItemStatusBinding
import app.pachli.entity.Emoji
import app.pachli.entity.Filter
import app.pachli.interfaces.StatusActionListener
import app.pachli.util.SmartLengthInputFilter
import app.pachli.util.StatusDisplayOptions
import app.pachli.util.emojify
import app.pachli.util.formatNumber
import app.pachli.util.hide
import app.pachli.util.show
import app.pachli.util.unicodeWrap
import app.pachli.util.visible
import app.pachli.viewdata.StatusViewData
import at.connyduck.sparkbutton.helpers.Utils

View File

@ -22,10 +22,9 @@ import android.view.ViewGroup
import androidx.core.view.size
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
import app.pachli.HASHTAG
import app.pachli.LIST
import app.pachli.R
import app.pachli.TabData
import app.pachli.TabViewData
import app.pachli.core.database.model.TabKind
import app.pachli.databinding.ItemTabPreferenceBinding
import app.pachli.databinding.ItemTabPreferenceSmallBinding
import app.pachli.util.BindingHolder
@ -35,22 +34,22 @@ import app.pachli.util.show
import com.google.android.material.chip.Chip
interface ItemInteractionListener {
fun onTabAdded(tab: TabData)
fun onTabAdded(tab: TabViewData)
fun onTabRemoved(position: Int)
fun onStartDelete(viewHolder: RecyclerView.ViewHolder)
fun onStartDrag(viewHolder: RecyclerView.ViewHolder)
fun onActionChipClicked(tab: TabData, tabPosition: Int)
fun onChipClicked(tab: TabData, tabPosition: Int, chipPosition: Int)
fun onActionChipClicked(tab: TabViewData, tabPosition: Int)
fun onChipClicked(tab: TabViewData, tabPosition: Int, chipPosition: Int)
}
class TabAdapter(
private var data: List<TabData>,
private var data: List<TabViewData>,
private val small: Boolean,
private val listener: ItemInteractionListener,
private var removeButtonEnabled: Boolean = false,
) : RecyclerView.Adapter<BindingHolder<ViewBinding>>() {
fun updateData(newData: List<TabData>) {
fun updateData(newData: List<TabViewData>) {
this.data = newData
notifyDataSetChanged()
}
@ -81,7 +80,7 @@ class TabAdapter(
} else {
val binding = holder.binding as ItemTabPreferenceBinding
if (tab.id == LIST) {
if (tab.kind == TabKind.LIST) {
binding.textView.text = tab.arguments.getOrNull(1).orEmpty()
} else {
binding.textView.setText(tab.text)
@ -107,7 +106,7 @@ class TabAdapter(
(if (removeButtonEnabled) android.R.attr.textColorTertiary else R.attr.textColorDisabled),
)
if (tab.id == HASHTAG) {
if (tab.kind == TabKind.HASHTAG) {
binding.chipGroup.show()
/*

View File

@ -1,7 +1,7 @@
package app.pachli.appstore
import app.pachli.db.AccountManager
import app.pachli.db.TimelineDao
import app.pachli.core.accounts.AccountManager
import app.pachli.core.database.dao.TimelineDao
import com.google.gson.Gson
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers

View File

@ -1,10 +1,10 @@
package app.pachli.appstore
import app.pachli.TabData
import app.pachli.entity.Account
import app.pachli.entity.Filter
import app.pachli.entity.Poll
import app.pachli.entity.Status
import app.pachli.core.database.model.TabData
import app.pachli.core.network.model.Account
import app.pachli.core.network.model.Filter
import app.pachli.core.network.model.Poll
import app.pachli.core.network.model.Status
data class FavoriteEvent(val statusId: String, val favourite: Boolean) : Event
data class ReblogEvent(val statusId: String, val reblog: Boolean) : Event

View File

@ -55,16 +55,17 @@ import app.pachli.components.account.list.ListsForAccountFragment
import app.pachli.components.accountlist.AccountListActivity
import app.pachli.components.compose.ComposeActivity
import app.pachli.components.report.ReportActivity
import app.pachli.core.database.model.AccountEntity
import app.pachli.core.network.model.Account
import app.pachli.core.network.model.Relationship
import app.pachli.core.network.parseAsMastodonHtml
import app.pachli.core.preferences.PrefKeys
import app.pachli.databinding.ActivityAccountBinding
import app.pachli.db.AccountEntity
import app.pachli.db.DraftsAlert
import app.pachli.entity.Account
import app.pachli.entity.Relationship
import app.pachli.interfaces.AccountSelectionListener
import app.pachli.interfaces.ActionButtonActivity
import app.pachli.interfaces.LinkListener
import app.pachli.interfaces.ReselectableFragment
import app.pachli.settings.PrefKeys
import app.pachli.util.Error
import app.pachli.util.Loading
import app.pachli.util.Success
@ -72,7 +73,6 @@ import app.pachli.util.emojify
import app.pachli.util.getDomain
import app.pachli.util.hide
import app.pachli.util.loadAvatar
import app.pachli.util.parseAsMastodonHtml
import app.pachli.util.reduceSwipeSensitivity
import app.pachli.util.setClickableText
import app.pachli.util.show
@ -659,9 +659,9 @@ class AccountActivity :
viewModel.changeSubscribingState()
}
if (relation.notifying != null) {
subscribing = relation.notifying
subscribing = relation.notifying!!
} else if (relation.subscribing != null) {
subscribing = relation.subscribing
subscribing = relation.subscribing!!
}
}

View File

@ -20,13 +20,13 @@ import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import app.pachli.R
import app.pachli.core.network.model.Emoji
import app.pachli.core.network.model.Field
import app.pachli.core.network.parseAsMastodonHtml
import app.pachli.databinding.ItemAccountFieldBinding
import app.pachli.entity.Emoji
import app.pachli.entity.Field
import app.pachli.interfaces.LinkListener
import app.pachli.util.BindingHolder
import app.pachli.util.emojify
import app.pachli.util.parseAsMastodonHtml
import app.pachli.util.setClickableText
class AccountFieldAdapter(

View File

@ -9,10 +9,10 @@ import app.pachli.appstore.EventHub
import app.pachli.appstore.MuteEvent
import app.pachli.appstore.ProfileEditedEvent
import app.pachli.appstore.UnfollowEvent
import app.pachli.db.AccountManager
import app.pachli.entity.Account
import app.pachli.entity.Relationship
import app.pachli.network.MastodonApi
import app.pachli.core.accounts.AccountManager
import app.pachli.core.network.model.Account
import app.pachli.core.network.model.Relationship
import app.pachli.core.network.retrofit.MastodonApi
import app.pachli.util.Error
import app.pachli.util.Loading
import app.pachli.util.Resource

View File

@ -18,8 +18,8 @@ package app.pachli.components.account.list
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import app.pachli.entity.MastoList
import app.pachli.network.MastodonApi
import app.pachli.core.network.model.MastoList
import app.pachli.core.network.retrofit.MastodonApi
import at.connyduck.calladapter.networkresult.getOrThrow
import at.connyduck.calladapter.networkresult.onFailure
import at.connyduck.calladapter.networkresult.onSuccess

View File

@ -33,12 +33,12 @@ import androidx.recyclerview.widget.GridLayoutManager
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
import app.pachli.R
import app.pachli.ViewMediaActivity
import app.pachli.core.accounts.AccountManager
import app.pachli.core.network.model.Attachment
import app.pachli.core.preferences.PrefKeys
import app.pachli.core.preferences.SharedPreferencesRepository
import app.pachli.databinding.FragmentTimelineBinding
import app.pachli.db.AccountManager
import app.pachli.entity.Attachment
import app.pachli.interfaces.RefreshableFragment
import app.pachli.settings.PrefKeys
import app.pachli.util.SharedPreferencesRepository
import app.pachli.util.hide
import app.pachli.util.openLink
import app.pachli.util.show

View File

@ -11,8 +11,8 @@ import androidx.core.view.setPadding
import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.DiffUtil
import app.pachli.R
import app.pachli.core.network.model.Attachment
import app.pachli.databinding.ItemAccountMediaBinding
import app.pachli.entity.Attachment
import app.pachli.util.BindingHolder
import app.pachli.util.decodeBlurHash
import app.pachli.util.getFormattedDescription

View File

@ -21,8 +21,8 @@ import androidx.paging.LoadType
import androidx.paging.PagingState
import androidx.paging.RemoteMediator
import app.pachli.components.timeline.util.ifExpected
import app.pachli.db.AccountEntity
import app.pachli.network.MastodonApi
import app.pachli.core.database.model.AccountEntity
import app.pachli.core.network.retrofit.MastodonApi
import app.pachli.viewdata.AttachmentViewData
import retrofit2.HttpException

View File

@ -22,8 +22,8 @@ import androidx.paging.ExperimentalPagingApi
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.cachedIn
import app.pachli.db.AccountManager
import app.pachli.network.MastodonApi
import app.pachli.core.accounts.AccountManager
import app.pachli.core.network.retrofit.MastodonApi
import app.pachli.viewdata.AttachmentViewData
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject

View File

@ -37,17 +37,17 @@ import app.pachli.components.accountlist.adapter.FollowAdapter
import app.pachli.components.accountlist.adapter.FollowRequestsAdapter
import app.pachli.components.accountlist.adapter.FollowRequestsHeaderAdapter
import app.pachli.components.accountlist.adapter.MutesAdapter
import app.pachli.core.accounts.AccountManager
import app.pachli.core.network.model.HttpHeaderLink
import app.pachli.core.network.model.Relationship
import app.pachli.core.network.model.TimelineAccount
import app.pachli.core.network.retrofit.MastodonApi
import app.pachli.core.preferences.PrefKeys
import app.pachli.core.preferences.SharedPreferencesRepository
import app.pachli.databinding.FragmentAccountListBinding
import app.pachli.db.AccountManager
import app.pachli.entity.Relationship
import app.pachli.entity.TimelineAccount
import app.pachli.interfaces.AccountActionListener
import app.pachli.interfaces.AppBarLayoutHost
import app.pachli.interfaces.LinkListener
import app.pachli.network.MastodonApi
import app.pachli.settings.PrefKeys
import app.pachli.util.HttpHeaderLink
import app.pachli.util.SharedPreferencesRepository
import app.pachli.util.hide
import app.pachli.util.show
import app.pachli.util.viewBinding

View File

@ -18,8 +18,8 @@ package app.pachli.components.accountlist.adapter
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import app.pachli.core.network.model.TimelineAccount
import app.pachli.databinding.ItemFooterBinding
import app.pachli.entity.TimelineAccount
import app.pachli.interfaces.AccountActionListener
import app.pachli.util.BindingHolder
import app.pachli.util.removeDuplicates

View File

@ -25,16 +25,16 @@ import android.view.ViewGroup
import androidx.core.view.size
import androidx.recyclerview.widget.RecyclerView
import app.pachli.R
import app.pachli.core.common.util.AbsoluteTimeFormatter
import app.pachli.core.network.model.Announcement
import app.pachli.core.network.parseAsMastodonHtml
import app.pachli.databinding.ItemAnnouncementBinding
import app.pachli.entity.Announcement
import app.pachli.interfaces.LinkListener
import app.pachli.util.AbsoluteTimeFormatter
import app.pachli.util.BindingHolder
import app.pachli.util.EmojiSpan
import app.pachli.util.emojify
import app.pachli.util.equalByMinute
import app.pachli.util.getRelativeTimeSpanString
import app.pachli.util.parseAsMastodonHtml
import app.pachli.util.setClickableText
import app.pachli.util.visible
import com.bumptech.glide.Glide

View File

@ -32,8 +32,8 @@ import app.pachli.R
import app.pachli.StatusListActivity
import app.pachli.adapter.EmojiAdapter
import app.pachli.adapter.OnEmojiSelectedListener
import app.pachli.core.preferences.PrefKeys
import app.pachli.databinding.ActivityAnnouncementsBinding
import app.pachli.settings.PrefKeys
import app.pachli.util.Error
import app.pachli.util.Loading
import app.pachli.util.Success

View File

@ -23,9 +23,9 @@ import androidx.lifecycle.viewModelScope
import app.pachli.appstore.AnnouncementReadEvent
import app.pachli.appstore.EventHub
import app.pachli.components.instanceinfo.InstanceInfoRepository
import app.pachli.entity.Announcement
import app.pachli.entity.Emoji
import app.pachli.network.MastodonApi
import app.pachli.core.network.model.Announcement
import app.pachli.core.network.model.Emoji
import app.pachli.core.network.retrofit.MastodonApi
import app.pachli.util.Error
import app.pachli.util.Loading
import app.pachli.util.Resource

View File

@ -76,19 +76,19 @@ import app.pachli.components.compose.dialog.showAddPollDialog
import app.pachli.components.compose.view.ComposeOptionsListener
import app.pachli.components.compose.view.ComposeScheduleView
import app.pachli.components.instanceinfo.InstanceInfoRepository
import app.pachli.core.database.model.AccountEntity
import app.pachli.core.database.model.DraftAttachment
import app.pachli.core.network.model.Attachment
import app.pachli.core.network.model.Emoji
import app.pachli.core.network.model.NewPoll
import app.pachli.core.network.model.Status
import app.pachli.core.preferences.PrefKeys
import app.pachli.core.preferences.PrefKeys.APP_THEME
import app.pachli.core.preferences.SharedPreferencesRepository
import app.pachli.databinding.ActivityComposeBinding
import app.pachli.db.AccountEntity
import app.pachli.db.DraftAttachment
import app.pachli.entity.Attachment
import app.pachli.entity.Emoji
import app.pachli.entity.NewPoll
import app.pachli.entity.Status
import app.pachli.settings.PrefKeys
import app.pachli.settings.PrefKeys.APP_THEME
import app.pachli.util.APP_THEME_DEFAULT
import app.pachli.util.MentionSpan
import app.pachli.util.PickMediaFiles
import app.pachli.util.SharedPreferencesRepository
import app.pachli.util.THEME_BLACK
import app.pachli.util.getInitialLanguages
import app.pachli.util.getLocaleList

View File

@ -24,11 +24,11 @@ import android.widget.Filter
import android.widget.Filterable
import androidx.annotation.WorkerThread
import app.pachli.R
import app.pachli.core.network.model.Emoji
import app.pachli.core.network.model.TimelineAccount
import app.pachli.databinding.ItemAutocompleteAccountBinding
import app.pachli.databinding.ItemAutocompleteEmojiBinding
import app.pachli.databinding.ItemAutocompleteHashtagBinding
import app.pachli.entity.Emoji
import app.pachli.entity.TimelineAccount
import app.pachli.util.emojify
import app.pachli.util.loadAvatar
import app.pachli.util.visible

View File

@ -27,16 +27,16 @@ import app.pachli.components.drafts.DraftHelper
import app.pachli.components.instanceinfo.InstanceInfo
import app.pachli.components.instanceinfo.InstanceInfoRepository
import app.pachli.components.search.SearchType
import app.pachli.db.AccountManager
import app.pachli.entity.Attachment
import app.pachli.entity.Emoji
import app.pachli.entity.NewPoll
import app.pachli.entity.Status
import app.pachli.network.MastodonApi
import app.pachli.core.accounts.AccountManager
import app.pachli.core.common.string.randomAlphanumericString
import app.pachli.core.network.model.Attachment
import app.pachli.core.network.model.Emoji
import app.pachli.core.network.model.NewPoll
import app.pachli.core.network.model.Status
import app.pachli.core.network.retrofit.MastodonApi
import app.pachli.service.MediaToSend
import app.pachli.service.ServiceClient
import app.pachli.service.StatusToSend
import app.pachli.util.randomAlphanumericString
import at.connyduck.calladapter.networkresult.fold
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers

View File

@ -30,13 +30,13 @@ import app.pachli.BuildConfig
import app.pachli.R
import app.pachli.components.compose.ComposeActivity.QueuedMedia
import app.pachli.components.instanceinfo.InstanceInfo
import app.pachli.network.MediaUploadApi
import app.pachli.core.common.string.randomAlphanumericString
import app.pachli.core.mastodon.model.MediaUploadApi
import app.pachli.network.ProgressRequestBody
import app.pachli.util.MEDIA_SIZE_UNKNOWN
import app.pachli.util.getImageSquarePixels
import app.pachli.util.getMediaSize
import app.pachli.util.getServerErrorMessage
import app.pachli.util.randomAlphanumericString
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers

View File

@ -22,8 +22,8 @@ import android.view.WindowManager
import android.widget.ArrayAdapter
import androidx.appcompat.app.AlertDialog
import app.pachli.R
import app.pachli.core.network.model.NewPoll
import app.pachli.databinding.DialogAddPollBinding
import app.pachli.entity.NewPoll
fun showAddPollDialog(
context: Context,

View File

@ -25,8 +25,8 @@ import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import app.pachli.core.network.model.Attachment.Focus
import app.pachli.databinding.DialogFocusBinding
import app.pachli.entity.Attachment.Focus
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException

View File

@ -20,7 +20,7 @@ import android.content.Context
import android.util.AttributeSet
import android.widget.RadioGroup
import app.pachli.R
import app.pachli.entity.Status
import app.pachli.core.network.model.Status
class ComposeOptionsView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : RadioGroup(context, attrs) {

View File

@ -10,7 +10,7 @@ import android.graphics.Point
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import app.pachli.entity.Attachment
import app.pachli.core.network.model.Attachment
import kotlin.math.ceil
import kotlin.math.max
import kotlin.math.min

View File

@ -22,8 +22,8 @@ import android.view.LayoutInflater
import android.widget.LinearLayout
import app.pachli.R
import app.pachli.adapter.PreviewPollOptionsAdapter
import app.pachli.core.network.model.NewPoll
import app.pachli.databinding.ViewPollPreviewBinding
import app.pachli.entity.NewPoll
class PollPreviewView @JvmOverloads constructor(
context: Context?,

View File

@ -20,7 +20,7 @@ import android.content.Context
import android.graphics.Color
import android.util.AttributeSet
import app.pachli.R
import app.pachli.entity.Status
import app.pachli.core.network.model.Status
import com.google.android.material.button.MaterialButton
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial

View File

@ -16,7 +16,9 @@
package app.pachli.components.conversation
import app.pachli.entity.Poll
import app.pachli.core.database.model.ConversationAccountEntity
import app.pachli.core.database.model.ConversationEntity
import app.pachli.core.network.model.Poll
import app.pachli.viewdata.StatusViewData
data class ConversationViewData(

View File

@ -25,6 +25,7 @@ import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import app.pachli.R
import app.pachli.adapter.StatusBaseViewHolder
import app.pachli.core.database.model.ConversationAccountEntity
import app.pachli.interfaces.StatusActionListener
import app.pachli.util.SmartLengthInputFilter
import app.pachli.util.StatusDisplayOptions

View File

@ -40,13 +40,13 @@ import app.pachli.StatusListActivity
import app.pachli.adapter.StatusBaseViewHolder
import app.pachli.appstore.EventHub
import app.pachli.components.account.AccountActivity
import app.pachli.core.preferences.PrefKeys
import app.pachli.core.preferences.SharedPreferencesRepository
import app.pachli.databinding.FragmentTimelineBinding
import app.pachli.fragment.SFragment
import app.pachli.interfaces.ActionButtonActivity
import app.pachli.interfaces.ReselectableFragment
import app.pachli.interfaces.StatusActionListener
import app.pachli.settings.PrefKeys
import app.pachli.util.SharedPreferencesRepository
import app.pachli.util.StatusDisplayOptionsRepository
import app.pachli.util.hide
import app.pachli.util.show

View File

@ -4,11 +4,12 @@ import androidx.paging.ExperimentalPagingApi
import androidx.paging.LoadType
import androidx.paging.PagingState
import androidx.paging.RemoteMediator
import app.pachli.db.AccountManager
import app.pachli.db.ConversationsDao
import app.pachli.di.TransactionProvider
import app.pachli.network.MastodonApi
import app.pachli.util.HttpHeaderLink
import app.pachli.core.accounts.AccountManager
import app.pachli.core.database.dao.ConversationsDao
import app.pachli.core.database.di.TransactionProvider
import app.pachli.core.database.model.ConversationEntity
import app.pachli.core.network.model.HttpHeaderLink
import app.pachli.core.network.retrofit.MastodonApi
import retrofit2.HttpException
@OptIn(ExperimentalPagingApi::class)

View File

@ -23,10 +23,11 @@ import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.cachedIn
import androidx.paging.map
import app.pachli.db.AccountManager
import app.pachli.db.ConversationsDao
import app.pachli.di.TransactionProvider
import app.pachli.network.MastodonApi
import app.pachli.core.accounts.AccountManager
import app.pachli.core.database.dao.ConversationsDao
import app.pachli.core.database.di.TransactionProvider
import app.pachli.core.database.model.ConversationEntity
import app.pachli.core.network.retrofit.MastodonApi
import app.pachli.usecase.TimelineCases
import app.pachli.util.EmptyPagingSource
import at.connyduck.calladapter.networkresult.fold

View File

@ -22,12 +22,12 @@ import android.webkit.MimeTypeMap
import androidx.core.content.FileProvider
import androidx.core.net.toUri
import app.pachli.BuildConfig
import app.pachli.db.DraftAttachment
import app.pachli.db.DraftDao
import app.pachli.db.DraftEntity
import app.pachli.entity.Attachment
import app.pachli.entity.NewPoll
import app.pachli.entity.Status
import app.pachli.core.database.dao.DraftDao
import app.pachli.core.database.model.DraftAttachment
import app.pachli.core.database.model.DraftEntity
import app.pachli.core.network.model.Attachment
import app.pachli.core.network.model.NewPoll
import app.pachli.core.network.model.Status
import app.pachli.util.copyToFile
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers

View File

@ -23,7 +23,7 @@ import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import app.pachli.R
import app.pachli.db.DraftAttachment
import app.pachli.core.database.model.DraftAttachment
import app.pachli.view.MediaPreviewImageView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy

View File

@ -27,10 +27,10 @@ import androidx.recyclerview.widget.LinearLayoutManager
import app.pachli.BaseActivity
import app.pachli.R
import app.pachli.components.compose.ComposeActivity
import app.pachli.core.database.model.DraftEntity
import app.pachli.core.network.parseAsMastodonHtml
import app.pachli.databinding.ActivityDraftsBinding
import app.pachli.db.DraftEntity
import app.pachli.db.DraftsAlert
import app.pachli.util.parseAsMastodonHtml
import app.pachli.util.visible
import at.connyduck.calladapter.networkresult.fold
import com.google.android.material.bottomsheet.BottomSheetBehavior
@ -103,7 +103,7 @@ class DraftsActivity : BaseActivity(), DraftActionListener {
lifecycleScope.launch {
bottomSheet.state = BottomSheetBehavior.STATE_COLLAPSED
viewModel.getStatus(draft.inReplyToId)
viewModel.getStatus(draft.inReplyToId!!)
.fold(
{ status ->
val composeOptions = ComposeActivity.ComposeOptions(

View File

@ -22,8 +22,8 @@ import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import app.pachli.core.database.model.DraftEntity
import app.pachli.databinding.ItemDraftBinding
import app.pachli.db.DraftEntity
import app.pachli.util.BindingHolder
import app.pachli.util.hide
import app.pachli.util.show
@ -80,12 +80,10 @@ class DraftsAdapter(
holder.binding.draftMediaPreview.visible(draft.attachments.isNotEmpty())
(holder.binding.draftMediaPreview.adapter as DraftMediaAdapter).submitList(draft.attachments)
if (draft.poll != null) {
draft.poll?.apply {
holder.binding.draftPoll.show()
holder.binding.draftPoll.setPoll(draft.poll)
} else {
holder.binding.draftPoll.hide()
}
holder.binding.draftPoll.setPoll(this)
} ?: holder.binding.draftPoll.hide()
}
}
}

View File

@ -21,11 +21,11 @@ import androidx.lifecycle.viewModelScope
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.cachedIn
import app.pachli.db.AccountManager
import app.pachli.db.DraftDao
import app.pachli.db.DraftEntity
import app.pachli.entity.Status
import app.pachli.network.MastodonApi
import app.pachli.core.accounts.AccountManager
import app.pachli.core.database.dao.DraftDao
import app.pachli.core.database.model.DraftEntity
import app.pachli.core.network.model.Status
import app.pachli.core.network.retrofit.MastodonApi
import at.connyduck.calladapter.networkresult.NetworkResult
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch

View File

@ -15,11 +15,11 @@ import androidx.lifecycle.lifecycleScope
import app.pachli.BaseActivity
import app.pachli.R
import app.pachli.appstore.EventHub
import app.pachli.core.network.model.Filter
import app.pachli.core.network.model.FilterKeyword
import app.pachli.core.network.retrofit.MastodonApi
import app.pachli.databinding.ActivityEditFilterBinding
import app.pachli.databinding.DialogFilterBinding
import app.pachli.entity.Filter
import app.pachli.entity.FilterKeyword
import app.pachli.network.MastodonApi
import app.pachli.util.viewBinding
import app.pachli.util.visible
import at.connyduck.calladapter.networkresult.fold

View File

@ -5,9 +5,9 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import app.pachli.appstore.EventHub
import app.pachli.appstore.FilterChangedEvent
import app.pachli.entity.Filter
import app.pachli.entity.FilterKeyword
import app.pachli.network.MastodonApi
import app.pachli.core.network.model.Filter
import app.pachli.core.network.model.FilterKeyword
import app.pachli.core.network.retrofit.MastodonApi
import at.connyduck.calladapter.networkresult.fold
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow

View File

@ -7,8 +7,8 @@ import androidx.activity.viewModels
import androidx.lifecycle.lifecycleScope
import app.pachli.BaseActivity
import app.pachli.R
import app.pachli.core.network.model.Filter
import app.pachli.databinding.ActivityFiltersBinding
import app.pachli.entity.Filter
import app.pachli.util.hide
import app.pachli.util.show
import app.pachli.util.viewBinding

View File

@ -4,8 +4,8 @@ import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import app.pachli.R
import app.pachli.core.network.model.Filter
import app.pachli.databinding.ItemRemovableBinding
import app.pachli.entity.Filter
import app.pachli.util.BindingHolder
import app.pachli.util.getRelativeTimeSpanString
@ -26,15 +26,14 @@ class FiltersAdapter(val listener: FiltersListener, val filters: List<Filter>) :
val filter = filters[position]
val context = binding.root.context
binding.textPrimary.text = if (filter.expiresAt == null) {
filter.title
} else {
binding.textPrimary.text = filter.expiresAt?.let {
context.getString(
R.string.filter_expiration_format,
filter.title,
getRelativeTimeSpanString(binding.root.context, filter.expiresAt.time, System.currentTimeMillis()),
getRelativeTimeSpanString(binding.root.context, it.time, System.currentTimeMillis()),
)
}
} ?: filter.title
binding.textSecondary.text = context.getString(
R.string.filter_description_format,
actions.getOrNull(filter.action.ordinal - 1),

View File

@ -1,6 +1,6 @@
package app.pachli.components.filters
import app.pachli.entity.Filter
import app.pachli.core.network.model.Filter
interface FiltersListener {
fun deleteFilter(filter: Filter)

View File

@ -5,8 +5,8 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import app.pachli.appstore.EventHub
import app.pachli.appstore.FilterChangedEvent
import app.pachli.entity.Filter
import app.pachli.network.MastodonApi
import app.pachli.core.network.model.Filter
import app.pachli.core.network.retrofit.MastodonApi
import at.connyduck.calladapter.networkresult.fold
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.lifecycle.HiltViewModel

View File

@ -15,10 +15,10 @@ import androidx.recyclerview.widget.SimpleItemAnimator
import app.pachli.BaseActivity
import app.pachli.R
import app.pachli.components.compose.ComposeAutoCompleteAdapter
import app.pachli.core.network.retrofit.MastodonApi
import app.pachli.core.preferences.PrefKeys
import app.pachli.databinding.ActivityFollowedTagsBinding
import app.pachli.interfaces.HashtagActionListener
import app.pachli.network.MastodonApi
import app.pachli.settings.PrefKeys
import app.pachli.util.hide
import app.pachli.util.show
import app.pachli.util.viewBinding

View File

@ -4,9 +4,9 @@ import androidx.paging.ExperimentalPagingApi
import androidx.paging.LoadType
import androidx.paging.PagingState
import androidx.paging.RemoteMediator
import app.pachli.entity.HashTag
import app.pachli.network.MastodonApi
import app.pachli.util.HttpHeaderLink
import app.pachli.core.network.model.HashTag
import app.pachli.core.network.model.HttpHeaderLink
import app.pachli.core.network.retrofit.MastodonApi
import retrofit2.HttpException
import retrofit2.Response

View File

@ -8,8 +8,8 @@ import androidx.paging.PagingConfig
import androidx.paging.cachedIn
import app.pachli.components.compose.ComposeAutoCompleteAdapter
import app.pachli.components.search.SearchType
import app.pachli.entity.HashTag
import app.pachli.network.MastodonApi
import app.pachli.core.network.model.HashTag
import app.pachli.core.network.retrofit.MastodonApi
import at.connyduck.calladapter.networkresult.fold
import dagger.hilt.android.lifecycle.HiltViewModel
import timber.log.Timber

View File

@ -16,12 +16,12 @@
package app.pachli.components.instanceinfo
import app.pachli.db.AccountManager
import app.pachli.db.EmojisEntity
import app.pachli.db.InstanceDao
import app.pachli.db.InstanceInfoEntity
import app.pachli.entity.Emoji
import app.pachli.network.MastodonApi
import app.pachli.core.accounts.AccountManager
import app.pachli.core.database.dao.InstanceDao
import app.pachli.core.database.model.EmojisEntity
import app.pachli.core.database.model.InstanceInfoEntity
import app.pachli.core.network.model.Emoji
import app.pachli.core.network.retrofit.MastodonApi
import at.connyduck.calladapter.networkresult.fold
import at.connyduck.calladapter.networkresult.getOrElse
import at.connyduck.calladapter.networkresult.onSuccess

View File

@ -9,9 +9,9 @@ import androidx.recyclerview.widget.RecyclerView
import app.pachli.R
import app.pachli.components.instancemute.adapter.DomainMutesAdapter
import app.pachli.components.instancemute.interfaces.InstanceActionListener
import app.pachli.core.network.model.HttpHeaderLink
import app.pachli.core.network.retrofit.MastodonApi
import app.pachli.databinding.FragmentInstanceListBinding
import app.pachli.network.MastodonApi
import app.pachli.util.HttpHeaderLink
import app.pachli.util.hide
import app.pachli.util.show
import app.pachli.util.viewBinding

View File

@ -32,10 +32,10 @@ import app.pachli.BaseActivity
import app.pachli.BuildConfig
import app.pachli.MainActivity
import app.pachli.R
import app.pachli.core.network.model.AccessToken
import app.pachli.core.network.retrofit.MastodonApi
import app.pachli.core.preferences.getNonNullString
import app.pachli.databinding.ActivityLoginBinding
import app.pachli.entity.AccessToken
import app.pachli.network.MastodonApi
import app.pachli.util.getNonNullString
import app.pachli.util.openLinkInCustomTab
import app.pachli.util.rickRoll
import app.pachli.util.shouldRickRoll

View File

@ -18,7 +18,7 @@ package app.pachli.components.login
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import app.pachli.network.MastodonApi
import app.pachli.core.network.retrofit.MastodonApi
import at.connyduck.calladapter.networkresult.fold
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow

View File

@ -19,16 +19,16 @@ package app.pachli.components.notifications
import androidx.recyclerview.widget.RecyclerView
import app.pachli.R
import app.pachli.core.common.string.unicodeWrap
import app.pachli.core.network.model.Notification
import app.pachli.core.network.model.TimelineAccount
import app.pachli.core.network.parseAsMastodonHtml
import app.pachli.databinding.ItemFollowBinding
import app.pachli.entity.Notification
import app.pachli.entity.TimelineAccount
import app.pachli.interfaces.LinkListener
import app.pachli.util.StatusDisplayOptions
import app.pachli.util.emojify
import app.pachli.util.loadAvatar
import app.pachli.util.parseAsMastodonHtml
import app.pachli.util.setClickableText
import app.pachli.util.unicodeWrap
import app.pachli.viewdata.NotificationViewData
class FollowViewHolder(

View File

@ -20,13 +20,13 @@ package app.pachli.components.notifications
import android.app.NotificationManager
import android.content.Context
import androidx.annotation.WorkerThread
import app.pachli.db.AccountEntity
import app.pachli.db.AccountManager
import app.pachli.entity.Marker
import app.pachli.entity.Notification
import app.pachli.network.Links
import app.pachli.network.MastodonApi
import app.pachli.util.isLessThan
import app.pachli.core.accounts.AccountManager
import app.pachli.core.common.string.isLessThan
import app.pachli.core.database.model.AccountEntity
import app.pachli.core.network.model.Links
import app.pachli.core.network.model.Marker
import app.pachli.core.network.model.Notification
import app.pachli.core.network.retrofit.MastodonApi
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.delay
import timber.log.Timber

View File

@ -45,12 +45,12 @@ import app.pachli.MainActivity.Companion.composeIntent
import app.pachli.MainActivity.Companion.openNotificationIntent
import app.pachli.R
import app.pachli.components.compose.ComposeActivity
import app.pachli.db.AccountEntity
import app.pachli.db.AccountManager
import app.pachli.entity.Notification
import app.pachli.core.accounts.AccountManager
import app.pachli.core.common.string.unicodeWrap
import app.pachli.core.database.model.AccountEntity
import app.pachli.core.network.model.Notification
import app.pachli.core.network.parseAsMastodonHtml
import app.pachli.receiver.SendStatusBroadcastReceiver
import app.pachli.util.parseAsMastodonHtml
import app.pachli.util.unicodeWrap
import app.pachli.viewdata.buildDescription
import app.pachli.viewdata.calculatePercent
import app.pachli.worker.NotificationWorker
@ -811,20 +811,20 @@ private fun bodyForType(
Notification.Type.MENTION, Notification.Type.FAVOURITE, Notification.Type.REBLOG, Notification.Type.STATUS -> {
val status = notification.status!!
return if (!TextUtils.isEmpty(status.spoilerText) && !alwaysOpenSpoiler) {
notification.status.spoilerText
status.spoilerText
} else {
notification.status.content.parseAsMastodonHtml().toString()
status.content.parseAsMastodonHtml().toString()
}
}
Notification.Type.POLL -> {
val status = notification.status!!
return if (!TextUtils.isEmpty(status.spoilerText) && !alwaysOpenSpoiler) {
notification.status.spoilerText
status.spoilerText
} else {
val builder = StringBuilder(notification.status.content.parseAsMastodonHtml())
val builder = StringBuilder(status.content.parseAsMastodonHtml())
builder.append('\n')
val poll = notification.status.poll!!
val poll = status.poll!!
val options = poll.options
for (i in options.indices) {
val (title, votesCount) = options[i]
@ -832,7 +832,7 @@ private fun bodyForType(
buildDescription(
title,
calculatePercent(votesCount, poll.votersCount, poll.votesCount),
poll.ownVotes != null && poll.ownVotes.contains(i),
poll.ownVotes != null && poll.ownVotes!!.contains(i),
context,
),
)

View File

@ -26,6 +26,7 @@ import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog
import androidx.core.view.MenuProvider
import androidx.core.view.isVisible
@ -45,10 +46,10 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
import app.pachli.R
import app.pachli.adapter.StatusBaseViewHolder
import app.pachli.components.timeline.TimelineLoadStateAdapter
import app.pachli.core.network.model.Filter
import app.pachli.core.network.model.Notification
import app.pachli.core.network.model.Status
import app.pachli.databinding.FragmentTimelineNotificationsBinding
import app.pachli.entity.Filter
import app.pachli.entity.Notification
import app.pachli.entity.Status
import app.pachli.fragment.SFragment
import app.pachli.interfaces.AccountActionListener
import app.pachli.interfaces.ActionButtonActivity
@ -698,7 +699,7 @@ class FilterDialogFragment(
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val context = requireContext()
val items = Notification.Type.visibleTypes.map { getString(it.uiString) }.toTypedArray()
val items = Notification.Type.visibleTypes.map { getString(it.uiString()) }.toTypedArray()
val checkedItems = Notification.Type.visibleTypes.map {
!activeFilter.contains(it)
}.toBooleanArray()
@ -719,3 +720,18 @@ class FilterDialogFragment(
return builder.create()
}
}
@StringRes
fun Notification.Type.uiString(): Int = when (this) {
Notification.Type.UNKNOWN -> R.string.notification_unknown_name
Notification.Type.MENTION -> R.string.notification_mention_name
Notification.Type.REBLOG -> R.string.notification_boost_name
Notification.Type.FAVOURITE -> R.string.notification_favourite_name
Notification.Type.FOLLOW -> R.string.notification_follow_name
Notification.Type.FOLLOW_REQUEST -> R.string.notification_follow_request_name
Notification.Type.POLL -> R.string.notification_poll_name
Notification.Type.STATUS -> R.string.notification_subscription_name
Notification.Type.SIGN_UP -> R.string.notification_sign_up_name
Notification.Type.UPDATE -> R.string.notification_update_name
Notification.Type.REPORT -> R.string.notification_report_name
}

View File

@ -24,6 +24,10 @@ import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import app.pachli.adapter.FollowRequestViewHolder
import app.pachli.adapter.ReportNotificationViewHolder
import app.pachli.core.common.util.AbsoluteTimeFormatter
import app.pachli.core.network.model.Filter
import app.pachli.core.network.model.Notification
import app.pachli.core.network.model.Status
import app.pachli.databinding.ItemFollowBinding
import app.pachli.databinding.ItemFollowRequestBinding
import app.pachli.databinding.ItemReportNotificationBinding
@ -31,12 +35,8 @@ import app.pachli.databinding.ItemStatusBinding
import app.pachli.databinding.ItemStatusNotificationBinding
import app.pachli.databinding.ItemStatusWrapperBinding
import app.pachli.databinding.SimpleListItem1Binding
import app.pachli.entity.Filter
import app.pachli.entity.Notification
import app.pachli.entity.Status
import app.pachli.interfaces.AccountActionListener
import app.pachli.interfaces.StatusActionListener
import app.pachli.util.AbsoluteTimeFormatter
import app.pachli.util.StatusDisplayOptions
import app.pachli.viewdata.NotificationViewData

View File

@ -20,10 +20,10 @@ package app.pachli.components.notifications
import androidx.paging.PagingSource
import androidx.paging.PagingSource.LoadResult
import androidx.paging.PagingState
import app.pachli.entity.Error
import app.pachli.entity.Notification
import app.pachli.network.Links
import app.pachli.network.MastodonApi
import app.pachli.core.network.model.Error
import app.pachli.core.network.model.Links
import app.pachli.core.network.model.Notification
import app.pachli.core.network.retrofit.MastodonApi
import com.google.gson.Gson
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope

View File

@ -22,9 +22,9 @@ import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import androidx.paging.PagingSource
import app.pachli.di.ApplicationScope
import app.pachli.entity.Notification
import app.pachli.network.MastodonApi
import app.pachli.core.common.di.ApplicationScope
import app.pachli.core.network.model.Notification
import app.pachli.core.network.retrofit.MastodonApi
import com.google.gson.Gson
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.async

View File

@ -33,14 +33,14 @@ import app.pachli.appstore.MuteEvent
import app.pachli.components.timeline.FilterKind
import app.pachli.components.timeline.FiltersRepository
import app.pachli.components.timeline.util.ifExpected
import app.pachli.db.AccountManager
import app.pachli.entity.Filter
import app.pachli.entity.Notification
import app.pachli.entity.Poll
import app.pachli.core.accounts.AccountManager
import app.pachli.core.network.model.Filter
import app.pachli.core.network.model.Notification
import app.pachli.core.network.model.Poll
import app.pachli.core.preferences.PrefKeys
import app.pachli.core.preferences.SharedPreferencesRepository
import app.pachli.network.FilterModel
import app.pachli.settings.PrefKeys
import app.pachli.usecase.TimelineCases
import app.pachli.util.SharedPreferencesRepository
import app.pachli.util.StatusDisplayOptionsRepository
import app.pachli.util.deserialize
import app.pachli.util.serialize

View File

@ -23,12 +23,12 @@ import android.view.View
import androidx.appcompat.app.AlertDialog
import app.pachli.R
import app.pachli.components.login.LoginActivity
import app.pachli.db.AccountEntity
import app.pachli.db.AccountManager
import app.pachli.entity.Notification
import app.pachli.network.MastodonApi
import app.pachli.core.accounts.AccountManager
import app.pachli.core.database.model.AccountEntity
import app.pachli.core.network.model.Notification
import app.pachli.core.network.retrofit.MastodonApi
import app.pachli.core.preferences.SharedPreferencesRepository
import app.pachli.util.CryptoUtil
import app.pachli.util.SharedPreferencesRepository
import at.connyduck.calladapter.networkresult.onFailure
import at.connyduck.calladapter.networkresult.onSuccess
import com.google.android.material.snackbar.Snackbar

View File

@ -34,19 +34,19 @@ import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import app.pachli.R
import app.pachli.adapter.StatusBaseViewHolder
import app.pachli.core.common.string.unicodeWrap
import app.pachli.core.common.util.AbsoluteTimeFormatter
import app.pachli.core.network.model.Emoji
import app.pachli.core.network.model.Notification
import app.pachli.databinding.ItemStatusNotificationBinding
import app.pachli.entity.Emoji
import app.pachli.entity.Notification
import app.pachli.interfaces.LinkListener
import app.pachli.interfaces.StatusActionListener
import app.pachli.util.AbsoluteTimeFormatter
import app.pachli.util.SmartLengthInputFilter
import app.pachli.util.StatusDisplayOptions
import app.pachli.util.emojify
import app.pachli.util.getRelativeTimeSpanString
import app.pachli.util.loadAvatar
import app.pachli.util.setClickableText
import app.pachli.util.unicodeWrap
import app.pachli.viewdata.NotificationViewData
import app.pachli.viewdata.StatusViewData
import at.connyduck.sparkbutton.helpers.Utils

View File

@ -19,9 +19,9 @@ package app.pachli.components.notifications
import app.pachli.adapter.FilterableStatusViewHolder
import app.pachli.adapter.StatusViewHolder
import app.pachli.core.network.model.Notification
import app.pachli.databinding.ItemStatusBinding
import app.pachli.databinding.ItemStatusWrapperBinding
import app.pachli.entity.Notification
import app.pachli.interfaces.StatusActionListener
import app.pachli.util.StatusDisplayOptions
import app.pachli.viewdata.NotificationViewData

View File

@ -32,12 +32,12 @@ import app.pachli.components.followedtags.FollowedTagsActivity
import app.pachli.components.instancemute.InstanceListActivity
import app.pachli.components.login.LoginActivity
import app.pachli.components.notifications.currentAccountNeedsMigration
import app.pachli.db.AccountManager
import app.pachli.entity.Account
import app.pachli.entity.Status
import app.pachli.network.MastodonApi
import app.pachli.core.accounts.AccountManager
import app.pachli.core.network.model.Account
import app.pachli.core.network.model.Status
import app.pachli.core.network.retrofit.MastodonApi
import app.pachli.core.preferences.PrefKeys
import app.pachli.settings.AccountPreferenceDataStore
import app.pachli.settings.PrefKeys
import app.pachli.settings.listPreference
import app.pachli.settings.makePreferenceScreen
import app.pachli.settings.preference

View File

@ -22,9 +22,9 @@ import app.pachli.R
import app.pachli.components.notifications.disablePullNotifications
import app.pachli.components.notifications.enablePullNotifications
import app.pachli.components.notifications.notificationsAreEnabled
import app.pachli.db.AccountEntity
import app.pachli.db.AccountManager
import app.pachli.settings.PrefKeys
import app.pachli.core.accounts.AccountManager
import app.pachli.core.database.model.AccountEntity
import app.pachli.core.preferences.PrefKeys
import app.pachli.settings.makePreferenceScreen
import app.pachli.settings.preferenceCategory
import app.pachli.settings.switchPreference

View File

@ -29,11 +29,11 @@ import app.pachli.BaseActivity
import app.pachli.MainActivity
import app.pachli.R
import app.pachli.appstore.EventHub
import app.pachli.core.preferences.PrefKeys
import app.pachli.core.preferences.PrefKeys.APP_THEME
import app.pachli.core.preferences.getNonNullString
import app.pachli.databinding.ActivityPreferencesBinding
import app.pachli.settings.PrefKeys
import app.pachli.settings.PrefKeys.APP_THEME
import app.pachli.util.APP_THEME_DEFAULT
import app.pachli.util.getNonNullString
import app.pachli.util.setAppNightMode
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.filterNotNull

View File

@ -20,10 +20,10 @@ import android.os.Bundle
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import app.pachli.R
import app.pachli.db.AccountManager
import app.pachli.entity.Notification
import app.pachli.settings.AppTheme
import app.pachli.settings.PrefKeys
import app.pachli.core.accounts.AccountManager
import app.pachli.core.network.model.Notification
import app.pachli.core.preferences.AppTheme
import app.pachli.core.preferences.PrefKeys
import app.pachli.settings.emojiPreference
import app.pachli.settings.listPreference
import app.pachli.settings.makePreferenceScreen

View File

@ -20,15 +20,15 @@ import android.os.Bundle
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import app.pachli.R
import app.pachli.settings.PrefKeys
import app.pachli.settings.ProxyConfiguration
import app.pachli.settings.ProxyConfiguration.Companion.MAX_PROXY_PORT
import app.pachli.settings.ProxyConfiguration.Companion.MIN_PROXY_PORT
import app.pachli.core.preferences.PrefKeys
import app.pachli.core.preferences.ProxyConfiguration
import app.pachli.core.preferences.ProxyConfiguration.Companion.MAX_PROXY_PORT
import app.pachli.core.preferences.ProxyConfiguration.Companion.MIN_PROXY_PORT
import app.pachli.core.preferences.getNonNullString
import app.pachli.settings.makePreferenceScreen
import app.pachli.settings.preferenceCategory
import app.pachli.settings.switchPreference
import app.pachli.settings.validatedEditTextPreference
import app.pachli.util.getNonNullString
import kotlin.system.exitProcess
class ProxyPreferencesFragment : PreferenceFragmentCompat() {

View File

@ -19,7 +19,7 @@ package app.pachli.components.preference
import android.os.Bundle
import androidx.preference.PreferenceFragmentCompat
import app.pachli.R
import app.pachli.settings.PrefKeys
import app.pachli.core.preferences.PrefKeys
import app.pachli.settings.makePreferenceScreen
import app.pachli.settings.preferenceCategory
import app.pachli.settings.switchPreference

View File

@ -29,9 +29,9 @@ import app.pachli.appstore.EventHub
import app.pachli.appstore.MuteEvent
import app.pachli.components.report.adapter.StatusesPagingSource
import app.pachli.components.report.model.StatusViewState
import app.pachli.entity.Relationship
import app.pachli.entity.Status
import app.pachli.network.MastodonApi
import app.pachli.core.network.model.Relationship
import app.pachli.core.network.model.Status
import app.pachli.core.network.retrofit.MastodonApi
import app.pachli.util.Error
import app.pachli.util.Loading
import app.pachli.util.Resource

View File

@ -17,7 +17,7 @@
package app.pachli.components.report.adapter
import android.view.View
import app.pachli.entity.Status
import app.pachli.core.network.model.Status
import app.pachli.interfaces.LinkListener
interface AdapterHandler : LinkListener {

View File

@ -22,12 +22,12 @@ import android.view.View
import androidx.recyclerview.widget.RecyclerView
import app.pachli.R
import app.pachli.components.report.model.StatusViewState
import app.pachli.core.common.util.AbsoluteTimeFormatter
import app.pachli.core.network.model.Emoji
import app.pachli.core.network.model.HashTag
import app.pachli.core.network.model.Status
import app.pachli.databinding.ItemReportStatusBinding
import app.pachli.entity.Emoji
import app.pachli.entity.HashTag
import app.pachli.entity.Status
import app.pachli.interfaces.LinkListener
import app.pachli.util.AbsoluteTimeFormatter
import app.pachli.util.StatusDisplayOptions
import app.pachli.util.StatusViewHelper
import app.pachli.util.StatusViewHelper.Companion.COLLAPSE_INPUT_FILTER

View File

@ -18,8 +18,8 @@ package app.pachli.components.report.adapter
import androidx.paging.PagingSource
import androidx.paging.PagingState
import app.pachli.entity.Status
import app.pachli.network.MastodonApi
import app.pachli.core.network.model.Status
import app.pachli.core.network.retrofit.MastodonApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.withContext

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