From ba78f6cb36623063f4403c96b5d01e0de36e411b Mon Sep 17 00:00:00 2001
From: "Mr. Traduttore" <53177864+MrTraduttore@users.noreply.github.com>
Date: Mon, 22 Jul 2019 12:40:03 +0000
Subject: [PATCH 01/21] Improved Italian translation
Fixed some errors and translated using Twitter's terms
---
.../main/res-localized/values-it/strings.xml | 130 +++++++++---------
1 file changed, 65 insertions(+), 65 deletions(-)
diff --git a/twidere/src/main/res-localized/values-it/strings.xml b/twidere/src/main/res-localized/values-it/strings.xml
index 2f9347259..9929a2650 100644
--- a/twidere/src/main/res-localized/values-it/strings.xml
+++ b/twidere/src/main/res-localized/values-it/strings.xml
@@ -29,14 +29,14 @@
Edita
Aggiungi ai preferiti
- Finish
+ Fine
Segui
Importa da…
- Like
- Muto
+ Mi piace
+ Silenzia
%1$s, salvato alle %2$s
Apri nel browser
Scegli colore
@@ -47,7 +47,7 @@
Riprova
- Retweet
+ Ritwitta
Salva
Cerca
@@ -63,17 +63,17 @@
Impostazioni
Scatta una foto
- Toggle
- Traduce
+ Apri
+ Traduci
Filtra utente
Utenti filtrati
Sblocca
Annulla like
- Togli dai preferiti
+ Rimuovi dai preferiti
Smetti di seguire
- Muto off
- Cancellati
+ Riattiva
+ Disiscriviti
Mappa
Account attivati
Mie Attività
@@ -82,7 +82,7 @@
Aggiungi immagine
e altri %d
Formato API URL
- La tua Twitter app
+ La tua app per Twitter
Twidere
Twidere si riavvierà per rendere effettive le nuove le impostazioni.
@@ -92,7 +92,7 @@
Modalità twip O
xAuth
Auto refresh
- Background
+ Sfondo
Modalità risparmio dati
Disabilita l\'anteprima dei media su connessione a consumo
Appartiene a
@@ -116,11 +116,11 @@
Commento…
Schede compatte
Visualizza più schede sullo schermo
- Compose Now
- Azione Compose Now
- Sostituisce la scorciatoia a Google Now con la Compose screen
+ Componimento veloce
+ Azione Componimento veloce
+ Sostituisce la scorciatoia a Google Now con la schemata di composizione
Confitti con %s
- Timeout connessione
+ Timeout per la connessione
Consumer Key
Consumer secret
Contenuto
@@ -141,19 +141,19 @@
Impostazioni di default API
Queste impostazioni veranno applicate al prossimo login
Suoneria predefinita
- Cancella conversazione
- Cancellare tutti i messaggi di questa conversazione?
+ Elimina conversazione
+ Eliminare tutti i messaggi di questa conversazione?
Eliminare le bozze selezionate?
Cancellare questo messaggio?
Elimina utente %s
Eliminare %s? Non è reversibile.
Rimuovere %1$s dalla lista "%2$s\"?
Elimina lista %s
- Eliminare lisra %s? Non potrà essere annullato.
+ Eliminare lista %s? Non potrà essere annullato.
Elimina utenti
- Lista cancellata \"%s\".
+ La lista \"%s\" è stata eliminata.
Rimosso %1$s dalla lista "%2$s\".
- Richiesta %s di follow negata.
+ Richiesta di follow di %s negata.
Rifiuta
Elimina ricerca salvata \"%s\"
Eliminare la ricerca \"%s\"? È possibile salvarla nuovamente in seguito.
@@ -200,19 +200,19 @@
Caricamento veloce immagini
Seleziona per far caricare più velocemente le immagini, disattivalo se alcune immagini non sono mostrate correttamente.
- Keywords
+ Parole chiave
Collegamenti
Fonti
Utenti
Richiesta di follow inviata
Seguito %s.
- I tuoi following
+ Persone che segui
Mostra notifiche solo dagli utenti che segui.
Chi ti segue
Segui
Carattere
Font
- Da Camera
+ Da fotocamera
Da galleria
Da %1$s
Da %1$s e %2$d altri
@@ -227,8 +227,8 @@
Hashtag
Impostazioni nascoste
MAI cambiare queste impostazioni se non sai esattamente cosa fanno, o potrebbero:\n * Uccidere il tuo gatto \n * Lanciare testate nucleari in Corea del Nord \n * fArtI tWitTtarE CM 1 PaxxErEllo\n * Distruggere l\'universo
- ATTENZIONE: queste opzioni possono far male!
- Nascondi azioni di carte
+ ATTENZIONE: queste opzioni creare danni!
+ Nascondi le azioni per il tweet
Nascondi citazioni
Nascondi risposte
Nascondi retweets
@@ -241,18 +241,18 @@
Indirizzo (può essere un altro indirizzo host)
Host
Rivoglio le mie stelline!
- Usa i preferiti (★) piuttosto che i like (♥︎)
+ Usa i preferiti (★) al posto dei mi piace (♥︎)
Icona
Icona ripristinata!
Importa/Esporta le impostazioni
Importa impostazioni
Importa impostazioni…
- Risposta a %s
+ Rispondi a %s
Inbox
- Richieste di seguirti in attesa
- Immissione testo
+ Richieste di follow in attesa
+ Immetti testo
Interazioni
- Chaive da consumatore invalida
+ Chiave da consumatore invalida
Segreto consumatore invalido
Deve iniziare con una lettera e può contenere solo lettere, numeri, \"-\" o \"_\".
Tab non valida
@@ -261,9 +261,9 @@
Indietro
Premi i tasti
Scorciatoie da tastiera
- Keyword: %s
+ Parola chiave: %s
Tipo di autenticazione
- Background operation service
+ Servizio di operazione in background
@@ -318,11 +318,11 @@
Membri
Menziona questo utente
Menziona %s
- Mention %1$s
+ Menziona %1$s
Solo menzioni
Dati delle API corrotti.
- [DOMAIN]: Twitter API domain.\nPer Esempio: https://[DOMAIN].twitter.com/ sarà sostituito da https://api.twitter.com/.
- Bloccato %s.
+ [DOMAIN]: Dominio dell' API di Twitter.\nPer Esempio: https://[DOMAIN].twitter.com/ sarà sostituito da https://api.twitter.com/.
+ %s bloccato.
Messaggio diretto cancellato.
Messaggio diretto inviato.
Attendere prego.
@@ -336,30 +336,30 @@
Link copiato negli appunti
Ecco un regalino per te, trovalo tra le impostazioni di sistema :)
- Verifica di login fallita.
+ Verifica del login fallita.
Nessun account
Nessun account selezionato.
Premi di nuovo per chiudere
Immagine dell\'intestazione profilo aggiornata.
Permesso di memoria richiesto per salvare questo media.
- Salvato in Galleria.
+ Salvato nella Galleria.
Permessi di memoria richiesti per selezionare il file.
Alcune applicazioni richiedono permessi per condividere alcuni media.
- Tweet salvato in bozze.
+ Tweet salvato nelle bozze.
Tweet inviato.
URL delle API non corretto, o consumer key/secret errata. Per favore, controllale.
Selezione multipla
- Muto %s
+ Silenzia %s
Silenziare %s? Non vedrai più i tweets di questo utente ma continuerai a seguirlo.
- Muto on %s
- %1$s e un altro hanno ReTweettato
- Retwittato da %1$s e da altri
+ %s silenziato
+ %1$s e un altro hanno Ritwittato
+ Ritwittato da %1$s e da altri
Mostra il nome prima
- Display \@nomeschermo primo
+ Mostra \@nomeschermo per primo
Visualizza prima il nome
%s non settato
- ReTweettato da %s
+ Ritwittato da %s
%1$s (%2$s)
Navigazione
Rete
@@ -420,7 +420,7 @@
Scrittura sul database, aggiornamento degli stati
Riduci il tweet
Sincronizza timeline
- Upload media
+ Carica media
Richiesta di permessi
L\'app richiede i seguenti permessi
Avviso di rischio phishing
@@ -429,11 +429,11 @@
Apre un avvertimento quando stai per aprire un link potenzialmente pericoloso in un DM.
Foto
Seleziona file
- Play
+ Riproduci
%1$s · %2$s
Interrompi auto-aggiornamento quando la batteria è scarica
Limite massimo degli elementi memorizzati nel database per ogni account, impostare su un valore inferiore per risparmiare spazio e aumentare la velocità di caricamento.
- Accounts
+ Profili
Avanzate
Limite dimensione database
Landscape
@@ -441,7 +441,7 @@
Archivio
Attiva streaming
Dimensione del testo
- Traduce
+ Traduci
Località per i Trends
Precarica solo se in Wi-Fi
Anteprima
@@ -465,7 +465,7 @@
Colore testo
Profilo aggiornato.
Avanzamento
- Project account
+ Account del progetto
Progetti di cui siamo parte
Twidere
@@ -515,9 +515,9 @@
Resettare le scorciatoie da tastiera ai valori di default?
Ripristina come da default
Riprova se si verifica un errore di rete
- ReTweettato da utenti
- ReTweettato da %s
- ReTweettato da %1$s e altri
+ Ritwittato da utenti
+ Ritwittato da %s
+ Ritwittato da %1$s e altri
I miei retweets
Revoca permessi
Tonda
@@ -526,9 +526,9 @@
Salva nella galleria
Ricerche salvate
Forse hai già salvato questa ricerca
- Tweet schedulati
+ Tweet programmati
Discarica
- Cerca tweets o utenti
+ Cerca tweet o utenti
Ricerca tweet
Tweet
Utenti
@@ -619,7 +619,7 @@
Timeline di streaming in esecuzione
Servizio sincronizzazione timeline
Informazioni
- Accounts
+ Profili
Aggiungi o rimuovi dalla lista
Blocca %s
Utenti bloccati
@@ -635,7 +635,7 @@
Preferiti
Filtri
URL
- Followers
+ Follower
Stai seguendo
Home
@@ -654,17 +654,17 @@
Utenti
Colori personalizzati
Appartiene a
- Users che hanno inserito tra i preferiti
+ Utenti che hanno inserito questo elemento tra i preferiti
Utenti a cui piace
- Utenti che hanno retwittato questo
+ Utenti che hanno retwittato questo elemento
Lingua
Traduttori
- Trends
- Località per i Trends
- Scegli la località per i trends.
+ Tendenze
+ Località per le tendenze
+ Scegli la località per le tendenze.
Tweet da %1$s
- Tweet #%1$s
+ Twitta #%1$s
Twidere test
Digita un nome da cercare
Digita per comporre
@@ -676,12 +676,12 @@
Disinstalla
Lingua sconosciuta
Posizione sconosciuta
- Muto off %s
- Mutato %s
+ Riattiva %s
+ %s riattivato
Conteggio elementi non letti
Annulla l\'iscrizione alla lista %s
Annullare l\'iscrizione alla lista %s? Potrai riscriverti in seguito.
- Cancellato dalla lista \"%s\".
+ Rimosso dalla lista \"%s\".
Per favore rettifica l\'azione qui sopra
Invia tweet
Dettagli della lista \"%s\" aggiornati.
From 78cf31881a3fe02d1b9224052d98e19bc13c807f Mon Sep 17 00:00:00 2001
From: Mariotaku
Date: Wed, 20 Nov 2019 17:54:30 +0900
Subject: [PATCH 02/21] =?UTF-8?q?=E3=81=95=E3=82=88=E3=81=AA=E3=82=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/README.md b/README.md
index 7d32500a9..a0c5fd261 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,11 @@
+# Good bye, my friends! #
+
+Months ago, I started a long and slow migration to fit new Android frameworks. Unfortunately, Twidere has hundreds of thousands lines of code, and over 50% needs to be rewritten because there're breaking changes. It's even impossible to maintain as publishing to Google Play requires new API target, and that target requires new libraries, and those libraries will break current code.
+
+So It's time to say goodbye. Don't worry, I don't like Twitter's new timeline & ads as much as you do. I can't give any promise, but my plan is to start a new one, focusing on cleaness & simplicity. And before this proposed product, I want to build a web service to help you manage your Twitter data, such as removing spam followers (like [TwitBlock](https://twitblock.org/)).
+
+Let's looking forward to something better come to this world.
+
# Twidere for Android #
[![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-Twidere-brightgreen.svg?style=flat)](http://android-arsenal.com/details/3/2453)
From af7791588803253bf022af32d0889722eb7e21e5 Mon Sep 17 00:00:00 2001
From: Suji Yan
Date: Sun, 2 Feb 2020 05:00:04 -0800
Subject: [PATCH 03/21] Update README.md
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index a0c5fd261..a95a07008 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,10 @@
-# Good bye, my friends! #
+# We'll be back soon #
Months ago, I started a long and slow migration to fit new Android frameworks. Unfortunately, Twidere has hundreds of thousands lines of code, and over 50% needs to be rewritten because there're breaking changes. It's even impossible to maintain as publishing to Google Play requires new API target, and that target requires new libraries, and those libraries will break current code.
So It's time to say goodbye. Don't worry, I don't like Twitter's new timeline & ads as much as you do. I can't give any promise, but my plan is to start a new one, focusing on cleaness & simplicity. And before this proposed product, I want to build a web service to help you manage your Twitter data, such as removing spam followers (like [TwitBlock](https://twitblock.org/)).
-Let's looking forward to something better come to this world.
+Twidere-Android will be maintained by community and supporter including [Dimension](https://dimension.im/). Stay tuned and we'll back soon!
# Twidere for Android #
From 24fc6f7de664cc01f520d0ba0e9b654abd006f30 Mon Sep 17 00:00:00 2001
From: Suji Yan
Date: Sun, 2 Feb 2020 07:08:29 -0800
Subject: [PATCH 04/21] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index a95a07008..be2f5b3d1 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
Months ago, I started a long and slow migration to fit new Android frameworks. Unfortunately, Twidere has hundreds of thousands lines of code, and over 50% needs to be rewritten because there're breaking changes. It's even impossible to maintain as publishing to Google Play requires new API target, and that target requires new libraries, and those libraries will break current code.
-So It's time to say goodbye. Don't worry, I don't like Twitter's new timeline & ads as much as you do. I can't give any promise, but my plan is to start a new one, focusing on cleaness & simplicity. And before this proposed product, I want to build a web service to help you manage your Twitter data, such as removing spam followers (like [TwitBlock](https://twitblock.org/)).
+Don't worry, I don't like Twitter's new timeline & ads as much as you do. I can't give any promise, but my plan is to start a new one, focusing on cleaness & simplicity. And before this proposed product, I want to build a web service to help you manage your Twitter data, such as removing spam followers (like [TwitBlock](https://twitblock.org/)).
Twidere-Android will be maintained by community and supporter including [Dimension](https://dimension.im/). Stay tuned and we'll back soon!
From 2d6952a0408d122c567ef1811055e4c5e1749a32 Mon Sep 17 00:00:00 2001
From: Suji Yan
Date: Sat, 28 Mar 2020 22:52:58 +0800
Subject: [PATCH 05/21] Update README.md
Twidere is back on Google Play and FDroid
---
README.md | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)
diff --git a/README.md b/README.md
index be2f5b3d1..564a31893 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,3 @@
-# We'll be back soon #
-
-Months ago, I started a long and slow migration to fit new Android frameworks. Unfortunately, Twidere has hundreds of thousands lines of code, and over 50% needs to be rewritten because there're breaking changes. It's even impossible to maintain as publishing to Google Play requires new API target, and that target requires new libraries, and those libraries will break current code.
-
-Don't worry, I don't like Twitter's new timeline & ads as much as you do. I can't give any promise, but my plan is to start a new one, focusing on cleaness & simplicity. And before this proposed product, I want to build a web service to help you manage your Twitter data, such as removing spam followers (like [TwitBlock](https://twitblock.org/)).
-
-Twidere-Android will be maintained by community and supporter including [Dimension](https://dimension.im/). Stay tuned and we'll back soon!
-
# Twidere for Android #
[![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-Twidere-brightgreen.svg?style=flat)](http://android-arsenal.com/details/3/2453)
@@ -14,6 +6,8 @@ Twidere-Android will be maintained by community and supporter including [Dimensi
Material Design ready and feature rich Twitter app for Android 4.0+
+Twidere-Android is maintained by community and supporter including [Dimension](https://dimension.im/).
+
---
## Features ##
From 925e041a4b23c6de41c6c8cb5fffbaf2d983d063 Mon Sep 17 00:00:00 2001
From: Tlaster
Date: Mon, 30 Mar 2020 17:03:15 +0800
Subject: [PATCH 06/21] Workaround for #1178
---
.../kotlin/org/mariotaku/twidere/view/ComposeEditText.kt | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/view/ComposeEditText.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/view/ComposeEditText.kt
index 5ba1b5c9e..7b64219dc 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/view/ComposeEditText.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/view/ComposeEditText.kt
@@ -89,6 +89,13 @@ class ComposeEditText(
} catch (e: AbstractMethodError) {
// http://crashes.to/s/69acd0ea0de
return true
+ }catch (e: IndexOutOfBoundsException) {
+ e.printStackTrace()
+ // workaround
+ // https://github.com/TwidereProject/Twidere-Android/issues/1178
+ setSelection(length() - 1, length() - 1)
+ setSelection(length(), length())
+ return true
}
}
From 4bc560e8d3ea5e7db03c2bc0fdbdd41a516e4d52 Mon Sep 17 00:00:00 2001
From: Tlaster
Date: Wed, 1 Apr 2020 14:36:00 +0800
Subject: [PATCH 07/21] Fix #1223
Only 1.1.0-alpha01 works now due to https://android.googlesource.com/platform/frameworks/support/+/6eae6436c7b70d783c2a2e53e582fe5a68f1ca2d remove ViewCompat.setSystemGestureExclusionRects call, DrawerLayout can not support automatic opt-out behavior out of the box.
---
twidere/build.gradle | 1 +
1 file changed, 1 insertion(+)
diff --git a/twidere/build.gradle b/twidere/build.gradle
index 9d80f9787..0de070290 100644
--- a/twidere/build.gradle
+++ b/twidere/build.gradle
@@ -216,6 +216,7 @@ dependencies {
implementation 'androidx.palette:palette:1.0.0'
implementation 'androidx.legacy:legacy-preference-v14:1.0.0'
implementation 'androidx.browser:browser:1.2.0'
+ implementation "androidx.drawerlayout:drawerlayout:1.1.0-alpha01"
implementation 'com.google.android.material:material:1.1.0'
implementation 'androidx.exifinterface:exifinterface:1.1.0'
implementation "com.twitter:twitter-text:${libVersions['TwitterText']}"
From 251b9b1d3bbf992023d682123397230555b5e3e0 Mon Sep 17 00:00:00 2001
From: Tlaster
Date: Wed, 1 Apr 2020 16:03:35 +0800
Subject: [PATCH 08/21] Fix #1067
---
.../twidere/activity/MediaViewerActivity.kt | 23 +++++++++++--------
1 file changed, 13 insertions(+), 10 deletions(-)
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/MediaViewerActivity.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/MediaViewerActivity.kt
index 8ac7f5e4c..f1e00586d 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/MediaViewerActivity.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/MediaViewerActivity.kt
@@ -73,6 +73,7 @@ import org.mariotaku.twidere.util.support.WindowSupport
import org.mariotaku.twidere.view.viewer.MediaSwipeCloseContainer
import java.io.File
import javax.inject.Inject
+import kotlin.concurrent.thread
import android.Manifest.permission as AndroidPermissions
class MediaViewerActivity : BaseActivity(), IMediaViewerActivity, MediaSwipeCloseContainer.Listener,
@@ -521,17 +522,19 @@ class MediaViewerActivity : BaseActivity(), IMediaViewerActivity, MediaSwipeClos
private fun openSaveToDocumentChooser() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return
val fileInfo = getCurrentCacheFileInfo(viewPager.currentItem) ?: return
- val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
- intent.type = fileInfo.mimeType ?: "*/*"
- intent.addCategory(Intent.CATEGORY_OPENABLE)
- val extension = fileInfo.fileExtension
- val saveFileName = if (extension != null) {
- "${fileInfo.fileName?.removeSuffix("_$extension")}.$extension"
- } else {
- fileInfo.fileName
+ thread {
+ val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
+ intent.type = fileInfo.mimeType ?: "*/*"
+ intent.addCategory(Intent.CATEGORY_OPENABLE)
+ val extension = fileInfo.fileExtension
+ val saveFileName = if (extension != null) {
+ "${fileInfo.fileName?.removeSuffix("_$extension")}.$extension"
+ } else {
+ fileInfo.fileName
+ }
+ intent.putExtra(Intent.EXTRA_TITLE, saveFileName)
+ startActivityForResult(intent, REQUEST_SELECT_SAVE_MEDIA)
}
- intent.putExtra(Intent.EXTRA_TITLE, saveFileName)
- startActivityForResult(intent, REQUEST_SELECT_SAVE_MEDIA)
}
private fun saveMediaToContentUri(data: Uri) {
From 177342a339692b0ebe2087d8e9b07221bb668d4f Mon Sep 17 00:00:00 2001
From: Tlaster
Date: Wed, 1 Apr 2020 16:06:04 +0800
Subject: [PATCH 09/21] Add android.permission.FOREGROUND_SERVICE
---
twidere/src/main/AndroidManifest.xml | 1 +
1 file changed, 1 insertion(+)
diff --git a/twidere/src/main/AndroidManifest.xml b/twidere/src/main/AndroidManifest.xml
index 0fba8b659..d013ecc65 100644
--- a/twidere/src/main/AndroidManifest.xml
+++ b/twidere/src/main/AndroidManifest.xml
@@ -58,6 +58,7 @@
+
Date: Wed, 1 Apr 2020 16:10:03 +0800
Subject: [PATCH 10/21] Wrap to safeGetInt when getting gap
---
.../org/mariotaku/twidere/adapter/ParcelableStatusesAdapter.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/ParcelableStatusesAdapter.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/ParcelableStatusesAdapter.kt
index 899d31901..db73a7492 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/ParcelableStatusesAdapter.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/ParcelableStatusesAdapter.kt
@@ -453,7 +453,7 @@ abstract class ParcelableStatusesAdapter(
val timestamp = cursor.safeGetLong(indices[Statuses.TIMESTAMP])
val sortId = cursor.safeGetLong(indices[Statuses.SORT_ID])
val positionKey = cursor.safeGetLong(indices[Statuses.POSITION_KEY])
- val gap = cursor.getInt(indices[Statuses.IS_GAP]) == 1
+ val gap = cursor.safeGetInt(indices[Statuses.IS_GAP]) == 1
val newInfo = StatusInfo(_id, accountKey, id, timestamp, sortId, positionKey, gap)
infoCache?.set(dataPosition, newInfo)
return@run newInfo
From 149bbbf9c2ff53db0eb30b09d7b2b795746d9edf Mon Sep 17 00:00:00 2001
From: Tlaster
Date: Wed, 1 Apr 2020 16:10:47 +0800
Subject: [PATCH 11/21] Check fetched items is not empty before inserting
---
.../org/mariotaku/twidere/task/twitter/GetActivitiesTask.kt | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/GetActivitiesTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/GetActivitiesTask.kt
index 00642782c..6703722d8 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/GetActivitiesTask.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/GetActivitiesTask.kt
@@ -181,8 +181,10 @@ abstract class GetActivitiesTask(
valuesList[valuesList.size - 1].put(Activities.IS_GAP, true)
}
}
- // Insert previously fetched items.
- ContentResolverUtils.bulkInsert(cr, writeUri, valuesList)
+ if (valuesList.isNotEmpty()) {
+ // Insert previously fetched items.
+ ContentResolverUtils.bulkInsert(cr, writeUri, valuesList)
+ }
// Remove gap flag
if (maxId != null && sinceId == null) {
From 1db9968359c2fcd031d21f2925274ed1901d8f28 Mon Sep 17 00:00:00 2001
From: Tlaster
Date: Wed, 1 Apr 2020 16:26:13 +0800
Subject: [PATCH 12/21] Fix build error
---
twidere/src/main/res-localized/values-it/strings.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/twidere/src/main/res-localized/values-it/strings.xml b/twidere/src/main/res-localized/values-it/strings.xml
index 9929a2650..3d2cbc235 100644
--- a/twidere/src/main/res-localized/values-it/strings.xml
+++ b/twidere/src/main/res-localized/values-it/strings.xml
@@ -321,7 +321,7 @@
Menziona %1$s
Solo menzioni
Dati delle API corrotti.
- [DOMAIN]: Dominio dell' API di Twitter.\nPer Esempio: https://[DOMAIN].twitter.com/ sarà sostituito da https://api.twitter.com/.
+ [DOMAIN]: Dominio dell\' API di Twitter.\nPer Esempio: https://[DOMAIN].twitter.com/ sarà sostituito da https://api.twitter.com/.
%s bloccato.
Messaggio diretto cancellato.
Messaggio diretto inviato.
From 70d3d1f67f384f475748fc3852da5e2605ab9d08 Mon Sep 17 00:00:00 2001
From: Tlaster
Date: Wed, 1 Apr 2020 17:50:00 +0800
Subject: [PATCH 13/21] Version 4.0.4
---
build.gradle | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/build.gradle b/build.gradle
index 06958c277..94720ed43 100644
--- a/build.gradle
+++ b/build.gradle
@@ -17,8 +17,8 @@ allprojects {
ext {
projectGroupId = 'org.mariotaku.twidere'
- projectVersionCode = 502
- projectVersionName = '4.0.3'
+ projectVersionCode = 503
+ projectVersionName = '4.0.4'
globalCompileSdkVersion = 29
globalBuildToolsVersion = '29.0.2'
From 4fa12257420e7c900acb3fa150b8410c7576eacb Mon Sep 17 00:00:00 2001
From: Tlaster
Date: Thu, 2 Apr 2020 15:04:31 +0800
Subject: [PATCH 14/21] Update storage api for android q
---
.../twidere/activity/MediaViewerActivity.kt | 19 ++++++--
.../twidere/task/SaveMediaToGalleryTask.kt | 47 ++++++++++++++++++-
2 files changed, 62 insertions(+), 4 deletions(-)
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/MediaViewerActivity.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/MediaViewerActivity.kt
index f1e00586d..df5f88f82 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/MediaViewerActivity.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/MediaViewerActivity.kt
@@ -26,6 +26,7 @@ import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.os.Parcelable
+import android.provider.MediaStore
import androidx.annotation.RequiresApi
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
@@ -505,13 +506,25 @@ class MediaViewerActivity : BaseActivity(), IMediaViewerActivity, MediaSwipeClos
val type = (fileInfo as? CacheProvider.CacheFileTypeSupport)?.cacheFileType
val pubDir = when (type) {
CacheFileType.VIDEO -> {
- Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES)
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES)
+ } else {
+ getExternalFilesDir(Environment.DIRECTORY_MOVIES)
+ }
}
CacheFileType.IMAGE -> {
- Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
+ } else {
+ getExternalFilesDir(Environment.DIRECTORY_PICTURES)
+ }
}
else -> {
- Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
+ } else {
+ getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)
+ }
}
}
val saveDir = File(pubDir, "Twidere")
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/SaveMediaToGalleryTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/SaveMediaToGalleryTask.kt
index 75f44a444..f1f5b1c7b 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/SaveMediaToGalleryTask.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/SaveMediaToGalleryTask.kt
@@ -20,9 +20,15 @@
package org.mariotaku.twidere.task
import android.app.Activity
+import android.content.ContentValues
import android.media.MediaScannerConnection
+import android.os.Build
+import android.os.Environment
+import android.provider.MediaStore
import android.widget.Toast
import org.mariotaku.twidere.R
+import org.mariotaku.twidere.annotation.CacheFileType
+import org.mariotaku.twidere.provider.CacheProvider
import java.io.File
/**
@@ -30,14 +36,53 @@ import java.io.File
*/
class SaveMediaToGalleryTask(
activity: Activity,
- fileInfo: FileInfo,
+ private val fileInfo: FileInfo,
destination: File
) : ProgressSaveFileTask(activity, destination, fileInfo) {
override fun onFileSaved(savedFile: File, mimeType: String?) {
val context = context ?: return
+
MediaScannerConnection.scanFile(context, arrayOf(savedFile.path),
arrayOf(mimeType), null)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ val type = (fileInfo as? CacheProvider.CacheFileTypeSupport)?.cacheFileType
+ val path = when (type) {
+ CacheFileType.VIDEO -> {
+ Environment.DIRECTORY_MOVIES
+ }
+ CacheFileType.IMAGE -> {
+ Environment.DIRECTORY_PICTURES
+ }
+ else -> {
+ Environment.DIRECTORY_DOWNLOADS
+ }
+ }
+ val url = when (type) {
+ CacheFileType.VIDEO -> {
+ MediaStore.Video.Media.EXTERNAL_CONTENT_URI
+ }
+ CacheFileType.IMAGE -> {
+ MediaStore.Images.Media.EXTERNAL_CONTENT_URI
+ }
+ else -> {
+ MediaStore.Downloads.EXTERNAL_CONTENT_URI
+ }
+ }
+ val contentValues = ContentValues()
+ contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, fileInfo.fileName)
+ contentValues.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis())
+ contentValues.put(MediaStore.Images.Media.MIME_TYPE, fileInfo.mimeType)
+ contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, "$path/Twidere")
+ context.contentResolver.insert(url, contentValues)?.let { uri ->
+ context.contentResolver.openOutputStream(uri)?.use {
+ savedFile.inputStream().use { fileInputStream ->
+ fileInputStream.copyTo(it)
+ }
+ }
+ }
+ }
+ savedFile.delete()
Toast.makeText(context, R.string.message_toast_saved_to_gallery, Toast.LENGTH_SHORT).show()
}
From 1ab66a9e95b54a9c0070dba96f05798dc401c572 Mon Sep 17 00:00:00 2001
From: Tlaster
Date: Thu, 2 Apr 2020 15:21:11 +0800
Subject: [PATCH 15/21] Show keyboard when enter ComposeActivity with default
intent
---
.../kotlin/org/mariotaku/twidere/activity/ComposeActivity.kt | 1 +
1 file changed, 1 insertion(+)
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/ComposeActivity.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/ComposeActivity.kt
index 62d110741..d9dd6e598 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/ComposeActivity.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/ComposeActivity.kt
@@ -1127,6 +1127,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
} else {
editText.setSelection(selection.coerceIn(0..editText.length()))
}
+ editText.requestFocus()
return true
}
From 2407b9f193424511fe97f483692de85877814901 Mon Sep 17 00:00:00 2001
From: Tlaster
Date: Fri, 3 Apr 2020 17:14:42 +0800
Subject: [PATCH 16/21] Revert "Removed private API usages"
This reverts commit 5052d1fc62809e7fa5aba5f256e0cd31c63f2c0c.
---
.../microblog/library/twitter/Twitter.java | 2 +-
.../library/twitter/TwitterPrivate.java | 35 +++
.../twitter/api/PrivateAccountResources.java | 37 +++
.../twitter/api/PrivateActivityResources.java | 52 ++++
.../api/PrivateDirectMessagesResources.java | 102 ++++++++
.../api/PrivateFriendsFollowersResources.java | 43 ++++
.../twitter/api/PrivateMutesResources.java | 42 ++++
.../library/twitter/api/PrivateResources.java | 23 ++
.../twitter/api/PrivateSearchResources.java | 38 +++
.../twitter/api/PrivateTimelineResources.java | 39 +++
.../twitter/api/PrivateTweetResources.java | 45 ++++
.../twidere/model/ConsumerKeyType.java | 3 +-
.../model/account/TwitterAccountExtras.java | 14 ++
.../twidere/model/util/AccountUtils.java | 17 ++
.../util/InternalTwitterContentUtils.java | 56 +++++
.../twidere/util/MicroBlogAPIFactory.java | 15 ++
.../twidere/activity/SignInActivity.kt | 15 +-
.../model/AccountDetailsExtensions.kt | 21 ++
.../extension/model/AccountExtensions.kt | 13 +
.../extension/model/CredentialsExtensions.kt | 5 +-
.../twidere/fragment/AbsActivitiesFragment.kt | 11 +-
.../twidere/fragment/CustomTabsFragment.kt | 7 +-
.../MessageConversationInfoFragment.kt | 72 ++++++
.../message/MessageNewConversationFragment.kt | 7 +-
.../status/PinStatusDialogFragment.kt | 55 ++++
.../twidere/fragment/status/StatusFragment.kt | 41 +++
.../status/UnpinStatusDialogFragment.kt | 55 ++++
.../users/IncomingFriendshipsFragment.kt | 3 +
.../loader/statuses/ConversationLoader.kt | 6 +
.../statuses/MediaStatusesSearchLoader.kt | 14 ++
.../loader/statuses/MediaTimelineLoader.kt | 42 ++--
.../loader/statuses/TweetSearchLoader.kt | 13 +
.../loader/statuses/UserMentionsLoader.kt | 4 +
.../loader/users/StatusFavoritersLoader.kt | 5 +-
.../tab/impl/InteractionsTabConfiguration.kt | 7 +-
.../twidere/service/StreamingService.kt | 235 ++++++++++++++++++
.../twidere/task/AcceptFriendshipTask.kt | 5 +-
.../twidere/task/DenyFriendshipTask.kt | 5 +-
.../twidere/task/status/PinStatusTask.kt | 52 ++++
.../twidere/task/status/UnpinStatusTask.kt | 52 ++++
.../task/twitter/GetActivitiesAboutMeTask.kt | 23 ++
.../twidere/task/twitter/GetStatusesTask.kt | 2 +-
.../twitter/message/AddParticipantsTask.kt | 97 ++++++++
.../message/DestroyConversationTask.kt | 4 +
.../twitter/message/DestroyMessageTask.kt | 4 +
.../task/twitter/message/GetMessagesTask.kt | 70 +++++-
.../twitter/message/MarkMessageReadTask.kt | 18 ++
.../task/twitter/message/SendMessageTask.kt | 49 +++-
...SetConversationNotificationDisabledTask.kt | 19 ++
.../twidere/util/AsyncTwitterWrapper.kt | 12 +
.../mariotaku/twidere/util/DataStoreUtils.kt | 19 +-
.../org/mariotaku/twidere/util/MenuUtils.kt | 9 +-
.../holder/status/DetailStatusViewHolder.kt | 22 +-
.../main/res/values/arrays_no_translate.xml | 51 ++++
54 files changed, 1659 insertions(+), 48 deletions(-)
create mode 100644 twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/TwitterPrivate.java
create mode 100644 twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateAccountResources.java
create mode 100644 twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateActivityResources.java
create mode 100644 twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateDirectMessagesResources.java
create mode 100644 twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateFriendsFollowersResources.java
create mode 100644 twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateMutesResources.java
create mode 100644 twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateResources.java
create mode 100644 twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateSearchResources.java
create mode 100644 twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateTimelineResources.java
create mode 100644 twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateTweetResources.java
create mode 100644 twidere/src/main/kotlin/org/mariotaku/twidere/fragment/status/PinStatusDialogFragment.kt
create mode 100644 twidere/src/main/kotlin/org/mariotaku/twidere/fragment/status/UnpinStatusDialogFragment.kt
create mode 100644 twidere/src/main/kotlin/org/mariotaku/twidere/task/status/PinStatusTask.kt
create mode 100644 twidere/src/main/kotlin/org/mariotaku/twidere/task/status/UnpinStatusTask.kt
create mode 100644 twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/message/AddParticipantsTask.kt
diff --git a/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/Twitter.java b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/Twitter.java
index d3cc2c7dc..94b11e67c 100644
--- a/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/Twitter.java
+++ b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/Twitter.java
@@ -41,5 +41,5 @@ public interface Twitter extends SearchResources, TimelineResources, TweetResour
ListResources, DirectMessagesResources, DirectMessagesEventResources,
FriendsFollowersResources, FavoritesResources, SpamReportingResources,
SavedSearchesResources, TrendsResources, PlacesGeoResources,
- HelpResources, MutesResources {
+ HelpResources, MutesResources, TwitterPrivate {
}
diff --git a/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/TwitterPrivate.java b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/TwitterPrivate.java
new file mode 100644
index 000000000..4414f99be
--- /dev/null
+++ b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/TwitterPrivate.java
@@ -0,0 +1,35 @@
+/*
+ * Twidere - Twitter client for Android
+ *
+ * Copyright 2012-2017 Mariotaku Lee
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mariotaku.microblog.library.twitter;
+
+import org.mariotaku.microblog.library.twitter.api.PrivateAccountResources;
+import org.mariotaku.microblog.library.twitter.api.PrivateActivityResources;
+import org.mariotaku.microblog.library.twitter.api.PrivateDirectMessagesResources;
+import org.mariotaku.microblog.library.twitter.api.PrivateFriendsFollowersResources;
+import org.mariotaku.microblog.library.twitter.api.PrivateSearchResources;
+import org.mariotaku.microblog.library.twitter.api.PrivateTimelineResources;
+import org.mariotaku.microblog.library.twitter.api.PrivateTweetResources;
+
+/**
+ * Created by mariotaku on 16/3/4.
+ */
+public interface TwitterPrivate extends PrivateActivityResources, PrivateTweetResources,
+ PrivateTimelineResources, PrivateFriendsFollowersResources, PrivateDirectMessagesResources,
+ PrivateSearchResources, PrivateAccountResources {
+}
diff --git a/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateAccountResources.java b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateAccountResources.java
new file mode 100644
index 000000000..9e6a699af
--- /dev/null
+++ b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateAccountResources.java
@@ -0,0 +1,37 @@
+/*
+ * Twidere - Twitter client for Android
+ *
+ * Copyright 2012-2017 Mariotaku Lee
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mariotaku.microblog.library.twitter.api;
+
+import org.mariotaku.microblog.library.MicroBlogException;
+import org.mariotaku.microblog.library.twitter.model.PinTweetResult;
+import org.mariotaku.restfu.annotation.method.POST;
+import org.mariotaku.restfu.annotation.param.Param;
+
+/**
+ * Created by mariotaku on 16/8/20.
+ */
+public interface PrivateAccountResources extends PrivateResources {
+
+ @POST("/account/pin_tweet.json")
+ PinTweetResult pinTweet(@Param("id") String id) throws MicroBlogException;
+
+ @POST("/account/unpin_tweet.json")
+ PinTweetResult unpinTweet(@Param("id") String id) throws MicroBlogException;
+
+}
diff --git a/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateActivityResources.java b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateActivityResources.java
new file mode 100644
index 000000000..92e13c820
--- /dev/null
+++ b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateActivityResources.java
@@ -0,0 +1,52 @@
+/*
+ * Twidere - Twitter client for Android
+ *
+ * Copyright 2012-2017 Mariotaku Lee
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mariotaku.microblog.library.twitter.api;
+
+import org.mariotaku.microblog.library.MicroBlogException;
+import org.mariotaku.microblog.library.twitter.model.Activity;
+import org.mariotaku.microblog.library.twitter.model.CursorTimestampResponse;
+import org.mariotaku.microblog.library.twitter.model.Paging;
+import org.mariotaku.microblog.library.twitter.model.ResponseList;
+import org.mariotaku.microblog.library.twitter.template.StatusAnnotationTemplate;
+import org.mariotaku.restfu.annotation.method.GET;
+import org.mariotaku.restfu.annotation.method.POST;
+import org.mariotaku.restfu.annotation.param.Param;
+import org.mariotaku.restfu.annotation.param.Params;
+import org.mariotaku.restfu.annotation.param.Queries;
+import org.mariotaku.restfu.annotation.param.Query;
+import org.mariotaku.restfu.http.BodyType;
+
+@SuppressWarnings("RedundantThrows")
+@Params(template = StatusAnnotationTemplate.class)
+public interface PrivateActivityResources extends PrivateResources {
+
+ @GET("/activity/about_me.json")
+ ResponseList getActivitiesAboutMe(@Query Paging paging) throws MicroBlogException;
+
+ @Queries({})
+ @GET("/activity/about_me/unread.json")
+ CursorTimestampResponse getActivitiesAboutMeUnread(@Query("cursor") boolean cursor) throws MicroBlogException;
+
+ @Queries({})
+ @POST("/activity/about_me/unread.json")
+ @BodyType(BodyType.FORM)
+ CursorTimestampResponse setActivitiesAboutMeUnread(@Param("cursor") long cursor) throws MicroBlogException;
+
+
+}
diff --git a/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateDirectMessagesResources.java b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateDirectMessagesResources.java
new file mode 100644
index 000000000..8aedefa37
--- /dev/null
+++ b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateDirectMessagesResources.java
@@ -0,0 +1,102 @@
+/*
+ * Twidere - Twitter client for Android
+ *
+ * Copyright 2012-2017 Mariotaku Lee
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mariotaku.microblog.library.twitter.api;
+
+import android.support.annotation.Nullable;
+
+import org.mariotaku.microblog.library.MicroBlogException;
+import org.mariotaku.microblog.library.twitter.model.ConversationTimeline;
+import org.mariotaku.microblog.library.twitter.model.DMResponse;
+import org.mariotaku.microblog.library.twitter.model.NewDm;
+import org.mariotaku.microblog.library.twitter.model.Paging;
+import org.mariotaku.microblog.library.twitter.model.ResponseCode;
+import org.mariotaku.microblog.library.twitter.model.UserEvents;
+import org.mariotaku.microblog.library.twitter.model.UserInbox;
+import org.mariotaku.microblog.library.twitter.template.DMAnnotationTemplate;
+import org.mariotaku.restfu.annotation.method.GET;
+import org.mariotaku.restfu.annotation.method.POST;
+import org.mariotaku.restfu.annotation.param.Param;
+import org.mariotaku.restfu.annotation.param.Params;
+import org.mariotaku.restfu.annotation.param.Path;
+import org.mariotaku.restfu.annotation.param.Query;
+import org.mariotaku.restfu.http.BodyType;
+
+@Params(template = DMAnnotationTemplate.class)
+public interface PrivateDirectMessagesResources extends PrivateResources {
+
+ @POST("/dm/conversation/{conversation_id}/delete.json")
+ @BodyType(BodyType.FORM)
+ ResponseCode deleteDmConversation(@Path("conversation_id") String conversationId)
+ throws MicroBlogException;
+
+ @POST("/dm/conversation/{conversation_id}/mark_read.json")
+ @BodyType(BodyType.FORM)
+ ResponseCode markDmRead(@Path("conversation_id") String conversationId,
+ @Param("last_read_event_id") String lastReadEventId) throws MicroBlogException;
+
+ @POST("/dm/update_last_seen_event_id.json")
+ @BodyType(BodyType.FORM)
+ ResponseCode updateLastSeenEventId(@Param("last_seen_event_id") String lastSeenEventId) throws MicroBlogException;
+
+ @POST("/dm/conversation/{conversation_id}/update_name.json")
+ @BodyType(BodyType.FORM)
+ ResponseCode updateDmConversationName(@Path("conversation_id") String conversationId,
+ @Param("name") String name) throws MicroBlogException;
+
+ /**
+ * Update DM conversation avatar
+ *
+ * @param conversationId DM conversation ID
+ * @param avatarId Avatar media ID, null for removing avatar
+ * @return HTTP response code
+ */
+ @POST("/dm/conversation/{conversation_id}/update_avatar.json")
+ @BodyType(BodyType.FORM)
+ ResponseCode updateDmConversationAvatar(@Path("conversation_id") String conversationId,
+ @Param(value = "avatar_id", ignoreOnNull = true) @Nullable String avatarId) throws MicroBlogException;
+
+ @POST("/dm/conversation/{conversation_id}/disable_notifications.json")
+ ResponseCode disableDmConversations(@Path("conversation_id") String conversationId)
+ throws MicroBlogException;
+
+ @POST("/dm/conversation/{conversation_id}/enable_notifications.json")
+ ResponseCode enableDmConversations(@Path("conversation_id") String conversationId)
+ throws MicroBlogException;
+
+ @POST("/dm/new.json")
+ DMResponse sendDm(@Param NewDm newDm) throws MicroBlogException;
+
+ @POST("/dm/conversation/{conversation_id}/add_participants.json")
+ DMResponse addParticipants(@Path("conversation_id") String conversationId,
+ @Param(value = "participant_ids", arrayDelimiter = ',') String[] participantIds)
+ throws MicroBlogException;
+
+ @POST("/dm/destroy.json")
+ ResponseCode destroyDm(@Param("dm_id") String id) throws MicroBlogException;
+
+ @GET("/dm/user_inbox.json")
+ UserInbox getUserInbox(@Query Paging paging) throws MicroBlogException;
+
+ @GET("/dm/user_updates.json")
+ UserEvents getUserUpdates(@Query("cursor") String cursor) throws MicroBlogException;
+
+ @GET("/dm/conversation/{conversation_id}.json")
+ ConversationTimeline getDmConversation(@Path("conversation_id") String conversationId,
+ @Query Paging paging) throws MicroBlogException;
+}
diff --git a/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateFriendsFollowersResources.java b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateFriendsFollowersResources.java
new file mode 100644
index 000000000..82cbedbd7
--- /dev/null
+++ b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateFriendsFollowersResources.java
@@ -0,0 +1,43 @@
+/*
+ * Twidere - Twitter client for Android
+ *
+ * Copyright 2012-2017 Mariotaku Lee
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mariotaku.microblog.library.twitter.api;
+
+import org.mariotaku.microblog.library.MicroBlogException;
+import org.mariotaku.microblog.library.twitter.model.User;
+import org.mariotaku.restfu.annotation.method.POST;
+import org.mariotaku.restfu.annotation.param.KeyValue;
+import org.mariotaku.restfu.annotation.param.Param;
+import org.mariotaku.restfu.annotation.param.Queries;
+
+@Queries({@KeyValue(key = "include_entities", valueKey = "include_entities")})
+public interface PrivateFriendsFollowersResources extends PrivateResources {
+
+ @POST("/friendships/accept.json")
+ User acceptFriendship(@Param("user_id") String userId) throws MicroBlogException;
+
+ @POST("/friendships/accept.json")
+ User acceptFriendshipByScreenName(@Param("screen_name") String screenName) throws MicroBlogException;
+
+ @POST("/friendships/deny.json")
+ User denyFriendship(@Param("user_id") String userId) throws MicroBlogException;
+
+ @POST("/friendships/deny.json")
+ User denyFriendshipByScreenName(@Param("screen_name") String screenName) throws MicroBlogException;
+
+}
diff --git a/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateMutesResources.java b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateMutesResources.java
new file mode 100644
index 000000000..deb4e98dc
--- /dev/null
+++ b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateMutesResources.java
@@ -0,0 +1,42 @@
+/*
+ * Twidere - Twitter client for Android
+ *
+ * Copyright 2012-2017 Mariotaku Lee
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mariotaku.microblog.library.twitter.api;
+
+import org.mariotaku.microblog.library.MicroBlogException;
+import org.mariotaku.microblog.library.twitter.model.IDs;
+import org.mariotaku.microblog.library.twitter.model.MutedKeyword;
+import org.mariotaku.microblog.library.twitter.model.PageableResponseList;
+import org.mariotaku.microblog.library.twitter.model.Paging;
+import org.mariotaku.microblog.library.twitter.template.UserAnnotationTemplate;
+import org.mariotaku.restfu.annotation.method.GET;
+import org.mariotaku.restfu.annotation.param.Params;
+import org.mariotaku.restfu.annotation.param.Query;
+
+/**
+ * Created by mariotaku on 2017/3/26.
+ */
+@Params(template = UserAnnotationTemplate.class)
+public interface PrivateMutesResources {
+
+ @GET("/mutes/keywords/ids.json")
+ IDs getMutesKeywordsIDs(Paging paging) throws MicroBlogException;
+
+ @GET("/mutes/keywords/list.json")
+ PageableResponseList getMutesKeywordsList(@Query Paging paging) throws MicroBlogException;
+}
diff --git a/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateResources.java b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateResources.java
new file mode 100644
index 000000000..03a6f8c98
--- /dev/null
+++ b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateResources.java
@@ -0,0 +1,23 @@
+/*
+ * Twidere - Twitter client for Android
+ *
+ * Copyright 2012-2017 Mariotaku Lee
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mariotaku.microblog.library.twitter.api;
+
+public interface PrivateResources {
+
+}
diff --git a/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateSearchResources.java b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateSearchResources.java
new file mode 100644
index 000000000..750fd4dac
--- /dev/null
+++ b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateSearchResources.java
@@ -0,0 +1,38 @@
+/*
+ * Twidere - Twitter client for Android
+ *
+ * Copyright 2012-2017 Mariotaku Lee
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mariotaku.microblog.library.twitter.api;
+
+import org.mariotaku.microblog.library.MicroBlogException;
+import org.mariotaku.microblog.library.twitter.model.UniversalSearchQuery;
+import org.mariotaku.microblog.library.twitter.model.UniversalSearchResult;
+import org.mariotaku.microblog.library.twitter.template.StatusAnnotationTemplate;
+import org.mariotaku.restfu.annotation.method.GET;
+import org.mariotaku.restfu.annotation.param.Params;
+import org.mariotaku.restfu.annotation.param.Query;
+
+/**
+ * Created by mariotaku on 15/10/21.
+ */
+public interface PrivateSearchResources extends PrivateResources {
+
+ @GET("/search/universal.json")
+ @Params(template = StatusAnnotationTemplate.class)
+ UniversalSearchResult universalSearch(@Query UniversalSearchQuery query) throws MicroBlogException;
+
+}
diff --git a/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateTimelineResources.java b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateTimelineResources.java
new file mode 100644
index 000000000..3b589dac1
--- /dev/null
+++ b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateTimelineResources.java
@@ -0,0 +1,39 @@
+/*
+ * Twidere - Twitter client for Android
+ *
+ * Copyright 2012-2017 Mariotaku Lee
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mariotaku.microblog.library.twitter.api;
+
+import org.mariotaku.microblog.library.MicroBlogException;
+import org.mariotaku.microblog.library.twitter.model.Paging;
+import org.mariotaku.microblog.library.twitter.model.ResponseList;
+import org.mariotaku.microblog.library.twitter.model.Status;
+import org.mariotaku.microblog.library.twitter.template.StatusAnnotationTemplate;
+import org.mariotaku.restfu.annotation.method.GET;
+import org.mariotaku.restfu.annotation.param.Params;
+import org.mariotaku.restfu.annotation.param.Query;
+
+@SuppressWarnings("RedundantThrows")
+@Params(template = StatusAnnotationTemplate.class)
+public interface PrivateTimelineResources extends PrivateResources {
+
+ @GET("/statuses/media_timeline.json")
+ ResponseList getMediaTimeline(@Query("user_id") String userId, @Query Paging paging) throws MicroBlogException;
+
+ @GET("/statuses/media_timeline.json")
+ ResponseList getMediaTimelineByScreenName(@Query("screen_name") String screenName, @Query Paging paging) throws MicroBlogException;
+}
diff --git a/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateTweetResources.java b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateTweetResources.java
new file mode 100644
index 000000000..de7a033d4
--- /dev/null
+++ b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateTweetResources.java
@@ -0,0 +1,45 @@
+/*
+ * Twidere - Twitter client for Android
+ *
+ * Copyright 2012-2017 Mariotaku Lee
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mariotaku.microblog.library.twitter.api;
+
+import org.mariotaku.microblog.library.MicroBlogException;
+import org.mariotaku.microblog.library.twitter.model.Paging;
+import org.mariotaku.microblog.library.twitter.model.ResponseList;
+import org.mariotaku.microblog.library.twitter.model.Status;
+import org.mariotaku.microblog.library.twitter.model.StatusActivitySummary;
+import org.mariotaku.microblog.library.twitter.model.TranslationResult;
+import org.mariotaku.microblog.library.twitter.template.StatusAnnotationTemplate;
+import org.mariotaku.restfu.annotation.method.GET;
+import org.mariotaku.restfu.annotation.param.Params;
+import org.mariotaku.restfu.annotation.param.Path;
+import org.mariotaku.restfu.annotation.param.Query;
+
+@SuppressWarnings("RedundantThrows")
+@Params(template = StatusAnnotationTemplate.class)
+public interface PrivateTweetResources extends PrivateResources {
+
+ @GET("/statuses/{id}/activity/summary.json")
+ StatusActivitySummary getStatusActivitySummary(@Path("id") String statusId) throws MicroBlogException;
+
+ @GET("/conversation/show.json")
+ ResponseList showConversation(@Query("id") String statusId, @Query Paging paging) throws MicroBlogException;
+
+ @GET("/translations/show.json")
+ TranslationResult showTranslation(@Query("id") String statusId, @Query("dest") String dest) throws MicroBlogException;
+}
diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/ConsumerKeyType.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/ConsumerKeyType.java
index 549db2714..944d023b0 100644
--- a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/ConsumerKeyType.java
+++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/ConsumerKeyType.java
@@ -24,7 +24,8 @@ import androidx.annotation.NonNull;
* Created by mariotaku on 15/4/20.
*/
public enum ConsumerKeyType {
- UNKNOWN;
+ TWITTER_FOR_ANDROID, TWITTER_FOR_IPHONE, TWITTER_FOR_IPAD, TWITTER_FOR_MAC,
+ TWITTER_FOR_WINDOWS_PHONE, TWITTER_FOR_GOOGLE_TV, TWEETDECK, UNKNOWN;
@NonNull
public static ConsumerKeyType parse(String type) {
diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/account/TwitterAccountExtras.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/account/TwitterAccountExtras.java
index 0e516b757..7eb204877 100644
--- a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/account/TwitterAccountExtras.java
+++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/account/TwitterAccountExtras.java
@@ -21,8 +21,10 @@ package org.mariotaku.twidere.model.account;
import android.os.Parcel;
import android.os.Parcelable;
+import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import com.hannesdorfmann.parcelableplease.annotation.ParcelablePlease;
+import com.hannesdorfmann.parcelableplease.annotation.ParcelableThisPlease;
/**
* Created by mariotaku on 16/2/26.
@@ -45,6 +47,17 @@ public class TwitterAccountExtras implements Parcelable, AccountExtras {
}
};
+ @JsonField(name = "official_credentials")
+ @ParcelableThisPlease
+ boolean officialCredentials;
+
+ public boolean isOfficialCredentials() {
+ return officialCredentials;
+ }
+
+ public void setIsOfficialCredentials(boolean officialCredentials) {
+ this.officialCredentials = officialCredentials;
+ }
@Override
public int describeContents() {
@@ -59,6 +72,7 @@ public class TwitterAccountExtras implements Parcelable, AccountExtras {
@Override
public String toString() {
return "TwitterAccountExtras{" +
+ "officialCredentials=" + officialCredentials +
'}';
}
}
diff --git a/twidere/src/main/java/org/mariotaku/twidere/model/util/AccountUtils.java b/twidere/src/main/java/org/mariotaku/twidere/model/util/AccountUtils.java
index 7a7a01985..65161e0e8 100644
--- a/twidere/src/main/java/org/mariotaku/twidere/model/util/AccountUtils.java
+++ b/twidere/src/main/java/org/mariotaku/twidere/model/util/AccountUtils.java
@@ -129,6 +129,23 @@ public class AccountUtils {
return null;
}
+ public static boolean isOfficial(@NonNull final Context context, @NonNull final UserKey accountKey) {
+ AccountManager am = AccountManager.get(context);
+ Account account = AccountUtils.findByAccountKey(am, accountKey);
+ if (account == null) return false;
+ return AccountExtensionsKt.isOfficial(account, am, context);
+ }
+
+ public static boolean hasOfficialKeyAccount(Context context) {
+ final AccountManager am = AccountManager.get(context);
+ for (Account account : getAccounts(am)) {
+ if (AccountExtensionsKt.isOfficial(account, am, context)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public static int getAccountTypeIcon(@Nullable String accountType) {
if (accountType == null) return R.drawable.ic_account_logo_twitter;
switch (accountType) {
diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/InternalTwitterContentUtils.java b/twidere/src/main/java/org/mariotaku/twidere/util/InternalTwitterContentUtils.java
index 8690cdbfd..fdef0f3b5 100644
--- a/twidere/src/main/java/org/mariotaku/twidere/util/InternalTwitterContentUtils.java
+++ b/twidere/src/main/java/org/mariotaku/twidere/util/InternalTwitterContentUtils.java
@@ -9,9 +9,14 @@ import org.mariotaku.microblog.library.twitter.model.DirectMessage;
import org.mariotaku.microblog.library.twitter.model.MediaEntity;
import org.mariotaku.microblog.library.twitter.model.UrlEntity;
import org.mariotaku.microblog.library.twitter.model.User;
+import org.mariotaku.twidere.R;
import org.mariotaku.twidere.extension.model.api.StatusExtensionsKt;
+import org.mariotaku.twidere.model.ConsumerKeyType;
import org.mariotaku.twidere.model.SpanItem;
+import java.nio.charset.Charset;
+import java.util.zip.CRC32;
+
import kotlin.Pair;
/**
@@ -96,4 +101,55 @@ public class InternalTwitterContentUtils {
}
+ public static boolean isOfficialKey(final Context context, final String consumerKey,
+ final String consumerSecret) {
+ if (context == null || consumerKey == null || consumerSecret == null) return false;
+ final String[] keySecrets = context.getResources().getStringArray(R.array.values_official_consumer_secret_crc32);
+ final CRC32 crc32 = new CRC32();
+ final byte[] consumerSecretBytes = consumerSecret.getBytes(Charset.forName("UTF-8"));
+ crc32.update(consumerSecretBytes, 0, consumerSecretBytes.length);
+ final long value = crc32.getValue();
+ crc32.reset();
+ for (final String keySecret : keySecrets) {
+ if (Long.parseLong(keySecret, 16) == value) return true;
+ }
+ return false;
+ }
+
+ public static String getOfficialKeyName(final Context context, final String consumerKey,
+ final String consumerSecret) {
+ if (context == null || consumerKey == null || consumerSecret == null) return null;
+ final String[] keySecrets = context.getResources().getStringArray(R.array.values_official_consumer_secret_crc32);
+ final String[] keyNames = context.getResources().getStringArray(R.array.names_official_consumer_secret);
+ final CRC32 crc32 = new CRC32();
+ final byte[] consumerSecretBytes = consumerSecret.getBytes(Charset.forName("UTF-8"));
+ crc32.update(consumerSecretBytes, 0, consumerSecretBytes.length);
+ final long value = crc32.getValue();
+ crc32.reset();
+ for (int i = 0, j = keySecrets.length; i < j; i++) {
+ if (Long.parseLong(keySecrets[i], 16) == value) return keyNames[i];
+ }
+ return null;
+ }
+
+ @NonNull
+ public static ConsumerKeyType getOfficialKeyType(final Context context, final String consumerKey,
+ final String consumerSecret) {
+ if (context == null || consumerKey == null || consumerSecret == null) {
+ return ConsumerKeyType.UNKNOWN;
+ }
+ final String[] keySecrets = context.getResources().getStringArray(R.array.values_official_consumer_secret_crc32);
+ final String[] keyNames = context.getResources().getStringArray(R.array.types_official_consumer_secret);
+ final CRC32 crc32 = new CRC32();
+ final byte[] consumerSecretBytes = consumerSecret.getBytes(Charset.forName("UTF-8"));
+ crc32.update(consumerSecretBytes, 0, consumerSecretBytes.length);
+ final long value = crc32.getValue();
+ crc32.reset();
+ for (int i = 0, j = keySecrets.length; i < j; i++) {
+ if (Long.parseLong(keySecrets[i], 16) == value) {
+ return ConsumerKeyType.parse(keyNames[i]);
+ }
+ }
+ return ConsumerKeyType.UNKNOWN;
+ }
}
diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/MicroBlogAPIFactory.java b/twidere/src/main/java/org/mariotaku/twidere/util/MicroBlogAPIFactory.java
index 456a15849..bca1bf941 100644
--- a/twidere/src/main/java/org/mariotaku/twidere/util/MicroBlogAPIFactory.java
+++ b/twidere/src/main/java/org/mariotaku/twidere/util/MicroBlogAPIFactory.java
@@ -166,6 +166,21 @@ public class MicroBlogAPIFactory implements TwidereConstants {
@WorkerThread
@Nullable
public static ExtraHeaders getExtraHeaders(Context context, ConsumerKeyType type) {
+ switch (type) {
+ case TWITTER_FOR_ANDROID: {
+ return TwitterAndroidExtraHeaders.INSTANCE;
+ }
+ case TWITTER_FOR_IPHONE:
+ case TWITTER_FOR_IPAD: {
+ return new UserAgentExtraHeaders("Twitter/6.75.2 CFNetwork/811.4.18 Darwin/16.5.0");
+ }
+ case TWITTER_FOR_MAC: {
+ return TwitterMacExtraHeaders.INSTANCE;
+ }
+ case TWEETDECK: {
+ return new UserAgentExtraHeaders(UserAgentUtils.getDefaultUserAgentStringSafe(context));
+ }
+ }
return null;
}
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/SignInActivity.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/SignInActivity.kt
index 32375b2d5..04efc0f8f 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/SignInActivity.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/SignInActivity.kt
@@ -70,6 +70,7 @@ import org.mariotaku.microblog.library.mastodon.annotation.AuthScope
import org.mariotaku.microblog.library.twitter.TwitterOAuth
import org.mariotaku.microblog.library.twitter.auth.BasicAuthorization
import org.mariotaku.microblog.library.twitter.auth.EmptyAuthorization
+import org.mariotaku.microblog.library.twitter.model.Paging
import org.mariotaku.microblog.library.twitter.model.User
import org.mariotaku.restfu.http.Endpoint
import org.mariotaku.restfu.oauth.OAuthToken
@@ -410,7 +411,7 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher,
result.addAccount(am, preferences[randomizeAccountNameKey])
Analyzer.log(SignIn(true, accountType = result.type,
credentialsType = apiConfig.credentialsType,
- officialKey = false))
+ officialKey = result.extras?.official == true))
finishSignIn()
}
}
@@ -1220,7 +1221,17 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher,
}
private fun getTwitterAccountExtras(twitter: MicroBlog): TwitterAccountExtras {
- return TwitterAccountExtras()
+ val extras = TwitterAccountExtras()
+ try {
+ // Get Twitter official only resource
+ val paging = Paging()
+ paging.count(1)
+ twitter.getActivitiesAboutMe(paging)
+ extras.setIsOfficialCredentials(true)
+ } catch (e: MicroBlogException) {
+ // Ignore
+ }
+ return extras
}
private fun getMastodonAccountExtras(mastodon: Mastodon): MastodonAccountExtras {
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/AccountDetailsExtensions.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/AccountDetailsExtensions.kt
index 00ef776f2..ed59385da 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/AccountDetailsExtensions.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/AccountDetailsExtensions.kt
@@ -16,6 +16,27 @@ import org.mariotaku.twidere.util.text.FanfouValidator
import org.mariotaku.twidere.util.text.MastodonValidator
import org.mariotaku.twidere.util.text.TwitterValidator
+fun AccountDetails.isOfficial(context: Context): Boolean {
+ val extra = this.extras
+ if (extra is TwitterAccountExtras) {
+ return extra.isOfficialCredentials
+ }
+ val credentials = this.credentials
+ if (credentials is OAuthCredentials) {
+ return InternalTwitterContentUtils.isOfficialKey(context,
+ credentials.consumer_key, credentials.consumer_secret)
+ }
+ return false
+}
+
+val AccountExtras.official: Boolean
+ get() {
+ if (this is TwitterAccountExtras) {
+ return isOfficialCredentials
+ }
+ return false
+ }
+
fun AccountDetails.newMicroBlogInstance(context: Context, cls: Class): T {
return credentials.newMicroBlogInstance(context, type, cls)
}
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/AccountExtensions.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/AccountExtensions.kt
index c0be5d256..930b3e128 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/AccountExtensions.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/AccountExtensions.kt
@@ -100,6 +100,19 @@ fun Account.setPosition(am: AccountManager, position: Int) {
am.setUserData(this, ACCOUNT_USER_DATA_POSITION, position.toString())
}
+fun Account.isOfficial(am: AccountManager, context: Context): Boolean {
+ val extras = getAccountExtras(am)
+ if (extras is TwitterAccountExtras) {
+ return extras.isOfficialCredentials
+ }
+ val credentials = getCredentials(am)
+ if (credentials is OAuthCredentials) {
+ return InternalTwitterContentUtils.isOfficialKey(context, credentials.consumer_key,
+ credentials.consumer_secret)
+ }
+ return false
+}
+
fun AccountManager.hasInvalidAccount(): Boolean {
val accounts = AccountUtils.getAccounts(this)
if (accounts.isEmpty()) return false
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/CredentialsExtensions.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/CredentialsExtensions.kt
index 135c5e442..3a4b864be 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/CredentialsExtensions.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/CredentialsExtensions.kt
@@ -23,7 +23,6 @@ import org.mariotaku.restfu.oauth.OAuthToken
import org.mariotaku.restfu.oauth2.OAuth2Authorization
import org.mariotaku.twidere.TwidereConstants.DEFAULT_TWITTER_API_URL_FORMAT
import org.mariotaku.twidere.annotation.AccountType
-import org.mariotaku.twidere.model.ConsumerKeyType
import org.mariotaku.twidere.model.account.cred.*
import org.mariotaku.twidere.util.HttpClientFactory
import org.mariotaku.twidere.util.InternalTwitterContentUtils
@@ -149,7 +148,9 @@ fun newMicroBlogInstance(context: Context, endpoint: Endpoint, auth: Authori
val factory = RestAPIFactory()
val extraHeaders = run {
if (auth !is OAuthAuthorization) return@run null
- return@run MicroBlogAPIFactory.getExtraHeaders(context, ConsumerKeyType.UNKNOWN)
+ val officialKeyType = InternalTwitterContentUtils.getOfficialKeyType(context,
+ auth.consumerKey, auth.consumerSecret)
+ return@run MicroBlogAPIFactory.getExtraHeaders(context, officialKeyType)
} ?: UserAgentExtraHeaders(MicroBlogAPIFactory.getTwidereUserAgent(context))
val holder = DependencyHolder.get(context)
var extraRequestParams: Map? = null
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AbsActivitiesFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AbsActivitiesFragment.kt
index f90216a0e..ac0ef2730 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AbsActivitiesFragment.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AbsActivitiesFragment.kt
@@ -306,10 +306,13 @@ abstract class AbsActivitiesFragment protected constructor() :
override fun onGapClick(holder: GapViewHolder, position: Int) {
val activity = adapter.getActivity(position)
DebugLog.v(msg = "Load activity gap $activity")
- if (activity.action !in Activity.Action.MENTION_ACTIONS) {
- adapter.removeGapLoadingId(ObjectId(activity.account_key, activity.id))
- adapter.notifyItemChanged(position)
- return
+ if (!AccountUtils.isOfficial(context, activity.account_key)) {
+ // Skip if item is not a status
+ if (activity.action !in Activity.Action.MENTION_ACTIONS) {
+ adapter.removeGapLoadingId(ObjectId(activity.account_key, activity.id))
+ adapter.notifyItemChanged(position)
+ return
+ }
}
val accountKeys = arrayOf(activity.account_key)
val pagination = arrayOf(SinceMaxPagination.maxId(activity.min_position,
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/CustomTabsFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/CustomTabsFragment.kt
index 1631299c9..c42d028db 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/CustomTabsFragment.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/CustomTabsFragment.kt
@@ -60,6 +60,7 @@ import org.mariotaku.twidere.adapter.ArrayAdapter
import org.mariotaku.twidere.annotation.CustomTabType
import org.mariotaku.twidere.annotation.TabAccountFlags
import org.mariotaku.twidere.extension.applyTheme
+import org.mariotaku.twidere.extension.model.isOfficial
import org.mariotaku.twidere.model.AccountDetails
import org.mariotaku.twidere.model.Tab
import org.mariotaku.twidere.model.tab.DrawableHolder
@@ -289,9 +290,9 @@ class CustomTabsFragment : BaseFragment(), LoaderCallbacks, MultiChoice
if (!accountRequired) {
accountsAdapter.add(AccountDetails.dummy())
}
- val officialKeyOnly = currentArguments.getBoolean(EXTRA_OFFICIAL_KEY_ONLY, false)
- accountsAdapter.addAll(AccountUtils.getAllAccountDetails(AccountManager.get(currentContext), true).filter {
- if (officialKeyOnly) {
+ val officialKeyOnly = arguments.getBoolean(EXTRA_OFFICIAL_KEY_ONLY, false)
+ accountsAdapter.addAll(AccountUtils.getAllAccountDetails(AccountManager.get(context), true).filter {
+ if (officialKeyOnly && !it.isOfficial(context)) {
return@filter false
}
return@filter conf.checkAccountAvailability(it)
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/message/MessageConversationInfoFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/message/MessageConversationInfoFragment.kt
index 6086b446e..6af87206c 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/message/MessageConversationInfoFragment.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/message/MessageConversationInfoFragment.kt
@@ -61,6 +61,7 @@ import org.mariotaku.ktextension.spannable
import org.mariotaku.library.objectcursor.ObjectCursor
import org.mariotaku.microblog.library.MicroBlog
import org.mariotaku.microblog.library.MicroBlogException
+import org.mariotaku.microblog.library.twitter.TwitterUpload
import org.mariotaku.pickncrop.library.MediaPickerActivity
import org.mariotaku.sqliteqb.library.Expression
import org.mariotaku.twidere.R
@@ -90,6 +91,8 @@ import org.mariotaku.twidere.model.ParcelableMessageConversation.ConversationTyp
import org.mariotaku.twidere.model.ParcelableMessageConversation.ExtrasType
import org.mariotaku.twidere.model.util.AccountUtils
import org.mariotaku.twidere.provider.TwidereDataStore.Messages.Conversations
+import org.mariotaku.twidere.task.twitter.UpdateStatusTask
+import org.mariotaku.twidere.task.twitter.message.AddParticipantsTask
import org.mariotaku.twidere.task.twitter.message.ClearMessagesTask
import org.mariotaku.twidere.task.twitter.message.DestroyConversationTask
import org.mariotaku.twidere.task.twitter.message.SetConversationNotificationDisabledTask
@@ -184,6 +187,12 @@ class MessageConversationInfoFragment : BaseFragment(), IToolBarSupportFragment,
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
+ REQUEST_CONVERSATION_ADD_USER -> {
+ if (resultCode == Activity.RESULT_OK && data != null) {
+ val user = data.getParcelableExtra(EXTRA_USER)
+ performAddParticipant(user)
+ }
+ }
REQUEST_PICK_MEDIA -> {
when (resultCode) {
Activity.RESULT_OK -> {
@@ -318,6 +327,19 @@ class MessageConversationInfoFragment : BaseFragment(), IToolBarSupportFragment,
TaskStarter.execute(task)
}
+ private fun performAddParticipant(user: ParcelableUser) {
+ ProgressDialogFragment.show(childFragmentManager, "add_participant_progress")
+ val weakThis = WeakReference(this)
+ val task = AddParticipantsTask(context, accountKey, conversationId, listOf(user))
+ task.callback = callback@ { succeed ->
+ val f = weakThis.get() ?: return@callback
+ f.dismissDialogThen("add_participant_progress") {
+ loaderManager.restartLoader(0, null, this)
+ }
+ }
+ TaskStarter.execute(task)
+ }
+
private fun performSetNotificationDisabled(disabled: Boolean) {
ProgressDialogFragment.show(childFragmentManager, "set_notifications_disabled_progress")
val weakThis = WeakReference(this)
@@ -361,6 +383,9 @@ class MessageConversationInfoFragment : BaseFragment(), IToolBarSupportFragment,
val context = fragment.context
when (account.type) {
AccountType.TWITTER -> {
+ if (account.isOfficial(context)) {
+ return@updateAction microBlog.updateDmConversationName(conversationId, name).isSuccessful
+ }
}
}
throw UnsupportedOperationException()
@@ -370,6 +395,53 @@ class MessageConversationInfoFragment : BaseFragment(), IToolBarSupportFragment,
}
private fun performSetConversationAvatar(uri: Uri?) {
+ val conversationId = this.conversationId
+ performUpdateInfo("set_avatar_progress", updateAction = updateAction@ { fragment, account, microBlog ->
+ val context = fragment.context
+ when (account.type) {
+ AccountType.TWITTER -> {
+ if (account.isOfficial(context)) {
+ val upload = account.newMicroBlogInstance(context, cls = TwitterUpload::class.java)
+ if (uri == null) {
+ val result = microBlog.updateDmConversationAvatar(conversationId, null)
+ if (result.isSuccessful) {
+ val dmResponse = microBlog.getDmConversation(conversationId, null).conversationTimeline
+ return@updateAction dmResponse.conversations[conversationId]?.avatarImageHttps
+ }
+ throw MicroBlogException("Error ${result.responseCode}")
+ }
+ var deleteAlways: List? = null
+ try {
+ val media = arrayOf(ParcelableMediaUpdate().apply {
+ this.uri = uri.toString()
+ this.delete_always = true
+ })
+ val uploadResult = UpdateStatusTask.uploadMicroBlogMediaShared(context,
+ upload, account, media, null, null, true, null)
+ deleteAlways = uploadResult.deleteAlways
+ val avatarId = uploadResult.ids.first()
+ val result = microBlog.updateDmConversationAvatar(conversationId, avatarId)
+ if (result.isSuccessful) {
+ uploadResult.deleteOnSuccess.forEach { it.delete(context) }
+ val dmResponse = microBlog.getDmConversation(conversationId, null).conversationTimeline
+ return@updateAction dmResponse.conversations[conversationId]?.avatarImageHttps
+ }
+ throw MicroBlogException("Error ${result.responseCode}")
+ } catch (e: UpdateStatusTask.UploadException) {
+ e.deleteAlways?.forEach {
+ it.delete(context)
+ }
+ throw e
+ } finally {
+ deleteAlways?.forEach { it.delete(context) }
+ }
+ }
+ }
+ }
+ throw UnsupportedOperationException()
+ }, successAction = { uri ->
+ put(Conversations.CONVERSATION_AVATAR, uri)
+ })
}
private inline fun performUpdateInfo(
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/message/MessageNewConversationFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/message/MessageNewConversationFragment.kt
index 63d320280..25480b318 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/message/MessageNewConversationFragment.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/message/MessageNewConversationFragment.kt
@@ -44,6 +44,7 @@ import org.mariotaku.twidere.R
import org.mariotaku.twidere.adapter.SelectableUsersAdapter
import org.mariotaku.twidere.constant.IntentConstants.*
import org.mariotaku.twidere.constant.nameFirstKey
+import org.mariotaku.twidere.extension.model.isOfficial
import org.mariotaku.twidere.extension.queryOne
import org.mariotaku.twidere.extension.text.appendCompat
import org.mariotaku.twidere.fragment.BaseFragment
@@ -242,7 +243,11 @@ class MessageNewConversationFragment : BaseFragment(), LoaderCallbacks maxParticipants) {
editParticipants.error = getString(R.string.error_message_message_too_many_participants)
return
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/status/PinStatusDialogFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/status/PinStatusDialogFragment.kt
new file mode 100644
index 000000000..0657636ba
--- /dev/null
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/status/PinStatusDialogFragment.kt
@@ -0,0 +1,55 @@
+/*
+ * Twidere - Twitter client for Android
+ *
+ * Copyright (C) 2012-2017 Mariotaku Lee
+ *
+ * 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.
+ *
+ * This program 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 this program. If not, see .
+ */
+
+package org.mariotaku.twidere.fragment.status
+
+import android.os.Bundle
+import android.support.v4.app.FragmentManager
+import org.mariotaku.abstask.library.TaskStarter
+import org.mariotaku.twidere.R
+import org.mariotaku.twidere.constant.IntentConstants.EXTRA_STATUS
+import org.mariotaku.twidere.model.ParcelableStatus
+import org.mariotaku.twidere.task.status.PinStatusTask
+
+class PinStatusDialogFragment : AbsSimpleStatusOperationDialogFragment() {
+
+ override val title: String?
+ get() = getString(R.string.title_pin_status_confirm)
+ override val message: String
+ get() = getString(R.string.message_pin_status_confirm)
+
+ override fun onPerformAction(status: ParcelableStatus) {
+ val task = PinStatusTask(context, status.account_key, status.id)
+ TaskStarter.execute(task)
+ }
+
+ companion object {
+
+ val FRAGMENT_TAG = "pin_status"
+
+ fun show(fm: FragmentManager, status: ParcelableStatus): PinStatusDialogFragment {
+ val args = Bundle()
+ args.putParcelable(EXTRA_STATUS, status)
+ val f = PinStatusDialogFragment()
+ f.arguments = args
+ f.show(fm, FRAGMENT_TAG)
+ return f
+ }
+ }
+}
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/status/StatusFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/status/StatusFragment.kt
index 058a157e2..4461bb170 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/status/StatusFragment.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/status/StatusFragment.kt
@@ -123,6 +123,7 @@ class StatusFragment : BaseFragment(), LoaderCallbacks
+ private var loadTranslationTask: LoadTranslationTask? = null
// Data fields
private var conversationLoaderInitialized: Boolean = false
@@ -505,9 +506,18 @@ class StatusFragment : BaseFragment(), LoaderCallbacks?) {
@@ -673,6 +683,37 @@ class StatusFragment : BaseFragment(), LoaderCallbacks(fragment.context, status.account_key) {
+
+ private val weakFragment = WeakReference(fragment)
+
+ override fun onExecute(account: AccountDetails, params: Any?): TranslationResult {
+ val twitter = account.newMicroBlogInstance(context, MicroBlog::class.java)
+ val prefDest = preferences.getString(KEY_TRANSLATION_DESTINATION, null).orEmpty()
+ val dest: String
+ if (TextUtils.isEmpty(prefDest)) {
+ dest = twitter.accountSettings.language
+ val editor = preferences.edit()
+ editor.putString(KEY_TRANSLATION_DESTINATION, dest)
+ editor.apply()
+ } else {
+ dest = prefDest
+ }
+ return twitter.showTranslation(status.originalId, dest)
+ }
+
+ override fun onSucceed(callback: Any?, result: TranslationResult) {
+ val fragment = weakFragment.get() ?: return
+ fragment.displayTranslation(result)
+ }
+
+ override fun onException(callback: Any?, exception: MicroBlogException) {
+ Toast.makeText(context, exception.getErrorMessage(context), Toast.LENGTH_SHORT).show()
+ }
+ }
+
+
class StatusActivitySummaryLoader(
context: Context,
private val accountKey: UserKey,
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/status/UnpinStatusDialogFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/status/UnpinStatusDialogFragment.kt
new file mode 100644
index 000000000..1d1190140
--- /dev/null
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/status/UnpinStatusDialogFragment.kt
@@ -0,0 +1,55 @@
+/*
+ * Twidere - Twitter client for Android
+ *
+ * Copyright (C) 2012-2017 Mariotaku Lee
+ *
+ * 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.
+ *
+ * This program 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 this program. If not, see .
+ */
+
+package org.mariotaku.twidere.fragment.status
+
+import android.os.Bundle
+import android.support.v4.app.FragmentManager
+import org.mariotaku.abstask.library.TaskStarter
+import org.mariotaku.twidere.R
+import org.mariotaku.twidere.constant.IntentConstants.EXTRA_STATUS
+import org.mariotaku.twidere.model.ParcelableStatus
+import org.mariotaku.twidere.task.status.UnpinStatusTask
+
+class UnpinStatusDialogFragment : AbsSimpleStatusOperationDialogFragment() {
+
+ override val title: String?
+ get() = getString(R.string.title_unpin_status_confirm)
+ override val message: String
+ get() = getString(R.string.message_unpin_status_confirm)
+
+ override fun onPerformAction(status: ParcelableStatus) {
+ val task = UnpinStatusTask(context, status.account_key, status.id)
+ TaskStarter.execute(task)
+ }
+
+ companion object {
+
+ val FRAGMENT_TAG = "unpin_status"
+
+ fun show(fm: FragmentManager, status: ParcelableStatus): UnpinStatusDialogFragment {
+ val args = Bundle()
+ args.putParcelable(EXTRA_STATUS, status)
+ val f = UnpinStatusDialogFragment()
+ f.arguments = args
+ f.show(fm, FRAGMENT_TAG)
+ return f
+ }
+ }
+}
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/users/IncomingFriendshipsFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/users/IncomingFriendshipsFragment.kt
index c28fa8977..50183a989 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/users/IncomingFriendshipsFragment.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/users/IncomingFriendshipsFragment.kt
@@ -32,6 +32,7 @@ import org.mariotaku.twidere.loader.users.AbsRequestUsersLoader
import org.mariotaku.twidere.loader.users.IncomingFriendshipsLoader
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.model.event.FriendshipTaskEvent
+import org.mariotaku.twidere.model.util.AccountUtils
import org.mariotaku.twidere.view.holder.UserViewHolder
class IncomingFriendshipsFragment : ParcelableUsersFragment(), IUsersAdapter.RequestClickListener {
@@ -48,6 +49,8 @@ class IncomingFriendshipsFragment : ParcelableUsersFragment(), IUsersAdapter.Req
val accountKey = arguments?.getParcelable(EXTRA_ACCOUNT_KEY) ?: return adapter
if (USER_TYPE_FANFOU_COM == accountKey.host) {
adapter.requestClickListener = this
+ } else if (AccountUtils.isOfficial(context, accountKey)) {
+ adapter.requestClickListener = this
}
return adapter
}
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/loader/statuses/ConversationLoader.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/loader/statuses/ConversationLoader.kt
index 975b74ccd..4a5d87030 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/loader/statuses/ConversationLoader.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/loader/statuses/ConversationLoader.kt
@@ -38,6 +38,7 @@ import org.mariotaku.twidere.extension.atto.filter
import org.mariotaku.twidere.extension.atto.firstElementOrNull
import org.mariotaku.twidere.extension.model.api.mastodon.toParcelable
import org.mariotaku.twidere.extension.model.api.toParcelable
+import org.mariotaku.twidere.extension.model.isOfficial
import org.mariotaku.twidere.extension.model.makeOriginal
import org.mariotaku.twidere.extension.model.newMicroBlogInstance
import org.mariotaku.twidere.model.AccountDetails
@@ -92,6 +93,11 @@ class ConversationLoader(
// val isOfficial = account.isOfficial(context)
val isOfficial = false
canLoadAllReplies = isOfficial
+ if (isOfficial) {
+ return microBlog.showConversation(status.id, paging).mapMicroBlogToPaginated {
+ it.toParcelable(account, profileImageSize)
+ }
+ }
return showConversationCompat(microBlog, account, status, true)
}
AccountType.STATUSNET -> {
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/loader/statuses/MediaStatusesSearchLoader.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/loader/statuses/MediaStatusesSearchLoader.kt
index 26077f5fc..7e0ef7027 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/loader/statuses/MediaStatusesSearchLoader.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/loader/statuses/MediaStatusesSearchLoader.kt
@@ -27,10 +27,12 @@ import org.mariotaku.microblog.library.MicroBlogException
import org.mariotaku.microblog.library.twitter.model.Paging
import org.mariotaku.microblog.library.twitter.model.SearchQuery
import org.mariotaku.microblog.library.twitter.model.Status
+import org.mariotaku.microblog.library.twitter.model.UniversalSearchQuery
import org.mariotaku.twidere.annotation.AccountType
import org.mariotaku.twidere.annotation.FilterScope
import org.mariotaku.twidere.extension.model.api.toParcelable
import org.mariotaku.twidere.extension.model.newMicroBlogInstance
+import org.mariotaku.twidere.extension.model.official
import org.mariotaku.twidere.model.AccountDetails
import org.mariotaku.twidere.model.ParcelableStatus
import org.mariotaku.twidere.model.UserKey
@@ -76,6 +78,9 @@ open class MediaStatusesSearchLoader(
protected open fun processQuery(details: AccountDetails, query: String): String {
if (details.type == AccountType.TWITTER) {
+ if (details.extras?.official == true) {
+ return TweetSearchLoader.smQuery("$query filter:media", pagination)
+ }
return "$query filter:media exclude:retweets"
}
return query
@@ -87,6 +92,15 @@ open class MediaStatusesSearchLoader(
val microBlog = account.newMicroBlogInstance(context, MicroBlog::class.java)
when (account.type) {
AccountType.TWITTER -> {
+ if (account.extras?.official == true) {
+ val universalQuery = UniversalSearchQuery(queryText)
+ universalQuery.setModules(UniversalSearchQuery.Module.TWEET)
+ universalQuery.setResultType(UniversalSearchQuery.ResultType.RECENT)
+ universalQuery.setPaging(paging)
+ val searchResult = microBlog.universalSearch(universalQuery)
+ return searchResult.modules.mapNotNull { it.status?.data }
+ }
+
val searchQuery = SearchQuery(queryText)
searchQuery.paging(paging)
return microBlog.search(searchQuery)
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/loader/statuses/MediaTimelineLoader.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/loader/statuses/MediaTimelineLoader.kt
index 359c644d1..f432a39b4 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/loader/statuses/MediaTimelineLoader.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/loader/statuses/MediaTimelineLoader.kt
@@ -34,6 +34,7 @@ import org.mariotaku.twidere.extension.model.api.mastodon.mapToPaginated
import org.mariotaku.twidere.extension.model.api.mastodon.toParcelable
import org.mariotaku.twidere.extension.model.api.toParcelable
import org.mariotaku.twidere.extension.model.api.updateFilterInfoForUserTimeline
+import org.mariotaku.twidere.extension.model.isOfficial
import org.mariotaku.twidere.extension.model.newMicroBlogInstance
import org.mariotaku.twidere.model.AccountDetails
import org.mariotaku.twidere.model.ParcelableStatus
@@ -89,23 +90,32 @@ class MediaTimelineLoader(
val microBlog = account.newMicroBlogInstance(context, MicroBlog::class.java)
when (account.type) {
AccountType.TWITTER -> {
- val screenName = this.screenName ?: run {
- return@run this.user ?: run fetchUser@ {
- if (userKey == null) throw MicroBlogException("Invalid parameters")
- val user = microBlog.tryShowUser(userKey.id, null, account.type)
- this.user = user
- return@fetchUser user
- }.screenName
+ if (account.isOfficial(context)) {
+ if (userKey != null) {
+ return microBlog.getMediaTimeline(userKey.id, paging)
+ }
+ if (screenName != null) {
+ return microBlog.getMediaTimelineByScreenName(screenName, paging)
+ }
+ } else {
+ val screenName = this.screenName ?: run {
+ return@run this.user ?: run fetchUser@ {
+ if (userKey == null) throw MicroBlogException("Invalid parameters")
+ val user = microBlog.tryShowUser(userKey.id, null, account.type)
+ this.user = user
+ return@fetchUser user
+ }.screenName
+ }
+ val query = SearchQuery("from:$screenName filter:media exclude:retweets")
+ query.paging(paging)
+ val result = ResponseList()
+ microBlog.search(query).filterTo(result) { status ->
+ val user = status.user
+ return@filterTo user.id == userKey?.id
+ || user.screenName.equals(this.screenName, ignoreCase = true)
+ }
+ return result
}
- val query = SearchQuery("from:$screenName filter:media exclude:retweets")
- query.paging(paging)
- val result = ResponseList()
- microBlog.search(query).filterTo(result) { status ->
- val user = status.user
- return@filterTo user.id == userKey?.id
- || user.screenName.equals(this.screenName, ignoreCase = true)
- }
- return result
throw MicroBlogException("Wrong user")
}
AccountType.FANFOU -> {
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/loader/statuses/TweetSearchLoader.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/loader/statuses/TweetSearchLoader.kt
index bafec138d..f91b735f9 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/loader/statuses/TweetSearchLoader.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/loader/statuses/TweetSearchLoader.kt
@@ -34,6 +34,7 @@ import org.mariotaku.twidere.extension.model.api.mastodon.mapToPaginated
import org.mariotaku.twidere.extension.model.api.mastodon.toParcelable
import org.mariotaku.twidere.extension.model.api.toParcelable
import org.mariotaku.twidere.extension.model.newMicroBlogInstance
+import org.mariotaku.twidere.extension.model.official
import org.mariotaku.twidere.model.AccountDetails
import org.mariotaku.twidere.model.ParcelableStatus
import org.mariotaku.twidere.model.UserKey
@@ -75,6 +76,9 @@ open class TweetSearchLoader(
protected open fun processQuery(details: AccountDetails, query: String): String {
if (details.type == AccountType.TWITTER) {
+ if (details.extras?.official == true) {
+ return smQuery(query, pagination)
+ }
return "$query exclude:retweets"
}
return query
@@ -104,6 +108,15 @@ open class TweetSearchLoader(
val queryText = processQuery(account, query)
when (account.type) {
AccountType.TWITTER -> {
+ if (account.extras?.official == true) {
+ val universalQuery = UniversalSearchQuery(queryText)
+ universalQuery.setModules(UniversalSearchQuery.Module.TWEET)
+ universalQuery.setResultType(UniversalSearchQuery.ResultType.RECENT)
+ universalQuery.setPaging(paging)
+ val searchResult = microBlog.universalSearch(universalQuery)
+ return searchResult.modules.mapNotNull { it.status?.data }
+ }
+
val searchQuery = SearchQuery(queryText)
searchQuery.paging(paging)
return microBlog.search(searchQuery)
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/loader/statuses/UserMentionsLoader.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/loader/statuses/UserMentionsLoader.kt
index e716e559d..e8075a7f0 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/loader/statuses/UserMentionsLoader.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/loader/statuses/UserMentionsLoader.kt
@@ -21,6 +21,7 @@ package org.mariotaku.twidere.loader.statuses
import android.content.Context
import org.mariotaku.twidere.annotation.AccountType
+import org.mariotaku.twidere.extension.model.official
import org.mariotaku.twidere.model.AccountDetails
import org.mariotaku.twidere.model.ParcelableStatus
import org.mariotaku.twidere.model.UserKey
@@ -42,6 +43,9 @@ class UserMentionsLoader(
override fun processQuery(details: AccountDetails, query: String): String {
val screenName = query.substringAfter("@")
if (details.type == AccountType.TWITTER) {
+ if (details.extras?.official == true) {
+ return smQuery("to:$screenName", pagination)
+ }
return "to:$screenName exclude:retweets"
}
return "@$screenName -RT"
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/loader/users/StatusFavoritersLoader.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/loader/users/StatusFavoritersLoader.kt
index 7e99ae2be..5054298b4 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/loader/users/StatusFavoritersLoader.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/loader/users/StatusFavoritersLoader.kt
@@ -36,6 +36,7 @@ import org.mariotaku.twidere.extension.api.lookupUsersMapPaginated
import org.mariotaku.twidere.extension.model.api.mastodon.mapToPaginated
import org.mariotaku.twidere.extension.model.api.mastodon.toParcelable
import org.mariotaku.twidere.extension.model.api.toParcelable
+import org.mariotaku.twidere.extension.model.isOfficial
import org.mariotaku.twidere.extension.model.newMicroBlogInstance
import org.mariotaku.twidere.model.AccountDetails
import org.mariotaku.twidere.model.ParcelableUser
@@ -62,7 +63,9 @@ class StatusFavoritersLoader(
}
AccountType.TWITTER -> {
val microBlog = details.newMicroBlogInstance(context, MicroBlog::class.java)
- val ids = run {
+ val ids = if (details.isOfficial(context)) {
+ microBlog.getStatusActivitySummary(statusId).favoriters
+ } else {
val web = details.newMicroBlogInstance(context, TwitterWeb::class.java)
val htmlUsers = web.getFavoritedPopup(statusId).htmlUsers
IDsAccessor.setIds(IDs(), parseUserIds(htmlUsers))
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/model/tab/impl/InteractionsTabConfiguration.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/model/tab/impl/InteractionsTabConfiguration.kt
index 8c6890acd..050e5158c 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/model/tab/impl/InteractionsTabConfiguration.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/model/tab/impl/InteractionsTabConfiguration.kt
@@ -29,6 +29,7 @@ import org.mariotaku.twidere.annotation.AccountType
import org.mariotaku.twidere.annotation.TabAccountFlags
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_MENTIONS_ONLY
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_MY_FOLLOWING_ONLY
+import org.mariotaku.twidere.extension.model.isOfficial
import org.mariotaku.twidere.fragment.InteractionsTimelineFragment
import org.mariotaku.twidere.model.AccountDetails
import org.mariotaku.twidere.model.Tab
@@ -104,11 +105,11 @@ class InteractionsTabConfiguration : TabConfiguration() {
val am = AccountManager.get(context)
val accounts = AccountUtils.getAllAccountDetails(am, false)
interactionsAvailable = accounts.any { it.supportsInteractions }
- requiresStreaming = accounts.all { true }
+ requiresStreaming = accounts.all { it.requiresStreaming }
} else when (account.type) {
AccountType.TWITTER -> {
interactionsAvailable = true
- requiresStreaming = true
+ requiresStreaming = !account.isOfficial(context)
}
AccountType.MASTODON -> {
interactionsAvailable = true
@@ -160,7 +161,7 @@ class InteractionsTabConfiguration : TabConfiguration() {
get() = type == AccountType.TWITTER || type == AccountType.MASTODON
private val AccountDetails.requiresStreaming: Boolean
- get() = true
+ get() = !isOfficial(context)
}
}
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/service/StreamingService.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/service/StreamingService.kt
index 885631828..8c1a7716d 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/service/StreamingService.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/service/StreamingService.kt
@@ -194,6 +194,11 @@ class StreamingService : BaseService() {
}
private fun newStreamingRunnable(account: AccountDetails, preferences: AccountPreferences): StreamingRunnable<*>? {
+ when (account.type) {
+ AccountType.TWITTER -> {
+ return TwitterStreamingRunnable(this, account, preferences)
+ }
+ }
return null
}
@@ -232,6 +237,236 @@ class StreamingService : BaseService() {
abstract fun onCancelled()
}
+ internal inner class TwitterStreamingRunnable(
+ context: Context,
+ account: AccountDetails,
+ accountPreferences: AccountPreferences
+ ) : StreamingRunnable(context, account, accountPreferences) {
+
+ private val profileImageSize = context.getString(R.string.profile_image_size)
+ private val isOfficial = account.isOfficial(context)
+
+ private var canGetInteractions: Boolean = true
+ private var canGetMessages: Boolean = true
+
+ private val interactionsTimeoutRunnable = Runnable {
+ canGetInteractions = true
+ }
+
+ private val messagesTimeoutRunnable = Runnable {
+ canGetMessages = true
+ }
+
+ val callback = object : TwitterTimelineStreamCallback(account.key.id) {
+
+ private var lastStatusTimestamps = LongArray(2)
+
+ private var homeInsertGap = false
+ private var interactionsInsertGap = false
+
+ private var lastActivityAboutMe: ParcelableActivity? = null
+
+ override fun onConnected(): Boolean {
+ homeInsertGap = true
+ interactionsInsertGap = true
+ return true
+ }
+
+ override fun onHomeTimeline(status: Status): Boolean {
+ if (!accountPreferences.isStreamHomeTimelineEnabled) {
+ homeInsertGap = true
+ return false
+ }
+ val parcelableStatus = status.toParcelable(account, profileImageSize = profileImageSize)
+ parcelableStatus.is_gap = homeInsertGap
+
+ val currentTimeMillis = System.currentTimeMillis()
+ if (lastStatusTimestamps[0] >= parcelableStatus.timestamp) {
+ val extraValue = (currentTimeMillis - lastStatusTimestamps[1]).coerceAtMost(499)
+ parcelableStatus.position_key = parcelableStatus.timestamp + extraValue
+ } else {
+ parcelableStatus.position_key = parcelableStatus.timestamp
+ }
+ parcelableStatus.inserted_date = currentTimeMillis
+
+ lastStatusTimestamps[0] = parcelableStatus.position_key
+ lastStatusTimestamps[1] = parcelableStatus.inserted_date
+
+ val values = ObjectCursor.valuesCreatorFrom(ParcelableStatus::class.java)
+ .create(parcelableStatus)
+ context.contentResolver.insert(Statuses.CONTENT_URI, values)
+ homeInsertGap = false
+ return true
+ }
+
+ override fun onActivityAboutMe(activity: Activity): Boolean {
+ if (!accountPreferences.isStreamInteractionsEnabled) {
+ interactionsInsertGap = true
+ return false
+ }
+ if (isOfficial) {
+ // Wait for 30 seconds to avoid rate limit
+ if (canGetInteractions) {
+ handler.post { getInteractions() }
+ canGetInteractions = false
+ handler.postDelayed(interactionsTimeoutRunnable, TimeUnit.SECONDS.toMillis(30))
+ }
+ } else {
+ val insertGap: Boolean
+ if (activity.action in Activity.Action.MENTION_ACTIONS) {
+ insertGap = interactionsInsertGap
+ interactionsInsertGap = false
+ } else {
+ insertGap = false
+ }
+ val curActivity = activity.toParcelable(account, insertGap, profileImageSize)
+ curActivity.account_color = account.color
+ curActivity.position_key = curActivity.timestamp
+ var updateId = -1L
+ if (curActivity.action !in Activity.Action.MENTION_ACTIONS) {
+ /* Merge two activities if:
+ * * Not mention/reply/quote
+ * * Same action
+ * * Same source or target or target object
+ */
+ val lastActivity = this.lastActivityAboutMe
+ if (lastActivity != null && curActivity.action == lastActivity.action) {
+ if (curActivity.reachedCountLimit) {
+ // Skip if more than 10 sources/targets/target_objects
+ } else if (curActivity.isSameSources(lastActivity)) {
+ curActivity.prependTargets(lastActivity)
+ curActivity.prependTargetObjects(lastActivity)
+ updateId = lastActivity._id
+ } else if (curActivity.isSameTarget(lastActivity)) {
+ curActivity.prependSources(lastActivity)
+ curActivity.prependTargets(lastActivity)
+ updateId = lastActivity._id
+ } else if (curActivity.isSameTargetObject(lastActivity)) {
+ curActivity.prependSources(lastActivity)
+ curActivity.prependTargets(lastActivity)
+ updateId = lastActivity._id
+ }
+ if (updateId > 0) {
+ curActivity.min_position = lastActivity.min_position
+ curActivity.min_sort_position = lastActivity.min_sort_position
+ }
+ }
+ }
+ val values = ObjectCursor.valuesCreatorFrom(ParcelableActivity::class.java)
+ .create(curActivity)
+ val resolver = context.contentResolver
+ if (updateId > 0) {
+ val where = Expression.equals(Activities._ID, updateId).sql
+ resolver.update(Activities.AboutMe.CONTENT_URI, values, where, null)
+ curActivity._id = updateId
+ } else {
+ val uri = resolver.insert(Activities.AboutMe.CONTENT_URI, values)
+ if (uri != null) {
+ curActivity._id = uri.lastPathSegment.toLongOr(-1L)
+ }
+ }
+ lastActivityAboutMe = curActivity
+ }
+ return true
+ }
+
+ @WorkerThread
+ override fun onDirectMessage(directMessage: DirectMessage): Boolean {
+ if (!accountPreferences.isStreamDirectMessagesEnabled) {
+ return false
+ }
+ if (canGetMessages) {
+ handler.post { getMessages() }
+ canGetMessages = false
+ val timeout = TimeUnit.SECONDS.toMillis(if (isOfficial) 30 else 90)
+ handler.postDelayed(messagesTimeoutRunnable, timeout)
+ }
+ return true
+ }
+
+ override fun onAllStatus(status: Status) {
+ if (!accountPreferences.isStreamNotificationUsersEnabled) {
+ return
+ }
+ val user = status.user ?: return
+ val userKey = user.key
+ val where = Expression.and(Expression.equalsArgs(CachedRelationships.ACCOUNT_KEY),
+ Expression.equalsArgs(CachedRelationships.USER_KEY),
+ Expression.equals(CachedRelationships.NOTIFICATIONS_ENABLED, 1)).sql
+ val whereArgs = arrayOf(account.key.toString(), userKey.toString())
+ if (context.contentResolver.queryCount(CachedRelationships.CONTENT_URI,
+ where, whereArgs) <= 0) return
+
+ contentNotificationManager.showUserNotification(account.key, status, userKey)
+ }
+
+ override fun onStatusDeleted(event: DeletionEvent): Boolean {
+ val deleteWhere = Expression.and(Expression.likeRaw(Columns.Column(Statuses.ACCOUNT_KEY), "'%@'||?"),
+ Expression.equalsArgs(Columns.Column(Statuses.ID))).sql
+ val deleteWhereArgs = arrayOf(account.key.host, event.id)
+ context.contentResolver.delete(Statuses.CONTENT_URI, deleteWhere, deleteWhereArgs)
+ return true
+ }
+
+ override fun onDisconnectNotice(code: Int, reason: String?): Boolean {
+ disconnect()
+ return true
+ }
+
+ override fun onException(ex: Throwable): Boolean {
+ DebugLog.w(LOGTAG, msg = "Exception for ${account.key}", tr = ex)
+ return true
+ }
+
+ override fun onUnhandledEvent(obj: TwitterStreamObject, json: String) {
+ DebugLog.d(LOGTAG, msg = "Unhandled event ${obj.determine()} for ${account.key}: $json")
+ }
+
+ @UiThread
+ private fun getInteractions() {
+ val task = GetActivitiesAboutMeTask(context)
+ task.params = object : RefreshTaskParam {
+ override val accountKeys: Array = arrayOf(account.key)
+
+ override val pagination by lazy {
+ val keys = accountKeys.toNulls()
+ val sinceIds = DataStoreUtils.getRefreshNewestActivityMaxPositions(context,
+ Activities.AboutMe.CONTENT_URI, keys)
+ val sinceSortIds = DataStoreUtils.getRefreshNewestActivityMaxSortPositions(context,
+ Activities.AboutMe.CONTENT_URI, keys)
+ return@lazy Array(keys.size) { idx ->
+ SinceMaxPagination.sinceId(sinceIds[idx], sinceSortIds[idx])
+ }
+ }
+
+ }
+ TaskStarter.execute(task)
+ }
+
+ @UiThread
+ private fun getMessages() {
+ val task = GetMessagesTask(context)
+ task.params = object : GetMessagesTask.RefreshMessagesTaskParam(context) {
+ override val accountKeys: Array = arrayOf(account.key)
+ }
+ TaskStarter.execute(task)
+ }
+ }
+
+ override fun createStreamingInstance(): TwitterUserStream {
+ return account.newMicroBlogInstance(context, cls = TwitterUserStream::class.java)
+ }
+
+ override fun TwitterUserStream.beginStreaming() {
+ getUserStream(StreamWith.USER, callback)
+ }
+
+ override fun onCancelled() {
+ callback.disconnect()
+ }
+
+ }
+
companion object {
private val NOTIFICATION_SERVICE_STARTED = 1
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/AcceptFriendshipTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/AcceptFriendshipTask.kt
index 40580b5f7..6202d9394 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/AcceptFriendshipTask.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/AcceptFriendshipTask.kt
@@ -15,7 +15,6 @@ import org.mariotaku.twidere.model.AccountDetails
import org.mariotaku.twidere.model.ParcelableUser
import org.mariotaku.twidere.model.event.FriendshipTaskEvent
import org.mariotaku.twidere.util.Utils
-import java.lang.UnsupportedOperationException
/**
* Created by mariotaku on 16/3/11.
@@ -36,7 +35,9 @@ class AcceptFriendshipTask(context: Context) : AbsFriendshipOperationTask(contex
return mastodon.getAccount(args.userKey.id).toParcelable(details)
}
else -> {
- throw UnsupportedOperationException()
+ val twitter = details.newMicroBlogInstance(context, MicroBlog::class.java)
+ return twitter.acceptFriendship(args.userKey.id).toParcelable(details,
+ profileImageSize = profileImageSize)
}
}
}
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/DenyFriendshipTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/DenyFriendshipTask.kt
index 81e7492f4..b7b8f2e2b 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/DenyFriendshipTask.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/DenyFriendshipTask.kt
@@ -15,7 +15,6 @@ import org.mariotaku.twidere.model.AccountDetails
import org.mariotaku.twidere.model.ParcelableUser
import org.mariotaku.twidere.model.event.FriendshipTaskEvent
import org.mariotaku.twidere.util.Utils
-import java.lang.UnsupportedOperationException
/**
* Created by mariotaku on 16/3/11.
@@ -36,7 +35,9 @@ class DenyFriendshipTask(context: Context) : AbsFriendshipOperationTask(context,
return mastodon.getAccount(args.userKey.id).toParcelable(details)
}
else -> {
- throw UnsupportedOperationException()
+ val twitter = details.newMicroBlogInstance(context, MicroBlog::class.java)
+ return twitter.denyFriendship(args.userKey.id).toParcelable(details,
+ profileImageSize = profileImageSize)
}
}
}
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/status/PinStatusTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/status/PinStatusTask.kt
new file mode 100644
index 000000000..49e1b2dcf
--- /dev/null
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/status/PinStatusTask.kt
@@ -0,0 +1,52 @@
+/*
+ * Twidere - Twitter client for Android
+ *
+ * Copyright (C) 2012-2017 Mariotaku Lee
+ *
+ * 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.
+ *
+ * This program 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 this program. If not, see .
+ */
+
+package org.mariotaku.twidere.task.status
+
+import android.content.Context
+import android.widget.Toast
+import org.mariotaku.microblog.library.MicroBlog
+import org.mariotaku.microblog.library.twitter.model.PinTweetResult
+import org.mariotaku.twidere.R
+import org.mariotaku.twidere.extension.model.newMicroBlogInstance
+import org.mariotaku.twidere.model.AccountDetails
+import org.mariotaku.twidere.model.UserKey
+import org.mariotaku.twidere.model.event.StatusPinEvent
+import org.mariotaku.twidere.task.AbsAccountRequestTask
+
+/**
+ * Created by mariotaku on 2017/4/28.
+ */
+
+class PinStatusTask(context: Context, accountKey: UserKey, val id: String) : AbsAccountRequestTask(context, accountKey) {
+
+ override fun onExecute(account: AccountDetails, params: Any?): PinTweetResult {
+ val twitter = account.newMicroBlogInstance(context, MicroBlog::class.java)
+ return twitter.pinTweet(id)
+ }
+
+ override fun onSucceed(callback: Any?, result: PinTweetResult) {
+ super.onSucceed(callback, result)
+ Toast.makeText(context, R.string.message_toast_status_pinned, Toast.LENGTH_SHORT).show()
+ if (accountKey != null) {
+ bus.post(StatusPinEvent(accountKey, true))
+ }
+ }
+}
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/status/UnpinStatusTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/status/UnpinStatusTask.kt
new file mode 100644
index 000000000..0bcc94aa7
--- /dev/null
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/status/UnpinStatusTask.kt
@@ -0,0 +1,52 @@
+/*
+ * Twidere - Twitter client for Android
+ *
+ * Copyright (C) 2012-2017 Mariotaku Lee
+ *
+ * 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.
+ *
+ * This program 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 this program. If not, see .
+ */
+
+package org.mariotaku.twidere.task.status
+
+import android.content.Context
+import android.widget.Toast
+import org.mariotaku.microblog.library.MicroBlog
+import org.mariotaku.microblog.library.twitter.model.PinTweetResult
+import org.mariotaku.twidere.R
+import org.mariotaku.twidere.extension.model.newMicroBlogInstance
+import org.mariotaku.twidere.model.AccountDetails
+import org.mariotaku.twidere.model.UserKey
+import org.mariotaku.twidere.model.event.StatusPinEvent
+import org.mariotaku.twidere.task.AbsAccountRequestTask
+
+/**
+ * Created by mariotaku on 2017/4/28.
+ */
+
+class UnpinStatusTask(context: Context, accountKey: UserKey, val id: String) : AbsAccountRequestTask(context, accountKey) {
+
+ override fun onExecute(account: AccountDetails, params: Any?): PinTweetResult {
+ val twitter = account.newMicroBlogInstance(context, MicroBlog::class.java)
+ return twitter.unpinTweet(id)
+ }
+
+ override fun onSucceed(callback: Any?, result: PinTweetResult) {
+ super.onSucceed(callback, result)
+ Toast.makeText(context, R.string.message_toast_status_unpinned, Toast.LENGTH_SHORT).show()
+ if (accountKey != null) {
+ bus.post(StatusPinEvent(accountKey, false))
+ }
+ }
+}
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/GetActivitiesAboutMeTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/GetActivitiesAboutMeTask.kt
index eead73e86..41901ef6a 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/GetActivitiesAboutMeTask.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/GetActivitiesAboutMeTask.kt
@@ -36,6 +36,7 @@ import org.mariotaku.twidere.extension.api.batchGetRelationships
import org.mariotaku.twidere.extension.model.api.mastodon.toParcelable
import org.mariotaku.twidere.extension.model.api.microblog.toParcelable
import org.mariotaku.twidere.extension.model.extractFanfouHashtags
+import org.mariotaku.twidere.extension.model.isOfficial
import org.mariotaku.twidere.extension.model.newMicroBlogInstance
import org.mariotaku.twidere.fragment.InteractionsTimelineFragment
import org.mariotaku.twidere.model.AccountDetails
@@ -81,6 +82,28 @@ class GetActivitiesAboutMeTask(context: Context) : GetActivitiesTask(context) {
notification.status?.tags?.map { it.name }.orEmpty()
})
}
+ AccountType.TWITTER -> {
+ val microBlog = account.newMicroBlogInstance(context, MicroBlog::class.java)
+ if (account.isOfficial(context)) {
+ val timeline = microBlog.getActivitiesAboutMe(paging)
+ val activities = timeline.map {
+ it.toParcelable(account, profileImageSize = profileImageSize)
+ }
+
+ return GetTimelineResult(account, activities, activities.flatMap {
+ it.sources?.toList().orEmpty()
+ }, timeline.flatMapTo(HashSet()) { activity ->
+ val mapResult = mutableSetOf()
+ activity.targetStatuses?.flatMapTo(mapResult) { status ->
+ status.entities?.hashtags?.map { it.text }.orEmpty()
+ }
+ activity.targetObjectStatuses?.flatMapTo(mapResult) { status ->
+ status.entities?.hashtags?.map { it.text }.orEmpty()
+ }
+ return@flatMapTo mapResult
+ })
+ }
+ }
AccountType.FANFOU -> {
val microBlog = account.newMicroBlogInstance(context, MicroBlog::class.java)
val activities = microBlog.getMentions(paging).map {
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/GetStatusesTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/GetStatusesTask.kt
index cb96ba1a6..458c041c1 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/GetStatusesTask.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/GetStatusesTask.kt
@@ -244,7 +244,7 @@ abstract class GetStatusesTask(
if (result == null) return@forEach
val account = result.account
val task = CacheTimelineResultTask(context, result,
- account.type == AccountType.STATUSNET)
+ account.type == AccountType.STATUSNET || account.isOfficial(context))
TaskStarter.execute(task)
}
}
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/message/AddParticipantsTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/message/AddParticipantsTask.kt
new file mode 100644
index 000000000..8c8983e7f
--- /dev/null
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/message/AddParticipantsTask.kt
@@ -0,0 +1,97 @@
+/*
+ * Twidere - Twitter client for Android
+ *
+ * Copyright (C) 2012-2017 Mariotaku Lee
+ *
+ * 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.
+ *
+ * This program 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 this program. If not, see .
+ */
+
+package org.mariotaku.twidere.task.twitter.message
+
+import android.accounts.AccountManager
+import android.content.Context
+import org.mariotaku.ktextension.mapToArray
+import org.mariotaku.microblog.library.MicroBlog
+import org.mariotaku.microblog.library.MicroBlogException
+import org.mariotaku.twidere.R
+import org.mariotaku.twidere.annotation.AccountType
+import org.mariotaku.twidere.extension.model.addParticipants
+import org.mariotaku.twidere.extension.model.isOfficial
+import org.mariotaku.twidere.extension.model.newMicroBlogInstance
+import org.mariotaku.twidere.model.AccountDetails
+import org.mariotaku.twidere.model.ParcelableMessageConversation
+import org.mariotaku.twidere.model.ParcelableUser
+import org.mariotaku.twidere.model.UserKey
+import org.mariotaku.twidere.model.util.AccountUtils
+import org.mariotaku.twidere.task.ExceptionHandlingAbstractTask
+import org.mariotaku.twidere.util.DataStoreUtils
+
+/**
+ * Created by mariotaku on 2017/2/25.
+ */
+
+class AddParticipantsTask(
+ context: Context,
+ val accountKey: UserKey,
+ val conversationId: String,
+ val participants: Collection
+) : ExceptionHandlingAbstractTask Unit)?>(context) {
+
+ private val profileImageSize: String = context.getString(R.string.profile_image_size)
+
+ override val exceptionClass = MicroBlogException::class.java
+
+ override fun onExecute(params: Unit?): Boolean {
+ val account = AccountUtils.getAccountDetails(AccountManager.get(context), accountKey, true) ?:
+ throw MicroBlogException("No account")
+ val conversation = DataStoreUtils.findMessageConversation(context, accountKey, conversationId)
+ if (conversation != null && conversation.is_temp) {
+ val addData = GetMessagesTask.DatabaseUpdateData(listOf(conversation), emptyList())
+ conversation.addParticipants(participants)
+ GetMessagesTask.storeMessages(context, addData, account, showNotification = false)
+ // Don't finish too fast
+ Thread.sleep(300L)
+ return true
+ }
+ val microBlog = account.newMicroBlogInstance(context, cls = MicroBlog::class.java)
+ val addData = requestAddParticipants(microBlog, account, conversation)
+ GetMessagesTask.storeMessages(context, addData, account, showNotification = false)
+ return true
+ }
+
+ override fun afterExecute(callback: ((Boolean) -> Unit)?, result: Boolean?, exception: MicroBlogException?) {
+ callback?.invoke(result ?: false)
+ }
+
+ private fun requestAddParticipants(microBlog: MicroBlog, account: AccountDetails, conversation: ParcelableMessageConversation?):
+ GetMessagesTask.DatabaseUpdateData {
+ when (account.type) {
+ AccountType.TWITTER -> {
+ if (account.isOfficial(context)) {
+ val ids = participants.mapToArray { it.key.id }
+ val response = microBlog.addParticipants(conversationId, ids)
+ if (conversation != null) {
+ conversation.addParticipants(participants)
+ return GetMessagesTask.DatabaseUpdateData(listOf(conversation), emptyList())
+ }
+ return GetMessagesTask.createDatabaseUpdateData(context, account, response,
+ profileImageSize)
+ }
+ }
+
+ }
+ throw MicroBlogException("Adding participants is not supported")
+ }
+
+}
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/message/DestroyConversationTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/message/DestroyConversationTask.kt
index 38975aee1..0dc989fe2 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/message/DestroyConversationTask.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/message/DestroyConversationTask.kt
@@ -25,6 +25,7 @@ import org.mariotaku.microblog.library.MicroBlog
import org.mariotaku.microblog.library.MicroBlogException
import org.mariotaku.sqliteqb.library.Expression
import org.mariotaku.twidere.annotation.AccountType
+import org.mariotaku.twidere.extension.model.isOfficial
import org.mariotaku.twidere.extension.model.newMicroBlogInstance
import org.mariotaku.twidere.model.AccountDetails
import org.mariotaku.twidere.model.ParcelableMessageConversation
@@ -88,6 +89,9 @@ class DestroyConversationTask(
private fun requestDestroyConversation(microBlog: MicroBlog, account: AccountDetails): Boolean {
when (account.type) {
AccountType.TWITTER -> {
+ if (account.isOfficial(context)) {
+ return microBlog.deleteDmConversation(conversationId).isSuccessful
+ }
}
}
return false
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/message/DestroyMessageTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/message/DestroyMessageTask.kt
index 7c6fbeb4c..dd0779e6a 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/message/DestroyMessageTask.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/message/DestroyMessageTask.kt
@@ -25,6 +25,7 @@ import org.mariotaku.microblog.library.MicroBlog
import org.mariotaku.microblog.library.MicroBlogException
import org.mariotaku.sqliteqb.library.Expression
import org.mariotaku.twidere.annotation.AccountType
+import org.mariotaku.twidere.extension.model.isOfficial
import org.mariotaku.twidere.extension.model.newMicroBlogInstance
import org.mariotaku.twidere.model.AccountDetails
import org.mariotaku.twidere.model.UserKey
@@ -74,6 +75,9 @@ class DestroyMessageTask(
account: AccountDetails, messageId: String): Boolean {
when (account.type) {
AccountType.TWITTER -> {
+ if (account.isOfficial(context)) {
+ return microBlog.destroyDm(messageId).isSuccessful
+ }
}
}
microBlog.destroyDirectMessage(messageId)
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/message/GetMessagesTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/message/GetMessagesTask.kt
index 39fb4d195..4d2b215a7 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/message/GetMessagesTask.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/message/GetMessagesTask.kt
@@ -99,11 +99,27 @@ class GetMessagesTask(
// Use fanfou DM api, disabled since it's conversation api is not suitable for paging
// return getFanfouMessages(microBlog, details, param, index)
}
+ AccountType.TWITTER -> {
+ // Use official DM api
+ if (details.isOfficial(context)) {
+ return getTwitterOfficialMessages(microBlog, details, param, index)
+ }
+ }
}
// Use default method
return getDefaultMessages(microBlog, details, param, index)
}
+ private fun getTwitterOfficialMessages(microBlog: MicroBlog, details: AccountDetails,
+ param: RefreshMessagesTaskParam, index: Int): DatabaseUpdateData {
+ val conversationId = param.conversationId
+ if (conversationId == null) {
+ return getTwitterOfficialUserInbox(microBlog, details, param, index)
+ } else {
+ return getTwitterOfficialConversation(microBlog, details, conversationId, param, index)
+ }
+ }
+
private fun getFanfouMessages(microBlog: MicroBlog, details: AccountDetails, param: RefreshMessagesTaskParam, index: Int): DatabaseUpdateData {
val conversationId = param.conversationId
if (conversationId == null) {
@@ -179,6 +195,35 @@ class GetMessagesTask(
}
+ private fun getTwitterOfficialConversation(microBlog: MicroBlog, details: AccountDetails,
+ conversationId: String, param: RefreshMessagesTaskParam, index: Int): DatabaseUpdateData {
+ val maxId = (param.pagination?.get(index) as? SinceMaxPagination)?.maxId
+ ?: return DatabaseUpdateData(emptyList(), emptyList())
+ val paging = Paging().apply {
+ maxId(maxId)
+ }
+
+ val response = microBlog.getDmConversation(conversationId, paging).conversationTimeline
+ return createDatabaseUpdateData(context, details, response, profileImageSize)
+ }
+
+ private fun getTwitterOfficialUserInbox(microBlog: MicroBlog, details: AccountDetails,
+ param: RefreshMessagesTaskParam, index: Int): DatabaseUpdateData {
+ val maxId = (param.pagination?.get(index) as? SinceMaxPagination)?.maxId
+ val cursor = (param.pagination?.get(index) as? CursorPagination)?.cursor
+ val response = if (cursor != null) {
+ microBlog.getUserUpdates(cursor).userEvents
+ } else {
+ microBlog.getUserInbox(Paging().apply {
+ if (maxId != null) {
+ maxId(maxId)
+ }
+ }).userInbox
+ } ?: throw MicroBlogException("Null response data")
+ return createDatabaseUpdateData(context, details, response, profileImageSize)
+ }
+
+
private fun getFanfouConversations(microBlog: MicroBlog, details: AccountDetails,
param: RefreshMessagesTaskParam, index: Int): DatabaseUpdateData {
val accountKey = details.key
@@ -230,10 +275,16 @@ class GetMessagesTask(
defaultKeys, false)
val outgoingIds = DataStoreUtils.getNewestMessageIds(context, Messages.CONTENT_URI,
defaultKeys, true)
+ val cursors = DataStoreUtils.getNewestConversations(context, Conversations.CONTENT_URI,
+ twitterOfficialKeys).mapToArray { it?.request_cursor }
accounts.forEachIndexed { index, details ->
if (details == null) return@forEachIndexed
- result[index] = SinceMaxPagination.sinceId(incomingIds[index], -1)
- result[accounts.size + index] = SinceMaxPagination.sinceId(outgoingIds[index], -1)
+ if (details.isOfficial(context)) {
+ result[index] = CursorPagination.valueOf(cursors[index])
+ } else {
+ result[index] = SinceMaxPagination.sinceId(incomingIds[index], -1)
+ result[accounts.size + index] = SinceMaxPagination.sinceId(outgoingIds[index], -1)
+ }
}
return@lazy result
}
@@ -250,7 +301,7 @@ class GetMessagesTask(
val outgoingIds = DataStoreUtils.getOldestMessageIds(context, Messages.CONTENT_URI,
defaultKeys, true)
val oldestConversations = DataStoreUtils.getOldestConversations(context,
- Conversations.CONTENT_URI, emptyArray())
+ Conversations.CONTENT_URI, twitterOfficialKeys)
oldestConversations.forEachIndexed { i, conversation ->
val extras = conversation?.conversation_extras as? TwitterOfficialConversationExtras ?: return@forEachIndexed
incomingIds[i] = extras.maxEntryId
@@ -291,6 +342,19 @@ class GetMessagesTask(
protected val defaultKeys: Array by lazy {
return@lazy accounts.map { account ->
account ?: return@map null
+ if (account.isOfficial(context)) {
+ return@map null
+ }
+ return@map account.key
+ }.toTypedArray()
+ }
+
+ protected val twitterOfficialKeys: Array by lazy {
+ return@lazy accounts.map { account ->
+ account ?: return@map null
+ if (!account.isOfficial(context)) {
+ return@map null
+ }
return@map account.key
}.toTypedArray()
}
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/message/MarkMessageReadTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/message/MarkMessageReadTask.kt
index ff1d398a2..5cd678c6c 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/message/MarkMessageReadTask.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/message/MarkMessageReadTask.kt
@@ -28,6 +28,7 @@ import org.mariotaku.microblog.library.MicroBlogException
import org.mariotaku.sqliteqb.library.Expression
import org.mariotaku.sqliteqb.library.OrderBy
import org.mariotaku.twidere.annotation.AccountType
+import org.mariotaku.twidere.extension.model.isOfficial
import org.mariotaku.twidere.extension.model.newMicroBlogInstance
import org.mariotaku.twidere.extension.model.timestamp
import org.mariotaku.twidere.extension.queryOne
@@ -80,6 +81,23 @@ class MarkMessageReadTask(
internal fun performMarkRead(context: Context, microBlog: MicroBlog, account: AccountDetails,
conversation: ParcelableMessageConversation): Pair? {
val cr = context.contentResolver
+ when (account.type) {
+ AccountType.TWITTER -> {
+ if (account.isOfficial(context)) {
+ val event = (conversation.conversation_extras as? TwitterOfficialConversationExtras)?.maxReadEvent ?: run {
+ val message = cr.findRecentMessage(account.key, conversation.id) ?: return null
+ return@run Pair(message.id, message.timestamp)
+ }
+ if (conversation.last_read_timestamp > event.second) {
+ // Local is newer, ignore network request
+ return event
+ }
+ if (microBlog.markDmRead(conversation.id, event.first).isSuccessful) {
+ return event
+ }
+ }
+ }
+ }
val message = cr.findRecentMessage(account.key, conversation.id) ?: return null
return Pair(message.id, message.timestamp)
}
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/message/SendMessageTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/message/SendMessageTask.kt
index e4f38be0b..9f9095913 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/message/SendMessageTask.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/message/SendMessageTask.kt
@@ -31,6 +31,7 @@ import org.mariotaku.sqliteqb.library.Expression
import org.mariotaku.twidere.R
import org.mariotaku.twidere.annotation.AccountType
import org.mariotaku.twidere.extension.model.api.*
+import org.mariotaku.twidere.extension.model.isOfficial
import org.mariotaku.twidere.extension.model.newMicroBlogInstance
import org.mariotaku.twidere.model.AccountDetails
import org.mariotaku.twidere.model.ParcelableMedia
@@ -41,6 +42,7 @@ import org.mariotaku.twidere.model.util.ParcelableMessageUtils
import org.mariotaku.twidere.provider.TwidereDataStore.Messages.Conversations
import org.mariotaku.twidere.task.ExceptionHandlingAbstractTask
import org.mariotaku.twidere.task.twitter.UpdateStatusTask
+import org.mariotaku.twidere.task.twitter.message.GetMessagesTask
import org.mariotaku.twidere.task.twitter.message.GetMessagesTask.Companion.addConversation
import org.mariotaku.twidere.task.twitter.message.GetMessagesTask.Companion.addLocalConversations
@@ -84,7 +86,11 @@ class SendMessageTask(
message: ParcelableNewMessage): GetMessagesTask.DatabaseUpdateData {
when (account.type) {
AccountType.TWITTER -> {
- return sendTwitterMessageEvent(microBlog, account, message)
+ if (account.isOfficial(context)) {
+ return sendTwitterOfficialDM(microBlog, account, message)
+ } else {
+ return sendTwitterMessageEvent(microBlog, account, message)
+ }
}
AccountType.FANFOU -> {
return sendFanfouDM(microBlog, account, message)
@@ -93,6 +99,47 @@ class SendMessageTask(
return sendDefaultDM(microBlog, account, message)
}
+ private fun sendTwitterOfficialDM(microBlog: MicroBlog, account: AccountDetails,
+ message: ParcelableNewMessage): GetMessagesTask.DatabaseUpdateData {
+ var deleteOnSuccess: List? = null
+ var deleteAlways: List? = null
+ val sendResponse = try {
+ val conversationId = message.conversation_id
+ val tempConversation = message.is_temp_conversation
+
+ val newDm = NewDm()
+ if (!tempConversation && conversationId != null) {
+ newDm.setConversationId(conversationId)
+ } else {
+ newDm.setRecipientIds(message.recipient_ids)
+ }
+ newDm.setText(message.text)
+
+ if (message.media.isNotNullOrEmpty()) {
+ val upload = account.newMicroBlogInstance(context, cls = TwitterUpload::class.java)
+ val uploadResult = UpdateStatusTask.uploadMicroBlogMediaShared(context,
+ upload, account, message.media, null, null, true, null)
+ newDm.setMediaId(uploadResult.ids[0])
+ deleteAlways = uploadResult.deleteAlways
+ deleteOnSuccess = uploadResult.deleteOnSuccess
+ }
+ microBlog.sendDm(newDm)
+ } catch (e: UpdateStatusTask.UploadException) {
+ e.deleteAlways?.forEach {
+ it.delete(context)
+ }
+ throw MicroBlogException(e)
+ } finally {
+ deleteAlways?.forEach { it.delete(context) }
+ }
+ deleteOnSuccess?.forEach { it.delete(context) }
+ val conversationId = sendResponse.entries?.firstOrNull {
+ it.message != null
+ }?.message?.conversationId
+ val response = microBlog.getDmConversation(conversationId, null).conversationTimeline
+ return GetMessagesTask.createDatabaseUpdateData(context, account, response, profileImageSize)
+ }
+
private fun sendTwitterMessageEvent(microBlog: MicroBlog, account: AccountDetails,
message: ParcelableNewMessage): GetMessagesTask.DatabaseUpdateData {
val recipientId = message.recipient_ids.singleOrNull() ?: throw MicroBlogException("No recipient")
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/message/SetConversationNotificationDisabledTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/message/SetConversationNotificationDisabledTask.kt
index b8bbc7b85..0239b4c20 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/message/SetConversationNotificationDisabledTask.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/message/SetConversationNotificationDisabledTask.kt
@@ -24,6 +24,7 @@ import android.content.Context
import org.mariotaku.microblog.library.MicroBlog
import org.mariotaku.microblog.library.MicroBlogException
import org.mariotaku.twidere.annotation.AccountType
+import org.mariotaku.twidere.extension.model.isOfficial
import org.mariotaku.twidere.extension.model.newMicroBlogInstance
import org.mariotaku.twidere.extension.model.notificationDisabled
import org.mariotaku.twidere.model.AccountDetails
@@ -60,6 +61,24 @@ class SetConversationNotificationDisabledTask(
private fun requestSetNotificationDisabled(microBlog: MicroBlog, account: AccountDetails):
GetMessagesTask.DatabaseUpdateData {
+ when (account.type) {
+ AccountType.TWITTER -> {
+ if (account.isOfficial(context)) {
+ val response = if (notificationDisabled) {
+ microBlog.disableDmConversations(conversationId)
+ } else {
+ microBlog.enableDmConversations(conversationId)
+ }
+ val conversation = DataStoreUtils.findMessageConversation(context, accountKey,
+ conversationId) ?: return GetMessagesTask.DatabaseUpdateData(emptyList(), emptyList())
+ if (response.isSuccessful) {
+ conversation.notificationDisabled = notificationDisabled
+ }
+ return GetMessagesTask.DatabaseUpdateData(listOf(conversation), emptyList())
+ }
+ }
+ }
+
val conversation = DataStoreUtils.findMessageConversation(context, accountKey,
conversationId) ?: return GetMessagesTask.DatabaseUpdateData(emptyList(), emptyList())
conversation.notificationDisabled = notificationDisabled
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/AsyncTwitterWrapper.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/AsyncTwitterWrapper.kt
index 740627656..5d38d5134 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/AsyncTwitterWrapper.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/AsyncTwitterWrapper.kt
@@ -377,6 +377,18 @@ class AsyncTwitterWrapper(
}
fun setActivitiesAboutMeUnreadAsync(accountKeys: Array, cursor: Long) {
+ val task = object : ExceptionHandlingAbstractTask(context) {
+ override val exceptionClass = MicroBlogException::class.java
+
+ override fun onExecute(params: Any?) {
+ for (accountKey in accountKeys) {
+ val microBlog = MicroBlogAPIFactory.getInstance(context, accountKey) ?: continue
+ if (!AccountUtils.isOfficial(context, accountKey)) continue
+ microBlog.setActivitiesAboutMeUnread(cursor)
+ }
+ }
+ }
+ TaskStarter.execute(task)
}
fun addUpdatingRelationshipId(accountKey: UserKey, userKey: UserKey) {
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreUtils.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreUtils.kt
index 02c63c229..dd6a742e8 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreUtils.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreUtils.kt
@@ -998,8 +998,23 @@ object DataStoreUtils {
private fun getOfficialSeparatedIds(context: Context, getFromDatabase: (Array, Boolean) -> T,
mergeResult: (T, T) -> T, accountKeys: Array): T {
- val officialMaxPositions = getFromDatabase(emptyArray(), true)
- val notOfficialMaxPositions = getFromDatabase(accountKeys, false)
+ val officialKeys = Array(accountKeys.size) {
+ val key = accountKeys[it] ?: return@Array null
+ if (AccountUtils.isOfficial(context, key)) {
+ return@Array key
+ }
+ return@Array null
+ }
+ val notOfficialKeys = Array(accountKeys.size) {
+ val key = accountKeys[it] ?: return@Array null
+ if (AccountUtils.isOfficial(context, key)) {
+ return@Array null
+ }
+ return@Array key
+ }
+
+ val officialMaxPositions = getFromDatabase(officialKeys, true)
+ val notOfficialMaxPositions = getFromDatabase(notOfficialKeys, false)
return mergeResult(officialMaxPositions, notOfficialMaxPositions)
}
}
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/MenuUtils.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/MenuUtils.kt
index e6b678b73..9800a6f40 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/MenuUtils.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/MenuUtils.kt
@@ -54,6 +54,7 @@ import org.mariotaku.twidere.app.TwidereApplication
import org.mariotaku.twidere.constant.favoriteConfirmationKey
import org.mariotaku.twidere.constant.iWantMyStarsBackKey
import org.mariotaku.twidere.constant.nameFirstKey
+import org.mariotaku.twidere.extension.model.isOfficial
import org.mariotaku.twidere.fragment.AbsStatusesFragment
import org.mariotaku.twidere.fragment.AddStatusFilterDialogFragment
import org.mariotaku.twidere.fragment.BaseFragment
@@ -188,7 +189,11 @@ object MenuUtils {
favorite.setTitle(if (isFavorite) R.string.action_undo_like else R.string.action_like)
}
}
- menu.setItemAvailability(R.id.translate, false)
+ val translate = menu.findItem(R.id.translate)
+ if (translate != null) {
+ val isOfficialKey = details.isOfficial(context)
+ menu.setItemAvailability(R.id.translate, isOfficialKey)
+ }
menu.removeGroup(MENU_GROUP_STATUS_EXTENSION)
addIntentToMenuForExtension(context, menu, MENU_GROUP_STATUS_EXTENSION,
INTENT_ACTION_EXTENSION_OPEN_STATUS, EXTRA_STATUS, EXTRA_STATUS_JSON, status)
@@ -279,8 +284,10 @@ object MenuUtils {
DestroyStatusDialogFragment.show(fm, status)
}
R.id.pin -> {
+ PinStatusDialogFragment.show(fm, status)
}
R.id.unpin -> {
+ UnpinStatusDialogFragment.show(fm, status)
}
R.id.add_to_filter -> {
AddStatusFilterDialogFragment.show(fm, status)
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/status/DetailStatusViewHolder.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/status/DetailStatusViewHolder.kt
index 883813851..55c43b55d 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/status/DetailStatusViewHolder.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/status/DetailStatusViewHolder.kt
@@ -339,8 +339,26 @@ class DetailStatusViewHolder(
val lang = status.lang
- translateLabelView.setText(R.string.unknown_language)
- translateContainer.visibility = View.GONE
+ if (CheckUtils.isValidLocale(lang) && account.isOfficial(context)) {
+ translateContainer.visibility = View.VISIBLE
+ if (translation != null) {
+ val locale = Locale(translation.translatedLang)
+ translateLabelView.text = context.getString(R.string.label_translated_to_language,
+ locale.displayLanguage)
+ translateResultView.visibility = View.VISIBLE
+ translateChangeLanguageView.visibility = View.VISIBLE
+ translateResultView.text = translation.text
+ } else {
+ val locale = Locale(lang)
+ translateLabelView.text = context.getString(R.string.label_translate_from_language,
+ locale.displayLanguage)
+ translateResultView.visibility = View.GONE
+ translateChangeLanguageView.visibility = View.GONE
+ }
+ } else {
+ translateLabelView.setText(R.string.unknown_language)
+ translateContainer.visibility = View.GONE
+ }
textView.setTextIsSelectable(true)
translateResultView.setTextIsSelectable(true)
diff --git a/twidere/src/main/res/values/arrays_no_translate.xml b/twidere/src/main/res/values/arrays_no_translate.xml
index c5a1030bc..70f30d522 100644
--- a/twidere/src/main/res/values/arrays_no_translate.xml
+++ b/twidere/src/main/res/values/arrays_no_translate.xml
@@ -25,6 +25,40 @@
- true
+
+
+ - Twitter for Android
+
+ - Twitter for iPhone
+
+ - Twitter for iPad
+
+ - Twitter for Mac
+
+ - Twitter for Windows Phone
+
+ - Twitter for Google TV
+
+ - TweetDeck
+
+
+
+
+ - TWITTER_FOR_ANDROID
+
+ - TWITTER_FOR_IPHONE
+
+ - TWITTER_FOR_IPAD
+
+ - TWITTER_FOR_MAC
+
+ - TWITTER_FOR_WINDOWS_PHONE
+
+ - TWITTER_FOR_GOOGLE_TV
+
+ - TWEETDECK
+
+
- camera
- gallery
@@ -65,6 +99,23 @@
- mentions
- inbox
+
+
+
+ - 6ce85096
+
+ - deffe9c7
+
+ - 9f00e0cb
+
+ - df27640e
+
+ - 62bd0d33
+
+ - 56d8f9ff
+
+ - ac602936
+
- round
- square
From ab6ce518728217b74662d2af9ef7ba187251f803 Mon Sep 17 00:00:00 2001
From: Tlaster
Date: Fri, 3 Apr 2020 17:17:41 +0800
Subject: [PATCH 17/21] Migrate PrivateDirectMessagesResources to AndroidX
---
.../library/twitter/api/PrivateDirectMessagesResources.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateDirectMessagesResources.java b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateDirectMessagesResources.java
index 8aedefa37..a88e96bde 100644
--- a/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateDirectMessagesResources.java
+++ b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/api/PrivateDirectMessagesResources.java
@@ -18,7 +18,7 @@
package org.mariotaku.microblog.library.twitter.api;
-import android.support.annotation.Nullable;
+import androidx.annotation.Nullable;
import org.mariotaku.microblog.library.MicroBlogException;
import org.mariotaku.microblog.library.twitter.model.ConversationTimeline;
From 1a0ff60215621bb7b47bb2d7e881a3bcc76379cd Mon Sep 17 00:00:00 2001
From: Tlaster
Date: Fri, 3 Apr 2020 17:49:35 +0800
Subject: [PATCH 18/21] Fix import && null safety
---
.../java/org/mariotaku/twidere/model/util/AccountUtils.java | 5 ++++-
.../mariotaku/twidere/util/InternalTwitterContentUtils.java | 1 +
.../java/org/mariotaku/twidere/util/MicroBlogAPIFactory.java | 3 +++
.../twidere/extension/model/AccountDetailsExtensions.kt | 5 ++++-
.../org/mariotaku/twidere/fragment/CustomTabsFragment.kt | 2 +-
.../fragment/message/MessageConversationInfoFragment.kt | 4 ++--
.../twidere/fragment/status/PinStatusDialogFragment.kt | 4 ++--
.../org/mariotaku/twidere/fragment/status/StatusFragment.kt | 2 +-
.../twidere/fragment/status/UnpinStatusDialogFragment.kt | 4 ++--
9 files changed, 20 insertions(+), 10 deletions(-)
diff --git a/twidere/src/main/java/org/mariotaku/twidere/model/util/AccountUtils.java b/twidere/src/main/java/org/mariotaku/twidere/model/util/AccountUtils.java
index 65161e0e8..9b721a867 100644
--- a/twidere/src/main/java/org/mariotaku/twidere/model/util/AccountUtils.java
+++ b/twidere/src/main/java/org/mariotaku/twidere/model/util/AccountUtils.java
@@ -129,7 +129,10 @@ public class AccountUtils {
return null;
}
- public static boolean isOfficial(@NonNull final Context context, @NonNull final UserKey accountKey) {
+ public static boolean isOfficial(@Nullable final Context context, @NonNull final UserKey accountKey) {
+ if (context == null) {
+ return false;
+ }
AccountManager am = AccountManager.get(context);
Account account = AccountUtils.findByAccountKey(am, accountKey);
if (account == null) return false;
diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/InternalTwitterContentUtils.java b/twidere/src/main/java/org/mariotaku/twidere/util/InternalTwitterContentUtils.java
index fdef0f3b5..717727ebe 100644
--- a/twidere/src/main/java/org/mariotaku/twidere/util/InternalTwitterContentUtils.java
+++ b/twidere/src/main/java/org/mariotaku/twidere/util/InternalTwitterContentUtils.java
@@ -2,6 +2,7 @@ package org.mariotaku.twidere.util;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import android.content.Context;
import android.text.TextUtils;
import org.mariotaku.microblog.library.twitter.model.DMResponse;
diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/MicroBlogAPIFactory.java b/twidere/src/main/java/org/mariotaku/twidere/util/MicroBlogAPIFactory.java
index bca1bf941..61ed2b846 100644
--- a/twidere/src/main/java/org/mariotaku/twidere/util/MicroBlogAPIFactory.java
+++ b/twidere/src/main/java/org/mariotaku/twidere/util/MicroBlogAPIFactory.java
@@ -25,6 +25,9 @@ import org.mariotaku.twidere.model.ConsumerKeyType;
import org.mariotaku.twidere.model.UserKey;
import org.mariotaku.twidere.model.account.cred.Credentials;
import org.mariotaku.twidere.model.util.AccountUtils;
+import org.mariotaku.twidere.util.api.TwitterAndroidExtraHeaders;
+import org.mariotaku.twidere.util.api.TwitterMacExtraHeaders;
+import org.mariotaku.twidere.util.api.UserAgentExtraHeaders;
import java.util.List;
import java.util.Locale;
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/AccountDetailsExtensions.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/AccountDetailsExtensions.kt
index ed59385da..92998a6aa 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/AccountDetailsExtensions.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/AccountDetailsExtensions.kt
@@ -16,7 +16,10 @@ import org.mariotaku.twidere.util.text.FanfouValidator
import org.mariotaku.twidere.util.text.MastodonValidator
import org.mariotaku.twidere.util.text.TwitterValidator
-fun AccountDetails.isOfficial(context: Context): Boolean {
+fun AccountDetails.isOfficial(context: Context?): Boolean {
+ if (context == null) {
+ return false
+ }
val extra = this.extras
if (extra is TwitterAccountExtras) {
return extra.isOfficialCredentials
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/CustomTabsFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/CustomTabsFragment.kt
index c42d028db..1bc4678fb 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/CustomTabsFragment.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/CustomTabsFragment.kt
@@ -290,7 +290,7 @@ class CustomTabsFragment : BaseFragment(), LoaderCallbacks, MultiChoice
if (!accountRequired) {
accountsAdapter.add(AccountDetails.dummy())
}
- val officialKeyOnly = arguments.getBoolean(EXTRA_OFFICIAL_KEY_ONLY, false)
+ val officialKeyOnly = arguments?.getBoolean(EXTRA_OFFICIAL_KEY_ONLY, false) ?: false
accountsAdapter.addAll(AccountUtils.getAllAccountDetails(AccountManager.get(context), true).filter {
if (officialKeyOnly && !it.isOfficial(context)) {
return@filter false
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/message/MessageConversationInfoFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/message/MessageConversationInfoFragment.kt
index 6af87206c..e6b05aa4c 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/message/MessageConversationInfoFragment.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/message/MessageConversationInfoFragment.kt
@@ -330,7 +330,7 @@ class MessageConversationInfoFragment : BaseFragment(), IToolBarSupportFragment,
private fun performAddParticipant(user: ParcelableUser) {
ProgressDialogFragment.show(childFragmentManager, "add_participant_progress")
val weakThis = WeakReference(this)
- val task = AddParticipantsTask(context, accountKey, conversationId, listOf(user))
+ val task = AddParticipantsTask(context!!, accountKey, conversationId, listOf(user))
task.callback = callback@ { succeed ->
val f = weakThis.get() ?: return@callback
f.dismissDialogThen("add_participant_progress") {
@@ -400,7 +400,7 @@ class MessageConversationInfoFragment : BaseFragment(), IToolBarSupportFragment,
val context = fragment.context
when (account.type) {
AccountType.TWITTER -> {
- if (account.isOfficial(context)) {
+ if (account.isOfficial(context) && context != null) {
val upload = account.newMicroBlogInstance(context, cls = TwitterUpload::class.java)
if (uri == null) {
val result = microBlog.updateDmConversationAvatar(conversationId, null)
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/status/PinStatusDialogFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/status/PinStatusDialogFragment.kt
index 0657636ba..6ae94d09b 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/status/PinStatusDialogFragment.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/status/PinStatusDialogFragment.kt
@@ -20,7 +20,7 @@
package org.mariotaku.twidere.fragment.status
import android.os.Bundle
-import android.support.v4.app.FragmentManager
+import androidx.fragment.app.FragmentManager
import org.mariotaku.abstask.library.TaskStarter
import org.mariotaku.twidere.R
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_STATUS
@@ -35,7 +35,7 @@ class PinStatusDialogFragment : AbsSimpleStatusOperationDialogFragment() {
get() = getString(R.string.message_pin_status_confirm)
override fun onPerformAction(status: ParcelableStatus) {
- val task = PinStatusTask(context, status.account_key, status.id)
+ val task = PinStatusTask(context!!, status.account_key, status.id)
TaskStarter.execute(task)
}
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/status/StatusFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/status/StatusFragment.kt
index 4461bb170..691dcc39a 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/status/StatusFragment.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/status/StatusFragment.kt
@@ -684,7 +684,7 @@ class StatusFragment : BaseFragment(), LoaderCallbacks(fragment.context, status.account_key) {
+ AbsAccountRequestTask(fragment.context!!, status.account_key) {
private val weakFragment = WeakReference(fragment)
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/status/UnpinStatusDialogFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/status/UnpinStatusDialogFragment.kt
index 1d1190140..49171f021 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/status/UnpinStatusDialogFragment.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/status/UnpinStatusDialogFragment.kt
@@ -20,7 +20,7 @@
package org.mariotaku.twidere.fragment.status
import android.os.Bundle
-import android.support.v4.app.FragmentManager
+import androidx.fragment.app.FragmentManager
import org.mariotaku.abstask.library.TaskStarter
import org.mariotaku.twidere.R
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_STATUS
@@ -35,7 +35,7 @@ class UnpinStatusDialogFragment : AbsSimpleStatusOperationDialogFragment() {
get() = getString(R.string.message_unpin_status_confirm)
override fun onPerformAction(status: ParcelableStatus) {
- val task = UnpinStatusTask(context, status.account_key, status.id)
+ val task = UnpinStatusTask(context!!, status.account_key, status.id)
TaskStarter.execute(task)
}
From 353e1e89ef86e378abf933c4f711e0961ab53fa0 Mon Sep 17 00:00:00 2001
From: Tlaster
Date: Tue, 7 Apr 2020 14:11:39 +0800
Subject: [PATCH 19/21] Update DEFAULT_API_CONFIGS_URL
---
.../org/mariotaku/twidere/loader/DefaultAPIConfigLoader.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/loader/DefaultAPIConfigLoader.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/loader/DefaultAPIConfigLoader.kt
index 4357d11b9..78061f709 100644
--- a/twidere/src/main/kotlin/org/mariotaku/twidere/loader/DefaultAPIConfigLoader.kt
+++ b/twidere/src/main/kotlin/org/mariotaku/twidere/loader/DefaultAPIConfigLoader.kt
@@ -62,6 +62,6 @@ class DefaultAPIConfigLoader(context: Context) : FixedAsyncTaskLoader
Date: Tue, 7 Apr 2020 15:00:58 +0800
Subject: [PATCH 20/21] Bump Version
---
build.gradle | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/build.gradle b/build.gradle
index 94720ed43..f5d970268 100644
--- a/build.gradle
+++ b/build.gradle
@@ -17,8 +17,8 @@ allprojects {
ext {
projectGroupId = 'org.mariotaku.twidere'
- projectVersionCode = 503
- projectVersionName = '4.0.4'
+ projectVersionCode = 504
+ projectVersionName = '4.0.5'
globalCompileSdkVersion = 29
globalBuildToolsVersion = '29.0.2'
From c9ec379f706d875284da01f064a5eb3d8c8cc8f3 Mon Sep 17 00:00:00 2001
From: Tlaster
Date: Tue, 7 Apr 2020 16:32:06 +0800
Subject: [PATCH 21/21] Bump Version
---
build.gradle | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/build.gradle b/build.gradle
index f5d970268..8962660e6 100644
--- a/build.gradle
+++ b/build.gradle
@@ -17,8 +17,8 @@ allprojects {
ext {
projectGroupId = 'org.mariotaku.twidere'
- projectVersionCode = 504
- projectVersionName = '4.0.5'
+ projectVersionCode = 505
+ projectVersionName = '4.0.6'
globalCompileSdkVersion = 29
globalBuildToolsVersion = '29.0.2'