diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5cda18337..5068c371a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,6 +32,12 @@ jobs: with: name: build-reports path: ./app/build/reports + + - name: Upload build mapping + uses: actions/upload-artifact@v2 + with: + name: build-mapping + path: ./app/build/outputs/mapping unit-test: runs-on: ubuntu-latest diff --git a/README.md b/README.md index 03f8c0ded..5eec16699 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ For 1.6.0, we're planning to build an experimatal desktop version, this is a big - Experimatal desktop version. - Mute and block support. -- Optimizing video play for timelien. +- Optimizing video play for timeline. - Bug fixes. - UI/UX tweaking. - Stability. diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ca31a7f4a..977f05f53 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -17,8 +17,8 @@ buildscript { if (enableGoogleVariant) { // START Non-FOSS component - classpath("com.google.gms:google-services:4.3.5") - classpath("com.google.firebase:firebase-crashlytics-gradle:2.5.2") + classpath("com.google.gms:google-services:4.3.10") + classpath("com.google.firebase:firebase-crashlytics-gradle:2.7.1") // END Non-FOSS component } } @@ -113,7 +113,8 @@ android { if (hasSigningProps) { signingConfig = signingConfigs.getByName("twidere") } - isMinifyEnabled = false + isMinifyEnabled = true + isShrinkResources = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" @@ -174,10 +175,6 @@ protobuf { } } -// TODO: workaround for https://github.com/google/ksp/issues/518 -evaluationDependsOn(":assistedProcessor") -evaluationDependsOn(":routeProcessor") - dependencies { android() kotlinSerialization() @@ -197,7 +194,7 @@ dependencies { if (enableGoogleVariant) { // START Non-FOSS component val googleImplementation by configurations - googleImplementation(platform("com.google.firebase:firebase-bom:26.1.0")) + googleImplementation(platform("com.google.firebase:firebase-bom:28.4.0")) googleImplementation("com.google.firebase:firebase-analytics-ktx") googleImplementation("com.google.firebase:firebase-crashlytics-ktx") googleImplementation("com.google.android.play:core-ktx:1.8.1") diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 481bb4348..08f49e699 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -18,4 +18,83 @@ # If you keep the line number information, uncomment this to # hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file +#-renamesourcefileattribute SourceFile + +-keepattributes *Annotation*, InnerClasses +-dontnote kotlinx.serialization.AnnotationsKt # core serialization annotations + +# kotlinx-serialization-json specific. Add this if you have java.lang.NoClassDefFoundError kotlinx.serialization.json.JsonObjectSerializer +-keepclassmembers class kotlinx.serialization.json.** { + *** Companion; +} +-keepclasseswithmembers class kotlinx.serialization.json.** { + kotlinx.serialization.KSerializer serializer(...); +} + +-keep,includedescriptorclasses class com.twidere.services.**$$serializer { *; } +-keepclassmembers class com.twidere.services.** { + *** Companion; +} +-keepclasseswithmembers class com.twidere.services.** { + kotlinx.serialization.KSerializer serializer(...); +} + +-keep,includedescriptorclasses class com.twidere.twiderex.**$$serializer { *; } +-keepclassmembers class com.twidere.twiderex.** { + *** Companion; +} +-keepclasseswithmembers class com.twidere.twiderex.** { + kotlinx.serialization.KSerializer serializer(...); +} + +-keepclassmembers class * extends androidx.datastore.preferences.protobuf.GeneratedMessageLite { + ; +} + + +# ServiceLoader support +-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {} +-keepnames class kotlinx.coroutines.CoroutineExceptionHandler {} + +# Most of volatile fields are updated with AFU and should not be mangled +-keepclassmembers class kotlinx.coroutines.** { + volatile ; +} + +# Same story for the standard library's SafeContinuation that also uses AtomicReferenceFieldUpdater +-keepclassmembers class kotlin.coroutines.SafeContinuation { + volatile ; +} + +# These classes are only required by kotlinx.coroutines.debug.AgentPremain, which is only loaded when +# kotlinx-coroutines-core is used as a Java agent, so these are not needed in contexts where ProGuard is used. +-dontwarn java.lang.instrument.ClassFileTransformer +-dontwarn sun.misc.SignalHandler +-dontwarn java.lang.instrument.Instrumentation +-dontwarn sun.misc.Signal + +# JSR 305 annotations are for embedding nullability information. +-dontwarn javax.annotation.** + +# A resource is loaded with a relative path so the package of this class must be preserved. +-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase + +# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java. +-dontwarn org.codehaus.mojo.animal_sniffer.* + +# OkHttp platform used only on JVM and when Conscrypt dependency is available. +-dontwarn okhttp3.internal.platform.ConscryptPlatform +-dontwarn org.conscrypt.ConscryptHostnameVerifier + +# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java. +-dontwarn org.codehaus.mojo.animal_sniffer.* + +-keeppackagenames org.jsoup.nodes + +-keep class * extends com.google.protobuf.GeneratedMessageLite { *; } + +-keep class com.twidere.services.nitter.model.** + +-keepclassmembers class ** { + @moe.tlaster.hson.annotations.HtmlSerializable public *; +} diff --git a/app/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt b/app/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt index c2f51c77e..14f37ea7a 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/TwidereXActivity.kt @@ -22,9 +22,6 @@ package com.twidere.twiderex import android.content.Intent import android.net.ConnectivityManager -import android.net.Network -import android.net.NetworkCapabilities -import android.net.NetworkRequest import android.net.Uri import android.os.Bundle import android.view.WindowManager @@ -55,7 +52,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.core.net.ConnectivityManagerCompat import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsControllerCompat import androidx.lifecycle.lifecycleScope @@ -78,6 +74,7 @@ import com.twidere.twiderex.ui.LocalIsActiveNetworkMetered import com.twidere.twiderex.ui.LocalWindow import com.twidere.twiderex.ui.LocalWindowInsetsController import com.twidere.twiderex.utils.CustomTabSignInChannel +import com.twidere.twiderex.utils.IsActiveNetworkMeteredLiveData import com.twidere.twiderex.utils.LocalPlatformResolver import com.twidere.twiderex.utils.PlatformResolver import com.twidere.twiderex.viewmodel.ActiveAccountViewModel @@ -92,19 +89,6 @@ class TwidereXActivity : ComponentActivity() { private val navController by lazy { NavController() } - private val isActiveNetworkMetered = MutableStateFlow(false) - private val networkCallback by lazy { - object : ConnectivityManager.NetworkCallback() { - override fun onCapabilitiesChanged( - network: Network, - networkCapabilities: NetworkCapabilities - ) { - isActiveNetworkMetered.value = ConnectivityManagerCompat.isActiveNetworkMetered( - connectivityManager - ) - } - } - } @Inject lateinit var viewModelHolder: TwidereXActivityAssistedViewModelHolder @@ -124,15 +108,19 @@ class TwidereXActivity : ComponentActivity() { @Inject lateinit var platformResolver: PlatformResolver + private val isActiveNetworkMetered = MutableStateFlow(false) + private val isActiveNetworkMeteredLiveData by lazy { + IsActiveNetworkMeteredLiveData(connectivityManager = connectivityManager) + } + @OptIn(ExperimentalAnimationApi::class) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - isActiveNetworkMetered.value = ConnectivityManagerCompat.isActiveNetworkMetered( - connectivityManager - ) - WindowCompat.setDecorFitsSystemWindows(window, false) window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) + isActiveNetworkMeteredLiveData.observe(this) { + isActiveNetworkMetered.value = it + } setContent { var showSplash by rememberSaveable { mutableStateOf(true) } LaunchedEffect(Unit) { @@ -230,19 +218,6 @@ class TwidereXActivity : ComponentActivity() { } } - override fun onStart() { - super.onStart() - connectivityManager.registerNetworkCallback( - NetworkRequest.Builder().build(), - networkCallback, - ) - } - - override fun onStop() { - super.onStop() - connectivityManager.unregisterNetworkCallback(networkCallback) - } - override fun onResume() { super.onResume() lifecycleScope.launchWhenResumed { diff --git a/app/src/main/kotlin/com/twidere/twiderex/http/TwidereServiceFactory.kt b/app/src/main/kotlin/com/twidere/twiderex/http/TwidereServiceFactory.kt index dcebf8e12..667624511 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/http/TwidereServiceFactory.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/http/TwidereServiceFactory.kt @@ -25,6 +25,7 @@ import com.twidere.services.http.config.HttpConfigClientFactory import com.twidere.services.mastodon.MastodonService import com.twidere.services.microblog.MicroBlogService import com.twidere.services.twitter.TwitterService +import com.twidere.twiderex.model.MicroBlogKey import com.twidere.twiderex.model.cred.Credentials import com.twidere.twiderex.model.cred.OAuth2Credentials import com.twidere.twiderex.model.cred.OAuthCredentials @@ -39,7 +40,7 @@ class TwidereServiceFactory(private val configProvider: TwidereHttpConfigProvide instance = TwidereServiceFactory(configProvider) } - fun createApiService(type: PlatformType, credentials: Credentials, host: String = ""): MicroBlogService { + fun createApiService(type: PlatformType, credentials: Credentials, accountKey: MicroBlogKey): MicroBlogService { return instance?.let { when (type) { PlatformType.Twitter -> { @@ -51,7 +52,8 @@ class TwidereServiceFactory(private val configProvider: TwidereHttpConfigProvide consumer_secret = it.consumer_secret, access_token = it.access_token, access_token_secret = it.access_token_secret, - httpClientFactory = createHttpClientFactory() + httpClientFactory = createHttpClientFactory(), + accountId = accountKey.id ) } } @@ -62,7 +64,7 @@ class TwidereServiceFactory(private val configProvider: TwidereHttpConfigProvide it as OAuth2Credentials }.let { MastodonService( - host, + accountKey.host, it.access_token, httpClientFactory = createHttpClientFactory() ) diff --git a/app/src/main/kotlin/com/twidere/twiderex/model/AccountDetails.kt b/app/src/main/kotlin/com/twidere/twiderex/model/AccountDetails.kt index e2f1c28b4..d1877aa89 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/model/AccountDetails.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/model/AccountDetails.kt @@ -58,7 +58,7 @@ data class AccountDetails( TwidereServiceFactory.createApiService( type = type, credentials = credentials, - host = accountKey.host + accountKey = accountKey ) } diff --git a/app/src/main/kotlin/com/twidere/twiderex/utils/IsActiveNetworkMeteredLiveData.kt b/app/src/main/kotlin/com/twidere/twiderex/utils/IsActiveNetworkMeteredLiveData.kt new file mode 100644 index 000000000..88b0e7fec --- /dev/null +++ b/app/src/main/kotlin/com/twidere/twiderex/utils/IsActiveNetworkMeteredLiveData.kt @@ -0,0 +1,66 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X 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. + * + * Twidere X 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 Twidere X. If not, see . + */ +package com.twidere.twiderex.utils + +import android.net.ConnectivityManager +import android.net.Network +import android.net.NetworkCapabilities +import android.net.NetworkRequest +import androidx.core.net.ConnectivityManagerCompat +import androidx.lifecycle.LiveData + +class IsActiveNetworkMeteredLiveData( + private val connectivityManager: ConnectivityManager, +) : LiveData() { + private val request = NetworkRequest.Builder().build() + private val networkCallback by lazy { + object : ConnectivityManager.NetworkCallback() { + override fun onCapabilitiesChanged( + network: Network, + networkCapabilities: NetworkCapabilities + ) { + updateValue() + } + } + } + + private fun updateValue() { + postValue( + ConnectivityManagerCompat.isActiveNetworkMetered( + connectivityManager + ) + ) + } + + override fun onActive() { + super.onActive() + updateValue() + connectivityManager.registerNetworkCallback( + request, + networkCallback, + ) + } + + override fun onInactive() { + super.onInactive() + connectivityManager.unregisterNetworkCallback(networkCallback) + } +} diff --git a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt b/app/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt index 9b061b024..6455961b0 100644 --- a/app/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt +++ b/app/src/main/kotlin/com/twidere/twiderex/viewmodel/twitter/TwitterSignInViewModel.kt @@ -109,6 +109,7 @@ class TwitterSignInViewModel @AssistedInject constructor( access_token = accessToken.oauth_token, access_token_secret = accessToken.oauth_token_secret ), + accountKey = MicroBlogKey.Empty ) as TwitterService ).verifyCredentials() if (user != null) { diff --git a/app/src/main/res-localized/values-el-rGR/strings.xml b/app/src/main/res-localized/values-el-rGR/strings.xml new file mode 100644 index 000000000..468886d99 --- /dev/null +++ b/app/src/main/res-localized/values-el-rGR/strings.xml @@ -0,0 +1,54 @@ + + %s αναιρέθηκε η σίγαση + Κατάργηση ακολούθησης χρήστη %s; + Τα πολυμέσα θα κοινοποιηθούν μετά την ολοκλήρωση της λήψης + Αποτυχία κατάργησης Ακολούθησης + Παρακαλούμε προσπαθήστε ξανά + Tweet Στάλθηκε + Πάρα Πολλά Αιτήματα + Ακολούθηση Επιτυχής + Αποτυχία ακολούθησης + Παρακαλούμε προσπαθήστε ξανά + Κανόνες Twitter + Ο Λογαριασμός έχει Ανασταλεί + Το Twitter αναστέλλει λογαριασμούς που παραβιάζουν το %s + Αποτυχία αποθήκευσης των πολυμέσων + Παρακαλούμε προσπαθήστε ξανά + %s έχει αποκλειστεί + Ακύρωση αιτήματος ακολούθησης για %s; + Παρακαλούμε προσπαθήστε ξανά + Αποτυχία Αποθήκευσης Φωτογραφίας + Παρακαλούμε προσπαθήστε ξανά + Δεν Βρέθηκαν Tweets + Αποθήκευση πολυμέσων + %s έχει γίνει σίγαση + Η Άδεια Απορρίφθηκε + Λυπούμαστε, δεν είστε εξουσιοδοτημένοι + Αποτυχία αναίρεσης σίγασης %s + Παρακαλούμε προσπαθήστε ξανά + Υπέρβαση Όριου Αξιολόγησης + Φτάσατε στο όριο χρήσης του Twitter API + Αποτυχία φόρτωσης + Παρακαλούμε προσπαθήστε ξανά + Η Άδεια Απορρίφθηκε + Έχετε αποκλειστεί από την ακολούθηση αυτού του λογαριασμού κατόπιν αιτήματος του χρήστη + Τα πολυμέσα αποθηκεύτηκαν + Το Tweet Διαγράφηκε + Το Αίτημα Ακολούθησης Στάλθηκε + Αποτυχία σίγασης %s + Παρακαλούμε προσπαθήστε ξανά + Γίνεται αποστολή tweet + Η Διαγραφή του Tweet Απέτυχε + Παρακαλούμε προσπαθήστε ξανά + Η Φωτογραφία Αποθηκεύτηκε + Προσωρινά Κλειδωμένος Λογαριασμός + Άνοιγμα του Twitter για ξεκλείδωμα + Αποτυχία αποκλεισμού %s + Παρακαλούμε προσπαθήστε ξανά + Αποτυχία Tweet + Παρακαλούμε προσπαθήστε ξανά + Κατάργηση της Ακολούθησης Επιτυχής + Παρακαλούμε προσπαθήστε ξανά + Παρακαλούμε προσπαθήστε ξανά + Η Άδεια Απορρίφθηκε + \ No newline at end of file diff --git a/app/src/main/res-localized/values-fr-rFR/strings.xml b/app/src/main/res-localized/values-fr-rFR/strings.xml index 65a3923e2..253c0a4d2 100644 --- a/app/src/main/res-localized/values-fr-rFR/strings.xml +++ b/app/src/main/res-localized/values-fr-rFR/strings.xml @@ -1,33 +1,77 @@ + Ne plus suivre l\'utilisateur %s? + Le média sera partagé une fois le téléchargement terminé + Échec de l\'annulation de l\'abonnement Merci de réessayer Tweet envoyé + Échec de connexion + L’adresse du serveur est incorrecte. + Trop de Requêtes + Abonnement réussi + Échec de l\'abonnement Merci de réessayer + %s a été signalé pour spam Règles de Twitter + Compte suspendu + Twitter suspend les comptes qui violent les %s + Impossible d\'enregistrer le média Merci de réessayer %s a été bloqué·e + Annuler la demande de suivi pour %s? + Échec du signalement de %s Merci de réessayer + %s a été bloqué et signalé pour spam + Échec de l\'enregistrement de la photo Merci de réessayer Aucun tweet trouvé + Enregistrement du média + Échec de connexion + Délai de connexion expiré. %s a été débloqué·e + Permission refusée + Désolé, vous n\'êtes pas autorisé + Envoi du message + Échec de l\'envoi du message Merci de réessayer + Limite d\'utilisation de l\'API Twitter atteinte + Échec du chargement Merci de réessayer + Permission refusée + Vous avez été empêché de suivre ce compte à la demande de l\'utilisateur + Média enregistré + Tweet supprimé + Demande de suivi envoyée Merci de réessayer Envoi du tweet + La suppression du Tweet a échoué Merci de réessayer + Photo Enregistrée Compte verrouillé temporairement Ouvrez Twitter pour le déverrouiller Échec du blocage de %s Merci de réessayer + Echec de l\'envoi du tweet Merci de réessayer + Désabonnement réussi + Impossible de signaler et de bloquer %s Merci de réessayer Merci de réessayer Messages Messages directs Interactions + %s a boosté votre pouet + %s a demandé à vous suivre + Votre sondage est terminé + %s vous a mentionné + Nouveau message %s vous a envoyé un message + Un sondage auquel vous avez participé est maintenant terminé + %s a aimé votre pouet + %s vous suit + %s vient de poster Bloquer %s abonné·e - abonné·e·s + abonnés %s ne vous suit pas Vous suit Mettre en sourdine %s @@ -38,12 +82,15 @@ En attente Signaler Mettre en sourdine + Rétablir le son Bloquer S’abonner Se désabonner - Abonné·e·s + Abonnés + Listé Abonnements Charger plus + Galerie photo Ajouter Annuler Aperçu @@ -69,15 +116,22 @@ Partager le lien Supprimer le tweet Citation + Retweeter Copier le texte Voter Copier le lien + %s citation + %s citations + %s a retweeté + %s a aimé %s membre %s membres %s photo %s photos %s tweet %s tweets + %s réponse + %s réponses %s liste %s listes Plus @@ -92,6 +146,7 @@ Fait Emplacement Média + Retweeter Réponse Taille du texte Sauvegarder @@ -124,9 +179,10 @@ Supprimer le compte Comptes Masquer + Rechercher des tweets ou des utilisateurs Média Tweets - Utilisateur·rice·s + Utilisateurs Mot clé Recherche Afficher plus @@ -156,7 +212,9 @@ Bonjour !\nConnectez-vous pour commencer. Se connecter avec Twitter Se connecter avec une clé Twitter personnalisée + L\'accès à l’API v2 Twitter est requis. Authentification + J\'aimes Agencement Afficher la notification Comptes @@ -216,6 +274,8 @@ Exclure les réponses Masquer la réponse Moi + Permission refusée + Vous avez été empêché de consulter le profil de cet utilisateur. Gestion des comptes Se connecter Brouillons @@ -223,7 +283,8 @@ Modifier le brouillon Chercher des utilisateur·rice·s Signet - Abonné·e·s + Abonnés + Listé Enregistrer le brouillon Enregistrer le brouillon ? Répondre à … @@ -252,13 +313,18 @@ Mentions Abonnements Fil d’actualité + [Photo] Rechercher des personnes + Trouver des gens + L\'envoi de message a échoué + Copier le texte du message Messages 1 citation %d citations 1 retweet %d retweets 1 j’aime + %d J\'aimes Pouet Tweet 1 réponse diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bff316365..940f4ccde 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -362,10 +362,10 @@ Search people Find people Send message failed - The Current account does not support direct messages Copy message text Delete message for you Messages + The Current account does not support direct messages Search hashtag 1 Quote %d Quotes diff --git a/buildSrc/src/main/kotlin/Package.kt b/buildSrc/src/main/kotlin/Package.kt index 45471ce4f..9558bebcb 100644 --- a/buildSrc/src/main/kotlin/Package.kt +++ b/buildSrc/src/main/kotlin/Package.kt @@ -2,6 +2,6 @@ object Package { const val group = "com.twidere" const val name = "TwidereX" const val id = "$group.twiderex" - const val versionName = "1.5.0" - const val versionCode = 53 + const val versionName = "1.5.1" + const val versionCode = 55 } diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 848815883..be551e13f 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -12,16 +12,16 @@ object Versions { val java = JavaVersion.VERSION_11 } - const val ksp = "${Kotlin.lang}-1.0.0-beta06" - const val agp = "7.0.0" + const val ksp = "${Kotlin.lang}-1.0.0-beta07" + const val agp = "7.0.1" const val spotless = "5.14.2" const val ktlint = "0.41.0" const val hilt = "2.38.1" const val okhttp = "4.9.1" const val retrofit2 = "2.9.0" const val hson = "0.1.4" - const val compose = "1.1.0-alpha01" - const val constraintLayout = "1.0.0-beta01" + const val compose = "1.1.0-alpha02" + const val constraintLayout = "1.0.0-beta02" const val paging = "3.1.0-alpha03" const val paging_compose = "1.0.0-alpha12" const val activity = "1.3.1" @@ -36,10 +36,10 @@ object Versions { const val swiper = "0.6.0" const val nestedScrollView = "0.7.0" const val startup = "1.1.0" - const val coil = "1.3.0" - const val accompanist = "0.15.0" - const val androidx_exifinterface = "1.3.2" - const val exoplayer = "2.14.2" + const val coil = "1.3.2" + const val accompanist = "0.17.0" + const val androidx_exifinterface = "1.3.3" + const val exoplayer = "2.15.0" const val browser = "1.3.0" const val protobuf = "3.17.3" const val androidx_test = "1.4.0" diff --git a/localization b/localization index 32a49017b..9490971a1 160000 --- a/localization +++ b/localization @@ -1 +1 @@ -Subproject commit 32a49017b010fe454b6829046c6394158750b84e +Subproject commit 9490971a1e0429701b15181dd3a1aa890a29a912 diff --git a/services/src/main/java/com/twidere/services/twitter/TwitterService.kt b/services/src/main/java/com/twidere/services/twitter/TwitterService.kt index 744a541d4..31aaab8b3 100644 --- a/services/src/main/java/com/twidere/services/twitter/TwitterService.kt +++ b/services/src/main/java/com/twidere/services/twitter/TwitterService.kt @@ -62,6 +62,7 @@ import com.twidere.services.twitter.model.fields.PlaceFields import com.twidere.services.twitter.model.fields.PollFields import com.twidere.services.twitter.model.fields.TweetFields import com.twidere.services.twitter.model.fields.UserFields +import com.twidere.services.twitter.model.request.TwitterReactionRequestBody import com.twidere.services.utils.Base64 import com.twidere.services.utils.await import com.twidere.services.utils.copyToInLength @@ -77,7 +78,8 @@ class TwitterService( private val consumer_secret: String, private val access_token: String, private val access_token_secret: String, - private val httpClientFactory: HttpClientFactory + private val httpClientFactory: HttpClientFactory, + private val accountId: String = "" ) : MicroBlogService, TimelineService, LookupService, @@ -377,13 +379,49 @@ class TwitterService( resources.unfollow(user_id) } - override suspend fun like(id: String) = resources.like(id) + override suspend fun like(id: String): IStatus { + return try { + resources.likeV2(userId = accountId, body = TwitterReactionRequestBody(tweet_id = id)) + .run { + lookupStatus(id) + } + } catch (e: TwitterApiExceptionV2) { + resources.like(id) + } + } - override suspend fun unlike(id: String) = resources.unlike(id) + override suspend fun unlike(id: String): IStatus { + return try { + resources.unlikeV2(userId = accountId, tweetId = id) + .run { + lookupStatus(id) + } + } catch (e: TwitterApiExceptionV2) { + resources.unlike(id) + } + } - override suspend fun retweet(id: String) = resources.retweet(id) + override suspend fun retweet(id: String): IStatus { + return try { + resources.retweetV2(userId = accountId, body = TwitterReactionRequestBody(tweet_id = id)) + .run { + lookupStatus(id) + } + } catch (e: TwitterApiExceptionV2) { + resources.retweet(id) + } + } - override suspend fun unRetweet(id: String) = resources.unretweet(id) + override suspend fun unRetweet(id: String): IStatus { + return try { + resources.unRetweetV2(userId = accountId, tweetId = id) + .run { + lookupStatus(id) + } + } catch (e: TwitterApiExceptionV2) { + resources.unretweet(id) + } + } override suspend fun delete(id: String) = resources.destroy(id) diff --git a/services/src/main/java/com/twidere/services/twitter/api/StatusResources.kt b/services/src/main/java/com/twidere/services/twitter/api/StatusResources.kt index a861c5ba2..0a1629d02 100644 --- a/services/src/main/java/com/twidere/services/twitter/api/StatusResources.kt +++ b/services/src/main/java/com/twidere/services/twitter/api/StatusResources.kt @@ -21,6 +21,11 @@ package com.twidere.services.twitter.api import com.twidere.services.twitter.model.Status +import com.twidere.services.twitter.model.StatusReactionsV2 +import com.twidere.services.twitter.model.TwitterResponseV2 +import com.twidere.services.twitter.model.request.TwitterReactionRequestBody +import retrofit2.http.Body +import retrofit2.http.DELETE import retrofit2.http.POST import retrofit2.http.Path import retrofit2.http.Query @@ -55,4 +60,16 @@ interface StatusResources { @POST("/1.1/statuses/destroy/{id}.json") suspend fun destroy(@Path(value = "id") id: String): Status + + @POST("/2/users/{userId}/retweets") + suspend fun retweetV2(@Path(value = "userId") userId: String, @Body body: TwitterReactionRequestBody): TwitterResponseV2 + + @DELETE("/2/users/{userId}/retweets/{tweetId}") + suspend fun unRetweetV2(@Path(value = "userId") userId: String, @Path(value = "tweetId") tweetId: String): TwitterResponseV2 + + @POST("/2/users/{userId}/likes") + suspend fun likeV2(@Path(value = "userId") userId: String, @Body body: TwitterReactionRequestBody): TwitterResponseV2 + + @DELETE("/2/users/{userId}/likes/{tweetId}") + suspend fun unlikeV2(@Path(value = "userId") userId: String, @Path(value = "tweetId") tweetId: String): TwitterResponseV2 } diff --git a/services/src/main/java/com/twidere/services/twitter/model/StatusReactionsV2.kt b/services/src/main/java/com/twidere/services/twitter/model/StatusReactionsV2.kt new file mode 100644 index 000000000..aa586a9f8 --- /dev/null +++ b/services/src/main/java/com/twidere/services/twitter/model/StatusReactionsV2.kt @@ -0,0 +1,29 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X 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. + * + * Twidere X 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 Twidere X. If not, see . + */ +package com.twidere.services.twitter.model + +import kotlinx.serialization.Serializable + +@Serializable +data class StatusReactionsV2( + val retweeted: Boolean? = null, + val liked: Boolean? = null +) diff --git a/services/src/main/java/com/twidere/services/twitter/model/request/TwitterReactionRequestBody.kt b/services/src/main/java/com/twidere/services/twitter/model/request/TwitterReactionRequestBody.kt new file mode 100644 index 000000000..cd078163e --- /dev/null +++ b/services/src/main/java/com/twidere/services/twitter/model/request/TwitterReactionRequestBody.kt @@ -0,0 +1,28 @@ +/* + * Twidere X + * + * Copyright (C) 2020-2021 Tlaster + * + * This file is part of Twidere X. + * + * Twidere X 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. + * + * Twidere X 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 Twidere X. If not, see . + */ +package com.twidere.services.twitter.model.request + +import kotlinx.serialization.Serializable + +@Serializable +data class TwitterReactionRequestBody( + val tweet_id: String +)