TiebaLite/app/src/main/java/com/huanchengfly/tieba/post/MainActivityV2.kt

254 lines
11 KiB
Kotlin
Raw Normal View History

2022-09-24 16:05:21 +08:00
package com.huanchengfly.tieba.post
2022-10-16 12:06:27 +08:00
import android.annotation.SuppressLint
import android.app.job.JobInfo
import android.app.job.JobScheduler
import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Intent
2022-09-24 16:05:21 +08:00
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import androidx.compose.animation.AnimatedContentScope
import androidx.compose.animation.ExperimentalAnimationApi
2023-01-26 13:10:20 +08:00
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.VisibilityThreshold
import androidx.compose.animation.core.spring
2023-01-25 22:26:23 +08:00
import androidx.compose.foundation.shape.RoundedCornerShape
2022-09-24 16:05:21 +08:00
import androidx.compose.material.Surface
import androidx.compose.runtime.Composable
2023-01-25 22:26:23 +08:00
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.staticCompositionLocalOf
2022-09-24 16:05:21 +08:00
import androidx.compose.ui.Alignment
2023-01-26 13:10:20 +08:00
import androidx.compose.ui.unit.IntOffset
2023-01-25 22:26:23 +08:00
import androidx.compose.ui.unit.dp
2023-01-01 21:26:46 +08:00
import androidx.core.content.ContextCompat
2022-09-24 16:05:21 +08:00
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
2023-01-07 16:00:39 +08:00
import androidx.lifecycle.flowWithLifecycle
2022-09-24 16:05:21 +08:00
import androidx.lifecycle.lifecycleScope
2023-01-25 22:26:23 +08:00
import androidx.navigation.plusAssign
2023-01-07 16:00:39 +08:00
import androidx.window.layout.FoldingFeature
import androidx.window.layout.WindowInfoTracker
2023-01-25 22:26:23 +08:00
import com.google.accompanist.navigation.animation.rememberAnimatedNavController
2022-09-24 16:05:21 +08:00
import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi
2023-01-25 22:26:23 +08:00
import com.google.accompanist.navigation.material.ModalBottomSheetLayout
import com.google.accompanist.navigation.material.rememberBottomSheetNavigator
2022-09-24 16:05:21 +08:00
import com.google.accompanist.systemuicontroller.SystemUiController
import com.huanchengfly.tieba.post.api.retrofit.exception.getErrorMessage
import com.huanchengfly.tieba.post.arch.BaseComposeActivity
2022-10-16 12:06:27 +08:00
import com.huanchengfly.tieba.post.services.NotifyJobService
2022-09-24 16:05:21 +08:00
import com.huanchengfly.tieba.post.ui.common.theme.compose.ExtendedTheme
import com.huanchengfly.tieba.post.ui.page.NavGraphs
import com.huanchengfly.tieba.post.ui.page.destinations.MainPageDestination
2023-01-07 16:00:39 +08:00
import com.huanchengfly.tieba.post.ui.utils.DevicePosture
import com.huanchengfly.tieba.post.ui.utils.isBookPosture
import com.huanchengfly.tieba.post.ui.utils.isSeparating
2022-09-24 16:05:21 +08:00
import com.huanchengfly.tieba.post.utils.AccountUtil
2022-10-16 12:06:27 +08:00
import com.huanchengfly.tieba.post.utils.JobServiceUtil
2022-09-24 16:05:21 +08:00
import com.huanchengfly.tieba.post.utils.PermissionUtils
import com.huanchengfly.tieba.post.utils.TiebaUtil
2023-01-26 12:30:41 +08:00
import com.huanchengfly.tieba.post.utils.newIntentFilter
2022-09-24 16:05:21 +08:00
import com.huanchengfly.tieba.post.utils.requestPermission
import com.ramcosta.composedestinations.DestinationsNavHost
import com.ramcosta.composedestinations.animations.defaults.RootNavGraphDefaultAnimations
import com.ramcosta.composedestinations.animations.rememberAnimatedNavHostEngine
import com.ramcosta.composedestinations.navigation.dependency
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Dispatchers
2023-01-25 22:26:23 +08:00
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
2023-01-07 16:00:39 +08:00
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
2022-09-24 16:05:21 +08:00
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flowOn
2023-01-07 16:00:39 +08:00
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
2022-09-24 16:05:21 +08:00
import kotlinx.coroutines.launch
2023-01-25 22:26:23 +08:00
val LocalNotificationCountFlow =
staticCompositionLocalOf<Flow<Int>> { throw IllegalStateException("not allowed here!") }
2022-09-24 16:05:21 +08:00
@AndroidEntryPoint
class MainActivityV2 : BaseComposeActivity() {
private val handler = Handler(Looper.getMainLooper())
2022-10-16 12:06:27 +08:00
private val newMessageReceiver: BroadcastReceiver = NewMessageReceiver()
2022-09-24 16:05:21 +08:00
2023-01-25 22:26:23 +08:00
private val notificationCountFlow: MutableSharedFlow<Int> =
MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
2023-01-07 16:00:39 +08:00
private val devicePostureFlow: StateFlow<DevicePosture> by lazy {
WindowInfoTracker.getOrCreate(this)
.windowLayoutInfo(this)
.flowWithLifecycle(lifecycle)
.map { layoutInfo ->
val foldingFeature =
layoutInfo.displayFeatures
.filterIsInstance<FoldingFeature>()
.firstOrNull()
when {
isBookPosture(foldingFeature) ->
DevicePosture.BookPosture(foldingFeature.bounds)
isSeparating(foldingFeature) ->
DevicePosture.Separating(foldingFeature.bounds, foldingFeature.orientation)
else -> DevicePosture.NormalPosture
}
}
.stateIn(
scope = lifecycleScope,
started = SharingStarted.Eagerly,
initialValue = DevicePosture.NormalPosture
)
}
2022-09-24 16:05:21 +08:00
private fun fetchAccount() {
lifecycleScope.launch(Dispatchers.Main) {
if (AccountUtil.isLoggedIn()) {
AccountUtil.fetchAccountFlow()
.flowOn(Dispatchers.IO)
.catch { e ->
toastShort(e.getErrorMessage())
e.printStackTrace()
}
.collect()
}
}
}
private fun requestNotificationPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && AccountUtil.isLoggedIn()) {
requestPermission {
permissions = listOf(PermissionUtils.POST_NOTIFICATIONS)
description = getString(R.string.desc_permission_post_notifications)
}
}
}
private fun initAutoSign() {
runCatching {
TiebaUtil.initAutoSign(this)
}
}
override fun onStart() {
super.onStart()
2022-10-16 12:06:27 +08:00
runCatching {
2023-01-01 21:26:46 +08:00
ContextCompat.registerReceiver(
this,
2022-10-16 12:06:27 +08:00
newMessageReceiver,
2023-01-26 12:30:41 +08:00
newIntentFilter(NotifyJobService.ACTION_NEW_MESSAGE),
2023-01-01 21:26:46 +08:00
ContextCompat.RECEIVER_NOT_EXPORTED
2022-10-16 12:06:27 +08:00
)
startService(Intent(this, NotifyJobService::class.java))
val builder = JobInfo.Builder(
JobServiceUtil.getJobId(this),
ComponentName(this, NotifyJobService::class.java)
)
.setPersisted(true)
.setPeriodic(30 * 60 * 1000L)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
val jobScheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
jobScheduler.schedule(builder.build())
}
2022-09-24 16:05:21 +08:00
handler.postDelayed({
requestNotificationPermission()
}, 100)
}
override fun onCreate(savedInstanceState: Bundle?) {
installSplashScreen()
super.onCreate(savedInstanceState)
}
override fun onCreateContent(systemUiController: SystemUiController) {
super.onCreateContent(systemUiController)
fetchAccount()
initAutoSign()
}
@OptIn(ExperimentalAnimationApi::class, ExperimentalMaterialNavigationApi::class)
@Composable
override fun createContent() {
2023-01-25 22:26:23 +08:00
CompositionLocalProvider(LocalNotificationCountFlow provides notificationCountFlow) {
Surface(
color = ExtendedTheme.colors.background
) {
2023-01-26 13:10:20 +08:00
val animationSpec = spring(
stiffness = Spring.StiffnessMediumLow,
visibilityThreshold = IntOffset.VisibilityThreshold
)
2023-01-25 22:26:23 +08:00
val engine = rememberAnimatedNavHostEngine(
navHostContentAlignment = Alignment.TopStart,
rootDefaultAnimations = RootNavGraphDefaultAnimations(
enterTransition = {
slideIntoContainer(
AnimatedContentScope.SlideDirection.Start,
2023-01-26 13:10:20 +08:00
animationSpec = animationSpec,
initialOffset = { it }
)
2023-01-25 22:26:23 +08:00
},
exitTransition = {
slideOutOfContainer(
AnimatedContentScope.SlideDirection.End,
2023-01-26 13:10:20 +08:00
animationSpec = animationSpec,
targetOffset = { -it }
)
2023-01-25 22:26:23 +08:00
},
popEnterTransition = {
slideIntoContainer(
AnimatedContentScope.SlideDirection.Start,
2023-01-26 13:10:20 +08:00
animationSpec = animationSpec,
initialOffset = { -it }
)
2023-01-25 22:26:23 +08:00
},
popExitTransition = {
slideOutOfContainer(
AnimatedContentScope.SlideDirection.End,
2023-01-26 13:10:20 +08:00
animationSpec = animationSpec,
targetOffset = { it }
)
2023-01-25 22:26:23 +08:00
},
),
)
val navController = rememberAnimatedNavController()
2023-01-26 13:10:20 +08:00
val bottomSheetNavigator =
rememberBottomSheetNavigator(animationSpec = spring(stiffness = Spring.StiffnessMediumLow))
2023-01-25 22:26:23 +08:00
navController.navigatorProvider += bottomSheetNavigator
ModalBottomSheetLayout(
bottomSheetNavigator = bottomSheetNavigator,
sheetShape = RoundedCornerShape(16.dp)
) {
DestinationsNavHost(
navController = navController,
navGraph = NavGraphs.root,
engine = engine,
dependenciesContainerBuilder = {
dependency(MainPageDestination) { this@MainActivityV2 }
dependency(devicePostureFlow)
}
)
2022-09-24 16:05:21 +08:00
}
2023-01-25 22:26:23 +08:00
}
2022-09-24 16:05:21 +08:00
}
}
2022-10-16 12:06:27 +08:00
private inner class NewMessageReceiver : BroadcastReceiver() {
@SuppressLint("RestrictedApi")
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == NotifyJobService.ACTION_NEW_MESSAGE) {
val channel = intent.getStringExtra("channel")
val count = intent.getIntExtra("count", 0)
if (channel != null && channel == NotifyJobService.CHANNEL_TOTAL) {
2023-01-25 22:26:23 +08:00
lifecycleScope.launch {
notificationCountFlow.emit(count)
}
2022-10-16 12:06:27 +08:00
}
}
}
}
2022-09-24 16:05:21 +08:00
}