diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 56120ebe..877c0e80 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -7,10 +7,10 @@ plugins { } android { - namespace = "dev.meloda.fast" + namespace = "dev.meloda.fastvk" defaultConfig { - applicationId = "dev.meloda.fast" + applicationId = "dev.meloda.fastvk" versionCode = libs.versions.versionCode.get().toInt() versionName = libs.versions.versionName.get() diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d57ff13c..5a3a7bf3 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,7 +22,7 @@ tools:targetApi="tiramisu"> @@ -38,13 +38,13 @@ diff --git a/app/src/main/kotlin/dev/meloda/fast/MainViewModel.kt b/app/src/main/kotlin/dev/meloda/fast/MainViewModel.kt index 8670d011..be50f87b 100644 --- a/app/src/main/kotlin/dev/meloda/fast/MainViewModel.kt +++ b/app/src/main/kotlin/dev/meloda/fast/MainViewModel.kt @@ -86,6 +86,8 @@ class MainViewModelImpl( BaseError.SessionExpired -> { isNeedToReplaceWithAuth.update { true } } + + is BaseError.SimpleError -> Unit // TODO: 21-Mar-25, Danil Nikolaev: show error in ui } } diff --git a/app/src/main/kotlin/dev/meloda/fast/service/OnlineService.kt b/app/src/main/kotlin/dev/meloda/fast/service/OnlineService.kt index 98556cae..354a1de4 100644 --- a/app/src/main/kotlin/dev/meloda/fast/service/OnlineService.kt +++ b/app/src/main/kotlin/dev/meloda/fast/service/OnlineService.kt @@ -95,11 +95,6 @@ class OnlineService : Service() { }.also { coroutine -> coroutine.invokeOnCompletion { onlineJob = null } } } - override fun onLowMemory() { - Log.d(STATE_TAG, "onLowMemory") - super.onLowMemory() - } - override fun onDestroy() { Log.d(STATE_TAG, "onDestroy") diff --git a/core/common/src/main/kotlin/dev/meloda/fast/common/util/AndroidUtils.kt b/core/common/src/main/kotlin/dev/meloda/fast/common/util/AndroidUtils.kt index 0f684a77..c88f3019 100644 --- a/core/common/src/main/kotlin/dev/meloda/fast/common/util/AndroidUtils.kt +++ b/core/common/src/main/kotlin/dev/meloda/fast/common/util/AndroidUtils.kt @@ -86,7 +86,7 @@ object AndroidUtils { action = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { Settings.ACTION_SECURITY_SETTINGS } else { - data = Uri.parse("package:dev.meloda.fast") + data = Uri.parse("package:dev.meloda.fastvk") Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES } }) diff --git a/core/model/src/main/kotlin/dev/meloda/fast/model/BaseError.kt b/core/model/src/main/kotlin/dev/meloda/fast/model/BaseError.kt index b2a8cb64..86cc6e2f 100644 --- a/core/model/src/main/kotlin/dev/meloda/fast/model/BaseError.kt +++ b/core/model/src/main/kotlin/dev/meloda/fast/model/BaseError.kt @@ -6,4 +6,6 @@ import androidx.compose.runtime.Immutable sealed class BaseError { data object SessionExpired : BaseError() + + data class SimpleError(val message: String) : BaseError() } diff --git a/core/model/src/main/kotlin/dev/meloda/fast/model/api/data/VkVideoData.kt b/core/model/src/main/kotlin/dev/meloda/fast/model/api/data/VkVideoData.kt index 8e9f3d5d..01bbfa3b 100644 --- a/core/model/src/main/kotlin/dev/meloda/fast/model/api/data/VkVideoData.kt +++ b/core/model/src/main/kotlin/dev/meloda/fast/model/api/data/VkVideoData.kt @@ -1,7 +1,7 @@ package dev.meloda.fast.model.api.data -import dev.meloda.fast.model.api.domain.VkVideoDomain import com.squareup.moshi.JsonClass +import dev.meloda.fast.model.api.domain.VkVideoDomain @JsonClass(generateAdapter = true) data class VkVideoData( @@ -12,7 +12,7 @@ data class VkVideoData( val duration: Int, val date: Int, val comments: Int?, - val description: String, + val description: String?, val player: String?, val added: Int?, val type: String, @@ -20,9 +20,9 @@ data class VkVideoData( val access_key: String?, val owner_id: Int, val is_favorite: Boolean?, - val image: List, + val image: List?, val first_frame: List?, - val files: File?, + val files: File? ) : VkAttachmentData { @JsonClass(generateAdapter = true) @@ -67,7 +67,7 @@ data class VkVideoData( fun toDomain() = VkVideoDomain( id = id, ownerId = owner_id, - images = image.map { it.asVideoImage() }, + images = image.orEmpty().map { it.asVideoImage() }, firstFrames = first_frame, accessKey = access_key, title = title diff --git a/core/network/src/main/kotlin/dev/meloda/fast/network/ResponseConverterFactory.kt b/core/network/src/main/kotlin/dev/meloda/fast/network/ResponseConverterFactory.kt index d4af413e..3168077d 100644 --- a/core/network/src/main/kotlin/dev/meloda/fast/network/ResponseConverterFactory.kt +++ b/core/network/src/main/kotlin/dev/meloda/fast/network/ResponseConverterFactory.kt @@ -46,8 +46,13 @@ class ResponseConverterFactory(private val converter: JsonConverter) : Converter return successModel }, onFailure = { failure -> - if(failure is JsonDataException) { - throw failure + if (failure is JsonDataException) { + throw ApiException( + RestApiError( + errorCode = -1, + errorMsg = failure.message.orEmpty() + ) + ) } val isUnit = successType == Unit::class.java diff --git a/core/network/src/main/kotlin/dev/meloda/fast/network/VkErrorCode.kt b/core/network/src/main/kotlin/dev/meloda/fast/network/VkErrorCode.kt index 42aaae91..b15d2734 100644 --- a/core/network/src/main/kotlin/dev/meloda/fast/network/VkErrorCode.kt +++ b/core/network/src/main/kotlin/dev/meloda/fast/network/VkErrorCode.kt @@ -1,6 +1,7 @@ package dev.meloda.fast.network enum class VkErrorCode(val code: Int) { + WTF(-1), UNKNOWN_ERROR(1), APP_DISABLED(2), UNKNOWN_METHOD(3), diff --git a/core/network/src/main/kotlin/dev/meloda/fast/network/di/NetworkModule.kt b/core/network/src/main/kotlin/dev/meloda/fast/network/di/NetworkModule.kt index cb608c13..936671dc 100644 --- a/core/network/src/main/kotlin/dev/meloda/fast/network/di/NetworkModule.kt +++ b/core/network/src/main/kotlin/dev/meloda/fast/network/di/NetworkModule.kt @@ -6,7 +6,6 @@ import com.slack.eithernet.integration.retrofit.ApiResultCallAdapterFactory import com.slack.eithernet.integration.retrofit.ApiResultConverterFactory import com.squareup.moshi.Moshi import dev.meloda.fast.common.AppConstants -import dev.meloda.fast.common.model.LogLevel import dev.meloda.fast.datastore.AppSettings import dev.meloda.fast.network.JsonConverter import dev.meloda.fast.network.MoshiConverter @@ -57,12 +56,8 @@ val networkModule = module { .followSslRedirects(true) .addInterceptor( HttpLoggingInterceptor().apply { - level = when (AppSettings.Debug.networkLogLevel) { - LogLevel.NONE -> HttpLoggingInterceptor.Level.NONE - LogLevel.BASIC -> HttpLoggingInterceptor.Level.BASIC - LogLevel.HEADERS -> HttpLoggingInterceptor.Level.HEADERS - LogLevel.BODY -> HttpLoggingInterceptor.Level.BODY - } + level = + HttpLoggingInterceptor.Level.entries[AppSettings.Debug.networkLogLevel.ordinal] } ) .build() diff --git a/core/ui/src/main/kotlin/dev/meloda/fast/ui/components/ErrorView.kt b/core/ui/src/main/kotlin/dev/meloda/fast/ui/components/ErrorView.kt index 1d43f793..32b3db31 100644 --- a/core/ui/src/main/kotlin/dev/meloda/fast/ui/components/ErrorView.kt +++ b/core/ui/src/main/kotlin/dev/meloda/fast/ui/components/ErrorView.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding import androidx.compose.material3.Button import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -22,7 +23,9 @@ fun ErrorView( onButtonClick: (() -> Unit)? = null, ) { Column( - modifier = modifier.fillMaxSize(), + modifier = modifier + .fillMaxSize() + .padding(horizontal = 16.dp), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { diff --git a/core/ui/src/main/res/values-ru/strings.xml b/core/ui/src/main/res/values-ru/strings.xml index b099304a..ec25a7be 100644 --- a/core/ui/src/main/res/values-ru/strings.xml +++ b/core/ui/src/main/res/values-ru/strings.xml @@ -133,6 +133,7 @@ Ваша история Динамические цвета Цвета для приложения будут извлечены из ваших обоев на главном экране + Использовать системный шрифт Язык приложения Текущий: %1$s Системный @@ -177,6 +178,7 @@ Основное Использовать имена контактов Приложение будет использовать доступные имена контактов для пользователей + Включить тактильную отдачу Внешний вид Многострочные заголовки и сообщения Заголовок чата и текст сообщения смогут занимать несколько строчек @@ -184,9 +186,11 @@ Fast текст LongPoll в фоне Ваши сообщения будут обновляться, даже если приложение находится в фоне + Использовать анимации везде, где возможно Активность Быть «в сети» Статус «в сети» будет отправляться каждые 5 минут + Экспериментальные - ОЧЕНЬ нестабильные Отладка Отключить Приложение не сможет обновлять сообщения в фоне без доступа к уведомлениям @@ -198,4 +202,14 @@ Уведомления без категории Сервис обновления сообщений Уведомления сервиса обновлений сообщений + Показывать кнопку эмоджи + Показывать кнопку эмоджи на панели чата + Показывать время в сервисных сообщениях + Использовать размытие + Добавлять размытие везде, где возможно.\\nРаботает только с 12 версии Android + Больше анимаций + Подтверждение + Вы уверены? Процесс ввода капчи будет отменён + Вы уверены? Процесс ввода кода-подтверждения будет отменён + Авторизоваться diff --git a/core/ui/src/main/res/values/strings.xml b/core/ui/src/main/res/values/strings.xml index a1cbf61e..d5a48e61 100644 --- a/core/ui/src/main/res/values/strings.xml +++ b/core/ui/src/main/res/values/strings.xml @@ -204,6 +204,8 @@ Dynamic colors The colors for the app will be extracted from your home screen wallpaper + Use system font + Application Language Current: %1$s @@ -235,6 +237,9 @@ General Use contact names App will use available contact names for users + Show emoji button + Show emoji button in chat panel + Enable haptic Appearance Multiline titles and messages The title of the conversation and the text of the message can take up multiple lines @@ -242,9 +247,18 @@ Fast text LongPoll in background Your messages will be updating even when app is not on the screen + Show time in action messages + Use blur + Adds blur wherever possible.\nWorks on android 12 and newer + More animations + Use animations wherever possible + Activity Send online status Online status will be sent every five minutes + + Experimental - VERY unstable + Debug The app won\'t be able to update messages in the background without access to notifications Disable diff --git a/feature/chatmaterials/src/main/kotlin/dev/meloda/fast/chatmaterials/presentation/ChatMaterialsScreen.kt b/feature/chatmaterials/src/main/kotlin/dev/meloda/fast/chatmaterials/presentation/ChatMaterialsScreen.kt index bc73daef..f7acddf4 100644 --- a/feature/chatmaterials/src/main/kotlin/dev/meloda/fast/chatmaterials/presentation/ChatMaterialsScreen.kt +++ b/feature/chatmaterials/src/main/kotlin/dev/meloda/fast/chatmaterials/presentation/ChatMaterialsScreen.kt @@ -62,8 +62,8 @@ import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import dev.chrisbanes.haze.HazeState -import dev.chrisbanes.haze.haze -import dev.chrisbanes.haze.hazeChild +import dev.chrisbanes.haze.hazeEffect +import dev.chrisbanes.haze.hazeSource import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi import dev.chrisbanes.haze.materials.HazeMaterials import dev.meloda.fast.chatmaterials.ChatMaterialsViewModel @@ -137,7 +137,7 @@ fun ChatMaterialsScreen( ) } - val titles = listOf("Photos", "Videos", "Audios", "Files", "Links") + val titles = listOf("Photos", "Videos", "Audios")//, "Files", "Links") val listState = rememberLazyListState() val gridState = rememberLazyGridState() @@ -179,7 +179,7 @@ fun ChatMaterialsScreen( modifier = Modifier .then( if (currentTheme.enableBlur) { - Modifier.hazeChild( + Modifier.hazeEffect( state = hazeState, style = hazeStyle ) @@ -311,7 +311,7 @@ fun ChatMaterialsScreen( modifier = Modifier .then( if (currentTheme.enableBlur) { - Modifier.haze(state = hazeState) + Modifier.hazeSource(state = hazeState) } else { Modifier } @@ -346,7 +346,7 @@ fun ChatMaterialsScreen( modifier = Modifier .then( if (currentTheme.enableBlur) { - Modifier.haze(state = hazeState) + Modifier.hazeSource(state = hazeState) } else { Modifier } diff --git a/feature/conversations/src/main/kotlin/dev/meloda/fast/conversations/ConversationsViewModel.kt b/feature/conversations/src/main/kotlin/dev/meloda/fast/conversations/ConversationsViewModel.kt index 35a4fad3..5806134d 100644 --- a/feature/conversations/src/main/kotlin/dev/meloda/fast/conversations/ConversationsViewModel.kt +++ b/feature/conversations/src/main/kotlin/dev/meloda/fast/conversations/ConversationsViewModel.kt @@ -134,6 +134,7 @@ class ConversationsViewModelImpl( } override fun onRefresh() { + baseError.setValue { null } loadConversations(offset = 0) } @@ -273,17 +274,7 @@ class ConversationsViewModelImpl( conversationsUseCase.getConversations(count = LOAD_COUNT, offset = offset) .listenValue(viewModelScope) { state -> state.processState( - error = { error -> - if (error is State.Error.ApiError) { - when (error.errorCode) { - VkErrorCode.USER_AUTHORIZATION_FAILED -> { - baseError.setValue { BaseError.SessionExpired } - } - - else -> Unit - } - } - }, + error = ::handleError, success = { response -> val itemsCountSufficient = response.size == LOAD_COUNT canPaginate.setValue { itemsCountSufficient } @@ -339,6 +330,40 @@ class ConversationsViewModelImpl( } } + private fun handleError(error: State.Error) { + when (error) { + is State.Error.ApiError -> { + when (error.errorCode) { + VkErrorCode.USER_AUTHORIZATION_FAILED -> { + baseError.setValue { BaseError.SessionExpired } + } + + else -> { + baseError.setValue { + BaseError.SimpleError(message = error.errorMessage) + } + } + } + } + State.Error.ConnectionError -> { + baseError.setValue { + BaseError.SimpleError(message = "Connection error") + } + } + State.Error.InternalError -> { + baseError.setValue { + BaseError.SimpleError(message = "Internal error") + } + } + State.Error.UnknownError -> { + baseError.setValue { + BaseError.SimpleError(message = "Unknown error") + } + } + else -> Unit + } + } + private fun deleteConversation(peerId: Int) { conversationsUseCase.delete(peerId).listenValue(viewModelScope) { state -> state.processState( diff --git a/feature/conversations/src/main/kotlin/dev/meloda/fast/conversations/presentation/ConversationsScreen.kt b/feature/conversations/src/main/kotlin/dev/meloda/fast/conversations/presentation/ConversationsScreen.kt index cf19746b..e7c42f1a 100644 --- a/feature/conversations/src/main/kotlin/dev/meloda/fast/conversations/presentation/ConversationsScreen.kt +++ b/feature/conversations/src/main/kotlin/dev/meloda/fast/conversations/presentation/ConversationsScreen.kt @@ -339,12 +339,24 @@ fun ConversationsScreen( } ) { padding -> when { - baseError is BaseError.SessionExpired -> { - ErrorView( - text = "Session expired", - buttonText = "Log out", - onButtonClick = onSessionExpiredLogOutButtonClicked - ) + baseError != null -> { + when (baseError) { + is BaseError.SessionExpired -> { + ErrorView( + text = "Session expired", + buttonText = "Log out", + onButtonClick = onSessionExpiredLogOutButtonClicked + ) + } + + is BaseError.SimpleError -> { + ErrorView( + text = baseError.message, + buttonText = "Try again", + onButtonClick = onRefresh + ) + } + } } screenState.isLoading && screenState.conversations.isEmpty() -> FullScreenLoader() diff --git a/feature/settings/src/main/kotlin/dev/meloda/fast/settings/SettingsViewModel.kt b/feature/settings/src/main/kotlin/dev/meloda/fast/settings/SettingsViewModel.kt index 00f3028b..b94909bb 100644 --- a/feature/settings/src/main/kotlin/dev/meloda/fast/settings/SettingsViewModel.kt +++ b/feature/settings/src/main/kotlin/dev/meloda/fast/settings/SettingsViewModel.kt @@ -278,14 +278,14 @@ class SettingsViewModelImpl( ) val generalShowEmojiButton = SettingsItem.Switch( key = SettingsKeys.KEY_SHOW_EMOJI_BUTTON, - title = UiText.Simple("Show emoji button"), - text = UiText.Simple("Show emoji button in chat panel"), + title = UiText.Resource(UiR.string.settings_general_show_emoji_button_title), + text = UiText.Resource(UiR.string.settings_general_show_emoji_button_summary), defaultValue = SettingsKeys.DEFAULT_VALUE_KEY_SHOW_EMOJI_BUTTON ) val generalEnableHaptic = SettingsItem.Switch( key = SettingsKeys.KEY_ENABLE_HAPTIC, defaultValue = SettingsKeys.DEFAULT_ENABLE_HAPTIC, - title = UiText.Simple("Enable haptic") + title = UiText.Resource(UiR.string.settings_general_enable_haptic_title) ) val appearanceTitle = SettingsItem.Title( @@ -342,7 +342,7 @@ class SettingsViewModelImpl( val appearanceUseSystemFont = SettingsItem.Switch( key = SettingsKeys.KEY_USE_SYSTEM_FONT, defaultValue = SettingsKeys.DEFAULT_USE_SYSTEM_FONT, - title = UiText.Simple("Use system font") + title = UiText.Resource(UiR.string.settings_appearance_use_system_font_title) ) val appearanceLanguage = SettingsItem.TitleText( key = SettingsKeys.KEY_APPEARANCE_LANGUAGE, @@ -379,7 +379,7 @@ class SettingsViewModelImpl( val experimentalTitle = SettingsItem.Title( key = "experimental", - title = UiText.Simple("Experimental - VERY unstable") + title = UiText.Resource(UiR.string.settings_experimental_title) ) val experimentalLongPollBackground = SettingsItem.Switch( key = SettingsKeys.KEY_LONG_POLL_IN_BACKGROUND, @@ -390,19 +390,20 @@ class SettingsViewModelImpl( val experimentalShowTimeInActionMessages = SettingsItem.Switch( key = SettingsKeys.KEY_SHOW_TIME_IN_ACTION_MESSAGES, defaultValue = SettingsKeys.DEFAULT_SHOW_TIME_IN_ACTION_MESSAGES, - title = UiText.Simple("Show time in action messages") + title = UiText.Resource(UiR.string.settings_features_show_time_in_action_messages_title) ) val experimentalUseBlur = SettingsItem.Switch( key = SettingsKeys.KEY_USE_BLUR, defaultValue = SettingsKeys.DEFAULT_USE_BLUR, - title = UiText.Simple("Use blur"), - text = UiText.Simple("Adds blur wherever possible\nWorks on android 12 and newer"), + title = UiText.Resource(UiR.string.settings_experimental_use_blur_title), + text = UiText.Resource(UiR.string.settings_experimental_use_blur_summary), + isVisible = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ) val enableAnimations = SettingsItem.Switch( key = SettingsKeys.KEY_MORE_ANIMATIONS, defaultValue = SettingsKeys.DEFAULT_MORE_ANIMATIONS, - title = UiText.Simple("More animations"), - text = UiText.Simple("Use animations wherever possible") + title = UiText.Resource(UiR.string.settings_experimental_more_animations_title), + text = UiText.Resource(UiR.string.settings_experimental_more_animations_summary) ) val debugTitle = SettingsItem.Title( diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 16d75de2..643477f7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,22 +5,22 @@ compileSdk = "35" versionCode = "9" versionName = "0.1.6" -agp = "8.7.3" +agp = "8.9.0" converterMoshi = "2.11.0" eithernet = "2.0.0" -haze = "1.1.1" -kotlin = "2.1.0" -ksp = "2.1.0-1.0.29" +haze = "1.5.1" +kotlin = "2.1.10" +ksp = "2.1.10-1.0.31" -compose-bom = "2024.12.01" -koin = "4.0.1" +compose-bom = "2025.03.00" +koin = "4.0.2" -accompanist = "0.37.0" +accompanist = "0.37.2" coil = "2.7.0" coroutines = "1.10.1" junit = "4.13.2" chucker = "4.1.0" -guava = "33.4.0-jre" +guava = "33.4.5-jre" lifecycle = "2.8.7" core-ktx = "1.15.0" material = "1.12.0" @@ -33,10 +33,10 @@ nanokt = "1.2.0" junitVersion = "1.2.1" espressoCore = "3.6.1" appcompat = "1.7.0" -androidx-navigation = "2.8.5" -serialization = "1.7.3" +androidx-navigation = "2.8.9" +serialization = "1.8.0" rebugger = "1.0.0-rc03" -moduleGraph = "2.7.1" +moduleGraph = "2.8.0" [libraries] accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanist" } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 41d9927a..2c352119 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 f22a7691..cea7a793 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ -#Mon Oct 28 18:41:43 MSK 2024 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 1b6c7873..f5feea6d 100644 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,13 +82,12 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,22 +134,29 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -193,11 +201,15 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ @@ -205,6 +217,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradlew.bat b/gradlew.bat index 107acd32..9d21a218 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,8 +13,10 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +27,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +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 @@ -56,11 +59,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 @@ -75,13 +78,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal