Convert GenericForegroundService to kotlin.

This commit is contained in:
Greyson Parrelli 2023-11-01 09:44:20 -04:00
parent a911926119
commit c69a4dda00
4 changed files with 395 additions and 435 deletions

View file

@ -1,315 +0,0 @@
package org.thoughtcrime.securesms.service;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import org.signal.core.util.PendingIntentFlags;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.MainActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.jobs.ForegroundServiceUtil;
import org.thoughtcrime.securesms.jobs.UnableToStartException;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.whispersystems.signalservice.api.util.Preconditions;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
public final class GenericForegroundService extends Service {
private static final String TAG = Log.tag(GenericForegroundService.class);
private final IBinder binder = new LocalBinder();
private static final int NOTIFICATION_ID = 827353982;
private static final String EXTRA_TITLE = "extra_title";
private static final String EXTRA_CHANNEL_ID = "extra_channel_id";
private static final String EXTRA_ICON_RES = "extra_icon_res";
private static final String EXTRA_ID = "extra_id";
private static final String EXTRA_PROGRESS = "extra_progress";
private static final String EXTRA_PROGRESS_MAX = "extra_progress_max";
private static final String EXTRA_PROGRESS_INDETERMINATE = "extra_progress_indeterminate";
private static final String ACTION_START = "start";
private static final String ACTION_STOP = "stop";
private static final AtomicInteger NEXT_ID = new AtomicInteger();
private final LinkedHashMap<Integer, Entry> allActiveMessages = new LinkedHashMap<>();
private static final Entry DEFAULTS = new Entry("", NotificationChannels.getInstance().OTHER, R.drawable.ic_notification, -1, 0, 0, false);
private @Nullable Entry lastPosted;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent == null) {
throw new IllegalStateException("Intent needs to be non-null.");
}
synchronized (GenericForegroundService.class) {
String action = intent.getAction();
if (action != null) {
if (ACTION_START.equals(action)) handleStart(intent);
else if (ACTION_STOP .equals(action)) handleStop(intent);
else throw new IllegalStateException(String.format("Action needs to be %s or %s.", ACTION_START, ACTION_STOP));
updateNotification();
}
return START_NOT_STICKY;
}
}
private synchronized void updateNotification() {
Iterator<Entry> iterator = allActiveMessages.values().iterator();
if (iterator.hasNext()) {
postObligatoryForegroundNotification(iterator.next());
} else {
Log.i(TAG, "Last request. Ending foreground service.");
postObligatoryForegroundNotification(lastPosted != null ? lastPosted : DEFAULTS);
stopForeground(true);
stopSelf();
}
}
private synchronized void handleStart(@NonNull Intent intent) {
Entry entry = Entry.fromIntent(intent);
Log.i(TAG, String.format(Locale.US, "handleStart() %s", entry));
allActiveMessages.put(entry.id, entry);
}
private synchronized void handleStop(@NonNull Intent intent) {
Log.i(TAG, "handleStop()");
int id = intent.getIntExtra(EXTRA_ID, -1);
Entry removed = allActiveMessages.remove(id);
if (removed == null) {
Log.w(TAG, "Could not find entry to remove");
}
}
private void postObligatoryForegroundNotification(@NonNull Entry active) {
lastPosted = active;
// TODO [greyson] Navigation
startForeground(NOTIFICATION_ID, new NotificationCompat.Builder(this, active.channelId)
.setSmallIcon(active.iconRes)
.setContentTitle(active.title)
.setProgress(active.progressMax, active.progress, active.indeterminate)
.setContentIntent(PendingIntent.getActivity(this, 0, MainActivity.clearTop(this), PendingIntentFlags.mutable()))
.setVibrate(new long[]{0})
.build());
}
@Override
public IBinder onBind(Intent intent) {
return binder;
}
/**
* Waits for {@param delayMillis} ms before starting the foreground task.
* <p>
* The delayed notification controller can also shown on demand and promoted to a regular notification controller to update the message etc.
*
* Do not call this method on API > 31
*/
public static DelayedNotificationController startForegroundTaskDelayed(@NonNull Context context, @NonNull String task, long delayMillis, @DrawableRes int iconRes) {
Preconditions.checkArgument(Build.VERSION.SDK_INT < 31);
return DelayedNotificationController.create(delayMillis, () -> {
try {
return startForegroundTask(context, task, DEFAULTS.channelId, iconRes);
} catch (UnableToStartException e) {
Log.w(TAG, "This should not happen on API < 31", e);
throw new AssertionError(e.getCause());
}
});
}
public static NotificationController startForegroundTask(@NonNull Context context, @NonNull String task) throws UnableToStartException {
return startForegroundTask(context, task, DEFAULTS.channelId);
}
public static NotificationController startForegroundTask(@NonNull Context context, @NonNull String task, @NonNull String channelId)
throws UnableToStartException
{
return startForegroundTask(context, task, channelId, DEFAULTS.iconRes);
}
public static NotificationController startForegroundTask(
@NonNull Context context,
@NonNull String task,
@NonNull String channelId,
@DrawableRes int iconRes)
throws UnableToStartException
{
final int id = NEXT_ID.getAndIncrement();
Intent intent = new Intent(context, GenericForegroundService.class);
intent.setAction(ACTION_START);
intent.putExtra(EXTRA_TITLE, task);
intent.putExtra(EXTRA_CHANNEL_ID, channelId);
intent.putExtra(EXTRA_ICON_RES, iconRes);
intent.putExtra(EXTRA_ID, id);
Log.i(TAG, String.format(Locale.US, "Starting foreground service (%s) id=%d", task, id));
ForegroundServiceUtil.start(context, intent);
return new NotificationController(context, id);
}
public static void stopForegroundTask(@NonNull Context context, int id) throws UnableToStartException, IllegalStateException {
Intent intent = new Intent(context, GenericForegroundService.class);
intent.setAction(ACTION_STOP);
intent.putExtra(EXTRA_ID, id);
Log.i(TAG, String.format(Locale.US, "Stopping foreground service id=%d", id));
ForegroundServiceUtil.startWhenCapable(context, intent);
}
synchronized void replaceTitle(int id, @NonNull String title) {
Entry oldEntry = allActiveMessages.get(id);
if (oldEntry == null) {
Log.w(TAG, "Failed to replace notification, it was not found");
return;
}
Entry newEntry = new Entry(title, oldEntry.channelId, oldEntry.iconRes, oldEntry.id, oldEntry.progressMax, oldEntry.progress, oldEntry.indeterminate);
if (oldEntry.equals(newEntry)) {
Log.d(TAG, String.format("handleReplace() skip, no change %s", newEntry));
return;
}
Log.i(TAG, String.format("handleReplace() %s", newEntry));
allActiveMessages.put(newEntry.id, newEntry);
updateNotification();
}
synchronized void replaceProgress(int id, int progressMax, int progress, boolean indeterminate) {
Entry oldEntry = allActiveMessages.get(id);
if (oldEntry == null) {
Log.w(TAG, "Failed to replace notification, it was not found");
return;
}
Entry newEntry = new Entry(oldEntry.title, oldEntry.channelId, oldEntry.iconRes, oldEntry.id, progressMax, progress, indeterminate);
if (oldEntry.equals(newEntry)) {
Log.d(TAG, String.format("handleReplace() skip, no change %s", newEntry));
return;
}
Log.i(TAG, String.format("handleReplace() %s", newEntry));
allActiveMessages.put(newEntry.id, newEntry);
updateNotification();
}
private static class Entry {
final @NonNull String title;
final @NonNull String channelId;
final int id;
final @DrawableRes int iconRes;
final int progress;
final int progressMax;
final boolean indeterminate;
private Entry(@NonNull String title, @NonNull String channelId, @DrawableRes int iconRes, int id, int progressMax, int progress, boolean indeterminate) {
this.title = title;
this.channelId = channelId;
this.iconRes = iconRes;
this.id = id;
this.progress = progress;
this.progressMax = progressMax;
this.indeterminate = indeterminate;
}
private static Entry fromIntent(@NonNull Intent intent) {
int id = intent.getIntExtra(EXTRA_ID, DEFAULTS.id);
String title = intent.getStringExtra(EXTRA_TITLE);
if (title == null) title = DEFAULTS.title;
String channelId = intent.getStringExtra(EXTRA_CHANNEL_ID);
if (channelId == null) channelId = DEFAULTS.channelId;
int iconRes = intent.getIntExtra(EXTRA_ICON_RES, DEFAULTS.iconRes);
int progress = intent.getIntExtra(EXTRA_PROGRESS, DEFAULTS.progress);
int progressMax = intent.getIntExtra(EXTRA_PROGRESS_MAX, DEFAULTS.progressMax);
boolean indeterminate = intent.getBooleanExtra(EXTRA_PROGRESS_INDETERMINATE, DEFAULTS.indeterminate);
return new Entry(title, channelId, iconRes, id, progressMax, progress, indeterminate);
}
@Override
public @NonNull String toString() {
return String.format(Locale.US, "ChannelId: %s Id: %d Progress: %d/%d %s", channelId, id, progress, progressMax, indeterminate ? "indeterminate" : "determinate");
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Entry entry = (Entry) o;
return id == entry.id &&
iconRes == entry.iconRes &&
progress == entry.progress &&
progressMax == entry.progressMax &&
indeterminate == entry.indeterminate &&
Objects.equals(title, entry.title) &&
Objects.equals(channelId, entry.channelId);
}
@Override
public int hashCode() {
int hashCode = title.hashCode();
hashCode *= 31;
hashCode += channelId.hashCode();
hashCode *= 31;
hashCode += id;
hashCode *= 31;
hashCode += iconRes;
hashCode *= 31;
hashCode += progress;
hashCode *= 31;
hashCode += progressMax;
hashCode *= 31;
hashCode += indeterminate ? 1 : 0;
return hashCode;
}
}
class LocalBinder extends Binder {
GenericForegroundService getService() {
// Return this instance of LocalService so clients can call public methods
return GenericForegroundService.this;
}
}
}

