diff --git a/app/src/main/java/com/todoroo/astrid/gtasks/api/GtasksInvoker.kt b/app/src/main/java/com/todoroo/astrid/gtasks/api/GtasksInvoker.kt index cd8199609..38af8932a 100644 --- a/app/src/main/java/com/todoroo/astrid/gtasks/api/GtasksInvoker.kt +++ b/app/src/main/java/com/todoroo/astrid/gtasks/api/GtasksInvoker.kt @@ -1,27 +1,16 @@ 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.json.GenericJson import com.google.api.client.json.jackson2.JacksonFactory 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.TaskList 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.withContext -import org.tasks.BuildConfig import org.tasks.DebugNetworkInterceptor -import org.tasks.gtasks.GoogleAccountManager +import org.tasks.googleapis.BaseInvoker import org.tasks.preferences.Preferences -import timber.log.Timber import java.io.IOException -import javax.inject.Inject /** * 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 */ -class GtasksInvoker { - private val context: Context - private val googleAccountManager: GoogleAccountManager - private val preferences: Preferences - private val interceptor: DebugNetworkInterceptor - private val account: String? - private val service: Tasks? - private val credentialsAdapter: HttpCredentialsAdapter? - - @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) - } +class GtasksInvoker( + credentials: HttpCredentialsAdapter, + preferences: Preferences, + interceptor: DebugNetworkInterceptor +) : BaseInvoker(credentials, preferences, interceptor) { + private val service = + Tasks.Builder(NetHttpTransport(), JacksonFactory(), credentials) + .setApplicationName(APP_NAME) + .build() @Throws(IOException::class) suspend fun allGtaskLists(pageToken: String?): TaskLists? = @@ -82,28 +36,28 @@ class GtasksInvoker { suspend fun getAllGtasksFromListId( listId: String?, lastSyncDate: Long, pageToken: String?): com.google.api.services.tasks.model.Tasks? = execute( - service!! - .tasks() - .list(listId) - .setMaxResults(100) - .setShowDeleted(true) - .setShowHidden(true) - .setPageToken(pageToken) - .setUpdatedMin( - GtasksApiUtilities.unixTimeToGtasksCompletionTime(lastSyncDate).toStringRfc3339())) + service!! + .tasks() + .list(listId) + .setMaxResults(100) + .setShowDeleted(true) + .setShowHidden(true) + .setPageToken(pageToken) + .setUpdatedMin( + GtasksApiUtilities.unixTimeToGtasksCompletionTime(lastSyncDate).toStringRfc3339())) @Throws(IOException::class) suspend fun getAllPositions( listId: String?, pageToken: String?): com.google.api.services.tasks.model.Tasks? = execute( - service!! - .tasks() - .list(listId) - .setMaxResults(100) - .setShowDeleted(false) - .setShowHidden(false) - .setPageToken(pageToken) - .setFields("items(id,parent,position),nextPageToken")) + service!! + .tasks() + .list(listId) + .setMaxResults(100) + .setShowDeleted(false) + .setShowHidden(false) + .setPageToken(pageToken) + .setFields("items(id,parent,position),nextPageToken")) @Throws(IOException::class) suspend fun createGtask( @@ -149,57 +103,4 @@ class GtasksInvoker { } catch (ignored: HttpNotFoundException) { } } - - @Synchronized - @Throws(IOException::class) - private suspend fun execute(request: TasksRequest): T? = execute(request, false) - - @Synchronized - @Throws(IOException::class) - private suspend fun execute(request: TasksRequest, 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 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 "" - } } \ No newline at end of file diff --git a/app/src/main/java/com/todoroo/astrid/gtasks/api/HttpCredentialsAdapter.kt b/app/src/main/java/com/todoroo/astrid/gtasks/api/HttpCredentialsAdapter.kt index da4f3e295..b2dc41fae 100644 --- a/app/src/main/java/com/todoroo/astrid/gtasks/api/HttpCredentialsAdapter.kt +++ b/app/src/main/java/com/todoroo/astrid/gtasks/api/HttpCredentialsAdapter.kt @@ -40,7 +40,11 @@ import java.net.URI import java.util.* 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 @@ -62,7 +66,7 @@ class HttpCredentialsAdapter @Inject constructor(private val googleAccountManage } } - suspend fun checkToken(account: String?, scope: String) { + suspend fun checkToken() { if (credentials == null) { val token = googleAccountManager.getAccessToken(account, scope) credentials = GoogleCredentials(AccessToken(token, null)) diff --git a/app/src/main/java/com/todoroo/astrid/gtasks/api/HttpNotFoundException.java b/app/src/main/java/com/todoroo/astrid/gtasks/api/HttpNotFoundException.java index 90b5967d3..5441772de 100644 --- a/app/src/main/java/com/todoroo/astrid/gtasks/api/HttpNotFoundException.java +++ b/app/src/main/java/com/todoroo/astrid/gtasks/api/HttpNotFoundException.java @@ -5,7 +5,7 @@ import java.io.IOException; public class HttpNotFoundException extends IOException { - HttpNotFoundException(HttpResponseException e) { + public HttpNotFoundException(HttpResponseException e) { super(e.getMessage()); } } diff --git a/app/src/main/java/org/tasks/activities/CreateListViewModel.kt b/app/src/main/java/org/tasks/activities/CreateListViewModel.kt index cd3169439..bd9985536 100644 --- a/app/src/main/java/org/tasks/activities/CreateListViewModel.kt +++ b/app/src/main/java/org/tasks/activities/CreateListViewModel.kt @@ -2,12 +2,13 @@ package org.tasks.activities import androidx.hilt.lifecycle.ViewModelInject 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 class CreateListViewModel @ViewModelInject constructor( - private val invoker: GtasksInvoker) : CompletableViewModel() { + private val invoker: InvokerFactory +) : CompletableViewModel() { suspend fun createList(account: String, name: String) { - run { invoker.forAccount(account).createGtaskList(name)!! } + run { invoker.getGtasksInvoker(account).createGtaskList(name)!! } } } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/activities/DeleteListViewModel.kt b/app/src/main/java/org/tasks/activities/DeleteListViewModel.kt index 224e01e1f..9d68bf8ae 100644 --- a/app/src/main/java/org/tasks/activities/DeleteListViewModel.kt +++ b/app/src/main/java/org/tasks/activities/DeleteListViewModel.kt @@ -1,13 +1,13 @@ package org.tasks.activities import androidx.hilt.lifecycle.ViewModelInject -import com.todoroo.astrid.gtasks.api.GtasksInvoker import org.tasks.data.GoogleTaskList +import org.tasks.googleapis.InvokerFactory import org.tasks.ui.ActionViewModel class DeleteListViewModel @ViewModelInject constructor( - private val invoker: GtasksInvoker) : ActionViewModel() { + private val invoker: InvokerFactory) : ActionViewModel() { suspend fun deleteList(list: GoogleTaskList) { - run { invoker.forAccount(list.account!!).deleteGtaskList(list.remoteId) } + run { invoker.getGtasksInvoker(list.account!!).deleteGtaskList(list.remoteId) } } } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/activities/GoogleTaskListSettingsActivity.kt b/app/src/main/java/org/tasks/activities/GoogleTaskListSettingsActivity.kt index 548109d5e..775586e25 100644 --- a/app/src/main/java/org/tasks/activities/GoogleTaskListSettingsActivity.kt +++ b/app/src/main/java/org/tasks/activities/GoogleTaskListSettingsActivity.kt @@ -15,7 +15,6 @@ import com.google.api.services.tasks.model.TaskList import com.todoroo.astrid.activity.MainActivity import com.todoroo.astrid.activity.TaskListFragment import com.todoroo.astrid.api.GtasksFilter -import com.todoroo.astrid.gtasks.api.GtasksInvoker import com.todoroo.astrid.service.TaskDeleter import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.qualifiers.ApplicationContext @@ -32,7 +31,6 @@ class GoogleTaskListSettingsActivity : BaseListSettingsActivity() { @Inject @ApplicationContext lateinit var context: Context @Inject lateinit var googleTaskListDao: GoogleTaskListDao @Inject lateinit var taskDeleter: TaskDeleter - @Inject lateinit var gtasksInvoker: GtasksInvoker @BindView(R.id.name) lateinit var name: TextInputEditText diff --git a/app/src/main/java/org/tasks/activities/RenameListViewModel.kt b/app/src/main/java/org/tasks/activities/RenameListViewModel.kt index 905fd9a10..3199bd08b 100644 --- a/app/src/main/java/org/tasks/activities/RenameListViewModel.kt +++ b/app/src/main/java/org/tasks/activities/RenameListViewModel.kt @@ -2,13 +2,13 @@ package org.tasks.activities import androidx.hilt.lifecycle.ViewModelInject import com.google.api.services.tasks.model.TaskList -import com.todoroo.astrid.gtasks.api.GtasksInvoker import org.tasks.data.GoogleTaskList +import org.tasks.googleapis.InvokerFactory import org.tasks.ui.CompletableViewModel class RenameListViewModel @ViewModelInject constructor( - private val invoker: GtasksInvoker) : CompletableViewModel() { + private val invoker: InvokerFactory) : CompletableViewModel() { 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)!! } } } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/drive/DriveInvoker.kt b/app/src/main/java/org/tasks/drive/DriveInvoker.kt index d097e06df..05534f0ef 100644 --- a/app/src/main/java/org/tasks/drive/DriveInvoker.kt +++ b/app/src/main/java/org/tasks/drive/DriveInvoker.kt @@ -2,39 +2,29 @@ package org.tasks.drive import android.content.Context import android.net.Uri -import com.google.api.client.http.HttpResponseException import com.google.api.client.http.InputStreamContent 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.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.todoroo.andlib.utility.DateUtilities import com.todoroo.astrid.backup.BackupConstants import com.todoroo.astrid.gtasks.api.HttpCredentialsAdapter 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.R import org.tasks.files.FileHelper +import org.tasks.googleapis.BaseInvoker import org.tasks.preferences.Preferences -import timber.log.Timber import java.io.IOException -import javax.inject.Inject -class DriveInvoker @Inject constructor( +class DriveInvoker( @param:ApplicationContext private val context: Context, - private val preferences: Preferences, - private val credentialsAdapter: HttpCredentialsAdapter, - private val interceptor: DebugNetworkInterceptor) { + preferences: Preferences, + credentialsAdapter: HttpCredentialsAdapter, + interceptor: DebugNetworkInterceptor +) : BaseInvoker(credentialsAdapter, preferences, interceptor) { private val service = - Drive - .Builder(NetHttpTransport(), JacksonFactory(), credentialsAdapter) - .setApplicationName(String.format("Tasks/%s", BuildConfig.VERSION_NAME)) + Drive.Builder(NetHttpTransport(), JacksonFactory(), credentialsAdapter) + .setApplicationName(APP_NAME) .build() @Throws(IOException::class) @@ -83,64 +73,6 @@ class DriveInvoker @Inject constructor( return execute(service.files().create(metadata, content)) } - @Synchronized - @Throws(IOException::class) - private suspend fun execute(request: DriveRequest): T? { - return execute(request, false) - } - - @Synchronized - @Throws(IOException::class) - private suspend fun execute( - request: DriveRequest, - 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 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 { private const val MIME_FOLDER = "application/vnd.google-apps.folder" private val DRIVE_FILE_COMPARATOR = Comparator { f1, f2 -> diff --git a/app/src/main/java/org/tasks/googleapis/BaseInvoker.kt b/app/src/main/java/org/tasks/googleapis/BaseInvoker.kt new file mode 100644 index 000000000..e2c91f688 --- /dev/null +++ b/app/src/main/java/org/tasks/googleapis/BaseInvoker.kt @@ -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 execute(request: AbstractGoogleJsonClientRequest): T? = execute(request, false) + + @Synchronized + @Throws(IOException::class) + private suspend fun execute(request: AbstractGoogleJsonClientRequest, 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 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}" + } +} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/googleapis/InvokerFactory.kt b/app/src/main/java/org/tasks/googleapis/InvokerFactory.kt new file mode 100644 index 000000000..1a7ce1cba --- /dev/null +++ b/app/src/main/java/org/tasks/googleapis/InvokerFactory.kt @@ -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 + ) +} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/gtasks/GoogleTaskSynchronizer.kt b/app/src/main/java/org/tasks/gtasks/GoogleTaskSynchronizer.kt index 41bdfa1b6..53c603bb2 100644 --- a/app/src/main/java/org/tasks/gtasks/GoogleTaskSynchronizer.kt +++ b/app/src/main/java/org/tasks/gtasks/GoogleTaskSynchronizer.kt @@ -25,6 +25,7 @@ import org.tasks.analytics.Firebase import org.tasks.billing.Inventory import org.tasks.data.* import org.tasks.date.DateTimeUtils.newDateTime +import org.tasks.googleapis.InvokerFactory import org.tasks.preferences.DefaultFilterProvider import org.tasks.preferences.PermissionChecker import org.tasks.preferences.Preferences @@ -55,7 +56,7 @@ class GoogleTaskSynchronizer @Inject constructor( private val localBroadcastManager: LocalBroadcastManager, private val inventory: Inventory, private val taskDeleter: TaskDeleter, - private val gtasksInvoker: GtasksInvoker) { + private val invokers: InvokerFactory) { suspend fun sync(account: GoogleTaskAccount, i: Int) { Timber.d("%s: start sync", account) @@ -106,7 +107,7 @@ class GoogleTaskSynchronizer @Inject constructor( account.error = context.getString(R.string.cannot_access_account) return } - val gtasksInvoker = gtasksInvoker.forAccount(account.account!!) + val gtasksInvoker = invokers.getGtasksInvoker(account.account!!) pushLocalChanges(account, gtasksInvoker) val gtaskLists: MutableList = ArrayList() var nextPageToken: String? = null diff --git a/app/src/main/java/org/tasks/jobs/DriveUploader.kt b/app/src/main/java/org/tasks/jobs/DriveUploader.kt index cbdaebee3..6b64e3c77 100644 --- a/app/src/main/java/org/tasks/jobs/DriveUploader.kt +++ b/app/src/main/java/org/tasks/jobs/DriveUploader.kt @@ -13,7 +13,7 @@ import org.tasks.LocalBroadcastManager import org.tasks.R import org.tasks.Strings.isNullOrEmpty import org.tasks.analytics.Firebase -import org.tasks.drive.DriveInvoker +import org.tasks.googleapis.InvokerFactory import org.tasks.injection.BaseWorker import org.tasks.preferences.Preferences import timber.log.Timber @@ -28,10 +28,11 @@ class DriveUploader @WorkerInject constructor( @Assisted context: Context, @Assisted workerParams: WorkerParameters, firebase: Firebase, - private val drive: DriveInvoker, + invokers: InvokerFactory, private val preferences: Preferences, private val localBroadcastManager: LocalBroadcastManager ) : BaseWorker(context, workerParams, firebase) { + private val drive = invokers.getDriveInvoker() override suspend fun run(): Result { val inputData = inputData diff --git a/app/src/main/java/org/tasks/preferences/PreferencesViewModel.kt b/app/src/main/java/org/tasks/preferences/PreferencesViewModel.kt index cd5d663ec..ce930254d 100644 --- a/app/src/main/java/org/tasks/preferences/PreferencesViewModel.kt +++ b/app/src/main/java/org/tasks/preferences/PreferencesViewModel.kt @@ -15,7 +15,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.tasks.R import org.tasks.date.DateTimeUtils.newDateTime -import org.tasks.drive.DriveInvoker +import org.tasks.googleapis.InvokerFactory import org.tasks.gtasks.GoogleAccountManager import timber.log.Timber import java.io.File @@ -23,9 +23,10 @@ import java.io.File class PreferencesViewModel @ViewModelInject constructor( @ApplicationContext private val context: Context, private val preferences: Preferences, - private val driveInvoker: DriveInvoker, + invokers: InvokerFactory, private val googleAccountManager: GoogleAccountManager, ) : ViewModel() { + private val driveInvoker = invokers.getDriveInvoker() val lastBackup = MutableLiveData() val lastDriveBackup = MutableLiveData() val lastAndroidBackup = MutableLiveData()