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

Commit d69c411b authored by Abhishek Aggarwal's avatar Abhishek Aggarwal
Browse files

refactor: Drop reflection and use exposed menthods from gplay api

parent 894e8429
Loading
Loading
Loading
Loading
+17 −58
Original line number Diff line number Diff line
@@ -17,73 +17,32 @@

package foundation.e.apps.data.playstore.utils

private const val INTERNAL_EXCEPTION_PREFIX = "com.aurora.gplayapi.exceptions.InternalException$"
private const val APP_NOT_FOUND_CLASS = "com.aurora.gplayapi.exceptions.InternalException\$AppNotFound"

private fun Throwable.isGplayInternalException(): Boolean {
    return javaClass.name.startsWith(INTERNAL_EXCEPTION_PREFIX)
}
import com.aurora.gplayapi.exceptions.InternalException

fun Throwable.isGplayInternalAppNotFound(): Boolean {
    return javaClass.name == APP_NOT_FOUND_CLASS
    return this is InternalException.AppNotFound
}

fun Throwable.gplayInternalExceptionHttpStatus(): Int? {
    if (!isGplayInternalException()) {
        return null
    return when (this) {
        is InternalException.AppNotFound -> code.takeIf { it > 0 }
        is InternalException.Server -> code.takeIf { it > 0 }
        else -> null
    }

    return readIntMember("code")
        ?: invokeIntGetter("Code")
}

fun Throwable.gplayInternalExceptionReason(): String? {
    if (!isGplayInternalException()) {
        return localizedMessage?.takeIf { it.isNotBlank() }
    }

    return readStringMember("reason")
        ?: invokeStringGetter("Reason")
}

private fun Throwable.readIntMember(name: String): Int? {
    return runCatching {
        javaClass.declaredFields
            .firstOrNull { it.name == name || it.name.startsWith("$name$") }
            ?.apply { isAccessible = true }
            ?.get(this) as? Int
    }.getOrNull()
}

private fun Throwable.readStringMember(name: String): String? {
    return runCatching {
        javaClass.declaredFields
            .firstOrNull { it.name == name || it.name.startsWith("$name$") }
            ?.apply { isAccessible = true }
            ?.get(this) as? String
    }.getOrNull()?.takeIf { it.isNotBlank() }
}

private fun Throwable.invokeIntGetter(memberNameSuffix: String): Int? {
    return runCatching {
        javaClass.declaredMethods
            .firstOrNull {
                (it.name == "get$memberNameSuffix" || it.name.startsWith("get$memberNameSuffix$")) &&
                    it.parameterCount == 0
            }
            ?.apply { isAccessible = true }
            ?.invoke(this) as? Int
    }.getOrNull()
}

private fun Throwable.invokeStringGetter(memberNameSuffix: String): String? {
    return runCatching {
        javaClass.declaredMethods
            .firstOrNull {
                (it.name == "get$memberNameSuffix" || it.name.startsWith("get$memberNameSuffix$")) &&
                    it.parameterCount == 0
            }
            ?.apply { isAccessible = true }
            ?.invoke(this) as? String
    }.getOrNull()?.takeIf { it.isNotBlank() }
    val resolvedReason = when (this) {
        is InternalException.AppNotFound -> this.reason
        is InternalException.AppNotPurchased -> this.reason
        is InternalException.AppNotSupported -> this.reason
        is InternalException.AppRemoved -> this.reason
        is InternalException.AuthException -> this.reason
        is InternalException.EmptyDownloads -> this.reason
        is InternalException.Server -> this.reason
        is InternalException.Unknown -> this.reason
        else -> localizedMessage
    }

    return resolvedReason?.takeIf { it.isNotBlank() }
}
+2 −0
Original line number Diff line number Diff line
@@ -33,8 +33,10 @@ import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config

@RunWith(RobolectricTestRunner::class)
@Config(sdk = [30])
class UserCapabilitiesProviderImplTest {

    @Test
+53 −0
Original line number Diff line number Diff line
package foundation.e.apps.data.playstore.utils

import com.aurora.gplayapi.exceptions.InternalException
import com.google.common.truth.Truth.assertThat
import org.junit.Test

class GplayInternalExceptionAdapterTest {

    @Test
    fun `app not found exposes status and reason`() {
        val exception = InternalException.AppNotFound(
            code = 404,
            reason = "missing",
        )

        assertThat(exception.isGplayInternalAppNotFound()).isTrue()
        assertThat(exception.gplayInternalExceptionHttpStatus()).isEqualTo(404)
        assertThat(exception.gplayInternalExceptionReason()).isEqualTo("missing")
    }

    @Test
    fun `app not found ignores zero status sentinel`() {
        val exception = InternalException.AppNotFound(
            code = 0,
            reason = "",
        )

        assertThat(exception.isGplayInternalAppNotFound()).isTrue()
        assertThat(exception.gplayInternalExceptionHttpStatus()).isNull()
        assertThat(exception.gplayInternalExceptionReason()).isNull()
    }

    @Test
    fun `server exposes status and reason`() {
        val exception = InternalException.Server(
            code = 500,
            reason = "server down",
        )

        assertThat(exception.isGplayInternalAppNotFound()).isFalse()
        assertThat(exception.gplayInternalExceptionHttpStatus()).isEqualTo(500)
        assertThat(exception.gplayInternalExceptionReason()).isEqualTo("server down")
    }

    @Test
    fun `non gplay exception falls back to localized message only`() {
        val exception = IllegalStateException("boom")

        assertThat(exception.isGplayInternalAppNotFound()).isFalse()
        assertThat(exception.gplayInternalExceptionHttpStatus()).isNull()
        assertThat(exception.gplayInternalExceptionReason()).isEqualTo("boom")
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -27,7 +27,7 @@ hiltWork = "1.2.0"
javaxInject = "1"
lifecycleExtensions = "1.1.1"
fragmentKtx = "1.8.5"
gplayapi = "3.5.8-b7878454"
gplayapi = "3.5.9-c398f64f"
gson = "2.11.0"
jacksonDataformatYaml = "2.17.0"
jsoup = "1.17.2"