View file

@ -0,0 +1,272 @@
package org.thoughtcrime.securesms.service
import android.app.PendingIntent
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.Binder
import android.os.Build
import android.os.IBinder
import androidx.annotation.DrawableRes
import androidx.core.app.NotificationCompat
import androidx.core.app.ServiceCompat
import org.signal.core.util.PendingIntentFlags.mutable
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.MainActivity
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.jobs.ForegroundServiceUtil
import org.thoughtcrime.securesms.jobs.UnableToStartException
import org.thoughtcrime.securesms.notifications.NotificationChannels
import org.whispersystems.signalservice.api.util.Preconditions
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock
class GenericForegroundService : Service() {
private val binder: IBinder = LocalBinder()
private val allActiveMessages = LinkedHashMap<Int, Entry>()
private val lock = ReentrantLock()
private var lastPosted: Entry? = null
companion object {
private val TAG = Log.tag(GenericForegroundService::class.java)
private const val NOTIFICATION_ID = 827353982
private const val EXTRA_TITLE = "extra_title"
private const val EXTRA_CHANNEL_ID = "extra_channel_id"
private const val EXTRA_ICON_RES = "extra_icon_res"
private const val EXTRA_ID = "extra_id"
private const val EXTRA_PROGRESS = "extra_progress"
private const val EXTRA_PROGRESS_MAX = "extra_progress_max"
private const val EXTRA_PROGRESS_INDETERMINATE = "extra_progress_indeterminate"
private const val ACTION_START = "start"
private const val ACTION_STOP = "stop"
private val NEXT_ID = AtomicInteger()
private val DEFAULT_ENTRY = Entry("", NotificationChannels.getInstance().OTHER, R.drawable.ic_notification, -1, 0, 0, false)
/**
* Waits for {@param delayMillis} ms before starting the foreground task.
*
*
* The delayed notification controller can also shown on demand and promoted to a regular notification controller to update the message etc.
*
* Do not call this method on API > 31
*/
@JvmStatic
fun startForegroundTaskDelayed(context: Context, task: String, delayMillis: Long, @DrawableRes iconRes: Int): DelayedNotificationController {
Preconditions.checkArgument(Build.VERSION.SDK_INT < 31)
Log.d(TAG, "[startForegroundTaskDelayed] Task: $task, Delay: $delayMillis")
return DelayedNotificationController.create(delayMillis) {
try {
return@create startForegroundTask(context, task, DEFAULT_ENTRY.channelId, iconRes)
} catch (e: UnableToStartException) {
Log.w(TAG, "This should not happen on API < 31", e)
throw AssertionError(e.cause)
}
}
}
@JvmStatic
@JvmOverloads
@Throws(UnableToStartException::class)
fun startForegroundTask(
context: Context,
task: String,
channelId: String = DEFAULT_ENTRY.channelId,
@DrawableRes iconRes: Int = DEFAULT_ENTRY.iconRes
): NotificationController {
val id = NEXT_ID.getAndIncrement()
Log.i(TAG, "[startForegroundTask] Task: $task, ID: $id")
val intent = Intent(context, GenericForegroundService::class.java).apply {
action = ACTION_START
putExtra(EXTRA_TITLE, task)
putExtra(EXTRA_CHANNEL_ID, channelId)
putExtra(EXTRA_ICON_RES, iconRes)
putExtra(EXTRA_ID, id)
}
ForegroundServiceUtil.start(context, intent)
return NotificationController(context, id)
}
@JvmStatic
@Throws(UnableToStartException::class, IllegalStateException::class)
fun stopForegroundTask(context: Context, id: Int) {
Log.d(TAG, "[stopForegroundTask] ID: $id")
val intent = Intent(context, GenericForegroundService::class.java).apply {
action = ACTION_STOP
putExtra(EXTRA_ID, id)
}
Log.i(TAG, "Stopping foreground service id=$id")
ForegroundServiceUtil.startWhenCapable(context, intent)
}
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
checkNotNull(intent) { "Intent needs to be non-null." }
Log.d(TAG, "[onStartCommand] Action: ${intent.action}")
lock.withLock {
when (val action = intent.action) {
ACTION_START -> {
val entry = Entry.fromIntent(intent)
Log.i(TAG, "[onStartCommand] Adding entry: $entry")
allActiveMessages[entry.id] = entry
}
ACTION_STOP -> {
val id = intent.getIntExtra(EXTRA_ID, -1)
val removed = allActiveMessages.remove(id)
if (removed != null) {
Log.i(TAG, "[onStartCommand] ID: $id, Removed: $removed")
} else {
Log.w(TAG, "[onStartCommand] Could not find entry to remove")
}
}
else -> throw IllegalStateException("Unexpected action: $action")
}
updateNotification()
return START_NOT_STICKY
}
}
override fun onCreate() {
Log.d(TAG, "[onCreate]")
super.onCreate()
}
override fun onBind(intent: Intent): IBinder {
Log.d(TAG, "[onBind]")
return binder
}
override fun onUnbind(intent: Intent?): Boolean {
Log.d(TAG, "[onUnbind]")
return super.onUnbind(intent)
}
override fun onRebind(intent: Intent?) {
Log.d(TAG, "[onRebind]")
super.onRebind(intent)
}
override fun onDestroy() {
Log.d(TAG, "[onDestroy]")
super.onDestroy()
}
override fun onLowMemory() {
Log.d(TAG, "[onLowMemory]")
super.onLowMemory()
}
override fun onTrimMemory(level: Int) {
Log.d(TAG, "[onTrimMemory] level: $level")
super.onTrimMemory(level)
}
fun replaceTitle(id: Int, title: String) {
lock.withLock {
updateEntry(id) { oldEntry ->
oldEntry.copy(title = title)
}
}
}
fun replaceProgress(id: Int, progressMax: Int, progress: Int, indeterminate: Boolean) {
lock.withLock {
updateEntry(id) { oldEntry ->
oldEntry.copy(progressMax = progressMax, progress = progress, indeterminate = indeterminate)
}
}
}
private fun updateNotification() {
if (allActiveMessages.isNotEmpty()) {
postObligatoryForegroundNotification(allActiveMessages.values.first())
} else {
Log.i(TAG, "Last request. Ending foreground service.")
postObligatoryForegroundNotification(lastPosted ?: DEFAULT_ENTRY)
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
stopSelf()
}
}
private fun postObligatoryForegroundNotification(active: Entry) {
lastPosted = active
startForeground(
NOTIFICATION_ID,
NotificationCompat.Builder(this, active.channelId)
.setSmallIcon(active.iconRes)
.setContentTitle(active.title)
.setProgress(active.progressMax, active.progress, active.indeterminate)
.setContentIntent(PendingIntent.getActivity(this, 0, MainActivity.clearTop(this), mutable()))
.setVibrate(longArrayOf(0))
.build()
)
}
private fun updateEntry(id: Int, transform: (Entry) -> Entry) {
val oldEntry = allActiveMessages[id]
if (oldEntry == null) {
Log.w(TAG, "Failed to replace notification, it was not found")
return
}
val newEntry = transform(oldEntry)
if (oldEntry == newEntry) {
Log.d(TAG, "handleReplace() skip, no change $newEntry")
return
}
Log.i(TAG, "handleReplace() $newEntry")
allActiveMessages[newEntry.id] = newEntry
updateNotification()
}
private data class Entry(
val title: String,
val channelId: String,
@field:DrawableRes @param:DrawableRes val iconRes: Int,
val id: Int,
val progressMax: Int,
val progress: Int,
val indeterminate: Boolean
) {
override fun toString(): String {
return "ChannelId: $channelId, ID: $id, Progress: $progress/$progressMax ${if (indeterminate) "indeterminate" else "determinate"}"
}
companion object {
fun fromIntent(intent: Intent): Entry {
return Entry(
title = intent.getStringExtra(EXTRA_TITLE) ?: DEFAULT_ENTRY.title,
channelId = intent.getStringExtra(EXTRA_CHANNEL_ID) ?: DEFAULT_ENTRY.channelId,
iconRes = intent.getIntExtra(EXTRA_ICON_RES, DEFAULT_ENTRY.iconRes),
id = intent.getIntExtra(EXTRA_ID, DEFAULT_ENTRY.id),
progressMax = intent.getIntExtra(EXTRA_PROGRESS_MAX, DEFAULT_ENTRY.progressMax),
progress = intent.getIntExtra(EXTRA_PROGRESS, DEFAULT_ENTRY.progress),
indeterminate = intent.getBooleanExtra(EXTRA_PROGRESS_INDETERMINATE, DEFAULT_ENTRY.indeterminate)
)
}
}
}
inner class LocalBinder : Binder() {
val service: GenericForegroundService
get() = this@GenericForegroundService
}
}

