Skip to content

Commit 0fe0e17

Browse files
committed
feat : 알람 테스트용 구현 완료 && 시스템 설정에 따르지 않고 소리나 진동울리게 구현
1 parent 85ad57d commit 0fe0e17

File tree

16 files changed

+418
-17
lines changed

16 files changed

+418
-17
lines changed

data/src/main/java/com/stop/data/repository/AlarmRepositoryImpl.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ internal class AlarmRepositoryImpl @Inject constructor(
2020
alarmUseCaseItem.routes,
2121
alarmUseCaseItem.lastTime,
2222
alarmUseCaseItem.alarmTime,
23+
alarmUseCaseItem.alarmCode,
2324
alarmUseCaseItem.alarmMethod,
2425
alarmUseCaseItem.isMission
2526
)

domain/src/main/java/com/stop/domain/model/alarm/AlarmUseCaseItem.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ data class AlarmUseCaseItem(
77
val lastTime: String, // 막차 시간 -> 23:30:15 시분초
88
val alarmTime: Int, // 10분 전 알람 설정 -> 10
99
val alarmCode: Int, // 알람을 식별하기 위한 알람 ID
10-
val alarmMethod: Boolean,
10+
val alarmMethod: Boolean, // true 소리 false 진동
1111
val isMission: Boolean,
1212
)

presentation/build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,7 @@ dependencies {
7474

7575
// LiveData
7676
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.5.1'
77+
78+
// LifeCycle Service
79+
implementation 'androidx.lifecycle:lifecycle-service:2.5.1'
7780
}

presentation/src/main/AndroidManifest.xml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@
66
<uses-permission android:name="android.permission.INTERNET" />
77
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
88
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
9+
910
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
11+
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
12+
<uses-permission android:name="android.permission.WAKE_LOCK" />
13+
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
14+
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
15+
<uses-permission android:name="android.permission.VIBRATE" />
1016

