finished streaming service lifecycle

This commit is contained in:
Mariotaku Lee 2017-03-13 15:13:39 +08:00
parent b767c28e75
commit 1b542cea6c
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
12 changed files with 237 additions and 168 deletions

View File

@ -647,7 +647,15 @@
<receiver android:name=".receiver.ConnectivityStateReceiver">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
<action
android:name="android.net.conn.CONNECTIVITY_CHANGE"
tools:ignore="BatteryLife"/>
</intent-filter>
</receiver>
<receiver android:name=".receiver.PowerStateReceiver">
<intent-filter>
<action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
</intent-filter>
</receiver>
<receiver

View File

@ -141,7 +141,7 @@ public class HotMobiLogger implements HotMobiConstants {
log(event, null);
}
public void log(final LogModel event, final PreProcessing preProcessing) {
public <T extends LogModel> void log(final T event, final PreProcessing<T> preProcessing) {
log(null, event, preProcessing);
}

View File

@ -1,122 +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.util;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.os.Bundle;
import org.apache.commons.collections.primitives.ArrayIntList;
import org.apache.commons.collections.primitives.IntList;
import org.mariotaku.twidere.BuildConfig;
import org.mariotaku.twidere.activity.HomeActivity;
import edu.tsinghua.hotmobi.HotMobiLogger;
import edu.tsinghua.hotmobi.PreProcessing;
import edu.tsinghua.hotmobi.model.SessionEvent;
/**
* Created by mariotaku on 15/10/5.
*/
public class ActivityTracker implements Application.ActivityLifecycleCallbacks {
private final IntList mInternalStack = new ArrayIntList();
private SessionEvent mSessionEvent;
private boolean mHomeActivityStarted;
private boolean isSwitchingInSameTask(int hashCode) {
return mInternalStack.lastIndexOf(hashCode) < mInternalStack.size() - 1;
}
public int size() {
return mInternalStack.size();
}
public boolean isEmpty() {
return mInternalStack.isEmpty();
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override
public void onActivityStarted(final Activity activity) {
mInternalStack.add(System.identityHashCode(activity));
if (activity instanceof HomeActivity) {
mHomeActivityStarted = true;
}
// BEGIN HotMobi
if (mSessionEvent == null && BuildConfig.HOTMOBI_LOG_ENABLED) {
mSessionEvent = SessionEvent.create(activity);
}
// END HotMobi
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
final int hashCode = System.identityHashCode(activity);
if (activity instanceof HomeActivity) {
mHomeActivityStarted = false;
}
// BEGIN HotMobi
final SessionEvent event = mSessionEvent;
if (event != null && !isSwitchingInSameTask(hashCode)) {
event.markEnd();
HotMobiLogger.getInstance(activity).log(event, new PreProcessing<SessionEvent>() {
@Override
public void process(SessionEvent event, Context appContext) {
event.dumpPreferences(appContext);
}
});
mSessionEvent = null;
}
// END HotMobi
mInternalStack.removeElement(hashCode);
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
public boolean isHomeActivityStarted() {
return mHomeActivityStarted;
}
}

View File

@ -66,8 +66,6 @@ import org.mariotaku.chameleon.ChameleonUtils
import org.mariotaku.kpreferences.get
import org.mariotaku.kpreferences.set
import org.mariotaku.ktextension.*
import org.mariotaku.sqliteqb.library.Columns
import org.mariotaku.sqliteqb.library.SQLFunctions
import org.mariotaku.twidere.Constants.*
import org.mariotaku.twidere.R
import org.mariotaku.twidere.activity.iface.IControlBarActivity.ControlBarShowHideHelper
@ -87,10 +85,9 @@ import org.mariotaku.twidere.model.SupportTabSpec
import org.mariotaku.twidere.model.Tab
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.model.event.UnreadCountUpdatedEvent
import org.mariotaku.twidere.provider.TwidereDataStore
import org.mariotaku.twidere.provider.TwidereDataStore.Activities
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses
import org.mariotaku.twidere.provider.TwidereDataStore.Messages.Conversations
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses
import org.mariotaku.twidere.service.StreamingService
import org.mariotaku.twidere.util.*
import org.mariotaku.twidere.util.KeyboardShortcutsHandler.KeyboardShortcutCallback
@ -270,7 +267,7 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp
val initialTabPosition = handleIntent(intent, savedInstanceState == null)
setTabPosition(initialTabPosition)
startService(Intent(this, StreamingService::class.java))
StreamingService.startOrStopService(this)
if (!showDrawerTutorial() && !kPreferences[defaultAutoRefreshAskedKey]) {
showAutoRefreshConfirm()
@ -312,7 +309,7 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp
override fun onDestroy() {
if (isFinishing) {
// Stop only when exiting explicitly
stopService(Intent(this, StreamingService::class.java))
StreamingService.startOrStopService(this)
}
// Delete unused items in databases.

View File

@ -45,10 +45,9 @@ import org.mariotaku.twidere.TwidereConstants.*
import org.mariotaku.twidere.activity.AssistLauncherActivity
import org.mariotaku.twidere.activity.MainActivity
import org.mariotaku.twidere.activity.MainHondaJOJOActivity
import org.mariotaku.twidere.constant.apiLastChangeKey
import org.mariotaku.twidere.constant.bugReportsKey
import org.mariotaku.twidere.constant.defaultFeatureLastUpdated
import org.mariotaku.twidere.constant.*
import org.mariotaku.twidere.model.DefaultFeatures
import org.mariotaku.twidere.service.StreamingService
import org.mariotaku.twidere.util.*
import org.mariotaku.twidere.util.content.TwidereSQLiteOpenHelper
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
@ -258,6 +257,14 @@ class TwidereApplication : Application(), Constants, OnSharedPreferenceChangeLis
KEY_NAME_FIRST, KEY_I_WANT_MY_STARS_BACK -> {
contentNotificationManager.updatePreferences()
}
streamingPowerSavingKey.key, streamingNonMeteredNetworkKey.key -> {
val streamingIntent = Intent(this, StreamingService::class.java)
if (activityTracker.isHomeActivityLaunched) {
startService(streamingIntent)
} else {
stopService(streamingIntent)
}
}
}
}

View File

@ -21,6 +21,7 @@ package org.mariotaku.twidere.fragment
import org.mariotaku.twidere.R
import org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_ENABLE_STREAMING
import org.mariotaku.twidere.service.StreamingService
class AccountStreamingSettingsFragment : BaseAccountPreferenceFragment() {
@ -31,4 +32,8 @@ class AccountStreamingSettingsFragment : BaseAccountPreferenceFragment() {
override val switchPreferenceKey: String? = KEY_ENABLE_STREAMING
override fun onSwitchPreferenceChanged(isChecked: Boolean) {
super.onSwitchPreferenceChanged(isChecked)
StreamingService.startOrStopService(context)
}
}

View File

@ -19,7 +19,6 @@
package org.mariotaku.twidere.fragment
import android.content.SharedPreferences
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import android.os.Bundle
import android.preference.PreferenceActivity.EXTRA_SHOW_FRAGMENT
@ -27,14 +26,30 @@ import android.text.TextUtils
import android.view.Menu
import android.view.MenuInflater
import android.widget.CompoundButton
import android.widget.CompoundButton.OnCheckedChangeListener
import org.mariotaku.twidere.R
import org.mariotaku.twidere.TwidereConstants.ACCOUNT_PREFERENCES_NAME_PREFIX
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_ACCOUNT
import org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_NAME_FIRST
import org.mariotaku.twidere.model.AccountDetails
abstract class BaseAccountPreferenceFragment : BasePreferenceFragment(), OnCheckedChangeListener, OnSharedPreferenceChangeListener {
abstract class BaseAccountPreferenceFragment : BasePreferenceFragment() {
protected val account: AccountDetails?
get() {
return arguments?.getParcelable(EXTRA_ACCOUNT)
}
protected abstract val preferencesResource: Int
protected abstract val switchPreferenceDefault: Boolean
protected abstract val switchPreferenceKey: String?
private val preferenceChangeListener = OnSharedPreferenceChangeListener { _, key ->
if (key == switchPreferenceKey) {
updatePreferenceScreen()
}
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
@ -48,7 +63,7 @@ abstract class BaseAccountPreferenceFragment : BasePreferenceFragment(), OnCheck
pm.sharedPreferencesName = preferenceName
addPreferencesFromResource(preferencesResource)
val prefs = pm.sharedPreferences
prefs.registerOnSharedPreferenceChangeListener(this)
prefs.registerOnSharedPreferenceChangeListener(preferenceChangeListener)
val activity = activity
val intent = activity.intent
if (intent.hasExtra(EXTRA_SHOW_FRAGMENT)) {
@ -63,19 +78,10 @@ abstract class BaseAccountPreferenceFragment : BasePreferenceFragment(), OnCheck
override fun onDestroy() {
val pm = preferenceManager
val prefs = pm.sharedPreferences
prefs.unregisterOnSharedPreferenceChangeListener(this)
prefs.unregisterOnSharedPreferenceChangeListener(preferenceChangeListener)
super.onDestroy()
}
override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) {
val prefs = preferenceManager.sharedPreferences
val editor = prefs.edit()
if (prefs.getBoolean(switchPreferenceKey, switchPreferenceDefault) != isChecked) {
editor.putBoolean(switchPreferenceKey, isChecked)
editor.apply()
updatePreferenceScreen()
}
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
val switchKey = switchPreferenceKey
@ -84,28 +90,24 @@ abstract class BaseAccountPreferenceFragment : BasePreferenceFragment(), OnCheck
val actionView = menu.findItem(R.id.toggle).actionView
val toggle = actionView.findViewById(android.R.id.toggle) as CompoundButton
val prefs = preferenceManager.sharedPreferences
toggle.setOnCheckedChangeListener(this)
toggle.setOnCheckedChangeListener { _, isChecked ->
val editor = prefs.edit()
if (prefs.getBoolean(switchPreferenceKey, switchPreferenceDefault) != isChecked) {
editor.putBoolean(switchPreferenceKey, isChecked)
editor.apply()
onSwitchPreferenceChanged(isChecked)
updatePreferenceScreen()
}
}
toggle.isChecked = prefs.getBoolean(switchKey, switchPreferenceDefault)
}
super.onCreateOptionsMenu(menu, inflater)
}
override fun onSharedPreferenceChanged(preferences: SharedPreferences, key: String) {
if (key == switchPreferenceKey) {
updatePreferenceScreen()
}
protected open fun onSwitchPreferenceChanged(isChecked: Boolean) {
}
protected val account: AccountDetails?
get() {
return arguments?.getParcelable(EXTRA_ACCOUNT)
}
protected abstract val preferencesResource: Int
protected abstract val switchPreferenceDefault: Boolean
protected abstract val switchPreferenceKey: String?
private fun updatePreferenceScreen() {
val screen = preferenceScreen

View File

@ -30,6 +30,7 @@ import edu.tsinghua.hotmobi.model.NetworkEvent
import org.mariotaku.kpreferences.get
import org.mariotaku.twidere.TwidereConstants.SHARED_PREFERENCES_NAME
import org.mariotaku.twidere.constant.usageStatisticsKey
import org.mariotaku.twidere.service.StreamingService
import org.mariotaku.twidere.util.DebugLog
import org.mariotaku.twidere.util.Utils
import org.mariotaku.twidere.util.dagger.DependencyHolder
@ -50,7 +51,8 @@ class ConnectivityStateReceiver : BroadcastReceiver() {
val appContext = context.applicationContext
val cm = appContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val isNetworkMetered = ConnectivityManagerCompat.isActiveNetworkMetered(cm)
DependencyHolder.get(context).mediaPreloader.isNetworkMetered = isNetworkMetered
val holder = DependencyHolder.get(context)
holder.mediaPreloader.isNetworkMetered = isNetworkMetered
val isCharging = Utils.isCharging(appContext)
if (!isNetworkMetered && isCharging) {
val currentTime = System.currentTimeMillis()
@ -59,7 +61,7 @@ class ConnectivityStateReceiver : BroadcastReceiver() {
appContext.startService(Intent(appContext, UploadLogsService::class.java))
}
}
StreamingService.startOrStopService(context)
}
companion object {

View File

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

View File

@ -45,6 +45,8 @@ abstract class BaseService : Service() {
lateinit var taskServiceRunner: TaskServiceRunner
@Inject
lateinit var connectivityManager: ConnectivityManager
@Inject
lateinit var activityTracker: ActivityTracker
override fun onCreate() {
super.onCreate()

View File

@ -41,6 +41,7 @@ import org.mariotaku.twidere.util.DataStoreUtils
import org.mariotaku.twidere.util.DebugLog
import org.mariotaku.twidere.util.IntentUtils
import org.mariotaku.twidere.util.Utils
import org.mariotaku.twidere.util.dagger.DependencyHolder
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
import org.mariotaku.twidere.util.streaming.TwitterTimelineStreamCallback
import java.util.*
@ -56,7 +57,9 @@ class StreamingService : BaseService() {
private val submittedTasks = WeakHashMap<UserKey, StreamingRunnable<*>>()
private val accountChangeObserver = OnAccountsUpdateListener {
setupStreaming()
if (!setupStreaming()) {
stopSelf()
}
}
override fun onCreate() {
@ -84,27 +87,31 @@ class StreamingService : BaseService() {
if (setupStreaming()) {
return START_STICKY
}
stopSelf()
return START_NOT_STICKY
}
override fun onBind(intent: Intent) = throw UnsupportedOperationException()
/**
* @return True if there're enabled accounts, false if request not met and service should be stopped
*/
private fun setupStreaming(): Boolean {
if (!activityTracker.isHomeActivityLaunched) {
return false
}
val isNetworkMetered = ConnectivityManagerCompat.isActiveNetworkMetered(connectivityManager)
if (preferences[streamingNonMeteredNetworkKey] && isNetworkMetered) {
stopSelf()
return false
}
val isCharging = Utils.isCharging(this)
if (preferences[streamingPowerSavingKey] && !isCharging) {
stopSelf()
return false
}
if (updateStreamingInstances()) {
showNotification()
return true
} else {
stopSelf()
return false
}
}
@ -339,6 +346,15 @@ class StreamingService : BaseService() {
private val NOTIFICATION_SERVICE_STARTED = 1
fun startOrStopService(context: Context) {
val streamingIntent = Intent(context, StreamingService::class.java)
val holder = DependencyHolder.get(context)
if (holder.activityTracker.isHomeActivityLaunched) {
context.startService(streamingIntent)
} else {
context.stopService(streamingIntent)
}
}
}
}

View File

@ -0,0 +1,109 @@
/*
* 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.util
import android.app.Activity
import android.app.Application
import android.os.Bundle
import edu.tsinghua.hotmobi.HotMobiLogger
import edu.tsinghua.hotmobi.model.SessionEvent
import org.apache.commons.collections.primitives.ArrayIntList
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.activity.HomeActivity
/**
* Created by mariotaku on 15/10/5.
*/
class ActivityTracker : Application.ActivityLifecycleCallbacks {
private val internalStack = ArrayIntList()
private var sessionEvent: SessionEvent? = null
var isHomeActivityStarted: Boolean = false
private set
var isHomeActivityLaunched: Boolean = false
private set
private fun isSwitchingInSameTask(hashCode: Int): Boolean {
return internalStack.lastIndexOf(hashCode) < internalStack.size() - 1
}
fun size(): Int {
return internalStack.size()
}
val isEmpty: Boolean
get() = internalStack.isEmpty
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
if (activity is HomeActivity) {
isHomeActivityLaunched = true
}
}
override fun onActivityStarted(activity: Activity) {
internalStack.add(System.identityHashCode(activity))
if (activity is HomeActivity) {
isHomeActivityStarted = true
}
// BEGIN HotMobi
if (sessionEvent == null && BuildConfig.HOTMOBI_LOG_ENABLED) {
sessionEvent = SessionEvent.create(activity)
}
// END HotMobi
}
override fun onActivityResumed(activity: Activity) {
}
override fun onActivityPaused(activity: Activity) {
}
override fun onActivityStopped(activity: Activity) {
val hashCode = System.identityHashCode(activity)
if (activity is HomeActivity) {
isHomeActivityStarted = false
}
// BEGIN HotMobi
val event = sessionEvent
if (event != null && !isSwitchingInSameTask(hashCode)) {
event.markEnd()
HotMobiLogger.getInstance(activity).log(event, SessionEvent::dumpPreferences)
sessionEvent = null
}
// END HotMobi
internalStack.removeElement(hashCode)
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
}
override fun onActivityDestroyed(activity: Activity) {
if (activity is HomeActivity && activity.isFinishing()) {
isHomeActivityLaunched = false
}
}
}