Skip to content

Commit

Permalink
service working
Browse files Browse the repository at this point in the history
  • Loading branch information
devrath committed Jan 4, 2024
1 parent 3cf30a2 commit bf979e6
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 100 deletions.
1 change: 1 addition & 0 deletions Code/StopwatchServiceApp/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<activity
android:name=".MainActivity"
android:exported="true"
android:screenOrientation="portrait"
android:label="@string/app_name"
android:theme="@style/Theme.StopWatchServiceApp">
<intent-filter>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,12 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import com.istudio.app.service.ServiceHelper
import com.istudio.app.service.ServiceHelper.leftButtonAction
import com.istudio.app.service.ServiceHelper.leftButtonText
import com.istudio.app.service.ServiceHelper.rightButtonAction
import com.istudio.app.service.StopwatchService
import com.istudio.app.ui.composables.AppButton
import com.istudio.app.ui.composables.AppText
import com.istudio.app.util.Constants.ACTION_SERVICE_CANCEL
import com.istudio.app.util.Constants.ACTION_SERVICE_START
import com.istudio.app.util.Constants.ACTION_SERVICE_STOP
import com.istudio.app.util.StopwatchState

@SuppressLint("UnusedContentLambdaTargetStateParameter")
@ExperimentalAnimationApi
Expand All @@ -36,6 +35,8 @@ fun MainScreen(stopwatchService: StopwatchService) {
val seconds by stopwatchService.seconds
// <-------------- Time states -------------->

val isRightActionEnabled = seconds != "00" && currentState != ServiceHelper.StopwatchState.Started

Column(
modifier = Modifier
.fillMaxSize()
Expand All @@ -56,39 +57,25 @@ fun MainScreen(stopwatchService: StopwatchService) {
AppText(text = seconds)
}
Row(modifier = Modifier.weight(weight = 1f)) {

val buttonModifier = Modifier.weight(1f).fillMaxHeight(0.8f)

AppButton(
modifier = Modifier
.weight(1f)
.fillMaxHeight(0.8f),
text = leftButtonTextAction(currentState),
onClick = {
ServiceHelper.triggerForegroundService(
context = context,
action = if (currentState == StopwatchState.Started) ACTION_SERVICE_STOP
else ACTION_SERVICE_START
)
})
modifier = buttonModifier,
text = leftButtonText(currentState),
onClick = { leftButtonAction(context, currentState) }
)

Spacer(modifier = Modifier.width(30.dp))

AppButton(
modifier = Modifier
.weight(1f)
.fillMaxHeight(0.8f),
enabled = seconds != "00" && currentState != StopwatchState.Started,
modifier = buttonModifier,
enabled = isRightActionEnabled,
text = "Cancel",
onClick = {
ServiceHelper.triggerForegroundService(
context = context, action = ACTION_SERVICE_CANCEL
)
rightButtonAction(context)
},
)
}
}
}

@Composable
private fun leftButtonTextAction(currentState: StopwatchState) =
if (currentState == StopwatchState.Started) "Stop"
else if ((currentState == StopwatchState.Stopped)) "Resume"
else "Start"
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import android.content.Intent
import android.os.Build
import androidx.compose.animation.ExperimentalAnimationApi
import com.istudio.app.MainActivity
import com.istudio.app.util.Constants
import com.istudio.app.util.Constants.CANCEL_REQUEST_CODE
import com.istudio.app.util.Constants.CLICK_REQUEST_CODE
import com.istudio.app.util.Constants.RESUME_REQUEST_CODE
import com.istudio.app.util.Constants.STOPWATCH_STATE
import com.istudio.app.util.Constants.STOP_REQUEST_CODE
import com.istudio.app.util.StopwatchState

