improved refresh services

This commit is contained in:
Mariotaku Lee 2016-12-17 23:05:02 +08:00
parent dcf608e3ff
commit 5b1ac84a3b
17 changed files with 307 additions and 390 deletions

View File

@ -72,19 +72,10 @@ public interface IntentConstants {
String BROADCAST_NOTIFICATION_DELETED = INTENT_PACKAGE_PREFIX + "NOTIFICATION_DELETED";
String BROADCAST_USER_LIST_MEMBERS_DELETED = INTENT_PACKAGE_PREFIX + "USER_LIST_MEMBER_DELETED";
String BROADCAST_REFRESH_HOME_TIMELINE = INTENT_PACKAGE_PREFIX + "REFRESH_HOME_TIMELINE";
String BROADCAST_REFRESH_NOTIFICATIONS = INTENT_PACKAGE_PREFIX + "REFRESH_NOTIFICATIONS";
String BROADCAST_REFRESH_DIRECT_MESSAGES = INTENT_PACKAGE_PREFIX + "REFRESH_DIRECT_MESSAGES";
String BROADCAST_REFRESH_TRENDS = INTENT_PACKAGE_PREFIX + "REFRESH_TRENDS";
String BROADCAST_RESCHEDULE_HOME_TIMELINE_REFRESHING = INTENT_PACKAGE_PREFIX
+ "RESCHEDULE_HOME_TIMELINE_REFRESHING";
String BROADCAST_RESCHEDULE_MENTIONS_REFRESHING = INTENT_PACKAGE_PREFIX
+ "RESCHEDULE_MENTIONS_REFRESHING";
String BROADCAST_RESCHEDULE_DIRECT_MESSAGES_REFRESHING = INTENT_PACKAGE_PREFIX
+ "RESCHEDULE_DIRECT_MESSAGES_REFRESHING";
String BROADCAST_RESCHEDULE_TRENDS_REFRESHING = INTENT_PACKAGE_PREFIX
+ "RESCHEDULE_TRENDS_REFRESHING";
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_LONGITUDE = "longitude";

View File

@ -39,6 +39,7 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.NFC"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<!-- Used for account management -->
<uses-permission
@ -588,14 +589,6 @@
android:scheme="android_secret_code"/>
</intent-filter>
</receiver>
<receiver android:name=".receiver.PowerStateReceiver">
<intent-filter>
<action android:name="android.intent.action.BATTERY_LOW"/>
<action android:name="android.intent.action.BATTERY_OKAY"/>
<action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
</intent-filter>
</receiver>
</application>

View File

@ -0,0 +1,17 @@
package org.mariotaku.twidere.annotation;
import android.support.annotation.StringDef;
/**
* Created by mariotaku on 2016/12/17.
*/
@StringDef({AutoRefreshType.HOME_TIMELINE, AutoRefreshType.INTERACTIONS_TIMELINE, AutoRefreshType.DIRECT_MESSAGES})
public @interface AutoRefreshType {
String HOME_TIMELINE = "home_timeline";
String INTERACTIONS_TIMELINE = "interactions_timeline";
String DIRECT_MESSAGES = "direct_messages";
String[] ALL = {HOME_TIMELINE, INTERACTIONS_TIMELINE, DIRECT_MESSAGES};
}

View File

@ -48,7 +48,6 @@ public class ConnectivityStateReceiver extends BroadcastReceiver implements Cons
if (!ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) return;
final TwidereApplication application = TwidereApplication.Companion.getInstance(context);
// application.reloadConnectivitySettings();
Utils.startRefreshServiceIfNeeded(application);
final SharedPreferences prefs = context.getSharedPreferences(SHARED_PREFERENCES_NAME,
Context.MODE_PRIVATE);
if (prefs.getBoolean(KEY_USAGE_STATISTICS, false) && prefs.getBoolean(KEY_SETTINGS_WIZARD_COMPLETED, false)) {

View File

@ -1019,8 +1019,11 @@ public class DataStoreUtils implements Constants {
}
public static void prepareDatabase(@NonNull Context context) {
context.getContentResolver().query(TwidereDataStore.CONTENT_URI_DATABASE_PREPARE, null,
null, null, null);
final ContentResolver cr = context.getContentResolver();
final Cursor cursor = cr.query(TwidereDataStore.CONTENT_URI_DATABASE_PREPARE, null, null,
null, null);
if (cursor == null) return;
cursor.close();
}
interface FieldArrayCreator<T> {

View File

@ -47,7 +47,6 @@ import android.net.NetworkInfo;
import android.net.Uri;
import android.nfc.NfcAdapter;
import android.nfc.NfcAdapter.CreateNdefMessageCallback;
import android.os.AsyncTask;
import android.os.BatteryManager;
import android.os.Build;
import android.os.Bundle;
@ -125,7 +124,6 @@ import org.mariotaku.twidere.provider.TwidereDataStore.CachedUsers;
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages;
import org.mariotaku.twidere.provider.TwidereDataStore.DirectMessages.ConversationEntries;
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses;
import org.mariotaku.twidere.service.RefreshService;
import org.mariotaku.twidere.util.TwidereLinkify.HighlightStyle;
import org.mariotaku.twidere.view.CardMediaContainer.PreviewStyle;
import org.mariotaku.twidere.view.ShapedImageView;
@ -1184,27 +1182,6 @@ public final class Utils implements Constants {
showErrorMessage(context, message, long_message);
}
public static void startRefreshServiceIfNeeded(@NonNull final Context context) {
final Context appContext = context.getApplicationContext();
if (appContext == null) return;
if (!appContext.getResources().getBoolean(R.bool.use_legacy_refresh_service)) return;
final Intent refreshServiceIntent = new Intent(appContext, RefreshService.class);
DataStoreUtils.prepareDatabase(context);
AsyncTask.execute(new Runnable() {
@Override
public void run() {
if (isNetworkAvailable(appContext) && hasAutoRefreshAccounts(appContext)) {
if (BuildConfig.DEBUG) {
Log.d(LOGTAG, "Start background refresh service");
}
appContext.startService(refreshServiceIntent);
} else {
appContext.stopService(refreshServiceIntent);
}
}
});
}
public static void startStatusShareChooser(final Context context, final ParcelableStatus status) {
final Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
@ -1230,10 +1207,6 @@ public final class Utils implements Constants {
}
}
public static String trim(final String str) {
return str != null ? str.trim() : null;
}
public static String trimLineBreak(final String orig) {
if (orig == null) return null;
return orig.replaceAll("\\n+", "\n");
@ -1306,11 +1279,6 @@ public final class Utils implements Constants {
}
public static void setSharedElementTransition(Context context, Window window, int transitionRes) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return;
UtilsL.setSharedElementTransition(context, window, transitionRes);
}
public static <T> Object findFieldOfTypes(T obj, Class<? extends T> cls, Class<?>... checkTypes) {
labelField:
for (Field field : cls.getDeclaredFields()) {
@ -1476,4 +1444,5 @@ public final class Utils implements Constants {
}
return true;
}
}

View File

@ -62,7 +62,6 @@ import org.mariotaku.twidere.constant.apiLastChangeKey
import org.mariotaku.twidere.constant.bugReportsKey
import org.mariotaku.twidere.constant.defaultFeatureLastUpdated
import org.mariotaku.twidere.model.DefaultFeatures
import org.mariotaku.twidere.service.RefreshService
import org.mariotaku.twidere.util.*
import org.mariotaku.twidere.util.content.TwidereSQLiteOpenHelper
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
@ -93,6 +92,8 @@ class TwidereApplication : Application(), Constants, OnSharedPreferenceChangeLis
lateinit internal var externalThemeManager: ExternalThemeManager
@Inject
lateinit internal var kPreferences: KPreferences
@Inject
lateinit internal var autoRefreshController: AutoRefreshController
private lateinit var profileImageViewViewProcessor: ProfileImageViewViewProcessor
private lateinit var fontFamilyTagProcessor: FontFamilyTagProcessor
@ -129,13 +130,10 @@ class TwidereApplication : Application(), Constants, OnSharedPreferenceChangeLis
updateEasterEggIcon()
migrateUsageStatisticsPreferences()
if (resources.getBoolean(R.bool.use_job_refresh_service)) {
} else {
Utils.startRefreshServiceIfNeeded(this)
}
GeneralComponentHelper.build(this).inject(this)
autoRefreshController.appStarted()
registerActivityLifecycleCallbacks(activityTracker)
listenExternalThemeChange()
@ -288,8 +286,7 @@ class TwidereApplication : Application(), Constants, OnSharedPreferenceChangeLis
override fun onSharedPreferenceChanged(preferences: SharedPreferences, key: String) {
when (key) {
KEY_REFRESH_INTERVAL -> {
stopService(Intent(this, RefreshService::class.java))
Utils.startRefreshServiceIfNeeded(this)
autoRefreshController.rescheduleAll()
}
KEY_ENABLE_PROXY, KEY_PROXY_HOST, KEY_PROXY_PORT, KEY_PROXY_TYPE, KEY_PROXY_USERNAME,
KEY_PROXY_PASSWORD, KEY_CONNECTION_TIMEOUT, KEY_RETRY_ON_NETWORK_ISSUE -> {

View File

@ -4,6 +4,7 @@ import android.content.SharedPreferences
import android.os.Build
import android.text.TextUtils
import org.mariotaku.kpreferences.*
import org.mariotaku.ktextension.toLong
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.Constants.KEY_NO_CLOSE_AFTER_TWEET_SENT
import org.mariotaku.twidere.TwidereConstants.*
@ -44,6 +45,18 @@ val randomizeAccountNameKey = KBooleanKey(KEY_RANDOMIZE_ACCOUNT_NAME, false)
val defaultAutoRefreshKey = KBooleanKey(KEY_DEFAULT_AUTO_REFRESH, false)
val defaultAutoRefreshKeyAsked = KBooleanKey("default_auto_refresh_asked", true)
object refreshIntervalKey : KSimpleKey<Long>(KEY_REFRESH_INTERVAL, 15) {
override fun read(preferences: SharedPreferences): Long {
return preferences.getString(key, null).toLong(def)
}
override fun write(editor: SharedPreferences.Editor, value: Long): Boolean {
editor.putString(key, value.toString())
return true
}
}
object defaultAPIConfigKey : KPreferenceKey<CustomAPIConfig> {
override fun contains(preferences: SharedPreferences): Boolean {
if (preferences.getString(KEY_API_URL_FORMAT, null) == null) return false

View File

@ -19,11 +19,9 @@
package org.mariotaku.twidere.fragment
import android.content.SharedPreferences
import org.mariotaku.twidere.R
import org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_AUTO_REFRESH
import org.mariotaku.twidere.constant.defaultAutoRefreshKey
import org.mariotaku.twidere.util.Utils
class AccountRefreshSettingsFragment : BaseAccountPreferenceFragment() {
@ -36,9 +34,4 @@ class AccountRefreshSettingsFragment : BaseAccountPreferenceFragment() {
override val switchPreferenceKey: String?
get() = KEY_AUTO_REFRESH
override fun onSharedPreferenceChanged(preferences: SharedPreferences, key: String) {
if (KEY_AUTO_REFRESH == key) {
Utils.startRefreshServiceIfNeeded(activity)
}
}
}

View File

@ -1,46 +0,0 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.receiver
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import edu.tsinghua.hotmobi.model.BatteryRecord
/**
* Created by mariotaku on 15/9/29.
*/
class PowerStateReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
Intent.ACTION_BATTERY_LOW, Intent.ACTION_BATTERY_OKAY, Intent.ACTION_POWER_CONNECTED,
Intent.ACTION_POWER_DISCONNECTED -> {
if (serviceReceiverStarted) return
BatteryRecord.log(context)
}
}
}
companion object {
var serviceReceiverStarted: Boolean = false
}
}

View File

@ -24,17 +24,13 @@ import android.annotation.TargetApi
import android.app.job.JobParameters
import android.app.job.JobService
import android.os.Build
import org.mariotaku.abstask.library.AbstractTask
import android.util.Log
import org.mariotaku.abstask.library.TaskStarter
import org.mariotaku.kpreferences.KPreferences
import org.mariotaku.twidere.constant.IntentConstants.*
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.TwidereConstants.LOGTAG
import org.mariotaku.twidere.annotation.AutoRefreshType
import org.mariotaku.twidere.constant.stopAutoRefreshWhenBatteryLowKey
import org.mariotaku.twidere.model.AccountPreferences
import org.mariotaku.twidere.provider.TwidereDataStore.*
import org.mariotaku.twidere.task.GetActivitiesAboutMeTask
import org.mariotaku.twidere.task.GetHomeTimelineTask
import org.mariotaku.twidere.task.GetReceivedDirectMessagesTask
import org.mariotaku.twidere.util.DataStoreUtils
import org.mariotaku.twidere.util.Utils
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
import javax.inject.Inject
@ -59,10 +55,16 @@ class JobRefreshService : JobService() {
// Low battery, don't refresh
return false
}
if (BuildConfig.DEBUG) {
Log.d(LOGTAG, "Running job ${params.jobId}")
}
val task = createJobTask(params) ?: return false
val task = run {
val type = getRefreshType(params.jobId) ?: return@run null
return@run RefreshService.createJobTask(this, type)
} ?: return false
task.callback = {
this.jobFinished(params, true)
this.jobFinished(params, false)
}
TaskStarter.execute(task)
return true
@ -72,30 +74,23 @@ class JobRefreshService : JobService() {
return false
}
internal fun createJobTask(params: JobParameters): AbstractTask<*, *, () -> Unit>? {
when (params.extras?.getString(EXTRA_ACTION)) {
BROADCAST_REFRESH_HOME_TIMELINE -> {
val task = GetHomeTimelineTask(this)
task.params = RefreshService.AutoRefreshTaskParam(this, AccountPreferences::isAutoRefreshHomeTimelineEnabled) { accountKeys ->
DataStoreUtils.getNewestStatusIds(this, Statuses.CONTENT_URI, accountKeys)
}
return task
}
BROADCAST_REFRESH_NOTIFICATIONS -> {
val task = GetActivitiesAboutMeTask(this)
task.params = RefreshService.AutoRefreshTaskParam(this, AccountPreferences::isAutoRefreshMentionsEnabled) { accountKeys ->
DataStoreUtils.getNewestActivityMaxPositions(this, Activities.AboutMe.CONTENT_URI, accountKeys)
}
return task
}
BROADCAST_REFRESH_DIRECT_MESSAGES -> {
val task = GetReceivedDirectMessagesTask(this)
task.params = RefreshService.AutoRefreshTaskParam(this, AccountPreferences::isAutoRefreshDirectMessagesEnabled) { accountKeys ->
DataStoreUtils.getNewestMessageIds(this, DirectMessages.Inbox.CONTENT_URI, accountKeys)
}
return task
}
companion object {
const val ID_REFRESH_HOME_TIMELINE = 1
const val ID_REFRESH_NOTIFICATIONS = 2
const val ID_REFRESH_DIRECT_MESSAGES = 3
fun getJobId(@AutoRefreshType type: String): Int = when (type) {
AutoRefreshType.HOME_TIMELINE -> JobRefreshService.ID_REFRESH_HOME_TIMELINE
AutoRefreshType.INTERACTIONS_TIMELINE -> JobRefreshService.ID_REFRESH_NOTIFICATIONS
AutoRefreshType.DIRECT_MESSAGES -> JobRefreshService.ID_REFRESH_DIRECT_MESSAGES
else -> 0
}
fun getRefreshType(jobId: Int): String? = when (jobId) {
JobRefreshService.ID_REFRESH_HOME_TIMELINE -> AutoRefreshType.HOME_TIMELINE
JobRefreshService.ID_REFRESH_NOTIFICATIONS -> AutoRefreshType.INTERACTIONS_TIMELINE
JobRefreshService.ID_REFRESH_DIRECT_MESSAGES -> AutoRefreshType.DIRECT_MESSAGES
else -> null
}
return null
}
}

View File

@ -19,269 +19,53 @@
package org.mariotaku.twidere.service
import android.app.AlarmManager
import android.app.PendingIntent
import android.app.Service
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.IBinder
import android.os.SystemClock
import android.util.Log
import edu.tsinghua.hotmobi.model.BatteryRecord
import edu.tsinghua.hotmobi.model.ScreenEvent
import org.apache.commons.lang3.math.NumberUtils
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.Constants
import org.mariotaku.abstask.library.AbstractTask
import org.mariotaku.ktextension.convert
import org.mariotaku.twidere.TwidereConstants.LOGTAG
import org.mariotaku.twidere.annotation.AutoRefreshType
import org.mariotaku.twidere.constant.IntentConstants.*
import org.mariotaku.twidere.constant.SharedPreferenceConstants.*
import org.mariotaku.twidere.model.AccountPreferences
import org.mariotaku.twidere.model.SimpleRefreshTaskParam
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.provider.TwidereDataStore.*
import org.mariotaku.twidere.receiver.PowerStateReceiver
import org.mariotaku.twidere.util.AsyncTwitterWrapper
import org.mariotaku.twidere.task.GetActivitiesAboutMeTask
import org.mariotaku.twidere.task.GetHomeTimelineTask
import org.mariotaku.twidere.task.GetReceivedDirectMessagesTask
import org.mariotaku.twidere.util.DataStoreUtils
import org.mariotaku.twidere.util.SharedPreferencesWrapper
import org.mariotaku.twidere.util.Utils
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
import javax.inject.Inject
class RefreshService : Service(), Constants {
class RefreshService : Service() {
@Inject
internal lateinit var preferences: SharedPreferencesWrapper
@Inject
internal lateinit var twitterWrapper: AsyncTwitterWrapper
private lateinit var alarmManager: AlarmManager
private var pendingRefreshHomeTimelineIntent: PendingIntent? = null
private var pendingRefreshMentionsIntent: PendingIntent? = null
private var pendingRefreshDirectMessagesIntent: PendingIntent? = null
private var pendingRefreshTrendsIntent: PendingIntent? = null
private val mStateReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val action = intent.action
if (BuildConfig.DEBUG) {
Log.d(LOGTAG, String.format("Refresh service received action %s", action))
}
when (action) {
BROADCAST_RESCHEDULE_HOME_TIMELINE_REFRESHING -> {
rescheduleHomeTimelineRefreshing()
}
BROADCAST_RESCHEDULE_MENTIONS_REFRESHING -> {
rescheduleMentionsRefreshing()
}
BROADCAST_RESCHEDULE_DIRECT_MESSAGES_REFRESHING -> {
rescheduleDirectMessagesRefreshing()
}
BROADCAST_RESCHEDULE_TRENDS_REFRESHING -> {
rescheduleTrendsRefreshing()
}
BROADCAST_REFRESH_HOME_TIMELINE -> {
if (isAutoRefreshAllowed) {
twitterWrapper.getHomeTimelineAsync(AutoRefreshTaskParam(context,
AccountPreferences::isAutoRefreshHomeTimelineEnabled) { accountKeys ->
DataStoreUtils.getNewestStatusIds(context, Statuses.CONTENT_URI, accountKeys)
})
}
}
BROADCAST_REFRESH_NOTIFICATIONS -> {
if (isAutoRefreshAllowed) {
twitterWrapper.getActivitiesAboutMeAsync(AutoRefreshTaskParam(context,
AccountPreferences::isAutoRefreshMentionsEnabled) { accountKeys ->
DataStoreUtils.getNewestActivityMaxPositions(context,
Activities.AboutMe.CONTENT_URI, accountKeys)
})
}
}
BROADCAST_REFRESH_DIRECT_MESSAGES -> {
if (isAutoRefreshAllowed) {
twitterWrapper.getReceivedDirectMessagesAsync(AutoRefreshTaskParam(context,
AccountPreferences::isAutoRefreshDirectMessagesEnabled) { accountKeys ->
DataStoreUtils.getNewestMessageIds(context,
DirectMessages.Inbox.CONTENT_URI, accountKeys)
})
}
}
BROADCAST_REFRESH_TRENDS -> {
if (isAutoRefreshAllowed) {
val prefs = AccountPreferences.getAccountPreferences(context,
DataStoreUtils.getAccountKeys(context)).filter(AccountPreferences::isAutoRefreshEnabled)
getLocalTrends(prefs.filter(AccountPreferences::isAutoRefreshTrendsEnabled)
.map(AccountPreferences::getAccountKey).toTypedArray())
}
}
}
}
}
private val mPowerStateReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
Intent.ACTION_BATTERY_CHANGED -> {
BatteryRecord.log(context, intent)
}
else -> {
BatteryRecord.log(context)
}
}
}
}
private val mScreenStateReceiver = object : BroadcastReceiver() {
var mPresentTime: Long = -1
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
Intent.ACTION_SCREEN_ON -> {
ScreenEvent.log(context, ScreenEvent.Action.ON, presentDuration)
}
Intent.ACTION_SCREEN_OFF -> {
ScreenEvent.log(context, ScreenEvent.Action.OFF, presentDuration)
mPresentTime = -1
}
Intent.ACTION_USER_PRESENT -> {
mPresentTime = SystemClock.elapsedRealtime()
ScreenEvent.log(context, ScreenEvent.Action.PRESENT, -1)
}
}
}
private val presentDuration: Long
get() {
if (mPresentTime < 0) return -1
return SystemClock.elapsedRealtime() - mPresentTime
}
}
override fun onBind(intent: Intent): IBinder? {
return null
}
override fun onBind(intent: Intent): IBinder? = null
override fun onCreate() {
super.onCreate()
GeneralComponentHelper.build(this).inject(this)
alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
pendingRefreshHomeTimelineIntent = PendingIntent.getBroadcast(this, 0, Intent(
BROADCAST_REFRESH_HOME_TIMELINE), 0)
pendingRefreshMentionsIntent = PendingIntent.getBroadcast(this, 0, Intent(BROADCAST_REFRESH_NOTIFICATIONS), 0)
pendingRefreshDirectMessagesIntent = PendingIntent.getBroadcast(this, 0, Intent(
BROADCAST_REFRESH_DIRECT_MESSAGES), 0)
pendingRefreshTrendsIntent = PendingIntent.getBroadcast(this, 0, Intent(BROADCAST_REFRESH_TRENDS), 0)
val refreshFilter = IntentFilter(BROADCAST_NOTIFICATION_DELETED)
refreshFilter.addAction(BROADCAST_REFRESH_HOME_TIMELINE)
refreshFilter.addAction(BROADCAST_REFRESH_NOTIFICATIONS)
refreshFilter.addAction(BROADCAST_REFRESH_DIRECT_MESSAGES)
refreshFilter.addAction(BROADCAST_RESCHEDULE_HOME_TIMELINE_REFRESHING)
refreshFilter.addAction(BROADCAST_RESCHEDULE_MENTIONS_REFRESHING)
refreshFilter.addAction(BROADCAST_RESCHEDULE_DIRECT_MESSAGES_REFRESHING)
registerReceiver(mStateReceiver, refreshFilter)
val batteryFilter = IntentFilter()
batteryFilter.addAction(Intent.ACTION_BATTERY_CHANGED)
batteryFilter.addAction(Intent.ACTION_BATTERY_OKAY)
batteryFilter.addAction(Intent.ACTION_BATTERY_LOW)
batteryFilter.addAction(Intent.ACTION_POWER_CONNECTED)
batteryFilter.addAction(Intent.ACTION_POWER_DISCONNECTED)
val screenFilter = IntentFilter()
screenFilter.addAction(Intent.ACTION_SCREEN_ON)
screenFilter.addAction(Intent.ACTION_SCREEN_OFF)
screenFilter.addAction(Intent.ACTION_USER_PRESENT)
registerReceiver(mPowerStateReceiver, batteryFilter)
registerReceiver(mScreenStateReceiver, screenFilter)
PowerStateReceiver.serviceReceiverStarted = true
if (Utils.hasAutoRefreshAccounts(this)) {
startAutoRefresh()
} else {
stopSelf()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.d(LOGTAG, "onStartCommand ${intent?.action}")
val task = run {
val type = intent?.action?.convert { getRefreshType(it) } ?: return@run null
return@run createJobTask(this, type)
} ?: run {
stopSelfResult(startId)
return START_NOT_STICKY
}
}
override fun onDestroy() {
PowerStateReceiver.serviceReceiverStarted = false
unregisterReceiver(mScreenStateReceiver)
unregisterReceiver(mPowerStateReceiver)
unregisterReceiver(mStateReceiver)
if (Utils.hasAutoRefreshAccounts(this)) {
// Auto refresh enabled, so I will try to start service after it was
// stopped.
startService(Intent(this, javaClass))
task.callback = {
stopSelfResult(startId)
}
super.onDestroy()
}
protected val isAutoRefreshAllowed: Boolean
get() = Utils.isNetworkAvailable(this) && (Utils.isBatteryOkay(this) || !Utils.shouldStopAutoRefreshOnBatteryLow(this))
private fun getLocalTrends(accountIds: Array<UserKey>) {
val account_id = Utils.getDefaultAccountKey(this)
val woeid = preferences.getInt(KEY_LOCAL_TRENDS_WOEID, 1)
twitterWrapper.getLocalTrendsAsync(account_id, woeid)
}
private val refreshInterval: Long
get() {
val prefValue = NumberUtils.toInt(preferences.getString(KEY_REFRESH_INTERVAL, DEFAULT_REFRESH_INTERVAL), -1)
return (Math.max(prefValue, 3) * 60 * 1000).toLong()
}
private fun rescheduleDirectMessagesRefreshing() {
alarmManager.cancel(pendingRefreshDirectMessagesIntent)
val refreshInterval = refreshInterval
if (refreshInterval > 0) {
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + refreshInterval,
refreshInterval, pendingRefreshDirectMessagesIntent)
}
}
private fun rescheduleHomeTimelineRefreshing() {
alarmManager.cancel(pendingRefreshHomeTimelineIntent)
val refreshInterval = refreshInterval
if (refreshInterval > 0) {
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + refreshInterval,
refreshInterval, pendingRefreshHomeTimelineIntent)
}
}
private fun rescheduleMentionsRefreshing() {
alarmManager.cancel(pendingRefreshMentionsIntent)
val refreshInterval = refreshInterval
if (refreshInterval > 0) {
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + refreshInterval,
refreshInterval, pendingRefreshMentionsIntent)
}
}
private fun rescheduleTrendsRefreshing() {
alarmManager.cancel(pendingRefreshTrendsIntent)
val refreshInterval = refreshInterval
if (refreshInterval > 0) {
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + refreshInterval,
refreshInterval, pendingRefreshTrendsIntent)
}
}
private fun startAutoRefresh(): Boolean {
stopAutoRefresh()
val refreshInterval = refreshInterval
if (refreshInterval <= 0) return false
rescheduleHomeTimelineRefreshing()
rescheduleMentionsRefreshing()
rescheduleDirectMessagesRefreshing()
rescheduleTrendsRefreshing()
return true
}
private fun stopAutoRefresh() {
alarmManager.cancel(pendingRefreshHomeTimelineIntent)
alarmManager.cancel(pendingRefreshMentionsIntent)
alarmManager.cancel(pendingRefreshDirectMessagesIntent)
alarmManager.cancel(pendingRefreshTrendsIntent)
return START_NOT_STICKY
}
class AutoRefreshTaskParam(
@ -301,4 +85,49 @@ class RefreshService : Service(), Constants {
get() = getSinceIds(accountKeys)
}
companion object {
fun createJobTask(context: Context, @AutoRefreshType refreshType: String): AbstractTask<*, *, () -> Unit>? {
when (refreshType) {
AutoRefreshType.HOME_TIMELINE -> {
val task = GetHomeTimelineTask(context)
task.params = RefreshService.AutoRefreshTaskParam(context, AccountPreferences::isAutoRefreshHomeTimelineEnabled) { accountKeys ->
DataStoreUtils.getNewestStatusIds(context, Statuses.CONTENT_URI, accountKeys)
}
return task
}
AutoRefreshType.INTERACTIONS_TIMELINE -> {
val task = GetActivitiesAboutMeTask(context)
task.params = RefreshService.AutoRefreshTaskParam(context, AccountPreferences::isAutoRefreshMentionsEnabled) { accountKeys ->
DataStoreUtils.getNewestActivityMaxPositions(context, Activities.AboutMe.CONTENT_URI, accountKeys)
}
return task
}
AutoRefreshType.DIRECT_MESSAGES -> {
val task = GetReceivedDirectMessagesTask(context)
task.params = RefreshService.AutoRefreshTaskParam(context, AccountPreferences::isAutoRefreshDirectMessagesEnabled) { accountKeys ->
DataStoreUtils.getNewestMessageIds(context, DirectMessages.Inbox.CONTENT_URI, accountKeys)
}
return task
}
}
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) {
AutoRefreshType.HOME_TIMELINE -> ACTION_REFRESH_HOME_TIMELINE
AutoRefreshType.INTERACTIONS_TIMELINE -> ACTION_REFRESH_NOTIFICATIONS
AutoRefreshType.DIRECT_MESSAGES -> ACTION_REFRESH_DIRECT_MESSAGES
else -> null
}
}
}

