diff --git a/.gitignore b/.gitignore index e5cbb641..33de84e3 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,6 @@ google-services.json # Android Profiling *.hprof + +# Claude Code (로컬 설정, 커밋하지 않음) +.claude/ diff --git a/Yoshi/NikeApp/.claude/settings.local.json b/Yoshi/NikeApp/.claude/settings.local.json deleted file mode 100644 index b6c5da46..00000000 --- a/Yoshi/NikeApp/.claude/settings.local.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "permissions": { - "allow": [ - "PowerShell(& \"C:\\\\Users\\\\doyeo\\\\AndroidStudioProjects\\\\NikeApp\\\\gradlew.bat\" assembleDebug 2>&1)" - ] - } -} diff --git a/Yoshi/NikeApp/app/build.gradle.kts b/Yoshi/NikeApp/app/build.gradle.kts index 66b1f88d..4df68a5d 100644 --- a/Yoshi/NikeApp/app/build.gradle.kts +++ b/Yoshi/NikeApp/app/build.gradle.kts @@ -1,3 +1,5 @@ +import java.util.Properties + plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) @@ -5,6 +7,14 @@ plugins { alias(libs.plugins.kotlin.serialization) } +// local.properties 에서 API 키를 읽어옵니다. (local.properties 는 git 에 올라가지 않습니다) +val localProperties = Properties().apply { + val localFile = rootProject.file("local.properties") + if (localFile.exists()) { + localFile.inputStream().use { load(it) } + } +} + android { namespace = "com.example.NikeApp" compileSdk = 35 @@ -17,6 +27,10 @@ android { versionName = "1.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + + // local.properties 의 NIKE_API_KEY 값을 BuildConfig.REQRES_API_KEY 로 노출 + val reqresApiKey = localProperties.getProperty("NIKE_API_KEY") ?: "" + buildConfigField("String", "REQRES_API_KEY", "\"$reqresApiKey\"") } buildTypes { @@ -37,6 +51,7 @@ android { } buildFeatures { compose = true + buildConfig = true } } @@ -58,6 +73,15 @@ dependencies { implementation(libs.androidx.navigation.compose) implementation(libs.kotlinx.serialization.json) + // Coil — Compose 이미지 로딩 + implementation(libs.coil.compose) + implementation(libs.coil.network.okhttp) + + // Retrofit + OkHttp — ReqRes API 통신 + implementation(libs.retrofit) + implementation(libs.retrofit.kotlinx.serialization) + implementation(libs.okhttp.logging.interceptor) + testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) diff --git a/Yoshi/NikeApp/app/src/main/java/com/example/NikeApp/data/UserRepository.kt b/Yoshi/NikeApp/app/src/main/java/com/example/NikeApp/data/UserRepository.kt new file mode 100644 index 00000000..36ea2965 --- /dev/null +++ b/Yoshi/NikeApp/app/src/main/java/com/example/NikeApp/data/UserRepository.kt @@ -0,0 +1,28 @@ +package com.example.NikeApp.data + +import com.example.NikeApp.data.remote.ReqResApi +import com.example.NikeApp.data.remote.ReqResClient +import com.example.NikeApp.data.remote.dto.toUser +import com.example.NikeApp.model.User + +/** + * 사용자 데이터 저장소. + * 화면(Composable)은 이 Repository 의 suspend 함수만 호출하면 됩니다. + */ +class UserRepository( + private val api: ReqResApi = ReqResClient.api, +) { + /** 미션 요구사항: 내 정보(userId = 1) 가져오기 */ + suspend fun getMyProfile(): User = api.getUser(MY_USER_ID).data.toUser() + + /** 팔로잉 리스트 구성: 사용자 목록에서 나(1번)를 제외하고 가져오기 */ + suspend fun getFollowing(): List = + api.getUsers(page = 1).data + .filter { it.id != MY_USER_ID } + .map { it.toUser() } + + companion object { + /** 워크북 미션 기준 내 userId 는 1번 */ + const val MY_USER_ID = 1 + } +} diff --git a/Yoshi/NikeApp/app/src/main/java/com/example/NikeApp/data/remote/ReqResApi.kt b/Yoshi/NikeApp/app/src/main/java/com/example/NikeApp/data/remote/ReqResApi.kt new file mode 100644 index 00000000..39a0d7f5 --- /dev/null +++ b/Yoshi/NikeApp/app/src/main/java/com/example/NikeApp/data/remote/ReqResApi.kt @@ -0,0 +1,21 @@ +package com.example.NikeApp.data.remote + +import com.example.NikeApp.data.remote.dto.SingleUserResponse +import com.example.NikeApp.data.remote.dto.UserListResponse +import retrofit2.http.GET +import retrofit2.http.Path +import retrofit2.http.Query + +/** + * ReqRes(https://reqres.in) 사용자 API + */ +interface ReqResApi { + + /** 단일 사용자 조회 — 미션 요구사항: userId = 1 */ + @GET("api/users/{id}") + suspend fun getUser(@Path("id") id: Int): SingleUserResponse + + /** 사용자 목록 조회 — 팔로잉 리스트 구성에 사용 */ + @GET("api/users") + suspend fun getUsers(@Query("page") page: Int = 1): UserListResponse +} diff --git a/Yoshi/NikeApp/app/src/main/java/com/example/NikeApp/data/remote/ReqResClient.kt b/Yoshi/NikeApp/app/src/main/java/com/example/NikeApp/data/remote/ReqResClient.kt new file mode 100644 index 00000000..22fae712 --- /dev/null +++ b/Yoshi/NikeApp/app/src/main/java/com/example/NikeApp/data/remote/ReqResClient.kt @@ -0,0 +1,46 @@ +package com.example.NikeApp.data.remote + +import com.example.NikeApp.BuildConfig +import kotlinx.serialization.json.Json +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.kotlinx.serialization.asConverterFactory + +/** + * Retrofit + OkHttp 설정을 한 곳에서 관리하는 싱글톤. + * - 모든 요청에 ReqRes 가 요구하는 `x-api-key` 헤더를 자동으로 붙입니다. + * - API 키는 .env -> BuildConfig.REQRES_API_KEY 를 통해 주입됩니다. + */ +object ReqResClient { + + private const val BASE_URL = "https://reqres.in/" + + private val json = Json { + ignoreUnknownKeys = true // 응답에 모르는 필드가 있어도 무시 + } + + private val okHttpClient: OkHttpClient = OkHttpClient.Builder() + // 1) API 키 헤더 자동 추가 + .addInterceptor { chain -> + val request = chain.request().newBuilder() + .addHeader("x-api-key", BuildConfig.REQRES_API_KEY) + .build() + chain.proceed(request) + } + // 2) 요청/응답 로깅 (디버깅용) + .addInterceptor( + HttpLoggingInterceptor().apply { + level = HttpLoggingInterceptor.Level.BODY + } + ) + .build() + + val api: ReqResApi = Retrofit.Builder() + .baseUrl(BASE_URL) + .client(okHttpClient) + .addConverterFactory(json.asConverterFactory("application/json".toMediaType())) + .build() + .create(ReqResApi::class.java) +} diff --git a/Yoshi/NikeApp/app/src/main/java/com/example/NikeApp/data/remote/dto/UserDto.kt b/Yoshi/NikeApp/app/src/main/java/com/example/NikeApp/data/remote/dto/UserDto.kt new file mode 100644 index 00000000..58a94c1c --- /dev/null +++ b/Yoshi/NikeApp/app/src/main/java/com/example/NikeApp/data/remote/dto/UserDto.kt @@ -0,0 +1,41 @@ +package com.example.NikeApp.data.remote.dto + +import com.example.NikeApp.model.User +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +/** + * ReqRes API 의 사용자 한 명을 표현하는 DTO + * 예) GET https://reqres.in/api/users/1 -> data 필드 + */ +@Serializable +data class UserDto( + val id: Int, + val email: String, + @SerialName("first_name") val firstName: String, + @SerialName("last_name") val lastName: String, + val avatar: String, +) + +/** GET /api/users/{id} 응답 */ +@Serializable +data class SingleUserResponse( + val data: UserDto, +) + +/** GET /api/users?page= 응답 */ +@Serializable +data class UserListResponse( + val page: Int, + @SerialName("per_page") val perPage: Int, + val total: Int, + @SerialName("total_pages") val totalPages: Int, + val data: List, +) + +/** DTO -> 도메인 모델 변환 (닉네임 = first_name + last_name) */ +fun UserDto.toUser(): User = User( + id = id, + nickname = "$firstName $lastName", + avatarUrl = avatar, +) diff --git a/Yoshi/NikeApp/app/src/main/java/com/example/NikeApp/model/User.kt b/Yoshi/NikeApp/app/src/main/java/com/example/NikeApp/model/User.kt new file mode 100644 index 00000000..e07fbe92 --- /dev/null +++ b/Yoshi/NikeApp/app/src/main/java/com/example/NikeApp/model/User.kt @@ -0,0 +1,12 @@ +package com.example.NikeApp.model + +/** + * 화면에서 사용하는 사용자 도메인 모델 + * - [nickname] 은 ReqRes 의 first_name + last_name 을 합친 값 + * - [avatarUrl] 은 프로필/팔로잉 이미지로 사용 + */ +data class User( + val id: Int, + val nickname: String, + val avatarUrl: String, +) diff --git a/Yoshi/NikeApp/app/src/main/java/com/example/NikeApp/ui/screen/ProfileScreen.kt b/Yoshi/NikeApp/app/src/main/java/com/example/NikeApp/ui/screen/ProfileScreen.kt index dbf00661..b68237bb 100644 --- a/Yoshi/NikeApp/app/src/main/java/com/example/NikeApp/ui/screen/ProfileScreen.kt +++ b/Yoshi/NikeApp/app/src/main/java/com/example/NikeApp/ui/screen/ProfileScreen.kt @@ -1,36 +1,340 @@ package com.example.NikeApp.ui.screen +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.aspectRatio 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.pager.HorizontalPager +import androidx.compose.foundation.pager.PageSize +import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Button +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Text +import androidx.compose.material3.VerticalDivider import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import coil3.compose.AsyncImage +import com.example.NikeApp.R +import com.example.NikeApp.data.UserRepository +import com.example.NikeApp.model.User import com.example.NikeApp.ui.theme.NikeAppTheme import com.example.NikeApp.ui.theme.NikeBlack +import com.example.NikeApp.ui.theme.NikeGray +import com.example.NikeApp.ui.theme.NikeLightGray +/** + * 프로필(마이페이지) 화면의 데이터 상태 + * - 워크북의 LaunchedEffect 비동기 패턴으로 ReqRes 에서 데이터를 받아옵니다. + */ +private sealed interface ProfileUiState { + data object Loading : ProfileUiState + data class Success(val me: User, val following: List) : ProfileUiState + data class Error(val message: String) : ProfileUiState +} + +/** + * 마이페이지 화면 + * - userId 1번 사용자의 이미지/닉네임을 ReqRes 에서 받아와 표시 + * - 팔로잉 리스트는 HorizontalPager 로 구현 + */ @Composable fun ProfileScreen(modifier: Modifier = Modifier) { + val repository = remember { UserRepository() } + + var uiState by remember { mutableStateOf(ProfileUiState.Loading) } + // 에러 시 "다시 시도"를 누르면 key 가 바뀌어 LaunchedEffect 가 재실행됩니다. + var retryKey by remember { mutableIntStateOf(0) } + + LaunchedEffect(retryKey) { + uiState = ProfileUiState.Loading + uiState = try { + val me = repository.getMyProfile() + val following = repository.getFollowing() + ProfileUiState.Success(me = me, following = following) + } catch (e: Exception) { + ProfileUiState.Error(e.message ?: "데이터를 불러오지 못했습니다.") + } + } + + when (val state = uiState) { + ProfileUiState.Loading -> LoadingView(modifier) + is ProfileUiState.Error -> ErrorView( + message = state.message, + onRetry = { retryKey++ }, + modifier = modifier, + ) + is ProfileUiState.Success -> ProfileContent( + me = state.me, + following = state.following, + modifier = modifier, + ) + } +} + +@Composable +private fun LoadingView(modifier: Modifier = Modifier) { + Box(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + CircularProgressIndicator(color = NikeBlack) + } +} + +@Composable +private fun ErrorView( + message: String, + onRetry: () -> Unit, + modifier: Modifier = Modifier, +) { Column( modifier = modifier .fillMaxSize() - .padding(horizontal = 20.dp, vertical = 16.dp), + .padding(24.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, ) { + Text(text = "오류가 발생했어요", fontWeight = FontWeight.Bold, fontSize = 18.sp) + Spacer(Modifier.height(8.dp)) + Text(text = message, color = NikeGray, fontSize = 14.sp) + Spacer(Modifier.height(16.dp)) + Button(onClick = onRetry) { Text("다시 시도") } + } +} + +@Composable +private fun ProfileContent( + me: User, + following: List, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Spacer(Modifier.height(24.dp)) + + // 프로필 이미지 (ReqRes avatar) — 원형 + AsyncImage( + model = me.avatarUrl, + contentDescription = "${me.nickname} 프로필 이미지", + contentScale = ContentScale.Crop, + modifier = Modifier + .size(120.dp) + .clip(CircleShape) + .background(NikeLightGray), + ) + + Spacer(Modifier.height(16.dp)) + + // 닉네임 (first_name + last_name) Text( - text = "프로필", - fontSize = 28.sp, + text = me.nickname, + fontSize = 24.sp, fontWeight = FontWeight.Bold, color = NikeBlack, ) + + Spacer(Modifier.height(16.dp)) + + OutlinedButton( + onClick = { /* 프로필 수정 (미구현) */ }, + shape = RoundedCornerShape(50), + modifier = Modifier.padding(horizontal = 80.dp), + ) { + Text( + text = "프로필 수정", + color = NikeBlack, + fontWeight = FontWeight.Bold, + modifier = Modifier.padding(vertical = 4.dp, horizontal = 24.dp), + ) + } + + Spacer(Modifier.height(24.dp)) + + // 빠른 메뉴: 주문 / 패스 / 이벤트 / 설정 + QuickActionRow() + + Spacer(Modifier.height(16.dp)) + HorizontalDivider(thickness = 8.dp, color = NikeLightGray) + + // 나이키 멤버 혜택 + MemberBenefitRow() + + HorizontalDivider(thickness = 8.dp, color = NikeLightGray) + + Spacer(Modifier.height(24.dp)) + + // 팔로잉 리스트 (HorizontalPager) + FollowingSection(following = following) + + Spacer(Modifier.height(40.dp)) + + Text( + text = "회원 가입일: 2025년 9월", + color = NikeGray, + fontSize = 13.sp, + ) + + Spacer(Modifier.height(24.dp)) + } +} + +/** 주문 / 패스 / 이벤트 / 설정 — 제공된 drawable(아이콘+라벨 일체형)을 그대로 사용 */ +@Composable +private fun QuickActionRow(modifier: Modifier = Modifier) { + val items = listOf( + R.drawable.menu_order, + R.drawable.menu_pass, + R.drawable.menu_event, + R.drawable.menu_settings, + ) + Row( + modifier = modifier + .fillMaxWidth() + .padding(horizontal = 20.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + items.forEachIndexed { index, drawable -> + Icon( + painter = painterResource(id = drawable), + contentDescription = null, + tint = NikeBlack, + modifier = Modifier + .weight(1f) + .height(46.dp), + ) + if (index != items.lastIndex) { + VerticalDivider( + modifier = Modifier.height(36.dp), + color = NikeLightGray, + ) + } + } + } +} + +/** 나이키 멤버 혜택 — 클릭 가능한 행 */ +@Composable +private fun MemberBenefitRow(modifier: Modifier = Modifier) { + Row( + modifier = modifier + .fillMaxWidth() + .padding(horizontal = 20.dp, vertical = 20.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Column(modifier = Modifier.weight(1f)) { + Text( + text = "나이키 멤버 혜택", + fontSize = 18.sp, + fontWeight = FontWeight.Bold, + color = NikeBlack, + ) + Spacer(Modifier.height(4.dp)) + Text(text = "0개 사용 가능", fontSize = 13.sp, color = NikeGray) + } + Icon( + painter = painterResource(id = R.drawable.ic_arrow_right), + contentDescription = null, + tint = NikeBlack, + ) + } +} + +/** 팔로잉 리스트 — 워크북 요구사항대로 HorizontalPager 로 구현 */ +@Composable +private fun FollowingSection( + following: List, + modifier: Modifier = Modifier, +) { + Column(modifier = modifier.fillMaxWidth()) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 20.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + ) { + Text( + text = "팔로잉 (${following.size})", + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + color = NikeBlack, + ) + Text(text = "편집", fontSize = 14.sp, color = NikeGray) + } + + Spacer(Modifier.height(16.dp)) + + if (following.isEmpty()) { + Text( + text = "팔로잉한 사용자가 없습니다.", + color = NikeGray, + modifier = Modifier.padding(horizontal = 20.dp), + ) + return@Column + } + + val pagerState = rememberPagerState(pageCount = { following.size }) + HorizontalPager( + state = pagerState, + pageSize = PageSize.Fixed(150.dp), + pageSpacing = 12.dp, + contentPadding = PaddingValues(horizontal = 20.dp), + ) { page -> + AsyncImage( + model = following[page].avatarUrl, + contentDescription = "${following[page].nickname} 이미지", + contentScale = ContentScale.Crop, + modifier = Modifier + .fillMaxWidth() + .aspectRatio(1f) + .clip(RoundedCornerShape(8.dp)) + .background(NikeLightGray), + ) + } } } @Preview(showBackground = true) @Composable -private fun ProfileScreenPreview() { - NikeAppTheme { ProfileScreen() } +private fun ProfileContentPreview() { + val sampleMe = User(1, "George Bluth", "") + val sampleFollowing = listOf( + User(2, "Janet Weaver", ""), + User(3, "Emma Wong", ""), + User(4, "Eve Holt", ""), + ) + NikeAppTheme { + ProfileContent(me = sampleMe, following = sampleFollowing) + } } diff --git a/Yoshi/NikeApp/gradle/libs.versions.toml b/Yoshi/NikeApp/gradle/libs.versions.toml index ba499b54..f1fe1622 100644 --- a/Yoshi/NikeApp/gradle/libs.versions.toml +++ b/Yoshi/NikeApp/gradle/libs.versions.toml @@ -10,6 +10,9 @@ activityCompose = "1.9.3" composeBom = "2024.12.01" navigationCompose = "2.8.5" kotlinxSerializationJson = "1.7.3" +coil = "3.1.0" +retrofit = "2.11.0" +okhttp = "4.12.0" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } @@ -24,6 +27,16 @@ androidx-material3 = { group = "androidx.compose.material3", name = "material3" androidx-material-icons-extended = { group = "androidx.compose.material", name = "material-icons-extended" } androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" } kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" } + +# Coil — Compose 이미지 로딩 (워크북 9주차) +coil-compose = { group = "io.coil-kt.coil3", name = "coil-compose", version.ref = "coil" } +coil-network-okhttp = { group = "io.coil-kt.coil3", name = "coil-network-okhttp", version.ref = "coil" } + +# Retrofit + OkHttp — ReqRes API 통신 +retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" } +retrofit-kotlinx-serialization = { group = "com.squareup.retrofit2", name = "converter-kotlinx-serialization", version.ref = "retrofit" } +okhttp-logging-interceptor = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp" } + junit = { group = "junit", name = "junit", version.ref = "junit" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/.gitignore" "b/\354\232\224\354\213\234/UMC10th_Android_week1/.gitignore" deleted file mode 100644 index aa724b77..00000000 --- "a/\354\232\224\354\213\234/UMC10th_Android_week1/.gitignore" +++ /dev/null @@ -1,15 +0,0 @@ -*.iml -.gradle -/local.properties -/.idea/caches -/.idea/libraries -/.idea/modules.xml -/.idea/workspace.xml -/.idea/navEditor.xml -/.idea/assetWizardSettings.xml -.DS_Store -/build -/captures -.externalNativeBuild -.cxx -local.properties diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/Group 462.svg" "b/\354\232\224\354\213\234/UMC10th_Android_week1/Group 462.svg" deleted file mode 100644 index 45ef4e50..00000000 --- "a/\354\232\224\354\213\234/UMC10th_Android_week1/Group 462.svg" +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/Group 573.svg" "b/\354\232\224\354\213\234/UMC10th_Android_week1/Group 573.svg" deleted file mode 100644 index b987bad8..00000000 --- "a/\354\232\224\354\213\234/UMC10th_Android_week1/Group 573.svg" +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/Group 574.svg" "b/\354\232\224\354\213\234/UMC10th_Android_week1/Group 574.svg" deleted file mode 100644 index 915d24f2..00000000 --- "a/\354\232\224\354\213\234/UMC10th_Android_week1/Group 574.svg" +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/Group 575.svg" "b/\354\232\224\354\213\234/UMC10th_Android_week1/Group 575.svg" deleted file mode 100644 index 84ee0c6f..00000000 --- "a/\354\232\224\354\213\234/UMC10th_Android_week1/Group 575.svg" +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/Group 576.svg" "b/\354\232\224\354\213\234/UMC10th_Android_week1/Group 576.svg" deleted file mode 100644 index b65626a4..00000000 --- "a/\354\232\224\354\213\234/UMC10th_Android_week1/Group 576.svg" +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/Vector.svg" "b/\354\232\224\354\213\234/UMC10th_Android_week1/Vector.svg" deleted file mode 100644 index dab8b024..00000000 --- "a/\354\232\224\354\213\234/UMC10th_Android_week1/Vector.svg" +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/.gitignore" "b/\354\232\224\354\213\234/UMC10th_Android_week1/app/.gitignore" deleted file mode 100644 index 42afabfd..00000000 --- "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/.gitignore" +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/build.gradle.kts" "b/\354\232\224\354\213\234/UMC10th_Android_week1/app/build.gradle.kts" deleted file mode 100644 index 4760d90d..00000000 --- "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/build.gradle.kts" +++ /dev/null @@ -1,47 +0,0 @@ -plugins { - alias(libs.plugins.android.application) -} - -android { - namespace = "com.example.umc10th_android_week1" - compileSdk { - version = release(36) { - minorApiLevel = 1 - } - } - - defaultConfig { - applicationId = "com.example.umc10th_android_week1" - minSdk = 24 - targetSdk = 36 - versionCode = 1 - versionName = "1.0" - - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - release { - isMinifyEnabled = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro" - ) - } - } - compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 - } -} - -dependencies { - implementation(libs.androidx.core.ktx) - implementation(libs.androidx.appcompat) - implementation(libs.material) - implementation(libs.androidx.activity) - implementation(libs.androidx.constraintlayout) - testImplementation(libs.junit) - androidTestImplementation(libs.androidx.junit) - androidTestImplementation(libs.androidx.espresso.core) -} \ No newline at end of file diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/proguard-rules.pro" "b/\354\232\224\354\213\234/UMC10th_Android_week1/app/proguard-rules.pro" deleted file mode 100644 index 481bb434..00000000 --- "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/proguard-rules.pro" +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/androidTest/java/com/example/umc10th_android_week1/ExampleInstrumentedTest.kt" "b/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/androidTest/java/com/example/umc10th_android_week1/ExampleInstrumentedTest.kt" deleted file mode 100644 index a77f1fd0..00000000 --- "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/androidTest/java/com/example/umc10th_android_week1/ExampleInstrumentedTest.kt" +++ /dev/null @@ -1,24 +0,0 @@ -package com.example.umc10th_android_week1 - -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.ext.junit.runners.AndroidJUnit4 - -import org.junit.Test -import org.junit.runner.RunWith - -import org.junit.Assert.* - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("com.example.umc10th_android_week1", appContext.packageName) - } -} \ No newline at end of file diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/AndroidManifest.xml" "b/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/AndroidManifest.xml" deleted file mode 100644 index 0e1b0236..00000000 --- "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/AndroidManifest.xml" +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/java/com/example/umc10th_android_week1/MainActivity.kt" "b/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/java/com/example/umc10th_android_week1/MainActivity.kt" deleted file mode 100644 index e0419abf..00000000 --- "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/java/com/example/umc10th_android_week1/MainActivity.kt" +++ /dev/null @@ -1,65 +0,0 @@ -package com.example.umc10th_android_week1 - -import android.os.Bundle -import androidx.activity.enableEdgeToEdge -import androidx.appcompat.app.AppCompatActivity -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat -import android.graphics.Color -import android.widget.ImageView -import android.widget.TextView - -class MainActivity : AppCompatActivity() { - private var selectedTextView: TextView? = null - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - enableEdgeToEdge() - setContentView(R.layout.activity_main) - ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets -> - val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) - v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) - insets - } - - setupEmotion( - imageId = R.id.imgHappy, - textId = R.id.txtHappy, - color = Color.parseColor("#FFB300") // 노란색 (행복) - ) - setupEmotion( - imageId = R.id.imgExcited, - textId = R.id.txtExcited, - color = Color.parseColor("#B8F8FB") // 초록색 (흥분) - ) - setupEmotion( - imageId = R.id.imgNormal, - textId = R.id.txtNormal, - color = Color.parseColor("#9C27B0") // 파란색 (평범) - ) - setupEmotion( - imageId = R.id.imgAnxious, - textId = R.id.txtAnxious, - color = Color.parseColor("#4CAF50") // 보라색 (불안) - ) - setupEmotion( - imageId = R.id.imgAngry, - textId = R.id.txtAngry, - color = Color.parseColor("#F44336") // 빨간색 (화남) - ) - } - private fun setupEmotion(imageId: Int, textId: Int, color: Int) { - val imageView = findViewById(imageId) - val textView = findViewById(textId) - - imageView.setOnClickListener { - // 이전에 선택된 텍스트 원래 색으로 복구 - selectedTextView?.setTextColor(Color.BLACK) - - // 현재 클릭된 텍스트 색상 변경 - textView.setTextColor(color) - - // 현재 선택 저장 - selectedTextView = textView - } - } -} \ No newline at end of file diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/drawable/group_462.xml" "b/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/drawable/group_462.xml" deleted file mode 100644 index c6b43a21..00000000 --- "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/drawable/group_462.xml" +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/drawable/group_573.xml" "b/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/drawable/group_573.xml" deleted file mode 100644 index 72176dae..00000000 --- "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/drawable/group_573.xml" +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/drawable/group_574.xml" "b/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/drawable/group_574.xml" deleted file mode 100644 index 5c4b19d6..00000000 --- "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/drawable/group_574.xml" +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/drawable/group_575.xml" "b/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/drawable/group_575.xml" deleted file mode 100644 index 9b02528e..00000000 --- "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/drawable/group_575.xml" +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/drawable/group_576.xml" "b/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/drawable/group_576.xml" deleted file mode 100644 index fe91f331..00000000 --- "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/drawable/group_576.xml" +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/drawable/ic_launcher_background.xml" "b/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/drawable/ic_launcher_background.xml" deleted file mode 100644 index 07d5da9c..00000000 --- "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/drawable/ic_launcher_background.xml" +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/drawable/ic_launcher_foreground.xml" "b/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/drawable/ic_launcher_foreground.xml" deleted file mode 100644 index 2b068d11..00000000 --- "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/drawable/ic_launcher_foreground.xml" +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/drawable/vector.xml" "b/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/drawable/vector.xml" deleted file mode 100644 index af620265..00000000 --- "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/drawable/vector.xml" +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/layout/activity_main.xml" "b/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/layout/activity_main.xml" deleted file mode 100644 index a1639f0f..00000000 --- "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/layout/activity_main.xml" +++ /dev/null @@ -1,167 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml" "b/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml" deleted file mode 100644 index 6f3b755b..00000000 --- "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml" +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml" "b/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml" deleted file mode 100644 index 6f3b755b..00000000 --- "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml" +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-hdpi/ic_launcher.webp" "b/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-hdpi/ic_launcher.webp" deleted file mode 100644 index c209e78e..00000000 Binary files "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-hdpi/ic_launcher.webp" and /dev/null differ diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp" "b/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp" deleted file mode 100644 index b2dfe3d1..00000000 Binary files "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp" and /dev/null differ diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-mdpi/ic_launcher.webp" "b/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-mdpi/ic_launcher.webp" deleted file mode 100644 index 4f0f1d64..00000000 Binary files "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-mdpi/ic_launcher.webp" and /dev/null differ diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp" "b/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp" deleted file mode 100644 index 62b611da..00000000 Binary files "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp" and /dev/null differ diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-xhdpi/ic_launcher.webp" "b/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-xhdpi/ic_launcher.webp" deleted file mode 100644 index 948a3070..00000000 Binary files "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-xhdpi/ic_launcher.webp" and /dev/null differ diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp" "b/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp" deleted file mode 100644 index 1b9a6956..00000000 Binary files "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp" and /dev/null differ diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp" "b/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp" deleted file mode 100644 index 28d4b77f..00000000 Binary files "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp" and /dev/null differ diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp" "b/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp" deleted file mode 100644 index 9287f508..00000000 Binary files "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp" and /dev/null differ diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp" "b/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp" deleted file mode 100644 index aa7d6427..00000000 Binary files "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp" and /dev/null differ diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp" "b/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp" deleted file mode 100644 index 9126ae37..00000000 Binary files "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp" and /dev/null differ diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/values-night/themes.xml" "b/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/values-night/themes.xml" deleted file mode 100644 index 87b18131..00000000 --- "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/values-night/themes.xml" +++ /dev/null @@ -1,7 +0,0 @@ - - - - \ No newline at end of file diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/values/colors.xml" "b/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/values/colors.xml" deleted file mode 100644 index c8524cd9..00000000 --- "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/values/colors.xml" +++ /dev/null @@ -1,5 +0,0 @@ - - - #FF000000 - #FFFFFFFF - \ No newline at end of file diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/values/strings.xml" "b/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/values/strings.xml" deleted file mode 100644 index d87c7d00..00000000 --- "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/values/strings.xml" +++ /dev/null @@ -1,3 +0,0 @@ - - UMC10th_Android_week1 - \ No newline at end of file diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/values/themes.xml" "b/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/values/themes.xml" deleted file mode 100644 index 8f0d38e7..00000000 --- "a/\354\232\224\354\213\234/UMC10th_Android_week1/app/src/main/res/values/themes.xml" +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - \ No newline at end of file diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week2/app/src/main/res/values/colors.xml" "b/\354\232\224\354\213\234/UMC10th_Android_week2/app/src/main/res/values/colors.xml" deleted file mode 100644 index c8524cd9..00000000 --- "a/\354\232\224\354\213\234/UMC10th_Android_week2/app/src/main/res/values/colors.xml" +++ /dev/null @@ -1,5 +0,0 @@ - - - #FF000000 - #FFFFFFFF - \ No newline at end of file diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week2/app/src/main/res/values/strings.xml" "b/\354\232\224\354\213\234/UMC10th_Android_week2/app/src/main/res/values/strings.xml" deleted file mode 100644 index dfe7f25e..00000000 --- "a/\354\232\224\354\213\234/UMC10th_Android_week2/app/src/main/res/values/strings.xml" +++ /dev/null @@ -1,5 +0,0 @@ - - UMC10th_Android_week2 - - Hello blank fragment - \ No newline at end of file diff --git "a/\354\232\224\354\213\234/UMC10th_Android_week2/app/src/main/res/values/themes.xml" "b/\354\232\224\354\213\234/UMC10th_Android_week2/app/src/main/res/values/themes.xml" deleted file mode 100644 index f7f76f0e..00000000 --- "a/\354\232\224\354\213\234/UMC10th_Android_week2/app/src/main/res/values/themes.xml" +++ /dev/null @@ -1,9 +0,0 @@ - - - - -