Skip to content

Commit

Permalink
Merge pull request #376 from apeun-gidaechi/release/0.0.4
Browse files Browse the repository at this point in the history
Release/0.0.4
  • Loading branch information
8954sood authored Dec 6, 2024
2 parents ac103f4 + ec5724a commit bfa3c0f
Show file tree
Hide file tree
Showing 250 changed files with 9,245 additions and 1,920 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ report/
# Local configuration file (sdk path, etc)
local.properties

seugi-android/feature-onboarding/start/src/main/res/values/*

# Log/OS Files
*.log

Expand Down
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2024 apeun-gidaechi

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
6 changes: 4 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ android {

defaultConfig {
applicationId = "com.seugi"
versionCode = 1
versionName = "0.0.1"
versionCode = 4
versionName = "0.0.4"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
Expand Down Expand Up @@ -43,6 +43,7 @@ dependencies {
implementation(platform(libs.firebase.bom))
implementation(libs.firebase.analytics)
implementation(libs.firebase.message)
implementation(libs.accompanist.permissions)


implementation(projects.network.core)
Expand All @@ -54,5 +55,6 @@ dependencies {
implementation(projects.data.token)
implementation(projects.data.firebaseToken)
implementation(projects.featureMain.mealWidget)
implementation(projects.featureMain.timetableWidget)

}
22 changes: 20 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.Seugi"
android:windowSoftInputMode="adjustResize">
android:windowSoftInputMode="adjustResize"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

Expand All @@ -43,7 +44,7 @@
<receiver
android:name=".meal.widget.MealWidgetReceiver"
android:exported="true"
android:label="Hello World">
android:label="급식 위젯">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
Expand All @@ -57,6 +58,23 @@
android:name=".meal.widget.MealWidgetService"
android:permission="android.permission.BIND_REMOTEVIEWS" />

<receiver
android:name=".timetable.widget.TimetableWidgetReceiver"
android:exported="true"
android:label="시간표 위젯">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/timetable_widget_info"
/>
</receiver>

<service
android:name=".timetable.widget.TimetableWidgetService"
android:permission="android.permission.BIND_REMOTEVIEWS" />


</application>

Expand Down
38 changes: 37 additions & 1 deletion app/src/main/java/com/seugi/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package com.seugi

import android.Manifest
import android.os.Build
import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.annotation.RequiresApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
Expand All @@ -15,6 +19,7 @@ import androidx.compose.material3.Snackbar
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
Expand All @@ -24,11 +29,17 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.rememberNavController
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.isGranted
import com.google.accompanist.permissions.rememberPermissionState
import com.google.accompanist.permissions.shouldShowRationale
import com.seugi.designsystem.component.SeugiDialog
import com.seugi.designsystem.theme.SeugiTheme
import com.seugi.main.navigation.MAIN_ROUTE
import com.seugi.main.navigation.mainScreen
Expand All @@ -54,7 +65,9 @@ class MainActivity : ComponentActivity() {
var showSplash by remember { mutableStateOf(true) }
val snackbarHostState = remember { SnackbarHostState() }
val coroutineScope = rememberCoroutineScope()

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
RequestNotificationPermissionDialog()
}
LaunchedEffect(state) {
if (state == null) {
delay(2000)
Expand Down Expand Up @@ -125,3 +138,26 @@ class MainActivity : ComponentActivity() {
}
}
}

@RequiresApi(Build.VERSION_CODES.TIRAMISU)
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun RequestNotificationPermissionDialog() {
val permissionState = rememberPermissionState(permission = Manifest.permission.POST_NOTIFICATIONS)
val context = LocalContext.current

if (!permissionState.status.isGranted) {
if (permissionState.status.shouldShowRationale) {
Toast.makeText(context, "알림을 켜주세요", Toast.LENGTH_SHORT).show()
} else {
SeugiDialog(
title = "스기 알람 설정",
content = "스기의 알람 기능을 이용하기 위해선 권한을 허용해야합니다.",
buttonText = "확인",
onDismissRequest = {
permissionState.launchPermissionRequest()
},
)
}
}
}
47 changes: 47 additions & 0 deletions app/src/main/java/com/seugi/util/FirebaseMessageService.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
package com.seugi.util

import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.NotificationManager.IMPORTANCE_DEFAULT
import android.app.PendingIntent
import android.app.PendingIntent.FLAG_IMMUTABLE
import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP
import android.os.Build
import android.util.Log
import androidx.core.app.NotificationCompat
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import com.seugi.MainActivity
import com.seugi.R
import com.seugi.data.firebasetoken.FirebaseTokenRepository
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.random.Random
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
Expand All @@ -32,11 +44,46 @@ class FirebaseMessageService : FirebaseMessagingService() {

override fun onMessageReceived(message: RemoteMessage) {
super.onMessageReceived(message)
message.notification?.let { message ->
sendNotification(message)
}
Log.d("TAG", "Notification Title :: ${message.notification?.title}")
Log.d("TAG", "Notification Body :: ${message.notification?.body}")
Log.d("TAG", "Notification Data :: ${message.data}")
}

private fun sendNotification(message: RemoteMessage.Notification) {
val intent = Intent(this, MainActivity::class.java).apply {
addFlags(FLAG_ACTIVITY_CLEAR_TOP)
}

val pendingIntent = PendingIntent.getActivity(
this,
0,
intent,
FLAG_IMMUTABLE,
)

val channelId = getString(R.string.default_notification_channel_id)
val channelName = getString(R.string.default_notification_channel_name)

val notificationBuilder = NotificationCompat.Builder(this, channelId)
.setContentTitle(message.title)
.setContentText(message.body)
.setSmallIcon(R.mipmap.ic_launcher)
.setAutoCancel(true)
.setContentIntent(pendingIntent)

val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(channelId, channelName, IMPORTANCE_DEFAULT)
manager.createNotificationChannel(channel)
}

manager.notify(Random.nextInt(), notificationBuilder.build())
}

override fun onDestroy() {
super.onDestroy()
serviceScope.cancel()
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
<resources>
<string name="app_name">스기</string>
<string name="widget_meal_description">스기 급식</string>
<string name="widget_timetable_description">스기 시간표 위젯</string>
<string name="default_notification_channel_id">seugi_default_channle</string>
<string name="default_notification_channel_name">Seugi Default Channle</string>
</resources>
12 changes: 12 additions & 0 deletions app/src/main/res/xml/timetable_widget_info.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:resizeMode="horizontal|vertical"
android:minHeight="64dp"
android:minWidth="128dp"
android:description="@string/widget_timetable_description"
android:updatePeriodMillis="900000"
android:minResizeHeight="128dp"
android:minResizeWidth="64dp"
android:maxResizeHeight="512dp"
android:maxResizeWidth="512dp"
android:initialLayout="@layout/widget_timetable_layout">
</appwidget-provider>
21 changes: 21 additions & 0 deletions common/src/main/java/com/seugi/common/utiles/Extension.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,34 @@ import kotlinx.datetime.LocalDate

fun LocalDateTime.toDeviceLocalDateTime(): LocalDateTime = this.atZone(ZoneId.of("UTC")).withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime()

/**
* return to ex) 2024년 7월 14일 금요일
*/
fun LocalDateTime.toFullFormatString(): String {
val formatter = DateTimeFormatter.ofPattern("yyyy년 M월 d일 E요일", Locale.KOREAN)
return this.toDeviceLocalDateTime().format(formatter)
}

