diff --git a/.github/workflows/on_push_development.yml b/.github/workflows/on_push_development.yml index 6233bc5d..cca70992 100644 --- a/.github/workflows/on_push_development.yml +++ b/.github/workflows/on_push_development.yml @@ -20,7 +20,7 @@ jobs: uses: actions/checkout@v4.1.1 - name: set up JDK 17 - uses: actions/setup-java@v4.1.0 + uses: actions/setup-java@v4.2.1 with: distribution: 'zulu' # See 'Supported distributions' for available options java-version: '17' diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 8d81632f..fe63bb67 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 8ec0f4be..07edca29 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -21,7 +21,7 @@ android { defaultConfig { applicationId = "com.joeloewi.croissant" - versionCode = 59 + versionCode = 60 versionName = "1.3.0" targetSdk = 34 @@ -68,9 +68,9 @@ baselineProfile { } dependencies { - implementation(project(":data")) - implementation(project(":domain")) - baselineProfile(project(":baselineprofile")) + implementation(projects.data) + implementation(projects.domain) + baselineProfile(projects.baselineprofile) implementation(libs.androidx.core.ktx) implementation(libs.android.material) @@ -111,7 +111,6 @@ dependencies { implementation(libs.accompanist.webview) implementation(libs.accompanist.pager.indicators) implementation(libs.accompanist.swiperefresh) - implementation(libs.accompanist.themeadapter.material3) implementation(libs.accompanist.navigation.material) //work diff --git a/app/src/main/kotlin/com/joeloewi/croissant/ui/navigation/main/attendances/screen/AttendancesScreen.kt b/app/src/main/kotlin/com/joeloewi/croissant/ui/navigation/main/attendances/screen/AttendancesScreen.kt index 941a2ba8..4ba67359 100644 --- a/app/src/main/kotlin/com/joeloewi/croissant/ui/navigation/main/attendances/screen/AttendancesScreen.kt +++ b/app/src/main/kotlin/com/joeloewi/croissant/ui/navigation/main/attendances/screen/AttendancesScreen.kt @@ -45,9 +45,9 @@ import androidx.compose.material3.rememberSwipeToDismissBoxState import androidx.compose.material3.surfaceColorAtElevation import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue +import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.snapshotFlow @@ -58,6 +58,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.scale import androidx.compose.ui.draw.shadow import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.buildAnnotatedString @@ -65,12 +66,14 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.core.os.bundleOf import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.flowWithLifecycle import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems import androidx.paging.compose.itemKey import androidx.work.ExistingWorkPolicy import androidx.work.WorkInfo import androidx.work.WorkManager +import androidx.work.WorkQuery import coil.compose.AsyncImage import coil.request.ImageRequest import com.google.firebase.Firebase @@ -252,12 +255,14 @@ fun AttendanceWithGamesItem( { attendance -> Firebase.analytics.logEvent("instant_attend_click", bundleOf()) - val oneTimeWork = - AttendCheckInEventWorker.buildOneTimeWork(attendanceId = attendance.id) + val oneTimeWork = AttendCheckInEventWorker.buildOneTimeWork( + attendanceId = attendance.id, + isInstantAttendance = true + ) WorkManager.getInstance(context).beginUniqueWork( attendance.oneTimeAttendCheckInEventWorkerName.toString(), - ExistingWorkPolicy.APPEND_OR_REPLACE, + ExistingWorkPolicy.REPLACE, oneTimeWork ).enqueue() @@ -401,14 +406,24 @@ private fun DismissContent( }, trailingContent = { val context = LocalContext.current + val lifecycleOwner = LocalLifecycleOwner.current val workerName = attendanceWithGames().value.attendance.oneTimeAttendCheckInEventWorkerName.toString() - val isRunning by remember(context, workerName) { + val isRunningFlow = remember(context, workerName) { WorkManager.getInstance(context) - .getWorkInfosForUniqueWorkFlow(workerName) - .map { list -> list.any { it.state == WorkInfo.State.RUNNING } } + .getWorkInfosFlow( + WorkQuery.Builder + .fromUniqueWorkNames(listOf(workerName)) + .addStates(listOf(WorkInfo.State.RUNNING)) + .build() + ) + .catch { } + .map { it.isNotEmpty() } .flowOn(Dispatchers.IO) - }.collectAsState(initial = false) + } + val isRunning by produceState(initialValue = false) { + isRunningFlow.flowWithLifecycle(lifecycleOwner.lifecycle).collect { value = it } + } IconButton( enabled = !isRunning, diff --git a/app/src/main/kotlin/com/joeloewi/croissant/util/NotificationGenerator.kt b/app/src/main/kotlin/com/joeloewi/croissant/util/NotificationGenerator.kt index eb9601f0..a58cbb8a 100644 --- a/app/src/main/kotlin/com/joeloewi/croissant/util/NotificationGenerator.kt +++ b/app/src/main/kotlin/com/joeloewi/croissant/util/NotificationGenerator.kt @@ -8,7 +8,6 @@ import android.content.Intent import android.content.pm.PackageManager import android.content.pm.ServiceInfo import android.net.Uri -import android.os.Build import androidx.core.app.NotificationChannelCompat import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat @@ -216,21 +215,15 @@ class NotificationGenerator( .setContentTitle(context.getString(R.string.attendance_foreground_notification_title)) .setContentText(context.getString(R.string.wait_for_a_moment)) .setSmallIcon(R.drawable.ic_baseline_bakery_dining_24) - .setForegroundServiceBehavior(Notification.FOREGROUND_SERVICE_IMMEDIATE) + .setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE) .setCategory(NotificationCompat.CATEGORY_SERVICE) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .build() .run { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - return@run ForegroundInfo( - notificationId, - this, - ServiceInfo.FOREGROUND_SERVICE_TYPE_SHORT_SERVICE - ) - } - return@run ForegroundInfo( + ForegroundInfo( notificationId, - this + this, + ServiceInfo.FOREGROUND_SERVICE_TYPE_SHORT_SERVICE ) } diff --git a/app/src/main/kotlin/com/joeloewi/croissant/worker/AttendCheckInEventWorker.kt b/app/src/main/kotlin/com/joeloewi/croissant/worker/AttendCheckInEventWorker.kt index 144ad298..4ed3f145 100644 --- a/app/src/main/kotlin/com/joeloewi/croissant/worker/AttendCheckInEventWorker.kt +++ b/app/src/main/kotlin/com/joeloewi/croissant/worker/AttendCheckInEventWorker.kt @@ -61,6 +61,7 @@ class AttendCheckInEventWorker @AssistedInject constructor( private val _firstTriggeredTimestamp by lazy { inputData.getLong(FIRST_TRIGGERED_TIMESTAMP, Instant.now().toEpochMilli()) } + private val _isInstantAttendance by lazy { inputData.getBoolean(IS_INSTANT_ATTENDANCE, false) } override suspend fun getForegroundInfo(): ForegroundInfo = notificationGenerator.createForegroundInfo(_attendanceId.toInt()) @@ -205,21 +206,41 @@ class AttendCheckInEventWorker @AssistedInject constructor( is HoYoLABUnsuccessfulResponseException -> { when (val retCode = HoYoLABRetCode.findByCode(cause.retCode)) { HoYoLABRetCode.TooManyRequests, HoYoLABRetCode.TooManyRequestsGenshinImpact -> { - //do not make log, not to skip this game when retry - - notificationGenerator.createAttendanceRetryScheduledNotification( - nickname = attendanceWithGames.attendance.nickname, - contentText = context.getString(R.string.attendance_retry_too_many_requests_error) - ).let { notification -> - notificationGenerator.safeNotify( - UUID.randomUUID().toString(), - game.type.gameId, - notification - ) + if (_isInstantAttendance) { + //make log and do not retry if this work was enqueued by clicking instant attendance + + addFailureLog(_attendanceId, game.type, cause) + + createUnsuccessfulAttendanceNotification( + nickname = attendanceWithGames.attendance.nickname, + hoYoLABGame = game.type, + region = game.region, + hoYoLABUnsuccessfulResponseException = cause + ).let { notification -> + notificationGenerator.safeNotify( + UUID.randomUUID().toString(), + game.type.gameId, + notification + ) + } + + Result.success() + } else { + //do not make log, not to skip this game when retry + notificationGenerator.createAttendanceRetryScheduledNotification( + nickname = attendanceWithGames.attendance.nickname, + contentText = context.getString(R.string.attendance_retry_too_many_requests_error) + ).let { notification -> + notificationGenerator.safeNotify( + UUID.randomUUID().toString(), + game.type.gameId, + notification + ) + } + + //good to retry + Result.retry() } - - //good to retry - Result.retry() } else -> { @@ -238,11 +259,11 @@ class AttendCheckInEventWorker @AssistedInject constructor( ) } - if (!listOf( + if (retCode !in listOf( HoYoLABRetCode.AlreadyCheckedIn, HoYoLABRetCode.CharacterNotExists, HoYoLABRetCode.LoginFailed - ).contains(retCode) + ) ) { //we don't know which error was occurred //record this error for monitoring @@ -265,21 +286,40 @@ class AttendCheckInEventWorker @AssistedInject constructor( } else -> { - //do not make log, not to pass this game when retry - notificationGenerator.createAttendanceRetryScheduledNotification( - nickname = attendanceWithGames.attendance.nickname - ).let { notification -> - notificationGenerator.safeNotify( - UUID.randomUUID().toString(), - game.type.gameId, - notification - ) - } + if (_isInstantAttendance) { + //make log and do not retry if this work was enqueued by clicking instant attendance + addFailureLog(_attendanceId, game.type, cause) + + notificationGenerator.createUnsuccessfulAttendanceNotification( + nickname = attendanceWithGames.attendance.nickname, + hoYoLABGame = game.type, + attendanceId = _attendanceId + ).let { notification -> + notificationGenerator.safeNotify( + UUID.randomUUID().toString(), + game.type.gameId, + notification + ) + } - //these errors are not hoyolab server's errors, but networks errors, generally - //do retry - Firebase.crashlytics.log("runAttemptCount: $runAttemptCount") - Result.retry() + Result.success() + } else { + //do not make log, not to pass this game when retry + notificationGenerator.createAttendanceRetryScheduledNotification( + nickname = attendanceWithGames.attendance.nickname + ).let { notification -> + notificationGenerator.safeNotify( + UUID.randomUUID().toString(), + game.type.gameId, + notification + ) + } + + //these errors are not hoyolab server's errors, but networks errors, generally + //do retry + Firebase.crashlytics.log("runAttemptCount: $runAttemptCount") + Result.retry() + } } } } @@ -287,12 +327,13 @@ class AttendCheckInEventWorker @AssistedInject constructor( } }.fold( onSuccess = { results -> - if (results.contains(Result.retry())) { + //do not retry if this work was enqueued by clicking instant attendance + if (results.contains(Result.retry()) && !_isInstantAttendance) { return@withContext Result.retry() } runAttemptCount.takeIf { count -> count > 0 }?.let { - Firebase.crashlytics.log("success after run attempts: $it") + Firebase.crashlytics.log("succeed after run attempts: $it") } Result.success() @@ -313,10 +354,12 @@ class AttendCheckInEventWorker @AssistedInject constructor( companion object { const val ATTENDANCE_ID = "attendanceId" const val FIRST_TRIGGERED_TIMESTAMP = "triggeredTimestamp" + const val IS_INSTANT_ATTENDANCE = "isInstantAttendance" fun buildOneTimeWork( attendanceId: Long, triggeredTimestamp: Long = Instant.now().toEpochMilli(), + isInstantAttendance: Boolean = false, constraints: Constraints = Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) .build() @@ -324,7 +367,8 @@ class AttendCheckInEventWorker @AssistedInject constructor( .setInputData( workDataOf( ATTENDANCE_ID to attendanceId, - FIRST_TRIGGERED_TIMESTAMP to triggeredTimestamp + FIRST_TRIGGERED_TIMESTAMP to triggeredTimestamp, + IS_INSTANT_ATTENDANCE to isInstantAttendance ) ) .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) diff --git a/app/src/main/kotlin/com/joeloewi/croissant/worker/CheckSessionWorker.kt b/app/src/main/kotlin/com/joeloewi/croissant/worker/CheckSessionWorker.kt index f0fc272b..e06eb3dc 100644 --- a/app/src/main/kotlin/com/joeloewi/croissant/worker/CheckSessionWorker.kt +++ b/app/src/main/kotlin/com/joeloewi/croissant/worker/CheckSessionWorker.kt @@ -85,7 +85,7 @@ class CheckSessionWorker @AssistedInject constructor( ) runAttemptCount.takeIf { count -> count > 0 }?.let { - Firebase.crashlytics.log("success after run attempts: $it") + Firebase.crashlytics.log("succeed after run attempts: $it") } Result.success() diff --git a/data/build.gradle.kts b/data/build.gradle.kts index 8a5c373b..1e35e354 100644 --- a/data/build.gradle.kts +++ b/data/build.gradle.kts @@ -21,7 +21,7 @@ android { } dependencies { - implementation(project(":domain")) + implementation(projects.domain) implementation(libs.androidx.core.ktx) implementation(libs.androidx.appcompat) @@ -31,9 +31,12 @@ dependencies { androidTestImplementation(libs.androidx.test.espresso.core) //retrofit2 + implementation(platform(libs.retrofit.bom)) implementation(libs.retrofit) implementation(libs.retrofit.converter.moshi) implementation(libs.retrofit.converter.scalars) + ksp(libs.retrofit.response.type.keeper) + implementation(platform(libs.okhttp3.bom)) implementation(libs.okhttp3.logging.interceptor) implementation(libs.okhttp3.dnsoverhttps) diff --git a/data/consumer-proguard-rules.pro b/data/consumer-proguard-rules.pro index 73c8f35f..63e51592 100644 --- a/data/consumer-proguard-rules.pro +++ b/data/consumer-proguard-rules.pro @@ -1,18 +1 @@ --keep class * extends com.google.protobuf.GeneratedMessageLite { *; } - --dontwarn okhttp3.internal.platform.** --dontwarn org.conscrypt.** --dontwarn org.bouncycastle.** --dontwarn org.openjsse.** - - # With R8 full mode generic signatures are stripped for classes that are not - # kept. Suspend functions are wrapped in continuations where the type argument - # is used. - -keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation - - # R8 full mode strips generic signatures from return types if not kept. - -if interface * { @retrofit2.http.* public *** *(...); } - -keep,allowoptimization,allowshrinking,allowobfuscation class <3> - - # With R8 full mode generic signatures are stripped for classes that are not kept. - -keep,allowobfuscation,allowshrinking class retrofit2.Response \ No newline at end of file +-keep class * extends com.google.protobuf.GeneratedMessageLite { *; } \ No newline at end of file diff --git a/data/src/main/kotlin/com/joeloewi/croissant/data/util/ApiResponseExtensions.kt b/data/src/main/kotlin/com/joeloewi/croissant/data/util/ApiResponseExtensions.kt index c0818121..d6e35ec6 100644 --- a/data/src/main/kotlin/com/joeloewi/croissant/data/util/ApiResponseExtensions.kt +++ b/data/src/main/kotlin/com/joeloewi/croissant/data/util/ApiResponseExtensions.kt @@ -21,7 +21,7 @@ suspend fun runAndRetryWithExponentialBackOff( task = task ) -private tailrec suspend fun retryTask( +internal tailrec suspend fun retryTask( attempt: Int = 1, initialDelay: Int, retryFactor: Float, @@ -46,25 +46,17 @@ private tailrec suspend fun retryTask( return when (val apiResponse = task()) { is ApiResponse.Success -> apiResponse is ApiResponse.Failure -> { - when (apiResponse) { - is ApiResponse.Failure.Error -> { - apiResponse - } - - is ApiResponse.Failure.Exception -> { - if (attempt < maxAttempts) { - retryTask( - attempt = attempt + 1, - initialDelay = initialDelay, - retryFactor = retryFactor, - maxAttempts = maxAttempts, - maxDelayMillis = maxDelayMillis, - task = task - ) - } else { - apiResponse - } - } + if (attempt < maxAttempts) { + retryTask( + attempt = attempt + 1, + initialDelay = initialDelay, + retryFactor = retryFactor, + maxAttempts = maxAttempts, + maxDelayMillis = maxDelayMillis, + task = task + ) + } else { + apiResponse } } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 484fc0ec..1e2a7a1a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,15 +1,15 @@ [versions] accompanist = "0.34.0" androidDesugarJdkLibs = "2.0.4" -androidGradlePlugin = "8.3.0" -androidxActivity = "1.8.2" +androidGradlePlugin = "8.3.2" +androidxActivity = "1.9.0" androidxAppCompat = "1.6.1" androidxCompose = "1.3.1" -androidxComposeCompiler = "1.5.10" +androidxComposeCompiler = "1.5.12" androidxComposeMaterial3 = "1.0.1" -androidxCore = "1.12.0" +androidxCore = "1.13.0" androidxCoreSplashscreen = "1.0.1" -androidxDataStore = "1.0.0" +androidxDataStore = "1.1.0" androidxEspresso = "3.5.1" androidxHiltNavigationCompose = "1.2.0" androidxLifecycle = "2.7.0" @@ -23,45 +23,45 @@ androidxTracing = "1.1.0" androidxUiAutomator = "2.2.0" androidxWork = "2.9.0" androidxWebkit = "1.10.0" -androidxComposeBom = "2024.02.02" +androidxComposeBom = "2024.04.01" androidxPagingCommon = "3.2.1" androidxPagingCompose = "3.2.1" androidMaterial = "1.11.0" coil = "2.6.0" -hilt = "2.51" +hilt = "2.51.1" hiltExt = "1.2.0" -kotlin = "1.9.22" +kotlin = "1.9.23" kotlinxCoroutines = "1.8.0" kotlinxCollectionsImmutable = "0.3.7" androidxProfileInstaller = "1.3.1" androidxTestRunner = "1.5.2" androidxTestCore = "1.5.0" -androidxBenchmarkMacroJunit4 = "1.2.3" +androidxBenchmarkMacroJunit4 = "1.2.4" androidxTestRules = "1.5.0" -protobuf = "3.25.3" +protobuf = "4.26.1" protobufPlugin = "0.9.4" -retrofit = "2.9.0" +retrofit = "2.11.0" room = "2.6.1" moshi = "1.15.1" androidPlayAppUpdateKtx = "2.1.0" androidPlayReviewKtx = "2.0.1" firebaseCrashlyticsPlugin = "2.9.9" gmsGoogleServicesGradlePlugin = "4.4.1" -firebaseBom = "32.7.4" -leakcanaryAndroid = "2.13" +firebaseBom = "32.8.1" +leakcanaryAndroid = "2.14" okhttp3Bom = "4.12.0" inject = "1" -sandwich = "2.0.5" +sandwich = "2.0.6" jsoup = "1.17.2" junit = "4.13.2" -kspPlugin = "1.9.22-1.0.18" -tts = "2.5.0" +kspPlugin = "1.9.23-1.0.20" +tts = "2.6.0" gmsOssLicensesPlugin = "0.10.6" gmsPlayServicesOssLicenses = "17.0.1" -androidxBaselineProfile = "1.2.3" -androidTools = "31.3.0" +androidxBaselineProfile = "1.2.4" +androidTools = "31.3.2" fornewidPlaceholderMaterial3 = "1.1.2" -kotlinxAtomicfu = "0.23.2" +kotlinxAtomicfu = "0.24.0" [libraries] accompanist-permissions = { group = "com.google.accompanist", name = "accompanist-permissions", version.ref = "accompanist" } @@ -71,7 +71,6 @@ accompanist-systemuicontroller = { group = "com.google.accompanist", name = "acc accompanist-webview = { group = "com.google.accompanist", name = "accompanist-webview", version.ref = "accompanist" } accompanist-pager-indicators = { group = "com.google.accompanist", name = "accompanist-pager-indicators", version.ref = "accompanist" } accompanist-swiperefresh = { group = "com.google.accompanist", name = "accompanist-swiperefresh", version.ref = "accompanist" } -accompanist-themeadapter-material3 = { group = "com.google.accompanist", name = "accompanist-themeadapter-material3", version.ref = "accompanist" } accompanist-navigation-material = { group = "com.google.accompanist", name = "accompanist-navigation-material", version.ref = "accompanist" } gms-google-services-gradlePlugin = { group = "com.google.gms", name = "google-services", version.ref = "gmsGoogleServicesGradlePlugin" } android-desugarJdkLibs = { group = "com.android.tools", name = "desugar_jdk_libs", version.ref = "androidDesugarJdkLibs" } @@ -129,10 +128,12 @@ kotlinx-collections-immutable = { group = "org.jetbrains.kotlinx", name = "kotli protobuf-protoc = { group = "com.google.protobuf", name = "protoc", version.ref = "protobuf" } protobuf-javalite = { group = "com.google.protobuf", name = "protobuf-javalite", version.ref = "protobuf" } protobuf-kotlin-lite = { group = "com.google.protobuf", name = "protobuf-kotlin-lite", version.ref = "protobuf" } -retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" } -retrofit-converter-moshi = { group = "com.squareup.retrofit2", name = "converter-moshi", version.ref = "retrofit" } -retrofit-converter-scalars = { group = "com.squareup.retrofit2", name = "converter-scalars", version.ref = "retrofit" } -okhttp3-dnsoverhttps = { group = "com.squareup.okhttp3", name = "okhttp-dnsoverhttps", version.ref = "okhttp3Bom" } +retrofit-bom = { group = "com.squareup.retrofit2", name = "retrofit-bom", version.ref = "retrofit" } +retrofit = { group = "com.squareup.retrofit2", name = "retrofit" } +retrofit-converter-moshi = { group = "com.squareup.retrofit2", name = "converter-moshi" } +retrofit-converter-scalars = { group = "com.squareup.retrofit2", name = "converter-scalars" } +retrofit-response-type-keeper = { group = "com.squareup.retrofit2", name = "response-type-keeper", version.ref = "retrofit" } +okhttp3-dnsoverhttps = { group = "com.squareup.okhttp3", name = "okhttp-dnsoverhttps" } okhttp3-bom = { group = "com.squareup.okhttp3", name = "okhttp-bom", version.ref = "okhttp3Bom" } okhttp3-logging-interceptor = { group = "com.squareup.okhttp3", name = "logging-interceptor" } room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 943f0cbf..e6441136 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 509c4a29..b82aa23a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 31042a67..1aa94a42 100644 --- a/gradlew +++ b/gradlew @@ -246,4 +246,4 @@ eval "set -- $( tr '\n' ' ' )" '"$@"' -exec "$JAVACMD" "$@" \ No newline at end of file +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 6689b85b..7101f8e4 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -43,11 +43,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail