diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 17537b68..46ec8133 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -17,7 +17,7 @@ jobs: # This workflow contains a single job called "build" build: # The type of runner that the job will run on - runs-on: macos-latest + runs-on: macos-latest-large timeout-minutes: 120 # Steps represent a sequence of tasks that will be executed as part of the job diff --git a/.github/workflows/on_pull_request.yml b/.github/workflows/on_pull_request.yml index 0a4a9d0e..9f9da4ff 100644 --- a/.github/workflows/on_pull_request.yml +++ b/.github/workflows/on_pull_request.yml @@ -17,7 +17,7 @@ jobs: # This workflow contains a single job called "build" build: # The type of runner that the job will run on - runs-on: macos-latest + runs-on: macos-latest-large timeout-minutes: 120 # Steps represent a sequence of tasks that will be executed as part of the job diff --git a/app/build.gradle.kts b/app/build.gradle.kts index fb00091e..a1a2231c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -21,8 +21,8 @@ android { defaultConfig { applicationId = "com.joeloewi.croissant" - versionCode = 61 - versionName = "1.3.0" + versionCode = 62 + versionName = "1.3.1" targetSdk = 34 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" diff --git a/app/src/main/kotlin/com/joeloewi/croissant/ui/navigation/main/attendances/screen/createattendance/composable/SelectGames.kt b/app/src/main/kotlin/com/joeloewi/croissant/ui/navigation/main/attendances/screen/createattendance/composable/SelectGames.kt index eb29be3f..7c376ec6 100644 --- a/app/src/main/kotlin/com/joeloewi/croissant/ui/navigation/main/attendances/screen/createattendance/composable/SelectGames.kt +++ b/app/src/main/kotlin/com/joeloewi/croissant/ui/navigation/main/attendances/screen/createattendance/composable/SelectGames.kt @@ -93,7 +93,8 @@ fun SelectGames( HoYoLABGame.HonkaiImpact3rd, HoYoLABGame.GenshinImpact, HoYoLABGame.TearsOfThemis, - HoYoLABGame.HonkaiStarRail + HoYoLABGame.HonkaiStarRail, + HoYoLABGame.ZenlessZoneZero ).toImmutableList() } val containsNotSupportedGame = stringResource(id = R.string.contains_not_supported_game) @@ -424,7 +425,7 @@ fun ConnectedGamesContentListItem( val enabled by remember(hoYoLABGame, gameRecord) { derivedStateOf { - hoYoLABGame == HoYoLABGame.TearsOfThemis || hoYoLABGame == HoYoLABGame.HonkaiStarRail || currentGameRecord.value.gameId != GameRecord.INVALID_GAME_ID + hoYoLABGame == HoYoLABGame.TearsOfThemis || currentGameRecord.value.gameId != GameRecord.INVALID_GAME_ID } } diff --git a/app/src/main/kotlin/com/joeloewi/croissant/ui/navigation/main/settings/screen/SettingsScreen.kt b/app/src/main/kotlin/com/joeloewi/croissant/ui/navigation/main/settings/screen/SettingsScreen.kt index f3c6337d..42977b3f 100644 --- a/app/src/main/kotlin/com/joeloewi/croissant/ui/navigation/main/settings/screen/SettingsScreen.kt +++ b/app/src/main/kotlin/com/joeloewi/croissant/ui/navigation/main/settings/screen/SettingsScreen.kt @@ -197,12 +197,14 @@ fun SettingsContent( value = !isUnusedAppRestrictionEnabled().getOrDefault(false), role = Role.Switch, onValueChange = { - activityResult.launch( - IntentCompat.createManageUnusedAppRestrictionsIntent( - activity, - activity.packageName + runCatching { + activityResult.launch( + IntentCompat.createManageUnusedAppRestrictionsIntent( + activity, + activity.packageName + ) ) - ) + } } ), leadingContent = { diff --git a/app/src/main/kotlin/com/joeloewi/croissant/util/HoYoLABGameNames.kt b/app/src/main/kotlin/com/joeloewi/croissant/util/HoYoLABGameNames.kt index d3443b41..114b3923 100644 --- a/app/src/main/kotlin/com/joeloewi/croissant/util/HoYoLABGameNames.kt +++ b/app/src/main/kotlin/com/joeloewi/croissant/util/HoYoLABGameNames.kt @@ -22,6 +22,10 @@ fun HoYoLABGame.gameNameStringResId(): Int = when (this) { R.string.honkai_star_rail_game_name } + HoYoLABGame.ZenlessZoneZero -> { + R.string.zenless_zone_zero_game_name + } + HoYoLABGame.Unknown -> { R.string.unknown_game_name } diff --git a/app/src/main/kotlin/com/joeloewi/croissant/worker/AttendCheckInEventWorker.kt b/app/src/main/kotlin/com/joeloewi/croissant/worker/AttendCheckInEventWorker.kt index 4ed3f145..2adfafa2 100644 --- a/app/src/main/kotlin/com/joeloewi/croissant/worker/AttendCheckInEventWorker.kt +++ b/app/src/main/kotlin/com/joeloewi/croissant/worker/AttendCheckInEventWorker.kt @@ -47,7 +47,8 @@ class AttendCheckInEventWorker @AssistedInject constructor( private val attendCheckInGenshinImpactUseCase: CheckInUseCase.AttendCheckInGenshinImpact, private val attendCheckInHonkaiImpact3rdUseCase: CheckInUseCase.AttendCheckInHonkaiImpact3rd, private val attendCheckInTearsOfThemisUseCase: CheckInUseCase.AttendCheckInTearsOfThemis, - private val attendCheckInHonkaiStarRail: CheckInUseCase.AttendCheckInHonkaiStarRail, + private val attendCheckInHonkaiStarRailUseCase: CheckInUseCase.AttendCheckInHonkaiStarRailUseCase, + private val attendCheckInZenlessZoneZeroUseCase: CheckInUseCase.AttendCheckInZenlessZoneZeroUseCase, private val insertWorkerExecutionLogUseCase: WorkerExecutionLogUseCase.Insert, private val hasExecutedAtLeastOnce: WorkerExecutionLogUseCase.HasExecutedAtLeastOnce, private val insertSuccessLogUseCase: SuccessLogUseCase.Insert, @@ -159,7 +160,11 @@ class AttendCheckInEventWorker @AssistedInject constructor( } HoYoLABGame.HonkaiStarRail -> { - attendCheckInHonkaiStarRail(cookie = cookie) + attendCheckInHonkaiStarRailUseCase(cookie = cookie) + } + + HoYoLABGame.ZenlessZoneZero -> { + attendCheckInZenlessZoneZeroUseCase(cookie = cookie) } HoYoLABGame.Unknown -> { diff --git a/app/src/main/res/values-en-rUS/strings.xml b/app/src/main/res/values-en-rUS/strings.xml index 1efc437c..9223a0b2 100644 --- a/app/src/main/res/values-en-rUS/strings.xml +++ b/app/src/main/res/values-en-rUS/strings.xml @@ -119,4 +119,5 @@ To ensure accuracy, the scheduled task will be executed 30 seconds after the specified time. For example, if you schedule a task for 12:34 PM, it will actually be executed at 12:34:30 PM. Attendance failed due to unknown error. Retry has scheduled. Attendance failed due to too many requests. Retry has scheduled. + Zenless Zone Zero \ No newline at end of file diff --git a/app/src/main/res/values-ko-rKR/strings.xml b/app/src/main/res/values-ko-rKR/strings.xml index 2d03e692..480c22bf 100644 --- a/app/src/main/res/values-ko-rKR/strings.xml +++ b/app/src/main/res/values-ko-rKR/strings.xml @@ -118,4 +118,5 @@ 작업을 정확하게 실행하기 위해, 지정된 시간보다 30초 늦게 실행됩니다. 예를 들어, 12시 34분에 작업을 예약하면 실제로는 12시 34분 30초에 실행됩니다. 알 수 없는 문제로 인해 출석하지 못했습니다. 재시도를 예약합니다. 요청이 빈번하여 출석하지 못 했습니다. 재시도를 예약합니다. + 젠레스 존 제로 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c4493d1f..49741014 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -123,4 +123,5 @@ To ensure accuracy, the scheduled task will be executed 30 seconds after the specified time. For example, if you schedule a task for 12:34 PM, it will actually be executed at 12:34:30 PM. Attendance failed due to unknown error. Retry has scheduled. Attendance failed due to too many requests. Retry has scheduled. + Zenless Zone Zero \ No newline at end of file diff --git a/data/src/main/kotlin/com/joeloewi/croissant/data/api/dao/ArcaLiveAppService.kt b/data/src/main/kotlin/com/joeloewi/croissant/data/api/dao/ArcaLiveAppService.kt index ba122f87..16649d25 100644 --- a/data/src/main/kotlin/com/joeloewi/croissant/data/api/dao/ArcaLiveAppService.kt +++ b/data/src/main/kotlin/com/joeloewi/croissant/data/api/dao/ArcaLiveAppService.kt @@ -26,7 +26,7 @@ import retrofit2.http.Query interface ArcaLiveAppService { @GET("view/article/{slug}/{articleId}") suspend fun getArticle( - @Header("User-Agent") userAgent: String = "live.arca.android.playstore/0.8.331-playstore", + @Header("User-Agent") userAgent: String = "net.umanle.arca.android.playstore/0.9.57", @Path("slug") slug: String, @Path("articleId") articleId: Long, @Query("viewCount") viewCount: Boolean = false diff --git a/data/src/main/kotlin/com/joeloewi/croissant/data/api/dao/GenshinImpactCheckInService.kt b/data/src/main/kotlin/com/joeloewi/croissant/data/api/dao/GenshinImpactCheckInService.kt index 6cec80c4..cd953ecf 100644 --- a/data/src/main/kotlin/com/joeloewi/croissant/data/api/dao/GenshinImpactCheckInService.kt +++ b/data/src/main/kotlin/com/joeloewi/croissant/data/api/dao/GenshinImpactCheckInService.kt @@ -5,15 +5,16 @@ import com.skydoves.sandwich.ApiResponse import retrofit2.http.Body import retrofit2.http.Header import retrofit2.http.POST -import retrofit2.http.Query import java.util.Locale interface GenshinImpactCheckInService { @POST("event/sol/sign") suspend fun attend( - @Query("lang") language: String = Locale.getDefault().toLanguageTag().lowercase(), @Header("Cookie") cookie: String, - @Body params: Map = mapOf("act_id" to "e202102251931481") + @Body params: Map = mapOf( + "act_id" to "e202102251931481", + "lang" to Locale.getDefault().toLanguageTag().lowercase() + ) ): ApiResponse } \ No newline at end of file diff --git a/data/src/main/kotlin/com/joeloewi/croissant/data/api/dao/ZenlessZoneZeroCheckInService.kt b/data/src/main/kotlin/com/joeloewi/croissant/data/api/dao/ZenlessZoneZeroCheckInService.kt new file mode 100644 index 00000000..94c34e30 --- /dev/null +++ b/data/src/main/kotlin/com/joeloewi/croissant/data/api/dao/ZenlessZoneZeroCheckInService.kt @@ -0,0 +1,35 @@ +package com.joeloewi.croissant.data.api.dao + +import com.joeloewi.croissant.data.api.model.response.AttendanceResponse +import com.skydoves.sandwich.ApiResponse +import retrofit2.http.Body +import retrofit2.http.Header +import retrofit2.http.POST +import java.util.Locale + +/* + * Copyright (C) 2024 joeloewi + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +interface ZenlessZoneZeroCheckInService { + + @POST("event/luna/zzz/os/sign") + suspend fun attend( + @Header("Cookie") cookie: String, + @Body params: Map = mapOf( + "act_id" to "e202406031448091", + "lang" to Locale.getDefault().toLanguageTag().lowercase() + ) + ): ApiResponse +} \ No newline at end of file diff --git a/data/src/main/kotlin/com/joeloewi/croissant/data/common/GameIntentGenerator.kt b/data/src/main/kotlin/com/joeloewi/croissant/data/common/GameIntentGenerator.kt index 0ed4ba8c..c6fcc067 100644 --- a/data/src/main/kotlin/com/joeloewi/croissant/data/common/GameIntentGenerator.kt +++ b/data/src/main/kotlin/com/joeloewi/croissant/data/common/GameIntentGenerator.kt @@ -35,6 +35,12 @@ fun generateGameIntent( } } + HoYoLABGame.ZenlessZoneZero -> { + with("com.HoYoverse.Nap") { + this to "market://details?id=${this}".toUri() + } + } + HoYoLABGame.Unknown -> { "" to Uri.EMPTY } diff --git a/data/src/main/kotlin/com/joeloewi/croissant/data/di/ApiModule.kt b/data/src/main/kotlin/com/joeloewi/croissant/data/di/ApiModule.kt index 99b585ff..7fc92912 100644 --- a/data/src/main/kotlin/com/joeloewi/croissant/data/di/ApiModule.kt +++ b/data/src/main/kotlin/com/joeloewi/croissant/data/di/ApiModule.kt @@ -21,6 +21,7 @@ import com.joeloewi.croissant.data.api.dao.ArcaLiveAppService import com.joeloewi.croissant.data.api.dao.CheckInService import com.joeloewi.croissant.data.api.dao.GenshinImpactCheckInService import com.joeloewi.croissant.data.api.dao.HoYoLABService +import com.joeloewi.croissant.data.api.dao.ZenlessZoneZeroCheckInService import com.joeloewi.croissant.data.api.model.response.AttendanceResponse import com.joeloewi.croissant.data.api.model.response.ChangeDataSwitchResponse import com.joeloewi.croissant.data.api.model.response.GameRecordCardResponse @@ -145,4 +146,12 @@ object ApiModule { .baseUrl("https://arca.live/api/app/") .build() .create() + + @Singleton + @Provides + fun providesZenlessZoneZeroCheckInService(retrofitBuilder: Retrofit.Builder): ZenlessZoneZeroCheckInService = + retrofitBuilder + .baseUrl("https://sg-act-nap-api.hoyolab.com") + .build() + .create() } \ No newline at end of file diff --git a/data/src/main/kotlin/com/joeloewi/croissant/data/repository/CheckInRepositoryImpl.kt b/data/src/main/kotlin/com/joeloewi/croissant/data/repository/CheckInRepositoryImpl.kt index 94d25d03..520e394d 100644 --- a/data/src/main/kotlin/com/joeloewi/croissant/data/repository/CheckInRepositoryImpl.kt +++ b/data/src/main/kotlin/com/joeloewi/croissant/data/repository/CheckInRepositoryImpl.kt @@ -43,4 +43,10 @@ class CheckInRepositoryImpl @Inject constructor( ): Result = checkInDataSource.runCatching { attendCheckInHonkaiImpact3rd(cookie).getOrThrow().throwIfNotOk() } + + override suspend fun attendCheckInZenlessZoneZero( + cookie: String + ): Result = checkInDataSource.runCatching { + attendCheckInZenlessZoneZero(cookie).getOrThrow().throwIfNotOk() + } } \ No newline at end of file diff --git a/data/src/main/kotlin/com/joeloewi/croissant/data/repository/remote/CheckInDataSource.kt b/data/src/main/kotlin/com/joeloewi/croissant/data/repository/remote/CheckInDataSource.kt index d24dd85e..af0d9579 100644 --- a/data/src/main/kotlin/com/joeloewi/croissant/data/repository/remote/CheckInDataSource.kt +++ b/data/src/main/kotlin/com/joeloewi/croissant/data/repository/remote/CheckInDataSource.kt @@ -26,4 +26,6 @@ interface CheckInDataSource { suspend fun attendCheckInGenshinImpact(cookie: String): ApiResponse suspend fun attendCheckInHonkaiImpact3rd(cookie: String): ApiResponse + + suspend fun attendCheckInZenlessZoneZero(cookie: String): ApiResponse } \ No newline at end of file diff --git a/data/src/main/kotlin/com/joeloewi/croissant/data/repository/remote/impl/ArcaLiveAppDataSourceImpl.kt b/data/src/main/kotlin/com/joeloewi/croissant/data/repository/remote/impl/ArcaLiveAppDataSourceImpl.kt index 5fb8a164..97d17c7e 100644 --- a/data/src/main/kotlin/com/joeloewi/croissant/data/repository/remote/impl/ArcaLiveAppDataSourceImpl.kt +++ b/data/src/main/kotlin/com/joeloewi/croissant/data/repository/remote/impl/ArcaLiveAppDataSourceImpl.kt @@ -76,6 +76,18 @@ class ArcaLiveAppDataSourceImpl @Inject constructor( } } + HoYoLABGame.ZenlessZoneZero -> { + arcaLiveAppService.getArticle( + slug = "zenlesszonezero", + articleId = 109976603 + ).mapSuccess { + Jsoup.parse(content) + .apply { select("img").remove() } + .html() + .replace("https://oo.pe/", "") + } + } + HoYoLABGame.TearsOfThemis, HoYoLABGame.Unknown -> throw IllegalStateException() } } diff --git a/data/src/main/kotlin/com/joeloewi/croissant/data/repository/remote/impl/CheckInDataSourceImpl.kt b/data/src/main/kotlin/com/joeloewi/croissant/data/repository/remote/impl/CheckInDataSourceImpl.kt index 1201f55c..1b4dfb93 100644 --- a/data/src/main/kotlin/com/joeloewi/croissant/data/repository/remote/impl/CheckInDataSourceImpl.kt +++ b/data/src/main/kotlin/com/joeloewi/croissant/data/repository/remote/impl/CheckInDataSourceImpl.kt @@ -18,6 +18,7 @@ package com.joeloewi.croissant.data.repository.remote.impl import com.joeloewi.croissant.data.api.dao.CheckInService import com.joeloewi.croissant.data.api.dao.GenshinImpactCheckInService +import com.joeloewi.croissant.data.api.dao.ZenlessZoneZeroCheckInService import com.joeloewi.croissant.data.api.model.response.AttendanceResponse import com.joeloewi.croissant.data.repository.remote.CheckInDataSource import com.joeloewi.croissant.data.util.runAndRetryWithExponentialBackOff @@ -28,7 +29,8 @@ import javax.inject.Inject class CheckInDataSourceImpl @Inject constructor( private val checkInService: CheckInService, - private val genshinImpactCheckInService: GenshinImpactCheckInService + private val genshinImpactCheckInService: GenshinImpactCheckInService, + private val zenlessZoneZeroCheckInService: ZenlessZoneZeroCheckInService ) : CheckInDataSource { override suspend fun attend(actId: String, cookie: String): ApiResponse = @@ -53,4 +55,12 @@ class CheckInDataSourceImpl @Inject constructor( checkInService.attendCheckInHonkaiImpact3rd(cookie = cookie) } } + + override suspend fun attendCheckInZenlessZoneZero( + cookie: String + ): ApiResponse = withContext(Dispatchers.IO) { + runAndRetryWithExponentialBackOff { + zenlessZoneZeroCheckInService.attend(cookie = cookie) + } + } } \ No newline at end of file diff --git a/domain/src/main/kotlin/com/joeloewi/croissant/domain/common/HoYoLABGame.kt b/domain/src/main/kotlin/com/joeloewi/croissant/domain/common/HoYoLABGame.kt index f9ceee51..542c40ff 100644 --- a/domain/src/main/kotlin/com/joeloewi/croissant/domain/common/HoYoLABGame.kt +++ b/domain/src/main/kotlin/com/joeloewi/croissant/domain/common/HoYoLABGame.kt @@ -23,19 +23,23 @@ enum class HoYoLABGame( ) { HonkaiImpact3rd( gameId = 1, - gameIconUrl = "https://webstatic-sea.hoyolab.com/communityweb/business/bh3_hoyoverse.png", + gameIconUrl = "https://hyl-static-res-prod.hoyolab.com/communityweb/business/bh3_hoyoverse.png", ), GenshinImpact( gameId = 2, - gameIconUrl = "https://webstatic-sea.hoyolab.com/communityweb/business/ys_hoyoverse.png", + gameIconUrl = "https://hyl-static-res-prod.hoyolab.com/communityweb/business/ys_hoyoverse.png", ), TearsOfThemis( gameId = 4, - gameIconUrl = "https://webstatic-sea.hoyolab.com/communityweb/business/nxx_hoyoverse.png", + gameIconUrl = "https://hyl-static-res-prod.hoyolab.com/communityweb/business/nxx_hoyoverse.png", ), HonkaiStarRail( gameId = 6, - gameIconUrl = "https://webstatic-sea.hoyolab.com/communityweb/business/starrail_hoyoverse.png" + gameIconUrl = "https://hyl-static-res-prod.hoyolab.com/communityweb/business/starrail_hoyoverse.png" + ), + ZenlessZoneZero( + gameId = 8, + gameIconUrl = "https://hyl-static-res-prod.hoyolab.com/communityweb/business/nap.png" ), Unknown( gameId = -1, diff --git a/domain/src/main/kotlin/com/joeloewi/croissant/domain/repository/CheckInRepository.kt b/domain/src/main/kotlin/com/joeloewi/croissant/domain/repository/CheckInRepository.kt index 5e0f0f3b..1b597d1c 100644 --- a/domain/src/main/kotlin/com/joeloewi/croissant/domain/repository/CheckInRepository.kt +++ b/domain/src/main/kotlin/com/joeloewi/croissant/domain/repository/CheckInRepository.kt @@ -25,4 +25,6 @@ interface CheckInRepository { suspend fun attendCheckInGenshinImpact(cookie: String): Result suspend fun attendCheckInHonkaiImpact3rd(cookie: String): Result + + suspend fun attendCheckInZenlessZoneZero(cookie: String): Result } \ No newline at end of file diff --git a/domain/src/main/kotlin/com/joeloewi/croissant/domain/usecase/CheckInUseCase.kt b/domain/src/main/kotlin/com/joeloewi/croissant/domain/usecase/CheckInUseCase.kt index 9ed60d1f..bacec219 100644 --- a/domain/src/main/kotlin/com/joeloewi/croissant/domain/usecase/CheckInUseCase.kt +++ b/domain/src/main/kotlin/com/joeloewi/croissant/domain/usecase/CheckInUseCase.kt @@ -30,7 +30,7 @@ sealed class CheckInUseCase { ) = checkInRepository.attend(actId, cookie) } - class AttendCheckInHonkaiStarRail @Inject constructor( + class AttendCheckInHonkaiStarRailUseCase @Inject constructor( private val checkInRepository: CheckInRepository ) : CheckInUseCase() { suspend operator fun invoke( @@ -54,4 +54,12 @@ sealed class CheckInUseCase { cookie: String ) = checkInRepository.attendCheckInGenshinImpact(cookie) } + + class AttendCheckInZenlessZoneZeroUseCase @Inject constructor( + private val checkInRepository: CheckInRepository + ) : CheckInUseCase() { + suspend operator fun invoke( + cookie: String + ) = checkInRepository.attendCheckInZenlessZoneZero(cookie) + } } \ No newline at end of file