TP-35964 Implementation of Widget Discovery Feature in UiTron App (#139)

This commit is contained in:
Aparna Vadlamani
2023-08-01 13:57:44 +05:30
committed by GitHub
parent efa4579e45
commit 54ff8b7c28
37 changed files with 1466 additions and 7 deletions

View File

@@ -1,6 +1,8 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
id 'com.google.dagger.hilt.android'
}
android {
@@ -42,11 +44,11 @@ android {
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = '1.8'
jvmTarget = '11'
}
buildFeatures {
compose true
@@ -62,7 +64,7 @@ android {
}
dependencies {
implementation platform("androidx.compose:compose-bom:2023.01.00")
implementation platform("androidx.compose:compose-bom:2023.06.01")
implementation project(':navi-uitron')
implementation 'androidx.appcompat:appcompat:1.6.1'
@@ -82,4 +84,29 @@ dependencies {
implementation "androidx.navigation:navigation-compose:$nav_version"
implementation 'androidx.profileinstaller:profileinstaller:1.3.1'
// Retrofit
api "com.squareup.retrofit2:retrofit:2.9.0"
api 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.okhttp3:okhttp:3.6.0'
implementation 'com.squareup.okhttp3:logging-interceptor:4.4.1'
// compose with lifecycle
api "androidx.lifecycle:lifecycle-runtime-compose:2.6.1"
// Pagination
implementation "androidx.paging:paging-runtime-ktx:3.1.1"
implementation "androidx.paging:paging-compose:1.0.0-alpha17"
//Room
def room_version = "2.5.2"
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-ktx:$room_version"
implementation "androidx.room:room-paging:$room_version"
// Hilt
implementation 'com.google.dagger:hilt-android:2.44'
kapt 'androidx.hilt:hilt-compiler:1.0.0'
kapt 'com.google.dagger:hilt-android-compiler:2.44'
implementation "androidx.hilt:hilt-navigation-compose:1.0.0"
}

View File

@@ -9,7 +9,9 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.navigation.compose.rememberNavController
import com.uitron.demo.home.HomeScreen
import com.uitron.demo.theme.UiTronTheme
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

View File

@@ -1,14 +1,17 @@
package com.uitron.demo
import android.app.Application
import android.content.Context
import com.navi.uitron.UiTronSdkManager
import com.uitron.demo.dazzledesignsystem.db.SharedPreferences
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
UiTronSdkManager.init(UiTronDependencyProvider())
SharedPreferences.init(this)
instance = this
}

View File

@@ -0,0 +1,5 @@
package com.uitron.demo.dazzledesignsystem
import isNull
fun Any?.isNotNull(): Boolean = !this.isNull()

View File

@@ -0,0 +1,4 @@
package com.uitron.demo.dazzledesignsystem
const val DB_LAST_REFRESHED_TIMESTAMP = "widgetListLastRefreshedTimestamp"
const val DB_REFRESH_TIMEOUT = 3600000L // 1 hour

View File

@@ -0,0 +1,22 @@
package com.uitron.demo.dazzledesignsystem.db
import androidx.room.Database
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import com.uitron.demo.dazzledesignsystem.db.converters.ListConverter
import com.uitron.demo.dazzledesignsystem.db.converters.WidgetModelDefinitionConverter
import com.uitron.demo.dazzledesignsystem.db.dao.WidgetTemplatesDao
import com.uitron.demo.dazzledesignsystem.db.entity.WidgetTemplate
@Database(
entities = [WidgetTemplate::class],
version = 1,
exportSchema = false
)
@TypeConverters(
WidgetModelDefinitionConverter::class,
ListConverter::class
)
abstract class DazzleDatabase: RoomDatabase() {
abstract fun getWidgetTemplatesDao(): WidgetTemplatesDao
}

View File

@@ -0,0 +1,26 @@
package com.uitron.demo.dazzledesignsystem.db
import android.app.Application
import android.content.Context
import android.content.SharedPreferences
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
object SharedPreferences {
private const val PREFS_FILENAME = "dazzle.preferences"
private lateinit var sharedPreferences: SharedPreferences
fun init(application: Application) {
sharedPreferences = application.getSharedPreferences(PREFS_FILENAME, Context.MODE_PRIVATE)
}
suspend fun setLongValue(key: String, value: Long) = withContext(Dispatchers.IO) {
val editor = sharedPreferences.edit()
editor.putLong(key, value)
editor.apply()
}
suspend fun getLongValue(key: String, defValue: Long = -1L) = withContext(Dispatchers.IO) {
sharedPreferences.getLong(key, defValue)
}
}

View File

@@ -0,0 +1,40 @@
package com.uitron.demo.dazzledesignsystem.db.converters
import androidx.room.TypeConverter
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.uitron.demo.dazzledesignsystem.db.entity.TemplateVariable
class ListConverter {
@TypeConverter
fun fromStringList(value: List<String>): String {
return Gson().toJson(value)
}
@TypeConverter
fun toStringList(value: String): List<String> {
return try {
Gson().fromJson(value)
} catch (e: Exception) {
listOf()
}
}
@TypeConverter
fun fromVariablesList(value: List<TemplateVariable>): String {
return Gson().toJson(value)
}
@TypeConverter
fun toVariablesList(value: String): List<TemplateVariable> {
return try {
Gson().fromJson(value)
} catch (e: Exception) {
listOf()
}
}
private inline fun <reified T> Gson.fromJson(json: String): T =
fromJson(json, object : TypeToken<T>() {}.type)
}

View File

@@ -0,0 +1,18 @@
package com.uitron.demo.dazzledesignsystem.db.converters
import androidx.room.TypeConverter
import com.google.gson.Gson
import com.uitron.demo.dazzledesignsystem.db.entity.WidgetModelDefinition
class WidgetModelDefinitionConverter {
@TypeConverter
fun fromWidgetModelDefinition(widgetModelDefinition: WidgetModelDefinition): String {
return Gson().toJson(widgetModelDefinition)
}
@TypeConverter
fun toWidgetModelDefinition(widgetModelDefinition: String): WidgetModelDefinition {
return Gson().fromJson(widgetModelDefinition, WidgetModelDefinition::class.java)
}
}

View File

@@ -0,0 +1,22 @@
package com.uitron.demo.dazzledesignsystem.db.dao
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.uitron.demo.dazzledesignsystem.db.entity.WidgetTemplate
@Dao
interface WidgetTemplatesDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAll(widgetTemplates: List<WidgetTemplate>)
@Query("select * from widget_templates")
fun getWidgetTemplates(): List<WidgetTemplate>
@Query("select * from widget_templates where name like :searchQuery")
fun searchWidgetTemplates(searchQuery: String): List<WidgetTemplate>
@Query("delete from widget_templates")
suspend fun clearAllWidgetTemplates()
}

View File

@@ -0,0 +1,70 @@
package com.uitron.demo.dazzledesignsystem.db.entity
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import androidx.room.TypeConverters
import com.uitron.demo.dazzledesignsystem.db.converters.ListConverter
import com.uitron.demo.dazzledesignsystem.db.converters.WidgetModelDefinitionConverter
@Entity(tableName = "widget_templates")
data class WidgetTemplate(
@PrimaryKey(autoGenerate = false)
val id: String,
@ColumnInfo(name = "name")
val name: String? = null,
@ColumnInfo(name = "version")
val version: Int? = null,
@ColumnInfo(name = "type")
val type: String? = null,
@ColumnInfo(name = "minAppVersion")
val minAppVersion: String? = null,
@ColumnInfo(name = "maxAppVersion")
val maxAppVersion: String? = null,
@ColumnInfo(name = "cta")
val cta: String? = null,
@ColumnInfo(name = "imageUrl")
val imageUrl: String? = null,
@ColumnInfo(name = "osType")
val osType: String? = null,
@ColumnInfo(name = "tags") @TypeConverters(ListConverter::class)
val tags: List<String>? = null,
@ColumnInfo(name = "config") @TypeConverters(WidgetModelDefinitionConverter::class)
val config: WidgetModelDefinition? = null,
@ColumnInfo(name = "vars") @TypeConverters(ListConverter::class)
val vars: List<TemplateVariable>? = null,
@ColumnInfo(name = "parentTemplateName")
val parentTemplateName: String? = null,
@ColumnInfo(name = "parentTemplateVersion")
val parentTemplateVersion: Int? = null,
@ColumnInfo(name = "hash")
val hash: String? = null
)
data class TemplateVariable(
val name: String? = null,
val type: String? = null,
val description: String? = null
)
data class WidgetModelDefinition(
val widgetId: String? = null,
val widgetType: String? = null,
val widgetName: String? = null,
val widgetData: Any? = null,
val widgetStates: List<WidgetState>? = null,
val widgetOutput: List<WidgetOutput>? = null,
val widgetRenderActions: RenderActions? = null,
val widgetEventListeners: List<WidgetEvent>? = null
)
data class RenderActions(
val preRenderAction: Any? = null,
val postRenderAction: Any? = null
)
data class WidgetState(val stateId: String, val actionData: Any)
data class WidgetOutput(val fieldName: String, val layoutId: String)
data class WidgetEvent(val eventName: String, val stateId: String)

View File

@@ -0,0 +1,43 @@
package com.uitron.demo.dazzledesignsystem.di
import android.content.Context
import androidx.room.Room
import com.uitron.demo.dazzledesignsystem.db.DazzleDatabase
import com.uitron.demo.dazzledesignsystem.db.dao.WidgetTemplatesDao
import com.uitron.demo.dazzledesignsystem.network.retrofit.RetrofitProvider
import com.uitron.demo.dazzledesignsystem.network.retrofit.RetrofitService
import com.uitron.demo.dazzledesignsystem.repository.DazzleRepository
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
class DazzleModule {
@Provides
@Singleton
fun provideRetrofitService(): RetrofitService {
return RetrofitProvider.instance.service
}
@Singleton
@Provides
fun providesNaviPayAppDatabase(@ApplicationContext context: Context) =
Room.databaseBuilder(context, DazzleDatabase::class.java, "dazzle_database").build()
@Singleton
@Provides
fun providesWidgetTemplatesDao(dazzleDatabase: DazzleDatabase) =
dazzleDatabase.getWidgetTemplatesDao()
@Provides
@Singleton
fun provideRepository(retrofitService: RetrofitService, widgetTemplatesDao: WidgetTemplatesDao
): DazzleRepository {
return DazzleRepository(retrofitService, widgetTemplatesDao)
}
}

View File

@@ -0,0 +1,13 @@
package com.uitron.demo.dazzledesignsystem.models
import com.uitron.demo.dazzledesignsystem.db.entity.WidgetTemplate
data class BottomSheetData(
val bottomSheetType: BottomSheetType? = null,
val widgetTemplate: WidgetTemplate? = null
)
enum class BottomSheetType {
FilterBottomSheet,
ShareWidgetConfigBottomSheet
}

View File

@@ -0,0 +1,10 @@
package com.uitron.demo.dazzledesignsystem.models
import com.uitron.demo.dazzledesignsystem.db.entity.WidgetTemplate
sealed class WidgetTemplateState {
object Loading : WidgetTemplateState()
data class Error(val message: String? = null) : WidgetTemplateState()
data class Success(val data: List<WidgetTemplate>) : WidgetTemplateState()
object Nothing : WidgetTemplateState()
}

View File

@@ -0,0 +1,6 @@
package com.uitron.demo.dazzledesignsystem.models.network
data class NetworkResult<T> (
var data: T? = null,
var errorMessage: String? = null
)

View File

@@ -0,0 +1,13 @@
package com.uitron.demo.dazzledesignsystem.models.network
import com.uitron.demo.dazzledesignsystem.db.entity.WidgetTemplate
data class WidgetTemplateResponse(
val content: List<WidgetTemplate>? = null,
val size: Int? = null,
val number: Int? = null,
val first: Boolean? = null,
val last: Boolean? = null,
val numberOfElements: Int? = null,
val empty: Boolean? = null
)

View File

@@ -0,0 +1,19 @@
package com.uitron.demo.dazzledesignsystem.network
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
class HttpClient {
val httpClientBuilder: OkHttpClient.Builder
get() {
val okHttpClientBuilder = OkHttpClient.Builder()
with(okHttpClientBuilder) {
addInterceptor(loggingInterceptor())
}
return okHttpClientBuilder
}
private fun loggingInterceptor() =
HttpLoggingInterceptor().apply { setLevel(HttpLoggingInterceptor.Level.BODY) }
}

View File

@@ -0,0 +1,28 @@
package com.uitron.demo.dazzledesignsystem.network.retrofit
import com.uitron.demo.dazzledesignsystem.network.HttpClient
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
class RetrofitProvider private constructor() {
val service: RetrofitService = defaultRetrofitClient.create(RetrofitService::class.java)
private object Holder {
val INSTANCE = RetrofitProvider()
}
companion object {
val instance: RetrofitProvider by lazy { Holder.INSTANCE }
val defaultRetrofitClient: Retrofit = createApRetrofitClient(
HttpClient().httpClientBuilder
)
}
}
fun createApRetrofitClient(okttpClientBuilder: OkHttpClient.Builder): Retrofit {
val baseUrl = "https://qa-dazzle.np.navi-sa.in/"
return Retrofit.Builder().baseUrl(baseUrl).addConverterFactory(GsonConverterFactory.create())
.client(okttpClientBuilder.build()).build()
}

View File

@@ -0,0 +1,21 @@
package com.uitron.demo.dazzledesignsystem.network.retrofit
import com.uitron.demo.dazzledesignsystem.models.network.WidgetTemplateResponse
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.Query
interface RetrofitService {
@GET("/dazzle/v1/widget/templates")
suspend fun fetchWidgetTemplates(
@Query("pageNumber") pageNumber: Int,
@Query("pageSize") pageSize: Int
): Response<WidgetTemplateResponse>
@POST("https://jsonblob.com/api/jsonBlob")
suspend fun postConfigAndGetJsonBlobUrl(
@Body body: Any
): Response<Any>
}

View File

@@ -0,0 +1,70 @@
package com.uitron.demo.dazzledesignsystem.repository
import com.uitron.demo.dazzledesignsystem.DB_LAST_REFRESHED_TIMESTAMP
import com.uitron.demo.dazzledesignsystem.DB_REFRESH_TIMEOUT
import com.uitron.demo.dazzledesignsystem.db.SharedPreferences
import com.uitron.demo.dazzledesignsystem.db.dao.WidgetTemplatesDao
import com.uitron.demo.dazzledesignsystem.db.entity.WidgetTemplate
import com.uitron.demo.dazzledesignsystem.models.network.NetworkResult
import com.uitron.demo.dazzledesignsystem.network.retrofit.RetrofitService
import orFalse
import retrofit2.Response
import javax.inject.Inject
class DazzleRepository @Inject constructor(
private val retrofitService: RetrofitService, private val widgetTemplatesDao: WidgetTemplatesDao
) {
suspend fun fetchWidgetTemplates(): NetworkResult<List<WidgetTemplate>> {
val lastRefreshTimeStamp = SharedPreferences.getLongValue(
key = DB_LAST_REFRESHED_TIMESTAMP, defValue = -1L
)
return if (System.currentTimeMillis() - lastRefreshTimeStamp < DB_REFRESH_TIMEOUT) {
NetworkResult(
data = widgetTemplatesDao.getWidgetTemplates(), errorMessage = null
)
} else fetchWidgetTemplatesFromNetwork()
}
suspend fun searchWidgetTemplates(searchQuery: String): NetworkResult<List<WidgetTemplate>> {
return NetworkResult(
data = widgetTemplatesDao.searchWidgetTemplates("%$searchQuery%"), errorMessage = null
)
}
private suspend fun fetchWidgetTemplatesFromNetwork(): NetworkResult<List<WidgetTemplate>> {
return try {
widgetTemplatesDao.clearAllWidgetTemplates()
val response = retrofitService.fetchWidgetTemplates(
pageNumber = 0, pageSize = 10000
)
val result = response.body()?.content?.filter {
it.tags?.isNotEmpty().orFalse() && it.type.equals("WIDGET")
}
SharedPreferences.setLongValue(
key = DB_LAST_REFRESHED_TIMESTAMP, value = System.currentTimeMillis()
)
widgetTemplatesDao.insertAll(result ?: listOf())
NetworkResult(data = result, errorMessage = null)
} catch (e: Exception) {
NetworkResult(
data = null, errorMessage = e.message
)
}
}
suspend fun postConfigAndGetJsonBlobUrl(
body: Any
): Response<Any> {
return try {
retrofitService.postConfigAndGetJsonBlobUrl(
body = body
)
} catch (e: Exception) {
e.printStackTrace()
return Response.error(500, null)
}
}
}

View File

@@ -0,0 +1,315 @@
package com.uitron.demo.dazzledesignsystem.ui
import android.content.Context
import android.content.Intent
import android.widget.Toast
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Checkbox
import androidx.compose.material.Divider
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
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.RectangleShape
import androidx.compose.ui.platform.ClipboardManager
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.Dimension
import androidx.core.content.ContextCompat.startActivity
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.demo.uitron.theme.Black191919
import com.demo.uitron.theme.Black1C1C1C
import com.demo.uitron.theme.Blue0276FE
import com.demo.uitron.theme.Gray969696
import com.demo.uitron.theme.GrayE8E8E8
import com.demo.uitron.theme.Purple1F002A
import com.demo.uitron.theme.Purple3C0050
import com.demo.uitron.theme.WhiteFCFCFD
import com.google.gson.Gson
import com.uitron.demo.R
import com.uitron.demo.dazzledesignsystem.db.entity.WidgetTemplate
import com.uitron.demo.dazzledesignsystem.models.BottomSheetData
import com.uitron.demo.dazzledesignsystem.models.BottomSheetType
import com.uitron.demo.dazzledesignsystem.viewmodel.DazzleViewModel
import com.uitron.demo.theme.fontFamily
@Composable
fun BottomSheetContent(
bottomSheetData: BottomSheetData?,
viewModel: DazzleViewModel,
context: Context,
clipboardManager: ClipboardManager
) {
when (bottomSheetData?.bottomSheetType) {
BottomSheetType.FilterBottomSheet -> {
FilterBottomSheet(viewModel = viewModel)
}
BottomSheetType.ShareWidgetConfigBottomSheet -> {
ShareWidgetConfigBottomSheet(
viewModel = viewModel,
widgetTemplate = bottomSheetData.widgetTemplate,
context = context,
clipboardManager = clipboardManager
)
}
else -> Spacer(modifier = Modifier.height(1.dp))
}
}
@Composable
fun FilterBottomSheet(viewModel: DazzleViewModel) {
val selectedFilterType by viewModel.selectedFilterType.collectAsStateWithLifecycle()
Column {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, end = 16.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(text = stringResource(id = R.string.filter_by), modifier = Modifier.padding(top = 16.dp))
Image(
painter = painterResource(id = R.drawable.ic_close),
contentDescription = null,
modifier = Modifier
.padding(top = 12.dp)
.clickable {
viewModel.hideBottomSheet()
}
)
}
Divider(modifier = Modifier.padding(top = 14.dp))
ConstraintLayout {
val (column1, divider, column2) = createRefs()
Column(modifier = Modifier
.constrainAs(column1) {
top.linkTo(parent.top)
start.linkTo(parent.start)
end.linkTo(divider.start)
}
.padding(top = 16.dp)
.verticalScroll(state = rememberScrollState(), enabled = true)) {
for (i in viewModel.getListOfFilterTypes()) {
TabItemView(modifier = Modifier.padding(bottom = 28.dp),
text = i,
isSelected = selectedFilterType == i,
onClick = { viewModel.updateFilterType(i) })
}
}
Divider(modifier = Modifier
.constrainAs(divider) {
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
start.linkTo(column1.end)
end.linkTo(column2.start)
height = Dimension.fillToConstraints
}
.padding(start = 24.dp, end = 18.dp)
.width(1.dp), color = GrayE8E8E8)
Column(modifier = Modifier
.constrainAs(column2) {
top.linkTo(parent.top)
start.linkTo(divider.end)
end.linkTo(parent.end)
}
.padding(top = 10.dp)
.verticalScroll(state = rememberScrollState(), enabled = true)) {
FiltersList(selectedFilterType = selectedFilterType, viewModel = viewModel)
}
}
Divider(modifier = Modifier.background(Gray969696))
Row(modifier = Modifier.fillMaxWidth()) {
Button(
onClick = { viewModel.removeAllFilters() },
shape = RectangleShape,
colors = ButtonDefaults.buttonColors(
backgroundColor = Color.White
),
elevation = ButtonDefaults.elevation(
defaultElevation = 0.dp
),
contentPadding = PaddingValues(
top = 12.dp,
bottom = 12.dp,
),
modifier = Modifier
.weight(1f)
.height(48.dp)
) {
Text(
text = stringResource(id = R.string.clear_all), style = TextStyle(
fontSize = 14.sp,
fontFamily = fontFamily,
fontWeight = FontWeight(600),
color = Purple3C0050,
textAlign = TextAlign.Center,
)
)
}
Button(
onClick = { viewModel.applySearchOrFilter()
viewModel.hideBottomSheet() },
shape = RectangleShape,
colors = ButtonDefaults.buttonColors(
backgroundColor = Purple1F002A
),
elevation = ButtonDefaults.elevation(
defaultElevation = 0.dp
),
contentPadding = PaddingValues(
top = 12.dp,
bottom = 12.dp,
),
modifier = Modifier
.weight(1f)
.height(48.dp)
) {
Text(
text = stringResource(id = R.string.apply_filters), style = TextStyle(
fontSize = 14.sp,
fontFamily = fontFamily,
fontWeight = FontWeight(600),
color = WhiteFCFCFD,
textAlign = TextAlign.Center,
)
)
}
}
}
}
@Composable
fun ShareWidgetConfigBottomSheet(
viewModel: DazzleViewModel,
widgetTemplate: WidgetTemplate? = null,
context: Context,
clipboardManager: ClipboardManager
) {
LaunchedEffect(Unit) {
viewModel.jsonBlobUrl.collect {
it?.let {
val sendIntent: Intent = Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, it)
type = "text/plain"
}
val shareIntent = Intent.createChooser(sendIntent, null)
startActivity(context, shareIntent, null)
viewModel.hideBottomSheet()
}
}
}
Column(modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 32.dp)) {
Row(modifier = Modifier
.padding(bottom = 20.dp)
.clickable {
Toast
.makeText(context, "Widget Template Copied!", Toast.LENGTH_SHORT)
.show()
val text = Gson().toJson(widgetTemplate, WidgetTemplate::class.java)
clipboardManager.setText(AnnotatedString(text))
viewModel.hideBottomSheet()
}) {
Image(
painter = painterResource(id = R.drawable.ic_copy_24x24), contentDescription = null
)
Text(text = stringResource(id = R.string.copy_widget_config), modifier = Modifier.padding(start = 14.dp))
}
Divider()
Row(modifier = Modifier
.padding(top = 20.dp)
.clickable {
widgetTemplate?.let {
viewModel.postConfigAndGetJsonBlobUrl(widgetTemplate)
}
}) {
Image(painter = painterResource(id = R.drawable.ic_share), contentDescription = null)
Text(text = stringResource(id = R.string.share_widget_config), modifier = Modifier.padding(start = 14.dp))
}
}
}
@Composable
fun TabItemView(
modifier: Modifier = Modifier, text: String, isSelected: Boolean, onClick: () -> Unit
) {
Box(
modifier = modifier
.wrapContentSize()
.clickable { onClick() },
contentAlignment = Alignment.CenterStart
) {
Text(
text = text, style = TextStyle(
fontSize = 14.sp,
fontFamily = fontFamily,
fontWeight = FontWeight(400),
color = Black1C1C1C
), modifier = Modifier.padding(12.dp)
)
if (isSelected) {
Box(
modifier = Modifier
.background(color = Blue0276FE)
.width(3.dp)
.height(40.dp)
)
}
}
}
@Composable
fun FiltersList(selectedFilterType: String, viewModel: DazzleViewModel) {
val filterList = viewModel.getListOfFiltersBasedOnFilterType(selectedFilterType)
val selectedFilters by viewModel.selectedFilters.collectAsStateWithLifecycle()
for (i in filterList) {
Row(
verticalAlignment = Alignment.CenterVertically
) {
Checkbox(checked = selectedFilters.contains(i), onCheckedChange = { checked ->
if (checked) {
viewModel.addFilter(i)
} else {
viewModel.removeFilter(i)
}
})
Text(
text = i, modifier = Modifier.padding(start = 8.dp), style = TextStyle(
fontSize = 16.sp,
fontFamily = fontFamily,
fontWeight = FontWeight(400),
color = Black191919,
)
)
}
}
}

