fixed #761
This commit is contained in:
parent
d5d0e9bfc6
commit
7d07c4f7a3
|
@ -36,7 +36,7 @@ subprojects {
|
||||||
Kotlin : '1.1.1',
|
Kotlin : '1.1.1',
|
||||||
SupportLib : '25.3.1',
|
SupportLib : '25.3.1',
|
||||||
MariotakuCommons : '0.9.13',
|
MariotakuCommons : '0.9.13',
|
||||||
RestFu : '0.9.48',
|
RestFu : '0.9.49',
|
||||||
ObjectCursor : '0.9.16',
|
ObjectCursor : '0.9.16',
|
||||||
PlayServices : '10.2.1',
|
PlayServices : '10.2.1',
|
||||||
MapsUtils : '0.4.4',
|
MapsUtils : '0.4.4',
|
||||||
|
|
|
@ -49,6 +49,7 @@ public interface TwidereConstants extends SharedPreferenceConstants, IntentConst
|
||||||
String ACCOUNT_USER_DATA_EXTRAS = "extras";
|
String ACCOUNT_USER_DATA_EXTRAS = "extras";
|
||||||
String ACCOUNT_USER_DATA_COLOR = "color";
|
String ACCOUNT_USER_DATA_COLOR = "color";
|
||||||
String ACCOUNT_USER_DATA_POSITION = "position";
|
String ACCOUNT_USER_DATA_POSITION = "position";
|
||||||
|
String ACCOUNT_USER_DATA_TEST = "test";
|
||||||
|
|
||||||
String LOGTAG = TWIDERE_APP_NAME;
|
String LOGTAG = TWIDERE_APP_NAME;
|
||||||
|
|
||||||
|
|
|
@ -221,4 +221,5 @@ public interface IntentConstants {
|
||||||
String EXTRA_PLACE = "place";
|
String EXTRA_PLACE = "place";
|
||||||
String EXTRA_PLACE_NAME = "place_name";
|
String EXTRA_PLACE_NAME = "place_name";
|
||||||
String EXTRA_SCHEDULE_INFO = "schedule_info";
|
String EXTRA_SCHEDULE_INFO = "schedule_info";
|
||||||
|
String EXTRA_SAVE_DRAFT = "save_draft";
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,6 +87,9 @@ public class AccountDetails implements Parcelable, Comparable<AccountDetails> {
|
||||||
@JsonField(name = "dummy")
|
@JsonField(name = "dummy")
|
||||||
public boolean dummy;
|
public boolean dummy;
|
||||||
|
|
||||||
|
@JsonField(name = "test")
|
||||||
|
public boolean test;
|
||||||
|
|
||||||
@JsonField(name = "credentials", typeConverter = JsonStringConverter.class)
|
@JsonField(name = "credentials", typeConverter = JsonStringConverter.class)
|
||||||
@ParcelableNoThanks
|
@ParcelableNoThanks
|
||||||
String credentials_json;
|
String credentials_json;
|
||||||
|
|
|
@ -57,6 +57,9 @@ public class UpdateStatusActionExtras implements ActionExtras {
|
||||||
@JsonField(name = "extended_reply_mode")
|
@JsonField(name = "extended_reply_mode")
|
||||||
@ParcelableThisPlease
|
@ParcelableThisPlease
|
||||||
boolean extendedReplyMode;
|
boolean extendedReplyMode;
|
||||||
|
@JsonField(name = "editing_text")
|
||||||
|
@ParcelableThisPlease
|
||||||
|
String editingText;
|
||||||
|
|
||||||
public ParcelableStatus getInReplyToStatus() {
|
public ParcelableStatus getInReplyToStatus() {
|
||||||
return inReplyToStatus;
|
return inReplyToStatus;
|
||||||
|
@ -114,6 +117,14 @@ public class UpdateStatusActionExtras implements ActionExtras {
|
||||||
this.extendedReplyMode = extendedReplyMode;
|
this.extendedReplyMode = extendedReplyMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getEditingText() {
|
||||||
|
return editingText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEditingText(final String editingText) {
|
||||||
|
this.editingText = editingText;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int describeContents() {
|
public int describeContents() {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -24,6 +24,7 @@ package org.mariotaku.twidere.provider;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.provider.BaseColumns;
|
import android.provider.BaseColumns;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
import org.mariotaku.twidere.model.DraftTableInfo;
|
import org.mariotaku.twidere.model.DraftTableInfo;
|
||||||
import org.mariotaku.twidere.model.FiltersData$BaseItemTableInfo;
|
import org.mariotaku.twidere.model.FiltersData$BaseItemTableInfo;
|
||||||
|
@ -588,6 +589,7 @@ public interface TwidereDataStore {
|
||||||
String TABLE_NAME = "statuses";
|
String TABLE_NAME = "statuses";
|
||||||
String CONTENT_PATH = TABLE_NAME;
|
String CONTENT_PATH = TABLE_NAME;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
Uri CONTENT_URI = Uri.withAppendedPath(BASE_CONTENT_URI, CONTENT_PATH);
|
Uri CONTENT_URI = Uri.withAppendedPath(BASE_CONTENT_URI, CONTENT_PATH);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -802,6 +804,7 @@ public interface TwidereDataStore {
|
||||||
String CONTENT_PATH = "activities_about_me";
|
String CONTENT_PATH = "activities_about_me";
|
||||||
String TABLE_NAME = "activities_about_me";
|
String TABLE_NAME = "activities_about_me";
|
||||||
|
|
||||||
|
@NonNull
|
||||||
Uri CONTENT_URI = Uri.withAppendedPath(BASE_CONTENT_URI, CONTENT_PATH);
|
Uri CONTENT_URI = Uri.withAppendedPath(BASE_CONTENT_URI, CONTENT_PATH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,188 @@
|
||||||
|
/*
|
||||||
|
* Twidere - Twitter client for Android
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mariotaku.twidere.activity
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Intent
|
||||||
|
import android.support.test.InstrumentationRegistry
|
||||||
|
import android.support.test.runner.AndroidJUnit4
|
||||||
|
import kotlinx.android.synthetic.main.activity_compose.*
|
||||||
|
import org.junit.Assert
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mariotaku.twidere.constant.IntentConstants.*
|
||||||
|
import org.mariotaku.twidere.model.ParcelableStatus
|
||||||
|
import org.mariotaku.twidere.model.ParcelableStatusUpdate
|
||||||
|
import org.mariotaku.twidere.test.R
|
||||||
|
import org.mariotaku.twidere.util.getJsonResource
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by mariotaku on 2017/4/16.
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
|
class ComposeActivityTest {
|
||||||
|
|
||||||
|
@get:Rule
|
||||||
|
val activityRule = ComposeActivityTestRule(launchActivity = false)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testReply() {
|
||||||
|
val context = InstrumentationRegistry.getContext()
|
||||||
|
val targetContext = InstrumentationRegistry.getTargetContext()
|
||||||
|
val status: ParcelableStatus = context.resources.getJsonResource(R.raw.parcelable_status_848051071444410368)
|
||||||
|
val intent = Intent(INTENT_ACTION_REPLY)
|
||||||
|
intent.setClass(targetContext, ComposeActivity::class.java)
|
||||||
|
intent.putExtra(EXTRA_STATUS, status)
|
||||||
|
intent.putExtra(EXTRA_SAVE_DRAFT, true)
|
||||||
|
val activity = activityRule.launchActivity(intent)
|
||||||
|
val getStatusUpdate = activity.javaClass.getDeclaredMethod("getStatusUpdate").apply {
|
||||||
|
isAccessible = true
|
||||||
|
}
|
||||||
|
activityRule.runOnUiThread {
|
||||||
|
activity.editText.setText("@t_deyarmin @nixcraft @mariotaku Test Reply")
|
||||||
|
}
|
||||||
|
val statusUpdate = getStatusUpdate(activity) as ParcelableStatusUpdate
|
||||||
|
Assert.assertEquals("Test Reply", statusUpdate.text)
|
||||||
|
assertExcludedMatches(emptyArray(), statusUpdate)
|
||||||
|
activity.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testReplyRemovedSomeMentions() {
|
||||||
|
val context = InstrumentationRegistry.getContext()
|
||||||
|
val targetContext = InstrumentationRegistry.getTargetContext()
|
||||||
|
val status: ParcelableStatus = context.resources.getJsonResource(R.raw.parcelable_status_848051071444410368)
|
||||||
|
val intent = Intent(INTENT_ACTION_REPLY)
|
||||||
|
intent.setClass(targetContext, ComposeActivity::class.java)
|
||||||
|
intent.putExtra(EXTRA_STATUS, status)
|
||||||
|
intent.putExtra(EXTRA_SAVE_DRAFT, true)
|
||||||
|
val activity = activityRule.launchActivity(intent)
|
||||||
|
val getStatusUpdate = activity.javaClass.getDeclaredMethod("getStatusUpdate").apply {
|
||||||
|
isAccessible = true
|
||||||
|
}
|
||||||
|
activityRule.runOnUiThread {
|
||||||
|
activity.editText.setText("@t_deyarmin Test Reply")
|
||||||
|
}
|
||||||
|
val statusUpdate = getStatusUpdate(activity) as ParcelableStatusUpdate
|
||||||
|
Assert.assertEquals("Test Reply", statusUpdate.text)
|
||||||
|
assertExcludedMatches(arrayOf("17484680", "57610574"), statusUpdate)
|
||||||
|
activity.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testReplyNoMentions() {
|
||||||
|
val context = InstrumentationRegistry.getContext()
|
||||||
|
val targetContext = InstrumentationRegistry.getTargetContext()
|
||||||
|
val status: ParcelableStatus = context.resources.getJsonResource(R.raw.parcelable_status_848051071444410368)
|
||||||
|
val intent = Intent(INTENT_ACTION_REPLY)
|
||||||
|
intent.setClass(targetContext, ComposeActivity::class.java)
|
||||||
|
intent.putExtra(EXTRA_STATUS, status)
|
||||||
|
intent.putExtra(EXTRA_SAVE_DRAFT, true)
|
||||||
|
val activity = activityRule.launchActivity(intent)
|
||||||
|
val getStatusUpdate = activity.javaClass.getDeclaredMethod("getStatusUpdate").apply {
|
||||||
|
isAccessible = true
|
||||||
|
}
|
||||||
|
activityRule.runOnUiThread {
|
||||||
|
activity.editText.setText("Test Reply")
|
||||||
|
}
|
||||||
|
val statusUpdate = getStatusUpdate(activity) as ParcelableStatusUpdate
|
||||||
|
Assert.assertEquals("Test Reply", statusUpdate.text)
|
||||||
|
Assert.assertEquals("https://twitter.com/t_deyarmin/status/847950697987493888",
|
||||||
|
statusUpdate.attachment_url)
|
||||||
|
assertExcludedMatches(emptyArray(), statusUpdate)
|
||||||
|
activity.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testReplySelf() {
|
||||||
|
val context = InstrumentationRegistry.getContext()
|
||||||
|
val targetContext = InstrumentationRegistry.getTargetContext()
|
||||||
|
val status: ParcelableStatus = context.resources.getJsonResource(R.raw.parcelable_status_852737226718838790)
|
||||||
|
val intent = Intent(INTENT_ACTION_REPLY)
|
||||||
|
intent.setClass(targetContext, ComposeActivity::class.java)
|
||||||
|
intent.putExtra(EXTRA_STATUS, status)
|
||||||
|
intent.putExtra(EXTRA_SAVE_DRAFT, true)
|
||||||
|
val activity = activityRule.launchActivity(intent)
|
||||||
|
val getStatusUpdate = activity.javaClass.getDeclaredMethod("getStatusUpdate").apply {
|
||||||
|
isAccessible = true
|
||||||
|
}
|
||||||
|
activityRule.runOnUiThread {
|
||||||
|
activity.editText.setText("@TwidereProject @mariotaku Test Reply")
|
||||||
|
}
|
||||||
|
val statusUpdate = getStatusUpdate(activity) as ParcelableStatusUpdate
|
||||||
|
Assert.assertEquals("Test Reply", statusUpdate.text)
|
||||||
|
assertExcludedMatches(emptyArray(), statusUpdate)
|
||||||
|
activity.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testReplySelfRemovedSomeMentions() {
|
||||||
|
val context = InstrumentationRegistry.getContext()
|
||||||
|
val targetContext = InstrumentationRegistry.getTargetContext()
|
||||||
|
val status: ParcelableStatus = context.resources.getJsonResource(R.raw.parcelable_status_852737226718838790)
|
||||||
|
val intent = Intent(INTENT_ACTION_REPLY)
|
||||||
|
intent.setClass(targetContext, ComposeActivity::class.java)
|
||||||
|
intent.putExtra(EXTRA_STATUS, status)
|
||||||
|
intent.putExtra(EXTRA_SAVE_DRAFT, true)
|
||||||
|
val activity = activityRule.launchActivity(intent)
|
||||||
|
val getStatusUpdate = activity.javaClass.getDeclaredMethod("getStatusUpdate").apply {
|
||||||
|
isAccessible = true
|
||||||
|
}
|
||||||
|
activityRule.runOnUiThread {
|
||||||
|
activity.editText.setText("@TwidereProject Test Reply")
|
||||||
|
}
|
||||||
|
val statusUpdate = getStatusUpdate(activity) as ParcelableStatusUpdate
|
||||||
|
Assert.assertEquals("Test Reply", statusUpdate.text)
|
||||||
|
assertExcludedMatches(arrayOf("57610574"), statusUpdate)
|
||||||
|
activity.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testReplySelfNoMentions() {
|
||||||
|
val context = InstrumentationRegistry.getContext()
|
||||||
|
val targetContext = InstrumentationRegistry.getTargetContext()
|
||||||
|
val status: ParcelableStatus = context.resources.getJsonResource(R.raw.parcelable_status_852737226718838790)
|
||||||
|
val intent = Intent(INTENT_ACTION_REPLY)
|
||||||
|
intent.setClass(targetContext, ComposeActivity::class.java)
|
||||||
|
intent.putExtra(EXTRA_STATUS, status)
|
||||||
|
intent.putExtra(EXTRA_SAVE_DRAFT, true)
|
||||||
|
val activity = activityRule.launchActivity(intent)
|
||||||
|
val getStatusUpdate = activity.javaClass.getDeclaredMethod("getStatusUpdate").apply {
|
||||||
|
isAccessible = true
|
||||||
|
}
|
||||||
|
activityRule.runOnUiThread {
|
||||||
|
activity.editText.setText("Test Reply")
|
||||||
|
}
|
||||||
|
val statusUpdate = getStatusUpdate(activity) as ParcelableStatusUpdate
|
||||||
|
Assert.assertEquals("Test Reply", statusUpdate.text)
|
||||||
|
assertExcludedMatches(arrayOf("583328497", "57610574"), statusUpdate)
|
||||||
|
activity.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertExcludedMatches(expectedIds: Array<String>, statusUpdate: ParcelableStatusUpdate): Boolean {
|
||||||
|
return statusUpdate.excluded_reply_user_ids?.all { excludedId ->
|
||||||
|
expectedIds.any { expectation ->
|
||||||
|
expectation.equals(excludedId, ignoreCase = true)
|
||||||
|
}
|
||||||
|
} ?: expectedIds.isEmpty()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Twidere - Twitter client for Android
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mariotaku.twidere.activity
|
||||||
|
|
||||||
|
import android.support.test.rule.ActivityTestRule
|
||||||
|
import org.mariotaku.twidere.util.TestAccountUtils
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by mariotaku on 2017/4/16.
|
||||||
|
*/
|
||||||
|
class ComposeActivityTestRule(initialTouchMode: Boolean = false, launchActivity: Boolean = true) :
|
||||||
|
ActivityTestRule<ComposeActivity>(ComposeActivity::class.java, initialTouchMode, launchActivity) {
|
||||||
|
|
||||||
|
override fun beforeActivityLaunched() {
|
||||||
|
TestAccountUtils.insertTestAccounts()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun afterActivityFinished() {
|
||||||
|
TestAccountUtils.removeTestAccounts()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Twidere - Twitter client for Android
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mariotaku.twidere.extension
|
||||||
|
|
||||||
|
import android.accounts.Account
|
||||||
|
import android.accounts.AccountManager
|
||||||
|
import org.mariotaku.twidere.TwidereConstants.ACCOUNT_USER_DATA_TEST
|
||||||
|
import org.mariotaku.twidere.extension.model.AccountDataQueue
|
||||||
|
|
||||||
|
|
||||||
|
fun Account.isTest(am: AccountManager): Boolean {
|
||||||
|
return AccountDataQueue.getUserData(am, this, ACCOUNT_USER_DATA_TEST)?.toBoolean() ?: true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Account.setTest(am: AccountManager, test: Boolean) {
|
||||||
|
am.setUserData(this, ACCOUNT_USER_DATA_TEST, test.toString())
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* Twidere - Twitter client for Android
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mariotaku.twidere.util
|
||||||
|
|
||||||
|
import android.accounts.AccountManager
|
||||||
|
import android.support.test.InstrumentationRegistry
|
||||||
|
import org.mariotaku.twidere.extensions.model.updateDetails
|
||||||
|
import org.mariotaku.twidere.model.AccountDetails
|
||||||
|
import org.mariotaku.twidere.model.util.AccountUtils
|
||||||
|
import org.mariotaku.twidere.test.R
|
||||||
|
import org.mariotaku.twidere.util.support.removeAccountSupport
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by mariotaku on 2017/4/16.
|
||||||
|
*/
|
||||||
|
object TestAccountUtils {
|
||||||
|
|
||||||
|
private val accountResources = intArrayOf(R.raw.account_4223092274_twitter_com)
|
||||||
|
|
||||||
|
fun insertTestAccounts() {
|
||||||
|
val targetContext = InstrumentationRegistry.getTargetContext()
|
||||||
|
val context = InstrumentationRegistry.getContext()
|
||||||
|
val am = AccountManager.get(targetContext)
|
||||||
|
val existingAccounts = AccountUtils.getAllAccountDetails(am, false)
|
||||||
|
accountResources.forEach { resId ->
|
||||||
|
val details = context.resources.openRawResource(resId).use {
|
||||||
|
JsonSerializer.parse(it, AccountDetails::class.java)
|
||||||
|
}
|
||||||
|
if (existingAccounts.any { it.account == details.account || it.key == details.key }) {
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
am.addAccountExplicitly(details.account, null, null)
|
||||||
|
details.account.updateDetails(am, details)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeTestAccounts() {
|
||||||
|
val targetContext = InstrumentationRegistry.getTargetContext()
|
||||||
|
val am = AccountManager.get(targetContext)
|
||||||
|
val existingAccounts = AccountUtils.getAllAccountDetails(am, false)
|
||||||
|
existingAccounts.filter { it.test }.forEach { am.removeAccountSupport(it.account) }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Twidere - Twitter client for Android
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mariotaku.twidere.util
|
||||||
|
|
||||||
|
import android.content.res.Resources
|
||||||
|
import android.support.annotation.RawRes
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by mariotaku on 2017/4/16.
|
||||||
|
*/
|
||||||
|
inline fun <reified T> Resources.getJsonResource(@RawRes id: Int) = openRawResource(id).use {
|
||||||
|
JsonSerializer.parse(it, T::class.java)
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
{
|
||||||
|
"account": {
|
||||||
|
"name": "TwidereTest@twitter.com",
|
||||||
|
"type": "org.mariotaku.twidere.account"
|
||||||
|
},
|
||||||
|
"activated": false,
|
||||||
|
"color": "#F44336",
|
||||||
|
"credentials": {
|
||||||
|
"access_token": "4223092274-REDACTED",
|
||||||
|
"access_token_secret": "REDACTED",
|
||||||
|
"consumer_key": "REDACTED",
|
||||||
|
"consumer_secret": "REDACTED",
|
||||||
|
"same_oauth_signing_url": false,
|
||||||
|
"api_url_format": "https:\/\/[DOMAIN.]twitter.com\/",
|
||||||
|
"no_version_suffix": false
|
||||||
|
},
|
||||||
|
"credentials_type": "oauth",
|
||||||
|
"dummy": false,
|
||||||
|
"test": true,
|
||||||
|
"extras": {
|
||||||
|
"official_credentials": false
|
||||||
|
},
|
||||||
|
"key": "4223092274@twitter.com",
|
||||||
|
"position": 4,
|
||||||
|
"type": "twitter",
|
||||||
|
"user": {
|
||||||
|
"account_id": "4223092274@twitter.com",
|
||||||
|
"background_color": "#C0DEED",
|
||||||
|
"created_at": 1447415354000,
|
||||||
|
"description_plain": "Posting test tweets, do not follow",
|
||||||
|
"description_spans": [],
|
||||||
|
"description_unescaped": "Posting test tweets, do not follow",
|
||||||
|
"extras": {
|
||||||
|
"blocked_by": false,
|
||||||
|
"blocking": false,
|
||||||
|
"followed_by": false,
|
||||||
|
"groups_count": -1,
|
||||||
|
"muting": false,
|
||||||
|
"notifications_enabled": false
|
||||||
|
},
|
||||||
|
"favorites_count": 0,
|
||||||
|
"followers_count": 1,
|
||||||
|
"friends_count": 2,
|
||||||
|
"is_basic": false,
|
||||||
|
"is_cache": true,
|
||||||
|
"is_follow_request_sent": false,
|
||||||
|
"is_following": false,
|
||||||
|
"is_protected": true,
|
||||||
|
"is_verified": false,
|
||||||
|
"id": "4223092274@twitter.com",
|
||||||
|
"link_color": "#1DA1F2",
|
||||||
|
"listed_count": 0,
|
||||||
|
"location": "",
|
||||||
|
"media_count": -1,
|
||||||
|
"name": "Twidere Test",
|
||||||
|
"position": 0,
|
||||||
|
"profile_background_url": "https:\/\/abs.twimg.com\/images\/themes\/theme1\/bg.png",
|
||||||
|
"profile_image_url": "https:\/\/pbs.twimg.com\/profile_images\/665134634229874688\/gQA1EEUb_reasonably_small.png",
|
||||||
|
"screen_name": "TwidereTest",
|
||||||
|
"statuses_count": 7,
|
||||||
|
"text_color": "#333333"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
{
|
||||||
|
"account_color": -769226,
|
||||||
|
"account_id": "4223092274@twitter.com",
|
||||||
|
"extras": {
|
||||||
|
"display_text_range": [
|
||||||
|
27,
|
||||||
|
33
|
||||||
|
],
|
||||||
|
"support_entities": true,
|
||||||
|
"user_profile_image_url_fallback": "https://pbs.twimg.com/profile_images/665134634229874688/gQA1EEUb_normal.png"
|
||||||
|
},
|
||||||
|
"favorite_count": 0,
|
||||||
|
"id": "852737226718838790",
|
||||||
|
"in_reply_to_name": "Twidere Project",
|
||||||
|
"in_reply_to_screen_name": "TwidereProject",
|
||||||
|
"in_reply_to_status_id": "852736831107944448",
|
||||||
|
"in_reply_to_user_id": "583328497@twitter.com",
|
||||||
|
"is_favorite": false,
|
||||||
|
"is_gap": false,
|
||||||
|
"is_possibly_sensitive": false,
|
||||||
|
"is_quote": false,
|
||||||
|
"is_retweet": false,
|
||||||
|
"lang": "zh",
|
||||||
|
"media": [],
|
||||||
|
"mentions": [
|
||||||
|
{
|
||||||
|
"id": "583328497@twitter.com",
|
||||||
|
"name": "Twidere Project",
|
||||||
|
"screen_name": "TwidereProject"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "57610574@twitter.com",
|
||||||
|
"name": "宅撩一边捣鼓着Twidere一边",
|
||||||
|
"screen_name": "mariotaku"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"position_key": 1492143372225,
|
||||||
|
"quoted_timestamp": 0,
|
||||||
|
"quoted_user_is_protected": false,
|
||||||
|
"quoted_user_is_verified": false,
|
||||||
|
"quoted_user_id": null,
|
||||||
|
"reply_count": -1,
|
||||||
|
"retweet_count": 0,
|
||||||
|
"retweet_timestamp": -1,
|
||||||
|
"retweeted": false,
|
||||||
|
"retweeted_by_user_id": null,
|
||||||
|
"sort_id": 852737226718838790,
|
||||||
|
"source": "<a href=\"https://github.com/mariotaku/twidere/\" rel=\"nofollow\">Twidere for Android #7</a>",
|
||||||
|
"spans": [],
|
||||||
|
"text_plain": "@TwidereProject @mariotaku 测试回复全部",
|
||||||
|
"text_unescaped": "@TwidereProject @mariotaku 测试回复全部",
|
||||||
|
"timestamp": 1492143372000,
|
||||||
|
"user_is_following": false,
|
||||||
|
"user_is_protected": true,
|
||||||
|
"user_is_verified": false,
|
||||||
|
"user_id": "4223092274@twitter.com",
|
||||||
|
"user_name": "Twidere Test",
|
||||||
|
"user_profile_image_url": "https://pbs.twimg.com/profile_images/665134634229874688/gQA1EEUb_reasonably_small.png",
|
||||||
|
"user_screen_name": "TwidereTest"
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* Twidere - Twitter client for Android
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.mariotaku.twidere.extensions.model
|
||||||
|
|
||||||
|
import android.accounts.Account
|
||||||
|
import android.accounts.AccountManager
|
||||||
|
import org.mariotaku.ktextension.HexColorFormat
|
||||||
|
import org.mariotaku.ktextension.toHexColor
|
||||||
|
import org.mariotaku.twidere.TwidereConstants.*
|
||||||
|
import org.mariotaku.twidere.model.AccountDetails
|
||||||
|
import org.mariotaku.twidere.util.JsonSerializer
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by mariotaku on 2017/4/16.
|
||||||
|
*/
|
||||||
|
fun Account.updateDetails(am: AccountManager, details: AccountDetails) {
|
||||||
|
am.setUserData(this, ACCOUNT_USER_DATA_KEY, details.key.toString())
|
||||||
|
am.setUserData(this, ACCOUNT_USER_DATA_TYPE, details.type)
|
||||||
|
am.setUserData(this, ACCOUNT_USER_DATA_CREDS_TYPE, details.credentials_type)
|
||||||
|
|
||||||
|
am.setUserData(this, ACCOUNT_USER_DATA_ACTIVATED, details.activated.toString())
|
||||||
|
am.setUserData(this, ACCOUNT_USER_DATA_TEST, details.test.toString())
|
||||||
|
am.setUserData(this, ACCOUNT_USER_DATA_COLOR, toHexColor(details.color, format = HexColorFormat.RGB))
|
||||||
|
|
||||||
|
am.setUserData(this, ACCOUNT_USER_DATA_USER, JsonSerializer.serialize(details.user))
|
||||||
|
am.setUserData(this, ACCOUNT_USER_DATA_EXTRAS, details.extras?.let { JsonSerializer.serialize(it) })
|
||||||
|
am.setAuthToken(this, ACCOUNT_AUTH_TOKEN_TYPE, JsonSerializer.serialize(details.credentials))
|
||||||
|
}
|
|
@ -19,7 +19,6 @@
|
||||||
|
|
||||||
package org.mariotaku.twidere.util.stetho
|
package org.mariotaku.twidere.util.stetho
|
||||||
|
|
||||||
import android.accounts.Account
|
|
||||||
import android.accounts.AccountManager
|
import android.accounts.AccountManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
|
@ -37,10 +36,8 @@ import com.jayway.jsonpath.spi.mapper.MappingProvider
|
||||||
import org.apache.commons.cli.*
|
import org.apache.commons.cli.*
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import org.mariotaku.ktextension.HexColorFormat
|
|
||||||
import org.mariotaku.ktextension.subArray
|
import org.mariotaku.ktextension.subArray
|
||||||
import org.mariotaku.ktextension.toHexColor
|
import org.mariotaku.twidere.extensions.model.updateDetails
|
||||||
import org.mariotaku.twidere.TwidereConstants.*
|
|
||||||
import org.mariotaku.twidere.model.AccountDetails
|
import org.mariotaku.twidere.model.AccountDetails
|
||||||
import org.mariotaku.twidere.model.UserKey
|
import org.mariotaku.twidere.model.UserKey
|
||||||
import org.mariotaku.twidere.model.util.AccountUtils
|
import org.mariotaku.twidere.model.util.AccountUtils
|
||||||
|
@ -281,19 +278,6 @@ class AccountsDumperPlugin(val context: Context) : DumperPlugin {
|
||||||
return SecretKeySpec(factory.generateSecret(spec).encoded, "AES")
|
return SecretKeySpec(factory.generateSecret(spec).encoded, "AES")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Account.updateDetails(am: AccountManager, details: AccountDetails) {
|
|
||||||
am.setUserData(this, ACCOUNT_USER_DATA_KEY, details.key.toString())
|
|
||||||
am.setUserData(this, ACCOUNT_USER_DATA_TYPE, details.type)
|
|
||||||
am.setUserData(this, ACCOUNT_USER_DATA_CREDS_TYPE, details.credentials_type)
|
|
||||||
|
|
||||||
am.setUserData(this, ACCOUNT_USER_DATA_ACTIVATED, true.toString())
|
|
||||||
am.setUserData(this, ACCOUNT_USER_DATA_COLOR, toHexColor(details.color, format = HexColorFormat.RGB))
|
|
||||||
|
|
||||||
am.setUserData(this, ACCOUNT_USER_DATA_USER, JsonSerializer.serialize(details.user))
|
|
||||||
am.setUserData(this, ACCOUNT_USER_DATA_EXTRAS, details.extras?.let { JsonSerializer.serialize(it) })
|
|
||||||
am.setAuthToken(this, ACCOUNT_AUTH_TOKEN_TYPE, JsonSerializer.serialize(details.credentials))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun AccountManager.docContext(forKey: String): DocumentContext {
|
private fun AccountManager.docContext(forKey: String): DocumentContext {
|
||||||
val accountKey = UserKey.valueOf(forKey)
|
val accountKey = UserKey.valueOf(forKey)
|
||||||
val details = AccountUtils.getAccountDetails(this, accountKey, true) ?: throw Utils.NoAccountException()
|
val details = AccountUtils.getAccountDetails(this, accountKey, true) ?: throw Utils.NoAccountException()
|
||||||
|
|
|
@ -50,6 +50,9 @@ public abstract class TabConfiguration {
|
||||||
@AccountFlags
|
@AccountFlags
|
||||||
public abstract int getAccountFlags();
|
public abstract int getAccountFlags();
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public abstract Class<? extends Fragment> getFragmentClass();
|
||||||
|
|
||||||
public boolean isSingleTab() {
|
public boolean isSingleTab() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -63,9 +66,6 @@ public abstract class TabConfiguration {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
|
||||||
public abstract Class<? extends Fragment> getFragmentClass();
|
|
||||||
|
|
||||||
public boolean applyExtraConfigurationTo(@NonNull Tab tab, @NonNull ExtraConfiguration extraConf) {
|
public boolean applyExtraConfigurationTo(@NonNull Tab tab, @NonNull ExtraConfiguration extraConf) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import static org.mariotaku.twidere.TwidereConstants.ACCOUNT_USER_DATA_CREDS_TYP
|
||||||
import static org.mariotaku.twidere.TwidereConstants.ACCOUNT_USER_DATA_EXTRAS;
|
import static org.mariotaku.twidere.TwidereConstants.ACCOUNT_USER_DATA_EXTRAS;
|
||||||
import static org.mariotaku.twidere.TwidereConstants.ACCOUNT_USER_DATA_KEY;
|
import static org.mariotaku.twidere.TwidereConstants.ACCOUNT_USER_DATA_KEY;
|
||||||
import static org.mariotaku.twidere.TwidereConstants.ACCOUNT_USER_DATA_POSITION;
|
import static org.mariotaku.twidere.TwidereConstants.ACCOUNT_USER_DATA_POSITION;
|
||||||
|
import static org.mariotaku.twidere.TwidereConstants.ACCOUNT_USER_DATA_TEST;
|
||||||
import static org.mariotaku.twidere.TwidereConstants.ACCOUNT_USER_DATA_TYPE;
|
import static org.mariotaku.twidere.TwidereConstants.ACCOUNT_USER_DATA_TYPE;
|
||||||
import static org.mariotaku.twidere.TwidereConstants.ACCOUNT_USER_DATA_USER;
|
import static org.mariotaku.twidere.TwidereConstants.ACCOUNT_USER_DATA_USER;
|
||||||
|
|
||||||
|
@ -41,6 +42,7 @@ public class AccountUtils {
|
||||||
ACCOUNT_USER_DATA_EXTRAS,
|
ACCOUNT_USER_DATA_EXTRAS,
|
||||||
ACCOUNT_USER_DATA_COLOR,
|
ACCOUNT_USER_DATA_COLOR,
|
||||||
ACCOUNT_USER_DATA_POSITION,
|
ACCOUNT_USER_DATA_POSITION,
|
||||||
|
ACCOUNT_USER_DATA_TEST,
|
||||||
};
|
};
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
|
|
@ -329,7 +329,8 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
if (!shouldSkipDraft && hasComposingStatus() && isFinishing) {
|
if (!shouldSkipDraft && intent.getBooleanExtra(EXTRA_SAVE_DRAFT, true)
|
||||||
|
&& hasComposingStatus() && isFinishing) {
|
||||||
saveToDrafts()
|
saveToDrafts()
|
||||||
Toast.makeText(this, R.string.message_toast_status_saved_to_draft, Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, R.string.message_toast_status_saved_to_draft, Toast.LENGTH_SHORT).show()
|
||||||
} else {
|
} else {
|
||||||
|
@ -737,11 +738,12 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
||||||
if (intent.action == INTENT_ACTION_EDIT_DRAFT) return true
|
if (intent.action == INTENT_ACTION_EDIT_DRAFT) return true
|
||||||
if (hasMedia) return true
|
if (hasMedia) return true
|
||||||
val text = editText.text?.toString().orEmpty()
|
val text = editText.text?.toString().orEmpty()
|
||||||
|
if (text == originalText) return false
|
||||||
val replyTextAndMentions = getTwitterReplyTextAndMentions(text)
|
val replyTextAndMentions = getTwitterReplyTextAndMentions(text)
|
||||||
if (replyTextAndMentions != null) {
|
if (replyTextAndMentions != null) {
|
||||||
return replyTextAndMentions.replyText.isNotEmpty()
|
return replyTextAndMentions.replyText.isNotEmpty()
|
||||||
}
|
}
|
||||||
return text.isNotEmpty() && text != originalText
|
return text.isNotEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun confirmAndUpdateStatus() {
|
private fun confirmAndUpdateStatus() {
|
||||||
|
@ -1051,8 +1053,13 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
||||||
editText.append("@${status.user_screen_name} ")
|
editText.append("@${status.user_screen_name} ")
|
||||||
}
|
}
|
||||||
|
|
||||||
val selectionEnd = editText.length()
|
val text = intent.getStringExtra(EXTRA_TEXT)
|
||||||
editText.setSelection(selectionStart, selectionEnd)
|
if (text != null) {
|
||||||
|
editText.append(text)
|
||||||
|
} else {
|
||||||
|
val selectionEnd = editText.length()
|
||||||
|
editText.setSelection(selectionStart, selectionEnd)
|
||||||
|
}
|
||||||
accountsAdapter.selectedAccountKeys = arrayOf(status.account_key)
|
accountsAdapter.selectedAccountKeys = arrayOf(status.account_key)
|
||||||
showReplyLabelAndHint(status)
|
showReplyLabelAndHint(status)
|
||||||
return true
|
return true
|
||||||
|
@ -1060,19 +1067,24 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
||||||
|
|
||||||
private fun handleEditDraftIntent(draft: Draft?): Boolean {
|
private fun handleEditDraftIntent(draft: Draft?): Boolean {
|
||||||
if (draft == null) return false
|
if (draft == null) return false
|
||||||
|
val extras = draft.action_extras as? UpdateStatusActionExtras
|
||||||
|
val media = draft.media
|
||||||
draftUniqueId = draft.unique_id_non_null
|
draftUniqueId = draft.unique_id_non_null
|
||||||
editText.setText(draft.text)
|
|
||||||
val selectionEnd = editText.length()
|
|
||||||
editText.setSelection(selectionEnd)
|
|
||||||
accountsAdapter.selectedAccountKeys = draft.account_keys ?: emptyArray()
|
|
||||||
if (draft.media != null) {
|
|
||||||
addMedia(Arrays.asList(*draft.media))
|
|
||||||
}
|
|
||||||
recentLocation = draft.location
|
recentLocation = draft.location
|
||||||
(draft.action_extras as? UpdateStatusActionExtras)?.let {
|
accountsAdapter.selectedAccountKeys = draft.account_keys ?: emptyArray()
|
||||||
possiblySensitive = it.isPossiblySensitive
|
|
||||||
inReplyToStatus = it.inReplyToStatus
|
editText.setText(extras?.editingText ?: draft.text)
|
||||||
|
editText.setSelection(editText.length())
|
||||||
|
|
||||||
|
if (media != null) {
|
||||||
|
addMedia(Arrays.asList(*media))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (extras != null) {
|
||||||
|
possiblySensitive = extras.isPossiblySensitive
|
||||||
|
inReplyToStatus = extras.inReplyToStatus
|
||||||
|
}
|
||||||
|
|
||||||
val tag = Uri.withAppendedPath(Drafts.CONTENT_URI, draft._id.toString()).toString()
|
val tag = Uri.withAppendedPath(Drafts.CONTENT_URI, draft._id.toString()).toString()
|
||||||
notificationManager.cancel(tag, NOTIFICATION_ID_DRAFTS)
|
notificationManager.cancel(tag, NOTIFICATION_ID_DRAFTS)
|
||||||
return true
|
return true
|
||||||
|
@ -1457,7 +1469,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
||||||
val accounts = AccountUtils.getAllAccountDetails(AccountManager.get(this), accountKeys, true)
|
val accounts = AccountUtils.getAllAccountDetails(AccountManager.get(this), accountKeys, true)
|
||||||
val maxLength = statusTextCount.maxLength
|
val maxLength = statusTextCount.maxLength
|
||||||
val inReplyTo = inReplyToStatus
|
val inReplyTo = inReplyToStatus
|
||||||
val replyTextAndMentions = getTwitterReplyTextAndMentions(text)
|
val replyTextAndMentions = getTwitterReplyTextAndMentions(text, accounts)
|
||||||
if (inReplyTo != null && replyTextAndMentions != null) {
|
if (inReplyTo != null && replyTextAndMentions != null) {
|
||||||
val (replyStartIndex, replyText, _, excludedMentions, replyToOriginalUser) =
|
val (replyStartIndex, replyText, _, excludedMentions, replyToOriginalUser) =
|
||||||
replyTextAndMentions
|
replyTextAndMentions
|
||||||
|
@ -1471,8 +1483,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
||||||
val replyToSelf = accounts.singleOrNull()?.key == inReplyTo.user_key
|
val replyToSelf = accounts.singleOrNull()?.key == inReplyTo.user_key
|
||||||
// Fix status to at least make mentioned user know what status it is
|
// Fix status to at least make mentioned user know what status it is
|
||||||
if (!replyToOriginalUser && !replyToSelf) {
|
if (!replyToOriginalUser && !replyToSelf) {
|
||||||
update.attachment_url = LinkCreator.getTwitterStatusLink(inReplyTo.user_screen_name,
|
update.attachment_url = LinkCreator.getStatusWebLink(inReplyTo).toString()
|
||||||
inReplyTo.id).toString()
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (text.isEmpty() && media.isEmpty()) throw NoContentException()
|
if (text.isEmpty() && media.isEmpty()) throw NoContentException()
|
||||||
|
@ -1494,7 +1505,9 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
||||||
update.media = media
|
update.media = media
|
||||||
update.in_reply_to_status = inReplyTo
|
update.in_reply_to_status = inReplyTo
|
||||||
update.is_possibly_sensitive = possiblySensitive
|
update.is_possibly_sensitive = possiblySensitive
|
||||||
update.draft_extras = update.updateStatusActionExtras()
|
update.draft_extras = update.updateStatusActionExtras().also {
|
||||||
|
it.editingText = text
|
||||||
|
}
|
||||||
return update
|
return update
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1541,11 +1554,12 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getTwitterReplyTextAndMentions(text: String = editText.text?.toString().orEmpty()):
|
private fun getTwitterReplyTextAndMentions(text: String = editText.text?.toString().orEmpty(),
|
||||||
ReplyTextAndMentions? {
|
accounts: Array<AccountDetails> = accountsAdapter.selectedAccounts): ReplyTextAndMentions? {
|
||||||
val inReplyTo = inReplyToStatus ?: return null
|
val inReplyTo = inReplyToStatus ?: return null
|
||||||
if (!ignoreMentions) return null
|
if (!ignoreMentions) return null
|
||||||
return extractor.extractReplyTextAndMentions(text, inReplyTo)
|
val account = accounts.singleOrNull() ?: return null
|
||||||
|
return extractor.extractReplyTextAndMentions(text, inReplyTo, account.key)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun saveToDrafts(): Uri? {
|
private fun saveToDrafts(): Uri? {
|
||||||
|
@ -2031,6 +2045,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
||||||
it.excludedReplyUserIds = excluded_reply_user_ids
|
it.excludedReplyUserIds = excluded_reply_user_ids
|
||||||
it.isExtendedReplyMode = extended_reply_mode
|
it.isExtendedReplyMode = extended_reply_mode
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,8 +34,6 @@ import nl.komponents.kovenant.android.startKovenant
|
||||||
import nl.komponents.kovenant.android.stopKovenant
|
import nl.komponents.kovenant.android.stopKovenant
|
||||||
import nl.komponents.kovenant.task
|
import nl.komponents.kovenant.task
|
||||||
import okhttp3.Dns
|
import okhttp3.Dns
|
||||||
import org.apache.commons.lang3.concurrent.ConcurrentUtils
|
|
||||||
import org.mariotaku.commons.logansquare.LoganSquareMapperFinder
|
|
||||||
import org.mariotaku.kpreferences.KPreferences
|
import org.mariotaku.kpreferences.KPreferences
|
||||||
import org.mariotaku.kpreferences.get
|
import org.mariotaku.kpreferences.get
|
||||||
import org.mariotaku.kpreferences.set
|
import org.mariotaku.kpreferences.set
|
||||||
|
@ -64,8 +62,6 @@ import org.mariotaku.twidere.util.refresh.AutoRefreshController
|
||||||
import org.mariotaku.twidere.util.sync.DataSyncProvider
|
import org.mariotaku.twidere.util.sync.DataSyncProvider
|
||||||
import org.mariotaku.twidere.util.sync.SyncController
|
import org.mariotaku.twidere.util.sync.SyncController
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.Callable
|
|
||||||
import java.util.concurrent.Future
|
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -124,11 +120,6 @@ class TwidereApplication : Application(), Constants, OnSharedPreferenceChangeLis
|
||||||
StrictModeUtils.detectAllVmPolicy()
|
StrictModeUtils.detectAllVmPolicy()
|
||||||
}
|
}
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
LoganSquareMapperFinder.setDefaultExecutor(object : LoganSquareMapperFinder.FutureExecutor {
|
|
||||||
override fun <T> submit(callable: Callable<T>): Future<T> {
|
|
||||||
return ConcurrentUtils.constantFuture(callable.call())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
applyLanguageSettings()
|
applyLanguageSettings()
|
||||||
startKovenant()
|
startKovenant()
|
||||||
initializeAsyncTask()
|
initializeAsyncTask()
|
||||||
|
|
|
@ -23,6 +23,7 @@ import com.twitter.Extractor
|
||||||
import org.mariotaku.twidere.extension.model.replyMentions
|
import org.mariotaku.twidere.extension.model.replyMentions
|
||||||
import org.mariotaku.twidere.model.ParcelableStatus
|
import org.mariotaku.twidere.model.ParcelableStatus
|
||||||
import org.mariotaku.twidere.model.ParcelableUserMention
|
import org.mariotaku.twidere.model.ParcelableUserMention
|
||||||
|
import org.mariotaku.twidere.model.UserKey
|
||||||
|
|
||||||
fun Extractor.extractMentionsAndNonMentionStartIndex(text: String, mentions: Array<ParcelableUserMention>?): MentionsAndNonMentionStartIndex {
|
fun Extractor.extractMentionsAndNonMentionStartIndex(text: String, mentions: Array<ParcelableUserMention>?): MentionsAndNonMentionStartIndex {
|
||||||
var nextExpectedPos = 0
|
var nextExpectedPos = 0
|
||||||
|
@ -39,7 +40,8 @@ fun Extractor.extractMentionsAndNonMentionStartIndex(text: String, mentions: Arr
|
||||||
return MentionsAndNonMentionStartIndex(entities, nextExpectedPos)
|
return MentionsAndNonMentionStartIndex(entities, nextExpectedPos)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Extractor.extractReplyTextAndMentions(text: String, inReplyTo: ParcelableStatus): ReplyTextAndMentions {
|
fun Extractor.extractReplyTextAndMentions(text: String, inReplyTo: ParcelableStatus,
|
||||||
|
accountKey: UserKey = inReplyTo.account_key): ReplyTextAndMentions {
|
||||||
// First extract mentions and 'real text' start index
|
// First extract mentions and 'real text' start index
|
||||||
val (textMentions, index) = extractMentionsAndNonMentionStartIndex(text, inReplyTo.replyMentions)
|
val (textMentions, index) = extractMentionsAndNonMentionStartIndex(text, inReplyTo.replyMentions)
|
||||||
|
|
||||||
|
@ -72,7 +74,7 @@ fun Extractor.extractReplyTextAndMentions(text: String, inReplyTo: ParcelableSta
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Find reply text contains mention to `inReplyTo.user`
|
// Find reply text contains mention to `inReplyTo.user`
|
||||||
val mentioningUser = textMentions.any {
|
val mentioningUser = accountKey == inReplyTo.user_key || textMentions.any {
|
||||||
it.value.equals(inReplyTo.user_screen_name, ignoreCase = true)
|
it.value.equals(inReplyTo.user_screen_name, ignoreCase = true)
|
||||||
}
|
}
|
||||||
if (!mentioningUser) {
|
if (!mentioningUser) {
|
||||||
|
|
|
@ -22,18 +22,21 @@ package org.mariotaku.twidere.extension.text.twitter
|
||||||
import com.twitter.Extractor
|
import com.twitter.Extractor
|
||||||
import com.twitter.Validator
|
import com.twitter.Validator
|
||||||
import org.mariotaku.twidere.model.ParcelableStatus
|
import org.mariotaku.twidere.model.ParcelableStatus
|
||||||
|
import org.mariotaku.twidere.model.UserKey
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by mariotaku on 2017/3/31.
|
* Created by mariotaku on 2017/3/31.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
fun Validator.getTweetLength(text: String, ignoreMentions: Boolean, inReplyTo: ParcelableStatus?): Int {
|
fun Validator.getTweetLength(text: String, ignoreMentions: Boolean, inReplyTo: ParcelableStatus?,
|
||||||
if (!ignoreMentions || inReplyTo == null) {
|
accountKey: UserKey? = inReplyTo?.account_key): Int {
|
||||||
|
if (!ignoreMentions || inReplyTo == null || accountKey == null) {
|
||||||
return getTweetLength(text)
|
return getTweetLength(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
val (_, replyText, _, _, _) = InternalExtractor.extractReplyTextAndMentions(text, inReplyTo)
|
val (_, replyText, _, _, _) = InternalExtractor.extractReplyTextAndMentions(text, inReplyTo,
|
||||||
|
accountKey)
|
||||||
return getTweetLength(replyText)
|
return getTweetLength(replyText)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -424,6 +424,9 @@ abstract class AbsActivitiesFragment protected constructor() :
|
||||||
@ReadPositionTag
|
@ReadPositionTag
|
||||||
get() = null
|
get() = null
|
||||||
|
|
||||||
|
protected open val timelineSyncTag: String?
|
||||||
|
get() = null
|
||||||
|
|
||||||
protected abstract fun hasMoreData(data: List<ParcelableActivity>?): Boolean
|
protected abstract fun hasMoreData(data: List<ParcelableActivity>?): Boolean
|
||||||
|
|
||||||
protected abstract fun onCreateActivitiesLoader(context: Context, args: Bundle,
|
protected abstract fun onCreateActivitiesLoader(context: Context, args: Bundle,
|
||||||
|
@ -442,7 +445,9 @@ abstract class AbsActivitiesFragment protected constructor() :
|
||||||
if (readStateManager.setPosition(tag, item.timestamp)) {
|
if (readStateManager.setPosition(tag, item.timestamp)) {
|
||||||
positionUpdated = true
|
positionUpdated = true
|
||||||
}
|
}
|
||||||
timelineSyncManager?.setPosition(positionTag, tag, item.position_key)
|
}
|
||||||
|
timelineSyncTag?.let { syncTag ->
|
||||||
|
timelineSyncManager?.setPosition(positionTag, syncTag, item.position_key)
|
||||||
}
|
}
|
||||||
currentReadPositionTag?.let { currentTag ->
|
currentReadPositionTag?.let { currentTag ->
|
||||||
readStateManager.setPosition(currentTag, item.timestamp, true)
|
readStateManager.setPosition(currentTag, item.timestamp, true)
|
||||||
|
|
|
@ -102,6 +102,9 @@ abstract class AbsStatusesFragment : AbsContentListRecyclerViewFragment<Parcelab
|
||||||
protected open val readPositionTag: String?
|
protected open val readPositionTag: String?
|
||||||
get() = null
|
get() = null
|
||||||
|
|
||||||
|
protected open val timelineSyncTag: String?
|
||||||
|
get() = null
|
||||||
|
|
||||||
protected open val readPositionTagWithArguments: String?
|
protected open val readPositionTagWithArguments: String?
|
||||||
get() = readPositionTag
|
get() = readPositionTag
|
||||||
|
|
||||||
|
@ -477,9 +480,11 @@ abstract class AbsStatusesFragment : AbsContentListRecyclerViewFragment<Parcelab
|
||||||
val tag = Utils.getReadPositionTagWithAccount(it, accountKey)
|
val tag = Utils.getReadPositionTagWithAccount(it, accountKey)
|
||||||
readStateManager.setPosition(tag, readPosition)
|
readStateManager.setPosition(tag, readPosition)
|
||||||
|
|
||||||
timelineSyncManager?.setPosition(positionTag, tag, status.position_key)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
timelineSyncTag?.let { syncTag ->
|
||||||
|
timelineSyncManager?.setPosition(positionTag, syncTag, status.position_key)
|
||||||
|
}
|
||||||
currentReadPositionTag?.let {
|
currentReadPositionTag?.let {
|
||||||
readStateManager.setPosition(it, readPosition, true)
|
readStateManager.setPosition(it, readPosition, true)
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,13 +19,13 @@
|
||||||
|
|
||||||
package org.mariotaku.twidere.fragment
|
package org.mariotaku.twidere.fragment
|
||||||
|
|
||||||
import android.net.Uri
|
|
||||||
import org.mariotaku.sqliteqb.library.Expression
|
import org.mariotaku.sqliteqb.library.Expression
|
||||||
import org.mariotaku.twidere.TwidereConstants.NOTIFICATION_ID_HOME_TIMELINE
|
import org.mariotaku.twidere.TwidereConstants.NOTIFICATION_ID_HOME_TIMELINE
|
||||||
import org.mariotaku.twidere.annotation.ReadPositionTag
|
import org.mariotaku.twidere.annotation.ReadPositionTag
|
||||||
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_EXTRAS
|
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_EXTRAS
|
||||||
import org.mariotaku.twidere.model.ParameterizedExpression
|
import org.mariotaku.twidere.model.ParameterizedExpression
|
||||||
import org.mariotaku.twidere.model.RefreshTaskParam
|
import org.mariotaku.twidere.model.RefreshTaskParam
|
||||||
|
import org.mariotaku.twidere.model.UserKey
|
||||||
import org.mariotaku.twidere.model.tab.extra.HomeTabExtras
|
import org.mariotaku.twidere.model.tab.extra.HomeTabExtras
|
||||||
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses
|
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses
|
||||||
import org.mariotaku.twidere.util.DataStoreUtils
|
import org.mariotaku.twidere.util.DataStoreUtils
|
||||||
|
@ -37,17 +37,18 @@ import java.util.*
|
||||||
*/
|
*/
|
||||||
class HomeTimelineFragment : CursorStatusesFragment() {
|
class HomeTimelineFragment : CursorStatusesFragment() {
|
||||||
|
|
||||||
override val errorInfoKey: String
|
override val errorInfoKey = ErrorInfoStore.KEY_HOME_TIMELINE
|
||||||
get() = ErrorInfoStore.KEY_HOME_TIMELINE
|
|
||||||
|
|
||||||
override val contentUri: Uri
|
override val contentUri = Statuses.CONTENT_URI
|
||||||
get() = Statuses.CONTENT_URI
|
|
||||||
|
|
||||||
override val notificationType: Int
|
override val notificationType = NOTIFICATION_ID_HOME_TIMELINE
|
||||||
get() = NOTIFICATION_ID_HOME_TIMELINE
|
|
||||||
|
|
||||||
override val isFilterEnabled: Boolean
|
override val isFilterEnabled = true
|
||||||
get() = true
|
|
||||||
|
override val readPositionTag = ReadPositionTag.HOME_TIMELINE
|
||||||
|
|
||||||
|
override val timelineSyncTag: String?
|
||||||
|
get() = getTimelineSyncTag(accountKeys)
|
||||||
|
|
||||||
override fun updateRefreshState() {
|
override fun updateRefreshState() {
|
||||||
val twitter = twitterWrapper
|
val twitter = twitterWrapper
|
||||||
|
@ -86,6 +87,11 @@ class HomeTimelineFragment : CursorStatusesFragment() {
|
||||||
return super.processWhere(where, whereArgs)
|
return super.processWhere(where, whereArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val readPositionTag: String = ReadPositionTag.HOME_TIMELINE
|
companion object {
|
||||||
|
|
||||||
|
fun getTimelineSyncTag(accountKeys: Array<UserKey>): String {
|
||||||
|
return "${ReadPositionTag.HOME_TIMELINE}_${accountKeys.sorted().joinToString(",")}"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
package org.mariotaku.twidere.fragment
|
package org.mariotaku.twidere.fragment
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import org.mariotaku.microblog.library.twitter.model.Activity
|
import org.mariotaku.microblog.library.twitter.model.Activity
|
||||||
import org.mariotaku.sqliteqb.library.Expression
|
import org.mariotaku.sqliteqb.library.Expression
|
||||||
|
@ -30,67 +29,69 @@ import org.mariotaku.twidere.annotation.ReadPositionTag
|
||||||
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_EXTRAS
|
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_EXTRAS
|
||||||
import org.mariotaku.twidere.model.ParameterizedExpression
|
import org.mariotaku.twidere.model.ParameterizedExpression
|
||||||
import org.mariotaku.twidere.model.RefreshTaskParam
|
import org.mariotaku.twidere.model.RefreshTaskParam
|
||||||
|
import org.mariotaku.twidere.model.UserKey
|
||||||
import org.mariotaku.twidere.model.tab.extra.InteractionsTabExtras
|
import org.mariotaku.twidere.model.tab.extra.InteractionsTabExtras
|
||||||
import org.mariotaku.twidere.provider.TwidereDataStore.Activities
|
import org.mariotaku.twidere.provider.TwidereDataStore.Activities
|
||||||
import org.mariotaku.twidere.util.ErrorInfoStore
|
import org.mariotaku.twidere.util.ErrorInfoStore
|
||||||
|
|
||||||
class InteractionsTimelineFragment : CursorActivitiesFragment() {
|
class InteractionsTimelineFragment : CursorActivitiesFragment() {
|
||||||
|
|
||||||
|
override val errorInfoKey = ErrorInfoStore.KEY_INTERACTIONS
|
||||||
|
|
||||||
|
override val contentUri = Activities.AboutMe.CONTENT_URI
|
||||||
|
|
||||||
|
override val notificationType = NOTIFICATION_ID_INTERACTIONS_TIMELINE
|
||||||
|
|
||||||
|
override val isFilterEnabled = true
|
||||||
|
|
||||||
|
@ReadPositionTag
|
||||||
|
override val readPositionTag = ReadPositionTag.ACTIVITIES_ABOUT_ME
|
||||||
|
|
||||||
|
override val timelineSyncTag: String?
|
||||||
|
get() = getTimelineSyncTag(accountKeys)
|
||||||
|
|
||||||
|
override fun onCreateAdapter(context: Context): ParcelableActivitiesAdapter {
|
||||||
|
val adapter = ParcelableActivitiesAdapter(context, Glide.with(this))
|
||||||
|
val extras: InteractionsTabExtras? = arguments.getParcelable(EXTRA_EXTRAS)
|
||||||
|
if (extras != null) {
|
||||||
|
adapter.followingOnly = extras.isMyFollowingOnly
|
||||||
|
adapter.mentionsOnly = extras.isMentionsOnly
|
||||||
|
}
|
||||||
|
return adapter
|
||||||
|
}
|
||||||
|
|
||||||
override fun getActivities(param: RefreshTaskParam): Boolean {
|
override fun getActivities(param: RefreshTaskParam): Boolean {
|
||||||
twitterWrapper.getActivitiesAboutMeAsync(param)
|
twitterWrapper.getActivitiesAboutMeAsync(param)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override val errorInfoKey: String
|
|
||||||
get() = ErrorInfoStore.KEY_INTERACTIONS
|
|
||||||
|
|
||||||
override val contentUri: Uri
|
|
||||||
get() = Activities.AboutMe.CONTENT_URI
|
|
||||||
|
|
||||||
override val notificationType: Int
|
|
||||||
get() = NOTIFICATION_ID_INTERACTIONS_TIMELINE
|
|
||||||
|
|
||||||
override val isFilterEnabled: Boolean
|
|
||||||
get() = true
|
|
||||||
|
|
||||||
override fun updateRefreshState() {
|
override fun updateRefreshState() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun processWhere(where: Expression, whereArgs: Array<String>): ParameterizedExpression {
|
override fun processWhere(where: Expression, whereArgs: Array<String>): ParameterizedExpression {
|
||||||
val arguments = arguments
|
val arguments = arguments
|
||||||
if (arguments != null) {
|
val extras: InteractionsTabExtras? = arguments.getParcelable(EXTRA_EXTRAS)
|
||||||
val extras = arguments.getParcelable<InteractionsTabExtras>(EXTRA_EXTRAS)
|
if (extras != null) {
|
||||||
if (extras != null) {
|
val expressions = mutableListOf(where)
|
||||||
val expressions = mutableListOf(where)
|
val combinedArgs = mutableListOf(*whereArgs)
|
||||||
val combinedArgs = mutableListOf(*whereArgs)
|
if (extras.isMentionsOnly) {
|
||||||
if (extras.isMentionsOnly) {
|
expressions.add(Expression.inArgs(Activities.ACTION, 3))
|
||||||
expressions.add(Expression.inArgs(Activities.ACTION, 3))
|
combinedArgs.addAll(arrayOf(Activity.Action.MENTION, Activity.Action.REPLY, Activity.Action.QUOTE))
|
||||||
combinedArgs.addAll(arrayOf(Activity.Action.MENTION, Activity.Action.REPLY, Activity.Action.QUOTE))
|
|
||||||
}
|
|
||||||
if (extras.isMyFollowingOnly) {
|
|
||||||
expressions.add(Expression.equals(Activities.HAS_FOLLOWING_SOURCE, 1))
|
|
||||||
}
|
|
||||||
return ParameterizedExpression(Expression.and(*expressions.toTypedArray()),
|
|
||||||
combinedArgs.toTypedArray())
|
|
||||||
}
|
}
|
||||||
|
if (extras.isMyFollowingOnly) {
|
||||||
|
expressions.add(Expression.equals(Activities.HAS_FOLLOWING_SOURCE, 1))
|
||||||
|
}
|
||||||
|
return ParameterizedExpression(Expression.and(*expressions.toTypedArray()),
|
||||||
|
combinedArgs.toTypedArray())
|
||||||
}
|
}
|
||||||
return super.processWhere(where, whereArgs)
|
return super.processWhere(where, whereArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateAdapter(context: Context): ParcelableActivitiesAdapter {
|
companion object {
|
||||||
val adapter = ParcelableActivitiesAdapter(context, Glide.with(this))
|
|
||||||
val arguments = arguments
|
fun getTimelineSyncTag(accountKeys: Array<UserKey>): String {
|
||||||
if (arguments != null) {
|
return "${ReadPositionTag.ACTIVITIES_ABOUT_ME}_${accountKeys.sorted().joinToString(",")}"
|
||||||
val extras = arguments.getParcelable<InteractionsTabExtras>(EXTRA_EXTRAS)
|
|
||||||
if (extras != null) {
|
|
||||||
adapter.followingOnly = extras.isMyFollowingOnly
|
|
||||||
adapter.mentionsOnly = extras.isMentionsOnly
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return adapter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ReadPositionTag
|
|
||||||
override val readPositionTag: String? = ReadPositionTag.ACTIVITIES_ABOUT_ME
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,11 +27,12 @@ import org.mariotaku.microblog.library.twitter.model.*
|
||||||
import org.mariotaku.twidere.annotation.AccountType
|
import org.mariotaku.twidere.annotation.AccountType
|
||||||
import org.mariotaku.twidere.annotation.ReadPositionTag
|
import org.mariotaku.twidere.annotation.ReadPositionTag
|
||||||
import org.mariotaku.twidere.extension.model.isOfficial
|
import org.mariotaku.twidere.extension.model.isOfficial
|
||||||
|
import org.mariotaku.twidere.fragment.InteractionsTimelineFragment
|
||||||
import org.mariotaku.twidere.model.AccountDetails
|
import org.mariotaku.twidere.model.AccountDetails
|
||||||
import org.mariotaku.twidere.model.UserKey
|
import org.mariotaku.twidere.model.UserKey
|
||||||
import org.mariotaku.twidere.provider.TwidereDataStore.Activities
|
import org.mariotaku.twidere.provider.TwidereDataStore.Activities
|
||||||
import org.mariotaku.twidere.util.ErrorInfoStore
|
import org.mariotaku.twidere.util.ErrorInfoStore
|
||||||
import org.mariotaku.twidere.util.Utils
|
import java.io.IOException
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by mariotaku on 16/2/11.
|
* Created by mariotaku on 16/2/11.
|
||||||
|
@ -44,21 +45,9 @@ class GetActivitiesAboutMeTask(context: Context) : GetActivitiesTask(context) {
|
||||||
override val contentUri: Uri
|
override val contentUri: Uri
|
||||||
get() = Activities.AboutMe.CONTENT_URI
|
get() = Activities.AboutMe.CONTENT_URI
|
||||||
|
|
||||||
override fun setLocalReadPosition(accountKey: UserKey, details: AccountDetails, twitter: MicroBlog) {
|
|
||||||
if (AccountType.TWITTER == details.type && details.isOfficial(context)) {
|
|
||||||
try {
|
|
||||||
val response = twitter.getActivitiesAboutMeUnread(true)
|
|
||||||
val tag = Utils.getReadPositionTagWithAccount(ReadPositionTag.ACTIVITIES_ABOUT_ME,
|
|
||||||
accountKey)
|
|
||||||
readStateManager.setPosition(tag, response.cursor, false)
|
|
||||||
} catch (e: MicroBlogException) {
|
|
||||||
// Ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(MicroBlogException::class)
|
@Throws(MicroBlogException::class)
|
||||||
override fun getActivities(twitter: MicroBlog, details: AccountDetails, paging: Paging): ResponseList<Activity> {
|
override fun getActivities(twitter: MicroBlog, details: AccountDetails, paging: Paging):
|
||||||
|
ResponseList<Activity> {
|
||||||
if (details.isOfficial(context)) {
|
if (details.isOfficial(context)) {
|
||||||
return twitter.getActivitiesAboutMe(paging)
|
return twitter.getActivitiesAboutMe(paging)
|
||||||
}
|
}
|
||||||
|
@ -75,4 +64,15 @@ class GetActivitiesAboutMeTask(context: Context) : GetActivitiesTask(context) {
|
||||||
statuses.mapTo(activities) { InternalActivityCreator.status(it, details.key.id) }
|
statuses.mapTo(activities) { InternalActivityCreator.status(it, details.key.id) }
|
||||||
return activities
|
return activities
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun setLocalReadPosition(accountKeys: Array<UserKey>, saveReadPosition: BooleanArray) {
|
||||||
|
val manager = timelineSyncManagerFactory.get() ?: return
|
||||||
|
val tag = InteractionsTimelineFragment.getTimelineSyncTag(accountKeys)
|
||||||
|
try {
|
||||||
|
manager.blockingGetPosition(ReadPositionTag.ACTIVITIES_ABOUT_ME, tag)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,11 +53,11 @@ abstract class GetActivitiesTask(
|
||||||
val cr = context.contentResolver
|
val cr = context.contentResolver
|
||||||
val result = ArrayList<TwitterListResponse<Activity>>()
|
val result = ArrayList<TwitterListResponse<Activity>>()
|
||||||
val loadItemLimit = preferences[loadItemLimitKey]
|
val loadItemLimit = preferences[loadItemLimitKey]
|
||||||
var saveReadPosition = false
|
val saveReadPosition = BooleanArray(accountKeys.size)
|
||||||
for (i in accountKeys.indices) {
|
accountKeys.forEachIndexed { i, accountKey ->
|
||||||
val accountKey = accountKeys[i]
|
|
||||||
val noItemsBefore = DataStoreUtils.getActivitiesCount(context, contentUri, accountKey) <= 0
|
val noItemsBefore = DataStoreUtils.getActivitiesCount(context, contentUri, accountKey) <= 0
|
||||||
val credentials = AccountUtils.getAccountDetails(AccountManager.get(context), accountKey, true) ?: continue
|
val credentials = AccountUtils.getAccountDetails(AccountManager.get(context), accountKey,
|
||||||
|
true) ?: return@forEachIndexed
|
||||||
val microBlog = credentials.newMicroBlogInstance(context = context, cls = MicroBlog::class.java)
|
val microBlog = credentials.newMicroBlogInstance(context = context, cls = MicroBlog::class.java)
|
||||||
val paging = Paging()
|
val paging = Paging()
|
||||||
paging.count(loadItemLimit)
|
paging.count(loadItemLimit)
|
||||||
|
@ -79,7 +79,7 @@ abstract class GetActivitiesTask(
|
||||||
paging.sinceId(sinceId)
|
paging.sinceId(sinceId)
|
||||||
if (maxIds == null || maxId == null) {
|
if (maxIds == null || maxId == null) {
|
||||||
paging.setLatestResults(true)
|
paging.setLatestResults(true)
|
||||||
saveReadPosition = true
|
saveReadPosition[i] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,8 +88,8 @@ abstract class GetActivitiesTask(
|
||||||
val activities = getActivities(microBlog, credentials, paging)
|
val activities = getActivities(microBlog, credentials, paging)
|
||||||
val storeResult = storeActivities(cr, loadItemLimit, credentials, noItemsBefore,
|
val storeResult = storeActivities(cr, loadItemLimit, credentials, noItemsBefore,
|
||||||
activities, sinceId, maxId, false)
|
activities, sinceId, maxId, false)
|
||||||
if (saveReadPosition) {
|
if (saveReadPosition[i]) {
|
||||||
setLocalReadPosition(accountKey, credentials, microBlog)
|
|
||||||
}
|
}
|
||||||
errorInfoStore.remove(errorInfoKey, accountKey)
|
errorInfoStore.remove(errorInfoKey, accountKey)
|
||||||
if (storeResult != 0) {
|
if (storeResult != 0) {
|
||||||
|
@ -106,6 +106,7 @@ abstract class GetActivitiesTask(
|
||||||
result.add(TwitterListResponse(accountKey, e))
|
result.add(TwitterListResponse(accountKey, e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
setLocalReadPosition(accountKeys, saveReadPosition)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +125,7 @@ abstract class GetActivitiesTask(
|
||||||
@Throws(MicroBlogException::class)
|
@Throws(MicroBlogException::class)
|
||||||
protected abstract fun getActivities(twitter: MicroBlog, details: AccountDetails, paging: Paging): ResponseList<Activity>
|
protected abstract fun getActivities(twitter: MicroBlog, details: AccountDetails, paging: Paging): ResponseList<Activity>
|
||||||
|
|
||||||
protected abstract fun setLocalReadPosition(accountKey: UserKey, details: AccountDetails, twitter: MicroBlog)
|
protected abstract fun setLocalReadPosition(accountKeys: Array<UserKey>, saveReadPosition: BooleanArray)
|
||||||
|
|
||||||
private fun storeActivities(cr: ContentResolver, loadItemLimit: Int, details: AccountDetails,
|
private fun storeActivities(cr: ContentResolver, loadItemLimit: Int, details: AccountDetails,
|
||||||
noItemsBefore: Boolean, activities: ResponseList<Activity>,
|
noItemsBefore: Boolean, activities: ResponseList<Activity>,
|
||||||
|
|
|
@ -54,8 +54,7 @@ class GetHomeTimelineTask(context: Context) : GetStatusesTask(context) {
|
||||||
val syncManager = timelineSyncManagerFactory.get() ?: return
|
val syncManager = timelineSyncManagerFactory.get() ?: return
|
||||||
try {
|
try {
|
||||||
val tag = Utils.getReadPositionTagWithAccount(ReadPositionTag.HOME_TIMELINE, accountKey)
|
val tag = Utils.getReadPositionTagWithAccount(ReadPositionTag.HOME_TIMELINE, accountKey)
|
||||||
val positionKey = syncManager.blockingGetPosition(ReadPositionTag.HOME_TIMELINE, tag)
|
syncManager.blockingGetPosition(ReadPositionTag.HOME_TIMELINE, tag)
|
||||||
readStateManager
|
|
||||||
}catch (e: IOException) {
|
}catch (e: IOException) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -227,7 +227,7 @@ class UpdateStatusTask(
|
||||||
val textLimit = account.textLimit
|
val textLimit = account.textLimit
|
||||||
val ignoreMentions = account.type == AccountType.TWITTER
|
val ignoreMentions = account.type == AccountType.TWITTER
|
||||||
if (textLimit >= 0 && validator.getTweetLength(text, ignoreMentions,
|
if (textLimit >= 0 && validator.getTweetLength(text, ignoreMentions,
|
||||||
update.in_reply_to_status) <= textLimit) {
|
update.in_reply_to_status, account.key) <= textLimit) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
shortener.waitForService()
|
shortener.waitForService()
|
||||||
|
@ -399,7 +399,9 @@ class UpdateStatusTask(
|
||||||
val details = statusUpdate.accounts[index]
|
val details = statusUpdate.accounts[index]
|
||||||
if (details.type == AccountType.TWITTER && statusUpdate.extended_reply_mode) {
|
if (details.type == AccountType.TWITTER && statusUpdate.extended_reply_mode) {
|
||||||
status.autoPopulateReplyMetadata(true)
|
status.autoPopulateReplyMetadata(true)
|
||||||
status.excludeReplyUserIds(statusUpdate.excluded_reply_user_ids)
|
if (statusUpdate.excluded_reply_user_ids.isNotNullOrEmpty()) {
|
||||||
|
status.excludeReplyUserIds(statusUpdate.excluded_reply_user_ids)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (statusUpdate.repost_status_id != null) {
|
if (statusUpdate.repost_status_id != null) {
|
||||||
|
|
|
@ -20,6 +20,9 @@
|
||||||
package org.mariotaku.twidere.util.api
|
package org.mariotaku.twidere.util.api
|
||||||
|
|
||||||
import android.support.v4.util.SimpleArrayMap
|
import android.support.v4.util.SimpleArrayMap
|
||||||
|
import com.bluelinelabs.logansquare.JsonMapper
|
||||||
|
import com.bluelinelabs.logansquare.ParameterizedType
|
||||||
|
import org.mariotaku.commons.logansquare.LoganSquareMapperFinder
|
||||||
|
|
||||||
import org.mariotaku.microblog.library.MicroBlogException
|
import org.mariotaku.microblog.library.MicroBlogException
|
||||||
import org.mariotaku.microblog.library.twitter.model.ResponseCode
|
import org.mariotaku.microblog.library.twitter.model.ResponseCode
|
||||||
|
@ -49,6 +52,14 @@ object TwitterConverterFactory : LoganSquareConverterFactory<MicroBlogException>
|
||||||
responseConverters.put(OAuthToken::class.java, OAuthTokenResponseConverter())
|
responseConverters.put(OAuthToken::class.java, OAuthTokenResponseConverter())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun <T : Any?> mapperFor(type: ParameterizedType<T>): JsonMapper<T> {
|
||||||
|
return LoganSquareMapperFinder.mapperFor(type)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <T : Any?> mapperFor(type: Class<T>): JsonMapper<T> {
|
||||||
|
return LoganSquareMapperFinder.mapperFor(type)
|
||||||
|
}
|
||||||
|
|
||||||
@Throws(RestConverter.ConvertException::class)
|
@Throws(RestConverter.ConvertException::class)
|
||||||
override fun forResponse(type: Type): RestConverter<HttpResponse, *, MicroBlogException> {
|
override fun forResponse(type: Type): RestConverter<HttpResponse, *, MicroBlogException> {
|
||||||
val converter = responseConverters.get(type)
|
val converter = responseConverters.get(type)
|
||||||
|
|
|
@ -33,17 +33,18 @@ import java.util.*
|
||||||
|
|
||||||
abstract class TimelineSyncManager(val context: Context) {
|
abstract class TimelineSyncManager(val context: Context) {
|
||||||
|
|
||||||
private val internalMap = ArrayMap<TimelineKey, Long>()
|
private val stagedCommits = ArrayMap<TimelineKey, Long>()
|
||||||
|
private val cachedPositions = ArrayMap<TimelineKey, Long>()
|
||||||
|
|
||||||
fun setPosition(@ReadPositionTag positionTag: String, currentTag: String?, positionKey: Long) {
|
fun setPosition(@ReadPositionTag positionTag: String, currentTag: String?, positionKey: Long) {
|
||||||
internalMap[TimelineKey(positionTag, currentTag)] = positionKey
|
stagedCommits[TimelineKey(positionTag, currentTag)] = positionKey
|
||||||
}
|
}
|
||||||
|
|
||||||
fun commit() {
|
fun commit() {
|
||||||
val data = internalMap.map { (key, value) ->
|
val data = stagedCommits.map { (key, value) ->
|
||||||
PositionData(key.positionTag, key.currentTag, value)
|
PositionData(key.positionTag, key.currentTag, value)
|
||||||
}.toTypedArray()
|
}.toTypedArray()
|
||||||
internalMap.clear()
|
stagedCommits.clear()
|
||||||
performSync(data)
|
performSync(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,5 +80,4 @@ abstract class TimelineSyncManager(val context: Context) {
|
||||||
fun newFactory(): Factory = ServiceLoader.load(Factory::class.java).firstOrNull() ?: DummyFactory
|
fun newFactory(): Factory = ServiceLoader.load(Factory::class.java).firstOrNull() ?: DummyFactory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ import org.mariotaku.twidere.extension.model.getActionName
|
||||||
import org.mariotaku.twidere.model.Draft
|
import org.mariotaku.twidere.model.Draft
|
||||||
import org.mariotaku.twidere.model.ParcelableMedia
|
import org.mariotaku.twidere.model.ParcelableMedia
|
||||||
import org.mariotaku.twidere.model.draft.StatusObjectActionExtras
|
import org.mariotaku.twidere.model.draft.StatusObjectActionExtras
|
||||||
|
import org.mariotaku.twidere.model.draft.UpdateStatusActionExtras
|
||||||
import org.mariotaku.twidere.util.DataStoreUtils
|
import org.mariotaku.twidere.util.DataStoreUtils
|
||||||
import org.mariotaku.twidere.util.Utils
|
import org.mariotaku.twidere.util.Utils
|
||||||
|
|
||||||
|
@ -50,6 +51,10 @@ class DraftViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||||
Draft.Action.UPDATE_STATUS, Draft.Action.UPDATE_STATUS_COMPAT_1,
|
Draft.Action.UPDATE_STATUS, Draft.Action.UPDATE_STATUS_COMPAT_1,
|
||||||
Draft.Action.UPDATE_STATUS_COMPAT_2, Draft.Action.REPLY, Draft.Action.QUOTE -> {
|
Draft.Action.UPDATE_STATUS_COMPAT_2, Draft.Action.REPLY, Draft.Action.QUOTE -> {
|
||||||
val media = draft.media?.mapToArray(::ParcelableMedia)
|
val media = draft.media?.mapToArray(::ParcelableMedia)
|
||||||
|
val extras = draft.action_extras as? UpdateStatusActionExtras
|
||||||
|
if (extras != null) {
|
||||||
|
summaryText = extras.editingText
|
||||||
|
}
|
||||||
mediaPreviewContainer.visibility = View.VISIBLE
|
mediaPreviewContainer.visibility = View.VISIBLE
|
||||||
mediaPreviewContainer.displayMedia(requestManager = requestManager,
|
mediaPreviewContainer.displayMedia(requestManager = requestManager,
|
||||||
media = media)
|
media = media)
|
||||||
|
|
Loading…
Reference in New Issue