Skip to content

Commit cf1d0d8

Browse files
committed
aa
1 parent f2b0416 commit cf1d0d8

7 files changed

Lines changed: 203 additions & 26 deletions

File tree

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package ceui.pixiv.paging
2+
3+
import androidx.paging.PagingSource
4+
import androidx.paging.PagingState
5+
import ceui.pixiv.db.GeneralEntity
6+
7+
8+
class NoOpPagingSource : PagingSource<String, GeneralEntity>() {
9+
10+
override fun getRefreshKey(state: PagingState<String, GeneralEntity>): String? {
11+
return null
12+
}
13+
14+
override suspend fun load(params: LoadParams<String>): LoadResult<String, GeneralEntity> {
15+
return LoadResult.Error(RuntimeException("Hello world"))
16+
}
17+
}

app/src/main/java/ceui/pixiv/paging/PagingViewModel.kt

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,38 +4,61 @@ import androidx.fragment.app.Fragment
44
import androidx.fragment.app.viewModels
55
import androidx.lifecycle.ViewModel
66
import androidx.lifecycle.ViewModelProvider
7+
import androidx.lifecycle.asFlow
78
import androidx.lifecycle.viewModelScope
89
import androidx.paging.ExperimentalPagingApi
910
import androidx.paging.Pager
1011
import androidx.paging.PagingConfig
12+
import androidx.paging.PagingData
1113
import androidx.paging.cachedIn
1214
import androidx.paging.flatMap
1315
import ceui.lisa.database.AppDatabase
1416
import ceui.lisa.models.ModelObject
17+
import ceui.loxia.requireNetworkStateManager
18+
import ceui.pixiv.utils.NetworkStateManager
1519
import kotlinx.coroutines.ExperimentalCoroutinesApi
1620
import kotlinx.coroutines.flow.MutableStateFlow
21+
import kotlinx.coroutines.flow.combine
1722
import kotlinx.coroutines.flow.flatMapLatest
23+
import kotlinx.coroutines.flow.flowOf
1824
import kotlinx.coroutines.flow.map
1925

2026
class PagingViewModel<ObjectT : ModelObject>(
2127
private val db: AppDatabase,
28+
private val networkStateManager: NetworkStateManager,
2229
private val repository: PagingAPIRepository<ObjectT>,
2330
) : ViewModel() {
2431

2532
private val refreshTrigger = MutableStateFlow(0)
33+
private val canAccessGoogle = networkStateManager.canAccessGoogle.asFlow()
2634

2735
@OptIn(ExperimentalPagingApi::class, ExperimentalCoroutinesApi::class)
28-
val pager = refreshTrigger.flatMapLatest {
29-
Pager(
30-
config = PagingConfig(
31-
pageSize = 30,
32-
initialLoadSize = 30,
33-
prefetchDistance = 0
34-
),
35-
remoteMediator = PagingRemoteMediator(db, repository, repository.recordType),
36-
pagingSourceFactory = { db.generalDao().pagingSource(repository.recordType) }
37-
).flow.map { pagingData ->
38-
pagingData.flatMap(repository::mapper)
36+
val pager = combine(refreshTrigger, canAccessGoogle) { _, canAccess ->
37+
canAccess
38+
}.flatMapLatest { canAccess ->
39+
if (!canAccess) {
40+
Pager(
41+
config = PagingConfig(
42+
pageSize = 30,
43+
initialLoadSize = 30,
44+
prefetchDistance = 0
45+
),
46+
pagingSourceFactory = { NoOpPagingSource() }
47+
).flow.map { pagingData ->
48+
pagingData.flatMap(repository::mapper)
49+
}
50+
} else {
51+
Pager(
52+
config = PagingConfig(
53+
pageSize = 30,
54+
initialLoadSize = 30,
55+
prefetchDistance = 0
56+
),
57+
remoteMediator = PagingRemoteMediator(db, repository, repository.recordType),
58+
pagingSourceFactory = { db.generalDao().pagingSource(repository.recordType) }
59+
).flow.map { pagingData ->
60+
pagingData.flatMap(repository::mapper)
61+
}
3962
}
4063
}.cachedIn(viewModelScope)
4164

@@ -53,8 +76,9 @@ inline fun <ObjectT : ModelObject> Fragment.pagingViewModel(
5376
object : ViewModelProvider.Factory {
5477
override fun <T : ViewModel> create(modelClass: Class<T>): T {
5578
val database = AppDatabase.getAppDatabase(requireContext())
79+
val networkStateManager = requireNetworkStateManager()
5680
val repository = repositoryProducer()
57-
return PagingViewModel(database, repository) as T
81+
return PagingViewModel(database, networkStateManager, repository) as T
5882
}
5983
}
6084
}
@@ -69,9 +93,10 @@ inline fun <ArgsT, ObjectT : ModelObject> Fragment.pagingViewModel(
6993
object : ViewModelProvider.Factory {
7094
override fun <T : ViewModel> create(modelClass: Class<T>): T {
7195
val database = AppDatabase.getAppDatabase(requireContext())
96+
val networkStateManager = requireNetworkStateManager()
7297
val args = argsProducer()
7398
val repository = repositoryProducer(args)
74-
return PagingViewModel(database, repository) as T
99+
return PagingViewModel(database, networkStateManager, repository) as T
75100
}
76101
}
77102
}

app/src/main/java/ceui/pixiv/ui/common/PixivFragment.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,10 +394,20 @@ fun <ObjectT : ModelObject> Fragment.setUpPagedList(
394394
}
395395
}
396396

397+
binding.openVpn.setOnClick {
398+
openClashApp(requireContext())
399+
}
400+
requireNetworkStateManager().canAccessGoogle.observe(viewLifecycleOwner) { canAccessGoogle ->
401+
binding.openVpn.isVisible = !canAccessGoogle
402+
binding.errorRetryButton.isVisible = canAccessGoogle
403+
}
404+
397405
viewLifecycleOwner.lifecycleScope.launch {
398406
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
399407
adapter.loadStateFlow.collectLatest { loadStates ->
400408
binding.refreshLayout.isRefreshing = loadStates.refresh is LoadState.Loading
409+
binding.errorLayout.isVisible = loadStates.refresh is LoadState.Error
410+
Timber.d("dsadsdsadasaw2 ${loadStates}")
401411
}
402412
}
403413
}

app/src/main/java/ceui/pixiv/ui/home/HomeViewPagerFragment.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import android.view.animation.OvershootInterpolator
77
import androidx.activity.OnBackPressedCallback
88
import androidx.core.view.ViewCompat
99
import androidx.core.view.WindowInsetsCompat
10+
import androidx.core.view.isVisible
1011
import androidx.core.view.updateLayoutParams
1112
import androidx.fragment.app.Fragment
1213
import androidx.fragment.app.viewModels
@@ -15,6 +16,8 @@ import androidx.viewpager2.widget.ViewPager2
1516
import ceui.lisa.R
1617
import ceui.lisa.databinding.FragmentHomeViewpagerBinding
1718
import ceui.lisa.utils.Common
19+
import ceui.loxia.RefreshState
20+
import ceui.loxia.openClashApp
1821
import ceui.loxia.pushFragment
1922
import ceui.loxia.requireNetworkStateManager
2023
import ceui.pixiv.session.SessionManager
@@ -75,8 +78,9 @@ class HomeViewPagerFragment : PixivFragment(R.layout.fragment_home_viewpager), V
7578
})
7679

