Fixed some design issues;
Changed chart's horizontal axis so that it displays correct date for each value of fear greed index; Implemented Cicerone as navigation library for the app; Implemented two additional screens - About and Learn; Implemented navigation drawer and made it open the respected screens as well as highlighting current screen in the list; TODO: About and Learn screens (text)
This commit is contained in:
		| @@ -49,6 +49,7 @@ dependencies { | ||||
|     implementation(libs.androidx.ui.graphics) | ||||
|     implementation(libs.androidx.ui.tooling.preview) | ||||
|     implementation(libs.androidx.material3) | ||||
|     implementation(libs.androidx.fragment.ktx) | ||||
|     testImplementation(libs.junit) | ||||
|     androidTestImplementation(libs.androidx.junit) | ||||
|     androidTestImplementation(libs.androidx.espresso.core) | ||||
| @@ -62,5 +63,6 @@ dependencies { | ||||
|     implementation(libs.kotlinx.coroutines.android) | ||||
|     implementation(libs.kotlinx.coroutines.core) | ||||
|     implementation(libs.vico.compose.m3) | ||||
|     implementation(libs.cicerone) | ||||
|  | ||||
| } | ||||
| @@ -3,6 +3,7 @@ | ||||
|     xmlns:tools="http://schemas.android.com/tools"> | ||||
|     <uses-permission android:name="android.permission.INTERNET" /> | ||||
|     <application | ||||
|         android:name=".App" | ||||
|         android:allowBackup="false" | ||||
|         android:dataExtractionRules="@xml/data_extraction_rules" | ||||
|         android:fullBackupContent="@xml/backup_rules" | ||||
|   | ||||
| @@ -0,0 +1,119 @@ | ||||
| package ru.vendetti.bitcoin_summarizer | ||||
|  | ||||
| import android.os.Bundle | ||||
| import android.view.LayoutInflater | ||||
| import android.view.View | ||||
| import android.view.ViewGroup | ||||
| import androidx.compose.foundation.background | ||||
| import androidx.compose.foundation.layout.Box | ||||
| import androidx.compose.foundation.layout.padding | ||||
| import androidx.compose.foundation.rememberScrollState | ||||
| import androidx.compose.foundation.verticalScroll | ||||
| import androidx.compose.material.icons.Icons | ||||
| import androidx.compose.material.icons.filled.Menu | ||||
| import androidx.compose.material.icons.filled.MoreVert | ||||
| import androidx.compose.material3.CenterAlignedTopAppBar | ||||
| import androidx.compose.material3.DrawerValue | ||||
| import androidx.compose.material3.ExperimentalMaterial3Api | ||||
| import androidx.compose.material3.Icon | ||||
| import androidx.compose.material3.IconButton | ||||
| import androidx.compose.material3.MaterialTheme | ||||
| import androidx.compose.material3.MediumTopAppBar | ||||
| import androidx.compose.material3.ModalNavigationDrawer | ||||
| import androidx.compose.material3.Scaffold | ||||
| import androidx.compose.material3.Text | ||||
| import androidx.compose.material3.TopAppBarDefaults | ||||
| import androidx.compose.material3.rememberDrawerState | ||||
| import androidx.compose.material3.rememberTopAppBarState | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.rememberCoroutineScope | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.graphics.Color | ||||
| import androidx.compose.ui.input.nestedscroll.nestedScroll | ||||
| import androidx.compose.ui.platform.ComposeView | ||||
| import androidx.compose.ui.text.style.TextOverflow | ||||
| import androidx.compose.ui.tooling.preview.Preview | ||||
| import androidx.fragment.app.Fragment | ||||
| import kotlinx.coroutines.launch | ||||
| import ru.vendetti.bitcoin_summarizer.ui.theme.BitcoinSummarizerTheme | ||||
|  | ||||
| class AboutFragment: Fragment() { | ||||
|     override fun onCreateView( | ||||
|         inflater: LayoutInflater, | ||||
|         container: ViewGroup?, | ||||
|         savedInstanceState: Bundle? | ||||
|     ): View { | ||||
|         return inflater.inflate(R.layout.fragment_compose_view, | ||||
|             container, | ||||
|             false).apply { | ||||
|             findViewById<ComposeView>(R.id.compose_view).setContent { | ||||
|                 BitcoinSummarizerTheme { | ||||
|                     AboutComposable() | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @OptIn(ExperimentalMaterial3Api::class) | ||||
| @Composable | ||||
| @Preview | ||||
| fun AboutComposable() { | ||||
|     val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed) | ||||
|     val scope = rememberCoroutineScope() | ||||
|  | ||||
|     ModalNavigationDrawer( | ||||
|         drawerState = drawerState, | ||||
|         drawerContent = { | ||||
|             MyHamburgerModal(ScreenKey.About) | ||||
|         } | ||||
|     ) { | ||||
|         Scaffold( | ||||
|             modifier = Modifier, | ||||
|  | ||||
|             topBar = { | ||||
|                 CenterAlignedTopAppBar( | ||||
|                     colors = TopAppBarDefaults.topAppBarColors( | ||||
|                         containerColor = MaterialTheme.colorScheme.primary, | ||||
|                         scrolledContainerColor = MaterialTheme.colorScheme.primary, | ||||
|                         titleContentColor = MaterialTheme.colorScheme.onPrimary, | ||||
|                         navigationIconContentColor = MaterialTheme.colorScheme.onPrimary, | ||||
|                         actionIconContentColor = MaterialTheme.colorScheme.onPrimary, | ||||
|                     ), | ||||
|                     title = { | ||||
|                         Text( | ||||
|                             "About", | ||||
|                             maxLines = 1, | ||||
|                             overflow = TextOverflow.Ellipsis | ||||
|                         ) | ||||
|                     }, | ||||
|                     navigationIcon = { | ||||
|                         IconButton(onClick = { | ||||
|                             scope.launch { | ||||
|                                 drawerState.apply { | ||||
|                                     if(isClosed) open() else close() | ||||
|                                 } | ||||
|                             } | ||||
|                         }) { | ||||
|                             Icon( | ||||
|                                 imageVector = Icons.Filled.Menu, | ||||
|                                 contentDescription = "Navigation hamburger menu" | ||||
|                             ) | ||||
|                         } | ||||
|                     }, | ||||
|                     scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) | ||||
|                 ) | ||||
|             }, | ||||
|         ) | ||||
|         { innerPadding -> | ||||
|             Box( | ||||
|                 modifier = Modifier | ||||
|                     .background(Color.Transparent) | ||||
|                     .padding(innerPadding) | ||||
|                     .verticalScroll(rememberScrollState()) | ||||
|             ) { | ||||
|  | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										23
									
								
								app/src/main/java/ru/vendetti/bitcoin_summarizer/App.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								app/src/main/java/ru/vendetti/bitcoin_summarizer/App.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| package ru.vendetti.bitcoin_summarizer | ||||
|  | ||||
| import android.app.Application | ||||
| import android.os.Debug | ||||
| import android.util.Log | ||||
| import com.github.terrakok.cicerone.Cicerone | ||||
|  | ||||
| class App : Application() { | ||||
|     private val cicerone = Cicerone.create() | ||||
|     val router get() = cicerone.router | ||||
|     val navigatorHolder get() = cicerone.getNavigatorHolder() | ||||
|  | ||||
|     override fun onCreate() { | ||||
|         super.onCreate() | ||||
|         INSTANCE = this | ||||
|         Log.println(Log.DEBUG, "App", "Instance is $INSTANCE") | ||||
|     } | ||||
|  | ||||
|     companion object { | ||||
|         internal lateinit var INSTANCE: App | ||||
|             private set | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,411 @@ | ||||
| package ru.vendetti.bitcoin_summarizer | ||||
|  | ||||
| import android.annotation.SuppressLint | ||||
| import android.os.Bundle | ||||
| import android.view.LayoutInflater | ||||
| import android.view.View | ||||
| import android.view.ViewGroup | ||||
| import androidx.compose.foundation.background | ||||
| import androidx.compose.foundation.layout.Box | ||||
| import androidx.compose.foundation.layout.Column | ||||
| import androidx.compose.foundation.layout.Spacer | ||||
| import androidx.compose.foundation.layout.height | ||||
| import androidx.compose.foundation.layout.padding | ||||
| import androidx.compose.foundation.rememberScrollState | ||||
| import androidx.compose.foundation.verticalScroll | ||||
| import androidx.compose.material.icons.Icons | ||||
| import androidx.compose.material.icons.filled.Menu | ||||
| import androidx.compose.material.icons.filled.MoreVert | ||||
| import androidx.compose.material3.CenterAlignedTopAppBar | ||||
| import androidx.compose.material3.DrawerValue | ||||
| import androidx.compose.material3.ExperimentalMaterial3Api | ||||
| import androidx.compose.material3.HorizontalDivider | ||||
| import androidx.compose.material3.Icon | ||||
| import androidx.compose.material3.IconButton | ||||
| import androidx.compose.material3.MaterialTheme | ||||
| import androidx.compose.material3.MediumTopAppBar | ||||
| import androidx.compose.material3.ModalDrawerSheet | ||||
| import androidx.compose.material3.ModalNavigationDrawer | ||||
| import androidx.compose.material3.Scaffold | ||||
| import androidx.compose.material3.Text | ||||
| import androidx.compose.material3.TopAppBarDefaults | ||||
| import androidx.compose.material3.rememberDrawerState | ||||
| import androidx.compose.material3.rememberTopAppBarState | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.LaunchedEffect | ||||
| import androidx.compose.runtime.getValue | ||||
| import androidx.compose.runtime.mutableIntStateOf | ||||
| import androidx.compose.runtime.mutableStateOf | ||||
| import androidx.compose.runtime.remember | ||||
| import androidx.compose.runtime.rememberCoroutineScope | ||||
| import androidx.compose.runtime.setValue | ||||
| import androidx.compose.ui.Alignment | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.graphics.Color | ||||
| import androidx.compose.ui.input.nestedscroll.nestedScroll | ||||
| import androidx.compose.ui.platform.ComposeView | ||||
| import androidx.compose.ui.text.style.TextOverflow | ||||
| import androidx.compose.ui.tooling.preview.Preview | ||||
| import androidx.compose.ui.unit.dp | ||||
| import androidx.compose.ui.unit.sp | ||||
| import androidx.fragment.app.Fragment | ||||
| import com.patrykandpatrick.vico.compose.cartesian.CartesianChartHost | ||||
| import com.patrykandpatrick.vico.compose.cartesian.axis.rememberAxisLabelComponent | ||||
| import com.patrykandpatrick.vico.compose.cartesian.axis.rememberAxisLineComponent | ||||
| import com.patrykandpatrick.vico.compose.cartesian.axis.rememberBottom | ||||
| import com.patrykandpatrick.vico.compose.cartesian.axis.rememberStart | ||||
| import com.patrykandpatrick.vico.compose.cartesian.layer.rememberLine | ||||
| import com.patrykandpatrick.vico.compose.cartesian.layer.rememberLineCartesianLayer | ||||
| import com.patrykandpatrick.vico.compose.cartesian.rememberCartesianChart | ||||
| import com.patrykandpatrick.vico.compose.cartesian.rememberVicoScrollState | ||||
| import com.patrykandpatrick.vico.compose.cartesian.rememberVicoZoomState | ||||
| import com.patrykandpatrick.vico.compose.common.component.rememberTextComponent | ||||
| import com.patrykandpatrick.vico.compose.common.component.shapeComponent | ||||
| import com.patrykandpatrick.vico.compose.common.fill | ||||
| import com.patrykandpatrick.vico.compose.common.shape.rounded | ||||
| import com.patrykandpatrick.vico.compose.common.vicoTheme | ||||
| import com.patrykandpatrick.vico.core.cartesian.Zoom | ||||
| import com.patrykandpatrick.vico.core.cartesian.axis.HorizontalAxis | ||||
| import com.patrykandpatrick.vico.core.cartesian.axis.VerticalAxis | ||||
| import com.patrykandpatrick.vico.core.cartesian.data.CartesianChartModelProducer | ||||
| import com.patrykandpatrick.vico.core.cartesian.data.CartesianLayerRangeProvider | ||||
| import com.patrykandpatrick.vico.core.cartesian.data.lineSeries | ||||
| import com.patrykandpatrick.vico.core.cartesian.decoration.HorizontalLine | ||||
| import com.patrykandpatrick.vico.core.cartesian.layer.LineCartesianLayer | ||||
| import com.patrykandpatrick.vico.core.common.Position | ||||
| import com.patrykandpatrick.vico.core.common.component.LineComponent | ||||
| import com.patrykandpatrick.vico.core.common.component.TextComponent | ||||
| import com.patrykandpatrick.vico.core.common.shape.CorneredShape | ||||
| import kotlinx.coroutines.launch | ||||
| import ru.vendetti.bitcoin_summarizer.ui.theme.BitcoinSummarizerTheme | ||||
| import ru.vendetti.bitcoin_summarizer.ui.theme.Flame | ||||
| import ru.vendetti.bitcoin_summarizer.ui.theme.Green2 | ||||
| import java.text.DecimalFormat | ||||
| import java.time.Instant | ||||
| import java.time.format.DateTimeFormatter | ||||
|  | ||||
| class CryptoFragment: Fragment() { | ||||
|     override fun onCreateView( | ||||
|         inflater: LayoutInflater, | ||||
|         container: ViewGroup?, | ||||
|         savedInstanceState: Bundle? | ||||
|     ): View { | ||||
|         return inflater.inflate(R.layout.fragment_compose_view, | ||||
|             container, | ||||
|             false).apply { | ||||
|             findViewById<ComposeView>(R.id.compose_view).setContent { | ||||
|                 BitcoinSummarizerTheme { | ||||
|                     CryptoComposable() | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @SuppressLint("MutableCollectionMutableState", "SimpleDateFormat") | ||||
| @OptIn(ExperimentalMaterial3Api::class) | ||||
| @Preview | ||||
| @Composable | ||||
| fun CryptoComposable() { | ||||
|     // Создаем репозиторий для работы с API | ||||
|     val cryptoRepository = remember { CryptoRepository(RetrofitClient.apiService) } | ||||
|     // Состояния для хранения результатов запросов | ||||
|     var bitcoinTicker by remember { mutableStateOf(TickerData()) } | ||||
|     var globalData by remember { mutableStateOf(GlobalResponse()) } | ||||
|     var fearGreedDataList by remember { mutableStateOf(ArrayList<FearAndGreedData>()) } | ||||
|  | ||||
|     var fearGreedIndexDaysCount by remember { mutableIntStateOf(30) } | ||||
|  | ||||
|     // Запускаем корутину для выполнения сетевых запросов | ||||
|     LaunchedEffect(fearGreedIndexDaysCount) { | ||||
|         try { | ||||
|             // Запрос Bitcoin Ticker | ||||
|             val tickerResponse = cryptoRepository.fetchBitcoinTicker() | ||||
|             bitcoinTicker = tickerResponse!!.first() | ||||
|  | ||||
|             // Запрос глобальных данных | ||||
|             val globalResponse = cryptoRepository.fetchGlobalData() | ||||
|             globalData = globalResponse!! | ||||
|  | ||||
|             // Запрос индекса страха и жадности | ||||
|             val fearResponse = cryptoRepository.fetchFearAndGreedData(fearGreedIndexDaysCount) | ||||
|             fearGreedDataList = fearResponse?.dataList as ArrayList<FearAndGreedData> | ||||
|             fearGreedDataList.reverse() | ||||
|         }catch (e: Exception) { | ||||
|             bitcoinTicker = TickerData( | ||||
|                 id = "", | ||||
|                 name = "", | ||||
|                 symbol = "", | ||||
|                 rank = "", | ||||
|                 priceUsd = "", | ||||
|                 priceBtc = "", | ||||
|                 volume24hUsd = "", | ||||
|                 marketCapUsd = "", | ||||
|                 availableSupply = "", | ||||
|                 totalSupply = "", | ||||
|                 maxSupply = "", | ||||
|                 percentChange1h = "", | ||||
|                 percentChange24h = "", | ||||
|                 percentChange7d = "", | ||||
|                 lastUpdated = "" | ||||
|             ) | ||||
|             globalData = GlobalResponse( | ||||
|                 activeCryptocurrencies = "", | ||||
|                 totalMarketCapUsd = "", | ||||
|                 total24hVolumeUsd = "", | ||||
|                 bitcoinPercentageOfMarketCap = "" | ||||
|             ) | ||||
|             fearGreedDataList = ArrayList<FearAndGreedData>() | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     val modelProducer = remember { CartesianChartModelProducer() } | ||||
|  | ||||
|     LaunchedEffect(fearGreedDataList) { | ||||
|         modelProducer.runTransaction { | ||||
|             var numberValues = Array(fearGreedDataList.count()) { | ||||
|                     index -> | ||||
|                 fearGreedDataList[index] | ||||
|                     .value.toInt() | ||||
|             } | ||||
|  | ||||
|             if(numberValues.isEmpty()) | ||||
|                 numberValues = Array(1) {0} | ||||
|  | ||||
|             lineSeries { series(numberValues.toList()) } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(rememberTopAppBarState()) | ||||
|  | ||||
|     // Отображаем результаты на странице | ||||
|  | ||||
|     val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed) | ||||
|     val scope = rememberCoroutineScope() | ||||
|  | ||||
|     ModalNavigationDrawer( | ||||
|         drawerState = drawerState, | ||||
|         drawerContent = { | ||||
|             MyHamburgerModal(ScreenKey.Crypto) | ||||
|         } | ||||
|     ) { | ||||
|         Scaffold( | ||||
|             modifier = Modifier | ||||
|                 .nestedScroll(scrollBehavior.nestedScrollConnection), | ||||
|  | ||||
|             topBar = { | ||||
|                 CenterAlignedTopAppBar( | ||||
|                     colors = TopAppBarDefaults.topAppBarColors( | ||||
|                         containerColor = MaterialTheme.colorScheme.primary, | ||||
|                         scrolledContainerColor = MaterialTheme.colorScheme.primary, | ||||
|                         titleContentColor = MaterialTheme.colorScheme.onPrimary, | ||||
|                         navigationIconContentColor = MaterialTheme.colorScheme.onPrimary, | ||||
|                         actionIconContentColor = MaterialTheme.colorScheme.onPrimary, | ||||
|                     ), | ||||
|                     title = { | ||||
|                         Text( | ||||
|                             "Crypto statistics", | ||||
|                             maxLines = 1, | ||||
|                             overflow = TextOverflow.Ellipsis | ||||
|                         ) | ||||
|                     }, | ||||
|                     navigationIcon = { | ||||
|                         IconButton(onClick = { | ||||
|                             scope.launch { | ||||
|                                 drawerState.apply { | ||||
|                                     if(isClosed) open() else close() | ||||
|                                 } | ||||
|                             } | ||||
|                         }) { | ||||
|                             Icon( | ||||
|                                 imageVector = Icons.Filled.Menu, | ||||
|                                 contentDescription = "Navigation hamburger menu" | ||||
|                             ) | ||||
|                         } | ||||
|                     }, | ||||
|                     scrollBehavior = scrollBehavior | ||||
|                 ) | ||||
|             }, | ||||
|         ) | ||||
|         { innerPadding -> | ||||
|             Box( | ||||
|                 modifier = Modifier | ||||
|                     .background(Color.Transparent) | ||||
|                     .padding(innerPadding) | ||||
|                     .verticalScroll(rememberScrollState()) | ||||
|             ) { | ||||
|                 Column( | ||||
|                     modifier = Modifier | ||||
|                         .padding(16.dp, 16.dp, 16.dp, 36.dp) | ||||
|                 ) { | ||||
|                     /* Fear Greed Chart Start */ | ||||
|                     Spacer(modifier = Modifier.height(16.dp)) | ||||
|                     Text( | ||||
|                         "Индекс страха/жадности", | ||||
|                         modifier = Modifier | ||||
|                             .align(alignment = Alignment.CenterHorizontally), | ||||
|                         fontSize = 24.sp | ||||
|                     ) | ||||
|                     Spacer(modifier = Modifier.height(16.dp)) | ||||
|                     if (fearGreedDataList.count() > 1) { | ||||
|                         CartesianChartHost( | ||||
|                             zoomState = rememberVicoZoomState( | ||||
|                                 zoomEnabled = true, | ||||
|                                 initialZoom = Zoom.x(fearGreedIndexDaysCount.toDouble()), | ||||
|                             ), | ||||
|                             scrollState = rememberVicoScrollState(scrollEnabled = true), | ||||
|                             chart = rememberCartesianChart( | ||||
|                                 rememberLineCartesianLayer( | ||||
|                                     lineProvider = LineCartesianLayer.LineProvider.series( | ||||
|                                         vicoTheme.lineCartesianLayerColors.map { _ -> | ||||
|                                             LineCartesianLayer | ||||
|                                                 .rememberLine( | ||||
|                                                     LineCartesianLayer.LineFill.single( | ||||
|                                                         fill(MaterialTheme.colorScheme.onPrimary) | ||||
|                                                     ) | ||||
|                                                 ) | ||||
|                                         } | ||||
|                                     ), | ||||
|                                     rangeProvider = remember { | ||||
|                                         CartesianLayerRangeProvider.fixed( | ||||
|                                             minY = 0.0, | ||||
|                                             maxY = 100.0, | ||||
|                                         ) | ||||
|                                     } | ||||
|                                 ), | ||||
|                                 startAxis = VerticalAxis.rememberStart( | ||||
|                                     title = "FGI", | ||||
|                                     titleComponent = rememberTextComponent(MaterialTheme.colorScheme.onPrimary), | ||||
|                                     line = rememberAxisLineComponent(fill(MaterialTheme.colorScheme.onPrimary)), | ||||
|                                     label = rememberAxisLabelComponent(MaterialTheme.colorScheme.onPrimary) | ||||
|                                 ), | ||||
|                                 bottomAxis = HorizontalAxis.rememberBottom( | ||||
|                                     valueFormatter = { _, value, _ -> | ||||
|                                         val date = DateTimeFormatter.ISO_INSTANT | ||||
|                                             .format(Instant.ofEpochSecond(fearGreedDataList[value.toInt()].timestamp.toLong())) | ||||
|                                         val dateComponents = date.split("T")[0].split("-") | ||||
|                                         // returns dd.mm | ||||
|                                         "${dateComponents[2]}.${dateComponents[1]}" | ||||
|                                     }, | ||||
|                                     title = "last $fearGreedIndexDaysCount days", | ||||
|                                     titleComponent = rememberTextComponent(MaterialTheme.colorScheme.onPrimary), | ||||
|                                     line = rememberAxisLineComponent(fill(MaterialTheme.colorScheme.onPrimary)), | ||||
|                                     label = rememberAxisLabelComponent(MaterialTheme.colorScheme.onPrimary) | ||||
|                                 ), | ||||
|                                 decorations = listOf( | ||||
|                                     remember { | ||||
|                                         HorizontalLine( | ||||
|                                             y = { 25.toDouble() }, | ||||
|                                             line = LineComponent(fill(Flame), 1f), | ||||
|                                             labelComponent = TextComponent( | ||||
|                                                 background = | ||||
|                                                     shapeComponent( | ||||
|                                                         fill(Flame), | ||||
|                                                         CorneredShape.rounded( | ||||
|                                                             topLeft = 4.dp, | ||||
|                                                             topRight = 4.dp | ||||
|                                                         ) | ||||
|                                                     ), | ||||
|                                             ), | ||||
|                                             label = { "Страх" }, | ||||
|                                             verticalLabelPosition = Position.Vertical.Top | ||||
|                                         ) | ||||
|                                     }, | ||||
|                                     remember { | ||||
|                                         HorizontalLine( | ||||
|                                             y = { 70.toDouble() }, | ||||
|                                             line = LineComponent(fill(Green2), 1f), | ||||
|                                             labelComponent = TextComponent( | ||||
|                                                 background = | ||||
|                                                     shapeComponent( | ||||
|                                                         fill(Green2), | ||||
|                                                         CorneredShape.rounded( | ||||
|                                                             bottomLeft = 4.dp, | ||||
|                                                             bottomRight = 4.dp | ||||
|                                                         ) | ||||
|                                                     ), | ||||
|                                             ), | ||||
|                                             label = { "Жадность" }, | ||||
|                                             verticalLabelPosition = Position.Vertical.Bottom | ||||
|                                         ) | ||||
|                                     } | ||||
|                                 ), | ||||
|                             ), | ||||
|                             modelProducer = modelProducer, | ||||
|                         ) | ||||
|                     } else { | ||||
|                         Text("Загрузка...") | ||||
|                     } | ||||
|                     /* Fear Greed Chart End */ | ||||
|                     Spacer(modifier = Modifier.height(16.dp)) | ||||
|                     HorizontalDivider(thickness = 2.dp) | ||||
|                     Spacer(modifier = Modifier.height(16.dp)) | ||||
|                     Text( | ||||
|                         "Данные о Биткоине", | ||||
|                         modifier = Modifier | ||||
|                             .align(alignment = Alignment.CenterHorizontally), | ||||
|                         fontSize = 24.sp | ||||
|                     ) | ||||
|                     Spacer(modifier = Modifier.height(16.dp)) | ||||
|                     val decimalFormatter = DecimalFormat("0.00") | ||||
|                     val largeNumberFormatter = DecimalFormat("#,###") | ||||
|  | ||||
|                     if (bitcoinTicker.lastUpdated.isNotEmpty()) { | ||||
|                         Text("Текущая цена: \n \$ ${decimalFormatter.format(bitcoinTicker.priceUsd.toFloat())}\n") | ||||
|                         Text("Суточный оборот: \n \$ ${largeNumberFormatter.format(bitcoinTicker.volume24hUsd.toFloat())}\n") | ||||
|                         Text("Капитализация: \n \$ ${largeNumberFormatter.format(bitcoinTicker.marketCapUsd.toFloat())}\n") | ||||
|                         Text( | ||||
|                             "Изменение курса за: " + | ||||
|                                     "\n Сутки: ${decimalFormatter.format(bitcoinTicker.percentChange24h.toFloat())}% " + | ||||
|                                     "\n Неделю: ${decimalFormatter.format(bitcoinTicker.percentChange7d.toFloat())}%\n" | ||||
|                         ) | ||||
|  | ||||
|                         val humanDate = DateTimeFormatter.ISO_INSTANT | ||||
|                             .format(Instant.ofEpochSecond(bitcoinTicker.lastUpdated.toLong())) | ||||
|  | ||||
|                         Text( | ||||
|                             "Время последнего обновления: \n ${humanDate.split("T")[0]}, " + | ||||
|                                     "${ | ||||
|                                         humanDate.split("T")[1].replace( | ||||
|                                             Regex(":[0-9]+[A-Z]"), | ||||
|                                             "" | ||||
|                                         ) | ||||
|                                     }\n" | ||||
|                         ) | ||||
|                     } else { | ||||
|                         Text("Загрузка...") | ||||
|                     } | ||||
|                     HorizontalDivider(thickness = 2.dp) | ||||
|                     Spacer(modifier = Modifier.height(16.dp)) | ||||
|                     Text( | ||||
|                         "Глобальные данные", | ||||
|                         modifier = Modifier | ||||
|                             .align(alignment = Alignment.CenterHorizontally), | ||||
|                         fontSize = 24.sp | ||||
|                     ) | ||||
|                     Spacer(modifier = Modifier.height(16.dp)) | ||||
|                     if (globalData.totalMarketCapUsd.isNotEmpty()) { | ||||
|                         Text( | ||||
|                             "Общая капитализация крипторынка: \n \$ ${ | ||||
|                                 largeNumberFormatter.format( | ||||
|                                     globalData.totalMarketCapUsd.toFloat() | ||||
|                                 ) | ||||
|                             }\n" | ||||
|                         ) | ||||
|                         Text("Всего тикеров: \n ${globalData.activeCryptocurrencies}\n") | ||||
|                         Text( | ||||
|                             "Суточный оборот всех криптовалют: \n \$ ${ | ||||
|                                 largeNumberFormatter.format( | ||||
|                                     globalData.total24hVolumeUsd.toFloat() | ||||
|                                 ) | ||||
|                             }\n" | ||||
|                         ) | ||||
|                         Text("Процент доминации Биткоина: \n ${decimalFormatter.format(globalData.bitcoinPercentageOfMarketCap.toFloat())}%\n") | ||||
|                     } else { | ||||
|                         Text("Загрузка...") | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,119 @@ | ||||
| package ru.vendetti.bitcoin_summarizer | ||||
|  | ||||
| import android.os.Bundle | ||||
| import android.view.LayoutInflater | ||||
| import android.view.View | ||||
| import android.view.ViewGroup | ||||
| import androidx.compose.foundation.background | ||||
| import androidx.compose.foundation.layout.Box | ||||
| import androidx.compose.foundation.layout.padding | ||||
| import androidx.compose.foundation.rememberScrollState | ||||
| import androidx.compose.foundation.verticalScroll | ||||
| import androidx.compose.material.icons.Icons | ||||
| import androidx.compose.material.icons.filled.Menu | ||||
| import androidx.compose.material.icons.filled.MoreVert | ||||
| import androidx.compose.material3.CenterAlignedTopAppBar | ||||
| import androidx.compose.material3.DrawerValue | ||||
| import androidx.compose.material3.ExperimentalMaterial3Api | ||||
| import androidx.compose.material3.Icon | ||||
| import androidx.compose.material3.IconButton | ||||
| import androidx.compose.material3.MaterialTheme | ||||
| import androidx.compose.material3.MediumTopAppBar | ||||
| import androidx.compose.material3.ModalNavigationDrawer | ||||
| import androidx.compose.material3.Scaffold | ||||
| import androidx.compose.material3.Text | ||||
| import androidx.compose.material3.TopAppBarDefaults | ||||
| import androidx.compose.material3.rememberDrawerState | ||||
| import androidx.compose.material3.rememberTopAppBarState | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.rememberCoroutineScope | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.graphics.Color | ||||
| import androidx.compose.ui.input.nestedscroll.nestedScroll | ||||
| import androidx.compose.ui.platform.ComposeView | ||||
| import androidx.compose.ui.text.style.TextOverflow | ||||
| import androidx.compose.ui.tooling.preview.Preview | ||||
| import androidx.fragment.app.Fragment | ||||
| import kotlinx.coroutines.launch | ||||
| import ru.vendetti.bitcoin_summarizer.ui.theme.BitcoinSummarizerTheme | ||||
|  | ||||
| class LearnFragment: Fragment() { | ||||
|     override fun onCreateView( | ||||
|         inflater: LayoutInflater, | ||||
|         container: ViewGroup?, | ||||
|         savedInstanceState: Bundle? | ||||
|     ): View { | ||||
|         return inflater.inflate(R.layout.fragment_compose_view, | ||||
|             container, | ||||
|             false).apply { | ||||
|             findViewById<ComposeView>(R.id.compose_view).setContent { | ||||
|                 BitcoinSummarizerTheme { | ||||
|                     LearnComposable() | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @OptIn(ExperimentalMaterial3Api::class) | ||||
| @Composable | ||||
| @Preview | ||||
| fun LearnComposable() { | ||||
|     val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed) | ||||
|     val scope = rememberCoroutineScope() | ||||
|  | ||||
|     ModalNavigationDrawer( | ||||
|         drawerState = drawerState, | ||||
|         drawerContent = { | ||||
|             MyHamburgerModal(ScreenKey.Learn) | ||||
|         } | ||||
|     ) { | ||||
|         Scaffold( | ||||
|             modifier = Modifier, | ||||
|  | ||||
|             topBar = { | ||||
|                 CenterAlignedTopAppBar( | ||||
|                     colors = TopAppBarDefaults.topAppBarColors( | ||||
|                         containerColor = MaterialTheme.colorScheme.primary, | ||||
|                         scrolledContainerColor = MaterialTheme.colorScheme.primary, | ||||
|                         titleContentColor = MaterialTheme.colorScheme.onPrimary, | ||||
|                         navigationIconContentColor = MaterialTheme.colorScheme.onPrimary, | ||||
|                         actionIconContentColor = MaterialTheme.colorScheme.onPrimary, | ||||
|                     ), | ||||
|                     title = { | ||||
|                         Text( | ||||
|                             "Learn", | ||||
|                             maxLines = 1, | ||||
|                             overflow = TextOverflow.Ellipsis | ||||
|                         ) | ||||
|                     }, | ||||
|                     navigationIcon = { | ||||
|                         IconButton(onClick = { | ||||
|                             scope.launch { | ||||
|                                 drawerState.apply { | ||||
|                                     if(isClosed) open() else close() | ||||
|                                 } | ||||
|                             } | ||||
|                         }) { | ||||
|                             Icon( | ||||
|                                 imageVector = Icons.Filled.Menu, | ||||
|                                 contentDescription = "Navigation hamburger menu" | ||||
|                             ) | ||||
|                         } | ||||
|                     }, | ||||
|                     scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) | ||||
|                 ) | ||||
|             }, | ||||
|         ) | ||||
|         { innerPadding -> | ||||
|             Box( | ||||
|                 modifier = Modifier | ||||
|                     .background(Color.Transparent) | ||||
|                     .padding(innerPadding) | ||||
|                     .verticalScroll(rememberScrollState()) | ||||
|             ) { | ||||
|  | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -2,6 +2,7 @@ package ru.vendetti.bitcoin_summarizer | ||||
|  | ||||
| import android.annotation.SuppressLint | ||||
| import android.os.Bundle | ||||
| import android.widget.FrameLayout | ||||
| import androidx.activity.ComponentActivity | ||||
| import androidx.activity.compose.setContent | ||||
| import androidx.compose.foundation.background | ||||
| @@ -40,6 +41,8 @@ import androidx.compose.ui.text.style.TextOverflow | ||||
| import androidx.compose.ui.tooling.preview.Preview | ||||
| import androidx.compose.ui.unit.dp | ||||
| import androidx.compose.ui.unit.sp | ||||
| import androidx.fragment.app.FragmentActivity | ||||
| import com.github.terrakok.cicerone.androidx.AppNavigator | ||||
| import com.patrykandpatrick.vico.compose.cartesian.CartesianChartHost | ||||
| import com.patrykandpatrick.vico.compose.cartesian.axis.rememberAxisLabelComponent | ||||
| import com.patrykandpatrick.vico.compose.cartesian.axis.rememberAxisLineComponent | ||||
| @@ -72,234 +75,25 @@ import ru.vendetti.bitcoin_summarizer.ui.theme.Green2 | ||||
| import java.text.DecimalFormat | ||||
| import java.time.format.DateTimeFormatter | ||||
|  | ||||
| class MainActivity : ComponentActivity() { | ||||
| class MainActivity : FragmentActivity() { | ||||
|  | ||||
|     private val navigator = AppNavigator(this, R.id.container) | ||||
|  | ||||
|     override fun onCreate(savedInstanceState: Bundle?) { | ||||
|         super.onCreate(savedInstanceState) | ||||
|         setContent { | ||||
|             BitcoinSummarizerTheme { | ||||
|                 CryptoScreen() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @SuppressLint("MutableCollectionMutableState", "SimpleDateFormat") | ||||
| @OptIn(ExperimentalMaterial3Api::class) | ||||
| @Preview | ||||
| @Composable | ||||
| fun CryptoScreen() { | ||||
|     // Создаем репозиторий для работы с API | ||||
|     val cryptoRepository = remember { CryptoRepository(RetrofitClient.apiService) } | ||||
|     // Состояния для хранения результатов запросов | ||||
|     var bitcoinTicker by remember { mutableStateOf(TickerData()) } | ||||
|     var globalData by remember { mutableStateOf(GlobalResponse()) } | ||||
|     var fearGreedDataList by remember { mutableStateOf(ArrayList<FearAndGreedData>()) } | ||||
|  | ||||
|     var fearGreedIndexDaysCount by remember { mutableIntStateOf(30) } | ||||
|  | ||||
|     // Запускаем корутину для выполнения сетевых запросов | ||||
|     LaunchedEffect(fearGreedIndexDaysCount) { | ||||
|         // Запрос Bitcoin Ticker | ||||
|         val tickerResponse = cryptoRepository.fetchBitcoinTicker() | ||||
|         bitcoinTicker = tickerResponse!!.first() | ||||
|  | ||||
|         // Запрос глобальных данных | ||||
|         val globalResponse = cryptoRepository.fetchGlobalData() | ||||
|         globalData = globalResponse!! | ||||
|  | ||||
|         // Запрос индекса страха и жадности | ||||
|         val fearResponse = cryptoRepository.fetchFearAndGreedData(fearGreedIndexDaysCount) | ||||
|         fearGreedDataList = fearResponse?.dataList as ArrayList<FearAndGreedData> | ||||
|     } | ||||
|  | ||||
|     val modelProducer = remember { CartesianChartModelProducer() } | ||||
|  | ||||
|     LaunchedEffect(fearGreedDataList) { | ||||
|         modelProducer.runTransaction { | ||||
|             var numberValues = Array(fearGreedDataList.count()) { | ||||
|                 index -> | ||||
|                     fearGreedDataList[fearGreedDataList.count() - index - 1] | ||||
|                         .value.toInt() | ||||
|             } | ||||
|  | ||||
|             if(numberValues.isEmpty()) | ||||
|                 numberValues = Array(1) {0} | ||||
|  | ||||
|             lineSeries { series(numberValues.toList()) } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(rememberTopAppBarState()) | ||||
|     val zoomState = rememberVicoZoomState( | ||||
|         zoomEnabled = false, | ||||
|         initialZoom = Zoom.x(fearGreedIndexDaysCount.toDouble())) | ||||
|  | ||||
|     // Отображаем результаты на странице | ||||
|     Scaffold ( | ||||
|         modifier = Modifier | ||||
|             .nestedScroll(scrollBehavior.nestedScrollConnection), | ||||
|  | ||||
|         topBar = { | ||||
|             MediumTopAppBar( | ||||
|                 colors = TopAppBarDefaults.topAppBarColors( | ||||
|                     containerColor = MaterialTheme.colorScheme.primary, | ||||
|                     scrolledContainerColor = MaterialTheme.colorScheme.primary, | ||||
|                     titleContentColor = MaterialTheme.colorScheme.onPrimary, | ||||
|                     navigationIconContentColor = MaterialTheme.colorScheme.onPrimary, | ||||
|                     actionIconContentColor = MaterialTheme.colorScheme.onPrimary, | ||||
|                 ), | ||||
|                 title = { | ||||
|                     Text( | ||||
|                         "Bitcoin Summarizer", | ||||
|                         maxLines = 1, | ||||
|                         overflow = TextOverflow.Ellipsis | ||||
|                     ) | ||||
|                 }, | ||||
|                 navigationIcon = { | ||||
|                     IconButton(onClick = {}) { | ||||
|                         Icon( | ||||
|                             imageVector = Icons.Filled.Menu, | ||||
|                             contentDescription = "Navigation hamburger menu" | ||||
|                         ) | ||||
|                     } | ||||
|                 }, | ||||
|                 actions = { | ||||
|                     IconButton(onClick = {}) { | ||||
|                         Icon( | ||||
|                             imageVector = Icons.Filled.MoreVert, | ||||
|                             contentDescription = "Actions" | ||||
|                         ) | ||||
|                     } | ||||
|                 }, | ||||
|                 scrollBehavior = scrollBehavior | ||||
|             ) | ||||
|         }, | ||||
|     ) | ||||
|     { innerPadding -> | ||||
|         Box( | ||||
|             modifier = Modifier | ||||
|                 .background(Color.Transparent) | ||||
|                 .padding(innerPadding) | ||||
|                 .verticalScroll(rememberScrollState()) | ||||
|         ) { | ||||
|             Column( | ||||
|                 modifier = Modifier | ||||
|                     .padding(16.dp, 16.dp, 16.dp, 36.dp) | ||||
|             ) { | ||||
|                 /* Fear Greed Chart Start */ | ||||
|                 CartesianChartHost( | ||||
|                     zoomState = zoomState, | ||||
|                     chart = rememberCartesianChart( | ||||
|                         rememberLineCartesianLayer( | ||||
|                             lineProvider = LineCartesianLayer.LineProvider.series( | ||||
|                                 vicoTheme.lineCartesianLayerColors.map{ | ||||
|                                     _ -> | ||||
|                                         LineCartesianLayer | ||||
|                                             .rememberLine( | ||||
|                                                 LineCartesianLayer.LineFill.single( | ||||
|                                                     fill(MaterialTheme.colorScheme.onPrimary) | ||||
|                                                 ) | ||||
|                                             ) | ||||
|                                 } | ||||
|                             ), | ||||
|                             rangeProvider = remember { CartesianLayerRangeProvider.fixed(minY = 0.0, maxY = 100.0)} | ||||
|                         ), | ||||
|                         startAxis = VerticalAxis.rememberStart( | ||||
|                             title = "FGI", | ||||
|                             titleComponent = rememberTextComponent(MaterialTheme.colorScheme.onPrimary), | ||||
|                             line = rememberAxisLineComponent(fill(MaterialTheme.colorScheme.onPrimary)), | ||||
|                             label = rememberAxisLabelComponent(MaterialTheme.colorScheme.onPrimary) | ||||
|                         ), | ||||
|                         bottomAxis = HorizontalAxis.rememberBottom( | ||||
|                             title = "последние $fearGreedIndexDaysCount дней", | ||||
|                             titleComponent = rememberTextComponent(MaterialTheme.colorScheme.onPrimary), | ||||
|                             line = rememberAxisLineComponent(fill(MaterialTheme.colorScheme.onPrimary)), | ||||
|                             label = rememberAxisLabelComponent(MaterialTheme.colorScheme.onPrimary) | ||||
|                         ), | ||||
|                         decorations = listOf( | ||||
|                             remember { | ||||
|                                 HorizontalLine( | ||||
|                                     y = { 25.toDouble() }, | ||||
|                                     line = LineComponent(fill(Flame), 1f), | ||||
|                                     labelComponent = TextComponent( | ||||
|                                         background = | ||||
|                                         shapeComponent( | ||||
|                                             fill(Flame), | ||||
|                                             CorneredShape.rounded( | ||||
|                                                 topLeft = 4.dp, | ||||
|                                                 topRight = 4.dp | ||||
|                                             ) | ||||
|                                         ), | ||||
|                                     ), | ||||
|                                     label = { "Страх" }, | ||||
|                                     verticalLabelPosition = Position.Vertical.Top | ||||
|                                 ) | ||||
|                             }, | ||||
|                             remember { | ||||
|                                 HorizontalLine( | ||||
|                                     y = { 70.toDouble() }, | ||||
|                                     line = LineComponent(fill(Green2), 1f), | ||||
|                                     labelComponent = TextComponent( | ||||
|                                         background = | ||||
|                                         shapeComponent( | ||||
|                                             fill(Green2), | ||||
|                                             CorneredShape.rounded( | ||||
|                                                 bottomLeft = 4.dp, | ||||
|                                                 bottomRight = 4.dp | ||||
|                                             ) | ||||
|                                         ), | ||||
|                                     ), | ||||
|                                     label = { "Жадность" }, | ||||
|                                     verticalLabelPosition = Position.Vertical.Bottom | ||||
|                                 ) | ||||
|                             } | ||||
|                         ), | ||||
|                     ), | ||||
|                     modelProducer = modelProducer, | ||||
|                 ) | ||||
|                 /* Fear Greed Chart End */ | ||||
|                 Spacer(modifier = Modifier.height(16.dp)) | ||||
|                 HorizontalDivider(thickness = 2.dp) | ||||
|                 Spacer(modifier = Modifier.height(16.dp)) | ||||
|                 Text( | ||||
|                     "Данные о Биткоине", | ||||
|                     modifier = Modifier | ||||
|                         .align(alignment = Alignment.CenterHorizontally), | ||||
|                     fontSize = 24.sp | ||||
|                 ) | ||||
|                 Spacer(modifier = Modifier.height(16.dp)) | ||||
|                 val formatter = DecimalFormat("0.00") | ||||
|                 Text("Текущая цена: \n \$ ${formatter.format(bitcoinTicker.priceUsd.toFloat())}\n") | ||||
|                 Text("Суточный оборот: \n \$ ${bitcoinTicker.volume24hUsd}\n") | ||||
|                 Text("Капитализация: \n \$ ${bitcoinTicker.marketCapUsd}\n") | ||||
|                 Text( | ||||
|                     "Изменение курса за: " + | ||||
|                             "\n Сутки: ${formatter.format(bitcoinTicker.percentChange24h.toFloat())}% " + | ||||
|                             "\n Неделю: ${formatter.format(bitcoinTicker.percentChange7d.toFloat())}%\n" | ||||
|                 ) | ||||
|  | ||||
|                 var humanDate = "" | ||||
|  | ||||
|                 if(bitcoinTicker.lastUpdated.isNotEmpty()) | ||||
|                     humanDate = DateTimeFormatter.ISO_INSTANT | ||||
|                         .format(java.time.Instant.ofEpochSecond(bitcoinTicker.lastUpdated.toLong())) | ||||
|  | ||||
|                 Text("Время последнего обновления: \n ${humanDate}\n") | ||||
|  | ||||
|                 HorizontalDivider(thickness = 2.dp) | ||||
|                 Spacer(modifier = Modifier.height(16.dp)) | ||||
|                 Text( | ||||
|                     "Глобальные данные", | ||||
|                     modifier = Modifier | ||||
|                         .align(alignment = Alignment.CenterHorizontally), | ||||
|                     fontSize = 24.sp | ||||
|                 ) | ||||
|                 Spacer(modifier = Modifier.height(16.dp)) | ||||
|                 Text("Общая капитализация крипторынка: \n \$ ${globalData.totalMarketCapUsd}\n") | ||||
|                 Text("Всего тикеров: \n ${globalData.activeCryptocurrencies}\n") | ||||
|                 Text("Суточный оборот всех криптовалют: \n \$ ${globalData.total24hVolumeUsd}\n") | ||||
|                 Text("Процент доминации Биткоина: \n ${formatter.format(globalData.bitcoinPercentageOfMarketCap.toFloat())}%\n") | ||||
|             } | ||||
|         } | ||||
|         setContentView(R.layout.fragment_container_main) | ||||
|  | ||||
|         // start the root fragment | ||||
|         App.INSTANCE.router.newRootScreen(Screens.Crypto) | ||||
|     } | ||||
|  | ||||
|     override fun onResumeFragments() { | ||||
|         super.onResumeFragments() | ||||
|         App.INSTANCE.navigatorHolder.setNavigator(navigator) | ||||
|     } | ||||
|  | ||||
|     override fun onPause() { | ||||
|         App.INSTANCE.navigatorHolder.removeNavigator() | ||||
|         super.onPause() | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,72 @@ | ||||
| package ru.vendetti.bitcoin_summarizer | ||||
|  | ||||
| import androidx.compose.foundation.layout.padding | ||||
| import androidx.compose.material3.HorizontalDivider | ||||
| import androidx.compose.material3.MaterialTheme | ||||
| import androidx.compose.material3.ModalDrawerSheet | ||||
| import androidx.compose.material3.NavigationDrawerItem | ||||
| import androidx.compose.material3.NavigationDrawerItemColors | ||||
| import androidx.compose.material3.NavigationDrawerItemDefaults | ||||
| import androidx.compose.material3.Text | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.unit.dp | ||||
| import androidx.compose.ui.unit.sp | ||||
| import ru.vendetti.bitcoin_summarizer.ui.theme.BitcoinSummarizerTheme | ||||
|  | ||||
| @Composable | ||||
| fun MyHamburgerModal( | ||||
|     screenKey: ScreenKey | ||||
| ) { | ||||
|     ModalDrawerSheet( | ||||
|         drawerContainerColor = MaterialTheme.colorScheme.primary, | ||||
|         drawerContentColor = MaterialTheme.colorScheme.onPrimary, | ||||
|     ) { | ||||
|         Text("Bitcoin summarizer", | ||||
|             modifier = Modifier.padding(16.dp), | ||||
|             fontSize = 24.sp) | ||||
|         HorizontalDivider() | ||||
|         NavigationDrawerItem( | ||||
|             colors = NavigationDrawerItemDefaults.colors( | ||||
|                 selectedContainerColor = MaterialTheme.colorScheme.tertiary, | ||||
|                 selectedTextColor = MaterialTheme.colorScheme.onTertiary, | ||||
|                 unselectedTextColor = MaterialTheme.colorScheme.onPrimary, | ||||
|             ), | ||||
|             label = { Text("Crypto statistics") }, | ||||
|             selected = (screenKey == ScreenKey.Crypto), | ||||
|             onClick = { | ||||
|                 if (screenKey != ScreenKey.Crypto) { | ||||
|                     App.INSTANCE.router.replaceScreen(Screens.Crypto) | ||||
|                 } | ||||
|             } | ||||
|         ) | ||||
|         NavigationDrawerItem( | ||||
|             colors = NavigationDrawerItemDefaults.colors( | ||||
|                 selectedContainerColor = MaterialTheme.colorScheme.tertiary, | ||||
|                 selectedTextColor = MaterialTheme.colorScheme.onTertiary, | ||||
|                 unselectedTextColor = MaterialTheme.colorScheme.onPrimary, | ||||
|             ), | ||||
|             label = { Text("About") }, | ||||
|             selected = (screenKey == ScreenKey.About), | ||||
|             onClick = { | ||||
|                 if (screenKey != ScreenKey.About) { | ||||
|                     App.INSTANCE.router.replaceScreen(Screens.About) | ||||
|                 } | ||||
|             } | ||||
|         ) | ||||
|         NavigationDrawerItem( | ||||
|             colors = NavigationDrawerItemDefaults.colors( | ||||
|                 selectedContainerColor = MaterialTheme.colorScheme.tertiary, | ||||
|                 selectedTextColor = MaterialTheme.colorScheme.onTertiary, | ||||
|                 unselectedTextColor = MaterialTheme.colorScheme.onPrimary, | ||||
|             ), | ||||
|             label = { Text("Learn") }, | ||||
|             selected = (screenKey == ScreenKey.Learn), | ||||
|             onClick = { | ||||
|                 if (screenKey != ScreenKey.Learn) { | ||||
|                     App.INSTANCE.router.replaceScreen(Screens.Learn) | ||||
|                 } | ||||
|             } | ||||
|         ) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										23
									
								
								app/src/main/java/ru/vendetti/bitcoin_summarizer/Screens.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								app/src/main/java/ru/vendetti/bitcoin_summarizer/Screens.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| package ru.vendetti.bitcoin_summarizer | ||||
|  | ||||
| import androidx.fragment.app.Fragment | ||||
| import androidx.fragment.app.FragmentFactory | ||||
| import com.github.terrakok.cicerone.androidx.FragmentScreen | ||||
|  | ||||
| class Screens { | ||||
|     object Crypto: FragmentScreen { | ||||
|         override fun createFragment(factory: FragmentFactory) = CryptoFragment() | ||||
|     } | ||||
|     object About: FragmentScreen { | ||||
|         override fun createFragment(factory: FragmentFactory) = AboutFragment() | ||||
|     } | ||||
|     object Learn: FragmentScreen { | ||||
|         override fun createFragment(factory: FragmentFactory) = LearnFragment() | ||||
|     } | ||||
| } | ||||
|  | ||||
| enum class ScreenKey { | ||||
|     Crypto, | ||||
|     About, | ||||
|     Learn | ||||
| } | ||||
| @@ -15,14 +15,14 @@ import androidx.compose.ui.platform.LocalContext | ||||
| private val DarkColorScheme = darkColorScheme( | ||||
|     primary = RaisinBlack, | ||||
|     secondary = DarkPurple, | ||||
|     tertiary = EnglishViolet, | ||||
|     tertiary = HunyadiYellow, | ||||
|  | ||||
|     // Other default colors to override | ||||
|     background = DarkPurple, | ||||
|     surface = Color(0xFFFFFBFE), | ||||
|     onPrimary = HunyadiYellow, | ||||
|     onSecondary = HunyadiYellow, | ||||
|     onTertiary = HunyadiYellow, | ||||
|     onTertiary = DarkPurple, | ||||
|     onBackground = HunyadiYellow, | ||||
|     onSurface = Color(0xFF1C1B1F), | ||||
| ) | ||||
|   | ||||
							
								
								
									
										10
									
								
								app/src/main/res/layout/fragment_compose_view.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								app/src/main/res/layout/fragment_compose_view.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:orientation="vertical" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="match_parent"> | ||||
|     <androidx.compose.ui.platform.ComposeView | ||||
|         android:id="@+id/compose_view" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="match_parent" /> | ||||
| </LinearLayout> | ||||
							
								
								
									
										10
									
								
								app/src/main/res/layout/fragment_container_main.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								app/src/main/res/layout/fragment_container_main.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:orientation="vertical" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="match_parent"> | ||||
|     <FrameLayout | ||||
|         android:id="@+id/container" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="match_parent"/> | ||||
| </LinearLayout> | ||||
							
								
								
									
										4
									
								
								app/src/main/res/values/ids.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								app/src/main/res/values/ids.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <resources> | ||||
|     <item name="container" type="id" /> | ||||
| </resources> | ||||
		Reference in New Issue
	
	Block a user
	 b3s23
					b3s23