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

[FEAT] [#8] 사진 가이드 화면 구현 #25

Merged
merged 7 commits into from
Feb 1, 2024
Merged
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
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@ dependencies {
projects.core.datastore,
projects.core.ui,

projects.feature.camera,
projects.feature.home,
projects.feature.intro,
projects.feature.login,
projects.feature.main,
projects.feature.navigator,
projects.feature.mypage,
projects.feature.setting,
projects.feature.uploadphoto,

libs.androidx.activity.compose,
libs.androidx.core,
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-feature
android:name="android.hardware.camera"
android:required="false" />

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

<application
android:name=".ILabApplication"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package com.nexters.ilab.android.core.common.extension

import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.provider.Settings

inline fun <reified T : Activity> Activity.startActivityWithAnimation(
withFinish: Boolean,
Expand All @@ -21,3 +23,10 @@ inline fun <reified T : Activity> Activity.startActivityWithAnimation(
}
if (withFinish) finish()
}

fun Activity.openAppSettings() {
Intent(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts("package", packageName, null),
).also(::startActivity)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.nexters.ilab.android.core.common.extension

import android.content.ContentValues
import android.content.Context
import android.graphics.Bitmap
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import java.io.ByteArrayOutputStream

fun Bitmap.toUri(context: Context): Uri {
val filename = "${System.currentTimeMillis()}.png"
val stream = ByteArrayOutputStream()
this.compress(Bitmap.CompressFormat.PNG, 100, stream)
val imageCollection = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
} else {
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
}

val contentValues = ContentValues().apply {
put(MediaStore.Images.Media.DISPLAY_NAME, filename)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES)
}
}

val imageUri = context.contentResolver.insert(imageCollection, contentValues)
imageUri?.let { uri ->
context.contentResolver.openOutputStream(uri).use { outputStream ->
if (outputStream != null) {
this.compress(Bitmap.CompressFormat.PNG, 100, outputStream)
}
}
return uri
} ?: error("Failed to create new MediaStore record.")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.nexters.ilab.android.core.common.extension

import android.app.Activity
import android.content.Context
import android.content.ContextWrapper

// https://stackoverflow.com/questions/64675386/how-to-get-activity-in-compose
fun Context.findActivity(): Activity {
var context = this
while (context is ContextWrapper) {
if (context is Activity) return context
context = context.baseContext
}
error("Permissions should be called in the context of an Activity")
}
11 changes: 9 additions & 2 deletions core/designsystem/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<resources>
<!-- main-->
<string name="home">홈</string>
<string name="camera">카메라</string>
<string name="upload_photo">사진 업로드</string>
<string name="my_page">마이페이지</string>
<string name="error_message_network">네트워크 연결이 원활하지 않습니다.</string>
<string name="error_message_unknown">알 수 없는 오류가 발생하였습니다.</string>

<!-- camera-->
<!-- upload photo-->
<string name="upload_top_title">사진 가이드</string>
<string name="upload_check_top_title">가이드 확인</string>
<string name="take_photo">사진 찍기</string>
Expand All @@ -26,4 +26,11 @@
<string name="choice_good_example_second">내가 제일 잘나왔다고 생각하는 사진을 선택해주세요.</string>
<string name="avoid_bad_example_first">어둡거나 흐린 사진은 피해주세요.</string>
<string name="avoid_bad_example_second">안경이나 마스크, 모자 등 얼굴을 가린 사진은 피해주세요.</string>
<string name="permission_required">권한 필요</string>
<string name="permission_required_description">사진을 업로드 하기 위해서는 권한이 필요합니다.</string>
<string name="go_to_app_setting">앱 설정으로 이동</string>
<string name="camera_permission_denied">"카메라 권한을 거부하였습니다. 앱 설정으로 이동하여 권한을 부여할 수 있습니다."</string>
<string name="select_photo">사진을 선택해 주세요.</string>


</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ fun NetworkImage(
.crossfade(true)
.build(),
contentDescription = contentDescription,
contentScale = ContentScale.Fit,
contentScale = ContentScale.Crop,
modifier = modifier,
)
}
Expand Down

This file was deleted.

This file was deleted.

