Skip to content

Add system ringtones feature to alarm clock #795

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
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
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,105 @@ class MainActivity : FlutterActivity() {
context.registerReceiver(TimerNotification(), intentFilter, Context.RECEIVER_EXPORTED)
}

private fun getSystemRingtones(): List<Map<String, String>> {
val ringtoneList = mutableListOf<Map<String, String>>()

try {
// Get alarm sounds
var manager = RingtoneManager(this)
manager.setType(RingtoneManager.TYPE_ALARM)

var cursor = manager.cursor
Log.d("Ringtones", "Found ${cursor.count} alarm sounds")

while (cursor.moveToNext()) {
try {
val title = cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX)
val uri = manager.getRingtoneUri(cursor.position).toString()

ringtoneList.add(mapOf(
"title" to "⏰ $title",
"uri" to uri
))
} catch (e: Exception) {
Log.e("Ringtones", "Error reading alarm sound", e)
}
}

// Get notification sounds
manager = RingtoneManager(this)
manager.setType(RingtoneManager.TYPE_NOTIFICATION)

cursor = manager.cursor
Log.d("Ringtones", "Found ${cursor.count} notification sounds")

while (cursor.moveToNext()) {
try {
val title = cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX)
val uri = manager.getRingtoneUri(cursor.position).toString()

ringtoneList.add(mapOf(
"title" to "🔔 $title",
"uri" to uri
))
} catch (e: Exception) {
Log.e("Ringtones", "Error reading notification sound", e)
}
}

// Get ringtones
manager = RingtoneManager(this)
manager.setType(RingtoneManager.TYPE_RINGTONE)

cursor = manager.cursor
Log.d("Ringtones", "Found ${cursor.count} ringtones")

while (cursor.moveToNext()) {
try {
val title = cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX)
val uri = manager.getRingtoneUri(cursor.position).toString()

ringtoneList.add(mapOf(
"title" to "📱 $title",
"uri" to uri
))
} catch (e: Exception) {
Log.e("Ringtones", "Error reading ringtone", e)
}
}

// Also add default alarm sounds
try {
val defaultUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM)
val defaultRingtone = RingtoneManager.getRingtone(this, defaultUri)
val title = defaultRingtone.getTitle(this)
ringtoneList.add(mapOf(
"title" to "⏰ Default: $title",
"uri" to defaultUri.toString()
))
} catch (e: Exception) {
Log.e("Ringtones", "Error getting default alarm", e)
}

Log.d("Ringtones", "Total ringtones found: ${ringtoneList.size}")
} catch (e: Exception) {
Log.e("Ringtones", "Error retrieving system ringtones", e)
}

return ringtoneList
}

private fun playSystemRingtone(uriString: String) {
stopSystemRingtone()
val uri = Uri.parse(uriString)
ringtone = RingtoneManager.getRingtone(applicationContext, uri)
ringtone?.play()
}

private fun stopSystemRingtone() {
ringtone?.stop()
ringtone = null
}

