13 Commits
1.0 ... 1.1-a

Author SHA1 Message Date
580c5b6611 Merge pull request 'removed dynamic color, cause we are controlling colors by ourself' (#21) from dev into main
All checks were successful
Gitea Android Builder / Build (push) Successful in 1m36s
Reviewed-on: #21
2025-03-08 11:45:03 +03:00
8f0ffb7041 removed dynamic color, cause we are controlling colors by ourself 2025-03-08 11:44:29 +03:00
0494efc2ad Merge pull request 'Fixed the design, added full color schemes for light and dark themes. Fixed number formatting with bitcoin data, as well as the line chart being fully visible on the vertical axis now! :3' (#20) from dev into main
All checks were successful
Gitea Android Builder / Build (push) Successful in 1m27s
Reviewed-on: #20
2025-03-07 16:13:29 +03:00
09871e8727 Fixed the design, added full color schemes for light and dark themes. Fixed number formatting with bitcoin data, as well as the line chart being fully visible on the vertical axis now! :3 2025-03-07 18:10:43 +05:00
d220a03a89 Merge pull request 'Update .gitignore' (#19) from dev into main
All checks were successful
Gitea Android Builder / Build (push) Successful in 1m18s
Reviewed-on: #19
2025-03-06 17:40:05 +03:00
95b2c1e70b Merge pull request 'CI/CD branching fix' (#18) from pipeline-test into main
Reviewed-on: #18
2025-03-05 19:42:02 +03:00
4005a39696 CI/CD branching fix
All checks were successful
Gitea Android Builder / Build (push) Successful in 1m18s
2025-03-05 21:40:07 +05:00
407a5d32a5 Merge pull request 'pipeline-test' (#17) from pipeline-test into main
All checks were successful
Gitea Android Builder / Build (push) Successful in 1m18s
Reviewed-on: #17
2025-03-05 19:34:40 +03:00
81ab379e0e CI/CD branching update
All checks were successful
Gitea Android Builder / Build (push) Successful in 1m25s
2025-03-05 21:30:26 +05:00
5a4d30b9f8 CI/CD image update revert
All checks were successful
Gitea Android Builder / Build (push) Successful in 1m17s
2025-03-05 21:18:04 +05:00
5c92f74ceb CI/CD image update fix
All checks were successful
Gitea Android Builder / Build (push) Successful in 1m23s
2025-03-05 21:14:13 +05:00
0906b95273 CI/CD image update
Some checks failed
Gitea Android Builder / Build (push) Failing after 20s
2025-03-05 21:11:15 +05:00
4c9c7a3aa4 Update .gitignore 2025-03-05 15:53:47 +03:00
9 changed files with 163 additions and 67 deletions

View File

@ -3,12 +3,11 @@ run-name: ${{ gitea.actor }} is building an Android application
on: on:
push: push:
branches: branches:
- main
- pipeline-test - pipeline-test
jobs: jobs:
Build: Build:
runs-on: ubuntu-22.04 runs-on: ubuntu-24.04
steps: steps:
- name: Checkout the repo - name: Checkout the repo
uses: actions/checkout@v4 uses: actions/checkout@v4

View File

@ -0,0 +1,40 @@
name: Gitea Android Builder
run-name: ${{ gitea.actor }} is building an Android application
on:
push:
tags:
- "*"
jobs:
Build:
runs-on: ubuntu-24.04
steps:
- name: Checkout the repo
uses: actions/checkout@v4
- name: Set up JDK 23
uses: actions/setup-java@v4
with:
java-version: '23'
distribution: 'temurin'
- name: Set up Android SDK
uses: android-actions/setup-android@v3
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Cache Gradle packages
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
- name: Build with Gradle
uses: gradle/gradle-build-action@v2
with:
arguments: assembleDebug
- name: Upload .apk Artifact
uses: actions/upload-artifact@v3
with:
name: android-app-apk
path: app/build/outputs/apk/debug/*.apk
- name: Status
run: echo "This job's status is ${{ job.status }}."

7
.gitignore vendored
View File

@ -1,12 +1,7 @@
*.iml *.iml
.gradle .gradle
/local.properties /local.properties
/.idea/caches /.idea
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store .DS_Store
/build /build
/captures /captures

1
.idea/misc.xml generated
View File

@ -1,4 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">

View File

@ -7,5 +7,5 @@ data class GlobalResponse(
@SerializedName("active_currencies") val activeCryptocurrencies: String = "", @SerializedName("active_currencies") val activeCryptocurrencies: String = "",
@SerializedName("total_market_cap_usd") val totalMarketCapUsd: String = "", @SerializedName("total_market_cap_usd") val totalMarketCapUsd: String = "",
@SerializedName("total_24h_volume_usd") val total24hVolumeUsd: String = "", @SerializedName("total_24h_volume_usd") val total24hVolumeUsd: String = "",
@SerializedName("bitcoin_percentage_of_market_cap") val bitcoinPercentageOfMarketCap: String = "" @SerializedName("bitcoin_percentage_of_market_cap") val bitcoinPercentageOfMarketCap: String = "0.0"
) )

View File

@ -4,6 +4,7 @@ import android.annotation.SuppressLint
import android.os.Bundle import android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
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.PaddingValues import androidx.compose.foundation.layout.PaddingValues
@ -42,28 +43,43 @@ import androidx.compose.ui.unit.TextUnit
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 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.rememberAxisLineComponent
import com.patrykandpatrick.vico.compose.cartesian.axis.rememberBottom import com.patrykandpatrick.vico.compose.cartesian.axis.rememberBottom
import com.patrykandpatrick.vico.compose.cartesian.axis.rememberStart 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.layer.rememberLineCartesianLayer
import com.patrykandpatrick.vico.compose.cartesian.rememberCartesianChart import com.patrykandpatrick.vico.compose.cartesian.rememberCartesianChart
import com.patrykandpatrick.vico.compose.cartesian.rememberVicoZoomState import com.patrykandpatrick.vico.compose.cartesian.rememberVicoZoomState
import com.patrykandpatrick.vico.compose.common.component.rememberLineComponent
import com.patrykandpatrick.vico.compose.common.component.rememberTextComponent import com.patrykandpatrick.vico.compose.common.component.rememberTextComponent
import com.patrykandpatrick.vico.compose.common.component.shapeComponent import com.patrykandpatrick.vico.compose.common.component.shapeComponent
import com.patrykandpatrick.vico.compose.common.fill import com.patrykandpatrick.vico.compose.common.fill
import com.patrykandpatrick.vico.compose.common.shape.rounded 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.Zoom
import com.patrykandpatrick.vico.core.cartesian.axis.BaseAxis
import com.patrykandpatrick.vico.core.cartesian.axis.HorizontalAxis import com.patrykandpatrick.vico.core.cartesian.axis.HorizontalAxis
import com.patrykandpatrick.vico.core.cartesian.axis.VerticalAxis import com.patrykandpatrick.vico.core.cartesian.axis.VerticalAxis
import com.patrykandpatrick.vico.core.cartesian.data.CartesianChartModelProducer 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.data.lineSeries
import com.patrykandpatrick.vico.core.cartesian.decoration.HorizontalLine 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.Position
import com.patrykandpatrick.vico.core.common.component.LineComponent import com.patrykandpatrick.vico.core.common.component.LineComponent
import com.patrykandpatrick.vico.core.common.component.TextComponent import com.patrykandpatrick.vico.core.common.component.TextComponent
import com.patrykandpatrick.vico.core.common.shape.CorneredShape import com.patrykandpatrick.vico.core.common.shape.CorneredShape
import ru.vendetti.bitcoin_summarizer.ui.theme.BitcoinSummarizerTheme import ru.vendetti.bitcoin_summarizer.ui.theme.BitcoinSummarizerTheme
import ru.vendetti.bitcoin_summarizer.ui.theme.EnglishViolet
import ru.vendetti.bitcoin_summarizer.ui.theme.Flame
import ru.vendetti.bitcoin_summarizer.ui.theme.Green2
import java.text.DateFormat import java.text.DateFormat
import java.text.DecimalFormat
import java.text.SimpleDateFormat
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import java.util.Date
import kotlin.math.roundToInt
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -76,7 +92,7 @@ class MainActivity : ComponentActivity() {
} }
} }
@SuppressLint("MutableCollectionMutableState") @SuppressLint("MutableCollectionMutableState", "SimpleDateFormat")
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Preview @Preview
@Composable @Composable
@ -109,15 +125,24 @@ fun CryptoScreen() {
LaunchedEffect(fearGreedDataList) { LaunchedEffect(fearGreedDataList) {
modelProducer.runTransaction { modelProducer.runTransaction {
var numberValues = Array<Int>(fearGreedDataList.count()) { index -> fearGreedDataList[index].value.toInt() } var numberValues = Array<Int>(fearGreedDataList.count()) {
index ->
fearGreedDataList[fearGreedDataList.count() - index - 1]
.value.toInt()
}
if(numberValues.isEmpty()) if(numberValues.isEmpty())
numberValues = Array<Int>(1) {0} numberValues = Array<Int>(1) {0}
lineSeries { series(numberValues.toList()) } lineSeries { series(numberValues.toList()) }
} }
} }
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(rememberTopAppBarState()) val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(rememberTopAppBarState())
val zoomState = rememberVicoZoomState(initialZoom = Zoom.x(fearGreedIndexDaysCount.toDouble())) val zoomState = rememberVicoZoomState(
zoomEnabled = false,
initialZoom = Zoom.x(fearGreedIndexDaysCount.toDouble()))
// Отображаем результаты на странице // Отображаем результаты на странице
Scaffold ( Scaffold (
modifier = Modifier modifier = Modifier
@ -126,8 +151,11 @@ fun CryptoScreen() {
topBar = { topBar = {
MediumTopAppBar( MediumTopAppBar(
colors = TopAppBarDefaults.topAppBarColors( colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.primaryContainer, containerColor = MaterialTheme.colorScheme.primary,
titleContentColor = MaterialTheme.colorScheme.primary, scrolledContainerColor = MaterialTheme.colorScheme.primary,
titleContentColor = MaterialTheme.colorScheme.onPrimary,
navigationIconContentColor = MaterialTheme.colorScheme.onPrimary,
actionIconContentColor = MaterialTheme.colorScheme.onPrimary,
), ),
title = { title = {
Text( Text(
@ -159,7 +187,7 @@ fun CryptoScreen() {
{ innerPadding -> { innerPadding ->
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxSize() .background(Color.Transparent)
.padding(innerPadding) .padding(innerPadding)
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
) { ) {
@ -171,27 +199,44 @@ fun CryptoScreen() {
CartesianChartHost( CartesianChartHost(
zoomState = zoomState, zoomState = zoomState,
chart = rememberCartesianChart( chart = rememberCartesianChart(
rememberLineCartesianLayer(), 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( startAxis = VerticalAxis.rememberStart(
titleComponent = rememberTextComponent(), title = "FGI",
title = "FGI" titleComponent = rememberTextComponent(MaterialTheme.colorScheme.onPrimary),
line = rememberAxisLineComponent(fill(MaterialTheme.colorScheme.onPrimary)),
label = rememberAxisLabelComponent(MaterialTheme.colorScheme.onPrimary)
), ),
bottomAxis = HorizontalAxis.rememberBottom( bottomAxis = HorizontalAxis.rememberBottom(
titleComponent = rememberTextComponent(), title = "last $fearGreedIndexDaysCount days",
title = "last $fearGreedIndexDaysCount days" titleComponent = rememberTextComponent(MaterialTheme.colorScheme.onPrimary),
line = rememberAxisLineComponent(fill(MaterialTheme.colorScheme.onPrimary)),
label = rememberAxisLabelComponent(MaterialTheme.colorScheme.onPrimary)
), ),
decorations = listOf( decorations = listOf(
remember { remember {
HorizontalLine( HorizontalLine(
y = { 15.toDouble() }, y = { 25.toDouble() },
line = LineComponent(fill(Color.Red), 2f), line = LineComponent(fill(Flame), 2f),
labelComponent = TextComponent( labelComponent = TextComponent(
background = background =
shapeComponent( shapeComponent(
fill(Color.Red), fill(Flame),
CorneredShape.rounded( CorneredShape.rounded(
bottomLeft = 4.dp, topLeft = 4.dp,
bottomRight = 4.dp topRight = 4.dp
) )
), ),
), ),
@ -201,12 +246,12 @@ fun CryptoScreen() {
}, },
remember { remember {
HorizontalLine( HorizontalLine(
y = { 60.toDouble() }, y = { 70.toDouble() },
line = LineComponent(fill(Color.Green), 2f), line = LineComponent(fill(Green2), 2f),
labelComponent = TextComponent( labelComponent = TextComponent(
background = background =
shapeComponent( shapeComponent(
fill(Color.Green), fill(Green2),
CorneredShape.rounded( CorneredShape.rounded(
bottomLeft = 4.dp, bottomLeft = 4.dp,
bottomRight = 4.dp bottomRight = 4.dp
@ -229,15 +274,23 @@ fun CryptoScreen() {
.align(alignment = Alignment.CenterHorizontally), .align(alignment = Alignment.CenterHorizontally),
fontSize = 24.sp fontSize = 24.sp
) )
Text("Текущая цена: \n ${bitcoinTicker.priceUsd}\n") val formatter = DecimalFormat("0.00")
Text("Суточный оборот: \n ${bitcoinTicker.volume24hUsd}\n") Text("Текущая цена: \n \$ ${formatter.format(bitcoinTicker.priceUsd.toFloat())}\n")
Text("Капитализация: \n ${bitcoinTicker.marketCapUsd}\n") Text("Суточный оборот: \n \$ ${bitcoinTicker.volume24hUsd}\n")
Text("Капитализация: \n \$ ${bitcoinTicker.marketCapUsd}\n")
Text( Text(
"Изменение курса за: " + "Изменение курса за: " +
"\n Сутки: ${bitcoinTicker.percentChange24h} " + "\n Сутки: ${formatter.format(bitcoinTicker.percentChange24h.toFloat())}% " +
"\n Неделю: ${bitcoinTicker.percentChange7d}\n" "\n Неделю: ${formatter.format(bitcoinTicker.percentChange7d.toFloat())}%\n"
) )
Text("Дата последнего обновления: \n ${bitcoinTicker.lastUpdated}\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) HorizontalDivider(thickness = 2.dp)
Text( Text(
@ -246,10 +299,10 @@ fun CryptoScreen() {
.align(alignment = Alignment.CenterHorizontally), .align(alignment = Alignment.CenterHorizontally),
fontSize = 24.sp fontSize = 24.sp
) )
Text("Общая капитализация крипторынка: \n ${globalData.totalMarketCapUsd}\n") Text("Общая капитализация крипторынка: \n \$ ${globalData.totalMarketCapUsd}\n")
Text("Всего видов криптовалют: \n ${globalData.activeCryptocurrencies}\n") Text("Всего видов криптовалют: \n ${globalData.activeCryptocurrencies}\n")
Text("Суточный оборот других криптовалют: \n ${globalData.total24hVolumeUsd}\n") Text("Суточный оборот других криптовалют: \n \$ ${globalData.total24hVolumeUsd}\n")
Text("Процент доминации Биткоина: \n ${globalData.bitcoinPercentageOfMarketCap}\n") Text("Процент доминации Биткоина: \n ${formatter.format(globalData.bitcoinPercentageOfMarketCap.toFloat())}%\n")
} }
} }
} }

View File

@ -11,15 +11,15 @@ data class TickerData(
val name: String = "", val name: String = "",
val symbol: String = "", val symbol: String = "",
val rank: String = "", val rank: String = "",
@SerializedName("price_usd") val priceUsd: String = "", @SerializedName("price_usd") val priceUsd: String = "0.0",
@SerializedName("price_btc") val priceBtc: String = "", @SerializedName("price_btc") val priceBtc: String = "0.0",
@SerializedName("24h_volume_usd") val volume24hUsd: String = "", @SerializedName("24h_volume_usd") val volume24hUsd: String = "0",
@SerializedName("market_cap_usd") val marketCapUsd: String = "", @SerializedName("market_cap_usd") val marketCapUsd: String = "0",
@SerializedName("available_supply") val availableSupply: String = "", @SerializedName("available_supply") val availableSupply: String = "0",
@SerializedName("total_supply") val totalSupply: String = "", @SerializedName("total_supply") val totalSupply: String = "0",
@SerializedName("max_supply") val maxSupply: String? = "", @SerializedName("max_supply") val maxSupply: String? = "0",
@SerializedName("percent_change_1h") val percentChange1h: String = "", @SerializedName("percent_change_1h") val percentChange1h: String = "0",
@SerializedName("percent_change_24h") val percentChange24h: String = "", @SerializedName("percent_change_24h") val percentChange24h: String = "0.0",
@SerializedName("percent_change_7d") val percentChange7d: String = "", @SerializedName("percent_change_7d") val percentChange7d: String = "0.0",
@SerializedName("last_updated") val lastUpdated: String = "" @SerializedName("last_updated") val lastUpdated: String = ""
) )

View File

@ -9,3 +9,11 @@ val Pink80 = Color(0xFFEFB8C8)
val Purple40 = Color(0xFF6650a4) val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71) val PurpleGrey40 = Color(0xFF625b71)
val Pink40 = Color(0xFF7D5260) val Pink40 = Color(0xFF7D5260)
val EnglishViolet = Color(0xFF44355B)
val DarkPurple = Color(0xFF31263E)
val RaisinBlack = Color(0xFF221E22)
val HunyadiYellow = Color(0xFFECA72C)
val Flame = Color(0xFFEE5622)
val Green2 = Color(0xFFB1E434)

View File

@ -9,43 +9,45 @@ import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
private val DarkColorScheme = darkColorScheme( private val DarkColorScheme = darkColorScheme(
primary = Purple80, primary = RaisinBlack,
secondary = PurpleGrey80, secondary = DarkPurple,
tertiary = Pink80 tertiary = EnglishViolet,
// Other default colors to override
background = DarkPurple,
surface = Color(0xFFFFFBFE),
onPrimary = HunyadiYellow,
onSecondary = HunyadiYellow,
onTertiary = HunyadiYellow,
onBackground = HunyadiYellow,
onSurface = Color(0xFF1C1B1F),
) )
private val LightColorScheme = lightColorScheme( private val LightColorScheme = lightColorScheme(
primary = Purple40, primary = HunyadiYellow,
secondary = PurpleGrey40, secondary = Flame,
tertiary = Pink40 tertiary = EnglishViolet,
/* Other default colors to override // Other default colors to override
background = Color(0xFFFFFBFE), background = Color.White,
surface = Color(0xFFFFFBFE), surface = Color(0xFFFFFBFE),
onPrimary = Color.White, onPrimary = RaisinBlack,
onSecondary = Color.White, onSecondary = Color.White,
onTertiary = Color.White, onTertiary = Color.White,
onBackground = Color(0xFF1C1B1F), onBackground = RaisinBlack,
onSurface = Color(0xFF1C1B1F), onSurface = Color(0xFF1C1B1F),
*/
) )
@Composable @Composable
fun BitcoinSummarizerTheme( fun BitcoinSummarizerTheme(
darkTheme: Boolean = isSystemInDarkTheme(), darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
content: @Composable () -> Unit content: @Composable () -> Unit
) { ) {
val colorScheme = when { val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme darkTheme -> DarkColorScheme
else -> LightColorScheme else -> LightColorScheme
} }