diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..aeb3aac
--- /dev/null
+++ b/README.md
@@ -0,0 +1,44 @@
+# AnimePlayerUA
+Застосунок для зручного перегляду аніме для Android/Android TV (версії 9+).
+
+# Скріни
+
+1. Головний екран
+
+![Скрін головного екрану](https://raw.github.com/dadencukillia/animeplayerua/main/screenshots/screenshot1.png)
+
+
+2. Екран пошуку
+
+![Скрін екрану пошуку](https://raw.github.com/dadencukillia/animeplayerua/main/screenshots/screenshot2.png)
+
+
+3. Екран аніме
+
+![Скрін екрану аніме](https://raw.github.com/dadencukillia/animeplayerua/main/screenshots/screenshot3.png)
+
+
+4. Екран плеєру
+
+![Скрін екрану плеєру](https://raw.github.com/dadencukillia/animeplayerua/main/screenshots/screenshot4.png)
+
+
+5. Екран аніме, додатковий функціонал
+
+![Скрін екрану аніме](https://raw.github.com/dadencukillia/animeplayerua/main/screenshots/screenshot5.png)
+
+
+6. Екран особистого списку аніме
+
+![Скрін екрану особистого списку](https://raw.github.com/dadencukillia/animeplayerua/main/screenshots/screenshot6.png)
+
+
+# Завантажити
+Доступне для Android/Android TV **версії 9 або більше**. Завантажити APK файл можете натиснувши тут: [Завантажити .APK](https://github.com/dadencukillia/animeplayerua/releases)
+
+# Допомога (Contribution)
+Допомога завжди вітається. Ви можете тестувати застосунок на баги і створювати [репорти](https://github.com/dadencukillia/animeplayerua/issues), [пропонувати ваші ідеї](https://github.com/dadencukillia/animeplayerua/discussions) або [допомогти оновлювати код](https://github.com/dadencukillia/animeplayerua/pulls). Застосунок розвивається і йому потрібна ваша допомога.
+
+Застосунок розроблений на мові програмування **Kotlin** з використанням **Jetpack Compose**. Серед залежностей такі бібліотеки: **room**, **exoplayer**, **navigation compose**, **ktor**, **kamel image**.
+
+Інформація береться (парситься) з сайту anitube у режимі реального часу.
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index f9c8924..5f07548 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -16,8 +16,8 @@ android {
applicationId = "com.crocoby.animeplayerua"
minSdk = 28
targetSdk = 34
- versionCode = 2
- versionName = "0.1.1"
+ versionCode = 3
+ versionName = "0.1.2"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
@@ -43,6 +43,7 @@ android {
}
buildFeatures {
compose = true
+ buildConfig = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.1"
diff --git a/app/src/main/java/com/crocoby/animeplayerua/App.kt b/app/src/main/java/com/crocoby/animeplayerua/App.kt
index 51f98be..82e8961 100644
--- a/app/src/main/java/com/crocoby/animeplayerua/App.kt
+++ b/app/src/main/java/com/crocoby/animeplayerua/App.kt
@@ -2,11 +2,43 @@ package com.crocoby.animeplayerua
import android.annotation.SuppressLint
import android.content.Context
+import android.util.Log
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.material3.AlertDialog
+import androidx.compose.material3.AlertDialogDefaults
+import androidx.compose.material3.BasicAlertDialog
+import androidx.compose.material3.Button
+import androidx.compose.material3.Card
+import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalUriHandler
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.compose.ui.window.Dialog
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.compose.LifecycleEventEffect
+import androidx.lifecycle.compose.LocalLifecycleOwner
+import androidx.lifecycle.whenCreated
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
@@ -18,17 +50,86 @@ import com.crocoby.animeplayerua.activities.SearchActivity
import com.crocoby.animeplayerua.activities.VideoActivity
import com.crocoby.animeplayerua.logic.AnimeDao
import com.crocoby.animeplayerua.logic.AppDatabase
+import com.crocoby.animeplayerua.logic.LatestAppVersionAndDownloadUrl
+import com.crocoby.animeplayerua.logic.parser
+import com.crocoby.animeplayerua.logic.runParser
+import kotlinx.coroutines.launch
var database: AnimeDao? = null
@SuppressLint("StaticFieldLeak")
var navController: NavHostController? = null
+@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun App(context: Context) {
navController = rememberNavController()
database = remember { AppDatabase.getDatabase(context).getDao() }
+ val coroutine = rememberCoroutineScope()
+ val uriHandler = LocalUriHandler.current
+ val appVersion = BuildConfig.VERSION_NAME
+ var updatePopupInfo by remember { mutableStateOf(null) }
+ var firstStart by rememberSaveable { mutableStateOf(true) }
+
+ LifecycleEventEffect(Lifecycle.Event.ON_START) {
+ if (!firstStart) {
+ return@LifecycleEventEffect
+ }
+ firstStart = false
+
+ coroutine.launch {
+ try {
+ val resp = parser.getLatestAppVersionAndDownloadUrl()
+ if (resp.version != appVersion) {
+ updatePopupInfo = resp
+ }
+ } catch (_: Exception) { }
+ }
+ }
+
MaterialTheme(colorScheme = darkScheme) {
+ if (updatePopupInfo != null) {
+ val curInfo = updatePopupInfo!!
+
+ BasicAlertDialog(
+ onDismissRequest = {
+ updatePopupInfo = null
+ },
+ ) {
+ Surface(
+ modifier = Modifier.fillMaxWidth().wrapContentHeight().padding(16.dp),
+ shape = MaterialTheme.shapes.large,
+ tonalElevation = AlertDialogDefaults.TonalElevation
+ ) {
+ Column(modifier = Modifier.padding(16.dp)) {
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ verticalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+ Text(
+ text = "Доступна нова версія застосунку!",
+ style = TextStyle(
+ fontWeight = FontWeight.Bold,
+ fontSize = 18.sp
+ )
+ )
+ Text(
+ text = "Оновіть застосунок до версії ${curInfo.version}, щоб отримати нові функції та користуватися стабільнішою версією програми.",
+ )
+ Button(
+ onClick = {
+ uriHandler.openUri(curInfo.downloadUrl)
+ updatePopupInfo = null
+ }
+ ) {
+ Text("Відвідати")
+ }
+ }
+ }
+ }
+ }
+ }
+
NavHost(
navController!!,
startDestination = Routes.HOME,
diff --git a/app/src/main/java/com/crocoby/animeplayerua/MainActivity.kt b/app/src/main/java/com/crocoby/animeplayerua/MainActivity.kt
index 871c10d..adeab78 100644
--- a/app/src/main/java/com/crocoby/animeplayerua/MainActivity.kt
+++ b/app/src/main/java/com/crocoby/animeplayerua/MainActivity.kt
@@ -1,6 +1,7 @@
package com.crocoby.animeplayerua
import android.os.Bundle
+import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
diff --git a/app/src/main/java/com/crocoby/animeplayerua/logic/Parser.kt b/app/src/main/java/com/crocoby/animeplayerua/logic/Parser.kt
index b5bae6b..d24727e 100644
--- a/app/src/main/java/com/crocoby/animeplayerua/logic/Parser.kt
+++ b/app/src/main/java/com/crocoby/animeplayerua/logic/Parser.kt
@@ -42,6 +42,7 @@ fun runParser(
}
data class MainPageAnime(val bestSeason: List, val new: List)
+data class LatestAppVersionAndDownloadUrl(val version: String, val downloadUrl: String)
class Parser {
private val client: HttpClient = HttpClient()
@@ -236,4 +237,16 @@ class Parser {
return fileUrl.substringBeforeLast("\"").substringAfterLast("\"").substringAfterLast(",").substringAfterLast("]")
}
+
+ suspend fun getLatestAppVersionAndDownloadUrl(): LatestAppVersionAndDownloadUrl {
+ val resp = client.get("https://raw.githubusercontent.com/dadencukillia/animeplayerua/main/releaseVersion") {
+ expectSuccess = true
+
+ header("User-Agent", userAgent)
+ }
+ val body = resp.bodyAsText()
+ val split = body.split("\n")
+
+ return LatestAppVersionAndDownloadUrl(split.first(), split.last())
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/crocoby/animeplayerua/widgets/VideoPlayer.kt b/app/src/main/java/com/crocoby/animeplayerua/widgets/VideoPlayer.kt
index 75b0a3e..c2af242 100644
--- a/app/src/main/java/com/crocoby/animeplayerua/widgets/VideoPlayer.kt
+++ b/app/src/main/java/com/crocoby/animeplayerua/widgets/VideoPlayer.kt
@@ -8,7 +8,10 @@ import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
@@ -50,6 +53,7 @@ fun VideoPlayer(modifier: Modifier, url: String) {
val context = LocalContext.current.applicationContext
val systemUiController = rememberSystemUiController()
val focusRequester = remember { FocusRequester() }
+ var lastBarVisibleState by remember { mutableStateOf(false) }
val lifecycleOwner = LocalLifecycleOwner.current
val exoPlayer: ExoPlayer = remember {
@@ -88,7 +92,12 @@ fun VideoPlayer(modifier: Modifier, url: String) {
launch {
while (true) {
if (systemUiController.isSystemBarsVisible || systemUiController.isStatusBarVisible || systemUiController.isNavigationBarVisible) {
- playerView.showController()
+ if (!lastBarVisibleState) {
+ lastBarVisibleState = true
+ playerView.showController()
+ }
+ } else {
+ lastBarVisibleState = false
}
delay(500)
}
@@ -140,7 +149,7 @@ fun VideoPlayer(modifier: Modifier, url: String) {
DisposableEffect(Unit) {
focusRequester.requestFocus()
- val observer = LifecycleEventObserver { source, event ->
+ val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_PAUSE) {
exoPlayer.pause()
}
diff --git a/gradle.properties b/gradle.properties
index 20e2a01..414e4e8 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -20,4 +20,5 @@ kotlin.code.style=official
# Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
-android.nonTransitiveRClass=true
\ No newline at end of file
+android.nonTransitiveRClass=true
+android.buildConfig=true
\ No newline at end of file
diff --git a/releaseVersion b/releaseVersion
new file mode 100644
index 0000000..2ab74c6
--- /dev/null
+++ b/releaseVersion
@@ -0,0 +1,2 @@
+0.1.2
+https://github.com/dadencukillia/animeplayerua/releases/tag/v0.1.2
\ No newline at end of file
diff --git a/screenshots/screenshot1.png b/screenshots/screenshot1.png
new file mode 100644
index 0000000..9b60ae2
Binary files /dev/null and b/screenshots/screenshot1.png differ
diff --git a/screenshots/screenshot2.png b/screenshots/screenshot2.png
new file mode 100644
index 0000000..e579c7e
Binary files /dev/null and b/screenshots/screenshot2.png differ
diff --git a/screenshots/screenshot3.png b/screenshots/screenshot3.png
new file mode 100644
index 0000000..28b93e2
Binary files /dev/null and b/screenshots/screenshot3.png differ
diff --git a/screenshots/screenshot4.png b/screenshots/screenshot4.png
new file mode 100644
index 0000000..31392b7
Binary files /dev/null and b/screenshots/screenshot4.png differ
diff --git a/screenshots/screenshot5.png b/screenshots/screenshot5.png
new file mode 100644
index 0000000..274ef3d
Binary files /dev/null and b/screenshots/screenshot5.png differ
diff --git a/screenshots/screenshot6.png b/screenshots/screenshot6.png
new file mode 100644
index 0000000..5dcaca8
Binary files /dev/null and b/screenshots/screenshot6.png differ