├── .github
└── workflows
│ └── ci.yml
├── .gitignore
├── README.md
├── build.gradle
├── examples
└── twitter
│ ├── README.md
│ └── TwitterClient
│ ├── build.gradle
│ └── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── birbit
│ │ └── android
│ │ └── jobqueue
│ │ └── examples
│ │ └── twitter
│ │ ├── Config.java
│ │ ├── SampleTwitterClient.java
│ │ ├── Test.java
│ │ ├── TwitterApplication.java
│ │ ├── activities
│ │ └── BaseActivity.java
│ │ ├── adapters
│ │ └── LazyListAdapter.java
│ │ ├── controllers
│ │ └── TwitterController.java
│ │ ├── dao
│ │ ├── DaoMaster.java
│ │ ├── DaoSession.java
│ │ └── TweetDao.java
│ │ ├── entities
│ │ ├── Tweet.java
│ │ └── TweetBase.java
│ │ ├── events
│ │ ├── DeletedTweetEvent.java
│ │ ├── FetchedNewTweetsEvent.java
│ │ ├── PostedTweetEvent.java
│ │ └── PostingTweetEvent.java
│ │ ├── jobs
│ │ ├── FetchTweetsJob.java
│ │ ├── PostTweetJob.java
│ │ └── Priority.java
│ │ ├── models
│ │ ├── DbHelper.java
│ │ └── TweetModel.java
│ │ ├── services
│ │ ├── MyGcmJobService.java
│ │ └── MyJobService.java
│ │ └── tasks
│ │ └── SimpleBackgroundTask.java
│ └── res
│ ├── layout
│ ├── list_tweet.xml
│ └── main.xml
│ └── values
│ └── strings.xml
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── jobqueue
├── LICENSE.txt
├── build.gradle
├── gradle.properties
└── src
│ ├── main
│ ├── AndroidManifest.xml
│ └── java
│ │ └── com
│ │ └── birbit
│ │ └── android
│ │ └── jobqueue
│ │ ├── AsyncAddCallback.java
│ │ ├── BatchingScheduler.java
│ │ ├── CallbackManager.java
│ │ ├── CancelHandler.java
│ │ ├── CancelReason.java
│ │ ├── CancelResult.java
│ │ ├── Constraint.java
│ │ ├── ConsumerManager.java
│ │ ├── DefaultQueueFactory.java
│ │ ├── IntCallback.java
│ │ ├── Job.java
│ │ ├── JobHolder.java
│ │ ├── JobManager.java
│ │ ├── JobManagerThread.java
│ │ ├── JobQueue.java
│ │ ├── JobStatus.java
│ │ ├── Params.java
│ │ ├── QueueFactory.java
│ │ ├── RetryConstraint.java
│ │ ├── RunningJobSet.java
│ │ ├── TagConstraint.java
│ │ ├── WrongThreadException.java
│ │ ├── cachedQueue
│ │ └── CachedJobQueue.java
│ │ ├── callback
│ │ ├── JobManagerCallback.java
│ │ └── JobManagerCallbackAdapter.java
│ │ ├── config
│ │ └── Configuration.java
│ │ ├── di
│ │ └── DependencyInjector.java
│ │ ├── inMemoryQueue
│ │ └── SimpleInMemoryPriorityQueue.java
│ │ ├── log
│ │ ├── CustomLogger.java
│ │ └── JqLog.java
│ │ ├── messaging
│ │ ├── DelayedMessageBag.java
│ │ ├── Message.java
│ │ ├── MessageFactory.java
│ │ ├── MessagePredicate.java
│ │ ├── MessageQueue.java
│ │ ├── MessageQueueConsumer.java
│ │ ├── PriorityMessageQueue.java
│ │ ├── SafeMessageQueue.java
│ │ ├── Type.java
│ │ ├── UnsafeMessageQueue.java
│ │ └── message
│ │ │ ├── AddJobMessage.java
│ │ │ ├── CallbackMessage.java
│ │ │ ├── CancelMessage.java
│ │ │ ├── CancelResultMessage.java
│ │ │ ├── CommandMessage.java
│ │ │ ├── ConstraintChangeMessage.java
│ │ │ ├── JobConsumerIdleMessage.java
│ │ │ ├── PublicQueryMessage.java
│ │ │ ├── RunJobMessage.java
│ │ │ ├── RunJobResultMessage.java
│ │ │ └── SchedulerMessage.java
│ │ ├── network
│ │ ├── NetworkEventProvider.java
│ │ ├── NetworkUtil.java
│ │ └── NetworkUtilImpl.java
│ │ ├── persistentQueue
│ │ └── sqlite
│ │ │ ├── DbOpenHelper.java
│ │ │ ├── FileStorage.java
│ │ │ ├── SqlHelper.java
│ │ │ ├── SqliteJobQueue.java
│ │ │ ├── Where.java
│ │ │ └── WhereQueryCache.java
│ │ ├── scheduling
│ │ ├── FrameworkJobSchedulerService.java
│ │ ├── FrameworkScheduler.java
│ │ ├── GcmJobSchedulerService.java
│ │ ├── GcmScheduler.java
│ │ ├── Scheduler.java
│ │ └── SchedulerConstraint.java
│ │ └── timer
│ │ ├── SystemTimer.java
│ │ └── Timer.java
│ └── test
│ ├── java
│ └── com
│ │ └── birbit
│ │ └── android
│ │ └── jobqueue
│ │ ├── BatchingSchedulerTest.java
│ │ ├── ConsumerControllerTest.java
│ │ ├── ConsumerTest.java
│ │ ├── JobManagerThreadRunnable.java
│ │ ├── JobManagerTrojan.java
│ │ ├── SystemTimerTest.java
│ │ ├── TestConstraint.java
│ │ ├── config
│ │ └── ConfigurationBuilderTest.java
│ │ ├── messaging
│ │ ├── DelayedMessageBagAddTest.java
│ │ ├── DelayedMessageBagRecycleTest.java
│ │ ├── DelayedMessageBagRemoveTest.java
│ │ ├── MessageFactoryTest.java
│ │ ├── MessageQueueTestBase.java
│ │ ├── PriorityMessageQueueTest.java
│ │ ├── SafeMessageQueueTest.java
│ │ └── UnsafeMessageQueueTest.java
│ │ ├── scheduling
│ │ ├── FrameworkJobSchedulerServiceTest.java
│ │ ├── FrameworkSchedulerTest.java
│ │ ├── GcmJobSchedulerServiceTest.java
│ │ ├── GcmSchedulerTest.java
│ │ ├── MockFwService.java
│ │ └── MockGcmService.java
│ │ ├── test
│ │ ├── TestBase.java
│ │ ├── jobmanager
│ │ │ ├── AddInBackgroundTest.java
│ │ │ ├── AddedCountTest.java
│ │ │ ├── ApplicationContextTests.java
│ │ │ ├── AssertThreadsTest.java
│ │ │ ├── CallbackTest.java
│ │ │ ├── CancelBeforeRunningTest.java
│ │ │ ├── CancelFailingJobsTest.java
│ │ │ ├── CancelWhileRunningTest.java
│ │ │ ├── CancelWhileRunningWithGroupsTest.java
│ │ │ ├── CancelWithNetworkToggleTest.java
│ │ │ ├── ClearTest.java
│ │ │ ├── ConsumerCountTest.java
│ │ │ ├── CountTest.java
│ │ │ ├── DeadlineTest.java
│ │ │ ├── DelayTest.java
│ │ │ ├── DelayedRunTest.java
│ │ │ ├── GroupingTest.java
│ │ │ ├── HitDeadlineAfterException.java
│ │ │ ├── InjectorTest.java
│ │ │ ├── JobManagerTestBase.java
│ │ │ ├── JobStatusTest.java
│ │ │ ├── KeepAliveTest.java
│ │ │ ├── LoadFactorTest.java
│ │ │ ├── MultiThreadTest.java
│ │ │ ├── NetworkJobTest.java
│ │ │ ├── NetworkJobWithConnectivityListenerTest.java
│ │ │ ├── NetworkNextJobTest.java
│ │ │ ├── NonSerializableJobTest.java
│ │ │ ├── PersistentJobTest.java
│ │ │ ├── PriorityTest.java
│ │ │ ├── ReRunWithLimitTest.java
│ │ │ ├── RetryLogicTest.java
│ │ │ ├── RunFailingJobTest.java
│ │ │ ├── RunManyNonPersistentTest.java
│ │ │ ├── SchedulerSimpleTestCase.java
│ │ │ ├── SessionIdTest.java
│ │ │ ├── SingleIdTest.java
│ │ │ ├── SlowOnAddedTest.java
│ │ │ └── ThreadFactoryTest.java
│ │ ├── jobqueue
│ │ │ ├── CachedPersistentJobQueueTest.java
│ │ │ ├── JobParamsTest.java
│ │ │ ├── JobQueueTestBase.java
│ │ │ ├── SimpleInMemoryJobQueueTest.java
│ │ │ └── SqliteJobQueueTest.java
│ │ ├── jobs
│ │ │ └── DummyJob.java
│ │ ├── timer
│ │ │ └── MockTimer.java
│ │ └── util
│ │ │ ├── JobQueueFactory.java
│ │ │ └── RunningJobSetTest.java
│ │ └── testing
│ │ ├── CleanupRule.java
│ │ ├── CollectLogsRule.java
│ │ ├── StackTraceRule.java
│ │ └── ThreadDumpRule.java
│ └── resources
│ └── robolectric.properties
├── lint
├── build.gradle
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
│ └── main
│ └── java
│ └── com
│ └── birbit
│ └── android
│ └── jobqueue
│ └── lint
│ ├── JobQueueDevelopmentLintRegistery.java
│ ├── NotifyOnObjectDetector.java
│ ├── SleepDetector.java
│ ├── SystemTimeDetector.java
│ └── WaitOnObjectWithTimeDetector.java
└── settings.gradle
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | build:
7 | name: JDK ${{ matrix.java_version }}
8 | runs-on: ubuntu-latest
9 | strategy:
10 | matrix:
11 | java_version: [1.8]
12 | steps:
13 | - name: Checkout
14 | uses: actions/checkout@v1
15 | - name: Gradle Wrapper Validation
16 | uses: gradle/wrapper-validation-action@v1
17 | - name: Install JDK ${{ matrix.java_version }}
18 | uses: actions/setup-java@v1
19 | with:
20 | java-version: ${{ matrix.java_version }}
21 | - name: Build with Gradle
22 | run: ./gradlew check :jobqueue:createDebugCoverageReport --stacktrace
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # built application files
2 | *.apk
3 | *.ap_
4 |
5 | # files for the dex VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # generated files
12 | bin/
13 | gen/
14 |
15 | # Local configuration file (sdk path, etc)
16 | local.properties
17 |
18 | # release configuration
19 | releaseConfig.properties
20 |
21 | # Eclipse project files
22 | .classpath
23 | .project
24 | .idea
25 | .css
26 | *.html
27 | .img
28 | .DS_Store
29 | jobqueue/out
30 | coverage-report
31 | junitvmwatcher*.properties
32 | jobqueue/cobertura.ser
33 | jobqueue/javadoc
34 | out
35 | *.iml
36 | pages
37 | .gradle
38 | build
39 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | google()
4 | jcenter()
5 | mavenCentral()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:3.5.3'
10 | classpath 'com.vanniktech:gradle-maven-publish-plugin:0.8.0'
11 | classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2'
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | google()
18 | mavenCentral()
19 | jcenter()
20 | }
21 | }
--------------------------------------------------------------------------------
/examples/twitter/README.md:
--------------------------------------------------------------------------------
1 | **build and run**
2 | gradle installDebug runTwitter
--------------------------------------------------------------------------------
/examples/twitter/TwitterClient/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | class RunApk extends DefaultTask {
4 | @TaskAction
5 | def runApp() {
6 | def adbExec = "${project.android.getSdkDirectory().getAbsolutePath()}/platform-tools/adb"
7 | println "running path on device or emulator"
8 | println "adb:" + adbExec
9 | def cmd = adbExec + " shell am start -a android.intent.action.MAIN -n com.birbit.android.jobqueue.examples.twitter/com.birbit.android.jobqueue.examples.twitter.SampleTwitterClient"
10 | println cmd
11 | def proc = cmd.execute()
12 | proc.in.eachLine {line -> println line}
13 | proc.err.eachLine {line -> println 'ERROR: ' + line}
14 | proc.waitFor()
15 | }
16 | }
17 |
18 | tasks.create(name: "runTwitter", type: RunApk)
19 |
20 | android {
21 | compileSdkVersion 29
22 | defaultConfig {
23 | applicationId "com.birbit.android.jobqueue.examples.twitter"
24 | minSdkVersion 15
25 | targetSdkVersion 29
26 | }
27 |
28 | dependencies {
29 | implementation 'org.greenrobot:greendao:3.2.2'
30 | implementation 'org.greenrobot:eventbus:3.1.1'
31 | implementation 'androidx.annotation:annotation:1.1.0'
32 | implementation 'org.twitter4j:twitter4j-core:3.0.5'
33 | implementation project(":jobqueue")
34 | implementation 'com.google.android.gms:play-services-gcm:17.0.0'
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/examples/twitter/TwitterClient/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
9 |
11 |
12 |
13 |
14 |
15 |
16 |
18 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/examples/twitter/TwitterClient/src/main/java/com/birbit/android/jobqueue/examples/twitter/Config.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.examples.twitter;
2 |
3 | public class Config {
4 | public static final String CONSUMER_KEY = "GfonALJ3wScsJfPsjLpl5g";
5 | public static final String CONSUMER_SECRET = "7EYvqptQeBQ9FFcbaPHv0WVe9rRbDi8dmX9DffIMIE";
6 |
7 | public static final String ACCESS_TOKEN = "1443060589-h6JU83NsHMYx5M47Is2RzlVZmvHPbxQND9xT6KQ";
8 | public static final String ACCESS_TOKEN_SECRET = "QLut9Mgwge5WptlVnCz9wxmbJrqBFNazkEYrGDZKYE";
9 |
10 | public static final String REQUEST_TOKEN_URL = "https://api.twitter.com/oauth/request_token";
11 | public static final String AUTHORIZE_URL = "https://api.twitter.com/oauth/authorize";
12 | public static final String ACCESS_TOKEN_URL = "https://api.twitter.com/oauth/access_token";
13 | }
14 |
--------------------------------------------------------------------------------
/examples/twitter/TwitterClient/src/main/java/com/birbit/android/jobqueue/examples/twitter/Test.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.examples.twitter;
2 |
3 | /**
4 | * Created by yboyar on 3/20/16.
5 | */
6 | public class Test {
7 | }
8 |
--------------------------------------------------------------------------------
/examples/twitter/TwitterClient/src/main/java/com/birbit/android/jobqueue/examples/twitter/TwitterApplication.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.examples.twitter;
2 |
3 | import android.app.Application;
4 | import android.os.Build;
5 | import android.util.Log;
6 |
7 | import com.birbit.android.jobqueue.examples.twitter.services.MyGcmJobService;
8 | import com.birbit.android.jobqueue.scheduling.FrameworkJobSchedulerService;
9 | import com.birbit.android.jobqueue.JobManager;
10 | import com.birbit.android.jobqueue.config.Configuration;
11 | import com.birbit.android.jobqueue.examples.twitter.services.MyJobService;
12 | import com.birbit.android.jobqueue.log.CustomLogger;
13 | import com.birbit.android.jobqueue.scheduling.GcmJobSchedulerService;
14 | import com.google.android.gms.common.ConnectionResult;
15 | import com.google.android.gms.common.GoogleApiAvailability;
16 |
17 |
18 | public class TwitterApplication extends Application {
19 | private static TwitterApplication instance;
20 | private JobManager jobManager;
21 |
22 | public TwitterApplication() {
23 | instance = this;
24 | }
25 |
26 | @Override
27 | public void onCreate() {
28 | super.onCreate();
29 | getJobManager();// ensure it is created
30 | }
31 |
32 | private void configureJobManager() {
33 | Configuration.Builder builder = new Configuration.Builder(this)
34 | .customLogger(new CustomLogger() {
35 | private static final String TAG = "JOBS";
36 | @Override
37 | public boolean isDebugEnabled() {
38 | return true;
39 | }
40 |
41 | @Override
42 | public void d(String text, Object... args) {
43 | Log.d(TAG, String.format(text, args));
44 | }
45 |
46 | @Override
47 | public void e(Throwable t, String text, Object... args) {
48 | Log.e(TAG, String.format(text, args), t);
49 | }
50 |
51 | @Override
52 | public void e(String text, Object... args) {
53 | Log.e(TAG, String.format(text, args));
54 | }
55 |
56 | @Override
57 | public void v(String text, Object... args) {
58 |
59 | }
60 | })
61 | .minConsumerCount(1)//always keep at least one consumer alive
62 | .maxConsumerCount(3)//up to 3 consumers at a time
63 | .loadFactor(3)//3 jobs per consumer
64 | .consumerKeepAlive(120);//wait 2 minute
65 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
66 | builder.scheduler(FrameworkJobSchedulerService.createSchedulerFor(this,
67 | MyJobService.class), true);
68 | } else {
69 | int enableGcm = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(this);
70 | if (enableGcm == ConnectionResult.SUCCESS) {
71 | builder.scheduler(GcmJobSchedulerService.createSchedulerFor(this,
72 | MyGcmJobService.class), true);
73 | }
74 | }
75 | jobManager = new JobManager(builder.build());
76 | }
77 |
78 | public synchronized JobManager getJobManager() {
79 | if (jobManager == null) {
80 | configureJobManager();
81 | }
82 | return jobManager;
83 | }
84 |
85 | public static TwitterApplication getInstance() {
86 | return instance;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/examples/twitter/TwitterClient/src/main/java/com/birbit/android/jobqueue/examples/twitter/activities/BaseActivity.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.examples.twitter.activities;
2 |
3 | import android.app.Activity;
4 |
5 | public class BaseActivity extends Activity {
6 | private boolean visible = false;
7 |
8 | @Override
9 | protected void onResume() {
10 | super.onResume();
11 | visible = true;
12 | }
13 |
14 | @Override
15 | protected void onPause() {
16 | super.onPause();
17 | visible = false;
18 | }
19 |
20 | public boolean isVisible() {
21 | return visible;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/examples/twitter/TwitterClient/src/main/java/com/birbit/android/jobqueue/examples/twitter/adapters/LazyListAdapter.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.examples.twitter.adapters;
2 |
3 | import android.widget.BaseAdapter;
4 | import org.greenrobot.greendao.query.LazyList;
5 |
6 | abstract public class LazyListAdapter extends BaseAdapter {
7 | LazyList lazyList = null;
8 | public LazyListAdapter() {
9 |
10 | }
11 |
12 | public LazyListAdapter(LazyList initialList) {
13 | lazyList = initialList;
14 | }
15 |
16 | public void replaceLazyList(LazyList newList) {
17 | if(lazyList != null) {
18 | lazyList.close();
19 | }
20 | lazyList = newList;
21 | notifyDataSetChanged();
22 | }
23 |
24 | @Override
25 | public int getCount() {
26 | return lazyList == null ? 0 : lazyList.size();
27 | }
28 |
29 | @Override
30 | public T getItem(int i) {
31 | return lazyList == null ? null : lazyList.get(i);
32 | }
33 |
34 | public void close() {
35 | if(lazyList != null) {
36 | lazyList.close();
37 | lazyList = null;
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/examples/twitter/TwitterClient/src/main/java/com/birbit/android/jobqueue/examples/twitter/controllers/TwitterController.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.examples.twitter.controllers;
2 |
3 | import com.birbit.android.jobqueue.examples.twitter.Config;
4 | import twitter4j.*;
5 | import twitter4j.auth.AccessToken;
6 | import twitter4j.conf.ConfigurationBuilder;
7 |
8 | import java.util.List;
9 |
10 | public class TwitterController {
11 | private static TwitterController instance;
12 | private Twitter twitter;
13 | // in a real app, this would be saved on login and come from shared preferences on startup
14 | private final long userId = 1443060589;
15 | public static final int PAGE_LENGTH = 20;
16 |
17 |
18 | public synchronized static TwitterController getInstance() {
19 | if(instance == null) {
20 | instance = new TwitterController();
21 | }
22 | return instance;
23 | }
24 |
25 | public TwitterController() {
26 | twitter = new TwitterFactory(new ConfigurationBuilder()
27 | .setOAuthAccessToken(Config.ACCESS_TOKEN)
28 | .setOAuthAccessTokenSecret(Config.ACCESS_TOKEN_SECRET)
29 | .setDebugEnabled(true)
30 | .setOAuthConsumerKey(Config.CONSUMER_KEY)
31 | .setOAuthConsumerKey(Config.CONSUMER_SECRET)
32 | .build()).getSingleton();
33 | AccessToken accessToken = new AccessToken(Config.ACCESS_TOKEN, Config.ACCESS_TOKEN_SECRET);
34 | twitter.setOAuthConsumer(Config.CONSUMER_KEY, Config.CONSUMER_SECRET);
35 | twitter.setOAuthAccessToken(accessToken);
36 | }
37 |
38 | public List loadTweets(Long sinceId) throws TwitterException {
39 | Paging paging = new Paging();
40 | paging.setCount(PAGE_LENGTH);
41 | if(sinceId != null) {
42 | paging.setSinceId(sinceId);
43 | }
44 | return twitter.getHomeTimeline(paging);
45 | }
46 |
47 | public Status postTweet(String status) throws TwitterException {
48 | return twitter.updateStatus(status);
49 | }
50 |
51 | public long getUserId() throws TwitterException {
52 | return userId;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/examples/twitter/TwitterClient/src/main/java/com/birbit/android/jobqueue/examples/twitter/dao/DaoMaster.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.examples.twitter.dao;
2 |
3 | import android.content.Context;
4 | import android.database.sqlite.SQLiteDatabase;
5 | import android.database.sqlite.SQLiteDatabase.CursorFactory;
6 | import android.database.sqlite.SQLiteOpenHelper;
7 | import android.util.Log;
8 | import org.greenrobot.greendao.AbstractDaoMaster;
9 | import org.greenrobot.greendao.AbstractDaoSession;
10 | import org.greenrobot.greendao.database.Database;
11 | import org.greenrobot.greendao.identityscope.IdentityScopeType;
12 |
13 | // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.
14 | /**
15 | * Master of DAO (schema version 3): knows all DAOs.
16 | */
17 | public class DaoMaster extends AbstractDaoMaster {
18 | public static final int SCHEMA_VERSION = 3;
19 |
20 | /** Creates underlying database table using DAOs. */
21 | public static void createAllTables(SQLiteDatabase db, boolean ifNotExists) {
22 | TweetDao.createTable(db, ifNotExists);
23 | }
24 |
25 | /** Drops underlying database table using DAOs. */
26 | public static void dropAllTables(SQLiteDatabase db, boolean ifExists) {
27 | TweetDao.dropTable(db, ifExists);
28 | }
29 |
30 | public static abstract class OpenHelper extends SQLiteOpenHelper {
31 |
32 | public OpenHelper(Context context, String name, CursorFactory factory) {
33 | super(context, name, factory, SCHEMA_VERSION);
34 | }
35 |
36 | @Override
37 | public void onCreate(SQLiteDatabase db) {
38 | Log.i("greenDAO", "Creating tables for schema version " + SCHEMA_VERSION);
39 | createAllTables(db, false);
40 | }
41 | }
42 |
43 | /** WARNING: Drops all table on Upgrade! Use only during development. */
44 | public static class DevOpenHelper extends OpenHelper {
45 | public DevOpenHelper(Context context, String name, CursorFactory factory) {
46 | super(context, name, factory);
47 | }
48 |
49 | @Override
50 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
51 | Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables");
52 | dropAllTables(db, true);
53 | onCreate(db);
54 | }
55 | }
56 |
57 | public DaoMaster(Database db, int schemaVersion) {
58 | super(db, schemaVersion);
59 | registerDaoClass(TweetDao.class);
60 | }
61 |
62 | @Override public AbstractDaoSession newSession(IdentityScopeType type) {
63 | return new DaoSession(db, type, daoConfigMap);
64 | }
65 |
66 | public DaoSession newSession() {
67 | return new DaoSession(db, IdentityScopeType.Session, daoConfigMap);
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/examples/twitter/TwitterClient/src/main/java/com/birbit/android/jobqueue/examples/twitter/dao/DaoSession.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.examples.twitter.dao;
2 |
3 | import java.util.Map;
4 |
5 | import com.birbit.android.jobqueue.examples.twitter.entities.Tweet;
6 | import org.greenrobot.greendao.AbstractDao;
7 | import org.greenrobot.greendao.AbstractDaoSession;
8 | import org.greenrobot.greendao.database.Database;
9 | import org.greenrobot.greendao.identityscope.IdentityScopeType;
10 | import org.greenrobot.greendao.internal.DaoConfig;
11 |
12 | // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.
13 |
14 | /**
15 | * {@inheritDoc}
16 | *
17 | * @see de.greenrobot.dao.AbstractDaoSession
18 | */
19 | public class DaoSession extends AbstractDaoSession {
20 |
21 | private final DaoConfig tweetDaoConfig;
22 |
23 | private final TweetDao tweetDao;
24 |
25 | public DaoSession(Database db, IdentityScopeType type, Map>, DaoConfig>
26 | daoConfigMap) {
27 | super(db);
28 |
29 | tweetDaoConfig = daoConfigMap.get(TweetDao.class).clone();
30 | tweetDaoConfig.initIdentityScope(type);
31 |
32 | tweetDao = new TweetDao(tweetDaoConfig, this);
33 |
34 | registerDao(Tweet.class, tweetDao);
35 | }
36 |
37 | public void clear() {
38 | tweetDaoConfig.getIdentityScope().clear();
39 | }
40 |
41 | public void deleteAllData() {
42 | tweetDao.deleteAll();
43 | }
44 |
45 |
46 | public TweetDao getTweetDao() {
47 | return tweetDao;
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/examples/twitter/TwitterClient/src/main/java/com/birbit/android/jobqueue/examples/twitter/entities/Tweet.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.examples.twitter.entities;
2 |
3 |
4 |
5 | // THIS CODE IS GENERATED BY greenDAO, EDIT ONLY INSIDE THE "KEEP"-SECTIONS
6 |
7 | // KEEP INCLUDES - put your custom includes here
8 | import twitter4j.Status;
9 | // KEEP INCLUDES END
10 | /**
11 | * Entity mapped to table TWEET.
12 | */
13 | public class Tweet extends TweetBase {
14 | // KEEP FIELDS - put your custom fields here
15 | // KEEP FIELDS END
16 | public Tweet() {
17 | }
18 |
19 | public Tweet(Long localId) {
20 | super(localId);
21 | }
22 |
23 | public Tweet(Long localId, Long serverId, String text, Long userId, Boolean isLocal, java.util.Date createdAt) {
24 | super(localId, serverId, text, userId, isLocal, createdAt);
25 | }
26 |
27 | // KEEP METHODS - put your custom methods here
28 | public Tweet(Status status) {
29 | super(null, status.getId(), status.getText(), status.getUser().getId(), false, status.getCreatedAt());
30 | }
31 |
32 | @Override
33 | public void onBeforeSave() {
34 | isLocal = serverId == null;
35 | super.onBeforeSave();
36 | }
37 | // KEEP METHODS END
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/examples/twitter/TwitterClient/src/main/java/com/birbit/android/jobqueue/examples/twitter/entities/TweetBase.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.examples.twitter.entities;
2 |
3 |
4 |
5 |
6 |
7 |
8 | // THIS CODE IS GENERATED BY greenDAO, EDIT ONLY INSIDE THE "KEEP"-SECTIONS
9 |
10 | // KEEP INCLUDES - put your custom includes here
11 | // KEEP INCLUDES END
12 | /**
13 | * Entity mapped to table TWEET.
14 | */
15 | abstract public class TweetBase {
16 |
17 | protected Long localId;
18 | protected Long serverId;
19 | protected String text;
20 | protected Long userId;
21 | protected Boolean isLocal;
22 | protected java.util.Date createdAt;
23 |
24 |
25 |
26 |
27 | // KEEP FIELDS - put your custom fields here
28 | // KEEP FIELDS END
29 |
30 | public TweetBase() {
31 | }
32 |
33 | public TweetBase(Long localId) {
34 | this.localId = localId;
35 | }
36 |
37 | public TweetBase(Long localId, Long serverId, String text, Long userId, Boolean isLocal, java.util.Date createdAt) {
38 | this.localId = localId;
39 | this.serverId = serverId;
40 | this.text = text;
41 | this.userId = userId;
42 | this.isLocal = isLocal;
43 | this.createdAt = createdAt;
44 | }
45 |
46 | public Long getLocalId() {
47 | return localId;
48 | }
49 |
50 | public void setLocalId(Long localId) {
51 | this.localId = localId;
52 | }
53 |
54 | public Long getServerId() {
55 | return serverId;
56 | }
57 |
58 | public void setServerId(Long serverId) {
59 | this.serverId = serverId;
60 | }
61 |
62 | public String getText() {
63 | return text;
64 | }
65 |
66 | public void setText(String text) {
67 | this.text = text;
68 | }
69 |
70 | public Long getUserId() {
71 | return userId;
72 | }
73 |
74 | public void setUserId(Long userId) {
75 | this.userId = userId;
76 | }
77 |
78 | public Boolean getIsLocal() {
79 | return isLocal;
80 | }
81 |
82 | public void setIsLocal(Boolean isLocal) {
83 | this.isLocal = isLocal;
84 | }
85 |
86 | public java.util.Date getCreatedAt() {
87 | return createdAt;
88 | }
89 |
90 | public void setCreatedAt(java.util.Date createdAt) {
91 | this.createdAt = createdAt;
92 | }
93 |
94 | public void updateNotNull(Tweet other) {
95 | if(this == other) {
96 | return;//both came from db, no need to run this.
97 | }
98 |
99 | if(other.localId != null) {
100 | this.localId = other.localId;
101 | }
102 |
103 |
104 | if(other.serverId != null) {
105 | this.serverId = other.serverId;
106 | }
107 |
108 |
109 | if(other.text != null) {
110 | this.text = other.text;
111 | }
112 |
113 |
114 | if(other.userId != null) {
115 | this.userId = other.userId;
116 | }
117 |
118 |
119 | if(other.isLocal != null) {
120 | this.isLocal = other.isLocal;
121 | }
122 |
123 |
124 | if(other.createdAt != null) {
125 | this.createdAt = other.createdAt;
126 | }
127 |
128 | // relationships
129 | }
130 |
131 |
132 | // KEEP METHODS - put your custom methods here
133 | // KEEP METHODS END
134 |
135 | public void onBeforeSave() {
136 | //you can override this method and do some stuff if you want to :)
137 |
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/examples/twitter/TwitterClient/src/main/java/com/birbit/android/jobqueue/examples/twitter/events/DeletedTweetEvent.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.examples.twitter.events;
2 |
3 | public class DeletedTweetEvent {
4 | private long id;
5 | public DeletedTweetEvent(long id) {
6 | this.id = id;
7 | }
8 |
9 | public long getId() {
10 | return id;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/examples/twitter/TwitterClient/src/main/java/com/birbit/android/jobqueue/examples/twitter/events/FetchedNewTweetsEvent.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.examples.twitter.events;
2 |
3 | public class FetchedNewTweetsEvent {
4 | }
5 |
--------------------------------------------------------------------------------
/examples/twitter/TwitterClient/src/main/java/com/birbit/android/jobqueue/examples/twitter/events/PostedTweetEvent.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.examples.twitter.events;
2 |
3 | import com.birbit.android.jobqueue.examples.twitter.entities.Tweet;
4 |
5 | public class PostedTweetEvent {
6 | private Tweet tweet;
7 | private long localId;
8 |
9 | public PostedTweetEvent(Tweet tweet, long localId) {
10 | this.tweet = tweet;
11 | this.localId = localId;
12 | }
13 |
14 | public Tweet getTweet() {
15 | return tweet;
16 | }
17 |
18 | public long getLocalId() {
19 | return localId;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/examples/twitter/TwitterClient/src/main/java/com/birbit/android/jobqueue/examples/twitter/events/PostingTweetEvent.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.examples.twitter.events;
2 |
3 | import com.birbit.android.jobqueue.examples.twitter.entities.Tweet;
4 |
5 | public class PostingTweetEvent {
6 | private Tweet tweeet;
7 |
8 | public PostingTweetEvent(Tweet tweeet) {
9 | this.tweeet = tweeet;
10 | }
11 |
12 | public Tweet getTweeet() {
13 | return tweeet;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/examples/twitter/TwitterClient/src/main/java/com/birbit/android/jobqueue/examples/twitter/jobs/FetchTweetsJob.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.examples.twitter.jobs;
2 |
3 | import androidx.annotation.Nullable;
4 |
5 | import com.birbit.android.jobqueue.CancelReason;
6 | import com.birbit.android.jobqueue.Job;
7 | import com.birbit.android.jobqueue.Params;
8 | import com.birbit.android.jobqueue.RetryConstraint;
9 | import com.birbit.android.jobqueue.examples.twitter.controllers.TwitterController;
10 | import com.birbit.android.jobqueue.examples.twitter.entities.Tweet;
11 | import com.birbit.android.jobqueue.examples.twitter.events.FetchedNewTweetsEvent;
12 | import com.birbit.android.jobqueue.examples.twitter.models.TweetModel;
13 | import org.greenrobot.eventbus.EventBus;
14 | import twitter4j.Status;
15 | import twitter4j.TwitterException;
16 |
17 | import java.util.ArrayList;
18 | import java.util.List;
19 |
20 |
21 | public class FetchTweetsJob extends Job {
22 |
23 | public FetchTweetsJob() {
24 | //use singleWith so that if the same job has already been added and is not yet running,
25 | //it will only run once.
26 | super(new Params(Priority.LOW).requireNetwork().groupBy("fetch-tweets").singleInstanceBy("fetch-tweets"));
27 | }
28 |
29 | @Override
30 | public void onAdded() {
31 |
32 | }
33 |
34 | @Override
35 | public void onRun() throws Throwable {
36 | TweetModel tweetModel = TweetModel.getInstance();
37 | Tweet lastTweet = tweetModel.getLastTweet();
38 | List statusList = TwitterController.getInstance().loadTweets(lastTweet == null ? null : lastTweet.getServerId());
39 | if(statusList.size() > 0) {
40 | List tweets = new ArrayList(statusList.size());
41 | for(Status status : statusList) {
42 | Tweet tweet = new Tweet(status);
43 | tweets.add(tweet);
44 | }
45 | tweetModel.insertOrReplaceAll(tweets);
46 | EventBus.getDefault().post(new FetchedNewTweetsEvent());
47 | }
48 | }
49 |
50 | @Override
51 | protected void onCancel(@CancelReason int cancelReason, @Nullable Throwable throwable) {
52 |
53 | }
54 |
55 | @Override
56 | protected RetryConstraint shouldReRunOnThrowable(Throwable throwable, int runCount,
57 | int maxRunCount) {
58 | if(throwable instanceof TwitterException) {
59 | //if it is a 4xx error, stop
60 | TwitterException twitterException = (TwitterException) throwable;
61 | int errorCode = twitterException.getErrorCode();
62 | return errorCode < 400 || errorCode > 499 ? RetryConstraint.RETRY : RetryConstraint.CANCEL;
63 | }
64 | return RetryConstraint.RETRY;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/examples/twitter/TwitterClient/src/main/java/com/birbit/android/jobqueue/examples/twitter/jobs/Priority.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.examples.twitter.jobs;
2 |
3 | public class Priority {
4 | public static int LOW = 0;
5 | public static int MID = 500;
6 | public static int HIGH = 1000;
7 | }
8 |
--------------------------------------------------------------------------------
/examples/twitter/TwitterClient/src/main/java/com/birbit/android/jobqueue/examples/twitter/models/DbHelper.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.examples.twitter.models;
2 |
3 | import android.content.Context;
4 | import android.database.sqlite.SQLiteDatabase;
5 | import com.birbit.android.jobqueue.examples.twitter.TwitterApplication;
6 | import com.birbit.android.jobqueue.examples.twitter.dao.DaoMaster;
7 | import com.birbit.android.jobqueue.examples.twitter.dao.DaoSession;
8 | import org.greenrobot.greendao.database.StandardDatabase;
9 |
10 | public class DbHelper {
11 | private static DbHelper instance;
12 | private DaoSession daoSession;
13 | private DaoMaster daoMaster;
14 | private SQLiteDatabase db;
15 |
16 | public synchronized static DbHelper getInstance() {
17 | if(instance == null) {
18 | instance = new DbHelper();
19 | }
20 | return instance;
21 | }
22 |
23 | public DbHelper() {
24 | Context appContext = TwitterApplication.getInstance().getApplicationContext();
25 | DaoMaster.DevOpenHelper devOpenHelper = new DaoMaster.DevOpenHelper(appContext, "twitter", null);
26 | db = devOpenHelper.getWritableDatabase();
27 | StandardDatabase standardDatabase = new StandardDatabase(db);
28 | daoMaster = new DaoMaster(standardDatabase, DaoMaster.SCHEMA_VERSION);
29 | daoSession = daoMaster.newSession();
30 | }
31 |
32 | public DaoSession getDaoSession() {
33 | return daoSession;
34 | }
35 |
36 | public DaoMaster getDaoMaster() {
37 | return daoMaster;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/examples/twitter/TwitterClient/src/main/java/com/birbit/android/jobqueue/examples/twitter/models/TweetModel.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.examples.twitter.models;
2 |
3 | import com.birbit.android.jobqueue.examples.twitter.dao.TweetDao;
4 | import com.birbit.android.jobqueue.examples.twitter.entities.Tweet;
5 |
6 | import java.util.Collection;
7 | import org.greenrobot.greendao.query.LazyList;
8 |
9 | public class TweetModel {
10 | private static TweetModel instance;
11 | private TweetDao tweetDao;
12 |
13 | public synchronized static TweetModel getInstance() {
14 | if(instance == null) {
15 | instance = new TweetModel();
16 | }
17 | return instance;
18 | }
19 |
20 | private TweetModel() {
21 | tweetDao = DbHelper.getInstance().getDaoSession().getTweetDao();
22 | }
23 |
24 | public Tweet getLastTweet() {
25 | return tweetDao.queryBuilder().where(TweetDao.Properties.ServerId.isNotNull())
26 | .orderDesc(TweetDao.Properties.CreatedAt)
27 | .limit(1).unique();
28 | }
29 |
30 | public void insertOrReplace(Tweet tweet) {
31 | tweetDao.insertOrReplace(tweet);
32 | }
33 |
34 | public void insertOrReplaceAll(Collection tweets) {
35 | tweetDao.insertOrReplaceInTx(tweets);
36 | }
37 |
38 | public LazyList lazyLoadTweets() {
39 | return tweetDao.queryBuilder().orderDesc(TweetDao.Properties.IsLocal, TweetDao.Properties.CreatedAt).listLazy();
40 |
41 | }
42 |
43 | public Tweet getTweetByLocalId(long localId) {
44 | return tweetDao.queryBuilder().where(TweetDao.Properties.LocalId.eq(localId)).limit(1).unique();
45 | }
46 |
47 | public void deleteTweetById(long localId) {
48 | tweetDao.deleteByKey(localId);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/examples/twitter/TwitterClient/src/main/java/com/birbit/android/jobqueue/examples/twitter/services/MyGcmJobService.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.examples.twitter.services;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.birbit.android.jobqueue.JobManager;
6 | import com.birbit.android.jobqueue.examples.twitter.TwitterApplication;
7 | import com.birbit.android.jobqueue.scheduling.GcmJobSchedulerService;
8 |
9 | /**
10 | * Created by yboyar on 3/20/16.
11 | */
12 | public class MyGcmJobService extends GcmJobSchedulerService {
13 | @NonNull
14 | @Override
15 | protected JobManager getJobManager() {
16 | return TwitterApplication.getInstance().getJobManager();
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/examples/twitter/TwitterClient/src/main/java/com/birbit/android/jobqueue/examples/twitter/services/MyJobService.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.examples.twitter.services;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.birbit.android.jobqueue.JobManager;
6 | import com.birbit.android.jobqueue.scheduling.FrameworkJobSchedulerService;
7 | import com.birbit.android.jobqueue.examples.twitter.TwitterApplication;
8 |
9 | public class MyJobService extends FrameworkJobSchedulerService {
10 | @NonNull
11 | @Override
12 | protected JobManager getJobManager() {
13 | return TwitterApplication.getInstance().getJobManager();
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/examples/twitter/TwitterClient/src/main/java/com/birbit/android/jobqueue/examples/twitter/tasks/SimpleBackgroundTask.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.examples.twitter.tasks;
2 |
3 | import android.app.Activity;
4 | import android.os.AsyncTask;
5 |
6 | import java.lang.ref.WeakReference;
7 |
8 | abstract public class SimpleBackgroundTask extends AsyncTask {
9 | WeakReference weakActivity;
10 | public SimpleBackgroundTask(Activity activity) {
11 | weakActivity = new WeakReference(activity);
12 | }
13 | @Override
14 | protected final T doInBackground(Void... voids) {
15 | return onRun();
16 | }
17 |
18 | private boolean canContinue() {
19 | Activity activity = weakActivity.get();
20 | return activity != null && activity.isFinishing() == false;
21 | }
22 |
23 | @Override
24 | protected void onPostExecute(T t) {
25 | if(canContinue()) {
26 | onSuccess(t);
27 | }
28 | }
29 |
30 | abstract protected T onRun();
31 |
32 | abstract protected void onSuccess(T result);
33 | }
34 |
--------------------------------------------------------------------------------
/examples/twitter/TwitterClient/src/main/res/layout/list_tweet.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
11 |
--------------------------------------------------------------------------------
/examples/twitter/TwitterClient/src/main/res/layout/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
12 |
15 |
20 |
25 |
26 |
27 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/examples/twitter/TwitterClient/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | TwitterClient
4 | Update your status
5 | Send
6 |
7 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | android.enableJetifier=true
2 | android.useAndroidX=true
3 |
4 | SONATYPE_NEXUS_USERNAME=yigit
5 | SONATYPE_NEXUS_PASSWORD=
6 | signing.keyId=
7 | signing.password=
8 | signing.secretKeyRingFile=/home/yboyar/src/android-priority-jobqueue/secring.gpg
9 | GROUP=com.birbit
10 | VERSION_NAME=3.0.0
11 |
12 | POM_DESCRIPTION=a Job Queue specifically written for Android to easily schedule jobs (tasks) that run in the background, improving UX and application stability.
13 | POM_INCEPTION_YEAR=2016
14 |
15 | POM_URL=https://github.com/yigit/android-priority-jobqueue/
16 | POM_SCM_URL=https://github.com/yigit/android-priority-jobqueue/
17 | POM_SCM_CONNECTION=scm:git:git://github.com/yigit/android-priority-jobqueue.git
18 | POM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com/yigit/android-priority-jobqueue.git
19 |
20 | POM_LICENCE_NAME=The MIT License (MIT)
21 | POM_LICENCE_URL=http://opensource.org/licenses/MIT
22 | POM_LICENCE_DIST=repo
23 |
24 | POM_DEVELOPER_ID=yigit
25 | POM_DEVELOPER_NAME=Yigit Boyar
26 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yigit/android-priority-jobqueue/360c090857eb0f6efbdde04a7c8bb5cc31582eb9/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1-all.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
34 |
35 | @rem Find java.exe
36 | if defined JAVA_HOME goto findJavaFromJavaHome
37 |
38 | set JAVA_EXE=java.exe
39 | %JAVA_EXE% -version >NUL 2>&1
40 | if "%ERRORLEVEL%" == "0" goto init
41 |
42 | echo.
43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
44 | echo.
45 | echo Please set the JAVA_HOME variable in your environment to match the
46 | echo location of your Java installation.
47 |
48 | goto fail
49 |
50 | :findJavaFromJavaHome
51 | set JAVA_HOME=%JAVA_HOME:"=%
52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
53 |
54 | if exist "%JAVA_EXE%" goto init
55 |
56 | echo.
57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
58 | echo.
59 | echo Please set the JAVA_HOME variable in your environment to match the
60 | echo location of your Java installation.
61 |
62 | goto fail
63 |
64 | :init
65 | @rem Get command-line arguments, handling Windows variants
66 |
67 | if not "%OS%" == "Windows_NT" goto win9xME_args
68 |
69 | :win9xME_args
70 | @rem Slurp the command line arguments.
71 | set CMD_LINE_ARGS=
72 | set _SKIP=2
73 |
74 | :win9xME_args_slurp
75 | if "x%~1" == "x" goto execute
76 |
77 | set CMD_LINE_ARGS=%*
78 |
79 | :execute
80 | @rem Setup the command line
81 |
82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
83 |
84 | @rem Execute Gradle
85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
86 |
87 | :end
88 | @rem End local scope for the variables with windows NT shell
89 | if "%ERRORLEVEL%"=="0" goto mainEnd
90 |
91 | :fail
92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
93 | rem the _cmd.exe /c_ return code!
94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
95 | exit /b 1
96 |
97 | :mainEnd
98 | if "%OS%"=="Windows_NT" endlocal
99 |
100 | :omega
101 |
--------------------------------------------------------------------------------
/jobqueue/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 Path, Inc.
4 | Copyright (c) 2014 Google, Inc.
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in
14 | all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | THE SOFTWARE.
--------------------------------------------------------------------------------
/jobqueue/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'com.vanniktech.maven.publish'
3 |
4 | dependencies {
5 | testImplementation 'junit:junit:4.12'
6 | testImplementation "org.robolectric:robolectric:4.3.1"
7 | testImplementation 'org.hamcrest:hamcrest-core:1.3'
8 | testImplementation 'org.easytesting:fest-util:1.2.5'
9 | testImplementation 'org.easytesting:fest-reflect:1.4.1'
10 | testImplementation 'org.mockito:mockito-core:2.7.13'
11 | testImplementation 'androidx.core:core:1.1.0'
12 | testImplementation 'com.google.android.gms:play-services-gcm:17.0.0'
13 |
14 | implementation 'com.squareup.okio:okio:1.17.5'
15 |
16 | compileOnly 'com.google.android.gms:play-services-gcm:17.0.0'
17 | }
18 |
19 | android {
20 | compileSdkVersion 29
21 | buildTypes {
22 | debug {
23 | testCoverageEnabled = true
24 | }
25 | release {
26 | testCoverageEnabled = false
27 | }
28 | }
29 |
30 | defaultConfig {
31 | minSdkVersion 14
32 | }
33 |
34 | testOptions {
35 | unitTests.all {
36 | // All the usual Gradle options.
37 | jvmArgs '-Xmx2000m', '-XX:+HeapDumpOnOutOfMemoryError', "-XX:HeapDumpPath=${System.env.CIRCLE_ARTIFACTS == null ? "." : System.env.CIRCLE_ARTIFACTS}/oom.hprof"
38 | }
39 | }
40 |
41 | lintOptions {
42 | warning 'InvalidPackage'
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/jobqueue/gradle.properties:
--------------------------------------------------------------------------------
1 | POM_ARTIFACT_ID=android-priority-jobqueue
2 | POM_NAME=Android Priority Job Queue
3 | POM_PACKAGING=aar
--------------------------------------------------------------------------------
/jobqueue/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/AsyncAddCallback.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue;
2 |
3 | import android.app.Activity;
4 |
5 | /**
6 | * If you are adding the job via the async adder, you can provide a callback method to confirm when it was added.
7 | * Please keep in mind that job manager will keep a strong reference to this callback. So if the callback is an
8 | * anonymous class inside an {@link Activity} context, it may leak the activity until the job is added.
9 | */
10 | public interface AsyncAddCallback {
11 | void onAdded();
12 | }
13 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/CancelReason.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue;
2 |
3 | import androidx.annotation.IntDef;
4 |
5 | import java.lang.annotation.Retention;
6 | import java.lang.annotation.RetentionPolicy;
7 |
8 | /**
9 | * A list of possible reasons why a Job was cancelled. A reason will be passed to {@link Job#onCancel(int, Throwable)}.
10 | */
11 | @Retention(RetentionPolicy.SOURCE)
12 | @IntDef({CancelReason.REACHED_RETRY_LIMIT,
13 | CancelReason.CANCELLED_WHILE_RUNNING,
14 | CancelReason.SINGLE_INSTANCE_WHILE_RUNNING,
15 | CancelReason.CANCELLED_VIA_SHOULD_RE_RUN,
16 | CancelReason.SINGLE_INSTANCE_ID_QUEUED,
17 | CancelReason.REACHED_DEADLINE})
18 | public @interface CancelReason {
19 | /**
20 | * Used when a job was added while another job with the same single instance ID was already
21 | * queued and not running. This job got cancelled immediately after being added and will not run.
22 | */
23 | int SINGLE_INSTANCE_ID_QUEUED = 1;
24 | /**
25 | * Used when job throws an exception in {@link Job#onRun()}
26 | * and will be cancelled because it has reached its retry limit.
27 | *
28 | * @see Job#getRetryLimit()
29 | */
30 | int REACHED_RETRY_LIMIT = JobHolder.RUN_RESULT_FAIL_RUN_LIMIT;
31 |
32 | /**
33 | * Used when a job is cancelled via {@link JobManager#cancelJobs(TagConstraint, String...)}.
34 | * It might be waiting to be run or might have thrown an exception in {@link Job#onRun()}.
35 | *
36 | * @see JobManager#cancelJobs(TagConstraint, String...)
37 | * @see JobManager#cancelJobsInBackground(CancelResult.AsyncCancelCallback, TagConstraint, String...)
38 | */
39 | int CANCELLED_WHILE_RUNNING = JobHolder.RUN_RESULT_FAIL_FOR_CANCEL;
40 |
41 | /**
42 | * Used when job throws an exception in {@link Job#onRun()}
43 | * and will be cancelled because another job with the same single instance id was
44 | * queued while it was running.
45 | *
46 | * @see Job#getSingleInstanceId()
47 | */
48 | int SINGLE_INSTANCE_WHILE_RUNNING = JobHolder.RUN_RESULT_FAIL_SINGLE_ID;
49 |
50 | /**
51 | * Used when job throws an exception in {@link Job#onRun()}
52 | * and will be cancelled because it decided not to run again via
53 | * {@link Job#shouldReRunOnThrowable(Throwable, int, int)}.
54 | */
55 | int CANCELLED_VIA_SHOULD_RE_RUN = JobHolder.RUN_RESULT_FAIL_SHOULD_RE_RUN;
56 |
57 | /**
58 | * Used when a Job is cancelled for hitting its deadline.
59 | *
60 | * This means the job's params were constructed using
61 | * {@link Params#overrideDeadlineToCancelInMs(long)}.
62 | */
63 | int REACHED_DEADLINE = JobHolder.RUN_RESULT_HIT_DEADLINE;
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/CancelResult.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue;
2 |
3 | import java.util.Collection;
4 |
5 | /**
6 | * This class holds the result of a cancel request via {@link JobManager#cancelJobs(TagConstraint, String...)}
7 | * or {@link JobManager#cancelJobsInBackground(CancelResult.AsyncCancelCallback, TagConstraint, String...)}.
8 | *
9 | * Cancelling jobs is an expensive operation because it requires JobManager to deserializer the job
10 | * from databases and call onCancel method on it.
11 | *
12 | * When cancelling jobs, if you need to get the list of cancelled jobs, you can provide this
13 | * callback to {@link JobManager#cancelJobsInBackground(CancelResult.AsyncCancelCallback, TagConstraint, String...)}
14 | * method.
15 | */
16 | public class CancelResult {
17 | private Collection cancelledJobs;
18 | private Collection failedToCancel;
19 |
20 | public CancelResult(Collection cancelledJobs, Collection failedToCancel) {
21 | this.cancelledJobs = cancelledJobs;
22 | this.failedToCancel = failedToCancel;
23 | }
24 |
25 | /**
26 | * @return The list of jobs that are cancelled before they did run
27 | */
28 | public Collection getCancelledJobs() {
29 | return cancelledJobs;
30 | }
31 |
32 | /**
33 | * @return The list of jobs that were running when cancel was called and onFinished running
34 | * successfully before they could be cancelled.
35 | */
36 | public Collection getFailedToCancel() {
37 | return failedToCancel;
38 | }
39 |
40 | public interface AsyncCancelCallback {
41 |
42 | /**
43 | * When job cancellation is complete, this method is called by the JobManager.
44 | *
45 | * @param cancelResult The result of the cancel request
46 | */
47 | void onCancelled(CancelResult cancelResult);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/DefaultQueueFactory.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue;
2 |
3 | import com.birbit.android.jobqueue.inMemoryQueue.SimpleInMemoryPriorityQueue;
4 | import com.birbit.android.jobqueue.*;
5 | import com.birbit.android.jobqueue.cachedQueue.CachedJobQueue;
6 | import com.birbit.android.jobqueue.config.Configuration;
7 | import com.birbit.android.jobqueue.persistentQueue.sqlite.SqliteJobQueue;
8 |
9 | /**
10 | * Default implementation of QueueFactory that creates one {@link SqliteJobQueue} and
11 | * one {@link SimpleInMemoryPriorityQueue} both are wrapped inside a {@link CachedJobQueue} to
12 | * improve performance
13 | */
14 | public class DefaultQueueFactory implements QueueFactory {
15 | SqliteJobQueue.JobSerializer jobSerializer;
16 |
17 | public DefaultQueueFactory() {
18 | jobSerializer = new SqliteJobQueue.JavaSerializer();
19 | }
20 |
21 | public DefaultQueueFactory(SqliteJobQueue.JobSerializer jobSerializer) {
22 | this.jobSerializer = jobSerializer;
23 | }
24 |
25 | @Override
26 | public JobQueue createPersistentQueue(Configuration configuration, long sessionId) {
27 | return new CachedJobQueue(new SqliteJobQueue(configuration, sessionId, jobSerializer));
28 | }
29 |
30 | @Override
31 | public JobQueue createNonPersistent(Configuration configuration, long sessionId) {
32 | return new CachedJobQueue(new SimpleInMemoryPriorityQueue(configuration, sessionId));
33 | }
34 | }
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/IntCallback.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue;
2 |
3 | /**
4 | * Internal interface to mimic sync requests.
5 | */
6 | public interface IntCallback {
7 | void onResult(int result);
8 | interface MessageWithCallback {
9 | void setCallback(IntCallback intCallback);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/JobStatus.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue;
2 |
3 | /**
4 | * Identifies the current status of a job if it is in the queue
5 | */
6 | public enum JobStatus {
7 | /**
8 | * Job is in the queue but cannot run yet.
9 | * As of v 1.1, this might be:
10 | *
11 | *
Job requires network but there is no available network connection
12 | *
Job is delayed. We are waiting for the time to pass
13 | *
14 | */
15 | WAITING_NOT_READY,
16 | /**
17 | * Job is in the queue, ready to be run. Waiting for an available consumer.
18 | */
19 | WAITING_READY,
20 | /**
21 | * Job is being executed by one of the runners.
22 | */
23 | RUNNING,
24 | /**
25 | * Job is not known by job queue.
26 | *
This might be:
27 | *
28 | *
Invalid ID
29 | *
Job has been completed
30 | *
Job has failed
31 | *
Job has just been added, about to be delivered into a queue
32 | *
33 | *
34 | */
35 | UNKNOWN
36 | }
37 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/QueueFactory.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue;
2 |
3 |
4 | import com.birbit.android.jobqueue.JobQueue;
5 | import com.birbit.android.jobqueue.config.Configuration;
6 |
7 | public interface QueueFactory {
8 | JobQueue createPersistentQueue(Configuration configuration, long sessionId);
9 | JobQueue createNonPersistent(Configuration configuration, long sessionId);
10 | }
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/RunningJobSet.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue;
2 |
3 | import com.birbit.android.jobqueue.log.JqLog;
4 | import com.birbit.android.jobqueue.timer.Timer;
5 |
6 | import java.util.ArrayList;
7 | import java.util.Collection;
8 | import java.util.HashMap;
9 | import java.util.Iterator;
10 | import java.util.Map;
11 | import java.util.TreeSet;
12 |
13 | /**
14 | * a util class that holds running jobs sorted by name and unique.
15 | * it behaves like CopyOnWriteLists
16 | */
17 | public class RunningJobSet {
18 | private ArrayList publicClone;
19 | private final TreeSet internalSet;
20 | private final Map groupDelays;
21 | private long groupDelayTimeout;
22 | private final Timer timer;
23 |
24 | public RunningJobSet(Timer timer) {
25 | internalSet = new TreeSet<>();
26 | groupDelays = new HashMap<>();
27 | groupDelayTimeout = Long.MAX_VALUE;
28 | this.timer = timer;
29 | }
30 |
31 | public synchronized void addGroupUntil(String group, long until) {
32 | JqLog.d("add group delay to %s until %s", group, until);
33 | Long current = groupDelays.get(group);
34 | if (current != null) {
35 | if (current > until) {
36 | return; // nothing to change
37 | }
38 | }
39 | groupDelays.put(group, until);
40 | groupDelayTimeout = calculateNextDelayForGroups();
41 | publicClone = null;
42 | }
43 |
44 | public synchronized Collection getSafe() {
45 | final long now = timer.nanoTime();
46 | if(publicClone == null || now > groupDelayTimeout) {
47 | if (groupDelays.isEmpty()) {
48 | publicClone = new ArrayList<>(internalSet);
49 | groupDelayTimeout = Long.MAX_VALUE;
50 | } else {
51 | TreeSet tmpClone = new TreeSet<>(internalSet);
52 | Iterator> itr = groupDelays.entrySet().iterator();
53 | while(itr.hasNext()) {
54 | Map.Entry entry = itr.next();
55 | if (entry.getValue() > now) {
56 | if (!tmpClone.contains(entry.getKey())) {
57 | tmpClone.add(entry.getKey());
58 | }
59 | } else {
60 | itr.remove();
61 | }
62 | }
63 | publicClone = new ArrayList<>(tmpClone);
64 | groupDelayTimeout = calculateNextDelayForGroups();
65 | }
66 | }
67 | return publicClone;
68 | }
69 | public Long getNextDelayForGroups() {
70 | if (groupDelayTimeout == Long.MAX_VALUE) {
71 | return null;
72 | }
73 | return groupDelayTimeout;
74 | }
75 | private long calculateNextDelayForGroups() {
76 | long result = Long.MAX_VALUE;
77 | for (Long value : groupDelays.values()) {
78 | if (value < result) {
79 | result = value;
80 | }
81 | }
82 | return result;
83 | }
84 |
85 | public synchronized void add(String group) {
86 | if (group == null) {
87 | return;
88 | }
89 | if(internalSet.add(group)) {
90 | publicClone = null;//invalidate
91 | }
92 | }
93 |
94 | public synchronized void remove(String group) {
95 | if (group == null) {
96 | return;
97 | }
98 | if(internalSet.remove(group)) {
99 | publicClone = null;
100 | }
101 | }
102 |
103 | public synchronized void clear() {
104 | internalSet.clear();
105 | groupDelays.clear();
106 | publicClone = null;
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/TagConstraint.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue;
2 |
3 | import java.util.Collection;
4 | import java.util.Set;
5 |
6 | public enum TagConstraint {
7 | ALL,
8 | ANY;
9 | public boolean matches(String[] constraintTags, Set jobTags) {
10 | if (this == TagConstraint.ANY) {
11 | for (String tag : constraintTags) {
12 | if (jobTags.contains(tag)) {
13 | return true;
14 | }
15 | }
16 | return false;
17 | } else {
18 | for (String tag : constraintTags) {
19 | if (!jobTags.contains(tag)) {
20 | return false;
21 | }
22 | }
23 | return true;
24 | }
25 | }
26 |
27 | public boolean matches(Collection constraintTags, Set jobTags) {
28 | if (this == TagConstraint.ANY) {
29 | for (String tag : constraintTags) {
30 | if (jobTags.contains(tag)) {
31 | return true;
32 | }
33 | }
34 | return false;
35 | } else {
36 | for (String tag : constraintTags) {
37 | if (!jobTags.contains(tag)) {
38 | return false;
39 | }
40 | }
41 | return true;
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/WrongThreadException.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue;
2 |
3 | /**
4 | * Called when a method is called in the wrong thread. There are 2 thread restrictions:
5 | *
6 | * Some methods of the JobManager cannot be called on its own Thread where your Job's onRun method
7 | * is executed. If you call any of these methods in that method, you'll receive this exception.
8 | *
9 | * Some methods of the JobManager may take a long time to execute. If you call these methods on
10 | * the main thread, it will thrown an exception.
11 | */
12 | public class WrongThreadException extends RuntimeException {
13 | public WrongThreadException(String detailMessage) {
14 | super(detailMessage);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/cachedQueue/CachedJobQueue.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.cachedQueue;
2 |
3 | import androidx.annotation.NonNull;
4 | import androidx.annotation.Nullable;
5 |
6 | import com.birbit.android.jobqueue.Constraint;
7 | import com.birbit.android.jobqueue.JobHolder;
8 | import com.birbit.android.jobqueue.JobQueue;
9 |
10 | import java.util.Set;
11 |
12 | /**
13 | * a class that implements {@link JobQueue} interface, wraps another {@link JobQueue} and caches
14 | * results to avoid unnecessary queries to wrapped JobQueue.
15 | * does very basic caching but should be sufficient for most of the repeated cases
16 | * element
17 | */
18 | public class CachedJobQueue implements JobQueue {
19 | private JobQueue delegate;
20 | private Integer cachedCount;
21 |
22 | public CachedJobQueue(JobQueue delegate) {
23 | this.delegate = delegate;
24 | }
25 |
26 | @Override
27 | public boolean insert(@NonNull JobHolder jobHolder) {
28 | invalidateCache();
29 | return delegate.insert(jobHolder);
30 | }
31 |
32 | private void invalidateCache() {
33 | cachedCount = null;
34 | }
35 |
36 | @Override
37 | public boolean insertOrReplace(@NonNull JobHolder jobHolder) {
38 | invalidateCache();
39 | return delegate.insertOrReplace(jobHolder);
40 | }
41 |
42 | @Override
43 | public void substitute(@NonNull JobHolder newJob, @NonNull JobHolder oldJob) {
44 | invalidateCache();
45 | delegate.substitute(newJob, oldJob);
46 | }
47 |
48 | @Override
49 | public void remove(@NonNull JobHolder jobHolder) {
50 | invalidateCache();
51 | delegate.remove(jobHolder);
52 | }
53 |
54 | @Override
55 | public int count() {
56 | if(cachedCount == null) {
57 | cachedCount = delegate.count();
58 | }
59 | return cachedCount;
60 | }
61 |
62 | private boolean isEmpty() {
63 | return cachedCount != null && cachedCount == 0;
64 | }
65 |
66 | @Override
67 | public int countReadyJobs(@NonNull Constraint constraint) {
68 | if (isEmpty()) {
69 | return 0;
70 | }
71 | return delegate.countReadyJobs(constraint);
72 | }
73 |
74 | @Override
75 | public JobHolder nextJobAndIncRunCount(@NonNull Constraint constraint) {
76 | if(isEmpty()) {
77 | return null;//we know we are empty, no need for querying
78 | }
79 | JobHolder holder = delegate.nextJobAndIncRunCount(constraint);
80 | if (holder != null && cachedCount != null) {
81 | cachedCount -= 1;
82 | }
83 | return holder;
84 | }
85 |
86 | @Override
87 | public Long getNextJobDelayUntilNs(@NonNull Constraint constraint) {
88 | return delegate.getNextJobDelayUntilNs(constraint);
89 | }
90 |
91 | @Override
92 | public void clear() {
93 | invalidateCache();
94 | delegate.clear();
95 | }
96 |
97 | @Override
98 | @NonNull
99 | public Set findJobs(@NonNull Constraint constraint) {
100 | return delegate.findJobs(constraint);
101 | }
102 |
103 | @Override
104 | public void onJobCancelled(@NonNull JobHolder holder) {
105 | invalidateCache();
106 | delegate.onJobCancelled(holder);
107 | }
108 |
109 | @Override
110 | @Nullable
111 | public JobHolder findJobById(@NonNull String id) {
112 | return delegate.findJobById(id);
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/callback/JobManagerCallbackAdapter.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.callback;
2 |
3 | import androidx.annotation.NonNull;
4 | import androidx.annotation.Nullable;
5 |
6 | import com.birbit.android.jobqueue.Job;
7 |
8 | /**
9 | * An empty implementation of {@link JobManagerCallback}. You are advice to override this one
10 | * instead so that if new methods are added to the interface, your code won't break.
11 | */
12 | public class JobManagerCallbackAdapter implements JobManagerCallback {
13 | @Override
14 | public void onJobAdded(@NonNull Job job) {
15 |
16 | }
17 |
18 | @Override
19 | public void onJobRun(@NonNull Job job, int resultCode) {
20 |
21 | }
22 |
23 | @Override
24 | public void onJobCancelled(@NonNull Job job, boolean byCancelRequest, @Nullable Throwable throwable) {
25 |
26 | }
27 |
28 | @Override
29 | public void onDone(@NonNull Job job) {
30 |
31 | }
32 |
33 | @Override
34 | public void onAfterJobRun(@NonNull Job job, int resultCode) {
35 |
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/di/DependencyInjector.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.di;
2 |
3 | import com.birbit.android.jobqueue.Job;
4 |
5 | /**
6 | * interface that can be provided to {@link com.birbit.android.jobqueue.JobManager} for dependency injection
7 | * it is called before the job's onAdded method is called. for persistent jobs, also run after job is brought
8 | * back from disk.
9 | */
10 | public interface DependencyInjector {
11 | void inject(Job job);
12 | }
13 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/log/CustomLogger.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.log;
2 |
3 | /**
4 | * You can provide your own logger implementation to {@link com.birbit.android.jobqueue.JobManager}
5 | * it is very similar to Roboguice's logger
6 | */
7 | public interface CustomLogger {
8 | /**
9 | * JobManager may call this before logging something that is (relatively) expensive to calculate
10 | * @return True if debug logs are enabled
11 | */
12 | boolean isDebugEnabled();
13 | void d(String text, Object... args);
14 | void e(Throwable t, String text, Object... args);
15 | void e(String text, Object... args);
16 | void v(String text, Object... args);
17 | }
18 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/log/JqLog.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.log;
2 |
3 | import android.util.Log;
4 |
5 | /**
6 | * Wrapper around {@link CustomLogger}. by default, logs to nowhere
7 | */
8 | public class JqLog {
9 | private static CustomLogger customLogger;
10 | static {
11 | clearLogger();
12 | }
13 |
14 | public static void clearLogger() {
15 | setCustomLogger(new CustomLogger() {
16 | @Override
17 | public boolean isDebugEnabled() {
18 | return false;
19 | }
20 |
21 | @Override
22 | public void d(String text, Object... args) {
23 | //void
24 | }
25 |
26 | @Override
27 | public void e(Throwable t, String text, Object... args) {
28 | //void
29 | }
30 |
31 | @Override
32 | public void e(String text, Object... args) {
33 | //void
34 | }
35 |
36 | @Override
37 | public void v(String text, Object... args) {
38 | //void
39 | }
40 | });
41 | }
42 |
43 | public static void setCustomLogger(CustomLogger customLogger) {
44 | JqLog.customLogger = customLogger;
45 | }
46 |
47 | public static boolean isDebugEnabled() {
48 | return customLogger.isDebugEnabled();
49 | }
50 |
51 | public static void d(String text, Object... args) {
52 | customLogger.d(text, args);
53 | }
54 |
55 | public static void e(Throwable t, String text, Object... args) {
56 | customLogger.e(t, text, args);
57 | }
58 |
59 | public static void e(String text, Object... args) {
60 | customLogger.e(text, args);
61 | }
62 |
63 | public static void v(String text, Object... args) {
64 | customLogger.v(text, args);
65 | }
66 |
67 | public static class ErrorLogger implements CustomLogger {
68 | private static final String TAG = "JobManager";
69 | @Override
70 | public boolean isDebugEnabled() {
71 | return false;
72 | }
73 |
74 | @Override
75 | public void d(String text, Object... args) {
76 |
77 | }
78 |
79 | @Override
80 | public void e(Throwable t, String text, Object... args) {
81 | Log.e(TAG, String.format(text, args), t);
82 | }
83 |
84 | @Override
85 | public void e(String text, Object... args) {
86 | Log.e(TAG, String.format(text, args));
87 | }
88 |
89 | @Override
90 | public void v(String text, Object... args) {
91 |
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/messaging/DelayedMessageBag.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.messaging;
2 |
3 | import com.birbit.android.jobqueue.log.JqLog;
4 |
5 | class DelayedMessageBag {
6 | Message queue = null;
7 | final MessageFactory factory;
8 |
9 | DelayedMessageBag(MessageFactory factory) {
10 | this.factory = factory;
11 | }
12 |
13 | Long flushReadyMessages(long now, MessageQueue addInto) {
14 | JqLog.d("flushing messages at time %s", now);
15 | while (queue != null && queue.readyNs <= now) {
16 | Message msg = queue;
17 | queue = msg.next;
18 | msg.next = null;
19 | addInto.post(msg);
20 | }
21 | if (queue != null) {
22 | JqLog.d("returning next ready at %d ns", (queue.readyNs - now));
23 | return queue.readyNs;
24 | }
25 | return null;
26 | }
27 | void add(Message message, long readyNs) {
28 | JqLog.d("add delayed message %s at time %s", message, readyNs);
29 | message.readyNs = readyNs;
30 | if (queue == null) {
31 | queue = message;
32 | return;
33 | }
34 | Message prev = null;
35 | Message curr = queue;
36 | while (curr != null && curr.readyNs <= readyNs) {
37 | prev = curr;
38 | curr = curr.next;
39 | }
40 | if (prev == null) {
41 | message.next = queue;
42 | queue = message;
43 | } else {
44 | prev.next = message;
45 | message.next = curr;
46 | }
47 | }
48 |
49 | public void clear() {
50 | while (queue != null) {
51 | Message curr = queue;
52 | queue = curr.next;
53 | factory.release(curr);
54 | }
55 | queue = null;
56 | }
57 |
58 | public void removeMessages(MessagePredicate predicate) {
59 | Message prev = null;
60 | Message curr = queue;
61 | while (curr != null) {
62 | final boolean remove = predicate.onMessage(curr);
63 | final Message next = curr.next;
64 | if (remove) {
65 | if (prev == null) {
66 | queue = curr.next;
67 | } else {
68 | prev.next = curr.next;
69 | }
70 | factory.release(curr);
71 | } else {
72 | prev = curr;
73 | }
74 | curr = next;
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/messaging/Message.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.messaging;
2 |
3 | abstract public class Message {
4 | public final Type type;
5 | // used by the pool
6 | Message next;
7 | public long readyNs = Long.MIN_VALUE;
8 |
9 | protected Message(Type type) {
10 | this.type = type;
11 | }
12 |
13 | abstract protected void onRecycled();
14 |
15 | final void recycle() {
16 | next = null;
17 | readyNs = Long.MIN_VALUE;
18 | onRecycled();
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/messaging/MessageFactory.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.messaging;
2 |
3 | import com.birbit.android.jobqueue.log.JqLog;
4 |
5 | import java.util.Arrays;
6 |
7 | public class MessageFactory {
8 | private static final int CACHE_LIMIT = 20;
9 | Message[] pools = new Message[Type.values().length];
10 | int[] counts = new int[pools.length];
11 |
12 | public MessageFactory() {
13 | Arrays.fill(counts, 0);
14 | }
15 |
16 | public T obtain(Class klass) {
17 | final Type type = Type.mapping.get(klass);
18 | //noinspection SynchronizationOnLocalVariableOrMethodParameter
19 | synchronized (type) {
20 | Message message = pools[type.ordinal()];
21 | if (message != null) {
22 | pools[type.ordinal()] = message.next;
23 | counts[type.ordinal()] -= 1;
24 | message.next = null;
25 | //noinspection unchecked
26 | return (T) message;
27 | }
28 | try {
29 | return klass.newInstance();
30 | } catch (InstantiationException e) {
31 | JqLog.e(e, "Cannot create an instance of " + klass + ". Make sure it has a empty" +
32 | " constructor.");
33 | } catch (IllegalAccessException e) {
34 | JqLog.e(e, "Cannot create an instance of " + klass + ". Make sure it has a public" +
35 | " empty constructor.");
36 | }
37 | }
38 | return null;
39 | }
40 | public void release(Message message) {
41 | final Type type = message.type;
42 | message.recycle();
43 | //noinspection SynchronizationOnLocalVariableOrMethodParameter
44 | synchronized (type) {
45 | if (counts[type.ordinal()] < CACHE_LIMIT) {
46 | message.next = pools[type.ordinal()];
47 | pools[type.ordinal()] = message;
48 | counts[type.ordinal()] += 1;
49 | }
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/messaging/MessagePredicate.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.messaging;
2 |
3 | public interface MessagePredicate {
4 | boolean onMessage(Message message);
5 | }
6 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/messaging/MessageQueue.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.messaging;
2 |
3 | public interface MessageQueue {
4 | void post(Message message);
5 | void postAt(Message message, long readyNs);
6 | void cancelMessages(MessagePredicate predicate);
7 | void stop();
8 | void consume(MessageQueueConsumer consumer);
9 | void clear();
10 | }
11 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/messaging/MessageQueueConsumer.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.messaging;
2 |
3 | public abstract class MessageQueueConsumer {
4 | abstract public void handleMessage(Message message);
5 | abstract public void onIdle();
6 | public void onStart() {
7 |
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/messaging/Type.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.messaging;
2 |
3 | import com.birbit.android.jobqueue.messaging.message.AddJobMessage;
4 | import com.birbit.android.jobqueue.messaging.message.CallbackMessage;
5 | import com.birbit.android.jobqueue.messaging.message.CancelMessage;
6 | import com.birbit.android.jobqueue.messaging.message.CancelResultMessage;
7 | import com.birbit.android.jobqueue.messaging.message.CommandMessage;
8 | import com.birbit.android.jobqueue.messaging.message.ConstraintChangeMessage;
9 | import com.birbit.android.jobqueue.messaging.message.PublicQueryMessage;
10 | import com.birbit.android.jobqueue.messaging.message.JobConsumerIdleMessage;
11 | import com.birbit.android.jobqueue.messaging.message.RunJobMessage;
12 | import com.birbit.android.jobqueue.messaging.message.RunJobResultMessage;
13 | import com.birbit.android.jobqueue.messaging.message.SchedulerMessage;
14 |
15 | import java.util.HashMap;
16 | import java.util.Map;
17 |
18 | /**
19 | * All message types
20 | */
21 | public enum Type {
22 | CALLBACK(CallbackMessage.class, 0),
23 | CANCEL_RESULT_CALLBACK(CancelResultMessage.class, 0),
24 | RUN_JOB(RunJobMessage.class, 0),
25 | COMMAND(CommandMessage.class, 0),
26 | PUBLIC_QUERY(PublicQueryMessage.class, 0),
27 | JOB_CONSUMER_IDLE(JobConsumerIdleMessage.class, 0), // MUST ARRIVE AFTER JOB RESULT
28 | ADD_JOB(AddJobMessage.class, 1),
29 | CANCEL(CancelMessage.class, 1),
30 | CONSTRAINT_CHANGE(ConstraintChangeMessage.class, 2),
31 | RUN_JOB_RESULT(RunJobResultMessage.class, 3),
32 | SCHEDULER(SchedulerMessage.class, 4);
33 | final Class extends Message> klass;
34 | final static Map, Type> mapping;
35 | final int priority; // higher is better
36 | final static int MAX_PRIORITY;
37 |
38 | Type(Class extends Message> klass, int priority) {
39 | this.klass = klass;
40 | this.priority = priority;
41 | }
42 | static {
43 | int maxPriority = 0;
44 | mapping = new HashMap<>();
45 | for (Type type : Type.values()) {
46 | mapping.put(type.klass, type);
47 | if (type.priority > maxPriority) {
48 | maxPriority = type.priority;
49 | }
50 | }
51 | MAX_PRIORITY = maxPriority;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/messaging/UnsafeMessageQueue.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.messaging;
2 |
3 | import com.birbit.android.jobqueue.log.JqLog;
4 |
5 | import java.util.concurrent.atomic.AtomicInteger;
6 |
7 | class UnsafeMessageQueue {
8 | private Message queue = null;
9 | private Message tail = null;
10 | private static final AtomicInteger idCounter = new AtomicInteger(0);
11 | public final String logTag;
12 | private final MessageFactory factory;
13 |
14 | public UnsafeMessageQueue(MessageFactory factory, String logTag) {
15 | this.factory = factory;
16 | this.logTag = logTag + "_" + idCounter.incrementAndGet();
17 | }
18 |
19 | Message next() {
20 | final Message result = queue;
21 | JqLog.d("[%s] remove message %s", logTag, result);
22 | if (result != null) {
23 | queue = result.next;
24 | if (tail == result) {
25 | tail = null;
26 | }
27 | }
28 | return result;
29 | }
30 |
31 | protected void post(Message message) {
32 | JqLog.d("[%s] post message %s", logTag, message);
33 | if (tail == null) {
34 | queue = message;
35 | tail = message;
36 | } else {
37 | tail.next = message;
38 | tail = message;
39 | }
40 | }
41 |
42 | protected void postAtFront(Message message) {
43 | message.next = queue;
44 | if (tail == null) {
45 | tail = message;
46 | }
47 | queue = message;
48 | }
49 |
50 | protected void removeMessages(MessagePredicate predicate) {
51 | Message prev = null;
52 | Message curr = queue;
53 | while (curr != null) {
54 | final boolean remove = predicate.onMessage(curr);
55 | if (remove) {
56 | final Message next = curr.next;
57 | remove(prev, curr);
58 | curr = next;
59 | } else {
60 | prev = curr;
61 | curr = curr.next;
62 | }
63 | }
64 | }
65 |
66 | private void remove(Message prev, Message curr) {
67 | if (tail == curr) {
68 | tail = prev;
69 | }
70 | if (prev == null) {
71 | queue = curr.next;
72 | } else {
73 | prev.next = curr.next;
74 | }
75 | factory.release(curr);
76 | }
77 |
78 | public void clear() {
79 | while (queue != null) {
80 | Message curr = queue;
81 | queue = curr.next;
82 | factory.release(curr);
83 | }
84 | tail = null;
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/messaging/message/AddJobMessage.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.messaging.message;
2 |
3 | import com.birbit.android.jobqueue.messaging.Message;
4 | import com.birbit.android.jobqueue.messaging.Type;
5 | import com.birbit.android.jobqueue.Job;
6 |
7 | public class AddJobMessage extends Message {
8 | private Job job;
9 | public AddJobMessage() {
10 | super(Type.ADD_JOB);
11 | }
12 |
13 | public Job getJob() {
14 | return job;
15 | }
16 |
17 | public void setJob(Job job) {
18 | this.job = job;
19 | }
20 |
21 | @Override
22 | protected void onRecycled() {
23 | job = null;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/messaging/message/CallbackMessage.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.messaging.message;
2 |
3 | import androidx.annotation.Nullable;
4 |
5 | import com.birbit.android.jobqueue.Job;
6 | import com.birbit.android.jobqueue.messaging.Message;
7 | import com.birbit.android.jobqueue.messaging.Type;
8 |
9 | /**
10 | * Used for external callbacks to user code
11 | */
12 | public class CallbackMessage extends Message {
13 | public static final int ON_ADDED = 1;
14 | public static final int ON_RUN = 2;
15 | public static final int ON_CANCEL = 3;
16 | public static final int ON_DONE = 4;
17 | public static final int ON_AFTER_RUN = 5;
18 |
19 | private int what;
20 | private int resultCode;
21 | private boolean byUserRequest;
22 | private Job job;
23 | @Nullable private Throwable throwable;
24 |
25 | public CallbackMessage() {
26 | super(Type.CALLBACK);
27 | }
28 |
29 | @Override
30 | protected void onRecycled() {
31 | job = null;
32 | throwable = null;
33 | }
34 |
35 | public void set(Job job, int what) {
36 | this.what = what;
37 | this.job = job;
38 | }
39 |
40 | public void set(Job job, int what, int resultCode) {
41 | this.what = what;
42 | this.resultCode = resultCode;
43 | this.job = job;
44 | }
45 |
46 | public void set(Job job, int what, boolean byUserRequest, @Nullable Throwable throwable) {
47 | this.what = what;
48 | this.byUserRequest = byUserRequest;
49 | this.job = job;
50 | this.throwable = throwable;
51 | }
52 |
53 | public int getWhat() {
54 | return what;
55 | }
56 |
57 | public int getResultCode() {
58 | return resultCode;
59 | }
60 |
61 | public boolean isByUserRequest() {
62 | return byUserRequest;
63 | }
64 |
65 | public Job getJob() {
66 | return job;
67 | }
68 |
69 | @Nullable
70 | public Throwable getThrowable() {
71 | return throwable;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/messaging/message/CancelMessage.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.messaging.message;
2 |
3 | import com.birbit.android.jobqueue.messaging.Message;
4 | import com.birbit.android.jobqueue.messaging.Type;
5 | import com.birbit.android.jobqueue.CancelResult;
6 | import com.birbit.android.jobqueue.TagConstraint;
7 |
8 | public class CancelMessage extends Message {
9 | private TagConstraint constraint;
10 | private String[] tags;
11 | private CancelResult.AsyncCancelCallback callback;
12 |
13 | public CancelMessage() {
14 | super(Type.CANCEL);
15 | }
16 |
17 | @Override
18 | protected void onRecycled() {
19 |
20 | }
21 |
22 | public TagConstraint getConstraint() {
23 | return constraint;
24 | }
25 |
26 | public void setConstraint(TagConstraint constraint) {
27 | this.constraint = constraint;
28 | }
29 |
30 | public String[] getTags() {
31 | return tags;
32 | }
33 |
34 | public void setTags(String[] tags) {
35 | this.tags = tags;
36 | }
37 |
38 | public CancelResult.AsyncCancelCallback getCallback() {
39 | return callback;
40 | }
41 |
42 | public void setCallback(CancelResult.AsyncCancelCallback callback) {
43 | this.callback = callback;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/messaging/message/CancelResultMessage.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.messaging.message;
2 |
3 | import com.birbit.android.jobqueue.messaging.Message;
4 | import com.birbit.android.jobqueue.messaging.Type;
5 | import com.birbit.android.jobqueue.CancelResult;
6 |
7 | public class CancelResultMessage extends Message {
8 | CancelResult.AsyncCancelCallback callback;
9 | CancelResult result;
10 | public CancelResultMessage() {
11 | super(Type.CANCEL_RESULT_CALLBACK);
12 | }
13 |
14 | @Override
15 | protected void onRecycled() {
16 | result = null;
17 | callback = null;
18 | }
19 |
20 | public void set(CancelResult.AsyncCancelCallback callback, CancelResult result) {
21 | this.callback = callback;
22 | this.result = result;
23 | }
24 |
25 | public CancelResult.AsyncCancelCallback getCallback() {
26 | return callback;
27 | }
28 |
29 | public CancelResult getResult() {
30 | return result;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/messaging/message/CommandMessage.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.messaging.message;
2 |
3 | import com.birbit.android.jobqueue.messaging.Message;
4 | import com.birbit.android.jobqueue.messaging.Type;
5 |
6 | public class CommandMessage extends Message {
7 | public static final int QUIT = 1;
8 | public static final int POKE = 2; // simple message to wake it up
9 | public static final int RUNNABLE = 3; // only used in tests
10 | private int what;
11 |
12 | private Runnable runnable;
13 |
14 | public CommandMessage() {
15 | super(Type.COMMAND);
16 | }
17 |
18 | @Override
19 | protected void onRecycled() {
20 | what = -1;
21 | runnable = null;
22 | }
23 |
24 | public int getWhat() {
25 | return what;
26 | }
27 |
28 | public void set(int what) {
29 | this.what = what;
30 | }
31 |
32 | public Runnable getRunnable() {
33 | return runnable;
34 | }
35 |
36 | public void setRunnable(Runnable runnable) {
37 | this.runnable = runnable;
38 | }
39 |
40 | @Override
41 | public String toString() {
42 | return "Command[" + what + "]";
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/messaging/message/ConstraintChangeMessage.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.messaging.message;
2 |
3 | import com.birbit.android.jobqueue.messaging.Message;
4 | import com.birbit.android.jobqueue.messaging.Type;
5 |
6 | public class ConstraintChangeMessage extends Message {
7 | private boolean forNextJob;
8 | public ConstraintChangeMessage() {
9 | super(Type.CONSTRAINT_CHANGE);
10 | }
11 |
12 | @Override
13 | protected void onRecycled() {
14 | forNextJob = false;
15 | }
16 |
17 | public boolean isForNextJob() {
18 | return forNextJob;
19 | }
20 |
21 | public void setForNextJob(boolean forNextJob) {
22 | this.forNextJob = forNextJob;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/messaging/message/JobConsumerIdleMessage.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.messaging.message;
2 |
3 | import com.birbit.android.jobqueue.messaging.Message;
4 | import com.birbit.android.jobqueue.messaging.Type;
5 |
6 | public class JobConsumerIdleMessage extends Message {
7 | private Object worker;
8 | private long lastJobCompleted;
9 |
10 | public JobConsumerIdleMessage() {
11 | super(Type.JOB_CONSUMER_IDLE);
12 | }
13 |
14 | @Override
15 | protected void onRecycled() {
16 | worker = null;
17 | }
18 |
19 | public long getLastJobCompleted() {
20 | return lastJobCompleted;
21 | }
22 |
23 | public Object getWorker() {
24 | return worker;
25 | }
26 |
27 | public void setWorker(Object worker) {
28 | this.worker = worker;
29 | }
30 |
31 | public void setLastJobCompleted(long lastJobCompleted) {
32 | this.lastJobCompleted = lastJobCompleted;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/messaging/message/PublicQueryMessage.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.messaging.message;
2 |
3 | import com.birbit.android.jobqueue.IntCallback;
4 | import com.birbit.android.jobqueue.messaging.Message;
5 | import com.birbit.android.jobqueue.messaging.Type;
6 |
7 | public class PublicQueryMessage extends Message implements IntCallback.MessageWithCallback {
8 | public static final int COUNT = 0;
9 | public static final int COUNT_READY = 1;
10 | public static final int START = 2;
11 | public static final int STOP = 3;
12 | public static final int JOB_STATUS = 4;
13 | public static final int CLEAR = 5;
14 | public static final int ACTIVE_CONSUMER_COUNT = 6;
15 | public static final int SCHEDULER_START = 7;
16 | // used for testing
17 | public static final int INTERNAL_RUNNABLE = 101;
18 |
19 | private IntCallback callback;
20 | private int what = -1;
21 | private String stringArg;
22 |
23 | public PublicQueryMessage() {
24 | super(Type.PUBLIC_QUERY);
25 | }
26 |
27 | public void set(int what, IntCallback callback) {
28 | this.callback = callback;
29 | this.what = what;
30 | }
31 |
32 | public void set(int what, String stringArg, IntCallback callback) {
33 | this.what = what;
34 | this.stringArg = stringArg;
35 | this.callback = callback;
36 | }
37 |
38 | public IntCallback getCallback() {
39 | return callback;
40 | }
41 |
42 | public int getWhat() {
43 | return what;
44 | }
45 |
46 | public String getStringArg() {
47 | return stringArg;
48 | }
49 |
50 | public void setCallback(IntCallback callback) {
51 | this.callback = callback;
52 | }
53 |
54 | @Override
55 | protected void onRecycled() {
56 | callback = null;
57 | what = -1;
58 | }
59 |
60 | @Override
61 | public String toString() {
62 | return "PublicQuery[" + what + "]";
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/messaging/message/RunJobMessage.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.messaging.message;
2 |
3 | import com.birbit.android.jobqueue.messaging.Message;
4 | import com.birbit.android.jobqueue.messaging.Type;
5 | import com.birbit.android.jobqueue.JobHolder;
6 |
7 | public class RunJobMessage extends Message {
8 | private JobHolder jobHolder;
9 | public RunJobMessage() {
10 | super(Type.RUN_JOB);
11 | }
12 |
13 | public JobHolder getJobHolder() {
14 | return jobHolder;
15 | }
16 |
17 | public void setJobHolder(JobHolder jobHolder) {
18 | this.jobHolder = jobHolder;
19 | }
20 |
21 | @Override
22 | protected void onRecycled() {
23 | jobHolder = null;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/messaging/message/RunJobResultMessage.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.messaging.message;
2 |
3 | import com.birbit.android.jobqueue.messaging.Message;
4 | import com.birbit.android.jobqueue.messaging.Type;
5 | import com.birbit.android.jobqueue.JobHolder;
6 |
7 | public class RunJobResultMessage extends Message {
8 | private JobHolder jobHolder;
9 | private Object worker;
10 | private int result;
11 |
12 | public RunJobResultMessage() {
13 | super(Type.RUN_JOB_RESULT);
14 | }
15 |
16 | public JobHolder getJobHolder() {
17 | return jobHolder;
18 | }
19 |
20 | public void setJobHolder(JobHolder jobHolder) {
21 | this.jobHolder = jobHolder;
22 | }
23 |
24 | @Override
25 | protected void onRecycled() {
26 | jobHolder = null;
27 | }
28 |
29 | public void setResult(int result) {
30 | this.result = result;
31 | }
32 |
33 | public int getResult() {
34 | return result;
35 | }
36 |
37 | public Object getWorker() {
38 | return worker;
39 | }
40 |
41 | public void setWorker(Object worker) {
42 | this.worker = worker;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/messaging/message/SchedulerMessage.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.messaging.message;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.birbit.android.jobqueue.messaging.Message;
6 | import com.birbit.android.jobqueue.messaging.Type;
7 | import com.birbit.android.jobqueue.scheduling.SchedulerConstraint;
8 |
9 | /**
10 | * The messages with the scheduler
11 | */
12 | public class SchedulerMessage extends Message {
13 | public static final int START = 1;
14 | public static final int STOP = 2;
15 |
16 | private int what;
17 | @SuppressWarnings("NullableProblems")
18 | @NonNull
19 | private SchedulerConstraint constraint;
20 |
21 | public SchedulerMessage() {
22 | super(Type.SCHEDULER);
23 | }
24 |
25 | public void set(int what, @NonNull SchedulerConstraint constraint) {
26 | this.what = what;
27 | this.constraint = constraint;
28 | }
29 |
30 | public int getWhat() {
31 | return what;
32 | }
33 |
34 | @NonNull
35 | public SchedulerConstraint getConstraint() {
36 | return constraint;
37 | }
38 |
39 | @Override
40 | protected void onRecycled() {
41 | //noinspection ConstantConditions
42 | constraint = null;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/network/NetworkEventProvider.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.network;
2 |
3 | /**
4 | * An interface that NetworkUtil can implement if it supports a callback method when network state is changed
5 | * This is not mandatory but highly suggested so that {@link com.birbit.android.jobqueue.JobManager} can avoid
6 | * busy loops when there is a job waiting for network and there is no network available
7 | */
8 | public interface NetworkEventProvider {
9 | void setListener(Listener listener);
10 | interface Listener {
11 | /**
12 | * @param networkStatus {@link com.birbit.android.jobqueue.network.NetworkUtil.NetworkStatus}
13 | */
14 | void onNetworkChange(@NetworkUtil.NetworkStatus int networkStatus);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/network/NetworkUtil.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.network;
2 |
3 | import android.content.Context;
4 | import androidx.annotation.IntDef;
5 |
6 | import java.lang.annotation.Retention;
7 | import java.lang.annotation.RetentionPolicy;
8 |
9 | /**
10 | * Interface which you can implement if you want to provide a custom Network callback.
11 | * Make sure you also implement {@link NetworkEventProvider} for best performance.
12 | */
13 | public interface NetworkUtil {
14 | /**
15 | * Order of these constant values matter as they are relied upon to be incrementing in terms
16 | * of availability.
17 | */
18 | @Retention(RetentionPolicy.SOURCE)
19 | @IntDef({DISCONNECTED, METERED, UNMETERED})
20 | @interface NetworkStatus {}
21 | int DISCONNECTED = 0;
22 | int METERED = 1;
23 | int UNMETERED = 2;
24 |
25 | /**
26 | * Returns the current connection status. If you cannot detect granular network type, return
27 | * {@link #UNMETERED} if there is an internet connection or {@link #DISCONNECTED} if there is no
28 | * connection.
29 | *
30 | * @param context The application context
31 | *
32 | * @return The current connection status. It should be one of {@link #DISCONNECTED},
33 | * {@link #METERED} or {@link #UNMETERED}.
34 | */
35 | @NetworkStatus
36 | int getNetworkStatus(Context context);
37 | }
38 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/persistentQueue/sqlite/FileStorage.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.persistentQueue.sqlite;
2 |
3 |
4 | import android.content.Context;
5 | import androidx.annotation.Nullable;
6 |
7 | import com.birbit.android.jobqueue.log.JqLog;
8 |
9 | import java.io.Closeable;
10 | import java.io.File;
11 | import java.io.IOException;
12 | import java.util.Set;
13 |
14 | import okio.BufferedSink;
15 | import okio.BufferedSource;
16 | import okio.Okio;
17 |
18 | /**
19 | * Provides a toFile based storage to keep jobs.
20 | * This class is NOT thread safe and re-uses Buffers
21 | */
22 | class FileStorage {
23 | private static final String EXT = ".jobs";
24 | private final File folder;
25 | FileStorage(Context appContext, String id) {
26 | this.folder = new File(appContext.getDir("com_birbit_jobqueue_jobs", Context.MODE_PRIVATE),
27 | "files_" + id);
28 | //noinspection ResultOfMethodCallIgnored
29 | this.folder.mkdirs();
30 | }
31 |
32 | void delete(String id) {
33 | final File file = toFile(id);
34 | if (file.exists()) {
35 | //noinspection ResultOfMethodCallIgnored
36 | file.delete();
37 | }
38 | }
39 |
40 | @Nullable
41 | byte[] load(String id) throws IOException {
42 | final File file = toFile(id);
43 | if (file.exists() && file.canRead()) {
44 | BufferedSource source = Okio.buffer(Okio.source(file));
45 | try {
46 | return source.readByteArray();
47 | } finally {
48 | closeQuitely(source);
49 | }
50 |
51 | }
52 | return null;
53 | }
54 |
55 | void save(String id, byte[] data) throws IOException {
56 | final File file = toFile(id);
57 | BufferedSink sink = Okio.buffer(Okio.sink(file));
58 | try {
59 | sink.write(data).flush();
60 | } finally {
61 | closeQuitely(sink);
62 | }
63 | }
64 |
65 | private static String filename(String id) {
66 | return id + EXT;
67 | }
68 |
69 | private File toFile(String id) {
70 | return new File(folder, filename(id));
71 | }
72 |
73 | @Nullable
74 | private static String filenameToId(String filename) {
75 | if (filename.length() < EXT.length() + 1) {
76 | return null;
77 | }
78 | return filename.substring(0, filename.length() - EXT.length());
79 | }
80 |
81 | void truncateExcept(Set ids) {
82 | for (String filename : folder.list()) {
83 | if (!filename.endsWith(EXT)) {
84 | continue;
85 | }
86 | String id = filenameToId(filename);
87 | if (!ids.contains(id)) {
88 | File file = new File(folder, filename);
89 | if (!file.delete()) {
90 | JqLog.d("cannot delete unused job toFile " + file.getAbsolutePath());
91 | }
92 | }
93 | }
94 | }
95 |
96 | private static void closeQuitely(Closeable closeable) {
97 | try {
98 | closeable.close();
99 | } catch (IOException ignored) {
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/scheduling/GcmJobSchedulerService.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.scheduling;
2 |
3 | import android.content.Context;
4 | import androidx.annotation.NonNull;
5 | import androidx.annotation.Nullable;
6 |
7 | import com.birbit.android.jobqueue.JobManager;
8 | import com.birbit.android.jobqueue.log.JqLog;
9 | import com.google.android.gms.gcm.GcmNetworkManager;
10 | import com.google.android.gms.gcm.GcmTaskService;
11 | import com.google.android.gms.gcm.TaskParams;
12 |
13 | abstract public class GcmJobSchedulerService extends GcmTaskService {
14 | /**
15 | * Creates a scheduler for the given service.
16 | * Keep in mind that there is a strict 1-1 mapping between the created scheduler and the
17 | * service. You should pass the returned scheduler to the JobManager configuration.
18 | *
19 | * @param appContext The application context
20 | * @param klass The service implementation that extends GcmJobSchedulerService.
21 | *
22 | * @return A scheduler that is associated with the given service class.
23 | */
24 | @SuppressWarnings("unused")
25 | public static GcmScheduler createSchedulerFor(Context appContext,
26 | Class extends GcmJobSchedulerService> klass) {
27 | if (GcmJobSchedulerService.class == klass) {
28 | throw new IllegalArgumentException("You must create a service that extends" +
29 | " GcmJobSchedulerService");
30 | }
31 | return new GcmScheduler(appContext.getApplicationContext(), klass);
32 | }
33 |
34 | @Override
35 | public int onRunTask(TaskParams taskParams) {
36 | GcmScheduler scheduler = getScheduler();
37 | if (scheduler != null) {
38 | return scheduler.onStartJob(taskParams);
39 | } else {
40 | JqLog.e("RunTask on GcmJobSchedulerService has been called but it does not have a " +
41 | "scheduler. Make sure you've initialized JobManager before the service might" +
42 | " be created.");
43 | return GcmNetworkManager.RESULT_FAILURE;
44 | }
45 |
46 | }
47 |
48 | @Nullable
49 | protected GcmScheduler getScheduler() {
50 | Scheduler scheduler = getJobManager().getScheduler();
51 | if (scheduler instanceof GcmScheduler) {
52 | return (GcmScheduler) scheduler;
53 | }
54 | JqLog.e("GcmJobSchedulerService has been created but the JobManager does not" +
55 | " have a scheduler created by GcmJobSchedulerService.");
56 | return null;
57 | }
58 |
59 | /**
60 | * Return the JobManager that is associated with this service
61 | *
62 | * @return The JobManager that is associated with this service
63 | */
64 | @NonNull
65 | protected abstract JobManager getJobManager();
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/scheduling/Scheduler.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.scheduling;
2 |
3 | import android.content.Context;
4 |
5 | /**
6 | * This class handles communication and tracking with a scheduler that can wake up the app / job
7 | * manager based on external events.
8 | *
9 | * JobManager will call attached scheduler every time it thinks that the app should be waken up for
10 | * the given job. Each request comes with a {@link SchedulerConstraint} which should be reported
11 | * back to the JobManager when the system service wakes it up.
12 | */
13 | abstract public class Scheduler {
14 | private Callback callback;
15 | private Context context;
16 |
17 | protected Scheduler() {
18 |
19 | }
20 |
21 | public void init(Context context, Callback callback) {
22 | this.context = context.getApplicationContext();
23 | this.callback = callback;
24 | }
25 |
26 | Context getApplicationContext() {
27 | return context;
28 | }
29 |
30 | abstract public void request(SchedulerConstraint constraint);
31 |
32 | /**
33 | * Triggers the JobManager to handle the given constraint. JobManager always call
34 | * {@link #onFinished(SchedulerConstraint, boolean)} with an async callback.
35 | *
36 | * @param constraint The constraint
37 | * @return true if there is work to do be done, false otherwise
38 | */
39 | public final boolean start(SchedulerConstraint constraint) {
40 | if (callback == null) {
41 | throw new IllegalStateException("JobManager callback is not configured");
42 | }
43 | return callback.start(constraint);
44 | }
45 |
46 | public final boolean stop(SchedulerConstraint constraint) {
47 | if (callback == null) {
48 | throw new IllegalStateException("JobManager callback is not configured");
49 | }
50 | return callback.stop(constraint);
51 | }
52 |
53 | /**
54 | * Called by the JobManager when a scheduled constraint is handled.
55 | *
56 | * @param constraint The original constraint
57 | * @param reschedule True if the job should be rescheduled
58 | */
59 | abstract public void onFinished(SchedulerConstraint constraint, boolean reschedule);
60 |
61 | /**
62 | * When called, should cancel all pending jobs
63 | */
64 | public abstract void cancelAll();
65 |
66 | /**
67 | * Internal class that handles the communication between the JobManager and the scheduler
68 | */
69 | public interface Callback {
70 | /**
71 | * @param constraint The constraint for the jobs to be run
72 | * @return True if there are jobs to run, false otherwise
73 | */
74 | boolean start(SchedulerConstraint constraint);
75 |
76 | /**
77 | * @param constraint The constraint for the jobs to be run
78 | * @return True if job should be rescheduled, false otherwise
79 | */
80 | boolean stop(SchedulerConstraint constraint);
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/jobqueue/src/main/java/com/birbit/android/jobqueue/scheduling/SchedulerConstraint.java:
--------------------------------------------------------------------------------
1 | package com.birbit.android.jobqueue.scheduling;
2 |
3 | import androidx.annotation.Nullable;
4 |
5 | import com.birbit.android.jobqueue.network.NetworkUtil;
6 |
7 | /**
8 | * The constraints that are passed into Scheduler from JobManager
9 | */
10 | public class SchedulerConstraint {
11 | private String uuid;
12 | private long delayInMs;
13 | private int networkStatus;
14 | private Long overrideDeadlineInMs;
15 | // arbitrary data that can be used by the scheduler
16 | private Object data;
17 |
18 | public SchedulerConstraint(String uuid) {
19 | this.uuid = uuid;
20 | }
21 |
22 | /**
23 | * The unique id assigned by the job manager. This is different from the ID that is assigned
24 | * by the third party scheduler.
25 | * @return The unique id assigned by the job manager
26 | */
27 | public String getUuid() {
28 | return uuid;
29 | }
30 |
31 | public void setUuid(String uuid) {
32 | this.uuid = uuid;
33 | }
34 |
35 | /**
36 | * The delay for the job
37 | * @return The delay before running the job
38 | */
39 | public long getDelayInMs() {
40 | return delayInMs;
41 | }
42 |
43 | public void setDelayInMs(long delayInMs) {
44 | this.delayInMs = delayInMs;
45 | }
46 |
47 | /**
48 | *
49 | * @return The network status required to run the job.
50 | */
51 | @NetworkUtil.NetworkStatus
52 | public int getNetworkStatus() {
53 | return networkStatus;
54 | }
55 |
56 | public void setNetworkStatus(int networkStatus) {
57 | this.networkStatus = networkStatus;
58 | }
59 |
60 | public Object getData() {
61 | return data;
62 | }
63 |
64 | public void setData(Object data) {
65 | this.data = data;
66 | }
67 |
68 | /**
69 | * The deadline in ms after which the job should be run even if the constraints are not match.
70 | *