View file

@ -1,120 +0,0 @@
package org.thoughtcrime.securesms.service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import androidx.annotation.NonNull;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.jobs.UnableToStartException;
import java.util.concurrent.atomic.AtomicReference;
public final class NotificationController implements AutoCloseable,
ServiceConnection
{
private static final String TAG = Log.tag(NotificationController.class);
private final Context context;
private final int id;
private int progress;
private int progressMax;
private boolean indeterminate;
private long percent = -1;
private boolean isBound;
private final AtomicReference<GenericForegroundService> service = new AtomicReference<>();
NotificationController(@NonNull Context context, int id) {
this.context = context;
this.id = id;
isBound = bindToService();
}
private boolean bindToService() {
return context.bindService(new Intent(context, GenericForegroundService.class), this, Context.BIND_AUTO_CREATE);
}
public int getId() {
return id;
}
@Override
public void close() {
try {
if (isBound) {
context.unbindService(this);
isBound = false;
} else {
Log.w(TAG, "Service was not bound at the time of close()...");
}
GenericForegroundService.stopForegroundTask(context, id);
} catch (IllegalStateException | UnableToStartException e) {
Log.w(TAG, "Failed to unbind service...", e);
}
}
public void setIndeterminateProgress() {
setProgress(0, 0, true);
}
public void setProgress(long newProgressMax, long newProgress) {
setProgress((int) newProgressMax, (int) newProgress, false);
}
public void replaceTitle(@NonNull String title) {
GenericForegroundService genericForegroundService = service.get();
if (genericForegroundService == null) return;
genericForegroundService.replaceTitle(id, title);
}
private synchronized void setProgress(int newProgressMax, int newProgress, boolean indeterminant) {
int newPercent = newProgressMax != 0 ? 100 * newProgress / newProgressMax : -1;
boolean same = newPercent == percent && indeterminate == indeterminant;
percent = newPercent;
progress = newProgress;
progressMax = newProgressMax;
indeterminate = indeterminant;
if (same) return;
updateProgressOnService();
}
private synchronized void updateProgressOnService() {
GenericForegroundService genericForegroundService = service.get();
if (genericForegroundService == null) return;
genericForegroundService.replaceProgress(id, progressMax, progress, indeterminate);
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i(TAG, "Service connected " + name);
GenericForegroundService.LocalBinder binder = (GenericForegroundService.LocalBinder) service;
GenericForegroundService genericForegroundService = binder.getService();
this.service.set(genericForegroundService);
updateProgressOnService();
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i(TAG, "Service disconnected " + name);
service.set(null);
}
}

