Loading app/build.gradle +2 −0 Original line number Diff line number Diff line Loading @@ -214,6 +214,7 @@ dependencies { implementation(libs.navigation.fragment.ktx) implementation(libs.navigation.ui.ktx) implementation(libs.activity.ktx) implementation(libs.paging.runtime.ktx) // Material Design implementation(libs.material) Loading Loading @@ -304,6 +305,7 @@ dependencies { implementation libs.activity.compose implementation libs.lifecycle.viewmodel.compose implementation libs.runtime.livedata implementation libs.paging.compose // Android Studio Preview support for Compose implementation libs.compose.ui.tooling.preview Loading app/src/main/java/foundation/e/apps/data/cleanapk/CleanApkRetrofit.kt +24 −24 Original line number Diff line number Diff line Loading @@ -66,7 +66,7 @@ interface CleanApkRetrofit { @Query("keyword") keyword: String, @Query("source") source: String = APP_SOURCE_FOSS, @Query("type") type: String = APP_TYPE_ANY, @Query("nres") nres: Int = 20, @Query("nres") pageSize: Int = 20, @Query("page") page: Int = 1, @Query("by") by: String? = null, @Query("architectures") architectures: List<String>? = null, Loading app/src/main/java/foundation/e/apps/data/cleanapk/CleanApkSearchHelper.kt +37 −5 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package foundation.e.apps.data.cleanapk import foundation.e.apps.data.application.data.Application import foundation.e.apps.data.cleanapk.data.search.Search import foundation.e.apps.data.cleanapk.repositories.NUMBER_OF_ITEMS import foundation.e.apps.data.cleanapk.repositories.NUMBER_OF_PAGES import foundation.e.apps.data.enums.Source Loading @@ -36,16 +37,47 @@ class CleanApkSearchHelper @Inject constructor( appType: String ): List<Application> { return withContext(Dispatchers.IO) { val searchResult = cleanApkRetrofit.searchApps( getSearchResultPage( keyword = keyword, appSource = appSource, appType = appType, page = NUMBER_OF_PAGES, pageSize = NUMBER_OF_ITEMS, ).apps } } suspend fun getSearchResultPage( keyword: String, appSource: String, appType: String, page: Int, pageSize: Int, ): Search { return withContext(Dispatchers.IO) { val response = cleanApkRetrofit.searchApps( keyword = keyword, source = appSource, type = appType, nres = NUMBER_OF_ITEMS, page = NUMBER_OF_PAGES, pageSize = pageSize, page = page, architectures = SystemInfoProvider.getSupportedArchitectureList(), ) searchResult.body()?.apps.orEmpty() .map { it.apply { source = mapSource(it) } } check(response.isSuccessful) { "CleanAPK search failed: HTTP ${response.code()}" } val body = checkNotNull(response.body()) { "CleanAPK search failed: empty body" } check(body.success) { "CleanAPK search failed: success=false" } body.apps.forEach { app -> app.source = mapSource(app) } body } } Loading app/src/main/java/foundation/e/apps/data/cleanapk/data/search/Search.kt +4 −2 Original line number Diff line number Diff line /* * Apps Quickly and easily install Android apps onto your device! * Copyright (C) 2021 E FOUNDATION * Copyright (C) 2021-2026 e Foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by Loading @@ -14,14 +13,17 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. * */ package foundation.e.apps.data.cleanapk.data.search import com.google.gson.annotations.SerializedName import foundation.e.apps.data.application.data.Application data class Search( val apps: List<Application> = emptyList(), val numberOfResults: Int = -1, @SerializedName(value = "pages") val numberOfPages: Int = 0, val success: Boolean = false ) app/src/main/java/foundation/e/apps/data/search/CleanApkSearchPagingRepository.kt 0 → 100644 +48 −0 Original line number Diff line number Diff line /* * Copyright (C) 2026 e Foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. * */ package foundation.e.apps.data.search import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.PagingData import foundation.e.apps.data.application.data.Application import foundation.e.apps.data.cleanapk.CleanApkSearchHelper import kotlinx.coroutines.flow.Flow import javax.inject.Inject class CleanApkSearchPagingRepository @Inject constructor( private val cleanApkSearchHelper: CleanApkSearchHelper, ) : SearchPagingRepository { override fun cleanApkSearch(params: CleanApkSearchParams): Flow<PagingData<Application>> { return Pager( config = PagingConfig( pageSize = params.pageSize, enablePlaceholders = false, prefetchDistance = 2 ), pagingSourceFactory = { CleanApkSearchPagingSource( cleanApkSearchHelper = cleanApkSearchHelper, params = params ) } ).flow } } Loading
app/build.gradle +2 −0 Original line number Diff line number Diff line Loading @@ -214,6 +214,7 @@ dependencies { implementation(libs.navigation.fragment.ktx) implementation(libs.navigation.ui.ktx) implementation(libs.activity.ktx) implementation(libs.paging.runtime.ktx) // Material Design implementation(libs.material) Loading Loading @@ -304,6 +305,7 @@ dependencies { implementation libs.activity.compose implementation libs.lifecycle.viewmodel.compose implementation libs.runtime.livedata implementation libs.paging.compose // Android Studio Preview support for Compose implementation libs.compose.ui.tooling.preview Loading
app/src/main/java/foundation/e/apps/data/cleanapk/CleanApkRetrofit.kt +24 −24 Original line number Diff line number Diff line Loading @@ -66,7 +66,7 @@ interface CleanApkRetrofit { @Query("keyword") keyword: String, @Query("source") source: String = APP_SOURCE_FOSS, @Query("type") type: String = APP_TYPE_ANY, @Query("nres") nres: Int = 20, @Query("nres") pageSize: Int = 20, @Query("page") page: Int = 1, @Query("by") by: String? = null, @Query("architectures") architectures: List<String>? = null, Loading
app/src/main/java/foundation/e/apps/data/cleanapk/CleanApkSearchHelper.kt +37 −5 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package foundation.e.apps.data.cleanapk import foundation.e.apps.data.application.data.Application import foundation.e.apps.data.cleanapk.data.search.Search import foundation.e.apps.data.cleanapk.repositories.NUMBER_OF_ITEMS import foundation.e.apps.data.cleanapk.repositories.NUMBER_OF_PAGES import foundation.e.apps.data.enums.Source Loading @@ -36,16 +37,47 @@ class CleanApkSearchHelper @Inject constructor( appType: String ): List<Application> { return withContext(Dispatchers.IO) { val searchResult = cleanApkRetrofit.searchApps( getSearchResultPage( keyword = keyword, appSource = appSource, appType = appType, page = NUMBER_OF_PAGES, pageSize = NUMBER_OF_ITEMS, ).apps } } suspend fun getSearchResultPage( keyword: String, appSource: String, appType: String, page: Int, pageSize: Int, ): Search { return withContext(Dispatchers.IO) { val response = cleanApkRetrofit.searchApps( keyword = keyword, source = appSource, type = appType, nres = NUMBER_OF_ITEMS, page = NUMBER_OF_PAGES, pageSize = pageSize, page = page, architectures = SystemInfoProvider.getSupportedArchitectureList(), ) searchResult.body()?.apps.orEmpty() .map { it.apply { source = mapSource(it) } } check(response.isSuccessful) { "CleanAPK search failed: HTTP ${response.code()}" } val body = checkNotNull(response.body()) { "CleanAPK search failed: empty body" } check(body.success) { "CleanAPK search failed: success=false" } body.apps.forEach { app -> app.source = mapSource(app) } body } } Loading
app/src/main/java/foundation/e/apps/data/cleanapk/data/search/Search.kt +4 −2 Original line number Diff line number Diff line /* * Apps Quickly and easily install Android apps onto your device! * Copyright (C) 2021 E FOUNDATION * Copyright (C) 2021-2026 e Foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by Loading @@ -14,14 +13,17 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. * */ package foundation.e.apps.data.cleanapk.data.search import com.google.gson.annotations.SerializedName import foundation.e.apps.data.application.data.Application data class Search( val apps: List<Application> = emptyList(), val numberOfResults: Int = -1, @SerializedName(value = "pages") val numberOfPages: Int = 0, val success: Boolean = false )
app/src/main/java/foundation/e/apps/data/search/CleanApkSearchPagingRepository.kt 0 → 100644 +48 −0 Original line number Diff line number Diff line /* * Copyright (C) 2026 e Foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. * */ package foundation.e.apps.data.search import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.PagingData import foundation.e.apps.data.application.data.Application import foundation.e.apps.data.cleanapk.CleanApkSearchHelper import kotlinx.coroutines.flow.Flow import javax.inject.Inject class CleanApkSearchPagingRepository @Inject constructor( private val cleanApkSearchHelper: CleanApkSearchHelper, ) : SearchPagingRepository { override fun cleanApkSearch(params: CleanApkSearchParams): Flow<PagingData<Application>> { return Pager( config = PagingConfig( pageSize = params.pageSize, enablePlaceholders = false, prefetchDistance = 2 ), pagingSourceFactory = { CleanApkSearchPagingSource( cleanApkSearchHelper = cleanApkSearchHelper, params = params ) } ).flow } }