override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
Expand Down Expand Up @@ -135,6 +234,20 @@ class MainActivity : FlutterActivity() {
} else if (call.method == "stopDefaultAlarm") {
stopDefaultAlarm()
result.success(null)
} else if (call.method == "getSystemRingtones") {
val ringtones = getSystemRingtones()
result.success(ringtones)
} else if (call.method == "playSystemRingtone") {
val uri = call.argument<String>("uri")
if (uri != null) {
playSystemRingtone(uri)
result.success(null)
} else {
result.error("INVALID_ARGUMENT", "URI cannot be null", null)
}
} else if (call.method == "stopSystemRingtone") {
stopSystemRingtone()
result.success(null)
} else {
result.notImplemented()
}
Expand Down Expand Up @@ -293,5 +406,4 @@ class MainActivity : FlutterActivity() {
intent.data = Uri.parse("package:${packageName}")
startActivity(intent)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ class AddOrUpdateAlarmController extends GetxController {
final RxInt hours = 0.obs, minutes = 0.obs, meridiemIndex = 0.obs;
final List<RxString> meridiem = ['AM'.obs, 'PM'.obs];

RxList<Map<String, dynamic>> systemRingtones = <Map<String, dynamic>>[].obs;
RxBool isLoadingSystemRingtones = false.obs;

Future<List<UserModel?>> fetchUserDetailsForSharedUsers() async {
List<UserModel?> userDetails = [];

Expand Down Expand Up @@ -290,7 +293,7 @@ class AddOrUpdateAlarmController extends GetxController {
Get.back();
},
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(kprimaryColor),
backgroundColor: WidgetStateProperty.all(kprimaryColor),
),
child: Text(
'Cancel'.tr,
Expand All @@ -305,7 +308,7 @@ class AddOrUpdateAlarmController extends GetxController {
Get.back();
},
style: OutlinedButton.styleFrom(
side: BorderSide(
side: const BorderSide(
color: Colors.red,
width: 1,
),
Expand Down Expand Up @@ -465,7 +468,7 @@ class AddOrUpdateAlarmController extends GetxController {
TextButton(
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(kprimaryColor),
WidgetStateProperty.all(kprimaryColor),
),
child: Text(
'Save',
Expand All @@ -485,7 +488,7 @@ class AddOrUpdateAlarmController extends GetxController {
TextButton(
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(kprimaryColor),
WidgetStateProperty.all(kprimaryColor),
),
child: Text(
'Retake',
Expand All @@ -505,7 +508,7 @@ class AddOrUpdateAlarmController extends GetxController {
TextButton(
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(kprimaryColor),
WidgetStateProperty.all(kprimaryColor),
),
child: Text(
'Disable',
Expand Down Expand Up @@ -572,7 +575,7 @@ class AddOrUpdateAlarmController extends GetxController {
},
confirm: TextButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(kprimaryColor),
backgroundColor: WidgetStateProperty.all(kprimaryColor),
),
child: Obx(
() => Text(
Expand All @@ -595,7 +598,7 @@ class AddOrUpdateAlarmController extends GetxController {
cancel: Obx(
() => TextButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(
backgroundColor: WidgetStateProperty.all(
themeController.primaryTextColor.value.withOpacity(0.5),
),
),
Expand Down Expand Up @@ -669,7 +672,7 @@ class AddOrUpdateAlarmController extends GetxController {

profileTextEditingController.text = homeController.isProfileUpdate.value
? homeController.selectedProfile.value
: "";
: '';
emailTextEditingController.text = '';

if (Get.arguments != null) {
Expand Down Expand Up @@ -1243,15 +1246,15 @@ class AddOrUpdateAlarmController extends GetxController {
return Theme(
data: themeController.currentTheme.value == ThemeMode.light
? ThemeData.light().copyWith(
colorScheme: const ColorScheme.light(
primary: kprimaryColor,
),
)
colorScheme: const ColorScheme.light(
primary: kprimaryColor,
),
)
: ThemeData.dark().copyWith(
colorScheme: const ColorScheme.dark(
primary: kprimaryColor,
),
),
colorScheme: const ColorScheme.dark(
primary: kprimaryColor,
),
),
child: child!,
);
},
Expand Down Expand Up @@ -1357,12 +1360,59 @@ class AddOrUpdateAlarmController extends GetxController {
storage.writeProfile(profileModel.profileName);
homeController.writeProfileName(profileModel.profileName);
}
}

int orderedCountryCode(Country countryA, Country countryB) {
// `??` for null safety of 'dialCode'
String dialCodeA = countryA.dialCode ?? '0';
String dialCodeB = countryB.dialCode ?? '0';
Future<void> loadSystemRingtones() async {
isLoadingSystemRingtones.value = true;

// No need for storage permission to access system ringtones
systemRingtones.value = await AudioUtils.getSystemRingtones();

return int.parse(dialCodeA).compareTo(int.parse(dialCodeB));
// Debug logging
debugPrint('Loaded ${systemRingtones.length} system ringtones');
if (systemRingtones.isEmpty) {
debugPrint('No system ringtones found');
} else {
debugPrint('First ringtone: ${systemRingtones[0]}');
}

isLoadingSystemRingtones.value = false;
}

Future<void> saveSystemRingtone(String title, String uri) async {
try {
// Create a ringtone model for the system ringtone
RingtoneModel systemRingtone = RingtoneModel(
ringtoneName: title,
ringtonePath: uri,
currentCounterOfUsage: 1,
);

// Update previous ringtone counter
AudioUtils.updateRingtoneCounterOfUsage(
customRingtoneName: previousRingtone,
counterUpdate: CounterUpdate.decrement,
);

// Save the ringtone to the database
await IsarDb.addCustomRingtone(systemRingtone);

// Update the current ringtone name
customRingtoneName.value = title;

// Add to the list if not already present
if (!customRingtoneNames.contains(title)) {
customRingtoneNames.add(title);
}
} catch (e) {
debugPrint(e.toString());
}
}
}

int orderedCountryCode(Country countryA, Country countryB) {
// `??` for null safety of 'dialCode'
String dialCodeA = countryA.dialCode ?? '0';
String dialCodeB = countryB.dialCode ?? '0';

return int.parse(dialCodeA).compareTo(int.parse(dialCodeB));
}
Loading