This commit is contained in:
Mariotaku Lee 2017-01-05 21:08:49 +08:00
parent 55b1631136
commit 5afc2855b5
20 changed files with 145 additions and 126 deletions

View File

@ -72,11 +72,6 @@ public interface IntentConstants {
String BROADCAST_NOTIFICATION_DELETED = INTENT_PACKAGE_PREFIX + "NOTIFICATION_DELETED"; String BROADCAST_NOTIFICATION_DELETED = INTENT_PACKAGE_PREFIX + "NOTIFICATION_DELETED";
String ACTION_REFRESH_HOME_TIMELINE = INTENT_PACKAGE_PREFIX + "REFRESH_HOME_TIMELINE";
String ACTION_REFRESH_NOTIFICATIONS = INTENT_PACKAGE_PREFIX + "REFRESH_NOTIFICATIONS";
String ACTION_REFRESH_DIRECT_MESSAGES = INTENT_PACKAGE_PREFIX + "REFRESH_DIRECT_MESSAGES";
String ACTION_REFRESH_TRENDS = INTENT_PACKAGE_PREFIX + "REFRESH_TRENDS";
String EXTRA_LATITUDE = "latitude"; String EXTRA_LATITUDE = "latitude";
String EXTRA_LONGITUDE = "longitude"; String EXTRA_LONGITUDE = "longitude";
String EXTRA_URI = "uri"; String EXTRA_URI = "uri";

View File

@ -482,11 +482,11 @@
</activity> </activity>
<service <service
android:name=".service.RefreshService" android:name=".service.LegacyTaskService"
android:enabled="@bool/use_legacy_refresh_service" android:enabled="@bool/use_legacy_refresh_service"
android:label="@string/label_refresh_service"/> android:label="@string/label_refresh_service"/>
<service <service
android:name=".service.JobRefreshService" android:name=".service.JobTaskService"
android:enabled="@bool/use_job_refresh_service" android:enabled="@bool/use_job_refresh_service"
android:exported="true" android:exported="true"
android:label="@string/label_refresh_service" android:label="@string/label_refresh_service"
@ -495,7 +495,7 @@
android:name=".service.StreamingService" android:name=".service.StreamingService"
android:label="@string/label_streaming_service"/> android:label="@string/label_streaming_service"/>
<service <service
android:name=".service.BackgroundOperationService" android:name=".service.LengthyOperationsService"
android:label="@string/label_background_operation_service"/> android:label="@string/label_background_operation_service"/>
<service <service
android:name=".service.AccountAuthenticatorService" android:name=".service.AccountAuthenticatorService"

View File

@ -88,6 +88,7 @@ import org.mariotaku.twidere.model.DraftCursorIndices;
import org.mariotaku.twidere.model.ParcelableActivity; import org.mariotaku.twidere.model.ParcelableActivity;
import org.mariotaku.twidere.model.ParcelableActivityCursorIndices; import org.mariotaku.twidere.model.ParcelableActivityCursorIndices;
import org.mariotaku.twidere.model.ParcelableDirectMessageCursorIndices; import org.mariotaku.twidere.model.ParcelableDirectMessageCursorIndices;
import org.mariotaku.twidere.model.ParcelableStatusCursorIndices;
import org.mariotaku.twidere.model.ParcelableUser; import org.mariotaku.twidere.model.ParcelableUser;
import org.mariotaku.twidere.model.SpanItem; import org.mariotaku.twidere.model.SpanItem;
import org.mariotaku.twidere.model.StringLongPair; import org.mariotaku.twidere.model.StringLongPair;
@ -113,7 +114,7 @@ import org.mariotaku.twidere.provider.TwidereDataStore.Statuses;
import org.mariotaku.twidere.provider.TwidereDataStore.Suggestions; import org.mariotaku.twidere.provider.TwidereDataStore.Suggestions;
import org.mariotaku.twidere.provider.TwidereDataStore.UnreadCounts; import org.mariotaku.twidere.provider.TwidereDataStore.UnreadCounts;
import org.mariotaku.twidere.receiver.NotificationReceiver; import org.mariotaku.twidere.receiver.NotificationReceiver;
import org.mariotaku.twidere.service.BackgroundOperationService; import org.mariotaku.twidere.service.LengthyOperationsService;
import org.mariotaku.twidere.util.ActivityTracker; import org.mariotaku.twidere.util.ActivityTracker;
import org.mariotaku.twidere.util.AsyncTwitterWrapper; import org.mariotaku.twidere.util.AsyncTwitterWrapper;
import org.mariotaku.twidere.util.DataStoreFunctionsKt; import org.mariotaku.twidere.util.DataStoreFunctionsKt;
@ -561,14 +562,14 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
nb.setAutoCancel(true); nb.setAutoCancel(true);
nb.setWhen(System.currentTimeMillis()); nb.setWhen(System.currentTimeMillis());
nb.setSmallIcon(R.drawable.ic_stat_draft); nb.setSmallIcon(R.drawable.ic_stat_draft);
final Intent discardIntent = new Intent(context, BackgroundOperationService.class); final Intent discardIntent = new Intent(context, LengthyOperationsService.class);
discardIntent.setAction(INTENT_ACTION_DISCARD_DRAFT); discardIntent.setAction(INTENT_ACTION_DISCARD_DRAFT);
final Uri draftUri = Uri.withAppendedPath(Drafts.CONTENT_URI, String.valueOf(draftId)); final Uri draftUri = Uri.withAppendedPath(Drafts.CONTENT_URI, String.valueOf(draftId));
discardIntent.setData(draftUri); discardIntent.setData(draftUri);
nb.addAction(R.drawable.ic_action_delete, context.getString(R.string.discard), PendingIntent.getService(context, 0, nb.addAction(R.drawable.ic_action_delete, context.getString(R.string.discard), PendingIntent.getService(context, 0,
discardIntent, PendingIntent.FLAG_ONE_SHOT)); discardIntent, PendingIntent.FLAG_ONE_SHOT));
final Intent sendIntent = new Intent(context, BackgroundOperationService.class); final Intent sendIntent = new Intent(context, LengthyOperationsService.class);
sendIntent.setAction(INTENT_ACTION_SEND_DRAFT); sendIntent.setAction(INTENT_ACTION_SEND_DRAFT);
sendIntent.setData(draftUri); sendIntent.setData(draftUri);
nb.addAction(R.drawable.ic_action_send, context.getString(R.string.action_send), nb.addAction(R.drawable.ic_action_send, context.getString(R.string.action_send),
@ -1254,7 +1255,7 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
Statuses.TABLE_NAME, selection).getSQL(); Statuses.TABLE_NAME, selection).getSQL();
final String[] selectionArgs = {accountKey.toString()}; final String[] selectionArgs = {accountKey.toString()};
final String[] userProjection = {Statuses.USER_KEY, Statuses.USER_NAME, Statuses.USER_SCREEN_NAME}; final String[] userProjection = {Statuses.USER_KEY, Statuses.USER_NAME, Statuses.USER_SCREEN_NAME};
final String[] statusProjection = {Statuses.STATUS_ID}; final String[] statusProjection = {Statuses.POSITION_KEY};
final Cursor statusCursor = mDatabaseWrapper.query(Statuses.TABLE_NAME, statusProjection, final Cursor statusCursor = mDatabaseWrapper.query(Statuses.TABLE_NAME, statusProjection,
filteredSelection, selectionArgs, null, null, Statuses.DEFAULT_SORT_ORDER); filteredSelection, selectionArgs, null, null, Statuses.DEFAULT_SORT_ORDER);
final Cursor userCursor = mDatabaseWrapper.query(Statuses.TABLE_NAME, userProjection, final Cursor userCursor = mDatabaseWrapper.query(Statuses.TABLE_NAME, userProjection,
@ -1264,24 +1265,22 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
final int usersCount = userCursor.getCount(); final int usersCount = userCursor.getCount();
final int statusesCount = statusCursor.getCount(); final int statusesCount = statusCursor.getCount();
if (statusesCount == 0 || usersCount == 0) return; if (statusesCount == 0 || usersCount == 0) return;
final int idxStatusId = statusCursor.getColumnIndex(Statuses.STATUS_ID), final ParcelableStatusCursorIndices statusIndices = new ParcelableStatusCursorIndices(statusCursor),
idxUserName = userCursor.getColumnIndex(Statuses.USER_NAME), userIndices = new ParcelableStatusCursorIndices(userCursor);
idxUserScreenName = userCursor.getColumnIndex(Statuses.USER_NAME), final long positionKey = statusCursor.moveToFirst() ? statusCursor.getLong(statusIndices.position_key) : -1;
idxUserId = userCursor.getColumnIndex(Statuses.USER_NAME);
final long statusId = statusCursor.moveToFirst() ? statusCursor.getLong(idxStatusId) : -1;
final String notificationTitle = resources.getQuantityString(R.plurals.N_new_statuses, final String notificationTitle = resources.getQuantityString(R.plurals.N_new_statuses,
statusesCount, statusesCount); statusesCount, statusesCount);
final String notificationContent; final String notificationContent;
userCursor.moveToFirst(); userCursor.moveToFirst();
final String displayName = mUserColorNameManager.getDisplayName(userCursor.getString(idxUserId), final String displayName = mUserColorNameManager.getDisplayName(userCursor.getString(userIndices.user_key),
userCursor.getString(idxUserName), userCursor.getString(idxUserScreenName), userCursor.getString(userIndices.user_name), userCursor.getString(userIndices.user_screen_name),
mNameFirst); mNameFirst);
if (usersCount == 1) { if (usersCount == 1) {
notificationContent = context.getString(R.string.from_name, displayName); notificationContent = context.getString(R.string.from_name, displayName);
} else if (usersCount == 2) { } else if (usersCount == 2) {
userCursor.moveToPosition(1); userCursor.moveToPosition(1);
final String othersName = mUserColorNameManager.getDisplayName(userCursor.getString(idxUserId), final String othersName = mUserColorNameManager.getDisplayName(userCursor.getString(userIndices.user_key),
userCursor.getString(idxUserName), userCursor.getString(idxUserScreenName), userCursor.getString(userIndices.user_name), userCursor.getString(userIndices.user_screen_name),
mNameFirst); mNameFirst);
notificationContent = resources.getString(R.string.from_name_and_name, displayName, othersName); notificationContent = resources.getString(R.string.from_name_and_name, displayName, othersName);
} else { } else {
@ -1297,9 +1296,9 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
builder.setContentText(notificationContent); builder.setContentText(notificationContent);
builder.setCategory(NotificationCompat.CATEGORY_SOCIAL); builder.setCategory(NotificationCompat.CATEGORY_SOCIAL);
builder.setContentIntent(getContentIntent(context, CustomTabType.HOME_TIMELINE, builder.setContentIntent(getContentIntent(context, CustomTabType.HOME_TIMELINE,
NotificationType.HOME_TIMELINE, accountKey, statusId)); NotificationType.HOME_TIMELINE, accountKey, positionKey));
builder.setDeleteIntent(getMarkReadDeleteIntent(context, NotificationType.HOME_TIMELINE, builder.setDeleteIntent(getMarkReadDeleteIntent(context, NotificationType.HOME_TIMELINE,
accountKey, statusId, false)); accountKey, positionKey, false));
builder.setNumber(statusesCount); builder.setNumber(statusesCount);
builder.setCategory(NotificationCompat.CATEGORY_SOCIAL); builder.setCategory(NotificationCompat.CATEGORY_SOCIAL);
applyNotificationPreferences(builder, pref, pref.getHomeTimelineNotificationType()); applyNotificationPreferences(builder, pref, pref.getHomeTimelineNotificationType());

View File

@ -92,7 +92,7 @@ import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages.Inbox;
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages.Outbox; import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages.Outbox;
import org.mariotaku.twidere.provider.TwidereDataStore.Drafts; import org.mariotaku.twidere.provider.TwidereDataStore.Drafts;
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses; import org.mariotaku.twidere.provider.TwidereDataStore.Statuses;
import org.mariotaku.twidere.service.BackgroundOperationService; import org.mariotaku.twidere.service.LengthyOperationsService;
import org.mariotaku.twidere.task.AcceptFriendshipTask; import org.mariotaku.twidere.task.AcceptFriendshipTask;
import org.mariotaku.twidere.task.AddUserListMembersTask; import org.mariotaku.twidere.task.AddUserListMembersTask;
import org.mariotaku.twidere.task.CreateFriendshipTask; import org.mariotaku.twidere.task.CreateFriendshipTask;
@ -483,7 +483,7 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
public int sendDirectMessageAsync(final UserKey accountKey, final String recipientId, final String text, public int sendDirectMessageAsync(final UserKey accountKey, final String recipientId, final String text,
final String imageUri) { final String imageUri) {
final Intent intent = new Intent(context, BackgroundOperationService.class); final Intent intent = new Intent(context, LengthyOperationsService.class);
intent.setAction(INTENT_ACTION_SEND_DIRECT_MESSAGE); intent.setAction(INTENT_ACTION_SEND_DIRECT_MESSAGE);
intent.putExtra(EXTRA_ACCOUNT_KEY, accountKey); intent.putExtra(EXTRA_ACCOUNT_KEY, accountKey);
intent.putExtra(EXTRA_RECIPIENT_ID, recipientId); intent.putExtra(EXTRA_RECIPIENT_ID, recipientId);

View File

@ -403,7 +403,7 @@ public final class Utils implements Constants {
public static String getReadPositionTagWithAccount(@NonNull final String tag, public static String getReadPositionTagWithAccount(@NonNull final String tag,
@Nullable final UserKey accountKey) { @Nullable final UserKey accountKey) {
if (accountKey == null) return tag; if (accountKey == null) return tag;
return tag + "_" + accountKey; return accountKey + ":" + tag;
} }
public static ParcelableDirectMessage findDirectMessageInDatabases(final Context context, public static ParcelableDirectMessage findDirectMessageInDatabases(final Context context,

View File

@ -25,3 +25,35 @@ fun <T> Cursor.map(indices: ObjectCursor.CursorIndices<T>): List<T> {
} }
return list return list
} }
/**
* Executes the given [block] function on this resource and then closes it down correctly whether an exception
* is thrown or not.
*
* @param block a function to process this closable resource.
* @return the result of [block] function on this closable resource.
*/
inline fun <R> Cursor.useCursor(block: (Cursor) -> R): R {
var closed = false
try {
return block(this)
} catch (e: Exception) {
closed = true
try {
this?.close()
} catch (closeException: Exception) {
// eat the closeException as we are already throwing the original cause
// and we don't want to mask the real exception
// TODO on Java 7 we should call
// e.addSuppressed(closeException)
// to work like try-with-resources
// http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html#suppressed-exceptions
}
throw e
} finally {
if (!closed) {
this?.close()
}
}
}

View File

@ -84,7 +84,7 @@ import org.mariotaku.twidere.model.util.AccountUtils
import org.mariotaku.twidere.model.util.ParcelableLocationUtils import org.mariotaku.twidere.model.util.ParcelableLocationUtils
import org.mariotaku.twidere.preference.ServicePickerPreference import org.mariotaku.twidere.preference.ServicePickerPreference
import org.mariotaku.twidere.provider.TwidereDataStore.Drafts import org.mariotaku.twidere.provider.TwidereDataStore.Drafts
import org.mariotaku.twidere.service.BackgroundOperationService import org.mariotaku.twidere.service.LengthyOperationsService
import org.mariotaku.twidere.text.MarkForDeleteSpan import org.mariotaku.twidere.text.MarkForDeleteSpan
import org.mariotaku.twidere.text.style.EmojiSpan import org.mariotaku.twidere.text.style.EmojiSpan
import org.mariotaku.twidere.util.* import org.mariotaku.twidere.util.*
@ -1315,7 +1315,7 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
update.in_reply_to_status = inReplyToStatus update.in_reply_to_status = inReplyToStatus
update.is_possibly_sensitive = isPossiblySensitive update.is_possibly_sensitive = isPossiblySensitive
update.attachment_url = (draft?.action_extras as? UpdateStatusActionExtras)?.attachmentUrl update.attachment_url = (draft?.action_extras as? UpdateStatusActionExtras)?.attachmentUrl
BackgroundOperationService.updateStatusesAsync(this, action, update) LengthyOperationsService.updateStatusesAsync(this, action, update)
if (preferences[noCloseAfterTweetSentKey] && inReplyToStatus == null) { if (preferences[noCloseAfterTweetSentKey] && inReplyToStatus == null) {
possiblySensitive = false possiblySensitive = false
shouldSaveAccounts = true shouldSaveAccounts = true

View File

@ -53,6 +53,7 @@ import org.mariotaku.twidere.util.content.TwidereSQLiteOpenHelper
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
import org.mariotaku.twidere.util.media.TwidereMediaDownloader import org.mariotaku.twidere.util.media.TwidereMediaDownloader
import org.mariotaku.twidere.util.net.TwidereDns import org.mariotaku.twidere.util.net.TwidereDns
import org.mariotaku.twidere.util.refresh.AutoRefreshController
import java.util.* import java.util.*
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
@ -204,7 +205,7 @@ class TwidereApplication : Application(), Constants, OnSharedPreferenceChangeLis
private val sharedPreferences: SharedPreferences by lazy { private val sharedPreferences: SharedPreferences by lazy {
val prefs = getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE) val prefs = getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE)
prefs.registerOnSharedPreferenceChangeListener(this) prefs.registerOnSharedPreferenceChangeListener(this)
prefs return@lazy prefs
} }
override fun onTrimMemory(level: Int) { override fun onTrimMemory(level: Int) {

View File

@ -61,7 +61,7 @@ import org.mariotaku.twidere.model.ParcelableMediaUpdate
import org.mariotaku.twidere.model.draft.SendDirectMessageActionExtras import org.mariotaku.twidere.model.draft.SendDirectMessageActionExtras
import org.mariotaku.twidere.model.util.ParcelableStatusUpdateUtils import org.mariotaku.twidere.model.util.ParcelableStatusUpdateUtils
import org.mariotaku.twidere.provider.TwidereDataStore.Drafts import org.mariotaku.twidere.provider.TwidereDataStore.Drafts
import org.mariotaku.twidere.service.BackgroundOperationService import org.mariotaku.twidere.service.LengthyOperationsService
import org.mariotaku.twidere.util.AsyncTaskUtils import org.mariotaku.twidere.util.AsyncTaskUtils
import org.mariotaku.twidere.util.JsonSerializer import org.mariotaku.twidere.util.JsonSerializer
import org.mariotaku.twidere.util.Utils.getDefaultTextSize import org.mariotaku.twidere.util.Utils.getDefaultTextSize
@ -214,7 +214,7 @@ class DraftsFragment : BaseSupportFragment(), LoaderCallbacks<Cursor?>, OnItemCl
} }
when (item.action_type) { when (item.action_type) {
Draft.Action.UPDATE_STATUS_COMPAT_1, Draft.Action.UPDATE_STATUS_COMPAT_2, Draft.Action.UPDATE_STATUS, Draft.Action.REPLY, Draft.Action.QUOTE -> { Draft.Action.UPDATE_STATUS_COMPAT_1, Draft.Action.UPDATE_STATUS_COMPAT_2, Draft.Action.UPDATE_STATUS, Draft.Action.REPLY, Draft.Action.QUOTE -> {
BackgroundOperationService.updateStatusesAsync(context, item.action_type, LengthyOperationsService.updateStatusesAsync(context, item.action_type,
ParcelableStatusUpdateUtils.fromDraftItem(activity, item)) ParcelableStatusUpdateUtils.fromDraftItem(activity, item))
} }
Draft.Action.SEND_DIRECT_MESSAGE_COMPAT, Draft.Action.SEND_DIRECT_MESSAGE -> { Draft.Action.SEND_DIRECT_MESSAGE_COMPAT, Draft.Action.SEND_DIRECT_MESSAGE -> {

View File

@ -46,7 +46,7 @@ import org.mariotaku.twidere.model.Draft
import org.mariotaku.twidere.model.ParcelableStatus import org.mariotaku.twidere.model.ParcelableStatus
import org.mariotaku.twidere.model.ParcelableStatusUpdate import org.mariotaku.twidere.model.ParcelableStatusUpdate
import org.mariotaku.twidere.model.util.AccountUtils import org.mariotaku.twidere.model.util.AccountUtils
import org.mariotaku.twidere.service.BackgroundOperationService import org.mariotaku.twidere.service.LengthyOperationsService
import org.mariotaku.twidere.util.Analyzer import org.mariotaku.twidere.util.Analyzer
import org.mariotaku.twidere.util.EditTextEnterHandler import org.mariotaku.twidere.util.EditTextEnterHandler
import org.mariotaku.twidere.util.LinkCreator import org.mariotaku.twidere.util.LinkCreator
@ -249,7 +249,7 @@ class RetweetQuoteDialogFragment : BaseDialogFragment() {
} }
update.text = commentText update.text = commentText
update.is_possibly_sensitive = status.is_possibly_sensitive update.is_possibly_sensitive = status.is_possibly_sensitive
BackgroundOperationService.updateStatusesAsync(context, Draft.Action.QUOTE, update) LengthyOperationsService.updateStatusesAsync(context, Draft.Action.QUOTE, update)
} else { } else {
twitter.retweetStatusAsync(status.account_key, status.id) twitter.retweetStatusAsync(status.account_key, status.id)
} }

View File

@ -40,7 +40,7 @@ import javax.inject.Inject
*/ */
@SuppressLint("Registered") @SuppressLint("Registered")
@TargetApi(Build.VERSION_CODES.LOLLIPOP) @TargetApi(Build.VERSION_CODES.LOLLIPOP)
class JobRefreshService : JobService() { class JobTaskService : JobService() {
@Inject @Inject
internal lateinit var preferences: KPreferences internal lateinit var preferences: KPreferences
@ -61,7 +61,7 @@ class JobRefreshService : JobService() {
val task = run { val task = run {
val type = getRefreshType(params.jobId) ?: return@run null val type = getRefreshType(params.jobId) ?: return@run null
return@run RefreshService.createJobTask(this, type) return@run LegacyTaskService.createJobTask(this, type)
} ?: return false } ?: return false
task.callback = { task.callback = {
this.jobFinished(params, false) this.jobFinished(params, false)
@ -75,21 +75,22 @@ class JobRefreshService : JobService() {
} }
companion object { companion object {
const val ID_REFRESH_HOME_TIMELINE = 1 const val JOB_ID_REFRESH_HOME_TIMELINE = 1
const val ID_REFRESH_NOTIFICATIONS = 2 const val JOB_ID_REFRESH_NOTIFICATIONS = 2
const val ID_REFRESH_DIRECT_MESSAGES = 3 const val JOB_ID_REFRESH_DIRECT_MESSAGES = 3
fun getJobId(@AutoRefreshType type: String): Int = when (type) { fun getJobId(@AutoRefreshType type: String): Int = when (type) {
AutoRefreshType.HOME_TIMELINE -> JobRefreshService.ID_REFRESH_HOME_TIMELINE AutoRefreshType.HOME_TIMELINE -> JOB_ID_REFRESH_HOME_TIMELINE
AutoRefreshType.INTERACTIONS_TIMELINE -> JobRefreshService.ID_REFRESH_NOTIFICATIONS AutoRefreshType.INTERACTIONS_TIMELINE -> JOB_ID_REFRESH_NOTIFICATIONS
AutoRefreshType.DIRECT_MESSAGES -> JobRefreshService.ID_REFRESH_DIRECT_MESSAGES AutoRefreshType.DIRECT_MESSAGES -> JOB_ID_REFRESH_DIRECT_MESSAGES
else -> 0 else -> 0
} }
@LegacyTaskService.Action
fun getRefreshType(jobId: Int): String? = when (jobId) { fun getRefreshType(jobId: Int): String? = when (jobId) {
JobRefreshService.ID_REFRESH_HOME_TIMELINE -> AutoRefreshType.HOME_TIMELINE JOB_ID_REFRESH_HOME_TIMELINE -> LegacyTaskService.ACTION_REFRESH_HOME_TIMELINE
JobRefreshService.ID_REFRESH_NOTIFICATIONS -> AutoRefreshType.INTERACTIONS_TIMELINE JOB_ID_REFRESH_NOTIFICATIONS -> LegacyTaskService.ACTION_REFRESH_NOTIFICATIONS
JobRefreshService.ID_REFRESH_DIRECT_MESSAGES -> AutoRefreshType.DIRECT_MESSAGES JOB_ID_REFRESH_DIRECT_MESSAGES -> LegacyTaskService.ACTION_REFRESH_DIRECT_MESSAGES
else -> null else -> null
} }
} }

View File

@ -23,10 +23,11 @@ import android.app.Service
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.IBinder import android.os.IBinder
import android.support.annotation.StringDef
import org.mariotaku.abstask.library.AbstractTask import org.mariotaku.abstask.library.AbstractTask
import org.mariotaku.abstask.library.TaskStarter import org.mariotaku.abstask.library.TaskStarter
import org.mariotaku.twidere.annotation.AutoRefreshType import org.mariotaku.twidere.annotation.AutoRefreshType
import org.mariotaku.twidere.constant.IntentConstants.* import org.mariotaku.twidere.constant.IntentConstants.INTENT_PACKAGE_PREFIX
import org.mariotaku.twidere.model.AccountPreferences import org.mariotaku.twidere.model.AccountPreferences
import org.mariotaku.twidere.model.SimpleRefreshTaskParam import org.mariotaku.twidere.model.SimpleRefreshTaskParam
import org.mariotaku.twidere.model.UserKey import org.mariotaku.twidere.model.UserKey
@ -39,7 +40,7 @@ import org.mariotaku.twidere.util.SharedPreferencesWrapper
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
import javax.inject.Inject import javax.inject.Inject
class RefreshService : Service() { class LegacyTaskService : Service() {
@Inject @Inject
internal lateinit var preferences: SharedPreferencesWrapper internal lateinit var preferences: SharedPreferencesWrapper
@ -54,8 +55,7 @@ class RefreshService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val task = run { val task = run {
val action = intent?.action ?: return@run null val action = intent?.action ?: return@run null
val type = getRefreshType(action) ?: return@run null return@run createJobTask(this, action)
return@run createJobTask(this, type)
} ?: run { } ?: run {
stopSelfResult(startId) stopSelfResult(startId)
return START_NOT_STICKY return START_NOT_STICKY
@ -84,25 +84,37 @@ class RefreshService : Service() {
get() = getSinceIds(accountKeys) get() = getSinceIds(accountKeys)
} }
companion object { @StringDef(ACTION_REFRESH_HOME_TIMELINE, ACTION_REFRESH_NOTIFICATIONS,
ACTION_REFRESH_DIRECT_MESSAGES, ACTION_REFRESH_TRENDS)
annotation class Action
fun createJobTask(context: Context, @AutoRefreshType refreshType: String): AbstractTask<*, *, () -> Unit>? { companion object {
when (refreshType) { @Action
AutoRefreshType.HOME_TIMELINE -> { const val ACTION_REFRESH_HOME_TIMELINE = INTENT_PACKAGE_PREFIX + "REFRESH_HOME_TIMELINE"
@Action
const val ACTION_REFRESH_NOTIFICATIONS = INTENT_PACKAGE_PREFIX + "REFRESH_NOTIFICATIONS"
@Action
const val ACTION_REFRESH_DIRECT_MESSAGES = INTENT_PACKAGE_PREFIX + "REFRESH_DIRECT_MESSAGES"
@Action
const val ACTION_REFRESH_TRENDS = INTENT_PACKAGE_PREFIX + "REFRESH_TRENDS"
fun createJobTask(context: Context, @Action action: String): AbstractTask<*, *, () -> Unit>? {
when (action) {
ACTION_REFRESH_HOME_TIMELINE -> {
val task = GetHomeTimelineTask(context) val task = GetHomeTimelineTask(context)
task.params = AutoRefreshTaskParam(context, AccountPreferences::isAutoRefreshHomeTimelineEnabled) { accountKeys -> task.params = AutoRefreshTaskParam(context, AccountPreferences::isAutoRefreshHomeTimelineEnabled) { accountKeys ->
DataStoreUtils.getNewestStatusIds(context, Statuses.CONTENT_URI, accountKeys) DataStoreUtils.getNewestStatusIds(context, Statuses.CONTENT_URI, accountKeys)
} }
return task return task
} }
AutoRefreshType.INTERACTIONS_TIMELINE -> { ACTION_REFRESH_NOTIFICATIONS -> {
val task = GetActivitiesAboutMeTask(context) val task = GetActivitiesAboutMeTask(context)
task.params = AutoRefreshTaskParam(context, AccountPreferences::isAutoRefreshMentionsEnabled) { accountKeys -> task.params = AutoRefreshTaskParam(context, AccountPreferences::isAutoRefreshMentionsEnabled) { accountKeys ->
DataStoreUtils.getNewestActivityMaxPositions(context, Activities.AboutMe.CONTENT_URI, accountKeys) DataStoreUtils.getNewestActivityMaxPositions(context, Activities.AboutMe.CONTENT_URI, accountKeys)
} }
return task return task
} }
AutoRefreshType.DIRECT_MESSAGES -> { ACTION_REFRESH_DIRECT_MESSAGES -> {
val task = GetReceivedDirectMessagesTask(context) val task = GetReceivedDirectMessagesTask(context)
task.params = AutoRefreshTaskParam(context, AccountPreferences::isAutoRefreshDirectMessagesEnabled) { accountKeys -> task.params = AutoRefreshTaskParam(context, AccountPreferences::isAutoRefreshDirectMessagesEnabled) { accountKeys ->
DataStoreUtils.getNewestMessageIds(context, DirectMessages.Inbox.CONTENT_URI, accountKeys) DataStoreUtils.getNewestMessageIds(context, DirectMessages.Inbox.CONTENT_URI, accountKeys)
@ -113,14 +125,6 @@ class RefreshService : Service() {
return null return null
} }
@AutoRefreshType
fun getRefreshType(action: String): String? = when (action) {
ACTION_REFRESH_HOME_TIMELINE -> AutoRefreshType.HOME_TIMELINE
ACTION_REFRESH_NOTIFICATIONS -> AutoRefreshType.INTERACTIONS_TIMELINE
ACTION_REFRESH_DIRECT_MESSAGES -> AutoRefreshType.DIRECT_MESSAGES
else -> null
}
fun getRefreshAction(@AutoRefreshType type: String): String? = when (type) { fun getRefreshAction(@AutoRefreshType type: String): String? = when (type) {
AutoRefreshType.HOME_TIMELINE -> ACTION_REFRESH_HOME_TIMELINE AutoRefreshType.HOME_TIMELINE -> ACTION_REFRESH_HOME_TIMELINE
AutoRefreshType.INTERACTIONS_TIMELINE -> ACTION_REFRESH_NOTIFICATIONS AutoRefreshType.INTERACTIONS_TIMELINE -> ACTION_REFRESH_NOTIFICATIONS

View File

@ -20,6 +20,7 @@
package org.mariotaku.twidere.service package org.mariotaku.twidere.service
import android.accounts.AccountManager import android.accounts.AccountManager
import android.annotation.SuppressLint
import android.app.Notification import android.app.Notification
import android.app.Service import android.app.Service
import android.content.ContentValues import android.content.ContentValues
@ -45,6 +46,7 @@ import org.mariotaku.abstask.library.ManualTaskStarter
import org.mariotaku.ktextension.configure import org.mariotaku.ktextension.configure
import org.mariotaku.ktextension.toLong import org.mariotaku.ktextension.toLong
import org.mariotaku.ktextension.toTypedArray import org.mariotaku.ktextension.toTypedArray
import org.mariotaku.ktextension.useCursor
import org.mariotaku.microblog.library.MicroBlog import org.mariotaku.microblog.library.MicroBlog
import org.mariotaku.microblog.library.MicroBlogException import org.mariotaku.microblog.library.MicroBlogException
import org.mariotaku.microblog.library.twitter.TwitterUpload import org.mariotaku.microblog.library.twitter.TwitterUpload
@ -74,7 +76,10 @@ import java.io.File
import java.io.IOException import java.io.IOException
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
class BackgroundOperationService : BaseIntentService("background_operation") { /**
* Intent service for lengthy operations like update status/send DM.
*/
class LengthyOperationsService : BaseIntentService("lengthy_operations") {
private val handler: Handler by lazy { Handler(Looper.getMainLooper()) } private val handler: Handler by lazy { Handler(Looper.getMainLooper()) }
@ -83,26 +88,6 @@ class BackgroundOperationService : BaseIntentService("background_operation") {
return Service.START_STICKY return Service.START_STICKY
} }
fun showErrorMessage(message: CharSequence, longMessage: Boolean) {
handler.post { Utils.showErrorMessage(this@BackgroundOperationService, message, longMessage) }
}
fun showErrorMessage(actionRes: Int, e: Exception?, longMessage: Boolean) {
handler.post { Utils.showErrorMessage(this@BackgroundOperationService, actionRes, e, longMessage) }
}
fun showErrorMessage(actionRes: Int, message: String, longMessage: Boolean) {
handler.post { Utils.showErrorMessage(this@BackgroundOperationService, actionRes, message, longMessage) }
}
fun showOkMessage(messageRes: Int, longMessage: Boolean) {
showToast(getString(messageRes), longMessage)
}
private fun showToast(message: CharSequence, longMessage: Boolean) {
handler.post { Toast.makeText(this@BackgroundOperationService, message, if (longMessage) Toast.LENGTH_LONG else Toast.LENGTH_SHORT).show() }
}
override fun onHandleIntent(intent: Intent?) { override fun onHandleIntent(intent: Intent?) {
if (intent == null) return if (intent == null) return
val action = intent.action ?: return val action = intent.action ?: return
@ -122,23 +107,28 @@ class BackgroundOperationService : BaseIntentService("background_operation") {
} }
} }
private fun showErrorMessage(actionRes: Int, e: Exception?, longMessage: Boolean) {
handler.post { Utils.showErrorMessage(this@LengthyOperationsService, actionRes, e, longMessage) }
}
private fun showOkMessage(message: Int, longMessage: Boolean) {
handler.post { Toast.makeText(this@LengthyOperationsService, message, if (longMessage) Toast.LENGTH_LONG else Toast.LENGTH_SHORT).show() }
}
private fun handleSendDraftIntent(intent: Intent) { private fun handleSendDraftIntent(intent: Intent) {
val uri = intent.data ?: return val uri = intent.data ?: return
notificationManager.cancel(uri.toString(), NOTIFICATION_ID_DRAFTS) notificationManager.cancel(uri.toString(), NOTIFICATION_ID_DRAFTS)
val draftId = uri.lastPathSegment.toLong(-1) val draftId = uri.lastPathSegment.toLong(-1)
if (draftId == -1L) return if (draftId == -1L) return
val where = Expression.equals(Drafts._ID, draftId) val where = Expression.equals(Drafts._ID, draftId)
val cr = contentResolver @SuppressLint("Recycle")
val c = cr.query(Drafts.CONTENT_URI, Drafts.COLUMNS, where.sql, null, null) ?: return val item: Draft = contentResolver.query(Drafts.CONTENT_URI, Drafts.COLUMNS, where.sql, null, null)?.useCursor {
@Suppress("ConvertTryFinallyToUseCall") val i = DraftCursorIndices(it)
val item: Draft = try { if (!it.moveToFirst()) return@useCursor null
val i = DraftCursorIndices(c) return@useCursor i.newObject(it)
if (!c.moveToFirst()) return } ?: return
i.newObject(c)
} finally { contentResolver.delete(Drafts.CONTENT_URI, where.sql, null)
c.close()
}
cr.delete(Drafts.CONTENT_URI, where.sql, null)
if (TextUtils.isEmpty(item.action_type)) { if (TextUtils.isEmpty(item.action_type)) {
item.action_type = Draft.Action.UPDATE_STATUS item.action_type = Draft.Action.UPDATE_STATUS
} }
@ -484,7 +474,7 @@ class BackgroundOperationService : BaseIntentService("background_operation") {
fun updateStatusesAsync(context: Context, @Draft.Action action: String, fun updateStatusesAsync(context: Context, @Draft.Action action: String,
vararg statuses: ParcelableStatusUpdate) { vararg statuses: ParcelableStatusUpdate) {
val intent = Intent(context, BackgroundOperationService::class.java) val intent = Intent(context, LengthyOperationsService::class.java)
intent.action = INTENT_ACTION_UPDATE_STATUS intent.action = INTENT_ACTION_UPDATE_STATUS
intent.putExtra(EXTRA_STATUSES, statuses) intent.putExtra(EXTRA_STATUSES, statuses)
intent.putExtra(EXTRA_ACTION, action) intent.putExtra(EXTRA_ACTION, action)

View File

@ -299,7 +299,8 @@ class StreamingService : Service() {
resolver.delete(Mentions.CONTENT_URI, where, whereArgs) resolver.delete(Mentions.CONTENT_URI, where, whereArgs)
resolver.insert(Statuses.CONTENT_URI, values) resolver.insert(Statuses.CONTENT_URI, values)
val rt = status.retweetedStatus val rt = status.retweetedStatus
if (rt != null && rt.extendedText.contains("@" + account.user.screen_name) || rt == null && status.extendedText.contains("@" + account.user.screen_name)) { if (rt != null && rt.extendedText.contains("@" + account.user.screen_name) ||
rt == null && status.extendedText.contains("@" + account.user.screen_name)) {
resolver.insert(Mentions.CONTENT_URI, values) resolver.insert(Mentions.CONTENT_URI, values)
} }
} }

View File

@ -21,6 +21,7 @@ package org.mariotaku.twidere.util.dagger
import android.app.Application import android.app.Application
import android.content.Context import android.content.Context
import android.os.Build
import android.os.Looper import android.os.Looper
import android.support.v4.text.BidiFormatter import android.support.v4.text.BidiFormatter
import com.nostra13.universalimageloader.cache.disc.DiskCache import com.nostra13.universalimageloader.cache.disc.DiskCache
@ -52,6 +53,9 @@ import org.mariotaku.twidere.util.imageloader.URLFileNameGenerator
import org.mariotaku.twidere.util.media.TwidereMediaDownloader import org.mariotaku.twidere.util.media.TwidereMediaDownloader
import org.mariotaku.twidere.util.media.UILFileCache import org.mariotaku.twidere.util.media.UILFileCache
import org.mariotaku.twidere.util.net.TwidereDns import org.mariotaku.twidere.util.net.TwidereDns
import org.mariotaku.twidere.util.refresh.AutoRefreshController
import org.mariotaku.twidere.util.refresh.JobSchedulerAutoRefreshController
import org.mariotaku.twidere.util.refresh.LegacyAutoRefreshController
import java.io.IOException import java.io.IOException
import javax.inject.Singleton import javax.inject.Singleton
@ -231,7 +235,8 @@ class ApplicationModule(private val application: Application) {
@Provides @Provides
fun autoRefreshController(kPreferences: KPreferences): AutoRefreshController { fun autoRefreshController(kPreferences: KPreferences): AutoRefreshController {
if (application.resources.getBoolean(R.bool.use_job_refresh_service)) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
application.resources.getBoolean(R.bool.use_job_refresh_service)) {
return JobSchedulerAutoRefreshController(application, kPreferences) return JobSchedulerAutoRefreshController(application, kPreferences)
} }
return LegacyAutoRefreshController(application, kPreferences) return LegacyAutoRefreshController(application, kPreferences)

View File

@ -37,8 +37,8 @@ import org.mariotaku.twidere.preference.KeyboardShortcutPreference
import org.mariotaku.twidere.provider.CacheProvider import org.mariotaku.twidere.provider.CacheProvider
import org.mariotaku.twidere.provider.TwidereDataProvider import org.mariotaku.twidere.provider.TwidereDataProvider
import org.mariotaku.twidere.service.BaseIntentService import org.mariotaku.twidere.service.BaseIntentService
import org.mariotaku.twidere.service.JobRefreshService import org.mariotaku.twidere.service.JobTaskService
import org.mariotaku.twidere.service.RefreshService import org.mariotaku.twidere.service.LegacyTaskService
import org.mariotaku.twidere.task.* import org.mariotaku.twidere.task.*
import org.mariotaku.twidere.task.twitter.GetActivitiesTask import org.mariotaku.twidere.task.twitter.GetActivitiesTask
import org.mariotaku.twidere.task.twitter.GetStatusesTask import org.mariotaku.twidere.task.twitter.GetStatusesTask
@ -62,7 +62,7 @@ interface GeneralComponent {
fun inject(obj: BaseDialogFragment) fun inject(obj: BaseDialogFragment)
fun inject(obj: RefreshService) fun inject(obj: LegacyTaskService)
fun inject(obj: ComposeActivity) fun inject(obj: ComposeActivity)
@ -110,7 +110,7 @@ interface GeneralComponent {
fun inject(task: GetStatusesTask) fun inject(task: GetStatusesTask)
fun inject(service: JobRefreshService) fun inject(service: JobTaskService)
fun inject(task: GetActivitiesTask) fun inject(task: GetActivitiesTask)

View File

@ -1,4 +1,4 @@
package org.mariotaku.twidere.util package org.mariotaku.twidere.util.refresh
import android.content.Context import android.content.Context
import org.mariotaku.kpreferences.KPreferences import org.mariotaku.kpreferences.KPreferences

View File

@ -1,4 +1,4 @@
package org.mariotaku.twidere.util package org.mariotaku.twidere.util.refresh
import android.annotation.TargetApi import android.annotation.TargetApi
import android.app.job.JobInfo import android.app.job.JobInfo
@ -9,7 +9,7 @@ import android.os.Build
import org.mariotaku.kpreferences.KPreferences import org.mariotaku.kpreferences.KPreferences
import org.mariotaku.twidere.annotation.AutoRefreshType import org.mariotaku.twidere.annotation.AutoRefreshType
import org.mariotaku.twidere.constant.refreshIntervalKey import org.mariotaku.twidere.constant.refreshIntervalKey
import org.mariotaku.twidere.service.JobRefreshService import org.mariotaku.twidere.service.JobTaskService
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import android.Manifest.permission as AndroidPermissions import android.Manifest.permission as AndroidPermissions
@ -22,16 +22,12 @@ class JobSchedulerAutoRefreshController(
context: Context, context: Context,
kPreferences: KPreferences kPreferences: KPreferences
) : AutoRefreshController(context, kPreferences) { ) : AutoRefreshController(context, kPreferences) {
val scheduler: JobScheduler val scheduler: JobScheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
init {
scheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
}
override fun appStarted() { override fun appStarted() {
val allJobs = scheduler.allPendingJobs val allJobs = scheduler.allPendingJobs
AutoRefreshType.ALL.forEach { type -> AutoRefreshType.ALL.forEach { type ->
val jobId = JobRefreshService.getJobId(type) val jobId = JobTaskService.getJobId(type)
if (allJobs.none { job -> job.id == jobId }) { if (allJobs.none { job -> job.id == jobId }) {
// Start non existing job // Start non existing job
schedule(type) schedule(type)
@ -41,18 +37,18 @@ class JobSchedulerAutoRefreshController(
} }
override fun schedule(@AutoRefreshType type: String) { override fun schedule(@AutoRefreshType type: String) {
val jobId = JobRefreshService.getJobId(type) val jobId = JobTaskService.getJobId(type)
scheduler.cancel(jobId) scheduler.cancel(jobId)
scheduleJob(jobId) scheduleJob(jobId)
} }
override fun unschedule(type: String) { override fun unschedule(type: String) {
val jobId = JobRefreshService.getJobId(type) val jobId = JobTaskService.getJobId(type)
scheduler.cancel(jobId) scheduler.cancel(jobId)
} }
fun scheduleJob(jobId: Int, persisted: Boolean = true) { fun scheduleJob(jobId: Int, persisted: Boolean = true) {
val builder = JobInfo.Builder(jobId, ComponentName(context, JobRefreshService::class.java)) val builder = JobInfo.Builder(jobId, ComponentName(context, JobTaskService::class.java))
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
builder.setPeriodic(TimeUnit.MINUTES.toMillis(kPreferences[refreshIntervalKey])) builder.setPeriodic(TimeUnit.MINUTES.toMillis(kPreferences[refreshIntervalKey]))
builder.setPersisted(persisted) builder.setPersisted(persisted)

View File

@ -1,4 +1,4 @@
package org.mariotaku.twidere.util package org.mariotaku.twidere.util.refresh
import android.app.AlarmManager import android.app.AlarmManager
import android.app.PendingIntent import android.app.PendingIntent
@ -9,7 +9,7 @@ import android.support.v4.util.ArrayMap
import org.mariotaku.kpreferences.KPreferences import org.mariotaku.kpreferences.KPreferences
import org.mariotaku.twidere.annotation.AutoRefreshType import org.mariotaku.twidere.annotation.AutoRefreshType
import org.mariotaku.twidere.constant.refreshIntervalKey import org.mariotaku.twidere.constant.refreshIntervalKey
import org.mariotaku.twidere.service.RefreshService import org.mariotaku.twidere.service.LegacyTaskService
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
class LegacyAutoRefreshController( class LegacyAutoRefreshController(
@ -17,18 +17,13 @@ class LegacyAutoRefreshController(
kPreferences: KPreferences kPreferences: KPreferences
) : AutoRefreshController(context, kPreferences) { ) : AutoRefreshController(context, kPreferences) {
private val alarmManager: AlarmManager private val alarmManager: AlarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
private val pendingIntents: ArrayMap<String, PendingIntent> = ArrayMap()
private val pendingIntents: ArrayMap<String, PendingIntent>
init { init {
alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
pendingIntents = ArrayMap()
AutoRefreshType.ALL.forEach { type -> AutoRefreshType.ALL.forEach { type ->
val action = RefreshService.getRefreshAction(type) ?: return@forEach val action = LegacyTaskService.getRefreshAction(type) ?: return@forEach
val intent = Intent(context, RefreshService::class.java) val intent = Intent(context, LegacyTaskService::class.java)
intent.action = action intent.action = action
pendingIntents[type] = PendingIntent.getService(context, 0, intent, 0) pendingIntents[type] = PendingIntent.getService(context, 0, intent, 0)
} }