Skip to content

Commit

Permalink
Added additional Android TV UI similar to 2.x UI.
Browse files Browse the repository at this point in the history
Apks are streamed directly to SessionInstaller instead of being saved to disk first.
  • Loading branch information
rumboalla committed Jul 30, 2023
1 parent b3e757a commit 6c9c623
Show file tree
Hide file tree
Showing 21 changed files with 355 additions and 75 deletions.
3 changes: 2 additions & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ dependencies {
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1'
implementation 'androidx.lifecycle:lifecycle-runtime-compose:2.6.1'
implementation 'androidx.compose.ui:ui:1.4.3'
implementation 'androidx.compose.ui:ui-tooling-preview:1.4.3'
implementation "androidx.tv:tv-foundation:1.0.0-alpha08"
// implementation "androidx.tv:tv-material:1.0.0-alpha08"
implementation 'androidx.compose.material3:material3:1.1.1'
implementation 'androidx.work:work-runtime-ktx:2.8.1'
implementation 'io.insert-koin:koin-android:3.4.2'
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/java/com/apkupdater/di/MainModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import com.apkupdater.service.GitHubService
import com.apkupdater.util.Downloader
import com.apkupdater.util.SessionInstaller
import com.apkupdater.util.UpdatesNotification
import com.apkupdater.util.isAndroidTv
import com.apkupdater.viewmodel.AppsViewModel
import com.apkupdater.viewmodel.MainViewModel
import com.apkupdater.viewmodel.SearchViewModel
Expand Down Expand Up @@ -106,7 +107,7 @@ val mainModule = module {

single { KryptoBuilder.nocrypt(get(), androidContext().getString(R.string.app_name)) }

single { Prefs(get()) }
single { Prefs(get(), androidContext().isAndroidTv()) }

single { UpdatesNotification(get()) }

Expand Down
6 changes: 5 additions & 1 deletion app/src/main/java/com/apkupdater/prefs/Prefs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import com.kryptoprefs.gson.json
import com.kryptoprefs.preferences.KryptoPrefs


class Prefs(prefs: KryptoPrefs): KryptoContext(prefs) {
class Prefs(
prefs: KryptoPrefs,
isAndroidTv: Boolean
): KryptoContext(prefs) {
val ignoredApps = json("ignoredApps", emptyList<String>(), true)
val excludeSystem = boolean("excludeSystem", defValue = true, backed = true)
val excludeDisabled = boolean("excludeDisabled", defValue = true, backed = true)
Expand All @@ -20,4 +23,5 @@ class Prefs(prefs: KryptoPrefs): KryptoContext(prefs) {
val enableAlarm = boolean("enableAlarm", defValue = false, backed = true)
val alarmHour = int("alarmHour", defValue = 12, backed = true)
val alarmFrequency = int("alarmFrequency", 0, backed = true)
val androidTvUi = boolean("androidTvUi", defValue = isAndroidTv, backed = true)
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import com.apkupdater.data.ui.getVersionCode
import com.apkupdater.prefs.Prefs
import com.apkupdater.service.ApkMirrorService
import com.apkupdater.util.combine
import com.apkupdater.util.isAndroidTv
import com.apkupdater.util.orFalse
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
Expand All @@ -39,7 +40,7 @@ class ApkMirrorRepository(
else -> "arm"
}

private val isAndroidTV = packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
private val isAndroidTV = packageManager.isAndroidTv()
private val api = Build.VERSION.SDK_INT

suspend fun updates(apps: List<AppInstalled>) = flow {
Expand All @@ -64,7 +65,7 @@ class ApkMirrorRepository(
name = h5[it].attr("title"),
link = "$baseUrl${h5[it].selectFirst("a")?.attr("href")}",
iconUri = Uri.parse("$baseUrl${img[it].attr("src")}".replace("=32", "=128")),
version = "",
version = "?",
versionCode = 0L,
source = ApkMirrorSource,
packageName = a[it].text() // Developer name in this case
Expand Down
51 changes: 51 additions & 0 deletions app/src/main/java/com/apkupdater/ui/component/Grid.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.apkupdater.ui.component

import android.content.res.Configuration
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.unit.dp
import androidx.tv.foundation.lazy.grid.TvGridCells
import androidx.tv.foundation.lazy.grid.TvLazyGridScope
import androidx.tv.foundation.lazy.grid.TvLazyVerticalGrid
import com.apkupdater.prefs.Prefs
import org.koin.androidx.compose.get

@Composable
fun InstalledGrid(content: LazyGridScope.() -> Unit) = LazyVerticalGrid(
columns = GridCells.Fixed(getNumColumns(LocalConfiguration.current.orientation)),
contentPadding = PaddingValues(horizontal = 8.dp, vertical = 8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
content = content
)

@Composable
fun TvInstalledGrid(content: TvLazyGridScope.() -> Unit) = TvLazyVerticalGrid(
columns = TvGridCells.Fixed(getTvNumColumns()),
contentPadding = PaddingValues(horizontal = 8.dp, vertical = 8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
content = content
)

@Composable
fun getNumColumns(orientation: Int): Int {
val prefs = get<Prefs>()
return if(orientation == Configuration.ORIENTATION_PORTRAIT)
prefs.portraitColumns.get()
else
prefs.landscapeColumns.get()
}

@Composable
fun getTvNumColumns(): Int {
return if(LocalConfiguration.current.orientation == Configuration.ORIENTATION_PORTRAIT)
1
else
2
}
2 changes: 1 addition & 1 deletion app/src/main/java/com/apkupdater/ui/component/Icons.kt
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ fun ExcludeDisabledIcon(exclude: Boolean) = ExcludeIcon(
fun SourceIcon(source: Source, modifier: Modifier) = Icon(
painterResource(id = source.resourceId),
source.name,
Modifier.size(32.dp).then(modifier)
modifier
)

@Composable
Expand Down
14 changes: 6 additions & 8 deletions app/src/main/java/com/apkupdater/ui/component/Image.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,12 @@ import com.apkupdater.util.getAppIcon
@Composable
private fun BaseLoadingImage(
request: ImageRequest,
height: Dp = 120.dp,
modifier: Modifier,
color: Color = Color.Transparent
) = AsyncImage(
model = request,
contentDescription = stringResource(R.string.app_cd),
modifier = Modifier
.fillMaxSize()
.height(height)
modifier = modifier
.padding(10.dp)
.clip(RoundedCornerShape(8.dp))
.background(color),
Expand All @@ -40,23 +38,23 @@ private fun BaseLoadingImage(
@Composable
fun LoadingImage(
uri: Uri,
height: Dp = 120.dp,
modifier: Modifier = Modifier.height(120.dp).fillMaxSize(),
crossfade: Boolean = true,
color: Color = Color.Transparent
) = BaseLoadingImage(
ImageRequest.Builder(LocalContext.current).data(uri).crossfade(crossfade).build(),
height,
modifier,
color
)

@Composable
fun LoadingImageApp(
packageName: String,
height: Dp = 120.dp,
modifier: Modifier = Modifier.height(120.dp).fillMaxSize(),
crossfade: Boolean = true,
color: Color = Color.Transparent
) = BaseLoadingImage(
ImageRequest.Builder(LocalContext.current).data(LocalContext.current.getAppIcon(packageName)).crossfade(crossfade).build(),
height,
modifier,
color
)
21 changes: 20 additions & 1 deletion app/src/main/java/com/apkupdater/ui/component/Text.kt
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,16 @@ fun SmallText(text: String, modifier: Modifier = Modifier) = Text(
)

@Composable
fun TitleText(text: String, modifier: Modifier = Modifier) = Text(
fun MediumText(text: String, modifier: Modifier = Modifier) = Text(
text = text,
style = MaterialTheme.typography.bodyMedium,
maxLines = 1,
modifier = modifier,
overflow = TextOverflow.Ellipsis
)

@Composable
fun MediumTitle(text: String, modifier: Modifier = Modifier) = Text(
text = text,
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Bold,
Expand All @@ -60,6 +69,16 @@ fun TitleText(text: String, modifier: Modifier = Modifier) = Text(
modifier = modifier
)

@Composable
fun LargeTitle(text: String, modifier: Modifier = Modifier) = Text(
text = text,
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Bold,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = modifier
)

@Composable
fun HugeText(text: String, modifier: Modifier = Modifier, maxLines: Int = 1) = Text(
text = text,
Expand Down
129 changes: 129 additions & 0 deletions app/src/main/java/com/apkupdater/ui/component/TvComponents.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package com.apkupdater.ui.component

import android.net.Uri
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Card
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ElevatedButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.apkupdater.R
import com.apkupdater.data.ui.AppInstalled
import com.apkupdater.data.ui.AppUpdate

@Composable
fun TvCommonItem(
packageName: String,
name: String,
version: String,
versionCode: Long,
uri: Uri? = null
) = Row {
if (uri == null) {
LoadingImageApp(
packageName,
Modifier
.height(90.dp)
.align(Alignment.CenterVertically)
)
} else {
LoadingImage(
uri,
Modifier
.height(90.dp)
.align(Alignment.CenterVertically)
)
}
Column(
Modifier
.align(Alignment.CenterVertically)
.padding(horizontal = 8.dp)
) {
LargeTitle(name)
MediumText(version)
MediumText(versionCode.toString())
}
}

@Composable
fun TvInstallButton(
app: AppUpdate,
onInstall: (String) -> Unit
) = ElevatedButton(
modifier = Modifier
.padding(top = 0.dp, bottom = 8.dp, start = 8.dp, end = 8.dp)
.width(120.dp),
onClick = { onInstall(app.packageName) }
) {
if (app.isInstalling) {
CircularProgressIndicator(Modifier.size(24.dp))
} else {
Text(stringResource(R.string.install_cd))
}
}

@Composable
fun BoxScope.TvSourceIcon(app: AppUpdate) = SourceIcon(
app.source,
Modifier
.align(Alignment.CenterStart)
.padding(top = 0.dp, bottom = 8.dp, start = 8.dp, end = 8.dp)
.size(32.dp)
)

@Composable
fun TvInstalledItem(app: AppInstalled, onIgnore: (String) -> Unit = {}) = Card(
modifier = Modifier.alpha(if (app.ignored) 0.5f else 1f)
) {
Column {
TvCommonItem(app.packageName, app.name, app.version, app.versionCode)
Row(modifier = Modifier.fillMaxSize(), horizontalArrangement = Arrangement.End) {
ElevatedButton(
modifier = Modifier.padding(top = 0.dp, bottom = 8.dp, start = 8.dp, end = 8.dp),
onClick = { onIgnore(app.packageName) }
) {
Text(stringResource(R.string.ignore_cd))
}
}
}
}

@Composable
fun TvUpdateItem(app: AppUpdate, onInstall: (String) -> Unit = {}) = Card {
Column {
TvCommonItem(app.packageName, app.name, app.version, app.versionCode)
Box {
TvSourceIcon(app)
Row(modifier = Modifier.fillMaxSize(), horizontalArrangement = Arrangement.End) {
TvInstallButton(app, onInstall)
}
}
}
}

@Composable
fun TvSearchItem(app: AppUpdate, onInstall: (String) -> Unit = {}) = Card {
Column {
TvCommonItem(app.packageName, app.name, app.version, app.versionCode, app.iconUri)
Box {
TvSourceIcon(app)
Row(modifier = Modifier.fillMaxSize(), horizontalArrangement = Arrangement.End) {
TvInstallButton(app, onInstall)
}
}
}
}
Loading

0 comments on commit 6c9c623

Please sign in to comment.