Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@
<intent-filter>
<action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
<action android:name="android.intent.action.PACKAGE_ADDED" />
<action android:name="android.intent.action.PACKAGE_CHANGED" />
<action android:name="android.intent.action.PACKAGE_REPLACED" />

<data android:scheme="package" />
Expand Down
4 changes: 3 additions & 1 deletion app/src/main/java/com/anod/appwatcher/AppModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import com.anod.appwatcher.preferences.Preferences
import com.anod.appwatcher.sync.UpdateCheck
import com.anod.appwatcher.utils.AppIconLoader
import com.anod.appwatcher.utils.PackageChangedReceiver
import com.anod.appwatcher.utils.PackageStateCache
import com.anod.appwatcher.utils.RealAppIconLoader
import com.anod.appwatcher.utils.date.UploadDateParserCache
import com.anod.appwatcher.watchlist.RecentlyInstalledAppsLoader
Expand Down Expand Up @@ -64,6 +65,7 @@ fun createAppModule(): Module = module {
single<LruCache<String, Any?>>(named("memoryCache")) { createLruCache() }
single { CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) }
singleOf(::PackageChangedReceiver)
singleOf(::PackageStateCache)
singleOf(::AuthTokenBlocking)
single {
HttpClient(OkHttp) {
Expand All @@ -85,7 +87,7 @@ fun createAppModule(): Module = module {
factory {
UpdateCheck(
context = get(),
packageManager = get(),
packageStates = get(),
notificationManager = get(),
database = get(),
authAccount = get(),
Expand Down
13 changes: 9 additions & 4 deletions app/src/main/java/com/anod/appwatcher/compose/IconsResources.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import androidx.compose.material.icons.filled.Label
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.icons.filled.OpenInNew
import androidx.compose.material.icons.filled.PhonelinkOff
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material.icons.filled.RadioButtonChecked
import androidx.compose.material.icons.filled.RadioButtonUnchecked
Expand Down Expand Up @@ -117,10 +118,14 @@ fun ShareIcon() {
}

@Composable
fun InstalledSignIcon(modifier: Modifier = Modifier, tint: Color = LocalContentColor.current) {
fun InstalledSignIcon(
enabled: Boolean,
modifier: Modifier = Modifier,
tint: Color = LocalContentColor.current
) {
Icon(
imageVector = Icons.Default.Smartphone,
contentDescription = stringResource(id = R.string.installed),
imageVector = if (enabled) Icons.Default.Smartphone else Icons.Default.PhonelinkOff,
contentDescription = stringResource(id = if (enabled) R.string.installed else R.string.installed_disabled),
modifier = modifier,
tint = tint
)
Expand Down Expand Up @@ -219,7 +224,7 @@ private fun IconsResourcesPreview() {
{ PlayStoreAppIcon() },
{ PlayStoreMyAppsIcon() },
{ ShareIcon() },
{ InstalledSignIcon() },
{ InstalledSignIcon(enabled = true) },
{ StoreVersionSignIcon() },
{ WatchedIcon(unwatch = true) },
{ WatchedIcon(unwatch = false) },
Expand Down
47 changes: 41 additions & 6 deletions app/src/main/java/com/anod/appwatcher/database/AppListTable.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ data class AppListRowSnapshot(
@ColumnInfo(name = BaseColumns._ID)
val rowId: Int,

@ColumnInfo(name = AppListTable.Columns.PACKAGE_NAME)
val packageName: String,

@ColumnInfo(name = AppListTable.Columns.VERSION_NUMBER)
val versionNumber: Int,

@ColumnInfo(name = AppListTable.Columns.STATUS)
val status: Int,

Expand Down Expand Up @@ -84,6 +90,12 @@ interface AppListTable {
)
fun observePackagesList(): Flow<List<PackageRowPair>>

@Query(
"SELECT ${BaseColumns._ID}, ${Columns.PACKAGE_NAME} FROM $TABLE WHERE " +
"${Columns.STATUS} = ${App.STATUS_UPDATED}"
)
suspend fun loadUpdatedPackages(): List<PackageRowPair>

@Query(
"SELECT ${BaseColumns._ID}, ${Columns.PACKAGE_NAME} FROM $TABLE WHERE " +
"CASE :includeDeleted WHEN 0 THEN ${Columns.STATUS} != ${App.STATUS_DELETED} ELSE ${Columns.STATUS} >= ${App.STATUS_NORMAL} END"
Expand Down Expand Up @@ -140,6 +152,22 @@ interface AppListTable {
)
suspend fun clearUpdateStatus(rowId: Int): Int

@Query(
"UPDATE $TABLE SET " +
"${Columns.STATUS} = ${App.STATUS_NORMAL}, " +
"${Columns.SYNC_TIMESTAMP} = 0 " +
"WHERE ${Columns.PACKAGE_NAME} = :packageName AND ${Columns.STATUS} = ${App.STATUS_UPDATED}"
)
suspend fun clearUpdateStatusByPackageName(packageName: String): Int

@Query(
"UPDATE $TABLE SET " +
"${Columns.STATUS} = ${App.STATUS_NORMAL}, " +
"${Columns.SYNC_TIMESTAMP} = 0 " +
"WHERE ${BaseColumns._ID} IN (:rowIds) AND ${Columns.STATUS} = ${App.STATUS_UPDATED}"
)
suspend fun clearUpdateStatuses(rowIds: List<Int>): Int

@Query(
"UPDATE $TABLE SET " +
"${Columns.TITLE} = :title, " +
Expand Down Expand Up @@ -227,12 +255,11 @@ interface AppListTable {

suspend fun loadAppListRows(
sortId: Int,
orderByRecentlyDiscovered: Boolean,
tagId: Int?,
titleFilter: String,
table: AppListTable
): List<AppListRowSnapshot> {
val query = createAppsListRowsQuery(sortId, orderByRecentlyDiscovered, tagId, titleFilter)
val query = createAppsListRowsQuery(sortId, tagId, titleFilter)
return table.loadRowSnapshots(SimpleSQLiteQuery(query.first, query.second))
}

Expand Down Expand Up @@ -265,17 +292,16 @@ interface AppListTable {

internal fun createAppsListRowsQuery(
sortId: Int,
orderByRecentlyDiscovered: Boolean,
tagId: Int?,
titleFilter: String
): Pair<String, Array<String>> {
val selection = createSelection(tagId, titleFilter)
val sql =
"SELECT $TABLE.${BaseColumns._ID}, ${Columns.STATUS}, " +
"SELECT $TABLE.${BaseColumns._ID}, ${Columns.PACKAGE_NAME}, ${Columns.VERSION_NUMBER}, ${Columns.STATUS}, " +
"CASE WHEN ${Columns.SYNC_TIMESTAMP} > $recentTime THEN 1 ELSE 0 END ${Columns.RECENT_FLAG} " +
"FROM $TABLE " +
"WHERE ${selection.first} " +
"ORDER BY ${createSortOrder(sortId, orderByRecentlyDiscovered)} "
"ORDER BY ${createSnapshotSortOrder(sortId)} "
return Pair(sql, selection.second)
}

Expand Down Expand Up @@ -365,14 +391,23 @@ interface AppListTable {
if (orderByRecentlyUpdated) {
filter.add("CASE WHEN ${Columns.SYNC_TIMESTAMP} > $recentTime THEN 1 ELSE 0 END DESC")
}
filter.addAll(createUserSortOrder(sortId))
return filter.joinToString(", ")
}

private fun createSnapshotSortOrder(sortId: Int): String =
createUserSortOrder(sortId).joinToString(", ")

private fun createUserSortOrder(sortId: Int): List<String> {
val filter = mutableListOf<String>()
when (sortId) {
Preferences.SORT_NAME_DESC -> filter.add(Columns.TITLE + " COLLATE NOCASE DESC")
Preferences.SORT_DATE_ASC -> filter.add(Columns.UPLOAD_TIMESTAMP + " ASC")
Preferences.SORT_DATE_DESC -> filter.add(Columns.UPLOAD_TIMESTAMP + " DESC")
else -> filter.add(Columns.TITLE + " COLLATE NOCASE ASC")
}
filter.add("$TABLE.${BaseColumns._ID} ASC")
return filter.joinToString(", ")
return filter
}

private fun createSelection(tagId: Int?, titleFilter: String): Pair<String, Array<String>> {
Expand Down
18 changes: 15 additions & 3 deletions app/src/main/java/com/anod/appwatcher/details/AppItemState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,28 +30,39 @@ fun rememberAppItemState(
app: App,
recentFlag: Boolean,
packageInfo: InstalledApps.Info,
isPackageEnabled: Boolean,
textColor: Color = MaterialTheme.colorScheme.onSurface,
primaryColor: Color = MaterialTheme.colorScheme.primary
): AppItemState {
val context = LocalContext.current
return remember(app, recentFlag, packageInfo, textColor, primaryColor, context) {
return remember(app, recentFlag, packageInfo, isPackageEnabled, textColor, primaryColor, context) {
calcAppItemState(
app, recentFlag, textColor, primaryColor, packageInfo, context
app, recentFlag, textColor, primaryColor, packageInfo, isPackageEnabled, context
)
}
}

private fun calcAppItemState(
internal fun calcAppItemState(
app: App,
recentFlag: Boolean,
textColor: Color,
primaryColor: Color,
packageInfo: InstalledApps.Info,
isPackageEnabled: Boolean,
context: Context
): AppItemState {
var color = textColor
var installed = false
val text = when {
packageInfo.isInstalled && !isPackageEnabled -> {
installed = true
context.getString(
R.string.installed_disabled_version,
packageInfo.versionName,
packageInfo.versionCode
)
}

app.versionNumber == 0 -> {
color = Amber800
context.getString(R.string.updates_not_available)
Expand Down Expand Up @@ -89,6 +100,7 @@ private fun calcAppItemState(
}

val showRecent = when {
!isPackageEnabled -> false
app.status == App.STATUS_UPDATED || recentFlag -> true
else -> false
}
Expand Down
53 changes: 35 additions & 18 deletions app/src/main/java/com/anod/appwatcher/details/DetailsPanel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,7 @@ fun VersionDetails(screenState: DetailsState) {
) {
if (screenState.packageInfo.isInstalled || screenState.isLocalApp) {
InstalledSignIcon(
enabled = screenState.isPackageEnabled,
modifier = Modifier
.size(16.dp)
.padding(end = 4.dp)
Expand All @@ -521,7 +522,8 @@ fun VersionDetails(screenState: DetailsState) {
val appItemState = rememberAppItemState(
app = screenState.app,
recentFlag = false,
packageInfo = screenState.packageInfo
packageInfo = screenState.packageInfo,
isPackageEnabled = screenState.isPackageEnabled
)
Text(
text = appItemState.text,
Expand Down Expand Up @@ -624,7 +626,10 @@ private fun ChangelogList(
items(changelogs.size) { i ->
Column {
if (i == dividerIndex) {
InstalledChangelogDivider(packageInfo = packageInfo)
InstalledChangelogDivider(
packageInfo = packageInfo,
isPackageEnabled = screenState.isPackageEnabled
)
}
ChangelogItem(
change = changelogs[i],
Expand All @@ -634,7 +639,10 @@ private fun ChangelogList(
}
if (dividerIndex == changelogs.size) {
item {
InstalledChangelogDivider(packageInfo = packageInfo)
InstalledChangelogDivider(
packageInfo = packageInfo,
isPackageEnabled = screenState.isPackageEnabled
)
}
}
}
Expand Down Expand Up @@ -705,9 +713,13 @@ private fun ChangelogItemHeader(change: AppChange) {
}

@Composable
private fun InstalledChangelogDivider(packageInfo: InstalledApps.Info, modifier: Modifier = Modifier) {
private fun InstalledChangelogDivider(
packageInfo: InstalledApps.Info,
isPackageEnabled: Boolean,
modifier: Modifier = Modifier
) {
val versionText = stringResource(id = R.string.version_text, packageInfo.versionName, packageInfo.versionCode)
val installedText = stringResource(id = R.string.installed)
val installedText = stringResource(id = if (isPackageEnabled) R.string.installed else R.string.installed_disabled)
Row(
modifier = modifier
.fillMaxWidth()
Expand All @@ -717,6 +729,7 @@ private fun InstalledChangelogDivider(packageInfo: InstalledApps.Info, modifier:
) {
ChangelogDividerLine(modifier = Modifier.weight(1f))
InstalledSignIcon(
enabled = isPackageEnabled,
tint = MaterialTheme.colorScheme.primary,
modifier = Modifier
.padding(start = 8.dp, end = 4.dp)
Expand Down Expand Up @@ -1010,19 +1023,21 @@ private fun DetailsTopAppBar(
)

if (screenState.packageInfo.isInstalled) {
DropdownMenuItem(
text = {
Text(
text = stringResource(id = R.string.open),
modifier = Modifier.padding(horizontal = 8.dp)
)
},
leadingIcon = { OpenAppIcon() },
onClick = {
onEvent(DetailsEvent.Open)
dismiss()
}
)
if (screenState.isPackageEnabled) {
DropdownMenuItem(
text = {
Text(
text = stringResource(id = R.string.open),
modifier = Modifier.padding(horizontal = 8.dp)
)
},
leadingIcon = { OpenAppIcon() },
onClick = {
onEvent(DetailsEvent.Open)
dismiss()
}
)
}
DropdownMenuItem(
text = {
Text(
Expand Down Expand Up @@ -1091,6 +1106,7 @@ private fun DetailsScreenPreview() {
syncTime = 0
),
title = "Test title long app name",
isPackageEnabled = true,
changelogState = ChangelogLoadState.Complete,
changelogs = listOf(
AppChange(
Expand Down Expand Up @@ -1178,6 +1194,7 @@ private fun VersionInfoPreview() {
syncTime = 0
),
title = "Test title long app name",
isPackageEnabled = true,
changelogState = ChangelogLoadState.Complete,
changelogs = listOf(),
remoteVersionInfo = AppVersionInfo(
Expand Down
Loading
Loading