Skip to content

Commit

Permalink
Fix custom song
Browse files Browse the repository at this point in the history
  • Loading branch information
Théophile Delmas committed Feb 7, 2024
1 parent 30513fe commit 0acb99a
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 192 deletions.
3 changes: 3 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />

<application
android:allowBackup="true"
Expand Down
269 changes: 117 additions & 152 deletions app/src/main/java/com/rooster/rooster/AlarmActivity.kt
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
package com.rooster.rooster

import android.content.Context
import android.icu.text.SimpleDateFormat
import android.media.AudioAttributes
import android.media.AudioFocusRequest
import android.media.AudioManager
import android.media.MediaPlayer
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.PowerManager
import android.os.VibrationEffect
import android.os.Vibrator
import android.util.Log
import android.view.View
Expand All @@ -22,6 +21,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date

Expand All @@ -34,157 +34,157 @@ class AlarmActivity : FragmentActivity() {
private var mediaPlayer: MediaPlayer? = null
private var wakeLock: PowerManager.WakeLock? = null

val alarmDbHelper = AlarmDbHelper(this)

// Assume AlarmDbHelper is a helper class for database operations
private lateinit var alarmDbHelper: AlarmDbHelper

override fun onCreate(savedInstanceState: Bundle?) {
val alarm = alarmDbHelper.getAlarm(alarmId)
Log.e("Alarm", "Alarm Activity Start\n" + "Alarm id: $alarmId")
alarmId = intent.getStringExtra("alarm_id")!!
.toLong() // -1 is a default value if "alarm_id" is not found
Log.e("Alarmclock Reciever", "Alarm id: $alarmId")
alarmIsRunning = true
super.setShowWhenLocked(true)
super.setTurnScreenOn(true)
super.onCreate(savedInstanceState)
setTheme(androidx.appcompat.R.style.Theme_AppCompat);
setContentView(R.layout.activity_alarm)

// Initialize AlarmDbHelper
alarmDbHelper = AlarmDbHelper(this)

alarmId = intent.getStringExtra("alarm_id")?.toLong() ?: -1 // Handle the null case
Log.e("AlarmActivity", "Alarm id: $alarmId")

alarmIsRunning = true
setShowWhenLocked(true)
setTurnScreenOn(true)

val alarm = alarmDbHelper.getAlarm(alarmId)
if (alarm != null) {
alarmRing(alarm.ringtoneUri)
}

setupSeekBar()
refreshCycle()
}

private fun setupSeekBar() {
val seekBar = findViewById<SeekBar>(R.id.seekBar)
seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
// Check if the seek bar is at 90%
if (progress >= 90 ) {
if (progress >= 90) {
stopAlarm(seekBar)
}
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {}

override fun onStartTrackingTouch(seekBar: SeekBar?) {}
override fun onStopTrackingTouch(seekBar: SeekBar?) {}
})
refreshCycle()
if (alarm != null) {
alarmRing(alarm.ringtoneUri)
}
//TODO Release wake lock
val alarmHandler = AlarmHandler()
alarmHandler.setNextAlarm(applicationContext)
}

