diff --git a/build.gradle b/build.gradle index 4d72face7..976917ddd 100644 --- a/build.gradle +++ b/build.gradle @@ -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', diff --git a/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/model/CardResponse.java b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/model/CardResponse.java index ba80fdd65..f4f3259ee 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/model/CardResponse.java +++ b/twidere.component.common/src/main/java/org/mariotaku/microblog/library/twitter/model/CardResponse.java @@ -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; diff --git a/twidere/src/main/java/org/mariotaku/abstask/library/ManualTaskStarter.java b/twidere/src/main/java/org/mariotaku/abstask/library/ManualTaskStarter.java index 6801b6219..b14136c40 100644 --- a/twidere/src/main/java/org/mariotaku/abstask/library/ManualTaskStarter.java +++ b/twidere/src/main/java/org/mariotaku/abstask/library/ManualTaskStarter.java @@ -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 void invokeAfterExecute(AbstractTask task, Result result) { - task.invokeAfterExecute(result); } @WorkerThread public static Result invokeExecute(AbstractTask task) { - return task.invokeExecute(); + return null; } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/model/util/ParcelableLocationUtils.java b/twidere/src/main/java/org/mariotaku/twidere/model/util/ParcelableLocationUtils.java index 647961820..babaceccd 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/model/util/ParcelableLocationUtils.java +++ b/twidere/src/main/java/org/mariotaku/twidere/model/util/ParcelableLocationUtils.java @@ -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)); } diff --git a/twidere/src/main/kotlin/org/mariotaku/ktextension/PromiseArrayCombine.kt b/twidere/src/main/kotlin/org/mariotaku/ktextension/PromiseExtensions.kt similarity index 71% rename from twidere/src/main/kotlin/org/mariotaku/ktextension/PromiseArrayCombine.kt rename to twidere/src/main/kotlin/org/mariotaku/ktextension/PromiseExtensions.kt index 2adbe1218..5a3df59ad 100644 --- a/twidere/src/main/kotlin/org/mariotaku/ktextension/PromiseArrayCombine.kt +++ b/twidere/src/main/kotlin/org/mariotaku/ktextension/PromiseExtensions.kt @@ -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 Promise.deadline(time: Long, unit: TimeUnit): Promise { + 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 combine(promises: List>): Promise, E> { return concreteCombine(promises) } @@ -50,3 +62,9 @@ fun concreteCombine(promises: List>): Promise, E> { return deferred.promise } + +private object WatchdogHandler : Handler() + +class DeadlineException : Exception() { + +} \ No newline at end of file diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/ComposeActivity.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/ComposeActivity.kt index ad39dce2b..6c06da718 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/ComposeActivity.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/ComposeActivity.kt @@ -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) { + 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 - ) : 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, ComposeActivity>() { - - override fun doLongOperation(location: ParcelableLocation): List
? { - 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
?) { - 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 { diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/presentation/ToggleRefreshActivity.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/presentation/ToggleRefreshActivity.kt index 8cc72060d..7f3575152 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/activity/presentation/ToggleRefreshActivity.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/activity/presentation/ToggleRefreshActivity.kt @@ -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) diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/alias/MastodonAliases.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/alias/MastodonAliases.kt index f868334e3..6da63a4c3 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/alias/MastodonAliases.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/alias/MastodonAliases.kt @@ -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 \ No newline at end of file +typealias MastodonStatusUpdate = StatusUpdate +typealias MastodonNotification = Notification \ No newline at end of file diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/data/fetcher/ActivitiesFetcher.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/data/fetcher/ActivitiesFetcher.kt new file mode 100644 index 000000000..bedf6d01e --- /dev/null +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/data/fetcher/ActivitiesFetcher.kt @@ -0,0 +1,48 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2017 Mariotaku Lee + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.mariotaku.twidere.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 + = throw APINotSupportedException(account.type) + + fun forTwitter(account: AccountDetails, twitter: MicroBlog, paging: Paging): List + = throw APINotSupportedException(account.type) + + fun forStatusNet(account: AccountDetails, statusNet: MicroBlog, paging: Paging): List + = throw APINotSupportedException(account.type) + + fun forFanfou(account: AccountDetails, fanfou: MicroBlog, paging: Paging): List + = throw APINotSupportedException(account.type) + + fun forMastodon(account: AccountDetails, mastodon: Mastodon, paging: Paging): LinkHeaderList + = throw APINotSupportedException(account.type) +} \ No newline at end of file diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/data/fetcher/activities/ActivitiesAboutMeFetcher.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/data/fetcher/activities/ActivitiesAboutMeFetcher.kt new file mode 100644 index 000000000..8d164b510 --- /dev/null +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/data/fetcher/activities/ActivitiesAboutMeFetcher.kt @@ -0,0 +1,52 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2017 Mariotaku Lee + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.mariotaku.twidere.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 { + return twitter.getActivitiesAboutMe(paging) + } + + override fun forTwitter(account: AccountDetails, twitter: MicroBlog, paging: Paging): List { + return twitter.getMentionsTimeline(paging) + } + + override fun forStatusNet(account: AccountDetails, statusNet: MicroBlog, paging: Paging): List { + return statusNet.getMentionsTimeline(paging) + } + + override fun forFanfou(account: AccountDetails, fanfou: MicroBlog, paging: Paging): List { + return fanfou.getMentions(paging) + } + + override fun forMastodon(account: AccountDetails, mastodon: Mastodon, paging: Paging): LinkHeaderList { + return mastodon.getNotifications(paging) + } +} diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/cache/CacheUsersStatusesTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/data/syncher/TimelinePositionSyncher.kt similarity index 54% rename from twidere/src/main/kotlin/org/mariotaku/twidere/task/cache/CacheUsersStatusesTask.kt rename to twidere/src/main/kotlin/org/mariotaku/twidere/data/syncher/TimelinePositionSyncher.kt index 034d6d9e1..8f5611e2d 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/cache/CacheUsersStatusesTask.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/data/syncher/TimelinePositionSyncher.kt @@ -1,39 +1,26 @@ /* - * Twidere - Twitter client for Android - * - * Copyright (C) 2012-2014 Mariotaku Lee - * + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2017 Mariotaku Lee + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.mariotaku.twidere.task.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 -) : AbstractTask() { - - - override fun doLongOperation(params: Any?) { - - } - +interface TimelinePositionSyncher { + fun get(accountKeys: Array) } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/FiltersSubscriptionExtensions.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/FiltersSubscriptionExtensions.kt index 35f790117..cc166b68f 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/FiltersSubscriptionExtensions.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/FiltersSubscriptionExtensions.kt @@ -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 diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/ParcelableCardEntityExtensions.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/ParcelableCardEntityExtensions.kt index e4b26e71d..089c83ea9 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/ParcelableCardEntityExtensions.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/model/ParcelableCardEntityExtensions.kt @@ -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 diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AbsContentRecyclerViewFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AbsContentRecyclerViewFragment.kt index 4aed666c7..0bd48808e 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AbsContentRecyclerViewFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/AbsContentRecyclerViewFragment.kt @@ -283,7 +283,7 @@ abstract class AbsContentRecyclerViewFragment fragment.childFragmentManager.dismissDialogFragment(DIALOG_FRAGMENT_TAG) fragment.activity.finish() diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/filter/FiltersSubscriptionsFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/filter/FiltersSubscriptionsFragment.kt index 9790131fe..d5f66022e 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/filter/FiltersSubscriptionsFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/filter/FiltersSubscriptionsFragment.kt @@ -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 } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/message/MessagesConversationFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/message/MessagesConversationFragment.kt index 8bb30e36c..069f0c114 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/message/MessagesConversationFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/message/MessagesConversationFragment.kt @@ -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 - ) : AbsDeleteMediaTask(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( diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/loader/ParcelableUserLoader.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/loader/ParcelableUserLoader.kt index 1b89676cb..11981eb4a 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/loader/ParcelableUserLoader.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/loader/ParcelableUserLoader.kt @@ -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(EXTRA_ACCOUNT) if (account != null) { val task = UpdateAccountInfoTask(context) - task.params = Pair(account, user) - TaskStarter.execute(task) + task.toPromise(Pair(account, user)) } } } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/service/JobTaskService.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/service/JobTaskService.kt index 1518e27d4..8155f5a43 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/service/JobTaskService.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/service/JobTaskService.kt @@ -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 } + } + } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/service/LegacyTaskService.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/service/LegacyTaskService.kt index b0757c666..1803a2a7c 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/service/LegacyTaskService.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/service/LegacyTaskService.kt @@ -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 { diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/BaseAbstractTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/BaseAbstractTask.kt index 3a9a8b1f6..67e17be5e 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/BaseAbstractTask.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/BaseAbstractTask.kt @@ -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(val context: Context) : AbstractTask() { @Inject @@ -73,4 +69,5 @@ abstract class BaseAbstractTask(val context: Context) @Suppress("UNCHECKED_CAST") GeneralComponent.get(context).inject(this as BaseAbstractTask) } + } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/PromiseTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/PromiseTask.kt new file mode 100644 index 000000000..ce5be1ff6 --- /dev/null +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/PromiseTask.kt @@ -0,0 +1,26 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2017 Mariotaku Lee + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.mariotaku.twidere.task + +import nl.komponents.kovenant.Promise + +interface PromiseTask { + fun toPromise(param: P): Promise +} \ No newline at end of file diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/UpdateAccountInfoTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/UpdateAccountInfoTask.kt index eef505b4d..f6d42700f 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/UpdateAccountInfoTask.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/UpdateAccountInfoTask.kt @@ -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, Unit, Unit?>() { +) : PromiseTask, Unit> { - override fun doLongOperation(params: Pair) { + override fun toPromise(param: Pair): Promise { 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() - 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 } } } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/filter/RefreshFiltersSubscriptionsTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/filter/RefreshFiltersSubscriptionsTask.kt index 5ece08354..d3f6a1c94 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/filter/RefreshFiltersSubscriptionsTask.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/filter/RefreshFiltersSubscriptionsTask.kt @@ -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>() { - - override fun doLongOperation(param: Unit?): Boolean { +class RefreshFiltersSubscriptionsTask(val context: Context) : PromiseTask { + override fun toPromise(param: Unit): Promise = task { val resolver = context.contentResolver val sourceIds = ArrayList() - 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)?, result: Boolean) { - callback?.invoke(result) + Thread.sleep(1000) } private fun updateUserItems(resolver: ContentResolver, items: List?, sourceId: Long) { diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/filter/RefreshLaunchPresentationsTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/filter/RefreshLaunchPresentationsTask.kt index 571150f4c..93ea61d86 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/filter/RefreshLaunchPresentationsTask.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/filter/RefreshLaunchPresentationsTask.kt @@ -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>(context) { override fun doLongOperation(params: Unit?): Boolean { val builder = HttpRequest.Builder() @@ -54,6 +51,10 @@ class RefreshLaunchPresentationsTask(context: Context) : BaseAbstractTask Unit)?, result: Boolean) { + callback?.invoke(result) + } + companion object { const val JSON_CACHE_KEY = "launch_presentations" } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetGroupTimelineTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetGroupTimelineTask.kt index ca9c0d01b..f3296e682 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetGroupTimelineTask.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetGroupTimelineTask.kt @@ -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(context) { @@ -44,7 +42,4 @@ class GetGroupTimelineTask(context: Context) : GetStatusesTask) { - } - } \ No newline at end of file diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetHomeTimelineTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetHomeTimelineTask.kt index cc5f207c4..e84eb4e47 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetHomeTimelineTask.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetHomeTimelineTask.kt @@ -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(context) { @@ -41,8 +37,4 @@ class GetHomeTimelineTask(context: Context) : GetStatusesTask) { - val tag = HomeTimelineFragment.getTimelineSyncTag(accountKeys) - manager.fetchSingle(ReadPositionTag.HOME_TIMELINE, tag) - } } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetListTimelineTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetListTimelineTask.kt index 667f5b891..7551699ab 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetListTimelineTask.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetListTimelineTask.kt @@ -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(context) { @@ -44,7 +42,4 @@ class GetListTimelineTask(context: Context) : GetStatusesTask) { - } - } \ No newline at end of file diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetMediaSearchTimelineTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetMediaSearchTimelineTask.kt index 48cbd130b..6aeabe165 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetMediaSearchTimelineTask.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetMediaSearchTimelineTask.kt @@ -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(context) { @@ -44,7 +42,4 @@ class GetMediaSearchTimelineTask(context: Context) : GetStatusesTask) { - } - } \ No newline at end of file diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetNetworkPublicTimelineTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetNetworkPublicTimelineTask.kt index dd4d27386..45dbf7bec 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetNetworkPublicTimelineTask.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetNetworkPublicTimelineTask.kt @@ -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(context) { @@ -41,8 +37,4 @@ class GetNetworkPublicTimelineTask(context: Context) : GetStatusesTask) { - val tag = NetworkPublicTimelineFragment.getTimelineSyncTag(accountKeys) - manager.fetchSingle(ReadPositionTag.NETWORK_PUBLIC_TIMELINE, tag) - } } \ No newline at end of file diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetPublicTimelineTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetPublicTimelineTask.kt index db43266e9..4b017688c 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetPublicTimelineTask.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetPublicTimelineTask.kt @@ -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(context) { @@ -41,8 +37,4 @@ class GetPublicTimelineTask(context: Context) : GetStatusesTask) { - val tag = PublicTimelineFragment.getTimelineSyncTag(accountKeys) - manager.fetchSingle(ReadPositionTag.PUBLIC_TIMELINE, tag) - } } \ No newline at end of file diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetSearchTimelineTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetSearchTimelineTask.kt index 60b4bb1a1..3a46fa5fd 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetSearchTimelineTask.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetSearchTimelineTask.kt @@ -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(context) { @@ -44,7 +42,4 @@ class GetSearchTimelineTask(context: Context) : GetStatusesTask) { - } - } \ No newline at end of file diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetStatusesTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetStatusesTask.kt index cd74979da..ed0ac67ee 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetStatusesTask.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetStatusesTask.kt @@ -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

( ) : BaseAbstractTask?, 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

( protected abstract val errorInfoKey: String - override fun doLongOperation(param: P): List?, Exception?>> { + private val profileImageSize = context.getString(R.string.profile_image_size) + + override final fun doLongOperation(param: P): List?, 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

( protected abstract fun getStatusesFetcher(params: P?): StatusesFetcher - protected abstract fun syncFetchReadPosition(manager: TimelineSyncManager, accountKeys: Array) + protected open fun getPositionSyncher(manager: TimelineSyncManager): TimelinePositionSyncher? = null + + private fun syncFetchReadPosition(manager: TimelineSyncManager, accountKeys: Array) { + val fetcher = getPositionSyncher(manager) ?: return + fetcher.get(accountKeys) + } private fun extractMicroBlogUsers(timeline: List, account: AccountDetails): List { return timeline.flatMap { status -> @@ -319,13 +325,12 @@ abstract class GetStatusesTask

( 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() } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetUserFavoritesTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetUserFavoritesTask.kt index 855f15feb..c773bb4a7 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetUserFavoritesTask.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetUserFavoritesTask.kt @@ -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(context) { @@ -43,7 +41,4 @@ class GetUserFavoritesTask(context: Context) : GetStatusesTask) { - } - } \ No newline at end of file diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetUserMediaTimelineTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetUserMediaTimelineTask.kt index 9be465711..988e0ed13 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetUserMediaTimelineTask.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetUserMediaTimelineTask.kt @@ -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(context) { @@ -44,7 +42,4 @@ class GetUserMediaTimelineTask(context: Context) : GetStatusesTask) { - } - } \ No newline at end of file diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetUserMentionsTimelineTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetUserMentionsTimelineTask.kt index 0a280dd52..b13118e89 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetUserMentionsTimelineTask.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetUserMentionsTimelineTask.kt @@ -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(context) { @@ -43,7 +41,4 @@ class GetUserMentionsTimelineTask(context: Context) : GetStatusesTask) { - } - } \ No newline at end of file diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetUserTimelineTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetUserTimelineTask.kt index 27eb386ef..b720d3105 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetUserTimelineTask.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/statuses/GetUserTimelineTask.kt @@ -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(context) { @@ -43,7 +41,4 @@ class GetUserTimelineTask(context: Context) : GetStatusesTask) { - } - } \ No newline at end of file diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/GetActivitiesAboutMeTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/GetActivitiesAboutMeTask.kt index dea1028c2..5f4489567 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/GetActivitiesAboutMeTask.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/GetActivitiesAboutMeTask.kt @@ -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 { - 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() - 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() - 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) { diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/GetActivitiesTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/GetActivitiesTask.kt index 01bcd8b5f..1357313c1 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/GetActivitiesTask.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/GetActivitiesTask.kt @@ -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?, 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?, 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 + protected fun getActivities(account: AccountDetails, paging: Paging): GetTimelineResult { + 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() + 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() + 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) diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/GetSavedSearchesTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/GetSavedSearchesTask.kt index daf2921a4..409ddb601 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/GetSavedSearchesTask.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/GetSavedSearchesTask.kt @@ -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, SingleResponse, Any?>() { +) : PromiseTask, List> { - override fun doLongOperation(params: Array): SingleResponse { - 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): Promise, 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) + } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/AsyncTwitterWrapper.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/AsyncTwitterWrapper.kt index 02e3d0007..6eab845e0 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/AsyncTwitterWrapper.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/AsyncTwitterWrapper.kt @@ -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) { + fun getSavedSearchesAsync(accountKeys: Array): Promise, Exception> { val task = GetSavedSearchesTask(context) - task.params = accountKeys - TaskStarter.execute(task) + return task.toPromise(accountKeys) } fun getSendingDraftIds(): LongArray { diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/MediaFunctions.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/MediaFunctions.kt new file mode 100644 index 000000000..77be7d827 --- /dev/null +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/MediaFunctions.kt @@ -0,0 +1,24 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2017 Mariotaku Lee + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.mariotaku.twidere.util + +/** + * Created by mariotaku on 2017/11/6. + */ diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/task/compose/AbsDeleteMediaTask.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/PromiseTaskEngine.kt similarity index 55% rename from twidere/src/main/kotlin/org/mariotaku/twidere/task/compose/AbsDeleteMediaTask.kt rename to twidere/src/main/kotlin/org/mariotaku/twidere/util/PromiseTaskEngine.kt index 802f41490..22db37098 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/task/compose/AbsDeleteMediaTask.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/PromiseTaskEngine.kt @@ -17,25 +17,21 @@ * along with this program. If not, see . */ -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( - context: Context, - val sources: Array -) : AbstractTask() { - - 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 execute(t: AbstractTask?) { + val dispatcher = getDispatcher(t) + task { + dispatcher.invokeExecute() + }.successUi { + dispatcher.invokeAfterExecute(it) + } } -} \ No newline at end of file +} diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/TaskServiceRunner.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/TaskServiceRunner.kt index 9b77a9f87..4665ded9e 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/TaskServiceRunner.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/TaskServiceRunner.kt @@ -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) diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/TimelineRefreshPromises.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/TimelineRefreshPromises.kt new file mode 100644 index 000000000..af1d3c0eb --- /dev/null +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/TimelineRefreshPromises.kt @@ -0,0 +1,21 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2017 Mariotaku Lee + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.mariotaku.twidere.util + diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/sync/SyncTaskRunner.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/sync/SyncTaskRunner.kt index 3001cfb0d..7fb6d60a6 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/sync/SyncTaskRunner.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/sync/SyncTaskRunner.kt @@ -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 -> diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/view/controller/twitter/card/CardPollViewController.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/view/controller/twitter/card/CardPollViewController.kt index 68b99ef0d..e497078ed 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/view/controller/twitter/card/CardPollViewController.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/view/controller/twitter/card/CardPollViewController.kt @@ -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() { - - 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,