mirror of https://github.com/tasks/tasks
Convert more classes to Kotlin
parent
998f5b51d1
commit
9bf5216081
@ -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"
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
Loading…
Reference in New Issue