Skip to content

Commit

Permalink
Merge pull request #133 from lihenggui/modularization
Browse files Browse the repository at this point in the history
Implement rule detail screen
  • Loading branch information
lihenggui committed Feb 23, 2023
2 parents fc7e240 + d91f07e commit 407cbd1
Show file tree
Hide file tree
Showing 23 changed files with 196 additions and 71 deletions.
Binary file added .idea/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/*
* Copyright 2023 Blocker
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -16,14 +17,25 @@

package com.merxury.blocker.core.analytics

import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.analytics.ktx.analytics
import com.google.firebase.ktx.Firebase
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
abstract class AnalyticsModule {
@Binds
abstract fun bindsAnalyticsHelper(analyticsHelperImpl: FirebaseAnalyticsHelper): AnalyticsHelper

companion object {
@Provides
@Singleton
fun provideFirebaseAnalytics(): FirebaseAnalytics { return Firebase.analytics }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,16 @@

package com.merxury.blocker.core.analytics

import com.google.firebase.analytics.ktx.analytics
import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.analytics.ktx.logEvent
import com.google.firebase.ktx.Firebase
import javax.inject.Inject
import javax.inject.Singleton

/**
* Implementation of `AnalyticsHelper` which logs events to a Firebase backend.
*/
@Singleton
class FirebaseAnalyticsHelper @Inject constructor() : AnalyticsHelper {

private val firebaseAnalytics = Firebase.analytics
class FirebaseAnalyticsHelper @Inject constructor(
private val firebaseAnalytics: FirebaseAnalytics,
) : AnalyticsHelper {

override fun logEvent(event: AnalyticsEvent) {
firebaseAnalytics.logEvent(event.type) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ interface GeneralRuleRepository {
*/
fun getGeneralRules(): Flow<List<GeneralRule>>

fun getGeneralRule(id: Int): Flow<GeneralRule>

/**
* Update the general rule from the backend API
* And emit results in a flow for application to listen
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.withContext
import timber.log.Timber
Expand All @@ -47,6 +48,11 @@ class OfflineFirstGeneralRuleRepository @Inject constructor(
.map { it.map(GeneralRuleEntity::asExternalModel) }
}

override fun getGeneralRule(id: Int): Flow<GeneralRule> {
return generalRuleDao.getGeneralRuleEntity(id)
.mapNotNull { it?.asExternalModel() }
}

override fun updateGeneralRule(): Flow<Result<Unit>> = flow {
try {
val networkRule = network.getGeneralRules()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ interface GeneralRuleDao {
@Query("SELECT * FROM general_rules WHERE name LIKE '%' || :keyword || '%'")
fun searchGeneralRule(keyword: String): Flow<List<GeneralRuleEntity>>

@Query("SELECT * FROM general_rules WHERE id = :id")
fun getGeneralRuleEntity(id: Int): Flow<GeneralRuleEntity?>

/**
* Deletes rows in the db matching the specified [ids]
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,14 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.layout.width
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
Expand All @@ -46,6 +47,7 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Devices
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
Expand Down Expand Up @@ -87,7 +89,7 @@ fun BlockerCollapsingTopAppBar(
modifier = modifier,
) {
Box(
modifier = Modifier
modifier = modifier
.statusBarsPadding()
.padding(horizontal = contentPadding)
.fillMaxSize(),
Expand All @@ -113,8 +115,10 @@ fun BlockerCollapsingTopAppBar(
overflow = TextOverflow.Ellipsis,
)
Row(
modifier = Modifier.wrapContentSize(),
horizontalArrangement = Arrangement.spacedBy(contentPadding),
modifier = modifier
.fillMaxWidth()
.padding(end = contentPadding),
horizontalArrangement = Arrangement.End,
verticalAlignment = Alignment.CenterVertically,
) {
if (actions != null) {
Expand Down Expand Up @@ -206,7 +210,7 @@ private fun CollapsingToolbarLayout(
),
)
actionsIcon.placeRelative(
x = constraints.maxWidth - actionsIcon.width,
x = navigationIcon.width,
y = MinToolbarHeight.roundToPx() / 2 - actionsIcon.height / 2,
)
subtitle.placeRelative(
Expand Down Expand Up @@ -267,6 +271,7 @@ fun CollapsingToolbarCollapsedPreview() {
tint = MaterialTheme.colorScheme.onSurface,
)
}
Spacer(modifier = Modifier.width(contentPadding))
IconButton(
onClick = {},
modifier = Modifier.then(Modifier.size(24.dp)),
Expand Down Expand Up @@ -306,6 +311,7 @@ fun CollapsingToolbarHalfwayPreview() {
tint = MaterialTheme.colorScheme.onSurface,
)
}
Spacer(modifier = Modifier.width(contentPadding))
IconButton(
onClick = {},
modifier = Modifier.then(Modifier.size(24.dp)),
Expand All @@ -328,6 +334,7 @@ fun CollapsingToolbarHalfwayPreview() {
}

@Preview
@Preview(device = Devices.TABLET)
@Composable
fun CollapsingToolbarExpandedPreview() {
BlockerTheme {
Expand All @@ -339,6 +346,7 @@ fun CollapsingToolbarExpandedPreview() {
keyword = TextFieldValue("blocker"),
onValueChange = {},
onClearClick = {},
modifier = Modifier.weight(1f),
)
IconButton(
onClick = {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.merxury.blocker.core.ui.component

import com.merxury.blocker.core.model.ComponentType
import com.merxury.blocker.core.model.data.ComponentInfo

data class ComponentItem(
val name: String,
Expand All @@ -30,3 +31,14 @@ data class ComponentItem(
) {
fun enabled() = !(pmBlocked || ifwBlocked)
}

fun ComponentInfo.toComponentItem(isRunning: Boolean = false) = ComponentItem(
name = name,
simpleName = simpleName,
packageName = packageName,
type = type,
pmBlocked = pmBlocked,
ifwBlocked = ifwBlocked,
description = description,
isRunning = isRunning,
)
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
package com.merxury.blocker.core.ui.rule

import android.content.res.Configuration
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
Expand Down Expand Up @@ -49,7 +49,7 @@ import com.merxury.blocker.core.ui.R.string
import com.merxury.blocker.core.ui.applist.AppIcon
import com.merxury.blocker.core.ui.applist.model.AppItem
import com.merxury.blocker.core.ui.component.ComponentItem
import com.merxury.blocker.core.ui.component.ComponentList
import com.merxury.blocker.core.ui.component.ComponentListItem

@Composable
fun MatchedComponentItem(
Expand All @@ -70,43 +70,47 @@ fun MatchedComponentItem(
} else {
BlockerIcons.ExpandMore
}
Box {
Column(
Column(modifier = modifier.animateContentSize()) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = modifier
.fillMaxWidth(),
.fillMaxWidth()
.clickable { expanded = !expanded }
.padding(horizontal = 16.dp, vertical = 8.dp),
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = modifier
.fillMaxWidth()
.clickable { }
.padding(horizontal = 16.dp, vertical = 8.dp),
) {
AppIcon(ruleMatchedApp.app.packageInfo, iconModifier.size(48.dp))
Spacer(modifier = Modifier.width(16.dp))
MatchedAppInfo(
label = ruleMatchedApp.app.label,
matchedComponentCount = ruleMatchedApp.componentList.size,
AppIcon(ruleMatchedApp.app.packageInfo, iconModifier.size(48.dp))
Spacer(modifier = Modifier.width(16.dp))
MatchedAppInfo(
label = ruleMatchedApp.app.label,
matchedComponentCount = ruleMatchedApp.componentList.size,
modifier = modifier.weight(1f),
)
IconButton(onClick = { expanded = !expanded }) {
Icon(
imageVector = expandIcon,
contentDescription = null,
)
Spacer(modifier = Modifier.weight(1f))
IconButton(onClick = { expanded = !expanded }) {
Icon(
imageVector = expandIcon,
contentDescription = null,
}
}
Divider()
if (expanded) {
Column {
ruleMatchedApp.componentList.forEach {
ComponentListItem(
simpleName = it.simpleName,
name = it.name,
packageName = it.packageName,
enabled = it.enabled(),
type = it.type,
isServiceRunning = it.isRunning,
onStopServiceClick = { onStopServiceClick(it.packageName, it.name) },
onLaunchActivityClick = { onLaunchActivityClick(it.packageName, it.name) },
onCopyNameClick = { onCopyNameClick(it.simpleName) },
onCopyFullNameClick = { onCopyFullNameClick(it.name) },
onSwitchClick = onSwitch,
)
}
}
Divider()
if (expanded) {
ComponentList(
components = ruleMatchedApp.componentList,
onStopServiceClick = onStopServiceClick,
onLaunchActivityClick = onLaunchActivityClick,
onCopyNameClick = onCopyNameClick,
onCopyFullNameClick = onCopyFullNameClick,
onSwitchClick = onSwitch,
)
}
}
}
}
Expand Down Expand Up @@ -183,7 +187,7 @@ fun MatchedComponentItemPreview() {
val ruleMatchedApp = RuleMatchedApp(
app = AppItem(
packageName = "com.merxury.blocker",
label = "Blocker",
label = "Blocker component name test long name",
isSystem = false,
),
componentList = listOf(componentInfo),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,22 @@

package com.merxury.blocker.core.ui.rule

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.merxury.blocker.core.designsystem.theme.BlockerTheme
import com.merxury.blocker.core.model.ComponentType.ACTIVITY
import com.merxury.blocker.core.ui.R.string
import com.merxury.blocker.core.ui.applist.model.AppItem
import com.merxury.blocker.core.ui.component.ComponentItem

Expand All @@ -40,8 +49,12 @@ fun RuleMatchedAppList(
when (ruleMatchedAppListUiState) {
RuleMatchedAppListUiState.Loading -> {}
is RuleMatchedAppListUiState.Success -> {
if (ruleMatchedAppListUiState.list.isEmpty()) {
NoApplicableAppScreen()
return
}
LazyColumn {
items(ruleMatchedAppListUiState.list, key = { it }) { ruleMatchedApp ->
items(ruleMatchedAppListUiState.list, key = { it.app.label }) { ruleMatchedApp ->
MatchedComponentItem(
ruleMatchedApp = ruleMatchedApp,
onStopServiceClick = onStopServiceClick,
Expand All @@ -56,6 +69,21 @@ fun RuleMatchedAppList(
}
}

@Composable
fun NoApplicableAppScreen() {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = stringResource(id = string.no_applicable_app),
color = MaterialTheme.colorScheme.outline,
style = MaterialTheme.typography.bodyLarge,
)
}
}

sealed interface RuleMatchedAppListUiState {
object Loading : RuleMatchedAppListUiState
data class Success(
Expand Down
1 change: 1 addition & 0 deletions core/ui/src/main/res/values-ru/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,5 @@
<string name="description">Описание</string>
<string name="applicable_application">Применимое приложение</string>
<string name="loading">Загрузка</string>
<string name="no_applicable_app">Нет применимого приложения</string>
</resources>
1 change: 1 addition & 0 deletions core/ui/src/main/res/values-zh-rCN/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,5 @@
<string name="description">描述</string>
<string name="applicable_application">适用应用程序</string>
<string name="loading">正在加载</string>
<string name="no_applicable_app">没有适用的应用</string>
</resources>
1 change: 1 addition & 0 deletions core/ui/src/main/res/values-zh-rTW/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,5 @@
<string name="description">描述</string>
<string name="applicable_application">適用應用程序</string>
<string name="loading">正在加載</string>
<string name="no_applicable_app">沒有適用的應用</string>
</resources>
1 change: 1 addition & 0 deletions core/ui/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,5 @@
<string name="description">Description</string>
<string name="applicable_application">Applicable app</string>
<string name="loading">Loading</string>
<string name="no_applicable_app">No applicable app</string>
</resources>
Loading

0 comments on commit 407cbd1

Please sign in to comment.