Added saving data to internal storage for later use.
Added dialog on internal storage usage, and for first start without internet connection
This commit is contained in:
		| @@ -1,3 +1,4 @@ | ||||
|  | ||||
| plugins { | ||||
|     alias(libs.plugins.android.application) | ||||
|     alias(libs.plugins.kotlin.android) | ||||
| @@ -64,5 +65,6 @@ dependencies { | ||||
|     implementation(libs.kotlinx.coroutines.core) | ||||
|     implementation(libs.vico.compose.m3) | ||||
|     implementation(libs.cicerone) | ||||
|     implementation(libs.androidx.datastore) | ||||
|  | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -1,21 +1,33 @@ | ||||
| package ru.vendetti.bitcoin_summarizer | ||||
|  | ||||
| import android.annotation.SuppressLint | ||||
| import android.content.Context | ||||
| import android.content.pm.PackageManager | ||||
| import android.os.Bundle | ||||
| import android.util.Log | ||||
| import android.view.LayoutInflater | ||||
| import android.view.View | ||||
| import android.view.ViewGroup | ||||
| import androidx.activity.compose.rememberLauncherForActivityResult | ||||
| import androidx.activity.result.contract.ActivityResultContracts | ||||
| 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.fillMaxSize | ||||
| import androidx.compose.foundation.layout.fillMaxWidth | ||||
| import androidx.compose.foundation.layout.height | ||||
| import androidx.compose.foundation.layout.padding | ||||
| import androidx.compose.foundation.layout.wrapContentSize | ||||
| import androidx.compose.foundation.rememberScrollState | ||||
| import androidx.compose.foundation.shape.RoundedCornerShape | ||||
| 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.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.DrawerValue | ||||
| import androidx.compose.material3.ExperimentalMaterial3Api | ||||
| @@ -23,11 +35,10 @@ 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.TextButton | ||||
| import androidx.compose.material3.TopAppBarDefaults | ||||
| import androidx.compose.material3.rememberDrawerState | ||||
| import androidx.compose.material3.rememberTopAppBarState | ||||
| @@ -42,13 +53,19 @@ 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.graphics.vector.ImageVector | ||||
| import androidx.compose.ui.input.nestedscroll.nestedScroll | ||||
| 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.tooling.preview.Preview | ||||
| import androidx.compose.ui.unit.dp | ||||
| import androidx.compose.ui.unit.sp | ||||
| import androidx.compose.ui.window.Dialog | ||||
| import androidx.core.content.ContextCompat | ||||
| import androidx.fragment.app.Fragment | ||||
| import com.google.gson.Gson | ||||
| import com.patrykandpatrick.vico.compose.cartesian.CartesianChartHost | ||||
| import com.patrykandpatrick.vico.compose.cartesian.axis.rememberAxisLabelComponent | ||||
| 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.Flame | ||||
| 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.time.Instant | ||||
| 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") | ||||
| @OptIn(ExperimentalMaterial3Api::class) | ||||
| @Preview | ||||
| @Composable | ||||
| fun CryptoComposable() { | ||||
|     val openAlertDialog1 = remember { mutableStateOf(false) } | ||||
|     val openAlertDialog2 = remember { mutableStateOf(false) } | ||||
|  | ||||
|     // Создаем репозиторий для работы с API | ||||
|     val cryptoRepository = remember { CryptoRepository(RetrofitClient.apiService) } | ||||
|     // Состояния для хранения результатов запросов | ||||
| @@ -116,8 +176,36 @@ fun CryptoComposable() { | ||||
|  | ||||
|     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 { | ||||
|             // Запрос Bitcoin Ticker | ||||
|             val tickerResponse = cryptoRepository.fetchBitcoinTicker() | ||||
| @@ -131,31 +219,53 @@ fun CryptoComposable() { | ||||
|             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 = "" | ||||
|  | ||||
|             // *** Сохраняем на диск *** | ||||
|             // 1) Сбор данных в один класс | ||||
|             val fullStatistics = StatisticsFull( | ||||
|                 FGI_list = fearGreedDataList, | ||||
|                 TickerData = bitcoinTicker, | ||||
|                 GlobalData = globalData, | ||||
|             ) | ||||
|             globalData = GlobalResponse( | ||||
|                 activeCryptocurrencies = "", | ||||
|                 totalMarketCapUsd = "", | ||||
|                 total24hVolumeUsd = "", | ||||
|                 bitcoinPercentageOfMarketCap = "" | ||||
|             ) | ||||
|             fearGreedDataList = ArrayList<FearAndGreedData>() | ||||
|  | ||||
|             // 2) Перевод класса в json-строку | ||||
|             val statisticsJsonString = gson.toJson(fullStatistics) | ||||
|  | ||||
|             // 3) Сохранение в файл во внутреннем хранилище | ||||
|             val fos: FileOutputStream = | ||||
|                 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, | ||||
| ) | ||||
		Reference in New Issue
	
	Block a user
	 b3s23
					b3s23