diff --git a/.gitignore b/.gitignore index f278eab3e2e5..6f38e94c02e2 100644 --- a/.gitignore +++ b/.gitignore @@ -68,3 +68,13 @@ fastlane/report.xml .idea/ app/CMakeUserPresets.json + +venv/ + +infra/scw-transfer/.terraform/ +infra/scw-transfer/.terraform.lock.hcl +infra/scw-transfer/secret.auto.tfvars + +# Terraform state files +*.tfstate +*.tfstate.backup diff --git a/app/build.gradle b/app/build.gradle index 5183e2e7f5a3..b8c466533b5c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,8 +2,6 @@ import org.apache.tools.ant.filters.ReplaceTokens plugins { id 'com.android.application' - id 'com.google.gms.google-services' - id 'com.google.firebase.crashlytics' id 'app.opendocument.conanandroidgradleplugin' } @@ -93,8 +91,6 @@ android { minifyEnabled true shrinkResources true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.txt' - - firebaseCrashlytics.nativeSymbolUploadEnabled true ndk.debugSymbolLevel = "full" } } @@ -135,13 +131,6 @@ android { } dependencies { - implementation platform('com.google.firebase:firebase-bom:34.1.0') - implementation 'com.google.firebase:firebase-storage' - implementation 'com.google.firebase:firebase-auth' - implementation 'com.google.firebase:firebase-crashlytics-ndk' - implementation 'com.google.firebase:firebase-analytics' - implementation 'com.google.firebase:firebase-config' - implementation 'com.google.android.gms:play-services-ads:24.3.0' implementation 'com.google.android.play:review:2.0.2' implementation 'com.google.android.ump:user-messaging-platform:3.1.0' diff --git a/app/src/lite/google-services.json b/app/src/lite/google-services.json deleted file mode 100644 index 1c6fd7931b07..000000000000 --- a/app/src/lite/google-services.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "project_info": { - "project_number": "1093041624247", - "firebase_url": "https://admob-app-id-9025061963.firebaseio.com", - "project_id": "admob-app-id-9025061963", - "storage_bucket": "admob-app-id-9025061963.appspot.com" - }, - "client": [ - { - "client_info": { - "mobilesdk_app_id": "1:1093041624247:android:6ee3f7722637d69e", - "android_client_info": { - "package_name": "at.tomtasche.reader" - } - }, - "oauth_client": [ - { - "client_id": "1093041624247-jl8grfrvqrjf5d7hg9b9gvhq3tkcg936.apps.googleusercontent.com", - "client_type": 1, - "android_info": { - "package_name": "at.tomtasche.reader", - "certificate_hash": "251d73b707fdaaaae180a52864d4892ace7025a1" - } - }, - { - "client_id": "1093041624247-nnsfmjhf0j1klsfadraatuedk8b8k8it.apps.googleusercontent.com", - "client_type": 1, - "android_info": { - "package_name": "at.tomtasche.reader", - "certificate_hash": "e501cb13981ea890ad3f276b8c53539401664d6e" - } - }, - { - "client_id": "1093041624247-ma9ptn5dtg2n9d80r6bkmpgjnn7os4g2.apps.googleusercontent.com", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "AIzaSyDUEJO6LgMZjZdAPXwwBkzodI2QyXiiPLM" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "1093041624247-ma9ptn5dtg2n9d80r6bkmpgjnn7os4g2.apps.googleusercontent.com", - "client_type": 3 - }, - { - "client_id": "1093041624247-dh397ads4r7tmk225emlmuk91sgv2fa8.apps.googleusercontent.com", - "client_type": 2, - "ios_info": { - "bundle_id": "at.tomtasche.reader", - "app_store_id": "1452061743" - } - } - ] - } - }, - "admob_app_id": "ca-app-pub-8161473686436957~9025061963" - } - ], - "configuration_version": "1" -} \ No newline at end of file diff --git a/app/src/main/java/at/tomtasche/reader/background/FileLoader.java b/app/src/main/java/at/tomtasche/reader/background/FileLoader.java index 15cbdc976210..96cbe24f3faa 100644 --- a/app/src/main/java/at/tomtasche/reader/background/FileLoader.java +++ b/app/src/main/java/at/tomtasche/reader/background/FileLoader.java @@ -6,13 +6,12 @@ import android.os.Parcel; import android.os.Parcelable; -import com.google.firebase.analytics.FirebaseAnalytics; - import java.io.File; import java.util.LinkedList; import java.util.List; import at.tomtasche.reader.nonfree.AnalyticsManager; +import at.tomtasche.reader.nonfree.AnalyticsConstants; import at.tomtasche.reader.nonfree.CrashManager; public abstract class FileLoader { @@ -86,7 +85,7 @@ void callOnSuccess(Result result) { mainHandler.post(new Runnable() { @Override public void run() { - analyticsManager.report("loader_success_" + type, FirebaseAnalytics.Param.CONTENT_TYPE, result.options.fileType, FirebaseAnalytics.Param.CONTENT, result.options.fileExtension); + analyticsManager.report("loader_success_" + type, AnalyticsConstants.PARAM_CONTENT_TYPE, result.options.fileType, AnalyticsConstants.PARAM_CONTENT, result.options.fileExtension); FileLoaderListener strongReferenceListener = listener; if (strongReferenceListener != null) { @@ -103,7 +102,7 @@ void callOnError(Result result, Throwable t) { mainHandler.post(new Runnable() { @Override public void run() { - analyticsManager.report("loader_error_" + type, FirebaseAnalytics.Param.CONTENT_TYPE, result.options.fileType, FirebaseAnalytics.Param.CONTENT, result.options.fileExtension); + analyticsManager.report("loader_error_" + type, AnalyticsConstants.PARAM_CONTENT_TYPE, result.options.fileType, AnalyticsConstants.PARAM_CONTENT, result.options.fileExtension); FileLoaderListener strongReferenceListener = listener; if (strongReferenceListener != null) { diff --git a/app/src/main/java/at/tomtasche/reader/background/LoaderService.java b/app/src/main/java/at/tomtasche/reader/background/LoaderService.java index 79822d7aea9b..cc9a574ac851 100644 --- a/app/src/main/java/at/tomtasche/reader/background/LoaderService.java +++ b/app/src/main/java/at/tomtasche/reader/background/LoaderService.java @@ -11,12 +11,11 @@ import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GoogleApiAvailability; -import com.google.firebase.analytics.FirebaseAnalytics; - import java.io.File; import java.io.OutputStream; import at.tomtasche.reader.R; +import at.tomtasche.reader.nonfree.AnalyticsConstants; import at.tomtasche.reader.nonfree.AnalyticsManager; import at.tomtasche.reader.nonfree.ConfigManager; import at.tomtasche.reader.nonfree.CrashManager; @@ -134,12 +133,12 @@ public void onSuccess(FileLoader.Result result) { if (result.loaderType == FileLoader.LoaderType.METADATA) { if (!coreLoader.isSupported(options)) { crashManager.log("we do not expect this file to be an ODF: " + options.originalUri.toString()); - analyticsManager.report("load_odf_error_expected", FirebaseAnalytics.Param.CONTENT_TYPE, options.fileType); + analyticsManager.report("load_odf_error_expected", AnalyticsConstants.PARAM_CONTENT_TYPE, options.fileType); } loadWithType(FileLoader.LoaderType.CORE, options); } else { - analyticsManager.report("load_success", FirebaseAnalytics.Param.CONTENT_TYPE, options.fileType, FirebaseAnalytics.Param.CONTENT, result.loaderType.toString()); + analyticsManager.report("load_success", AnalyticsConstants.PARAM_CONTENT_TYPE, options.fileType, AnalyticsConstants.PARAM_CONTENT, result.loaderType.toString()); if (currentListener != null) { currentListener.onLoadSuccess(result); @@ -167,7 +166,7 @@ public void onError(FileLoader.Result result, Throwable error) { } if (result.loaderType == FileLoader.LoaderType.CORE) { - analyticsManager.report("load_odf_error", FirebaseAnalytics.Param.CONTENT_TYPE, options.fileType); + analyticsManager.report("load_odf_error", AnalyticsConstants.PARAM_CONTENT_TYPE, options.fileType); if (rawLoader.isSupported(options)) { loadWithType(FileLoader.LoaderType.RAW, options); @@ -192,7 +191,7 @@ public void onError(FileLoader.Result result, Throwable error) { // MetadataLoader failed, so there's no point in trying to parse or upload the file - analyticsManager.report("load_error", FirebaseAnalytics.Param.CONTENT_TYPE, options.fileType, FirebaseAnalytics.Param.CONTENT, result.loaderType.toString()); + analyticsManager.report("load_error", AnalyticsConstants.PARAM_CONTENT_TYPE, options.fileType, AnalyticsConstants.PARAM_CONTENT, result.loaderType.toString()); if (currentListener != null) { currentListener.onError(result, error); @@ -238,7 +237,7 @@ private void saveSync(FileLoader.Result lastResult, Uri outFile, String htmlDiff } }); } catch (Throwable e) { - analyticsManager.report("save_error", FirebaseAnalytics.Param.CONTENT_TYPE, lastResult.options.fileType); + analyticsManager.report("save_error", AnalyticsConstants.PARAM_CONTENT_TYPE, lastResult.options.fileType); crashManager.log(e, lastResult.options.originalUri); if (currentListener != null) { @@ -287,4 +286,4 @@ public interface LoaderListener { void onUnsupported(FileLoader.Result result); void onSaveError(); } -} \ No newline at end of file +} diff --git a/app/src/main/java/at/tomtasche/reader/background/OnlineLoader.java b/app/src/main/java/at/tomtasche/reader/background/OnlineLoader.java index b6298e2f7e63..89c76d327c05 100644 --- a/app/src/main/java/at/tomtasche/reader/background/OnlineLoader.java +++ b/app/src/main/java/at/tomtasche/reader/background/OnlineLoader.java @@ -3,21 +3,12 @@ import android.content.Context; import android.net.Uri; import android.os.Build; -import android.os.Handler; import androidx.annotation.RequiresApi; -import com.google.android.gms.tasks.Task; -import com.google.android.gms.tasks.Tasks; -import com.google.firebase.auth.AuthResult; -import com.google.firebase.auth.FirebaseAuth; -import com.google.firebase.storage.FirebaseStorage; -import com.google.firebase.storage.StorageMetadata; -import com.google.firebase.storage.StorageReference; -import com.google.firebase.storage.UploadTask; - import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; @@ -26,14 +17,11 @@ import java.net.URL; import java.net.URLEncoder; import java.nio.file.Files; -import java.util.UUID; -import java.util.concurrent.ExecutionException; - -import at.tomtasche.reader.nonfree.AnalyticsManager; -import at.tomtasche.reader.nonfree.CrashManager; public class OnlineLoader extends FileLoader { + private static final String TRANSFER_BASE_URL = "https://transfer.opendocument.app/"; + // https://help.joomlatools.com/article/169-google-viewer // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Complete_list_of_MIME_types private static final String[] MIME_WHITELIST = {"text/", "image/", "video/", "audio/", @@ -76,26 +64,11 @@ public class OnlineLoader extends FileLoader { private final CoreLoader coreLoader; - private StorageReference storage; - private FirebaseAuth auth; - public OnlineLoader(Context context, CoreLoader coreLoader) { super(context, LoaderType.ONLINE); this.coreLoader = coreLoader; } - @Override - public void initialize(FileLoaderListener listener, Handler mainHandler, Handler backgroundHandler, AnalyticsManager analyticsManager, CrashManager crashManager) { - super.initialize(listener, mainHandler, backgroundHandler, analyticsManager, crashManager); - - try { - storage = FirebaseStorage.getInstance().getReference(); - auth = FirebaseAuth.getInstance(); - } catch (Throwable e) { - crashManager.log(e); - } - } - @Override public boolean isSupported(Options options) { String fileType = options.fileType; @@ -129,7 +102,7 @@ public void loadSync(Options options) { ("text/rtf".equals(options.fileType) || "application/vnd.wordperfect".equals(options.fileType) || coreLoader.isSupported(options) || "application/vnd.ms-excel".equals(options.fileType) || "application/msword".equals(options.fileType) || "application/vnd.ms-powerpoint".equals(options.fileType) || options.fileType.startsWith("application/vnd.openxmlformats-officedocument.") || options.fileType.equals("application/pdf"))) { viewerUri = doOnlineConvert(options); } else { - viewerUri = doFirebaseConvert(options); + viewerUri = doTransferUpload(options); } result.partTitles.add(null); @@ -175,66 +148,63 @@ private Uri doOnlineConvert(Options options) throws IOException { return Uri.parse(basePath + redirectUrl); } - private Uri doFirebaseConvert(Options options) throws ExecutionException, InterruptedException, UnsupportedEncodingException { - if (auth == null || storage == null) { - throw new RuntimeException("firebase not initialized"); - } + private Uri doTransferUpload(Options options) throws IOException { + File binaryFile = AndroidFileCache.getCacheFile(context, options.cacheUri); + String filename = options.filename; + String encodedFilename = URLEncoder.encode(filename, StreamUtil.ENCODING); - Task authenticationTask = null; - String currentUserId = null; - if (auth.getCurrentUser() != null) { - currentUserId = auth.getCurrentUser().getUid(); - } else { - authenticationTask = auth.signInAnonymously(); + HttpURLConnection connection = (HttpURLConnection) new URL(TRANSFER_BASE_URL + encodedFilename).openConnection(); + connection.setRequestMethod("PUT"); + connection.setDoOutput(true); + connection.setInstanceFollowRedirects(false); + + try (OutputStream outputStream = connection.getOutputStream()) { + Files.copy(binaryFile.toPath(), outputStream); + outputStream.flush(); } - if (authenticationTask != null) { - Tasks.await(authenticationTask); + int responseCode = connection.getResponseCode(); + if (responseCode >= 200 && responseCode < 300) { + String downloadUrl = readBody(connection); + if (downloadUrl == null || downloadUrl.isEmpty()) { + throw new IOException("server couldn't handle request"); + } - currentUserId = authenticationTask.getResult().getUser().getUid(); + return buildViewerUri(options, downloadUrl.trim()); + } else { + String error = readError(connection); + throw new IOException("server couldn't handle request: " + responseCode + " " + error); } + } - StorageMetadata.Builder metadataBuilder = new StorageMetadata.Builder(); - if (!"N/A".equals(options.fileType)) { - metadataBuilder.setContentType(options.fileType); + private Uri buildViewerUri(Options options, String downloadUrl) throws UnsupportedEncodingException { + if (coreLoader.isSupported(options)) { + // ODF does not seem to be supported by google docs viewer + return Uri.parse(MICROSOFT_VIEWER_URL + downloadUrl); + } else { + return Uri.parse(GOOGLE_VIEWER_URL + URLEncoder.encode(downloadUrl, StreamUtil.ENCODING)); } + } - String filePath = currentUserId + "/" + UUID.randomUUID() + "." + options.fileExtension; - StorageReference reference = storage.child("uploads/" + filePath); - UploadTask uploadTask = reference.putFile(options.cacheUri, metadataBuilder.build()); - Tasks.await(uploadTask); - - if (uploadTask.isSuccessful()) { - Uri viewerUri; - if (coreLoader.isSupported(options)) { - // ODF does not seem to be supported by google docs viewer - String downloadUrl = "https://us-central1-admob-app-id-9025061963.cloudfunctions.net/download?filePath=" + filePath; + private String readBody(HttpURLConnection connection) throws IOException { + InputStream inputStream = connection.getInputStream(); + if (inputStream == null) { + return null; + } - viewerUri = Uri.parse(MICROSOFT_VIEWER_URL + downloadUrl); - } else { - Task urlTask = reference.getDownloadUrl(); - Tasks.await(urlTask); - String downloadUrl = urlTask.getResult().toString(); + return StreamUtil.readFully(inputStream); + } - viewerUri = Uri.parse(GOOGLE_VIEWER_URL + URLEncoder.encode(downloadUrl, StreamUtil.ENCODING)); + private String readError(HttpURLConnection connection) { + try { + InputStream errorStream = connection.getErrorStream(); + if (errorStream == null) { + return null; } - return viewerUri; - } else { - throw new RuntimeException("server couldn't handle request"); + return StreamUtil.readFully(errorStream); + } catch (Throwable t) { + return null; } } - - @Override - public void close() { - super.close(); - - backgroundHandler.post(new Runnable() { - @Override - public void run() { - auth = null; - storage = null; - } - }); - } } diff --git a/app/src/main/java/at/tomtasche/reader/background/StreamUtil.java b/app/src/main/java/at/tomtasche/reader/background/StreamUtil.java index 7a87d87a8412..67c88c585d16 100644 --- a/app/src/main/java/at/tomtasche/reader/background/StreamUtil.java +++ b/app/src/main/java/at/tomtasche/reader/background/StreamUtil.java @@ -1,5 +1,6 @@ package at.tomtasche.reader.background; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -27,6 +28,13 @@ public static void copy(InputStream in, File dst) throws IOException { out.close(); } + public static String readFully(InputStream in) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + copy(in, out); + + return out.toString(ENCODING); + } + // taken from: https://stackoverflow.com/a/9293885/198996 public static void copy(InputStream in, OutputStream out) throws IOException { try { diff --git a/app/src/main/java/at/tomtasche/reader/nonfree/AnalyticsConstants.java b/app/src/main/java/at/tomtasche/reader/nonfree/AnalyticsConstants.java new file mode 100644 index 000000000000..a95130419caa --- /dev/null +++ b/app/src/main/java/at/tomtasche/reader/nonfree/AnalyticsConstants.java @@ -0,0 +1,16 @@ +package at.tomtasche.reader.nonfree; + +public final class AnalyticsConstants { + + private AnalyticsConstants() { + } + + public static final String PARAM_CONTENT_TYPE = "content_type"; + public static final String PARAM_CONTENT = "content"; + public static final String PARAM_ITEM_NAME = "item_name"; + + public static final String EVENT_SELECT_CONTENT = "select_content"; + public static final String EVENT_VIEW_ITEM = "view_item"; + public static final String EVENT_ADD_TO_CART = "add_to_cart"; + public static final String EVENT_SEARCH = "search"; +} diff --git a/app/src/main/java/at/tomtasche/reader/nonfree/AnalyticsManager.java b/app/src/main/java/at/tomtasche/reader/nonfree/AnalyticsManager.java index 29f948e190eb..0d62975720d7 100644 --- a/app/src/main/java/at/tomtasche/reader/nonfree/AnalyticsManager.java +++ b/app/src/main/java/at/tomtasche/reader/nonfree/AnalyticsManager.java @@ -5,22 +5,12 @@ import android.os.Bundle; import android.util.Log; -import com.google.firebase.analytics.FirebaseAnalytics; - public class AnalyticsManager { private boolean enabled; - private FirebaseAnalytics analytics; - public void initialize(Context context) { - if (!enabled) { - FirebaseAnalytics.getInstance(context).setAnalyticsCollectionEnabled(false); - - return; - } - - analytics = FirebaseAnalytics.getInstance(context); + return; } public void setEnabled(boolean enabled) { @@ -44,7 +34,7 @@ public void report(String event, String key1, Object value1, String key2, Object bundle.putString(key2, String.valueOf(value2)); } - analytics.logEvent(event, bundle); + Log.i("smn", event); } public void report(String event, String key, Object value) { @@ -57,8 +47,9 @@ public void setCurrentScreen(Activity activity, String name) { } Bundle bundle = new Bundle(); - bundle.putString(FirebaseAnalytics.Param.SCREEN_NAME, name); - bundle.putString(FirebaseAnalytics.Param.SCREEN_CLASS, activity.getClass().getSimpleName()); - analytics.logEvent(FirebaseAnalytics.Event.SCREEN_VIEW, bundle); + bundle.putString("screen_name", name); + bundle.putString("screen_class", activity.getClass().getSimpleName()); + + Log.i("smn", name); } } diff --git a/app/src/main/java/at/tomtasche/reader/nonfree/ConfigManager.java b/app/src/main/java/at/tomtasche/reader/nonfree/ConfigManager.java index a223ecef2e39..c306ec25c517 100644 --- a/app/src/main/java/at/tomtasche/reader/nonfree/ConfigManager.java +++ b/app/src/main/java/at/tomtasche/reader/nonfree/ConfigManager.java @@ -1,19 +1,7 @@ package at.tomtasche.reader.nonfree; -import android.net.Uri; - -import com.google.android.gms.tasks.OnCompleteListener; -import com.google.android.gms.tasks.Task; -import com.google.firebase.remoteconfig.FirebaseRemoteConfig; - import java.util.LinkedList; import java.util.List; -import java.util.concurrent.TimeoutException; - -import androidx.annotation.NonNull; - -import at.tomtasche.reader.R; -import at.tomtasche.reader.ui.activity.MainActivity; public class ConfigManager { @@ -21,8 +9,6 @@ public class ConfigManager { private boolean loaded; - private FirebaseRemoteConfig remoteConfig; - private final List callbacks; public ConfigManager() { @@ -34,19 +20,13 @@ public void initialize() { return; } - remoteConfig = FirebaseRemoteConfig.getInstance(); - remoteConfig.fetchAndActivate().addOnCompleteListener(new OnCompleteListener() { - @Override - public void onComplete(@NonNull Task task) { - synchronized (callbacks) { - loaded = true; + synchronized (callbacks) { + loaded = true; - for (Runnable callback : callbacks) { - callback.run(); - } - } + for (Runnable callback : callbacks) { + callback.run(); } - }); + } } public boolean isLoaded() { @@ -80,7 +60,7 @@ public void run() { } } - boolean value = remoteConfig.getBoolean(key); + boolean value = getBooleanConfig(key); configListener.onConfig(key, value); } @@ -89,7 +69,7 @@ public boolean getBooleanConfig(String key) { return false; } - return remoteConfig.getBoolean(key); + return false; } public interface ConfigListener { diff --git a/app/src/main/java/at/tomtasche/reader/nonfree/CrashManager.java b/app/src/main/java/at/tomtasche/reader/nonfree/CrashManager.java index 3788228f8641..74bf97789934 100644 --- a/app/src/main/java/at/tomtasche/reader/nonfree/CrashManager.java +++ b/app/src/main/java/at/tomtasche/reader/nonfree/CrashManager.java @@ -3,25 +3,17 @@ import android.net.Uri; import android.util.Log; -import com.google.firebase.crashlytics.FirebaseCrashlytics; - import java.util.concurrent.TimeoutException; public class CrashManager { private boolean enabled; - private FirebaseCrashlytics crashlytics; - public void initialize() { if (!enabled) { - FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(false); - return; } - crashlytics = FirebaseCrashlytics.getInstance(); - // mitigate TimeoutException on finalize // https://stackoverflow.com/a/55999687/198996 final Thread.UncaughtExceptionHandler defaultUncaughtExceptionHandler = @@ -31,7 +23,7 @@ public void initialize() { public void uncaughtException(Thread t, Throwable e) { if (t.getName().equals("FinalizerWatchdogDaemon") && e instanceof TimeoutException) { log(e); - } else { + } else if (defaultUncaughtExceptionHandler != null) { defaultUncaughtExceptionHandler.uncaughtException(t, e); } } @@ -48,8 +40,6 @@ public void log(String message) { } Log.d("ODR", message); - - crashlytics.log(message); } public void log(Throwable error, Uri uri) { @@ -57,12 +47,9 @@ public void log(Throwable error, Uri uri) { return; } - String uriString = "null"; - if (uri != null) { - uriString = uri.toString(); - } + String uriString = uri != null ? uri.toString() : "null"; - crashlytics.log("could not load document at: " + uriString); + Log.d("ODR", "could not load document at: " + uriString); log(error); } @@ -73,6 +60,6 @@ public void log(Throwable error) { return; } - crashlytics.recordException(error); + Log.e("ODR", "Error reported", error); } } diff --git a/app/src/main/java/at/tomtasche/reader/ui/activity/DocumentFragment.java b/app/src/main/java/at/tomtasche/reader/ui/activity/DocumentFragment.java index 8fc0b55ce042..da74a72eb723 100644 --- a/app/src/main/java/at/tomtasche/reader/ui/activity/DocumentFragment.java +++ b/app/src/main/java/at/tomtasche/reader/ui/activity/DocumentFragment.java @@ -19,7 +19,6 @@ import com.google.android.play.core.review.ReviewInfo; import com.google.android.play.core.review.ReviewManager; import com.google.android.play.core.review.ReviewManagerFactory; -import com.google.firebase.analytics.FirebaseAnalytics; import java.io.File; import java.io.FileNotFoundException; @@ -40,6 +39,7 @@ import at.tomtasche.reader.background.LoaderService; import at.tomtasche.reader.background.LoaderServiceQueue; import at.tomtasche.reader.background.StreamUtil; +import at.tomtasche.reader.nonfree.AnalyticsConstants; import at.tomtasche.reader.nonfree.AnalyticsManager; import at.tomtasche.reader.nonfree.ConfigManager; import at.tomtasche.reader.nonfree.CrashManager; @@ -526,7 +526,7 @@ public void onSaveError() { private void offerUpload(Activity activity, FileLoader.Options options, boolean invasive) { String fileType = options.fileType; if (invasive) { - analyticsManager.report("upload_offer_invasive", FirebaseAnalytics.Param.CONTENT_TYPE, fileType, FirebaseAnalytics.Param.CONTENT, options.originalUri); + analyticsManager.report("upload_offer_invasive", AnalyticsConstants.PARAM_CONTENT_TYPE, fileType, AnalyticsConstants.PARAM_CONTENT, options.originalUri); AlertDialog.Builder builder = new AlertDialog.Builder(activity); builder.setTitle(R.string.toast_error_illegal_file); @@ -538,7 +538,7 @@ private void offerUpload(Activity activity, FileLoader.Options options, boolean @Override public void onClick(DialogInterface dialog, int whichButton) { - analyticsManager.report("load_upload", FirebaseAnalytics.Param.CONTENT_TYPE, fileType); + analyticsManager.report("load_upload", AnalyticsConstants.PARAM_CONTENT_TYPE, fileType); loadWithType(FileLoader.LoaderType.ONLINE, options); @@ -548,7 +548,7 @@ public void onClick(DialogInterface dialog, builder.setNegativeButton(getString(android.R.string.cancel), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int i) { - analyticsManager.report("load_upload_cancel", FirebaseAnalytics.Param.CONTENT_TYPE, fileType); + analyticsManager.report("load_upload_cancel", AnalyticsConstants.PARAM_CONTENT_TYPE, fileType); offerReopen(activity, options, R.string.toast_error_illegal_file_reopen, true); @@ -558,7 +558,7 @@ public void onClick(DialogInterface dialog, int i) { builder.show(); } else { - analyticsManager.report("upload_offer_subtle", FirebaseAnalytics.Param.CONTENT_TYPE, fileType, FirebaseAnalytics.Param.CONTENT, options.originalUri); + analyticsManager.report("upload_offer_subtle", AnalyticsConstants.PARAM_CONTENT_TYPE, fileType, AnalyticsConstants.PARAM_CONTENT, options.originalUri); SnackbarHelper.show(activity, R.string.toast_hint_upload_file, new Runnable() { @Override @@ -572,7 +572,7 @@ public void run() { private void offerReopen(Activity activity, FileLoader.Options options, int description, boolean isIndefinite) { String fileType = options.fileType; - analyticsManager.report("reopen_offer", FirebaseAnalytics.Param.CONTENT_TYPE, fileType, FirebaseAnalytics.Param.CONTENT, options.originalUri); + analyticsManager.report("reopen_offer", AnalyticsConstants.PARAM_CONTENT_TYPE, fileType, AnalyticsConstants.PARAM_CONTENT, options.originalUri); SnackbarHelper.show(activity, description, new Runnable() { @Override @@ -640,11 +640,11 @@ private void doReopen(FileLoader.Options options, Activity activity, boolean gra try { activity.startActivity(chooserIntent); - analyticsManager.report(logPrefix + "_success", FirebaseAnalytics.Param.CONTENT_TYPE, fileType); + analyticsManager.report(logPrefix + "_success", AnalyticsConstants.PARAM_CONTENT_TYPE, fileType); } catch (Throwable e) { crashManager.log(e); - analyticsManager.report(logPrefix + "_failed", FirebaseAnalytics.Param.CONTENT_TYPE, fileType); + analyticsManager.report(logPrefix + "_failed", AnalyticsConstants.PARAM_CONTENT_TYPE, fileType); if (grantPermission) { // if we're trying to reopen the originalUri, the provider might decline the request diff --git a/app/src/main/java/at/tomtasche/reader/ui/activity/MainActivity.java b/app/src/main/java/at/tomtasche/reader/ui/activity/MainActivity.java index 81aa4f3248bf..829b9ecc4c87 100644 --- a/app/src/main/java/at/tomtasche/reader/ui/activity/MainActivity.java +++ b/app/src/main/java/at/tomtasche/reader/ui/activity/MainActivity.java @@ -42,7 +42,6 @@ import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GoogleApiAvailability; -import com.google.firebase.analytics.FirebaseAnalytics; import java.util.LinkedList; import java.util.List; @@ -52,6 +51,7 @@ import at.tomtasche.reader.background.LoaderServiceQueue; import at.tomtasche.reader.background.PrintingManager; import at.tomtasche.reader.nonfree.AdManager; +import at.tomtasche.reader.nonfree.AnalyticsConstants; import at.tomtasche.reader.nonfree.AnalyticsManager; import at.tomtasche.reader.nonfree.BillingManager; import at.tomtasche.reader.nonfree.ConfigManager; @@ -183,7 +183,7 @@ public void onClick(View view) { if (getIntent().getData() != null) { loadOnStart = getIntent().getData(); - analyticsManager.report(FirebaseAnalytics.Event.SELECT_CONTENT, FirebaseAnalytics.Param.CONTENT_TYPE, "other"); + analyticsManager.report(AnalyticsConstants.EVENT_SELECT_CONTENT, AnalyticsConstants.PARAM_CONTENT_TYPE, "other"); } else { analyticsManager.setCurrentScreen(this, "screen_main"); } @@ -323,7 +323,7 @@ protected void onNewIntent(Intent intent) { loadUri(intent.getData()); - analyticsManager.report(FirebaseAnalytics.Event.SELECT_CONTENT, FirebaseAnalytics.Param.CONTENT_TYPE, "other"); + analyticsManager.report(AnalyticsConstants.EVENT_SELECT_CONTENT, AnalyticsConstants.PARAM_CONTENT_TYPE, "other"); } } @@ -380,7 +380,7 @@ public void loadUri(Uri uri) { } crashManager.log("loading document at: " + uri.toString()); - analyticsManager.report(FirebaseAnalytics.Event.VIEW_ITEM, FirebaseAnalytics.Param.ITEM_NAME, uri.toString()); + analyticsManager.report(AnalyticsConstants.EVENT_VIEW_ITEM, AnalyticsConstants.PARAM_ITEM_NAME, uri.toString()); boolean isPersistentUri = false; try { @@ -411,12 +411,12 @@ public boolean onMenuItemSelected(@NonNull MenuItem item) { startSupportActionMode(findActionModeCallback); analyticsManager.report("menu_search"); - analyticsManager.report(FirebaseAnalytics.Event.SEARCH); + analyticsManager.report(AnalyticsConstants.EVENT_SEARCH); } else if (itemId == R.id.menu_open) { findDocument(); analyticsManager.report("menu_open"); - analyticsManager.report(FirebaseAnalytics.Event.SELECT_CONTENT, FirebaseAnalytics.Param.CONTENT_TYPE, "choose"); + analyticsManager.report(AnalyticsConstants.EVENT_SELECT_CONTENT, AnalyticsConstants.PARAM_CONTENT_TYPE, "choose"); } else if (itemId == R.id.menu_open_with) { documentFragment.openWith(this); @@ -521,11 +521,11 @@ private void showRecent() { DialogFragment chooserDialog = new RecentDocumentDialogFragment(); chooserDialog.show(transaction, RecentDocumentDialogFragment.FRAGMENT_TAG); - analyticsManager.report(FirebaseAnalytics.Event.SELECT_CONTENT, FirebaseAnalytics.Param.CONTENT_TYPE, "recent"); + analyticsManager.report(AnalyticsConstants.EVENT_SELECT_CONTENT, AnalyticsConstants.PARAM_CONTENT_TYPE, "recent"); } private void buyAdRemoval() { - analyticsManager.report(FirebaseAnalytics.Event.ADD_TO_CART); + analyticsManager.report(AnalyticsConstants.EVENT_ADD_TO_CART); startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=at.tomtasche.reader.pro"))); } @@ -623,7 +623,7 @@ public void run() { }, true, true); } - analyticsManager.report(FirebaseAnalytics.Event.SELECT_CONTENT, FirebaseAnalytics.Param.CONTENT_TYPE, target.activityInfo.packageName); + analyticsManager.report(AnalyticsConstants.EVENT_SELECT_CONTENT, AnalyticsConstants.PARAM_CONTENT_TYPE, target.activityInfo.packageName); dialog.dismiss(); } diff --git a/app/src/pro/google-services.json b/app/src/pro/google-services.json deleted file mode 100644 index a427d6abcdc7..000000000000 --- a/app/src/pro/google-services.json +++ /dev/null @@ -1,110 +0,0 @@ -{ - "project_info": { - "project_number": "1093041624247", - "firebase_url": "https://admob-app-id-9025061963.firebaseio.com", - "project_id": "admob-app-id-9025061963", - "storage_bucket": "admob-app-id-9025061963.appspot.com" - }, - "client": [ - { - "client_info": { - "mobilesdk_app_id": "1:1093041624247:android:6ee3f7722637d69e", - "android_client_info": { - "package_name": "at.tomtasche.reader" - } - }, - "oauth_client": [ - { - "client_id": "1093041624247-r99k5n37emneuo117a7mhm8b7hk6eh77.apps.googleusercontent.com", - "client_type": 1, - "android_info": { - "package_name": "at.tomtasche.reader", - "certificate_hash": "ce886552f406efbd45a7408375422af74f18412b" - } - }, - { - "client_id": "1093041624247-jl8grfrvqrjf5d7hg9b9gvhq3tkcg936.apps.googleusercontent.com", - "client_type": 1, - "android_info": { - "package_name": "at.tomtasche.reader", - "certificate_hash": "251d73b707fdaaaae180a52864d4892ace7025a1" - } - }, - { - "client_id": "1093041624247-nnsfmjhf0j1klsfadraatuedk8b8k8it.apps.googleusercontent.com", - "client_type": 1, - "android_info": { - "package_name": "at.tomtasche.reader", - "certificate_hash": "e501cb13981ea890ad3f276b8c53539401664d6e" - } - }, - { - "client_id": "1093041624247-ma9ptn5dtg2n9d80r6bkmpgjnn7os4g2.apps.googleusercontent.com", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "AIzaSyDitZgQ9LSQaWVpNngpFdeM9F74ecGCPVs" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "1093041624247-ma9ptn5dtg2n9d80r6bkmpgjnn7os4g2.apps.googleusercontent.com", - "client_type": 3 - }, - { - "client_id": "1093041624247-3mpspg70slchbpn1vut1h5g884mkt778.apps.googleusercontent.com", - "client_type": 2, - "ios_info": { - "bundle_id": "at.tomtasche.reader.lite1", - "app_store_id": "1510195065" - } - } - ] - } - }, - "admob_app_id": "ca-app-pub-8161473686436957~9025061963" - }, - { - "client_info": { - "mobilesdk_app_id": "1:1093041624247:android:171236dad185bfad136932", - "android_client_info": { - "package_name": "at.tomtasche.reader.pro" - } - }, - "oauth_client": [ - { - "client_id": "1093041624247-ma9ptn5dtg2n9d80r6bkmpgjnn7os4g2.apps.googleusercontent.com", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "AIzaSyDitZgQ9LSQaWVpNngpFdeM9F74ecGCPVs" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "1093041624247-ma9ptn5dtg2n9d80r6bkmpgjnn7os4g2.apps.googleusercontent.com", - "client_type": 3 - }, - { - "client_id": "1093041624247-3mpspg70slchbpn1vut1h5g884mkt778.apps.googleusercontent.com", - "client_type": 2, - "ios_info": { - "bundle_id": "at.tomtasche.reader.lite1", - "app_store_id": "1510195065" - } - } - ] - } - } - } - ], - "configuration_version": "1" -} \ No newline at end of file diff --git a/build.gradle b/build.gradle index 692445a5800e..75b5a098e0ea 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,4 @@ plugins { id 'com.android.application' version '8.13.1' apply false - id 'com.google.gms.google-services' version '4.4.2' apply false - id 'com.google.firebase.crashlytics' version '3.0.6' apply false id 'app.opendocument.conanandroidgradleplugin' version '0.9.6' apply false } diff --git a/infra/scw-transfer/main.tf b/infra/scw-transfer/main.tf new file mode 100644 index 000000000000..1c8aa40411c6 --- /dev/null +++ b/infra/scw-transfer/main.tf @@ -0,0 +1,70 @@ +terraform { + required_version = ">= 1.5.0" + + required_providers { + scaleway = { + source = "scaleway/scaleway" + version = "~> 2.36" + } + } +} + +provider "scaleway" { + project_id = var.project_id + region = var.region + zone = var.zone + access_key = var.access_key + secret_key = var.secret_key +} + +resource "scaleway_container_namespace" "transfer" { + name = var.namespace_name + description = "Temporary uploads for OpenDocument Reader via transfer.sh" + region = var.region +} + +resource "scaleway_container" "transfer" { + name = var.container_name + namespace_id = scaleway_container_namespace.transfer.id + registry_image = var.registry_image + + min_scale = var.min_scale + max_scale = var.max_scale + + memory_limit = var.memory_limit_mb + cpu_limit = var.cpu_limit_millicpu + + port = var.port + privacy = "public" + protocol = "http1" + timeout = 300 + deploy = true + + environment_variables = var.environment_variables + + health_check { + failure_threshold = 3 + interval = "30s" + + http { + path = var.healthcheck_path + } + } + + args = [ + "--provider", "local", + "--basedir", var.storage_dir, + "--listener", ":${var.port}", + "--purge-days", var.purge_days, + "--purge-interval", var.purge_interval_hours, + "--max-upload-size", var.max_upload_size, + ] +} + +output "transfer_domain" { + value = scaleway_container.transfer.domain_name +} + +output "transfer_base_url" { + value = "https://${scaleway_container.transfer.domain_name}" +} diff --git a/infra/scw-transfer/secret.auto.tfvars.example b/infra/scw-transfer/secret.auto.tfvars.example new file mode 100644 index 000000000000..94b056ec4981 --- /dev/null +++ b/infra/scw-transfer/secret.auto.tfvars.example @@ -0,0 +1,5 @@ +# Copy to secret.auto.tfvars (gitignored) and fill with live credentials or use SCW_* env vars instead. + +project_id = "00000000-0000-0000-0000-000000000000" +access_key = "SCWXXXXXXXXXXXXXXXXX" +secret_key = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" diff --git a/infra/scw-transfer/terraform.tfvars b/infra/scw-transfer/terraform.tfvars new file mode 100644 index 000000000000..20d82048844c --- /dev/null +++ b/infra/scw-transfer/terraform.tfvars @@ -0,0 +1,25 @@ +# Deployment settings for transfer.sh (non-secret values). +# Put project_id/access_key/secret_key in infra/scw-transfer/secret.auto.tfvars (gitignored) or set SCW_* env vars. + +region = "nl-ams" +zone = "nl-ams-1" + +namespace_name = "transfer-sh" +container_name = "transfer-sh" + +registry_image = "rg.nl-ams.scw.cloud/funcscwtransfershxuil1jyq/transfer-sh:v1.6.1" + +purge_days = 1 +purge_interval_hours = 1 +max_upload_size = 51200 +storage_dir = "/data" +port = 8080 +cpu_limit_millicpu = 350 +memory_limit_mb = 512 +min_scale = 1 +max_scale = 1 +healthcheck_path = "/" + +environment_variables = { + TMPDIR = "/data" +} diff --git a/infra/scw-transfer/variables.tf b/infra/scw-transfer/variables.tf new file mode 100644 index 000000000000..d3be6fc8c5c3 --- /dev/null +++ b/infra/scw-transfer/variables.tf @@ -0,0 +1,115 @@ +variable "project_id" { + description = "Scaleway project ID (can also be set via SCW_PROJECT_ID env var)." + type = string + default = null +} + +variable "access_key" { + description = "Scaleway access key (prefer env var SCW_ACCESS_KEY)." + type = string + default = null +} + +variable "secret_key" { + description = "Scaleway secret key (prefer env var SCW_SECRET_KEY)." + type = string + default = null +} + +variable "region" { + description = "Scaleway region for the container namespace." + type = string + default = "fr-par" +} + +variable "zone" { + description = "Scaleway zone; required by some APIs even if containers are regional." + type = string + default = "fr-par-1" +} + +variable "namespace_name" { + description = "Container namespace name." + type = string + default = "transfer-sh" +} + +variable "container_name" { + description = "Container name." + type = string + default = "transfer-sh" +} + +variable "registry_image" { + description = "Docker image for transfer.sh." + type = string + default = "docker.io/dutchcoders/transfer.sh:v1.6.1" +} + +variable "purge_interval_hours" { + description = "Interval between purge runs (hours; transfer.sh expects hours)." + type = number + default = 1 +} + +variable "max_upload_size" { + description = "Maximum upload size allowed by transfer.sh in kilobytes (e.g., 51200 for ~50 MB)." + type = number + default = 51200 +} + +variable "purge_days" { + description = "Number of days before uploads are purged (integer, transfer.sh flag). Minimum supported is 1." + type = number + default = 1 +} + +variable "storage_dir" { + description = "Path inside the container where uploads are stored." + type = string + default = "/data" +} + +variable "port" { + description = "Port the container listens on." + type = number + default = 8080 +} + +variable "max_scale" { + description = "Maximum number of container instances." + type = number + default = 1 +} + +variable "min_scale" { + description = "Minimum number of container instances." + type = number + default = 1 +} + +variable "healthcheck_path" { + description = "HTTP path for container healthcheck." + type = string + default = "/" +} + +variable "cpu_limit_millicpu" { + description = "CPU limit in millicpu." + type = number + default = 350 +} + +variable "memory_limit_mb" { + description = "Memory limit in MB." + type = number + default = 512 +} + +variable "environment_variables" { + description = "Plain environment variables for the container." + type = map(string) + default = { + TMPDIR = "/data" + } +} diff --git a/storage.rules b/storage.rules deleted file mode 100644 index 56a983cc416b..000000000000 --- a/storage.rules +++ /dev/null @@ -1,7 +0,0 @@ -service firebase.storage { - match /b/{bucket}/o { - match /{allPaths=**} { - allow read, write; - } - } -}