Add BaseInvoker and InvokerFactory

pull/1216/head
Alex Baker 4 years ago
parent 0664e23076
commit 727ad6e7a4

@ -1,27 +1,16 @@
package com.todoroo.astrid.gtasks.api package com.todoroo.astrid.gtasks.api
import android.content.Context
import com.google.api.client.http.HttpResponseException
import com.google.api.client.http.javanet.NetHttpTransport import com.google.api.client.http.javanet.NetHttpTransport
import com.google.api.client.json.GenericJson
import com.google.api.client.json.jackson2.JacksonFactory import com.google.api.client.json.jackson2.JacksonFactory
import com.google.api.services.tasks.Tasks import com.google.api.services.tasks.Tasks
import com.google.api.services.tasks.TasksRequest
import com.google.api.services.tasks.TasksScopes
import com.google.api.services.tasks.model.Task import com.google.api.services.tasks.model.Task
import com.google.api.services.tasks.model.TaskList import com.google.api.services.tasks.model.TaskList
import com.google.api.services.tasks.model.TaskLists import com.google.api.services.tasks.model.TaskLists
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import org.tasks.BuildConfig
import org.tasks.DebugNetworkInterceptor import org.tasks.DebugNetworkInterceptor
import org.tasks.gtasks.GoogleAccountManager import org.tasks.googleapis.BaseInvoker
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import timber.log.Timber
import java.io.IOException import java.io.IOException
import javax.inject.Inject
/** /**
* Wrapper around the official Google Tasks API to simplify common operations. In the case of an * Wrapper around the official Google Tasks API to simplify common operations. In the case of an
@ -29,50 +18,15 @@ import javax.inject.Inject
* *
* @author Sam Bosley * @author Sam Bosley
*/ */
class GtasksInvoker { class GtasksInvoker(
private val context: Context credentials: HttpCredentialsAdapter,
private val googleAccountManager: GoogleAccountManager preferences: Preferences,
private val preferences: Preferences interceptor: DebugNetworkInterceptor
private val interceptor: DebugNetworkInterceptor ) : BaseInvoker(credentials, preferences, interceptor) {
private val account: String? private val service =
private val service: Tasks? Tasks.Builder(NetHttpTransport(), JacksonFactory(), credentials)
private val credentialsAdapter: HttpCredentialsAdapter? .setApplicationName(APP_NAME)
.build()
@Inject
constructor(
@ApplicationContext context: Context,
googleAccountManager: GoogleAccountManager,
preferences: Preferences,
interceptor: DebugNetworkInterceptor) {
this.context = context
this.googleAccountManager = googleAccountManager
this.preferences = preferences
this.interceptor = interceptor
account = null
service = null
credentialsAdapter = null
}
private constructor(
context: Context,
googleAccountManager: GoogleAccountManager,
preferences: Preferences,
interceptor: DebugNetworkInterceptor,
account: String) {
this.context = context
this.googleAccountManager = googleAccountManager
this.preferences = preferences
this.interceptor = interceptor
this.account = account
credentialsAdapter = HttpCredentialsAdapter(googleAccountManager)
service = Tasks.Builder(NetHttpTransport(), JacksonFactory(), credentialsAdapter)
.setApplicationName(String.format("Tasks/%s", BuildConfig.VERSION_NAME))
.build()
}
fun forAccount(account: String): GtasksInvoker {
return GtasksInvoker(context, googleAccountManager, preferences, interceptor, account)
}
@Throws(IOException::class) @Throws(IOException::class)
suspend fun allGtaskLists(pageToken: String?): TaskLists? = suspend fun allGtaskLists(pageToken: String?): TaskLists? =
@ -82,28 +36,28 @@ class GtasksInvoker {
suspend fun getAllGtasksFromListId( suspend fun getAllGtasksFromListId(
listId: String?, lastSyncDate: Long, pageToken: String?): com.google.api.services.tasks.model.Tasks? = listId: String?, lastSyncDate: Long, pageToken: String?): com.google.api.services.tasks.model.Tasks? =
execute( execute(
service!! service!!
.tasks() .tasks()
.list(listId) .list(listId)
.setMaxResults(100) .setMaxResults(100)
.setShowDeleted(true) .setShowDeleted(true)
.setShowHidden(true) .setShowHidden(true)
.setPageToken(pageToken) .setPageToken(pageToken)
.setUpdatedMin( .setUpdatedMin(
GtasksApiUtilities.unixTimeToGtasksCompletionTime(lastSyncDate).toStringRfc3339())) GtasksApiUtilities.unixTimeToGtasksCompletionTime(lastSyncDate).toStringRfc3339()))
@Throws(IOException::class) @Throws(IOException::class)
suspend fun getAllPositions( suspend fun getAllPositions(
listId: String?, pageToken: String?): com.google.api.services.tasks.model.Tasks? = listId: String?, pageToken: String?): com.google.api.services.tasks.model.Tasks? =
execute( execute(
service!! service!!
.tasks() .tasks()
.list(listId) .list(listId)
.setMaxResults(100) .setMaxResults(100)
.setShowDeleted(false) .setShowDeleted(false)
.setShowHidden(false) .setShowHidden(false)
.setPageToken(pageToken) .setPageToken(pageToken)
.setFields("items(id,parent,position),nextPageToken")) .setFields("items(id,parent,position),nextPageToken"))
@Throws(IOException::class) @Throws(IOException::class)
suspend fun createGtask( suspend fun createGtask(
@ -149,57 +103,4 @@ class GtasksInvoker {
} catch (ignored: HttpNotFoundException) { } catch (ignored: HttpNotFoundException) {
} }
} }
@Synchronized
@Throws(IOException::class)
private suspend fun <T> execute(request: TasksRequest<T>): T? = execute(request, false)
@Synchronized
@Throws(IOException::class)
private suspend fun <T> execute(request: TasksRequest<T>, retry: Boolean): T? =
withContext(Dispatchers.IO) {
credentialsAdapter!!.checkToken(account, TasksScopes.TASKS)
val response: T?
response = try {
val httpRequest = request.buildHttpRequest()
Timber.d("%s", httpRequest.url)
if (preferences.isFlipperEnabled) {
interceptor.execute(httpRequest, request.responseClass)
} else {
httpRequest.execute().parseAs(request.responseClass)
}
} catch (e: HttpResponseException) {
return@withContext if (e.statusCode == 401 && !retry) {
credentialsAdapter.invalidateToken()
execute(request, true)
} else if (e.statusCode == 404) {
throw HttpNotFoundException(e)
} else {
throw e
}
}
Timber.d("%s response: %s", getCaller(retry), prettyPrint(response))
response
}
@Throws(IOException::class)
private fun <T> prettyPrint(`object`: T?): Any? {
if (BuildConfig.DEBUG) {
if (`object` is GenericJson) {
return (`object` as GenericJson).toPrettyString()
}
}
return `object`
}
private fun getCaller(retry: Boolean): String {
if (BuildConfig.DEBUG) {
try {
return Thread.currentThread().stackTrace[if (retry) 6 else 5].methodName
} catch (e: Exception) {
Timber.e(e)
}
}
return ""
}
} }

