Skip to content
This repository has been archived by the owner on Dec 17, 2023. It is now read-only.

Commit

Permalink
Optimize shared preference usage
Browse files Browse the repository at this point in the history
1. Thread safe implementation of immutable Settings propagation over
   EventBus
2. Introduce EventBus delivering updates on background thread
3. Run the recurring task in an executor that does heavy processing in
   the background and posts tile updates in the main thread

Release v2.2 to the Play Store

Fixes #6
  • Loading branch information
nazmulidris committed Jun 11, 2020
1 parent d49e2f6 commit f644cb3
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 52 deletions.
5 changes: 3 additions & 2 deletions .idea/modules/app/app.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ android {
applicationId "com.r3bl.stayawake"
minSdkVersion 25
targetSdkVersion 28
versionCode 13
versionName "2.1"
versionCode 14
versionName "2.2"
}
buildTypes {
release {
Expand All @@ -40,9 +40,10 @@ android {

dependencies {
implementation 'androidx.appcompat:appcompat:1.3.0-alpha01'
implementation 'junit:junit:4.12'
implementation 'junit:junit:4.13'
implementation "androidx.core:core-ktx:1.3.0"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'org.greenrobot:eventbus:3.2.0'
}
repositories {
mavenCentral()
Expand Down
49 changes: 37 additions & 12 deletions app/src/main/java/com/r3bl/stayawake/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,15 @@ import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.text.util.LinkifyCompat
import com.r3bl.stayawake.MyTileService.Companion.TAG
import com.r3bl.stayawake.MyTileServiceSettings.loadSharedPreferences
import com.r3bl.stayawake.MyTileServiceSettings.saveSharedPreferencesAfterRunningLambda
import com.r3bl.stayawake.MyTileServiceSettings.changeSettings
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.activity_main.view.*
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import java.util.concurrent.TimeUnit


/** More info: https://stackoverflow.com/a/25510848/2085356 */
private class MySpinnerAdapter(context: Context, resource: Int, items: List<String>, private val font: Typeface) :
ArrayAdapter<String>(context, resource, items) {
Expand All @@ -55,32 +58,54 @@ class MainActivity : AppCompatActivity() {
private lateinit var typeNotoSansBold: Typeface
private lateinit var typeTitilumWebLight: Typeface
private lateinit var typeTitilumWebRegular: Typeface
private lateinit var mySettingsHolder: MyTileServiceSettings.Holder

/** Handle [SettingsChangedEvent] from [EventBus]. */
@Subscribe(threadMode = ThreadMode.BACKGROUND)
fun onSettingsChangedEvent(event: MyTileServiceSettings.SettingsChangedEvent) = event.settings.apply {
mySettingsHolder.value = this
d(TAG, "MainActivity.onSettingsChangedEvent: ${mySettingsHolder}")
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

mySettingsHolder = MyTileServiceSettings.Holder(this)

//showAppIconInActionBar();
//hideStatusBar();

loadAndApplyFonts()
formatMessages()

handleAutoStartOfService()

setupCheckbox()
setupSpinner(typeNotoSansRegular)
}

override fun onStart() {
super.onStart()
MyTileServiceSettings.registerWithEventBus(this)
}

override fun onStop() {
MyTileServiceSettings.unregisterFromEventBus(this)
super.onStop()
}

private fun handleAutoStartOfService() {
if (loadSharedPreferences(this).autoStartEnabled && MyTileService.isCharging(this)) {
if (mySettingsHolder.value.autoStartEnabled && MyTileService.isCharging(this)) {
MyTileService.fireIntentWithStartService(this)
d(TAG, "MainActivity.handleAutoStartOfService: Initiate auto start")
}
else d(TAG, "MainActivity.handleAutoStartOfService: Do nothing, auto start disabled")
}

private fun setupCheckbox() {
loadSharedPreferences(this).autoStartEnabled.let { autoStartEnabled ->
checkbox_prefs_auto_start.isChecked = autoStartEnabled
d(TAG, "setupCheckbox: set checkbox state to: $autoStartEnabled")
}
private fun setupCheckbox() = mySettingsHolder.value.autoStartEnabled.let { autoStartEnabled ->
checkbox_prefs_auto_start.isChecked = autoStartEnabled
d(TAG, "setupCheckbox: set checkbox state to: $autoStartEnabled")
}

private fun loadAndApplyFonts() {
Expand Down Expand Up @@ -119,7 +144,7 @@ class MainActivity : AppCompatActivity() {
adapter = myAdapter

// Restore saved selection.
val savedTimeoutInSec: Long = loadSharedPreferences(this@MainActivity).timeoutNotChargingSec
val savedTimeoutInSec: Long = mySettingsHolder.value.timeoutNotChargingSec
val savedTimeoutInMin: Long = TimeUnit.MINUTES.convert(savedTimeoutInSec, TimeUnit.SECONDS)
val position = myAdapter.getPosition(savedTimeoutInMin.toString())
if (position != -1) {
Expand All @@ -135,7 +160,7 @@ class MainActivity : AppCompatActivity() {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
val selectionInMin: String = parent?.getItemAtPosition(position).toString()
val selectionInSec: Long = TimeUnit.SECONDS.convert(selectionInMin.toLong(), TimeUnit.MINUTES)
saveSharedPreferencesAfterRunningLambda(this@MainActivity) {
changeSettings(this@MainActivity) {
timeoutNotChargingSec = selectionInSec
}
formatMessages()
Expand All @@ -162,7 +187,7 @@ class MainActivity : AppCompatActivity() {

private fun formatMessages() {
// Add actual minutes to string template.
val hours = TimeUnit.SECONDS.toMinutes(loadSharedPreferences(this).timeoutNotChargingSec)
val hours = TimeUnit.SECONDS.toMinutes(mySettingsHolder.value.timeoutNotChargingSec)
text_introduction_content.text = getString(R.string.introduction_body, hours)

// Linkify github link.
Expand Down Expand Up @@ -195,6 +220,6 @@ class MainActivity : AppCompatActivity() {
fun buttonClicked(ignore: View) = MyTileService.fireIntentWithStartService(this)

fun checkboxClicked(view: View) = (view as CheckBox).let { checkbox ->
saveSharedPreferencesAfterRunningLambda(this) { autoStartEnabled = checkbox.isChecked }
changeSettings(this) { autoStartEnabled = checkbox.isChecked }
}
}
29 changes: 22 additions & 7 deletions app/src/main/java/com/r3bl/stayawake/MyTileService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ import android.service.quicksettings.TileService
import android.util.Log
import android.util.Log.d
import androidx.annotation.MainThread
import com.r3bl.stayawake.MyTileServiceSettings.loadSharedPreferences
import androidx.annotation.WorkerThread
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import java.io.PrintWriter
import java.io.StringWriter
import java.io.Writer
Expand All @@ -58,6 +61,14 @@ class MyTileService : TileService() {
private var myIconEyeClosed: Icon? = null
private var myHandler: Handler? = null
private var myReceiver: PowerConnectionReceiver? = null
private lateinit var mySettingsHolder: MyTileServiceSettings.Holder

/** Handle [SettingsChangedEvent] from [EventBus]. */
@Subscribe(threadMode = ThreadMode.BACKGROUND)
fun onSettingsChangedEvent(event: MyTileServiceSettings.SettingsChangedEvent) = event.settings.apply {
mySettingsHolder.value = this
d(TAG, "MyTileService.onSettingsChangedEvent: ${mySettingsHolder}")
}

// General service code.
override fun onCreate() {
Expand All @@ -66,12 +77,14 @@ class MyTileService : TileService() {
myIconEyeOpen = Icon.createWithResource(this, R.drawable.ic_stat_visibility)
myIconEyeClosed = Icon.createWithResource(this, R.drawable.ic_stat_visibility_off)
myReceiver = PowerConnectionReceiver(this)
mySettingsHolder = MyTileServiceSettings.Holder(this)
MyTileServiceSettings.registerWithEventBus(this)
handleAutoStartOfService()
d(TAG, "onCreate: ")
}

private fun handleAutoStartOfService() {
if (loadSharedPreferences(this).autoStartEnabled && isCharging(this)) {
if (mySettingsHolder.value.autoStartEnabled && isCharging(this)) {
commandStart()
d(TAG, "MyTileService.handleAutoStartOfService: Initiate auto start")
}
Expand All @@ -90,6 +103,7 @@ class MyTileService : TileService() {
unregister()
d(TAG, "unregisterReceiver: PowerConnectionReceiver")
}
MyTileServiceSettings.unregisterFromEventBus(this)
}

// Bound Service code & TileService code.
Expand Down Expand Up @@ -232,7 +246,7 @@ class MyTileService : TileService() {
if (myExecutor == null) {
myTimeRunning_sec = 0
myExecutor = Executors.newSingleThreadScheduledExecutor().apply {
scheduleWithFixedDelay({ myHandler?.post { recurringTask() } }, DELAY_INITIAL, DELAY_RECURRING, DELAY_UNIT)
scheduleWithFixedDelay({ recurringTask() }, DELAY_INITIAL, DELAY_RECURRING, DELAY_UNIT)
}
d(TAG, "commandStart: starting executor")
}
Expand Down Expand Up @@ -284,7 +298,7 @@ class MyTileService : TileService() {
}
}

@MainThread
@WorkerThread
private fun recurringTask() {
if (isCharging(this)) {
// Reset the countdown timer.
Expand All @@ -293,7 +307,7 @@ class MyTileService : TileService() {
else {
// Run down the countdown timer.
myTimeRunning_sec += DELAY_UNIT.convert(DELAY_RECURRING, TimeUnit.SECONDS)
if (myTimeRunning_sec >= loadSharedPreferences(this).timeoutNotChargingSec) {
if (myTimeRunning_sec >= mySettingsHolder.value.timeoutNotChargingSec) {
// Timer has run out.
if (isCharging(this)) {
d(TAG, "recurringTask: timer ended but phone is charging")
Expand All @@ -308,9 +322,10 @@ class MyTileService : TileService() {
// d(TAG, "recurringTask: normal");
}
}
updateTile()
myHandler?.post { updateTile() }
}

@MainThread
private fun updateTile() {
val tile = qsTile
val isRunning = myExecutor != null && !myExecutor!!.isShutdown
Expand Down Expand Up @@ -340,7 +355,7 @@ class MyTileService : TileService() {
else {
tile.state = Tile.STATE_ACTIVE
tile.icon = myIconEyeOpen
val timeRemaining = loadSharedPreferences(this).timeoutNotChargingSec - myTimeRunning_sec
val timeRemaining = mySettingsHolder.value.timeoutNotChargingSec - myTimeRunning_sec
val formatTime = formatTime(timeRemaining)
tile.label = getString(R.string.tile_active_text, formatTime)
}
Expand Down
Loading

0 comments on commit f644cb3

Please sign in to comment.