App Build

This commit is contained in:
2024-06-22 04:25:20 +08:00
commit 5ba5803ba2
105 changed files with 2789 additions and 0 deletions

View File

@@ -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
)

View 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 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)
}
}
}

View 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()
}
}
}

View 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))
}
}
}
}
}

View 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)
)

View 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) })
}

View 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
)
}

View 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)
}
}

View 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)
}
}
}

View 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 = "",
)
}
}
}
}

View 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)
)

View 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)

View 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
)
}

View 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
)
*/
)