View File

@@ -0,0 +1,140 @@
package com.uitron.demo.dazzledesignsystem.ui
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.ModalBottomSheetLayout
import androidx.compose.material.ModalBottomSheetValue
import androidx.compose.material.Text
import androidx.compose.material.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.ClipboardManager
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.demo.uitron.theme.WhiteF7F7F7
import com.uitron.demo.dazzledesignsystem.models.WidgetTemplateState
import com.uitron.demo.dazzledesignsystem.viewmodel.DazzleViewModel
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun DazzleDesignSystemScreen(
viewModel: DazzleViewModel = hiltViewModel()
) {
val widgetTemplates = viewModel.widgetTemplates.collectAsStateWithLifecycle()
val searchAndFilterResults = viewModel.searchAndFilterResults.collectAsStateWithLifecycle()
val applySearchOrFilter by viewModel.applySearchOrFilter.collectAsStateWithLifecycle()
val bottomSheetState = rememberModalBottomSheetState(
initialValue = ModalBottomSheetValue.Hidden, skipHalfExpanded = true
)
val bottomSheetData by viewModel.bottomSheetData.collectAsStateWithLifecycle()
val scope = rememberCoroutineScope()
val context = LocalContext.current
val clipboardManager: ClipboardManager = LocalClipboardManager.current
LaunchedEffect(Unit) {
viewModel.fetchWidgetTemplates()
}
LaunchedEffect(Unit) {
viewModel.bottomSheetVisibilityState.collect { isVisible ->
if (isVisible) {
scope.launch {
bottomSheetState.show()
}
} else {
scope.launch {
bottomSheetState.hide()
}
}
}
}
ModalBottomSheetLayout(sheetState = bottomSheetState, sheetContent = {
BottomSheetContent(
bottomSheetData = bottomSheetData,
viewModel = viewModel,
context = context,
clipboardManager = clipboardManager
)
}, sheetShape = RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp)) {
Column(
modifier = Modifier
.fillMaxSize()
.background(color = WhiteF7F7F7)
) {
Toolbar(viewModel = viewModel)
if (applySearchOrFilter) {
WidgetTemplatesList(widgetTemplates = searchAndFilterResults, viewModel = viewModel)
} else {
WidgetTemplatesList(widgetTemplates = widgetTemplates, viewModel = viewModel)
}
}
}
}
@Composable
fun WidgetTemplatesList(widgetTemplates: State<WidgetTemplateState>, viewModel: DazzleViewModel) {
LazyColumn(
modifier = Modifier.fillMaxWidth()
) {
val widgetTemplatesList = (widgetTemplates.value as? WidgetTemplateState.Success)?.data
widgetTemplatesList?.let {
items(widgetTemplatesList) { widgetTemplate ->
WidgetItemView(
widgetTemplate = widgetTemplate, viewModel = viewModel
)
}
}
when (widgetTemplates.value) {
is WidgetTemplateState.Loading -> {
item { LoadingView(modifier = Modifier.fillParentMaxSize()) }
}
is WidgetTemplateState.Error -> {
item { ErrorView(errorMessage = (widgetTemplates.value as WidgetTemplateState.Error).message) }
}
else -> Unit
}
}
}
@Composable
fun LoadingView(modifier: Modifier = Modifier) {
Column(
modifier = modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
CircularProgressIndicator()
}
}
@Composable
fun ErrorView(errorMessage: String?) {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
Text(text = errorMessage ?: "Something went wrong!")
}
}