private fun refreshCycle() {
val progressBar = findViewById<ProgressBar>(R.id.progress_cycle)
val progressText = findViewById<TextView>(R.id.progress_text)
val handler = Handler()
val delayMillis = 1000
val maxProgress = 100

val updateRunnable = object : Runnable {
var i = 0

override fun run() {
val currentTime = System.currentTimeMillis()
val sdf = SimpleDateFormat("HH:mm")
val formattedTime = sdf.format(Date(currentTime))
val percentage = getPercentageOfDay().toLong()
if (percentage <= maxProgress) {
progressText.text = formattedTime
progressBar.progress = percentage.toInt()
handler.postDelayed(this, delayMillis.toLong())
} else {
// Reset progress bar and text
progressBar.progress = 0
progressText.text = "00:00"
// Start the loop again
handler.postDelayed(this, delayMillis.toLong())
}
}
}

handler.post(updateRunnable)
private fun alarmRing(ringtoneUri: String) {
val audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
val audioAttributes = AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.setUsage(AudioAttributes.USAGE_ALARM)
.build()

val focusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
.setAudioAttributes(audioAttributes)
.setOnAudioFocusChangeListener { /* Handle focus change */ }
.build()

if (audioManager.requestAudioFocus(focusRequest) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
wakePhone()
playRingtone(ringtoneUri)
}
}

private fun alarmRing(ringtoneUri: String) {
// Wake Phone
val powerManager: PowerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
private fun wakePhone() {
val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
wakeLock = powerManager.newWakeLock(
PowerManager.ACQUIRE_CAUSES_WAKEUP or
PowerManager.ON_AFTER_RELEASE or
PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "rooster:wakelock"
)
wakeLock?.acquire()

vibrator = applicationContext.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// it is safe to cancel other vibrations currently taking place
vibrator!!.cancel()
val soundUri = if (ringtoneUri == "default") {
// Fallback to a default sound if the URI is null or empty
Uri.parse("android.resource://${applicationContext.packageName}/raw/alarmclock")
} else {
// Use the provided URI
Uri.parse(ringtoneUri)
}
PowerManager.PARTIAL_WAKE_LOCK or
PowerManager.ACQUIRE_CAUSES_WAKEUP or
PowerManager.ON_AFTER_RELEASE, "rooster:wakelock"
).apply { acquire(10 * 60 * 1000L /*10 minutes*/) }
}

private fun playRingtone(ringtoneUri: String) {
vibrator = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
vibrator?.let {
it.cancel()
val uri = if (ringtoneUri == "default") Uri.parse("android.resource://${packageName}/raw/alarmclock") else Uri.parse(ringtoneUri)

mediaPlayer = MediaPlayer().apply {
setDataSource(applicationContext, soundUri)
setAudioAttributes(
AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_ALARM)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.setUsage(AudioAttributes.USAGE_ALARM)
.build()
)
setOnCompletionListener {
// Release MediaPlayer resources after completion
it?.release()
}
prepareAsync()
setOnPreparedListener { player ->
// Start playback when prepared
player.start()
startAlarmLoop()
try {
setDataSource(applicationContext, uri)
prepare() // Prepare the MediaPlayer asynchronously
setOnErrorListener { _, what, extra ->
Log.e("MediaPlayer Error", "What: $what, Extra: $extra")
true
}
setOnPreparedListener { start() }
isLooping = true
} catch (e: Exception) {
Log.e("MediaPlayer", "Error setting data source", e)
}
isLooping = true
}
}
}
private fun startAlarmLoop() {
// Start a loop that checks a SharedPreferences for a flag to stop the vibration
val vibesPattern = createWaveformVibrationPattern(longArrayOf(0, 1000, 2000, 3000), repeat = -1)
var vibrationEffect1 = VibrationEffect.createWaveform(vibesPattern, 3)
CoroutineScope(Dispatchers.Default).launch {
isVibrating = true
vibrator?.vibrate(vibrationEffect1)
if (alarmIsRunning) {
mediaPlayer?.start()
}
while (isVibrating) {
Log.w("Rooster Alarm", "Ring")
// Check for alarmIsRunning flag to control MediaPlayer and vibration
if (alarmIsRunning) {
delay(500)
} else {
isVibrating = false
break
}
delay(1000)

// Remaining methods including refreshCycle, getPercentageOfDay, stopAlarm, onResume, onPause, releaseResources...

private fun releaseResources() {
mediaPlayer?.let {
it.stop()
it.release()
}
mediaPlayer = null
wakeLock?.let {
if (it.isHeld) {
it.release()
}
}
wakeLock = null
vibrator?.cancel()
}

fun createWaveformVibrationPattern(timings: LongArray, repeat: Int = -1): LongArray {
var pattern = LongArray(timings.size + 1)
pattern[0] = 0L
for (i in 1 until pattern.size) {
pattern[i] = pattern[i - 1] + timings[i - 1]
}
fun stopAlarm(view: View?) {
alarmIsRunning = false
isVibrating = false
releaseResources()
finish()
}

if (repeat != -1) {
val repeatIndex = pattern[repeat]
val repeatPattern = pattern.copyOfRange(repeatIndex.toInt(), pattern.size)
pattern = pattern.copyOfRange(0, repeatIndex.toInt()) + repeatPattern
private fun refreshCycle() {
val progressBar = findViewById<ProgressBar>(R.id.progress_cycle)
val progressText = findViewById<TextView>(R.id.progress_text)
val handler = Handler()
val delayMillis = 1000
val maxProgress = 100

val updateRunnable = object : Runnable {
var i = 0

override fun run() {
val currentTime = System.currentTimeMillis()
val sdf = SimpleDateFormat("HH:mm")
val formattedTime = sdf.format(Date(currentTime))
val percentage = getPercentageOfDay().toLong()
if (percentage <= maxProgress) {
progressText.text = formattedTime
progressBar.progress = percentage.toInt()
handler.postDelayed(this, delayMillis.toLong())
} else {
// Reset progress bar and text
progressBar.progress = 0
progressText.text = "00:00"
// Start the loop again
handler.postDelayed(this, delayMillis.toLong())
}
}
}

return pattern
handler.post(updateRunnable)
}

fun getPercentageOfDay(): Float {
Expand All @@ -200,39 +200,4 @@ class AlarmActivity : FragmentActivity() {
val percentage = (totalSeconds / secondsInDay) * 100
return percentage.toFloat()
}

fun stopAlarm(view: View?) {
alarmIsRunning = false
releaseResources()
supportFragmentManager.popBackStackImmediate()
finish()
}

override fun onResume() {
super.onResume()
wakeLock?.acquire()

if (!alarmIsRunning) {
//alarmIsRunning = true
}
}

override fun onPause() {
super.onPause()
stopAlarm(null)
/*alarmIsRunning = false
mediaPlayer?.stop()
vibrator?.cancel()*/
}

private fun releaseResources() {
vibrator?.cancel()
mediaPlayer?.stop()
mediaPlayer?.reset()
mediaPlayer?.release()
mediaPlayer = null

wakeLock?.release()
wakeLock = null
}
}
1 change: 1 addition & 0 deletions app/src/main/java/com/rooster/rooster/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class MainActivity() : ComponentActivity() {
Manifest.permission.USE_FULL_SCREEN_INTENT,
Manifest.permission.SYSTEM_ALERT_WINDOW,
Manifest.permission.WAKE_LOCK,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.SCHEDULE_EXACT_ALARM,
Manifest.permission.FOREGROUND_SERVICE
)
Expand Down
Loading

0 comments on commit 0acb99a

Please sign in to comment.