@ -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
}
}
}