Convert DriveInvoker to Kotlin

pull/1122/head
Alex Baker 4 years ago
parent ddf9f79b18
commit 345346b1e9

@ -1,146 +0,0 @@
package org.tasks.drive;
import static com.todoroo.andlib.utility.DateUtilities.now;
import android.content.Context;
import android.net.Uri;
import com.google.api.client.http.HttpResponse;
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.astrid.gtasks.api.HttpCredentialsAdapter;
import dagger.hilt.android.qualifiers.ApplicationContext;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import javax.inject.Inject;
import org.tasks.BuildConfig;
import org.tasks.DebugNetworkInterceptor;
import org.tasks.R;
import org.tasks.files.FileHelper;
import org.tasks.preferences.Preferences;
import timber.log.Timber;
public class DriveInvoker {
private static final String MIME_FOLDER = "application/vnd.google-apps.folder";
private final Context context;
private final Preferences preferences;
private final DebugNetworkInterceptor interceptor;
private final Drive service;
private final HttpCredentialsAdapter credentialsAdapter;
@Inject
public DriveInvoker(
@ApplicationContext Context context,
Preferences preferences,
HttpCredentialsAdapter credentialsAdapter,
DebugNetworkInterceptor interceptor) {
this.context = context;
this.preferences = preferences;
this.credentialsAdapter = credentialsAdapter;
this.interceptor = interceptor;
service =
new Drive.Builder(new NetHttpTransport(), new JacksonFactory(), credentialsAdapter)
.setApplicationName(String.format("Tasks/%s", BuildConfig.VERSION_NAME))
.build();
}
public File getFile(String folderId) throws IOException {
return execute(service.files().get(folderId).setFields("id, trashed"));
}
public void delete(File file) throws IOException {
execute(service.files().delete(file.getId()));
}
public List<File> getFilesByPrefix(String folderId, String prefix) throws IOException {
String query =
String.format(
"'%s' in parents and name contains '%s' and trashed = false and mimeType != '%s'",
folderId, prefix, MIME_FOLDER);
return execute(
service
.files()
.list()
.setQ(query)
.setSpaces("drive")
.setFields("files(id, modifiedTime)"))
.getFiles();
}
public File createFolder(String name) throws IOException {
File folder = new File().setName(name).setMimeType("application/vnd.google-apps.folder");
return execute(service.files().create(folder).setFields("id"));
}
public void createFile(String folderId, Uri uri) throws IOException {
String mime = FileHelper.getMimeType(context, uri);
File metadata =
new File()
.setParents(Collections.singletonList(folderId))
.setMimeType(mime)
.setName(FileHelper.getFilename(context, uri));
InputStreamContent content =
new InputStreamContent(mime, context.getContentResolver().openInputStream(uri));
execute(service.files().create(metadata, content));
}
private synchronized <T> T execute(DriveRequest<T> request) throws IOException {
return execute(request, false);
}
private synchronized <T> T execute(DriveRequest<T> request, boolean retry) throws IOException {
String account = preferences.getStringValue(R.string.p_google_drive_backup_account);
credentialsAdapter.checkToken(account, DriveScopes.DRIVE_FILE);
Timber.d("%s request: %s", getCaller(), request);
T response;
try {
if (preferences.isFlipperEnabled()) {
long start = now();
HttpResponse httpResponse = request.executeUnparsed();
response = interceptor.report(httpResponse, request.getResponseClass(), start, now());
} else {
response = request.execute();
}
} catch (HttpResponseException e) {
if (e.getStatusCode() == 401 && !retry) {
credentialsAdapter.invalidateToken();
return execute(request, true);
} else {
throw e;
}
}
Timber.d("%s response: %s", getCaller(), prettyPrint(response));
return response;
}
private <T> Object prettyPrint(T object) throws IOException {
if (BuildConfig.DEBUG) {
if (object instanceof GenericJson) {
return ((GenericJson) object).toPrettyString();
}
}
return object;
}
private String getCaller() {
if (BuildConfig.DEBUG) {
try {
return Thread.currentThread().getStackTrace()[4].getMethodName();
} catch (Exception e) {
Timber.e(e);
}
}
return "";
}
}

@ -0,0 +1,143 @@
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.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.preferences.Preferences
import timber.log.Timber
import java.io.IOException
import javax.inject.Inject
class DriveInvoker @Inject constructor(
@param:ApplicationContext private val context: Context,
private val preferences: Preferences,
private val credentialsAdapter: HttpCredentialsAdapter,
private val interceptor: DebugNetworkInterceptor) {
private val service =
Drive
.Builder(NetHttpTransport(), JacksonFactory(), credentialsAdapter)
.setApplicationName(String.format("Tasks/%s", BuildConfig.VERSION_NAME))
.build()
@Throws(IOException::class)
suspend fun getFile(folderId: String?): File? {
return execute(service.files()[folderId].setFields("id, trashed"))
}
@Throws(IOException::class)
suspend fun delete(file: File) {
execute(service.files().delete(file.id))
}
@Throws(IOException::class)
suspend fun getFilesByPrefix(folderId: String?, prefix: String?): List<File> {
val query = String.format(
"'%s' in parents and name contains '%s' and trashed = false and mimeType != '%s'",
folderId, prefix, MIME_FOLDER)
return execute(
service
.files()
.list()
.setQ(query)
.setSpaces("drive")
.setFields("files(id, modifiedTime)"))
?.files
?: emptyList()
}
@Throws(IOException::class)
suspend fun createFolder(name: String?): File? {
val folder = File().setName(name).setMimeType("application/vnd.google-apps.folder")
return execute(service.files().create(folder).setFields("id"))
}
@Throws(IOException::class)
suspend fun createFile(folderId: String, uri: Uri?) {
val mime = FileHelper.getMimeType(context, uri)
val metadata = File()
.setParents(listOf(folderId))
.setMimeType(mime)
.setName(FileHelper.getFilename(context, uri))
val content = InputStreamContent(mime, context.contentResolver.openInputStream(uri!!))
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 {
private const val MIME_FOLDER = "application/vnd.google-apps.folder"
}
}

@ -33,7 +33,7 @@ class DriveUploader @WorkerInject constructor(
val inputData = inputData
val uri = Uri.parse(inputData.getString(EXTRA_URI))
return try {
val folder = folder
val folder = getFolder() ?: return Result.failure()
preferences.setString(R.string.p_google_drive_backup_folder, folder.id)
drive.createFile(folder.id, uri)
if (inputData.getBoolean(EXTRA_PURGE, false)) {
@ -69,22 +69,21 @@ class DriveUploader @WorkerInject constructor(
}
}
@get:Throws(IOException::class)
private val folder: File
get() {
val folderId = preferences.getStringValue(R.string.p_google_drive_backup_folder)
var file: File? = null
if (!isNullOrEmpty(folderId)) {
try {
file = drive.getFile(folderId)
} catch (e: GoogleJsonResponseException) {
if (e.statusCode != 404) {
throw e
}
@Throws(IOException::class)
private suspend fun getFolder(): File? {
val folderId = preferences.getStringValue(R.string.p_google_drive_backup_folder)
var file: File? = null
if (!isNullOrEmpty(folderId)) {
try {
file = drive.getFile(folderId)
} catch (e: GoogleJsonResponseException) {
if (e.statusCode != 404) {
throw e
}
}
return if (file == null || file.trashed) drive.createFolder(FOLDER_NAME) else file
}
return if (file == null || file.trashed) drive.createFolder(FOLDER_NAME) else file
}
companion object {
private const val FOLDER_NAME = "Tasks Backups"

Loading…
Cancel
Save