Compare commits

...
5 Commits
Author SHA1 Message Date
Sweetbread d7a2a26097 style: simplify db declaration 2025-04-23 00:36:20 +03:00
Sweetbread f1b330c1ce fix: builds variants names 2025-04-22 23:24:44 +03:00
Sweetbread 1ede1a4c2d fix: apply warning fixes 2025-04-22 20:28:36 +03:00
Sweetbread 08949c5f0d impr: add empty list message 2025-04-22 18:44:04 +03:00
Sweetbread be230e8a11 fix: apply API changes 2025-04-22 18:27:44 +03:00
11 changed files with 70 additions and 63 deletions

No files matched your search

+6 -4
View File
@@ -34,16 +34,17 @@ android {
release { release {
isMinifyEnabled = false isMinifyEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
resValue("string", "app_name", "@string/app_name_reg")
} }
debug { debug {
applicationIdSuffix = ".debug" applicationIdSuffix = ".debug"
versionNameSuffix = versionNameSuffix =
"-debug+${LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)}" "-debug+${LocalDateTime.now().format(DateTimeFormatter.BASIC_ISO_DATE)}"
resValue("string", "app_name", "app_name_dev") resValue("string", "app_name", "@string/app_name_dev")
} }
create("beta") { create("beta") {
versionNameSuffix = versionNameSuffix =
"-beta+${LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)}" "-beta+${LocalDateTime.now().format(DateTimeFormatter.BASIC_ISO_DATE)}"
resValue("string", "app_name", "@string/app_name_beta") resValue("string", "app_name", "@string/app_name_beta")
signingConfig = signingConfigs.getByName("debug") signingConfig = signingConfigs.getByName("debug")
} }
@@ -104,7 +105,8 @@ dependencies {
implementation(libs.coil.compose) implementation(libs.coil.compose)
implementation(libs.androidx.datastore.preferences) implementation(libs.androidx.datastore.preferences)
implementation(libs.splitties.funpack.android.base.with.views.dsl) implementation(libs.splitties.base)
implementation(libs.splitties.room)
implementation(libs.compose) implementation(libs.compose)
+32 -30
View File
@@ -19,11 +19,14 @@ import ru.sweetbread.unn.db.loadSchedule
import ru.sweetbread.unn.db.loadUserByBitrixId import ru.sweetbread.unn.db.loadUserByBitrixId
import ru.sweetbread.unn.ui.layout.LoginData import ru.sweetbread.unn.ui.layout.LoginData
import ru.sweetbread.unn.ui.layout.client import ru.sweetbread.unn.ui.layout.client
import java.time.Instant
import java.time.LocalDate import java.time.LocalDate
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.LocalTime import java.time.LocalTime
import java.time.ZoneId
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
private lateinit var PHPSESSID: String private lateinit var PHPSESSID: String
private lateinit var CSRF: String private lateinit var CSRF: String
lateinit var ME: User lateinit var ME: User
@@ -31,8 +34,10 @@ lateinit var ME: User
const val portalURL = "https://portal.unn.ru" const val portalURL = "https://portal.unn.ru"
const val ruzapiURL = "$portalURL/ruzapi" const val ruzapiURL = "$portalURL/ruzapi"
const val vuzapiURL = "$portalURL/bitrix/vuz/api" const val vuzapiURL = "$portalURL/bitrix/vuz/api"
const val prtl2URL = "$portalURL/portal2/api"
const val restURL = "$portalURL/rest" const val restURL = "$portalURL/rest"
enum class Type(val s: String) { enum class Type(val s: String) {
Student("student"), Student("student"),
Group("group"), Group("group"),
@@ -109,7 +114,6 @@ class User(
class Post( class Post(
val id: Int, val id: Int,
val blogId: Int,
val authorId: Int, val authorId: Int,
val enableComments: Boolean, val enableComments: Boolean,
val numComments: Int, val numComments: Int,
@@ -123,6 +127,7 @@ class AvatarSet(
val small: String val small: String
) )
/** /**
* Authorize user by [login] and [password] * Authorize user by [login] and [password]
* *
@@ -149,10 +154,8 @@ suspend fun auth(
if (r.status.value == 302) { if (r.status.value == 302) {
PHPSESSID = PHPSESSID =
"""PHPSESSID=([\w\d]+)""".toRegex().find(r.headers["Set-Cookie"]!!)!!.groupValues[1] """PHPSESSID=([\w\d]+)""".toRegex().find(r.headers["Set-Cookie"]!!)!!.groupValues[1]
GlobalScope.launch(Dispatchers.IO) {
getMyself(login) getMyself(login)
getCSRF() getCSRF()
}
return true return true
} }
return false return false
@@ -162,9 +165,10 @@ suspend fun auth(
* Save info about current [User] in memory * Save info about current [User] in memory
*/ */
private suspend fun getMyself(login: String) { private suspend fun getMyself(login: String) {
GlobalScope.launch(Dispatchers.IO) { // WARNING: trailing / is important, 'cuz API devs are eating shit
val studentinfo = JSONObject(client.get("$ruzapiURL/studentinfo") { val studentinfo = JSONObject(client.get("$ruzapiURL/studentinfo/") {
parameter("uns", login.substring(1)) header("Cookie", "PHPSESSID=$PHPSESSID")
parameter("uns", login.drop(1))
}.bodyAsText()) }.bodyAsText())
val user = JSONObject( val user = JSONObject(
@@ -173,6 +177,8 @@ private suspend fun getMyself(login: String) {
}.bodyAsText() }.bodyAsText()
) )
Log.d("studentInfo", studentinfo.toString(2))
ME = User( ME = User(
unnId = studentinfo.getString("id").toInt(), unnId = studentinfo.getString("id").toInt(),
bitrixId = user.getInt("bitrix_id"), bitrixId = user.getInt("bitrix_id"),
@@ -185,10 +191,10 @@ private suspend fun getMyself(login: String) {
nameRu = user.getString("fullname"), nameRu = user.getString("fullname"),
nameEn = user.getString("fullname_en"), nameEn = user.getString("fullname_en"),
isMale = user.getString("sex") == "M", isMale = user.getString("sex") == "M",
birthday = LocalDate.parse( birthday = Instant
user.getString("birthdate"), .parse(user.getString("birthdate"))
DateTimeFormatter.ofPattern("yyyy-MM-dd") .atZone(ZoneId.of("Europe/Moscow"))
), .toLocalDate(),
avatar = user.getJSONObject("photo").let { avatar = user.getJSONObject("photo").let {
AvatarSet( AvatarSet(
it.getString("orig"), it.getString("orig"),
@@ -197,7 +203,6 @@ private suspend fun getMyself(login: String) {
) )
} }
) )
}
} }
suspend fun getScheduleDay( suspend fun getScheduleDay(
@@ -209,10 +214,9 @@ suspend fun getScheduleDay(
if ((type == ME.type) and (id == ME.unnId!!)) { if ((type == ME.type) and (id == ME.unnId!!)) {
val schedule = withContext(Dispatchers.IO) { loadSchedule(date) } val schedule = withContext(Dispatchers.IO) { loadSchedule(date) }
Log.d("Schedule", schedule.joinToString()) Log.d("Schedule", schedule.joinToString())
if (schedule.size != 0) { if (schedule.isNotEmpty())
return schedule return schedule
} }
}
return getSchedule(type, id, date, date) return getSchedule(type, id, date, date)
} }
@@ -223,7 +227,7 @@ suspend fun getSchedule(
start: LocalDate, start: LocalDate,
finish: LocalDate finish: LocalDate
): ArrayList<ScheduleUnit> { ): ArrayList<ScheduleUnit> {
val unnDatePattern = DateTimeFormatter.ofPattern("yyyy.MM.dd") val unnDatePattern = DateTimeFormatter.ofPattern("yyyy-MM-dd")
val r = client.get("$ruzapiURL/schedule/${type.s}/$id") { val r = client.get("$ruzapiURL/schedule/${type.s}/$id") {
parameter("start", start.format(unnDatePattern)) parameter("start", start.format(unnDatePattern))
@@ -310,28 +314,26 @@ suspend fun getCSRF() {
} }
suspend fun getBlogposts(): ArrayList<Post> { suspend fun getBlogposts(): ArrayList<Post> {
val r = client.get("$restURL/log.blogpost.get") { val r = client.get("$prtl2URL/news.php") {
header("Cookie", "PHPSESSID=$PHPSESSID") header("Cookie", "PHPSESSID=$PHPSESSID")
parameter("sessid", CSRF) header("x-bitrix-sessid-token", CSRF)
} }
val json = JSONObject(r.bodyAsText()) val result = JSONArray(r.bodyAsText())
val result = json.getJSONArray("result")
val out = arrayListOf<Post>() val out = arrayListOf<Post>()
for (i in 0 until result.length()) { for (i in 0 until result.length()) {
val el = result.getJSONObject(i) val el = result.getJSONObject(i)
out.add( out.add(
Post( Post(
id = el.getString("ID").toInt(), id = el.getString("id").toInt(),
blogId = el.getString("BLOG_ID").toInt(), authorId = el.getJSONObject("author").getInt("id").toInt(),
authorId = el.getString("AUTHOR_ID").toInt(), enableComments = true, // FIXME: Delete the field or get correct value
enableComments = el.getString("ENABLE_COMMENTS") == "Y", numComments = el.getString("commentsnum").toInt(),
numComments = el.getString("NUM_COMMENTS").toInt(),
date = LocalDateTime.parse( date = LocalDateTime.parse(
el.getString("DATE_PUBLISH"), el.getString("time"),
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'+03:00'") DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss")
), ),
content = el.getString("DETAIL_TEXT") content = el.getString("fulltext")
) )
) )
} }
@@ -375,10 +377,10 @@ suspend fun getUser(id: Int): User {
nameRu = json.getString("fullname"), nameRu = json.getString("fullname"),
nameEn = json.getString("fullname_en"), nameEn = json.getString("fullname_en"),
isMale = json.getString("sex") == "M", isMale = json.getString("sex") == "M",
birthday = LocalDate.parse( birthday = Instant
json.getString("birthdate"), .parse(json.getString("birthdate"))
DateTimeFormatter.ofPattern("yyyy-MM-dd") .atZone(ZoneId.of("Europe/Moscow"))
), .toLocalDate(),
avatar = json.getJSONObject("photo").let { avatar = json.getJSONObject("photo").let {
AvatarSet( AvatarSet(
it.getString("orig"), it.getString("orig"),
+10 -10
View File
@@ -11,15 +11,15 @@ class UNNApp : Application() {
override fun attachBaseContext(base: Context) { override fun attachBaseContext(base: Context) {
super.attachBaseContext(base) super.attachBaseContext(base)
initAcra { // initAcra {
buildConfigClass = BuildConfig::class.java // buildConfigClass = BuildConfig::class.java
reportFormat = StringFormat.JSON // reportFormat = StringFormat.JSON
httpSender { // httpSender {
uri = BuildConfig.ACRA_URL // uri = BuildConfig.ACRA_URL
basicAuthLogin = BuildConfig.ACRA_LOGIN // basicAuthLogin = BuildConfig.ACRA_LOGIN
basicAuthPassword = BuildConfig.ACRA_PASS // basicAuthPassword = BuildConfig.ACRA_PASS
httpMethod = HttpSender.Method.POST // httpMethod = HttpSender.Method.POST
} // }
} // }
} }
} }
@@ -376,7 +376,7 @@ fun loadSchedule(oid: Int): ScheduleUnit? {
fun loadSchedule(date: LocalDate): ArrayList<ScheduleUnit> { fun loadSchedule(date: LocalDate): ArrayList<ScheduleUnit> {
db.scheduleDao().getSchedule(date.format(DateTimeFormatter.ISO_DATE)) db.scheduleDao().getSchedule(date.format(DateTimeFormatter.ISO_DATE))
.mapNotNull { Log.d("meow", "${it.oid}: ${loadSchedule(it.oid)}") } .map { Log.d("meow", "${it.oid}: ${loadSchedule(it.oid)}") }
return ArrayList( return ArrayList(
db.scheduleDao().getSchedule(date.format(DateTimeFormatter.ISO_DATE)) db.scheduleDao().getSchedule(date.format(DateTimeFormatter.ISO_DATE))
.mapNotNull { loadSchedule(it.oid) } .mapNotNull { loadSchedule(it.oid) }
@@ -278,7 +278,6 @@ fun UserItemPreview() {
fun PostItemPreview() { fun PostItemPreview() {
val post = Post( val post = Post(
id = 154923, id = 154923,
blogId = 121212,
authorId = 165945, authorId = 165945,
enableComments = true, enableComments = true,
numComments = 0, numComments = 0,
@@ -51,6 +51,7 @@ import ru.sweetbread.unn.R
import ru.sweetbread.unn.ScheduleUnit import ru.sweetbread.unn.ScheduleUnit
import ru.sweetbread.unn.getScheduleDay import ru.sweetbread.unn.getScheduleDay
import ru.sweetbread.unn.ui.theme.UNNTheme import ru.sweetbread.unn.ui.theme.UNNTheme
import splitties.resources.appStr
import java.time.DayOfWeek import java.time.DayOfWeek
import java.time.LocalDate import java.time.LocalDate
import java.time.LocalDateTime import java.time.LocalDateTime
@@ -101,12 +102,15 @@ fun ScheduleDay(modifier: Modifier = Modifier, date: LocalDate) {
if (loadedDate == date) { if (loadedDate == date) {
Log.d("Loaded", "${date.format(DateTimeFormatter.ISO_DATE)} ${lessons.size}") Log.d("Loaded", "${date.format(DateTimeFormatter.ISO_DATE)} ${lessons.size}")
LazyColumn (modifier) { LazyColumn (modifier) {
items(lessons) { // TODO: Add empty list notification items(lessons) {
ScheduleItem(unit = it, modifier = Modifier.clickable { ScheduleItem(unit = it, modifier = Modifier.clickable {
expanded = if (it.oid == expanded) 0 expanded = if (it.oid == expanded) 0
else it.oid else it.oid
}, expanded = expanded == it.oid) }, expanded = expanded == it.oid)
} }
if (lessons.isEmpty())
item { Text(appStr(R.string.noData)) }
} }
} else { } else {
LinearProgressIndicator( LinearProgressIndicator(
@@ -38,6 +38,7 @@ import ru.sweetbread.unn.db.AppDatabase
import ru.sweetbread.unn.ui.composes.Blogposts import ru.sweetbread.unn.ui.composes.Blogposts
import ru.sweetbread.unn.ui.composes.Schedule import ru.sweetbread.unn.ui.composes.Schedule
import ru.sweetbread.unn.ui.theme.UNNTheme import ru.sweetbread.unn.ui.theme.UNNTheme
import splitties.arch.room.roomDb
import splitties.toast.toast import splitties.toast.toast
val client = HttpClient(Android) { val client = HttpClient(Android) {
@@ -62,18 +63,13 @@ val client = HttpClient(Android) {
} }
} }
lateinit var db: AppDatabase val db = roomDb<AppDatabase>(name = "database")
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
db = Room.databaseBuilder(
applicationContext,
AppDatabase::class.java, "database"
).build()
setContent { setContent {
UNNTheme { UNNTheme {
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) { Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
+4 -2
View File
@@ -1,7 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="app_name_reg">ННГУ</string>
<string name="app_name_dev">ННГУ Альфа</string>
<string name="app_name_beta">ННГУ Бета</string>
<string name="prompt_password">Пароль</string> <string name="prompt_password">Пароль</string>
<string name="app_name">ННГУ</string>
<string name="prompt_login">Логин</string> <string name="prompt_login">Логин</string>
<string name="sign_in">Войти</string> <string name="sign_in">Войти</string>
<string name="assistant">Ассистент</string> <string name="assistant">Ассистент</string>
@@ -11,5 +13,5 @@
<string name="auditorium">Аудитория</string> <string name="auditorium">Аудитория</string>
<string name="building">Здание</string> <string name="building">Здание</string>
<string name="floor">Этаж</string> <string name="floor">Этаж</string>
<string name="app_name_beta">ННГУ Бета</string> <string name="noData">Нет данных</string>
</resources> </resources>
+5 -4
View File
@@ -1,6 +1,8 @@
<resources> <resources>
<string name="app_name">UNN</string> <string name="app_name_reg">UNN</string>
<!-- <string name="title_activity_login">LoginActivity</string>--> <string name="app_name_dev">UNN Dev</string>
<string name="app_name_beta">UNN Beta</string>
<!-- <string name="title_activity_login">LoginActivity</string>-->
<string name="prompt_email" translatable="false">Email</string> <string name="prompt_email" translatable="false">Email</string>
<string name="prompt_login">Login</string> <string name="prompt_login">Login</string>
<string name="prompt_password">Password</string> <string name="prompt_password">Password</string>
@@ -13,7 +15,6 @@
<string name="building">Building</string> <string name="building">Building</string>
<string name="floor">Floor</string> <string name="floor">Floor</string>
<string name="lorem" translatable="false">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam vel iaculis elit. Aliquam varius urna ut nisl rhoncus ullamcorper. Maecenas et nisl at dui mollis maximus nec in libero. Ut eu nulla id felis hendrerit lobortis. Maecenas vel facilisis lectus. Morbi eleifend massa a ante consequat, eu aliquam elit euismod. Aenean quis erat tincidunt, egestas ligula id, convallis tortor. Vivamus volutpat condimentum nisl sed eleifend. Aenean dapibus dolor ut orci lobortis, placerat lobortis tortor pretium. Nam eros lectus, convallis sed ultricies sit amet, lacinia sed sem. In mi odio, porta non malesuada et, cursus a metus. Morbi quis odio sed quam commodo gravida id sit amet dolor. Donec ac iaculis massa. Nulla mauris sapien, auctor consequat est in, tempus accumsan ipsum. Donec semper volutpat nisi. Quisque dignissim tellus ipsum, sed malesuada libero aliquam sed. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nam eleifend pharetra orci eu scelerisque. In hac habitasse platea dictumst. Sed non neque vitae metus porttitor vestibulum ut eget felis. Aliquam venenatis a magna eu mattis. Proin rutrum, sapien id viverra finibus, nisi quam aliquam eros, et dignissim lectus sem sit amet purus. Donec et semper enim, sed pretium lacus. Nullam venenatis ullamcorper maximus. Mauris pellentesque velit non sem sollicitudin molestie. Duis hendrerit consequat enim eget euismod.</string> <string name="lorem" translatable="false">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam vel iaculis elit. Aliquam varius urna ut nisl rhoncus ullamcorper. Maecenas et nisl at dui mollis maximus nec in libero. Ut eu nulla id felis hendrerit lobortis. Maecenas vel facilisis lectus. Morbi eleifend massa a ante consequat, eu aliquam elit euismod. Aenean quis erat tincidunt, egestas ligula id, convallis tortor. Vivamus volutpat condimentum nisl sed eleifend. Aenean dapibus dolor ut orci lobortis, placerat lobortis tortor pretium. Nam eros lectus, convallis sed ultricies sit amet, lacinia sed sem. In mi odio, porta non malesuada et, cursus a metus. Morbi quis odio sed quam commodo gravida id sit amet dolor. Donec ac iaculis massa. Nulla mauris sapien, auctor consequat est in, tempus accumsan ipsum. Donec semper volutpat nisi. Quisque dignissim tellus ipsum, sed malesuada libero aliquam sed. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nam eleifend pharetra orci eu scelerisque. In hac habitasse platea dictumst. Sed non neque vitae metus porttitor vestibulum ut eget felis. Aliquam venenatis a magna eu mattis. Proin rutrum, sapien id viverra finibus, nisi quam aliquam eros, et dignissim lectus sem sit amet purus. Donec et semper enim, sed pretium lacus. Nullam venenatis ullamcorper maximus. Mauris pellentesque velit non sem sollicitudin molestie. Duis hendrerit consequat enim eget euismod.</string>
<string name="app_name_dev" translatable="false">UNN Dev</string> <string name="noData">No Data</string>
<string name="app_name_beta">UNN Beta</string>
<!-- <string name="login_failed">"Login failed"</string>--> <!-- <string name="login_failed">"Login failed"</string>-->
</resources> </resources>
+4 -3
View File
@@ -1,6 +1,6 @@
[versions] [versions]
acraHttp = "5.11.3" acraHttp = "5.11.3"
agp = "8.5.2" agp = "8.7.0"
calendar = "2.5.4" calendar = "2.5.4"
coilCompose = "2.7.0" coilCompose = "2.7.0"
compose = "1.6.4" # Updating this will cause an error! compose = "1.6.4" # Updating this will cause an error!
@@ -23,7 +23,7 @@ activity = "1.9.2"
navigationCompose = "2.7.7" # Updating this will cause an error! navigationCompose = "2.7.7" # Updating this will cause an error!
roomRuntime = "2.6.1" roomRuntime = "2.6.1"
secretsGradlePlugin = "2.0.1" secretsGradlePlugin = "2.0.1"
splittiesFunPackAndroidBaseWithViewsDsl = "3.0.0" splitties = "3.0.0"
kefirbb = "1.5" kefirbb = "1.5"
[libraries] [libraries]
@@ -62,7 +62,8 @@ androidx-lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecy
androidx-lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycle" } androidx-lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycle" }
androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" } androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
secrets-gradle-plugin = { module = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin", version.ref = "secretsGradlePlugin" } secrets-gradle-plugin = { module = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin", version.ref = "secretsGradlePlugin" }
splitties-funpack-android-base-with-views-dsl = { module = "com.louiscad.splitties:splitties-fun-pack-android-base-with-views-dsl", version.ref = "splittiesFunPackAndroidBaseWithViewsDsl" } splitties-base = { module = "com.louiscad.splitties:splitties-fun-pack-android-base-with-views-dsl", version.ref = "splitties" }
splitties-room = { module = "com.louiscad.splitties:splitties-arch-room", version.ref = "splitties" }
kefirbb = { group = "org.kefirsf", name = "kefirbb", version.ref = "kefirbb" } kefirbb = { group = "org.kefirsf", name = "kefirbb", version.ref = "kefirbb" }
[plugins] [plugins]
+1 -1
View File
@@ -1,6 +1,6 @@
#Sat Mar 16 18:30:45 MSK 2024 #Sat Mar 16 18:30:45 MSK 2024
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