/**
* return to ex) 12:44
*/
fun LocalDateTime.toShortString(): String {
val formatter = DateTimeFormatter.ofPattern("HH:mm")
return this.toDeviceLocalDateTime().format(formatter)
}

/**
* return to ex) 오후 12:44
*/
fun LocalDateTime.toAmShortString(): String {
val time = this.toDeviceLocalDateTime()
val isAm = time.hour < 12
return (if (isAm) "오전" else "오후") + " " + time.minusHours(if (!isAm && time.hour != 12) 12 else 0).format(DateTimeFormatter.ofPattern("HH:mm"))
}

/**
* return to ex) 7월 11일
*/
fun LocalDateTime.toTimeString(): String {
val time = this.toDeviceLocalDateTime()

Expand Down Expand Up @@ -53,3 +65,12 @@ fun LocalDateTime.toEpochMilli() = this
.takeIf {
it >= 0
} ?: 0

fun <T, R> List<T>.toMap(key: (T) -> R): MutableMap<R, T> {
val mutableMap = mutableMapOf<R, T>()
this.forEach {
mutableMap[key(it)] = it
}

return mutableMap
}
17 changes: 17 additions & 0 deletions data/assignment/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
plugins {
alias(libs.plugins.seugi.android.feature)
alias(libs.plugins.seugi.android.hilt)
alias(libs.plugins.seugi.android.kotlin)
}

android {
namespace = "com.seugi.data.task"
}

dependencies {

implementation(projects.common)
implementation(projects.network.core)
implementation(projects.network.assignment)
api(projects.data.core)
}
Empty file.
21 changes: 21 additions & 0 deletions data/assignment/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
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.seugi.data.assignment

import com.seugi.common.model.Result
import com.seugi.data.assignment.model.AssignmentModel
import java.time.LocalDateTime
import kotlinx.collections.immutable.ImmutableList
import kotlinx.coroutines.flow.Flow

interface AssignmentRepository {

suspend fun getWorkspaceTaskAll(workspaceId: String): Flow<Result<ImmutableList<AssignmentModel>>>

suspend fun getGoogleTaskAll(): Flow<Result<ImmutableList<AssignmentModel>>>

suspend fun createTask(workspaceId: String, title: String, description: String, dueDate: LocalDateTime): Flow<Result<Boolean>>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.seugi.data.assignment.di

import com.seugi.data.assignment.AssignmentRepository
import com.seugi.data.assignment.repository.AssignmentRepositoryImpl
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
interface RepositoryModule {

@Singleton
@Binds
fun bindsAssignmentRepository(taskRepositoryImpl: AssignmentRepositoryImpl): AssignmentRepository
}
Loading

0 comments on commit bfa3c0f

Please sign in to comment.