View File

@ -0,0 +1,31 @@
package org.mariotaku.twidere.util
import android.content.Context
import org.mariotaku.kpreferences.KPreferences
import org.mariotaku.twidere.annotation.AutoRefreshType
/**
* Created by mariotaku on 2016/12/17.
*/
abstract class AutoRefreshController(
val context: Context,
val kPreferences: KPreferences
) {
abstract fun appStarted()
abstract fun schedule(@AutoRefreshType type: String)
abstract fun unschedule(@AutoRefreshType type: String)
fun reschedule(@AutoRefreshType type: String) {
unschedule(type)
schedule(type)
}
fun rescheduleAll() {
AutoRefreshType.ALL.forEach { reschedule(it) }
}
}

View File

@ -0,0 +1,69 @@
package org.mariotaku.twidere.util
import android.annotation.TargetApi
import android.app.job.JobInfo
import android.app.job.JobScheduler
import android.content.ComponentName
import android.content.Context
import android.os.Build
import org.mariotaku.kpreferences.KPreferences
import org.mariotaku.twidere.annotation.AutoRefreshType
import org.mariotaku.twidere.constant.refreshIntervalKey
import org.mariotaku.twidere.service.JobRefreshService
import java.util.concurrent.TimeUnit
import android.Manifest.permission as AndroidPermissions
/**
* Created by mariotaku on 2016/12/17.
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
class JobSchedulerAutoRefreshController(
context: Context,
kPreferences: KPreferences
) : AutoRefreshController(context, kPreferences) {
val scheduler: JobScheduler
init {
scheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
}
override fun appStarted() {
val allJobs = scheduler.allPendingJobs
AutoRefreshType.ALL.forEach { type ->
val jobId = JobRefreshService.getJobId(type)
if (allJobs.none { job -> job.id == jobId }) {
// Start non existing job
schedule(type)
}
}
}
override fun schedule(@AutoRefreshType type: String) {
val jobId = JobRefreshService.getJobId(type)
scheduler.cancel(jobId)
scheduleJob(jobId)
}
override fun unschedule(type: String) {
val jobId = JobRefreshService.getJobId(type)
scheduler.cancel(jobId)
}
fun scheduleJob(jobId: Int, persisted: Boolean = true) {
val builder = JobInfo.Builder(jobId, ComponentName(context, JobRefreshService::class.java))
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
builder.setPeriodic(TimeUnit.MINUTES.toMillis(kPreferences[refreshIntervalKey]))
builder.setPersisted(persisted)
try {
scheduler.schedule(builder.build())
} catch (e: IllegalArgumentException) {
if (persisted) {
scheduleJob(jobId, false)
}
}
}
}

View File

@ -0,0 +1,54 @@
package org.mariotaku.twidere.util
import android.app.AlarmManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.support.v4.util.ArrayMap
import org.mariotaku.kpreferences.KPreferences
import org.mariotaku.twidere.annotation.AutoRefreshType
import org.mariotaku.twidere.constant.refreshIntervalKey
import org.mariotaku.twidere.service.RefreshService
import java.util.concurrent.TimeUnit
class LegacyAutoRefreshController(
context: Context,
kPreferences: KPreferences
) : AutoRefreshController(context, kPreferences) {
private val alarmManager: AlarmManager
private val pendingIntents: ArrayMap<String, PendingIntent>
init {
alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
pendingIntents = ArrayMap()
AutoRefreshType.ALL.forEach { type ->
val action = RefreshService.getRefreshAction(type) ?: return@forEach
val intent = Intent(context, RefreshService::class.java)
intent.action = action
pendingIntents[type] = PendingIntent.getService(context, 0, intent, 0)
}
}
override fun appStarted() {
rescheduleAll()
}
override fun unschedule(type: String) {
val pendingIntent = pendingIntents[type] ?: return
alarmManager.cancel(pendingIntent)
}
override fun schedule(type: String) {
val pendingIntent = pendingIntents[type] ?: return
val interval = TimeUnit.MINUTES.toMillis(kPreferences[refreshIntervalKey])
if (interval > 0) {
val triggerAt = System.currentTimeMillis() + interval
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, triggerAt, interval, pendingIntent)
}
}
}

View File

@ -42,6 +42,7 @@ import org.mariotaku.mediaviewer.library.MediaDownloader
import org.mariotaku.restfu.http.RestHttpClient
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.Constants
import org.mariotaku.twidere.R
import org.mariotaku.twidere.constant.SharedPreferenceConstants
import org.mariotaku.twidere.model.DefaultFeatures
import org.mariotaku.twidere.util.*
@ -228,6 +229,14 @@ class ApplicationModule(private val application: Application) {
return BidiFormatter.getInstance()
}
@Provides
fun autoRefreshController(kPreferences: KPreferences): AutoRefreshController {
if (application.resources.getBoolean(R.bool.use_job_refresh_service)) {
return JobSchedulerAutoRefreshController(application, kPreferences)
}
return LegacyAutoRefreshController(application, kPreferences)
}
@Provides
@Singleton
fun defaultFeatures(preferences: SharedPreferencesWrapper): DefaultFeatures {
@ -270,3 +279,4 @@ class ApplicationModule(private val application: Application) {
}
}
}

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--<bool name="use_job_refresh_service">true</bool>-->
<!--<bool name="use_legacy_refresh_service">false</bool>-->
<bool name="use_job_refresh_service">true</bool>
<bool name="use_legacy_refresh_service">false</bool>
</resources>