Skip to content

Commit bbb85e8

Browse files
committed
[feature|optimize|build] Support hide the duplicate title present in description; support swipe on article item; migrate feed screen, media screen, article screen, search screen to Compose; update dependencies
1 parent 7a63462 commit bbb85e8

File tree

126 files changed

+3381
-2634
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

126 files changed

+3381
-2634
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
<b>AniVu</b>, an <b>all-in-one tool</b> for <b>RSS anime subscription and updates</b>, <b>bit torrent downloads</b>, and <b>video playback</b>.
3131
</p>
3232
<p>
33-
AniVu utilizes the <b><a href="https://developer.android.com/topic/architecture#recommended-app-arch">MVI</a></b> architecture and fully adopts the <b><a href="https://m3.material.io/">Material You</a></b> design style. All pages are developed using <b>Android View</b>.
33+
AniVu utilizes the <b><a href="https://developer.android.com/topic/architecture#recommended-app-arch">MVI</a></b> architecture and fully adopts the <b><a href="https://m3.material.io/">Material You</a></b> design style. All pages are developed using <b>Android View</b> and <b>Jetpack Compose</b>.
3434
</p>
3535
<p>
3636
<b><a href="doc/readme/README-zh-rCN.md">中文</a></b>
@@ -39,6 +39,7 @@
3939

4040

4141

42+
4243
## 💡 Features
4344

4445
1. **Subscribe to RSS**, Update RSS, **Read** RSS
@@ -72,6 +73,7 @@
7273
## 🛠 Primary technology stack
7374

7475
- **MVI** Architecture
76+
- Jetpack **Compose**
7577
- Kotlin **Coroutines and Flow**
7678
- **Material You**
7779
- **ViewModel**