2 changes: 1 addition & 1 deletion feature/main/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ android {

dependencies {
implementations(
projects.feature.camera,
projects.feature.home,
projects.feature.mypage,
projects.feature.setting,
projects.feature.uploadphoto,

libs.androidx.core,
libs.kotlinx.collections.immutable,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import androidx.navigation.NavHostController
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navOptions
import com.nexters.ilab.android.feature.camera.navigation.navigateToCamera
import com.nexters.ilab.android.feature.camera.navigation.navigateToUploadCheck
import com.nexters.ilab.android.feature.uploadphoto.navigation.navigateToUploadPhoto
import com.nexters.ilab.android.feature.uploadphoto.navigation.navigateToUploadCheck
import com.nexters.ilab.android.feature.home.navigation.HOME_ROUTE
import com.nexters.ilab.android.feature.home.navigation.navigateToHome
import com.nexters.ilab.android.feature.mypage.navigation.navigateToMyPage
Expand Down Expand Up @@ -40,7 +40,7 @@ internal class MainNavController(

when (tab) {
MainTab.HOME -> navController.navigateToHome(navOptions)
MainTab.CAMERA -> navController.navigateToCamera(navOptions)
MainTab.UPLOAD_PHOTO -> navController.navigateToUploadPhoto(navOptions)
MainTab.MY_PAGE -> navController.navigateToMyPage(navOptions)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.navigation.compose.NavHost
import com.nexters.ilab.android.core.designsystem.R
import com.nexters.ilab.android.feature.camera.navigation.cameraNavGraph
import com.nexters.ilab.android.feature.uploadphoto.navigation.uploadPhotoNavGraph
import com.nexters.ilab.android.feature.home.navigation.homeNavGraph
import com.nexters.ilab.android.feature.mypage.navigation.myPageNavGraph
import com.nexters.ilab.android.feature.setting.navigation.settingNavGraph
Expand Down Expand Up @@ -82,7 +82,8 @@ internal fun MainScreen(
onShowErrorSnackBar = onShowErrorSnackBar,
)

cameraNavGraph(
uploadPhotoNavGraph(
navController = navigator.navController,
onBackClick = navigator::popBackStackIfNotHome,
onNavigateToUploadCheck = navigator::navigateToUploadCheck,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ internal enum class MainTab(
contentDescription = "홈",
route = "home_route",
),
CAMERA(
UPLOAD_PHOTO(
iconResId = R.drawable.ic_camera,
contentDescription = "카메라",
route = "camera_route",
contentDescription = "사진 업로드",
route = "upload_photo_route",
),
MY_PAGE(
iconResId = R.drawable.ic_my_page,
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ plugins {
}

android {
namespace = "com.nexters.ilab.android.feature.camera"
namespace = "com.nexters.ilab.android.feature.uploadphoto"
}

dependencies {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package com.nexters.ilab.android.feature.uploadphoto

import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.BasicAlertDialog
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.nexters.ilab.android.core.designsystem.R

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun PermissionDialog(
permissionTextProvider: PermissionTextProvider,
isPermanentlyDeclined: Boolean,
onDismiss: () -> Unit,
onOkClick: () -> Unit,
onGoToAppSettingsClick: () -> Unit,
modifier: Modifier = Modifier,
) {
BasicAlertDialog(
onDismissRequest = onDismiss,
content = {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
) {
Text(text = stringResource(id = R.string.permission_required))
Text(
text = permissionTextProvider.getDescription(
isPermanentlyDeclined = isPermanentlyDeclined,
),
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 16.dp),
)
HorizontalDivider()
Text(
text = if (isPermanentlyDeclined) {
stringResource(id = R.string.go_to_app_setting)
} else {
stringResource(id = R.string.check)
},
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.clickable {
if (isPermanentlyDeclined) {
onGoToAppSettingsClick()
} else {
onOkClick()
}
}
.padding(16.dp),
)
}
},
modifier = modifier
.clip(RoundedCornerShape(12.dp))
.background(color = Color.White),
)
}

interface PermissionTextProvider {
fun getDescription(isPermanentlyDeclined: Boolean): String
}

class CameraPermissionTextProvider : PermissionTextProvider {
override fun getDescription(isPermanentlyDeclined: Boolean): String {
return if (isPermanentlyDeclined) {
"카메라 권한을 거부하였습니다. 앱 설정으로 이동하여 권한을 부여할 수 있습니다."
} else {
"프로필 사진을 만들기 위해서는 카메라 접근 권한이 필요합니다."
}
}
}
Loading
Loading