-
-
Notifications
You must be signed in to change notification settings - Fork 70
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
278b89d
commit f439fa0
Showing
13 changed files
with
264 additions
and
71 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
88 changes: 56 additions & 32 deletions
88
android/src/main/kotlin/com/gdelataillade/alarm/alarm/AlarmPlugin.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,52 +1,76 @@ | ||
package com.gdelataillade.alarm.alarm | ||
|
||
import android.app.AlarmManager | ||
import android.app.PendingIntent | ||
import android.content.Context | ||
import android.content.Intent | ||
import androidx.annotation.NonNull | ||
import io.flutter.Log | ||
import io.flutter.embedding.engine.plugins.FlutterPlugin | ||
import io.flutter.plugin.common.MethodCall | ||
import io.flutter.plugin.common.MethodChannel | ||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler | ||
import io.flutter.plugin.common.MethodChannel.Result | ||
import io.flutter.Log | ||
|
||
/// Communication between Flutter Alarm service and native Android. | ||
class AlarmPlugin: FlutterPlugin, MethodCallHandler { | ||
private lateinit var context: Context | ||
private lateinit var channel : MethodChannel | ||
private lateinit var context: Context | ||
private lateinit var channel : MethodChannel | ||
|
||
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { | ||
context = flutterPluginBinding.applicationContext | ||
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "com.gdelataillade.alarm/notifOnAppKill") | ||
channel.setMethodCallHandler(this) | ||
} | ||
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { | ||
context = flutterPluginBinding.applicationContext | ||
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "com.gdelataillade.alarm/notifOnAppKill") | ||
channel.setMethodCallHandler(this) | ||
} | ||
|
||
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { | ||
when (call.method) { | ||
"setNotificationOnKillService" -> { | ||
val title = call.argument<String>("title") | ||
val description = call.argument<String>("description") | ||
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { | ||
when (call.method) { | ||
"setAlarm" -> { | ||
val alarmIntent = Intent(context, AlarmReceiver::class.java) | ||
|
||
val serviceIntent = Intent(context, NotificationOnKillService::class.java) | ||
val delayInSeconds = call.argument<Int>("delayInSeconds") | ||
|
||
serviceIntent.putExtra("title", title) | ||
serviceIntent.putExtra("description", description) | ||
alarmIntent.putExtra("id", call.argument<Int>("id")) | ||
alarmIntent.putExtra("assetAudioPath", call.argument<String>("assetAudioPath")) | ||
alarmIntent.putExtra("loopAudio", call.argument<Boolean>("loopAudio")) | ||
alarmIntent.putExtra("vibrate", call.argument<Boolean>("vibrate")) | ||
alarmIntent.putExtra("volume", call.argument<Boolean>("volume")) | ||
alarmIntent.putExtra("fadeDuration", call.argument<Double>("fadeDuration")) | ||
alarmIntent.putExtra("notificationTitle", call.argument<String>("notificationTitle")) | ||
alarmIntent.putExtra("notificationBody", call.argument<String>("notificationBody")) | ||
|
||
context.startService(serviceIntent) | ||
result.success(true) | ||
} | ||
"stopNotificationOnKillService" -> { | ||
val serviceIntent = Intent(context, NotificationOnKillService::class.java) | ||
context.stopService(serviceIntent) | ||
result.success(true) | ||
} | ||
else -> { | ||
result.notImplemented() | ||
val triggerAtMillis = System.currentTimeMillis() + delayInSeconds!! * 1000 | ||
val pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, 0) | ||
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager | ||
|
||
Log.d("AlarmService", "triggerAtMillis: $triggerAtMillis") | ||
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent) | ||
|
||
result.success(true) | ||
} | ||
"setNotificationOnKillService" -> { | ||
val title = call.argument<String>("title") | ||
val description = call.argument<String>("description") | ||
val body = call.argument<String>("body") | ||
|
||
val serviceIntent = Intent(context, NotificationOnKillService::class.java) | ||
serviceIntent.putExtra("title", title) | ||
serviceIntent.putExtra("description", description) | ||
|
||
context.startService(serviceIntent) | ||
result.success(true) | ||
} | ||
"stopNotificationOnKillService" -> { | ||
val serviceIntent = Intent(context, NotificationOnKillService::class.java) | ||
context.stopService(serviceIntent) | ||
result.success(true) | ||
} | ||
else -> { | ||
result.notImplemented() | ||
} | ||
} | ||
} | ||
} | ||
|
||
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { | ||
channel.setMethodCallHandler(null) | ||
} | ||
} | ||
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { | ||
channel.setMethodCallHandler(null) | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
android/src/main/kotlin/com/gdelataillade/alarm/alarm/AlarmReceiver.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package com.gdelataillade.alarm.alarm | ||
|
||
import android.content.BroadcastReceiver | ||
import android.content.Context | ||
import android.content.Intent | ||
import io.flutter.Log | ||
|
||
class AlarmReceiver : BroadcastReceiver() { | ||
override fun onReceive(context: Context, intent: Intent) { | ||
Log.d("AlarmReceiver", "AlarmReceiver triggered") | ||
|
||
val serviceIntent = Intent(context, AlarmService::class.java) | ||
serviceIntent.putExtras(intent) | ||
|
||
context.startService(serviceIntent) | ||
} | ||
} |
132 changes: 132 additions & 0 deletions
132
android/src/main/kotlin/com/gdelataillade/alarm/alarm/AlarmService.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
package com.gdelataillade.alarm.alarm | ||
|
||
import android.app.NotificationChannel | ||
import android.app.NotificationManager | ||
import android.app.Service | ||
import android.app.PendingIntent | ||
import android.content.Context | ||
import android.content.Intent | ||
import android.media.MediaPlayer | ||
import android.media.AudioManager | ||
import android.media.AudioManager.FLAG_SHOW_UI | ||
import android.os.Build | ||
import android.os.IBinder | ||
import android.os.PowerManager | ||
import android.os.VibrationEffect | ||
import android.os.Vibrator | ||
import androidx.core.app.NotificationCompat | ||
import io.flutter.Log | ||
import kotlin.math.round | ||
|
||
class AlarmService : Service() { | ||
private var mediaPlayer: MediaPlayer? = null | ||
private val CHANNEL_ID = "AlarmServiceChannel" | ||
|
||
override fun onCreate() { | ||
super.onCreate() | ||
createNotificationChannel() | ||
} | ||
|
||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { | ||
Log.d("AlarmService", "onStartCommand") | ||
|
||
val id = intent?.getIntExtra("id", 0) | ||
val assetAudioPath = intent?.getStringExtra("assetAudioPath") | ||
val loopAudio = intent?.getBooleanExtra("loopAudio", true) | ||
val vibrate = intent?.getBooleanExtra("vibrate", true) | ||
val volume = intent?.getDoubleExtra("volume", -1.0) | ||
val fadeDuration = intent?.getDoubleExtra("fadeDuration", 0.0) | ||
val notificationTitle = intent?.getStringExtra("notificationTitle") | ||
val notificationBody = intent?.getStringExtra("notificationBody") | ||
val showSystemUI = true | ||
|
||
Log.d("AlarmService", "id: $id") | ||
Log.d("AlarmService", "assetAudioPath: $assetAudioPath") | ||
Log.d("AlarmService", "loopAudio: $loopAudio") | ||
Log.d("AlarmService", "vibrate: $vibrate") | ||
Log.d("AlarmService", "volume: $volume") | ||
Log.d("AlarmService", "fadeDuration: $fadeDuration") | ||
Log.d("AlarmService", "notificationTitle: $notificationTitle") | ||
Log.d("AlarmService", "notificationBody: $notificationBody") | ||
|
||
if (notificationTitle != null && notificationBody != null) { | ||
val iconResId = applicationContext.resources.getIdentifier("ic_launcher", "mipmap", applicationContext.packageName) | ||
val intent = applicationContext.packageManager.getLaunchIntentForPackage(applicationContext.packageName) | ||
val pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT) | ||
|
||
val notification = NotificationCompat.Builder(this, CHANNEL_ID) | ||
.setContentTitle(notificationTitle) | ||
.setContentText(notificationBody) | ||
.setSmallIcon(iconResId) | ||
.setContentIntent(pendingIntent) | ||
.setAutoCancel(true) // Automatically remove the notification when tapped | ||
.build() | ||
|
||
startForeground(id!!, notification) | ||
} | ||
|
||
if (volume != -1.0) { | ||
val audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager | ||
/// | ||
var maxVolume:Int = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC) | ||
var _volume:Int = (round(volume!! * maxVolume)).toInt() | ||
|
||
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, _volume, if (showSystemUI) FLAG_SHOW_UI else 0) | ||
} | ||
|
||
try { | ||
val assetManager = applicationContext.assets | ||
val descriptor = assetManager.openFd("flutter_assets/" + assetAudioPath!!) | ||
|
||
val mediaPlayer = MediaPlayer().apply { | ||
setDataSource(descriptor.fileDescriptor, descriptor.startOffset, descriptor.length) | ||
prepare() | ||
isLooping = loopAudio!! | ||
} | ||
mediaPlayer.start() | ||
|
||
} catch (e: Exception) { | ||
// Handle exceptions related to asset loading or MediaPlayer | ||
e.printStackTrace() | ||
} | ||
|
||
if (vibrate!!) { | ||
// Vibrate the device in a loop: vibrate for 500ms, pause for 500ms | ||
val vibrator = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator | ||
val pattern = longArrayOf(0, 500, 500) // Start immediately, vibrate 500ms, pause 500ms | ||
val repeat = 1 // Repeat from the second element (0-based) of the pattern, which is the pause | ||
val vibrationEffect = VibrationEffect.createWaveform(pattern, repeat) | ||
vibrator.vibrate(vibrationEffect) | ||
} | ||
|
||
// Wake up the device | ||
val wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager) | ||
.newWakeLock(PowerManager.FULL_WAKE_LOCK or PowerManager.ACQUIRE_CAUSES_WAKEUP, "app:AlarmWakelockTag") | ||
wakeLock.acquire(5 * 60 * 1000L /*5 minutes*/) | ||
|
||
return START_STICKY | ||
} | ||
|
||
private fun createNotificationChannel() { | ||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { | ||
val serviceChannel = NotificationChannel( | ||
CHANNEL_ID, | ||
"Alarm Service Channel", | ||
NotificationManager.IMPORTANCE_DEFAULT | ||
) | ||
|
||
val manager = getSystemService(NotificationManager::class.java) | ||
manager.createNotificationChannel(serviceChannel) | ||
} | ||
} | ||
|
||
override fun onDestroy() { | ||
mediaPlayer?.stop() | ||
mediaPlayer?.release() | ||
super.onDestroy() | ||
} | ||
|
||
override fun onBind(intent: Intent?): IBinder? { | ||
return null | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.