Convert more classes to Kotlin

pull/996/head
Alex Baker 5 years ago
parent 998f5b51d1
commit 9bf5216081

@ -12,16 +12,6 @@ import java.io.InputStreamReader
import java.io.StringReader import java.io.StringReader
object TestUtilities { object TestUtilities {
private var mockitoInitialized = false
fun initializeMockito(context: Context) {
if (!mockitoInitialized) {
// for mockito: https://code.google.com/p/dexmaker/issues/detail?id=2
System.setProperty("dexmaker.dexcache", context.cacheDir.toString())
mockitoInitialized = true
}
}
fun newPreferences(context: Context?): Preferences { fun newPreferences(context: Context?): Preferences {
return Preferences(context, "test_preferences") return Preferences(context, "test_preferences")
} }

@ -3,7 +3,6 @@ package org.tasks.injection
import android.content.Context import android.content.Context
import androidx.test.core.app.ApplicationProvider import androidx.test.core.app.ApplicationProvider
import org.junit.Before import org.junit.Before
import org.tasks.TestUtilities.initializeMockito
import timber.log.Timber import timber.log.Timber
abstract class InjectingTestCase { abstract class InjectingTestCase {
@ -11,7 +10,6 @@ abstract class InjectingTestCase {
open fun setUp() { open fun setUp() {
Thread.setDefaultUncaughtExceptionHandler { _, e: Throwable? -> Timber.e(e) } Thread.setDefaultUncaughtExceptionHandler { _, e: Throwable? -> Timber.e(e) }
val context = ApplicationProvider.getApplicationContext<Context>() val context = ApplicationProvider.getApplicationContext<Context>()
initializeMockito(context)
val component = DaggerTestComponent.builder() val component = DaggerTestComponent.builder()
.applicationModule(ApplicationModule(context)) .applicationModule(ApplicationModule(context))
.testModule(TestModule()).build() .testModule(TestModule()).build()

@ -1,26 +0,0 @@
package org.tasks.billing;
import android.app.Activity;
import android.content.Context;
import androidx.annotation.Nullable;
import org.tasks.analytics.Firebase;
public class BillingClientImpl implements BillingClient {
public static final String TYPE_SUBS = "";
public BillingClientImpl(Context context, Inventory inventory, Firebase firebase) {}
@Override
public void queryPurchases() {}
@Override
public void initiatePurchaseFlow(
Activity activity, String sku, String skuType, @Nullable String oldSku) {}
@Override
public void addPurchaseCallback(OnPurchasesUpdated onPurchasesUpdated) {}
@Override
public void consume(String sku) {}
}

@ -0,0 +1,19 @@
package org.tasks.billing
import android.app.Activity
import android.content.Context
import org.tasks.analytics.Firebase
class BillingClientImpl(context: Context?, inventory: Inventory?, firebase: Firebase?) : BillingClient {
override fun queryPurchases() {}
override fun initiatePurchaseFlow(
activity: Activity, sku: String, skuType: String, oldSku: String?) {
}
override fun addPurchaseCallback(onPurchasesUpdated: OnPurchasesUpdated) {}
override fun consume(sku: String) {}
companion object {
const val TYPE_SUBS = ""
}
}

@ -1,30 +0,0 @@
package org.tasks.billing;
public class Purchase {
public Purchase(@SuppressWarnings("unused") String json) {}
public String getSku() {
return null;
}
public String toJson() {
return null;
}
public boolean isCanceled() {
return false;
}
public int getSubscriptionPrice() {
return 0;
}
public boolean isMonthly() {
return false;
}
boolean isProSubscription() {
return false;
}
}

@ -0,0 +1,22 @@
package org.tasks.billing
class Purchase(json: String?) {
val sku: String?
get() = null
fun toJson(): String? {
return null
}
val isCanceled: Boolean
get() = false
val subscriptionPrice: Int
get() = 0
val isMonthly: Boolean
get() = false
val isProSubscription: Boolean
get() = false
}

@ -1,13 +0,0 @@
package org.tasks.billing;
import javax.inject.Inject;
public class SignatureVerifier {
@Inject
public SignatureVerifier() {}
public boolean verifySignature(Purchase purchase) {
return true;
}
}

@ -0,0 +1,9 @@
package org.tasks.billing
import javax.inject.Inject
class SignatureVerifier @Inject constructor() {
fun verifySignature(purchase: org.tasks.billing.Purchase): Boolean {
return true
}
}

@ -1,34 +0,0 @@
package org.tasks.gtasks;
import android.app.Activity;
import android.content.Context;
import com.todoroo.astrid.activity.MainActivity;
import io.reactivex.disposables.Disposable;
import io.reactivex.disposables.Disposables;
import javax.inject.Inject;
public class PlayServices {
@Inject
public PlayServices() {}
public boolean isPlayServicesAvailable() {
return false;
}
public boolean refreshAndCheck() {
return false;
}
public void resolve(Activity activity) {}
public String getStatus() {
return null;
}
public Disposable check(MainActivity mainActivity) {
return Disposables.empty();
}
public void updateSecurityProvider(Context context) {}
}

@ -0,0 +1,28 @@
package org.tasks.gtasks
import android.app.Activity
import android.content.Context
import com.todoroo.astrid.activity.MainActivity
import io.reactivex.disposables.Disposable
import io.reactivex.disposables.Disposables
import javax.inject.Inject
class PlayServices @Inject constructor() {
val isPlayServicesAvailable: Boolean
get() = false
fun refreshAndCheck(): Boolean {
return false
}
fun resolve(activity: Activity?) {}
val status: String?
get() = null
fun check(mainActivity: MainActivity?): Disposable {
return Disposables.empty()
}
fun updateSecurityProvider(context: Context?) {}
}

@ -1,24 +0,0 @@
package org.tasks.injection;
import android.content.Context;
import dagger.Module;
import dagger.Provides;
import org.tasks.location.MapFragment;
import org.tasks.location.MapboxMapFragment;
import org.tasks.location.MapboxSearchProvider;
import org.tasks.location.PlaceSearchProvider;
@Module
public class LocationModule {
@Provides
@ActivityScope
public PlaceSearchProvider getPlaceSearchProvider(@ForApplication Context context) {
return new MapboxSearchProvider(context);
}
@Provides
@ActivityScope
public MapFragment getMapFragment(@ForApplication Context context) {
return new MapboxMapFragment(context);
}
}

@ -0,0 +1,24 @@
package org.tasks.injection
import android.content.Context
import dagger.Module
import dagger.Provides
import org.tasks.location.MapFragment
import org.tasks.location.MapboxMapFragment
import org.tasks.location.MapboxSearchProvider
import org.tasks.location.PlaceSearchProvider
@Module
class LocationModule {
@Provides
@ActivityScope
fun getPlaceSearchProvider(@ForApplication context: Context): PlaceSearchProvider {
return MapboxSearchProvider(context)
}
@Provides
@ActivityScope
fun getMapFragment(@ForApplication context: Context): MapFragment {
return MapboxMapFragment(context)
}
}

@ -1,20 +0,0 @@
package org.tasks.location;
import javax.inject.Inject;
import org.jetbrains.annotations.Nullable;
import org.tasks.data.Place;
@SuppressWarnings("EmptyMethod")
public class GeofenceApi {
@Inject
public GeofenceApi() {}
public void registerAll() {}
public void update(@Nullable Place place) {}
public void update(String place) {}
public void update(long taskId) {}
}

@ -0,0 +1,11 @@
package org.tasks.location
import org.tasks.data.Place
import javax.inject.Inject
class GeofenceApi @Inject constructor() {
fun registerAll() {}
fun update(place: Place?) {}
fun update(place: String?) {}
fun update(taskId: Long) {}
}

@ -1,22 +0,0 @@
package org.tasks.location;
import android.app.IntentService;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import androidx.annotation.Nullable;
public class GeofenceTransitionsIntentService extends IntentService {
public GeofenceTransitionsIntentService() {
super("GeofenceTransitionsIntentService");
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {}
public static class Broadcast extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {}
}
}

@ -0,0 +1,13 @@
package org.tasks.location
import android.app.IntentService
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
class GeofenceTransitionsIntentService : IntentService("GeofenceTransitionsIntentService") {
override fun onHandleIntent(intent: Intent?) {}
class Broadcast : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {}
}
}

@ -1,66 +0,0 @@
package org.tasks.billing;
import com.google.gson.GsonBuilder;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Purchase {
private static final Pattern PATTERN = Pattern.compile("^(annual|monthly)_([0-1][0-9]|499)$");
private final com.android.billingclient.api.Purchase purchase;
public Purchase(String json) {
this(new GsonBuilder().create().fromJson(json, com.android.billingclient.api.Purchase.class));
}
public Purchase(com.android.billingclient.api.Purchase purchase) {
this.purchase = purchase;
}
public String toJson() {
return new GsonBuilder().create().toJson(purchase);
}
String getOriginalJson() {
return purchase.getOriginalJson();
}
String getSignature() {
return purchase.getSignature();
}
public String getSku() {
return purchase.getSku();
}
String getPurchaseToken() {
return purchase.getPurchaseToken();
}
boolean isProSubscription() {
return PATTERN.matcher(getSku()).matches();
}
boolean isMonthly() {
return getSku().startsWith("monthly");
}
boolean isCanceled() {
return !purchase.isAutoRenewing();
}
Integer getSubscriptionPrice() {
Matcher matcher = PATTERN.matcher(getSku());
if (matcher.matches()) {
int price = Integer.parseInt(matcher.group(2));
return price == 499 ? 5 : price;
}
return null;
}
@Override
public String toString() {
return "Purchase{" + "purchase=" + purchase + '}';
}
}

@ -0,0 +1,53 @@
package org.tasks.billing
import com.android.billingclient.api.Purchase
import com.google.gson.GsonBuilder
import java.util.regex.Pattern
class Purchase(private val purchase: Purchase) {
constructor(json: String?) : this(GsonBuilder().create().fromJson<Purchase>(json, Purchase::class.java))
fun toJson(): String {
return GsonBuilder().create().toJson(purchase)
}
override fun toString(): String {
return "Purchase(purchase=$purchase)"
}
val originalJson: String
get() = purchase.originalJson
val signature: String
get() = purchase.signature
val sku: String
get() = purchase.sku
val purchaseToken: String
get() = purchase.purchaseToken
val isProSubscription: Boolean
get() = PATTERN.matcher(sku).matches()
val isMonthly: Boolean
get() = sku.startsWith("monthly")
val isCanceled: Boolean
get() = !purchase.isAutoRenewing
val subscriptionPrice: Int?
get() {
val matcher = PATTERN.matcher(sku)
if (matcher.matches()) {
val price = matcher.group(2).toInt()
return if (price == 499) 5 else price
}
return null
}
companion object {
private val PATTERN = Pattern.compile("^(annual|monthly)_([0-1][0-9]|499)$")
}
}

@ -1,28 +0,0 @@
package org.tasks.billing;
import android.content.Context;
import java.io.IOException;
import javax.inject.Inject;
import org.tasks.R;
import org.tasks.injection.ForApplication;
import timber.log.Timber;
class SignatureVerifier {
private final String billingKey;
@Inject
SignatureVerifier(@ForApplication Context context) {
billingKey = context.getString(R.string.gp_key);
}
boolean verifySignature(Purchase purchase) {
try {
return Security.verifyPurchase(
billingKey, purchase.getOriginalJson(), purchase.getSignature());
} catch (IOException e) {
Timber.e(e);
return false;
}
}
}

@ -0,0 +1,22 @@
package org.tasks.billing
import android.content.Context
import org.tasks.R
import org.tasks.injection.ForApplication
import timber.log.Timber
import java.io.IOException
import javax.inject.Inject
internal class SignatureVerifier @Inject constructor(@ForApplication context: Context) {
private val billingKey: String = context.getString(R.string.gp_key)
fun verifySignature(purchase: Purchase): Boolean {
return try {
Security.verifyPurchase(
billingKey, purchase.originalJson, purchase.signature)
} catch (e: IOException) {
Timber.e(e)
false
}
}
}

@ -1,108 +0,0 @@
package org.tasks.gtasks;
import static io.reactivex.Single.fromCallable;
import android.app.Activity;
import android.content.Context;
import android.widget.Toast;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
import com.google.android.gms.common.GooglePlayServicesRepairableException;
import com.google.android.gms.security.ProviderInstaller;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import javax.inject.Inject;
import org.tasks.R;
import org.tasks.data.GoogleTaskListDao;
import org.tasks.data.LocationDao;
import org.tasks.injection.ForApplication;
import org.tasks.preferences.Preferences;
import timber.log.Timber;
public class PlayServices {
private static final int REQUEST_RESOLUTION = 10000;
private final Context context;
private final Preferences preferences;
private final GoogleTaskListDao googleTaskListDao;
private final LocationDao locationDao;
@Inject
public PlayServices(
@ForApplication Context context,
Preferences preferences,
GoogleTaskListDao googleTaskListDao,
LocationDao locationDao) {
this.context = context;
this.preferences = preferences;
this.googleTaskListDao = googleTaskListDao;
this.locationDao = locationDao;
}
public Disposable check(Activity activity) {
return Single.zip(
googleTaskListDao.accountCount(),
locationDao.geofenceCount(),
fromCallable(() -> preferences.getBoolean(R.string.p_google_drive_backup, false)),
(gtaskCount, geofenceCount, gdrive) -> gtaskCount > 0 || geofenceCount > 0 || gdrive)
.subscribeOn(Schedulers.io())
.map(needsCheck -> !needsCheck || refreshAndCheck())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
success -> {
if (!success && !preferences.getBoolean(R.string.warned_play_services, false)) {
preferences.setBoolean(R.string.warned_play_services, true);
resolve(activity);
}
});
}
public boolean refreshAndCheck() {
refresh();
return isPlayServicesAvailable();
}
public boolean isPlayServicesAvailable() {
return getResult() == ConnectionResult.SUCCESS;
}
private void refresh() {
GoogleApiAvailability instance = GoogleApiAvailability.getInstance();
int googlePlayServicesAvailable = instance.isGooglePlayServicesAvailable(context);
preferences.setInt(R.string.play_services_available, googlePlayServicesAvailable);
if (googlePlayServicesAvailable == ConnectionResult.SUCCESS) {
preferences.setBoolean(R.string.warned_play_services, false);
}
Timber.d(getStatus());
}
public void resolve(Activity activity) {
GoogleApiAvailability googleApiAvailability = GoogleApiAvailability.getInstance();
int error = preferences.getInt(R.string.play_services_available, -1);
if (googleApiAvailability.isUserResolvableError(error)) {
googleApiAvailability.getErrorDialog(activity, error, REQUEST_RESOLUTION).show();
} else {
Toast.makeText(activity, getStatus(), Toast.LENGTH_LONG).show();
}
}
private String getStatus() {
return GoogleApiAvailability.getInstance().getErrorString(getResult());
}
private int getResult() {
return preferences.getInt(R.string.play_services_available, -1);
}
public void updateSecurityProvider(Context context) {
try {
ProviderInstaller.installIfNeeded(context);
} catch (GooglePlayServicesRepairableException | GooglePlayServicesNotAvailableException e) {
Timber.e(e);
}
}
}

@ -0,0 +1,94 @@
package org.tasks.gtasks
import android.app.Activity
import android.content.Context
import android.widget.Toast
import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.GoogleApiAvailability
import com.google.android.gms.common.GooglePlayServicesNotAvailableException
import com.google.android.gms.common.GooglePlayServicesRepairableException
import com.google.android.gms.security.ProviderInstaller
import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.functions.Function3
import io.reactivex.schedulers.Schedulers
import org.tasks.R
import org.tasks.data.GoogleTaskListDao
import org.tasks.data.LocationDao
import org.tasks.injection.ForApplication
import org.tasks.preferences.Preferences
import timber.log.Timber
import javax.inject.Inject
class PlayServices @Inject constructor(
@param:ForApplication private val context: Context,
private val preferences: Preferences,
private val googleTaskListDao: GoogleTaskListDao,
private val locationDao: LocationDao) {
fun check(activity: Activity?): Disposable {
return Single.zip(
googleTaskListDao.accountCount(),
locationDao.geofenceCount(),
Single.fromCallable { preferences.getBoolean(R.string.p_google_drive_backup, false) },
Function3 { gtaskCount: Int, geofenceCount: Int, gdrive: Boolean -> gtaskCount > 0 || geofenceCount > 0 || gdrive })
.subscribeOn(Schedulers.io())
.map { !it || refreshAndCheck() }
.observeOn(AndroidSchedulers.mainThread())
.subscribe { success: Boolean ->
if (!success && !preferences.getBoolean(R.string.warned_play_services, false)) {
preferences.setBoolean(R.string.warned_play_services, true)
resolve(activity)
}
}
}
fun refreshAndCheck(): Boolean {
refresh()
return isPlayServicesAvailable
}
val isPlayServicesAvailable: Boolean
get() = result == ConnectionResult.SUCCESS
private fun refresh() {
val instance = GoogleApiAvailability.getInstance()
val googlePlayServicesAvailable = instance.isGooglePlayServicesAvailable(context)
preferences.setInt(R.string.play_services_available, googlePlayServicesAvailable)
if (googlePlayServicesAvailable == ConnectionResult.SUCCESS) {
preferences.setBoolean(R.string.warned_play_services, false)
}
Timber.d(status)
}
fun resolve(activity: Activity?) {
val googleApiAvailability = GoogleApiAvailability.getInstance()
val error = preferences.getInt(R.string.play_services_available, -1)
if (googleApiAvailability.isUserResolvableError(error)) {
googleApiAvailability.getErrorDialog(activity, error, REQUEST_RESOLUTION).show()
} else {
Toast.makeText(activity, status, Toast.LENGTH_LONG).show()
}
}
private val status: String
get() = GoogleApiAvailability.getInstance().getErrorString(result)
private val result: Int
get() = preferences.getInt(R.string.play_services_available, -1)
fun updateSecurityProvider(context: Context?) {
try {
ProviderInstaller.installIfNeeded(context)
} catch (e: GooglePlayServicesRepairableException) {
Timber.e(e)
} catch (e: GooglePlayServicesNotAvailableException) {
Timber.e(e)
}
}
companion object {
private const val REQUEST_RESOLUTION = 10000
}
}

@ -1,40 +0,0 @@
package org.tasks.injection;
import android.content.Context;
import dagger.Module;
import dagger.Provides;
import org.tasks.billing.Inventory;
import org.tasks.gtasks.PlayServices;
import org.tasks.location.GoogleMapFragment;
import org.tasks.location.GooglePlacesSearchProvider;
import org.tasks.location.MapFragment;
import org.tasks.location.MapboxMapFragment;
import org.tasks.location.MapboxSearchProvider;
import org.tasks.location.PlaceSearchProvider;
import org.tasks.preferences.Preferences;
@Module
class LocationModule {
@Provides
@ActivityScope
PlaceSearchProvider getPlaceSearchProvider(
@ForApplication Context context,
Preferences preferences,
PlayServices playServices,
Inventory inventory) {
return preferences.useGooglePlaces()
&& playServices.isPlayServicesAvailable()
&& inventory.hasPro()
? new GooglePlacesSearchProvider(context)
: new MapboxSearchProvider(context);
}
@Provides
@ActivityScope
MapFragment getMapFragment(@ForApplication Context context, Preferences preferences) {
return preferences.useGoogleMaps()
? new GoogleMapFragment(context)
: new MapboxMapFragment(context);
}
}

@ -0,0 +1,38 @@
package org.tasks.injection
import android.content.Context
import dagger.Module
import dagger.Provides
import org.tasks.billing.Inventory
import org.tasks.gtasks.PlayServices
import org.tasks.location.*
import org.tasks.preferences.Preferences
@Module
internal class LocationModule {
@Provides
@ActivityScope
fun getPlaceSearchProvider(
@ForApplication context: Context,
preferences: Preferences,
playServices: PlayServices,
inventory: Inventory): PlaceSearchProvider {
return if (preferences.useGooglePlaces()
&& playServices.isPlayServicesAvailable
&& inventory.hasPro()) {
GooglePlacesSearchProvider(context)
} else {
MapboxSearchProvider(context)
}
}
@Provides
@ActivityScope
fun getMapFragment(@ForApplication context: Context, preferences: Preferences): MapFragment {
return if (preferences.useGoogleMaps()) {
GoogleMapFragment(context)
} else {
MapboxMapFragment(context)
}
}
}

@ -1,89 +0,0 @@
package org.tasks.location;
import static com.google.android.gms.location.Geofence.NEVER_EXPIRE;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import androidx.annotation.Nullable;
import com.google.android.gms.location.GeofencingClient;
import com.google.android.gms.location.GeofencingRequest;
import com.google.android.gms.location.GeofencingRequest.Builder;
import com.google.android.gms.location.LocationServices;
import com.google.common.collect.ImmutableList;
import javax.inject.Inject;
import org.tasks.data.LocationDao;
import org.tasks.data.MergedGeofence;
import org.tasks.data.Place;
import org.tasks.injection.ForApplication;
import org.tasks.preferences.PermissionChecker;
import timber.log.Timber;
public class GeofenceApi {
private final Context context;
private final PermissionChecker permissionChecker;
private final LocationDao locationDao;
@Inject
public GeofenceApi(
@ForApplication Context context,
PermissionChecker permissionChecker,
LocationDao locationDao) {
this.context = context;
this.permissionChecker = permissionChecker;
this.locationDao = locationDao;
}
public void registerAll() {
for (Place place : locationDao.getPlacesWithGeofences()) {
update(place);
}
}
public void update(long taskId) {
update(locationDao.getPlaceForTask(taskId));
}
public void update(String place) {
update(locationDao.getPlace(place));
}
public void update(@Nullable Place place) {
if (place == null || !permissionChecker.canAccessLocation()) {
return;
}
GeofencingClient client = LocationServices.getGeofencingClient(context);
MergedGeofence geofence = locationDao.getGeofencesByPlace(place.getUid());
if (geofence != null) {
Timber.d("Adding geofence for %s", geofence);
client.addGeofences(
new Builder().addGeofence(toGoogleGeofence(geofence)).build(),
PendingIntent.getBroadcast(
context,
0,
new Intent(context, GeofenceTransitionsIntentService.Broadcast.class),
PendingIntent.FLAG_UPDATE_CURRENT));
} else {
Timber.d("Removing geofence for %s", place);
client.removeGeofences(ImmutableList.of(Long.toString(place.getId())));
}
}
private com.google.android.gms.location.Geofence toGoogleGeofence(MergedGeofence geofence) {
int transitionTypes = 0;
if (geofence.getArrival()) {
transitionTypes |= GeofencingRequest.INITIAL_TRIGGER_ENTER;
}
if (geofence.getDeparture()) {
transitionTypes |= GeofencingRequest.INITIAL_TRIGGER_EXIT;
}
return new com.google.android.gms.location.Geofence.Builder()
.setCircularRegion(geofence.getLatitude(), geofence.getLongitude(), geofence.getRadius())
.setRequestId(geofence.getUid())
.setTransitionTypes(transitionTypes)
.setExpirationDuration(NEVER_EXPIRE)
.build();
}
}

@ -0,0 +1,65 @@
package org.tasks.location
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import com.google.android.gms.location.Geofence
import com.google.android.gms.location.GeofencingRequest
import com.google.android.gms.location.LocationServices
import org.tasks.data.LocationDao
import org.tasks.data.MergedGeofence
import org.tasks.data.Place
import org.tasks.injection.ForApplication
import org.tasks.preferences.PermissionChecker
import timber.log.Timber
import javax.inject.Inject
class GeofenceApi @Inject constructor(
@param:ForApplication private val context: Context,
private val permissionChecker: PermissionChecker,
private val locationDao: LocationDao) {
fun registerAll() = locationDao.getPlacesWithGeofences().forEach(this::update)
fun update(taskId: Long) = locationDao.getPlaceForTask(taskId).apply(this::update)
fun update(place: String) = locationDao.getPlace(place).apply(this::update)
fun update(place: Place?) {
if (place == null || !permissionChecker.canAccessLocation()) {
return
}
val client = LocationServices.getGeofencingClient(context)
val geofence = locationDao.getGeofencesByPlace(place.uid!!)
if (geofence != null) {
Timber.d("Adding geofence for %s", geofence)
client.addGeofences(
GeofencingRequest.Builder().addGeofence(toGoogleGeofence(geofence)).build(),
PendingIntent.getBroadcast(
context,
0,
Intent(context, GeofenceTransitionsIntentService.Broadcast::class.java),
PendingIntent.FLAG_UPDATE_CURRENT))
} else {
Timber.d("Removing geofence for %s", place)
client.removeGeofences(listOf(place.id.toString()))
}
}
private fun toGoogleGeofence(geofence: MergedGeofence): Geofence {
var transitionTypes = 0
if (geofence.arrival) {
transitionTypes = transitionTypes or GeofencingRequest.INITIAL_TRIGGER_ENTER
}
if (geofence.departure) {
transitionTypes = transitionTypes or GeofencingRequest.INITIAL_TRIGGER_EXIT
}
return Geofence.Builder()
.setCircularRegion(geofence.latitude, geofence.longitude, geofence.radius.toFloat())
.setRequestId(geofence.uid)
.setTransitionTypes(transitionTypes)
.setExpirationDuration(Geofence.NEVER_EXPIRE)
.build()
}
}

@ -1,97 +0,0 @@
package org.tasks.location;
import static com.google.android.gms.location.Geofence.GEOFENCE_TRANSITION_ENTER;
import static com.google.android.gms.location.Geofence.GEOFENCE_TRANSITION_EXIT;
import static com.todoroo.andlib.utility.DateUtilities.now;
import static com.todoroo.astrid.reminders.ReminderService.TYPE_GEOFENCE_ENTER;
import static com.todoroo.astrid.reminders.ReminderService.TYPE_GEOFENCE_EXIT;
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import androidx.core.app.JobIntentService;
import com.google.android.gms.location.GeofencingEvent;
import com.google.common.collect.Lists;
import java.util.List;
import javax.inject.Inject;
import org.tasks.Notifier;
import org.tasks.data.Geofence;
import org.tasks.data.LocationDao;
import org.tasks.data.Place;
import org.tasks.injection.InjectingJobIntentService;
import org.tasks.injection.ServiceComponent;
import org.tasks.notifications.Notification;
import timber.log.Timber;
public class GeofenceTransitionsIntentService extends InjectingJobIntentService {
@Inject LocationDao locationDao;
@Inject Notifier notifier;
@Override
protected void doWork(Intent intent) {
GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
if (geofencingEvent.hasError()) {
Timber.e("geofence error code %s", geofencingEvent.getErrorCode());
return;
}
int transitionType = geofencingEvent.getGeofenceTransition();
List<com.google.android.gms.location.Geofence> triggeringGeofences =
geofencingEvent.getTriggeringGeofences();
Timber.i("Received geofence transition: %s, %s", transitionType, triggeringGeofences);
if (transitionType == GEOFENCE_TRANSITION_ENTER || transitionType == GEOFENCE_TRANSITION_EXIT) {
for (com.google.android.gms.location.Geofence triggerGeofence : triggeringGeofences) {
triggerNotification(triggerGeofence, transitionType == GEOFENCE_TRANSITION_ENTER);
}
} else {
Timber.w("invalid geofence transition type: %s", transitionType);
}
}
@Override
protected void inject(ServiceComponent component) {
component.inject(this);
}
private void triggerNotification(
com.google.android.gms.location.Geofence triggeringGeofence, boolean arrival) {
String requestId = triggeringGeofence.getRequestId();
try {
Place place = locationDao.getPlace(requestId);
if (place == null) {
Timber.e("Can't find place for requestId %s", requestId);
return;
}
List<Geofence> geofences = arrival
? locationDao.getArrivalGeofences(place.getUid(), now())
: locationDao.getDepartureGeofences(place.getUid(), now());
notifier.triggerNotifications(
Lists.transform(geofences, g -> toNotification(place, g, arrival)));
} catch (Exception e) {
Timber.e(e, "Error triggering geofence %s: %s", requestId, e.getMessage());
}
}
private Notification toNotification(Place place, Geofence geofence, boolean arrival) {
Notification notification = new Notification();
notification.setTaskId(geofence.getTask());
notification.setType(arrival ? TYPE_GEOFENCE_ENTER : TYPE_GEOFENCE_EXIT);
notification.setTimestamp(currentTimeMillis());
notification.setLocation(place.getId());
return notification;
}
public static class Broadcast extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
JobIntentService.enqueueWork(
context,
GeofenceTransitionsIntentService.class,
InjectingJobIntentService.JOB_ID_GEOFENCE_TRANSITION,
intent);
}
}
}

@ -0,0 +1,83 @@
package org.tasks.location
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import com.google.android.gms.location.Geofence
import com.google.android.gms.location.GeofencingEvent
import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.astrid.reminders.ReminderService
import org.tasks.Notifier
import org.tasks.data.LocationDao
import org.tasks.data.Place
import org.tasks.injection.InjectingJobIntentService
import org.tasks.injection.ServiceComponent
import org.tasks.notifications.Notification
import org.tasks.time.DateTimeUtils
import timber.log.Timber
import javax.inject.Inject
class GeofenceTransitionsIntentService : InjectingJobIntentService() {
@Inject lateinit var locationDao: LocationDao
@Inject lateinit var notifier: Notifier
override fun doWork(intent: Intent) {
val geofencingEvent = GeofencingEvent.fromIntent(intent)
if (geofencingEvent.hasError()) {
Timber.e("geofence error code %s", geofencingEvent.errorCode)
return
}
val transitionType = geofencingEvent.geofenceTransition
val triggeringGeofences = geofencingEvent.triggeringGeofences
Timber.i("Received geofence transition: %s, %s", transitionType, triggeringGeofences)
if (transitionType == Geofence.GEOFENCE_TRANSITION_ENTER || transitionType == Geofence.GEOFENCE_TRANSITION_EXIT) {
triggeringGeofences.forEach {
triggerNotification(it, transitionType == Geofence.GEOFENCE_TRANSITION_ENTER)
}
} else {
Timber.w("invalid geofence transition type: %s", transitionType)
}
}
private fun triggerNotification(triggeringGeofence: Geofence, arrival: Boolean) {
val requestId = triggeringGeofence.requestId
try {
val place = locationDao.getPlace(requestId)
if (place == null) {
Timber.e("Can't find place for requestId %s", requestId)
return
}
val geofences = if (arrival) {
locationDao.getArrivalGeofences(place.uid!!, DateUtilities.now())
} else {
locationDao.getDepartureGeofences(place.uid!!, DateUtilities.now())
}
geofences
.map { toNotification(place, it, arrival) }
.apply(notifier::triggerNotifications)
} catch (e: Exception) {
Timber.e(e, "Error triggering geofence %s: %s", requestId, e.message)
}
}
private fun toNotification(place: Place, geofence: org.tasks.data.Geofence?, arrival: Boolean): Notification {
val notification = Notification()
notification.taskId = geofence!!.task
notification.type = if (arrival) ReminderService.TYPE_GEOFENCE_ENTER else ReminderService.TYPE_GEOFENCE_EXIT
notification.timestamp = DateTimeUtils.currentTimeMillis()
notification.location = place.id
return notification
}
override fun inject(component: ServiceComponent) = component.inject(this)
class Broadcast : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
enqueueWork(
context,
GeofenceTransitionsIntentService::class.java,
JOB_ID_GEOFENCE_TRANSITION,
intent)
}
}
}

@ -1,123 +0,0 @@
package org.tasks.location;
import android.annotation.SuppressLint;
import android.content.Context;
import androidx.fragment.app.FragmentManager;
import com.google.android.gms.maps.CameraUpdate;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnMarkerClickListener;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.UiSettings;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MapStyleOptions;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import java.util.ArrayList;
import java.util.List;
import org.tasks.R;
import org.tasks.data.Place;
public class GoogleMapFragment implements MapFragment, OnMapReadyCallback, OnMarkerClickListener {
private static final String FRAG_TAG_MAP = "frag_tag_map";
private final Context context;
private final List<Marker> markers = new ArrayList<>();
private MapFragmentCallback callbacks;
private boolean dark;
private GoogleMap map;
public GoogleMapFragment(Context context) {
this.context = context;
}
@Override
public void init(FragmentManager fragmentManager, MapFragmentCallback callbacks, boolean dark) {
this.callbacks = callbacks;
this.dark = dark;
SupportMapFragment mapFragment =
(SupportMapFragment) fragmentManager.findFragmentByTag(FRAG_TAG_MAP);
if (mapFragment == null) {
mapFragment = new SupportMapFragment();
fragmentManager.beginTransaction().replace(R.id.map, mapFragment).commit();
}
mapFragment.getMapAsync(this);
}
@Override
public MapPosition getMapPosition() {
if (map == null) {
return null;
}
CameraPosition cameraPosition = map.getCameraPosition();
LatLng target = cameraPosition.target;
return new MapPosition(target.latitude, target.longitude, cameraPosition.zoom);
}
@Override
public void movePosition(MapPosition mapPosition, boolean animate) {
CameraUpdate cameraUpdate =
CameraUpdateFactory.newCameraPosition(
CameraPosition.builder()
.target(new LatLng(mapPosition.getLatitude(), mapPosition.getLongitude()))
.zoom(mapPosition.getZoom())
.build());
if (animate) {
map.animateCamera(cameraUpdate);
} else {
map.moveCamera(cameraUpdate);
}
}
@Override
public void setMarkers(List<Place> places) {
for (Marker marker : markers) {
marker.remove();
}
markers.clear();
for (Place place : places) {
Marker marker =
map.addMarker(
new MarkerOptions().position(new LatLng(place.getLatitude(), place.getLongitude())));
marker.setTag(place);
markers.add(marker);
}
}
@Override
public void disableGestures() {
map.getUiSettings().setAllGesturesEnabled(false);
}
@SuppressLint("MissingPermission")
@Override
public void showMyLocation() {
map.setMyLocationEnabled(true);
}
@Override
public void onMapReady(GoogleMap googleMap) {
map = googleMap;
if (dark) {
map.setMapStyle(MapStyleOptions.loadRawResourceStyle(context, R.raw.mapstyle_night));
}
UiSettings uiSettings = map.getUiSettings();
uiSettings.setMyLocationButtonEnabled(false);
uiSettings.setRotateGesturesEnabled(false);
map.setOnMarkerClickListener(this);
callbacks.onMapReady(this);
}
@Override
public int getMarkerId() {
return R.id.google_marker;
}
@Override
public boolean onMarkerClick(Marker marker) {
callbacks.onPlaceSelected((Place) marker.getTag());
return true;
}
}

@ -0,0 +1,99 @@
package org.tasks.location
import android.annotation.SuppressLint
import android.content.Context
import androidx.fragment.app.FragmentManager
import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.OnMapReadyCallback
import com.google.android.gms.maps.SupportMapFragment
import com.google.android.gms.maps.model.*
import org.tasks.R
import org.tasks.data.Place
import org.tasks.location.MapFragment.MapFragmentCallback
import java.util.*
class GoogleMapFragment(private val context: Context) : MapFragment, OnMapReadyCallback, GoogleMap.OnMarkerClickListener {
private val markers: MutableList<Marker> = ArrayList()
private lateinit var callbacks: MapFragmentCallback
private var dark = false
private var map: GoogleMap? = null
override fun init(fragmentManager: FragmentManager, callbacks: MapFragmentCallback, dark: Boolean) {
this.callbacks = callbacks
this.dark = dark
var mapFragment = fragmentManager.findFragmentByTag(FRAG_TAG_MAP) as SupportMapFragment?
if (mapFragment == null) {
mapFragment = SupportMapFragment()
fragmentManager.beginTransaction().replace(R.id.map, mapFragment).commit()
}
mapFragment.getMapAsync(this)
}
override fun getMapPosition(): MapPosition? {
if (map == null) {
return null
}
val cameraPosition = map!!.cameraPosition
val target = cameraPosition.target
return MapPosition(target.latitude, target.longitude, cameraPosition.zoom)
}
override fun movePosition(mapPosition: MapPosition, animate: Boolean) {
val cameraUpdate = CameraUpdateFactory.newCameraPosition(
CameraPosition.builder()
.target(LatLng(mapPosition.latitude, mapPosition.longitude))
.zoom(mapPosition.zoom)
.build())
if (animate) {
map!!.animateCamera(cameraUpdate)
} else {
map!!.moveCamera(cameraUpdate)
}
}
override fun setMarkers(places: List<Place>) {
for (marker in markers) {
marker.remove()
}
markers.clear()
for (place in places) {
val marker = map!!.addMarker(
MarkerOptions().position(LatLng(place.latitude, place.longitude)))
marker.tag = place
markers.add(marker)
}
}
override fun disableGestures() {
map!!.uiSettings.setAllGesturesEnabled(false)
}
@SuppressLint("MissingPermission")
override fun showMyLocation() {
map!!.isMyLocationEnabled = true
}
override fun onMapReady(googleMap: GoogleMap) {
map = googleMap
if (dark) {
map!!.setMapStyle(MapStyleOptions.loadRawResourceStyle(context, R.raw.mapstyle_night))
}
val uiSettings = map!!.uiSettings
uiSettings.isMyLocationButtonEnabled = false
uiSettings.isRotateGesturesEnabled = false
map!!.setOnMarkerClickListener(this)
callbacks.onMapReady(this)
}
override fun getMarkerId() = R.id.google_marker
override fun onMarkerClick(marker: Marker): Boolean {
callbacks.onPlaceSelected(marker.tag as Place?)
return true
}
companion object {
private const val FRAG_TAG_MAP = "frag_tag_map"
}
}

@ -1,141 +0,0 @@
package org.tasks.location;
import static java.util.Arrays.asList;
import static org.tasks.data.Place.newPlace;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import androidx.annotation.Nullable;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.android.libraries.places.api.Places;
import com.google.android.libraries.places.api.model.AutocompletePrediction;
import com.google.android.libraries.places.api.model.AutocompleteSessionToken;
import com.google.android.libraries.places.api.model.Place.Field;
import com.google.android.libraries.places.api.model.RectangularBounds;
import com.google.android.libraries.places.api.net.FetchPlaceRequest;
import com.google.android.libraries.places.api.net.FetchPlaceResponse;
import com.google.android.libraries.places.api.net.FindAutocompletePredictionsRequest;
import com.google.android.libraries.places.api.net.FindAutocompletePredictionsRequest.Builder;
import com.google.android.libraries.places.api.net.PlacesClient;
import java.util.ArrayList;
import java.util.List;
import org.tasks.Callback;
import org.tasks.R;
import org.tasks.data.Place;
public class GooglePlacesSearchProvider implements PlaceSearchProvider {
private static final String EXTRA_SESSION_TOKEN = "extra_session_token";
private final Context context;
private AutocompleteSessionToken token;
private PlacesClient placesClient;
public GooglePlacesSearchProvider(Context context) {
this.context = context;
}
@Override
public void restoreState(Bundle savedInstanceState) {
token = savedInstanceState.getParcelable(EXTRA_SESSION_TOKEN);
}
@Override
public void saveState(Bundle outState) {
outState.putParcelable(EXTRA_SESSION_TOKEN, token);
}
@Override
public int getAttributionRes(boolean dark) {
return dark
? R.drawable.places_powered_by_google_dark
: R.drawable.places_powered_by_google_light;
}
@Override
public void search(
String query,
@Nullable MapPosition bias,
Callback<List<PlaceSearchResult>> onSuccess,
Callback<String> onError) {
if (!Places.isInitialized()) {
Places.initialize(context, context.getString(R.string.google_key));
}
if (placesClient == null) {
placesClient = Places.createClient(context);
}
if (token == null) {
token = AutocompleteSessionToken.newInstance();
}
Builder request =
FindAutocompletePredictionsRequest.builder().setSessionToken(token).setQuery(query);
if (bias != null) {
request.setLocationBias(
RectangularBounds.newInstance(
LatLngBounds.builder()
.include(new LatLng(bias.getLatitude(), bias.getLongitude()))
.build()));
}
placesClient
.findAutocompletePredictions(request.build())
.addOnSuccessListener(
response -> onSuccess.call(toSearchResults(response.getAutocompletePredictions())))
.addOnFailureListener(e -> onError.call(e.getMessage()));
}
@Override
public void fetch(
PlaceSearchResult placeSearchResult, Callback<Place> onSuccess, Callback<String> onError) {
placesClient
.fetchPlace(
FetchPlaceRequest.builder(
placeSearchResult.getId(),
asList(
Field.ID,
Field.LAT_LNG,
Field.ADDRESS,
Field.WEBSITE_URI,
Field.NAME,
Field.PHONE_NUMBER))
.setSessionToken(token)
.build())
.addOnSuccessListener(result -> onSuccess.call(toPlace(result)))
.addOnFailureListener(e -> onError.call(e.getMessage()));
}
private List<PlaceSearchResult> toSearchResults(List<AutocompletePrediction> predictions) {
List<PlaceSearchResult> results = new ArrayList<>();
for (AutocompletePrediction prediction : predictions) {
results.add(
new PlaceSearchResult(
prediction.getPlaceId(),
prediction.getPrimaryText(null).toString(),
prediction.getSecondaryText(null).toString()));
}
return results;
}
private Place toPlace(FetchPlaceResponse fetchPlaceResponse) {
com.google.android.libraries.places.api.model.Place place = fetchPlaceResponse.getPlace();
Place result = newPlace();
result.setName(place.getName());
CharSequence address = place.getAddress();
if (address != null) {
result.setAddress(place.getAddress());
}
CharSequence phoneNumber = place.getPhoneNumber();
if (phoneNumber != null) {
result.setPhone(phoneNumber.toString());
}
Uri uri = place.getWebsiteUri();
if (uri != null) {
result.setUrl(uri.toString());
}
LatLng latLng = place.getLatLng();
result.setLatitude(latLng.latitude);
result.setLongitude(latLng.longitude);
return result;
}
}

@ -0,0 +1,115 @@
package org.tasks.location
import android.content.Context
import android.os.Bundle
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.LatLngBounds
import com.google.android.libraries.places.api.Places
import com.google.android.libraries.places.api.model.AutocompletePrediction
import com.google.android.libraries.places.api.model.AutocompleteSessionToken
import com.google.android.libraries.places.api.model.Place.Field
import com.google.android.libraries.places.api.model.RectangularBounds
import com.google.android.libraries.places.api.net.*
import org.tasks.Callback
import org.tasks.R
import org.tasks.data.Place
import org.tasks.data.Place.Companion.newPlace
class GooglePlacesSearchProvider(private val context: Context) : PlaceSearchProvider {
private var token: AutocompleteSessionToken? = null
private var placesClient: PlacesClient? = null
override fun restoreState(savedInstanceState: Bundle) {
token = savedInstanceState.getParcelable(EXTRA_SESSION_TOKEN)
}
override fun saveState(outState: Bundle) {
outState.putParcelable(EXTRA_SESSION_TOKEN, token)
}
override fun getAttributionRes(dark: Boolean): Int {
return if (dark) R.drawable.places_powered_by_google_dark else R.drawable.places_powered_by_google_light
}
override fun search(
query: String,
bias: MapPosition?,
onSuccess: Callback<List<PlaceSearchResult>>,
onError: Callback<String>) {
if (!Places.isInitialized()) {
Places.initialize(context, context.getString(R.string.google_key))
}
if (placesClient == null) {
placesClient = Places.createClient(context)
}
if (token == null) {
token = AutocompleteSessionToken.newInstance()
}
val request = FindAutocompletePredictionsRequest.builder().setSessionToken(token).setQuery(query)
if (bias != null) {
request.setLocationBias(
RectangularBounds.newInstance(
LatLngBounds.builder()
.include(LatLng(bias.latitude, bias.longitude))
.build()))
}
placesClient!!
.findAutocompletePredictions(request.build())
.addOnSuccessListener { response: FindAutocompletePredictionsResponse -> onSuccess.call(toSearchResults(response.autocompletePredictions)) }
.addOnFailureListener { e: Exception -> onError.call(e.message) }
}
override fun fetch(
placeSearchResult: PlaceSearchResult, onSuccess: Callback<Place>, onError: Callback<String>) {
placesClient!!
.fetchPlace(
FetchPlaceRequest.builder(
placeSearchResult.id,
listOf(
Field.ID,
Field.LAT_LNG,
Field.ADDRESS,
Field.WEBSITE_URI,
Field.NAME,
Field.PHONE_NUMBER))
.setSessionToken(token)
.build())
.addOnSuccessListener { result: FetchPlaceResponse -> onSuccess.call(toPlace(result)) }
.addOnFailureListener { e: Exception -> onError.call(e.message) }
}
private fun toSearchResults(predictions: List<AutocompletePrediction>): List<PlaceSearchResult> {
return predictions.map {
PlaceSearchResult(
it.placeId,
it.getPrimaryText(null).toString(),
it.getSecondaryText(null).toString())
}
}
private fun toPlace(fetchPlaceResponse: FetchPlaceResponse): Place {
val place = fetchPlaceResponse.place
val result = newPlace()
result.name = place.name
val address: CharSequence? = place.address
if (address != null) {
result.address = place.address
}
val phoneNumber: CharSequence? = place.phoneNumber
if (phoneNumber != null) {
result.phone = phoneNumber.toString()
}
val uri = place.websiteUri
if (uri != null) {
result.url = uri.toString()
}
val latLng = place.latLng
result.latitude = latLng!!.latitude
result.longitude = latLng.longitude
return result
}
companion object {
private const val EXTRA_SESSION_TOKEN = "extra_session_token"
}
}

@ -1,103 +0,0 @@
/*
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.dao;
import androidx.room.RoomDatabase;
import com.todoroo.astrid.data.Task;
import org.tasks.data.Alarm;
import org.tasks.data.AlarmDao;
import org.tasks.data.CaldavAccount;
import org.tasks.data.CaldavCalendar;
import org.tasks.data.CaldavDao;
import org.tasks.data.CaldavTask;
import org.tasks.data.DeletionDao;
import org.tasks.data.Filter;
import org.tasks.data.FilterDao;
import org.tasks.data.Geofence;
import org.tasks.data.GoogleTask;
import org.tasks.data.GoogleTaskAccount;
import org.tasks.data.GoogleTaskDao;
import org.tasks.data.GoogleTaskList;
import org.tasks.data.GoogleTaskListDao;
import org.tasks.data.LocationDao;
import org.tasks.data.Place;
import org.tasks.data.Tag;
import org.tasks.data.TagDao;
import org.tasks.data.TagData;
import org.tasks.data.TagDataDao;
import org.tasks.data.TaskAttachment;
import org.tasks.data.TaskAttachmentDao;
import org.tasks.data.TaskListMetadata;
import org.tasks.data.TaskListMetadataDao;
import org.tasks.data.UserActivity;
import org.tasks.data.UserActivityDao;
import org.tasks.notifications.Notification;
import org.tasks.notifications.NotificationDao;
@androidx.room.Database(
entities = {
Notification.class,
TagData.class,
UserActivity.class,
TaskAttachment.class,
TaskListMetadata.class,
Task.class,
Alarm.class,
Place.class,
Geofence.class,
Tag.class,
GoogleTask.class,
Filter.class,
GoogleTaskList.class,
CaldavCalendar.class,
CaldavTask.class,
CaldavAccount.class,
GoogleTaskAccount.class
},
version = 74)
public abstract class Database extends RoomDatabase {
public static final String NAME = "database";
public abstract NotificationDao notificationDao();
public abstract TagDataDao getTagDataDao();
public abstract UserActivityDao getUserActivityDao();
public abstract TaskAttachmentDao getTaskAttachmentDao();
public abstract TaskListMetadataDao getTaskListMetadataDao();
public abstract AlarmDao getAlarmDao();
public abstract LocationDao getLocationDao();
public abstract TagDao getTagDao();
public abstract GoogleTaskDao getGoogleTaskDao();
public abstract FilterDao getFilterDao();
public abstract GoogleTaskListDao getGoogleTaskListDao();
public abstract TaskDao getTaskDao();
public abstract CaldavDao getCaldavDao();
public abstract DeletionDao getDeletionDao();
public String getName() {
return NAME;
}
/** @return human-readable database name for debugging */
@Override
public String toString() {
return "DB:" + getName();
}
}