7780
val networkManager = requireNetworkStateManager()
78-
networkManager.canAccessGoogle.observe(viewLifecycleOwner) {
79-
Timber.d("dsasdadsw2 canAccessGoogle: ${it}")
81+
82+
networkManager.refreshState.observe(viewLifecycleOwner) { state ->
83+
binding.networkStateLoading.isVisible = state is RefreshState.LOADING
8084
}
8185

8286
binding.userIcon.setOnClick {

app/src/main/java/ceui/pixiv/utils/NetworkStateManager.kt

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import android.net.NetworkRequest
88
import android.os.Build
99
import androidx.lifecycle.LiveData
1010
import androidx.lifecycle.MutableLiveData
11+
import ceui.loxia.RefreshState
1112
import ceui.pixiv.utils.NetworkStateManager.NetworkType
1213
import kotlinx.coroutines.Dispatchers
1314
import kotlinx.coroutines.MainScope
@@ -39,6 +40,9 @@ class NetworkStateManager(private val context: Context) : INetworkState {
3940
private val _canAccessGoogle = MutableLiveData<Boolean>()
4041
val canAccessGoogle: LiveData<Boolean> = _canAccessGoogle
4142

43+
private val _refreshState = MutableLiveData<RefreshState>()
44+
val refreshState: LiveData<RefreshState> = _refreshState
45+
4246
private val connectivityManager: ConnectivityManager =
4347
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
4448

@@ -106,21 +110,31 @@ class NetworkStateManager(private val context: Context) : INetworkState {
106110
}
107111
}
108112

109-
private suspend fun canAccessGoogle(): Boolean = withContext(Dispatchers.IO) {
113+
private suspend fun canAccessGoogle(): Boolean = withContext(Dispatchers.Main) {
110114
try {
111-
val client = OkHttpClient.Builder()
112-
.callTimeout(2, TimeUnit.SECONDS)
113-
.build()
115+
_refreshState.value = RefreshState.LOADING()
114116

115-
val request = Request.Builder()
116-
.url("https://www.google.com/generate_204") // ✅ 专门用于网络连通性检测的轻量接口
117-
.build()
117+
val canAccess = withContext(Dispatchers.IO) {
118+
val client = OkHttpClient.Builder()
119+
.callTimeout(2, TimeUnit.SECONDS)
120+
.build()
121+
122+
val request = Request.Builder()
123+
.url("https://www.google.com/generate_204") // ✅ 专门用于网络连通性检测的轻量接口
124+
.build()
125+
126+
val response = client.newCall(request).execute()
127+
response.code == 204
128+
}
118129

119-
val response = client.newCall(request).execute()
120-
return@withContext response.code == 204 // Google 返回 204 表示成功连接
121-
} catch (e: IOException) {
122-
Timber.w("Google connectivity check failed: ${e.message}")
130+
_refreshState.value = RefreshState.LOADED(hasContent = canAccess, hasNext = false)
131+
return@withContext canAccess // Google 返回 204 表示成功连接
132+
} catch (ex: IOException) {
133+
Timber.w("Google connectivity check failed: ${ex.message}")
134+
_refreshState.value = RefreshState.ERROR(ex)
123135
return@withContext false
136+
} finally {
137+
_taskMutex.unlock() // 释放锁
124138
}
125139
}
126140
}

app/src/main/res/layout/fragment_home_viewpager.xml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,31 @@
103103
</androidx.appcompat.widget.Toolbar>
104104
</com.google.android.material.appbar.CollapsingToolbarLayout>
105105

106+
<FrameLayout
107+
android:layout_width="match_parent"
108+
android:layout_height="wrap_content">
109+
110+
<LinearLayout
111+
android:id="@+id/network_state_loading"
112+
android:layout_width="wrap_content"
113+
android:visibility="gone"
114+
android:gravity="center"
115+
android:layout_gravity="center"
116+
android:layout_height="wrap_content">
117+
118+
<ProgressBar
119+
android:id="@+id/progress_bar"
120+
android:layout_width="24dp"
121+
android:layout_marginEnd="4dp"
122+
android:layout_height="24dp"/>
123+
124+
<TextView
125+
android:textColor="@color/text00"
126+
android:text="连接中"
127+
android:layout_width="wrap_content"
128+
android:layout_height="wrap_content"/>
129+
</LinearLayout>
130+
</FrameLayout>
106131
</com.google.android.material.appbar.AppBarLayout>
107132

108133

app/src/main/res/layout/fragment_paged_list.xml

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,88 @@
2222
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
2323

2424

25+
<FrameLayout
26+
android:layout_width="wrap_content"
27+
android:layout_height="wrap_content"
28+
android:layout_marginHorizontal="20dp"
29+
app:layout_constraintBottom_toBottomOf="parent"
30+
app:layout_constraintEnd_toEndOf="parent"
31+
app:layout_constraintStart_toStartOf="parent"
32+
app:layout_constraintTop_toTopOf="parent">
33+
34+
<LinearLayout
35+
android:id="@+id/empty_layout"
36+
android:layout_width="wrap_content"
37+
android:layout_height="wrap_content"
38+
android:layout_marginHorizontal="50dp"
39+
android:layout_gravity="center"
40+
android:gravity="center"
41+
android:orientation="vertical"
42+
android:visibility="gone">
43+
44+
<ImageView
45+
android:id="@+id/empty_image"
46+
android:layout_width="wrap_content"
47+
android:layout_height="wrap_content"
48+
android:src="@drawable/icon_empty_state" />
49+
50+
<TextView
51+
android:layout_width="wrap_content"
52+
android:layout_height="wrap_content"
53+
android:layout_marginTop="10dp"
54+
android:gravity="center"
55+
android:text="@string/string_48"
56+
android:textColor="@color/text10" />
57+
</LinearLayout>
58+
59+
<LinearLayout
60+
android:id="@+id/error_layout"
61+
android:layout_width="wrap_content"
62+
android:layout_height="wrap_content"
63+
android:layout_marginHorizontal="50dp"
64+
android:layout_gravity="center"
65+
android:gravity="center"
66+
android:orientation="vertical"
67+
android:visibility="gone">
68+
69+
<Button
70+
android:id="@+id/error_retry_button"
71+
android:layout_width="wrap_content"
72+
android:layout_height="wrap_content"
73+
android:layout_gravity="center_horizontal"
74+
android:layout_marginBottom="12dp"
75+
android:background="@drawable/refresh_button_background"
76+
android:gravity="center"
77+
android:minHeight="0dp"
78+
android:paddingHorizontal="20dp"
79+
android:paddingVertical="8dp"
80+
android:stateListAnimator="@animator/button_press_alpha"
81+
android:text="@string/retry"
82+
android:textAllCaps="false"
83+
android:textColor="@color/colorWhite60"
84+
android:textSize="15sp" />
85+
86+
<ceui.loxia.ProgressTextButton
87+
android:id="@+id/open_vpn"
88+
android:visibility="gone"
89+
style="@style/GreenShiningButton"
90+
android:layout_gravity="center"
91+
android:paddingHorizontal="12dp"
92+
android:text="打开VPN"
93+
android:layout_width="wrap_content"
94+
android:layout_height="36dp"/>
95+
96+
<TextView
97+
android:id="@+id/error_text"
98+
android:layout_width="wrap_content"
99+
android:gravity="center"
100+
android:layout_height="wrap_content"
101+
android:layout_marginTop="10dp"
102+
android:text="@string/string_48"
103+
android:textColor="@color/text10" />
104+
</LinearLayout>
105+
</FrameLayout>
106+
25107
<View
26108
android:id="@+id/top_shadow"
27109
android:layout_width="match_parent"

0 commit comments

Comments
 (0)