dev #16
| @@ -61,5 +61,6 @@ dependencies { | ||||
|     implementation(libs.converter.gson) | ||||
|     implementation(libs.kotlinx.coroutines.android) | ||||
|     implementation(libs.kotlinx.coroutines.core) | ||||
|     implementation(libs.vico.compose.m3) | ||||
|  | ||||
| } | ||||
| @@ -4,8 +4,8 @@ import com.google.gson.annotations.SerializedName | ||||
|  | ||||
| // Данные | ||||
| data class GlobalResponse( | ||||
|     @SerializedName("active_currencies") val activeCryptocurrencies: String, | ||||
|     @SerializedName("total_market_cap_usd") val totalMarketCapUsd: String, | ||||
|     @SerializedName("total_24h_volume_usd") val total24hVolumeUsd: String, | ||||
|     @SerializedName("bitcoin_percentage_of_market_cap") val bitcoinPercentageOfMarketCap: String | ||||
|     @SerializedName("active_currencies") val activeCryptocurrencies: String = "", | ||||
|     @SerializedName("total_market_cap_usd") val totalMarketCapUsd: String = "", | ||||
|     @SerializedName("total_24h_volume_usd") val total24hVolumeUsd: String = "", | ||||
|     @SerializedName("bitcoin_percentage_of_market_cap") val bitcoinPercentageOfMarketCap: String = "" | ||||
| ) | ||||
| @@ -1,26 +1,69 @@ | ||||
| package ru.vendetti.bitcoin_summarizer | ||||
|  | ||||
| import android.annotation.SuppressLint | ||||
| import android.os.Bundle | ||||
| import androidx.activity.ComponentActivity | ||||
| import androidx.activity.compose.setContent | ||||
| import androidx.compose.foundation.layout.Box | ||||
| import androidx.compose.foundation.layout.Column | ||||
| import androidx.compose.foundation.layout.Spacer | ||||
| import androidx.compose.foundation.layout.PaddingValues | ||||
| import androidx.compose.foundation.layout.fillMaxSize | ||||
| import androidx.compose.foundation.layout.height | ||||
| import androidx.compose.foundation.layout.padding | ||||
| import androidx.compose.foundation.magnifier | ||||
| 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.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.Scaffold | ||||
| import androidx.compose.material3.Text | ||||
| import androidx.compose.material3.TopAppBarDefaults | ||||
| 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.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.text.style.TextOverflow | ||||
| import androidx.compose.ui.tooling.preview.Preview | ||||
| import androidx.compose.ui.unit.TextUnit | ||||
| import androidx.compose.ui.unit.dp | ||||
| import androidx.compose.ui.unit.sp | ||||
| import com.patrykandpatrick.vico.compose.cartesian.CartesianChartHost | ||||
| import com.patrykandpatrick.vico.compose.cartesian.axis.rememberBottom | ||||
| import com.patrykandpatrick.vico.compose.cartesian.axis.rememberStart | ||||
| import com.patrykandpatrick.vico.compose.cartesian.layer.rememberLineCartesianLayer | ||||
| import com.patrykandpatrick.vico.compose.cartesian.rememberCartesianChart | ||||
| 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.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.lineSeries | ||||
| import com.patrykandpatrick.vico.core.cartesian.decoration.HorizontalLine | ||||
| 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 ru.vendetti.bitcoin_summarizer.ui.theme.BitcoinSummarizerTheme | ||||
| import java.text.DateFormat | ||||
| import java.time.format.DateTimeFormatter | ||||
|  | ||||
| class MainActivity : ComponentActivity() { | ||||
|     override fun onCreate(savedInstanceState: Bundle?) { | ||||
| @@ -33,53 +76,181 @@ class MainActivity : ComponentActivity() { | ||||
|     } | ||||
| } | ||||
|  | ||||
| @SuppressLint("MutableCollectionMutableState") | ||||
| @OptIn(ExperimentalMaterial3Api::class) | ||||
| @Preview | ||||
| @Composable | ||||
| fun CryptoScreen() { | ||||
|     // Создаем репозиторий для работы с API | ||||
|     val cryptoRepository = remember { CryptoRepository(RetrofitClient.apiService) } | ||||
|  | ||||
|     // Состояния для хранения результатов запросов | ||||
|     var bitcoinTicker by remember { mutableStateOf("Загрузка Bitcoin Ticker...") } | ||||
|     var globalData by remember { mutableStateOf("Загрузка глобальных данных...") } | ||||
|     var fearGreedData by remember { mutableStateOf("Загрузка индекса страха и жадности...") } | ||||
|     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(Unit) { | ||||
|     LaunchedEffect(fearGreedIndexDaysCount) { | ||||
|         // Запрос Bitcoin Ticker | ||||
|         val tickerResponse = cryptoRepository.fetchBitcoinTicker() | ||||
|         bitcoinTicker = tickerResponse?.joinToString(separator = "\n") { data -> | ||||
|             "Название: ${data.name}, Цена: ${data.priceUsd}, Обновлено: ${data.lastUpdated}" | ||||
|         } ?: "Ошибка загрузки Bitcoin Ticker" | ||||
|         bitcoinTicker = tickerResponse!!.first() | ||||
|  | ||||
|         // Запрос глобальных данных | ||||
|         val globalResponse = cryptoRepository.fetchGlobalData() | ||||
|         globalData = globalResponse?.let { data -> | ||||
|             "Активных криптовалют: ${data.activeCryptocurrencies}\nРыночная капитализация: ${data.totalMarketCapUsd}\n24h Объем: ${data.total24hVolumeUsd}" | ||||
|         } ?: "Ошибка загрузки глобальных данных" | ||||
|         globalData = globalResponse!! | ||||
|  | ||||
|         // Запрос индекса страха и жадности | ||||
|         val fearResponse = cryptoRepository.fetchFearAndGreedData(30) | ||||
|         fearGreedData = fearResponse?.dataList?.joinToString(separator = "\n") { data -> | ||||
|             "Время: ${data.timestamp}, Значение: ${data.value}, Классификация: ${data.valueClassification}" | ||||
|         } ?: "Ошибка загрузки данных страха и жадности" | ||||
|         val fearResponse = cryptoRepository.fetchFearAndGreedData(fearGreedIndexDaysCount) | ||||
|         fearGreedDataList = fearResponse?.dataList as ArrayList<FearAndGreedData> | ||||
|     } | ||||
|  | ||||
|     // Отображаем результаты в виде простой страницы | ||||
|     Column( | ||||
|     val modelProducer = remember { CartesianChartModelProducer() } | ||||
|  | ||||
|     LaunchedEffect(fearGreedDataList) { | ||||
|         modelProducer.runTransaction { | ||||
|             var numberValues = Array<Int>(fearGreedDataList.count()) { index -> fearGreedDataList[index].value.toInt() } | ||||
|             if(numberValues.isEmpty()) | ||||
|                 numberValues = Array<Int>(1) {0} | ||||
|             lineSeries { series(numberValues.toList()) } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(rememberTopAppBarState()) | ||||
|     val zoomState = rememberVicoZoomState(initialZoom = Zoom.x(fearGreedIndexDaysCount.toDouble())) | ||||
|     // Отображаем результаты на странице | ||||
|     Scaffold ( | ||||
|         modifier = Modifier | ||||
|             .fillMaxSize() | ||||
|             .padding(16.dp) | ||||
|             .verticalScroll(rememberScrollState()) | ||||
|     ) { | ||||
|         Text(text = "Bitcoin Ticker Data", style = MaterialTheme.typography.bodyMedium) | ||||
|         Text(text = bitcoinTicker) | ||||
|         Spacer(modifier = Modifier.height(16.dp)) | ||||
|             .nestedScroll(scrollBehavior.nestedScrollConnection), | ||||
|  | ||||
|         Text(text = "Global Data", style = MaterialTheme.typography.bodyMedium) | ||||
|         Text(text = globalData) | ||||
|         Spacer(modifier = Modifier.height(16.dp)) | ||||
|         topBar = { | ||||
|             MediumTopAppBar( | ||||
|                 colors = TopAppBarDefaults.topAppBarColors( | ||||
|                     containerColor = MaterialTheme.colorScheme.primaryContainer, | ||||
|                     titleContentColor = MaterialTheme.colorScheme.primary, | ||||
|                 ), | ||||
|                 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 | ||||
|                 .fillMaxSize() | ||||
|                 .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(), | ||||
|                         startAxis = VerticalAxis.rememberStart( | ||||
|                             titleComponent = rememberTextComponent(), | ||||
|                             title = "FGI" | ||||
|                         ), | ||||
|                         bottomAxis = HorizontalAxis.rememberBottom( | ||||
|                             titleComponent = rememberTextComponent(), | ||||
|                             title = "last $fearGreedIndexDaysCount days" | ||||
|                         ), | ||||
|                         decorations = listOf( | ||||
|                             remember { | ||||
|                                 HorizontalLine( | ||||
|                                     y = { 15.toDouble() }, | ||||
|                                     line = LineComponent(fill(Color.Red), 2f), | ||||
|                                     labelComponent = TextComponent( | ||||
|                                         background = | ||||
|                                         shapeComponent( | ||||
|                                             fill(Color.Red), | ||||
|                                             CorneredShape.rounded( | ||||
|                                                 bottomLeft = 4.dp, | ||||
|                                                 bottomRight = 4.dp | ||||
|                                             ) | ||||
|                                         ), | ||||
|                                     ), | ||||
|                                     label = { "Fear" }, | ||||
|                                     verticalLabelPosition = Position.Vertical.Top | ||||
|                                 ) | ||||
|                             }, | ||||
|                             remember { | ||||
|                                 HorizontalLine( | ||||
|                                     y = { 60.toDouble() }, | ||||
|                                     line = LineComponent(fill(Color.Green), 2f), | ||||
|                                     labelComponent = TextComponent( | ||||
|                                         background = | ||||
|                                         shapeComponent( | ||||
|                                             fill(Color.Green), | ||||
|                                             CorneredShape.rounded( | ||||
|                                                 bottomLeft = 4.dp, | ||||
|                                                 bottomRight = 4.dp | ||||
|                                             ) | ||||
|                                         ), | ||||
|                                     ), | ||||
|                                     label = { "Greed" }, | ||||
|                                     verticalLabelPosition = Position.Vertical.Bottom | ||||
|                                 ) | ||||
|                             } | ||||
|                         ), | ||||
|                     ), | ||||
|                     modelProducer = modelProducer, | ||||
|                 ) | ||||
|                 /* Fear Greed Chart End */ | ||||
|                 HorizontalDivider(thickness = 2.dp) | ||||
|                 Text( | ||||
|                     "Данные о Биткойне", | ||||
|                     modifier = Modifier | ||||
|                         .align(alignment = Alignment.CenterHorizontally), | ||||
|                     fontSize = 24.sp | ||||
|                 ) | ||||
|                 Text("Текущая цена: \n ${bitcoinTicker.priceUsd}\n") | ||||
|                 Text("Суточный оборот: \n ${bitcoinTicker.volume24hUsd}\n") | ||||
|                 Text("Капитализация: \n ${bitcoinTicker.marketCapUsd}\n") | ||||
|                 Text( | ||||
|                     "Изменение курса за: " + | ||||
|                             "\n Сутки: ${bitcoinTicker.percentChange24h} " + | ||||
|                             "\n Неделю: ${bitcoinTicker.percentChange7d}\n" | ||||
|                 ) | ||||
|                 Text("Дата последнего обновления: \n ${bitcoinTicker.lastUpdated}\n") | ||||
|  | ||||
|         Text(text = "Fear & Greed Index", style = MaterialTheme.typography.bodyMedium) | ||||
|         Text(text = fearGreedData) | ||||
|                 HorizontalDivider(thickness = 2.dp) | ||||
|                 Text( | ||||
|                     "Глобальные данные", | ||||
|                     modifier = Modifier | ||||
|                         .align(alignment = Alignment.CenterHorizontally), | ||||
|                     fontSize = 24.sp | ||||
|                 ) | ||||
|                 Text("Общая капитализация крипторынка: \n ${globalData.totalMarketCapUsd}\n") | ||||
|                 Text("Всего видов криптовалют: \n ${globalData.activeCryptocurrencies}\n") | ||||
|                 Text("Суточный оборот других криптовалют: \n ${globalData.total24hVolumeUsd}\n") | ||||
|                 Text("Процент доминации Биткоина: \n ${globalData.bitcoinPercentageOfMarketCap}\n") | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -7,19 +7,19 @@ typealias TickerResponse = List<TickerData> | ||||
|  | ||||
| // Сами данные | ||||
| data class TickerData( | ||||
|     val id: String, | ||||
|     val name: String, | ||||
|     val symbol: String, | ||||
|     val rank: String, | ||||
|     @SerializedName("price_usd") val priceUsd: String, | ||||
|     @SerializedName("price_btc") val priceBtc: String, | ||||
|     @SerializedName("24h_volume_usd") val volume24hUsd: String, | ||||
|     @SerializedName("market_cap_usd") val marketCapUsd: String, | ||||
|     @SerializedName("available_supply") val availableSupply: String, | ||||
|     @SerializedName("total_supply") val totalSupply: String, | ||||
|     @SerializedName("max_supply") val maxSupply: String?, | ||||
|     @SerializedName("percent_change_1h") val percentChange1h: String, | ||||
|     @SerializedName("percent_change_24h") val percentChange24h: String, | ||||
|     @SerializedName("percent_change_7d") val percentChange7d: String, | ||||
|     @SerializedName("last_updated") val lastUpdated: String | ||||
|     val id: String = "", | ||||
|     val name: String = "", | ||||
|     val symbol: String = "", | ||||
|     val rank: String = "", | ||||
|     @SerializedName("price_usd") val priceUsd: String = "", | ||||
|     @SerializedName("price_btc") val priceBtc: String = "", | ||||
|     @SerializedName("24h_volume_usd") val volume24hUsd: String = "", | ||||
|     @SerializedName("market_cap_usd") val marketCapUsd: String = "", | ||||
|     @SerializedName("available_supply") val availableSupply: String = "", | ||||
|     @SerializedName("total_supply") val totalSupply: String = "", | ||||
|     @SerializedName("max_supply") val maxSupply: String? = "", | ||||
|     @SerializedName("percent_change_1h") val percentChange1h: String = "", | ||||
|     @SerializedName("percent_change_24h") val percentChange24h: String = "", | ||||
|     @SerializedName("percent_change_7d") val percentChange7d: String = "", | ||||
|     @SerializedName("last_updated") val lastUpdated: String = "" | ||||
| ) | ||||
| @@ -12,6 +12,7 @@ lifecycleRuntimeKtx = "2.8.7" | ||||
| activityCompose = "1.10.1" | ||||
| composeBom = "2025.02.00" | ||||
| retrofit = "2.9.0" | ||||
| vico = "2.0.2" | ||||
|  | ||||
| [libraries] | ||||
| androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } | ||||
| @@ -32,6 +33,7 @@ androidx-material3 = { group = "androidx.compose.material3", name = "material3" | ||||
| kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinxCoroutinesAndroid" } | ||||
| kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutinesCore" } | ||||
| retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" } | ||||
| vico-compose-m3 = { group = "com.patrykandpatrick.vico", name = "compose-m3", version.ref = "vico" } | ||||
|  | ||||
| [plugins] | ||||
| android-application = { id = "com.android.application", version.ref = "agp" } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user