package space.neothefox.laytray; import android.accessibilityservice.AccessibilityService; import android.accessibilityservice.AccessibilityServiceInfo; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.content.Context; import android.content.SharedPreferences; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.RectF; import android.graphics.drawable.Icon; import android.os.Build; import android.preference.PreferenceManager; import android.util.Log; import android.view.accessibility.AccessibilityEvent; import static android.graphics.Paint.ANTI_ALIAS_FLAG; @SuppressWarnings("SuspiciousNameCombination") public class IconService extends AccessibilityService implements SharedPreferences.OnSharedPreferenceChangeListener { public static final String TAG = "layicon"; public static final String channelId = "space.neothefox.laytray.IC"; private final AccessibilityServiceInfo serviceInfo = new AccessibilityServiceInfo(); private SharedPreferences layouts; private SharedPreferences options; private String lastToast; private NotificationManager iconManager; private NotificationChannel iconChannel; @Override protected void onServiceConnected() { super.onServiceConnected(); Log.d(TAG, "Icon service started"); serviceInfo.eventTypes = AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED; serviceInfo.packageNames = new String[] {"com.blackberry.keyboard"}; serviceInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_VISUAL; serviceInfo.notificationTimeout = 100; this.setServiceInfo(serviceInfo); layouts = getSharedPreferences("layouts", 0); options = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); lastToast = "EMPT"; options.registerOnSharedPreferenceChangeListener(this); iconManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { try { iconChannel = iconManager.getNotificationChannel(channelId); } catch (NullPointerException e) { Log.d(TAG, "No NotificationChannel found"); } if(iconChannel == null) { iconChannel = new NotificationChannel( channelId, getString(R.string.title_icon_channel), NotificationManager.IMPORTANCE_LOW); iconChannel.setShowBadge(false); iconChannel.enableLights(false); iconChannel.enableVibration(false); iconChannel.setLockscreenVisibility(Notification.VISIBILITY_SECRET); iconManager.createNotificationChannel(iconChannel); } } } private void updateNotification(String toast) { Notification indicator; String textIcon = layouts.getString(toast,"EMPT"); if(textIcon.equals("EMPT")) { SharedPreferences.Editor layoutsEditor = layouts.edit(); layoutsEditor.putString(toast, "??"); layoutsEditor.apply(); textIcon = "??"; } Icon smallIcon = Icon.createWithBitmap(textAsBitmap(textIcon, Integer.parseInt(options.getString("textSize", "48")), options.getBoolean("textFakeBold", true), Integer.parseInt(options.getString("textMode", "0")), Color.WHITE)); if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { indicator = new Notification.Builder(this, channelId) .setSmallIcon(smallIcon) .setContentTitle(toast) .setOngoing(true) .setVisibility(Notification.VISIBILITY_SECRET) .build(); } else { indicator = new Notification.Builder(this) .setSmallIcon(smallIcon) .setContentTitle(toast) .setOngoing(true) .setPriority(Integer.parseInt(options.getString("notificationImportance", "0"))) .setVisibility(Notification.VISIBILITY_SECRET) .build(); } iconManager.notify(0, indicator); return; } //Borrowed from Ted Hopp from StackOverflow public static Bitmap textAsBitmap(String text, float textSize, boolean fakeBold, int mode, int textColor) { Paint paint = new Paint(ANTI_ALIAS_FLAG); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT)); paint.setTextSize(textSize); paint.setFakeBoldText(fakeBold); paint.setColor(textColor); switch (mode) { case 1: { int width = 48; int height = 48; Bitmap image = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(image); canvas.drawCircle(width / 2f, height / 2f, width / 2f, paint); paint.setAlpha(255); paint.setColor(Color.TRANSPARENT); paint.setTextSize(textSize); paint.setTextAlign(Paint.Align.CENTER); canvas.drawText(text, width/2f, ((height/2f) - (paint.descent()+paint.ascent())/2f), paint); return image; } case 2: { int width = 48; int height = 48; Bitmap image = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(image); canvas.drawRoundRect(new RectF(0, 0, height, width), 5, 5, paint); paint.setAlpha(255); paint.setColor(Color.TRANSPARENT); paint.setTextSize(textSize); paint.setTextAlign(Paint.Align.CENTER); canvas.drawText(text, width / 2f, ((height/2f) - (paint.descent()+paint.ascent())/2f), paint); return image; } case 3: { int width = 48; int height = 48; Bitmap image = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(image); canvas.drawRoundRect(new RectF(0, 0, height, width), 5, 5, paint); paint.setAlpha(255); paint.setColor(Color.TRANSPARENT); canvas.drawRoundRect(new RectF(2,2,height-2,width-2), 5, 5, paint); paint.setColor(textColor); paint.setTextSize(textSize); paint.setTextAlign(Paint.Align.CENTER); canvas.drawText(text, width / 2f, ((height/2f) - (paint.descent()+paint.ascent())/2f), paint); return image; } case 0: default: paint.setTextAlign(Paint.Align.LEFT); float baseline = -paint.ascent(); // ascent() is negative int width = (int) (paint.measureText(text) + 0.5f); // round int height = (int) (baseline + paint.descent() + 0.5f); Bitmap image = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(image); canvas.drawText(text, 0, baseline, paint); return image; } } @Override public void onAccessibilityEvent(AccessibilityEvent event) { Log.d(TAG, event.toString()); if(event.getEventType() == AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) { Log.d(TAG, "Caught a Toast: "); Log.d(TAG, (String)event.getPackageName()); lastToast = (String)event.getText().get(0); Log.d(TAG, lastToast); updateNotification(lastToast); } else Log.d(TAG, "Caution! This service had been tampered with!"); } @Override public void onInterrupt() { } @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { options = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); updateNotification(lastToast); } }