Skip to content

Commit

Permalink
Merge pull request #407 from joeloewi7178/development
Browse files Browse the repository at this point in the history
Development
  • Loading branch information
joeloewi7178 authored Apr 19, 2024
2 parents 7a87121 + d7f5820 commit 7c1c7bb
Show file tree
Hide file tree
Showing 15 changed files with 166 additions and 135 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/on_push_development.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
uses: actions/checkout@v4.1.1

- name: set up JDK 17
uses: actions/setup-java@v4.1.0
uses: actions/setup-java@v4.2.1
with:
distribution: 'zulu' # See 'Supported distributions' for available options
java-version: '17'
Expand Down
2 changes: 1 addition & 1 deletion .idea/kotlinc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 4 additions & 5 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ android {

defaultConfig {
applicationId = "com.joeloewi.croissant"
versionCode = 59
versionCode = 60
versionName = "1.3.0"
targetSdk = 34

Expand Down Expand Up @@ -68,9 +68,9 @@ baselineProfile {
}

dependencies {
implementation(project(":data"))
implementation(project(":domain"))
baselineProfile(project(":baselineprofile"))
implementation(projects.data)
implementation(projects.domain)
baselineProfile(projects.baselineprofile)

implementation(libs.androidx.core.ktx)
implementation(libs.android.material)
Expand Down Expand Up @@ -111,7 +111,6 @@ dependencies {
implementation(libs.accompanist.webview)
implementation(libs.accompanist.pager.indicators)
implementation(libs.accompanist.swiperefresh)
implementation(libs.accompanist.themeadapter.material3)
implementation(libs.accompanist.navigation.material)

//work
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ import androidx.compose.material3.rememberSwipeToDismissBoxState
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.snapshotFlow
Expand All @@ -58,19 +58,22 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.scale
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.core.os.bundleOf
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.flowWithLifecycle
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemKey
import androidx.work.ExistingWorkPolicy
import androidx.work.WorkInfo
import androidx.work.WorkManager
import androidx.work.WorkQuery
import coil.compose.AsyncImage
import coil.request.ImageRequest
import com.google.firebase.Firebase
Expand Down Expand Up @@ -252,12 +255,14 @@ fun AttendanceWithGamesItem(
{ attendance ->
Firebase.analytics.logEvent("instant_attend_click", bundleOf())

val oneTimeWork =
AttendCheckInEventWorker.buildOneTimeWork(attendanceId = attendance.id)
val oneTimeWork = AttendCheckInEventWorker.buildOneTimeWork(
attendanceId = attendance.id,
isInstantAttendance = true
)

WorkManager.getInstance(context).beginUniqueWork(
attendance.oneTimeAttendCheckInEventWorkerName.toString(),
ExistingWorkPolicy.APPEND_OR_REPLACE,
ExistingWorkPolicy.REPLACE,
oneTimeWork
).enqueue()

Expand Down Expand Up @@ -401,14 +406,24 @@ private fun DismissContent(
},
trailingContent = {
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
val workerName =
attendanceWithGames().value.attendance.oneTimeAttendCheckInEventWorkerName.toString()
val isRunning by remember(context, workerName) {
val isRunningFlow = remember(context, workerName) {
WorkManager.getInstance(context)
.getWorkInfosForUniqueWorkFlow(workerName)
.map { list -> list.any { it.state == WorkInfo.State.RUNNING } }
.getWorkInfosFlow(
WorkQuery.Builder
.fromUniqueWorkNames(listOf(workerName))
.addStates(listOf(WorkInfo.State.RUNNING))
.build()
)
.catch { }
.map { it.isNotEmpty() }
.flowOn(Dispatchers.IO)
}.collectAsState(initial = false)
}
val isRunning by produceState(initialValue = false) {
isRunningFlow.flowWithLifecycle(lifecycleOwner.lifecycle).collect { value = it }
}

IconButton(
enabled = !isRunning,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.ServiceInfo
import android.net.Uri
import android.os.Build
import androidx.core.app.NotificationChannelCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
Expand Down Expand Up @@ -216,21 +215,15 @@ class NotificationGenerator(
.setContentTitle(context.getString(R.string.attendance_foreground_notification_title))
.setContentText(context.getString(R.string.wait_for_a_moment))
.setSmallIcon(R.drawable.ic_baseline_bakery_dining_24)
.setForegroundServiceBehavior(Notification.FOREGROUND_SERVICE_IMMEDIATE)
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
.setCategory(NotificationCompat.CATEGORY_SERVICE)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.build()
.run {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
return@run ForegroundInfo(
notificationId,
this,
ServiceInfo.FOREGROUND_SERVICE_TYPE_SHORT_SERVICE
)
}
return@run ForegroundInfo(
ForegroundInfo(
notificationId,
this
this,
ServiceInfo.FOREGROUND_SERVICE_TYPE_SHORT_SERVICE
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class AttendCheckInEventWorker @AssistedInject constructor(
private val _firstTriggeredTimestamp by lazy {
inputData.getLong(FIRST_TRIGGERED_TIMESTAMP, Instant.now().toEpochMilli())
}
private val _isInstantAttendance by lazy { inputData.getBoolean(IS_INSTANT_ATTENDANCE, false) }

override suspend fun getForegroundInfo(): ForegroundInfo =
notificationGenerator.createForegroundInfo(_attendanceId.toInt())
Expand Down Expand Up @@ -205,21 +206,41 @@ class AttendCheckInEventWorker @AssistedInject constructor(
is HoYoLABUnsuccessfulResponseException -> {
when (val retCode = HoYoLABRetCode.findByCode(cause.retCode)) {
HoYoLABRetCode.TooManyRequests, HoYoLABRetCode.TooManyRequestsGenshinImpact -> {
//do not make log, not to skip this game when retry

notificationGenerator.createAttendanceRetryScheduledNotification(
nickname = attendanceWithGames.attendance.nickname,
contentText = context.getString(R.string.attendance_retry_too_many_requests_error)
).let { notification ->
notificationGenerator.safeNotify(
UUID.randomUUID().toString(),
game.type.gameId,
notification
)
if (_isInstantAttendance) {
//make log and do not retry if this work was enqueued by clicking instant attendance

addFailureLog(_attendanceId, game.type, cause)

createUnsuccessfulAttendanceNotification(
nickname = attendanceWithGames.attendance.nickname,
hoYoLABGame = game.type,
region = game.region,
hoYoLABUnsuccessfulResponseException = cause
).let { notification ->
notificationGenerator.safeNotify(
UUID.randomUUID().toString(),
game.type.gameId,
notification
)
}

Result.success()
} else {
//do not make log, not to skip this game when retry
notificationGenerator.createAttendanceRetryScheduledNotification(
nickname = attendanceWithGames.attendance.nickname,
contentText = context.getString(R.string.attendance_retry_too_many_requests_error)
).let { notification ->
notificationGenerator.safeNotify(
UUID.randomUUID().toString(),
game.type.gameId,
notification
)
}

//good to retry
Result.retry()
}

//good to retry
Result.retry()
}

else -> {
Expand All @@ -238,11 +259,11 @@ class AttendCheckInEventWorker @AssistedInject constructor(
)
}

if (!listOf(
if (retCode !in listOf(
HoYoLABRetCode.AlreadyCheckedIn,
HoYoLABRetCode.CharacterNotExists,
HoYoLABRetCode.LoginFailed
).contains(retCode)
)
) {
//we don't know which error was occurred
//record this error for monitoring
Expand All @@ -265,34 +286,54 @@ class AttendCheckInEventWorker @AssistedInject constructor(
}

else -> {
//do not make log, not to pass this game when retry
notificationGenerator.createAttendanceRetryScheduledNotification(
nickname = attendanceWithGames.attendance.nickname
).let { notification ->
notificationGenerator.safeNotify(
UUID.randomUUID().toString(),
game.type.gameId,
notification
)
}
if (_isInstantAttendance) {
//make log and do not retry if this work was enqueued by clicking instant attendance
addFailureLog(_attendanceId, game.type, cause)

notificationGenerator.createUnsuccessfulAttendanceNotification(
nickname = attendanceWithGames.attendance.nickname,
hoYoLABGame = game.type,
attendanceId = _attendanceId
).let { notification ->
notificationGenerator.safeNotify(
UUID.randomUUID().toString(),
game.type.gameId,
notification
)
}

//these errors are not hoyolab server's errors, but networks errors, generally
//do retry
Firebase.crashlytics.log("runAttemptCount: $runAttemptCount")
Result.retry()
Result.success()
} else {
//do not make log, not to pass this game when retry
notificationGenerator.createAttendanceRetryScheduledNotification(
nickname = attendanceWithGames.attendance.nickname
).let { notification ->
notificationGenerator.safeNotify(
UUID.randomUUID().toString(),
game.type.gameId,
notification
)
}

//these errors are not hoyolab server's errors, but networks errors, generally
//do retry
Firebase.crashlytics.log("runAttemptCount: $runAttemptCount")
Result.retry()
}
}
}
}
)
}
}.fold(
onSuccess = { results ->
if (results.contains(Result.retry())) {
//do not retry if this work was enqueued by clicking instant attendance
if (results.contains(Result.retry()) && !_isInstantAttendance) {
return@withContext Result.retry()
}

runAttemptCount.takeIf { count -> count > 0 }?.let {
Firebase.crashlytics.log("success after run attempts: $it")
Firebase.crashlytics.log("succeed after run attempts: $it")
}

Result.success()
Expand All @@ -313,18 +354,21 @@ class AttendCheckInEventWorker @AssistedInject constructor(
companion object {
const val ATTENDANCE_ID = "attendanceId"
const val FIRST_TRIGGERED_TIMESTAMP = "triggeredTimestamp"
const val IS_INSTANT_ATTENDANCE = "isInstantAttendance"

fun buildOneTimeWork(
attendanceId: Long,
triggeredTimestamp: Long = Instant.now().toEpochMilli(),
isInstantAttendance: Boolean = false,
constraints: Constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
) = OneTimeWorkRequestBuilder<AttendCheckInEventWorker>()
.setInputData(
workDataOf(
ATTENDANCE_ID to attendanceId,
FIRST_TRIGGERED_TIMESTAMP to triggeredTimestamp
FIRST_TRIGGERED_TIMESTAMP to triggeredTimestamp,
IS_INSTANT_ATTENDANCE to isInstantAttendance
)
)
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class CheckSessionWorker @AssistedInject constructor(
)

runAttemptCount.takeIf { count -> count > 0 }?.let {
Firebase.crashlytics.log("success after run attempts: $it")
Firebase.crashlytics.log("succeed after run attempts: $it")
}

Result.success()
Expand Down
5 changes: 4 additions & 1 deletion data/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ android {
}

dependencies {
implementation(project(":domain"))
implementation(projects.domain)

implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
Expand All @@ -31,9 +31,12 @@ dependencies {
androidTestImplementation(libs.androidx.test.espresso.core)

//retrofit2
implementation(platform(libs.retrofit.bom))
implementation(libs.retrofit)
implementation(libs.retrofit.converter.moshi)
implementation(libs.retrofit.converter.scalars)
ksp(libs.retrofit.response.type.keeper)

implementation(platform(libs.okhttp3.bom))
implementation(libs.okhttp3.logging.interceptor)
implementation(libs.okhttp3.dnsoverhttps)
Expand Down
19 changes: 1 addition & 18 deletions data/consumer-proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -1,18 +1 @@
-keep class * extends com.google.protobuf.GeneratedMessageLite { *; }

-dontwarn okhttp3.internal.platform.**
-dontwarn org.conscrypt.**
-dontwarn org.bouncycastle.**
-dontwarn org.openjsse.**

# With R8 full mode generic signatures are stripped for classes that are not
# kept. Suspend functions are wrapped in continuations where the type argument
# is used.
-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation

# R8 full mode strips generic signatures from return types if not kept.
-if interface * { @retrofit2.http.* public *** *(...); }
-keep,allowoptimization,allowshrinking,allowobfuscation class <3>

# With R8 full mode generic signatures are stripped for classes that are not kept.
-keep,allowobfuscation,allowshrinking class retrofit2.Response
-keep class * extends com.google.protobuf.GeneratedMessageLite { *; }
Loading

0 comments on commit 7c1c7bb

Please sign in to comment.