@ -0,0 +1,39 @@
package com.todoroo.astrid.dao
import androidx.room.Database
import androidx.room.RoomDatabase
import com.todoroo.astrid.data.Task
import org.tasks.data.*
import org.tasks.notifications.Notification
import org.tasks.notifications.NotificationDao
@Database(entities = [Notification::class, TagData::class, UserActivity::class, TaskAttachment::class, TaskListMetadata::class, Task::class, Alarm::class, Place::class, Geofence::class, Tag::class, GoogleTask::class, Filter::class, GoogleTaskList::class, CaldavCalendar::class, CaldavTask::class, CaldavAccount::class, GoogleTaskAccount::class], version = 74)
abstract class Database : RoomDatabase() {
abstract fun notificationDao(): NotificationDao
abstract val tagDataDao: TagDataDao
abstract val userActivityDao: UserActivityDao
abstract val taskAttachmentDao: TaskAttachmentDao
abstract val taskListMetadataDao: TaskListMetadataDao
abstract val alarmDao: AlarmDao
abstract val locationDao: LocationDao
abstract val tagDao: TagDao
abstract val googleTaskDao: GoogleTaskDao
abstract val filterDao: FilterDao
abstract val googleTaskListDao: GoogleTaskListDao
abstract val taskDao: TaskDao
abstract val caldavDao: CaldavDao
abstract val deletionDao: DeletionDao
/** @return human-readable database name for debugging
*/
override fun toString(): String {
return "DB:$name"
}
val name: String
get() = NAME
companion object {
const val NAME = "database"
}
}