1117
<application
1218
android:name=".TroubleShooterApplication"
@@ -20,10 +26,31 @@
2026
android:theme="@style/Theme.PlzStop"
2127
android:usesCleartextTraffic="true"
2228
tools:targetApi="31">
29+
30+
<activity
31+
android:name=".TestActivity"
32+
android:excludeFromRecents="true"
33+
android:exported="true"
34+
android:launchMode="singleTop"
35+
android:showOnLockScreen="true">
36+
<intent-filter>
37+
<action android:name="android.intent.action.MAIN" />
38+
<category android:name="android.intent.category.DEFAULT" />
39+
</intent-filter>
40+
</activity>
41+
42+
<receiver
43+
android:name=".AlarmReceiver"
44+
android:enabled="true"/>
45+
46+
<service
47+
android:name=".SoundService"/>
48+
2349
<provider
2450
android:name="androidx.startup.InitializationProvider"
2551
android:authorities="${applicationId}.androidx-startup"
2652
tools:node="remove" />
53+
2754
<activity
2855
android:name=".MainActivity"
2956
android:exported="true">
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package com.stop
2+
3+
import android.app.AlarmManager
4+
import android.app.PendingIntent
5+
import android.content.Context
6+
import android.content.Intent
7+
import android.os.Build
8+
import java.text.ParseException
9+
import java.text.SimpleDateFormat
10+
import java.util.*
11+
12+
class AlarmFunctions(
13+
private val context: Context
14+
) {
15+
16+
private lateinit var pendingIntent: PendingIntent
17+
18+
fun callAlarm(lastTime: String, alarmTime: Int, alarmCode: Int, alarmContent: String) {
19+
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
20+
21+
val receiverIntent = Intent(context, AlarmReceiver::class.java)
22+
receiverIntent.apply {
23+
putExtra("ALARM_REQUEST_CODE", alarmCode)
24+
putExtra("ALARM_CONTENT", alarmContent)
25+
}
26+
27+
val pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
28+
PendingIntent.getBroadcast(context, alarmCode, receiverIntent, PendingIntent.FLAG_IMMUTABLE)
29+
} else {
30+
PendingIntent.getBroadcast(context, alarmCode, receiverIntent, PendingIntent.FLAG_UPDATE_CURRENT)
31+
}
32+
33+
val currentTime = System.currentTimeMillis()
34+
val currentFormat = SimpleDateFormat("yyyy:MM:dd", Locale.getDefault())
35+
val currentDateTime = currentFormat.format(currentTime)
36+
val fullTime = "$currentDateTime:$lastTime"
37+
38+
39+
val dateFormat = SimpleDateFormat("yyyy:MM:dd:HH:mm:ss", Locale.getDefault())
40+
var dateTime = Date()
41+
try {
42+
dateTime = dateFormat.parse(fullTime) as Date
43+
} catch (e: ParseException) {
44+
e.printStackTrace()
45+
}
46+
47+
val calendar = Calendar.getInstance()
48+
calendar.time = dateTime
49+
50+
alarmManager.setExactAndAllowWhileIdle(
51+
AlarmManager.RTC_WAKEUP,
52+
calendar.timeInMillis + (alarmTime * 60 * 1000), // 막차 시간에서 알람시간 만큼 뺀 timeInMillis
53+
pendingIntent
54+
)
55+
}
56+
57+
fun cancelAlarm(alarmCode: Int) {
58+
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
59+
val intent = Intent(context, AlarmReceiver::class.java)
60+
61+
pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
62+
PendingIntent.getBroadcast(context, alarmCode, intent, PendingIntent.FLAG_IMMUTABLE)
63+
} else {
64+
PendingIntent.getBroadcast(context, alarmCode, intent, PendingIntent.FLAG_UPDATE_CURRENT)
65+
}
66+
67+
alarmManager.cancel(pendingIntent)
68+
}
69+
70+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package com.stop
2+
3+
import android.app.NotificationChannel
4+
import android.app.NotificationManager
5+
import android.app.PendingIntent
6+
import android.content.BroadcastReceiver
7+
import android.content.Context
8+
import android.content.Intent
9+
import androidx.core.app.NotificationCompat
10+
11+
class AlarmReceiver : BroadcastReceiver() {
12+
13+
override fun onReceive(context: Context, intent: Intent) {
14+
val requestCode = intent.extras?.getInt("ALARM_REQUEST_CODE") ?: -1
15+
val alarmContent = intent.extras?.getString("ALARM_CONTENT") ?: ""
16+
17+
val soundServiceIntent = Intent(context, SoundService::class.java)
18+
19+
if (isMoreThanOreo()) {
20+
context.startForegroundService(soundServiceIntent)
21+
22+
val notificationManager = context.getSystemService(NotificationManager::class.java)
23+
if (notificationManager.getNotificationChannel(ALARM_RECEIVER_CHANNEL_ID) == null) {
24+
NotificationChannel(ALARM_RECEIVER_CHANNEL_ID, ALARM_RECEIVER_CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH).apply {
25+
notificationManager.createNotificationChannel(this)
26+
}
27+
}
28+
29+
val pendingIntent = PendingIntent.getActivity(context, requestCode, Intent(context, TestActivity::class.java).apply {
30+
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
31+
}, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
32+
33+
val builder = NotificationCompat.Builder(context, ALARM_RECEIVER_CHANNEL_ID)
34+
.setSmallIcon(R.drawable.ic_baseline_alarm_on_24)
35+
.setContentTitle(LAST_TRANSPORT_NOTIFICATION_TITLE)
36+
.setContentText(alarmContent)
37+
.setAutoCancel(true)
38+
.setPriority(NotificationCompat.PRIORITY_HIGH)
39+
.setCategory(NotificationCompat.CATEGORY_ALARM)
40+
.setFullScreenIntent(pendingIntent, true)
41+
42+
try {
43+
notificationManager.notify(ALARM_NOTIFICATION_ID, builder.build())
44+
} catch (e: Exception) {
45+
e.printStackTrace()
46+
}
47+
} else {
48+
context.startService(soundServiceIntent)
49+
50+
Intent(context, TestActivity::class.java).apply {
51+
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
52+
context.startActivity(this)
53+
}
54+
}
55+
}
56+
57+
companion object {
58+
const val ALARM_RECEIVER_CHANNEL_ID = "ALARM_RECEIVER_CHANNEL_ID"
59+
const val ALARM_RECEIVER_CHANNEL_NAME = "ALARM_RECEIVER_CHANNEL_NAME"
60+
const val ALARM_NOTIFICATION_ID = 123
61+
private const val LAST_TRANSPORT_NOTIFICATION_TITLE = "막차 알림"
62+
}
63+
64+
}

presentation/src/main/java/com/stop/AlarmWorker.kt

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,10 @@ import androidx.work.CoroutineWorker
77
import androidx.work.Data
88
import androidx.work.WorkerParameters
99
import androidx.work.workDataOf
10-
import com.stop.domain.model.nearplace.Place
1110
import com.stop.domain.usecase.nearplace.GetNearPlacesUseCase
1211
import dagger.assisted.Assisted
1312
import dagger.assisted.AssistedInject
1413
import kotlinx.coroutines.Dispatchers
15-
import kotlinx.coroutines.flow.collectLatest
1614
import kotlinx.coroutines.withContext
1715

1816
// 일단은 NearPlaceUseCase 이용하여서 데이터 가져오기 구현
@@ -24,12 +22,10 @@ class AlarmWorker @AssistedInject constructor(
2422
private val getNearPlacesUseCase: GetNearPlacesUseCase
2523
) : CoroutineWorker(context, workerParameters) {
2624

27-
lateinit var resultList: List<Place>
28-
2925
override suspend fun doWork(): Result {
3026
return try {
3127
callApi()
32-
val output: Data = workDataOf("WORK_RESULT" to resultList.toString())
28+
val output: Data = workDataOf("WORK_RESULT" to "result")
3329
Result.success(output)
3430
} catch (e: Exception) {
3531
Log.e("ABC", e.toString())
@@ -39,15 +35,14 @@ class AlarmWorker @AssistedInject constructor(
3935

4036
private suspend fun callApi() {
4137
withContext(Dispatchers.IO) {
42-
getNearPlacesUseCase.getNearPlaces(
38+
val list = getNearPlacesUseCase.getNearPlaces(
4339
1,
4440
"아남타워",
4541
126.969652,
4642
37.553836,
4743
BuildConfig.TMAP_APP_KEY
48-
).collectLatest {
49-
resultList = it
50-
}
44+
)
45+
Log.e("ABC", list.toString())
5146
}
5247

5348
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.stop
2+
3+
import android.os.Build
4+
5+
fun isMoreThanOreo(): Boolean =
6+
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
7+
8+
fun isMoreThanOreoMr1(): Boolean =
9+
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1
10+
11+
fun isUnderOreo(): Boolean =
12+
Build.VERSION.SDK_INT < Build.VERSION_CODES.O
13+
14+
fun isMoreThanOreoUnderRedVelVet(): Boolean =
15+
(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) and (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R)
16+
17+
fun isMoreThanSnow(): Boolean =
18+
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S

presentation/src/main/java/com/stop/MainActivity.kt

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,28 @@
11
package com.stop
22

3+
import android.app.ActivityManager
4+
import android.app.KeyguardManager
35
import android.content.Context
46
import android.graphics.Rect
7+
import android.os.Build
58
import android.os.Bundle
6-
import com.stop.databinding.ActivityMainBinding
79
import android.view.MotionEvent
810
import android.view.View
911
import android.view.WindowManager
1012
import android.view.inputmethod.InputMethodManager
1113
import android.widget.EditText
1214
import androidx.appcompat.app.AppCompatActivity
15+
import com.stop.databinding.ActivityMainBinding
1316
import dagger.hilt.android.AndroidEntryPoint
1417

18+
1519
@AndroidEntryPoint
1620
class MainActivity : AppCompatActivity() {
1721
private lateinit var binding: ActivityMainBinding
1822

1923
override fun onCreate(savedInstanceState: Bundle?) {
2024
super.onCreate(savedInstanceState)
21-
25+
turnScreenOnAndKeyguardOff()
2226
binding = ActivityMainBinding.inflate(layoutInflater)
2327
hideStatusBar()
2428
setContentView(binding.root)
@@ -53,4 +57,37 @@ class MainActivity : AppCompatActivity() {
5357
this.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
5458
inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0)
5559
}
60+
61+
62+
fun isServiceRunningInForeground(context: Context, serviceClass: Class<*>): Boolean {
63+
val manager = context.getSystemService(ACTIVITY_SERVICE) as ActivityManager
64+
for (service in manager.getRunningServices(Int.MAX_VALUE)) {
65+
if (serviceClass.name == service.service.className) {
66+
if (service.foreground) {
67+
return true
68+
}
69+
}
70+
}
71+
return false
72+
}
73+
74+
private fun turnScreenOnAndKeyguardOff(){
75+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
76+
setShowWhenLocked(true)
77+
setTurnScreenOn(true)
78+
window.addFlags(
79+
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
80+
or WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON)
81+
} else {
82+
window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED // deprecated api 27
83+
or WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD // deprecated api 26
84+
or WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
85+
or WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON // deprecated api 27
86+
or WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON)
87+
}
88+
val keyguardMgr = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
89+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
90+
keyguardMgr.requestDismissKeyguard(this, null)
91+
}
92+
}
5693
}

0 commit comments

Comments
 (0)