View File

@@ -0,0 +1,118 @@
package com.uitron.demo.dazzledesignsystem.ui
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Text
import androidx.compose.material.TextField
import androidx.compose.material.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.demo.uitron.theme.GrayA8A8A8
import com.demo.uitron.theme.GrayE3E5E5
import com.demo.uitron.theme.WhiteF5F5F5
import com.uitron.demo.R
import com.uitron.demo.dazzledesignsystem.models.BottomSheetType
import com.uitron.demo.dazzledesignsystem.viewmodel.DazzleViewModel
import com.uitron.demo.theme.fontFamily
@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun Toolbar(viewModel: DazzleViewModel) {
val keyboardController = LocalSoftwareKeyboardController.current
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.height(76.dp)
.background(color = Color.White)
.padding(start = 8.dp, end = 8.dp, top = 14.dp, bottom = 14.dp)
) {
val searchQuery by viewModel.searchQuery.collectAsStateWithLifecycle()
TextField(value = searchQuery,
onValueChange = {
viewModel.updateSearchQuery(searchQuery = it)
},
maxLines = 1,
colors = TextFieldDefaults.textFieldColors(
textColor = Color.DarkGray,
disabledTextColor = Color.Transparent,
backgroundColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent
),
textStyle = TextStyle.Default.copy(
fontSize = 14.sp, fontFamily = fontFamily, fontWeight = FontWeight(400)
),
keyboardOptions = KeyboardOptions(
autoCorrect = false, imeAction = ImeAction.Search
),
keyboardActions = KeyboardActions(onSearch = {
viewModel.applySearchOrFilter()
keyboardController?.hide()
}),
modifier = Modifier
.height(48.dp)
.weight(2f)
.border(width = 1.dp, shape = RoundedCornerShape(8.dp), color = GrayE3E5E5),
placeholder = {
Text(
modifier = Modifier.align(Alignment.CenterVertically),
text = stringResource(id = R.string.search),
fontSize = 14.sp,
fontFamily = fontFamily,
fontWeight = FontWeight(400),
color = GrayA8A8A8
)
},
leadingIcon = {
Image(
painter = painterResource(id = R.drawable.ic_search), contentDescription = null
)
})
Button(
onClick = {
keyboardController?.hide()
viewModel.showBottomSheet(BottomSheetType.FilterBottomSheet) },
shape = RoundedCornerShape(4.dp),
colors = ButtonDefaults.buttonColors(
backgroundColor = WhiteF5F5F5
),
elevation = ButtonDefaults.elevation(
defaultElevation = 0.dp
),
modifier = Modifier
.padding(start = 8.dp)
.fillMaxHeight()
) {
Image(
painter = painterResource(id = R.drawable.ic_filter), contentDescription = null
)
}
}
}

