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

Release 1.0.2 #155

Merged
merged 12 commits into from
Jul 1, 2024
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
# 청년을 위한 플랫폼 '청하'

청하(청춘하랑)는 ‘청춘’과 “함께 높이 날다”라는 의미를 가진 순우리말 ‘하랑’을 합쳐,
우리 청년들이 이루고자 하는 목표를 위해 함께 날아가고자 만들어진 서비스예요.
청하에서 여러분이 이루고자 하는 목표를 위한 첫걸음을 시작해 보세요.
청하(청년하랑) 앱은 청년들을 위한 정책 정보를 제공하고, 사용자들 간의 소통을 도와주는 커뮤니티 플랫폼입니다.

최신 청년 정책 정보 제공: 청년을 위한 다양한 정책과 그 세부 정보를 확인할 수 있습니다.
커뮤니티 기능: 같은 관심사를 가진 사용자들과 소통하고 네트워크를 형성할 수 있는 커뮤니티 기능을 갖추고 있습니다.

청하 앱을 통해 청년 정책에 대한 정보를 쉽게 얻고, 다른 청년들과 소통하며 유익한 커뮤니티를 형성하세요!

## Environment
- Android Studio Jellyfish
Expand Down
9 changes: 7 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ android {
defaultConfig {
applicationId = "com.withpeace.withpeace"
targetSdk = 34
versionCode = 6
versionName = "1.0.1"
versionCode = 7
versionName = "1.0.2"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
Expand All @@ -33,6 +33,11 @@ android {
"proguard-rules.pro",
)
}
create("benchmark") {
initWith(buildTypes.getByName("release"))
matchingFallbacks += listOf("release")
isDebuggable = false
}
}
}

Expand Down
23 changes: 13 additions & 10 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.withpeace.withpeace">

<uses-permission android:name="android.permission.INTERNET" />

<!-- Devices running Android 12L (API level 32) or lower -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />

<!-- Devices running Android 13 (API level 33) or higher -->
<uses-permission android:name="android.permission.INTERNET" /> <!-- Devices running Android 12L (API level 32) or lower -->
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" /> <!-- Devices running Android 13 (API level 33) or higher -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />

<application
Expand All @@ -17,22 +15,27 @@
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_app_logo"
android:usesCleartextTraffic="true"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_app_logo_round"
android:supportsRtl="true"
android:theme="@style/Theme.Withpeace"
android:usesCleartextTraffic="true"
tools:targetApi="31">
<profileable
android:shell="true"
tools:targetApi="29" />

<activity
android:name=".MainActivity"
android:exported="true"
android:windowSoftInputMode="adjustResize"
android:theme="@style/Theme.Withpeace.Starting">
android:theme="@style/Theme.Withpeace.Starting"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

