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/usagestatusmanager] : 이용시간 통계 뷰 구현 #33

Merged
merged 30 commits into from
Jan 4, 2024
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
0302931
[feat]: Feat UsageStatsManager
kez-lab Dec 30, 2023
da50642
[feat]: Feat Domain Module
kez-lab Dec 30, 2023
c8049f0
[ui] 이용시간통계뷰 구현
jihyun0v0 Dec 30, 2023
16ff5be
[feat]: Feat MainViewModel And Add Domain Implementation
kez-lab Dec 31, 2023
b302737
[fix]: Merge Error
kez-lab Dec 31, 2023
91791dc
[feat]: Feat Statics Module
kez-lab Dec 31, 2023
7d42279
[feat] : usage stats get기능 추가
jihyun0v0 Jan 2, 2024
f5aa265
[chore] usage stats manager 탐구
jihyun0v0 Jan 2, 2024
3e17123
[chore] : usage stats manager 를 위한 공부
jihyun0v0 Jan 2, 2024
6a5eb29
[feat] app icon 가져오기 기능 추가
jihyun0v0 Jan 2, 2024
4ad96dd
Merge remote-tracking branch 'origin/develop' into feat/usagestatusma…
jihyun0v0 Jan 2, 2024
2b53751
Merge remote-tracking branch 'origin/develop' into feat/usagestatusma…
jihyun0v0 Jan 2, 2024
953e2c6
[chore] : 의진이 hot fix pull , error 수정
jihyun0v0 Jan 2, 2024
73318db
[fix]: Fix StaticsActivity
kez-lab Jan 2, 2024
fafb9dc
[ui] : 프로그래스 바 ui 수정
jihyun0v0 Jan 2, 2024
e256c0e
[fix] 참조 에러 수정
jihyun0v0 Jan 2, 2024
0e99366
[ui] : 이용시간 통계 뷰 ui 수정
jihyun0v0 Jan 3, 2024
81f58ad
[ui] : string 추가
jihyun0v0 Jan 3, 2024
34ddafa
[feat] : 목표 시간 데이터를 위한 hilt 기능 추가
jihyun0v0 Jan 3, 2024
ed72602
[feat] : 멀티 뷰타입 적용을 위한 item, viewholder추가
jihyun0v0 Jan 3, 2024
19213d2
[feat] : 멀티 뷰타입 적용, time method 오류 수정
jihyun0v0 Jan 3, 2024
11d818a
[fix] error 수정
jihyun0v0 Jan 3, 2024
679033c
[refact] : 유리 코리 반영
jihyun0v0 Jan 3, 2024
8aec1c8
[refact] : 관심사 분리
jihyun0v0 Jan 4, 2024
1f979e5
[refact] : 관심사 변경에 따라 adapter 수정
jihyun0v0 Jan 4, 2024
44c2133
[refact] : mock hilt refact
jihyun0v0 Jan 4, 2024
efd278a
[refact] : log 삭제
jihyun0v0 Jan 4, 2024
7756d74
Merge branch 'develop' into feat/usagestatusmanager
kez-lab Jan 4, 2024
f3e4b47
[fix]: Fix Merge Error
kez-lab Jan 4, 2024
e8a97ea
[fix]: Fix Code Convention
kez-lab Jan 4, 2024
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
12 changes: 12 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,18 @@ dependencies {
implementation(projects.feature.onboarding)
implementation(projects.feature.main)

// Feature
implementation(projects.feature.statistics)

// Domain
implementation(projects.domain.usagestats)

// Data
implementation(projects.data.usagestats)

// Core
implementation(projects.core.common)

// Firebase
implementation(platform(libs.firebase))
implementation(libs.bundles.firebase)
Expand Down
8 changes: 7 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission
android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />

<application
android:name=".HMHApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
Expand All @@ -15,7 +22,6 @@
<activity
android:name=".SampleActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.App.Starting">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/java/com/hmh/hamyeonham/HMHApplication.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.hmh.hamyeonham

import android.app.Application
import dagger.hilt.android.HiltAndroidApp

@HiltAndroidApp
class HMHApplication : Application() {

}
32 changes: 19 additions & 13 deletions app/src/main/java/com/hmh/hamyeonham/SampleActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,39 @@ import android.view.animation.AnimationUtils
import androidx.appcompat.app.AppCompatActivity
import androidx.core.splashscreen.SplashScreen
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import com.hmh.hamyeonham.feature.main.MainActivity
import com.hmh.hamyeonham.feature.onboarding.OnBoardingActivity
import com.hmh.hamyeonham.databinding.ActivitySampleBinding
import com.hmh.hamyeonham.statistics.StaticsActivity
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
class SampleActivity : AppCompatActivity() {
private lateinit var binding: ActivitySampleBinding

override fun onCreate(savedInstanceState: Bundle?) {
binding = ActivitySampleBinding.inflate(layoutInflater)
super.onCreate(savedInstanceState)

val splashScreen = installSplashScreen()
initSplashAnimation(splashScreen)

setContentView(R.layout.activity_sample)
Intent(this, MainActivity::class.java).let(::startActivity)
setContentView(binding.root)
Intent(this, StaticsActivity::class.java).let(::startActivity)
}

private fun initSplashAnimation(splashScreen: SplashScreen) {
splashScreen.setOnExitAnimationListener { splashScreenViewProvider ->
val splashScreenView = splashScreenViewProvider.view
val fadeOut = AnimationUtils.loadAnimation(this, R.anim.fade_out)

fadeOut.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationStart(animation: Animation) {}
override fun onAnimationEnd(animation: Animation) {
splashScreenViewProvider.remove()
}
fadeOut.setAnimationListener(
object : Animation.AnimationListener {
override fun onAnimationStart(animation: Animation) {}

override fun onAnimationEnd(animation: Animation) {
splashScreenViewProvider.remove()
}

override fun onAnimationRepeat(animation: Animation) {}
})
override fun onAnimationRepeat(animation: Animation) {}
},
)
splashScreenView.startAnimation(fadeOut)
}
}
Expand Down
13 changes: 1 addition & 12 deletions app/src/main/res/layout/activity_sample.xml
Original file line number Diff line number Diff line change
@@ -1,18 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:textSize="30sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
android:background="@color/black">

