started to abandon AbstractTask
This commit is contained in:
parent
9a21a5f4ee
commit
b533e4157d
|
@ -77,7 +77,7 @@ subprojects {
|
|||
AndroidImageCropper : '2.4.6',
|
||||
ExportablePreferences : '0.9.7',
|
||||
ACRA : '4.9.2',
|
||||
AbstractTask : '0.9.5',
|
||||
AbstractTask : '0.9.8',
|
||||
Dagger : '2.11',
|
||||
StethoBeanShellREPL : '0.3',
|
||||
ArchLifecycleExtensions: '1.0.0-beta2',
|
||||
|
|
|
@ -23,11 +23,11 @@ import android.os.Parcelable;
|
|||
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||
import com.bluelinelabs.logansquare.annotation.OnJsonParseComplete;
|
||||
import com.hannesdorfmann.parcelableplease.annotation.ParcelablePlease;
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 15/12/31.
|
||||
*/
|
||||
import java.io.IOException;
|
||||
|
||||
@ParcelablePlease
|
||||
@JsonObject
|
||||
public class CardResponse implements Parcelable {
|
||||
|
@ -38,6 +38,11 @@ public class CardResponse implements Parcelable {
|
|||
return card;
|
||||
}
|
||||
|
||||
@OnJsonParseComplete
|
||||
void onParseComplete() throws IOException {
|
||||
if (card == null) throw new IOException("Empty card result");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
|
|
|
@ -3,23 +3,18 @@ package org.mariotaku.abstask.library;
|
|||
import android.support.annotation.UiThread;
|
||||
import android.support.annotation.WorkerThread;
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 16/5/25.
|
||||
*/
|
||||
public class ManualTaskStarter {
|
||||
@UiThread
|
||||
public static void invokeBeforeExecute(AbstractTask<?, ?, ?> task) {
|
||||
task.invokeBeforeExecute();
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public static <Result> void invokeAfterExecute(AbstractTask<?, Result, ?> task, Result result) {
|
||||
task.invokeAfterExecute(result);
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public static <Result> Result invokeExecute(AbstractTask<?, Result, ?> task) {
|
||||
return task.invokeExecute();
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.mariotaku.twidere.model.util;
|
||||
|
||||
import android.location.Location;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import org.mariotaku.microblog.library.twitter.model.GeoLocation;
|
||||
|
@ -14,7 +15,7 @@ public class ParcelableLocationUtils {
|
|||
private ParcelableLocationUtils() {
|
||||
}
|
||||
|
||||
public static String getHumanReadableString(ParcelableLocation obj, int decimalDigits) {
|
||||
public static String getHumanReadableString(@NonNull ParcelableLocation obj, int decimalDigits) {
|
||||
return String.format("%s,%s", InternalParseUtils.parsePrettyDecimal(obj.latitude, decimalDigits),
|
||||
InternalParseUtils.parsePrettyDecimal(obj.longitude, decimalDigits));
|
||||
}
|
||||
|
|
|
@ -1,14 +1,26 @@
|
|||
package org.mariotaku.ktextension
|
||||
|
||||
import android.os.Handler
|
||||
import nl.komponents.kovenant.Deferred
|
||||
import nl.komponents.kovenant.Kovenant
|
||||
import nl.komponents.kovenant.Promise
|
||||
import nl.komponents.kovenant.deferred
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.concurrent.atomic.AtomicReferenceArray
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2016/12/2.
|
||||
*/
|
||||
|
||||
fun <V> Promise<V, Exception>.deadline(time: Long, unit: TimeUnit): Promise<V, Exception> {
|
||||
val weakPromise = toWeak()
|
||||
WatchdogHandler.postDelayed({
|
||||
val promise = weakPromise.get() ?: return@postDelayed
|
||||
if (promise.isDone()) return@postDelayed
|
||||
Kovenant.cancel(promise, DeadlineException())
|
||||
}, unit.toMillis(time))
|
||||
return this
|
||||
}
|
||||
|
||||
|
||||
fun <V, E> combine(promises: List<Promise<V, E>>): Promise<List<V>, E> {
|
||||
return concreteCombine(promises)
|
||||
}
|
||||
|
@ -50,3 +62,9 @@ fun <V, E> concreteCombine(promises: List<Promise<V, E>>): Promise<List<V>, E> {
|
|||
|
||||
return deferred.promise
|
||||
}
|
||||
|
||||
private object WatchdogHandler : Handler()
|
||||
|
||||
class DeadlineException : Exception() {
|
||||
|
||||
}
|
|
@ -59,9 +59,15 @@ import com.bumptech.glide.Glide
|
|||
import com.twitter.Extractor
|
||||
import com.twitter.Validator
|
||||
import kotlinx.android.synthetic.main.activity_compose.*
|
||||
import nl.komponents.kovenant.combine.and
|
||||
import nl.komponents.kovenant.task
|
||||
import org.mariotaku.abstask.library.AbstractTask
|
||||
import nl.komponents.kovenant.then
|
||||
import nl.komponents.kovenant.ui.alwaysUi
|
||||
import nl.komponents.kovenant.ui.failUi
|
||||
import nl.komponents.kovenant.ui.promiseOnUi
|
||||
import nl.komponents.kovenant.ui.successUi
|
||||
import org.mariotaku.abstask.library.TaskStarter
|
||||
import org.mariotaku.kpreferences.edit
|
||||
import org.mariotaku.kpreferences.get
|
||||
import org.mariotaku.kpreferences.set
|
||||
import org.mariotaku.ktextension.*
|
||||
|
@ -91,7 +97,6 @@ import org.mariotaku.twidere.preference.ComponentPickerPreference
|
|||
import org.mariotaku.twidere.provider.TwidereDataStore.Drafts
|
||||
import org.mariotaku.twidere.service.LengthyOperationsService
|
||||
import org.mariotaku.twidere.task.compose.AbsAddMediaTask
|
||||
import org.mariotaku.twidere.task.compose.AbsDeleteMediaTask
|
||||
import org.mariotaku.twidere.task.status.UpdateStatusTask
|
||||
import org.mariotaku.twidere.text.MarkForDeleteSpan
|
||||
import org.mariotaku.twidere.text.style.EmojiSpan
|
||||
|
@ -107,10 +112,10 @@ import org.mariotaku.twidere.view.ExtendedRecyclerView
|
|||
import org.mariotaku.twidere.view.ShapedImageView
|
||||
import org.mariotaku.twidere.view.helper.SimpleItemTouchHelperCallback
|
||||
import org.mariotaku.twidere.view.holder.compose.MediaPreviewViewHolder
|
||||
import java.io.IOException
|
||||
import java.text.Normalizer
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import kotlin.NoSuchElementException
|
||||
import kotlin.collections.ArrayList
|
||||
import android.Manifest.permission as AndroidPermission
|
||||
|
||||
|
@ -300,7 +305,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
|||
showLabelAndHint(intent)
|
||||
val selectedAccountKeys = accountsAdapter.selectedAccountKeys
|
||||
if (selectedAccountKeys.isNullOrEmpty()) {
|
||||
val idsInPrefs = kPreferences[composeAccountsKey]?.asList() ?: emptyList()
|
||||
val idsInPrefs = preferences[composeAccountsKey]?.asList() ?: emptyList()
|
||||
val intersection = defaultAccountKeys.intersect(idsInPrefs)
|
||||
|
||||
if (intersection.isEmpty()) {
|
||||
|
@ -357,9 +362,9 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
|||
override fun onStart() {
|
||||
super.onStart()
|
||||
|
||||
imageUploaderUsed = !ComponentPickerPreference.isNoneValue(kPreferences[mediaUploaderKey])
|
||||
statusShortenerUsed = !ComponentPickerPreference.isNoneValue(kPreferences[statusShortenerKey])
|
||||
if (kPreferences[attachLocationKey]) {
|
||||
imageUploaderUsed = !ComponentPickerPreference.isNoneValue(preferences[mediaUploaderKey])
|
||||
statusShortenerUsed = !ComponentPickerPreference.isNoneValue(preferences[statusShortenerKey])
|
||||
if (preferences[attachLocationKey]) {
|
||||
if (checkAnySelfPermissionsGranted(AndroidPermission.ACCESS_COARSE_LOCATION,
|
||||
AndroidPermission.ACCESS_FINE_LOCATION)) {
|
||||
try {
|
||||
|
@ -558,18 +563,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
|||
IntentUtils.openDrafts(this)
|
||||
}
|
||||
R.id.delete -> {
|
||||
val media = this.media
|
||||
val task = DeleteMediaTask(this, media)
|
||||
task.callback = { result ->
|
||||
setProgressVisible(false)
|
||||
removeMedia(media.filterIndexed { i, _ -> result[i] })
|
||||
setMenu()
|
||||
if (result.contains(false)) {
|
||||
Toast.makeText(this, R.string.message_toast_error_occurred,
|
||||
Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
TaskStarter.execute(task)
|
||||
deleteMedia(media)
|
||||
}
|
||||
R.id.toggle_sensitive -> {
|
||||
if (!hasMedia) return true
|
||||
|
@ -720,7 +714,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
|||
}
|
||||
} else {
|
||||
Toast.makeText(this, R.string.message_toast_cannot_get_location, Toast.LENGTH_SHORT).show()
|
||||
kPreferences.edit {
|
||||
preferences.edit {
|
||||
this[attachLocationKey] = false
|
||||
this[attachPreciseLocationKey] = false
|
||||
}
|
||||
|
@ -774,7 +768,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
|||
attachPreciseLocationChecked = false
|
||||
}
|
||||
}
|
||||
kPreferences.edit {
|
||||
preferences.edit {
|
||||
this[attachLocationKey] = attachLocationChecked
|
||||
this[attachPreciseLocationKey] = attachPreciseLocationChecked
|
||||
}
|
||||
|
@ -1328,8 +1322,8 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
|||
menu.setGroupAvailability(MENU_GROUP_IMAGE_EXTENSION, hasMedia)
|
||||
menu.setItemChecked(R.id.toggle_sensitive, possiblySensitive)
|
||||
|
||||
val attachLocation = kPreferences[attachLocationKey]
|
||||
val attachPreciseLocation = kPreferences[attachPreciseLocationKey]
|
||||
val attachLocation = preferences[attachLocationKey]
|
||||
val attachPreciseLocation = preferences[attachPreciseLocationKey]
|
||||
|
||||
if (!attachLocation) {
|
||||
menu.setItemChecked(R.id.location_off, true)
|
||||
|
@ -1383,20 +1377,12 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
|||
}
|
||||
|
||||
private fun setRecentLocation(location: ParcelableLocation?) {
|
||||
if (location != null) {
|
||||
val attachPreciseLocation = kPreferences[attachPreciseLocationKey]
|
||||
if (attachPreciseLocation) {
|
||||
locationLabel.spannable = ParcelableLocationUtils.getHumanReadableString(location, 3)
|
||||
} else {
|
||||
if (locationLabel.tag == null || location != recentLocation) {
|
||||
val task = DisplayPlaceNameTask()
|
||||
task.params = location
|
||||
task.callback = this
|
||||
TaskStarter.execute(task)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (location == null) {
|
||||
locationLabel.setText(R.string.unknown_location)
|
||||
} else if (preferences[attachPreciseLocationKey]) {
|
||||
locationLabel.spannable = ParcelableLocationUtils.getHumanReadableString(location, 3)
|
||||
} else if (locationLabel.tag == null || location != recentLocation) {
|
||||
loadPlaceName(location)
|
||||
}
|
||||
recentLocation = location
|
||||
}
|
||||
|
@ -1409,11 +1395,11 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
|||
@Throws(SecurityException::class)
|
||||
private fun startLocationUpdateIfEnabled(): Boolean {
|
||||
if (locationListener != null) return true
|
||||
val attachLocation = kPreferences[attachLocationKey]
|
||||
val attachLocation = preferences[attachLocationKey]
|
||||
if (!attachLocation) {
|
||||
return false
|
||||
}
|
||||
val attachPreciseLocation = kPreferences[attachPreciseLocationKey]
|
||||
val attachPreciseLocation = preferences[attachPreciseLocationKey]
|
||||
val criteria = Criteria()
|
||||
if (attachPreciseLocation) {
|
||||
criteria.accuracy = Criteria.ACCURACY_FINE
|
||||
|
@ -1583,8 +1569,8 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
|||
}
|
||||
update.summary = summary
|
||||
|
||||
val attachLocation = kPreferences[attachLocationKey]
|
||||
val attachPreciseLocation = kPreferences[attachPreciseLocationKey]
|
||||
val attachLocation = preferences[attachLocationKey]
|
||||
val attachPreciseLocation = preferences[attachPreciseLocationKey]
|
||||
update.draft_action = draftAction
|
||||
update.accounts = accounts
|
||||
if (attachLocation) {
|
||||
|
@ -1636,7 +1622,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
|||
}
|
||||
|
||||
private fun updateLocationState() {
|
||||
if (kPreferences[attachLocationKey]) {
|
||||
if (preferences[attachLocationKey]) {
|
||||
locationLabel.visibility = View.VISIBLE
|
||||
if (recentLocation != null) {
|
||||
setRecentLocation(recentLocation)
|
||||
|
@ -1814,6 +1800,72 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
|||
editTextContainer.touchDelegate = ComposeEditTextTouchDelegate(editTextContainer, editText)
|
||||
}
|
||||
|
||||
private fun loadPlaceName(location: ParcelableLocation) {
|
||||
val weakThis by weak(this)
|
||||
promiseOnUi {
|
||||
val activity = weakThis ?: return@promiseOnUi
|
||||
activity.updateLocationLabel(location, false)
|
||||
} and task {
|
||||
val activity = weakThis ?: throw InterruptedException()
|
||||
val gcd = Geocoder(activity, Locale.getDefault())
|
||||
return@task gcd.getFromLocation(location.latitude, location.longitude, 1)
|
||||
?.firstOrNull() ?: throw NoSuchElementException("Address not found")
|
||||
}.successUi { address ->
|
||||
val activity = weakThis ?: return@successUi
|
||||
activity.locationLabel.tag = address
|
||||
activity.updateLocationLabel(location, true)
|
||||
}.failUi { ex ->
|
||||
val activity = weakThis ?: return@failUi
|
||||
activity.locationLabel.tag = if (ex is NoSuchElementException) NoAddress else null
|
||||
activity.updateLocationLabel(location, true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateLocationLabel(location: ParcelableLocation, finished: Boolean) {
|
||||
when {
|
||||
!preferences[attachLocationKey] -> {
|
||||
locationLabel.setText(R.string.no_location)
|
||||
}
|
||||
preferences[attachPreciseLocationKey] -> {
|
||||
locationLabel.string = ParcelableLocationUtils.getHumanReadableString(location, 3)
|
||||
}
|
||||
else -> {
|
||||
val tag = locationLabel.tag
|
||||
when (tag) {
|
||||
is Address -> locationLabel.text = tag.locality
|
||||
is NoAddress -> locationLabel.setText(R.string.label_location_your_coarse_location)
|
||||
null -> if (finished) {
|
||||
locationLabel.setText(R.string.no_location)
|
||||
} else {
|
||||
locationLabel.setText(R.string.getting_location)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun deleteMedia(media:Array<ParcelableMediaUpdate>) {
|
||||
val weakThis by weak(this)
|
||||
promiseOnUi {
|
||||
weakThis?.setProgressVisible(true)
|
||||
}.then {
|
||||
val context = weakThis ?: throw InterruptedException()
|
||||
media.forEach {
|
||||
Utils.deleteMedia(context, Uri.parse(it.uri))
|
||||
}
|
||||
}.alwaysUi {
|
||||
weakThis?.setProgressVisible(false)
|
||||
weakThis?.removeMedia(media.toList())
|
||||
setMenu()
|
||||
}.failUi {
|
||||
val context = weakThis ?: throw InterruptedException()
|
||||
Toast.makeText(context, R.string.message_toast_error_occurred,
|
||||
Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
internal object NoAddress
|
||||
|
||||
class RetweetProtectedStatusWarnFragment : BaseDialogFragment(), DialogInterface.OnClickListener {
|
||||
|
||||
override fun onClick(dialog: DialogInterface, which: Int) {
|
||||
|
@ -2066,93 +2118,6 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
|||
|
||||
}
|
||||
|
||||
private class DeleteMediaTask(
|
||||
activity: ComposeActivity,
|
||||
media: Array<ParcelableMediaUpdate>
|
||||
) : AbsDeleteMediaTask<((BooleanArray) -> Unit)?>(activity, media.mapToArray { Uri.parse(it.uri) }) {
|
||||
|
||||
override fun beforeExecute() {
|
||||
val activity = context as? ComposeActivity ?: return
|
||||
activity.setProgressVisible(true)
|
||||
}
|
||||
|
||||
override fun afterExecute(callback: ((BooleanArray) -> Unit)?, result: BooleanArray) {
|
||||
callback?.invoke(result)
|
||||
}
|
||||
}
|
||||
|
||||
private class DisplayPlaceNameTask : AbstractTask<ParcelableLocation, List<Address>, ComposeActivity>() {
|
||||
|
||||
override fun doLongOperation(location: ParcelableLocation): List<Address>? {
|
||||
try {
|
||||
val activity = callback ?: throw IOException("Interrupted")
|
||||
val gcd = Geocoder(activity, Locale.getDefault())
|
||||
return gcd.getFromLocation(location.latitude, location.longitude, 1)
|
||||
} catch (e: IOException) {
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun beforeExecute() {
|
||||
val location = params
|
||||
val activity = callback ?: return
|
||||
val textView = activity.locationLabel ?: return
|
||||
|
||||
val preferences = activity.preferences
|
||||
val attachLocation = preferences[attachLocationKey]
|
||||
val attachPreciseLocation = preferences[attachPreciseLocationKey]
|
||||
if (attachLocation) {
|
||||
if (attachPreciseLocation) {
|
||||
textView.spannable = ParcelableLocationUtils.getHumanReadableString(location, 3)
|
||||
textView.tag = location
|
||||
} else {
|
||||
val tag = textView.tag
|
||||
if (tag is Address) {
|
||||
textView.spannable = tag.locality
|
||||
} else if (tag is NoAddress) {
|
||||
textView.setText(R.string.label_location_your_coarse_location)
|
||||
} else {
|
||||
textView.setText(R.string.getting_location)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
textView.setText(R.string.no_location)
|
||||
}
|
||||
}
|
||||
|
||||
override fun afterExecute(activity: ComposeActivity?, addresses: List<Address>?) {
|
||||
if (activity == null) return
|
||||
val textView = activity.locationLabel ?: return
|
||||
val preferences = activity.preferences
|
||||
val attachLocation = preferences[attachLocationKey]
|
||||
val attachPreciseLocation = preferences[attachPreciseLocationKey]
|
||||
if (attachLocation) {
|
||||
if (attachPreciseLocation) {
|
||||
val location = params
|
||||
textView.spannable = ParcelableLocationUtils.getHumanReadableString(location, 3)
|
||||
textView.tag = location
|
||||
} else if (addresses == null || addresses.isEmpty()) {
|
||||
val tag = textView.tag
|
||||
if (tag is Address) {
|
||||
textView.spannable = tag.locality
|
||||
} else {
|
||||
textView.setText(R.string.label_location_your_coarse_location)
|
||||
textView.tag = NoAddress()
|
||||
}
|
||||
} else {
|
||||
val address = addresses[0]
|
||||
textView.spannable = address.locality
|
||||
textView.tag = address
|
||||
}
|
||||
} else {
|
||||
textView.setText(R.string.no_location)
|
||||
}
|
||||
}
|
||||
|
||||
internal class NoAddress
|
||||
}
|
||||
|
||||
private class ComposeEnterListener(private val activity: ComposeActivity?) : EnterListener {
|
||||
|
||||
override fun shouldCallListener(): Boolean {
|
||||
|
|
|
@ -23,10 +23,6 @@ import android.os.Bundle
|
|||
import org.mariotaku.twidere.activity.BaseActivity
|
||||
import org.mariotaku.twidere.util.TaskServiceRunner
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/8/22.
|
||||
*/
|
||||
|
||||
class ToggleRefreshActivity : BaseActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
|
|
@ -19,14 +19,12 @@
|
|||
|
||||
package org.mariotaku.twidere.alias
|
||||
|
||||
import org.mariotaku.microblog.library.mastodon.model.Notification
|
||||
import org.mariotaku.microblog.library.mastodon.model.Status
|
||||
import org.mariotaku.microblog.library.mastodon.model.StatusUpdate
|
||||
import org.mariotaku.microblog.library.mastodon.model.TimelineOption
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/4/21.
|
||||
*/
|
||||
|
||||
typealias MastodonStatus = Status
|
||||
typealias MastodonTimelineOption = TimelineOption
|
||||
typealias MastodonStatusUpdate = StatusUpdate
|
||||
typealias MastodonStatusUpdate = StatusUpdate
|
||||
typealias MastodonNotification = Notification
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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.data.fetcher
|
||||
|
||||
import org.mariotaku.microblog.library.MicroBlog
|
||||
import org.mariotaku.microblog.library.mastodon.Mastodon
|
||||
import org.mariotaku.microblog.library.mastodon.model.LinkHeaderList
|
||||
import org.mariotaku.microblog.library.twitter.model.Activity
|
||||
import org.mariotaku.microblog.library.twitter.model.Paging
|
||||
import org.mariotaku.microblog.library.twitter.model.Status
|
||||
import org.mariotaku.twidere.alias.MastodonNotification
|
||||
import org.mariotaku.twidere.exception.APINotSupportedException
|
||||
import org.mariotaku.twidere.model.AccountDetails
|
||||
|
||||
interface ActivitiesFetcher {
|
||||
|
||||
fun forTwitterOfficial(account: AccountDetails, twitter: MicroBlog, paging: Paging): List<Activity>
|
||||
= throw APINotSupportedException(account.type)
|
||||
|
||||
fun forTwitter(account: AccountDetails, twitter: MicroBlog, paging: Paging): List<Status>
|
||||
= throw APINotSupportedException(account.type)
|
||||
|
||||
fun forStatusNet(account: AccountDetails, statusNet: MicroBlog, paging: Paging): List<Status>
|
||||
= throw APINotSupportedException(account.type)
|
||||
|
||||
fun forFanfou(account: AccountDetails, fanfou: MicroBlog, paging: Paging): List<Status>
|
||||
= throw APINotSupportedException(account.type)
|
||||
|
||||
fun forMastodon(account: AccountDetails, mastodon: Mastodon, paging: Paging): LinkHeaderList<MastodonNotification>
|
||||
= throw APINotSupportedException(account.type)
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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.data.fetcher.activities
|
||||
|
||||
import org.mariotaku.microblog.library.MicroBlog
|
||||
import org.mariotaku.microblog.library.mastodon.Mastodon
|
||||
import org.mariotaku.microblog.library.mastodon.model.LinkHeaderList
|
||||
import org.mariotaku.microblog.library.twitter.model.Activity
|
||||
import org.mariotaku.microblog.library.twitter.model.Paging
|
||||
import org.mariotaku.microblog.library.twitter.model.Status
|
||||
import org.mariotaku.twidere.alias.MastodonNotification
|
||||
import org.mariotaku.twidere.data.fetcher.ActivitiesFetcher
|
||||
import org.mariotaku.twidere.model.AccountDetails
|
||||
|
||||
class ActivitiesAboutMeFetcher : ActivitiesFetcher {
|
||||
override fun forTwitterOfficial(account: AccountDetails, twitter: MicroBlog, paging: Paging): List<Activity> {
|
||||
return twitter.getActivitiesAboutMe(paging)
|
||||
}
|
||||
|
||||
override fun forTwitter(account: AccountDetails, twitter: MicroBlog, paging: Paging): List<Status> {
|
||||
return twitter.getMentionsTimeline(paging)
|
||||
}
|
||||
|
||||
override fun forStatusNet(account: AccountDetails, statusNet: MicroBlog, paging: Paging): List<Status> {
|
||||
return statusNet.getMentionsTimeline(paging)
|
||||
}
|
||||
|
||||
override fun forFanfou(account: AccountDetails, fanfou: MicroBlog, paging: Paging): List<Status> {
|
||||
return fanfou.getMentions(paging)
|
||||
}
|
||||
|
||||
override fun forMastodon(account: AccountDetails, mastodon: Mastodon, paging: Paging): LinkHeaderList<MastodonNotification> {
|
||||
return mastodon.getNotifications(paging)
|
||||
}
|
||||
}
|
|
@ -1,39 +1,26 @@
|
|||
/*
|
||||
* Twidere - Twitter client for Android
|
||||
*
|
||||
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||
*
|
||||
* 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.task.cache
|
||||
package org.mariotaku.twidere.data.syncher
|
||||
|
||||
import android.content.Context
|
||||
import org.mariotaku.abstask.library.AbstractTask
|
||||
import org.mariotaku.twidere.model.ParcelableStatus
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
|
||||
class CacheUsersStatusesTask(
|
||||
private val context: Context,
|
||||
private val accountKey: UserKey,
|
||||
private val accountType: String,
|
||||
private val statuses: List<ParcelableStatus>
|
||||
) : AbstractTask<Any?, Unit?, Unit?>() {
|
||||
|
||||
|
||||
override fun doLongOperation(params: Any?) {
|
||||
|
||||
}
|
||||
|
||||
interface TimelinePositionSyncher {
|
||||
fun get(accountKeys: Array<UserKey>)
|
||||
}
|
|
@ -9,9 +9,6 @@ import org.mariotaku.twidere.util.JsonSerializer
|
|||
import org.mariotaku.twidere.util.filter.FiltersSubscriptionProvider
|
||||
import org.mariotaku.twidere.util.filter.LocalFiltersSubscriptionProvider
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/1/9.
|
||||
*/
|
||||
|
||||
fun FiltersSubscription.instantiateComponent(context: Context): FiltersSubscriptionProvider? {
|
||||
val component = this.component ?: return null
|
||||
|
|
|
@ -30,7 +30,7 @@ import org.mariotaku.twidere.model.util.ParcelableCardEntityUtils
|
|||
import java.text.ParseException
|
||||
import java.util.*
|
||||
|
||||
fun CardEntity.toParcelable(accountKey: UserKey, accountType: String): ParcelableCardEntity? {
|
||||
fun CardEntity.toParcelable(accountKey: UserKey, accountType: String): ParcelableCardEntity {
|
||||
val obj = ParcelableCardEntity()
|
||||
obj.name = name
|
||||
obj.url = url
|
||||
|
|
|
@ -283,7 +283,7 @@ abstract class AbsContentRecyclerViewFragment<A : LoadMoreSupportAdapter<Recycle
|
|||
val progressCircleDiameter = swipeLayout.progressCircleDiameter
|
||||
if (progressCircleDiameter == 0) return
|
||||
val progressViewStart = 0 - progressCircleDiameter
|
||||
val progressViewEnd = insets.top + resources.getDimensionPixelSize(R.dimen.element_spacing_normal)
|
||||
val progressViewEnd = insets.top + resources.getDimensionPixelSize(R.dimen.element_spacing_large)
|
||||
swipeLayout.setProgressViewOffset(false, progressViewStart, progressViewEnd)
|
||||
}
|
||||
|
||||
|
|
|
@ -399,8 +399,7 @@ class UserProfileEditorFragment : BaseFragment(), OnSizeChangedListener,
|
|||
val context = this.callback?.context ?: return@promiseOnUi
|
||||
val (user, account) = result
|
||||
val task = UpdateAccountInfoTask(context)
|
||||
task.params = Pair(account, user)
|
||||
TaskStarter.execute(task)
|
||||
task.toPromise(Pair(account, user))
|
||||
} and callback.executeAfterFragmentResumed { fragment ->
|
||||
fragment.childFragmentManager.dismissDialogFragment(DIALOG_FRAGMENT_TAG)
|
||||
fragment.activity.finish()
|
||||
|
|
|
@ -18,8 +18,9 @@ import android.widget.ListView
|
|||
import android.widget.TextView
|
||||
import com.rengwuxian.materialedittext.MaterialEditText
|
||||
import kotlinx.android.synthetic.main.layout_list_with_empty_view.*
|
||||
import nl.komponents.kovenant.combine.and
|
||||
import nl.komponents.kovenant.ui.alwaysUi
|
||||
import okhttp3.HttpUrl
|
||||
import org.mariotaku.abstask.library.TaskStarter
|
||||
import org.mariotaku.ktextension.*
|
||||
import org.mariotaku.library.objectcursor.ObjectCursor
|
||||
import org.mariotaku.sqliteqb.library.Expression
|
||||
|
@ -35,7 +36,6 @@ import org.mariotaku.twidere.extension.util.isAdvancedFiltersEnabled
|
|||
import org.mariotaku.twidere.fragment.BaseDialogFragment
|
||||
import org.mariotaku.twidere.fragment.BaseFragment
|
||||
import org.mariotaku.twidere.fragment.ExtraFeaturesIntroductionDialogFragment
|
||||
import org.mariotaku.twidere.fragment.ProgressDialogFragment
|
||||
import org.mariotaku.twidere.model.FiltersSubscription
|
||||
import org.mariotaku.twidere.model.analyzer.PurchaseFinished
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Filters
|
||||
|
@ -44,7 +44,6 @@ import org.mariotaku.twidere.util.Analyzer
|
|||
import org.mariotaku.twidere.util.content.ContentResolverUtils
|
||||
import org.mariotaku.twidere.util.premium.ExtraFeaturesService
|
||||
import org.mariotaku.twidere.util.view.SimpleTextWatcher
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
|
||||
/**
|
||||
|
@ -134,16 +133,9 @@ class FiltersSubscriptionsFragment : BaseFragment(), LoaderManager.LoaderCallbac
|
|||
return true
|
||||
}
|
||||
R.id.refresh -> {
|
||||
executeAfterFragmentResumed { fragment ->
|
||||
ProgressDialogFragment.show(fragment.childFragmentManager, FRAGMENT_TAG_RREFRESH_FILTERS)
|
||||
val task = RefreshFiltersSubscriptionsTask(fragment.context)
|
||||
val fragmentRef = WeakReference(fragment)
|
||||
task.callback = {
|
||||
fragmentRef.get()?.executeAfterFragmentResumed { fragment ->
|
||||
fragment.fragmentManager.dismissDialogFragment(FRAGMENT_TAG_RREFRESH_FILTERS)
|
||||
}
|
||||
}
|
||||
TaskStarter.execute(task)
|
||||
val weakThis by weak(this)
|
||||
showProgressDialog(FRAGMENT_TAG_RREFRESH_FILTERS) and RefreshFiltersSubscriptionsTask(context).toPromise(Unit).alwaysUi {
|
||||
weakThis?.fragmentManager?.dismissDialogFragment(FRAGMENT_TAG_RREFRESH_FILTERS)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -44,6 +44,9 @@ import kotlinx.android.synthetic.main.activity_premium_dashboard.*
|
|||
import kotlinx.android.synthetic.main.fragment_messages_conversation.*
|
||||
import kotlinx.android.synthetic.main.fragment_messages_conversation.view.*
|
||||
import kotlinx.android.synthetic.main.layout_toolbar_message_conversation_title.*
|
||||
import nl.komponents.kovenant.then
|
||||
import nl.komponents.kovenant.ui.alwaysUi
|
||||
import nl.komponents.kovenant.ui.promiseOnUi
|
||||
import org.mariotaku.abstask.library.TaskStarter
|
||||
import org.mariotaku.chameleon.Chameleon
|
||||
import org.mariotaku.chameleon.ChameleonUtils
|
||||
|
@ -79,14 +82,10 @@ import org.mariotaku.twidere.model.util.AccountUtils
|
|||
import org.mariotaku.twidere.provider.TwidereDataStore.Messages
|
||||
import org.mariotaku.twidere.service.LengthyOperationsService
|
||||
import org.mariotaku.twidere.task.compose.AbsAddMediaTask
|
||||
import org.mariotaku.twidere.task.compose.AbsDeleteMediaTask
|
||||
import org.mariotaku.twidere.task.twitter.message.DestroyMessageTask
|
||||
import org.mariotaku.twidere.task.twitter.message.GetMessagesTask
|
||||
import org.mariotaku.twidere.task.twitter.message.MarkMessageReadTask
|
||||
import org.mariotaku.twidere.util.ClipboardUtils
|
||||
import org.mariotaku.twidere.util.DataStoreUtils
|
||||
import org.mariotaku.twidere.util.IntentUtils
|
||||
import org.mariotaku.twidere.util.PreviewGridItemDecoration
|
||||
import org.mariotaku.twidere.util.*
|
||||
import org.mariotaku.twidere.view.ExtendedRecyclerView
|
||||
import org.mariotaku.twidere.view.holder.compose.MediaPreviewViewHolder
|
||||
import java.lang.ref.WeakReference
|
||||
|
@ -148,9 +147,7 @@ class MessagesConversationFragment : AbsContentListRecyclerViewFragment<Messages
|
|||
|
||||
mediaPreviewAdapter.listener = object : MediaPreviewAdapter.Listener {
|
||||
override fun onRemoveClick(position: Int, holder: MediaPreviewViewHolder) {
|
||||
val task = DeleteMediaTask(this@MessagesConversationFragment,
|
||||
arrayOf(mediaPreviewAdapter.getItem(position)))
|
||||
TaskStarter.execute(task)
|
||||
deleteMedia(mediaPreviewAdapter.getItem(position))
|
||||
}
|
||||
|
||||
override fun onEditClick(position: Int, holder: MediaPreviewViewHolder) {
|
||||
|
@ -565,27 +562,19 @@ class MessagesConversationFragment : AbsContentListRecyclerViewFragment<Messages
|
|||
|
||||
}
|
||||
|
||||
internal class DeleteMediaTask(
|
||||
fragment: MessagesConversationFragment,
|
||||
val media: Array<ParcelableMediaUpdate>
|
||||
) : AbsDeleteMediaTask<MessagesConversationFragment>(fragment.context,
|
||||
media.mapToArray { Uri.parse(it.uri) }) {
|
||||
|
||||
init {
|
||||
callback = fragment
|
||||
private fun deleteMedia(vararg media: ParcelableMediaUpdate) {
|
||||
val weakThis by weak(this)
|
||||
promiseOnUi {
|
||||
weakThis?.setProgressVisible(true)
|
||||
}.then {
|
||||
val context = weakThis?.context ?: throw InterruptedException()
|
||||
media.forEach {
|
||||
Utils.deleteMedia(context, Uri.parse(it.uri))
|
||||
}
|
||||
}.alwaysUi {
|
||||
weakThis?.setProgressVisible(false)
|
||||
weakThis?.removeMedia(media.toList())
|
||||
}
|
||||
|
||||
override fun afterExecute(callback: MessagesConversationFragment?, results: BooleanArray) {
|
||||
if (callback == null) return
|
||||
callback.setProgressVisible(false)
|
||||
callback.removeMedia(media.toList())
|
||||
}
|
||||
|
||||
override fun beforeExecute() {
|
||||
val fragment = callback ?: return
|
||||
fragment.setProgressVisible(true)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal class ConversationLoader(
|
||||
|
|
|
@ -25,7 +25,6 @@ import android.os.Bundle
|
|||
import android.support.v4.content.FixedAsyncTaskLoader
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import org.mariotaku.abstask.library.TaskStarter
|
||||
import org.mariotaku.ktextension.set
|
||||
import org.mariotaku.library.objectcursor.ObjectCursor
|
||||
import org.mariotaku.microblog.library.MicroBlog
|
||||
|
@ -202,8 +201,7 @@ class ParcelableUserLoader(
|
|||
val account = data.extras.getParcelable<AccountDetails>(EXTRA_ACCOUNT)
|
||||
if (account != null) {
|
||||
val task = UpdateAccountInfoTask(context)
|
||||
task.params = Pair(account, user)
|
||||
TaskStarter.execute(task)
|
||||
task.toPromise(Pair(account, user))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,26 +19,23 @@
|
|||
|
||||
package org.mariotaku.twidere.service
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.annotation.TargetApi
|
||||
import android.app.job.JobParameters
|
||||
import android.app.job.JobService
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import nl.komponents.kovenant.ui.failUi
|
||||
import nl.komponents.kovenant.ui.successUi
|
||||
import org.mariotaku.kpreferences.KPreferences
|
||||
import org.mariotaku.twidere.TwidereConstants.LOGTAG
|
||||
import org.mariotaku.ktextension.deadline
|
||||
import org.mariotaku.twidere.annotation.AutoRefreshType
|
||||
import org.mariotaku.twidere.constant.autoRefreshCompatibilityModeKey
|
||||
import org.mariotaku.twidere.util.Analyzer
|
||||
import org.mariotaku.twidere.util.TaskServiceRunner
|
||||
import org.mariotaku.twidere.util.dagger.GeneralComponent
|
||||
import org.mariotaku.twidere.util.support.JobServiceSupport
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 14/12/12.
|
||||
*/
|
||||
@SuppressLint("Registered")
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
class JobTaskService : JobService() {
|
||||
|
||||
|
@ -49,22 +46,19 @@ class JobTaskService : JobService() {
|
|||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
Log.d(LOGTAG, "JobTaskService started")
|
||||
GeneralComponent.get(this).inject(this)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
Log.d(LOGTAG, "JobTaskService destroyed")
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onStartJob(params: JobParameters): Boolean {
|
||||
Log.d(LOGTAG, "JobTaskService received job $params")
|
||||
if (kPreferences[autoRefreshCompatibilityModeKey]) return false
|
||||
val action = getTaskAction(params.jobId) ?: return false
|
||||
return taskServiceRunner.runTask(action) {
|
||||
this.jobFinished(params, false)
|
||||
val promise = taskServiceRunner.createPromise(action) ?: return false
|
||||
promise.deadline(3, TimeUnit.MINUTES).successUi {
|
||||
jobFinished(params, false)
|
||||
}.failUi {
|
||||
jobFinished(params, false)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onStopJob(params: JobParameters): Boolean {
|
||||
|
@ -116,5 +110,7 @@ class JobTaskService : JobService() {
|
|||
JOB_ID_SYNC_USER_COLORS -> TaskServiceRunner.ACTION_SYNC_USER_COLORS
|
||||
else -> null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -23,7 +23,10 @@ import android.content.Intent
|
|||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import android.util.Log
|
||||
import nl.komponents.kovenant.ui.failUi
|
||||
import nl.komponents.kovenant.ui.successUi
|
||||
import org.mariotaku.kpreferences.get
|
||||
import org.mariotaku.ktextension.deadline
|
||||
import org.mariotaku.twidere.TwidereConstants.LOGTAG
|
||||
import org.mariotaku.twidere.annotation.AutoRefreshType
|
||||
import org.mariotaku.twidere.constant.autoRefreshCompatibilityModeKey
|
||||
|
@ -31,6 +34,7 @@ import org.mariotaku.twidere.util.TaskServiceRunner.Companion.ACTION_REFRESH_DIR
|
|||
import org.mariotaku.twidere.util.TaskServiceRunner.Companion.ACTION_REFRESH_HOME_TIMELINE
|
||||
import org.mariotaku.twidere.util.TaskServiceRunner.Companion.ACTION_REFRESH_NOTIFICATIONS
|
||||
import org.mariotaku.twidere.util.dagger.GeneralComponent
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class LegacyTaskService : BaseService() {
|
||||
|
||||
|
@ -50,14 +54,22 @@ class LegacyTaskService : BaseService() {
|
|||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
Log.d(LOGTAG, "LegacyTaskService received $intent")
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
|
||||
!preferences[autoRefreshCompatibilityModeKey]) return START_NOT_STICKY
|
||||
val action = intent?.action ?: return START_NOT_STICKY
|
||||
taskServiceRunner.runTask(action) {
|
||||
!preferences[autoRefreshCompatibilityModeKey]) return serviceNotHandled(startId)
|
||||
val action = intent?.action ?: return serviceNotHandled(startId)
|
||||
val promise = taskServiceRunner.createPromise(action) ?: return serviceNotHandled(startId)
|
||||
promise.deadline(3, TimeUnit.MINUTES).successUi {
|
||||
stopSelfResult(startId)
|
||||
}.failUi {
|
||||
stopSelfResult(startId)
|
||||
}
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
||||
private fun serviceNotHandled(startId: Int): Int {
|
||||
stopSelfResult(startId)
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
|
||||
|
|
|
@ -21,10 +21,6 @@ import org.mariotaku.twidere.util.sync.SyncPreferences
|
|||
import org.mariotaku.twidere.util.sync.TimelineSyncManager
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/2/7.
|
||||
*/
|
||||
|
||||
abstract class BaseAbstractTask<Params, Result, Callback>(val context: Context) : AbstractTask<Params, Result, Callback>() {
|
||||
|
||||
@Inject
|
||||
|
@ -73,4 +69,5 @@ abstract class BaseAbstractTask<Params, Result, Callback>(val context: Context)
|
|||
@Suppress("UNCHECKED_CAST")
|
||||
GeneralComponent.get(context).inject(this as BaseAbstractTask<Any, Any, Any>)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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.task
|
||||
|
||||
import nl.komponents.kovenant.Promise
|
||||
|
||||
interface PromiseTask<in P, out T : Any> {
|
||||
fun toPromise(param: P): Promise<T, Exception>
|
||||
}
|
|
@ -5,44 +5,46 @@ import android.accounts.AccountManager
|
|||
import android.content.ContentResolver
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.support.v4.util.LongSparseArray
|
||||
import android.text.TextUtils
|
||||
import org.mariotaku.abstask.library.AbstractTask
|
||||
import org.mariotaku.library.objectcursor.ObjectCursor
|
||||
import nl.komponents.kovenant.Promise
|
||||
import nl.komponents.kovenant.task
|
||||
import nl.komponents.kovenant.then
|
||||
import org.mariotaku.sqliteqb.library.Expression
|
||||
import org.mariotaku.twidere.TwidereConstants.ACCOUNT_TYPE
|
||||
import org.mariotaku.twidere.extension.model.setAccountKey
|
||||
import org.mariotaku.twidere.extension.model.setAccountUser
|
||||
import org.mariotaku.twidere.extension.queryReference
|
||||
import org.mariotaku.twidere.model.AccountDetails
|
||||
import org.mariotaku.twidere.model.ParcelableUser
|
||||
import org.mariotaku.twidere.model.Tab
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.*
|
||||
import java.io.IOException
|
||||
import org.mariotaku.twidere.util.updateItems
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 16/3/8.
|
||||
*/
|
||||
class UpdateAccountInfoTask(
|
||||
private val context: Context
|
||||
) : AbstractTask<Pair<AccountDetails, ParcelableUser>, Unit, Unit?>() {
|
||||
) : PromiseTask<Pair<AccountDetails, ParcelableUser>, Unit> {
|
||||
|
||||
override fun doLongOperation(params: Pair<AccountDetails, ParcelableUser>) {
|
||||
override fun toPromise(param: Pair<AccountDetails, ParcelableUser>): Promise<Unit, Exception> {
|
||||
val resolver = context.contentResolver
|
||||
val (details, user) = params
|
||||
val (details, user) = param
|
||||
if (user.is_cache) {
|
||||
return
|
||||
return Promise.ofSuccess(Unit)
|
||||
}
|
||||
if (!user.key.maybeEquals(user.account_key)) {
|
||||
return
|
||||
return Promise.ofSuccess(Unit)
|
||||
}
|
||||
return task {
|
||||
val am = AccountManager.get(context)
|
||||
val account = Account(details.account.name, ACCOUNT_TYPE)
|
||||
account.setAccountUser(am, user)
|
||||
account.setAccountKey(am, user.key)
|
||||
}.then {
|
||||
updateTimeline(user, details, resolver)
|
||||
}.then {
|
||||
updateTabs(resolver, user.key)
|
||||
}
|
||||
}
|
||||
|
||||
val am = AccountManager.get(context)
|
||||
val account = Account(details.account.name, ACCOUNT_TYPE)
|
||||
account.setAccountUser(am, user)
|
||||
account.setAccountKey(am, user.key)
|
||||
|
||||
private fun updateTimeline(user: ParcelableUser, details: AccountDetails, resolver: ContentResolver) {
|
||||
val accountKeyValues = ContentValues().apply {
|
||||
put(AccountSupportColumns.ACCOUNT_KEY, user.key.toString())
|
||||
}
|
||||
|
@ -54,38 +56,19 @@ class UpdateAccountInfoTask(
|
|||
resolver.update(Messages.CONTENT_URI, accountKeyValues, accountKeyWhere, accountKeyWhereArgs)
|
||||
resolver.update(Messages.Conversations.CONTENT_URI, accountKeyValues, accountKeyWhere, accountKeyWhereArgs)
|
||||
resolver.update(CachedRelationships.CONTENT_URI, accountKeyValues, accountKeyWhere, accountKeyWhereArgs)
|
||||
|
||||
updateTabs(resolver, user.key)
|
||||
}
|
||||
|
||||
private fun updateTabs(resolver: ContentResolver, accountKey: UserKey) {
|
||||
resolver.queryReference(Tabs.CONTENT_URI, Tabs.COLUMNS, null, null,
|
||||
null)?.use { (tabsCursor) ->
|
||||
val indices = ObjectCursor.indicesFrom(tabsCursor, Tab::class.java)
|
||||
val creator = ObjectCursor.valuesCreatorFrom(Tab::class.java)
|
||||
tabsCursor.moveToFirst()
|
||||
val values = LongSparseArray<ContentValues>()
|
||||
try {
|
||||
while (!tabsCursor.isAfterLast) {
|
||||
val tab = indices.newObject(tabsCursor)
|
||||
val arguments = tab.arguments
|
||||
if (arguments != null) {
|
||||
val accountId = arguments.accountId
|
||||
val keys = arguments.accountKeys
|
||||
if (TextUtils.equals(accountKey.id, accountId) && keys == null) {
|
||||
arguments.accountKeys = arrayOf(accountKey)
|
||||
values.put(tab.id, creator.create(tab))
|
||||
}
|
||||
}
|
||||
tabsCursor.moveToNext()
|
||||
resolver.updateItems(Tabs.CONTENT_URI, Tabs.COLUMNS, null, null, Tab::class.java) { tab ->
|
||||
val arguments = tab.arguments
|
||||
if (arguments != null) {
|
||||
val accountId = arguments.accountId
|
||||
val keys = arguments.accountKeys
|
||||
if (accountKey.id == accountId && keys == null) {
|
||||
arguments.accountKeys = arrayOf(accountKey)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
// Ignore
|
||||
}
|
||||
for (i in 0 until values.size()) {
|
||||
val where = Expression.equals(Tabs._ID, values.keyAt(i)).sql
|
||||
resolver.update(Tabs.CONTENT_URI, values.valueAt(i), where, null)
|
||||
}
|
||||
return@updateItems tab
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,46 +3,39 @@ package org.mariotaku.twidere.task.filter
|
|||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import org.mariotaku.abstask.library.AbstractTask
|
||||
import nl.komponents.kovenant.Promise
|
||||
import nl.komponents.kovenant.task
|
||||
import org.mariotaku.library.objectcursor.ObjectCursor
|
||||
import org.mariotaku.sqliteqb.library.Expression
|
||||
import org.mariotaku.twidere.extension.model.instantiateComponent
|
||||
import org.mariotaku.twidere.extension.queryReference
|
||||
import org.mariotaku.twidere.extension.queryAll
|
||||
import org.mariotaku.twidere.model.FiltersData
|
||||
import org.mariotaku.twidere.model.FiltersSubscription
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Filters
|
||||
import org.mariotaku.twidere.task.PromiseTask
|
||||
import org.mariotaku.twidere.util.DebugLog
|
||||
import org.mariotaku.twidere.util.content.ContentResolverUtils
|
||||
import org.mariotaku.twidere.util.sync.LOGTAG_SYNC
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
||||
class RefreshFiltersSubscriptionsTask(val context: Context) : AbstractTask<Unit?, Boolean, (Boolean) -> Unit>() {
|
||||
|
||||
override fun doLongOperation(param: Unit?): Boolean {
|
||||
class RefreshFiltersSubscriptionsTask(val context: Context) : PromiseTask<Unit, Unit> {
|
||||
override fun toPromise(param: Unit): Promise<Unit, Exception> = task {
|
||||
val resolver = context.contentResolver
|
||||
val sourceIds = ArrayList<Long>()
|
||||
resolver.queryReference(Filters.Subscriptions.CONTENT_URI, Filters.Subscriptions.COLUMNS,
|
||||
null, null, null)?.use { (cursor) ->
|
||||
val indices = ObjectCursor.indicesFrom(cursor, FiltersSubscription::class.java)
|
||||
cursor.moveToFirst()
|
||||
while (!cursor.isAfterLast) {
|
||||
val subscription = indices.newObject(cursor)
|
||||
sourceIds.add(subscription.id)
|
||||
val component = subscription.instantiateComponent(context)
|
||||
if (component != null) {
|
||||
try {
|
||||
if (component.fetchFilters()) {
|
||||
updateUserItems(resolver, component.users, subscription.id)
|
||||
updateBaseItems(resolver, component.keywords, Filters.Keywords.CONTENT_URI, subscription.id)
|
||||
updateBaseItems(resolver, component.links, Filters.Links.CONTENT_URI, subscription.id)
|
||||
updateBaseItems(resolver, component.sources, Filters.Sources.CONTENT_URI, subscription.id)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
DebugLog.w(LOGTAG_SYNC, "Unable to refresh filters", e)
|
||||
}
|
||||
resolver.queryAll(Filters.Subscriptions.CONTENT_URI, Filters.Subscriptions.COLUMNS,
|
||||
null, null, cls = FiltersSubscription::class.java).forEach { subscription ->
|
||||
sourceIds.add(subscription.id)
|
||||
val component = subscription.instantiateComponent(context) ?: return@forEach
|
||||
try {
|
||||
if (component.fetchFilters()) {
|
||||
updateUserItems(resolver, component.users, subscription.id)
|
||||
updateBaseItems(resolver, component.keywords, Filters.Keywords.CONTENT_URI, subscription.id)
|
||||
updateBaseItems(resolver, component.links, Filters.Links.CONTENT_URI, subscription.id)
|
||||
updateBaseItems(resolver, component.sources, Filters.Sources.CONTENT_URI, subscription.id)
|
||||
}
|
||||
cursor.moveToNext()
|
||||
} catch (e: IOException) {
|
||||
DebugLog.w(LOGTAG_SYNC, "Unable to refresh filters", e)
|
||||
}
|
||||
}
|
||||
// Delete 'orphaned' filter items with `sourceId` > 0
|
||||
|
@ -55,16 +48,7 @@ class RefreshFiltersSubscriptionsTask(val context: Context) : AbstractTask<Unit?
|
|||
true, sourceIds, extraWhere, null)
|
||||
ContentResolverUtils.bulkDelete(resolver, Filters.Links.CONTENT_URI, Filters.Links.SOURCE,
|
||||
true, sourceIds, extraWhere, null)
|
||||
try {
|
||||
Thread.sleep(1000)
|
||||
} catch (e: InterruptedException) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun afterExecute(callback: ((Boolean) -> Unit)?, result: Boolean) {
|
||||
callback?.invoke(result)
|
||||
Thread.sleep(1000)
|
||||
}
|
||||
|
||||
private fun updateUserItems(resolver: ContentResolver, items: List<FiltersData.UserItem>?, sourceId: Long) {
|
||||
|
|
|
@ -29,9 +29,6 @@ import org.mariotaku.twidere.util.JsonSerializer
|
|||
import java.io.IOException
|
||||
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/8/22.
|
||||
*/
|
||||
class RefreshLaunchPresentationsTask(context: Context) : BaseAbstractTask<Unit?, Boolean, (Boolean) -> Unit>(context) {
|
||||
override fun doLongOperation(params: Unit?): Boolean {
|
||||
val builder = HttpRequest.Builder()
|
||||
|
@ -54,6 +51,10 @@ class RefreshLaunchPresentationsTask(context: Context) : BaseAbstractTask<Unit?,
|
|||
}
|
||||
}
|
||||
|
||||
override fun afterExecute(callback: ((Boolean) -> Unit)?, result: Boolean) {
|
||||
callback?.invoke(result)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val JSON_CACHE_KEY = "launch_presentations"
|
||||
}
|
||||
|
|
|
@ -25,11 +25,9 @@ import org.mariotaku.twidere.annotation.FilterScope
|
|||
import org.mariotaku.twidere.data.fetcher.GroupTimelineFetcher
|
||||
import org.mariotaku.twidere.data.fetcher.StatusesFetcher
|
||||
import org.mariotaku.twidere.extension.withAppendedPath
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
import org.mariotaku.twidere.model.refresh.GroupTimelineContentRefreshParam
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses
|
||||
import org.mariotaku.twidere.util.ErrorInfoStore
|
||||
import org.mariotaku.twidere.util.sync.TimelineSyncManager
|
||||
|
||||
class GetGroupTimelineTask(context: Context) : GetStatusesTask<GroupTimelineContentRefreshParam>(context) {
|
||||
|
||||
|
@ -44,7 +42,4 @@ class GetGroupTimelineTask(context: Context) : GetStatusesTask<GroupTimelineCont
|
|||
return GroupTimelineFetcher(params?.id, params?.name)
|
||||
}
|
||||
|
||||
override fun syncFetchReadPosition(manager: TimelineSyncManager, accountKeys: Array<UserKey>) {
|
||||
}
|
||||
|
||||
}
|
|
@ -22,14 +22,10 @@ package org.mariotaku.twidere.task.statuses
|
|||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import org.mariotaku.twidere.annotation.FilterScope
|
||||
import org.mariotaku.twidere.annotation.ReadPositionTag
|
||||
import org.mariotaku.twidere.data.fetcher.HomeTimelineFetcher
|
||||
import org.mariotaku.twidere.fragment.timeline.HomeTimelineFragment
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
import org.mariotaku.twidere.model.refresh.ContentRefreshParam
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses
|
||||
import org.mariotaku.twidere.util.ErrorInfoStore
|
||||
import org.mariotaku.twidere.util.sync.TimelineSyncManager
|
||||
|
||||
class GetHomeTimelineTask(context: Context) : GetStatusesTask<ContentRefreshParam>(context) {
|
||||
|
||||
|
@ -41,8 +37,4 @@ class GetHomeTimelineTask(context: Context) : GetStatusesTask<ContentRefreshPara
|
|||
|
||||
override fun getStatusesFetcher(params: ContentRefreshParam?) = HomeTimelineFetcher()
|
||||
|
||||
override fun syncFetchReadPosition(manager: TimelineSyncManager, accountKeys: Array<UserKey>) {
|
||||
val tag = HomeTimelineFragment.getTimelineSyncTag(accountKeys)
|
||||
manager.fetchSingle(ReadPositionTag.HOME_TIMELINE, tag)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,11 +25,9 @@ import org.mariotaku.twidere.annotation.FilterScope
|
|||
import org.mariotaku.twidere.data.fetcher.ListTimelineFetcher
|
||||
import org.mariotaku.twidere.data.fetcher.StatusesFetcher
|
||||
import org.mariotaku.twidere.extension.withAppendedPath
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
import org.mariotaku.twidere.model.refresh.ListTimelineContentRefreshParam
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses
|
||||
import org.mariotaku.twidere.util.ErrorInfoStore
|
||||
import org.mariotaku.twidere.util.sync.TimelineSyncManager
|
||||
|
||||
class GetListTimelineTask(context: Context) : GetStatusesTask<ListTimelineContentRefreshParam>(context) {
|
||||
|
||||
|
@ -44,7 +42,4 @@ class GetListTimelineTask(context: Context) : GetStatusesTask<ListTimelineConten
|
|||
return ListTimelineFetcher(params?.id, params?.slug, params?.ownerId, params?.ownerScreenName)
|
||||
}
|
||||
|
||||
override fun syncFetchReadPosition(manager: TimelineSyncManager, accountKeys: Array<UserKey>) {
|
||||
}
|
||||
|
||||
}
|
|
@ -25,11 +25,9 @@ import org.mariotaku.twidere.annotation.FilterScope
|
|||
import org.mariotaku.twidere.data.fetcher.MediaSearchTimelineFetcher
|
||||
import org.mariotaku.twidere.data.fetcher.StatusesFetcher
|
||||
import org.mariotaku.twidere.extension.withAppendedPath
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
import org.mariotaku.twidere.model.refresh.SearchTimelineContentRefreshParam
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses
|
||||
import org.mariotaku.twidere.util.ErrorInfoStore
|
||||
import org.mariotaku.twidere.util.sync.TimelineSyncManager
|
||||
|
||||
class GetMediaSearchTimelineTask(context: Context) : GetStatusesTask<SearchTimelineContentRefreshParam>(context) {
|
||||
|
||||
|
@ -44,7 +42,4 @@ class GetMediaSearchTimelineTask(context: Context) : GetStatusesTask<SearchTimel
|
|||
return MediaSearchTimelineFetcher(params?.query)
|
||||
}
|
||||
|
||||
override fun syncFetchReadPosition(manager: TimelineSyncManager, accountKeys: Array<UserKey>) {
|
||||
}
|
||||
|
||||
}
|
|
@ -22,14 +22,10 @@ package org.mariotaku.twidere.task.statuses
|
|||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import org.mariotaku.twidere.annotation.FilterScope
|
||||
import org.mariotaku.twidere.annotation.ReadPositionTag
|
||||
import org.mariotaku.twidere.data.fetcher.NetworkPublicTimelineFetcher
|
||||
import org.mariotaku.twidere.fragment.timeline.NetworkPublicTimelineFragment
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
import org.mariotaku.twidere.model.refresh.ContentRefreshParam
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses
|
||||
import org.mariotaku.twidere.util.ErrorInfoStore
|
||||
import org.mariotaku.twidere.util.sync.TimelineSyncManager
|
||||
|
||||
class GetNetworkPublicTimelineTask(context: Context) : GetStatusesTask<ContentRefreshParam>(context) {
|
||||
|
||||
|
@ -41,8 +37,4 @@ class GetNetworkPublicTimelineTask(context: Context) : GetStatusesTask<ContentRe
|
|||
|
||||
override fun getStatusesFetcher(params: ContentRefreshParam?) = NetworkPublicTimelineFetcher()
|
||||
|
||||
override fun syncFetchReadPosition(manager: TimelineSyncManager, accountKeys: Array<UserKey>) {
|
||||
val tag = NetworkPublicTimelineFragment.getTimelineSyncTag(accountKeys)
|
||||
manager.fetchSingle(ReadPositionTag.NETWORK_PUBLIC_TIMELINE, tag)
|
||||
}
|
||||
}
|
|
@ -22,14 +22,10 @@ package org.mariotaku.twidere.task.statuses
|
|||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import org.mariotaku.twidere.annotation.FilterScope
|
||||
import org.mariotaku.twidere.annotation.ReadPositionTag
|
||||
import org.mariotaku.twidere.data.fetcher.PublicTimelineFetcher
|
||||
import org.mariotaku.twidere.fragment.timeline.PublicTimelineFragment
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
import org.mariotaku.twidere.model.refresh.ContentRefreshParam
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses
|
||||
import org.mariotaku.twidere.util.ErrorInfoStore
|
||||
import org.mariotaku.twidere.util.sync.TimelineSyncManager
|
||||
|
||||
class GetPublicTimelineTask(context: Context) : GetStatusesTask<ContentRefreshParam>(context) {
|
||||
|
||||
|
@ -41,8 +37,4 @@ class GetPublicTimelineTask(context: Context) : GetStatusesTask<ContentRefreshPa
|
|||
|
||||
override fun getStatusesFetcher(params: ContentRefreshParam?) = PublicTimelineFetcher()
|
||||
|
||||
override fun syncFetchReadPosition(manager: TimelineSyncManager, accountKeys: Array<UserKey>) {
|
||||
val tag = PublicTimelineFragment.getTimelineSyncTag(accountKeys)
|
||||
manager.fetchSingle(ReadPositionTag.PUBLIC_TIMELINE, tag)
|
||||
}
|
||||
}
|
|
@ -25,11 +25,9 @@ import org.mariotaku.twidere.annotation.FilterScope
|
|||
import org.mariotaku.twidere.data.fetcher.SearchTimelineFetcher
|
||||
import org.mariotaku.twidere.data.fetcher.StatusesFetcher
|
||||
import org.mariotaku.twidere.extension.withAppendedPath
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
import org.mariotaku.twidere.model.refresh.SearchTimelineContentRefreshParam
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses
|
||||
import org.mariotaku.twidere.util.ErrorInfoStore
|
||||
import org.mariotaku.twidere.util.sync.TimelineSyncManager
|
||||
|
||||
class GetSearchTimelineTask(context: Context) : GetStatusesTask<SearchTimelineContentRefreshParam>(context) {
|
||||
|
||||
|
@ -44,7 +42,4 @@ class GetSearchTimelineTask(context: Context) : GetStatusesTask<SearchTimelineCo
|
|||
return SearchTimelineFetcher(params?.query, params?.local ?: false)
|
||||
}
|
||||
|
||||
override fun syncFetchReadPosition(manager: TimelineSyncManager, accountKeys: Array<UserKey>) {
|
||||
}
|
||||
|
||||
}
|
|
@ -42,6 +42,7 @@ import org.mariotaku.twidere.annotation.AccountType
|
|||
import org.mariotaku.twidere.annotation.FilterScope
|
||||
import org.mariotaku.twidere.constant.loadItemLimitKey
|
||||
import org.mariotaku.twidere.data.fetcher.StatusesFetcher
|
||||
import org.mariotaku.twidere.data.syncher.TimelinePositionSyncher
|
||||
import org.mariotaku.twidere.exception.AccountNotFoundException
|
||||
import org.mariotaku.twidere.extension.model.*
|
||||
import org.mariotaku.twidere.extension.model.api.applyLoadLimit
|
||||
|
@ -72,8 +73,6 @@ abstract class GetStatusesTask<P : ContentRefreshParam>(
|
|||
) : BaseAbstractTask<P, List<Pair<GetTimelineResult<ParcelableStatus>?, Exception?>>,
|
||||
(Boolean) -> Unit>(context) {
|
||||
|
||||
private val profileImageSize = context.getString(R.string.profile_image_size)
|
||||
|
||||
protected abstract val contentUri: Uri
|
||||
|
||||
@FilterScope
|
||||
|
@ -81,7 +80,9 @@ abstract class GetStatusesTask<P : ContentRefreshParam>(
|
|||
|
||||
protected abstract val errorInfoKey: String
|
||||
|
||||
override fun doLongOperation(param: P): List<Pair<GetTimelineResult<ParcelableStatus>?, Exception?>> {
|
||||
private val profileImageSize = context.getString(R.string.profile_image_size)
|
||||
|
||||
override final fun doLongOperation(param: P): List<Pair<GetTimelineResult<ParcelableStatus>?, Exception?>> {
|
||||
if (param.shouldAbort) return emptyList()
|
||||
val accountKeys = param.accountKeys.takeIf { it.isNotEmpty() } ?: return emptyList()
|
||||
val loadItemLimit = preferences[loadItemLimitKey]
|
||||
|
@ -210,7 +211,12 @@ abstract class GetStatusesTask<P : ContentRefreshParam>(
|
|||
|
||||
protected abstract fun getStatusesFetcher(params: P?): StatusesFetcher
|
||||
|
||||
protected abstract fun syncFetchReadPosition(manager: TimelineSyncManager, accountKeys: Array<UserKey>)
|
||||
protected open fun getPositionSyncher(manager: TimelineSyncManager): TimelinePositionSyncher? = null
|
||||
|
||||
private fun syncFetchReadPosition(manager: TimelineSyncManager, accountKeys: Array<UserKey>) {
|
||||
val fetcher = getPositionSyncher(manager) ?: return
|
||||
fetcher.get(accountKeys)
|
||||
}
|
||||
|
||||
private fun extractMicroBlogUsers(timeline: List<Status>, account: AccountDetails): List<ParcelableUser> {
|
||||
return timeline.flatMap { status ->
|
||||
|
@ -319,13 +325,12 @@ abstract class GetStatusesTask<P : ContentRefreshParam>(
|
|||
fun getPositionKey(timestamp: Long, sortId: Long, lastSortId: Long, sortDiff: Long,
|
||||
position: Int, count: Int): Long {
|
||||
if (sortDiff == 0L) return timestamp
|
||||
val extraValue: Int
|
||||
if (sortDiff > 0) {
|
||||
val extraValue = if (sortDiff > 0) {
|
||||
// descent sorted by time
|
||||
extraValue = count - 1 - position
|
||||
count - 1 - position
|
||||
} else {
|
||||
// ascent sorted by time
|
||||
extraValue = position
|
||||
position
|
||||
}
|
||||
return timestamp + (sortId - lastSortId) * (499 - count) / sortDiff + extraValue.toLong()
|
||||
}
|
||||
|
|
|
@ -24,11 +24,9 @@ import android.net.Uri
|
|||
import org.mariotaku.twidere.annotation.FilterScope
|
||||
import org.mariotaku.twidere.data.fetcher.UserFavoritesFetcher
|
||||
import org.mariotaku.twidere.extension.withAppendedPath
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
import org.mariotaku.twidere.model.refresh.UserRelatedContentRefreshParam
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses
|
||||
import org.mariotaku.twidere.util.ErrorInfoStore
|
||||
import org.mariotaku.twidere.util.sync.TimelineSyncManager
|
||||
|
||||
class GetUserFavoritesTask(context: Context) : GetStatusesTask<UserRelatedContentRefreshParam>(context) {
|
||||
|
||||
|
@ -43,7 +41,4 @@ class GetUserFavoritesTask(context: Context) : GetStatusesTask<UserRelatedConten
|
|||
return UserFavoritesFetcher(params?.userKey, params?.userScreenName)
|
||||
}
|
||||
|
||||
override fun syncFetchReadPosition(manager: TimelineSyncManager, accountKeys: Array<UserKey>) {
|
||||
}
|
||||
|
||||
}
|
|
@ -25,11 +25,9 @@ import org.mariotaku.twidere.annotation.FilterScope
|
|||
import org.mariotaku.twidere.data.fetcher.StatusesFetcher
|
||||
import org.mariotaku.twidere.data.fetcher.UserMediaTimelineFetcher
|
||||
import org.mariotaku.twidere.extension.withAppendedPath
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
import org.mariotaku.twidere.model.refresh.UserRelatedContentRefreshParam
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses
|
||||
import org.mariotaku.twidere.util.ErrorInfoStore
|
||||
import org.mariotaku.twidere.util.sync.TimelineSyncManager
|
||||
|
||||
class GetUserMediaTimelineTask(context: Context) : GetStatusesTask<UserRelatedContentRefreshParam>(context) {
|
||||
|
||||
|
@ -44,7 +42,4 @@ class GetUserMediaTimelineTask(context: Context) : GetStatusesTask<UserRelatedCo
|
|||
return UserMediaTimelineFetcher(params?.userKey, params?.userScreenName)
|
||||
}
|
||||
|
||||
override fun syncFetchReadPosition(manager: TimelineSyncManager, accountKeys: Array<UserKey>) {
|
||||
}
|
||||
|
||||
}
|
|
@ -24,11 +24,9 @@ import android.net.Uri
|
|||
import org.mariotaku.twidere.annotation.FilterScope
|
||||
import org.mariotaku.twidere.data.fetcher.UserTimelineFetcher
|
||||
import org.mariotaku.twidere.extension.withAppendedPath
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
import org.mariotaku.twidere.model.refresh.UserRelatedContentRefreshParam
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses
|
||||
import org.mariotaku.twidere.util.ErrorInfoStore
|
||||
import org.mariotaku.twidere.util.sync.TimelineSyncManager
|
||||
|
||||
class GetUserMentionsTimelineTask(context: Context) : GetStatusesTask<UserRelatedContentRefreshParam>(context) {
|
||||
|
||||
|
@ -43,7 +41,4 @@ class GetUserMentionsTimelineTask(context: Context) : GetStatusesTask<UserRelate
|
|||
return UserTimelineFetcher(params?.userKey, params?.userScreenName, null)
|
||||
}
|
||||
|
||||
override fun syncFetchReadPosition(manager: TimelineSyncManager, accountKeys: Array<UserKey>) {
|
||||
}
|
||||
|
||||
}
|
|
@ -24,11 +24,9 @@ import android.net.Uri
|
|||
import org.mariotaku.twidere.annotation.FilterScope
|
||||
import org.mariotaku.twidere.data.fetcher.UserTimelineFetcher
|
||||
import org.mariotaku.twidere.extension.withAppendedPath
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
import org.mariotaku.twidere.model.refresh.UserRelatedContentRefreshParam
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses
|
||||
import org.mariotaku.twidere.util.ErrorInfoStore
|
||||
import org.mariotaku.twidere.util.sync.TimelineSyncManager
|
||||
|
||||
class GetUserTimelineTask(context: Context) : GetStatusesTask<UserRelatedContentRefreshParam>(context) {
|
||||
|
||||
|
@ -43,7 +41,4 @@ class GetUserTimelineTask(context: Context) : GetStatusesTask<UserRelatedContent
|
|||
return UserTimelineFetcher(params?.userKey, params?.userScreenName, null)
|
||||
}
|
||||
|
||||
override fun syncFetchReadPosition(manager: TimelineSyncManager, accountKeys: Array<UserKey>) {
|
||||
}
|
||||
|
||||
}
|
|
@ -21,111 +21,25 @@ package org.mariotaku.twidere.task.twitter
|
|||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import org.mariotaku.ktextension.addTo
|
||||
import org.mariotaku.microblog.library.MicroBlog
|
||||
import org.mariotaku.microblog.library.MicroBlogException
|
||||
import org.mariotaku.microblog.library.mastodon.Mastodon
|
||||
import org.mariotaku.microblog.library.twitter.model.Activity
|
||||
import org.mariotaku.microblog.library.twitter.model.InternalActivityCreator
|
||||
import org.mariotaku.microblog.library.twitter.model.Paging
|
||||
import org.mariotaku.twidere.R
|
||||
import org.mariotaku.twidere.annotation.AccountType
|
||||
import org.mariotaku.twidere.annotation.FilterScope
|
||||
import org.mariotaku.twidere.annotation.ReadPositionTag
|
||||
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.data.fetcher.ActivitiesFetcher
|
||||
import org.mariotaku.twidere.data.fetcher.activities.ActivitiesAboutMeFetcher
|
||||
import org.mariotaku.twidere.fragment.activities.InteractionsActivitiesFragment
|
||||
import org.mariotaku.twidere.model.AccountDetails
|
||||
import org.mariotaku.twidere.model.ParcelableActivity
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
import org.mariotaku.twidere.model.task.GetTimelineResult
|
||||
import org.mariotaku.twidere.model.refresh.ContentRefreshParam
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Activities
|
||||
import org.mariotaku.twidere.util.ErrorInfoStore
|
||||
import org.mariotaku.twidere.util.sync.TimelineSyncManager
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 16/2/11.
|
||||
*/
|
||||
class GetActivitiesAboutMeTask(context: Context) : GetActivitiesTask(context) {
|
||||
|
||||
override val errorInfoKey: String = ErrorInfoStore.KEY_INTERACTIONS
|
||||
override val filterScopes: Int = FilterScope.INTERACTIONS
|
||||
override val contentUri: Uri = Activities.AboutMe.CONTENT_URI
|
||||
|
||||
private val profileImageSize = context.getString(R.string.profile_image_size)
|
||||
|
||||
@Throws(MicroBlogException::class)
|
||||
override fun getActivities(account: AccountDetails, paging: Paging): GetTimelineResult<ParcelableActivity> {
|
||||
when (account.type) {
|
||||
AccountType.MASTODON -> {
|
||||
val mastodon = account.newMicroBlogInstance(context, Mastodon::class.java)
|
||||
val notifications = mastodon.getNotifications(paging)
|
||||
val userIds = notifications.flatMapTo(HashSet()) {
|
||||
val mapResult = mutableSetOf<String>()
|
||||
it?.account?.id?.addTo(mapResult)
|
||||
it.status?.account?.id?.addTo(mapResult)
|
||||
return@flatMapTo mapResult
|
||||
}
|
||||
val relationships = mastodon.batchGetRelationships(userIds)
|
||||
val activities = notifications.mapNotNull {
|
||||
val activity = it.toParcelable(account, relationships)
|
||||
if (activity.action == Activity.Action.INVALID) return@mapNotNull null
|
||||
return@mapNotNull activity
|
||||
}
|
||||
return GetTimelineResult(account, activities, activities.flatMap {
|
||||
it.sources?.toList().orEmpty()
|
||||
}, notifications.flatMapTo(HashSet()) { notification ->
|
||||
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<String>()
|
||||
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 {
|
||||
InternalActivityCreator.status(it, account.key.id).toParcelable(account,
|
||||
profileImageSize = profileImageSize)
|
||||
}
|
||||
return GetTimelineResult(account, activities, activities.flatMap {
|
||||
it.sources?.toList().orEmpty()
|
||||
}, activities.flatMap { it.extractFanfouHashtags() })
|
||||
}
|
||||
}
|
||||
val microBlog = account.newMicroBlogInstance(context, MicroBlog::class.java)
|
||||
val timeline = microBlog.getMentionsTimeline(paging)
|
||||
val activities = timeline.map {
|
||||
InternalActivityCreator.status(it, account.key.id).toParcelable(account,
|
||||
profileImageSize = profileImageSize)
|
||||
}
|
||||
return GetTimelineResult(account, activities, activities.flatMap {
|
||||
it.sources?.toList().orEmpty()
|
||||
}, timeline.flatMap {
|
||||
it.entities?.hashtags?.map { it.text }.orEmpty()
|
||||
})
|
||||
override fun getActivitiesFetcher(params: ContentRefreshParam?): ActivitiesFetcher {
|
||||
return ActivitiesAboutMeFetcher()
|
||||
}
|
||||
|
||||
override fun syncFetchReadPosition(manager: TimelineSyncManager, accountKeys: Array<UserKey>) {
|
||||
|
|
|
@ -6,23 +6,32 @@ import android.content.Context
|
|||
import android.net.Uri
|
||||
import android.support.annotation.UiThread
|
||||
import org.mariotaku.kpreferences.get
|
||||
import org.mariotaku.ktextension.addTo
|
||||
import org.mariotaku.library.objectcursor.ObjectCursor
|
||||
import org.mariotaku.microblog.library.MicroBlog
|
||||
import org.mariotaku.microblog.library.MicroBlogException
|
||||
import org.mariotaku.microblog.library.mastodon.Mastodon
|
||||
import org.mariotaku.microblog.library.twitter.model.Activity
|
||||
import org.mariotaku.microblog.library.twitter.model.InternalActivityCreator
|
||||
import org.mariotaku.microblog.library.twitter.model.Paging
|
||||
import org.mariotaku.sqliteqb.library.Expression
|
||||
import org.mariotaku.twidere.R
|
||||
import org.mariotaku.twidere.TwidereConstants.LOGTAG
|
||||
import org.mariotaku.twidere.TwidereConstants.QUERY_PARAM_NOTIFY_CHANGE
|
||||
import org.mariotaku.twidere.annotation.AccountType
|
||||
import org.mariotaku.twidere.annotation.FilterScope
|
||||
import org.mariotaku.twidere.constant.loadItemLimitKey
|
||||
import org.mariotaku.twidere.data.fetcher.ActivitiesFetcher
|
||||
import org.mariotaku.twidere.exception.AccountNotFoundException
|
||||
import org.mariotaku.twidere.extension.model.getMaxId
|
||||
import org.mariotaku.twidere.extension.model.getMaxSortId
|
||||
import org.mariotaku.twidere.extension.model.getSinceId
|
||||
import org.mariotaku.twidere.extension.api.batchGetRelationships
|
||||
import org.mariotaku.twidere.extension.model.*
|
||||
import org.mariotaku.twidere.extension.model.api.mastodon.toParcelable
|
||||
import org.mariotaku.twidere.extension.model.api.microblog.toParcelable
|
||||
import org.mariotaku.twidere.model.AccountDetails
|
||||
import org.mariotaku.twidere.model.ParcelableActivity
|
||||
import org.mariotaku.twidere.model.refresh.ContentRefreshParam
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
import org.mariotaku.twidere.model.event.GetActivitiesTaskEvent
|
||||
import org.mariotaku.twidere.model.refresh.ContentRefreshParam
|
||||
import org.mariotaku.twidere.model.task.GetTimelineResult
|
||||
import org.mariotaku.twidere.model.util.AccountUtils
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Activities
|
||||
|
@ -37,9 +46,6 @@ import org.mariotaku.twidere.util.sync.SyncTaskRunner
|
|||
import org.mariotaku.twidere.util.sync.TimelineSyncManager
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 16/1/4.
|
||||
*/
|
||||
abstract class GetActivitiesTask(
|
||||
context: Context
|
||||
) : BaseAbstractTask<ContentRefreshParam, List<Pair<GetTimelineResult<ParcelableActivity>?, Exception?>>,
|
||||
|
@ -52,6 +58,8 @@ abstract class GetActivitiesTask(
|
|||
|
||||
protected abstract val contentUri: Uri
|
||||
|
||||
private val profileImageSize = context.getString(R.string.profile_image_size)
|
||||
|
||||
override fun doLongOperation(param: ContentRefreshParam): List<Pair<GetTimelineResult<ParcelableActivity>?, Exception?>> {
|
||||
if (param.shouldAbort) return emptyList()
|
||||
val accountKeys = param.accountKeys.takeIf { it.isNotEmpty() } ?: return emptyList()
|
||||
|
@ -119,7 +127,91 @@ abstract class GetActivitiesTask(
|
|||
}
|
||||
|
||||
@Throws(MicroBlogException::class)
|
||||
protected abstract fun getActivities(account: AccountDetails, paging: Paging): GetTimelineResult<ParcelableActivity>
|
||||
protected fun getActivities(account: AccountDetails, paging: Paging): GetTimelineResult<ParcelableActivity> {
|
||||
val fetcher = getActivitiesFetcher(params)
|
||||
when (account.type) {
|
||||
AccountType.MASTODON -> {
|
||||
val mastodon = account.newMicroBlogInstance(context, Mastodon::class.java)
|
||||
val notifications = fetcher.forMastodon(account, mastodon, paging)
|
||||
val userIds = notifications.flatMapTo(HashSet()) {
|
||||
val mapResult = mutableSetOf<String>()
|
||||
it?.account?.id?.addTo(mapResult)
|
||||
it.status?.account?.id?.addTo(mapResult)
|
||||
return@flatMapTo mapResult
|
||||
}
|
||||
val relationships = mastodon.batchGetRelationships(userIds)
|
||||
val activities = notifications.mapNotNull {
|
||||
val activity = it.toParcelable(account, relationships)
|
||||
if (activity.action == Activity.Action.INVALID) return@mapNotNull null
|
||||
return@mapNotNull activity
|
||||
}
|
||||
return GetTimelineResult(account, activities, activities.flatMap {
|
||||
it.sources?.toList().orEmpty()
|
||||
}, notifications.flatMapTo(HashSet()) { notification ->
|
||||
notification.status?.tags?.map { it.name }.orEmpty()
|
||||
})
|
||||
}
|
||||
AccountType.TWITTER -> {
|
||||
val microBlog = account.newMicroBlogInstance(context, MicroBlog::class.java)
|
||||
if (account.isOfficial(context)) {
|
||||
val timeline = fetcher.forTwitterOfficial(account, microBlog, 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<String>()
|
||||
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
|
||||
})
|
||||
} else {
|
||||
val timeline = fetcher.forTwitter(account, microBlog, paging)
|
||||
val activities = timeline.map {
|
||||
InternalActivityCreator.status(it, account.key.id).toParcelable(account,
|
||||
profileImageSize = profileImageSize)
|
||||
}
|
||||
return GetTimelineResult(account, activities, activities.flatMap {
|
||||
it.sources?.toList().orEmpty()
|
||||
}, timeline.flatMap {
|
||||
it.entities?.hashtags?.map { it.text }.orEmpty()
|
||||
})
|
||||
}
|
||||
}
|
||||
AccountType.FANFOU -> {
|
||||
val microBlog = account.newMicroBlogInstance(context, MicroBlog::class.java)
|
||||
val activities = fetcher.forFanfou(account, microBlog, paging).map {
|
||||
InternalActivityCreator.status(it, account.key.id).toParcelable(account,
|
||||
profileImageSize = profileImageSize)
|
||||
}
|
||||
return GetTimelineResult(account, activities, activities.flatMap {
|
||||
it.sources?.toList().orEmpty()
|
||||
}, activities.flatMap { it.extractFanfouHashtags() })
|
||||
}
|
||||
AccountType.STATUSNET -> {
|
||||
val microBlog = account.newMicroBlogInstance(context, MicroBlog::class.java)
|
||||
val timeline = fetcher.forStatusNet(account, microBlog, paging)
|
||||
val activities = timeline.map {
|
||||
InternalActivityCreator.status(it, account.key.id).toParcelable(account,
|
||||
profileImageSize = profileImageSize)
|
||||
}
|
||||
return GetTimelineResult(account, activities, activities.flatMap {
|
||||
it.sources?.toList().orEmpty()
|
||||
}, timeline.flatMap {
|
||||
it.entities?.hashtags?.map { it.text }.orEmpty()
|
||||
})
|
||||
}
|
||||
else -> throw UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun getActivitiesFetcher(params: ContentRefreshParam?): ActivitiesFetcher
|
||||
|
||||
protected abstract fun syncFetchReadPosition(manager: TimelineSyncManager, accountKeys: Array<UserKey>)
|
||||
|
||||
|
|
|
@ -20,41 +20,36 @@
|
|||
package org.mariotaku.twidere.task.twitter
|
||||
|
||||
import android.content.Context
|
||||
import org.mariotaku.abstask.library.AbstractTask
|
||||
import org.mariotaku.microblog.library.MicroBlogException
|
||||
import nl.komponents.kovenant.Promise
|
||||
import nl.komponents.kovenant.all
|
||||
import nl.komponents.kovenant.task
|
||||
import org.mariotaku.sqliteqb.library.Expression
|
||||
import org.mariotaku.twidere.TwidereConstants.LOGTAG
|
||||
import org.mariotaku.twidere.model.SingleResponse
|
||||
import org.mariotaku.twidere.exception.NoAccountException
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.SavedSearches
|
||||
import org.mariotaku.twidere.task.PromiseTask
|
||||
import org.mariotaku.twidere.util.ContentValuesCreator
|
||||
import org.mariotaku.twidere.util.DebugLog
|
||||
import org.mariotaku.twidere.util.MicroBlogAPIFactory
|
||||
import org.mariotaku.twidere.util.content.ContentResolverUtils
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 16/2/13.
|
||||
*/
|
||||
class GetSavedSearchesTask(
|
||||
private val context: Context
|
||||
) : AbstractTask<Array<UserKey>, SingleResponse<Unit>, Any?>() {
|
||||
) : PromiseTask<Array<UserKey>, List<Unit>> {
|
||||
|
||||
override fun doLongOperation(params: Array<UserKey>): SingleResponse<Unit> {
|
||||
val cr = context.contentResolver
|
||||
for (accountKey in params) {
|
||||
val twitter = MicroBlogAPIFactory.getInstance(context, accountKey) ?: continue
|
||||
try {
|
||||
val searches = twitter.savedSearches
|
||||
val values = ContentValuesCreator.createSavedSearches(searches,
|
||||
accountKey)
|
||||
val where = Expression.equalsArgs(SavedSearches.ACCOUNT_KEY)
|
||||
val whereArgs = arrayOf(accountKey.toString())
|
||||
cr.delete(SavedSearches.CONTENT_URI, where.sql, whereArgs)
|
||||
ContentResolverUtils.bulkInsert(cr, SavedSearches.CONTENT_URI, values)
|
||||
} catch (e: MicroBlogException) {
|
||||
DebugLog.w(LOGTAG, tr = e)
|
||||
}
|
||||
override fun toPromise(param: Array<UserKey>): Promise<List<Unit>, Exception> = all(param.map { accountKey ->
|
||||
return@map task {
|
||||
val cr = context.contentResolver
|
||||
val twitter = MicroBlogAPIFactory.getInstance(context, accountKey) ?:
|
||||
throw NoAccountException()
|
||||
val searches = twitter.savedSearches
|
||||
val values = ContentValuesCreator.createSavedSearches(searches,
|
||||
accountKey)
|
||||
val where = Expression.equalsArgs(SavedSearches.ACCOUNT_KEY)
|
||||
val whereArgs = arrayOf(accountKey.toString())
|
||||
cr.delete(SavedSearches.CONTENT_URI, where.sql, whereArgs)
|
||||
ContentResolverUtils.bulkInsert(cr, SavedSearches.CONTENT_URI, values)
|
||||
return@task
|
||||
}
|
||||
return SingleResponse(Unit)
|
||||
}
|
||||
}, cancelOthersOnError = false)
|
||||
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import android.net.Uri
|
|||
import android.widget.Toast
|
||||
import com.squareup.otto.Bus
|
||||
import com.squareup.otto.Subscribe
|
||||
import nl.komponents.kovenant.Promise
|
||||
import org.mariotaku.abstask.library.TaskStarter
|
||||
import org.mariotaku.kpreferences.get
|
||||
import org.mariotaku.ktextension.mapToArray
|
||||
|
@ -245,10 +246,9 @@ class AsyncTwitterWrapper(
|
|||
TaskStarter.execute(task)
|
||||
}
|
||||
|
||||
fun getSavedSearchesAsync(accountKeys: Array<UserKey>) {
|
||||
fun getSavedSearchesAsync(accountKeys: Array<UserKey>): Promise<List<Unit>, Exception> {
|
||||
val task = GetSavedSearchesTask(context)
|
||||
task.params = accountKeys
|
||||
TaskStarter.execute(task)
|
||||
return task.toPromise(accountKeys)
|
||||
}
|
||||
|
||||
fun getSendingDraftIds(): LongArray {
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/11/6.
|
||||
*/
|
|
@ -17,25 +17,21 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.mariotaku.twidere.task.compose
|
||||
package org.mariotaku.twidere.util
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import nl.komponents.kovenant.task
|
||||
import nl.komponents.kovenant.ui.successUi
|
||||
import org.mariotaku.abstask.library.AbstractTask
|
||||
import org.mariotaku.twidere.util.Utils
|
||||
import java.lang.ref.WeakReference
|
||||
import org.mariotaku.abstask.library.TaskEngine
|
||||
|
||||
open class AbsDeleteMediaTask<Callback>(
|
||||
context: Context,
|
||||
val sources: Array<Uri>
|
||||
) : AbstractTask<Unit, BooleanArray, Callback>() {
|
||||
|
||||
private val contextRef = WeakReference(context)
|
||||
val context: Context? get() = contextRef.get()
|
||||
|
||||
override fun doLongOperation(params: Unit?): BooleanArray {
|
||||
val context = contextRef.get() ?: return kotlin.BooleanArray(sources.size) { false }
|
||||
return BooleanArray(sources.size) { Utils.deleteMedia(context, sources[it]) }
|
||||
object PromiseTaskEngine : TaskEngine() {
|
||||
override fun <Params : Any?, Result : Any?, Callback : Any?> execute(t: AbstractTask<Params, Result, Callback>?) {
|
||||
val dispatcher = getDispatcher<Params, Result, Callback>(t)
|
||||
task {
|
||||
dispatcher.invokeExecute()
|
||||
}.successUi {
|
||||
dispatcher.invokeAfterExecute(it)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -3,14 +3,13 @@ package org.mariotaku.twidere.util
|
|||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.support.annotation.StringDef
|
||||
import android.util.Log
|
||||
import com.squareup.otto.Bus
|
||||
import nl.komponents.kovenant.Promise
|
||||
import org.mariotaku.abstask.library.AbstractTask
|
||||
import org.mariotaku.abstask.library.TaskStarter
|
||||
import org.mariotaku.kpreferences.get
|
||||
import org.mariotaku.ktextension.mapToArray
|
||||
import org.mariotaku.ktextension.toNulls
|
||||
import org.mariotaku.twidere.TwidereConstants.LOGTAG
|
||||
import org.mariotaku.twidere.constant.IntentConstants.INTENT_PACKAGE_PREFIX
|
||||
import org.mariotaku.twidere.constant.dataSyncProviderInfoKey
|
||||
import org.mariotaku.twidere.constant.stopAutoRefreshWhenBatteryLowKey
|
||||
|
@ -26,10 +25,7 @@ import org.mariotaku.twidere.task.filter.RefreshLaunchPresentationsTask
|
|||
import org.mariotaku.twidere.task.statuses.GetHomeTimelineTask
|
||||
import org.mariotaku.twidere.task.twitter.GetActivitiesAboutMeTask
|
||||
import org.mariotaku.twidere.task.twitter.message.GetMessagesTask
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/1/6.
|
||||
*/
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class TaskServiceRunner(
|
||||
val context: Context,
|
||||
|
@ -38,8 +34,17 @@ class TaskServiceRunner(
|
|||
val bus: Bus
|
||||
) {
|
||||
|
||||
fun runTask(@Action action: String, callback: (Boolean) -> Unit): Boolean {
|
||||
Log.d(LOGTAG, "TaskServiceRunner run task $action")
|
||||
fun createPromise(action: String): Promise<*, Exception>? {
|
||||
return when (action) {
|
||||
ACTION_REFRESH_LAUNCH_PRESENTATIONS -> {
|
||||
RefreshFiltersSubscriptionsTask(context).toPromise(Unit)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
fun runTask(@Action action: String, timeout: Long = 0, unit: TimeUnit? = null, callback: (Boolean) -> Unit): Boolean {
|
||||
DebugLog.d(msg = "TaskServiceRunner run task $action")
|
||||
when (action) {
|
||||
ACTION_REFRESH_HOME_TIMELINE, ACTION_REFRESH_NOTIFICATIONS,
|
||||
ACTION_REFRESH_DIRECT_MESSAGES, ACTION_REFRESH_FILTERS_SUBSCRIPTIONS,
|
||||
|
@ -51,7 +56,7 @@ class TaskServiceRunner(
|
|||
}
|
||||
ACTION_SYNC_DRAFTS, ACTION_SYNC_FILTERS, ACTION_SYNC_USER_NICKNAMES, ACTION_SYNC_USER_COLORS -> {
|
||||
val runner = preferences[dataSyncProviderInfoKey]?.newSyncTaskRunner(context) ?: return false
|
||||
return runner.runTask(action, callback)
|
||||
return runner.runTask(action, timeout, unit, callback)
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
@ -96,9 +101,6 @@ class TaskServiceRunner(
|
|||
}
|
||||
return task
|
||||
}
|
||||
ACTION_REFRESH_FILTERS_SUBSCRIPTIONS -> {
|
||||
return RefreshFiltersSubscriptionsTask(context)
|
||||
}
|
||||
ACTION_REFRESH_LAUNCH_PRESENTATIONS -> {
|
||||
return RefreshLaunchPresentationsTask(context)
|
||||
}
|
||||
|
@ -151,11 +153,12 @@ class TaskServiceRunner(
|
|||
const val ACTION_SYNC_FILTERS = INTENT_PACKAGE_PREFIX + "SYNC_FILTERS"
|
||||
@Action
|
||||
const val ACTION_SYNC_USER_NICKNAMES = INTENT_PACKAGE_PREFIX + "SYNC_USER_NICKNAMES"
|
||||
|
||||
@Action
|
||||
const val ACTION_SYNC_USER_COLORS = INTENT_PACKAGE_PREFIX + "SYNC_USER_COLORS"
|
||||
|
||||
val ACTIONS_SYNC = arrayOf(ACTION_SYNC_DRAFTS, ACTION_SYNC_FILTERS, ACTION_SYNC_USER_COLORS,
|
||||
ACTION_SYNC_USER_NICKNAMES)
|
||||
|
||||
}
|
||||
|
||||
data class SyncFinishedEvent(val syncType: String, val success: Boolean)
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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
|
||||
|
|
@ -9,12 +9,9 @@ import org.mariotaku.twidere.util.UserColorNameManager
|
|||
import org.mariotaku.twidere.util.dagger.GeneralComponent
|
||||
import java.lang.Exception
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/1/3.
|
||||
*/
|
||||
|
||||
abstract class SyncTaskRunner(val context: Context) {
|
||||
@Inject
|
||||
protected lateinit var userColorNameManager: UserColorNameManager
|
||||
|
@ -35,7 +32,7 @@ abstract class SyncTaskRunner(val context: Context) {
|
|||
*/
|
||||
protected abstract fun onRunningTask(action: String, callback: ((Boolean) -> Unit)): Boolean
|
||||
|
||||
fun runTask(action: String, callback: ((Boolean) -> Unit)? = null): Boolean {
|
||||
fun runTask(action: String, timeout: Long = 0, unit: TimeUnit? = null, callback: ((Boolean) -> Unit)? = null): Boolean {
|
||||
val syncType = SyncTaskRunner.getSyncType(action) ?: return false
|
||||
if (!syncPreferences.isSyncEnabled(syncType)) return false
|
||||
return onRunningTask(action) { success ->
|
||||
|
|
|
@ -24,23 +24,21 @@ import android.graphics.*
|
|||
import android.graphics.drawable.Drawable
|
||||
import android.support.v4.content.ContextCompat
|
||||
import android.text.format.DateUtils
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.RadioButton
|
||||
import android.widget.TextView
|
||||
import kotlinx.android.synthetic.main.layout_twitter_card_poll.view.*
|
||||
import nl.komponents.kovenant.task
|
||||
import nl.komponents.kovenant.then
|
||||
import nl.komponents.kovenant.ui.successUi
|
||||
import org.mariotaku.abstask.library.AbstractTask
|
||||
import org.mariotaku.abstask.library.TaskStarter
|
||||
import org.mariotaku.ktextension.spannable
|
||||
import org.mariotaku.ktextension.toLongOr
|
||||
import org.mariotaku.microblog.library.MicroBlogException
|
||||
import org.mariotaku.ktextension.weak
|
||||
import org.mariotaku.microblog.library.twitter.TwitterCaps
|
||||
import org.mariotaku.microblog.library.twitter.model.CardDataMap
|
||||
import org.mariotaku.twidere.Constants.LOGTAG
|
||||
import org.mariotaku.twidere.R
|
||||
import org.mariotaku.twidere.exception.NoAccountException
|
||||
import org.mariotaku.twidere.extension.model.*
|
||||
import org.mariotaku.twidere.model.ParcelableCardEntity
|
||||
import org.mariotaku.twidere.model.ParcelableStatus
|
||||
|
@ -49,18 +47,15 @@ import org.mariotaku.twidere.util.MicroBlogAPIFactory
|
|||
import org.mariotaku.twidere.util.TwitterCardUtils
|
||||
import org.mariotaku.twidere.util.support.ViewSupport
|
||||
import org.mariotaku.twidere.view.ContainerView
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 15/12/20.
|
||||
*/
|
||||
class CardPollViewController : ContainerView.ViewController() {
|
||||
|
||||
private lateinit var status: ParcelableStatus
|
||||
private var fetchedCard: ParcelableCardEntity? = null
|
||||
private val card: ParcelableCardEntity
|
||||
get() = fetchedCard ?: status.card!!
|
||||
private var clickedChoice: Boolean = false
|
||||
private val card: ParcelableCardEntity?
|
||||
get() = fetchedCard ?: status.card
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
@ -73,6 +68,7 @@ class CardPollViewController : ContainerView.ViewController() {
|
|||
}
|
||||
|
||||
private fun initChoiceView() {
|
||||
val card = this.card ?: return
|
||||
val choicesCount = TwitterCardUtils.getChoicesCount(card)
|
||||
val inflater = LayoutInflater.from(context)
|
||||
|
||||
|
@ -90,11 +86,13 @@ class CardPollViewController : ContainerView.ViewController() {
|
|||
}
|
||||
|
||||
private fun loadCardPoll() {
|
||||
val weakThis = WeakReference(this)
|
||||
val status = this.status
|
||||
val card = this.card ?: return
|
||||
val weakThis by weak(this)
|
||||
task {
|
||||
val vc = weakThis.get() ?: throw IllegalStateException()
|
||||
val card = vc.card
|
||||
val details = AccountUtils.getAccountDetails(AccountManager.get(vc.context), card.account_key, true)!!
|
||||
val vc = weakThis ?: throw IllegalStateException()
|
||||
val details = AccountUtils.getAccountDetails(AccountManager.get(vc.context),
|
||||
card.account_key, true) ?: throw NoAccountException()
|
||||
val caps = details.newMicroBlogInstance(vc.context, cls = TwitterCaps::class.java)
|
||||
val params = CardDataMap()
|
||||
params.putString("card_uri", card.url)
|
||||
|
@ -106,7 +104,7 @@ class CardPollViewController : ContainerView.ViewController() {
|
|||
}
|
||||
return@task cardResponse.toParcelable(details.key, details.type)
|
||||
}.successUi { data ->
|
||||
weakThis.get()?.displayPoll(data, status)
|
||||
weakThis?.displayPoll(data, status)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,54 +124,13 @@ class CardPollViewController : ContainerView.ViewController() {
|
|||
votesSum += card.getAsInteger("choice${choiceIndex}_count", 0)
|
||||
}
|
||||
|
||||
val clickListener = object : View.OnClickListener {
|
||||
private var clickedChoice: Boolean = false
|
||||
|
||||
override fun onClick(v: View) {
|
||||
if (hasChoice || clickedChoice) return
|
||||
for (i in 0 until view.pollContainer.childCount) {
|
||||
val pollItem = view.pollContainer.getChildAt(i)
|
||||
pollItem.isClickable = false
|
||||
clickedChoice = true
|
||||
val choiceRadioButton: RadioButton = pollItem.findViewById(R.id.choice_button)
|
||||
val checked = v === pollItem
|
||||
choiceRadioButton.isChecked = checked
|
||||
if (checked) {
|
||||
val cardData = CardDataMap()
|
||||
cardData.putLong("original_tweet_id", status.id.toLongOr(-1L))
|
||||
cardData.putString("card_uri", card.url)
|
||||
cardData.putString("cards_platform", MicroBlogAPIFactory.CARDS_PLATFORM_ANDROID_12)
|
||||
cardData.putString("response_card_name", card.name)
|
||||
cardData.putString("selected_choice", (i + 1).toString())
|
||||
val task = object : AbstractTask<CardDataMap, ParcelableCardEntity, CardPollViewController>() {
|
||||
|
||||
override fun afterExecute(callback: CardPollViewController?, results: ParcelableCardEntity?) {
|
||||
results ?: return
|
||||
callback?.displayAndReloadPoll(results, status)
|
||||
}
|
||||
|
||||
override fun doLongOperation(cardDataMap: CardDataMap): ParcelableCardEntity? {
|
||||
val details = AccountUtils.getAccountDetails(AccountManager.get(context),
|
||||
card.account_key, true) ?: return null
|
||||
val caps = details.newMicroBlogInstance(context, cls = TwitterCaps::class.java)
|
||||
try {
|
||||
val cardEntity = caps.sendPassThrough(cardDataMap).card ?: run {
|
||||
return null
|
||||
}
|
||||
return cardEntity.toParcelable(card.account_key, details.type)
|
||||
} catch (e: MicroBlogException) {
|
||||
Log.w(LOGTAG, e)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
task.callback = this@CardPollViewController
|
||||
task.params = cardData
|
||||
TaskStarter.execute(task)
|
||||
}
|
||||
}
|
||||
}
|
||||
val clickListener = View.OnClickListener click@ { v ->
|
||||
if (hasChoice || clickedChoice) return@click
|
||||
clickedChoice = true
|
||||
val i = view.pollContainer.indexOfChild(v)
|
||||
if (i < 0) return@click
|
||||
v.isClickable = false
|
||||
submitChoice(i + 1)
|
||||
}
|
||||
|
||||
val color = ContextCompat.getColor(context, R.color.material_light_blue_a200)
|
||||
|
@ -218,6 +175,28 @@ class CardPollViewController : ContainerView.ViewController() {
|
|||
view.pollSummary.spannable = context.getString(R.string.poll_summary_format, nVotes, timeLeft)
|
||||
}
|
||||
|
||||
private fun submitChoice(which: Int) {
|
||||
val status = this.status
|
||||
val card = this.card ?: return
|
||||
val cardData = CardDataMap()
|
||||
cardData.putLong("original_tweet_id", status.id.toLongOr(-1L))
|
||||
cardData.putString("card_uri", card.url)
|
||||
cardData.putString("cards_platform", MicroBlogAPIFactory.CARDS_PLATFORM_ANDROID_12)
|
||||
cardData.putString("response_card_name", card.name)
|
||||
cardData.putString("selected_choice", which.toString())
|
||||
val weakThis by weak(this)
|
||||
task {
|
||||
val vc = weakThis ?: throw InterruptedException()
|
||||
val details = AccountUtils.getAccountDetails(AccountManager.get(vc.context),
|
||||
card.account_key, true) ?: throw NoAccountException()
|
||||
val caps = details.newMicroBlogInstance(vc.context, cls = TwitterCaps::class.java)
|
||||
val cardEntity = caps.sendPassThrough(cardData).card
|
||||
return@task cardEntity.toParcelable(card.account_key, details.type)
|
||||
}.then { result ->
|
||||
weakThis?.displayAndReloadPoll(result, status)
|
||||
}
|
||||
}
|
||||
|
||||
private class PercentDrawable internal constructor(
|
||||
private val percent: Float,
|
||||
private val radius: Float,
|
||||
|
|
Loading…
Reference in New Issue