compileSdk = 36
defaultConfig {
- minSdk 26
+ minSdk 29
targetSdk 36
versionCode 81
versionName "3.20.1"
<body>
<h3><a href="https://www.stoutner.com/privacy-browser-android-3-20-1">3.20.1</a> (Code-Version 81)</h3>
- <p>30. März 2026 - Mindest-API 26, Ziel-API 36</p>
+ <p><a href="https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=commitdiff;h=0b90f3cc8acfeb432aad1f5060f82fd9ca593a50;ds=sidebyside">30. März 2026</a> - Mindest-API 26, Ziel-API 36</p>
<ul>
<li>Fehler der <a href="https://redmine.stoutner.com/issues/1300">F-Droid-Builds</a> durch die Verwendung der Standard-Java-Version behoben.</li>
</ul>
<body>
<h3><a href="https://www.stoutner.com/privacy-browser-android-3-20-1">3.20.1</a> (version code 81)</h3>
- <p>30 March 2026 - minimum API 26, target API 36</p>
+ <p><a href="https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=commitdiff;h=0b90f3cc8acfeb432aad1f5060f82fd9ca593a50;ds=sidebyside">30 March 2026</a> - minimum API 26, target API 36</p>
<ul>
<li>Fix the <a href="https://redmine.stoutner.com/issues/1300">F-Droid builds</a> by using the default Java version.</li>
</ul>
<body>
<h3><a href="https://www.stoutner.com/privacy-browser-android-3-20-1">3.20.1</a> (código de versión 81)</h3>
- <p>30 de marzo de 2026 - API mínimo 26, API objetivo 36</p>
+ <p><a href="https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=commitdiff;h=0b90f3cc8acfeb432aad1f5060f82fd9ca593a50;ds=sidebyside">30 de marzo de 2026</a> - API mínimo 26, API objetivo 36</p>
<ul>
<li>Corregir las <a href="https://redmine.stoutner.com/issues/1300">compilaciones de F-Droid</a> utilizando la versión predeterminada de Java.</li>
</ul>
<body>
<h3><a href="https://www.stoutner.com/privacy-browser-android-3-20-1">3.20.1</a> (version du code 81)</h3>
- <p>30 Mars 2026 - API minimale : 26, API optimale : 36</p>
+ <p><a href="https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=commitdiff;h=0b90f3cc8acfeb432aad1f5060f82fd9ca593a50;ds=sidebyside">30 Mars 2026</a> - API minimale : 26, API optimale : 36</p>
<ul>
<li>Fix the <a href="https://redmine.stoutner.com/issues/1300">F-Droid builds</a> by using the default Java version.</li>
</ul>
<body>
<h3><a href="https://www.stoutner.com/privacy-browser-android-3-20-1">3.20.1</a> (versione codice 81)</h3>
- <p>30 Marzo 2026 - minima API 26, target API 36</p>
+ <p><a href="https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=commitdiff;h=0b90f3cc8acfeb432aad1f5060f82fd9ca593a50;ds=sidebyside">30 Marzo 2026</a> - minima API 26, target API 36</p>
<ul>
<li>Sistemazione della <a href="https://redmine.stoutner.com/issues/1300">compilazione per F-Droid</a> utilizzando la versione base di Java.</li>
</ul>
<body>
<h3><a href="https://www.stoutner.com/privacy-browser-android-3-20-1">3.20.1</a> (código de versão 81)</h3>
- <p>30 de março de 2026 - minimum API 26, target API 36</p>
+ <p><a href="https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=commitdiff;h=0b90f3cc8acfeb432aad1f5060f82fd9ca593a50;ds=sidebyside">30 de março de 2026</a> - minimum API 26, target API 36</p>
<ul>
<li>Fix the <a href="https://redmine.stoutner.com/issues/1300">F-Droid builds</a> by using the default Java version.</li>
</ul>
<body>
<h3><a href="https://www.stoutner.com/privacy-browser-android-3-20-1">3.20.1</a> (код версии 81)</h3>
- <p>30 марта 2026 года - минимальный API 26, целевой API 36</p>
+ <p><a href="https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=commitdiff;h=0b90f3cc8acfeb432aad1f5060f82fd9ca593a50;ds=sidebyside">30 марта 2026 года</a> - минимальный API 26, целевой API 36</p>
<ul>
<li>Исправлены <a href="https://redmine.stoutner.com/issues/1300">сборки для F-Droid</a> с использованием версии Java по умолчанию.</li>
</ul>
<body>
<h3><a href="https://www.stoutner.com/privacy-browser-android-3-20-1">3.20.1</a> (version code 81)</h3>
- <p>30 Mart 2026 - minimum API 26, target API 36</p>
+ <p><a href="https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=commitdiff;h=0b90f3cc8acfeb432aad1f5060f82fd9ca593a50;ds=sidebyside">30 Mart 2026</a> - minimum API 26, target API 36</p>
<ul>
<li>Fix the <a href="https://redmine.stoutner.com/issues/1300">F-Droid builds</a> by using the default Java version.</li>
</ul>
<body>
<h3><a href="https://www.stoutner.com/privacy-browser-android-3-20-1">3.20.1</a> (version code 81)</h3>
- <p>30 March 2026 - 最低支持API 26, 最高支持API 36</p>
+ <p><a href="https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=commitdiff;h=0b90f3cc8acfeb432aad1f5060f82fd9ca593a50;ds=sidebyside">30 March 2026</a> - 最低支持API 26, 最高支持API 36</p>
<ul>
<li>Fix the <a href="https://redmine.stoutner.com/issues/1300">F-Droid builds</a> by using the default Java version.</li>
</ul>
/* SPDX-License-Identifier: GPL-3.0-or-later
- * SPDX-FileCopyrightText: 2018-2025 Soren Stoutner <soren@stoutner.com>
+ * SPDX-FileCopyrightText: 2018-2026 Soren Stoutner <soren@stoutner.com>
*
* This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android/>.
*
import androidx.appcompat.widget.Toolbar
import androidx.cardview.widget.CardView
import androidx.core.content.FileProvider
+import androidx.core.net.toUri
import androidx.preference.PreferenceManager
import com.google.android.material.snackbar.Snackbar
openKeychainInstalled = try {
// If the safe call (`?.`) is null, the Elvis operator (`?"`) returns the following value instead, which is `false`.
packageManager.getPackageInfo("org.sufficientlysecure.keychain", 0).versionName?.isNotEmpty() ?: false
- } catch (exception: PackageManager.NameNotFoundException) {
+ } catch (_: PackageManager.NameNotFoundException) {
// The package is not installed
false
}
// Create an array adapter for the spinner.
val encryptionArrayAdapter = ArrayAdapter.createFromResource(this, R.array.encryption_type, R.layout.spinner_item)
- // Set the drop down view resource on the spinner.
+ // Set the drop-down view resource on the spinner.
encryptionArrayAdapter.setDropDownViewResource(R.layout.spinner_dropdown_items)
// Set the array adapter for the spinner.
NO_ENCRYPTION -> {
try {
// Get an input stream for the file name.
- // A file may be opened directly once the minimum API >= 29. <https://developer.android.com/reference/kotlin/android/content/ContentResolver#openfile>
- val inputStream = contentResolver.openInputStream(Uri.parse(fileNameString))!!
+ val inputStream = contentResolver.openInputStream(fileNameString.toUri())!!
// Import the unencrypted file.
importStatus = importExportDatabaseHelper.importUnencrypted(inputStream, this)
val encryptionPasswordString = encryptionPasswordEditText.text.toString()
// Get an input stream for the file name.
- val inputStream = contentResolver.openInputStream(Uri.parse(fileNameString))!!
+ val inputStream = contentResolver.openInputStream(fileNameString.toUri())!!
// Initialize a salt byte array. Salt protects against rainbow table attacks.
val saltByteArray = ByteArray(32)
// Populate the second part of the encryption password with salt byte array with the salt.
System.arraycopy(saltByteArray, 0, encryptionPasswordWithSaltByteArray, encryptionPasswordByteArray.size, saltByteArray.size)
- // Get a SHA-512 message digest.
+ // Get an SHA-512 message digest.
val messageDigest = MessageDigest.getInstance("SHA-512")
// Hash the salted encryption password. Otherwise, any characters after the 32nd character in the password are ignored.
val hashedEncryptionPasswordWithSaltByteArray = messageDigest.digest(encryptionPasswordWithSaltByteArray)
// Truncate the encryption password byte array to 256 bits (32 bytes).
- val truncatedHashedEncryptionPasswordWithSaltByteArray = Arrays.copyOf(hashedEncryptionPasswordWithSaltByteArray, 32)
+ val truncatedHashedEncryptionPasswordWithSaltByteArray = hashedEncryptionPasswordWithSaltByteArray.copyOf(32)
// Create an AES secret key from the encryption password byte array.
val secretKey = SecretKeySpec(truncatedHashedEncryptionPasswordWithSaltByteArray, "AES")
- // Get a Advanced Encryption Standard, Galois/Counter Mode, No Padding cipher instance. Galois/Counter mode protects against modification of the ciphertext. It doesn't use padding.
+ // Get an Advanced Encryption Standard, Galois/Counter Mode, No Padding cipher instance. Galois/Counter mode protects against modification of the ciphertext. It doesn't use padding.
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
// Set the GCM tag length to be 128 bits (the maximum) and apply the initialization vector.
// Create a private temporary unencrypted import file.
val temporaryUnencryptedImportFile = File.createTempFile("temporary_unencrypted_import_file", null, applicationContext.cacheDir)
- // Create an temporary unencrypted import file output stream.
+ // Create a temporary unencrypted import file output stream.
val temporaryUnencryptedImportFileOutputStream = FileOutputStream(temporaryUnencryptedImportFile)
// Read up to 128 bits (16 bytes) of data from the cipher input stream. `-1` will be returned when the end of the file is reached.
val temporaryPgpEncryptedImportFileOutputStream = FileOutputStream(temporaryPgpEncryptedImportFile)
// Get an input stream for the file name.
- val inputStream = contentResolver.openInputStream(Uri.parse(fileNameString))!!
+ val inputStream = contentResolver.openInputStream(fileNameString.toUri())!!
// Create a transfer byte array.
val transferByteArray = ByteArray(1024)
try {
// Get the export file output stream, truncating any existing content.
- // A file may be opened directly once the minimum API >= 29. <https://developer.android.com/reference/kotlin/android/content/ContentResolver#openfile>
- val exportFileOutputStream = contentResolver.openOutputStream(Uri.parse(noEncryptionFileNameString), "wt")!!
+ val exportFileOutputStream = contentResolver.openOutputStream(noEncryptionFileNameString.toUri(), "wt")!!
// Export the unencrypted file.
val noEncryptionExportStatus = importExportDatabaseHelper.exportUnencrypted(exportFileOutputStream, this)
// Populate the second part of the encryption password with salt byte array with the salt.
System.arraycopy(saltByteArray, 0, encryptionPasswordWithSaltByteArray, encryptionPasswordByteArray.size, saltByteArray.size)
- // Get a SHA-512 message digest.
+ // Get an SHA-512 message digest.
val messageDigest = MessageDigest.getInstance("SHA-512")
// Hash the salted encryption password. Otherwise, any characters after the 32nd character in the password are ignored.
val hashedEncryptionPasswordWithSaltByteArray = messageDigest.digest(encryptionPasswordWithSaltByteArray)
// Truncate the encryption password byte array to 256 bits (32 bytes).
- val truncatedHashedEncryptionPasswordWithSaltByteArray = Arrays.copyOf(hashedEncryptionPasswordWithSaltByteArray, 32)
+ val truncatedHashedEncryptionPasswordWithSaltByteArray = hashedEncryptionPasswordWithSaltByteArray.copyOf(32)
// Create an AES secret key from the encryption password byte array.
val secretKey = SecretKeySpec(truncatedHashedEncryptionPasswordWithSaltByteArray, "AES")
// Populate the initialization vector with random data.
secureRandom.nextBytes(initializationVector)
- // Get a Advanced Encryption Standard, Galois/Counter Mode, No Padding cipher instance. Galois/Counter mode protects against modification of the ciphertext. It doesn't use padding.
+ // Get an Advanced Encryption Standard, Galois/Counter Mode, No Padding cipher instance. Galois/Counter mode protects against modification of the ciphertext. It doesn't use padding.
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
// Set the GCM tag length to be 128 bits (the maximum) and apply the initialization vector.
val passwordEncryptionFileNameString = settingsFileNameEditText.text.toString()
// Get the export file output stream, truncating any existing content.
- val exportFileOutputStream = contentResolver.openOutputStream(Uri.parse(passwordEncryptionFileNameString), "wt")!!
+ val exportFileOutputStream = contentResolver.openOutputStream(passwordEncryptionFileNameString.toUri(), "wt")!!
// Add the salt and the initialization vector to the export file output stream.
exportFileOutputStream.write(saltByteArray)
/* SPDX-License-Identifier: GPL-3.0-or-later
- * SPDX-FileCopyrightText: 2019-2025 Soren Stoutner <soren@stoutner.com>
+ * SPDX-FileCopyrightText: 2019-2026 Soren Stoutner <soren@stoutner.com>
*
* This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android/>.
*
// Reload the logcat.
populateLogcat()
- } catch (exception: Exception) {
+ } catch (_: Exception) {
// Do nothing.
}
logcatLineString = logcatLineString!!.trim()
// Apply syntax highlighting to the logcat.
- if (logcatLineString!!.contains("crash") || logcatLineString!!.contains("Exception") ) { // Colorize crashes.
+ if (logcatLineString.contains("crash") || logcatLineString.contains("Exception") ) { // Colorize crashes.
logcatHtmlStringBuilder.append("<strong class=\"crash\">")
logcatHtmlStringBuilder.append(logcatLineString)
logcatHtmlStringBuilder.append("</strong>")
- } else if (logcatLineString!!.startsWith("at") || logcatLineString!!.startsWith("Process:") || logcatLineString!!.contains("FATAL")) { // Colorize lines relating to crashes.
+ } else if (logcatLineString.startsWith("at") || logcatLineString.startsWith("Process:") || logcatLineString.contains("FATAL")) { // Colorize lines relating to crashes.
logcatHtmlStringBuilder.append("<span class=\"crash\">")
logcatHtmlStringBuilder.append(logcatLineString)
logcatHtmlStringBuilder.append("</span>")
- } else if (logcatLineString!!.startsWith("-")) { // Colorize the headers.
+ } else if (logcatLineString.startsWith("-")) { // Colorize the headers.
logcatHtmlStringBuilder.append("<span class=\"header\">")
logcatHtmlStringBuilder.append(logcatLineString)
logcatHtmlStringBuilder.append("</span>")
- } else if (logcatLineString!!.startsWith("[ ")) { // Colorize the time stamps.
+ } else if (logcatLineString.startsWith("[ ")) { // Colorize the time stamps.
logcatHtmlStringBuilder.append("<span style=color:gray>")
logcatHtmlStringBuilder.append(logcatLineString)
logcatHtmlStringBuilder.append("</span>")
// Close the buffered reader.
logcatBufferedReader.close()
- } catch (exception: IOException) {
+ } catch (_: IOException) {
// Do nothing.
}
displayUnderCutouts = sharedPreferences.getBoolean(getString(R.string.display_under_cutouts_key), false)
// Set the display under cutouts mode. This must be done here as it doesn't appear to work correctly if handled after the app is fully initialized.
- if ((Build.VERSION.SDK_INT < 35) && displayUnderCutouts) {
- if (Build.VERSION.SDK_INT >= 30)
- window.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
- else if (Build.VERSION.SDK_INT >= 28)
+ if ((Build.VERSION.SDK_INT <= 34) && displayUnderCutouts) {
+ if (Build.VERSION.SDK_INT <= 29)
window.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
+ else
+ window.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
}
// Get the entry values string arrays.
val currentThemeStatus = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
// Set the app theme according to the preference. A switch statement cannot be used because the theme entry values string array is not a compile time constant.
- if (appTheme == appThemeEntryValuesStringArray[1]) { // The light theme is selected.
- // Apply the light theme.
- AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
- } else if (appTheme == appThemeEntryValuesStringArray[2]) { // The dark theme is selected.
- // Apply the dark theme.
- AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
- } else { // The system default theme is selected.
- if (Build.VERSION.SDK_INT >= 28) { // The system default theme is supported.
- // Follow the system default theme.
- AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
- } else { // The system default theme is not supported.
- // Follow the battery saver mode.
- AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY)
- }
+ when (appTheme) {
+ appThemeEntryValuesStringArray[1] -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) // The light theme is selected.
+ appThemeEntryValuesStringArray[2] -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) // The dark theme is selected.
+ else -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) // The system default theme is selected.
}
// Do not continue if the app theme is different than the OS theme. The app always initially starts in the OS theme.
// Set the download destination.
downloadRequest.setDestinationInExternalPublicDir(downloadDirectory, fileNameString)
- // Allow media scanner to index the download if it is a media file. This is automatic for API >= 29.
- @Suppress("DEPRECATION")
- if (Build.VERSION.SDK_INT <= 28)
- downloadRequest.allowScanningByMediaScanner()
-
// Add the URL as the description for the download.
downloadRequest.setDescription(saveUrlString)
/* SPDX-License-Identifier: GPL-3.0-or-later
- * SPDX-FileCopyrightText: 2017-2024 Soren Stoutner <soren@stoutner.com>
+ * SPDX-FileCopyrightText: 2017-2024, 2026 Soren Stoutner <soren@stoutner.com>
*
* This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android/>.
*
import android.app.Application
import android.content.ContentResolver
import android.graphics.Typeface
-import android.net.Uri
import android.text.SpannableStringBuilder
import android.text.Spanned
import android.text.style.StyleSpan
import javax.net.ssl.SSLSession
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager
+import androidx.core.net.toUri
class GetHeadersBackgroundTask {
// Attempt to read the content data. Return an error if this fails.
try {
// Get a URI for the content URL.
- val contentUri = Uri.parse(urlString)
+ val contentUri = urlString.toUri()
// Get a cursor with metadata about the content URL.
val contentCursor = contentResolver.query(contentUri, null, null, null, null)!!
// Populate the SSL certificate, returned separately.
sslCertificateBuilder.append(serverCertificate.toString())
- } catch (exception: Exception) {
+ } catch (_: Exception) {
// Do nothing.
}
package com.stoutner.privacybrowser.fragments
-import android.annotation.SuppressLint
import android.app.Activity
import android.app.ActivityManager
import android.content.ClipData
// Display the package signature.
try {
- // Get the first package signature. Suppress the lint warning about the need to be careful in implementing comparison of certificates for security purposes.
- // Once the minimum API >= 28, `GET_SIGNING_CERTIFICATES` can be used instead. Once the minimum API >= 33, the newer `getPackageInfo()` may be used.
- @Suppress("DEPRECATION")
- @SuppressLint("PackageManagerGetSignatures") val packageSignature = requireContext().packageManager.getPackageInfo(requireContext().packageName,PackageManager.GET_SIGNATURES).signatures!![0]
+ // Get the first package signature.
+ val packageSignature = requireContext().packageManager.getPackageInfo(requireContext().packageName,PackageManager.GET_SIGNING_CERTIFICATES).signingInfo!!.apkContentsSigners[0]
// Convert the signature to a byte array input stream.
val certificateByteArrayInputStream: InputStream = ByteArrayInputStream(packageSignature.toByteArray())
/* SPDX-License-Identifier: GPL-3.0-or-later
- * SPDX-FileCopyrightText: 2016-2025 Soren Stoutner <soren@stoutner.com>
+ * SPDX-FileCopyrightText: 2016-2026 Soren Stoutner <soren@stoutner.com>
*
* This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android/>.
*
// Update the theme preference summary text.
appThemePreference.summary = appThemeEntriesStringArray[0]
- // Apply the new theme.
- if (Build.VERSION.SDK_INT >= 28) { // The system default theme is supported.
- // Follow the system default theme.
- AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
- } else { // The system default theme is not supported.
- // Follow the battery saver mode.
- AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY)
- }
+ // Follow the system default theme.
+ AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
}
1 -> { // The light theme is selected.
// Create a temporary import file.
val temporaryImportFile = File.createTempFile("temporary_import_file", null, context.cacheDir)
- // The file may be copied directly in Kotlin using `File.copyTo`. <https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/java.io.-file/copy-to.html>
- // It can be copied in Android using `Files.copy` once the minimum API >= 26.
- // <https://developer.android.com/reference/java/nio/file/Files#copy(java.nio.file.Path,%20java.nio.file.Path,%20java.nio.file.CopyOption...)>
- // However, the file cannot be acquired from the content URI until the minimum API >= 29. <https://developer.android.com/reference/kotlin/android/content/ContentResolver#openfile>
-
// Create a temporary file output stream.
val temporaryImportFileOutputStream = FileOutputStream(temporaryImportFile)
// Get a handle for the shared preference.
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
- // Open the import database. Once the minimum API >= 27 the file can be opened directly without using the string.
+ // Open the import database.
val importDatabase = SQLiteDatabase.openDatabase(temporaryImportFile.toString(), null, SQLiteDatabase.OPEN_READWRITE)
// Get the database version.
// Close the temporary export database.
temporaryExportDatabase.close()
-
- // The file may be copied directly in Kotlin using `File.copyTo`. <https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/java.io.-file/copy-to.html>
- // It can be copied in Android using `Files.copy` once the minimum API >= 26.
- // <https://developer.android.com/reference/java/nio/file/Files#copy(java.nio.file.Path,%20java.nio.file.Path,%20java.nio.file.CopyOption...)>
- // However, the file cannot be acquired from the content URI until the minimum API >= 29. <https://developer.android.com/reference/kotlin/android/content/ContentResolver#openfile>
-
// Create the temporary export file input stream.
val temporaryExportFileInputStream = FileInputStream(temporaryExportFile)
}
dependencies {
- classpath 'com.android.tools.build:gradle:9.1.0'
+ classpath 'com.android.tools.build:gradle:9.2.0'
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:2.2.10'
// NOTE: Do not place your application dependencies here; they belong
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip