From 0302931a4a85dc6cdaf09088fc9b59abc5e834a3 Mon Sep 17 00:00:00 2001 From: KwakEuiJin Date: Sun, 31 Dec 2023 01:43:23 +0900 Subject: [PATCH 01/26] [feat]: Feat UsageStatsManager --- app/build.gradle.kts | 1 + app/src/main/AndroidManifest.xml | 8 ++- .../java/com/hmh/hamyeonham/HMHApplication.kt | 9 +++ .../java/com/hmh/hamyeonham/MainActivity.kt | 47 +++++++++++++- data/usagestats/.gitignore | 1 + data/usagestats/build.gradle.kts | 12 ++++ data/usagestats/consumer-rules.pro | 0 data/usagestats/proguard-rules.pro | 21 +++++++ data/usagestats/src/main/AndroidManifest.xml | 4 ++ .../datasource/UsageStatsDataSource.kt | 17 ++++++ .../datasource/UsageStatsDataSourceImpl.kt | 61 +++++++++++++++++++ .../usagestats/di/UsageStatsModule.kt | 32 ++++++++++ .../repository/DefaultRepository.kt | 4 ++ settings.gradle.kts | 2 + 14 files changed, 217 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/com/hmh/hamyeonham/HMHApplication.kt create mode 100644 data/usagestats/.gitignore create mode 100644 data/usagestats/build.gradle.kts create mode 100644 data/usagestats/consumer-rules.pro create mode 100644 data/usagestats/proguard-rules.pro create mode 100644 data/usagestats/src/main/AndroidManifest.xml create mode 100644 data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/datasource/UsageStatsDataSource.kt create mode 100644 data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/datasource/UsageStatsDataSourceImpl.kt create mode 100644 data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/di/UsageStatsModule.kt create mode 100644 data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/repository/DefaultRepository.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 20eaf069..0bda49b5 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -36,4 +36,5 @@ dependencies { // Splash implementation(libs.splash.screen) + implementation(projects.data.usagestats) } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3de3e838..a491ab53 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,7 +2,14 @@ + + + diff --git a/app/src/main/java/com/hmh/hamyeonham/HMHApplication.kt b/app/src/main/java/com/hmh/hamyeonham/HMHApplication.kt new file mode 100644 index 00000000..6b867294 --- /dev/null +++ b/app/src/main/java/com/hmh/hamyeonham/HMHApplication.kt @@ -0,0 +1,9 @@ +package com.hmh.hamyeonham + +import android.app.Application +import dagger.hilt.android.HiltAndroidApp + +@HiltAndroidApp +class HMHApplication : Application() { + +} diff --git a/app/src/main/java/com/hmh/hamyeonham/MainActivity.kt b/app/src/main/java/com/hmh/hamyeonham/MainActivity.kt index 7ee73dc9..461b7b99 100644 --- a/app/src/main/java/com/hmh/hamyeonham/MainActivity.kt +++ b/app/src/main/java/com/hmh/hamyeonham/MainActivity.kt @@ -1,20 +1,56 @@ package com.hmh.hamyeonham +import android.content.pm.PackageManager import android.os.Bundle +import android.util.Log import android.view.animation.Animation 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.usagestats.datasource.UsageStatsDataSource +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.datetime.Clock +import kotlinx.datetime.LocalDate +import kotlinx.datetime.LocalDateTime +import kotlinx.datetime.TimeZone +import kotlinx.datetime.toInstant +import kotlinx.datetime.toLocalDateTime +import javax.inject.Inject +@AndroidEntryPoint class MainActivity : AppCompatActivity() { + + @Inject + lateinit var usageStatsDataSource: UsageStatsDataSource + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val splashScreen = installSplashScreen() initSplashAnimation(splashScreen) - setContentView(R.layout.activity_main) + + val currentDateTime = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()) + val startDate = + LocalDate(currentDateTime.year, currentDateTime.monthNumber, currentDateTime.dayOfMonth) + val startOfDay = + LocalDateTime(startDate.year, startDate.monthNumber, startDate.dayOfMonth, 0, 0) + val endOfDay = + LocalDateTime(startDate.year, startDate.monthNumber, startDate.dayOfMonth, 23, 59, 59) + + val startTime = startOfDay.toInstant(TimeZone.currentSystemDefault()).toEpochMilliseconds() + val endTime = endOfDay.toInstant(TimeZone.currentSystemDefault()).toEpochMilliseconds() + + + val list = usageStatsDataSource.getUsageStats(startTime, endTime) + list.filter { it.totalTimeInForeground > 0 }.sortedByDescending { it.totalTimeInForeground } + .forEach { + Log.d( + "usageStatsDataSource", + "onCreate: ${getAppNameFromPackageName(it.packageName)} ${it.totalTimeInForeground}" + ) + } } private fun initSplashAnimation(splashScreen: SplashScreen) { @@ -33,4 +69,13 @@ class MainActivity : AppCompatActivity() { splashScreenView.startAnimation(fadeOut) } } + + fun getAppNameFromPackageName(packageName: String): String { + return packageManager.getApplicationLabel( + packageManager.getApplicationInfo( + packageName, + PackageManager.GET_META_DATA + ) + ).toString() + } } diff --git a/data/usagestats/.gitignore b/data/usagestats/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/data/usagestats/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/data/usagestats/build.gradle.kts b/data/usagestats/build.gradle.kts new file mode 100644 index 00000000..80157531 --- /dev/null +++ b/data/usagestats/build.gradle.kts @@ -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 { + +} diff --git a/data/usagestats/consumer-rules.pro b/data/usagestats/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/data/usagestats/proguard-rules.pro b/data/usagestats/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/data/usagestats/proguard-rules.pro @@ -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 \ No newline at end of file diff --git a/data/usagestats/src/main/AndroidManifest.xml b/data/usagestats/src/main/AndroidManifest.xml new file mode 100644 index 00000000..8bdb7e14 --- /dev/null +++ b/data/usagestats/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + diff --git a/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/datasource/UsageStatsDataSource.kt b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/datasource/UsageStatsDataSource.kt new file mode 100644 index 00000000..e679c037 --- /dev/null +++ b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/datasource/UsageStatsDataSource.kt @@ -0,0 +1,17 @@ +package com.hmh.hamyeonham.usagestats.datasource + +interface UsageStatsDataSource { + fun getUsageStats(startTime: Long, endTime: Long): List + fun getUsageTimeForPackage(startTime: Long, endTime: Long, packageName: String): Long + fun getUsageTimeForPackages( + startTime: Long, + endTime: Long, + vararg packageNames: String + ): List + + fun getUsageTimeForPackages( + startTime: Long, + endTime: Long, + packageNames: List + ): List +} diff --git a/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/datasource/UsageStatsDataSourceImpl.kt b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/datasource/UsageStatsDataSourceImpl.kt new file mode 100644 index 00000000..9381c682 --- /dev/null +++ b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/datasource/UsageStatsDataSourceImpl.kt @@ -0,0 +1,61 @@ +package com.hmh.hamyeonham.usagestats.datasource + +import android.app.usage.UsageStats +import android.app.usage.UsageStatsManager +import javax.inject.Inject + +class UsageStatsDataSourceImpl @Inject constructor( + private val usageStatsManager: UsageStatsManager? +) : UsageStatsDataSource { + + override fun getUsageStats(startTime: Long, endTime: Long): List { + val usageStatsList = queryUsageStats(startTime, endTime) + return usageStatsList?.map { UsageStatModel(it.packageName, it.totalTimeInForeground) } + ?: emptyList() + } + + override fun getUsageTimeForPackage(startTime: Long, endTime: Long, packageName: String): Long { + val usageStatsList = queryUsageStats(startTime, endTime) + return usageStatsList?.firstOrNull { it.packageName == packageName }?.totalTimeInForeground + ?: 0 + } + + override fun getUsageTimeForPackages( + startTime: Long, + endTime: Long, + vararg packageNames: String + ): List { + val usageStatsList = queryUsageStats(startTime, endTime) + return packageNames.map { packageName -> + val totalUsageTime = usageStatsList + ?.filter { it.packageName == packageName } + ?.sumOf { it.totalTimeInForeground } ?: 0 + UsageStatModel(packageName, totalUsageTime) + } + } + + override fun getUsageTimeForPackages( + startTime: Long, + endTime: Long, + packageNames: List + ): List { + val usageStatsList = queryUsageStats(startTime, endTime) + return packageNames.map { packageName -> + val totalUsageTime = usageStatsList + ?.filter { it.packageName == packageName } + ?.sumOf { it.totalTimeInForeground } ?: 0 + UsageStatModel(packageName, totalUsageTime) + } + } + + + private fun queryUsageStats(startTime: Long, endTime: Long): List? { + return usageStatsManager?.queryUsageStats( + UsageStatsManager.INTERVAL_DAILY, + startTime, + endTime + ) + } +} + +data class UsageStatModel(val packageName: String, val totalTimeInForeground: Long) diff --git a/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/di/UsageStatsModule.kt b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/di/UsageStatsModule.kt new file mode 100644 index 00000000..9ef9125d --- /dev/null +++ b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/di/UsageStatsModule.kt @@ -0,0 +1,32 @@ +package com.hmh.hamyeonham.usagestats.di + +import android.app.usage.UsageStatsManager +import android.content.Context +import com.hmh.hamyeonham.usagestats.datasource.UsageStatsDataSource +import com.hmh.hamyeonham.usagestats.datasource.UsageStatsDataSourceImpl +import dagger.Binds +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + + +@Module +@InstallIn(SingletonComponent::class) +object UsageStatsModule { + @Singleton + @Provides + fun provideUsageStatusManager(@ApplicationContext context: Context): UsageStatsManager? { + return context.getSystemService(Context.USAGE_STATS_SERVICE) as? UsageStatsManager + } + + @Module + @InstallIn(SingletonComponent::class) + interface Binder { + @Binds + @Singleton + fun provideUsageStatusDataSource(usageStatsDataSource: UsageStatsDataSourceImpl): UsageStatsDataSource + } +} diff --git a/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/repository/DefaultRepository.kt b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/repository/DefaultRepository.kt new file mode 100644 index 00000000..73aaaba3 --- /dev/null +++ b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/repository/DefaultRepository.kt @@ -0,0 +1,4 @@ +package com.hmh.hamyeonham.usagestats.repository + +class DefaultRepository { +} diff --git a/settings.gradle.kts b/settings.gradle.kts index fcc2fc01..7b86359a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -19,3 +19,5 @@ dependencyResolutionManagement { rootProject.name = "HMH-Android" include(":app") include(":core:common") +include(":data") +include(":data:usagestats") From da506428b5851f2d4698441fcbb4e9c38ede7c47 Mon Sep 17 00:00:00 2001 From: KwakEuiJin Date: Sun, 31 Dec 2023 02:48:42 +0900 Subject: [PATCH 02/26] [feat]: Feat Domain Module --- data/usagestats/build.gradle.kts | 2 +- .../datasource/UsageStatsDataSource.kt | 15 ++---- .../datasource/UsageStatsDataSourceImpl.kt | 48 ++--------------- .../usagestats/di/UsageStatsModule.kt | 6 +++ .../usagestats/model/UsageStatModel.kt | 6 +++ .../repository/DefaultRepository.kt | 4 -- .../repository/DefaultUsageStatsRepository.kt | 51 +++++++++++++++++++ domain/usagestats/.gitignore | 1 + domain/usagestats/build.gradle.kts | 9 ++++ domain/usagestats/consumer-rules.pro | 0 domain/usagestats/proguard-rules.pro | 21 ++++++++ .../usagestats/ExampleInstrumentedTest.kt | 24 +++++++++ .../usagestats/src/main/AndroidManifest.xml | 4 ++ .../hamyeonham/usagestats/model/UsageStat.kt | 6 +++ .../repository/UsageStatsRepository.kt | 19 +++++++ .../domain/usagestats/ExampleUnitTest.kt | 17 +++++++ settings.gradle.kts | 2 + 17 files changed, 173 insertions(+), 62 deletions(-) create mode 100644 data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/model/UsageStatModel.kt delete mode 100644 data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/repository/DefaultRepository.kt create mode 100644 data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/repository/DefaultUsageStatsRepository.kt create mode 100644 domain/usagestats/.gitignore create mode 100644 domain/usagestats/build.gradle.kts create mode 100644 domain/usagestats/consumer-rules.pro create mode 100644 domain/usagestats/proguard-rules.pro create mode 100644 domain/usagestats/src/androidTest/java/com/hmh/hamyeonham/domain/usagestats/ExampleInstrumentedTest.kt create mode 100644 domain/usagestats/src/main/AndroidManifest.xml create mode 100644 domain/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/model/UsageStat.kt create mode 100644 domain/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/repository/UsageStatsRepository.kt create mode 100644 domain/usagestats/src/test/java/com/hmh/hamyeonham/domain/usagestats/ExampleUnitTest.kt diff --git a/data/usagestats/build.gradle.kts b/data/usagestats/build.gradle.kts index 80157531..3bcb101a 100644 --- a/data/usagestats/build.gradle.kts +++ b/data/usagestats/build.gradle.kts @@ -8,5 +8,5 @@ android { } dependencies { - + implementation(projects.domain.usagestats) } diff --git a/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/datasource/UsageStatsDataSource.kt b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/datasource/UsageStatsDataSource.kt index e679c037..4d4c6da0 100644 --- a/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/datasource/UsageStatsDataSource.kt +++ b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/datasource/UsageStatsDataSource.kt @@ -1,17 +1,8 @@ package com.hmh.hamyeonham.usagestats.datasource +import com.hmh.hamyeonham.usagestats.model.UsageStat +import com.hmh.hamyeonham.usagestats.model.UsageStatModel + interface UsageStatsDataSource { fun getUsageStats(startTime: Long, endTime: Long): List - fun getUsageTimeForPackage(startTime: Long, endTime: Long, packageName: String): Long - fun getUsageTimeForPackages( - startTime: Long, - endTime: Long, - vararg packageNames: String - ): List - - fun getUsageTimeForPackages( - startTime: Long, - endTime: Long, - packageNames: List - ): List } diff --git a/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/datasource/UsageStatsDataSourceImpl.kt b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/datasource/UsageStatsDataSourceImpl.kt index 9381c682..d6ba477c 100644 --- a/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/datasource/UsageStatsDataSourceImpl.kt +++ b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/datasource/UsageStatsDataSourceImpl.kt @@ -1,7 +1,8 @@ package com.hmh.hamyeonham.usagestats.datasource -import android.app.usage.UsageStats import android.app.usage.UsageStatsManager +import com.hmh.hamyeonham.usagestats.model.UsageStat +import com.hmh.hamyeonham.usagestats.model.UsageStatModel import javax.inject.Inject class UsageStatsDataSourceImpl @Inject constructor( @@ -9,53 +10,10 @@ class UsageStatsDataSourceImpl @Inject constructor( ) : UsageStatsDataSource { override fun getUsageStats(startTime: Long, endTime: Long): List { - val usageStatsList = queryUsageStats(startTime, endTime) - return usageStatsList?.map { UsageStatModel(it.packageName, it.totalTimeInForeground) } - ?: emptyList() - } - - override fun getUsageTimeForPackage(startTime: Long, endTime: Long, packageName: String): Long { - val usageStatsList = queryUsageStats(startTime, endTime) - return usageStatsList?.firstOrNull { it.packageName == packageName }?.totalTimeInForeground - ?: 0 - } - - override fun getUsageTimeForPackages( - startTime: Long, - endTime: Long, - vararg packageNames: String - ): List { - val usageStatsList = queryUsageStats(startTime, endTime) - return packageNames.map { packageName -> - val totalUsageTime = usageStatsList - ?.filter { it.packageName == packageName } - ?.sumOf { it.totalTimeInForeground } ?: 0 - UsageStatModel(packageName, totalUsageTime) - } - } - - override fun getUsageTimeForPackages( - startTime: Long, - endTime: Long, - packageNames: List - ): List { - val usageStatsList = queryUsageStats(startTime, endTime) - return packageNames.map { packageName -> - val totalUsageTime = usageStatsList - ?.filter { it.packageName == packageName } - ?.sumOf { it.totalTimeInForeground } ?: 0 - UsageStatModel(packageName, totalUsageTime) - } - } - - - private fun queryUsageStats(startTime: Long, endTime: Long): List? { return usageStatsManager?.queryUsageStats( UsageStatsManager.INTERVAL_DAILY, startTime, endTime - ) + )?.map { UsageStatModel(it.packageName, it.totalTimeInForeground) } ?: emptyList() } } - -data class UsageStatModel(val packageName: String, val totalTimeInForeground: Long) diff --git a/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/di/UsageStatsModule.kt b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/di/UsageStatsModule.kt index 9ef9125d..59170907 100644 --- a/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/di/UsageStatsModule.kt +++ b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/di/UsageStatsModule.kt @@ -4,6 +4,8 @@ import android.app.usage.UsageStatsManager import android.content.Context import com.hmh.hamyeonham.usagestats.datasource.UsageStatsDataSource import com.hmh.hamyeonham.usagestats.datasource.UsageStatsDataSourceImpl +import com.hmh.hamyeonham.usagestats.repository.DefaultUsageStatsRepository +import com.hmh.hamyeonham.usagestats.repository.UsageStatsRepository import dagger.Binds import dagger.Module import dagger.Provides @@ -28,5 +30,9 @@ object UsageStatsModule { @Binds @Singleton fun provideUsageStatusDataSource(usageStatsDataSource: UsageStatsDataSourceImpl): UsageStatsDataSource + + @Binds + @Singleton + fun provideUsageStatusRepository(usageStatsRepository: DefaultUsageStatsRepository): UsageStatsRepository } } diff --git a/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/model/UsageStatModel.kt b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/model/UsageStatModel.kt new file mode 100644 index 00000000..99efbfb8 --- /dev/null +++ b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/model/UsageStatModel.kt @@ -0,0 +1,6 @@ +package com.hmh.hamyeonham.usagestats.model + +data class UsageStatModel( + val packageName: String, + val totalTimeInForeground: Long +) diff --git a/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/repository/DefaultRepository.kt b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/repository/DefaultRepository.kt deleted file mode 100644 index 73aaaba3..00000000 --- a/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/repository/DefaultRepository.kt +++ /dev/null @@ -1,4 +0,0 @@ -package com.hmh.hamyeonham.usagestats.repository - -class DefaultRepository { -} diff --git a/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/repository/DefaultUsageStatsRepository.kt b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/repository/DefaultUsageStatsRepository.kt new file mode 100644 index 00000000..f39f105e --- /dev/null +++ b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/repository/DefaultUsageStatsRepository.kt @@ -0,0 +1,51 @@ +package com.hmh.hamyeonham.usagestats.repository + +import com.hmh.hamyeonham.usagestats.datasource.UsageStatsDataSource +import com.hmh.hamyeonham.usagestats.model.UsageStat +import javax.inject.Inject + +class DefaultUsageStatsRepository @Inject constructor( + private val usageStatsDataSource: UsageStatsDataSource +) : UsageStatsRepository { + + override fun getUsageStats(startTime: Long, endTime: Long): List { + val usageStatsList = usageStatsDataSource.getUsageStats(startTime, endTime) + return usageStatsList.map { usageStatModel -> + UsageStat(usageStatModel.packageName, usageStatModel.totalTimeInForeground) + } + } + + override fun getUsageTimeForPackage(startTime: Long, endTime: Long, packageName: String): Long { + val usageStatsList = getUsageStats(startTime, endTime) + return usageStatsList.firstOrNull { it.packageName == packageName }?.totalTimeInForeground + ?: 0 + } + + override fun getUsageTimeForPackages( + startTime: Long, + endTime: Long, + vararg packageNames: String + ): List { + val usageStatsList = getUsageStats(startTime, endTime) + return packageNames.map { packageName -> + val totalUsageTime = usageStatsList + .filter { it.packageName == packageName } + .sumOf { it.totalTimeInForeground } + UsageStat(packageName, totalUsageTime) + } + } + + override fun getUsageTimeForPackages( + startTime: Long, + endTime: Long, + packageNames: List + ): List { + val usageStatsList = getUsageStats(startTime, endTime) + return packageNames.map { packageName -> + val totalUsageTime = usageStatsList + .filter { it.packageName == packageName } + .sumOf { it.totalTimeInForeground } + UsageStat(packageName, totalUsageTime) + } + } +} diff --git a/domain/usagestats/.gitignore b/domain/usagestats/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/domain/usagestats/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/domain/usagestats/build.gradle.kts b/domain/usagestats/build.gradle.kts new file mode 100644 index 00000000..ed722350 --- /dev/null +++ b/domain/usagestats/build.gradle.kts @@ -0,0 +1,9 @@ +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + `java-library` + kotlin("jvm") +} + +dependencies { + implementation(libs.javax.inject) +} diff --git a/domain/usagestats/consumer-rules.pro b/domain/usagestats/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/domain/usagestats/proguard-rules.pro b/domain/usagestats/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/domain/usagestats/proguard-rules.pro @@ -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 \ No newline at end of file diff --git a/domain/usagestats/src/androidTest/java/com/hmh/hamyeonham/domain/usagestats/ExampleInstrumentedTest.kt b/domain/usagestats/src/androidTest/java/com/hmh/hamyeonham/domain/usagestats/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..46c29247 --- /dev/null +++ b/domain/usagestats/src/androidTest/java/com/hmh/hamyeonham/domain/usagestats/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.hmh.hamyeonham.domain.usagestats + +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.hmh.hamyeonham.domain.usagestats.test", appContext.packageName) + } +} diff --git a/domain/usagestats/src/main/AndroidManifest.xml b/domain/usagestats/src/main/AndroidManifest.xml new file mode 100644 index 00000000..8bdb7e14 --- /dev/null +++ b/domain/usagestats/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + diff --git a/domain/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/model/UsageStat.kt b/domain/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/model/UsageStat.kt new file mode 100644 index 00000000..73dbbcbd --- /dev/null +++ b/domain/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/model/UsageStat.kt @@ -0,0 +1,6 @@ +package com.hmh.hamyeonham.usagestats.model + +data class UsageStat( + val packageName: String, + val totalTimeInForeground: Long +) diff --git a/domain/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/repository/UsageStatsRepository.kt b/domain/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/repository/UsageStatsRepository.kt new file mode 100644 index 00000000..f9a2da68 --- /dev/null +++ b/domain/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/repository/UsageStatsRepository.kt @@ -0,0 +1,19 @@ +package com.hmh.hamyeonham.usagestats.repository + +import com.hmh.hamyeonham.usagestats.model.UsageStat + +interface UsageStatsRepository { + fun getUsageStats(startTime: Long, endTime: Long): List + fun getUsageTimeForPackage(startTime: Long, endTime: Long, packageName: String): Long + fun getUsageTimeForPackages( + startTime: Long, + endTime: Long, + vararg packageNames: String + ): List + + fun getUsageTimeForPackages( + startTime: Long, + endTime: Long, + packageNames: List + ): List +} diff --git a/domain/usagestats/src/test/java/com/hmh/hamyeonham/domain/usagestats/ExampleUnitTest.kt b/domain/usagestats/src/test/java/com/hmh/hamyeonham/domain/usagestats/ExampleUnitTest.kt new file mode 100644 index 00000000..d9a1e905 --- /dev/null +++ b/domain/usagestats/src/test/java/com/hmh/hamyeonham/domain/usagestats/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.hmh.hamyeonham.domain.usagestats + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 7b86359a..65069a04 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -21,3 +21,5 @@ include(":app") include(":core:common") include(":data") include(":data:usagestats") +include(":domain") +include(":domain:usagestats") From c8049f03b293556517061d7c4d06438c3936a2f1 Mon Sep 17 00:00:00 2001 From: memoryBangwool Date: Sun, 31 Dec 2023 03:04:52 +0900 Subject: [PATCH 03/26] =?UTF-8?q?[ui]=20=EC=9D=B4=EC=9A=A9=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=ED=86=B5=EA=B3=84=EB=B7=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/hmh/hamyeonham/MainActivity.kt | 36 ++++++---- app/src/main/res/layout/activity_main.xml | 68 ++++++++++++++++--- app/src/main/res/layout/item_usagestat.xml | 49 +++++++++++++ app/src/main/res/values/strings.xml | 1 + app/src/main/res/values/themes.xml | 9 ++- 5 files changed, 142 insertions(+), 21 deletions(-) create mode 100644 app/src/main/res/layout/item_usagestat.xml diff --git a/app/src/main/java/com/hmh/hamyeonham/MainActivity.kt b/app/src/main/java/com/hmh/hamyeonham/MainActivity.kt index 461b7b99..369a7be0 100644 --- a/app/src/main/java/com/hmh/hamyeonham/MainActivity.kt +++ b/app/src/main/java/com/hmh/hamyeonham/MainActivity.kt @@ -20,7 +20,6 @@ import javax.inject.Inject @AndroidEntryPoint class MainActivity : AppCompatActivity() { - @Inject lateinit var usageStatsDataSource: UsageStatsDataSource @@ -30,7 +29,18 @@ class MainActivity : AppCompatActivity() { val splashScreen = installSplashScreen() initSplashAnimation(splashScreen) setContentView(R.layout.activity_main) + // todo - 리사이클러뷰 구현 +// initAdapter() + initUsageStat() + } +// private fun initAdapter() { +// val usagestatAdapter = UsagestatAdapter(viewModel.mockAppUsageList) +// binding.rvUsagestat.adapter = usagestatAdapter +// binding.rvUsagestat.addItemDecoration(CustomItemDecoration()) +// } + + private fun initUsageStat() { val currentDateTime = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()) val startDate = LocalDate(currentDateTime.year, currentDateTime.monthNumber, currentDateTime.dayOfMonth) @@ -42,13 +52,12 @@ class MainActivity : AppCompatActivity() { val startTime = startOfDay.toInstant(TimeZone.currentSystemDefault()).toEpochMilliseconds() val endTime = endOfDay.toInstant(TimeZone.currentSystemDefault()).toEpochMilliseconds() - val list = usageStatsDataSource.getUsageStats(startTime, endTime) list.filter { it.totalTimeInForeground > 0 }.sortedByDescending { it.totalTimeInForeground } .forEach { Log.d( "usageStatsDataSource", - "onCreate: ${getAppNameFromPackageName(it.packageName)} ${it.totalTimeInForeground}" + "onCreate: ${getAppNameFromPackageName(it.packageName)} ${it.totalTimeInForeground}", ) } } @@ -58,14 +67,17 @@ class MainActivity : AppCompatActivity() { 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 onAnimationRepeat(animation: Animation) {} - }) + override fun onAnimationEnd(animation: Animation) { + splashScreenViewProvider.remove() + } + + override fun onAnimationRepeat(animation: Animation) {} + }, + ) splashScreenView.startAnimation(fadeOut) } } @@ -74,8 +86,8 @@ class MainActivity : AppCompatActivity() { return packageManager.getApplicationLabel( packageManager.getApplicationInfo( packageName, - PackageManager.GET_META_DATA - ) + PackageManager.GET_META_DATA, + ), ).toString() } } diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 25105a0d..745f3e72 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -3,16 +3,68 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical"> + android:background="@color/black"> - + + + + + + + + + + + + + app:layout_constraintTop_toBottomOf="@id/view_usagestat_total" /> - + \ No newline at end of file diff --git a/app/src/main/res/layout/item_usagestat.xml b/app/src/main/res/layout/item_usagestat.xml new file mode 100644 index 00000000..7ea84df2 --- /dev/null +++ b/app/src/main/res/layout/item_usagestat.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4e12be50..6ad288b1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,3 +1,4 @@ HMH-Android + 목표시간 \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 47cb31f8..b9035c84 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,5 +1,5 @@ - + + + From 16ff5be7d78fe0eb898579f08fe74fa7ee7d06cc Mon Sep 17 00:00:00 2001 From: KwakEuiJin Date: Sun, 31 Dec 2023 13:42:25 +0900 Subject: [PATCH 04/26] [feat]: Feat MainViewModel And Add Domain Implementation --- app/build.gradle.kts | 11 +++- .../java/com/hmh/hamyeonham/MainActivity.kt | 61 +++++-------------- .../java/com/hmh/hamyeonham/MainViewModel.kt | 26 ++++++++ .../hamyeonham/common/context/ContextExt.kt | 11 ++++ .../com/hmh/hamyeonham/common/time/TimeExt.kt | 18 ++++++ .../datasource/UsageStatsDataSource.kt | 1 - .../usagestats/di/UsageStatsModule.kt | 3 +- 7 files changed, 82 insertions(+), 49 deletions(-) create mode 100644 app/src/main/java/com/hmh/hamyeonham/MainViewModel.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 0bda49b5..ba3909ee 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -29,12 +29,19 @@ android { dependencies { + // Domain + implementation(projects.domain.usagestats) + + // Data + implementation(projects.data.usagestats) + + // Core + implementation(projects.core.common) + // Firebase implementation(platform(libs.firebase)) implementation(libs.bundles.firebase) // Splash implementation(libs.splash.screen) - - implementation(projects.data.usagestats) } diff --git a/app/src/main/java/com/hmh/hamyeonham/MainActivity.kt b/app/src/main/java/com/hmh/hamyeonham/MainActivity.kt index 369a7be0..9ec25b1a 100644 --- a/app/src/main/java/com/hmh/hamyeonham/MainActivity.kt +++ b/app/src/main/java/com/hmh/hamyeonham/MainActivity.kt @@ -1,27 +1,22 @@ package com.hmh.hamyeonham -import android.content.pm.PackageManager import android.os.Bundle import android.util.Log import android.view.animation.Animation import android.view.animation.AnimationUtils +import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.core.splashscreen.SplashScreen import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen -import com.hmh.hamyeonham.usagestats.datasource.UsageStatsDataSource +import androidx.lifecycle.lifecycleScope +import com.hmh.hamyeonham.common.context.getAppNameFromPackageName import dagger.hilt.android.AndroidEntryPoint -import kotlinx.datetime.Clock -import kotlinx.datetime.LocalDate -import kotlinx.datetime.LocalDateTime -import kotlinx.datetime.TimeZone -import kotlinx.datetime.toInstant -import kotlinx.datetime.toLocalDateTime -import javax.inject.Inject +import kotlinx.coroutines.launch @AndroidEntryPoint class MainActivity : AppCompatActivity() { - @Inject - lateinit var usageStatsDataSource: UsageStatsDataSource + + private val viewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -34,32 +29,17 @@ class MainActivity : AppCompatActivity() { initUsageStat() } -// private fun initAdapter() { -// val usagestatAdapter = UsagestatAdapter(viewModel.mockAppUsageList) -// binding.rvUsagestat.adapter = usagestatAdapter -// binding.rvUsagestat.addItemDecoration(CustomItemDecoration()) -// } - - private fun initUsageStat() { - val currentDateTime = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()) - val startDate = - LocalDate(currentDateTime.year, currentDateTime.monthNumber, currentDateTime.dayOfMonth) - val startOfDay = - LocalDateTime(startDate.year, startDate.monthNumber, startDate.dayOfMonth, 0, 0) - val endOfDay = - LocalDateTime(startDate.year, startDate.monthNumber, startDate.dayOfMonth, 23, 59, 59) - - val startTime = startOfDay.toInstant(TimeZone.currentSystemDefault()).toEpochMilliseconds() - val endTime = endOfDay.toInstant(TimeZone.currentSystemDefault()).toEpochMilliseconds() - - val list = usageStatsDataSource.getUsageStats(startTime, endTime) - list.filter { it.totalTimeInForeground > 0 }.sortedByDescending { it.totalTimeInForeground } - .forEach { - Log.d( - "usageStatsDataSource", - "onCreate: ${getAppNameFromPackageName(it.packageName)} ${it.totalTimeInForeground}", - ) + lifecycleScope.launch { + viewModel.usageStatsList.collect { + it.forEach { + Log.d( + "MainActivity", + "packageName: ${getAppNameFromPackageName(it.packageName)}" + ) + Log.d("MainActivity", "totalTimeInForeground: ${it.totalTimeInForeground}") + } } + } } private fun initSplashAnimation(splashScreen: SplashScreen) { @@ -81,13 +61,4 @@ class MainActivity : AppCompatActivity() { splashScreenView.startAnimation(fadeOut) } } - - fun getAppNameFromPackageName(packageName: String): String { - return packageManager.getApplicationLabel( - packageManager.getApplicationInfo( - packageName, - PackageManager.GET_META_DATA, - ), - ).toString() - } } diff --git a/app/src/main/java/com/hmh/hamyeonham/MainViewModel.kt b/app/src/main/java/com/hmh/hamyeonham/MainViewModel.kt new file mode 100644 index 00000000..3a195ef5 --- /dev/null +++ b/app/src/main/java/com/hmh/hamyeonham/MainViewModel.kt @@ -0,0 +1,26 @@ +package com.hmh.hamyeonham + +import androidx.lifecycle.ViewModel +import com.hmh.hamyeonham.common.time.getCurrentDayStartEndEpochMillis +import com.hmh.hamyeonham.usagestats.model.UsageStat +import com.hmh.hamyeonham.usagestats.repository.UsageStatsRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import javax.inject.Inject + +@HiltViewModel +class MainViewModel @Inject constructor( + private val usageStatsRepository: UsageStatsRepository +) : ViewModel() { + + val usageStatsList = MutableStateFlow>(emptyList()) + + init { + val (startTime, endTime) = getCurrentDayStartEndEpochMillis() + getUsageStats(startTime, endTime) + } + + fun getUsageStats(startTime: Long, endTime: Long) { + usageStatsList.value = usageStatsRepository.getUsageStats(startTime, endTime) + } +} diff --git a/core/common/src/main/java/com/hmh/hamyeonham/common/context/ContextExt.kt b/core/common/src/main/java/com/hmh/hamyeonham/common/context/ContextExt.kt index c9943e4d..c248e17a 100644 --- a/core/common/src/main/java/com/hmh/hamyeonham/common/context/ContextExt.kt +++ b/core/common/src/main/java/com/hmh/hamyeonham/common/context/ContextExt.kt @@ -2,6 +2,7 @@ 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.os.Build import android.view.View @@ -66,3 +67,13 @@ 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" + } + diff --git a/core/common/src/main/java/com/hmh/hamyeonham/common/time/TimeExt.kt b/core/common/src/main/java/com/hmh/hamyeonham/common/time/TimeExt.kt index fd90e0b2..fc62799e 100644 --- a/core/common/src/main/java/com/hmh/hamyeonham/common/time/TimeExt.kt +++ b/core/common/src/main/java/com/hmh/hamyeonham/common/time/TimeExt.kt @@ -5,7 +5,9 @@ import android.text.format.DateUtils 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 fun Instant.Companion.systemNow(): Instant = Clock.System.now() @@ -28,3 +30,19 @@ fun Long.formatNumericDate(context: Context): String = DateUtils.formatDateTime( 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 { + 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) +} diff --git a/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/datasource/UsageStatsDataSource.kt b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/datasource/UsageStatsDataSource.kt index 4d4c6da0..8525039e 100644 --- a/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/datasource/UsageStatsDataSource.kt +++ b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/datasource/UsageStatsDataSource.kt @@ -1,6 +1,5 @@ package com.hmh.hamyeonham.usagestats.datasource -import com.hmh.hamyeonham.usagestats.model.UsageStat import com.hmh.hamyeonham.usagestats.model.UsageStatModel interface UsageStatsDataSource { diff --git a/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/di/UsageStatsModule.kt b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/di/UsageStatsModule.kt index 59170907..0b47d2c5 100644 --- a/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/di/UsageStatsModule.kt +++ b/data/usagestats/src/main/java/com/hmh/hamyeonham/usagestats/di/UsageStatsModule.kt @@ -18,8 +18,9 @@ import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) object UsageStatsModule { - @Singleton + @Provides + @Singleton fun provideUsageStatusManager(@ApplicationContext context: Context): UsageStatsManager? { return context.getSystemService(Context.USAGE_STATS_SERVICE) as? UsageStatsManager } From b3027376cade27d57ef73aa57d3acbd956a8bdec Mon Sep 17 00:00:00 2001 From: KwakEuiJin Date: Sun, 31 Dec 2023 13:43:33 +0900 Subject: [PATCH 05/26] [fix]: Merge Error --- app/src/main/java/com/hmh/hamyeonham/MainActivity.kt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/src/main/java/com/hmh/hamyeonham/MainActivity.kt b/app/src/main/java/com/hmh/hamyeonham/MainActivity.kt index 9ec25b1a..7e3ec705 100644 --- a/app/src/main/java/com/hmh/hamyeonham/MainActivity.kt +++ b/app/src/main/java/com/hmh/hamyeonham/MainActivity.kt @@ -24,10 +24,6 @@ class MainActivity : AppCompatActivity() { val splashScreen = installSplashScreen() initSplashAnimation(splashScreen) setContentView(R.layout.activity_main) - // todo - 리사이클러뷰 구현 -// initAdapter() - initUsageStat() - } lifecycleScope.launch { viewModel.usageStatsList.collect { From 91791dc2168cb5629e040cd402968e85b4b57743 Mon Sep 17 00:00:00 2001 From: KwakEuiJin Date: Sun, 31 Dec 2023 15:20:41 +0900 Subject: [PATCH 06/26] [feat]: Feat Statics Module --- app/build.gradle.kts | 3 +++ .../java/com/hmh/hamyeonham/MainActivity.kt | 4 ++++ feature/statistics/.gitignore | 1 + feature/statistics/build.gradle.kts | 16 +++++++++++++ feature/statistics/consumer-rules.pro | 0 feature/statistics/proguard-rules.pro | 21 ++++++++++++++++ .../statistics/ExampleInstrumentedTest.kt | 24 +++++++++++++++++++ .../statistics/src/main/AndroidManifest.xml | 10 ++++++++ .../hamyeonham/statistics/StaticsActivity.kt | 12 ++++++++++ .../src/main/res/layout/activity_statics.xml | 9 +++++++ .../src/main/res/values/strings.xml | 1 + settings.gradle.kts | 3 +-- 12 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 feature/statistics/.gitignore create mode 100644 feature/statistics/build.gradle.kts create mode 100644 feature/statistics/consumer-rules.pro create mode 100644 feature/statistics/proguard-rules.pro create mode 100644 feature/statistics/src/androidTest/java/com/hmh/hamyeonham/statistics/ExampleInstrumentedTest.kt create mode 100644 feature/statistics/src/main/AndroidManifest.xml create mode 100644 feature/statistics/src/main/java/com/hmh/hamyeonham/statistics/StaticsActivity.kt create mode 100644 feature/statistics/src/main/res/layout/activity_statics.xml create mode 100644 feature/statistics/src/main/res/values/strings.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ba3909ee..db086c05 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -29,6 +29,9 @@ android { dependencies { + // Feature + implementation(projects.feature.statistics) + // Domain implementation(projects.domain.usagestats) diff --git a/app/src/main/java/com/hmh/hamyeonham/MainActivity.kt b/app/src/main/java/com/hmh/hamyeonham/MainActivity.kt index 7e3ec705..59f886f3 100644 --- a/app/src/main/java/com/hmh/hamyeonham/MainActivity.kt +++ b/app/src/main/java/com/hmh/hamyeonham/MainActivity.kt @@ -1,5 +1,6 @@ package com.hmh.hamyeonham +import android.content.Intent import android.os.Bundle import android.util.Log import android.view.animation.Animation @@ -10,6 +11,7 @@ import androidx.core.splashscreen.SplashScreen import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.lifecycle.lifecycleScope import com.hmh.hamyeonham.common.context.getAppNameFromPackageName +import com.hmh.hamyeonham.statistics.StaticsActivity import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch @@ -24,6 +26,8 @@ class MainActivity : AppCompatActivity() { val splashScreen = installSplashScreen() initSplashAnimation(splashScreen) setContentView(R.layout.activity_main) + val intent = Intent(this, StaticsActivity::class.java) + startActivity(intent) lifecycleScope.launch { viewModel.usageStatsList.collect { diff --git a/feature/statistics/.gitignore b/feature/statistics/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/feature/statistics/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/statistics/build.gradle.kts b/feature/statistics/build.gradle.kts new file mode 100644 index 00000000..2a7887cc --- /dev/null +++ b/feature/statistics/build.gradle.kts @@ -0,0 +1,16 @@ +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + hmh("feature") + alias(libs.plugins.kotlin.android) +} + +android { + namespace = "com.hmh.hamyeonham.feature.statistics" +} + +dependencies { + implementation(projects.domain.usagestats) + implementation(libs.appcompat) + implementation(libs.material) + implementation(libs.constraintlayout) +} diff --git a/feature/statistics/consumer-rules.pro b/feature/statistics/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/feature/statistics/proguard-rules.pro b/feature/statistics/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/feature/statistics/proguard-rules.pro @@ -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 \ No newline at end of file diff --git a/feature/statistics/src/androidTest/java/com/hmh/hamyeonham/statistics/ExampleInstrumentedTest.kt b/feature/statistics/src/androidTest/java/com/hmh/hamyeonham/statistics/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..c928ceb9 --- /dev/null +++ b/feature/statistics/src/androidTest/java/com/hmh/hamyeonham/statistics/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.hmh.hamyeonham.statistics + +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.hmh.hamyeonham.statistics.test", appContext.packageName) + } +} diff --git a/feature/statistics/src/main/AndroidManifest.xml b/feature/statistics/src/main/AndroidManifest.xml new file mode 100644 index 00000000..9ef30252 --- /dev/null +++ b/feature/statistics/src/main/AndroidManifest.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/feature/statistics/src/main/java/com/hmh/hamyeonham/statistics/StaticsActivity.kt b/feature/statistics/src/main/java/com/hmh/hamyeonham/statistics/StaticsActivity.kt new file mode 100644 index 00000000..41e2872b --- /dev/null +++ b/feature/statistics/src/main/java/com/hmh/hamyeonham/statistics/StaticsActivity.kt @@ -0,0 +1,12 @@ +package com.hmh.hamyeonham.statistics + +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import com.hmh.hamyeonham.feature.statistics.R + +class StaticsActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_statics) + } +} \ No newline at end of file diff --git a/feature/statistics/src/main/res/layout/activity_statics.xml b/feature/statistics/src/main/res/layout/activity_statics.xml new file mode 100644 index 00000000..a343388e --- /dev/null +++ b/feature/statistics/src/main/res/layout/activity_statics.xml @@ -0,0 +1,9 @@ + + + + diff --git a/feature/statistics/src/main/res/values/strings.xml b/feature/statistics/src/main/res/values/strings.xml new file mode 100644 index 00000000..7abc06d3 --- /dev/null +++ b/feature/statistics/src/main/res/values/strings.xml @@ -0,0 +1 @@ + diff --git a/settings.gradle.kts b/settings.gradle.kts index 65069a04..d4ee2f82 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -19,7 +19,6 @@ dependencyResolutionManagement { rootProject.name = "HMH-Android" include(":app") include(":core:common") -include(":data") include(":data:usagestats") -include(":domain") include(":domain:usagestats") +include(":feature:statistics") From f5aa2653d2e2dbb1da7c262f494b93424e513cea Mon Sep 17 00:00:00 2001 From: memoryBangwool Date: Tue, 2 Jan 2024 16:46:21 +0900 Subject: [PATCH 07/26] =?UTF-8?q?[chore]=20usage=20stats=20manager=20?= =?UTF-8?q?=ED=83=90=EA=B5=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/hmh/hamyeonham/MainViewModel.kt | 34 +++++++++++++------ .../java/com/hmh/hamyeonham/SampleActivity.kt | 29 +++++++--------- 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/com/hmh/hamyeonham/MainViewModel.kt b/app/src/main/java/com/hmh/hamyeonham/MainViewModel.kt index 3a195ef5..e84570c6 100644 --- a/app/src/main/java/com/hmh/hamyeonham/MainViewModel.kt +++ b/app/src/main/java/com/hmh/hamyeonham/MainViewModel.kt @@ -1,5 +1,6 @@ package com.hmh.hamyeonham +import android.util.Log import androidx.lifecycle.ViewModel import com.hmh.hamyeonham.common.time.getCurrentDayStartEndEpochMillis import com.hmh.hamyeonham.usagestats.model.UsageStat @@ -9,18 +10,29 @@ import kotlinx.coroutines.flow.MutableStateFlow import javax.inject.Inject @HiltViewModel -class MainViewModel @Inject constructor( - private val usageStatsRepository: UsageStatsRepository -) : ViewModel() { +class MainViewModel + @Inject + constructor( + private val usageStatsRepository: UsageStatsRepository, + ) : ViewModel() { + val usageStatsList = MutableStateFlow>(emptyList()) - val usageStatsList = MutableStateFlow>(emptyList()) + init { + val (startTime, endTime) = getCurrentDayStartEndEpochMillis() + getUsageStats(startTime, endTime) + } - init { - val (startTime, endTime) = getCurrentDayStartEndEpochMillis() - getUsageStats(startTime, endTime) - } + fun getUsageStats( + startTime: Long, + endTime: Long, + ) { + usageStatsList.value = usageStatsRepository.getUsageStats(startTime, endTime) + } - fun getUsageStats(startTime: Long, endTime: Long) { - usageStatsList.value = usageStatsRepository.getUsageStats(startTime, endTime) + fun printUsageStats() { + for (i in usageStatsList.value.listIterator()) { + Log.d("usage id", i.packageName.toString()) + Log.d("usage time", i.totalTimeInForeground.toString()) + } + } } -} diff --git a/app/src/main/java/com/hmh/hamyeonham/SampleActivity.kt b/app/src/main/java/com/hmh/hamyeonham/SampleActivity.kt index 85dde485..b1092487 100644 --- a/app/src/main/java/com/hmh/hamyeonham/SampleActivity.kt +++ b/app/src/main/java/com/hmh/hamyeonham/SampleActivity.kt @@ -4,16 +4,17 @@ import android.os.Bundle import android.util.Log import android.view.animation.Animation import android.view.animation.AnimationUtils +import android.widget.Button import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.core.splashscreen.SplashScreen import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.lifecycle.lifecycleScope import com.hmh.hamyeonham.common.context.getAppNameFromPackageName +import com.hmh.hamyeonham.common.time.getCurrentDayStartEndEpochMillis import com.hmh.hamyeonham.databinding.ActivitySampleBinding import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch -import java.util.Calendar @AndroidEntryPoint class SampleActivity : AppCompatActivity() { @@ -26,9 +27,6 @@ class SampleActivity : AppCompatActivity() { val splashScreen = installSplashScreen() initSplashAnimation(splashScreen) setContentView(R.layout.activity_sample) -// val intent = Intent(this, StaticsActivity::class.java) -// startActivity(intent) - getUsageStats() lifecycleScope.launch { viewModel.usageStatsList.collect { it.forEach { @@ -40,21 +38,20 @@ class SampleActivity : AppCompatActivity() { } } } + btUsageClick() } - private fun getUsageStats() { - Log.e("get usage stats function", "start here") - binding.btUsagestat.setOnClickListener { - Log.e("setOnClickListener", "btUsageStat") - val endTime = System.currentTimeMillis() - val startDate = Calendar.getInstance() - startDate.set(Calendar.HOUR_OF_DAY, 0) - startDate.set(Calendar.MINUTE, 0) - startDate.set(Calendar.SECOND, 0) - val startTime = startDate.getTimeInMillis() - Log.e("endtime", endTime.toString()) - Log.e("start time", startTime.toString()) + private fun btUsageClick() { + val button = findViewById