@ -40,7 +40,11 @@ import java.net.URI
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
class HttpCredentialsAdapter @Inject constructor(private val googleAccountManager: GoogleAccountManager) : HttpRequestInitializer { class HttpCredentialsAdapter @Inject constructor(
private val googleAccountManager: GoogleAccountManager,
private val account: String,
private val scope: String
) : HttpRequestInitializer {
private var credentials: GoogleCredentials? = null private var credentials: GoogleCredentials? = null
@ -62,7 +66,7 @@ class HttpCredentialsAdapter @Inject constructor(private val googleAccountManage
} }
} }
suspend fun checkToken(account: String?, scope: String) { suspend fun checkToken() {
if (credentials == null) { if (credentials == null) {
val token = googleAccountManager.getAccessToken(account, scope) val token = googleAccountManager.getAccessToken(account, scope)
credentials = GoogleCredentials(AccessToken(token, null)) credentials = GoogleCredentials(AccessToken(token, null))

@ -5,7 +5,7 @@ import java.io.IOException;
public class HttpNotFoundException extends IOException { public class HttpNotFoundException extends IOException {
HttpNotFoundException(HttpResponseException e) { public HttpNotFoundException(HttpResponseException e) {
super(e.getMessage()); super(e.getMessage());
} }
} }

@ -2,12 +2,13 @@ package org.tasks.activities
import androidx.hilt.lifecycle.ViewModelInject import androidx.hilt.lifecycle.ViewModelInject
import com.google.api.services.tasks.model.TaskList import com.google.api.services.tasks.model.TaskList
import com.todoroo.astrid.gtasks.api.GtasksInvoker import org.tasks.googleapis.InvokerFactory
import org.tasks.ui.CompletableViewModel import org.tasks.ui.CompletableViewModel
class CreateListViewModel @ViewModelInject constructor( class CreateListViewModel @ViewModelInject constructor(
private val invoker: GtasksInvoker) : CompletableViewModel<TaskList>() { private val invoker: InvokerFactory
) : CompletableViewModel<TaskList>() {
suspend fun createList(account: String, name: String) { suspend fun createList(account: String, name: String) {
run { invoker.forAccount(account).createGtaskList(name)!! } run { invoker.getGtasksInvoker(account).createGtaskList(name)!! }
} }
} }

@ -1,13 +1,13 @@
package org.tasks.activities package org.tasks.activities
import androidx.hilt.lifecycle.ViewModelInject import androidx.hilt.lifecycle.ViewModelInject
import com.todoroo.astrid.gtasks.api.GtasksInvoker
import org.tasks.data.GoogleTaskList import org.tasks.data.GoogleTaskList
import org.tasks.googleapis.InvokerFactory
import org.tasks.ui.ActionViewModel import org.tasks.ui.ActionViewModel
class DeleteListViewModel @ViewModelInject constructor( class DeleteListViewModel @ViewModelInject constructor(
private val invoker: GtasksInvoker) : ActionViewModel() { private val invoker: InvokerFactory) : ActionViewModel() {
suspend fun deleteList(list: GoogleTaskList) { suspend fun deleteList(list: GoogleTaskList) {
run { invoker.forAccount(list.account!!).deleteGtaskList(list.remoteId) } run { invoker.getGtasksInvoker(list.account!!).deleteGtaskList(list.remoteId) }
} }
} }

@ -15,7 +15,6 @@ import com.google.api.services.tasks.model.TaskList
import com.todoroo.astrid.activity.MainActivity import com.todoroo.astrid.activity.MainActivity
import com.todoroo.astrid.activity.TaskListFragment import com.todoroo.astrid.activity.TaskListFragment
import com.todoroo.astrid.api.GtasksFilter import com.todoroo.astrid.api.GtasksFilter
import com.todoroo.astrid.gtasks.api.GtasksInvoker
import com.todoroo.astrid.service.TaskDeleter import com.todoroo.astrid.service.TaskDeleter
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
@ -32,7 +31,6 @@ class GoogleTaskListSettingsActivity : BaseListSettingsActivity() {
@Inject @ApplicationContext lateinit var context: Context @Inject @ApplicationContext lateinit var context: Context
@Inject lateinit var googleTaskListDao: GoogleTaskListDao @Inject lateinit var googleTaskListDao: GoogleTaskListDao
@Inject lateinit var taskDeleter: TaskDeleter @Inject lateinit var taskDeleter: TaskDeleter
@Inject lateinit var gtasksInvoker: GtasksInvoker
@BindView(R.id.name) @BindView(R.id.name)
lateinit var name: TextInputEditText lateinit var name: TextInputEditText

@ -2,13 +2,13 @@ package org.tasks.activities
import androidx.hilt.lifecycle.ViewModelInject import androidx.hilt.lifecycle.ViewModelInject
import com.google.api.services.tasks.model.TaskList import com.google.api.services.tasks.model.TaskList
import com.todoroo.astrid.gtasks.api.GtasksInvoker
import org.tasks.data.GoogleTaskList import org.tasks.data.GoogleTaskList
import org.tasks.googleapis.InvokerFactory
import org.tasks.ui.CompletableViewModel import org.tasks.ui.CompletableViewModel
class RenameListViewModel @ViewModelInject constructor( class RenameListViewModel @ViewModelInject constructor(
private val invoker: GtasksInvoker) : CompletableViewModel<TaskList>() { private val invoker: InvokerFactory) : CompletableViewModel<TaskList>() {
suspend fun renameList(list: GoogleTaskList, name: String) { suspend fun renameList(list: GoogleTaskList, name: String) {
run { invoker.forAccount(list.account!!).renameGtaskList(list.remoteId, name)!! } run { invoker.getGtasksInvoker(list.account!!).renameGtaskList(list.remoteId, name)!! }
} }
} }

@ -2,39 +2,29 @@ package org.tasks.drive
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import com.google.api.client.http.HttpResponseException
import com.google.api.client.http.InputStreamContent import com.google.api.client.http.InputStreamContent
import com.google.api.client.http.javanet.NetHttpTransport import com.google.api.client.http.javanet.NetHttpTransport
import com.google.api.client.json.GenericJson
import com.google.api.client.json.jackson2.JacksonFactory import com.google.api.client.json.jackson2.JacksonFactory
import com.google.api.services.drive.Drive import com.google.api.services.drive.Drive
import com.google.api.services.drive.DriveRequest
import com.google.api.services.drive.DriveScopes
import com.google.api.services.drive.model.File import com.google.api.services.drive.model.File
import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.astrid.backup.BackupConstants import com.todoroo.astrid.backup.BackupConstants
import com.todoroo.astrid.gtasks.api.HttpCredentialsAdapter import com.todoroo.astrid.gtasks.api.HttpCredentialsAdapter
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.tasks.BuildConfig
import org.tasks.DebugNetworkInterceptor import org.tasks.DebugNetworkInterceptor
import org.tasks.R
import org.tasks.files.FileHelper import org.tasks.files.FileHelper
import org.tasks.googleapis.BaseInvoker
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import timber.log.Timber
import java.io.IOException import java.io.IOException
import javax.inject.Inject
class DriveInvoker @Inject constructor( class DriveInvoker(
@param:ApplicationContext private val context: Context, @param:ApplicationContext private val context: Context,
private val preferences: Preferences, preferences: Preferences,
private val credentialsAdapter: HttpCredentialsAdapter, credentialsAdapter: HttpCredentialsAdapter,
private val interceptor: DebugNetworkInterceptor) { interceptor: DebugNetworkInterceptor
) : BaseInvoker(credentialsAdapter, preferences, interceptor) {
private val service = private val service =
Drive Drive.Builder(NetHttpTransport(), JacksonFactory(), credentialsAdapter)
.Builder(NetHttpTransport(), JacksonFactory(), credentialsAdapter) .setApplicationName(APP_NAME)
.setApplicationName(String.format("Tasks/%s", BuildConfig.VERSION_NAME))
.build() .build()
@Throws(IOException::class) @Throws(IOException::class)
@ -83,64 +73,6 @@ class DriveInvoker @Inject constructor(
return execute(service.files().create(metadata, content)) return execute(service.files().create(metadata, content))
} }
@Synchronized
@Throws(IOException::class)
private suspend fun <T> execute(request: DriveRequest<T>): T? {
return execute(request, false)
}
@Synchronized
@Throws(IOException::class)
private suspend fun <T> execute(
request: DriveRequest<T>,
retry: Boolean
): T? = withContext(Dispatchers.IO) {
val account = preferences.getStringValue(R.string.p_google_drive_backup_account)
credentialsAdapter.checkToken(account, DriveScopes.DRIVE_FILE)
Timber.d("%s request: %s", caller, request)
val response: T?
response = try {
if (preferences.isFlipperEnabled) {
val start = DateUtilities.now()
val httpResponse = request.executeUnparsed()
interceptor.report(httpResponse, request.responseClass, start, DateUtilities.now())
} else {
request.execute()
}
} catch (e: HttpResponseException) {
return@withContext if (e.statusCode == 401 && !retry) {
credentialsAdapter.invalidateToken()
execute(request, true)
} else {
throw e
}
}
Timber.d("%s response: %s", caller, prettyPrint(response))
return@withContext response
}
@Throws(IOException::class)
private fun <T> prettyPrint(`object`: T?): Any? {
if (BuildConfig.DEBUG) {
if (`object` is GenericJson) {
return (`object` as GenericJson).toPrettyString()
}
}
return `object`
}
private val caller: String
get() {
if (BuildConfig.DEBUG) {
try {
return Thread.currentThread().stackTrace[4].methodName
} catch (e: Exception) {
Timber.e(e)
}
}
return ""
}
companion object { companion object {
private const val MIME_FOLDER = "application/vnd.google-apps.folder" private const val MIME_FOLDER = "application/vnd.google-apps.folder"
private val DRIVE_FILE_COMPARATOR = Comparator<File> { f1, f2 -> private val DRIVE_FILE_COMPARATOR = Comparator<File> { f1, f2 ->

@ -0,0 +1,80 @@
package org.tasks.googleapis
import com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest
import com.google.api.client.http.HttpResponseException
import com.google.api.client.json.GenericJson
import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.astrid.gtasks.api.HttpCredentialsAdapter
import com.todoroo.astrid.gtasks.api.HttpNotFoundException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.tasks.BuildConfig
import org.tasks.DebugNetworkInterceptor
import org.tasks.preferences.Preferences
import timber.log.Timber
import java.io.IOException
abstract class BaseInvoker(
private val credentialsAdapter: HttpCredentialsAdapter,
private val preferences: Preferences,
private val interceptor: DebugNetworkInterceptor
) {
@Synchronized
@Throws(IOException::class)
protected suspend fun <T> execute(request: AbstractGoogleJsonClientRequest<T>): T? = execute(request, false)
@Synchronized
@Throws(IOException::class)
private suspend fun <T> execute(request: AbstractGoogleJsonClientRequest<T>, retry: Boolean): T? =
withContext(Dispatchers.IO) {
credentialsAdapter.checkToken()
Timber.d("%s request: %s", caller, request)
val response: T?
response = try {
if (preferences.isFlipperEnabled) {
val start = DateUtilities.now()
val httpResponse = request.executeUnparsed()
interceptor.report(httpResponse, request.responseClass, start, DateUtilities.now())
} else {
request.execute()
}
} catch (e: HttpResponseException) {
return@withContext if (e.statusCode == 401 && !retry) {
credentialsAdapter.invalidateToken()
execute(request, true)
} else if (e.statusCode == 404) {
throw HttpNotFoundException(e)
} else {
throw e
}
}
Timber.d("%s response: %s", caller, prettyPrint(response))
response
}
@Throws(IOException::class)
private fun <T> prettyPrint(`object`: T?): Any? {
if (BuildConfig.DEBUG) {
if (`object` is GenericJson) {
return (`object` as GenericJson).toPrettyString()
}
}
return `object`
}
private val caller: String
get() {
if (BuildConfig.DEBUG) {
try {
return Thread.currentThread().stackTrace[4].methodName
} catch (e: Exception) {
Timber.e(e)
}
}
return ""
}
companion object {
const val APP_NAME = "Tasks/${BuildConfig.VERSION_NAME}"
}
}

@ -0,0 +1,39 @@
package org.tasks.googleapis
import android.content.Context
import com.google.api.services.drive.DriveScopes
import com.google.api.services.tasks.TasksScopes
import com.todoroo.astrid.gtasks.api.GtasksInvoker
import com.todoroo.astrid.gtasks.api.HttpCredentialsAdapter
import dagger.hilt.android.qualifiers.ApplicationContext
import org.tasks.DebugNetworkInterceptor
import org.tasks.R
import org.tasks.drive.DriveInvoker
import org.tasks.gtasks.GoogleAccountManager
import org.tasks.preferences.Preferences
import javax.inject.Inject
class InvokerFactory @Inject constructor(
@ApplicationContext private val context: Context,
private val googleAccountManager: GoogleAccountManager,
private val preferences: Preferences,
private val interceptor: DebugNetworkInterceptor
) {
fun getDriveInvoker() = DriveInvoker(
context,
preferences,
HttpCredentialsAdapter(
googleAccountManager,
preferences.getStringValue(R.string.p_google_drive_backup_account) ?: "",
DriveScopes.DRIVE_FILE
),
interceptor
)
fun getGtasksInvoker(account: String) = GtasksInvoker(
HttpCredentialsAdapter(googleAccountManager, account, TasksScopes.TASKS),
preferences,
interceptor
)
}

@ -25,6 +25,7 @@ import org.tasks.analytics.Firebase
import org.tasks.billing.Inventory import org.tasks.billing.Inventory
import org.tasks.data.* import org.tasks.data.*
import org.tasks.date.DateTimeUtils.newDateTime import org.tasks.date.DateTimeUtils.newDateTime
import org.tasks.googleapis.InvokerFactory
import org.tasks.preferences.DefaultFilterProvider import org.tasks.preferences.DefaultFilterProvider
import org.tasks.preferences.PermissionChecker import org.tasks.preferences.PermissionChecker
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
@ -55,7 +56,7 @@ class GoogleTaskSynchronizer @Inject constructor(
private val localBroadcastManager: LocalBroadcastManager, private val localBroadcastManager: LocalBroadcastManager,
private val inventory: Inventory, private val inventory: Inventory,
private val taskDeleter: TaskDeleter, private val taskDeleter: TaskDeleter,
private val gtasksInvoker: GtasksInvoker) { private val invokers: InvokerFactory) {
suspend fun sync(account: GoogleTaskAccount, i: Int) { suspend fun sync(account: GoogleTaskAccount, i: Int) {
Timber.d("%s: start sync", account) Timber.d("%s: start sync", account)
@ -106,7 +107,7 @@ class GoogleTaskSynchronizer @Inject constructor(
account.error = context.getString(R.string.cannot_access_account) account.error = context.getString(R.string.cannot_access_account)
return return
} }
val gtasksInvoker = gtasksInvoker.forAccount(account.account!!) val gtasksInvoker = invokers.getGtasksInvoker(account.account!!)
pushLocalChanges(account, gtasksInvoker) pushLocalChanges(account, gtasksInvoker)
val gtaskLists: MutableList<TaskList> = ArrayList() val gtaskLists: MutableList<TaskList> = ArrayList()
var nextPageToken: String? = null var nextPageToken: String? = null

@ -13,7 +13,7 @@ import org.tasks.LocalBroadcastManager
import org.tasks.R import org.tasks.R
import org.tasks.Strings.isNullOrEmpty import org.tasks.Strings.isNullOrEmpty
import org.tasks.analytics.Firebase import org.tasks.analytics.Firebase
import org.tasks.drive.DriveInvoker import org.tasks.googleapis.InvokerFactory
import org.tasks.injection.BaseWorker import org.tasks.injection.BaseWorker
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import timber.log.Timber import timber.log.Timber
@ -28,10 +28,11 @@ class DriveUploader @WorkerInject constructor(
@Assisted context: Context, @Assisted context: Context,
@Assisted workerParams: WorkerParameters, @Assisted workerParams: WorkerParameters,
firebase: Firebase, firebase: Firebase,
private val drive: DriveInvoker, invokers: InvokerFactory,
private val preferences: Preferences, private val preferences: Preferences,
private val localBroadcastManager: LocalBroadcastManager private val localBroadcastManager: LocalBroadcastManager
) : BaseWorker(context, workerParams, firebase) { ) : BaseWorker(context, workerParams, firebase) {
private val drive = invokers.getDriveInvoker()
override suspend fun run(): Result { override suspend fun run(): Result {
val inputData = inputData val inputData = inputData

@ -15,7 +15,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.tasks.R import org.tasks.R
import org.tasks.date.DateTimeUtils.newDateTime import org.tasks.date.DateTimeUtils.newDateTime
import org.tasks.drive.DriveInvoker import org.tasks.googleapis.InvokerFactory
import org.tasks.gtasks.GoogleAccountManager import org.tasks.gtasks.GoogleAccountManager
import timber.log.Timber import timber.log.Timber
import java.io.File import java.io.File
@ -23,9 +23,10 @@ import java.io.File
class PreferencesViewModel @ViewModelInject constructor( class PreferencesViewModel @ViewModelInject constructor(
@ApplicationContext private val context: Context, @ApplicationContext private val context: Context,
private val preferences: Preferences, private val preferences: Preferences,
private val driveInvoker: DriveInvoker, invokers: InvokerFactory,
private val googleAccountManager: GoogleAccountManager, private val googleAccountManager: GoogleAccountManager,
) : ViewModel() { ) : ViewModel() {
private val driveInvoker = invokers.getDriveInvoker()
val lastBackup = MutableLiveData<Long?>() val lastBackup = MutableLiveData<Long?>()
val lastDriveBackup = MutableLiveData<Long?>() val lastDriveBackup = MutableLiveData<Long?>()
val lastAndroidBackup = MutableLiveData<Long>() val lastAndroidBackup = MutableLiveData<Long>()

Loading…
Cancel
Save