</androidx.constraintlayout.widget.ConstraintLayout>
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<resources>
<string name="app_name">HMH-Android</string>
<string name="usagestat_total_title">목표시간</string>
</resources>
1 change: 1 addition & 0 deletions app/src/main/res/values/themes.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>

<style name="Theme.HMHAndroid" parent="Theme.Material3.Light.NoActionBar" />

<style name="Theme.App.Starting" parent="Theme.SplashScreen">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package com.hmh.hamyeonham.common.context

import android.app.Dialog
import android.content.Context
import android.content.pm.PackageManager
import android.graphics.Point
import android.graphics.drawable.Drawable
import android.os.Build
import android.view.View
import android.view.WindowInsets
Expand All @@ -13,6 +15,7 @@ import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.core.content.ContextCompat
import com.google.android.material.snackbar.Snackbar
import com.hmh.hamyeonham.common.R

fun Context.toast(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
Expand All @@ -22,17 +25,29 @@ fun Context.longToast(message: String) {
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
}

fun Context.snackBar(anchorView: View, message: () -> String) {
fun Context.snackBar(
anchorView: View,
message: () -> String,
) {
Snackbar.make(anchorView, message(), Snackbar.LENGTH_SHORT).show()
}

fun Context.stringOf(@StringRes resId: Int) = getString(resId)
fun Context.stringOf(
@StringRes resId: Int,
) = getString(resId)

fun Context.colorOf(@ColorRes resId: Int) = ContextCompat.getColor(this, resId)
fun Context.colorOf(
@ColorRes resId: Int,
) = ContextCompat.getColor(this, resId)

fun Context.drawableOf(@DrawableRes resId: Int) = ContextCompat.getDrawable(this, resId)
fun Context.drawableOf(
@DrawableRes resId: Int,
) = ContextCompat.getDrawable(this, resId)

fun Context.dialogWidthPercent(dialog: Dialog?, percent: Double = 0.8) {
fun Context.dialogWidthPercent(
dialog: Dialog?,
percent: Double = 0.8,
) {
val deviceSize = getDeviceSize()
dialog?.window?.run {
val params = attributes
Expand All @@ -48,9 +63,10 @@ fun Context.getDeviceSize(): IntArray {
val windowMetrics = windowManager.currentWindowMetrics
val windowInsets = windowMetrics.windowInsets

val insets = windowInsets.getInsetsIgnoringVisibility(
WindowInsets.Type.navigationBars() or WindowInsets.Type.displayCutout()
)
val insets =
windowInsets.getInsetsIgnoringVisibility(
WindowInsets.Type.navigationBars() or WindowInsets.Type.displayCutout(),
)
val insetsWidth = insets.right + insets.left
val insetsHeight = insets.top + insets.bottom

Expand All @@ -66,3 +82,21 @@ fun Context.getDeviceSize(): IntArray {
return intArrayOf(size.x, size.y)
}
}

fun Context.getAppNameFromPackageName(packageName: String): String =
try {
val appInfo = packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA)
packageManager.getApplicationLabel(appInfo).toString()
} catch (e: PackageManager.NameNotFoundException) {
"Unknown"
}

fun Context.getAppIconFromPackageName(packageName: String): Drawable? {
try {
val appInfo = packageManager.getApplicationInfo(packageName, 0)
return appInfo.loadIcon(packageManager)
} catch (e: PackageManager.NameNotFoundException) {
e.printStackTrace()
}
return ContextCompat.getDrawable(this, R.drawable.ic_launcher_foreground)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,87 @@ package com.hmh.hamyeonham.common.time

import android.content.Context
import android.text.format.DateUtils
import android.util.Log
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlinx.datetime.LocalDate
import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toInstant
import kotlinx.datetime.toLocalDateTime
import java.util.concurrent.TimeUnit

fun Instant.Companion.systemNow(): Instant = Clock.System.now()

fun Instant.toDefaultLocalDate(): LocalDate = toLocalDateTime(TimeZone.currentSystemDefault()).date

fun Long.formatDate(context: Context): String = DateUtils.formatDateTime(
context,
this,
DateUtils.FORMAT_SHOW_YEAR or DateUtils.FORMAT_SHOW_DATE
)
fun Long.formatDate(context: Context): String =
DateUtils.formatDateTime(
context,
this,
DateUtils.FORMAT_SHOW_YEAR or DateUtils.FORMAT_SHOW_DATE,
)

fun Instant.formatDate(context: Context): String = toEpochMilliseconds().formatDate(context)

fun Long.formatNumericDate(context: Context): String = DateUtils.formatDateTime(
context,
this,
DateUtils.FORMAT_SHOW_YEAR or DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NUMERIC_DATE
)
fun Long.formatNumericDate(context: Context): String =
DateUtils.formatDateTime(
context,
this,
DateUtils.FORMAT_SHOW_YEAR or DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NUMERIC_DATE,
)

fun Instant.formatNumericDate(context: Context): String =
toEpochMilliseconds().formatNumericDate(context)
fun Instant.formatNumericDate(context: Context): String = toEpochMilliseconds().formatNumericDate(context)

// LocalDate의 확장 함수로 해당 날짜의 시작 시간과 종료 시간을 LocalDateTime으로 반환
fun LocalDate.toStartOfDay(): LocalDateTime = LocalDateTime(year, monthNumber, dayOfMonth, 0, 0)

fun LocalDate.toEndOfDay(): LocalDateTime = LocalDateTime(year, monthNumber, dayOfMonth, 23, 59, 59)

// LocalDateTime의 확장 함수로 해당 시간을 Epoch 밀리초로 변환
fun LocalDateTime.toEpochMilliseconds(timeZone: TimeZone): Long = toInstant(timeZone).toEpochMilliseconds()

// 현재 날짜의 시작 시간과 종료 시간을 Epoch 밀리초로 반환하는 함수
fun getCurrentDayStartEndEpochMillis(): Pair<Long, Long> {
val timeZone = TimeZone.currentSystemDefault()
val currentDate = Clock.System.now().toLocalDateTime(timeZone).date
val startOfDay = currentDate.toStartOfDay().toEpochMilliseconds(timeZone)
val endOfDay = currentDate.toEndOfDay().toEpochMilliseconds(timeZone)
return Pair(startOfDay, endOfDay)
}

fun convertMillisecondsToMinute(ms: Long) = TimeUnit.MILLISECONDS.toMinutes(ms)

fun convertTimeToString(time: Long): String {
val hour = convertMillisecondsToMinute(time) / 60
val min = convertMillisecondsToMinute(time) % 60
var result = ""
if (hour > 0) result = result.plus("$hour 시간")
if (min > 0) result = result.plus("$min 분")

Log.d("time", time.toString())
Log.d("converted", convertMillisecondsToMinute(time).toString())
Log.d("hour", hour.toString())
Log.d("min", min.toString())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

로그가 확인용이라면 확인 후에 push할 때에는 지워줘도 될 것 같습니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵! 아직 수정할 부분이 남아있을 것 같아 남겨두었습니다! 나중에 로직 완성하면 지우겠습니다~

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이거 이제 지우기 ㄱㄱ?!


Log.d("result", result.toString())
return result
}

fun getUsedPercentage(
usage: Long,
goal: Long,
): Int {
try {
return (usage * 100 / goal).toInt()
} catch (e: ArithmeticException) {
return 0
}
}

fun getLeftTimeInString(
usage: Long,
goal: Long,
): String {
return convertTimeToString(goal - usage)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 함수들이 해당 파일에 있는 것이 맞을까요?? 관심사가 조금 다른 것 같습니다.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네! 어제 스크럼 이후에 수정하려 했는데 아직 못했네요. 오늘 스크럼 전까지 해서 올리겠습니다!

30 changes: 30 additions & 0 deletions core/common/src/main/res/drawable/ic_launcher_foreground.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>
1 change: 1 addition & 0 deletions data/usagestats/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
12 changes: 12 additions & 0 deletions data/usagestats/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
plugins {
hmh("feature")
}

android {
namespace = "com.hmh.hamyeonham.data.usagestats"
}

dependencies {
implementation(projects.domain.usagestats)
}
Empty file.
21 changes: 21 additions & 0 deletions data/usagestats/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# 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
Loading