加入短视频
This commit is contained in:
@@ -62,7 +62,12 @@ dependencies {
|
||||
implementation(libs.androidx.navigation.compose)
|
||||
implementation (libs.androidx.paging.compose)
|
||||
implementation(libs.androidx.paging.runtime)
|
||||
implementation("com.google.maps.android:maps-compose:4.3.3")
|
||||
implementation(libs.maps.compose)
|
||||
implementation(libs.accompanist.systemuicontroller)
|
||||
implementation(libs.androidx.media3.exoplayer) // 核心播放器
|
||||
implementation(libs.androidx.media3.ui) // UI组件(可选)
|
||||
implementation(libs.androidx.media3.session)
|
||||
implementation(libs.androidx.activity.ktx) // 用于媒体会话(可选)
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
androidTestImplementation(libs.androidx.espresso.core)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.aiosman.riderpro
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.StatusBarManager
|
||||
import android.os.Bundle
|
||||
import android.widget.HorizontalScrollView
|
||||
@@ -8,6 +9,9 @@ import androidx.activity.ComponentActivity
|
||||
import androidx.activity.SystemBarStyle
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
@@ -60,6 +64,7 @@ import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.aiosman.riderpro.ui.theme.RiderProTheme
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import com.google.android.gms.maps.model.CameraPosition
|
||||
import com.google.android.gms.maps.model.LatLng
|
||||
import com.google.maps.android.compose.GoogleMap
|
||||
@@ -82,7 +87,14 @@ class MainActivity : ComponentActivity() {
|
||||
@Composable
|
||||
fun NavigationController(navController: NavHostController){
|
||||
NavHost(
|
||||
navController = navController, startDestination = NavigationItem.Home.route){
|
||||
navController = navController,
|
||||
startDestination = NavigationItem.Home.route,
|
||||
enterTransition = {
|
||||
fadeIn(animationSpec = tween(300))
|
||||
},
|
||||
exitTransition = {
|
||||
fadeOut(animationSpec = tween(300))
|
||||
}){
|
||||
composable(route = NavigationItem.Home.route){
|
||||
Home()
|
||||
}
|
||||
@@ -93,7 +105,7 @@ fun NavigationController(navController: NavHostController){
|
||||
Add()
|
||||
}
|
||||
composable(route = NavigationItem.Message.route){
|
||||
Message()
|
||||
Video()
|
||||
}
|
||||
composable(route = NavigationItem.Profile.route){
|
||||
Profile()
|
||||
@@ -124,13 +136,31 @@ fun Navigation(){
|
||||
){
|
||||
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
||||
val currentRoute = navBackStackEntry?.destination?.route
|
||||
val systemUiController = rememberSystemUiController()
|
||||
item.forEach{ it ->
|
||||
NavigationBarItem(
|
||||
selected = currentRoute == it.route ,
|
||||
onClick = {
|
||||
if(currentRoute != it.route){
|
||||
navController.navigate(it.route)
|
||||
}
|
||||
if(currentRoute != it.route){
|
||||
navController.navigate(it.route)
|
||||
}
|
||||
when (it.route) {
|
||||
NavigationItem.Add.route -> {
|
||||
systemUiController.setSystemBarsColor(
|
||||
color = Color.Black
|
||||
)
|
||||
}
|
||||
NavigationItem.Message.route -> {
|
||||
systemUiController.setSystemBarsColor(
|
||||
color = Color.Black
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
systemUiController.setSystemBarsColor(
|
||||
color = Color.Transparent
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
colors = NavigationBarItemColors(
|
||||
selectedIconColor = Color.Red,
|
||||
@@ -193,6 +223,17 @@ fun Add(){
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Video(){
|
||||
Column (
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.Top,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
){
|
||||
ShortVideo()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Message(){
|
||||
Column (
|
||||
|
||||
14
app/src/main/java/com/aiosman/riderpro/ModifierExp.kt
Normal file
14
app/src/main/java/com/aiosman/riderpro/ModifierExp.kt
Normal file
@@ -0,0 +1,14 @@
|
||||
package com.aiosman.riderpro
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
|
||||
inline fun Modifier.noRippleClickable(crossinline onClick: () -> Unit): Modifier = composed {
|
||||
this.clickable(indication = null,
|
||||
interactionSource = remember { MutableInteractionSource() }) {
|
||||
onClick()
|
||||
}
|
||||
}
|
||||
232
app/src/main/java/com/aiosman/riderpro/Pager.kt
Normal file
232
app/src/main/java/com/aiosman/riderpro/Pager.kt
Normal file
@@ -0,0 +1,232 @@
|
||||
package com.aiosman.riderpro
|
||||
|
||||
import androidx.compose.animation.core.Animatable
|
||||
import androidx.compose.foundation.gestures.Orientation
|
||||
import androidx.compose.foundation.gestures.draggable
|
||||
import androidx.compose.foundation.gestures.rememberDraggableState
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.Layout
|
||||
import androidx.compose.ui.layout.Measurable
|
||||
import androidx.compose.ui.layout.ParentDataModifier
|
||||
import androidx.compose.ui.unit.Density
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class PagerState(
|
||||
currentPage: Int = 0,
|
||||
minPage: Int = 0,
|
||||
maxPage: Int = 0
|
||||
) {
|
||||
private var _minPage by mutableStateOf(minPage)
|
||||
var minPage: Int
|
||||
get() = _minPage
|
||||
set(value) {
|
||||
_minPage = value.coerceAtMost(_maxPage)
|
||||
_currentPage = _currentPage.coerceIn(_minPage, _maxPage)
|
||||
}
|
||||
|
||||
private var _maxPage by mutableStateOf(maxPage, structuralEqualityPolicy())
|
||||
var maxPage: Int
|
||||
get() = _maxPage
|
||||
set(value) {
|
||||
_maxPage = value.coerceAtLeast(_minPage)
|
||||
_currentPage = _currentPage.coerceIn(_minPage, maxPage)
|
||||
}
|
||||
|
||||
private var _currentPage by mutableStateOf(currentPage.coerceIn(minPage, maxPage))
|
||||
var currentPage: Int
|
||||
get() = _currentPage
|
||||
set(value) {
|
||||
_currentPage = value.coerceIn(minPage, maxPage)
|
||||
}
|
||||
|
||||
enum class SelectionState { Selected, Undecided }
|
||||
|
||||
var selectionState by mutableStateOf(SelectionState.Selected)
|
||||
|
||||
suspend inline fun <R> selectPage(block: PagerState.() -> R): R = try {
|
||||
selectionState = SelectionState.Undecided
|
||||
block()
|
||||
} finally {
|
||||
selectPage()
|
||||
}
|
||||
|
||||
suspend fun selectPage() {
|
||||
currentPage -= currentPageOffset.roundToInt()
|
||||
snapToOffset(0f)
|
||||
selectionState = SelectionState.Selected
|
||||
}
|
||||
|
||||
private var _currentPageOffset = Animatable(0f).apply {
|
||||
updateBounds(-1f, 1f)
|
||||
}
|
||||
val currentPageOffset: Float
|
||||
get() = _currentPageOffset.value
|
||||
|
||||
suspend fun snapToOffset(offset: Float) {
|
||||
val max = if (currentPage == minPage) 0f else 1f
|
||||
val min = if (currentPage == maxPage) 0f else -1f
|
||||
_currentPageOffset.snapTo(offset.coerceIn(min, max))
|
||||
}
|
||||
|
||||
suspend fun fling(velocity: Float) {
|
||||
if (velocity < 0 && currentPage == maxPage) return
|
||||
if (velocity > 0 && currentPage == minPage) return
|
||||
|
||||
// 根据 fling 的方向滑动到下一页或上一页
|
||||
_currentPageOffset.animateTo(velocity)
|
||||
selectPage()
|
||||
}
|
||||
|
||||
override fun toString(): String = "PagerState{minPage=$minPage, maxPage=$maxPage, " +
|
||||
"currentPage=$currentPage, currentPageOffset=$currentPageOffset}"
|
||||
}
|
||||
|
||||
@Immutable
|
||||
private data class PageData(val page: Int) : ParentDataModifier {
|
||||
override fun Density.modifyParentData(parentData: Any?): Any? = this@PageData
|
||||
}
|
||||
|
||||
private val Measurable.page: Int
|
||||
get() = (parentData as? PageData)?.page ?: error("no PageData for measurable $this")
|
||||
|
||||
@Composable
|
||||
fun Pager(
|
||||
modifier: Modifier = Modifier,
|
||||
state: PagerState,
|
||||
orientation: Orientation = Orientation.Horizontal,
|
||||
offscreenLimit: Int = 2,
|
||||
horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally, // 新增水平对齐参数
|
||||
verticalAlignment: Alignment.Vertical = Alignment.CenterVertically, // 新增垂直对齐参数
|
||||
content: @Composable PagerScope.() -> Unit
|
||||
) {
|
||||
var pageSize by remember { mutableStateOf(0) }
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
Layout(
|
||||
content = {
|
||||
// 根据 offscreenLimit 计算页面范围
|
||||
val minPage = maxOf(state.currentPage - offscreenLimit, state.minPage)
|
||||
val maxPage = minOf(state.currentPage + offscreenLimit, state.maxPage)
|
||||
|
||||
for (page in minPage..maxPage) {
|
||||
val pageData = PageData(page)
|
||||
val scope = PagerScope(state, page)
|
||||
key(pageData) {
|
||||
Column(modifier = pageData) {
|
||||
scope.content()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = modifier.draggable(
|
||||
orientation = orientation,
|
||||
onDragStarted = {
|
||||
state.selectionState = PagerState.SelectionState.Undecided
|
||||
},
|
||||
onDragStopped = { velocity ->
|
||||
coroutineScope.launch {
|
||||
// 根据速度判断是否滑动到下一页
|
||||
val threshold = 1000f // 速度阈值,可调整
|
||||
if (velocity > threshold) {
|
||||
state.fling(1f) // 向右滑动
|
||||
} else if (velocity < -threshold) {
|
||||
state.fling(-1f) // 向左滑动
|
||||
} else {
|
||||
state.fling(0f) // 保持当前页
|
||||
}
|
||||
}
|
||||
},
|
||||
state = rememberDraggableState { dy ->
|
||||
coroutineScope.launch {
|
||||
with(state) {
|
||||
val pos = pageSize * currentPageOffset
|
||||
val max = if (currentPage == minPage) 0 else pageSize
|
||||
val min = if (currentPage == maxPage) 0 else -pageSize
|
||||
|
||||
// 直接将手指的位移应用到 currentPageOffset
|
||||
val newPos = (pos + dy).coerceIn(min.toFloat(), max.toFloat())
|
||||
snapToOffset(newPos / pageSize)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
) { measurables, constraints ->
|
||||
layout(constraints.maxWidth, constraints.maxHeight) {
|
||||
val currentPage = state.currentPage
|
||||
val offset = state.currentPageOffset
|
||||
val childConstraints = constraints.copy(minWidth = 0, minHeight = 0)
|
||||
|
||||
measurables.forEach { measurable ->
|
||||
val placeable = measurable.measure(childConstraints)
|
||||
val page = measurable.page
|
||||
|
||||
// 根据对齐参数计算 x 和 y 位置
|
||||
val xPosition = when (horizontalAlignment) {
|
||||
Alignment.Start -> 0
|
||||
Alignment.CenterHorizontally -> (constraints.maxWidth - placeable.width) / 2
|
||||
Alignment.End -> constraints.maxWidth - placeable.width
|
||||
else -> 0
|
||||
}
|
||||
|
||||
val yPosition = when (verticalAlignment) {
|
||||
Alignment.Top -> 0
|
||||
Alignment.CenterVertically -> (constraints.maxHeight - placeable.height) / 2
|
||||
Alignment.Bottom -> constraints.maxHeight - placeable.height
|
||||
else -> 0
|
||||
}
|
||||
|
||||
if (currentPage == page) { // 只在当前页面设置 pageSize,避免不必要的设置
|
||||
pageSize = if (orientation == Orientation.Horizontal) {
|
||||
placeable.width
|
||||
} else {
|
||||
placeable.height
|
||||
}
|
||||
}
|
||||
|
||||
val isVisible = abs(page - (currentPage - offset)) <= 1
|
||||
|
||||
if (isVisible) {
|
||||
// 修正 x 的计算
|
||||
val xOffset = if (orientation == Orientation.Horizontal) {
|
||||
((page - currentPage) * pageSize + offset * pageSize).roundToInt()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
|
||||
// 使用 placeRelative 进行放置
|
||||
placeable.placeRelative(
|
||||
x = xPosition + xOffset,
|
||||
y = yPosition + if (orientation == Orientation.Vertical) ((page - (currentPage - offset)) * placeable.height).roundToInt() else 0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PagerScope(
|
||||
private val state: PagerState,
|
||||
val page: Int
|
||||
) {
|
||||
val currentPage: Int
|
||||
get() = state.currentPage
|
||||
|
||||
val currentPageOffset: Float
|
||||
get() = state.currentPageOffset
|
||||
|
||||
val selectionState: PagerState.SelectionState
|
||||
get() = state.selectionState
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
27
app/src/main/java/com/aiosman/riderpro/ShortVideo.kt
Normal file
27
app/src/main/java/com/aiosman/riderpro/ShortVideo.kt
Normal file
@@ -0,0 +1,27 @@
|
||||
package com.aiosman.riderpro
|
||||
|
||||
import android.app.Activity
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.aiosman.riderpro.ShortViewCompose
|
||||
import com.aiosman.riderpro.ui.theme.RiderProTheme
|
||||
|
||||
val videoUrls = listOf(
|
||||
"https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4",
|
||||
"https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
|
||||
"https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/WeAreGoingOnBullrun.mp4"
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun ShortVideo() {
|
||||
RiderProTheme {
|
||||
Surface(color = MaterialTheme.colorScheme.background) {
|
||||
ShortViewCompose(
|
||||
videoItemsUrl = videoUrls,
|
||||
clickItemPosition = 0
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
180
app/src/main/java/com/aiosman/riderpro/ShortViewCompose.kt
Normal file
180
app/src/main/java/com/aiosman/riderpro/ShortViewCompose.kt
Normal file
@@ -0,0 +1,180 @@
|
||||
package com.aiosman.riderpro
|
||||
|
||||
import android.app.Activity
|
||||
import android.net.Uri
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.gestures.Orientation
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.PlayArrow
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.media3.common.C
|
||||
import androidx.media3.common.MediaItem
|
||||
import androidx.media3.common.Player
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.media3.common.util.Util
|
||||
import androidx.media3.datasource.DataSource
|
||||
import androidx.media3.datasource.DefaultDataSourceFactory
|
||||
import androidx.media3.exoplayer.ExoPlayer
|
||||
import androidx.media3.exoplayer.SimpleExoPlayer
|
||||
import androidx.media3.exoplayer.source.ProgressiveMediaSource
|
||||
import androidx.media3.ui.AspectRatioFrameLayout
|
||||
import androidx.media3.ui.PlayerView
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun ShortViewCompose(
|
||||
videoItemsUrl:List<String>,
|
||||
clickItemPosition:Int = 0,
|
||||
videoHeader:@Composable () -> Unit = {},
|
||||
videoBottom:@Composable () -> Unit = {}
|
||||
) {
|
||||
val pagerState: PagerState = run {
|
||||
remember {
|
||||
PagerState(clickItemPosition, 0, videoItemsUrl.size - 1)
|
||||
}
|
||||
}
|
||||
val initialLayout= remember {
|
||||
mutableStateOf(true)
|
||||
}
|
||||
val pauseIconVisibleState = remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
Pager(
|
||||
state = pagerState,
|
||||
orientation = Orientation.Vertical,
|
||||
offscreenLimit = 1
|
||||
) {
|
||||
pauseIconVisibleState.value=false
|
||||
SingleVideoItemContent(videoItemsUrl[page],
|
||||
pagerState,
|
||||
page,
|
||||
initialLayout,
|
||||
pauseIconVisibleState,
|
||||
videoHeader,
|
||||
videoBottom)
|
||||
}
|
||||
|
||||
LaunchedEffect(clickItemPosition){
|
||||
delay(300)
|
||||
initialLayout.value=false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SingleVideoItemContent(
|
||||
videoUrl: String,
|
||||
pagerState: PagerState,
|
||||
pager: Int,
|
||||
initialLayout: MutableState<Boolean>,
|
||||
pauseIconVisibleState: MutableState<Boolean>,
|
||||
VideoHeader: @Composable() () -> Unit,
|
||||
VideoBottom: @Composable() () -> Unit,
|
||||
) {
|
||||
Box(modifier = Modifier.fillMaxSize()){
|
||||
VideoPlayer(videoUrl,pagerState,pager,pauseIconVisibleState)
|
||||
VideoHeader.invoke()
|
||||
Box(modifier = Modifier.align(Alignment.BottomStart)){
|
||||
VideoBottom.invoke()
|
||||
}
|
||||
if (initialLayout.value) {
|
||||
Box(modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(color = Color.Black))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(UnstableApi::class)
|
||||
@Composable
|
||||
fun VideoPlayer(
|
||||
videoUrl: String,
|
||||
pagerState: PagerState,
|
||||
pager: Int,
|
||||
pauseIconVisibleState: MutableState<Boolean>,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val scope= rememberCoroutineScope()
|
||||
|
||||
val exoPlayer = remember {
|
||||
SimpleExoPlayer.Builder(context)
|
||||
.build()
|
||||
.apply {
|
||||
val dataSourceFactory: DataSource.Factory = DefaultDataSourceFactory(
|
||||
context,
|
||||
Util.getUserAgent(context, context.packageName)
|
||||
)
|
||||
val source = ProgressiveMediaSource.Factory(dataSourceFactory)
|
||||
.createMediaSource(MediaItem.fromUri(Uri.parse(videoUrl)))
|
||||
|
||||
this.prepare(source)
|
||||
}
|
||||
}
|
||||
if (pager == pagerState.currentPage) {
|
||||
exoPlayer.playWhenReady = true
|
||||
exoPlayer.play()
|
||||
} else {
|
||||
exoPlayer.pause()
|
||||
}
|
||||
exoPlayer.videoScalingMode = C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING
|
||||
exoPlayer.repeatMode = Player.REPEAT_MODE_ONE
|
||||
DisposableEffect(
|
||||
Box(modifier = Modifier.fillMaxSize()){
|
||||
AndroidView(factory = {
|
||||
PlayerView(context).apply {
|
||||
hideController()
|
||||
useController = false
|
||||
resizeMode = AspectRatioFrameLayout.RESIZE_MODE_ZOOM
|
||||
|
||||
player = exoPlayer
|
||||
layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
|
||||
}
|
||||
},modifier = Modifier.noRippleClickable {
|
||||
pauseIconVisibleState.value=true
|
||||
exoPlayer.pause()
|
||||
scope.launch {
|
||||
delay(500)
|
||||
if (exoPlayer.isPlaying) {
|
||||
exoPlayer.pause()
|
||||
} else {
|
||||
pauseIconVisibleState.value=false
|
||||
exoPlayer.play()
|
||||
}
|
||||
}
|
||||
})
|
||||
if (pauseIconVisibleState.value)
|
||||
Icon(imageVector = Icons.Default.PlayArrow, contentDescription = null,
|
||||
modifier = Modifier
|
||||
.align(Alignment.Center)
|
||||
.size(80.dp))
|
||||
}
|
||||
) {
|
||||
onDispose {
|
||||
exoPlayer.release()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
100
app/src/main/java/com/aiosman/riderpro/StatusBarExp.kt
Normal file
100
app/src/main/java/com/aiosman/riderpro/StatusBarExp.kt
Normal file
@@ -0,0 +1,100 @@
|
||||
package com.aiosman.riderpro
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Build
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import android.widget.RelativeLayout
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.annotation.ColorRes
|
||||
//import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.Fragment
|
||||
|
||||
private const val COLOR_TRANSPARENT = 0
|
||||
|
||||
@SuppressLint("ObsoleteSdkInt")
|
||||
@JvmOverloads
|
||||
fun Activity.immersive(@ColorInt color: Int = COLOR_TRANSPARENT, darkMode: Boolean? = null) {
|
||||
when {
|
||||
Build.VERSION.SDK_INT >= 21 -> {
|
||||
when (color) {
|
||||
COLOR_TRANSPARENT -> {
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
|
||||
var systemUiVisibility = window.decorView.systemUiVisibility
|
||||
systemUiVisibility = systemUiVisibility or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
systemUiVisibility = systemUiVisibility or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
window.decorView.systemUiVisibility = systemUiVisibility
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
|
||||
window.statusBarColor = color
|
||||
}
|
||||
else -> {
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
|
||||
var systemUiVisibility = window.decorView.systemUiVisibility
|
||||
systemUiVisibility = systemUiVisibility and View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
systemUiVisibility = systemUiVisibility and View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
window.decorView.systemUiVisibility = systemUiVisibility
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
|
||||
window.statusBarColor = color
|
||||
}
|
||||
}
|
||||
}
|
||||
Build.VERSION.SDK_INT >= 19 -> {
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
|
||||
if (color != COLOR_TRANSPARENT) {
|
||||
setTranslucentView(window.decorView as ViewGroup, color)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (darkMode != null) {
|
||||
darkMode(darkMode)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun Activity.darkMode(darkMode: Boolean = true) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
var systemUiVisibility = window.decorView.systemUiVisibility
|
||||
systemUiVisibility = if (darkMode) {
|
||||
systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
|
||||
} else {
|
||||
systemUiVisibility and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
|
||||
}
|
||||
window.decorView.systemUiVisibility = systemUiVisibility
|
||||
}
|
||||
}
|
||||
|
||||
private fun Context.setTranslucentView(container: ViewGroup, color: Int) {
|
||||
if (Build.VERSION.SDK_INT >= 19) {
|
||||
var simulateStatusBar: View? = container.findViewById(android.R.id.custom)
|
||||
if (simulateStatusBar == null && color != 0) {
|
||||
simulateStatusBar = View(container.context)
|
||||
simulateStatusBar.id = android.R.id.custom
|
||||
val lp = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, statusBarHeight)
|
||||
container.addView(simulateStatusBar, lp)
|
||||
}
|
||||
simulateStatusBar?.setBackgroundColor(color)
|
||||
}
|
||||
}
|
||||
|
||||
val Context?.statusBarHeight: Int
|
||||
get() {
|
||||
this ?: return 0
|
||||
var result = 24
|
||||
val resId = resources.getIdentifier("status_bar_height", "dimen", "android")
|
||||
result = if (resId > 0) {
|
||||
resources.getDimensionPixelSize(resId)
|
||||
} else {
|
||||
TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP,
|
||||
result.toFloat(), Resources.getSystem().displayMetrics
|
||||
).toInt()
|
||||
}
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user