diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml
index b1521ba66c..6358e9dc46 100644
--- a/android/AndroidManifest.xml
+++ b/android/AndroidManifest.xml
@@ -32,7 +32,7 @@
-
+
@@ -455,6 +455,14 @@
android:exported="false"
android:permission="android.permission.BIND_JOB_SERVICE"/>
+
+
+
+
+
+
diff --git a/android/build.gradle b/android/build.gradle
index 9aa246159e..faacb85f40 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -109,6 +109,7 @@ dependencies {
}
implementation 'com.android.support.constraint:constraint-layout:1.1.0'
implementation 'com.android.billingclient:billing:1.1'
+ implementation 'com.firebase:firebase-jobdispatcher:0.8.5'
}
def getDate() {
diff --git a/android/src/com/mapswithme/maps/MwmApplication.java b/android/src/com/mapswithme/maps/MwmApplication.java
index 620f3df3d9..e5a83715e9 100644
--- a/android/src/com/mapswithme/maps/MwmApplication.java
+++ b/android/src/com/mapswithme/maps/MwmApplication.java
@@ -33,10 +33,6 @@ import com.mapswithme.maps.routing.RoutingController;
import com.mapswithme.maps.scheduling.ConnectivityListener;
import com.mapswithme.maps.scheduling.ConnectivityJobScheduler;
import com.mapswithme.maps.sound.TtsPlayer;
-import com.mapswithme.maps.routing.RoutingController;
-import com.mapswithme.maps.scheduling.ConnectivityListener;
-import com.mapswithme.maps.scheduling.ConnectivityJobScheduler;
-import com.mapswithme.maps.sound.TtsPlayer;
import com.mapswithme.maps.ugc.UGC;
import com.mapswithme.util.Config;
import com.mapswithme.util.Counters;
diff --git a/android/src/com/mapswithme/maps/scheduling/ConnectivityJobScheduler.java b/android/src/com/mapswithme/maps/scheduling/ConnectivityJobScheduler.java
index 94aaf66eec..46fd4cb220 100644
--- a/android/src/com/mapswithme/maps/scheduling/ConnectivityJobScheduler.java
+++ b/android/src/com/mapswithme/maps/scheduling/ConnectivityJobScheduler.java
@@ -5,35 +5,50 @@ import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.Context;
-import android.content.IntentFilter;
-import android.net.ConnectivityManager;
import android.os.Build;
import android.support.annotation.NonNull;
+import com.crashlytics.android.Crashlytics;
+import com.firebase.jobdispatcher.Constraint;
+import com.firebase.jobdispatcher.FirebaseJobDispatcher;
+import com.firebase.jobdispatcher.GooglePlayDriver;
+import com.firebase.jobdispatcher.Job;
+import com.firebase.jobdispatcher.Lifetime;
+import com.firebase.jobdispatcher.Trigger;
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.GoogleApiAvailability;
import com.mapswithme.maps.MwmApplication;
-import com.mapswithme.maps.background.ConnectivityChangedReceiver;
-import com.mapswithme.util.ConnectionState;
+import com.mapswithme.util.Utils;
import java.util.Objects;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.TimeUnit;
public class ConnectivityJobScheduler implements ConnectivityListener
{
- public static final int PERIODIC_IN_MILLIS = 4000;
+ private static final int SCHEDULE_PERIOD_IN_HOURS = 1;
@NonNull
private final ConnectivityListener mMasterConnectivityListener;
- @NonNull
- private final AtomicInteger mCurrentNetworkType;
-
public ConnectivityJobScheduler(@NonNull MwmApplication context)
{
- mMasterConnectivityListener = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
- ? new NativeConnectivityListener(context)
- : new LegacyConnectivityListener(context);
+ mMasterConnectivityListener = Utils.isLollipopOrLater()
+ ? createNativeJobScheduler(context)
+ : createCompatJobScheduler(context);
+ }
- mCurrentNetworkType = new AtomicInteger(getCurrentNetworkType().ordinal());
+ @NonNull
+ private ConnectivityListener createCompatJobScheduler(@NonNull MwmApplication context)
+ {
+ int status = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context);
+ boolean isAvailable = status == ConnectionResult.SUCCESS;
+ return isAvailable ? new ConnectivityListenerCompat(context) : new ConnectivityListenerStub();
+ }
+
+ @NonNull
+ private NativeConnectivityListener createNativeJobScheduler(@NonNull MwmApplication context)
+ {
+ return new NativeConnectivityListener(context);
}
@Override
@@ -42,22 +57,6 @@ public class ConnectivityJobScheduler implements ConnectivityListener
mMasterConnectivityListener.listen();
}
- @NonNull
- public NetworkStatus getNetworkStatus()
- {
- ConnectionState.Type currentNetworkType = getCurrentNetworkType();
- int prevTypeIndex = mCurrentNetworkType.getAndSet(currentNetworkType.ordinal());
- ConnectionState.Type prevNetworkType = ConnectionState.Type.values()[prevTypeIndex];
- boolean isNetworkChanged = prevNetworkType != currentNetworkType;
- return new NetworkStatus(isNetworkChanged, currentNetworkType);
- }
-
- @NonNull
- private ConnectionState.Type getCurrentNetworkType()
- {
- return ConnectionState.requestCurrentType();
- }
-
public static ConnectivityJobScheduler from(@NonNull Context context)
{
MwmApplication application = (MwmApplication) context.getApplicationContext();
@@ -88,55 +87,53 @@ public class ConnectivityJobScheduler implements ConnectivityListener
JobInfo jobInfo = new JobInfo
.Builder(jobId, component)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
- .setPeriodic(PERIODIC_IN_MILLIS)
+ .setPersisted(true)
+ .setMinimumLatency(TimeUnit.HOURS.toMillis(SCHEDULE_PERIOD_IN_HOURS))
.build();
mJobScheduler.schedule(jobInfo);
}
}
- public static class NetworkStatus
- {
- private final boolean mNetworkStateChanged;
- @NonNull
- private final ConnectionState.Type mCurrentNetworkType;
-
- NetworkStatus(boolean networkStateChanged,
- @NonNull ConnectionState.Type currentNetworkType)
- {
- mNetworkStateChanged = networkStateChanged;
- mCurrentNetworkType = currentNetworkType;
- }
-
- public boolean isNetworkStateChanged()
- {
- return mNetworkStateChanged;
- }
-
- @NonNull
- public ConnectionState.Type getCurrentNetworkType()
- {
- return mCurrentNetworkType;
- }
- }
-
- private static class LegacyConnectivityListener implements ConnectivityListener
+ private static class ConnectivityListenerCompat implements ConnectivityListener
{
@NonNull
- private final MwmApplication mContext;
- @NonNull
- private final ConnectivityChangedReceiver mReceiver;
+ private final FirebaseJobDispatcher mJobDispatcher;
- LegacyConnectivityListener(@NonNull MwmApplication context)
+ ConnectivityListenerCompat(@NonNull MwmApplication context)
{
- mContext = context;
- mReceiver = new ConnectivityChangedReceiver();
+ mJobDispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(context));
}
@Override
public void listen()
{
- IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
- mContext.registerReceiver(mReceiver, filter);
+ String tag = String.valueOf(FirebaseJobService.class.hashCode());
+ int executionWindowStart = (int) TimeUnit.HOURS.toSeconds(SCHEDULE_PERIOD_IN_HOURS);
+ Job job = mJobDispatcher.newJobBuilder()
+ .setTag(tag)
+ .setService(FirebaseJobService.class)
+ .setConstraints(Constraint.ON_ANY_NETWORK)
+ .setLifetime(Lifetime.FOREVER)
+ .setTrigger(Trigger.executionWindow(executionWindowStart, ++executionWindowStart))
+ .build();
+ mJobDispatcher.mustSchedule(job);
}
}
+
+ private static class ConnectivityListenerStub implements ConnectivityListener
+ {
+ ConnectivityListenerStub()
+ {
+ IllegalStateException exception = new IllegalStateException("Play services doesn't exist on" +
+ " the device");
+ Crashlytics.logException(exception);
+ }
+
+ @Override
+ public void listen()
+ {
+ /* Do nothing */
+ }
+ }
+
}
diff --git a/android/src/com/mapswithme/maps/scheduling/FirebaseJobService.java b/android/src/com/mapswithme/maps/scheduling/FirebaseJobService.java
new file mode 100644
index 0000000000..fa4321c96b
--- /dev/null
+++ b/android/src/com/mapswithme/maps/scheduling/FirebaseJobService.java
@@ -0,0 +1,27 @@
+package com.mapswithme.maps.scheduling;
+
+import com.firebase.jobdispatcher.JobParameters;
+import com.firebase.jobdispatcher.JobService;
+import com.mapswithme.util.log.Logger;
+import com.mapswithme.util.log.LoggerFactory;
+
+public class FirebaseJobService extends JobService
+{
+ private static final Logger LOGGER = LoggerFactory.INSTANCE.getLogger(LoggerFactory.Type.MISC);
+ private static final String TAG = NativeJobService.class.getSimpleName();
+
+ @Override
+ public boolean onStartJob(JobParameters job)
+ {
+ LOGGER.d(TAG, "onStartJob FirebaseJobService");
+ JobServiceDelegate delegate = new JobServiceDelegate(getApplication());
+ delegate.onStartJob();
+ return true;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters job)
+ {
+ return false;
+ }
+}
diff --git a/android/src/com/mapswithme/maps/scheduling/JobServiceDelegate.java b/android/src/com/mapswithme/maps/scheduling/JobServiceDelegate.java
new file mode 100644
index 0000000000..c258d962e4
--- /dev/null
+++ b/android/src/com/mapswithme/maps/scheduling/JobServiceDelegate.java
@@ -0,0 +1,33 @@
+package com.mapswithme.maps.scheduling;
+
+import android.app.Application;
+import android.support.annotation.NonNull;
+
+import com.mapswithme.maps.background.NotificationService;
+import com.mapswithme.util.ConnectionState;
+
+class JobServiceDelegate
+{
+ @NonNull
+ private final Application mApp;
+
+ JobServiceDelegate(@NonNull Application app)
+ {
+ mApp = app;
+ }
+
+ public void onStartJob()
+ {
+ ConnectionState.Type type = ConnectionState.requestCurrentType();
+ if (type == ConnectionState.Type.WIFI)
+ NotificationService.startOnConnectivityChanged(mApp);
+
+ retryJob();
+ }
+
+
+ private void retryJob()
+ {
+ ConnectivityJobScheduler.from(mApp).listen();
+ }
+}
diff --git a/android/src/com/mapswithme/maps/scheduling/NativeJobService.java b/android/src/com/mapswithme/maps/scheduling/NativeJobService.java
index 5e4c6cb29f..32bb01b17d 100644
--- a/android/src/com/mapswithme/maps/scheduling/NativeJobService.java
+++ b/android/src/com/mapswithme/maps/scheduling/NativeJobService.java
@@ -1,46 +1,28 @@
package com.mapswithme.maps.scheduling;
import android.annotation.TargetApi;
-import android.app.job.JobInfo;
import android.app.job.JobParameters;
-import android.app.job.JobScheduler;
import android.app.job.JobService;
-import android.content.ComponentName;
import android.os.Build;
-import com.mapswithme.maps.background.NotificationService;
-
-import java.util.Objects;
+import com.mapswithme.util.log.Logger;
+import com.mapswithme.util.log.LoggerFactory;
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class NativeJobService extends JobService
{
+ private static final Logger LOGGER = LoggerFactory.INSTANCE.getLogger(LoggerFactory.Type.MISC);
+ private static final String TAG = NativeJobService.class.getSimpleName();
+
@Override
public boolean onStartJob(JobParameters params)
{
- ConnectivityJobScheduler jobDispatcher = ConnectivityJobScheduler.from(this);
- ConnectivityJobScheduler.NetworkStatus status = jobDispatcher.getNetworkStatus();
- if (status.isNetworkStateChanged())
- NotificationService.startOnConnectivityChanged(this);
-
- if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
- scheduleRefresh();
-
+ LOGGER.d(TAG, "onStartJob");
+ JobServiceDelegate delegate = new JobServiceDelegate(getApplication());
+ delegate.onStartJob();
return true;
}
- private void scheduleRefresh()
- {
- JobScheduler service = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE);
- ComponentName component = new ComponentName(getApplicationContext(), NativeJobService.class);
- int jobId = NativeJobService.class.hashCode();
- JobInfo jobInfo = new JobInfo.Builder(jobId, component)
- .setMinimumLatency(ConnectivityJobScheduler.PERIODIC_IN_MILLIS)
- .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
- .build();
- Objects.requireNonNull(service).schedule(jobInfo);
- }
-
@Override
public boolean onStopJob(JobParameters params)
{
diff --git a/android/src/com/mapswithme/util/ConnectionState.java b/android/src/com/mapswithme/util/ConnectionState.java
index d5e57b4df2..fb0b056a9c 100644
--- a/android/src/com/mapswithme/util/ConnectionState.java
+++ b/android/src/com/mapswithme/util/ConnectionState.java
@@ -24,23 +24,23 @@ public class ConnectionState
WIFI(CONNECTION_WIFI, ConnectivityManager.TYPE_WIFI),
WWAN(CONNECTION_WWAN, ConnectivityManager.TYPE_MOBILE);
- private final byte mValue;
- private final int mNetwork;
+ private final byte mNativeRepresentation;
+ private final int mPlatformRepresentation;
- Type(byte value, int network)
+ Type(byte nativeRepresentation, int platformRepresentation)
{
- mValue = value;
- mNetwork = network;
+ mNativeRepresentation = nativeRepresentation;
+ mPlatformRepresentation = platformRepresentation;
}
- public byte getValue()
+ public byte getNativeRepresentation()
{
- return mValue;
+ return mNativeRepresentation;
}
- public int getNetwork()
+ public int getPlatformRepresentation()
{
- return mNetwork;
+ return mPlatformRepresentation;
}
}
@@ -118,9 +118,11 @@ public class ConnectionState
return info != null && info.isRoaming();
}
+ /*jni call*/
+ @SuppressWarnings("unused")
public static byte getConnectionState()
{
- return requestCurrentType().getValue();
+ return requestCurrentType().getNativeRepresentation();
}
@NonNull
@@ -128,7 +130,7 @@ public class ConnectionState
{
for (ConnectionState.Type each : ConnectionState.Type.values())
{
- if (isNetworkConnected(each.getNetwork()))
+ if (isNetworkConnected(each.getPlatformRepresentation()))
return each;
}
return NONE;