Skip to content

Commit

Permalink
Attempt to implement usage of Worker and WorkManager as this seems to…
Browse files Browse the repository at this point in the history
… be necessary with api 34. ForegroundService is not allowed anymore in certain situations so need to use a slightly changed copy of this, the ForegroundWorker when we are on api34. This forces me to manually start the existing AsyncTask on the ui thread since I don't want to re-implement the whole thing...
  • Loading branch information
nilsbraden committed Feb 22, 2024
1 parent b27c21a commit 75f8fd0
Show file tree
Hide file tree
Showing 8 changed files with 174 additions and 23 deletions.
4 changes: 4 additions & 0 deletions ttrssreader/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,8 @@ dependencies {

// Fix duplicate class errors
implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.9.10"))

// F*** Google, lots of difficulties with permissions for starting services...
implementation 'androidx.work:work-runtime:2.9.0'
implementation 'com.google.guava:guava:27.0.1-android'
}
16 changes: 16 additions & 0 deletions ttrssreader/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
<!-- Necessary to be able to call the background update from Tasker/Locale plugin -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

<!-- Needed since sdk 34 (Android 14) for Foreground services -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" android:minSdkVersion="34" />

<!-- Add features to extend range of Play Store supported devices to Android TV -->
<uses-feature android:name="android.hardware.faketouch" />
<uses-feature
Expand Down Expand Up @@ -127,6 +130,19 @@
android:resource="@xml/file_paths" />
</provider>

<!-- Otherwise, remove only the WorkManagerInitializer node.-->
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<!-- If you are using androidx.startup to initialize other components -->
<meta-data
android:name="androidx.work.WorkManagerInitializer"
android:value="androidx.startup"
tools:node="remove" />
</provider>

<!--
Note that Locale will reject plug-in BroadcastReceivers for the following reasons:
- The BroadcastReceiver isn't exported (e.g. android:exported="false")
Expand Down
3 changes: 3 additions & 0 deletions ttrssreader/src/main/java/org/ttrssreader/MyApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import org.ttrssreader.utils.PRNGFixes;

import androidx.multidex.MultiDexApplication;
import androidx.work.Configuration;
import androidx.work.WorkManager;