View File

@@ -0,0 +1,131 @@
package com.uitron.demo.dazzledesignsystem.ui
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
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.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Card
import androidx.compose.material.Divider
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.demo.uitron.theme.Black1C1C1C
import com.demo.uitron.theme.BlueE4EEFF
import com.demo.uitron.theme.GrayE8E8E8
import com.demo.uitron.theme.Purple1F002A
import com.google.gson.Gson
import com.navi.uitron.render.UiTronRenderer
import com.uitron.demo.R
import com.uitron.demo.dazzledesignsystem.db.entity.WidgetTemplate
import com.uitron.demo.dazzledesignsystem.models.BottomSheetType
import com.uitron.demo.dazzledesignsystem.viewmodel.DazzleViewModel
import com.uitron.demo.stringToUiTronResponse
import com.uitron.demo.theme.fontFamily
@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun WidgetItemView(widgetTemplate: WidgetTemplate?, viewModel: DazzleViewModel) {
val keyboardController = LocalSoftwareKeyboardController.current
val widgetData = widgetTemplate?.config?.widgetData?.let {
try {
stringToUiTronResponse(Gson().toJson(it))
} catch (e: Exception) {
null
}
}
widgetData?.let { uiTronResponse ->
Card(modifier = Modifier.padding(start = 8.dp, end = 8.dp, top = 16.dp)) {
Column(
modifier = Modifier
.fillMaxWidth()
.background(color = Color.White)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.fillMaxWidth()
) {
Text(
text = widgetTemplate.name.orEmpty(),
modifier = Modifier
.padding(
start = 16.dp, end = 16.dp, top = 13.dp
)
.weight(1f),
fontSize = 16.sp,
fontFamily = fontFamily,
fontWeight = FontWeight.Medium,
color = Black1C1C1C
)
Image(
painter = painterResource(id = R.drawable.ic_vertical_dots),
contentDescription = null,
modifier = Modifier
.padding(top = 10.dp, end = 12.dp)
.clickable {
keyboardController?.hide()
viewModel.showBottomSheet(
BottomSheetType.ShareWidgetConfigBottomSheet, widgetTemplate
)
},
)
}
Row(
modifier = Modifier
.fillMaxWidth()
.padding(start = 8.dp, end = 8.dp, top = 18.dp)
) {
widgetTemplate.tags?.forEach { tag ->
Card(
shape = RoundedCornerShape(100.dp),
backgroundColor = BlueE4EEFF,
modifier = Modifier.padding(end = 8.dp),
elevation = 0.dp
) {
Text(
text = tag,
fontSize = 12.sp,
fontFamily = fontFamily,
fontWeight = FontWeight.Medium,
color = Purple1F002A,
modifier = Modifier.padding(
horizontal = 12.dp, vertical = 4.dp
)
)
}
}
}
Divider(
modifier = Modifier.padding(8.dp), color = GrayE8E8E8
)
Box(
modifier = Modifier.padding(
start = 8.dp, end = 8.dp, bottom = 8.dp
)
) {
UiTronRenderer(
uiTronResponse.data, viewModel
).Render(
composeViews = uiTronResponse.parentComposeView.orEmpty()
)
}
}
}
}
}

