Bump targetSdkVersion to 35 and update dependencies

This also includes changes to make the status guard hack work
on Android 15 and a couple of small adjustments to support edge-to-edge
in all activities.
This commit is contained in:
Alexander Bakker 2024-10-18 15:27:26 +02:00
parent 939fa0e1ec
commit d395bbeb8d
21 changed files with 55 additions and 57 deletions

View file

@ -20,14 +20,14 @@ def fileProviderAuthority = "${packageName}.fileprovider"
def fileProviderAuthorityDebug = "${packageName}.debug.fileprovider" def fileProviderAuthorityDebug = "${packageName}.debug.fileprovider"
android { android {
compileSdk 34 compileSdk 35
namespace packageName namespace packageName
defaultConfig { defaultConfig {
applicationId "${packageName}" applicationId "${packageName}"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 34 targetSdkVersion 35
versionCode 71 versionCode 71
versionName "3.2" versionName "3.2"
multiDexEnabled true multiDexEnabled true
@ -143,19 +143,19 @@ aboutLibraries {
dependencies { dependencies {
def cameraxVersion = '1.3.4' def cameraxVersion = '1.3.4'
def glideVersion = '4.16.0' def glideVersion = '4.16.0'
def guavaVersion = '33.3.0' def guavaVersion = '33.3.1'
def hiltVersion = '2.52' def hiltVersion = '2.52'
def junitVersion = '4.13.2' def junitVersion = '4.13.2'
def libsuVersion = '6.0.0' def libsuVersion = '6.0.0'
def roomVersion = "2.6.1" def roomVersion = "2.6.1"
annotationProcessor 'androidx.annotation:annotation:1.8.2' annotationProcessor 'androidx.annotation:annotation:1.9.0'
annotationProcessor "androidx.room:room-compiler:$roomVersion" annotationProcessor "androidx.room:room-compiler:$roomVersion"
annotationProcessor "com.google.dagger:hilt-compiler:$hiltVersion" annotationProcessor "com.google.dagger:hilt-compiler:$hiltVersion"
annotationProcessor "com.github.bumptech.glide:compiler:${glideVersion}" annotationProcessor "com.github.bumptech.glide:compiler:${glideVersion}"
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.activity:activity:1.9.2' implementation 'androidx.activity:activity:1.9.3'
implementation 'androidx.appcompat:appcompat:1.7.0' implementation 'androidx.appcompat:appcompat:1.7.0'
implementation "androidx.biometric:biometric:1.1.0" implementation "androidx.biometric:biometric:1.1.0"
implementation "androidx.camera:camera-camera2:$cameraxVersion" implementation "androidx.camera:camera-camera2:$cameraxVersion"
@ -202,7 +202,7 @@ dependencies {
androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.6.1' androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.6.1'
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.6.1' androidTestImplementation 'androidx.test.espresso:espresso-intents:3.6.1'
androidTestImplementation "junit:junit:${junitVersion}" androidTestImplementation "junit:junit:${junitVersion}"
androidTestUtil 'androidx.test:orchestrator:1.5.0' androidTestUtil 'androidx.test:orchestrator:1.5.1'
testImplementation 'androidx.test:core:1.6.1' testImplementation 'androidx.test:core:1.6.1'
testImplementation "com.google.guava:guava:${guavaVersion}-jre" testImplementation "com.google.guava:guava:${guavaVersion}-jre"

View file

@ -14,10 +14,4 @@ public class ThemeMap {
Theme.DARK, R.style.Theme_Aegis_Dark, Theme.DARK, R.style.Theme_Aegis_Dark,
Theme.AMOLED, R.style.Theme_Aegis_Amoled Theme.AMOLED, R.style.Theme_Aegis_Amoled
); );
public static final Map<Theme, Integer> FULLSCREEN = ImmutableMap.of(
Theme.LIGHT, R.style.Theme_Aegis_Light_Fullscreen,
Theme.DARK, R.style.Theme_Aegis_Dark_Fullscreen,
Theme.AMOLED, R.style.Theme_Aegis_Amoled_Fullscreen
);
} }

View file

@ -4,7 +4,7 @@ import android.annotation.SuppressLint;
import android.app.Activity; import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.os.Build; import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -12,7 +12,6 @@ import android.view.WindowManager;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.CallSuper; import androidx.annotation.CallSuper;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.view.ActionMode; import androidx.appcompat.view.ActionMode;
@ -26,6 +25,7 @@ import com.beemdevelopment.aegis.helpers.ThemeHelper;
import com.beemdevelopment.aegis.icons.IconPackManager; import com.beemdevelopment.aegis.icons.IconPackManager;
import com.beemdevelopment.aegis.vault.VaultManager; import com.beemdevelopment.aegis.vault.VaultManager;
import com.beemdevelopment.aegis.vault.VaultRepositoryException; import com.beemdevelopment.aegis.vault.VaultRepositoryException;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.color.MaterialColors; import com.google.android.material.color.MaterialColors;
import java.lang.reflect.Field; import java.lang.reflect.Field;
@ -186,13 +186,9 @@ public abstract class AegisActivity extends AppCompatActivity implements VaultMa
private class ActionModeStatusGuardHack { private class ActionModeStatusGuardHack {
private Field _fadeAnimField; private Field _fadeAnimField;
private Field _actionModeViewField; private Field _actionModeViewField;
private Drawable _appBarBackground;
@ColorInt
private final int _statusBarColor;
private ActionModeStatusGuardHack() { private ActionModeStatusGuardHack() {
_statusBarColor = getWindow().getStatusBarColor();
try { try {
_fadeAnimField = getDelegate().getClass().getDeclaredField("mFadeAnim"); _fadeAnimField = getDelegate().getClass().getDeclaredField("mFadeAnim");
_fadeAnimField.setAccessible(true); _fadeAnimField.setAccessible(true);
@ -216,20 +212,26 @@ public abstract class AegisActivity extends AppCompatActivity implements VaultMa
return; return;
} }
if (fadeAnim == null || actionModeView == null) { AppBarLayout appBarLayout = findViewById(R.id.app_bar_layout);
if (appBarLayout != null && _appBarBackground == null) {
_appBarBackground = appBarLayout.getBackground();
}
if (fadeAnim == null || actionModeView == null || appBarLayout == null || _appBarBackground == null) {
return; return;
} }
fadeAnim.cancel(); fadeAnim.cancel();
actionModeView.setVisibility(visibility); if (visibility == View.VISIBLE) {
actionModeView.setAlpha(visibility == View.VISIBLE ? 1f : 0f); actionModeView.setVisibility(visibility);
actionModeView.setAlpha(1f);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { int color = MaterialColors.getColor(appBarLayout, com.google.android.material.R.attr.colorSurfaceContainer);
int statusBarColor = visibility == View.VISIBLE appBarLayout.setBackgroundColor(color);
? MaterialColors.getColor(actionModeView, com.google.android.material.R.attr.colorSurfaceContainer) } else {
: _statusBarColor; actionModeView.setVisibility(visibility);
getWindow().setStatusBarColor(statusBarColor); actionModeView.setAlpha(0f);
appBarLayout.setBackground(_appBarBackground);
} }
} }
} }

View file

@ -18,7 +18,6 @@ import androidx.camera.view.PreviewView;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import com.beemdevelopment.aegis.R; import com.beemdevelopment.aegis.R;
import com.beemdevelopment.aegis.ThemeMap;
import com.beemdevelopment.aegis.helpers.QrCodeAnalyzer; import com.beemdevelopment.aegis.helpers.QrCodeAnalyzer;
import com.beemdevelopment.aegis.otp.GoogleAuthInfo; import com.beemdevelopment.aegis.otp.GoogleAuthInfo;
import com.beemdevelopment.aegis.otp.GoogleAuthInfoException; import com.beemdevelopment.aegis.otp.GoogleAuthInfoException;
@ -87,11 +86,6 @@ public class ScannerActivity extends AegisActivity implements QrCodeAnalyzer.Lis
}, ContextCompat.getMainExecutor(this)); }, ContextCompat.getMainExecutor(this));
} }
@Override
protected void onSetTheme() {
_themeHelper.setTheme(ThemeMap.FULLSCREEN);
}
@Override @Override
protected void onDestroy() { protected void onDestroy() {
if (_executor != null) { if (_executor != null) {

View file

@ -7,6 +7,7 @@
android:fitsSystemWindows="true" android:fitsSystemWindows="true"
tools:context="com.beemdevelopment.aegis.ui.AboutActivity"> tools:context="com.beemdevelopment.aegis.ui.AboutActivity">
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fitsSystemWindows="true"> android:fitsSystemWindows="true">

View file

@ -8,6 +8,7 @@
tools:context="com.beemdevelopment.aegis.ui.AssignIconsActivity"> tools:context="com.beemdevelopment.aegis.ui.AssignIconsActivity">
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fitsSystemWindows="true"> android:fitsSystemWindows="true">

View file

@ -5,6 +5,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:fillViewport="true" android:fillViewport="true"
android:fitsSystemWindows="true"
tools:context="com.beemdevelopment.aegis.ui.AuthActivity"> tools:context="com.beemdevelopment.aegis.ui.AuthActivity">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -8,6 +8,7 @@
android:fitsSystemWindows="true" android:fitsSystemWindows="true"
tools:context="com.beemdevelopment.aegis.ui.EditEntryActivity"> tools:context="com.beemdevelopment.aegis.ui.EditEntryActivity">
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fitsSystemWindows="true"> android:fitsSystemWindows="true">
@ -201,7 +202,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/secret" android:hint="@string/secret"
android:inputType="textPassword | textMultiLine"/> android:inputType="textPassword|textMultiLine"/>
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout

View file

@ -8,6 +8,7 @@
tools:context="com.beemdevelopment.aegis.ui.GroupManagerActivity"> tools:context="com.beemdevelopment.aegis.ui.GroupManagerActivity">
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fitsSystemWindows="true"> android:fitsSystemWindows="true">

View file

@ -9,6 +9,7 @@
tools:context="com.beemdevelopment.aegis.ui.ImportEntriesActivity"> tools:context="com.beemdevelopment.aegis.ui.ImportEntriesActivity">
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fitsSystemWindows="true"> android:fitsSystemWindows="true">

View file

@ -6,6 +6,7 @@
android:orientation="vertical" android:orientation="vertical"
android:fitsSystemWindows="true"> android:fitsSystemWindows="true">
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fitsSystemWindows="true"> android:fitsSystemWindows="true">

View file

@ -9,7 +9,7 @@
tools:context="com.beemdevelopment.aegis.ui.ScannerActivity"> tools:context="com.beemdevelopment.aegis.ui.ScannerActivity">
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/toolbar_layout" android:id="@+id/app_bar_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@android:color/transparent" android:background="@android:color/transparent"

View file

@ -9,7 +9,7 @@
tools:context="com.beemdevelopment.aegis.ui.TransferEntriesActivity"> tools:context="com.beemdevelopment.aegis.ui.TransferEntriesActivity">
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/toolbar_layout" android:id="@+id/app_bar_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fitsSystemWindows="true"> android:fitsSystemWindows="true">

View file

@ -1,6 +1,6 @@
{ {
"metadata": { "metadata": {
"generated": "2024-09-21T10:56:03.535Z" "generated": "2024-10-18T11:49:53.854Z"
}, },
"libraries": [ "libraries": [
{ {
@ -13,14 +13,14 @@
"name": "The Android Open Source Project" "name": "The Android Open Source Project"
} }
], ],
"artifactVersion": "1.9.2", "artifactVersion": "1.9.3",
"description": "Provides the base Activity subclass and the relevant hooks to build a composable structure on top.", "description": "Provides the base Activity subclass and the relevant hooks to build a composable structure on top.",
"scm": { "scm": {
"connection": "scm:git:https://android.googlesource.com/platform/frameworks/support", "connection": "scm:git:https://android.googlesource.com/platform/frameworks/support",
"url": "https://cs.android.com/androidx/platform/frameworks/support" "url": "https://cs.android.com/androidx/platform/frameworks/support"
}, },
"name": "Activity", "name": "Activity",
"website": "https://developer.android.com/jetpack/androidx/releases/activity#1.9.2", "website": "https://developer.android.com/jetpack/androidx/releases/activity#1.9.3",
"licenses": [ "licenses": [
"Apache-2.0" "Apache-2.0"
], ],
@ -38,14 +38,14 @@
"name": "The Android Open Source Project" "name": "The Android Open Source Project"
} }
], ],
"artifactVersion": "1.9.2", "artifactVersion": "1.9.3",
"description": "Kotlin extensions for 'activity' artifact", "description": "Kotlin extensions for 'activity' artifact",
"scm": { "scm": {
"connection": "scm:git:https://android.googlesource.com/platform/frameworks/support", "connection": "scm:git:https://android.googlesource.com/platform/frameworks/support",
"url": "https://cs.android.com/androidx/platform/frameworks/support" "url": "https://cs.android.com/androidx/platform/frameworks/support"
}, },
"name": "Activity Kotlin Extensions", "name": "Activity Kotlin Extensions",
"website": "https://developer.android.com/jetpack/androidx/releases/activity#1.9.2", "website": "https://developer.android.com/jetpack/androidx/releases/activity#1.9.3",
"licenses": [ "licenses": [
"Apache-2.0" "Apache-2.0"
], ],
@ -2350,7 +2350,7 @@
"developers": [ "developers": [
], ],
"artifactVersion": "33.3.0-android", "artifactVersion": "33.3.1-android",
"description": "Guava is a suite of core and expanded libraries that include\n utility classes, Google's collections, I/O classes, and\n much more.", "description": "Guava is a suite of core and expanded libraries that include\n utility classes, Google's collections, I/O classes, and\n much more.",
"scm": { "scm": {
"connection": "scm:git:https://github.com/google/guava.git", "connection": "scm:git:https://github.com/google/guava.git",

View file

@ -176,24 +176,12 @@
<style name="Theme.Aegis.Light" parent="Base.Theme.Aegis.Light"> <style name="Theme.Aegis.Light" parent="Base.Theme.Aegis.Light">
</style> </style>
<style name="Theme.Aegis.Light.Fullscreen" parent="Theme.Aegis.Light">
<item name="android:windowNoTitle">true</item>
<item name="android:windowFullscreen">true</item>
</style>
<style name="Theme.Aegis.Dark" parent="Base.Theme.Aegis.Dark"> <style name="Theme.Aegis.Dark" parent="Base.Theme.Aegis.Dark">
</style> </style>
<style name="Theme.Aegis.Dark.Fullscreen" parent="Theme.Aegis.Dark">
<item name="android:windowNoTitle">true</item>
<item name="android:windowFullscreen">true</item>
</style>
<style name="Theme.Aegis.Amoled" parent="Base.Theme.Aegis.Amoled"> <style name="Theme.Aegis.Amoled" parent="Base.Theme.Aegis.Amoled">
</style> </style>
<style name="Theme.Aegis.Amoled.Fullscreen" parent="Theme.Aegis.Amoled">
<item name="android:windowNoTitle">true</item>
<item name="android:windowFullscreen">true</item>
</style>
<style name="Widget.Aegis.Dropdown" parent="Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu" /> <style name="Widget.Aegis.Dropdown" parent="Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu" />

View file

@ -7,6 +7,7 @@ import static org.junit.Assert.assertTrue;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Build;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
@ -14,10 +15,12 @@ import androidx.test.core.app.ApplicationProvider;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import java.util.Date; import java.util.Date;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@Config(sdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
public class PreferencesTest { public class PreferencesTest {
@Test @Test
public void testIsPasswordReminderNeeded() { public void testIsPasswordReminderNeeded() {

View file

@ -7,6 +7,7 @@ import static org.junit.Assert.assertTrue;
import android.graphics.Rect; import android.graphics.Rect;
import android.media.Image; import android.media.Image;
import android.os.Build;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -18,6 +19,7 @@ import com.beemdevelopment.aegis.util.IOUtils;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -26,6 +28,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@Config(sdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
public class QrCodeAnalyzerTest { public class QrCodeAnalyzerTest {
private static final String _expectedUri = "otpauth://totp/neo4j:Charlotte?secret=B33WS2ALPT34K4BNY24AYROE4M&issuer=neo4j&algorithm=SHA1&digits=6&period=30"; private static final String _expectedUri = "otpauth://totp/neo4j:Charlotte?secret=B33WS2ALPT34K4BNY24AYROE4M&issuer=neo4j&algorithm=SHA1&digits=6&period=30";

View file

@ -7,6 +7,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import android.content.Context; import android.content.Context;
import android.os.Build;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
@ -25,6 +26,7 @@ import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -32,6 +34,7 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@Config(sdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
public class DatabaseImporterTest { public class DatabaseImporterTest {
private List<VaultEntry> _vectors; private List<VaultEntry> _vectors;

View file

@ -2,11 +2,15 @@ package com.beemdevelopment.aegis.otp;
import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertThrows;
import android.os.Build;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@Config(sdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
public class GoogleAuthInfoTest { public class GoogleAuthInfoTest {
@Test @Test
public void testGoogleAuthInfoEmptySecret() throws GoogleAuthInfoException { public void testGoogleAuthInfoEmptySecret() throws GoogleAuthInfoException {

View file

@ -6,7 +6,7 @@ buildscript {
google() google()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:8.6.1' classpath 'com.android.tools.build:gradle:8.7.1'
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.52' classpath 'com.google.dagger:hilt-android-gradle-plugin:2.52'
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.9.4' classpath 'com.google.protobuf:protobuf-gradle-plugin:0.9.4'

View file

@ -1,6 +1,6 @@
#Tue Aug 15 23:01:16 CEST 2023 #Tue Aug 15 23:01:16 CEST 2023
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists