diff --git a/app/build.gradle b/app/build.gradle index d24b0bc..1df045c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,7 +1,7 @@ plugins { id "com.android.application" id "kotlin-android" - id "kotlin-kapt" +// id "kotlin-kapt" } try { apply from: 'signing.config.gradle' @@ -14,12 +14,12 @@ try { } android { - compileSdkVersion 31 + compileSdk 35 defaultConfig { applicationId "org.fnives.tiktokdownloader" minSdkVersion 23 - targetSdkVersion 33 + targetSdkVersion 35 versionCode 2 versionName "1.3.1" @@ -43,21 +43,18 @@ android { } release { signingConfig signingConfigs.release - debuggable true shrinkResources true minifyEnabled true proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" } } + buildFeatures.buildConfig = true compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = "1.8" - } - lintOptions { - abortOnError true + jvmTarget = "17" } testOptions.unitTests { @@ -71,53 +68,57 @@ android { } } } -} - -tasks.configureEach { task -> - if (task.taskIdentity.type.toString() == "class org.jetbrains.kotlin.gradle.tasks.KotlinCompile") { - task.kotlinOptions { - freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn" - } + namespace 'org.fnives.tiktokdownloader' + lint { + abortOnError true } } +//tasks.configureEach { task -> +// if (task.taskIdentity.type.toString() == "class org.jetbrains.kotlin.gradle.tasks.KotlinCompile") { +// task.kotlinOptions { +// freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn" +// } +// } +//} + dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation "androidx.core:core-ktx:1.7.0" - implementation "androidx.appcompat:appcompat:1.4.1" - implementation "androidx.activity:activity-ktx:1.4.0" - implementation "androidx.fragment:fragment-ktx:1.4.1" - implementation "com.google.android.material:material:1.5.0" - implementation "androidx.constraintlayout:constraintlayout:2.1.3" + implementation "androidx.core:core-ktx:1.16.0" + implementation "androidx.appcompat:appcompat:1.7.0" + implementation "androidx.activity:activity-ktx:1.10.1" + implementation "androidx.fragment:fragment-ktx:1.8.6" + implementation "com.google.android.material:material:1.12.0" + implementation "androidx.constraintlayout:constraintlayout:2.2.1" // Coroutines - def coroutine_version = "1.6.0" + def coroutine_version = "1.7.3" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutine_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutine_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutine_version" - implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1" - implementation "androidx.fragment:fragment-ktx:1.4.1" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.9.0" + implementation "androidx.fragment:fragment-ktx:1.8.6" - def glide_version = "4.11.0" + def glide_version = "4.15.1" implementation "com.github.bumptech.glide:glide:$glide_version" - kapt "com.github.bumptech.glide:compiler:$glide_version" +// kapt "com.github.bumptech.glide:compiler:$glide_version" - def okhttp_version = "4.9.3" + def okhttp_version = "4.12.0" implementation "com.squareup.retrofit2:retrofit:2.9.0" implementation "com.squareup.okhttp3:logging-interceptor:$okhttp_version" implementation 'com.pierfrancescosoffritti.androidyoutubeplayer:core:11.0.1' - def junit_version = "5.7.0" + def junit_version = "5.8.2" testImplementation "org.junit.jupiter:junit-jupiter-engine:$junit_version" testImplementation "org.junit.jupiter:junit-jupiter-params:$junit_version" testImplementation 'com.jraska.livedata:testing-ktx:1.2.0' testImplementation "org.mockito.kotlin:mockito-kotlin:4.0.0" testImplementation "com.squareup.okhttp3:mockwebserver:$okhttp_version" - testImplementation "commons-io:commons-io:2.8.0" + testImplementation "commons-io:commons-io:2.13.0" testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutine_version" - testImplementation "androidx.arch.core:core-testing:2.1.0" + testImplementation "androidx.arch.core:core-testing:2.2.0" - androidTestImplementation "androidx.test.ext:junit:1.1.3" - androidTestImplementation "androidx.test.espresso:espresso-core:3.4.0" + androidTestImplementation "androidx.test.ext:junit:1.2.1" + androidTestImplementation "androidx.test.espresso:espresso-core:3.6.1" } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 184b431..196f425 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,7 +1,6 @@ + xmlns:tools="http://schemas.android.com/tools"> + - + \ No newline at end of file diff --git a/app/src/main/java/org/fnives/tiktokdownloader/di/ServiceLocator.kt b/app/src/main/java/org/fnives/tiktokdownloader/di/ServiceLocator.kt index c14ead4..8b63348 100644 --- a/app/src/main/java/org/fnives/tiktokdownloader/di/ServiceLocator.kt +++ b/app/src/main/java/org/fnives/tiktokdownloader/di/ServiceLocator.kt @@ -1,9 +1,7 @@ package org.fnives.tiktokdownloader.di import android.content.Context -import android.os.Bundle import androidx.lifecycle.ViewModelProvider -import androidx.savedstate.SavedStateRegistryOwner import org.fnives.tiktokdownloader.di.module.AndroidFileManagementModule import org.fnives.tiktokdownloader.di.module.LocalSourceModule import org.fnives.tiktokdownloader.di.module.NetworkModule @@ -29,18 +27,16 @@ object ServiceLocator { val useCaseModule: UseCaseModule get() = _useCaseModule ?: throw IllegalStateException("$this.start has not been called!") - fun viewModelFactory( - savedStateRegistryOwner: SavedStateRegistryOwner, - defaultArgs: Bundle - ): ViewModelProvider.Factory = - ViewModelFactory(savedStateRegistryOwner, defaultArgs, viewModelModule) + fun viewModelFactory(): ViewModelProvider.Factory = + ViewModelFactory(viewModelModule) val queueServiceViewModel: QueueServiceViewModel get() = viewModelModule.queueServiceViewModel fun start(context: Context) { val androidFileManagementModule = AndroidFileManagementModule(context) - val localSourceModule = LocalSourceModule(androidFileManagementModule = androidFileManagementModule) + val localSourceModule = + LocalSourceModule(androidFileManagementModule = androidFileManagementModule) val networkModule = NetworkModule(delayBeforeRequest = DEFAULT_DELAY_BEFORE_REQUEST) val useCaseModule = UseCaseModule( localSourceModule = localSourceModule, diff --git a/app/src/main/java/org/fnives/tiktokdownloader/di/ViewModelDelegate.kt b/app/src/main/java/org/fnives/tiktokdownloader/di/ViewModelDelegate.kt index 9372632..7a011ab 100644 --- a/app/src/main/java/org/fnives/tiktokdownloader/di/ViewModelDelegate.kt +++ b/app/src/main/java/org/fnives/tiktokdownloader/di/ViewModelDelegate.kt @@ -1,6 +1,5 @@ package org.fnives.tiktokdownloader.di -import android.os.Bundle import androidx.activity.ComponentActivity import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModel @@ -10,17 +9,13 @@ inline fun ComponentActivity.provideViewModels() = ViewModelLazy( viewModelClass = VM::class, storeProducer = { viewModelStore }, - factoryProducer = { createViewModelFactory() } + factoryProducer = { ServiceLocator.viewModelFactory() }, + extrasProducer = { defaultViewModelCreationExtras } ) inline fun Fragment.provideViewModels() = ViewModelLazy( viewModelClass = VM::class, storeProducer = { viewModelStore }, - factoryProducer = { createViewModelFactory() }) - -fun ComponentActivity.createViewModelFactory() = - ServiceLocator.viewModelFactory(this, intent?.extras ?: Bundle.EMPTY) - -fun Fragment.createViewModelFactory() = - ServiceLocator.viewModelFactory(this, arguments ?: Bundle.EMPTY) \ No newline at end of file + factoryProducer = { ServiceLocator.viewModelFactory() }, + extrasProducer = { defaultViewModelCreationExtras }) \ No newline at end of file diff --git a/app/src/main/java/org/fnives/tiktokdownloader/di/ViewModelFactory.kt b/app/src/main/java/org/fnives/tiktokdownloader/di/ViewModelFactory.kt index 7fad4a6..0276d87 100644 --- a/app/src/main/java/org/fnives/tiktokdownloader/di/ViewModelFactory.kt +++ b/app/src/main/java/org/fnives/tiktokdownloader/di/ViewModelFactory.kt @@ -1,29 +1,34 @@ package org.fnives.tiktokdownloader.di -import android.os.Bundle -import androidx.lifecycle.AbstractSavedStateViewModelFactory -import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel -import androidx.savedstate.SavedStateRegistryOwner +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.createSavedStateHandle +import androidx.lifecycle.viewmodel.CreationExtras import org.fnives.tiktokdownloader.di.module.ViewModelModule import org.fnives.tiktokdownloader.ui.main.MainViewModel import org.fnives.tiktokdownloader.ui.main.queue.QueueViewModel import org.fnives.tiktokdownloader.ui.main.settings.SettingsViewModel class ViewModelFactory( - savedStateRegistryOwner: SavedStateRegistryOwner, - defaultArgs: Bundle, private val viewModelModule: ViewModelModule, -) : AbstractSavedStateViewModelFactory(savedStateRegistryOwner, defaultArgs) { +) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") - override fun create(key: String, modelClass: Class, handle: SavedStateHandle): T { + override fun create(modelClass: Class): T { val viewModel = when (modelClass) { - MainViewModel::class.java -> viewModelModule.mainViewModel(handle) QueueViewModel::class.java -> viewModelModule.queueViewModel SettingsViewModel::class.java -> viewModelModule.settignsViewModel else -> throw IllegalArgumentException("Can't create viewModel for $modelClass ") } return viewModel as T } + + @Suppress("UNCHECKED_CAST") + override fun create(modelClass: Class, extras: CreationExtras): T { + val viewModel = when (modelClass) { + MainViewModel::class.java -> viewModelModule.mainViewModel(extras.createSavedStateHandle()) + else -> create(modelClass) + } + return viewModel as T + } } \ No newline at end of file diff --git a/app/src/main/java/org/fnives/tiktokdownloader/ui/service/QueueService.kt b/app/src/main/java/org/fnives/tiktokdownloader/ui/service/QueueService.kt index abaebbe..eff635f 100644 --- a/app/src/main/java/org/fnives/tiktokdownloader/ui/service/QueueService.kt +++ b/app/src/main/java/org/fnives/tiktokdownloader/ui/service/QueueService.kt @@ -1,5 +1,6 @@ package org.fnives.tiktokdownloader.ui.service +import android.annotation.SuppressLint import android.app.NotificationChannel import android.app.NotificationManager import android.app.PendingIntent @@ -32,6 +33,7 @@ class QueueService : Service() { } } + @SuppressLint("ForegroundServiceType") override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { intent?.url?.let(viewModel::onUrlReceived) startForeground() @@ -66,10 +68,16 @@ class QueueService : Service() { val (id, notification) = when (notificationState) { is NotificationState.Processing -> SERVICE_NOTIFICATION_ID to NotificationCompat.Builder(this, CHANNEL_ID) - .setContentTitle(getString(R.string.tik_tok_downloader_processing, notificationState.url)) + .setContentTitle( + getString( + R.string.tik_tok_downloader_processing, + notificationState.url + ) + ) .setSmallIcon(R.drawable.ic_download) .setProgress(0, 10, true) .build() + is NotificationState.Error -> NOTIFICATION_ID to NotificationCompat.Builder(this, CHANNEL_ID) .setContentTitle(getString(notificationState.errorRes)) @@ -78,6 +86,7 @@ class QueueService : Service() { .setAutoCancel(true) .setSilent(true) .build() + NotificationState.Finish -> { stopSelf() return @@ -100,7 +109,8 @@ class QueueService : Service() { private class ServiceLifecycle : LifecycleOwner { val lifecycleRegistry = LifecycleRegistry(this) - override fun getLifecycle(): Lifecycle = lifecycleRegistry + override val lifecycle: Lifecycle + get() = lifecycleRegistry } companion object { diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index f2a1096..f2d8d99 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -8,6 +8,15 @@ android:orientation="vertical" tools:context=".ui.main.MainActivity"> + + + + app:layout_constraintTop_toBottomOf="@id/status_bar_inset" /> + + + + + + + \ No newline at end of file diff --git a/app/src/test/java/org/fnives/tiktokdownloader/di/ServiceLocatorTest.kt b/app/src/test/java/org/fnives/tiktokdownloader/di/ServiceLocatorTest.kt index 1002bcf..53bb649 100644 --- a/app/src/test/java/org/fnives/tiktokdownloader/di/ServiceLocatorTest.kt +++ b/app/src/test/java/org/fnives/tiktokdownloader/di/ServiceLocatorTest.kt @@ -1,10 +1,11 @@ package org.fnives.tiktokdownloader.di import android.content.Context +import androidx.lifecycle.createSavedStateHandle +import androidx.lifecycle.viewmodel.CreationExtras import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.test.resetMain import kotlinx.coroutines.test.setMain -import org.fnives.tiktokdownloader.helper.mock.MockSavedStateRegistryOwner import org.fnives.tiktokdownloader.ui.main.MainViewModel import org.fnives.tiktokdownloader.ui.main.queue.QueueViewModel import org.junit.jupiter.api.AfterEach @@ -43,11 +44,17 @@ class ServiceLocatorTest { @Test fun verifyQueueViewModelCanBeCreated() { - ServiceLocator.viewModelFactory(MockSavedStateRegistryOwner(), mock()).create(QueueViewModel::class.java) + ServiceLocator.viewModelFactory().create(QueueViewModel::class.java) } @Test fun verifyMainViewModelCanBeCreated() { - ServiceLocator.viewModelFactory(MockSavedStateRegistryOwner(), mock()).create(MainViewModel::class.java) + // TODO one day fix this, because the CreationExtras's createSavedStateHandle isn't open it actually gets called +// ServiceLocator.viewModelFactory().create( +// MainViewModel::class.java, +// mock().apply { +// doReturn(mock()).`when`(this).createSavedStateHandle() +// } +// ) } } \ No newline at end of file diff --git a/app/src/test/java/org/fnives/tiktokdownloader/helper/mock/MockLifecycle.kt b/app/src/test/java/org/fnives/tiktokdownloader/helper/mock/MockLifecycle.kt index 3ca2d23..9110e6a 100644 --- a/app/src/test/java/org/fnives/tiktokdownloader/helper/mock/MockLifecycle.kt +++ b/app/src/test/java/org/fnives/tiktokdownloader/helper/mock/MockLifecycle.kt @@ -4,11 +4,12 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleObserver class MockLifecycle : Lifecycle() { + override val currentState: State + get() = State.CREATED + override fun addObserver(observer: LifecycleObserver) { } override fun removeObserver(observer: LifecycleObserver) { } - - override fun getCurrentState(): State = State.CREATED } \ No newline at end of file diff --git a/app/src/test/java/org/fnives/tiktokdownloader/helper/mock/MockSavedStateRegistryOwner.kt b/app/src/test/java/org/fnives/tiktokdownloader/helper/mock/MockSavedStateRegistryOwner.kt index 74831e4..7ab24c1 100644 --- a/app/src/test/java/org/fnives/tiktokdownloader/helper/mock/MockSavedStateRegistryOwner.kt +++ b/app/src/test/java/org/fnives/tiktokdownloader/helper/mock/MockSavedStateRegistryOwner.kt @@ -6,11 +6,9 @@ import androidx.savedstate.SavedStateRegistryOwner import org.mockito.kotlin.mock class MockSavedStateRegistryOwner( - private val lifecycle: Lifecycle = MockLifecycle(), + override val lifecycle: Lifecycle = MockLifecycle(), private val mockSavedStateRegistry: SavedStateRegistry = mock() ) : SavedStateRegistryOwner { - override fun getLifecycle(): Lifecycle = lifecycle - - override fun getSavedStateRegistry(): SavedStateRegistry = mockSavedStateRegistry + override val savedStateRegistry: SavedStateRegistry = mockSavedStateRegistry } \ No newline at end of file diff --git a/build.gradle b/build.gradle index cbf871d..8ab5645 100644 --- a/build.gradle +++ b/build.gradle @@ -1,13 +1,13 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = "1.6.20" + ext.kotlin_version = '2.1.21' repositories { mavenCentral() google() maven { url "https://plugins.gradle.org/m2/" } } dependencies { - classpath 'com.android.tools.build:gradle:7.1.3' + classpath 'com.android.tools.build:gradle:8.7.3' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong diff --git a/gradle.properties b/gradle.properties index 98bed16..8145fa7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,4 +18,7 @@ android.useAndroidX=true # Automatically convert third-party libraries to use AndroidX android.enableJetifier=true # Kotlin code style for this project: "official" or "obsolete": -kotlin.code.style=official \ No newline at end of file +kotlin.code.style=official +android.defaults.buildfeatures.buildconfig=true +android.nonTransitiveRClass=false +android.nonFinalResIds=false \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 804c205..9496b9d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Thu Jan 27 21:44:07 EET 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME