调整细节
This commit is contained in:
@@ -1,10 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
@@ -16,8 +18,9 @@
|
|||||||
android:theme="@style/Theme.RiderPro"
|
android:theme="@style/Theme.RiderPro"
|
||||||
android:usesCleartextTraffic="true"
|
android:usesCleartextTraffic="true"
|
||||||
tools:targetApi="31">
|
tools:targetApi="31">
|
||||||
<meta-data android:name="com.google.android.geo.API_KEY"
|
<meta-data
|
||||||
android:value="AIzaSyBM9xMcybq9IbFSFVneZ4nAqQ0ZmTnHGO4"/>
|
android:name="com.google.android.geo.API_KEY"
|
||||||
|
android:value="AIzaSyBM9xMcybq9IbFSFVneZ4nAqQ0ZmTnHGO4" />
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="com.google.firebase.messaging.default_notification_icon"
|
android:name="com.google.firebase.messaging.default_notification_icon"
|
||||||
android:resource="@drawable/googleg_standard_color_18" />
|
android:resource="@drawable/googleg_standard_color_18" />
|
||||||
@@ -49,6 +52,17 @@
|
|||||||
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</service>
|
</service>
|
||||||
|
<provider
|
||||||
|
android:name="androidx.core.content.FileProvider"
|
||||||
|
android:authorities="com.aiosman.riderpro.fileprovider"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/file_paths" />
|
||||||
|
</provider>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -0,0 +1,278 @@
|
|||||||
|
package com.aiosman.riderpro.ui.composables
|
||||||
|
|
||||||
|
import androidx.compose.animation.core.Animatable
|
||||||
|
import androidx.compose.animation.core.Spring
|
||||||
|
import androidx.compose.animation.core.VectorConverter
|
||||||
|
import androidx.compose.animation.core.VisibilityThreshold
|
||||||
|
import androidx.compose.animation.core.spring
|
||||||
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
|
import androidx.compose.foundation.gestures.detectDragGesturesAfterLongPress
|
||||||
|
import androidx.compose.foundation.gestures.scrollBy
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.lazy.grid.GridCells
|
||||||
|
import androidx.compose.foundation.lazy.grid.LazyGridItemInfo
|
||||||
|
import androidx.compose.foundation.lazy.grid.LazyGridItemScope
|
||||||
|
import androidx.compose.foundation.lazy.grid.LazyGridState
|
||||||
|
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||||
|
import androidx.compose.foundation.lazy.grid.itemsIndexed
|
||||||
|
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.geometry.Size
|
||||||
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
|
import androidx.compose.ui.unit.IntOffset
|
||||||
|
import androidx.compose.ui.unit.IntSize
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.toOffset
|
||||||
|
import androidx.compose.ui.unit.toSize
|
||||||
|
import androidx.compose.ui.zIndex
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.channels.Channel
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|
||||||
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
|
@Composable
|
||||||
|
fun <T : Any> DraggableGrid(
|
||||||
|
items: List<T>,
|
||||||
|
onMove: (Int, Int) -> Unit,
|
||||||
|
onDragModeStart: () -> Unit, // New parameter for drag start
|
||||||
|
onDragModeEnd: () -> Unit, // New parameter for drag end,
|
||||||
|
additionalItems: List<@Composable () -> Unit> = emptyList(), // New parameter for additional items
|
||||||
|
lockedIndices: List<Int> = emptyList(), // New parameter for locked indices
|
||||||
|
content: @Composable (T, Boolean) -> Unit,
|
||||||
|
) {
|
||||||
|
|
||||||
|
val gridState = rememberLazyGridState()
|
||||||
|
val dragDropState =
|
||||||
|
rememberGridDragDropState(gridState, onMove, onDragModeStart, onDragModeEnd, lockedIndices)
|
||||||
|
LazyVerticalGrid(
|
||||||
|
columns = GridCells.Fixed(3),
|
||||||
|
modifier = Modifier.dragContainer(dragDropState),
|
||||||
|
state = gridState,
|
||||||
|
contentPadding = PaddingValues(16.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
|
|
||||||
|
) {
|
||||||
|
itemsIndexed(items, key = { _, item -> item }) { index, item ->
|
||||||
|
DraggableItem(dragDropState, index) { isDragging ->
|
||||||
|
content(item, isDragging)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
additionalItems.forEach { additionalItem ->
|
||||||
|
item {
|
||||||
|
additionalItem()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Modifier.dragContainer(dragDropState: GridDragDropState): Modifier {
|
||||||
|
return pointerInput(dragDropState) {
|
||||||
|
detectDragGesturesAfterLongPress(
|
||||||
|
onDrag = { change, offset ->
|
||||||
|
change.consume()
|
||||||
|
dragDropState.onDrag(offset = offset)
|
||||||
|
},
|
||||||
|
onDragStart = { offset -> dragDropState.onDragStart(offset) },
|
||||||
|
onDragEnd = { dragDropState.onDragInterrupted() },
|
||||||
|
onDragCancel = { dragDropState.onDragInterrupted() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExperimentalFoundationApi
|
||||||
|
@Composable
|
||||||
|
fun LazyGridItemScope.DraggableItem(
|
||||||
|
dragDropState: GridDragDropState,
|
||||||
|
index: Int,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
content: @Composable (isDragging: Boolean) -> Unit,
|
||||||
|
) {
|
||||||
|
val dragging = index == dragDropState.draggingItemIndex
|
||||||
|
val draggingModifier = if (dragging) {
|
||||||
|
Modifier
|
||||||
|
.zIndex(1f)
|
||||||
|
.graphicsLayer {
|
||||||
|
translationX = dragDropState.draggingItemOffset.x
|
||||||
|
translationY = dragDropState.draggingItemOffset.y
|
||||||
|
}
|
||||||
|
} else if (index == dragDropState.previousIndexOfDraggedItem) {
|
||||||
|
Modifier
|
||||||
|
.zIndex(1f)
|
||||||
|
.graphicsLayer {
|
||||||
|
translationX = dragDropState.previousItemOffset.value.x
|
||||||
|
translationY = dragDropState.previousItemOffset.value.y
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Modifier.animateItemPlacement()
|
||||||
|
}
|
||||||
|
Box(modifier = modifier.then(draggingModifier), propagateMinConstraints = true) {
|
||||||
|
content(dragging)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun rememberGridDragDropState(
|
||||||
|
gridState: LazyGridState,
|
||||||
|
onMove: (Int, Int) -> Unit,
|
||||||
|
onDragModeStart: () -> Unit,
|
||||||
|
onDragModeEnd: () -> Unit,
|
||||||
|
lockedIndices: List<Int> // New parameter for locked indices
|
||||||
|
): GridDragDropState {
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val state = remember(gridState) {
|
||||||
|
GridDragDropState(
|
||||||
|
state = gridState,
|
||||||
|
onMove = onMove,
|
||||||
|
scope = scope,
|
||||||
|
onDragModeStart = onDragModeStart,
|
||||||
|
onDragModeEnd = onDragModeEnd,
|
||||||
|
lockedIndices = lockedIndices // Pass the locked indices
|
||||||
|
)
|
||||||
|
}
|
||||||
|
LaunchedEffect(state) {
|
||||||
|
while (true) {
|
||||||
|
val diff = state.scrollChannel.receive()
|
||||||
|
gridState.scrollBy(diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
class GridDragDropState internal constructor(
|
||||||
|
private val state: LazyGridState,
|
||||||
|
private val scope: CoroutineScope,
|
||||||
|
private val onMove: (Int, Int) -> Unit,
|
||||||
|
private val onDragModeStart: () -> Unit,
|
||||||
|
private val onDragModeEnd: () -> Unit,
|
||||||
|
private val lockedIndices: List<Int> // New parameter for locked indices
|
||||||
|
) {
|
||||||
|
var draggingItemIndex by mutableStateOf<Int?>(null)
|
||||||
|
private set
|
||||||
|
|
||||||
|
internal val scrollChannel = Channel<Float>()
|
||||||
|
|
||||||
|
private var draggingItemDraggedDelta by mutableStateOf(Offset.Zero)
|
||||||
|
private var draggingItemInitialOffset by mutableStateOf(Offset.Zero)
|
||||||
|
internal val draggingItemOffset: Offset
|
||||||
|
get() = draggingItemLayoutInfo?.let { item ->
|
||||||
|
draggingItemInitialOffset + draggingItemDraggedDelta - item.offset.toOffset()
|
||||||
|
} ?: Offset.Zero
|
||||||
|
|
||||||
|
private val draggingItemLayoutInfo: LazyGridItemInfo?
|
||||||
|
get() = state.layoutInfo.visibleItemsInfo
|
||||||
|
.firstOrNull { it.index == draggingItemIndex }
|
||||||
|
|
||||||
|
internal var previousIndexOfDraggedItem by mutableStateOf<Int?>(null)
|
||||||
|
private set
|
||||||
|
internal var previousItemOffset = Animatable(Offset.Zero, Offset.VectorConverter)
|
||||||
|
private set
|
||||||
|
|
||||||
|
internal fun onDragStart(offset: Offset) {
|
||||||
|
state.layoutInfo.visibleItemsInfo
|
||||||
|
.firstOrNull { item ->
|
||||||
|
offset.x.toInt() in item.offset.x..item.offsetEnd.x &&
|
||||||
|
offset.y.toInt() in item.offset.y..item.offsetEnd.y
|
||||||
|
}?.also {
|
||||||
|
if (it.index !in lockedIndices) { // Check if the item is not locked
|
||||||
|
draggingItemIndex = it.index
|
||||||
|
draggingItemInitialOffset = it.offset.toOffset()
|
||||||
|
onDragModeStart() // Notify drag start
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun onDragInterrupted() {
|
||||||
|
if (draggingItemIndex != null) {
|
||||||
|
previousIndexOfDraggedItem = draggingItemIndex
|
||||||
|
val startOffset = draggingItemOffset
|
||||||
|
scope.launch {
|
||||||
|
previousItemOffset.snapTo(startOffset)
|
||||||
|
previousItemOffset.animateTo(
|
||||||
|
Offset.Zero,
|
||||||
|
spring(
|
||||||
|
stiffness = Spring.StiffnessMediumLow,
|
||||||
|
visibilityThreshold = Offset.VisibilityThreshold
|
||||||
|
)
|
||||||
|
)
|
||||||
|
previousIndexOfDraggedItem = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
draggingItemDraggedDelta = Offset.Zero
|
||||||
|
draggingItemIndex = null
|
||||||
|
draggingItemInitialOffset = Offset.Zero
|
||||||
|
onDragModeEnd() // Notify drag end
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun onDrag(offset: Offset) {
|
||||||
|
draggingItemDraggedDelta += offset
|
||||||
|
|
||||||
|
val draggingItem = draggingItemLayoutInfo ?: return
|
||||||
|
val startOffset = draggingItem.offset.toOffset() + draggingItemOffset
|
||||||
|
val endOffset = startOffset + draggingItem.size.toSize()
|
||||||
|
val middleOffset = startOffset + (endOffset - startOffset) / 2f
|
||||||
|
|
||||||
|
val targetItem = state.layoutInfo.visibleItemsInfo.find { item ->
|
||||||
|
middleOffset.x.toInt() in item.offset.x..item.offsetEnd.x &&
|
||||||
|
middleOffset.y.toInt() in item.offset.y..item.offsetEnd.y &&
|
||||||
|
draggingItem.index != item.index &&
|
||||||
|
item.index !in lockedIndices // Check if the target item is not locked
|
||||||
|
}
|
||||||
|
if (targetItem != null) {
|
||||||
|
val scrollToIndex = if (targetItem.index == state.firstVisibleItemIndex) {
|
||||||
|
draggingItem.index
|
||||||
|
} else if (draggingItem.index == state.firstVisibleItemIndex) {
|
||||||
|
targetItem.index
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
if (scrollToIndex != null) {
|
||||||
|
scope.launch {
|
||||||
|
state.scrollToItem(scrollToIndex, state.firstVisibleItemScrollOffset)
|
||||||
|
onMove.invoke(draggingItem.index, targetItem.index)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
onMove.invoke(draggingItem.index, targetItem.index)
|
||||||
|
}
|
||||||
|
draggingItemIndex = targetItem.index
|
||||||
|
} else {
|
||||||
|
val overscroll = when {
|
||||||
|
draggingItemDraggedDelta.y > 0 ->
|
||||||
|
(endOffset.y - state.layoutInfo.viewportEndOffset).coerceAtLeast(0f)
|
||||||
|
|
||||||
|
draggingItemDraggedDelta.y < 0 ->
|
||||||
|
(startOffset.y - state.layoutInfo.viewportStartOffset).coerceAtMost(0f)
|
||||||
|
|
||||||
|
else -> 0f
|
||||||
|
}
|
||||||
|
if (overscroll != 0f) {
|
||||||
|
scrollChannel.trySend(overscroll)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val LazyGridItemInfo.offsetEnd: IntOffset
|
||||||
|
get() = this.offset + this.size
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun IntOffset.plus(size: IntSize): IntOffset {
|
||||||
|
return IntOffset(x + size.width, y + size.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun Offset.plus(size: Size): Offset {
|
||||||
|
return Offset(x + size.width, y + size.height)
|
||||||
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
package com.aiosman.riderpro.ui.post
|
package com.aiosman.riderpro.ui.post
|
||||||
|
|
||||||
import android.app.Activity
|
import android.net.Uri
|
||||||
import android.content.Intent
|
|
||||||
import android.util.Log
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.core.animateDpAsState
|
||||||
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
@@ -14,10 +14,10 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
|
|||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.FlowRow
|
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.aspectRatio
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
@@ -25,9 +25,12 @@ import androidx.compose.foundation.layout.heightIn
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.lazy.grid.GridCells
|
||||||
|
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.text.BasicTextField
|
import androidx.compose.foundation.text.BasicTextField
|
||||||
import androidx.compose.material.LinearProgressIndicator
|
import androidx.compose.material.LinearProgressIndicator
|
||||||
|
import androidx.compose.material.MaterialTheme
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.ModalBottomSheet
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
@@ -36,12 +39,15 @@ import androidx.compose.runtime.LaunchedEffect
|
|||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.draw.drawBehind
|
import androidx.compose.ui.draw.drawBehind
|
||||||
|
import androidx.compose.ui.draw.shadow
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.ColorFilter
|
||||||
import androidx.compose.ui.graphics.PathEffect
|
import androidx.compose.ui.graphics.PathEffect
|
||||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
@@ -50,17 +56,24 @@ import androidx.compose.ui.res.painterResource
|
|||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.core.content.FileProvider
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import coil.compose.AsyncImage
|
|
||||||
import com.aiosman.riderpro.LocalNavController
|
import com.aiosman.riderpro.LocalNavController
|
||||||
import com.aiosman.riderpro.R
|
import com.aiosman.riderpro.R
|
||||||
import com.aiosman.riderpro.ui.NavigationRoute
|
import com.aiosman.riderpro.ui.NavigationRoute
|
||||||
import com.aiosman.riderpro.ui.composables.CustomAsyncImage
|
import com.aiosman.riderpro.ui.composables.CustomAsyncImage
|
||||||
|
import com.aiosman.riderpro.ui.composables.DraggableGrid
|
||||||
import com.aiosman.riderpro.ui.composables.RelPostCard
|
import com.aiosman.riderpro.ui.composables.RelPostCard
|
||||||
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
|
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
|
||||||
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
||||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import org.burnoutcrew.reorderable.NoDragCancelledAnimation
|
||||||
|
import org.burnoutcrew.reorderable.ReorderableItem
|
||||||
|
import org.burnoutcrew.reorderable.detectReorderAfterLongPress
|
||||||
|
import org.burnoutcrew.reorderable.rememberReorderableLazyGridState
|
||||||
|
import org.burnoutcrew.reorderable.reorderable
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@@ -76,7 +89,9 @@ fun NewPostScreen() {
|
|||||||
}
|
}
|
||||||
StatusBarMaskLayout(
|
StatusBarMaskLayout(
|
||||||
darkIcons = true,
|
darkIcons = true,
|
||||||
modifier = Modifier.fillMaxSize().background(Color.White)
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(Color.White)
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -94,14 +109,19 @@ fun NewPostScreen() {
|
|||||||
NewPostTextField("Share your adventure…", NewPostViewModel.textContent) {
|
NewPostTextField("Share your adventure…", NewPostViewModel.textContent) {
|
||||||
NewPostViewModel.textContent = it
|
NewPostViewModel.textContent = it
|
||||||
}
|
}
|
||||||
Column (
|
Column(
|
||||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp)
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 16.dp)
|
||||||
) {
|
) {
|
||||||
model.relMoment?.let {
|
model.relMoment?.let {
|
||||||
Text("Share with")
|
Text("Share with")
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier.clip(RoundedCornerShape(8.dp)).background(color = Color(0xFFEEEEEE)).padding(24.dp)
|
modifier = Modifier
|
||||||
|
.clip(RoundedCornerShape(8.dp))
|
||||||
|
.background(color = Color(0xFFEEEEEE))
|
||||||
|
.padding(24.dp)
|
||||||
) {
|
) {
|
||||||
RelPostCard(
|
RelPostCard(
|
||||||
momentEntity = it,
|
momentEntity = it,
|
||||||
@@ -188,6 +208,7 @@ fun NewPostTopBar(onSendClick: () -> Unit = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun NewPostTextField(hint: String, value: String, onValueChange: (String) -> Unit) {
|
fun NewPostTextField(hint: String, value: String, onValueChange: (String) -> Unit) {
|
||||||
|
|
||||||
@@ -211,7 +232,48 @@ fun NewPostTextField(hint: String, value: String, onValueChange: (String) -> Uni
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalLayoutApi::class)
|
@Composable
|
||||||
|
private fun VerticalGrid(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
var data by remember { mutableStateOf((0 until 20).toList()) }
|
||||||
|
val state = rememberReorderableLazyGridState(onMove = { from, to ->
|
||||||
|
data = data.toMutableList().apply {
|
||||||
|
add(to.index, removeAt(from.index))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
LazyVerticalGrid(
|
||||||
|
columns = GridCells.Fixed(4),
|
||||||
|
state = state.gridState,
|
||||||
|
contentPadding = PaddingValues(horizontal = 8.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
|
modifier = modifier.reorderable(state)
|
||||||
|
) {
|
||||||
|
items(data.size, { it }) { item ->
|
||||||
|
val imageItem = data[item]
|
||||||
|
ReorderableItem(state, item) { isDragging ->
|
||||||
|
val elevation = animateDpAsState(if (isDragging) 8.dp else 0.dp)
|
||||||
|
Box(
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
modifier = Modifier
|
||||||
|
.detectReorderAfterLongPress(state)
|
||||||
|
.shadow(elevation.value)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.background(MaterialTheme.colors.primary)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = imageItem.toString(),
|
||||||
|
color = Color.White
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AddImageGrid() {
|
fun AddImageGrid() {
|
||||||
val navController = LocalNavController.current
|
val navController = LocalNavController.current
|
||||||
@@ -226,44 +288,104 @@ fun AddImageGrid() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val takePictureLauncher = rememberLauncherForActivityResult(
|
||||||
|
contract = ActivityResultContracts.TakePicture()
|
||||||
|
) { success ->
|
||||||
|
if (success) {
|
||||||
|
model.imageUriList += model.currentPhotoUri.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val state =
|
||||||
|
rememberReorderableLazyGridState(
|
||||||
|
onMove = { from, to ->
|
||||||
|
model.imageUriList = model.imageUriList.toMutableList().apply {
|
||||||
|
add(to.index, removeAt(from.index))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
val stroke = Stroke(
|
val stroke = Stroke(
|
||||||
width = 2f,
|
width = 2f,
|
||||||
pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f), 0f)
|
pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f), 0f)
|
||||||
)
|
)
|
||||||
Box(
|
DraggableGrid(
|
||||||
modifier = Modifier.fillMaxWidth()
|
items = NewPostViewModel.imageUriList,
|
||||||
) {
|
onMove = { from, to ->
|
||||||
FlowRow(
|
NewPostViewModel.imageUriList = NewPostViewModel.imageUriList.toMutableList().apply {
|
||||||
modifier = Modifier
|
add(to, removeAt(from))
|
||||||
.fillMaxWidth()
|
}
|
||||||
.padding(18.dp),
|
},
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
lockedIndices = listOf(
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
|
||||||
) {
|
|
||||||
model.imageUriList.forEach {
|
|
||||||
CustomAsyncImage(
|
|
||||||
context,
|
|
||||||
it,
|
|
||||||
contentDescription = "Image",
|
|
||||||
modifier = Modifier
|
|
||||||
.size(110.dp)
|
|
||||||
|
|
||||||
.drawBehind {
|
),
|
||||||
drawRoundRect(color = Color(0xFF999999), style = stroke)
|
onDragModeEnd = {},
|
||||||
}.noRippleClickable {
|
onDragModeStart = {},
|
||||||
navController.navigate(NavigationRoute.NewPostImageGrid.route)
|
additionalItems = listOf(
|
||||||
},
|
|
||||||
contentScale = ContentScale.Crop
|
),
|
||||||
|
|
||||||
|
) { item, isDrag ->
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
) {
|
||||||
|
CustomAsyncImage(
|
||||||
|
LocalContext.current,
|
||||||
|
item,
|
||||||
|
contentDescription = "Image",
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.noRippleClickable {
|
||||||
|
navController.navigate(NavigationRoute.NewPostImageGrid.route)
|
||||||
|
},
|
||||||
|
contentScale = ContentScale.Crop
|
||||||
|
)
|
||||||
|
if (isDrag) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(Color(0x66000000))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LazyVerticalGrid(
|
||||||
|
columns = GridCells.Fixed(3),
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(18.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
// items(model.imageUriList.size) { index ->
|
||||||
|
// val uri = model.imageUriList[index]
|
||||||
|
// Box(
|
||||||
|
// modifier = Modifier
|
||||||
|
// .drawBehind {
|
||||||
|
// drawRoundRect(color = Color(0xFF999999), style = stroke)
|
||||||
|
// }
|
||||||
|
// ) {
|
||||||
|
// CustomAsyncImage(
|
||||||
|
// context,
|
||||||
|
// uri,
|
||||||
|
// contentDescription = "Image",
|
||||||
|
// modifier = Modifier
|
||||||
|
// .fillMaxWidth().aspectRatio(1f)
|
||||||
|
// .noRippleClickable {
|
||||||
|
// navController.navigate(NavigationRoute.NewPostImageGrid.route)
|
||||||
|
// },
|
||||||
|
// contentScale = ContentScale.Crop
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
item {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(110.dp)
|
.fillMaxWidth()
|
||||||
|
.aspectRatio(1f)
|
||||||
.drawBehind {
|
.drawBehind {
|
||||||
drawRoundRect(color = Color(0xFF999999), style = stroke)
|
drawRoundRect(color = Color(0xFF999999), style = stroke)
|
||||||
}
|
}
|
||||||
.noRippleClickable{
|
.noRippleClickable {
|
||||||
pickImagesLauncher.launch("image/*")
|
pickImagesLauncher.launch("image/*")
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
@@ -275,10 +397,37 @@ fun AddImageGrid() {
|
|||||||
.align(Alignment.Center)
|
.align(Alignment.Center)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.drawBehind {
|
||||||
|
drawRoundRect(color = Color(0xFF999999), style = stroke)
|
||||||
|
}
|
||||||
|
.noRippleClickable {
|
||||||
|
val photoFile = File(context.cacheDir, "photo.jpg")
|
||||||
|
val photoUri: Uri = FileProvider.getUriForFile(
|
||||||
|
context,
|
||||||
|
"${context.packageName}.fileprovider",
|
||||||
|
photoFile
|
||||||
|
)
|
||||||
|
model.currentPhotoUri = photoUri
|
||||||
|
takePictureLauncher.launch(photoUri)
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
painter = painterResource(id = R.drawable.rider_pro_camera),
|
||||||
|
contentDescription = "Take Photo",
|
||||||
|
modifier = Modifier
|
||||||
|
.size(48.dp)
|
||||||
|
.align(Alignment.Center),
|
||||||
|
colorFilter = ColorFilter.tint(Color.Gray)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ object NewPostViewModel : ViewModel() {
|
|||||||
var imageUriList by mutableStateOf(listOf<String>())
|
var imageUriList by mutableStateOf(listOf<String>())
|
||||||
var relPostId by mutableStateOf<Int?>(null)
|
var relPostId by mutableStateOf<Int?>(null)
|
||||||
var relMoment by mutableStateOf<MomentEntity?>(null)
|
var relMoment by mutableStateOf<MomentEntity?>(null)
|
||||||
|
var currentPhotoUri: Uri? = null
|
||||||
fun asNewPost() {
|
fun asNewPost() {
|
||||||
textContent = ""
|
textContent = ""
|
||||||
searchPlaceAddressResult = null
|
searchPlaceAddressResult = null
|
||||||
|
|||||||
Reference in New Issue
Block a user