@ExperimentalAnimationApi
object ServiceHelper {
Expand All @@ -22,6 +22,7 @@ object ServiceHelper {
else
0

/** ************************ Pending Intents ************************ **/
fun clickPendingIntent(context: Context): PendingIntent {
val clickIntent = Intent(context, MainActivity::class.java).apply {
putExtra(STOPWATCH_STATE, StopwatchState.Started.name)
Expand Down Expand Up @@ -57,11 +58,69 @@ object ServiceHelper {
context, CANCEL_REQUEST_CODE, cancelIntent, flag
)
}
/** ************************ Pending Intents ************************ **/

fun triggerForegroundService(context: Context, action: String) {
fun toggleLeftButtonAction(currentState: StopwatchState): String {
if (currentState == StopwatchState.Started){
// If the service is already in started state --> Stop it
return Constants.ACTION_SERVICE_STOP
}else{
// If the service is in stopped/resume state --> Start it
return Constants.ACTION_SERVICE_START
}
}

fun leftButtonText(currentState: StopwatchState): String {
when (currentState) {
StopwatchState.Started -> {
// If the current service state is started ---> Display "Stop" text
return "Stop"
}
StopwatchState.Stopped -> {
// If the current service state is stopped ---> Display "Resume" text
return "Resume"
}
else -> {
// Else display "Start" text
return "Start"
}
}
}


/** ************************ Service triggers ************************ **/

/** ************ Actions ************ **/
fun leftButtonAction(
context: Context,
currentState: StopwatchState
) {
triggerForegroundService(
context = context,
action = toggleLeftButtonAction(currentState)
)
}

fun rightButtonAction(context: Context) {
triggerForegroundService(
context = context,
action = Constants.ACTION_SERVICE_CANCEL
)
}
/** ************ Actions ************ **/

private fun triggerForegroundService(context: Context, action: String) {
Intent(context, StopwatchService::class.java).apply {
this.action = action
context.startService(this)
}
}

/** ************************ Service triggers ************************ **/


enum class StopwatchState {
Idle, Started, Stopped, Canceled
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import com.istudio.app.util.Constants.NOTIFICATION_CHANNEL_ID
import com.istudio.app.util.Constants.NOTIFICATION_CHANNEL_NAME
import com.istudio.app.util.Constants.NOTIFICATION_ID
import com.istudio.app.util.Constants.STOPWATCH_STATE
import com.istudio.app.util.StopwatchState
import com.istudio.app.util.formatTime
import com.istudio.app.util.pad
import dagger.hilt.android.AndroidEntryPoint
Expand Down Expand Up @@ -49,63 +48,65 @@ class StopwatchService : Service() {
private set
var hours = mutableStateOf("00")
private set
var currentState = mutableStateOf(StopwatchState.Idle)
var currentState = mutableStateOf(ServiceHelper.StopwatchState.Idle)
private set



override fun onBind(intent: Intent?) = binder



/**
* This method is triggered when another android component sends intent to the running service
*/
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {

when (intent?.getStringExtra(STOPWATCH_STATE)) {
StopwatchState.Started.name -> {
setStopButton()
startForegroundService()
startStopwatch { hours, minutes, seconds ->
updateNotification(hours = hours, minutes = minutes, seconds = seconds)
}
}
StopwatchState.Stopped.name -> {
stopStopwatch()
setResumeButton()
}
StopwatchState.Canceled.name -> {
stopStopwatch()
cancelStopwatch()
stopForegroundService()
}
ServiceHelper.StopwatchState.Started.name -> start()
ServiceHelper.StopwatchState.Stopped.name -> stop()
ServiceHelper.StopwatchState.Canceled.name -> cancel()
}
intent?.action.let {
when (it) {
ACTION_SERVICE_START -> {
setStopButton()
startForegroundService()
startStopwatch { hours, minutes, seconds ->
updateNotification(hours = hours, minutes = minutes, seconds = seconds)
}
}
ACTION_SERVICE_STOP -> {
stopStopwatch()
setResumeButton()
}
ACTION_SERVICE_CANCEL -> {
stopStopwatch()
cancelStopwatch()
stopForegroundService()
}
ACTION_SERVICE_START -> start()
ACTION_SERVICE_STOP -> stop()
ACTION_SERVICE_CANCEL -> cancel()
}
}
return super.onStartCommand(intent, flags, startId)
}

/** ********************** MAIN SERVICE ACTIONS *************** **/
private fun start() {
// Set the UI
setStopButton()
// Update counter
startStopwatch { hours, minutes, seconds ->
updateNotification(hours = hours, minutes = minutes, seconds = seconds)
}
// Update service
startForegroundService()
}

private fun stop() {
// Set the UI
setResumeButton()
// Update service
stopStopwatch()
}

private fun cancel() {
// Set the UI
cancelStopwatch()
// Update counter
stopStopwatch()
// Update service
stopForegroundService()
}
/** ********************** MAIN SERVICE ACTIONS *************** **/

/** **************** STOP-WATCH ACTIONS ********************** **/
private fun startStopwatch(onTick: (h: String, m: String, s: String) -> Unit) {
currentState.value = StopwatchState.Started
currentState.value = ServiceHelper.StopwatchState.Started
timer = fixedRateTimer(initialDelay = 1000L, period = 1000L) {
duration = duration.plus(1.seconds)
updateTimeUnits()
Expand All @@ -117,14 +118,16 @@ class StopwatchService : Service() {
if (this::timer.isInitialized) {
timer.cancel()
}
currentState.value = StopwatchState.Stopped
currentState.value = ServiceHelper.StopwatchState.Stopped
}

private fun cancelStopwatch() {
duration = Duration.ZERO
currentState.value = StopwatchState.Idle
currentState.value = ServiceHelper.StopwatchState.Idle
updateTimeUnits()
}
/** **************** STOP-WATCH ACTIONS ********************** **/


private fun updateTimeUnits() {
duration.toComponents { hours, minutes, seconds, _ ->
Expand Down Expand Up @@ -157,45 +160,48 @@ class StopwatchService : Service() {
}

private fun updateNotification(hours: String, minutes: String, seconds: String) {
val formattedText = formatTime( hours, minutes, seconds)
notificationManager.notify(
NOTIFICATION_ID,
notificationBuilder.setContentText(
formatTime(
hours = hours,
minutes = minutes,
seconds = seconds,
)
).build()
NOTIFICATION_ID, notificationBuilder.setContentText(formattedText).build()
)
}

private fun setStopButton() {
notificationBuilder.mActions.removeAt(0)
notificationBuilder.mActions.add(
0,
NotificationCompat.Action(
0,
"Stop",
ServiceHelper.stopPendingIntent(this)
)
)
val index = 0
val title = "Stop"
val intent = ServiceHelper.stopPendingIntent(baseContext)
val notification = NotificationCompat.Action(0, title,intent)

updateNotificationStructure(index, notification)

notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build())
}


private fun setResumeButton() {
notificationBuilder.mActions.removeAt(0)
notificationBuilder.mActions.add(
0,
NotificationCompat.Action(
0,
"Resume",
ServiceHelper.resumePendingIntent(this)
)
)

val index = 0
val title = "Resume"
val intent = ServiceHelper.resumePendingIntent(baseContext)
val notification = NotificationCompat.Action(0, title,intent)

updateNotificationStructure(index, notification)

notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build())
}

private fun updateNotificationStructure(
index: Int,
notification: NotificationCompat.Action
) {
notificationBuilder.mActions.apply {
// Remove the existing notification
removeAt(index)
// Add new notification at same position
add(index, notification)
}
}

inner class StopwatchBinder : Binder() {
fun getService(): StopwatchService = this@StopwatchService
}
Expand Down

This file was deleted.

0 comments on commit bf979e6

Please sign in to comment.