Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Performance improvements 🚀 #92

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,28 @@ android {

buildTypes {
debug {
isDebuggable = true
enableUnitTestCoverage = true
enableAndroidTestCoverage = true
// versionNameSuffix = "-release" // Optional: Adds a suffix to the version name for differentiation
buildConfigField("Boolean", "IS_DEVELOPER_MODE_ENABLED", "true")
}
create("benchmark") {
initWith(buildTypes.getByName("release"))
signingConfig = signingConfigs.getByName("debug")
matchingFallbacks += listOf("release")
}
release {
isMinifyEnabled = false
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
// Configures the signing with the debug key for now so that we can run the release build
signingConfig = signingConfigs.getByName("debug")
isDebuggable = false // Disables debugging for security reasons
isShrinkResources = true // Further reduces the size of the APK by removing unused resources
buildConfigField("Boolean", "IS_DEVELOPER_MODE_ENABLED", "false")
}
}

Expand Down
47 changes: 28 additions & 19 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools">

<application
android:name=".TangaApp"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
Expand All @@ -11,8 +12,11 @@
android:roundIcon="@mipmap/ic_launcher"
android:supportsRtl="true"
android:theme="@style/Theme.SplashScreenTheme"
android:name=".TangaApp"
tools:targetApi="31">
<profileable
android:shell="true"
tools:targetApi="29" />

<activity
android:name=".MainActivity"
android:exported="true"
Expand All @@ -26,23 +30,28 @@
<meta-data
android:name="android.app.lib_name"
android:value="" />
</activity>

<!-- Required: set your sentry.io project identifier (DSN) -->
<meta-data android:name="io.sentry.dsn" android:value="https://d3da1b415af3e6a3482e2c7fee0740a0@o4506014737104896.ingest.sentry.io/4506014893342720" />

<!-- enable automatic breadcrumbs for user interactions (clicks, swipes, scrolls) -->
<meta-data android:name="io.sentry.traces.user-interaction.enable" android:value="true" />
<meta-data android:name="io.sentry.breadcrumbs.user-interaction" android:value="true" />
<!-- enable screenshot for crashes (could contain sensitive/PII data) -->
<meta-data android:name="io.sentry.attach-screenshot" android:value="true" />
<!-- enable view hierarchy for crashes -->
<meta-data android:name="io.sentry.attach-view-hierarchy" android:value="true" />

<!-- enable the performance API by setting a sample-rate, adjust in production env -->
<meta-data android:name="io.sentry.traces.sample-rate" android:value="1.0" />
<!-- enable profiling when starting transactions, adjust in production env -->
<meta-data android:name="io.sentry.traces.profiling.sample-rate" android:value="1.0" />
</application>
</activity> <!-- Required: set your sentry.io project identifier (DSN) -->
<meta-data
android:name="io.sentry.dsn"
android:value="https://d3da1b415af3e6a3482e2c7fee0740a0@o4506014737104896.ingest.sentry.io/4506014893342720" /> <!-- enable automatic breadcrumbs for user interactions (clicks, swipes, scrolls) -->
<meta-data
android:name="io.sentry.traces.user-interaction.enable"
android:value="true" />
<meta-data
android:name="io.sentry.breadcrumbs.user-interaction"
android:value="true" /> <!-- enable screenshot for crashes (could contain sensitive/PII data) -->
<meta-data
android:name="io.sentry.attach-screenshot"
android:value="true" /> <!-- enable view hierarchy for crashes -->
<meta-data
android:name="io.sentry.attach-view-hierarchy"
android:value="true" /> <!-- enable the performance API by setting a sample-rate, adjust in production env -->
<meta-data
android:name="io.sentry.traces.sample-rate"
android:value="1.0" /> <!-- enable profiling when starting transactions, adjust in production env -->
<meta-data
android:name="io.sentry.traces.profiling.sample-rate"
android:value="1.0" />
</application>

</manifest>
26 changes: 26 additions & 0 deletions app/src/main/java/app/books/tanga/TangaApp.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package app.books.tanga

import android.app.Application
import android.os.StrictMode
import app.books.tanga.di.TimberTrees
import app.books.tanga.di.plantAll
import app.books.tanga.errors.TangaErrorTracker
Expand All @@ -22,5 +23,30 @@ class TangaApp : Application() {

// Initialize the error tracker
errorTracker.init()

configureStrictMode()
}

private fun configureStrictMode() {
if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyLog()
.penaltyFlashScreen()
.build()
)

StrictMode.setVmPolicy(
StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.detectActivityLeaks()
.penaltyLog()
.build()
)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package app.books.tanga.common.urls

import app.books.tanga.di.IoDispatcher
import app.books.tanga.entity.SummaryId
import com.google.firebase.ktx.Firebase
import com.google.firebase.storage.FirebaseStorage
Expand All @@ -11,7 +12,10 @@ import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.tasks.await
import kotlinx.coroutines.withContext
import timber.log.Timber

/**
Expand All @@ -35,7 +39,8 @@ interface DownloadUrlGenerator {
* using Firebase Storage.
*/
class StorageDownloadUrlGenerator @Inject constructor(
private val storage: FirebaseStorage
private val storage: FirebaseStorage,
@IoDispatcher private val ioDispatcher: CoroutineDispatcher
) : DownloadUrlGenerator {

/**
Expand All @@ -44,10 +49,12 @@ class StorageDownloadUrlGenerator @Inject constructor(
*/
override suspend fun generateCoverDownloadUrl(
summaryId: SummaryId
): String? = SummaryCoverUrlCache.get(summaryId) ?: getSummaryReference(summaryId).generateDownloadUrl(
path = SummaryFormatType.COVER.filename
).also { url ->
url?.let { SummaryCoverUrlCache.put(summaryId, it) }
): String? = withContext(ioDispatcher) {
SummaryCoverUrlCache.get(summaryId) ?: getSummaryReference(summaryId).generateDownloadUrl(
path = SummaryFormatType.COVER.filename
).also { url ->
url?.let { SummaryCoverUrlCache.put(summaryId, it) }
}
}

override suspend fun generateTextDownloadUrl(summaryId: SummaryId): String? = getSummaryReference(
Expand Down Expand Up @@ -86,7 +93,7 @@ class StorageDownloadUrlGenerator @Inject constructor(
* Singleton instance of [StorageDownloadUrlGenerator] for convenience in places where when can't use Hilt DI.
*/
val instance: StorageDownloadUrlGenerator
get() = StorageDownloadUrlGenerator(storage = Firebase.storage)
get() = StorageDownloadUrlGenerator(storage = Firebase.storage, ioDispatcher = Dispatchers.IO)
}
}

Expand Down
4 changes: 2 additions & 2 deletions app/src/main/java/app/books/tanga/di/GoogleSignInModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
.builder()
.setSupported(true)
.setServerClientId(clientId)
.setFilterByAuthorizedAccounts(true)
.setFilterByAuthorizedAccounts(false)

Check warning on line 41 in app/src/main/java/app/books/tanga/di/GoogleSignInModule.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/java/app/books/tanga/di/GoogleSignInModule.kt#L41

Added line #L41 was not covered by tests
.build()
).setAutoSelectEnabled(true)
.build()
Expand All @@ -55,7 +55,7 @@
.builder()
.setSupported(true)
.setServerClientId(clientId)
.setFilterByAuthorizedAccounts(true)
.setFilterByAuthorizedAccounts(false)

Check warning on line 58 in app/src/main/java/app/books/tanga/di/GoogleSignInModule.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/java/app/books/tanga/di/GoogleSignInModule.kt#L58

Added line #L58 was not covered by tests
.build()
).build()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class AnonymousAuthServiceImpl @Inject constructor(
@Suppress("TooGenericExceptionThrown")
override suspend fun signInAnonymously(): AuthResult {
val result = auth.signInAnonymously().await()
val firebaseUser = result.user ?: throw Throwable("Failed to sign in anonymously")
val firebaseUser = result.user ?: throw Throwable("Anonymous sign in returned null user")

val user = firebaseUser.toAnonymousUser()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class AuthenticationInteractor @Inject constructor(
val authResult = anonymousAuthService.signInAnonymously()
authResult.user
}.onFailure {
Timber.e("Anonymous sign in failed", it)
Timber.e(it, "Anonymous sign in failed")
return Result.failure(DomainError.AuthenticationError(it))
}

Expand Down Expand Up @@ -65,6 +65,7 @@ class AuthenticationInteractor @Inject constructor(
sessionManager.openSession(sessionId)
user
}.onFailure {
Timber.e(it, "Complete Google sign in failed")
return Result.failure(DomainError.UnableToSignInWithGoogleError(it))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
Expand Down Expand Up @@ -57,6 +58,15 @@

val pagerState = rememberPagerState()
val scope = rememberCoroutineScope()
val onNextClick: () -> Unit = remember {
{
scope.launch {

Check warning on line 63 in app/src/main/java/app/books/tanga/feature/onboarding/OnboardingScreen.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/java/app/books/tanga/feature/onboarding/OnboardingScreen.kt#L61-L63

Added lines #L61 - L63 were not covered by tests
if (pagerState.currentPage < MAX_PAGER_INDEX) {
pagerState.animateScrollToPage(pagerState.currentPage + 1)

Check warning on line 65 in app/src/main/java/app/books/tanga/feature/onboarding/OnboardingScreen.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/java/app/books/tanga/feature/onboarding/OnboardingScreen.kt#L65

Added line #L65 was not covered by tests
}
}
}

Check warning on line 68 in app/src/main/java/app/books/tanga/feature/onboarding/OnboardingScreen.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/java/app/books/tanga/feature/onboarding/OnboardingScreen.kt#L67-L68

Added lines #L67 - L68 were not covered by tests
}

Column(
modifier = modifier
Expand Down Expand Up @@ -85,11 +95,7 @@
FinishOnboardingButton(
modifier = Modifier.weight(1f),
pagerState = pagerState,
onNextClick = {
scope.launch {
pagerState.animateScrollToPage(pagerState.currentPage.inc())
}
},
onNextClick = onNextClick,

Check warning on line 98 in app/src/main/java/app/books/tanga/feature/onboarding/OnboardingScreen.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/java/app/books/tanga/feature/onboarding/OnboardingScreen.kt#L98

Added line #L98 was not covered by tests
onFinishClick = {
onboardingViewModel.onOnboardingCompleted()
navController.popBackStack()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@ import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.unmockkAll
import io.mockk.verify
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.tasks.await
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test

@OptIn(ExperimentalCoroutinesApi::class)
class StorageDownloadUrlGeneratorTest {

private lateinit var generator: StorageDownloadUrlGenerator
Expand All @@ -28,6 +31,8 @@ class StorageDownloadUrlGeneratorTest {
private val mockkStorageReferenceSummary = mockk<StorageReference>()
private val mockkStorageReferenceSummaryCover = mockk<StorageReference>()

private val testDispatcher = UnconfinedTestDispatcher()

@BeforeEach
fun setUp() {
mockkStatic("kotlinx.coroutines.tasks.TasksKt")
Expand All @@ -37,7 +42,7 @@ class StorageDownloadUrlGeneratorTest {
every { mockkStorageReferenceSummary.child(any()) } returns mockkStorageReferenceSummaryCover
coEvery { mockkStorageReferenceSummaryCover.downloadUrl.await().toString() } returns mockUrl

generator = StorageDownloadUrlGenerator(storage = mockStorage)
generator = StorageDownloadUrlGenerator(storage = mockStorage, testDispatcher)
}

@Test
Expand Down
1 change: 1 addition & 0 deletions benchmark/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
52 changes: 52 additions & 0 deletions benchmark/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
plugins {
alias(libs.plugins.androidTest)
alias(libs.plugins.kotlin.android)
}

android {
namespace = "app.books.tanga.benchmark"
compileSdk = 34

compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

kotlinOptions {
jvmTarget = "17"
}

defaultConfig {
minSdk = 24
targetSdk = 34

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

buildTypes {
// This benchmark buildType is used for benchmarking, and should function like your
// release build (for example, with minification on). It"s signed with a debug key
// for easy local/CI testing.
create("benchmark") {
isDebuggable = false
signingConfig = getByName("debug").signingConfig
matchingFallbacks += listOf("release")
}
}

targetProjectPath = ":app"
experimentalProperties["android.experimental.self-instrumenting"] = true
}

dependencies {
implementation(libs.android.test.junit)
implementation(libs.android.espresso.core)
implementation(libs.uiautomator)
implementation(libs.benchmark.macro.junit4)
}

androidComponents {
beforeVariants(selector().all()) {
it.enable = it.buildType == "benchmark"
}
}
1 change: 1 addition & 0 deletions benchmark/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<manifest />
Loading
Loading