App Build
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
package com.aiosman.riderpro
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
|
||||
data class ChatNotificationData(
|
||||
@DrawableRes val avatar: Int,
|
||||
val name: String,
|
||||
val message: String,
|
||||
val time: String,
|
||||
val unread: Int
|
||||
)
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.aiosman.riderpro
|
||||
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.paging.PagingState
|
||||
import kotlin.math.ceil
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
internal class TestChatBackend(
|
||||
private val backendDataList: List<ChatNotificationData>,
|
||||
private val loadDelay: Long = 500,
|
||||
) {
|
||||
val DataBatchSize = 1
|
||||
class DesiredLoadResultPageResponse(val data: List<ChatNotificationData>)
|
||||
/** Returns [DataBatchSize] items for a key */
|
||||
fun searchItemsByKey(key: Int): DesiredLoadResultPageResponse {
|
||||
val maxKey = ceil(backendDataList.size.toFloat() / DataBatchSize).toInt()
|
||||
if (key >= maxKey) {
|
||||
return DesiredLoadResultPageResponse(emptyList())
|
||||
}
|
||||
val from = key * DataBatchSize
|
||||
val to = minOf((key + 1) * DataBatchSize, backendDataList.size)
|
||||
val currentSublist = backendDataList.subList(from, to)
|
||||
return DesiredLoadResultPageResponse(currentSublist)
|
||||
}
|
||||
fun getAllData() = TestChatPagingSource(this, loadDelay)
|
||||
}
|
||||
internal class TestChatPagingSource(
|
||||
private val backend: TestChatBackend,
|
||||
private val loadDelay: Long,
|
||||
) : PagingSource<Int, ChatNotificationData>() {
|
||||
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, ChatNotificationData> {
|
||||
// Simulate latency
|
||||
delay(loadDelay)
|
||||
val pageNumber = params.key ?: 0
|
||||
val response = backend.searchItemsByKey(pageNumber)
|
||||
// Since 0 is the lowest page number, return null to signify no more pages should
|
||||
// be loaded before it.
|
||||
val prevKey = if (pageNumber > 0) pageNumber - 1 else null
|
||||
// This API defines that it's out of data when a page returns empty. When out of
|
||||
// data, we return `null` to signify no more pages should be loaded
|
||||
val nextKey = if (response.data.isNotEmpty()) pageNumber + 1 else null
|
||||
return LoadResult.Page(data = response.data, prevKey = prevKey, nextKey = nextKey)
|
||||
}
|
||||
override fun getRefreshKey(state: PagingState<Int, ChatNotificationData>): Int? {
|
||||
return state.anchorPosition?.let {
|
||||
state.closestPageToPosition(it)?.prevKey?.plus(1)
|
||||
?: state.closestPageToPosition(it)?.nextKey?.minus(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
227
app/src/main/java/com/aiosman/riderpro/MainActivity.kt
Normal file
227
app/src/main/java/com/aiosman/riderpro/MainActivity.kt
Normal file
@@ -0,0 +1,227 @@
|
||||
package com.aiosman.riderpro
|
||||
|
||||
import android.app.StatusBarManager
|
||||
import android.os.Bundle
|
||||
import android.widget.HorizontalScrollView
|
||||
import android.widget.ScrollView
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.SystemBarStyle
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.foundation.layout.windowInsetsStartWidth
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.foundation.text.BasicText
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.NavigationBar
|
||||
import androidx.compose.material3.NavigationBarItem
|
||||
import androidx.compose.material3.NavigationBarItemColors
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorProducer
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
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.android.gms.maps.model.CameraPosition
|
||||
import com.google.android.gms.maps.model.LatLng
|
||||
import com.google.maps.android.compose.GoogleMap
|
||||
import com.google.maps.android.compose.Marker
|
||||
import com.google.maps.android.compose.MarkerComposable
|
||||
import com.google.maps.android.compose.MarkerState
|
||||
import com.google.maps.android.compose.rememberCameraPositionState
|
||||
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
setContent {
|
||||
Navigation()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NavigationController(navController: NavHostController){
|
||||
NavHost(
|
||||
navController = navController, startDestination = NavigationItem.Home.route){
|
||||
composable(route = NavigationItem.Home.route){
|
||||
Home()
|
||||
}
|
||||
composable(route = NavigationItem.Street.route){
|
||||
Street()
|
||||
}
|
||||
composable(route = NavigationItem.Add.route){
|
||||
Add()
|
||||
}
|
||||
composable(route = NavigationItem.Message.route){
|
||||
Message()
|
||||
}
|
||||
composable(route = NavigationItem.Profile.route){
|
||||
Profile()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Navigation(){
|
||||
val navigationBarHeight = with(LocalDensity.current) {
|
||||
WindowInsets.navigationBars.getBottom(this).toDp()
|
||||
}
|
||||
val navController = rememberNavController()
|
||||
val item = listOf(
|
||||
NavigationItem.Home,
|
||||
NavigationItem.Street,
|
||||
NavigationItem.Add,
|
||||
NavigationItem.Message,
|
||||
NavigationItem.Profile
|
||||
)
|
||||
Scaffold (
|
||||
modifier = Modifier.statusBarsPadding(),
|
||||
topBar = {},
|
||||
bottomBar = {
|
||||
NavigationBar (
|
||||
modifier = Modifier.height(56.dp + navigationBarHeight),
|
||||
containerColor = Color.Black
|
||||
){
|
||||
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
||||
val currentRoute = navBackStackEntry?.destination?.route
|
||||
item.forEach{ it ->
|
||||
NavigationBarItem(
|
||||
selected = currentRoute == it.route ,
|
||||
onClick = {
|
||||
if(currentRoute != it.route){
|
||||
navController.navigate(it.route)
|
||||
}
|
||||
},
|
||||
colors = NavigationBarItemColors(
|
||||
selectedIconColor = Color.Red,
|
||||
selectedTextColor = Color.Red,
|
||||
selectedIndicatorColor = Color.Black,
|
||||
unselectedIconColor = Color.Red,
|
||||
unselectedTextColor = Color.Red,
|
||||
disabledIconColor = Color.Red,
|
||||
disabledTextColor = Color.Red,
|
||||
|
||||
),
|
||||
icon = {
|
||||
Icon(modifier = Modifier.size(24.dp),
|
||||
imageVector = it.icon(), contentDescription = null,
|
||||
tint = if(currentRoute == it.route) Color.Red else Color.White
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
){
|
||||
NavigationController(navController = navController)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Home(){
|
||||
Column (
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
){
|
||||
PagingBackendSample()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun Street(){
|
||||
Column (
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
StreetPage()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Add(){
|
||||
Column (
|
||||
modifier = Modifier.fillMaxSize().background(Color.Black),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
){
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Message(){
|
||||
Column (
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.Top,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
){
|
||||
MessagePage()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Profile(){
|
||||
Column (
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
){
|
||||
ProfilePage()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun GreetingPreview() {
|
||||
RiderProTheme {
|
||||
Surface (modifier = Modifier.fillMaxSize(), color = Color.White){
|
||||
Navigation()
|
||||
}
|
||||
}
|
||||
}
|
||||
231
app/src/main/java/com/aiosman/riderpro/Message.kt
Normal file
231
app/src/main/java/com/aiosman/riderpro/Message.kt
Normal file
@@ -0,0 +1,231 @@
|
||||
package com.aiosman.riderpro
|
||||
|
||||
import android.view.RoundedCorner
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Person
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.paging.LoadState
|
||||
import androidx.paging.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
|
||||
val chatNotificationData = ChatNotificationData(
|
||||
R.drawable.default_avatar,
|
||||
"Tokunaga Yae",
|
||||
"Memphis",
|
||||
"3 minutes ago",
|
||||
6
|
||||
)
|
||||
|
||||
private val ChatData = (0..10).toList().map { chatNotificationData}
|
||||
|
||||
@Composable
|
||||
fun MessagePage(){
|
||||
val myBackend = remember { TestChatBackend(ChatData) }
|
||||
val pager = remember {
|
||||
Pager(
|
||||
PagingConfig(
|
||||
pageSize = myBackend.DataBatchSize,
|
||||
enablePlaceholders = true,
|
||||
maxSize = 200
|
||||
)
|
||||
) {
|
||||
myBackend.getAllData()
|
||||
}
|
||||
}
|
||||
val lazyPagingItems = pager.flow.collectAsLazyPagingItems()
|
||||
MessageTopSwitchBtnGroup()
|
||||
MessageBarrierLine()
|
||||
MessageNotification()
|
||||
LazyColumn (
|
||||
modifier = Modifier.padding(bottom = 20.dp)
|
||||
){
|
||||
if (lazyPagingItems.loadState.refresh == LoadState.Loading) {
|
||||
item {
|
||||
MomentListLoading()
|
||||
}
|
||||
}
|
||||
items(count = lazyPagingItems.itemCount) { index ->
|
||||
val item = lazyPagingItems[index]
|
||||
if (item != null) {
|
||||
ChatItem(item)
|
||||
}
|
||||
}
|
||||
if (lazyPagingItems.loadState.append == LoadState.Loading) {
|
||||
item {
|
||||
MomentListLoading()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MessageTopSwitchBtnGroup(){
|
||||
Column (
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(113.dp)
|
||||
) {
|
||||
Row(modifier = Modifier.fillMaxSize()){
|
||||
val notificationBtnModifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.weight(1f)
|
||||
NotificationBtn(notificationBtnModifier,drawableId = R.drawable.rider_pro_like,
|
||||
displayText = "LIKE")
|
||||
NotificationBtn(notificationBtnModifier,drawableId = R.drawable.rider_pro_followers,
|
||||
displayText = "FOLLOWERS")
|
||||
NotificationBtn(notificationBtnModifier,drawableId = R.drawable.rider_pro_comments,
|
||||
displayText = "COMMENTS")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MessageBarrierLine(){
|
||||
Box(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(1.dp)
|
||||
.padding(start = 24.dp, end = 24.dp)
|
||||
.border(width = 1.dp, Color(0f, 0f, 0f, 0.1f))
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NotificationBtn(modifier: Modifier, drawableId: Int, displayText: String){
|
||||
Box(modifier = modifier,
|
||||
contentAlignment = Alignment.Center){
|
||||
Column(modifier = Modifier
|
||||
.size(width = 79.dp, height = 88.dp),
|
||||
verticalArrangement = Arrangement.Top,
|
||||
horizontalAlignment = Alignment.CenterHorizontally){
|
||||
Box(modifier = Modifier
|
||||
.padding(top = 8.dp)
|
||||
.size(width = 55.dp, height = 55.dp)
|
||||
.shadow(
|
||||
spotColor = Color.White,
|
||||
ambientColor = Color(0f, 0f, 0f, 0.4f),
|
||||
elevation = 12.dp,
|
||||
),
|
||||
contentAlignment = Alignment.Center,
|
||||
){
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.size(width = 24.dp, height = 24.dp),
|
||||
painter = painterResource(id = drawableId),
|
||||
contentDescription = ""
|
||||
)
|
||||
}
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 8.dp),
|
||||
text = displayText,
|
||||
fontSize = 12.sp, style = TextStyle(fontWeight = FontWeight.Bold)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MessageNotification(){
|
||||
Row(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(88.dp)
|
||||
.padding(start = 22.dp, top = 20.dp, bottom = 20.dp, end = 24.dp),
|
||||
verticalAlignment = Alignment.CenterVertically){
|
||||
Box(modifier = Modifier
|
||||
.size(width = 48.dp, height = 48.dp)
|
||||
.border(width = 1.dp, Color(0f, 0f, 0f, 0.1f), RoundedCornerShape(2.dp)),
|
||||
contentAlignment = Alignment.Center){
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.size(width = 24.dp, height = 24.dp),
|
||||
painter = painterResource(R.drawable.rider_pro_notification),
|
||||
contentDescription = ""
|
||||
)
|
||||
}
|
||||
Text(text = "NOTIFICATIONS", fontSize = 18.sp, modifier = Modifier.padding(start = 12.dp), style = TextStyle(fontWeight = FontWeight.Bold))
|
||||
Box(modifier = Modifier.fillMaxSize(),
|
||||
contentAlignment = Alignment.CenterEnd){
|
||||
Box(modifier = Modifier
|
||||
.height(18.dp)
|
||||
.clip(RoundedCornerShape(10.dp))
|
||||
.background(Color.Red),
|
||||
contentAlignment = Alignment.Center){
|
||||
Text(text = "18", fontSize = 10.sp, color = Color.White, modifier = Modifier.padding(start = 6.dp, end = 6.dp, top = 2.dp, bottom = 2.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ChatItem(chatNotificationData: ChatNotificationData){
|
||||
Row(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(88.dp)
|
||||
.padding(start = 22.dp, top = 20.dp, bottom = 20.dp, end = 24.dp),
|
||||
verticalAlignment = Alignment.CenterVertically){
|
||||
Image(modifier = Modifier
|
||||
.size(width = 48.dp, height = 48.dp)
|
||||
.clip(RoundedCornerShape(2.dp)),
|
||||
painter = painterResource(chatNotificationData.avatar),
|
||||
contentDescription = "")
|
||||
Column (
|
||||
modifier = Modifier.fillMaxHeight().padding(start = 12.dp),
|
||||
verticalArrangement = Arrangement.SpaceAround
|
||||
){
|
||||
Text(text = chatNotificationData.name, fontSize = 18.sp, style = TextStyle(fontWeight = FontWeight.Bold))
|
||||
Text(text = chatNotificationData.message, fontSize = 14.sp,
|
||||
color = Color(0f, 0f, 0f, 0.6f))
|
||||
}
|
||||
|
||||
Box(modifier = Modifier.fillMaxSize(),
|
||||
contentAlignment = Alignment.CenterEnd){
|
||||
Column (
|
||||
modifier = Modifier.fillMaxHeight(),
|
||||
verticalArrangement = Arrangement.SpaceAround,
|
||||
horizontalAlignment = Alignment.End
|
||||
){
|
||||
Text(text = chatNotificationData.time, fontSize = 12.sp,color = Color(0f, 0f, 0f, 0.4f))
|
||||
Box(modifier = Modifier
|
||||
.height(18.dp)
|
||||
.clip(RoundedCornerShape(10.dp))
|
||||
.background(Color.Red),
|
||||
contentAlignment = Alignment.Center){
|
||||
Text(text = chatNotificationData.unread.toString(), fontSize = 10.sp, color = Color.White, modifier = Modifier.padding(start = 6.dp, end = 6.dp, top = 2.dp, bottom = 2.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
74
app/src/main/java/com/aiosman/riderpro/MomentItem.kt
Normal file
74
app/src/main/java/com/aiosman/riderpro/MomentItem.kt
Normal file
@@ -0,0 +1,74 @@
|
||||
package com.aiosman.riderpro
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
|
||||
data class MomentItem(
|
||||
val id: Int,
|
||||
@DrawableRes val avatar: Int,
|
||||
val nickname: String,
|
||||
val location: String,
|
||||
val time: String,
|
||||
val followStatus: Boolean,
|
||||
val momentTextContent: String,
|
||||
@DrawableRes val momentPicture: Int,
|
||||
val likeCount: Int,
|
||||
val commentCount: Int,
|
||||
val shareCount: Int,
|
||||
val favoriteCount: Int
|
||||
)
|
||||
|
||||
val momentTestItem = MomentItem(
|
||||
id = 1,
|
||||
avatar = R.drawable.default_avatar,
|
||||
nickname = "Onyama Limba",
|
||||
location = "Japan",
|
||||
time = "2023.02.02 11:23",
|
||||
followStatus = false,
|
||||
momentTextContent = "By strongarming Ducati into giving him the factory seat.Marquez effectively …",
|
||||
momentPicture = R.drawable.default_moment_img,
|
||||
likeCount = 21,
|
||||
commentCount = 43,
|
||||
shareCount = 33,
|
||||
favoriteCount = 211)
|
||||
|
||||
val profileMomentItems = listOf(
|
||||
MomentItem(
|
||||
id = 1,
|
||||
avatar = R.drawable.default_avatar,
|
||||
nickname = "Onyama Limba",
|
||||
location = "Japan",
|
||||
time = "2024.06.08 12:23",
|
||||
followStatus = false,
|
||||
momentTextContent = "Modifications that are made to make your motorbike more like you",
|
||||
momentPicture = R.drawable.rider_pro_moment_demo_1,
|
||||
likeCount = 2345,
|
||||
commentCount = 12,
|
||||
shareCount = 33,
|
||||
favoriteCount = 211),
|
||||
MomentItem(
|
||||
id = 1,
|
||||
avatar = R.drawable.default_avatar,
|
||||
nickname = "Onyama Limba",
|
||||
location = "Japan",
|
||||
time = "2024.03.03 12:31",
|
||||
followStatus = false,
|
||||
momentTextContent = "At least 500 units will be made, to meet homologation requirements.",
|
||||
momentPicture = R.drawable.rider_pro_moment_demo_2,
|
||||
likeCount = 211,
|
||||
commentCount = 33,
|
||||
shareCount = 33,
|
||||
favoriteCount = 211),
|
||||
MomentItem(
|
||||
id = 1,
|
||||
avatar = R.drawable.default_avatar,
|
||||
nickname = "Onyama Limba",
|
||||
location = "Japan",
|
||||
time = "2024.02.02 11:23",
|
||||
followStatus = false,
|
||||
momentTextContent = "The bike is already FIM legal (and soon-to-be MotoAmerica legal as well).",
|
||||
momentPicture = R.drawable.rider_pro_moment_demo_3,
|
||||
likeCount = 987,
|
||||
commentCount = 21,
|
||||
shareCount = 33,
|
||||
favoriteCount = 211)
|
||||
)
|
||||
23
app/src/main/java/com/aiosman/riderpro/NavigationItem.kt
Normal file
23
app/src/main/java/com/aiosman/riderpro/NavigationItem.kt
Normal file
@@ -0,0 +1,23 @@
|
||||
package com.aiosman.riderpro
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.AccountCircle
|
||||
import androidx.compose.material.icons.filled.Email
|
||||
import androidx.compose.material.icons.filled.Home
|
||||
import androidx.compose.material.icons.filled.Place
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
|
||||
sealed class NavigationItem(val route:String, val icon: @Composable () -> ImageVector){
|
||||
data object Home : NavigationItem("Home",
|
||||
{ ImageVector.vectorResource(R.drawable.rider_pro_home) })
|
||||
data object Street : NavigationItem("Street",
|
||||
{ ImageVector.vectorResource(R.drawable.rider_pro_street) })
|
||||
data object Add : NavigationItem("Add",
|
||||
{ ImageVector.vectorResource(R.drawable.rider_pro_moment_add) })
|
||||
data object Message : NavigationItem("Message",
|
||||
{ ImageVector.vectorResource(R.drawable.rider_pro_message) })
|
||||
data object Profile : NavigationItem("Profile",
|
||||
{ ImageVector.vectorResource(R.drawable.rider_pro_profile) })
|
||||
}
|
||||
236
app/src/main/java/com/aiosman/riderpro/PagingSimple.kt
Normal file
236
app/src/main/java/com/aiosman/riderpro/PagingSimple.kt
Normal file
@@ -0,0 +1,236 @@
|
||||
package com.aiosman.riderpro
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.PlatformTextStyle
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.LineHeightStyle
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.em
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.paging.LoadState
|
||||
import androidx.paging.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
|
||||
private val DATA = (0..60).toList().map { momentTestItem}
|
||||
|
||||
@Composable
|
||||
fun PagingBackendSample() {
|
||||
val myBackend = remember { TestBackend(DATA) }
|
||||
val pager = remember {
|
||||
Pager(
|
||||
PagingConfig(
|
||||
pageSize = myBackend.DataBatchSize,
|
||||
enablePlaceholders = true,
|
||||
maxSize = 200
|
||||
)
|
||||
) {
|
||||
myBackend.getAllData()
|
||||
}
|
||||
}
|
||||
val lazyPagingItems = pager.flow.collectAsLazyPagingItems()
|
||||
|
||||
LazyColumn {
|
||||
if (lazyPagingItems.loadState.refresh == LoadState.Loading) {
|
||||
item {
|
||||
MomentListLoading()
|
||||
}
|
||||
}
|
||||
items(count = lazyPagingItems.itemCount) { index ->
|
||||
val item = lazyPagingItems[index]
|
||||
if (item != null) {
|
||||
MomentCard(item)
|
||||
}
|
||||
}
|
||||
if (lazyPagingItems.loadState.append == LoadState.Loading) {
|
||||
item {
|
||||
MomentListLoading()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MomentCard(momentItem: MomentItem){
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
){
|
||||
MomentTopRowGroup(momentItem = momentItem)
|
||||
MomentContentGroup(momentItem = momentItem)
|
||||
val momentOperateBtnBoxModifier = Modifier.fillMaxHeight().weight(1f)
|
||||
MomentBottomOperateRowGroup(momentOperateBtnBoxModifier)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MomentName(name: String){
|
||||
Text(
|
||||
modifier = Modifier,
|
||||
textAlign = TextAlign.Start,
|
||||
text = name,
|
||||
color = Color(0f,0f,0f),
|
||||
fontSize = 16.sp, style = TextStyle(fontWeight = FontWeight.Bold))
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MomentFollowBtn(){
|
||||
Box(modifier = Modifier
|
||||
.size(width = 53.dp, height = 18.dp)
|
||||
.padding(start = 8.dp),
|
||||
contentAlignment = Alignment.Center){
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.fillMaxSize(),
|
||||
painter = painterResource(id = R.drawable.follow_bg),
|
||||
contentDescription = ""
|
||||
)
|
||||
Text(text = "Follow",
|
||||
color = Color.White,
|
||||
fontSize = 12.sp)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MomentPostLocation(location: String){
|
||||
Text(text = location,
|
||||
color = Color(0f,0f,0f,0.6f),
|
||||
fontSize = 12.sp)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MomentPostTime(time: String){
|
||||
Text(modifier = Modifier.padding(start = 8.dp),
|
||||
text = time, color = Color(0f,0f,0f,0.6f),
|
||||
fontSize = 12.sp
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MomentTopRowGroup(momentItem: MomentItem){
|
||||
Row (modifier = Modifier
|
||||
.height(40.dp)
|
||||
.padding(top = 0.dp, bottom = 0.dp, start = 24.dp, end = 24.dp)
|
||||
){
|
||||
Image(
|
||||
painter = painterResource(id = momentItem.avatar),
|
||||
contentDescription = ""
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.defaultMinSize()
|
||||
.padding(start = 12.dp, end = 12.dp)
|
||||
) {
|
||||
Row (modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(22.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
){
|
||||
MomentName(momentItem.nickname)
|
||||
MomentFollowBtn()
|
||||
}
|
||||
Row (modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(21.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
){
|
||||
MomentPostLocation(momentItem.location)
|
||||
MomentPostTime(momentItem.time)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MomentContentGroup(momentItem: MomentItem){
|
||||
Text(text = momentItem.momentTextContent,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 22.dp, bottom = 16.dp, start = 24.dp, end = 24.dp),
|
||||
fontSize = 16.sp
|
||||
)
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
painter = painterResource(id = momentItem.momentPicture),
|
||||
contentDescription = ""
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MomentOperateBtn(@DrawableRes icon: Int, count: String){
|
||||
Row (verticalAlignment = Alignment.CenterVertically){
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.size(width = 24.dp, height = 24.dp),
|
||||
painter = painterResource(id = icon),
|
||||
contentDescription = "")
|
||||
Text(text = count,
|
||||
modifier = Modifier.padding(start = 7.dp),
|
||||
fontSize = 12.sp,)
|
||||
}
|
||||
}
|
||||
@Composable
|
||||
fun MomentBottomOperateRowGroup(modifier: Modifier){
|
||||
Row (modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(56.dp)){
|
||||
Box(modifier = modifier,
|
||||
contentAlignment = Alignment.Center
|
||||
){
|
||||
MomentOperateBtn(icon = R.drawable.rider_pro_like, count = "21")
|
||||
}
|
||||
Box(modifier = modifier,
|
||||
contentAlignment = Alignment.Center
|
||||
){
|
||||
MomentOperateBtn(icon = R.drawable.rider_pro_moment_comment, count = "43")
|
||||
}
|
||||
Box(modifier = modifier,
|
||||
contentAlignment = Alignment.Center
|
||||
){
|
||||
MomentOperateBtn(icon = R.drawable.rider_pro_share, count = "33")
|
||||
}
|
||||
Box(modifier = modifier,
|
||||
contentAlignment = Alignment.Center
|
||||
){
|
||||
MomentOperateBtn(icon = R.drawable.rider_pro_favoriate, count = "211")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MomentListLoading(){
|
||||
CircularProgressIndicator(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentWidth(Alignment.CenterHorizontally),
|
||||
color = Color.Red
|
||||
)
|
||||
}
|
||||
305
app/src/main/java/com/aiosman/riderpro/Profile.kt
Normal file
305
app/src/main/java/com/aiosman/riderpro/Profile.kt
Normal file
@@ -0,0 +1,305 @@
|
||||
package com.aiosman.riderpro
|
||||
|
||||
import android.widget.ScrollView
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
|
||||
@Composable
|
||||
fun ProfilePage(){
|
||||
Column (
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(bottom = 150.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Top,
|
||||
){
|
||||
CarGroup()
|
||||
UserInformation()
|
||||
RidingStyle()
|
||||
UserMoment()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CarGroup(){
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 54.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
CarTopInformation()
|
||||
CarTopPicture()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CarTopInformation(){
|
||||
Row{
|
||||
Text(text = "BMW", color = Color.Black, fontSize = 12.sp, style = TextStyle(fontWeight = FontWeight.Bold))
|
||||
Text(modifier = Modifier.padding(start = 4.dp), text = "/", color = Color.Gray, fontSize = 12.sp)
|
||||
Text(modifier = Modifier.padding(start = 4.dp), text = "M1000RR", color = Color.Gray, fontSize = 12.sp)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CarTopPicture(){
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.size(width = 336.dp, height = 224.dp)
|
||||
.padding(top = 42.dp),
|
||||
painter = painterResource(id = R.drawable.default_profile_moto), contentDescription = "")
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UserInformation(){
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 8.dp, start = 33.dp, end = 33.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally){
|
||||
Row (modifier = Modifier.fillMaxWidth()){
|
||||
val userInfoModifier = Modifier.weight(1f)
|
||||
UserInformationFollowers(userInfoModifier)
|
||||
UserInformationBasic(userInfoModifier)
|
||||
UserInformationFollowing(userInfoModifier)
|
||||
}
|
||||
UserInformationSlogan()
|
||||
CommunicationOperatorGroup()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UserInformationFollowers(modifier: Modifier){
|
||||
Column(modifier = modifier.padding(top = 31.dp)) {
|
||||
Text(modifier = Modifier.padding(bottom = 5.dp),text = "183", fontSize = 24.sp, color = Color.Black, style = TextStyle(fontWeight = FontWeight.Bold))
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
.size(width = 88.83.dp, height = 1.dp)
|
||||
.border(width = 1.dp, color = Color.Gray)
|
||||
.padding(top = 5.dp, bottom = 5.dp)
|
||||
)
|
||||
Text(modifier = Modifier.padding(top = 5.dp),text = "FOLLOWERS", fontSize = 12.sp, color = Color.Black, style = TextStyle(fontWeight = FontWeight.Bold))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UserInformationBasic(modifier: Modifier){
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Box(modifier = Modifier.size(width = 112.dp, height = 112.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
){
|
||||
Image(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
painter = painterResource(id = R.drawable.avatar_bold), contentDescription = "")
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.size(width = 88.dp, height = 88.dp)
|
||||
.clip(
|
||||
RoundedCornerShape(88.dp)
|
||||
),
|
||||
painter = painterResource(id = R.drawable.default_avatar), contentDescription = "")
|
||||
|
||||
}
|
||||
Text(modifier = Modifier.padding(top = 8.dp), text = "Atom", fontSize = 32.sp, color = Color.Black, style = TextStyle(fontWeight = FontWeight.Bold))
|
||||
Text(modifier = Modifier.padding(top = 4.dp), text = "America", fontSize = 12.sp, color = Color.Gray)
|
||||
Text(modifier = Modifier.padding(top = 4.dp), text = "Member since Jun 4.2019", fontSize = 12.sp, color = Color.Gray)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UserInformationFollowing(modifier: Modifier){
|
||||
Column(modifier = modifier.padding(top = 6.dp),
|
||||
horizontalAlignment = Alignment.End) {
|
||||
Text(modifier = Modifier.padding(bottom = 5.dp), text = "306", fontSize = 24.sp, color = Color.Black, style = TextStyle(fontWeight = FontWeight.Bold))
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(width = 88.83.dp, height = 1.dp)
|
||||
.border(width = 1.dp, color = Color.Gray)
|
||||
|
||||
)
|
||||
Text(modifier = Modifier.padding(top = 5.dp),text = "FOLLOWING", fontSize = 12.sp, color = Color.Black, style = TextStyle(fontWeight = FontWeight.Bold))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UserInformationSlogan(){
|
||||
Text(modifier = Modifier.padding(top = 23.dp),text = "Ridering shooting fishing not much more", fontSize = 13.sp, color = Color.Black, style = TextStyle(fontWeight = FontWeight.Bold))
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CommunicationOperatorGroup(){
|
||||
Row (modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 16.dp), horizontalArrangement = Arrangement.Center){
|
||||
Box(modifier = Modifier.size(width = 142.dp, height = 40.dp),
|
||||
contentAlignment = Alignment.Center){
|
||||
Image(modifier = Modifier.fillMaxSize(), painter = painterResource(id = R.drawable.rider_pro_profile_follow), contentDescription = "")
|
||||
Text(text = "FOLLOW", fontSize = 16.sp, color = Color.White, style = TextStyle(fontWeight = FontWeight.Bold))
|
||||
}
|
||||
Box(modifier = Modifier
|
||||
.size(width = 142.dp, height = 40.dp)
|
||||
.padding(start = 6.dp),
|
||||
contentAlignment = Alignment.Center){
|
||||
Image(modifier = Modifier.fillMaxSize(),painter = painterResource(id = R.drawable.rider_pro_profile_message), contentDescription = "")
|
||||
Text(text = "MESSAGE", fontSize = 16.sp, color = Color.White, style = TextStyle(fontWeight = FontWeight.Bold))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
fun RidingStyle(){
|
||||
Column(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 24.dp, top = 40.dp, end = 24.dp),
|
||||
horizontalAlignment = Alignment.Start) {
|
||||
Text(text = "RIDING STYLES", fontSize = 18.sp, color = Color.Black, style = TextStyle(fontWeight = FontWeight.Bold))
|
||||
Image(modifier = Modifier
|
||||
.padding(top = 4.dp)
|
||||
.height(8.dp),painter = painterResource(id = R.drawable.rider_pro_profile_line), contentDescription = "")
|
||||
FlowRow (modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 24.dp)){
|
||||
RidingStyleItem(styleContent = "Cruiser")
|
||||
RidingStyleItem(styleContent = "Bobber")
|
||||
RidingStyleItem(styleContent = "Cafe")
|
||||
RidingStyleItem(styleContent = "Chopper")
|
||||
RidingStyleItem(styleContent = "Sport")
|
||||
RidingStyleItem(styleContent = "Vintage")
|
||||
RidingStyleItem(styleContent = "Trike")
|
||||
RidingStyleItem(styleContent = "Touring")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RidingStyleItem(styleContent: String){
|
||||
Box(modifier = Modifier.padding(bottom = 8.dp, end = 8.dp),
|
||||
contentAlignment = Alignment.Center) {
|
||||
Image(modifier = Modifier.shadow(
|
||||
ambientColor = Color.Gray,
|
||||
spotColor = Color(0f,0f,0f,0.2f),
|
||||
elevation = 20.dp,
|
||||
), painter = painterResource(id = R.drawable.rider_pro_style_wrapper), contentDescription = "")
|
||||
Text(modifier = Modifier.padding(start = 5.dp, end = 5.dp), text = styleContent, fontSize = 12.sp, color = Color.Gray, style = TextStyle(fontWeight = FontWeight.Bold))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UserMoment(){
|
||||
profileMomentItems.forEach(
|
||||
action = { MomentPostUnit(it) }
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MomentPostUnit(momentItem: MomentItem){
|
||||
TimeGroup(momentItem.time)
|
||||
MomentCard(momentItem.momentTextContent,
|
||||
momentItem.momentPicture,
|
||||
momentItem.likeCount.toString(),
|
||||
momentItem.commentCount.toString())
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TimeGroup(time: String = "2024.06.08 12:23"){
|
||||
Row(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 24.dp, top = 40.dp, end = 24.dp),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically){
|
||||
Image(
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
painter = painterResource(id = R.drawable.rider_pro_moment_time_flag), contentDescription = "")
|
||||
Text(text = time,fontSize = 22.sp, color = Color.Black, style = TextStyle(fontWeight = FontWeight.Bold))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MomentCard(content: String,@DrawableRes picture: Int, like: String, comment: String){
|
||||
Column(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 48.dp, top = 18.dp, end = 24.dp)
|
||||
.border(width = 1.dp, color = Color(0f,0f,0f,0.1f), shape = RoundedCornerShape(6.dp))
|
||||
){
|
||||
MomentCardTopContent(content)
|
||||
MomentCardPicture(picture)
|
||||
MomentCardOperation(like,comment)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MomentCardTopContent(content: String){
|
||||
Row(modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically){
|
||||
Text(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
text = content,fontSize = 16.sp, color = Color.Black)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MomentCardPicture(@DrawableRes drawable: Int){
|
||||
Image(modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(16.dp), painter = painterResource(id = drawable), contentDescription = "")
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MomentCardOperation(like: String, comment: String){
|
||||
Row(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(56.dp),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically){
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
MomentCardOperationItem(drawable = R.drawable.rider_pro_like, number = like, modifier = Modifier.weight(1f))
|
||||
MomentCardOperationItem(drawable = R.drawable.rider_pro_moment_comment, number = comment, modifier = Modifier.weight(1f))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MomentCardOperationItem(@DrawableRes drawable: Int, number: String, modifier: Modifier){
|
||||
Row(modifier = modifier,
|
||||
verticalAlignment = Alignment.CenterVertically){
|
||||
Image(modifier = Modifier.padding(start = 16.dp, end = 8.dp),
|
||||
painter = painterResource(id = drawable), contentDescription = "")
|
||||
Text(text = number)
|
||||
}
|
||||
}
|
||||
50
app/src/main/java/com/aiosman/riderpro/SimpleUtils.kt
Normal file
50
app/src/main/java/com/aiosman/riderpro/SimpleUtils.kt
Normal file
@@ -0,0 +1,50 @@
|
||||
package com.aiosman.riderpro
|
||||
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.paging.PagingState
|
||||
import kotlin.math.ceil
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
internal class TestBackend(
|
||||
private val backendDataList: List<MomentItem>,
|
||||
private val loadDelay: Long = 500,
|
||||
) {
|
||||
val DataBatchSize = 5
|
||||
class DesiredLoadResultPageResponse(val data: List<MomentItem>)
|
||||
/** Returns [DataBatchSize] items for a key */
|
||||
fun searchItemsByKey(key: Int): DesiredLoadResultPageResponse {
|
||||
val maxKey = ceil(backendDataList.size.toFloat() / DataBatchSize).toInt()
|
||||
if (key >= maxKey) {
|
||||
return DesiredLoadResultPageResponse(emptyList())
|
||||
}
|
||||
val from = key * DataBatchSize
|
||||
val to = minOf((key + 1) * DataBatchSize, backendDataList.size)
|
||||
val currentSublist = backendDataList.subList(from, to)
|
||||
return DesiredLoadResultPageResponse(currentSublist)
|
||||
}
|
||||
fun getAllData() = TestPagingSource(this, loadDelay)
|
||||
}
|
||||
internal class TestPagingSource(
|
||||
private val backend: TestBackend,
|
||||
private val loadDelay: Long,
|
||||
) : PagingSource<Int, MomentItem>() {
|
||||
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, MomentItem> {
|
||||
// Simulate latency
|
||||
delay(loadDelay)
|
||||
val pageNumber = params.key ?: 0
|
||||
val response = backend.searchItemsByKey(pageNumber)
|
||||
// Since 0 is the lowest page number, return null to signify no more pages should
|
||||
// be loaded before it.
|
||||
val prevKey = if (pageNumber > 0) pageNumber - 1 else null
|
||||
// This API defines that it's out of data when a page returns empty. When out of
|
||||
// data, we return `null` to signify no more pages should be loaded
|
||||
val nextKey = if (response.data.isNotEmpty()) pageNumber + 1 else null
|
||||
return LoadResult.Page(data = response.data, prevKey = prevKey, nextKey = nextKey)
|
||||
}
|
||||
override fun getRefreshKey(state: PagingState<Int, MomentItem>): Int? {
|
||||
return state.anchorPosition?.let {
|
||||
state.closestPageToPosition(it)?.prevKey?.plus(1)
|
||||
?: state.closestPageToPosition(it)?.nextKey?.minus(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
38
app/src/main/java/com/aiosman/riderpro/Street.kt
Normal file
38
app/src/main/java/com/aiosman/riderpro/Street.kt
Normal file
@@ -0,0 +1,38 @@
|
||||
package com.aiosman.riderpro
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import com.google.android.gms.maps.model.CameraPosition
|
||||
import com.google.android.gms.maps.model.LatLng
|
||||
import com.google.maps.android.compose.GoogleMap
|
||||
import com.google.maps.android.compose.MarkerComposable
|
||||
import com.google.maps.android.compose.MarkerState
|
||||
import com.google.maps.android.compose.rememberCameraPositionState
|
||||
|
||||
@Composable
|
||||
fun StreetPage(){
|
||||
val cameraPositionState = rememberCameraPositionState {
|
||||
position = CameraPosition.fromLatLngZoom(
|
||||
LatLng(countries[1].lat, countries[1].lng),
|
||||
4f)
|
||||
}
|
||||
GoogleMap(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
cameraPositionState = cameraPositionState
|
||||
) {
|
||||
countries.forEach { position ->
|
||||
MarkerComposable(
|
||||
state = MarkerState(position = LatLng(position.lat, position.lng)),
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.rider_pro_map_mark),
|
||||
contentDescription = "",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
28
app/src/main/java/com/aiosman/riderpro/TestStreetMap.kt
Normal file
28
app/src/main/java/com/aiosman/riderpro/TestStreetMap.kt
Normal file
@@ -0,0 +1,28 @@
|
||||
package com.aiosman.riderpro
|
||||
|
||||
data class StreetPosition(
|
||||
val name:String,
|
||||
val lat:Double,
|
||||
val lng:Double
|
||||
)
|
||||
val countries = listOf(
|
||||
StreetPosition("哈龙湾, 越南",16.5000, 107.1000),
|
||||
StreetPosition("芽庄, 越南",12.2500, 109.0833),
|
||||
StreetPosition("岘港, 越南",16.0667, 108.2167),
|
||||
StreetPosition("美奈, 越南",11.9333, 108.9833),
|
||||
StreetPosition("富国岛, 越南",10.0000, 104.0000),
|
||||
StreetPosition("金三角, 泰国, 缅甸, 老挝",20.2500, 99.7500),
|
||||
StreetPosition("普吉岛, 泰国",7.9444, 98.3000),
|
||||
StreetPosition("苏梅岛, 泰国",9.5333, 99.9333),
|
||||
StreetPosition("曼谷, 泰国",13.7500, 100.5000),
|
||||
StreetPosition("马六甲, 马来西亚",2.2000, 102.2500),
|
||||
StreetPosition("兰卡威群岛, 马来西亚",6.3000, 99.9000),
|
||||
StreetPosition("沙巴, 马来西亚",6.0833, 116.0833),
|
||||
StreetPosition("巴厘岛, 印度尼西亚",8.3333, 115.1000),
|
||||
StreetPosition("龙目岛, 印度尼西亚",8.3333, 116.4000),
|
||||
StreetPosition("婆罗洲, 印度尼西亚",3.0000, 114.0000),
|
||||
StreetPosition("宿务, 菲律宾",10.3167, 123.8833),
|
||||
StreetPosition("长滩岛, 菲律宾",11.5833, 121.9167),
|
||||
StreetPosition("保和岛, 菲律宾",10.3000, 123.3333),
|
||||
StreetPosition("科隆岛, 菲律宾",5.1167, 119.3333)
|
||||
)
|
||||
11
app/src/main/java/com/aiosman/riderpro/ui/theme/Color.kt
Normal file
11
app/src/main/java/com/aiosman/riderpro/ui/theme/Color.kt
Normal file
@@ -0,0 +1,11 @@
|
||||
package com.aiosman.riderpro.ui.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val Purple80 = Color(0xFFD0BCFF)
|
||||
val PurpleGrey80 = Color(0xFFCCC2DC)
|
||||
val Pink80 = Color(0xFFEFB8C8)
|
||||
|
||||
val Purple40 = Color(0xFF6650a4)
|
||||
val PurpleGrey40 = Color(0xFF625b71)
|
||||
val Pink40 = Color(0xFF7D5260)
|
||||
58
app/src/main/java/com/aiosman/riderpro/ui/theme/Theme.kt
Normal file
58
app/src/main/java/com/aiosman/riderpro/ui/theme/Theme.kt
Normal file
@@ -0,0 +1,58 @@
|
||||
package com.aiosman.riderpro.ui.theme
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
||||
private val DarkColorScheme = darkColorScheme(
|
||||
primary = Purple80,
|
||||
secondary = PurpleGrey80,
|
||||
tertiary = Pink80
|
||||
)
|
||||
|
||||
private val LightColorScheme = lightColorScheme(
|
||||
primary = Purple40,
|
||||
secondary = PurpleGrey40,
|
||||
tertiary = Pink40
|
||||
|
||||
/* Other default colors to override
|
||||
background = Color(0xFFFFFBFE),
|
||||
surface = Color(0xFFFFFBFE),
|
||||
onPrimary = Color.White,
|
||||
onSecondary = Color.White,
|
||||
onTertiary = Color.White,
|
||||
onBackground = Color(0xFF1C1B1F),
|
||||
onSurface = Color(0xFF1C1B1F),
|
||||
*/
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun RiderProTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
// Dynamic color is available on Android 12+
|
||||
dynamicColor: Boolean = true,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val colorScheme = when {
|
||||
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
val context = LocalContext.current
|
||||
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||
}
|
||||
|
||||
darkTheme -> DarkColorScheme
|
||||
else -> LightColorScheme
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = Typography,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
34
app/src/main/java/com/aiosman/riderpro/ui/theme/Type.kt
Normal file
34
app/src/main/java/com/aiosman/riderpro/ui/theme/Type.kt
Normal file
@@ -0,0 +1,34 @@
|
||||
package com.aiosman.riderpro.ui.theme
|
||||
|
||||
import androidx.compose.material3.Typography
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
// Set of Material typography styles to start with
|
||||
val Typography = Typography(
|
||||
bodyLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 16.sp,
|
||||
lineHeight = 24.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
/* Other default text styles to override
|
||||
titleLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 22.sp,
|
||||
lineHeight = 28.sp,
|
||||
letterSpacing = 0.sp
|
||||
),
|
||||
labelSmall = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 11.sp,
|
||||
lineHeight = 16.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
*/
|
||||
)
|
||||
Reference in New Issue
Block a user