From 89a55697c88f724604b3818b13ad960f7cd8844d Mon Sep 17 00:00:00 2001 From: Elelan's Macbook Pro Date: Wed, 13 Nov 2024 23:33:42 +0530 Subject: [PATCH 1/2] AOO-24: stabilized latest branch - removed unavailable libs 1. com.github.stfalcon:frescoimageviewer 2. com.amulyakhare:com.amulyakhare.textdrawable --- app/build.gradle | 98 +++++++++---------- .../opendasharchive/openarchive/db/Space.kt | 23 ++++- .../features/media/ReviewActivity.kt | 7 +- .../openarchive/util/DrawableUtil.kt | 32 ++++++ 4 files changed, 103 insertions(+), 57 deletions(-) create mode 100644 app/src/main/java/net/opendasharchive/openarchive/util/DrawableUtil.kt diff --git a/app/build.gradle b/app/build.gradle index 6b8548eb..153f277e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,6 +1,6 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply plugin: 'org.jetbrains.kotlin.android' +apply plugin: "com.android.application" +apply plugin: "kotlin-android" +apply plugin: "org.jetbrains.kotlin.android" android { compileOptions { @@ -12,40 +12,40 @@ android { } compileSdk 34 -// buildToolsVersion '34.0.0' defaultConfig { applicationId "net.opendasharchive.openarchive" minSdkVersion 29 + //noinspection OldTargetApi targetSdkVersion 34 versionCode 30001 - versionName '0.7.4' + versionName "0.7.4" archivesBaseName = "Save-$versionName" multiDexEnabled true vectorDrawables.useSupportLibrary = true - testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } flavorDimensions += "free" buildTypes { release { minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" } } packagingOptions { resources { - excludes += ['META-INF/LICENSE.txt', 'META-INF/NOTICE.txt', 'META-INF/LICENSE', 'META-INF/NOTICE', 'META-INF/DEPENDENCIES', 'LICENSE.txt'] + excludes += ["META-INF/LICENSE.txt", "META-INF/NOTICE.txt", "META-INF/LICENSE", "META-INF/NOTICE", "META-INF/DEPENDENCIES", "LICENSE.txt"] } } productFlavors { releaseflavor { dimension "free" - applicationId 'net.opendasharchive.openarchive.release' + applicationId "net.opendasharchive.openarchive.release" } } kotlinOptions { - jvmTarget = '1.8' + jvmTarget = "1.8" } buildFeatures { @@ -62,7 +62,7 @@ android { kotlinCompilerExtensionVersion "1.5.13" } - namespace 'net.opendasharchive.openarchive' + namespace "net.opendasharchive.openarchive" } dependencies { @@ -72,20 +72,20 @@ dependencies { // implementation "androidx.core:core-ktx:1.13.1" implementation "androidx.appcompat:appcompat:1.6.1" - implementation 'androidx.biometric:biometric:1.1.0' + implementation "androidx.biometric:biometric:1.1.0" implementation "androidx.constraintlayout:constraintlayout:2.1.4" implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0" implementation "androidx.legacy:legacy-support-v4:1.0.0" implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.7.0" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0" - implementation 'androidx.preference:preference-ktx:1.2.1' + implementation "androidx.preference:preference-ktx:1.2.1" implementation "androidx.work:work-runtime:2.9.0" implementation "androidx.work:work-runtime-ktx:2.9.0" implementation "androidx.work:work-testing:2.9.0" implementation "androidx.compose.ui:ui:1.6.7" implementation "androidx.compose.material3:material3:1.2.1" - implementation 'androidx.compose.foundation:foundation:1.6.7' + implementation "androidx.compose.foundation:foundation:1.6.7" implementation "androidx.compose.ui:ui-tooling-preview:1.6.7" implementation "androidx.activity:activity-compose:1.9.0" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0" @@ -110,11 +110,9 @@ dependencies { annotationProcessor "com.github.bumptech.glide:compiler:4.16.0" implementation "com.github.derlio:audio-waveform:v1.0.1" implementation "com.github.esafirm:android-image-picker:3.0.0" - implementation "com.github.stfalcon:frescoimageviewer:0.5.0" implementation "com.facebook.fresco:fresco:2.6.0" implementation "com.squareup.picasso:picasso:2.5.2" - implementation "com.amulyakhare:com.amulyakhare.textdrawable:1.0.1" implementation "com.github.abdularis:circularimageview:1.4" implementation "org.cleaninsights.sdk:clean-insights-sdk:2.8.0" @@ -125,62 +123,62 @@ dependencies { transitive = false - exclude group: 'org.bitcoinj' - exclude group: 'com.google.protobuf' - exclude group: 'org.slf4j' - exclude group: 'net.jcip' - exclude group: 'commons-cli' - exclude group: 'org.json' - exclude group: 'com.google.guava' - exclude group: 'com.google.guava', module: 'guava-jdk5' - exclude group: 'com.google.code.findbugs', module: 'annotations' + exclude group: "org.bitcoinj" + exclude group: "com.google.protobuf" + exclude group: "org.slf4j" + exclude group: "net.jcip" + exclude group: "commons-cli" + exclude group: "org.json" + exclude group: "com.google.guava" + exclude group: "com.google.guava", module: "guava-jdk5" + exclude group: "com.google.code.findbugs", module: "annotations" exclude group: "com.squareup.okio", module: "okio" } implementation "com.google.guava:guava:31.0.1-jre" - implementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava' + implementation "com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava" - implementation 'org.bouncycastle:bcpkix-jdk15to18:1.72' - implementation 'org.bouncycastle:bcprov-jdk15to18:1.72' - api 'org.bouncycastle:bcpg-jdk15to18:1.71' + implementation "org.bouncycastle:bcpkix-jdk15to18:1.72" + implementation "org.bouncycastle:bcprov-jdk15to18:1.72" + api "org.bouncycastle:bcpg-jdk15to18:1.71" implementation "com.tbuonomo:dotsindicator:5.0" - implementation 'com.guolindev.permissionx:permissionx:1.6.4' + implementation "com.guolindev.permissionx:permissionx:1.6.4" - implementation 'com.jakewharton.timber:timber:5.0.1' + implementation "com.jakewharton.timber:timber:5.0.1" // Google Drive API - implementation 'com.google.android.gms:play-services-auth:21.1.1' - implementation 'com.google.http-client:google-http-client-gson:1.42.1' - implementation 'com.google.api-client:google-api-client-android:1.26.0' - implementation 'com.google.apis:google-api-services-drive:v3-rev136-1.25.0' + implementation "com.google.android.gms:play-services-auth:21.1.1" + implementation "com.google.http-client:google-http-client-gson:1.42.1" + implementation "com.google.api-client:google-api-client-android:1.26.0" + implementation "com.google.apis:google-api-services-drive:v3-rev136-1.25.0" // Tor - implementation 'info.guardianproject:tor-android:0.4.7.14' - implementation 'info.guardianproject:jtorctl:0.4.5.7' + implementation "info.guardianproject:tor-android:0.4.7.14" + implementation "info.guardianproject:jtorctl:0.4.5.7" // New Play libraries - implementation 'com.google.android.play:asset-delivery:2.2.2' - implementation 'com.google.android.play:asset-delivery-ktx:2.2.2' + implementation "com.google.android.play:asset-delivery:2.2.2" + implementation "com.google.android.play:asset-delivery-ktx:2.2.2" - implementation 'com.google.android.play:feature-delivery:2.1.0' - implementation 'com.google.android.play:feature-delivery-ktx:2.1.0' + implementation "com.google.android.play:feature-delivery:2.1.0" + implementation "com.google.android.play:feature-delivery-ktx:2.1.0" - implementation 'com.google.android.play:review:2.0.1' - implementation 'com.google.android.play:review-ktx:2.0.1' + implementation "com.google.android.play:review:2.0.1" + implementation "com.google.android.play:review-ktx:2.0.1" - implementation 'com.google.android.play:app-update:2.1.0' - implementation 'com.google.android.play:app-update-ktx:2.1.0' + implementation "com.google.android.play:app-update:2.1.0" + implementation "com.google.android.play:app-update-ktx:2.1.0" // Tests - testImplementation 'junit:junit:4.13.2' - testImplementation 'org.robolectric:robolectric:4.7.3' - androidTestImplementation 'androidx.test.ext:junit:1.1.5' - androidTestImplementation 'androidx.test:runner:1.5.2' + testImplementation "junit:junit:4.13.2" + testImplementation "org.robolectric:robolectric:4.7.3" + androidTestImplementation "androidx.test.ext:junit:1.1.5" + androidTestImplementation "androidx.test:runner:1.5.2" } configurations { - all*.exclude group: 'com.google.guava', module: 'listenablefuture' + all*.exclude group: "com.google.guava", module: "listenablefuture" } /** diff --git a/app/src/main/java/net/opendasharchive/openarchive/db/Space.kt b/app/src/main/java/net/opendasharchive/openarchive/db/Space.kt index bf5c8ba4..88a29e87 100644 --- a/app/src/main/java/net/opendasharchive/openarchive/db/Space.kt +++ b/app/src/main/java/net/opendasharchive/openarchive/db/Space.kt @@ -2,23 +2,36 @@ package net.opendasharchive.openarchive.db import android.content.Context import android.content.Intent +import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable import android.widget.ImageView import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat -import com.amulyakhare.textdrawable.TextDrawable import com.github.abdularis.civ.AvatarImageView import com.orm.SugarRecord import net.opendasharchive.openarchive.R import net.opendasharchive.openarchive.features.onboarding.SpaceSetupActivity import net.opendasharchive.openarchive.services.gdrive.GDriveConduit import net.opendasharchive.openarchive.services.internetarchive.IaConduit +import net.opendasharchive.openarchive.util.DrawableUtil import net.opendasharchive.openarchive.util.Prefs import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import java.util.Locale - +/** + * Space - Account to connect to. + * + * @property type + * @property name Server given name + * @property username username for server + * @property displayname not in use + * @property password password for login + * @property host server url + * @property metaData + * @property licenseUrl + * @constructor Create empty Space + */ data class Space( var type: Int = 0, var name: String = "", @@ -164,7 +177,11 @@ data class Space( Type.GDRIVE -> ContextCompat.getDrawable(context, R.drawable.logo_gdrive_outline) // ?.tint(color) - else -> TextDrawable.builder().buildRound(initial, color) + else -> { + // Replace the original TextDrawable code with the custom drawable function + val initial = "A" // Replace with actual initial if available + BitmapDrawable(context.resources, DrawableUtil.createCircularTextDrawable(initial, color)) + } } } diff --git a/app/src/main/java/net/opendasharchive/openarchive/features/media/ReviewActivity.kt b/app/src/main/java/net/opendasharchive/openarchive/features/media/ReviewActivity.kt index 1c1f5245..691938e7 100644 --- a/app/src/main/java/net/opendasharchive/openarchive/features/media/ReviewActivity.kt +++ b/app/src/main/java/net/opendasharchive/openarchive/features/media/ReviewActivity.kt @@ -10,9 +10,9 @@ import android.view.MenuItem import android.view.View import android.widget.ImageView import com.bumptech.glide.Glide +import com.facebook.drawee.view.SimpleDraweeView import com.github.derlio.waveform.SimpleWaveformView import com.squareup.picasso.Picasso -import com.stfalcon.frescoimageviewer.ImageViewer import net.opendasharchive.openarchive.R import net.opendasharchive.openarchive.databinding.ActivityReviewBinding import net.opendasharchive.openarchive.db.Media @@ -168,9 +168,8 @@ class ReviewActivity : BaseActivity(), View.OnClickListener { when (view) { mBinding.waveform, mBinding.image -> { if (mMedia?.mimeType?.startsWith("image") == true) { - ImageViewer.Builder(this, listOf(mMedia?.fileUri)) - .setStartPosition(0) - .show() + val draweeView = SimpleDraweeView(this) + draweeView.setImageURI(mMedia?.fileUri) } } mBinding.btFlag -> { diff --git a/app/src/main/java/net/opendasharchive/openarchive/util/DrawableUtil.kt b/app/src/main/java/net/opendasharchive/openarchive/util/DrawableUtil.kt new file mode 100644 index 00000000..2c225e55 --- /dev/null +++ b/app/src/main/java/net/opendasharchive/openarchive/util/DrawableUtil.kt @@ -0,0 +1,32 @@ +package net.opendasharchive.openarchive.util + +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.Typeface + +object DrawableUtil { + fun createCircularTextDrawable(initial: String, backgroundColor: Int): Bitmap { + val size = 100 // size of the bitmap + val bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888) + val canvas = Canvas(bitmap) + + val paint = Paint().apply { + isAntiAlias = true + color = backgroundColor // No conflict here + } + canvas.drawCircle(size / 2f, size / 2f, size / 2f, paint) + + paint.color = Color.WHITE // Setting a new color for the text + paint.textSize = 40f + paint.textAlign = Paint.Align.CENTER + paint.typeface = Typeface.DEFAULT_BOLD + + val xPos = size / 2f + val yPos = (canvas.height / 2f) - ((paint.descent() + paint.ascent()) / 2) + canvas.drawText(initial, xPos, yPos, paint) + + return bitmap + } +} \ No newline at end of file From 0d00b33a838e31c2631a66ec0eff40ec4572a6f1 Mon Sep 17 00:00:00 2001 From: Elelan's Macbook Pro Date: Thu, 14 Nov 2024 00:29:44 +0530 Subject: [PATCH 2/2] AOO-24: fixed upload service added AppLogger fixed initial in Space fixed pager notify --- app/build.gradle | 3 + .../opendasharchive/openarchive/SaveApp.kt | 14 +- .../openarchive/core/logger/AppLogger.kt | 221 ++++++++++++++++++ .../opendasharchive/openarchive/db/Space.kt | 7 +- .../openarchive/features/main/MainActivity.kt | 4 +- .../openarchive/upload/UploadService.kt | 185 +++++---------- 6 files changed, 294 insertions(+), 140 deletions(-) create mode 100644 app/src/main/java/net/opendasharchive/openarchive/core/logger/AppLogger.kt diff --git a/app/build.gradle b/app/build.gradle index 153f277e..fd0d0fd6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -175,6 +175,9 @@ dependencies { testImplementation "org.robolectric:robolectric:4.7.3" androidTestImplementation "androidx.test.ext:junit:1.1.5" androidTestImplementation "androidx.test:runner:1.5.2" + + // Pretty Logging + implementation "com.orhanobut:logger:2.2.0" } configurations { diff --git a/app/src/main/java/net/opendasharchive/openarchive/SaveApp.kt b/app/src/main/java/net/opendasharchive/openarchive/SaveApp.kt index 404f1a86..9c9f0c06 100644 --- a/app/src/main/java/net/opendasharchive/openarchive/SaveApp.kt +++ b/app/src/main/java/net/opendasharchive/openarchive/SaveApp.kt @@ -8,10 +8,13 @@ import com.orm.SugarApp import info.guardianproject.netcipher.proxy.OrbotHelper import net.opendasharchive.openarchive.core.di.coreModule import net.opendasharchive.openarchive.core.di.featuresModule +import net.opendasharchive.openarchive.core.logger.AppLogger import net.opendasharchive.openarchive.util.Prefs import net.opendasharchive.openarchive.util.Theme import org.koin.android.ext.koin.androidContext +import org.koin.android.ext.koin.androidLogger import org.koin.core.context.startKoin +import org.koin.core.logger.Level import timber.log.Timber @@ -25,10 +28,15 @@ class SaveApp : SugarApp() { super.onCreate() startKoin { + androidLogger(Level.DEBUG) androidContext(this@SaveApp) modules(coreModule, featuresModule) } + if(BuildConfig.DEBUG) { + AppLogger.init(applicationContext, initDebugger = true) + } + val config = ImagePipelineConfig.newBuilder(this) .setProgressiveJpegConfig(SimpleProgressiveJpegConfig()) .setResizeAndRotateEnabledForNetwork(true) @@ -43,12 +51,6 @@ class SaveApp : SugarApp() { Theme.set(Prefs.theme) CleanInsightsManager.init(this) - - // enable timber logging library for debug builds - if (BuildConfig.DEBUG){ - Timber.plant(Timber.DebugTree()) - Timber.tag("SAVE") - } } private fun initNetCipher() { diff --git a/app/src/main/java/net/opendasharchive/openarchive/core/logger/AppLogger.kt b/app/src/main/java/net/opendasharchive/openarchive/core/logger/AppLogger.kt new file mode 100644 index 00000000..97a0c83f --- /dev/null +++ b/app/src/main/java/net/opendasharchive/openarchive/core/logger/AppLogger.kt @@ -0,0 +1,221 @@ +package net.opendasharchive.openarchive.core.logger + +import android.content.Context +import com.orhanobut.logger.AndroidLogAdapter +import com.orhanobut.logger.DiskLogAdapter +import com.orhanobut.logger.FormatStrategy +import com.orhanobut.logger.Logger +import com.orhanobut.logger.PrettyFormatStrategy +import net.opendasharchive.openarchive.BuildConfig +import timber.log.Timber + + +/** + * A utility object for centralized logging in Android applications. + * This object simplifies the logging process by integrating with the Timber and + * AndroidRemoteDebugger libraries. + * + * Logs will only be generated if the [init] method is called. The class supports + * different log levels and allows for conditional remote debugging. + * The name of the class from which AppLogger was called will automatically be set as a tag + */ +object AppLogger { + + /** + * Initializes the logger by planting a Timber DebugTree and optionally + * initializing the AndroidRemoteDebugger. + * + * @param context The context used to initialize the AndroidRemoteDebugger. + * @param initDebugger A boolean flag to determine whether AndroidRemoteDebugger + * should be initialized. + */ + fun init(context: Context, initDebugger: Boolean) { + val formatStrategy: FormatStrategy = PrettyFormatStrategy.newBuilder() + .showThreadInfo(false) + .methodCount(2) + .methodOffset(7) + .tag("SAVE") + .build() + + Logger.addLogAdapter(object : AndroidLogAdapter(formatStrategy) { + override fun isLoggable(priority: Int, tag: String?): Boolean { + return BuildConfig.DEBUG + } + }) + Logger.addLogAdapter(DiskLogAdapter()); + + Timber.plant(object : Timber.DebugTree() { + override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { + if (t == null) { + Logger.log(priority, "-$tag", message, null) + } else { + Logger.log(priority, "-$tag", message, t) + } + } + }) + //if(initDebugger) + //AndroidRemoteDebugger.init(context) + } + + /** + * Logs a warning message with the given format string and arguments. + * + * @param s The format string. + * @param objects The arguments referenced by the format specifiers in the format string. + */ + fun w(s: String, vararg objects: Any) { + val tag = Thread.currentThread().stackTrace[3].fileName + Timber.tag(tag).w(s, *objects) + //AndroidRemoteDebugger.Log.w(tag, s) + } + + /** + * Logs a debug message with the given format string and arguments. + * + * @param s The format string. + * @param objects The arguments referenced by the format specifiers in the format string. + */ + fun d(s: String, vararg objects: Any) { + val tag = Thread.currentThread().stackTrace[3].fileName + Timber.tag(tag).d(s, *objects) + //AndroidRemoteDebugger.Log.d(tag,s) + } + + /** + * Logs a debug message with a specific tag, format string, and arguments. + * + * @param tag The specific tag for the log message. + * @param s The format string. + * @param objects The arguments referenced by the format specifiers in the format string. + */ + fun tagD(tag: String, s: String, vararg objects: Any) { + val tagAuto = Thread.currentThread().stackTrace[3].fileName + Timber.tag(tagAuto).d("specific TAG: $tag, $s", *objects) + //AndroidRemoteDebugger.Log.d(tagAuto, "specific TAG: $tag, $s") + } + + /** + * Logs an info message with a specific tag, format string, and arguments. + * + * @param tag The specific tag for the log message. + * @param s The format string. + * @param objects The arguments referenced by the format specifiers in the format string. + */ + fun tagI(tag: String, s: String, vararg objects: Any) { + val tagAuto = Thread.currentThread().stackTrace[3].fileName + Timber.tag(tagAuto).i("specific TAG: $tag, $s", *objects) + //AndroidRemoteDebugger.Log.d(tagAuto, "specific TAG: $tag, $s") + } + + /** + * Logs an error message with a specific tag, format string, and arguments. + * + * @param tag The specific tag for the log message. + * @param s The format string. + * @param objects The arguments referenced by the format specifiers in the format string. + */ + fun tagE(tag: String, s: String, vararg objects: Any) { + val tagAuto = Thread.currentThread().stackTrace[3].fileName + Timber.tag(tagAuto).e("specific TAG: $tag, $s", *objects) + //AndroidRemoteDebugger.Log.d(tagAuto, "specific TAG: $tag, $s") + } + + /** + * Logs a verbose message with the given format string and arguments. + * + * @param s The format string. + * @param objects The arguments referenced by the format specifiers in the format string. + */ + fun v(s: String, vararg objects: Any) { + val tag = Thread.currentThread().stackTrace[3].fileName + Timber.tag(tag).v(s, *objects) + //AndroidRemoteDebugger.Log.v(tag, s) + } + + /** + * Logs a debug message with an associated throwable, format string, and arguments. + * + * @param throwable The throwable to log. + * @param s The format string. + * @param objects The arguments referenced by the format specifiers in the format string. + */ + fun d(throwable: Throwable, s: String, vararg objects: Any) { + val tag = Thread.currentThread().stackTrace[3].fileName + Timber.tag(tag).d(throwable, s, *objects) + //AndroidRemoteDebugger.Log.d(tag, throwable.stackTrace.toString()) + } + + /** + * Logs an error message with the given format string and arguments. + * + * @param s The format string. + * @param objects The arguments referenced by the format specifiers in the format string. + */ + fun e(s: String, vararg objects: Any) { + val tag = Thread.currentThread().stackTrace[3].fileName + Timber.tag(tag).e(s, *objects) + //AndroidRemoteDebugger.Log.e(tag, s) + } + + /** + * Logs an error message with an associated throwable, format string, and arguments. + * + * @param throwable The throwable to log. + * @param s The format string. + * @param objects The arguments referenced by the format specifiers in the format string. + */ + fun e(throwable: Throwable, s: String, vararg objects: Any) { + val tag = Thread.currentThread().stackTrace[3].fileName + Timber.tag(tag).e(throwable, s, *objects) + //AndroidRemoteDebugger.Log.e(tag, throwable.stackTrace.toString()) + } + + /** + * Logs an error message with an associated throwable. + * + * @param throwable The throwable to log. + */ + fun e(throwable: Throwable) { + val tag = Thread.currentThread().stackTrace[3].fileName + Timber.tag(tag).e(throwable) + //AndroidRemoteDebugger.Log.e(tag, throwable.stackTrace.toString()) + } + + /** + * Logs an info message with the given format string and arguments. + * + * @param s The format string. + * @param objects The arguments referenced by the format specifiers in the format string. + */ + fun i(s: String, vararg objects: Any) { + val tag = Thread.currentThread().stackTrace[3].fileName + Timber.tag(tag).i(s, *objects) + //AndroidRemoteDebugger.Log.i(tag, s) + } + + /** + * Logs an info message with an associated throwable, format string, and arguments. + * + * @param throwable The throwable to log. + * @param s The format string. + * @param objects The arguments referenced by the format specifiers in the format string. + */ + fun i(throwable: Throwable, s: String, vararg objects: Any) { + val tag = Thread.currentThread().stackTrace[3].fileName + Timber.tag(tag).i(throwable, s, *objects) + //AndroidRemoteDebugger.Log.i(tag, throwable.stackTrace.toString()) + } + + /** + * Logs a warning message with an associated throwable, format string, and arguments. + * + * @param throwable The throwable to log. + * @param s The format string. + * @param objects The arguments referenced by the format specifiers in the format string. + */ + fun w(throwable: Throwable, s: String, vararg objects: Any) { + val tag = Thread.currentThread().stackTrace[3].fileName + Timber.tag(tag).w(throwable, s, *objects) + //AndroidRemoteDebugger.Log.w(tag, throwable.stackTrace.toString()) + } +} \ No newline at end of file diff --git a/app/src/main/java/net/opendasharchive/openarchive/db/Space.kt b/app/src/main/java/net/opendasharchive/openarchive/db/Space.kt index 88a29e87..db17a30b 100644 --- a/app/src/main/java/net/opendasharchive/openarchive/db/Space.kt +++ b/app/src/main/java/net/opendasharchive/openarchive/db/Space.kt @@ -177,11 +177,8 @@ data class Space( Type.GDRIVE -> ContextCompat.getDrawable(context, R.drawable.logo_gdrive_outline) // ?.tint(color) - else -> { - // Replace the original TextDrawable code with the custom drawable function - val initial = "A" // Replace with actual initial if available - BitmapDrawable(context.resources, DrawableUtil.createCircularTextDrawable(initial, color)) - } + else -> BitmapDrawable(context.resources, DrawableUtil.createCircularTextDrawable(initial, color)) + } } diff --git a/app/src/main/java/net/opendasharchive/openarchive/features/main/MainActivity.kt b/app/src/main/java/net/opendasharchive/openarchive/features/main/MainActivity.kt index b261044d..eebfa222 100644 --- a/app/src/main/java/net/opendasharchive/openarchive/features/main/MainActivity.kt +++ b/app/src/main/java/net/opendasharchive/openarchive/features/main/MainActivity.kt @@ -468,7 +468,9 @@ class MainActivity : BaseActivity(), FolderAdapterListener, SpaceAdapterListener val project = getSelectedProject() if (project != null) { - mPagerAdapter.notifyProjectChanged(project) + mBinding.pager.post { + mPagerAdapter.notifyProjectChanged(project) + } project.space?.setAvatar(mBinding.currentFolderIcon) mBinding.currentFolderIcon.show() diff --git a/app/src/main/java/net/opendasharchive/openarchive/upload/UploadService.kt b/app/src/main/java/net/opendasharchive/openarchive/upload/UploadService.kt index bbdb9d37..02054497 100644 --- a/app/src/main/java/net/opendasharchive/openarchive/upload/UploadService.kt +++ b/app/src/main/java/net/opendasharchive/openarchive/upload/UploadService.kt @@ -12,57 +12,21 @@ import android.net.ConnectivityManager import android.net.NetworkCapabilities import android.os.Build import androidx.core.app.NotificationCompat -import androidx.core.app.NotificationManagerCompat import androidx.core.content.ContextCompat import androidx.work.Configuration import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.cancel import kotlinx.coroutines.launch import net.opendasharchive.openarchive.CleanInsightsManager import net.opendasharchive.openarchive.R +import net.opendasharchive.openarchive.core.logger.AppLogger import net.opendasharchive.openarchive.db.Media import net.opendasharchive.openarchive.features.main.MainActivity import net.opendasharchive.openarchive.services.Conduit import net.opendasharchive.openarchive.util.Prefs -import timber.log.Timber import java.io.IOException import java.util.* -//class StartTor(val appContext: Context, workerParams: WorkerParameters) : Worker(appContext, workerParams) { -// -// override fun doWork(): Result { -// Timber.d("StartTor") -// bindService(Intent(appContext, TorService::class.java), object : ServiceConnection { -// override fun onServiceConnected(name: ComponentName, service: IBinder) { -// val torService: TorService = (service as TorService.LocalBinder).service -// -// while (torService.torControlConnection == null) { -// try { -// Timber.d("Sleeping") -// Thread.sleep(500) -// } catch (e: InterruptedException) { -// e.printStackTrace() -// } -// } -// -//// Toast.makeText( -//// this@MainActivity, -//// "Got Tor control connection", -//// Toast.LENGTH_LONG -// } -//// ).show() -// -// override fun onServiceDisconnected(name: ComponentName) { -// // Things... -// } -// }, BIND_AUTO_CREATE) -// -// return Result.success() -// } -//} - class UploadService : JobService() { companion object { @@ -72,23 +36,19 @@ class UploadService : JobService() { fun startUploadService(activity: Activity) { val jobScheduler = ContextCompat.getSystemService(activity, JobScheduler::class.java) ?: return - var jobBuilder = JobInfo.Builder( MY_BACKGROUND_JOB, ComponentName(activity, UploadService::class.java) ).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { jobBuilder = jobBuilder.setUserInitiated(true) } - jobScheduler.schedule(jobBuilder.build()) } fun stopUploadService(context: Context) { val jobScheduler = ContextCompat.getSystemService(context, JobScheduler::class.java) ?: return - jobScheduler.cancel(MY_BACKGROUND_JOB) } } @@ -96,41 +56,15 @@ class UploadService : JobService() { private var mRunning = false private var mKeepUploading = true private val mConduits = ArrayList() - private lateinit var notification: Notification - -// private val constraints = Constraints.Builder() override fun onCreate() { super.onCreate() createNotificationChannel() - notification = prepNotification() Configuration.Builder().setJobSchedulerJobIdRange(0, Integer.MAX_VALUE).build() - - with (NotificationManagerCompat.from(this)) { - try { - notify(23, notification) - } catch(e: SecurityException) { - Timber.d(e) - } - } - -// val contentUri = Uri.parse("content://org.opendasharchive.safe.provider.tor/status") -// constraints.addContentUriTrigger(contentUri, true) -// -// val myConstraints = constraints.build() -// -// val workRequest = OneTimeWorkRequestBuilder() -// .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) -// .setConstraints(myConstraints) -// .build() -// -// WorkManager.getInstance(this).enqueue(workRequest) } - private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) - override fun onStartJob(params: JobParameters): Boolean { - scope.launch { + CoroutineScope(Dispatchers.IO).launch { upload { jobFinished(params, false) } @@ -152,88 +86,91 @@ class UploadService : JobService() { mKeepUploading = false for (conduit in mConduits) conduit.cancel() mConduits.clear() - scope.cancel() + return true } private suspend fun upload(completed: () -> Unit) { - if (mRunning) { - return completed() - } + if (mRunning) return completed() mRunning = true + AppLogger.i("upload started") if (!shouldUpload()) { mRunning = false - + AppLogger.i("no network, upload stopped") return completed() } // Get all media items that are set into queued state. - val results = Media.getByStatus( - listOf(Media.Status.Queued, Media.Status.Uploading), - Media.ORDER_PRIORITY - ).toMutableList() + var results = emptyList() while (mKeepUploading && - results.isNotEmpty() + Media.getByStatus( + listOf(Media.Status.Queued, Media.Status.Uploading), + Media.ORDER_PRIORITY + ) + .also { results = it } + .isNotEmpty() ) { val datePublish = Date() - val media = results.removeFirst() - - if (media.sStatus != Media.Status.Uploading) { - media.uploadDate = datePublish - media.progress = 0 // Should we reset this? - media.sStatus = Media.Status.Uploading - media.statusMessage = "" - } + for (media in results) { + if (media.sStatus != Media.Status.Uploading) { + media.uploadDate = datePublish + media.progress = 0 // Should we reset this? + media.sStatus = Media.Status.Uploading + media.statusMessage = "" + } - media.licenseUrl = media.project?.licenseUrl + media.licenseUrl = media.project?.licenseUrl - val collection = media.collection + val collection = media.collection - if (collection?.uploadDate == null) { - collection?.uploadDate = datePublish - collection?.save() - } + if (collection?.uploadDate == null) { + collection?.uploadDate = datePublish + collection?.save() + } - try { - upload(media) - } catch (ioe: IOException) { - Timber.d(ioe) + try { + AppLogger.i("Started uploading", media) + upload(media) + } catch (ioe: IOException) { + AppLogger.e(ioe) - media.statusMessage = "error in uploading media: " + ioe.message - media.sStatus = Media.Status.Error - media.save() + media.statusMessage = "error in uploading media: " + ioe.message + media.sStatus = Media.Status.Error + media.save() + } - BroadcastManager.postChange(applicationContext, media.collectionId, media.id) + if (!mKeepUploading) break // Time to end this. } - - if (!mKeepUploading) break // Time to end this. } + AppLogger.i("Uploads completed") + mRunning = false completed() } @Throws(IOException::class) private suspend fun upload(media: Media): Boolean { - - val conduit = Conduit.get(media, this) ?: return false - media.sStatus = Media.Status.Uploading + AppLogger.i("${media.id} - media status changed to uploading") media.save() BroadcastManager.postChange(this, media.collectionId, media.id) + val conduit = Conduit.get(media, this) + if (conduit == null) { + AppLogger.e("Conduit is null") + return false + } + CleanInsightsManager.measureEvent("upload", "try_upload", media.space?.tType?.friendlyName) mConduits.add(conduit) - - scope.launch { - conduit.upload() - mConduits.remove(conduit) - } + conduit.upload() + mConduits.remove(conduit) return true } @@ -246,13 +183,8 @@ class UploadService : JobService() { if (isNetworkAvailable(requireUnmetered)) return true - if (Prefs.useTor && isTorAvailable()) return true - - val type = if (requireUnmetered) { - JobInfo.NETWORK_TYPE_UNMETERED - } else { - JobInfo.NETWORK_TYPE_ANY - } + val type = + if (requireUnmetered) JobInfo.NETWORK_TYPE_UNMETERED else JobInfo.NETWORK_TYPE_ANY // Try again when there is a network. val job = JobInfo.Builder( @@ -268,12 +200,9 @@ class UploadService : JobService() { return false } - private fun isTorAvailable(): Boolean { - return false - } - private fun isNetworkAvailable(requireUnmetered: Boolean): Boolean { - val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager ?: return false + val cm = + getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager ?: return false val cap = cm.getNetworkCapabilities(cm.activeNetwork) ?: return false @@ -297,7 +226,7 @@ class UploadService : JobService() { private fun createNotificationChannel() { val channel = NotificationChannel( NOTIFICATION_CHANNEL_ID, getString(R.string.uploads), - NotificationManager.IMPORTANCE_DEFAULT + NotificationManager.IMPORTANCE_LOW ) channel.description = getString(R.string.uploads_notification_descriptions) @@ -306,16 +235,16 @@ class UploadService : JobService() { channel.setShowBadge(false) channel.lockscreenVisibility = Notification.VISIBILITY_SECRET - val notificationManager = - getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - - notificationManager.createNotificationChannel(channel) + val notificationManager = (getSystemService(NOTIFICATION_SERVICE) as? NotificationManager) + notificationManager?.createNotificationChannel(channel) } private fun prepNotification(): Notification { + val pendingIntent = PendingIntent.getActivity( this, 0, - Intent(this, MainActivity::class.java), PendingIntent.FLAG_IMMUTABLE + Intent(this, MainActivity::class.java), + PendingIntent.FLAG_IMMUTABLE ) return NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)