View file

@ -0,0 +1,123 @@
package org.thoughtcrime.securesms.service
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.IBinder
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.jobs.UnableToStartException
import org.thoughtcrime.securesms.service.GenericForegroundService.Companion.stopForegroundTask
import org.thoughtcrime.securesms.service.GenericForegroundService.LocalBinder
import java.util.concurrent.atomic.AtomicReference
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock
class NotificationController internal constructor(private val context: Context, val id: Int) : AutoCloseable, ServiceConnection {
private val service = AtomicReference<GenericForegroundService?>()
private val lock = ReentrantLock()
private var progress = 0
private var progressMax = 0
private var indeterminate = false
private var percent: Int = -1
private var isBound: Boolean
companion object {
private val TAG = Log.tag(NotificationController::class.java)
}
init {
isBound = bindToService()
}
override fun onServiceConnected(name: ComponentName, service: IBinder) {
Log.i(TAG, "[onServiceConnected] Name: $name")
val binder = service as LocalBinder
val genericForegroundService = binder.service
this.service.set(genericForegroundService)
lock.withLock {
updateProgressOnService()
}
}
override fun onServiceDisconnected(name: ComponentName) {
Log.i(TAG, "[onServiceDisconnected] Name: $name")
service.set(null)
}
override fun close() {
try {
if (isBound) {
Log.d(TAG, "[close] Unbinding service.")
context.unbindService(this)
isBound = false
} else {
Log.w(TAG, "[close] Service was not bound at the time of close()...")
}
stopForegroundTask(context, id)
} catch (e: IllegalStateException) {
Log.w(TAG, "[close] Failed to unbind service...", e)
} catch (e: UnableToStartException) {
Log.w(TAG, "[close] Failed to unbind service...", e)
}
}
fun setIndeterminateProgress() {
lock.withLock {
setProgress(
newProgressMax = 0,
newProgress = 0,
indeterminant = true
)
}
}
fun setProgress(newProgressMax: Long, newProgress: Long) {
lock.withLock {
setProgress(
newProgressMax = newProgressMax.toInt(),
newProgress = newProgress.toInt(),
indeterminant = false
)
}
}
fun replaceTitle(title: String) {
lock.withLock {
service.get()?.replaceTitle(id, title)
?: Log.w(TAG, "Tried to update the title, but the service was no longer bound!")
}
}
private fun bindToService(): Boolean {
return context.bindService(Intent(context, GenericForegroundService::class.java), this, Context.BIND_AUTO_CREATE)
}
private fun setProgress(newProgressMax: Int, newProgress: Int, indeterminant: Boolean) {
val newPercent = if (newProgressMax != 0) {
100 * newProgress / newProgressMax
} else {
-1
}
val same = newPercent == percent && indeterminate == indeterminant
percent = newPercent
progress = newProgress
progressMax = newProgressMax
indeterminate = indeterminant
if (!same) {
updateProgressOnService()
}
}
private fun updateProgressOnService() {
service.get()?.replaceProgress(id, progressMax, progress, indeterminate)
?: Log.w(TAG, "Tried to update the progress, but the service was no longer bound!")
}
}