View File

@@ -0,0 +1,144 @@
package com.uitron.demo.dazzledesignsystem.viewmodel
import androidx.lifecycle.viewModelScope
import com.navi.uitron.viewmodel.UiTronViewModel
import com.uitron.demo.dazzledesignsystem.db.entity.WidgetTemplate
import com.uitron.demo.dazzledesignsystem.isNotNull
import com.uitron.demo.dazzledesignsystem.models.BottomSheetData
import com.uitron.demo.dazzledesignsystem.models.BottomSheetType
import com.uitron.demo.dazzledesignsystem.models.WidgetTemplateState
import com.uitron.demo.dazzledesignsystem.repository.DazzleRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class DazzleViewModel @Inject constructor(private val repository: DazzleRepository) : UiTronViewModel() {
private val _searchQuery = MutableStateFlow("")
val searchQuery = _searchQuery.asStateFlow()
private val _widgetTemplates = MutableStateFlow<WidgetTemplateState>(WidgetTemplateState.Nothing)
val widgetTemplates = _widgetTemplates.asStateFlow()
private val _searchAndFilterResults = MutableStateFlow<WidgetTemplateState>(WidgetTemplateState.Nothing)
val searchAndFilterResults = _searchAndFilterResults.asStateFlow()
private val _selectedFilterType = MutableStateFlow("Widget\nType")
val selectedFilterType = _selectedFilterType.asStateFlow()
private val _selectedFilters = MutableStateFlow(listOf<String>())
val selectedFilters = _selectedFilters.asStateFlow()
private val _applySearchOrFilter = MutableStateFlow(false)
val applySearchOrFilter = _applySearchOrFilter.asStateFlow()
private val _bottomSheetVisibilityState = MutableSharedFlow<Boolean>()
val bottomSheetVisibilityState = _bottomSheetVisibilityState.asSharedFlow()
private val _bottomSheetData = MutableStateFlow<BottomSheetData?>(null)
val bottomSheetData = _bottomSheetData.asStateFlow()
private val _jsonBlobUrl = MutableStateFlow<String?>(null)
val jsonBlobUrl = _jsonBlobUrl.asStateFlow()
fun fetchWidgetTemplates() {
viewModelScope.launch(Dispatchers.IO) {
_widgetTemplates.value = WidgetTemplateState.Loading
val response = repository.fetchWidgetTemplates()
_widgetTemplates.value = when {
response.data.isNotNull() -> WidgetTemplateState.Success(response.data!!)
else -> WidgetTemplateState.Error(response.errorMessage)
}
}
}
private fun searchAndFilterWidgetTemplates() {
viewModelScope.launch(Dispatchers.IO) {
_searchAndFilterResults.value = WidgetTemplateState.Loading
when (_widgetTemplates.value) {
is WidgetTemplateState.Success -> {
val widgetTemplates = repository.searchWidgetTemplates(_searchQuery.value)
val results = widgetTemplates.data?.filter { it.tags?.containsAll(_selectedFilters.value) == true }
_searchAndFilterResults.value = WidgetTemplateState.Success(results.orEmpty())
}
else -> {
_searchAndFilterResults.value = WidgetTemplateState.Error("No data found")
}
}
}
}
fun postConfigAndGetJsonBlobUrl(body: Any) {
viewModelScope.launch(Dispatchers.IO){
val response = repository.postConfigAndGetJsonBlobUrl(body = body)
if (response.isSuccessful) {
val splitUrl = response.headers()["location"]?.split("/")
val resultUrl = "https://jsonblob.com/${splitUrl?.get(splitUrl.size - 1)}"
_jsonBlobUrl.update { resultUrl }
}
}
}
fun updateSearchQuery(searchQuery: String = "") {
_searchQuery.update { searchQuery }
}
fun applySearchOrFilter() {
_applySearchOrFilter.update { true }
searchAndFilterWidgetTemplates()
}
fun updateFilterType(filterType: String) {
_selectedFilterType.update { filterType }
}
fun addFilter(filter: String) {
_selectedFilters.update { selectedFilters.value.plus(filter) }
}
fun removeFilter(filter: String) {
_selectedFilters.update { selectedFilters.value.minus(filter) }
}
fun removeAllFilters() {
_selectedFilters.update { listOf() }
}
fun showBottomSheet(bottomSheetType: BottomSheetType?, widgetTemplate: WidgetTemplate? = null) {
viewModelScope.launch {
_bottomSheetData.emit(BottomSheetData(bottomSheetType = bottomSheetType, widgetTemplate = widgetTemplate))
_bottomSheetVisibilityState.emit(true)
}
}
fun hideBottomSheet() {
viewModelScope.launch {
_bottomSheetVisibilityState.emit(false)
}
}
fun getListOfFilterTypes(): List<String> {
val list = mutableListOf<String>()
list.add("Widget\nType")
list.add("Widget\nSubtype")
list.add("Team Name")
return list
}
fun getListOfFiltersBasedOnFilterType(filterType: String): List<String> {
return when (filterType) {
"Widget\nType" -> mutableListOf("Header", "Footer", "Content", "Bottom sheet", "Toast")
"Widget\nSubtype" -> mutableListOf("Title", "Icon", "Radio pill", "Button", "Input")
"Team Name" -> mutableListOf("PL", "HL_PP_TR", "KYC", "AMC", "Navi Pay", "HI", "GI", "Digital Gold", "Rewards", "Referrals")
else -> mutableListOf()
}
}
}