app/build.gradle.kts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ android {
2121
minSdk = 24
2222
targetSdk = 34
2323
versionCode = 13
24-
versionName = "1.1-beta11"
24+
versionName = "1.1-beta12"
2525

2626
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
2727

@@ -144,8 +144,10 @@ dependencies {
144144
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
145145
implementation("androidx.navigation:navigation-fragment-ktx:2.7.7")
146146
implementation("androidx.navigation:navigation-ui-ktx:2.7.7")
147+
implementation("androidx.navigation:navigation-compose:2.7.7")
147148
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.7.0")
148149
implementation("androidx.compose.ui:ui:1.6.5")
150+
implementation("androidx.compose.material:material:1.6.5")
149151
implementation("androidx.compose.material3:material3:1.2.1")
150152
implementation("androidx.compose.material3:material3-window-size-class:1.2.1")
151153
implementation("androidx.compose.material:material-icons-extended:1.6.5")
@@ -158,10 +160,11 @@ dependencies {
158160
implementation("androidx.datastore:datastore-preferences:1.0.0")
159161
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
160162
implementation("androidx.core:core-splashscreen:1.0.1")
161-
implementation("androidx.media3:media3-exoplayer:1.3.0")
162-
implementation("androidx.media3:media3-exoplayer-dash:1.3.0")
163-
implementation("androidx.media3:media3-ui:1.3.0")
163+
implementation("androidx.media3:media3-exoplayer:1.3.1")
164+
implementation("androidx.media3:media3-exoplayer-dash:1.3.1")
165+
implementation("androidx.media3:media3-ui:1.3.1")
164166
implementation("androidx.paging:paging-runtime-ktx:3.2.1")
167+
implementation("androidx.paging:paging-compose:3.3.0-beta01")
165168
implementation("androidx.preference:preference-ktx:1.2.1")
166169
implementation("com.google.code.gson:gson:2.10.1")
167170

@@ -180,6 +183,7 @@ dependencies {
180183

181184
implementation("io.coil-kt:coil:2.6.0")
182185
implementation("io.coil-kt:coil-compose:2.6.0")
186+
implementation("io.coil-kt:coil-gif:2.5.0")
183187
implementation("com.rometools:rome:2.1.0")
184188
implementation("net.dankito.readability4j:readability4j:1.0.8")
185189

app/src/main/java/com/skyd/anivu/base/BaseActivity.kt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.skyd.anivu.base
22

3-
import android.content.res.Resources
43
import android.os.Bundle
54
import androidx.activity.enableEdgeToEdge
65
import androidx.appcompat.app.AppCompatActivity
@@ -28,9 +27,7 @@ abstract class BaseActivity<T : ViewBinding> : AppCompatActivity() {
2827
protected open fun T.initView() {}
2928

3029
protected open fun beforeSetContentView() {}
31-
override fun getTheme(): Resources.Theme {
32-
return super.getTheme()
33-
}
30+
3431
private fun initTheme() {
3532
setTheme(ThemePreference.toResId(this))
3633
}

app/src/main/java/com/skyd/anivu/base/BaseComposeFragment.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ abstract class BaseComposeFragment : Fragment() {
4444
}
4545
}
4646

47-
private val defaultTransitionProvider = BaseFragment.TransitionProvider()
47+
protected val defaultTransitionProvider = BaseFragment.TransitionProvider()
4848

4949
protected val nullTransitionProvider: BaseFragment.TransitionProvider? = null
5050

app/src/main/java/com/skyd/anivu/base/mvi/MviViewState.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package com.skyd.anivu.base.mvi
33
import android.os.Bundle
44

55
/**
6-
* Immutable object which contains all the required information to render a [MviView].
6+
* Immutable object which contains all the required information to render a View.
77
*/
88
interface MviViewState
99

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.skyd.anivu.ext
2+
3+
import androidx.compose.material3.MaterialTheme
4+
import androidx.compose.runtime.Composable
5+
import androidx.compose.runtime.ReadOnlyComposable
6+
import androidx.compose.runtime.Stable
7+
import androidx.compose.ui.graphics.Color
8+
import com.skyd.anivu.model.preference.appearance.DarkModePreference
9+
import com.skyd.anivu.ui.local.LocalDarkMode
10+
11+
@Stable
12+
@Composable
13+
@ReadOnlyComposable
14+
infix fun Color.alwaysLight(isAlways: Boolean): Color {
15+
val colorScheme = MaterialTheme.colorScheme
16+
return if (isAlways && DarkModePreference.inDark(value = LocalDarkMode.current)) {
17+
when (this) {
18+
colorScheme.primary -> colorScheme.onPrimary
19+
colorScheme.secondary -> colorScheme.onSecondary
20+
colorScheme.tertiary -> colorScheme.onTertiary
21+
colorScheme.background -> colorScheme.onBackground
22+
colorScheme.error -> colorScheme.onError
23+
colorScheme.surface -> colorScheme.onSurface
24+
colorScheme.surfaceVariant -> colorScheme.onSurfaceVariant
25+
colorScheme.primaryContainer -> colorScheme.onPrimaryContainer
26+
colorScheme.secondaryContainer -> colorScheme.onSecondaryContainer
27+
colorScheme.tertiaryContainer -> colorScheme.onTertiaryContainer
28+
colorScheme.errorContainer -> colorScheme.onErrorContainer
29+
colorScheme.inverseSurface -> colorScheme.inverseOnSurface
30+
31+
colorScheme.onPrimary -> colorScheme.primary
32+
colorScheme.onSecondary -> colorScheme.secondary
33+
colorScheme.onTertiary -> colorScheme.tertiary
34+
colorScheme.onBackground -> colorScheme.background
35+
colorScheme.onError -> colorScheme.error
36+
colorScheme.onSurface -> colorScheme.surface
37+
colorScheme.onSurfaceVariant -> colorScheme.surfaceVariant
38+
colorScheme.onPrimaryContainer -> colorScheme.primaryContainer
39+
colorScheme.onSecondaryContainer -> colorScheme.secondaryContainer
40+
colorScheme.onTertiaryContainer -> colorScheme.tertiaryContainer
41+
colorScheme.onErrorContainer -> colorScheme.errorContainer
42+
colorScheme.inverseOnSurface -> colorScheme.inverseSurface
43+
44+
else -> Color.Unspecified
45+
}
46+
} else {
47+
this
48+
}
49+
}

app/src/main/java/com/skyd/anivu/ext/PreferenceExt.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import com.skyd.anivu.model.preference.IgnoreUpdateVersionPreference
55
import com.skyd.anivu.model.preference.Settings
66
import com.skyd.anivu.model.preference.appearance.DarkModePreference
77
import com.skyd.anivu.model.preference.appearance.ThemePreference
8+
import com.skyd.anivu.model.preference.behavior.article.ArticleSwipeLeftActionPreference
9+
import com.skyd.anivu.model.preference.behavior.article.ArticleTapActionPreference
10+
import com.skyd.anivu.model.preference.behavior.article.DeduplicateTitleInDescPreference
811

912
fun Preferences.toSettings(): Settings {
1013
return Settings(
@@ -14,5 +17,10 @@ fun Preferences.toSettings(): Settings {
1417

1518
// Update
1619
ignoreUpdateVersion = IgnoreUpdateVersionPreference.fromPreferences(this),
20+
21+
// Behavior
22+
deduplicateTitleInDesc = DeduplicateTitleInDescPreference.fromPreferences(this),
23+
articleTapAction = ArticleTapActionPreference.fromPreferences(this),
24+
articleSwipeLeftAction = ArticleSwipeLeftActionPreference.fromPreferences(this),
1725
)
1826
}

app/src/main/java/com/skyd/anivu/model/db/dao/FeedDao.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ interface FeedDao {
7777

7878
@Transaction
7979
@RawQuery(observedEntities = [FeedBean::class])
80-
fun getFeedList(sql: SupportSQLiteQuery): List<FeedBean>
80+
fun getFeedListPagingSource(sql: SupportSQLiteQuery): List<FeedBean>
8181

8282
@Transaction
8383
@Query("SELECT ${FeedBean.URL_COLUMN} FROM $FEED_TABLE_NAME")

app/src/main/java/com/skyd/anivu/model/preference/Settings.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,13 @@ import com.skyd.anivu.ext.dataStore
1010
import com.skyd.anivu.ext.toSettings
1111
import com.skyd.anivu.model.preference.appearance.DarkModePreference
1212
import com.skyd.anivu.model.preference.appearance.ThemePreference
13+
import com.skyd.anivu.model.preference.behavior.article.ArticleSwipeLeftActionPreference
14+
import com.skyd.anivu.model.preference.behavior.article.ArticleTapActionPreference
15+
import com.skyd.anivu.model.preference.behavior.article.DeduplicateTitleInDescPreference
16+
import com.skyd.anivu.ui.local.LocalArticleSwipeLeftAction
17+
import com.skyd.anivu.ui.local.LocalArticleTapAction
1318
import com.skyd.anivu.ui.local.LocalDarkMode
19+
import com.skyd.anivu.ui.local.LocalDeduplicateTitleInDesc
1420
import com.skyd.anivu.ui.local.LocalIgnoreUpdateVersion
1521
import com.skyd.anivu.ui.local.LocalTheme
1622
import kotlinx.coroutines.Dispatchers
@@ -22,6 +28,10 @@ data class Settings(
2228
val darkMode: Int = DarkModePreference.default,
2329
// Update
2430
val ignoreUpdateVersion: Long = IgnoreUpdateVersionPreference.default,
31+
// Behavior
32+
val deduplicateTitleInDesc: Boolean = DeduplicateTitleInDescPreference.default,
33+
val articleTapAction: String = ArticleTapActionPreference.default,
34+
val articleSwipeLeftAction: String = ArticleSwipeLeftActionPreference.default,
2535
)
2636

2737
@Composable
@@ -38,6 +48,10 @@ fun SettingsProvider(
3848
LocalDarkMode provides settings.darkMode,
3949
// Update
4050
LocalIgnoreUpdateVersion provides settings.ignoreUpdateVersion,
51+
// Behavior
52+
LocalDeduplicateTitleInDesc provides settings.deduplicateTitleInDesc,
53+
LocalArticleTapAction provides settings.articleTapAction,
54+
LocalArticleSwipeLeftAction provides settings.articleSwipeLeftAction,
4155
) {
4256
content()
4357
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.skyd.anivu.model.preference.behavior.article
2+
3+
import android.content.Context
4+
import androidx.datastore.preferences.core.Preferences
5+
import androidx.datastore.preferences.core.stringPreferencesKey
6+
import com.skyd.anivu.R
7+
import com.skyd.anivu.base.BasePreference
8+
import com.skyd.anivu.ext.dataStore
9+
import com.skyd.anivu.ext.getOrDefault
10+
import com.skyd.anivu.ext.put
11+
import kotlinx.coroutines.CoroutineScope
12+
import kotlinx.coroutines.Dispatchers
13+
import kotlinx.coroutines.launch
14+
15+
object ArticleSwipeLeftActionPreference : BasePreference<String> {
16+
private const val ARTICLE_SWIPE_LEFT_ACTION = "articleSwipeLeftAction"
17+
18+
const val READ = "Read"
19+
const val SHOW_ENCLOSURES = "ShowEnclosures"
20+
21+
val values = arrayOf(READ, SHOW_ENCLOSURES)
22+
23+
override val default = SHOW_ENCLOSURES
24+
25+
val key = stringPreferencesKey(ARTICLE_SWIPE_LEFT_ACTION)
26+
27+
fun put(context: Context, scope: CoroutineScope, value: String) {
28+
scope.launch(Dispatchers.IO) {
29+
context.dataStore.put(key, value)
30+
}
31+
}
32+
33+
override fun fromPreferences(preferences: Preferences): String = preferences[key] ?: default
34+
35+
fun toDisplayName(
36+
context: Context,
37+
value: String = context.dataStore.getOrDefault(this),
38+
): String = when (value) {
39+
READ -> context.getString(R.string.article_action_read)
40+
SHOW_ENCLOSURES -> context.getString(R.string.article_action_show_enclosures)
41+
else -> context.getString(R.string.unknown)
42+
}
43+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.skyd.anivu.model.preference.behavior.article
2+
3+
import android.content.Context
4+
import androidx.datastore.preferences.core.Preferences
5+
import androidx.datastore.preferences.core.stringPreferencesKey
6+
import com.skyd.anivu.R
7+
import com.skyd.anivu.base.BasePreference
8+
import com.skyd.anivu.ext.dataStore
9+
import com.skyd.anivu.ext.getOrDefault
10+
import com.skyd.anivu.ext.put
11+
import kotlinx.coroutines.CoroutineScope
12+
import kotlinx.coroutines.Dispatchers
13+
import kotlinx.coroutines.launch
14+
15+
object ArticleTapActionPreference : BasePreference<String> {
16+
private const val ARTICLE_TAP_ACTION = "articleTapAction"
17+
18+
const val READ = "Read"
19+
const val SHOW_ENCLOSURES = "ShowEnclosures"
20+
21+
val values = arrayOf(READ, SHOW_ENCLOSURES)
22+
23+
override val default = READ
24+
25+
val key = stringPreferencesKey(ARTICLE_TAP_ACTION)
26+
27+
fun put(context: Context, scope: CoroutineScope, value: String) {
28+
scope.launch(Dispatchers.IO) {
29+
context.dataStore.put(key, value)
30+
}
31+
}
32+
33+
override fun fromPreferences(preferences: Preferences): String = preferences[key] ?: default
34+
35+
fun toDisplayName(
36+
context: Context,
37+
value: String = context.dataStore.getOrDefault(this),
38+
): String = when (value) {
39+
READ -> context.getString(R.string.article_action_read)
40+
SHOW_ENCLOSURES -> context.getString(R.string.article_action_show_enclosures)
41+
else -> context.getString(R.string.unknown)
42+
}
43+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.skyd.anivu.model.preference.behavior.article
2+
3+
import android.content.Context
4+
import androidx.datastore.preferences.core.Preferences
5+
import androidx.datastore.preferences.core.booleanPreferencesKey
6+
import com.skyd.anivu.base.BasePreference
7+
import com.skyd.anivu.ext.dataStore
8+
import com.skyd.anivu.ext.put
9+
import kotlinx.coroutines.CoroutineScope
10+
import kotlinx.coroutines.Dispatchers
11+
import kotlinx.coroutines.launch
12+
13+
object DeduplicateTitleInDescPreference : BasePreference<Boolean> {
14+
private const val DEDUPLICATE_TITLE_IN_DESC = "deduplicateTitleInDesc"
15+
override val default = true
16+
17+
val key = booleanPreferencesKey(DEDUPLICATE_TITLE_IN_DESC)
18+
19+
fun put(context: Context, scope: CoroutineScope, value: Boolean) {
20+
scope.launch(Dispatchers.IO) {
21+
context.dataStore.put(key, value)
22+
}
23+
}
24+
25+
override fun fromPreferences(preferences: Preferences): Boolean = preferences[key] ?: default
26+
}

app/src/main/java/com/skyd/anivu/model/repository/ArticleRepository.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import androidx.paging.Pager
44
import androidx.paging.PagingConfig
55
import androidx.paging.PagingData
66
import com.skyd.anivu.base.BaseRepository
7-
import com.skyd.anivu.model.bean.ArticleBean
87
import com.skyd.anivu.model.bean.ArticleWithEnclosureBean
98
import com.skyd.anivu.model.db.dao.ArticleDao
109
import com.skyd.anivu.model.db.dao.FeedDao

app/src/main/java/com/skyd/anivu/model/repository/SearchRepository.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class SearchRepository @Inject constructor(
5353
k = query,
5454
limit = { key to params.loadSize },
5555
)
56-
resultList.addAll(feedDao.getFeedList(feedSql))
56+
resultList.addAll(feedDao.getFeedListPagingSource(feedSql))
5757
val articleSql by lazy {
5858
genSql(
5959
tableName = ARTICLE_TABLE_NAME,

app/src/main/java/com/skyd/anivu/model/worker/deletearticle/DeleteArticleWorker.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,6 @@ class DeleteArticleWorker(context: Context, parameters: WorkerParameters) :
3636
}
3737

3838
companion object {
39-
const val uniqueWorkName = "deleteArticleWorker"
39+
const val UNIQUE_WORK_NAME = "deleteArticleWorker"
4040
}
4141
}

app/src/main/java/com/skyd/anivu/model/worker/deletearticle/Util.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ fun listenerDeleteArticleFrequency(context: Context) {
4040
)
4141
}.distinctUntilChanged().combine(
4242
WorkManager.getInstance(context)
43-
.getWorkInfosForUniqueWorkFlow(DeleteArticleWorker.uniqueWorkName)
43+
.getWorkInfosForUniqueWorkFlow(DeleteArticleWorker.UNIQUE_WORK_NAME)
4444
.distinctUntilChanged(),
4545
) { deleteArticleConfiguration, workInfos ->
4646
val workInfo = workInfos.firstOrNull()
@@ -103,7 +103,7 @@ fun startRssSyncWorker(
103103
deleteArticleFrequency: Long,
104104
) {
105105
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
106-
DeleteArticleWorker.uniqueWorkName,
106+
DeleteArticleWorker.UNIQUE_WORK_NAME,
107107
ExistingPeriodicWorkPolicy.KEEP,
108108
getDeleteArticleWorkRequest(
109109
deleteArticleFrequency = deleteArticleFrequency,
@@ -112,7 +112,7 @@ fun startRssSyncWorker(
112112
}
113113

114114
fun stopDeleteArticleWorker(context: Context) {
115-
WorkManager.getInstance(context).cancelUniqueWork(DeleteArticleWorker.uniqueWorkName)
115+
WorkManager.getInstance(context).cancelUniqueWork(DeleteArticleWorker.UNIQUE_WORK_NAME)
116116
}
117117

118118
fun getDeleteArticleWorkRequest(

app/src/main/java/com/skyd/anivu/model/worker/rsssync/RssSyncWorker.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,6 @@ class RssSyncWorker(context: Context, parameters: WorkerParameters) :
4646
}
4747

4848
companion object {
49-
const val uniqueWorkName = "rssSyncWorker"
49+
const val UNIQUE_WORK_NAME = "rssSyncWorker"
5050
}
5151
}

0 commit comments

Comments
 (0)