Skip to content
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
4 changes: 4 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ dependencies {
implementation(libs.material)
implementation(libs.androidx.activity)
implementation(libs.androidx.constraintlayout)
implementation(libs.coil)
implementation(libs.coil.network.okhttp)
implementation(libs.navigation.fragment.ktx)
implementation(libs.navigation.ui.ktx)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
Expand Down
21 changes: 18 additions & 3 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,33 @@

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

<queries>
<intent>
<action android:name="android.media.action.VIDEO_CAPTURE" />
</intent>
</queries>

<application
android:name=".MumuApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:icon="@drawable/mumu_logo"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:roundIcon="@drawable/mumu_logo"
android:supportsRtl="true"
android:theme="@style/Theme.Mumu"
android:usesCleartextTraffic="true"
tools:targetApi="31">
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
<activity
android:name=".presentation.join.JoinActivity"
android:exported="false" />
Expand All @@ -33,4 +48,4 @@
android:exported="false" />
</application>

</manifest>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package kr.ac.anu.mumu.data.datasource

import kr.ac.anu.mumu.data.model.AnalysisDetailDto
import kr.ac.anu.mumu.data.model.AnalysisTriggerDto
import kr.ac.anu.mumu.data.model.BaseResponse
import okhttp3.MultipartBody
import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Multipart
import retrofit2.http.POST
import retrofit2.http.Part
import retrofit2.http.Path
import retrofit2.http.Query

interface AnalysisService {
@Multipart
@POST("/api/behavior-analyses")
suspend fun triggerBehaviorAnalysis(
@Query("petId") petId: Long,
@Part video: MultipartBody.Part
): Response<BaseResponse<AnalysisTriggerDto>>

@GET("/api/behavior-analyses/{id}")
suspend fun getBehaviorAnalysis(
@Path("id") analysisId: Long
): Response<BaseResponse<AnalysisDetailDto>>
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ import retrofit2.http.Body
import retrofit2.http.POST

interface AuthService {
@POST("/api/users/login")
@POST("/api/auth/login")
suspend fun login(@Body request: LoginRequest): Response<LoginResponse>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package kr.ac.anu.mumu.data.datasource

import kr.ac.anu.mumu.data.model.BaseResponse
import kr.ac.anu.mumu.data.model.CommunityPostDto
import kr.ac.anu.mumu.data.model.PaginatedData
import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Query

interface CommunityService {
@GET("/api/community")
suspend fun getPosts(
@Query("page") page: Int = 0,
@Query("size") size: Int = 50
): Response<BaseResponse<PaginatedData<CommunityPostDto>>>
}
20 changes: 20 additions & 0 deletions app/src/main/java/kr/ac/anu/mumu/data/datasource/HistoryService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package kr.ac.anu.mumu.data.datasource

import kr.ac.anu.mumu.data.model.AnalysisHistoryDto
import kr.ac.anu.mumu.data.model.BaseResponse
import kr.ac.anu.mumu.data.model.PaginatedData
import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Path
import retrofit2.http.Query

interface HistoryService {
@GET("/api/pets/{petId}/analyses")
suspend fun getAnalysisHistory(
@Path("petId") petId: Long,
@Query("type") type: String = "behavior",
@Query("status") status: String = "completed",
@Query("page") page: Int = 0,
@Query("size") size: Int = 50
): Response<BaseResponse<PaginatedData<AnalysisHistoryDto>>>
}
11 changes: 11 additions & 0 deletions app/src/main/java/kr/ac/anu/mumu/data/datasource/PetService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package kr.ac.anu.mumu.data.datasource

import kr.ac.anu.mumu.data.model.BaseResponse
import kr.ac.anu.mumu.data.model.PetDto
import retrofit2.Response
import retrofit2.http.GET

interface PetService {
@GET("/api/pets")
suspend fun getMyPets(): Response<BaseResponse<List<PetDto>>>
}
31 changes: 31 additions & 0 deletions app/src/main/java/kr/ac/anu/mumu/data/local/SessionManager.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package kr.ac.anu.mumu.data.local

import android.content.Context
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class SessionManager @Inject constructor(
@ApplicationContext context: Context
) {
private val preferences = context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE)

val accessToken: String?
get() = preferences.getString(KEY_ACCESS_TOKEN, null)

fun saveTokens(accessToken: String, refreshToken: String, tokenType: String) {
preferences.edit()
.putString(KEY_ACCESS_TOKEN, accessToken)
.putString(KEY_REFRESH_TOKEN, refreshToken)
.putString(KEY_TOKEN_TYPE, tokenType)
.apply()
}

private companion object {
const val PREFERENCES_NAME = "mumu_session"
const val KEY_ACCESS_TOKEN = "access_token"
const val KEY_REFRESH_TOKEN = "refresh_token"
const val KEY_TOKEN_TYPE = "token_type"
}
}
35 changes: 35 additions & 0 deletions app/src/main/java/kr/ac/anu/mumu/data/mapper/AnalysisHistoryDto.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package kr.ac.anu.mumu.data.mapper

import kr.ac.anu.mumu.data.model.AnalysisHistoryDto
import kr.ac.anu.mumu.domain.model.AnalysisHistory
import java.time.OffsetDateTime
import java.time.format.DateTimeFormatter
import kotlin.math.roundToInt

fun AnalysisHistoryDto.toDomain(): AnalysisHistory {
val formattedDate = analyzedAt?.let { value ->
runCatching {
OffsetDateTime.parse(value).format(DateTimeFormatter.ofPattern("yyyy.MM.dd"))
}.getOrDefault(value.take(10).replace('-', '.'))
} ?: "-"

val probability = confidence?.roundToInt()?.coerceIn(0, 100) ?: 0
val isNormal = resultLabel.equals("NORMAL", ignoreCase = true) ||
resultLabel == "정상" ||
(suspectedItems.isNullOrEmpty() && probability < 50)

val joinedBehavior = if (suspectedItems.isNullOrEmpty()) {
if (isNormal) "특이 행동 없음" else resultLabel ?: "분석 결과 확인"
} else {
suspectedItems.joinToString(", ")
}

return AnalysisHistory(
id = this.analysisId,
isNormal = isNormal,
behaviorText = joinedBehavior,
probability = probability,
date = formattedDate,
resultLabel = resultLabel
)
}
21 changes: 21 additions & 0 deletions app/src/main/java/kr/ac/anu/mumu/data/model/AnalysisDtos.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package kr.ac.anu.mumu.data.model

data class AnalysisTriggerDto(
val analysisId: Long,
val status: String
)

data class AnalysisDetailDto(
val analysisId: Long,
val type: String?,
val status: String,
val resultLabel: String?,
val suspectedItems: List<String>?,
val confidence: Double?,
val analyzedAt: String?
)

data class PetDto(
val petId: Long,
val name: String?
)
26 changes: 9 additions & 17 deletions app/src/main/java/kr/ac/anu/mumu/data/model/AuthDtos.kt
Original file line number Diff line number Diff line change
@@ -1,28 +1,20 @@
package kr.ac.anu.mumu.data.model

import com.google.gson.annotations.SerializedName

data class LoginRequest(
@SerializedName("id")
val id: String,
@SerializedName("password")
val pw: String
val loginId: String,
val password: String,
val deviceType: String = "android",
val deviceInfo: String? = null
)

data class LoginResponse(
val success: Boolean,
val message: String,
val token: String,
val user: UserDto
val data: TokenDto
)

data class UserDto(
@SerializedName("user_id")
val userId: Int,
val id: String,
val name: String,
val phone: String?,
@SerializedName("profile_image")
val profileImage: String?,
val role: String
data class TokenDto(
val accessToken: String,
val refreshToken: String,
val tokenType: String
)
19 changes: 19 additions & 0 deletions app/src/main/java/kr/ac/anu/mumu/data/model/CommunityPostDto.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package kr.ac.anu.mumu.data.model

data class CommunityPostDto(
val communityId: Long,
val userId: Long,
val petId: Long?,
val category: String,
val title: String,
val content: String,
val hashtags: List<String>?,
val viewCount: Int,
val likeCount: Int,
val commentCount: Int,
val bookmarkCount: Int,
val isBest: Boolean,
val createdAt: String,
val updatedAt: String,
val thumbnailUrl: String?
)
25 changes: 25 additions & 0 deletions app/src/main/java/kr/ac/anu/mumu/data/model/HistoryDtos.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package kr.ac.anu.mumu.data.model

data class BaseResponse<T>(
val success: Boolean,
val message: String,
val data: T?
)

data class PaginatedData<T>(
val content: List<T>,
val page: Int,
val size: Int,
val totalElements: Long,
val totalPages: Int
)

data class AnalysisHistoryDto(
val analysisId: Long,
val type: String,
val status: String,
val resultLabel: String?,
val suspectedItems: List<String>?,
val confidence: Double?,
val analyzedAt: String?
)
18 changes: 6 additions & 12 deletions app/src/main/java/kr/ac/anu/mumu/data/model/JoinDto.kt
Original file line number Diff line number Diff line change
@@ -1,29 +1,23 @@
package kr.ac.anu.mumu.data.model

import com.google.gson.annotations.SerializedName

data class JoinRequest(
val id: String,
val loginId: String,
val password: String,
val name: String,
val phone: String,
val address: String,
@SerializedName("detail_address") val detailAddress: String,
@SerializedName("postal_code") val postalCode: String,
@SerializedName("terms_agreed") val termsAgreed: Boolean,
@SerializedName("privacy_agreed") val privacyAgreed: Boolean,
@SerializedName("marketing_agreed") val marketingAgreed: Boolean
val termsAgreed: Boolean,
val privacyAgreed: Boolean,
val marketingAgreed: Boolean
)

data class JoinResponse(
val success: Boolean,
val message: String,
val token: String?,
val user: UserDto?
val data: UserDto?
) {
data class UserDto(
val userId: Int,
val id: String,
val loginId: String,
val name: String,
val phone: String
)
Expand Down
Loading
Loading