View File

@@ -5,6 +5,7 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.CutCornerShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
@@ -23,6 +24,7 @@ import androidx.constraintlayout.compose.Dimension
import androidx.navigation.NavHostController
import com.demo.uitron.theme.PurpleDCD1F4
import com.demo.uitron.theme.RedEF0000
import com.uitron.demo.navigation.DAZZLE_DESIGN_SYSTEM
import com.uitron.demo.navigation.DESIGN_SYSTEM
import com.uitron.demo.navigation.MOCK
import com.uitron.demo.navigation.PLAYGROUND
@@ -65,7 +67,7 @@ fun HomeScreen(navHostController: NavHostController) {
ConstraintLayout(modifier = Modifier.fillMaxSize()) {
val startGuideline = createGuidelineFromAbsoluteLeft(.5f)
val (playgroundRef, mockRef, designSystemRef) = createRefs()
val (playgroundRef, mockRef, designSystemRef, dazzleDesignSystemRef) = createRefs()
PlaygroundBubble(
navHostController = navHostController,
modifier = Modifier.constrainAs(playgroundRef) {
@@ -91,6 +93,15 @@ fun HomeScreen(navHostController: NavHostController) {
width = Dimension.fillToConstraints
}
)
DazzleDesignSystemBubble(
navHostController = navHostController,
modifier = Modifier.constrainAs(dazzleDesignSystemRef) {
start.linkTo(startGuideline, 16.dp)
end.linkTo(parent.end, 16.dp)
top.linkTo(designSystemRef.bottom, 16.dp)
width = Dimension.fillToConstraints
}
)
}
}
}
@@ -151,4 +162,23 @@ fun MockBubble(navHostController: NavHostController, modifier: Modifier) {
fontFamily = fontFamily
)
}
}
@Composable
fun DazzleDesignSystemBubble(navHostController: NavHostController, modifier: Modifier) {
Box(
modifier = modifier
.height(150.dp)
.background(color = Color.Cyan, shape = RoundedCornerShape(40.dp))
.clickable {
navHostController.navigate(DAZZLE_DESIGN_SYSTEM)
}, contentAlignment = Alignment.Center
) {
Text(
text = "Dazzle\nDesign\nSystem",
fontSize = 22.sp,
fontWeight = FontWeight(800),
fontFamily = fontFamily
)
}
}

