Merge pull request 'final tweaks' (#28) from dev into main
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				Gitea Android Builder / Build (push) Successful in 3m44s
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	Gitea Android Builder / Build (push) Successful in 3m44s
				
			Reviewed-on: #28
This commit is contained in:
		| @@ -1,3 +1,4 @@ | |||||||
|  |  | ||||||
| plugins { | plugins { | ||||||
|     alias(libs.plugins.android.application) |     alias(libs.plugins.android.application) | ||||||
|     alias(libs.plugins.kotlin.android) |     alias(libs.plugins.kotlin.android) | ||||||
| @@ -64,5 +65,6 @@ dependencies { | |||||||
|     implementation(libs.kotlinx.coroutines.core) |     implementation(libs.kotlinx.coroutines.core) | ||||||
|     implementation(libs.vico.compose.m3) |     implementation(libs.vico.compose.m3) | ||||||
|     implementation(libs.cicerone) |     implementation(libs.cicerone) | ||||||
|  |     implementation(libs.androidx.datastore) | ||||||
|  |  | ||||||
| } | } | ||||||
| @@ -1,21 +1,33 @@ | |||||||
| package ru.vendetti.bitcoin_summarizer | package ru.vendetti.bitcoin_summarizer | ||||||
|  |  | ||||||
| import android.annotation.SuppressLint | import android.annotation.SuppressLint | ||||||
|  | import android.content.Context | ||||||
|  | import android.content.pm.PackageManager | ||||||
| import android.os.Bundle | import android.os.Bundle | ||||||
|  | import android.util.Log | ||||||
| import android.view.LayoutInflater | import android.view.LayoutInflater | ||||||
| import android.view.View | import android.view.View | ||||||
| import android.view.ViewGroup | import android.view.ViewGroup | ||||||
|  | import androidx.activity.compose.rememberLauncherForActivityResult | ||||||
|  | import androidx.activity.result.contract.ActivityResultContracts | ||||||
| import androidx.compose.foundation.background | import androidx.compose.foundation.background | ||||||
| import androidx.compose.foundation.layout.Box | import androidx.compose.foundation.layout.Box | ||||||
| import androidx.compose.foundation.layout.Column | import androidx.compose.foundation.layout.Column | ||||||
| import androidx.compose.foundation.layout.Spacer | import androidx.compose.foundation.layout.Spacer | ||||||
|  | import androidx.compose.foundation.layout.fillMaxSize | ||||||
|  | import androidx.compose.foundation.layout.fillMaxWidth | ||||||
| import androidx.compose.foundation.layout.height | import androidx.compose.foundation.layout.height | ||||||
| import androidx.compose.foundation.layout.padding | import androidx.compose.foundation.layout.padding | ||||||
|  | import androidx.compose.foundation.layout.wrapContentSize | ||||||
| import androidx.compose.foundation.rememberScrollState | import androidx.compose.foundation.rememberScrollState | ||||||
|  | import androidx.compose.foundation.shape.RoundedCornerShape | ||||||
| import androidx.compose.foundation.verticalScroll | import androidx.compose.foundation.verticalScroll | ||||||
| import androidx.compose.material.icons.Icons | import androidx.compose.material.icons.Icons | ||||||
| import androidx.compose.material.icons.filled.Menu | import androidx.compose.material.icons.filled.Menu | ||||||
| import androidx.compose.material.icons.filled.MoreVert | import androidx.compose.material3.AlertDialog | ||||||
|  | import androidx.compose.material3.ButtonColors | ||||||
|  | import androidx.compose.material3.ButtonDefaults | ||||||
|  | import androidx.compose.material3.Card | ||||||
| import androidx.compose.material3.CenterAlignedTopAppBar | import androidx.compose.material3.CenterAlignedTopAppBar | ||||||
| import androidx.compose.material3.DrawerValue | import androidx.compose.material3.DrawerValue | ||||||
| import androidx.compose.material3.ExperimentalMaterial3Api | import androidx.compose.material3.ExperimentalMaterial3Api | ||||||
| @@ -23,11 +35,10 @@ import androidx.compose.material3.HorizontalDivider | |||||||
| import androidx.compose.material3.Icon | import androidx.compose.material3.Icon | ||||||
| import androidx.compose.material3.IconButton | import androidx.compose.material3.IconButton | ||||||
| import androidx.compose.material3.MaterialTheme | import androidx.compose.material3.MaterialTheme | ||||||
| import androidx.compose.material3.MediumTopAppBar |  | ||||||
| import androidx.compose.material3.ModalDrawerSheet |  | ||||||
| import androidx.compose.material3.ModalNavigationDrawer | import androidx.compose.material3.ModalNavigationDrawer | ||||||
| import androidx.compose.material3.Scaffold | import androidx.compose.material3.Scaffold | ||||||
| import androidx.compose.material3.Text | import androidx.compose.material3.Text | ||||||
|  | import androidx.compose.material3.TextButton | ||||||
| import androidx.compose.material3.TopAppBarDefaults | import androidx.compose.material3.TopAppBarDefaults | ||||||
| import androidx.compose.material3.rememberDrawerState | import androidx.compose.material3.rememberDrawerState | ||||||
| import androidx.compose.material3.rememberTopAppBarState | import androidx.compose.material3.rememberTopAppBarState | ||||||
| @@ -42,13 +53,19 @@ import androidx.compose.runtime.setValue | |||||||
| import androidx.compose.ui.Alignment | import androidx.compose.ui.Alignment | ||||||
| import androidx.compose.ui.Modifier | import androidx.compose.ui.Modifier | ||||||
| import androidx.compose.ui.graphics.Color | import androidx.compose.ui.graphics.Color | ||||||
|  | import androidx.compose.ui.graphics.vector.ImageVector | ||||||
| import androidx.compose.ui.input.nestedscroll.nestedScroll | import androidx.compose.ui.input.nestedscroll.nestedScroll | ||||||
| import androidx.compose.ui.platform.ComposeView | import androidx.compose.ui.platform.ComposeView | ||||||
|  | import androidx.compose.ui.platform.LocalContext | ||||||
|  | import androidx.compose.ui.text.style.TextAlign | ||||||
| import androidx.compose.ui.text.style.TextOverflow | import androidx.compose.ui.text.style.TextOverflow | ||||||
| import androidx.compose.ui.tooling.preview.Preview | import androidx.compose.ui.tooling.preview.Preview | ||||||
| import androidx.compose.ui.unit.dp | import androidx.compose.ui.unit.dp | ||||||
| import androidx.compose.ui.unit.sp | import androidx.compose.ui.unit.sp | ||||||
|  | import androidx.compose.ui.window.Dialog | ||||||
|  | import androidx.core.content.ContextCompat | ||||||
| import androidx.fragment.app.Fragment | import androidx.fragment.app.Fragment | ||||||
|  | import com.google.gson.Gson | ||||||
| import com.patrykandpatrick.vico.compose.cartesian.CartesianChartHost | import com.patrykandpatrick.vico.compose.cartesian.CartesianChartHost | ||||||
| import com.patrykandpatrick.vico.compose.cartesian.axis.rememberAxisLabelComponent | import com.patrykandpatrick.vico.compose.cartesian.axis.rememberAxisLabelComponent | ||||||
| import com.patrykandpatrick.vico.compose.cartesian.axis.rememberAxisLineComponent | import com.patrykandpatrick.vico.compose.cartesian.axis.rememberAxisLineComponent | ||||||
| @@ -80,6 +97,9 @@ import kotlinx.coroutines.launch | |||||||
| import ru.vendetti.bitcoin_summarizer.ui.theme.BitcoinSummarizerTheme | import ru.vendetti.bitcoin_summarizer.ui.theme.BitcoinSummarizerTheme | ||||||
| import ru.vendetti.bitcoin_summarizer.ui.theme.Flame | import ru.vendetti.bitcoin_summarizer.ui.theme.Flame | ||||||
| import ru.vendetti.bitcoin_summarizer.ui.theme.Green2 | import ru.vendetti.bitcoin_summarizer.ui.theme.Green2 | ||||||
|  | import java.io.FileInputStream | ||||||
|  | import java.io.FileOutputStream | ||||||
|  | import java.lang.StringBuilder | ||||||
| import java.text.DecimalFormat | import java.text.DecimalFormat | ||||||
| import java.time.Instant | import java.time.Instant | ||||||
| import java.time.format.DateTimeFormatter | import java.time.format.DateTimeFormatter | ||||||
| @@ -102,11 +122,51 @@ class CryptoFragment: Fragment() { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @Composable | ||||||
|  | fun ShowAlertDialog( | ||||||
|  |     onDismissRequest: () -> Unit, | ||||||
|  |     onConfirmation: () -> Unit, | ||||||
|  |     dialogTitle: String, | ||||||
|  |     dialogText: String, | ||||||
|  | ) { | ||||||
|  |     AlertDialog( | ||||||
|  |         icon = {}, | ||||||
|  |         title = { | ||||||
|  |             Text(text = dialogTitle) | ||||||
|  |         }, | ||||||
|  |         text = { | ||||||
|  |             Text(text = dialogText) | ||||||
|  |         }, | ||||||
|  |         onDismissRequest = { | ||||||
|  |             onDismissRequest() | ||||||
|  |         }, | ||||||
|  |         confirmButton = {}, | ||||||
|  |         dismissButton = { | ||||||
|  |             TextButton( | ||||||
|  |                 onClick = { | ||||||
|  |                     onDismissRequest() | ||||||
|  |                 } | ||||||
|  |             ) { | ||||||
|  |                 Text( | ||||||
|  |                     text = "Окей", | ||||||
|  |                     color = MaterialTheme.colorScheme.onPrimary | ||||||
|  |                 ) | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         containerColor = MaterialTheme.colorScheme.primary, | ||||||
|  |         titleContentColor = MaterialTheme.colorScheme.onPrimary, | ||||||
|  |         textContentColor = MaterialTheme.colorScheme.onPrimary, | ||||||
|  |     ) | ||||||
|  | } | ||||||
|  |  | ||||||
| @SuppressLint("MutableCollectionMutableState", "SimpleDateFormat") | @SuppressLint("MutableCollectionMutableState", "SimpleDateFormat") | ||||||
| @OptIn(ExperimentalMaterial3Api::class) | @OptIn(ExperimentalMaterial3Api::class) | ||||||
| @Preview | @Preview | ||||||
| @Composable | @Composable | ||||||
| fun CryptoComposable() { | fun CryptoComposable() { | ||||||
|  |     val openAlertDialog1 = remember { mutableStateOf(false) } | ||||||
|  |     val openAlertDialog2 = remember { mutableStateOf(false) } | ||||||
|  |  | ||||||
|     // Создаем репозиторий для работы с API |     // Создаем репозиторий для работы с API | ||||||
|     val cryptoRepository = remember { CryptoRepository(RetrofitClient.apiService) } |     val cryptoRepository = remember { CryptoRepository(RetrofitClient.apiService) } | ||||||
|     // Состояния для хранения результатов запросов |     // Состояния для хранения результатов запросов | ||||||
| @@ -116,8 +176,36 @@ fun CryptoComposable() { | |||||||
|  |  | ||||||
|     var fearGreedIndexDaysCount by remember { mutableIntStateOf(30) } |     var fearGreedIndexDaysCount by remember { mutableIntStateOf(30) } | ||||||
|  |  | ||||||
|  |     val gson = Gson() | ||||||
|  |  | ||||||
|  |     val context = LocalContext.current | ||||||
|  |  | ||||||
|  |     when { | ||||||
|  |         openAlertDialog1.value -> { | ||||||
|  |             ShowAlertDialog( | ||||||
|  |                 onDismissRequest = {openAlertDialog1.value = false}, | ||||||
|  |                 onConfirmation = {}, | ||||||
|  |                 dialogTitle = "Внимание!", | ||||||
|  |                 dialogText = "Отсутствует интернет-подключение, загружаю сохраненные данные. " + | ||||||
|  |                         "" + | ||||||
|  |                         "Обратите внимание, что данные могут быть неактуальными.", | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |         openAlertDialog2.value -> { | ||||||
|  |             ShowAlertDialog( | ||||||
|  |                 onDismissRequest = { | ||||||
|  |                     openAlertDialog2.value = false | ||||||
|  |                     App.INSTANCE.router.exit() | ||||||
|  |                                    }, | ||||||
|  |                 onConfirmation = {}, | ||||||
|  |                 dialogTitle = "Внимание!", | ||||||
|  |                 dialogText = "При первом подключении необходимо подключение к интернету!", | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // Запускаем корутину для выполнения сетевых запросов |     // Запускаем корутину для выполнения сетевых запросов | ||||||
|     LaunchedEffect(fearGreedIndexDaysCount) { |     LaunchedEffect(fearGreedIndexDaysCount, context) { | ||||||
|         try { |         try { | ||||||
|             // Запрос Bitcoin Ticker |             // Запрос Bitcoin Ticker | ||||||
|             val tickerResponse = cryptoRepository.fetchBitcoinTicker() |             val tickerResponse = cryptoRepository.fetchBitcoinTicker() | ||||||
| @@ -131,31 +219,53 @@ fun CryptoComposable() { | |||||||
|             val fearResponse = cryptoRepository.fetchFearAndGreedData(fearGreedIndexDaysCount) |             val fearResponse = cryptoRepository.fetchFearAndGreedData(fearGreedIndexDaysCount) | ||||||
|             fearGreedDataList = fearResponse?.dataList as ArrayList<FearAndGreedData> |             fearGreedDataList = fearResponse?.dataList as ArrayList<FearAndGreedData> | ||||||
|             fearGreedDataList.reverse() |             fearGreedDataList.reverse() | ||||||
|         }catch (e: Exception) { |  | ||||||
|             bitcoinTicker = TickerData( |             // *** Сохраняем на диск *** | ||||||
|                 id = "", |             // 1) Сбор данных в один класс | ||||||
|                 name = "", |             val fullStatistics = StatisticsFull( | ||||||
|                 symbol = "", |                 FGI_list = fearGreedDataList, | ||||||
|                 rank = "", |                 TickerData = bitcoinTicker, | ||||||
|                 priceUsd = "", |                 GlobalData = globalData, | ||||||
|                 priceBtc = "", |  | ||||||
|                 volume24hUsd = "", |  | ||||||
|                 marketCapUsd = "", |  | ||||||
|                 availableSupply = "", |  | ||||||
|                 totalSupply = "", |  | ||||||
|                 maxSupply = "", |  | ||||||
|                 percentChange1h = "", |  | ||||||
|                 percentChange24h = "", |  | ||||||
|                 percentChange7d = "", |  | ||||||
|                 lastUpdated = "" |  | ||||||
|             ) |             ) | ||||||
|             globalData = GlobalResponse( |  | ||||||
|                 activeCryptocurrencies = "", |             // 2) Перевод класса в json-строку | ||||||
|                 totalMarketCapUsd = "", |             val statisticsJsonString = gson.toJson(fullStatistics) | ||||||
|                 total24hVolumeUsd = "", |  | ||||||
|                 bitcoinPercentageOfMarketCap = "" |             // 3) Сохранение в файл во внутреннем хранилище | ||||||
|             ) |             val fos: FileOutputStream = | ||||||
|             fearGreedDataList = ArrayList<FearAndGreedData>() |                 context.openFileOutput("statistics.txt", Context.MODE_PRIVATE) | ||||||
|  |             fos.write(statisticsJsonString.toByteArray()) | ||||||
|  |             fos.flush() | ||||||
|  |             fos.close() | ||||||
|  |         } | ||||||
|  |         catch (e: Exception) { | ||||||
|  |             try { | ||||||
|  |                 // *** Загрузка данных из файла *** | ||||||
|  |                 // 1) Получение строки из файла | ||||||
|  |                 val fis: FileInputStream = | ||||||
|  |                     context.openFileInput("statistics.txt") | ||||||
|  |                 var a: Int | ||||||
|  |                 val temp = StringBuilder() | ||||||
|  |                 while (fis.read().also { a = it } != -1) { | ||||||
|  |                     temp.append(a.toChar()) | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 // Показ предупреждения. | ||||||
|  |                 openAlertDialog1.value = true | ||||||
|  |  | ||||||
|  |                 val statisticsJsonString = temp.toString() | ||||||
|  |  | ||||||
|  |                 // 2) Перевод строки в класс | ||||||
|  |                 val statisticsFull = gson.fromJson(statisticsJsonString, StatisticsFull::class.java) | ||||||
|  |  | ||||||
|  |                 // 3) Распределение данных по переменным | ||||||
|  |                 bitcoinTicker = statisticsFull.TickerData | ||||||
|  |                 globalData = statisticsFull.GlobalData | ||||||
|  |                 fearGreedDataList = statisticsFull.FGI_list | ||||||
|  |             } | ||||||
|  |             catch (e: Exception) { | ||||||
|  |                 openAlertDialog2.value = true | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -0,0 +1,7 @@ | |||||||
|  | package ru.vendetti.bitcoin_summarizer | ||||||
|  |  | ||||||
|  | class StatisticsFull ( | ||||||
|  |     val FGI_list: ArrayList<FearAndGreedData>, | ||||||
|  |     val TickerData: TickerData, | ||||||
|  |     val GlobalData: GlobalResponse, | ||||||
|  | ) | ||||||
| @@ -15,6 +15,7 @@ retrofit = "2.9.0" | |||||||
| vico = "2.0.2" | vico = "2.0.2" | ||||||
| ciceroneVer = "7.1" | ciceroneVer = "7.1" | ||||||
| fragmentKtx = "1.8.6" | fragmentKtx = "1.8.6" | ||||||
|  | protoDataStore = "1.1.3" | ||||||
|  |  | ||||||
| [libraries] | [libraries] | ||||||
| androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } | androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } | ||||||
| @@ -38,6 +39,7 @@ retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit | |||||||
| vico-compose-m3 = { group = "com.patrykandpatrick.vico", name = "compose-m3", version.ref = "vico" } | vico-compose-m3 = { group = "com.patrykandpatrick.vico", name = "compose-m3", version.ref = "vico" } | ||||||
| cicerone = {module = "com.github.terrakok:cicerone", version.ref = "ciceroneVer"} | cicerone = {module = "com.github.terrakok:cicerone", version.ref = "ciceroneVer"} | ||||||
| androidx-fragment-ktx = { group = "androidx.fragment", name = "fragment-ktx", version.ref = "fragmentKtx" } | androidx-fragment-ktx = { group = "androidx.fragment", name = "fragment-ktx", version.ref = "fragmentKtx" } | ||||||
|  | androidx-datastore = {group = "androidx.datastore", name = "datastore", version.ref = "protoDataStore"} | ||||||
|  |  | ||||||
| [plugins] | [plugins] | ||||||
| android-application = { id = "com.android.application", version.ref = "agp" } | android-application = { id = "com.android.application", version.ref = "agp" } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user