Even when i set a small icon in my Alarm Manager extension with an image from Android Asset Studio it still shows a circle, white in light mode, black in night mode. Normally you would put the hdpi or mdpi file in the res/drawable folder of the project, but in Mit App Inventor this isn't possible. Here's the code:
package com.damiano.alarm;
import android.app.AlarmManager;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Build;
import android.widget.Toast;
import androidx.core.app.NotificationCompat;
import com.google.appinventor.components.annotations.SimpleEvent;
import com.google.appinventor.components.annotations.SimpleFunction;
import com.google.appinventor.components.runtime.AndroidNonvisibleComponent;
import com.google.appinventor.components.runtime.ComponentContainer;
import com.google.appinventor.components.runtime.EventDispatcher;
import com.google.appinventor.components.runtime.util.MediaUtil;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
public class AlarmManagerExtension extends AndroidNonvisibleComponent {
private static AlarmManagerExtension instance;
private final Context context;
public AlarmManagerExtension(ComponentContainer container) {
super(container.$form());
this.context = container.$context();
instance = this;
}
public static AlarmManagerExtension getInstance() {
return instance;
}
@SimpleFunction(description = "Set an exact alarm at a specified date and time with a custom notification. Date format should be 'yyyy-MM-dd HH:mm'.")
public void SetAlarm(String dateTime, String notificationTitle, String notificationText, String notificationIcon) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault());
try {
Date date = format.parse(dateTime);
if (date != null) {
long triggerAtMillis = date.getTime();
int id = (int) (System.currentTimeMillis() % Integer.MAX_VALUE);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AlarmReceiver.class);
intent.putExtra("NOTIFICATION_TITLE", notificationTitle);
intent.putExtra("NOTIFICATION_TEXT", notificationText);
intent.putExtra("NOTIFICATION_ICON", notificationIcon);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, id, intent, PendingIntent.FLAG_IMMUTABLE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
} else {
alarmManager.set(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
}
showToast("Alarm set for: " + dateTime + " with ID: " + id);
}
} catch (ParseException e) {
showToast("Failed to parse dateTime: " + dateTime);
}
}
@SimpleFunction(description = "Set a repeating alarm every day at the specified time with a custom notification. Time format should be 'HH:mm'.")
public void SetRepeatingAlarm(String time, String notificationTitle, String notificationText, String notificationIcon) {
SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm", Locale.getDefault());
try {
Date date = timeFormat.parse(time);
if (date != null) {
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, date.getHours());
calendar.set(Calendar.MINUTE, date.getMinutes());
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
long triggerAtMillis = calendar.getTimeInMillis();
if (System.currentTimeMillis() > triggerAtMillis) {
triggerAtMillis += AlarmManager.INTERVAL_DAY;
}
int id = (int) (System.currentTimeMillis() % Integer.MAX_VALUE);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AlarmReceiver.class);
intent.putExtra("NOTIFICATION_TITLE", notificationTitle);
intent.putExtra("NOTIFICATION_TEXT", notificationText);
intent.putExtra("NOTIFICATION_ICON", notificationIcon);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, id, intent, PendingIntent.FLAG_IMMUTABLE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
} else {
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, triggerAtMillis, AlarmManager.INTERVAL_DAY, pendingIntent);
}
showToast("Repeating alarm set for: " + time + " every day with ID: " + id);
}
} catch (ParseException e) {
showToast("Failed to parse time: " + time);
}
}
@SimpleFunction(description = "Cancel an alarm with the specified ID.")
public void CancelAlarm(int id) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, id, intent, PendingIntent.FLAG_IMMUTABLE);
alarmManager.cancel(pendingIntent);
showToast("Alarm cancelled with ID: " + id);
}
@SimpleFunction(description = "Request permission to post notifications on Android 13 and above.")
public void AskNotificationsPermission() {
if (Build.VERSION.SDK_INT >= 33) {
form.requestPermissions(new String[]{"android.permission.POST_NOTIFICATIONS"}, 7);
}
}
@SimpleFunction(description = "Request permission to schedule exact alarms on Android 12 and above.")
public void AskScheduleExactAlarmPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
form.requestPermissions(new String[]{"android.permission.SCHEDULE_EXACT_ALARM"}, 8);
}
}
@SimpleEvent(description = "Triggered when the alarm goes off.")
public void OnAlarm() {
showToast("OnAlarm event triggered");
EventDispatcher.dispatchEvent(this, "OnAlarm");
}
private void showToast(String message) {
Toast.makeText(context, message, Toast.LENGTH_LONG).show();
}
private Bitmap getBitmapFromAsset(String assetName) {
try {
return MediaUtil.getBitmapDrawable(form, assetName).getBitmap();
} catch (IOException e) {
showToast("Failed to load image: " + assetName);
return null;
}
}
private void sendNotification(String title, String text, String iconName) {
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
String channelId = "alarm_channel_id";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(channelId, "Alarm Channel", NotificationManager.IMPORTANCE_HIGH);
notificationManager.createNotificationChannel(channel);
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, channelId)
.setContentTitle(title)
.setContentText(text)
.setSmallIcon(android.R.drawable.ic_dialog_info); // Fallback small icon
if (iconName != null && !iconName.isEmpty()) {
Bitmap iconBitmap = getBitmapFromAsset(iconName);
if (iconBitmap != null) {
builder.setLargeIcon(iconBitmap);
}
}
notificationManager.notify((int) System.currentTimeMillis(), builder.build());
}
}