View File

@@ -5,4 +5,5 @@ const val PLAYGROUND = "playground"
const val MOCK = "mock"
const val DESIGN_SYSTEM = "designSystem"
const val DESIGN_REPO_WITH_NAV = "designRepo/{id}"
const val DESIGN_REPO = "designRepo/"
const val DESIGN_REPO = "designRepo/"
const val DAZZLE_DESIGN_SYSTEM = "dazzle/"

View File

@@ -4,6 +4,7 @@ import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.navArgument
import com.uitron.demo.dazzledesignsystem.ui.DazzleDesignSystemScreen
import com.uitron.demo.home.HomeScreen
import com.uitron.demo.navigation.*
import com.uitron.demo.playground.PlayGroundScreen
@@ -34,6 +35,9 @@ fun UiTronDemoNavGraph(navController: NavHostController) {
) {
DesignSystemRepoScreen(designComponentId = it.arguments?.get("id").toString())
}
composable(route = DAZZLE_DESIGN_SYSTEM) {
DazzleDesignSystemScreen()
}
}
}

View File

@@ -19,4 +19,15 @@ val RedEF0000 = Color(0xFFEF0000)
val WhiteF8F8F8 = Color(0xFFF8F8F8)
val Black444444 = Color(0xFF444444)
val PurpleDCD1F4 = Color(0xFFDCD1F4)
val WhiteF7F7F7 = Color(0xFFF7F7F7)
val Black1C1C1C = Color(0xFF1C1C1C)
val Blue0276FE = Color(0xFF0276FE)
val WhiteFCFCFD = Color(0xFFFCFCFD)
val Purple3C0050 = Color(0xFF3C0050)
val Gray969696 = Color(0xFF969696)
val GrayE8E8E8 = Color(0xFFE8E8E8)
val Purple1F002A = Color(0xFF1F002A)
val BlueE4EEFF = Color(0xFFE4EEFF)
val GrayA8A8A8 = Color(0xFFA8A8A8)
val WhiteF5F5F5 = Color(0xFFF5F5F5)

View File

@@ -0,0 +1,14 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="25dp"
android:viewportWidth="24"
android:viewportHeight="25">
<path
android:pathData="M18.707,5.323C19.098,5.716 19.098,6.353 18.707,6.746L6.707,18.815C6.317,19.208 5.683,19.208 5.293,18.815C4.902,18.422 4.902,17.785 5.293,17.393L17.293,5.323C17.683,4.931 18.317,4.931 18.707,5.323Z"
android:fillColor="#1F002A"
android:fillType="evenOdd"/>
<path
android:pathData="M5.293,5.323C5.683,4.931 6.317,4.931 6.707,5.323L18.707,17.393C19.098,17.785 19.098,18.422 18.707,18.815C18.317,19.208 17.683,19.208 17.293,18.815L5.293,6.746C4.902,6.353 4.902,5.716 5.293,5.323Z"
android:fillColor="#1F002A"
android:fillType="evenOdd"/>
</vector>

View File

