Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ flutter {
}

dependencies {
implementation("androidx.work:work-runtime-ktx:2.9.0")
implementation 'com.google.android.gms:play-services-wearable:18.1.0'
implementation 'com.google.code.gson:gson:2.10.1'
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.7.10"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.10"
Expand Down
9 changes: 8 additions & 1 deletion android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,14 @@
android:enabled="true"
android:foregroundServiceType="systemExempted"
android:exported="true"></service>

<service
android:name=".communication.UACDataLayerListenerService"
android:exported="true">
<intent-filter>
<action android:name="com.google.android.gms.wearable.DATA_CHANGED" />
<data android:scheme="wear" android:host="*" />
</intent-filter>
</service>
<service
android:name=".FirebaseMessagingService"
android:exported="true">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,40 @@ import android.util.Log
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import com.google.android.gms.wearable.PutDataMapRequest
import com.google.android.gms.wearable.Wearable

class AlarmReceiver : BroadcastReceiver() {
companion object {
private var lastTriggeredTime = 0L
private var lastTriggeredType = ""
private const val DUPLICATE_PREVENTION_WINDOW = 10000 // 10 seconds
}

private fun sendVerdictToWatch(context: Context, alarmId: String, willRing: Boolean, reason: String) {
Log.d("ActivityCheck", "Attempting to send verdict to watch for alarm: $alarmId")
val path = "/uac/pre_check_verdict"

val putDataMapRequest = PutDataMapRequest.create(path)
putDataMapRequest.dataMap.apply {
putString("alarmID", alarmId)
putBoolean("willRing", willRing)
putString("reason", reason)
putLong("timestamp", System.currentTimeMillis())
}

val putDataRequest = putDataMapRequest.asPutDataRequest().setUrgent()

val dataClient = Wearable.getDataClient(context)
dataClient.putDataItem(putDataRequest).apply {
addOnSuccessListener {
Log.d("ActivityCheck", "Successfully sent verdict to watch: ${it.uri}")
}
addOnFailureListener {
Log.e("ActivityCheck", "Failed to send verdict to watch", it)
}
}
}

override fun onReceive(context: Context?, intent: Intent?) {
if (context == null || intent == null) {
Expand Down Expand Up @@ -197,7 +224,10 @@ class AlarmReceiver : BroadcastReceiver() {
}

Log.d("AlarmReceiver", "Decision: shouldRing = $shouldRing")

val alarmId = intent.getStringExtra("alarmID")
if (alarmId != null) {
sendVerdictToWatch(context, alarmId, shouldRing, logMessage)
}
if (shouldRing) {
println("ANDROID STARTING APP")
context.startActivity(flutterIntent)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ import com.ccextractor.ultimate_alarm_clock.LogDatabaseHelper
import java.text.SimpleDateFormat
import java.util.*
import java.util.Locale
import androidx.work.Data
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import com.ccextractor.ultimate_alarm_clock.LocationCheckWorker
import com.ccextractor.ultimate_alarm_clock.WeatherCheckWorker
import java.util.concurrent.TimeUnit

object AlarmUtils {
@SuppressLint("ScheduleExactAlarm")
Expand Down Expand Up @@ -136,6 +142,84 @@ object AlarmUtils {
alarmID = alarmID
)
}

//* This checks the code for the smart-control features to send beforehand to watch
//! locaiton checker
if (isLocation == 1) {
val workManager = WorkManager.getInstance(context)

val precheckMinutes = 1L
val triggerAtMillis = System.currentTimeMillis() + intervalToAlarm
val precheckMillis = TimeUnit.MINUTES.toMillis(precheckMinutes)

val delay = triggerAtMillis - System.currentTimeMillis() - precheckMillis

var actualDelay = 0L
if (delay > 0) {
actualDelay = delay
Log.d("AlarmUtils", "Scheduling pre-check with a delay of ${actualDelay}ms.")
} else {
actualDelay = 0L
Log.d("AlarmUtils", "Pre-check window has passed. Scheduling worker to run immediately.")
}

if (intervalToAlarm > 0) {
val requestCode = if (isShared) MainActivity.REQUEST_CODE_SHARED_ALARM else MainActivity.REQUEST_CODE_LOCAL_ALARM

val data = Data.Builder()
.putString("ALARM_ID", alarmID)
.putString("LOCATION", location)
.putInt("LOCATION_CONDITION_TYPE", locationConditionType)
.putBoolean("IS_SHARED_ALARM", isShared)
.putInt("ALARM_REQUEST_CODE", requestCode)
.build()

val locationCheckRequest = OneTimeWorkRequest.Builder(LocationCheckWorker::class.java)
.setInitialDelay(actualDelay, TimeUnit.MILLISECONDS)
.setInputData(data)
.addTag(alarmID)
.build()

workManager.enqueue(locationCheckRequest)
Log.d("AlarmUtils", "✅ WORKER ENQUEUED for alarm ID: $alarmID")
}
}
//! weather checker
if (isWeather == 1) {
val workManager = WorkManager.getInstance(context)

val precheckMinutes = 1L
val triggerAtMillis = System.currentTimeMillis() + intervalToAlarm
val precheckMillis = TimeUnit.MINUTES.toMillis(precheckMinutes)
val delay = triggerAtMillis - System.currentTimeMillis() - precheckMillis

var actualDelay = 0L
if (delay > 0) {
actualDelay = delay
Log.d("AlarmUtils", "Scheduling pre-check with a delay of ${actualDelay}ms.")
} else {
actualDelay = 0L
Log.d("AlarmUtils", "Pre-check window has passed. Scheduling worker to run immediately.")
}

if (intervalToAlarm > 0) {
val requestCode = if (isShared) MainActivity.REQUEST_CODE_SHARED_ALARM else MainActivity.REQUEST_CODE_LOCAL_ALARM

val data = Data.Builder()
.putString("ALARM_ID", alarmID)
.putInt("ALARM_REQUEST_CODE", requestCode)
.putString("WEATHER_TYPES", weatherTypes)
.putInt("WEATHER_CONDITION_TYPE", weatherConditionType)
.build()
val weatherCheckRequest = OneTimeWorkRequest.Builder(WeatherCheckWorker::class.java)
.setInitialDelay(actualDelay, TimeUnit.MILLISECONDS)
.setInputData(data)
.addTag(alarmID)
.build()
workManager.enqueue(weatherCheckRequest)
Log.d("AlarmUtils", "✅ WeatherCheckWorker ENQUEUED for alarm ID: $alarmID")
}
}
}

fun cancelAlarmById(context: Context, alarmID: String, isShared: Boolean) {
Expand Down Expand Up @@ -229,6 +313,7 @@ object AlarmUtils {
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
)

// val activityCheckTime = 1000.toLong()
val activityCheckTime = triggerAtMillis - (15 * 60 * 1000)

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,13 @@ data class AlarmModel(
val location: String,
val alarmDate: String,
val alarmId: String,
val ringOn: Int
val ringOn: Int,
val isEnabled: Int,
val isGuardian: Int,
val guardian: Int,
val guardianTimer: Int,
val isCall: Int,

) {
companion object {
@SuppressLint("Range")
Expand All @@ -451,6 +457,12 @@ data class AlarmModel(
val activityMonitor = cursor.getInt(cursor.getColumnIndex("activityMonitor"))
val isWeatherEnabled = cursor.getInt(cursor.getColumnIndex("isWeatherEnabled"))
val weatherTypes = cursor.getString(cursor.getColumnIndex("weatherTypes"))
val isEnabled = cursor.getInt(cursor.getColumnIndex("isEnabled"))
val isGuardian = cursor.getInt(cursor.getColumnIndex("isGuardian"))
val guardian = cursor.getInt(cursor.getColumnIndex("guardian"))
val guardianTimer = cursor.getInt(cursor.getColumnIndex("guardianTimer"))
val isCall = cursor.getInt(cursor.getColumnIndex("isCall"))



val weatherConditionTypeIndex = cursor.getColumnIndex("weatherConditionType")
Expand Down Expand Up @@ -504,7 +516,12 @@ data class AlarmModel(
location,
alarmDate,
alarmId,
ringOn
ringOn,
isEnabled,
isGuardian,
guardian,
guardianTimer,
isCall,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,21 @@ import java.util.Locale
import android.app.NotificationChannel
import android.graphics.Color
import androidx.core.app.NotificationCompat
import com.google.gson.Gson
import com.ccextractor.ultimate_alarm_clock.communication.UACDataLayerListenerService
import com.google.android.gms.wearable.Wearable
import com.google.android.gms.wearable.DataClient
import com.google.android.gms.wearable.DataEventBuffer
import com.google.android.gms.wearable.DataEvent
import com.ccextractor.ultimate_alarm_clock.communication.PhoneSender


class MainActivity : FlutterActivity() {
companion object {
const val CHANNEL1 = "ulticlock"
const val CHANNEL2 = "timer"
const val CHANNEL3 = "system_ringtones"
const val CHANNEL4 = "watch_action_channel"
const val ACTION_START_FLUTTER_APP = "com.ccextractor.ultimate_alarm_clock"
const val EXTRA_KEY = "alarmRing"
const val ALARM_TYPE = "isAlarm"
Expand Down Expand Up @@ -94,10 +102,14 @@ class MainActivity : FlutterActivity() {

override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)

UACDataLayerListenerService.flutterEngine = flutterEngine

window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON or WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON)
var methodChannel1 = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL1)
var methodChannel2 = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL2)
var methodChannel3 = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL3)
val methodChannel4 = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL4)

val intent = intent

Expand Down Expand Up @@ -141,6 +153,34 @@ class MainActivity : FlutterActivity() {
alarmConfig["isSharedAlarm"] = false
}

methodChannel4.setMethodCallHandler { call, result ->
when (call.method) {
"sendActionToWatch" -> {
val action = call.argument<String>("action")
val id = call.argument<String>("id") ?: ""
if (action != null) {
PhoneSender.sendActionToWatch(this, action, id)
result.success("Action '$action' sent to watch.")
} else {
result.error("INVALID_ARGUMENTS", "Missing 'action' or 'id'", null)
}
}
"sendAlarmToWatch" -> {
val alarmMap = call.arguments as? Map<String, Any>
val isarId = call.argument<Int>("isarId")
if (alarmMap != null && isarId != null) {
val alarmMapMutable = alarmMap.toMutableMap()
alarmMapMutable["isarid"] = isarId
PhoneSender.sendAlarmToWatch(context, alarmMapMutable)
result.success("Alarm sent to watch.")
} else {
result.error("INVALID_ARGUMENT", "Alarm data or alarmId missing.", null)
}
}
else -> result.notImplemented()
}
}

methodChannel3.setMethodCallHandler { call, result ->
when (call.method) {
"getSystemRingtones" -> {
Expand Down
Loading