</manifest>
5 changes: 1 addition & 4 deletions app/src/main/java/com/withpeace/withpeace/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ class MainActivity : ComponentActivity() {
super.onCreate(savedInstanceState)
// System Bar에 가려지는 뷰 영역을 개발자가 제어하겠다.
WindowCompat.setDecorFitsSystemWindows(window, false)

lifecycleScope.launch {
splashScreen = installSplashScreen()
splashScreen.setKeepOnScreenCondition { true }
Expand All @@ -63,9 +62,7 @@ class MainActivity : ComponentActivity() {
when (uiState) {
MainUiState.Home -> composeStart(HOME_ROUTE)
MainUiState.Login -> composeStart(LOGIN_ROUTE)
MainUiState.Update -> {
compulsionUpdate()
}
MainUiState.Update -> {}

MainUiState.Error -> finish()
MainUiState.Loading -> {}
Expand Down
16 changes: 10 additions & 6 deletions app/src/main/java/com/withpeace/withpeace/MainViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ import com.withpeace.withpeace.core.domain.usecase.CheckAppUpdateUseCase
import com.withpeace.withpeace.core.domain.usecase.IsLoginUseCase
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import javax.inject.Inject

Expand All @@ -15,8 +19,8 @@ class MainViewModel @Inject constructor(
private val isLoginUseCase: IsLoginUseCase,
private val checkAppUpdateUseCase: CheckAppUpdateUseCase,
) : ViewModel() {
private val _uiState: Channel<MainUiState> = Channel()
val uiState = _uiState.receiveAsFlow()
private val _uiState: MutableStateFlow<MainUiState> = MutableStateFlow(MainUiState.Loading)
val uiState = _uiState.asStateFlow()

init {
checkUpdate()
Expand All @@ -27,18 +31,18 @@ class MainViewModel @Inject constructor(
checkAppUpdateUseCase(
currentVersion = BuildConfig.VERSION_CODE,
onError = {
_uiState.send(MainUiState.Error)
_uiState.update { MainUiState.Error }
},
).collect { shouldUpdate ->
if (shouldUpdate) {
_uiState.send(MainUiState.Update)
_uiState.update {MainUiState.Update }
return@collect
}
val isLogin = isLoginUseCase()
if (isLogin) {
_uiState.send(MainUiState.Home)
_uiState.update { MainUiState.Home }
} else {
_uiState.send(MainUiState.Login)
_uiState.update { MainUiState.Login }
}
}
}
Expand Down
8 changes: 7 additions & 1 deletion app/src/main/java/com/withpeace/withpeace/WithpeaceApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,18 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.testTagsAsResourceId
import androidx.navigation.NavHostController
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.withpeace.withpeace.core.designsystem.theme.WithpeaceTheme
import com.withpeace.withpeace.navigation.WithpeaceNavHost
import kotlinx.coroutines.launch

@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun WithpeaceApp(
startDestination: String,
Expand Down Expand Up @@ -47,7 +51,9 @@ fun WithpeaceApp(
)
}
},
modifier = Modifier.fillMaxSize(),
modifier = Modifier.fillMaxSize().semantics {
testTagsAsResourceId = true
},
snackbarHost = { SnackbarHost(snackBarHostState) },
containerColor = WithpeaceTheme.colors.SystemWhite,
) { innerPadding ->
Expand Down
1 change: 1 addition & 0 deletions benchmark/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
53 changes: 53 additions & 0 deletions benchmark/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
plugins {
alias(libs.plugins.android.test)
alias(libs.plugins.kotlin.android)
}

android {
namespace = "com.withpeace.withpeace.benchmark"
compileSdk = 34

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

kotlinOptions {
jvmTarget = "1.8"
}

defaultConfig {
minSdk = 26
targetSdk = 34

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] = "EMULATOR"
}

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 = true
signingConfig = getByName("debug").signingConfig
matchingFallbacks += listOf("release")
}
}

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

dependencies {
implementation(libs.junit)
implementation(libs.androidx.test.espresso.core)
implementation(libs.androidx.uiautomator)
implementation(libs.androidx.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 />
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.withpeace.withpeace.benchmark

import androidx.benchmark.macro.CompilationMode
import androidx.benchmark.macro.StartupMode
import androidx.benchmark.macro.StartupTimingMetric
import androidx.benchmark.macro.junit4.MacrobenchmarkRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

/**
* This is an example startup benchmark.
*
* It navigates to the device's home screen, and launches the default activity.
*
* Before running this benchmark:
* 1) switch your app's active build variant in the Studio (affects Studio runs only)
* 2) add `<profileable android:shell="true" />` to your app's manifest, within the `<application>` tag
*
* Run this benchmark from Studio to see startup measurements, and captured system traces
* for investigating your app's performance.
*/
@RunWith(AndroidJUnit4::class)
class ExampleStartupBenchmark {
@get:Rule
val benchmarkRule = MacrobenchmarkRule()

@Test
fun startup() = benchmarkRule.measureRepeated(
packageName = "com.withpeace.withpeace",
metrics = listOf(StartupTimingMetric()),
iterations = 5,
startupMode = StartupMode.COLD
) {
pressHome()
startActivityAndWait()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.withpeace.withpeace.benchmark.home

import androidx.benchmark.macro.MacrobenchmarkScope
import androidx.test.uiautomator.By
import androidx.test.uiautomator.BySelector
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.UiObject2
import androidx.test.uiautomator.Until

fun MacrobenchmarkScope.policyContent() {
device.wait(Until.gone(By.res("circular_progress_bar")), 5_000)

val obj = device.waitAndFindObject(By.res("forYou:topicSelection"), 10_000)

}

fun UiDevice.waitAndFindObject(selector: BySelector, timeout: Long): UiObject2 {
if (!wait(Until.hasObject(selector), timeout)) {
throw AssertionError("Element not found on screen in ${timeout}ms (selector=$selector)")
}

return findObject(selector)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.withpeace.withpeace.benchmark.home

import androidx.benchmark.macro.FrameTimingMetric
import androidx.benchmark.macro.StartupMode
import androidx.benchmark.macro.junit4.MacrobenchmarkRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Direction
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class HomeBenchMark {
@get:Rule
val benchmarkRule = MacrobenchmarkRule()

@Test
fun scrollPolicy() = benchmarkRule.measureRepeated(
packageName = "com.withpeace.withpeace",
metrics = listOf(FrameTimingMetric()),
iterations = 10,
startupMode = StartupMode.HOT,
setupBlock = {
pressHome()
startActivityAndWait()
}
) {
val contentList = device.waitAndFindObject(By.res("home:policies"), 10000)
contentList.setGestureMargin(device.displayWidth / 5)

contentList.fling(Direction.DOWN)

device.waitForIdle()
}
}
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ plugins {
alias(libs.plugins.kotlin.serialization) apply false
alias(libs.plugins.firebase.services) apply false
alias(libs.plugins.firebase.crashlytics) apply false
alias(libs.plugins.android.test) apply false
}

apply {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
Expand Down Expand Up @@ -178,7 +179,7 @@ private fun PolicyItems(
) {
Spacer(modifier = modifier.height(8.dp))
LazyColumn(
modifier = modifier.fillMaxSize(),
modifier = modifier.fillMaxSize().testTag("home:policies"),
contentPadding = PaddingValues(bottom = 16.dp),
) {
items(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ fun PolicyConsentScreen(
Text(
modifier = modifier.align(Alignment.CenterHorizontally),
textAlign = TextAlign.Center,
text = "청하(청춘하랑)에 어서오세요!\n" +
text = "청하(청년하랑)에 어서오세요!\n" +
"약관에 동의하시면 청하와의 여정을\n" +
"시작할 수 있어요!",
style = WithpeaceTheme.typography.title2.merge(
Expand Down
Loading
Loading