@@ -0,0 +1,14 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M11,10C10.448,10 10,10.448 10,11V20C10,20.552 10.448,21 11,21H20C20.552,21 21,20.552 21,20V11C21,10.448 20.552,10 20,10H11ZM8,11C8,9.343 9.343,8 11,8H20C21.657,8 23,9.343 23,11V20C23,21.657 21.657,23 20,23H11C9.343,23 8,21.657 8,20V11Z"
android:fillColor="#1F002A"
android:fillType="evenOdd"/>
<path
android:pathData="M4,3C3.735,3 3.48,3.105 3.293,3.293C3.105,3.48 3,3.735 3,4V13C3,13.265 3.105,13.52 3.293,13.707C3.48,13.895 3.735,14 4,14H5C5.552,14 6,14.448 6,15C6,15.552 5.552,16 5,16H4C3.204,16 2.441,15.684 1.879,15.121C1.316,14.559 1,13.796 1,13V4C1,3.204 1.316,2.441 1.879,1.879C2.441,1.316 3.204,1 4,1H13C13.796,1 14.559,1.316 15.121,1.879C15.684,2.441 16,3.204 16,4V5C16,5.552 15.552,6 15,6C14.448,6 14,5.552 14,5V4C14,3.735 13.895,3.48 13.707,3.293C13.52,3.105 13.265,3 13,3H4Z"
android:fillColor="#1F002A"
android:fillType="evenOdd"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="25dp"
android:height="24dp"
android:viewportWidth="25"
android:viewportHeight="24">
<path
android:pathData="M1.593,2.579C1.757,2.226 2.111,2 2.5,2H22.5C22.889,2 23.243,2.226 23.407,2.579C23.571,2.932 23.515,3.348 23.264,3.646L15.5,12.826V21C15.5,21.347 15.321,21.668 15.026,21.851C14.731,22.033 14.363,22.049 14.053,21.894L10.053,19.894C9.714,19.725 9.5,19.379 9.5,19V12.826L1.736,3.646C1.485,3.348 1.429,2.932 1.593,2.579ZM4.655,4L11.264,11.814C11.416,11.995 11.5,12.224 11.5,12.46V18.382L13.5,19.382V12.46C13.5,12.224 13.584,11.995 13.736,11.814L20.345,4H4.655Z"
android:fillColor="#1F002A"
android:fillType="evenOdd"/>
</vector>

View File

@@ -0,0 +1,14 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M11,4C7.134,4 4,7.134 4,11C4,14.866 7.134,18 11,18C14.866,18 18,14.866 18,11C18,7.134 14.866,4 11,4ZM2,11C2,6.029 6.029,2 11,2C15.971,2 20,6.029 20,11C20,15.971 15.971,20 11,20C6.029,20 2,15.971 2,11Z"
android:fillColor="#1F002A"
android:fillType="evenOdd"/>
<path
android:pathData="M15.943,15.943C16.333,15.552 16.966,15.552 17.357,15.943L21.707,20.293C22.097,20.683 22.097,21.316 21.707,21.707C21.316,22.097 20.683,22.097 20.293,21.707L15.943,17.357C15.552,16.966 15.552,16.333 15.943,15.943Z"
android:fillColor="#1F002A"
android:fillType="evenOdd"/>
</vector>

View File

@@ -0,0 +1,26 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M18,3C16.895,3 16,3.895 16,5C16,6.105 16.895,7 18,7C19.105,7 20,6.105 20,5C20,3.895 19.105,3 18,3ZM14,5C14,2.791 15.791,1 18,1C20.209,1 22,2.791 22,5C22,7.209 20.209,9 18,9C15.791,9 14,7.209 14,5Z"
android:fillColor="#1F002A"
android:fillType="evenOdd"/>
<path
android:pathData="M6,10C4.895,10 4,10.895 4,12C4,13.105 4.895,14 6,14C7.105,14 8,13.105 8,12C8,10.895 7.105,10 6,10ZM2,12C2,9.791 3.791,8 6,8C8.209,8 10,9.791 10,12C10,14.209 8.209,16 6,16C3.791,16 2,14.209 2,12Z"
android:fillColor="#1F002A"
android:fillType="evenOdd"/>
<path
android:pathData="M18,17C16.895,17 16,17.895 16,19C16,20.105 16.895,21 18,21C19.105,21 20,20.105 20,19C20,17.895 19.105,17 18,17ZM14,19C14,16.791 15.791,15 18,15C20.209,15 22,16.791 22,19C22,21.209 20.209,23 18,23C15.791,23 14,21.209 14,19Z"
android:fillColor="#1F002A"
android:fillType="evenOdd"/>
<path
android:pathData="M7.726,13.007C8.004,12.529 8.616,12.368 9.093,12.646L15.924,16.626C16.401,16.904 16.562,17.516 16.284,17.993C16.006,18.471 15.394,18.632 14.917,18.354L8.087,14.374C7.609,14.096 7.448,13.484 7.726,13.007Z"
android:fillColor="#1F002A"
android:fillType="evenOdd"/>
<path
android:pathData="M16.274,6.006C16.552,6.483 16.391,7.095 15.914,7.374L9.094,11.354C8.617,11.632 8.005,11.471 7.726,10.994C7.448,10.517 7.609,9.905 8.086,9.626L14.906,5.646C15.383,5.368 15.995,5.529 16.274,6.006Z"
android:fillColor="#1F002A"
android:fillType="evenOdd"/>
</vector>

View File

@@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M10,12C10,10.895 10.895,10 12,10C13.105,10 14,10.895 14,12C14,13.105 13.105,14 12,14C10.895,14 10,13.105 10,12Z"
android:fillColor="#1F002A"
android:fillType="evenOdd"/>
<path
android:pathData="M10,5C10,3.895 10.895,3 12,3C13.105,3 14,3.895 14,5C14,6.105 13.105,7 12,7C10.895,7 10,6.105 10,5Z"
android:fillColor="#1F002A"
android:fillType="evenOdd"/>
<path
android:pathData="M10,19C10,17.895 10.895,17 12,17C13.105,17 14,17.895 14,19C14,20.105 13.105,21 12,21C10.895,21 10,20.105 10,19Z"
android:fillColor="#1F002A"
android:fillType="evenOdd"/>
</vector>

View File

@@ -1,4 +1,10 @@
<resources>
<string name="app_name">UiTron</string>
<string name="title_activity_main">UiTron Demo Home</string>
<string name="filter_by">FILTER BY ></string>
<string name="clear_all">Clear All</string>
<string name="apply_filters">Apply Filters</string>
<string name="copy_widget_config">Copy widget config</string>
<string name="share_widget_config">Share widget config</string>
<string name="search">Search</string>
</resources>

View File

@@ -9,4 +9,5 @@ plugins {
id 'com.android.library' version '7.3.1' apply false
id 'org.jetbrains.kotlin.android' version '1.8.10' apply false
id 'com.android.test' version '7.3.1' apply false
id 'com.google.dagger.hilt.android' version '2.44' apply false
}