@ -171,7 +171,7 @@ class PurchaseActivity : ThemedInjectingAppCompatActivity(), OnPurchasesUpdated,
&& adapter.selected == currentSubscription!!.subscriptionPrice && adapter.selected == currentSubscription!!.subscriptionPrice
private fun isUpgrade() = if (isMonthly() == currentSubscription!!.isMonthly) { private fun isUpgrade() = if (isMonthly() == currentSubscription!!.isMonthly) {
currentSubscription!!.subscriptionPrice < adapter.selected currentSubscription!!.subscriptionPrice!! < adapter.selected
} else { } else {
isMonthly() isMonthly()
} }

@ -3,7 +3,7 @@ package org.tasks.location;
import java.util.Objects; import java.util.Objects;
import org.tasks.data.Place; import org.tasks.data.Place;
class PlaceSearchResult { public class PlaceSearchResult {
private final String id; private final String id;
private final String name; private final String name;

@ -1,38 +0,0 @@
package org.tasks;
import android.util.Log;
import javax.inject.Inject;
import timber.log.Timber;
public class BuildSetup {
@Inject
public BuildSetup() {}
public void setup() {
Timber.plant(new ErrorReportingTree());
}
private static class ErrorReportingTree extends Timber.Tree {
@Override
protected void log(int priority, String tag, String message, Throwable t) {
if (priority < Log.WARN) {
return;
}
if (priority == Log.ERROR) {
if (t == null) {
Log.e(tag, message);
} else {
Log.e(tag, message, t);
}
} else if (priority == Log.WARN) {
if (t == null) {
Log.w(tag, message);
} else {
Log.w(tag, message, t);
}
}
}
}
}

@ -0,0 +1,32 @@
package org.tasks
import android.annotation.SuppressLint
import android.util.Log
import timber.log.Timber
import javax.inject.Inject
class BuildSetup @Inject constructor() {
fun setup() = Timber.plant(ErrorReportingTree())
private class ErrorReportingTree : Timber.Tree() {
@SuppressLint("LogNotTimber")
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
if (priority < Log.WARN) {
return
}
if (priority == Log.ERROR) {
if (t == null) {
Log.e(tag, message)
} else {
Log.e(tag, message, t)
}
} else if (priority == Log.WARN) {
if (t == null) {
Log.w(tag, message)
} else {
Log.w(tag, message, t)
}
}
}
}
}

@ -1,21 +0,0 @@
package org.tasks;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpResponse;
import javax.inject.Inject;
import okhttp3.OkHttpClient;
public class DebugNetworkInterceptor {
@Inject
public DebugNetworkInterceptor() {}
public void add(OkHttpClient.Builder builder) {}
public <T> T execute(HttpRequest httpRequest, Class<T> responseClass) {
return null;
}
public <T> T report(HttpResponse httpResponse, Class<T> responseClass, long start, long finish) {
return null;
}
}

@ -0,0 +1,12 @@
package org.tasks
import com.google.api.client.http.HttpRequest
import com.google.api.client.http.HttpResponse
import okhttp3.OkHttpClient
import javax.inject.Inject
class DebugNetworkInterceptor @Inject constructor() {
fun add(builder: OkHttpClient.Builder?) {}
fun <T> execute(httpRequest: HttpRequest?, responseClass: Class<T>?): T? = null
fun <T> report(httpResponse: HttpResponse?, responseClass: Class<T>?, start: Long, finish: Long): T? = null
}

@ -8,8 +8,7 @@ class Debug : InjectingPreferenceFragment() {
override fun getPreferenceXml() = 0 override fun getPreferenceXml() = 0
override fun setupPreferences(savedInstanceState: Bundle?) { override fun setupPreferences(savedInstanceState: Bundle?) {}
}
override fun inject(component: FragmentComponent) = component.inject(this) override fun inject(component: FragmentComponent) {}
} }
Loading…
Cancel
Save