package com.example.fmassive import android.annotation.SuppressLint import android.app.* import android.content.Context import android.content.Intent import android.media.AudioAttributes import android.media.MediaPlayer import android.media.MediaPlayer.OnPreparedListener import android.net.Uri import android.os.* import android.util.Log import androidx.annotation.RequiresApi import androidx.core.app.NotificationCompat @RequiresApi(Build.VERSION_CODES.O) class AlarmService : Service(), OnPreparedListener { private var mediaPlayer: MediaPlayer? = null private var vibrator: Vibrator? = null private fun getBuilder(): NotificationCompat.Builder { val context = applicationContext val contentIntent = Intent(context, MainActivity::class.java) val pendingContent = PendingIntent.getActivity(context, 0, contentIntent, PendingIntent.FLAG_IMMUTABLE) val addBroadcast = Intent(MainActivity.ADD_BROADCAST).apply { setPackage(context.packageName) } val pendingAdd = PendingIntent.getBroadcast(context, 0, addBroadcast, PendingIntent.FLAG_MUTABLE) val stopBroadcast = Intent(MainActivity.STOP_BROADCAST) stopBroadcast.setPackage(context.packageName) val pendingStop = PendingIntent.getBroadcast(context, 0, stopBroadcast, PendingIntent.FLAG_IMMUTABLE) return NotificationCompat.Builder(context, MainActivity.CHANNEL_ID_PENDING) .setSmallIcon(R.drawable.baseline_hourglass_bottom_24).setContentTitle("Resting") .setContentIntent(pendingContent) .addAction(R.drawable.launch_background, "Stop", pendingStop) .addAction(R.drawable.launch_background, "Add 1 min", pendingAdd) .setDeleteIntent(pendingStop) } @SuppressLint("Range") private fun getSettings(): Settings { val db = DatabaseHelper(applicationContext).readableDatabase val cursor = db.rawQuery("SELECT sound, no_sound, vibrate FROM settings", null) cursor.moveToFirst() val sound = cursor.getString(cursor.getColumnIndex("sound")) val noSound = cursor.getInt(cursor.getColumnIndex("no_sound")) == 1 val vibrate = cursor.getInt(cursor.getColumnIndex("vibrate")) == 1 Log.d("AlarmService", "vibrate=$vibrate") cursor.close() return Settings(sound, noSound, vibrate) } private fun playSound(settings: Settings) { if (settings.sound == null && !settings.noSound) { Log.d("AlarmService", "Playing default alarm sound...") mediaPlayer = MediaPlayer.create(applicationContext, R.raw.argon) mediaPlayer?.start() mediaPlayer?.setOnCompletionListener { vibrator?.cancel() } } else if (settings.sound != null && !settings.noSound) { Log.d("AlarmService", "Playing custom alarm sound ${settings.sound}...") mediaPlayer = MediaPlayer().apply { setAudioAttributes( AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .setUsage(AudioAttributes.USAGE_MEDIA) .build() ) setDataSource(applicationContext, Uri.parse(settings.sound)) prepare() start() setOnCompletionListener { vibrator?.cancel() } } } } private fun doNotify(): Notification { Log.d("AlarmService", "doNotify") val alarmsChannel = NotificationChannel( CHANNEL_ID_DONE, CHANNEL_ID_DONE, NotificationManager.IMPORTANCE_HIGH ) alarmsChannel.description = "Alarms for rest timers." alarmsChannel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC alarmsChannel.setSound(null, null) val manager = applicationContext.getSystemService( NotificationManager::class.java ) manager.createNotificationChannel(alarmsChannel) val builder = getBuilder() val context = applicationContext val finishIntent = Intent(context, StopAlarm::class.java) val finishPending = PendingIntent.getActivity( context, 0, finishIntent, PendingIntent.FLAG_IMMUTABLE ) builder.setContentText("Timer finished.").setProgress(0, 0, false) .setAutoCancel(true).setOngoing(true) .setContentIntent(finishPending).setChannelId(CHANNEL_ID_DONE) .setCategory(NotificationCompat.CATEGORY_ALARM).priority = NotificationCompat.PRIORITY_HIGH val notification = builder.build() manager.notify(NOTIFICATION_ID_DONE, notification) manager.cancel(MainActivity.NOTIFICATION_ID_PENDING) return notification } @SuppressLint("Recycle") override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { Log.d("AlarmService", "onStartCommand") val notification = doNotify() startForeground(NOTIFICATION_ID_DONE, notification) val settings = getSettings() playSound(settings) if (!settings.vibrate) return START_STICKY val pattern = longArrayOf(0, 300, 1300, 300, 1300, 300) vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { val vibratorManager = getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager vibratorManager.defaultVibrator } else { @Suppress("DEPRECATION") getSystemService(VIBRATOR_SERVICE) as Vibrator } val audioAttributes = AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) .setUsage(AudioAttributes.USAGE_ALARM) .build() vibrator!!.vibrate(VibrationEffect.createWaveform(pattern, 1), audioAttributes) return START_STICKY } override fun onBind(intent: Intent): IBinder? { return null } override fun onPrepared(player: MediaPlayer) { player.start() } override fun onDestroy() { super.onDestroy() mediaPlayer?.stop() mediaPlayer?.release() vibrator?.cancel() } companion object { const val CHANNEL_ID_DONE = "Alarm" const val NOTIFICATION_ID_DONE = 2 } }