Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Verified Commit d49e0d96 authored by Fahim M. Choudhury's avatar Fahim M. Choudhury
Browse files

refactor: move Application -> AppInstall conversion logic to AppInstallRequestFactory

Move AppInstall construction into a focused factory so request mapping can be tested directly without changing how update detection or enqueueing works.
parent 5abb003e
Loading
Loading
Loading
Loading
+3 −24
Original line number Diff line number Diff line
@@ -27,7 +27,6 @@ import foundation.e.apps.data.ResultSupreme
import foundation.e.apps.data.application.ApplicationRepository
import foundation.e.apps.data.application.data.Application
import foundation.e.apps.data.enums.ResultStatus
import foundation.e.apps.data.enums.Source
import foundation.e.apps.data.enums.Status
import foundation.e.apps.data.enums.Type
import foundation.e.apps.data.event.AppEvent
@@ -46,7 +45,7 @@ import kotlinx.coroutines.DelicateCoroutinesApi
import timber.log.Timber
import javax.inject.Inject

@Suppress("LongParameterList")
@Suppress("LongParameterList") // FIXME: Remove suppression and fix detekt
class AppInstallProcessor @Inject constructor(
    @ApplicationContext private val context: Context,
    private val appInstallComponents: AppInstallComponents,
@@ -59,6 +58,7 @@ class AppInstallProcessor @Inject constructor(
    private val networkStatusChecker: NetworkStatusChecker,
    private val appInstallAgeLimitGate: AppInstallAgeLimitGate,
    private val appInstallWorkRunner: AppInstallWorkRunner,
    private val appInstallRequestFactory: AppInstallRequestFactory,
) {
    @Inject
    lateinit var appManager: AppManager
@@ -73,28 +73,7 @@ class AppInstallProcessor @Inject constructor(
        application: Application,
        isAnUpdate: Boolean = false
    ): Boolean {
        val appInstall = AppInstall(
            application._id,
            application.source,
            application.status,
            application.name,
            application.package_name,
            mutableListOf(),
            mutableMapOf(),
            application.status,
            application.type,
            application.icon_image_path,
            application.latest_version_code,
            application.offer_type,
            application.isFree,
            application.originalSize
        ).also {
            it.contentRating = application.contentRating
        }

        if (appInstall.type == Type.PWA || application.source == Source.SYSTEM_APP) {
            appInstall.downloadURLList = mutableListOf(application.url)
        }
        val appInstall = appInstallRequestFactory.create(application)

        val isUpdate = isAnUpdate ||
            application.status == Status.UPDATABLE ||
+54 −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.install.workmanager

import foundation.e.apps.data.application.data.Application
import foundation.e.apps.data.enums.Source
import foundation.e.apps.data.enums.Type
import foundation.e.apps.data.install.models.AppInstall
import javax.inject.Inject

class AppInstallRequestFactory @Inject constructor() {
    fun create(application: Application): AppInstall {
        val appInstall = AppInstall(
            application._id,
            application.source,
            application.status,
            application.name,
            application.package_name,
            mutableListOf(),
            mutableMapOf(),
            application.status,
            application.type,
            application.icon_image_path,
            application.latest_version_code,
            application.offer_type,
            application.isFree,
            application.originalSize
        ).also {
            it.contentRating = application.contentRating
        }

        if (appInstall.type == Type.PWA || application.source == Source.SYSTEM_APP) {
            appInstall.downloadURLList = mutableListOf(application.url)
        }

        return appInstall
    }
}
+7 −2
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import foundation.e.apps.data.install.AppManagerWrapper
import foundation.e.apps.data.install.models.AppInstall
import foundation.e.apps.data.install.notification.StorageNotificationManager
import foundation.e.apps.data.install.workmanager.AppInstallAgeLimitGate
import foundation.e.apps.data.install.workmanager.AppInstallRequestFactory
import foundation.e.apps.data.install.workmanager.AppInstallProcessor
import foundation.e.apps.data.install.workmanager.AppInstallWorkRunner
import foundation.e.apps.data.install.workmanager.AppUpdateCompletionHandler
@@ -119,6 +120,7 @@ class AppInstallProcessorTest {
    private lateinit var appInstallAgeLimitGate: AppInstallAgeLimitGate
    private lateinit var appUpdateCompletionHandler: AppUpdateCompletionHandler
    private lateinit var appInstallWorkRunner: AppInstallWorkRunner
    private lateinit var appInstallRequestFactory: AppInstallRequestFactory

    @Before
    fun setup() {
@@ -130,6 +132,7 @@ class AppInstallProcessorTest {
        coEvery { sessionRepository.awaitUser() } returns User.NO_GOOGLE
        coEvery { playStoreAuthStore.awaitAuthData() } returns null
        appEventDispatcher = FakeAppEventDispatcher()
        appInstallRequestFactory = AppInstallRequestFactory()
        storageSpaceChecker = mockk(relaxed = true)
        parentalControlAuthGateway = mockk(relaxed = true)
        updatesTracker = mockk(relaxed = true)
@@ -175,7 +178,8 @@ class AppInstallProcessorTest {
            storageSpaceChecker,
            networkStatusChecker,
            appInstallAgeLimitGate,
            appInstallWorkRunner
            appInstallWorkRunner,
            appInstallRequestFactory
        )
    }

@@ -666,7 +670,8 @@ class AppInstallProcessorTest {
            storageSpaceChecker,
            networkStatusChecker,
            ageLimitGate,
            workRunner
            workRunner,
            appInstallRequestFactory
        )
    }
}
+108 −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.installProcessor

import com.aurora.gplayapi.data.models.ContentRating
import foundation.e.apps.data.application.data.Application
import foundation.e.apps.data.enums.Source
import foundation.e.apps.data.enums.Status
import foundation.e.apps.data.enums.Type
import foundation.e.apps.data.install.workmanager.AppInstallRequestFactory
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test

class AppInstallRequestFactoryTest {
    private lateinit var factory: AppInstallRequestFactory

    @Before
    fun setup() {
        factory = AppInstallRequestFactory()
    }

    @Test
    fun create_copiesExpectedFields() {
        val application = Application(
            _id = "123",
            source = Source.PLAY_STORE,
            status = Status.AWAITING,
            name = "Example",
            package_name = "com.example.app",
            type = Type.NATIVE,
            icon_image_path = "icon.png",
            latest_version_code = 42,
            offer_type = 1,
            isFree = false,
            originalSize = 2048L
        )

        val appInstall = factory.create(application)

        assertEquals("123", appInstall.id)
        assertEquals(Source.PLAY_STORE, appInstall.source)
        assertEquals(Status.AWAITING, appInstall.status)
        assertEquals("Example", appInstall.name)
        assertEquals("com.example.app", appInstall.packageName)
        assertEquals(Type.NATIVE, appInstall.type)
        assertEquals("icon.png", appInstall.iconImageUrl)
        assertEquals(42, appInstall.versionCode)
        assertEquals(1, appInstall.offerType)
        assertEquals(false, appInstall.isFree)
        assertEquals(2048L, appInstall.appSize)
    }

    @Test
    fun create_setsContentRating() {
        val contentRating = ContentRating()
        val application = Application(contentRating = contentRating)

        val appInstall = factory.create(application)

        assertEquals(contentRating, appInstall.contentRating)
    }

    @Test
    fun create_initializesDirectUrlForPwa() {
        val application = Application(type = Type.PWA, url = "https://example.com")

        val appInstall = factory.create(application)

        assertEquals(mutableListOf("https://example.com"), appInstall.downloadURLList)
    }

    @Test
    fun create_initializesDirectUrlForSystemApp() {
        val application = Application(source = Source.SYSTEM_APP, url = "file://app.apk")

        val appInstall = factory.create(application)

        assertEquals(mutableListOf("file://app.apk"), appInstall.downloadURLList)
    }

    @Test
    fun create_doesNotForceDirectUrlForNativeNonSystemApp() {
        val application =
            Application(source = Source.PLAY_STORE, type = Type.NATIVE, url = "ignored")

        val appInstall = factory.create(application)

        assertTrue(appInstall.downloadURLList.isEmpty())
    }
}