public class MyApplication extends MultiDexApplication {

Expand All @@ -39,6 +41,7 @@ public void onCreate() {
PRNGFixes.apply();
initSingletons();
Data.getInstance().notifyListeners(); // Notify once to make sure the handler is initialized
WorkManager.initialize(this, new Configuration.Builder().build());

//AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
}
Expand Down
20 changes: 19 additions & 1 deletion ttrssreader/src/main/java/org/ttrssreader/gui/MenuActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@

import android.content.Context;
import android.content.Intent;

import android.content.res.Configuration;
import android.graphics.Rect;
import android.net.ConnectivityManager;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.util.TypedValue;
Expand Down Expand Up @@ -49,6 +51,7 @@
import org.ttrssreader.gui.interfaces.IItemSelectedListener;
import org.ttrssreader.gui.interfaces.IUpdateEndListener;
import org.ttrssreader.imageCache.ForegroundService;
import org.ttrssreader.imageCache.ForegroundWorker;
import org.ttrssreader.model.updaters.IUpdatable;
import org.ttrssreader.model.updaters.StateSynchronisationUpdater;
import org.ttrssreader.model.updaters.Updater;
Expand All @@ -61,6 +64,9 @@
import androidx.annotation.NonNull;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.FragmentManager;
import androidx.work.Data;
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkManager;

import static org.ttrssreader.controllers.Controller.isTablet;

Expand Down Expand Up @@ -486,7 +492,19 @@ private void doCache(final int networkState) {
intent.putExtra(ForegroundService.PARAM_NETWORK, networkState);
intent.setClass(this.getApplicationContext(), ForegroundService.class);

this.startService(intent);
// Also see PluginReceiver.java...
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
Data data = new Data.Builder()
.putString(ForegroundService.PARAM_ACTION, ForegroundService.ACTION_LOAD_IMAGES)
.putInt(ForegroundService.PARAM_NETWORK, networkState)
.build();
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder ( ForegroundWorker.class ).setInputData(data).addTag ( ForegroundWorker.TAG ).build ();
WorkManager.getInstance ( this ).enqueue ( request );
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
this.startForegroundService ( intent );
} else {
this.startService ( intent );
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import android.app.Notification;
import android.app.Service;
import android.content.Intent;
import android.content.pm.ServiceInfo;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;

Expand All @@ -36,6 +38,7 @@ public class ForegroundService extends Service implements ICacheEndListener {

private static final String TAG = ForegroundService.class.getSimpleName();

public static final String PARAM_ACTION = "action";
public static final String PARAM_SHOW_NOTIFICATION = "show_notification";
public static final String PARAM_NETWORK = "network";

Expand Down Expand Up @@ -79,7 +82,7 @@ public void onCacheStart() {
public void onCacheEnd() {
WakeLocker.release();
if (instance != null) {
stopForeground(true);
stopForeground(STOP_FOREGROUND_REMOVE);
imageCacher = null;
instance = null;
}
Expand Down Expand Up @@ -111,19 +114,13 @@ public int onStartCommand(Intent intent, int flags, int startId) {
if (imageCacher == null && intent != null && intent.getAction() != null) {

int networkType = intent.getIntExtra(PARAM_NETWORK, Utils.NETWORK_NONE);
boolean onlyArticles = ACTION_LOAD_ARTICLES.equals(intent.getAction());

CharSequence title = "";
if (ACTION_LOAD_IMAGES.equals(intent.getAction())) {
title = getText(R.string.Cache_service_imagecache);
imageCacher = new ImageCacher(this, this, false, networkType);
imageCacher.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
Log.i(TAG, "Caching images started");
} else if (ACTION_LOAD_ARTICLES.equals(intent.getAction())) {
title = getText(R.string.Cache_service_articlecache);
imageCacher = new ImageCacher(this, this, true, networkType);
imageCacher.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
Log.i(TAG, "Caching (articles only) started");
}
CharSequence title = onlyArticles ? getText(R.string.Cache_service_articlecache) : getText(R.string.Cache_service_imagecache);

Log.i(TAG, String.format("Caching (%s) started", onlyArticles ? "articles" : "images"));
imageCacher = new ImageCacher(instance, this, onlyArticles, networkType);
imageCacher.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);

WakeLocker.acquire(this);

Expand All @@ -132,7 +129,7 @@ public int onStartCommand(Intent intent, int flags, int startId) {
CharSequence ticker = getText(R.string.Cache_service_started);
CharSequence text = getText(R.string.Cache_service_text);
Notification notification = Utils.buildNotification(this, icon, ticker, title, text, true, new Intent(), Data.NOTIFICATION_CHANNEL_ID_TASKER);
startForeground(R.string.Cache_service_started, notification);
doStartForeground(R.string.Cache_service_started, notification);
} else {
// Show dummy notification with a text exlaining this can be hidden...
// According to google this should be done with an Intent Service. Half a year later it doesn't work
Expand All @@ -143,12 +140,20 @@ public int onStartCommand(Intent intent, int flags, int startId) {
CharSequence ticker = getText(R.string.Cache_service_started);
CharSequence text = getText(R.string.Cache_service_text);
Notification notification = Utils.buildNotification(this, icon, ticker, dummytitle, text, true, new Intent(), Data.NOTIFICATION_CHANNEL_ID_TASKER);
startForeground(R.string.Cache_service_started, notification);
doStartForeground(R.string.Cache_service_started, notification);
}

}
}
return START_STICKY;
}

public void doStartForeground(int id, Notification notification) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
startForeground(id, notification);
} else {
startForeground(id, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package org.ttrssreader.imageCache;

import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import org.ttrssreader.gui.interfaces.ICacheEndListener;
import org.ttrssreader.utils.AsyncTask;
import org.ttrssreader.utils.Utils;
import org.ttrssreader.utils.WakeLocker;

import androidx.annotation.NonNull;
import androidx.work.WorkInfo;
import androidx.work.Worker;
import androidx.work.WorkerParameters;

public class ForegroundWorker extends Worker implements ICacheEndListener {
public static final String TAG = ForegroundWorker.class.getSimpleName();


private static final Object LOCK_INSTANCE = new Object();
private static volatile ForegroundWorker instance = null;
private static ICacheEndListener parent;
private static ImageCacher imageCacher;

public ForegroundWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
instance = this;
}

@NonNull
@Override
public Result doWork() {
Context ctx = getApplicationContext();
androidx.work.Data data = getInputData();

//call methods to perform background task
synchronized (LOCK_INSTANCE) {
if (imageCacher == null) {
int networkType = data.getInt(ForegroundService.PARAM_NETWORK, Utils.NETWORK_NONE);
boolean onlyArticles = ForegroundService.ACTION_LOAD_ARTICLES.equals(data.getString(ForegroundService.PARAM_ACTION));

Log.i(TAG, String.format("Caching (%s) started", onlyArticles ? "articles" : "images"));
new Handler(Looper.getMainLooper()).post(() -> {
Log.d("UI thread", "I am the UI thread");
imageCacher = new ImageCacher(instance, ctx, onlyArticles, networkType);
imageCacher.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
});

WakeLocker.acquire(ctx);
}
}
return Result.success();
}

@Override
public void onCacheStart() {
if (parent != null)
parent.onCacheStart();
}

/**
* Cleans up all running notifications, notifies waiting activities and clears the instance of the service.
*/
@SuppressLint("RestrictedApi")
@Override
public void onCacheEnd() {
WakeLocker.release();
if (instance != null) {
imageCacher = null;
instance = null;
}
if (parent != null)
parent.onCacheEnd();
this.stop(WorkInfo.STOP_REASON_CANCELLED_BY_APP);
}

@Override
public void onCacheInterrupted() {
if (parent != null)
parent.onCacheInterrupted();
}

@Override
public void onCacheProgress(int taskCount, int progress) {
if (parent != null)
parent.onCacheProgress(taskCount, progress);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.util.Log;

import com.twofortyfouram.locale.sdk.client.receiver.AbstractPluginSettingReceiver;
Expand All @@ -25,6 +26,9 @@
import org.ttrssreader.controllers.Controller;

import androidx.annotation.NonNull;
import androidx.work.Data;
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkManager;

/**
* This is the "fire" BroadcastReceiver for a Locale Plug-in setting.
Expand Down Expand Up @@ -52,15 +56,22 @@ protected void firePluginSetting(@NonNull final Context context, @NonNull final
final boolean images = PluginJsonValues.getExtraImages(json);
final boolean notification = PluginJsonValues.getExtraNotification(json);

Intent serviceIntent;
if (images) {
serviceIntent = new Intent(ForegroundService.ACTION_LOAD_IMAGES);
Intent intent = new Intent(images ? ForegroundService.ACTION_LOAD_IMAGES : ForegroundService.ACTION_LOAD_ARTICLES);
intent.setClass(context, ForegroundService.class);
intent.putExtra(ForegroundService.PARAM_SHOW_NOTIFICATION, notification);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
Data data = new Data.Builder()
.putString(ForegroundService.PARAM_ACTION, images ? ForegroundService.ACTION_LOAD_IMAGES : ForegroundService.ACTION_LOAD_ARTICLES)
.putBoolean(ForegroundService.PARAM_SHOW_NOTIFICATION, notification)
.build();
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder ( ForegroundWorker.class ).setInputData(data).addTag ( ForegroundWorker.TAG ).build ();
WorkManager.getInstance ( context ).enqueue ( request );
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService ( intent );
} else {
serviceIntent = new Intent(ForegroundService.ACTION_LOAD_ARTICLES);
context.startService ( intent );
}
serviceIntent.setClass(context, ForegroundService.class);
serviceIntent.putExtra(ForegroundService.PARAM_SHOW_NOTIFICATION, notification);
context.startForegroundService(serviceIntent);
}

}
3 changes: 3 additions & 0 deletions ttrssreader/src/main/res/values/update.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
<resources>

<string-array name="updates">
<item>Version 1.97.3
* Added new permissions to support android 14
* Attempt to implement usage of Worker and WorkManager</item>
<item>Version 1.97.2
* Updated libraries and API levels</item>
<item>Version 1.97.1
Expand Down

0 comments on commit 75f8fd0

Please sign in to comment.