From 325aa4b2ddfc36a922ed1532e9379b2a241ca87a Mon Sep 17 00:00:00 2001
From: Neamar <neamar@neamar.fr>
Date: Wed, 24 Apr 2019 20:58:55 +0100
Subject: [PATCH 01/30] Starting work on a notification dot system

---
 app/build.gradle                              |  1 +
 app/src/main/AndroidManifest.xml              |  8 +++++
 .../java/fr/neamar/kiss/MainActivity.java     | 30 +++++++++++++++++-
 .../notification/NotificationListener.java    | 31 +++++++++++++++++++
 app/src/main/res/values-v18/bools.xml         |  4 +++
 app/src/main/res/values/bools.xml             |  4 +++
 6 files changed, 77 insertions(+), 1 deletion(-)
 create mode 100644 app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java
 create mode 100644 app/src/main/res/values-v18/bools.xml
 create mode 100644 app/src/main/res/values/bools.xml

diff --git a/app/build.gradle b/app/build.gradle
index 5b319e50c..c9e9e359e 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -25,6 +25,7 @@ dependencies {
     androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
     androidTestImplementation 'androidx.test:rules:1.1.1'
     implementation 'androidx.annotation:annotation:1.0.2'
+    implementation "com.android.support:support-compat:28.0.0"
 }
 
 buildscript {
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 71102eb2f..70fcbdc48 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -149,6 +149,14 @@
         <service android:name=".dataprovider.ContactsProvider" />
         <service android:name=".dataprovider.SettingsProvider" />
         <service android:name=".dataprovider.ShortcutsProvider" />
+
+        <service android:name=".notification.NotificationListener"
+            android:enabled="@bool/notification_service_enabled"
+            android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
+            <intent-filter>
+                <action android:name="android.service.notification.NotificationListenerService" />
+            </intent-filter>
+        </service>
     </application>
 
 </manifest>
diff --git a/app/src/main/java/fr/neamar/kiss/MainActivity.java b/app/src/main/java/fr/neamar/kiss/MainActivity.java
index d598bd97a..4e30f4339 100644
--- a/app/src/main/java/fr/neamar/kiss/MainActivity.java
+++ b/app/src/main/java/fr/neamar/kiss/MainActivity.java
@@ -4,6 +4,8 @@
 import android.animation.AnimatorListenerAdapter;
 import android.annotation.SuppressLint;
 import android.app.Activity;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -32,9 +34,12 @@
 import android.widget.TextView;
 import android.widget.TextView.OnEditorActionListener;
 
+import androidx.annotation.NonNull;
+import androidx.core.app.NotificationCompat;
+import androidx.core.app.NotificationManagerCompat;
+
 import java.util.ArrayList;
 
-import androidx.annotation.NonNull;
 import fr.neamar.kiss.adapter.RecordAdapter;
 import fr.neamar.kiss.broadcast.IncomingCallHandler;
 import fr.neamar.kiss.forwarder.ForwarderManager;
@@ -632,6 +637,29 @@ private void displayKissBar(boolean display, boolean clearSearchText) {
         int finalRadius = Math.max(kissBar.getWidth(), kissBar.getHeight());
 
         if (display) {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+                CharSequence name = "fake";
+                String description = "fake description";
+                int importance = NotificationManager.IMPORTANCE_DEFAULT;
+                NotificationChannel channel = new NotificationChannel("channel", name, importance);
+                channel.setDescription(description);
+                // Register the channel with the system; you can't change the importance
+                // or other notification behaviors after this
+                NotificationManager notificationManager = getSystemService(NotificationManager.class);
+                notificationManager.createNotificationChannel(channel);
+            }
+
+            NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "channel")
+                    .setSmallIcon(R.drawable.call)
+                    .setContentTitle("TITLE")
+                    .setContentText("Content forever")
+                    .setPriority(NotificationCompat.PRIORITY_DEFAULT);
+
+            NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
+
+            // notificationId is a unique int for each notification that you must define
+            notificationManager.notify(1, builder.build());
+
             // Display the app list
             if (searchEditText.getText().length() != 0) {
                 searchEditText.setText("");
diff --git a/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java b/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java
new file mode 100644
index 000000000..548741d5a
--- /dev/null
+++ b/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java
@@ -0,0 +1,31 @@
+package fr.neamar.kiss.notification;
+
+import android.content.Intent;
+import android.os.Build;
+import android.os.IBinder;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+
+import androidx.annotation.RequiresApi;
+
+@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
+public class NotificationListener extends NotificationListenerService {
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return super.onBind(intent);
+    }
+
+    @Override
+    public void onNotificationPosted(StatusBarNotification sbn){
+        // Implement what you want here
+        Log.e("WTF", "Got a notification" + sbn.getPackageName());
+    }
+
+    @Override
+    public void onNotificationRemoved(StatusBarNotification sbn){
+        // Implement what you want here
+        Log.e("WTF", "Dismissed a notification");
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/res/values-v18/bools.xml b/app/src/main/res/values-v18/bools.xml
new file mode 100644
index 000000000..35ea192b7
--- /dev/null
+++ b/app/src/main/res/values-v18/bools.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <bool name="notification_service_enabled">true</bool>
+</resources>
\ No newline at end of file
diff --git a/app/src/main/res/values/bools.xml b/app/src/main/res/values/bools.xml
new file mode 100644
index 000000000..0ad3620a9
--- /dev/null
+++ b/app/src/main/res/values/bools.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <bool name="notification_service_enabled">false</bool>
+</resources>

From 88f5eb4314711a836c8f59ba005748b473f89b16 Mon Sep 17 00:00:00 2001
From: Neamar <neamar@neamar.fr>
Date: Wed, 24 Apr 2019 23:24:28 +0100
Subject: [PATCH 02/30] Store notifications in SharedPreferences

---
 .../notification/NotificationListener.java    | 55 +++++++++++++++++--
 1 file changed, 49 insertions(+), 6 deletions(-)

diff --git a/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java b/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java
index 548741d5a..8c623be1f 100644
--- a/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java
+++ b/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java
@@ -1,6 +1,8 @@
 package fr.neamar.kiss.notification;
 
+import android.content.Context;
 import android.content.Intent;
+import android.content.SharedPreferences;
 import android.os.Build;
 import android.os.IBinder;
 import android.service.notification.NotificationListenerService;
@@ -9,8 +11,20 @@
 
 import androidx.annotation.RequiresApi;
 
+import java.util.HashSet;
+import java.util.Set;
+
 @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
 public class NotificationListener extends NotificationListenerService {
+    public static final String NOTIFICATION_PREFERENCES_NAME = "notifications";
+
+    private SharedPreferences prefs;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        prefs = getBaseContext().getSharedPreferences(NOTIFICATION_PREFERENCES_NAME, Context.MODE_PRIVATE);
+    }
 
     @Override
     public IBinder onBind(Intent intent) {
@@ -18,14 +32,43 @@ public IBinder onBind(Intent intent) {
     }
 
     @Override
-    public void onNotificationPosted(StatusBarNotification sbn){
-        // Implement what you want here
-        Log.e("WTF", "Got a notification" + sbn.getPackageName());
+    public void onNotificationPosted(StatusBarNotification sbn) {
+        Set<String> currentNotifications = getCurrentNotificationsForPackage(sbn.getPackageName());
+
+        currentNotifications.add(Integer.toString(sbn.getId()));
+        prefs.edit().putStringSet(sbn.getPackageName(), currentNotifications).apply();
+
+        Log.e("WTF", "ADD Notifications for " + sbn.getPackageName() + currentNotifications.toString());
     }
 
     @Override
-    public void onNotificationRemoved(StatusBarNotification sbn){
-        // Implement what you want here
-        Log.e("WTF", "Dismissed a notification");
+    public void onNotificationRemoved(StatusBarNotification sbn) {
+        Set<String> currentNotifications = getCurrentNotificationsForPackage(sbn.getPackageName());
+
+        currentNotifications.remove(Integer.toString(sbn.getId()));
+
+        SharedPreferences.Editor editor = prefs.edit();
+        if(currentNotifications.size() == 0) {
+            // Clean up!
+            editor.remove(sbn.getPackageName());
+        }
+        else {
+            editor.putStringSet(sbn.getPackageName(), currentNotifications);
+        }
+        editor.apply();
+
+        Log.e("WTF", "DEL Notifications for " + sbn.getPackageName() + currentNotifications.toString());
+
+    }
+
+    public Set<String> getCurrentNotificationsForPackage(String packageName) {
+        Set<String> currentNotifications = prefs.getStringSet(packageName, null);
+        if (currentNotifications == null) {
+            return new HashSet<>();
+        } else {
+            // The set returned by getStringSet() should NOT be modified
+            // see https://developer.android.com/reference/android/content/SharedPreferences.html#getStringSet(java.lang.String,%2520java.util.Set%3Cjava.lang.String%3E)
+           return new HashSet<>(currentNotifications);
+        }
     }
 }
\ No newline at end of file

From 20c49e43a5d0b661b548950c4cdbf97677c0cdcf Mon Sep 17 00:00:00 2001
From: Neamar <neamar@neamar.fr>
Date: Wed, 24 Apr 2019 23:29:38 +0100
Subject: [PATCH 03/30] Added notification dot on app layout

---
 .../main/res/drawable/notification_dot.xml    | 12 ++++++++
 app/src/main/res/layout/item_app.xml          | 28 +++++++++++++------
 2 files changed, 31 insertions(+), 9 deletions(-)
 create mode 100644 app/src/main/res/drawable/notification_dot.xml

diff --git a/app/src/main/res/drawable/notification_dot.xml b/app/src/main/res/drawable/notification_dot.xml
new file mode 100644
index 000000000..0b2d04f04
--- /dev/null
+++ b/app/src/main/res/drawable/notification_dot.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="oval">
+
+    <solid
+        android:color="#666666"/>
+
+    <size
+        android:width="8dp"
+        android:height="8dp"/>
+</shape>
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_app.xml b/app/src/main/res/layout/item_app.xml
index 7747c6fea..962f20b6e 100644
--- a/app/src/main/res/layout/item_app.xml
+++ b/app/src/main/res/layout/item_app.xml
@@ -5,27 +5,37 @@
     android:layout_height="match_parent"
     android:gravity="center_vertical"
     android:orientation="horizontal"
-    android:paddingBottom="@dimen/result_margin_bottom"
-    android:paddingEnd="@dimen/result_margin_right"
+    android:paddingStart="@dimen/result_margin_left"
     android:paddingLeft="@dimen/result_margin_left"
+    android:paddingTop="@dimen/result_margin_top"
+    android:paddingEnd="@dimen/result_margin_right"
     android:paddingRight="@dimen/result_margin_right"
-    android:paddingStart="@dimen/result_margin_left"
-    android:paddingTop="@dimen/result_margin_top">
+    android:paddingBottom="@dimen/result_margin_bottom">
 
     <ImageView
         android:id="@+id/item_app_icon"
         android:layout_width="48dp"
         android:layout_height="48dp"
         android:layout_centerVertical="true"
-        android:layout_marginBottom="@dimen/icon_margin_bottom"
-        android:layout_marginEnd="@dimen/icon_margin_right"
-        android:layout_marginLeft="@dimen/icon_margin_left"
-        android:layout_marginRight="@dimen/icon_margin_right"
         android:layout_marginStart="@dimen/icon_margin_left"
+        android:layout_marginLeft="@dimen/icon_margin_left"
         android:layout_marginTop="@dimen/icon_margin_top"
+        android:layout_marginEnd="@dimen/icon_margin_right"
+        android:layout_marginRight="@dimen/icon_margin_right"
+        android:layout_marginBottom="@dimen/icon_margin_bottom"
         android:contentDescription="@null"
         tools:src="@drawable/ic_launcher" />
 
+    <ImageView
+        android:id="@+id/item_notification_dot"
+        android:layout_width="14dp"
+        android:layout_height="14dp"
+        android:layout_alignTop="@id/item_app_icon"
+        android:layout_alignEnd="@id/item_app_icon"
+        android:layout_alignRight="@id/item_app_icon"
+        android:contentDescription="@null"
+        android:src="@drawable/notification_dot" />
+
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="match_parent"
@@ -41,9 +51,9 @@
             android:layout_height="wrap_content"
             android:ellipsize="end"
             android:maxLines="1"
-            android:paddingBottom="3dp"
             android:paddingEnd="2dp"
             android:paddingRight="2dp"
+            android:paddingBottom="3dp"
             android:shadowColor="?attr/resultShadowColor"
             android:shadowDx="1"
             android:shadowDy="2"

From 93df986e3237abce7497fe940b46edbf9811bf30 Mon Sep 17 00:00:00 2001
From: Neamar <neamar@neamar.fr>
Date: Wed, 24 Apr 2019 23:33:06 +0100
Subject: [PATCH 04/30] Display dot if app is in list

---
 .../main/java/fr/neamar/kiss/result/AppResult.java  | 13 ++++++++++---
 app/src/main/res/layout/item_app.xml                |  1 +
 2 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/app/src/main/java/fr/neamar/kiss/result/AppResult.java b/app/src/main/java/fr/neamar/kiss/result/AppResult.java
index e3ce4f0ac..f8f63dd4b 100644
--- a/app/src/main/java/fr/neamar/kiss/result/AppResult.java
+++ b/app/src/main/java/fr/neamar/kiss/result/AppResult.java
@@ -31,6 +31,7 @@
 import fr.neamar.kiss.MainActivity;
 import fr.neamar.kiss.R;
 import fr.neamar.kiss.adapter.RecordAdapter;
+import fr.neamar.kiss.notification.NotificationListener;
 import fr.neamar.kiss.pojo.AppPojo;
 import fr.neamar.kiss.ui.ListPopup;
 import fr.neamar.kiss.utils.FuzzyScore;
@@ -81,6 +82,12 @@ public View display(final Context context, int position, View convertView, Fuzzy
         } else {
             appIcon.setImageDrawable(null);
         }
+
+        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+            SharedPreferences notificationPrefs = context.getSharedPreferences(NotificationListener.NOTIFICATION_PREFERENCES_NAME, Context.MODE_PRIVATE);
+            view.findViewById(R.id.item_notification_dot).setVisibility(notificationPrefs.contains(className.getPackageName()) ? View.VISIBLE : View.GONE);
+        }
+        
         return view;
     }
 
@@ -143,8 +150,8 @@ protected boolean popupMenuClickHandler(final Context context, final RecordAdapt
                 final int EXCLUDE_KISS_ID = 1;
                 PopupMenu popupExcludeMenu = new PopupMenu(context, parentView);
                 //Adding menu items
-                popupExcludeMenu.getMenu().add(EXCLUDE_HISTORY_ID,Menu.NONE, Menu.NONE,R.string.menu_exclude_history);
-                popupExcludeMenu.getMenu().add(EXCLUDE_KISS_ID,Menu.NONE, Menu.NONE,R.string.menu_exclude_kiss);
+                popupExcludeMenu.getMenu().add(EXCLUDE_HISTORY_ID, Menu.NONE, Menu.NONE, R.string.menu_exclude_history);
+                popupExcludeMenu.getMenu().add(EXCLUDE_KISS_ID, Menu.NONE, Menu.NONE, R.string.menu_exclude_kiss);
                 //registering popup with OnMenuItemClickListener
                 popupExcludeMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
                     public boolean onMenuItemClick(MenuItem item) {
@@ -179,7 +186,7 @@ private void excludeFromHistory(Context context, AppPojo appPojo, final RecordAd
         //remove from history
         deleteRecord(context);
         //refresh current history
-        if (!((MainActivity)context).isViewingAllApps()) {
+        if (!((MainActivity) context).isViewingAllApps()) {
             parent.removeResult(AppResult.this);
         }
         //inform user
diff --git a/app/src/main/res/layout/item_app.xml b/app/src/main/res/layout/item_app.xml
index 962f20b6e..59b08bd4c 100644
--- a/app/src/main/res/layout/item_app.xml
+++ b/app/src/main/res/layout/item_app.xml
@@ -33,6 +33,7 @@
         android:layout_alignTop="@id/item_app_icon"
         android:layout_alignEnd="@id/item_app_icon"
         android:layout_alignRight="@id/item_app_icon"
+        android:visibility="gone"
         android:contentDescription="@null"
         android:src="@drawable/notification_dot" />
 

From 8cd0d4a73efe2c05ca8892d074c30ce6f637587a Mon Sep 17 00:00:00 2001
From: Neamar <neamar@neamar.fr>
Date: Wed, 24 Apr 2019 23:44:26 +0100
Subject: [PATCH 05/30] Added first version of a listener

---
 .../java/fr/neamar/kiss/MainActivity.java     | 20 +++++++++++++++++++
 .../java/fr/neamar/kiss/result/AppResult.java |  2 +-
 2 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/app/src/main/java/fr/neamar/kiss/MainActivity.java b/app/src/main/java/fr/neamar/kiss/MainActivity.java
index 4e30f4339..b820fc937 100644
--- a/app/src/main/java/fr/neamar/kiss/MainActivity.java
+++ b/app/src/main/java/fr/neamar/kiss/MainActivity.java
@@ -43,6 +43,7 @@
 import fr.neamar.kiss.adapter.RecordAdapter;
 import fr.neamar.kiss.broadcast.IncomingCallHandler;
 import fr.neamar.kiss.forwarder.ForwarderManager;
+import fr.neamar.kiss.notification.NotificationListener;
 import fr.neamar.kiss.result.Result;
 import fr.neamar.kiss.searcher.ApplicationsSearcher;
 import fr.neamar.kiss.searcher.HistorySearcher;
@@ -163,6 +164,13 @@ public class MainActivity extends Activity implements QueryInterface, KeyboardSc
 
     private ForwarderManager forwarderManager;
 
+    private SharedPreferences.OnSharedPreferenceChangeListener onNotificationDisplayed = new SharedPreferences.OnSharedPreferenceChangeListener() {
+        @Override
+        public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+            updateSearchRecords();
+        }
+    };
+
     /**
      * Called when the activity is first created.
      */
@@ -416,9 +424,21 @@ protected void onResume() {
 
         forwarderManager.onResume();
 
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+            getSharedPreferences(NotificationListener.NOTIFICATION_PREFERENCES_NAME, MODE_PRIVATE).registerOnSharedPreferenceChangeListener(onNotificationDisplayed);
+        }
         super.onResume();
     }
 
+    @Override
+    protected void onPause() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+            getSharedPreferences(NotificationListener.NOTIFICATION_PREFERENCES_NAME, MODE_PRIVATE).unregisterOnSharedPreferenceChangeListener(onNotificationDisplayed);
+        }
+        
+        super.onPause();
+    }
+
     @Override
     protected void onDestroy() {
         super.onDestroy();
diff --git a/app/src/main/java/fr/neamar/kiss/result/AppResult.java b/app/src/main/java/fr/neamar/kiss/result/AppResult.java
index f8f63dd4b..aaf9b09c1 100644
--- a/app/src/main/java/fr/neamar/kiss/result/AppResult.java
+++ b/app/src/main/java/fr/neamar/kiss/result/AppResult.java
@@ -87,7 +87,7 @@ public View display(final Context context, int position, View convertView, Fuzzy
             SharedPreferences notificationPrefs = context.getSharedPreferences(NotificationListener.NOTIFICATION_PREFERENCES_NAME, Context.MODE_PRIVATE);
             view.findViewById(R.id.item_notification_dot).setVisibility(notificationPrefs.contains(className.getPackageName()) ? View.VISIBLE : View.GONE);
         }
-        
+
         return view;
     }
 

From 1d98a13ad604b292f1342e5ac32342f0ebc84c3d Mon Sep 17 00:00:00 2001
From: Neamar <neamar@neamar.fr>
Date: Thu, 25 Apr 2019 22:01:37 +0100
Subject: [PATCH 06/30] Move everything to a Forwarder

---
 .../java/fr/neamar/kiss/MainActivity.java     | 26 ++---------
 .../fr/neamar/kiss/adapter/RecordAdapter.java |  3 +-
 .../kiss/forwarder/ForwarderManager.java      |  5 +++
 .../neamar/kiss/forwarder/Notification.java   | 43 +++++++++++++++++++
 4 files changed, 54 insertions(+), 23 deletions(-)
 create mode 100644 app/src/main/java/fr/neamar/kiss/forwarder/Notification.java

diff --git a/app/src/main/java/fr/neamar/kiss/MainActivity.java b/app/src/main/java/fr/neamar/kiss/MainActivity.java
index b820fc937..f56757099 100644
--- a/app/src/main/java/fr/neamar/kiss/MainActivity.java
+++ b/app/src/main/java/fr/neamar/kiss/MainActivity.java
@@ -43,7 +43,6 @@
 import fr.neamar.kiss.adapter.RecordAdapter;
 import fr.neamar.kiss.broadcast.IncomingCallHandler;
 import fr.neamar.kiss.forwarder.ForwarderManager;
-import fr.neamar.kiss.notification.NotificationListener;
 import fr.neamar.kiss.result.Result;
 import fr.neamar.kiss.searcher.ApplicationsSearcher;
 import fr.neamar.kiss.searcher.HistorySearcher;
@@ -164,13 +163,6 @@ public class MainActivity extends Activity implements QueryInterface, KeyboardSc
 
     private ForwarderManager forwarderManager;
 
-    private SharedPreferences.OnSharedPreferenceChangeListener onNotificationDisplayed = new SharedPreferences.OnSharedPreferenceChangeListener() {
-        @Override
-        public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
-            updateSearchRecords();
-        }
-    };
-
     /**
      * Called when the activity is first created.
      */
@@ -281,11 +273,13 @@ public void onChanged() {
                     emptyListView.setVisibility(View.VISIBLE);
                 } else {
                     // Otherwise, display results
-                    listContainer.setVisibility(View.VISIBLE);
+                    if(listContainer.getVisibility() != View.VISIBLE) {
+                        listContainer.setVisibility(View.VISIBLE);
+                    }
                     emptyListView.setVisibility(View.GONE);
                 }
 
-                forwarderManager.onDataSetChanged();
+                // forwarderManager.onDataSetChanged();
 
             }
         });
@@ -424,21 +418,9 @@ protected void onResume() {
 
         forwarderManager.onResume();
 
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
-            getSharedPreferences(NotificationListener.NOTIFICATION_PREFERENCES_NAME, MODE_PRIVATE).registerOnSharedPreferenceChangeListener(onNotificationDisplayed);
-        }
         super.onResume();
     }
 
-    @Override
-    protected void onPause() {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
-            getSharedPreferences(NotificationListener.NOTIFICATION_PREFERENCES_NAME, MODE_PRIVATE).unregisterOnSharedPreferenceChangeListener(onNotificationDisplayed);
-        }
-        
-        super.onPause();
-    }
-
     @Override
     protected void onDestroy() {
         super.onDestroy();
diff --git a/app/src/main/java/fr/neamar/kiss/adapter/RecordAdapter.java b/app/src/main/java/fr/neamar/kiss/adapter/RecordAdapter.java
index c8236c9e9..56bbb3b12 100644
--- a/app/src/main/java/fr/neamar/kiss/adapter/RecordAdapter.java
+++ b/app/src/main/java/fr/neamar/kiss/adapter/RecordAdapter.java
@@ -7,13 +7,14 @@
 import android.widget.BaseAdapter;
 import android.widget.SectionIndexer;
 
+import androidx.annotation.NonNull;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Set;
 
-import androidx.annotation.NonNull;
 import fr.neamar.kiss.KissApplication;
 import fr.neamar.kiss.normalizer.StringNormalizer;
 import fr.neamar.kiss.result.AppResult;
diff --git a/app/src/main/java/fr/neamar/kiss/forwarder/ForwarderManager.java b/app/src/main/java/fr/neamar/kiss/forwarder/ForwarderManager.java
index 26644de21..e4c1457c1 100644
--- a/app/src/main/java/fr/neamar/kiss/forwarder/ForwarderManager.java
+++ b/app/src/main/java/fr/neamar/kiss/forwarder/ForwarderManager.java
@@ -18,6 +18,7 @@ public class ForwarderManager extends Forwarder {
     private final Permission permissionForwarder;
     private final OreoShortcuts shortcutsForwarder;
     private final TagsMenu tagsMenu;
+    private final Notification notificationForwarder;
 
 
     public ForwarderManager(MainActivity mainActivity) {
@@ -30,6 +31,7 @@ public ForwarderManager(MainActivity mainActivity) {
         this.favoritesForwarder = new Favorites(mainActivity);
         this.permissionForwarder = new Permission(mainActivity);
         this.shortcutsForwarder = new OreoShortcuts(mainActivity);
+        this.notificationForwarder = new Notification(mainActivity);
         this.tagsMenu = new TagsMenu(mainActivity);
     }
 
@@ -39,7 +41,9 @@ public void onCreate() {
         interfaceTweaks.onCreate();
         experienceTweaks.onCreate();
         shortcutsForwarder.onCreate();
+        notificationForwarder.onCreate();
         tagsMenu.onCreate();
+
     }
 
     public void onResume() {
@@ -54,6 +58,7 @@ public void onStart() {
 
     public void onStop() {
         widgetForwarder.onStop();
+        notificationForwarder.onStop();
     }
 
     public void onActivityResult(int requestCode, int resultCode, Intent data) {
diff --git a/app/src/main/java/fr/neamar/kiss/forwarder/Notification.java b/app/src/main/java/fr/neamar/kiss/forwarder/Notification.java
new file mode 100644
index 000000000..ffa8ad10c
--- /dev/null
+++ b/app/src/main/java/fr/neamar/kiss/forwarder/Notification.java
@@ -0,0 +1,43 @@
+package fr.neamar.kiss.forwarder;
+
+import android.content.SharedPreferences;
+import android.os.Build;
+import android.util.Log;
+
+import fr.neamar.kiss.MainActivity;
+import fr.neamar.kiss.notification.NotificationListener;
+
+import static android.content.Context.MODE_PRIVATE;
+
+class Notification extends Forwarder {
+    private final SharedPreferences notificationPreferences;
+
+    private SharedPreferences.OnSharedPreferenceChangeListener onNotificationDisplayed = new SharedPreferences.OnSharedPreferenceChangeListener() {
+        @Override
+        public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+            Log.e("WTF", "Invalidated dataset: " + key);
+            mainActivity.adapter.notifyDataSetChanged();
+        }
+    };
+
+    Notification(MainActivity mainActivity) {
+        super(mainActivity);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+            notificationPreferences = mainActivity.getSharedPreferences(NotificationListener.NOTIFICATION_PREFERENCES_NAME, MODE_PRIVATE);
+        } else {
+            notificationPreferences = null;
+        }
+    }
+
+    void onCreate() {
+        if (notificationPreferences != null) {
+            notificationPreferences.registerOnSharedPreferenceChangeListener(onNotificationDisplayed);
+        }
+    }
+
+    void onStop() {
+        if (notificationPreferences != null) {
+            notificationPreferences.unregisterOnSharedPreferenceChangeListener(onNotificationDisplayed);
+        }
+    }
+}

From 2d24be4dbe886db6c3cf23ada4131ee020fdaa18 Mon Sep 17 00:00:00 2001
From: Neamar <neamar@neamar.fr>
Date: Fri, 26 Apr 2019 08:58:29 +0100
Subject: [PATCH 07/30] Working implementation in search and history

---
 .../java/fr/neamar/kiss/MainActivity.java     |  2 +-
 .../neamar/kiss/forwarder/Notification.java   | 45 +++++++++++++++++--
 .../java/fr/neamar/kiss/result/AppResult.java |  4 +-
 3 files changed, 46 insertions(+), 5 deletions(-)

diff --git a/app/src/main/java/fr/neamar/kiss/MainActivity.java b/app/src/main/java/fr/neamar/kiss/MainActivity.java
index f56757099..846f7b3f1 100644
--- a/app/src/main/java/fr/neamar/kiss/MainActivity.java
+++ b/app/src/main/java/fr/neamar/kiss/MainActivity.java
@@ -273,7 +273,7 @@ public void onChanged() {
                     emptyListView.setVisibility(View.VISIBLE);
                 } else {
                     // Otherwise, display results
-                    if(listContainer.getVisibility() != View.VISIBLE) {
+                    if (listContainer.getVisibility() != View.VISIBLE) {
                         listContainer.setVisibility(View.VISIBLE);
                     }
                     emptyListView.setVisibility(View.GONE);
diff --git a/app/src/main/java/fr/neamar/kiss/forwarder/Notification.java b/app/src/main/java/fr/neamar/kiss/forwarder/Notification.java
index ffa8ad10c..9da5a80a8 100644
--- a/app/src/main/java/fr/neamar/kiss/forwarder/Notification.java
+++ b/app/src/main/java/fr/neamar/kiss/forwarder/Notification.java
@@ -1,10 +1,15 @@
 package fr.neamar.kiss.forwarder;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.content.SharedPreferences;
 import android.os.Build;
 import android.util.Log;
+import android.view.View;
+import android.widget.ListView;
 
 import fr.neamar.kiss.MainActivity;
+import fr.neamar.kiss.R;
 import fr.neamar.kiss.notification.NotificationListener;
 
 import static android.content.Context.MODE_PRIVATE;
@@ -14,9 +19,43 @@ class Notification extends Forwarder {
 
     private SharedPreferences.OnSharedPreferenceChangeListener onNotificationDisplayed = new SharedPreferences.OnSharedPreferenceChangeListener() {
         @Override
-        public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
-            Log.e("WTF", "Invalidated dataset: " + key);
-            mainActivity.adapter.notifyDataSetChanged();
+        public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String packageName) {
+            Log.e("WTF", "Invalidated dataset: " + packageName);
+            ListView list = mainActivity.list;
+
+            // A new notification was received, iterate over the currently displayed results
+            // if one of them is for the package that just received a notification,
+            // update the notification dot visual if required.
+            //
+            // This implementation should be more efficient than calling notifyDataSetInvalidated()
+            // since we only iterate over the items currently displayed in the list
+            // and do not rebuild them all, just toggle visibility if required.
+            for (int i = 0; i <= list.getLastVisiblePosition() - list.getFirstVisiblePosition(); i++) {
+                View v = list.getChildAt(i);
+                final View notificationDot = v.findViewById(R.id.item_notification_dot);
+                if (notificationDot != null && notificationDot.getTag().toString().equals(packageName)) {
+                    int currentVisibility = notificationDot.getVisibility();
+                    boolean hasNotification = notificationPreferences.contains(packageName);
+                    
+                    if(currentVisibility != View.VISIBLE && hasNotification) {
+                        // There is a notification and dot was not visible
+                        notificationDot.setVisibility(View.VISIBLE);
+                        notificationDot.setAlpha(0);
+                        notificationDot.animate().alpha(1).setListener(null);
+                    }
+                    else if(currentVisibility == View.VISIBLE && !hasNotification) {
+                        // There is no notification anymore, and dot was visible
+                        notificationDot.animate().alpha(0).setListener(new AnimatorListenerAdapter() {
+                            @Override
+                            public void onAnimationEnd(Animator animation) {
+                                super.onAnimationEnd(animation);
+                                notificationDot.setVisibility(View.GONE);
+                                notificationDot.setAlpha(1);
+                            }
+                        });
+                    }
+                }
+            }
         }
     };
 
diff --git a/app/src/main/java/fr/neamar/kiss/result/AppResult.java b/app/src/main/java/fr/neamar/kiss/result/AppResult.java
index aaf9b09c1..b601079c3 100644
--- a/app/src/main/java/fr/neamar/kiss/result/AppResult.java
+++ b/app/src/main/java/fr/neamar/kiss/result/AppResult.java
@@ -85,7 +85,9 @@ public View display(final Context context, int position, View convertView, Fuzzy
 
         if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
             SharedPreferences notificationPrefs = context.getSharedPreferences(NotificationListener.NOTIFICATION_PREFERENCES_NAME, Context.MODE_PRIVATE);
-            view.findViewById(R.id.item_notification_dot).setVisibility(notificationPrefs.contains(className.getPackageName()) ? View.VISIBLE : View.GONE);
+            View notificationView = view.findViewById(R.id.item_notification_dot);
+            notificationView.setVisibility(notificationPrefs.contains(className.getPackageName()) ? View.VISIBLE : View.GONE);
+            notificationView.setTag(className.getPackageName());
         }
 
         return view;

From 115fd9c1fea5ac1885f07074817251e10da8e541 Mon Sep 17 00:00:00 2001
From: Neamar <neamar@neamar.fr>
Date: Fri, 26 Apr 2019 09:17:17 +0100
Subject: [PATCH 08/30] Started investigation to add dot on favorite

Used the opportunity to clean up some code
---
 .../fr/neamar/kiss/forwarder/Favorites.java   | 20 ++++++++-----------
 .../neamar/kiss/forwarder/Notification.java   |  2 +-
 .../fr/neamar/kiss/result/ContactsResult.java |  3 +--
 3 files changed, 10 insertions(+), 15 deletions(-)

diff --git a/app/src/main/java/fr/neamar/kiss/forwarder/Favorites.java b/app/src/main/java/fr/neamar/kiss/forwarder/Favorites.java
index 35be98668..a90e77255 100644
--- a/app/src/main/java/fr/neamar/kiss/forwarder/Favorites.java
+++ b/app/src/main/java/fr/neamar/kiss/forwarder/Favorites.java
@@ -19,6 +19,8 @@
 import android.view.ViewGroup;
 import android.widget.ImageView;
 
+import androidx.annotation.NonNull;
+
 import java.util.ArrayList;
 
 import fr.neamar.kiss.KissApplication;
@@ -122,8 +124,6 @@ void onFavoriteChange() {
 
         // Don't look for items after favIds length, we won't be able to display them
         for (int i = 0; i < favoritesPojo.size(); i++) {
-            Pojo pojo = favoritesPojo.get(i);
-
             ImageView image;
 
             if (favoritesViews.size() <= i) {
@@ -139,9 +139,10 @@ void onFavoriteChange() {
                 ((ViewGroup) mainActivity.favoritesBar).addView(image);
                 favoritesViews.add(image);
             } else {
-                image = (ImageView) favoritesViews.get(i);
+                image = favoritesViews.get(i);
             }
 
+            Pojo pojo = favoritesPojo.get(i);
             Result result = Result.fromPojo(mainActivity, pojo);
             Drawable drawable = result.getDrawable(mainActivity);
             if (drawable != null) {
@@ -187,7 +188,7 @@ void onDataSetChanged() {
      */
     private void addDefaultAppsToFavs() {
         {
-            //Default phone call app
+            // Default phone call app
             Intent phoneIntent = new Intent(Intent.ACTION_DIAL);
             phoneIntent.setData(Uri.parse("tel:0000"));
             ResolveInfo resolveInfo = mainActivity.getPackageManager().resolveActivity(phoneIntent, PackageManager.MATCH_DEFAULT_ONLY);
@@ -212,7 +213,7 @@ private void addDefaultAppsToFavs() {
             }
         }
         {
-            //Default contacts app
+            // Default contacts app
             Intent contactsIntent = new Intent(Intent.ACTION_DEFAULT, ContactsContract.Contacts.CONTENT_URI);
             ResolveInfo resolveInfo = mainActivity.getPackageManager().resolveActivity(contactsIntent, PackageManager.MATCH_DEFAULT_ONLY);
             if (resolveInfo != null) {
@@ -225,7 +226,7 @@ private void addDefaultAppsToFavs() {
 
         }
         {
-            //Default browser
+            // Default browser
             Intent browserIntent = new Intent("android.intent.action.VIEW", Uri.parse("http://"));
             ResolveInfo resolveInfo = mainActivity.getPackageManager().resolveActivity(browserIntent, PackageManager.MATCH_DEFAULT_ONLY);
             if (resolveInfo != null) {
@@ -248,13 +249,8 @@ private void addDefaultAppsToFavs() {
         }
     }
 
+    @NonNull
     private Result getFavResult(int favNumber) {
-        if (favNumber >= favoritesPojo.size()) {
-            // Clicking on a favorite before everything is loaded.
-            Log.i(TAG, "Clicking on an uninitialized favorite.");
-            return null;
-        }
-
         // Favorites handling
         Pojo pojo = favoritesPojo.get(favNumber);
         return Result.fromPojo(mainActivity, pojo);
diff --git a/app/src/main/java/fr/neamar/kiss/forwarder/Notification.java b/app/src/main/java/fr/neamar/kiss/forwarder/Notification.java
index 9da5a80a8..9be5833ac 100644
--- a/app/src/main/java/fr/neamar/kiss/forwarder/Notification.java
+++ b/app/src/main/java/fr/neamar/kiss/forwarder/Notification.java
@@ -36,7 +36,7 @@ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, Strin
                 if (notificationDot != null && notificationDot.getTag().toString().equals(packageName)) {
                     int currentVisibility = notificationDot.getVisibility();
                     boolean hasNotification = notificationPreferences.contains(packageName);
-                    
+
                     if(currentVisibility != View.VISIBLE && hasNotification) {
                         // There is a notification and dot was not visible
                         notificationDot.setVisibility(View.VISIBLE);
diff --git a/app/src/main/java/fr/neamar/kiss/result/ContactsResult.java b/app/src/main/java/fr/neamar/kiss/result/ContactsResult.java
index 49c048655..c0bceb123 100644
--- a/app/src/main/java/fr/neamar/kiss/result/ContactsResult.java
+++ b/app/src/main/java/fr/neamar/kiss/result/ContactsResult.java
@@ -174,7 +174,6 @@ void setDrawableCache(Drawable drawable) {
         icon = drawable;
     }
 
-    @SuppressWarnings("deprecation")
     @Override
     public Drawable getDrawable(Context context) {
         synchronized (this) {
@@ -203,7 +202,7 @@ public Drawable getDrawable(Context context) {
         }
     }
 
-    public void launchContactView(Context context, View v) {
+    private void launchContactView(Context context, View v) {
         Intent viewContact = new Intent(Intent.ACTION_VIEW);
 
         viewContact.setData(Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI,

From 6a3cac601f5825b596274c585066c2c7029c513b Mon Sep 17 00:00:00 2001
From: Neamar <neamar@neamar.fr>
Date: Fri, 26 Apr 2019 09:20:16 +0100
Subject: [PATCH 09/30] Removed stub code to generate notification

Was used for testing.
---
 app/build.gradle                                   |  1 -
 app/src/main/java/fr/neamar/kiss/MainActivity.java | 13 -------------
 2 files changed, 14 deletions(-)

diff --git a/app/build.gradle b/app/build.gradle
index c9e9e359e..5b319e50c 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -25,7 +25,6 @@ dependencies {
     androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
     androidTestImplementation 'androidx.test:rules:1.1.1'
     implementation 'androidx.annotation:annotation:1.0.2'
-    implementation "com.android.support:support-compat:28.0.0"
 }
 
 buildscript {
diff --git a/app/src/main/java/fr/neamar/kiss/MainActivity.java b/app/src/main/java/fr/neamar/kiss/MainActivity.java
index 846f7b3f1..da9c88d91 100644
--- a/app/src/main/java/fr/neamar/kiss/MainActivity.java
+++ b/app/src/main/java/fr/neamar/kiss/MainActivity.java
@@ -35,8 +35,6 @@
 import android.widget.TextView.OnEditorActionListener;
 
 import androidx.annotation.NonNull;
-import androidx.core.app.NotificationCompat;
-import androidx.core.app.NotificationManagerCompat;
 
 import java.util.ArrayList;
 
@@ -651,17 +649,6 @@ private void displayKissBar(boolean display, boolean clearSearchText) {
                 notificationManager.createNotificationChannel(channel);
             }
 
-            NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "channel")
-                    .setSmallIcon(R.drawable.call)
-                    .setContentTitle("TITLE")
-                    .setContentText("Content forever")
-                    .setPriority(NotificationCompat.PRIORITY_DEFAULT);
-
-            NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
-
-            // notificationId is a unique int for each notification that you must define
-            notificationManager.notify(1, builder.build());
-
             // Display the app list
             if (searchEditText.getText().length() != 0) {
                 searchEditText.setText("");

From 99f283a00e793f7daa16507230717c424964ba6f Mon Sep 17 00:00:00 2001
From: Neamar <neamar@neamar.fr>
Date: Sun, 28 Apr 2019 14:16:05 +0100
Subject: [PATCH 10/30] Use main color for notification dot

---
 app/src/main/java/fr/neamar/kiss/MainActivity.java     | 2 +-
 app/src/main/java/fr/neamar/kiss/UIColors.java         | 2 +-
 app/src/main/java/fr/neamar/kiss/result/AppResult.java | 6 +++++-
 3 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/app/src/main/java/fr/neamar/kiss/MainActivity.java b/app/src/main/java/fr/neamar/kiss/MainActivity.java
index da9c88d91..1824a98b0 100644
--- a/app/src/main/java/fr/neamar/kiss/MainActivity.java
+++ b/app/src/main/java/fr/neamar/kiss/MainActivity.java
@@ -277,7 +277,7 @@ public void onChanged() {
                     emptyListView.setVisibility(View.GONE);
                 }
 
-                // forwarderManager.onDataSetChanged();
+                forwarderManager.onDataSetChanged();
 
             }
         });
diff --git a/app/src/main/java/fr/neamar/kiss/UIColors.java b/app/src/main/java/fr/neamar/kiss/UIColors.java
index 8aa77d2fa..e2488151a 100644
--- a/app/src/main/java/fr/neamar/kiss/UIColors.java
+++ b/app/src/main/java/fr/neamar/kiss/UIColors.java
@@ -71,7 +71,7 @@ public static int getPrimaryColor(Context context) {
         return primaryColor;
     }
 
-    public static void clearPrimaryColorCache(Context context) {
+    static void clearPrimaryColorCache(Context context) {
         primaryColor = -1;
     }
 }
diff --git a/app/src/main/java/fr/neamar/kiss/result/AppResult.java b/app/src/main/java/fr/neamar/kiss/result/AppResult.java
index b601079c3..e052a3e91 100644
--- a/app/src/main/java/fr/neamar/kiss/result/AppResult.java
+++ b/app/src/main/java/fr/neamar/kiss/result/AppResult.java
@@ -30,6 +30,7 @@
 import fr.neamar.kiss.KissApplication;
 import fr.neamar.kiss.MainActivity;
 import fr.neamar.kiss.R;
+import fr.neamar.kiss.UIColors;
 import fr.neamar.kiss.adapter.RecordAdapter;
 import fr.neamar.kiss.notification.NotificationListener;
 import fr.neamar.kiss.pojo.AppPojo;
@@ -85,9 +86,12 @@ public View display(final Context context, int position, View convertView, Fuzzy
 
         if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
             SharedPreferences notificationPrefs = context.getSharedPreferences(NotificationListener.NOTIFICATION_PREFERENCES_NAME, Context.MODE_PRIVATE);
-            View notificationView = view.findViewById(R.id.item_notification_dot);
+            ImageView notificationView = view.findViewById(R.id.item_notification_dot);
             notificationView.setVisibility(notificationPrefs.contains(className.getPackageName()) ? View.VISIBLE : View.GONE);
             notificationView.setTag(className.getPackageName());
+
+            int primaryColor = UIColors.getPrimaryColor(context);
+            notificationView.setColorFilter(primaryColor);
         }
 
         return view;

From 1ca5cf7f939f9acf964dca1166c17dce49f28ada Mon Sep 17 00:00:00 2001
From: Neamar <neamar@neamar.fr>
Date: Sun, 28 Apr 2019 14:18:48 +0100
Subject: [PATCH 11/30] Cleanup

---
 .../java/fr/neamar/kiss/MainActivity.java     | 19 +------------------
 1 file changed, 1 insertion(+), 18 deletions(-)

diff --git a/app/src/main/java/fr/neamar/kiss/MainActivity.java b/app/src/main/java/fr/neamar/kiss/MainActivity.java
index 1824a98b0..cf2155955 100644
--- a/app/src/main/java/fr/neamar/kiss/MainActivity.java
+++ b/app/src/main/java/fr/neamar/kiss/MainActivity.java
@@ -4,8 +4,6 @@
 import android.animation.AnimatorListenerAdapter;
 import android.annotation.SuppressLint;
 import android.app.Activity;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -270,10 +268,7 @@ public void onChanged() {
                     listContainer.setVisibility(View.GONE);
                     emptyListView.setVisibility(View.VISIBLE);
                 } else {
-                    // Otherwise, display results
-                    if (listContainer.getVisibility() != View.VISIBLE) {
-                        listContainer.setVisibility(View.VISIBLE);
-                    }
+                    listContainer.setVisibility(View.VISIBLE);
                     emptyListView.setVisibility(View.GONE);
                 }
 
@@ -637,18 +632,6 @@ private void displayKissBar(boolean display, boolean clearSearchText) {
         int finalRadius = Math.max(kissBar.getWidth(), kissBar.getHeight());
 
         if (display) {
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-                CharSequence name = "fake";
-                String description = "fake description";
-                int importance = NotificationManager.IMPORTANCE_DEFAULT;
-                NotificationChannel channel = new NotificationChannel("channel", name, importance);
-                channel.setDescription(description);
-                // Register the channel with the system; you can't change the importance
-                // or other notification behaviors after this
-                NotificationManager notificationManager = getSystemService(NotificationManager.class);
-                notificationManager.createNotificationChannel(channel);
-            }
-
             // Display the app list
             if (searchEditText.getText().length() != 0) {
                 searchEditText.setText("");

From 0fa573ddd5dd4ea5221e490777b2d8718f882a60 Mon Sep 17 00:00:00 2001
From: Neamar <neamar@neamar.fr>
Date: Sun, 28 Apr 2019 15:05:12 +0100
Subject: [PATCH 12/30] Animate favorites too

---
 .../java/fr/neamar/kiss/MainActivity.java     |  2 +-
 .../fr/neamar/kiss/forwarder/Favorites.java   | 71 +++++++++++++------
 .../neamar/kiss/forwarder/Notification.java   | 62 +++++++++-------
 .../java/fr/neamar/kiss/result/AppResult.java |  8 ++-
 app/src/main/res/layout/favorite_item.xml     | 26 +++++--
 app/src/main/res/layout/main.xml              | 12 ++--
 6 files changed, 121 insertions(+), 60 deletions(-)

diff --git a/app/src/main/java/fr/neamar/kiss/MainActivity.java b/app/src/main/java/fr/neamar/kiss/MainActivity.java
index cf2155955..50d52db82 100644
--- a/app/src/main/java/fr/neamar/kiss/MainActivity.java
+++ b/app/src/main/java/fr/neamar/kiss/MainActivity.java
@@ -115,7 +115,7 @@ public class MainActivity extends Activity implements QueryInterface, KeyboardSc
      * Favorites bar. Can be either the favorites within the KISS bar,
      * or the external favorites bar (default)
      */
-    public View favoritesBar;
+    public ViewGroup favoritesBar;
     /**
      * Progress bar displayed when loading
      */
diff --git a/app/src/main/java/fr/neamar/kiss/forwarder/Favorites.java b/app/src/main/java/fr/neamar/kiss/forwarder/Favorites.java
index a90e77255..72772ebf9 100644
--- a/app/src/main/java/fr/neamar/kiss/forwarder/Favorites.java
+++ b/app/src/main/java/fr/neamar/kiss/forwarder/Favorites.java
@@ -2,6 +2,7 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.graphics.Bitmap;
@@ -9,6 +10,7 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
+import android.os.Build;
 import android.provider.ContactsContract;
 import android.util.Log;
 import android.view.DragEvent;
@@ -27,7 +29,9 @@
 import fr.neamar.kiss.MainActivity;
 import fr.neamar.kiss.R;
 import fr.neamar.kiss.db.DBHelper;
+import fr.neamar.kiss.notification.NotificationListener;
 import fr.neamar.kiss.pojo.Pojo;
+import fr.neamar.kiss.result.AppResult;
 import fr.neamar.kiss.result.ContactsResult;
 import fr.neamar.kiss.result.Result;
 import fr.neamar.kiss.ui.ListPopup;
@@ -42,7 +46,7 @@ public class Favorites extends Forwarder implements View.OnClickListener, View.O
     /**
      * IDs for the favorites buttons
      */
-    private ArrayList<ImageView> favoritesViews = new ArrayList<>();
+    private ArrayList<View> favoritesViews = new ArrayList<>();
 
     /**
      * Currently displayed favorites
@@ -68,6 +72,8 @@ public class Favorites extends Forwarder implements View.OnClickListener, View.O
     private boolean contextMenuShown = false;
     private int favCount = -1;
 
+    private SharedPreferences notificationPrefs = null;
+
     Favorites(MainActivity mainActivity) {
         super(mainActivity);
     }
@@ -91,6 +97,11 @@ void onCreate() {
             // set flag to false
             prefs.edit().putBoolean("firstRun", false).apply();
         }
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+            notificationPrefs = mainActivity.getSharedPreferences(NotificationListener.NOTIFICATION_PREFERENCES_NAME, Context.MODE_PRIVATE);
+        }
+
     }
 
     private static Bitmap drawableToBitmap(Drawable drawable) {
@@ -124,7 +135,7 @@ void onFavoriteChange() {
 
         // Don't look for items after favIds length, we won't be able to display them
         for (int i = 0; i < favoritesPojo.size(); i++) {
-            ImageView image;
+            View favoriteView;
 
             if (favoritesViews.size() <= i) {
                 if (layoutInflater == null) {
@@ -132,32 +143,50 @@ void onFavoriteChange() {
                             .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                 }
                 assert layoutInflater != null;
-                image = (ImageView) layoutInflater.inflate(R.layout.favorite_item, (ViewGroup) mainActivity.favoritesBar, false);
-                image.setTag(i);
-                image.setOnDragListener(this);
-                image.setOnTouchListener(this);
-                ((ViewGroup) mainActivity.favoritesBar).addView(image);
-                favoritesViews.add(image);
+                favoriteView = layoutInflater.inflate(R.layout.favorite_item, (ViewGroup) mainActivity.favoritesBar, false);
+                favoriteView.setTag(i);
+                favoriteView.setOnDragListener(this);
+                favoriteView.setOnTouchListener(this);
+                mainActivity.favoritesBar.addView(favoriteView);
+                favoritesViews.add(favoriteView);
             } else {
-                image = favoritesViews.get(i);
+                favoriteView = favoritesViews.get(i);
             }
 
             Pojo pojo = favoritesPojo.get(i);
             Result result = Result.fromPojo(mainActivity, pojo);
             Drawable drawable = result.getDrawable(mainActivity);
+            ImageView favoriteImage = favoriteView.findViewById(R.id.favorite);
             if (drawable != null) {
                 if (result instanceof ContactsResult) {
                     Bitmap iconBitmap = drawableToBitmap(drawable);
                     drawable = new RoundedQuickContactBadge.RoundedDrawable(iconBitmap);
                 }
-                image.setImageDrawable(drawable);
+                favoriteImage.setImageDrawable(drawable);
             } else {
                 // Use a placeholder if no drawable found
-                image.setImageResource(R.drawable.ic_launcher_white);
+                favoriteImage.setImageResource(R.drawable.ic_launcher_white);
             }
 
-            image.setVisibility(View.VISIBLE);
-            image.setContentDescription(pojo.getName());
+            if(notificationPrefs != null) {
+                ImageView notificationDot = favoriteView.findViewById(R.id.item_notification_dot);
+                if (result instanceof AppResult) {
+                    String packageName = ((AppResult) result).getPackageName();
+                    notificationDot.setTag(packageName);
+                    Log.e("WTF", "PN" + packageName);
+                    if(notificationPrefs.contains(packageName)) {
+                        Log.e("WTF", "PNVISIBVLE" + packageName);
+
+                        notificationDot.setVisibility(View.VISIBLE);
+                    }
+                } else {
+                    notificationDot.setTag(null);
+                }
+
+            }
+
+            favoriteView.setVisibility(View.VISIBLE);
+            favoriteView.setContentDescription(pojo.getName());
         }
 
         // Hide empty favorites (not enough favorites yet)
@@ -178,7 +207,7 @@ void updateSearchRecords(String query) {
 
     void onDataSetChanged() {
         // Do not display the external bar when viewing all apps
-        if(mainActivity.isViewingAllApps() && isExternalFavoriteBarEnabled()) {
+        if (mainActivity.isViewingAllApps() && isExternalFavoriteBarEnabled()) {
             mainActivity.favoritesBar.setVisibility(View.GONE);
         }
     }
@@ -198,7 +227,7 @@ private void addDefaultAppsToFavs() {
 
                 if ((resolveInfo.activityInfo.name != null) && (!resolveInfo.activityInfo.name.equals(DEFAULT_RESOLVER))) {
                     String activityName = resolveInfo.activityInfo.name;
-                    if(packageName.equals("com.google.android.dialer")) {
+                    if (packageName.equals("com.google.android.dialer")) {
                         // Default dialer has two different activities, one when calling a phone number and one when opening the app from the launcher.
                         // (com.google.android.apps.dialer.extensions.GoogleDialtactsActivity vs. com.google.android.dialer.extensions.GoogleDialtactsActivity)
                         // (notice the .apps. in the middle)
@@ -235,7 +264,7 @@ private void addDefaultAppsToFavs() {
 
                 if ((resolveInfo.activityInfo.name != null) && (!resolveInfo.activityInfo.name.equals(DEFAULT_RESOLVER))) {
                     String activityName = resolveInfo.activityInfo.name;
-                    if(packageName.equalsIgnoreCase("com.android.chrome")) {
+                    if (packageName.equalsIgnoreCase("com.android.chrome")) {
                         // Chrome has two different activities, one for Launcher and one when opening an URL.
                         // The browserIntent above resolve to the latter, which isn't exposed as a Launcher activity and thus can't be displayed
                         // This hack uses the correct resolver when the application is Chrome.
@@ -288,7 +317,7 @@ public boolean onTouch(View view, MotionEvent motionEvent) {
             return true;
         }
         // No need to do the extra work
-        if(isDragging) {
+        if (isDragging) {
             return true;
         }
 
@@ -298,7 +327,7 @@ public boolean onTouch(View view, MotionEvent motionEvent) {
             this.onClick(view);
             return true;
         }
-        if(!contextMenuShown && holdTime > LONG_PRESS_DELAY) {
+        if (!contextMenuShown && holdTime > LONG_PRESS_DELAY) {
             contextMenuShown = true;
             this.onLongClick(view);
             return true;
@@ -348,7 +377,7 @@ public boolean onDrag(View v, final DragEvent event) {
 
             case DragEvent.ACTION_DRAG_ENDED:
                 // Only need to handle this action once.
-                if(!isDragging) {
+                if (!isDragging) {
                     return true;
                 }
                 isDragging = false;
@@ -382,8 +411,8 @@ public void run() {
 
                 final int pos = KissApplication.getApplication(mainActivity).getDataHandler().getFavoritePosition(mainActivity, overApp.id);
                 draggedView.post(new Runnable() {
-                     @Override
-                     public void run() {
+                    @Override
+                    public void run() {
                         // Signals to a View that the drag and drop operation has concluded.
                         // If event result is set, this means the dragged view was dropped in target
                         if (event.getResult()) {
diff --git a/app/src/main/java/fr/neamar/kiss/forwarder/Notification.java b/app/src/main/java/fr/neamar/kiss/forwarder/Notification.java
index 9be5833ac..2fbd74565 100644
--- a/app/src/main/java/fr/neamar/kiss/forwarder/Notification.java
+++ b/app/src/main/java/fr/neamar/kiss/forwarder/Notification.java
@@ -6,6 +6,7 @@
 import android.os.Build;
 import android.util.Log;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.ListView;
 
 import fr.neamar.kiss.MainActivity;
@@ -30,32 +31,10 @@ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, Strin
             // This implementation should be more efficient than calling notifyDataSetInvalidated()
             // since we only iterate over the items currently displayed in the list
             // and do not rebuild them all, just toggle visibility if required.
-            for (int i = 0; i <= list.getLastVisiblePosition() - list.getFirstVisiblePosition(); i++) {
-                View v = list.getChildAt(i);
-                final View notificationDot = v.findViewById(R.id.item_notification_dot);
-                if (notificationDot != null && notificationDot.getTag().toString().equals(packageName)) {
-                    int currentVisibility = notificationDot.getVisibility();
-                    boolean hasNotification = notificationPreferences.contains(packageName);
+            updateDots(list, list.getLastVisiblePosition() - list.getFirstVisiblePosition(), packageName);
+
+            updateDots(mainActivity.favoritesBar, mainActivity.favoritesBar.getChildCount(), packageName);
 
-                    if(currentVisibility != View.VISIBLE && hasNotification) {
-                        // There is a notification and dot was not visible
-                        notificationDot.setVisibility(View.VISIBLE);
-                        notificationDot.setAlpha(0);
-                        notificationDot.animate().alpha(1).setListener(null);
-                    }
-                    else if(currentVisibility == View.VISIBLE && !hasNotification) {
-                        // There is no notification anymore, and dot was visible
-                        notificationDot.animate().alpha(0).setListener(new AnimatorListenerAdapter() {
-                            @Override
-                            public void onAnimationEnd(Animator animation) {
-                                super.onAnimationEnd(animation);
-                                notificationDot.setVisibility(View.GONE);
-                                notificationDot.setAlpha(1);
-                            }
-                        });
-                    }
-                }
-            }
         }
     };
 
@@ -79,4 +58,37 @@ void onStop() {
             notificationPreferences.unregisterOnSharedPreferenceChangeListener(onNotificationDisplayed);
         }
     }
+
+    private void updateDots(ViewGroup vg, int childCount, String packageName) {
+        for (int i = 0; i < childCount; i++) {
+            View v = vg.getChildAt(i);
+            final View notificationDot = v.findViewById(R.id.item_notification_dot);
+            if (notificationDot != null && notificationDot.getTag().toString().equals(packageName)) {
+                boolean hasNotification = notificationPreferences.contains(packageName);
+                animateDot(notificationDot, hasNotification);
+            }
+        }
+    }
+
+    private void animateDot(final View notificationDot, Boolean hasNotification) {
+        int currentVisibility = notificationDot.getVisibility();
+
+        if(currentVisibility != View.VISIBLE && hasNotification) {
+            // There is a notification and dot was not visible
+            notificationDot.setVisibility(View.VISIBLE);
+            notificationDot.setAlpha(0);
+            notificationDot.animate().alpha(1).setListener(null);
+        }
+        else if(currentVisibility == View.VISIBLE && !hasNotification) {
+            // There is no notification anymore, and dot was visible
+            notificationDot.animate().alpha(0).setListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    super.onAnimationEnd(animation);
+                    notificationDot.setVisibility(View.GONE);
+                    notificationDot.setAlpha(1);
+                }
+            });
+        }
+    }
 }
diff --git a/app/src/main/java/fr/neamar/kiss/result/AppResult.java b/app/src/main/java/fr/neamar/kiss/result/AppResult.java
index e052a3e91..61862b758 100644
--- a/app/src/main/java/fr/neamar/kiss/result/AppResult.java
+++ b/app/src/main/java/fr/neamar/kiss/result/AppResult.java
@@ -87,8 +87,8 @@ public View display(final Context context, int position, View convertView, Fuzzy
         if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
             SharedPreferences notificationPrefs = context.getSharedPreferences(NotificationListener.NOTIFICATION_PREFERENCES_NAME, Context.MODE_PRIVATE);
             ImageView notificationView = view.findViewById(R.id.item_notification_dot);
-            notificationView.setVisibility(notificationPrefs.contains(className.getPackageName()) ? View.VISIBLE : View.GONE);
-            notificationView.setTag(className.getPackageName());
+            notificationView.setVisibility(notificationPrefs.contains(getPackageName()) ? View.VISIBLE : View.GONE);
+            notificationView.setTag(getPackageName());
 
             int primaryColor = UIColors.getPrimaryColor(context);
             notificationView.setColorFilter(primaryColor);
@@ -97,6 +97,10 @@ public View display(final Context context, int position, View convertView, Fuzzy
         return view;
     }
 
+    public String getPackageName() {
+        return appPojo.packageName;
+    }
+
     @Override
     protected ListPopup buildPopupMenu(Context context, ArrayAdapter<ListPopup.Item> adapter, final RecordAdapter parent, View parentView) {
         if ((!(context instanceof MainActivity)) || (((MainActivity) context).isViewingSearchResults())) {
diff --git a/app/src/main/res/layout/favorite_item.xml b/app/src/main/res/layout/favorite_item.xml
index 79959d5e2..d2fd9990e 100644
--- a/app/src/main/res/layout/favorite_item.xml
+++ b/app/src/main/res/layout/favorite_item.xml
@@ -1,11 +1,27 @@
 <?xml version="1.0" encoding="utf-8"?>
-<ImageView
+<RelativeLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="0px"
-    android:layout_height="match_parent"
+    android:layout_height="wrap_content"
     android:layout_weight="1"
     android:background="?attr/appSelectableItemBackground"
-    android:contentDescription="@null"
-    android:padding="@dimen/favorite_padding"
-    tools:src="@drawable/ic_contact" />
+    android:padding="@dimen/favorite_padding">
+    <ImageView
+        android:contentDescription="@null"
+        android:id="@+id/favorite"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        tools:src="@drawable/ic_contact" />
+
+    <ImageView
+        android:id="@+id/item_notification_dot"
+        android:layout_width="14dp"
+        android:layout_height="14dp"
+        android:layout_alignTop="@id/favorite"
+        android:layout_alignEnd="@id/favorite"
+        android:layout_alignRight="@id/favorite"
+        android:visibility="gone"
+        android:contentDescription="@null"
+        android:src="@drawable/notification_dot" />
+</RelativeLayout>
diff --git a/app/src/main/res/layout/main.xml b/app/src/main/res/layout/main.xml
index 5443e1d47..23f211e75 100644
--- a/app/src/main/res/layout/main.xml
+++ b/app/src/main/res/layout/main.xml
@@ -22,7 +22,7 @@
         android:id="@+id/resultLayout"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:layout_above="@+id/externalFavoriteBar"
+        android:layout_above="@id/externalFavoriteBar"
         android:layout_alignParentStart="true"
         android:layout_alignParentLeft="true"
         android:layout_alignParentTop="false"
@@ -60,7 +60,7 @@
         android:id="@+id/externalFavoriteBar"
         android:layout_width="match_parent"
         android:layout_height="58dp"
-        android:layout_above="@+id/searchEditLayout"
+        android:layout_above="@id/searchEditLayout"
         android:layout_marginLeft="10dp"
         android:layout_marginRight="10dp"
         android:layout_marginBottom="10dp"
@@ -126,10 +126,10 @@
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:layout_centerVertical="true"
-            android:layout_toStartOf="@+id/rightHandSideButtonsWrapper"
-            android:layout_toLeftOf="@+id/rightHandSideButtonsWrapper"
-            android:layout_toEndOf="@+id/leftHandSideButtonsWrapper"
-            android:layout_toRightOf="@+id/leftHandSideButtonsWrapper"
+            android:layout_toStartOf="@id/rightHandSideButtonsWrapper"
+            android:layout_toLeftOf="@id/rightHandSideButtonsWrapper"
+            android:layout_toEndOf="@id/leftHandSideButtonsWrapper"
+            android:layout_toRightOf="@id/leftHandSideButtonsWrapper"
             android:layout_margin="1dp"
             android:background="?attr/searchBackgroundColor"
             android:hint="@string/ui_search_hint"

From faa98aa68a91788300a3326d2461cda3c9cc7af8 Mon Sep 17 00:00:00 2001
From: Neamar <neamar@neamar.fr>
Date: Sun, 28 Apr 2019 15:07:31 +0100
Subject: [PATCH 13/30] Removed logging

---
 .../java/fr/neamar/kiss/forwarder/Favorites.java     |  6 +++---
 .../java/fr/neamar/kiss/forwarder/Notification.java  | 12 ++++++------
 .../kiss/notification/NotificationListener.java      |  6 ------
 3 files changed, 9 insertions(+), 15 deletions(-)

diff --git a/app/src/main/java/fr/neamar/kiss/forwarder/Favorites.java b/app/src/main/java/fr/neamar/kiss/forwarder/Favorites.java
index 72772ebf9..6a88321c2 100644
--- a/app/src/main/java/fr/neamar/kiss/forwarder/Favorites.java
+++ b/app/src/main/java/fr/neamar/kiss/forwarder/Favorites.java
@@ -28,6 +28,7 @@
 import fr.neamar.kiss.KissApplication;
 import fr.neamar.kiss.MainActivity;
 import fr.neamar.kiss.R;
+import fr.neamar.kiss.UIColors;
 import fr.neamar.kiss.db.DBHelper;
 import fr.neamar.kiss.notification.NotificationListener;
 import fr.neamar.kiss.pojo.Pojo;
@@ -173,10 +174,9 @@ void onFavoriteChange() {
                 if (result instanceof AppResult) {
                     String packageName = ((AppResult) result).getPackageName();
                     notificationDot.setTag(packageName);
-                    Log.e("WTF", "PN" + packageName);
+                    int primaryColor = UIColors.getPrimaryColor(mainActivity);
+                    notificationDot.setColorFilter(primaryColor);
                     if(notificationPrefs.contains(packageName)) {
-                        Log.e("WTF", "PNVISIBVLE" + packageName);
-
                         notificationDot.setVisibility(View.VISIBLE);
                     }
                 } else {
diff --git a/app/src/main/java/fr/neamar/kiss/forwarder/Notification.java b/app/src/main/java/fr/neamar/kiss/forwarder/Notification.java
index 2fbd74565..8d084770e 100644
--- a/app/src/main/java/fr/neamar/kiss/forwarder/Notification.java
+++ b/app/src/main/java/fr/neamar/kiss/forwarder/Notification.java
@@ -4,7 +4,6 @@
 import android.animation.AnimatorListenerAdapter;
 import android.content.SharedPreferences;
 import android.os.Build;
-import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ListView;
@@ -21,7 +20,6 @@ class Notification extends Forwarder {
     private SharedPreferences.OnSharedPreferenceChangeListener onNotificationDisplayed = new SharedPreferences.OnSharedPreferenceChangeListener() {
         @Override
         public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String packageName) {
-            Log.e("WTF", "Invalidated dataset: " + packageName);
             ListView list = mainActivity.list;
 
             // A new notification was received, iterate over the currently displayed results
@@ -76,17 +74,19 @@ private void animateDot(final View notificationDot, Boolean hasNotification) {
         if(currentVisibility != View.VISIBLE && hasNotification) {
             // There is a notification and dot was not visible
             notificationDot.setVisibility(View.VISIBLE);
-            notificationDot.setAlpha(0);
-            notificationDot.animate().alpha(1).setListener(null);
+            notificationDot.setScaleX(0);
+            notificationDot.setScaleY(0);
+            notificationDot.animate().scaleX(1).scaleY(1).setListener(null);
         }
         else if(currentVisibility == View.VISIBLE && !hasNotification) {
             // There is no notification anymore, and dot was visible
-            notificationDot.animate().alpha(0).setListener(new AnimatorListenerAdapter() {
+            notificationDot.animate().scaleX(0).scaleY(0).setListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
                     super.onAnimationEnd(animation);
                     notificationDot.setVisibility(View.GONE);
-                    notificationDot.setAlpha(1);
+                    notificationDot.setScaleX(1);
+                    notificationDot.setScaleY(1);
                 }
             });
         }
diff --git a/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java b/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java
index 8c623be1f..23d1479e4 100644
--- a/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java
+++ b/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java
@@ -7,7 +7,6 @@
 import android.os.IBinder;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
-import android.util.Log;
 
 import androidx.annotation.RequiresApi;
 
@@ -37,8 +36,6 @@ public void onNotificationPosted(StatusBarNotification sbn) {
 
         currentNotifications.add(Integer.toString(sbn.getId()));
         prefs.edit().putStringSet(sbn.getPackageName(), currentNotifications).apply();
-
-        Log.e("WTF", "ADD Notifications for " + sbn.getPackageName() + currentNotifications.toString());
     }
 
     @Override
@@ -56,9 +53,6 @@ public void onNotificationRemoved(StatusBarNotification sbn) {
             editor.putStringSet(sbn.getPackageName(), currentNotifications);
         }
         editor.apply();
-
-        Log.e("WTF", "DEL Notifications for " + sbn.getPackageName() + currentNotifications.toString());
-
     }
 
     public Set<String> getCurrentNotificationsForPackage(String packageName) {

From 54cc13e7581205327d1b0a0b90c1c68dc8956882 Mon Sep 17 00:00:00 2001
From: Neamar <neamar@neamar.fr>
Date: Sun, 28 Apr 2019 16:35:53 +0100
Subject: [PATCH 14/30] Enable or disable notification listener from settings

---
 .../java/fr/neamar/kiss/SettingsActivity.java |  3 ++-
 .../preference/NotificationPreference.java    | 23 +++++++++++++++++++
 app/src/main/res/values/strings.xml           |  2 ++
 app/src/main/res/xml/preferences.xml          |  5 ++++
 4 files changed, 32 insertions(+), 1 deletion(-)
 create mode 100644 app/src/main/java/fr/neamar/kiss/preference/NotificationPreference.java

diff --git a/app/src/main/java/fr/neamar/kiss/SettingsActivity.java b/app/src/main/java/fr/neamar/kiss/SettingsActivity.java
index 9b281857c..413d799db 100644
--- a/app/src/main/java/fr/neamar/kiss/SettingsActivity.java
+++ b/app/src/main/java/fr/neamar/kiss/SettingsActivity.java
@@ -20,12 +20,13 @@
 import android.view.MenuItem;
 import android.widget.Toast;
 
+import androidx.annotation.NonNull;
+
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
 
-import androidx.annotation.NonNull;
 import fr.neamar.kiss.broadcast.IncomingCallHandler;
 import fr.neamar.kiss.dataprovider.AppProvider;
 import fr.neamar.kiss.dataprovider.SearchProvider;
diff --git a/app/src/main/java/fr/neamar/kiss/preference/NotificationPreference.java b/app/src/main/java/fr/neamar/kiss/preference/NotificationPreference.java
new file mode 100644
index 000000000..cf73c6761
--- /dev/null
+++ b/app/src/main/java/fr/neamar/kiss/preference/NotificationPreference.java
@@ -0,0 +1,23 @@
+package fr.neamar.kiss.preference;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.preference.DialogPreference;
+import android.util.AttributeSet;
+
+public class NotificationPreference extends DialogPreference {
+    private static final String ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS";
+
+    public NotificationPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        super.onClick(dialog, which);
+        if (which == DialogInterface.BUTTON_POSITIVE) {
+            getContext().startActivity(new Intent(ACTION_NOTIFICATION_LISTENER_SETTINGS));
+        }
+    }
+}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index b78596ea1..95d3f232c 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -213,4 +213,6 @@
     <string name="unable_to_initialize_shortcuts">Unable to initialize shortcuts. Is KISS your default launcher?</string>
     <string name="enable_gestures">Enable gestures on empty areas</string>
     <string name="enable_gestures_desc">(↑ for keyboard, ↓ for notifications)</string>
+    <string name="notification_support">Display a dot on apps with notifications</string>
+    <string name="notification_dialog">To enable or disable this feature, check or uncheck KISS in the following screen.</string>
 </resources>
\ No newline at end of file
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index 1fa4c0273..aa3392dd3 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -286,6 +286,11 @@
         </PreferenceCategory>
     </PreferenceScreen>
     <PreferenceScreen android:title="@string/title_advanced">
+        <fr.neamar.kiss.preference.NotificationPreference
+            android:defaultValue="true"
+            android:key="enable-notifications"
+            android:dialogMessage="@string/notification_dialog"
+            android:title="@string/notification_support" />
         <fr.neamar.kiss.preference.DefaultLauncherPreference
             android:dialogMessage="@string/default_launcher_warn"
             android:key="default-launcher"

From 676a2d0c7a16455d78e6976c1945c72af2014a6e Mon Sep 17 00:00:00 2001
From: Neamar <neamar@neamar.fr>
Date: Sun, 28 Apr 2019 16:45:58 +0100
Subject: [PATCH 15/30] Hide notification setting pre-android 18

---
 app/src/main/java/fr/neamar/kiss/SettingsActivity.java | 6 ++++--
 app/src/main/res/xml/preferences.xml                   | 2 +-
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/app/src/main/java/fr/neamar/kiss/SettingsActivity.java b/app/src/main/java/fr/neamar/kiss/SettingsActivity.java
index 413d799db..9ff4ade23 100644
--- a/app/src/main/java/fr/neamar/kiss/SettingsActivity.java
+++ b/app/src/main/java/fr/neamar/kiss/SettingsActivity.java
@@ -12,7 +12,6 @@
 import android.preference.MultiSelectListPreference;
 import android.preference.Preference;
 import android.preference.PreferenceActivity;
-import android.preference.PreferenceCategory;
 import android.preference.PreferenceGroup;
 import android.preference.PreferenceManager;
 import android.view.Menu;
@@ -97,6 +96,9 @@ protected void onCreate(Bundle savedInstanceState) {
             removePreference("history-hide-section", "pref-hide-navbar");
             removePreference("history-hide-section", "pref-hide-statusbar");
         }
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
+            removePreference("advanced", "enable-notifications");
+        }
 
         addHiddenTagsTogglesInformation(prefs);
     }
@@ -120,7 +122,7 @@ public boolean onMenuItemSelected(int featureId, MenuItem item) {
     }
 
     private void removePreference(String parent, String category) {
-        PreferenceCategory p = (PreferenceCategory) findPreference(parent);
+        PreferenceGroup p = (PreferenceGroup) findPreference(parent);
         Preference c = findPreference(category);
         p.removePreference(c);
     }
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index aa3392dd3..f00ffe599 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -285,7 +285,7 @@
                 android:title="@string/search_provider_reset" />
         </PreferenceCategory>
     </PreferenceScreen>
-    <PreferenceScreen android:title="@string/title_advanced">
+    <PreferenceScreen android:title="@string/title_advanced" android:key="advanced">
         <fr.neamar.kiss.preference.NotificationPreference
             android:defaultValue="true"
             android:key="enable-notifications"

From ea72a1fff8bc2f39ee50c0dbfaf468dc4d86b612 Mon Sep 17 00:00:00 2001
From: Neamar <neamar@neamar.fr>
Date: Sun, 28 Apr 2019 16:58:15 +0100
Subject: [PATCH 16/30] Synchronise current notifications on start

---
 .../notification/NotificationListener.java    | 32 +++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java b/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java
index 23d1479e4..b869ee137 100644
--- a/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java
+++ b/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java
@@ -10,7 +10,9 @@
 
 import androidx.annotation.RequiresApi;
 
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Set;
 
 @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
@@ -30,6 +32,36 @@ public IBinder onBind(Intent intent) {
         return super.onBind(intent);
     }
 
+    @Override
+    public void onListenerConnected() {
+        super.onListenerConnected();
+
+        // Build a map of notifications, ordered per package
+        StatusBarNotification[] sbns = getActiveNotifications();
+        Map<String, Set<String>> notificationsByPackage = new HashMap<>();
+        for(StatusBarNotification sbn: sbns) {
+            String packageName = sbn.getPackageName();
+            if(!notificationsByPackage.containsKey(packageName)) {
+                notificationsByPackage.put(packageName, new HashSet<String>());
+            }
+
+            notificationsByPackage.get(packageName).add(Integer.toString(sbn.getId()));
+        }
+
+        // And synchronise this map with our sharedpreferences
+        SharedPreferences.Editor editor = prefs.edit();
+        for(String packageName : prefs.getAll().keySet()) {
+            if(notificationsByPackage.containsKey(packageName)) {
+                editor.putStringSet(packageName, notificationsByPackage.get(packageName));
+            }
+            else {
+                editor.remove(packageName);
+            }
+        }
+
+        editor.apply();
+    }
+
     @Override
     public void onNotificationPosted(StatusBarNotification sbn) {
         Set<String> currentNotifications = getCurrentNotificationsForPackage(sbn.getPackageName());

From 66aa0e274429b41133dc0794dda5e3c8e36e7a06 Mon Sep 17 00:00:00 2001
From: Neamar <neamar@neamar.fr>
Date: Sun, 28 Apr 2019 22:09:52 +0100
Subject: [PATCH 17/30] Code review: perf and documentation

---
 .../java/fr/neamar/kiss/MainActivity.java     |  1 +
 .../fr/neamar/kiss/forwarder/Favorites.java   |  6 ++-
 .../neamar/kiss/forwarder/Notification.java   |  1 +
 .../notification/NotificationListener.java    | 37 +++++++++----------
 4 files changed, 23 insertions(+), 22 deletions(-)

diff --git a/app/src/main/java/fr/neamar/kiss/MainActivity.java b/app/src/main/java/fr/neamar/kiss/MainActivity.java
index 50d52db82..3a300506a 100644
--- a/app/src/main/java/fr/neamar/kiss/MainActivity.java
+++ b/app/src/main/java/fr/neamar/kiss/MainActivity.java
@@ -268,6 +268,7 @@ public void onChanged() {
                     listContainer.setVisibility(View.GONE);
                     emptyListView.setVisibility(View.VISIBLE);
                 } else {
+                    // Otherwise, display results
                     listContainer.setVisibility(View.VISIBLE);
                     emptyListView.setVisibility(View.GONE);
                 }
diff --git a/app/src/main/java/fr/neamar/kiss/forwarder/Favorites.java b/app/src/main/java/fr/neamar/kiss/forwarder/Favorites.java
index 6a88321c2..b9089fd59 100644
--- a/app/src/main/java/fr/neamar/kiss/forwarder/Favorites.java
+++ b/app/src/main/java/fr/neamar/kiss/forwarder/Favorites.java
@@ -174,13 +174,15 @@ void onFavoriteChange() {
                 if (result instanceof AppResult) {
                     String packageName = ((AppResult) result).getPackageName();
                     notificationDot.setTag(packageName);
-                    int primaryColor = UIColors.getPrimaryColor(mainActivity);
-                    notificationDot.setColorFilter(primaryColor);
                     if(notificationPrefs.contains(packageName)) {
                         notificationDot.setVisibility(View.VISIBLE);
+                        int primaryColor = UIColors.getPrimaryColor(mainActivity);
+                        notificationDot.setColorFilter(primaryColor);
                     }
                 } else {
+                    // Ensure view is clean (might have been recycled after a drag and drop)
                     notificationDot.setTag(null);
+                    notificationDot.setVisibility(View.INVISIBLE);
                 }
 
             }
diff --git a/app/src/main/java/fr/neamar/kiss/forwarder/Notification.java b/app/src/main/java/fr/neamar/kiss/forwarder/Notification.java
index 8d084770e..a3e516f21 100644
--- a/app/src/main/java/fr/neamar/kiss/forwarder/Notification.java
+++ b/app/src/main/java/fr/neamar/kiss/forwarder/Notification.java
@@ -29,6 +29,7 @@ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, Strin
             // This implementation should be more efficient than calling notifyDataSetInvalidated()
             // since we only iterate over the items currently displayed in the list
             // and do not rebuild them all, just toggle visibility if required.
+            // Also, it means we get to display an animation, and that's cool :D
             updateDots(list, list.getLastVisiblePosition() - list.getFirstVisiblePosition(), packageName);
 
             updateDots(mainActivity.favoritesBar, mainActivity.favoritesBar.getChildCount(), packageName);
diff --git a/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java b/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java
index b869ee137..a67ad3a44 100644
--- a/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java
+++ b/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java
@@ -1,10 +1,8 @@
 package fr.neamar.kiss.notification;
 
 import android.content.Context;
-import android.content.Intent;
 import android.content.SharedPreferences;
 import android.os.Build;
-import android.os.IBinder;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 
@@ -27,34 +25,34 @@ public void onCreate() {
         prefs = getBaseContext().getSharedPreferences(NOTIFICATION_PREFERENCES_NAME, Context.MODE_PRIVATE);
     }
 
-    @Override
-    public IBinder onBind(Intent intent) {
-        return super.onBind(intent);
-    }
-
     @Override
     public void onListenerConnected() {
         super.onListenerConnected();
 
-        // Build a map of notifications, ordered per package
+        // Build a map of notifications currently displayed,
+        // ordered per package
         StatusBarNotification[] sbns = getActiveNotifications();
         Map<String, Set<String>> notificationsByPackage = new HashMap<>();
-        for(StatusBarNotification sbn: sbns) {
+        for (StatusBarNotification sbn : sbns) {
             String packageName = sbn.getPackageName();
-            if(!notificationsByPackage.containsKey(packageName)) {
+            if (!notificationsByPackage.containsKey(packageName)) {
                 notificationsByPackage.put(packageName, new HashSet<String>());
             }
 
             notificationsByPackage.get(packageName).add(Integer.toString(sbn.getId()));
         }
 
-        // And synchronise this map with our sharedpreferences
+        // And synchronise this map with our SharedPreferences
+        // (an easier option would have been to .clear() the SharedPreferences,
+        // but then the listeners on SharedPreferences are not properly triggered)
         SharedPreferences.Editor editor = prefs.edit();
-        for(String packageName : prefs.getAll().keySet()) {
-            if(notificationsByPackage.containsKey(packageName)) {
+        // allKeys contains all the package names either in preferences or in the current notifications
+        Set<String> allKeys = prefs.getAll().keySet();
+        allKeys.addAll(notificationsByPackage.keySet());
+        for (String packageName : allKeys) {
+            if (notificationsByPackage.containsKey(packageName)) {
                 editor.putStringSet(packageName, notificationsByPackage.get(packageName));
-            }
-            else {
+            } else {
                 editor.remove(packageName);
             }
         }
@@ -77,11 +75,10 @@ public void onNotificationRemoved(StatusBarNotification sbn) {
         currentNotifications.remove(Integer.toString(sbn.getId()));
 
         SharedPreferences.Editor editor = prefs.edit();
-        if(currentNotifications.size() == 0) {
+        if (currentNotifications.isEmpty()) {
             // Clean up!
             editor.remove(sbn.getPackageName());
-        }
-        else {
+        } else {
             editor.putStringSet(sbn.getPackageName(), currentNotifications);
         }
         editor.apply();
@@ -94,7 +91,7 @@ public Set<String> getCurrentNotificationsForPackage(String packageName) {
         } else {
             // The set returned by getStringSet() should NOT be modified
             // see https://developer.android.com/reference/android/content/SharedPreferences.html#getStringSet(java.lang.String,%2520java.util.Set%3Cjava.lang.String%3E)
-           return new HashSet<>(currentNotifications);
+            return new HashSet<>(currentNotifications);
         }
     }
-}
\ No newline at end of file
+}

From e1c380c3b28e74ba655f023aaec6da5ecb203af2 Mon Sep 17 00:00:00 2001
From: Neamar <neamar@neamar.fr>
Date: Sun, 28 Apr 2019 22:49:57 +0100
Subject: [PATCH 18/30] Need to create a new set

---
 .../java/fr/neamar/kiss/notification/NotificationListener.java  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java b/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java
index a67ad3a44..8610c9d63 100644
--- a/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java
+++ b/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java
@@ -47,7 +47,7 @@ public void onListenerConnected() {
         // but then the listeners on SharedPreferences are not properly triggered)
         SharedPreferences.Editor editor = prefs.edit();
         // allKeys contains all the package names either in preferences or in the current notifications
-        Set<String> allKeys = prefs.getAll().keySet();
+        Set<String> allKeys = new HashSet<>(prefs.getAll().keySet());
         allKeys.addAll(notificationsByPackage.keySet());
         for (String packageName : allKeys) {
             if (notificationsByPackage.containsKey(packageName)) {

From d3e94afdd06097dab71ecb80c030cf50ed6b74d6 Mon Sep 17 00:00:00 2001
From: Neamar <neamar@neamar.fr>
Date: Tue, 30 Apr 2019 19:51:00 +0100
Subject: [PATCH 19/30] Added a log to debug some issue

---
 .../java/fr/neamar/kiss/notification/NotificationListener.java | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java b/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java
index 8610c9d63..f686c633b 100644
--- a/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java
+++ b/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java
@@ -5,6 +5,7 @@
 import android.os.Build;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
+import android.util.Log;
 
 import androidx.annotation.RequiresApi;
 
@@ -15,6 +16,7 @@
 
 @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
 public class NotificationListener extends NotificationListenerService {
+    public static final String TAG = "NotifListener";
     public static final String NOTIFICATION_PREFERENCES_NAME = "notifications";
 
     private SharedPreferences prefs;
@@ -28,6 +30,7 @@ public void onCreate() {
     @Override
     public void onListenerConnected() {
         super.onListenerConnected();
+        Log.i(TAG, "Notification listener connected");
 
         // Build a map of notifications currently displayed,
         // ordered per package

From 357655b45189fcb23f3bc6c0c2049ec171394412 Mon Sep 17 00:00:00 2001
From: Matthieu Bacconnier <neamar@neamar.fr>
Date: Sat, 4 May 2019 11:36:42 +0200
Subject: [PATCH 20/30] Ensure notification dot is cleaned up after a drag and
 drop operation

---
 app/src/main/java/fr/neamar/kiss/forwarder/Favorites.java | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/app/src/main/java/fr/neamar/kiss/forwarder/Favorites.java b/app/src/main/java/fr/neamar/kiss/forwarder/Favorites.java
index b9089fd59..dc724acd7 100644
--- a/app/src/main/java/fr/neamar/kiss/forwarder/Favorites.java
+++ b/app/src/main/java/fr/neamar/kiss/forwarder/Favorites.java
@@ -179,10 +179,13 @@ void onFavoriteChange() {
                         int primaryColor = UIColors.getPrimaryColor(mainActivity);
                         notificationDot.setColorFilter(primaryColor);
                     }
+                    else {
+                        notificationDot.setVisibility(View.GONE);
+                    }
                 } else {
                     // Ensure view is clean (might have been recycled after a drag and drop)
                     notificationDot.setTag(null);
-                    notificationDot.setVisibility(View.INVISIBLE);
+                    notificationDot.setVisibility(View.GONE);
                 }
 
             }

From dddcd2a84fe606e007fd2328b0a67d436911e7f2 Mon Sep 17 00:00:00 2001
From: Neamar <neamar@neamar.fr>
Date: Thu, 9 May 2019 22:04:12 +0200
Subject: [PATCH 21/30] Fix a bug where tinting might not have been applied
 correctly

---
 .../java/fr/neamar/kiss/forwarder/Favorites.java   | 14 +++++---------
 .../kiss/notification/NotificationListener.java    | 11 +++++++++++
 2 files changed, 16 insertions(+), 9 deletions(-)

diff --git a/app/src/main/java/fr/neamar/kiss/forwarder/Favorites.java b/app/src/main/java/fr/neamar/kiss/forwarder/Favorites.java
index dc724acd7..ce2d81c8f 100644
--- a/app/src/main/java/fr/neamar/kiss/forwarder/Favorites.java
+++ b/app/src/main/java/fr/neamar/kiss/forwarder/Favorites.java
@@ -169,19 +169,15 @@ void onFavoriteChange() {
                 favoriteImage.setImageResource(R.drawable.ic_launcher_white);
             }
 
-            if(notificationPrefs != null) {
+            if (notificationPrefs != null) {
                 ImageView notificationDot = favoriteView.findViewById(R.id.item_notification_dot);
+                int primaryColor = UIColors.getPrimaryColor(mainActivity);
+                notificationDot.setColorFilter(primaryColor);
+
                 if (result instanceof AppResult) {
                     String packageName = ((AppResult) result).getPackageName();
                     notificationDot.setTag(packageName);
-                    if(notificationPrefs.contains(packageName)) {
-                        notificationDot.setVisibility(View.VISIBLE);
-                        int primaryColor = UIColors.getPrimaryColor(mainActivity);
-                        notificationDot.setColorFilter(primaryColor);
-                    }
-                    else {
-                        notificationDot.setVisibility(View.GONE);
-                    }
+                    notificationDot.setVisibility(notificationPrefs.contains(packageName) ? View.VISIBLE : View.GONE);
                 } else {
                     // Ensure view is clean (might have been recycled after a drag and drop)
                     notificationDot.setTag(null);
diff --git a/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java b/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java
index f686c633b..585a10a04 100644
--- a/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java
+++ b/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java
@@ -63,12 +63,21 @@ public void onListenerConnected() {
         editor.apply();
     }
 
+    @Override
+    public void onListenerDisconnected() {
+        Log.i(TAG, "Notification listener disconnected");
+
+        super.onListenerDisconnected();
+    }
+
     @Override
     public void onNotificationPosted(StatusBarNotification sbn) {
         Set<String> currentNotifications = getCurrentNotificationsForPackage(sbn.getPackageName());
 
         currentNotifications.add(Integer.toString(sbn.getId()));
         prefs.edit().putStringSet(sbn.getPackageName(), currentNotifications).apply();
+
+        Log.v(TAG, "Added notification for " + sbn.getPackageName() + ": " + currentNotifications.toString());
     }
 
     @Override
@@ -85,6 +94,8 @@ public void onNotificationRemoved(StatusBarNotification sbn) {
             editor.putStringSet(sbn.getPackageName(), currentNotifications);
         }
         editor.apply();
+
+        Log.v(TAG, "Removed notification for " + sbn.getPackageName() + ": " + currentNotifications.toString());
     }
 
     public Set<String> getCurrentNotificationsForPackage(String packageName) {

From a40e595408efc27d90fe2aadee1234ef4e35ab02 Mon Sep 17 00:00:00 2001
From: Neamar <neamar@neamar.fr>
Date: Thu, 9 May 2019 22:22:10 +0200
Subject: [PATCH 22/30] Listener needs to be registered to onResume() and
 onPause()

Without this, the listener is sometimes disconnected without getting reconnected after.
---
 app/src/main/java/fr/neamar/kiss/MainActivity.java  | 13 ++++++++++---
 .../fr/neamar/kiss/forwarder/ForwarderManager.java  | 12 ++++++++----
 .../java/fr/neamar/kiss/forwarder/Notification.java |  5 +++--
 3 files changed, 21 insertions(+), 9 deletions(-)

diff --git a/app/src/main/java/fr/neamar/kiss/MainActivity.java b/app/src/main/java/fr/neamar/kiss/MainActivity.java
index 3a300506a..a1c838ce0 100644
--- a/app/src/main/java/fr/neamar/kiss/MainActivity.java
+++ b/app/src/main/java/fr/neamar/kiss/MainActivity.java
@@ -415,10 +415,11 @@ protected void onResume() {
         super.onResume();
     }
 
+
     @Override
-    protected void onDestroy() {
-        super.onDestroy();
-        this.unregisterReceiver(this.mReceiver);
+    protected void onPause() {
+        super.onPause();
+        forwarderManager.onPause();
     }
 
     @Override
@@ -427,6 +428,12 @@ protected void onStop() {
         forwarderManager.onStop();
     }
 
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        this.unregisterReceiver(this.mReceiver);
+    }
+
     @Override
     protected void onNewIntent(Intent intent) {
         // This is called when the user press Home again while already browsing MainActivity
diff --git a/app/src/main/java/fr/neamar/kiss/forwarder/ForwarderManager.java b/app/src/main/java/fr/neamar/kiss/forwarder/ForwarderManager.java
index e4c1457c1..0ab8408f1 100644
--- a/app/src/main/java/fr/neamar/kiss/forwarder/ForwarderManager.java
+++ b/app/src/main/java/fr/neamar/kiss/forwarder/ForwarderManager.java
@@ -7,6 +7,7 @@
 import android.view.View;
 
 import androidx.annotation.NonNull;
+
 import fr.neamar.kiss.MainActivity;
 
 public class ForwarderManager extends Forwarder {
@@ -41,24 +42,27 @@ public void onCreate() {
         interfaceTweaks.onCreate();
         experienceTweaks.onCreate();
         shortcutsForwarder.onCreate();
-        notificationForwarder.onCreate();
         tagsMenu.onCreate();
 
     }
 
+    public void onStart() {
+        widgetForwarder.onStart();
+    }
+
     public void onResume() {
         interfaceTweaks.onResume();
         experienceTweaks.onResume();
+        notificationForwarder.onResume();
         tagsMenu.onResume();
     }
 
-    public void onStart() {
-        widgetForwarder.onStart();
+    public void onPause() {
+        notificationForwarder.onPause();
     }
 
     public void onStop() {
         widgetForwarder.onStop();
-        notificationForwarder.onStop();
     }
 
     public void onActivityResult(int requestCode, int resultCode, Intent data) {
diff --git a/app/src/main/java/fr/neamar/kiss/forwarder/Notification.java b/app/src/main/java/fr/neamar/kiss/forwarder/Notification.java
index a3e516f21..5b5785746 100644
--- a/app/src/main/java/fr/neamar/kiss/forwarder/Notification.java
+++ b/app/src/main/java/fr/neamar/kiss/forwarder/Notification.java
@@ -4,6 +4,7 @@
 import android.animation.AnimatorListenerAdapter;
 import android.content.SharedPreferences;
 import android.os.Build;
+import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ListView;
@@ -46,13 +47,13 @@ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, Strin
         }
     }
 
-    void onCreate() {
+    void onResume() {
         if (notificationPreferences != null) {
             notificationPreferences.registerOnSharedPreferenceChangeListener(onNotificationDisplayed);
         }
     }
 
-    void onStop() {
+    void onPause() {
         if (notificationPreferences != null) {
             notificationPreferences.unregisterOnSharedPreferenceChangeListener(onNotificationDisplayed);
         }

From 54c2c06a1ae8b54a082d223d3351617d094d3a41 Mon Sep 17 00:00:00 2001
From: Neamar <neamar@neamar.fr>
Date: Thu, 9 May 2019 23:06:44 +0200
Subject: [PATCH 23/30] Cleanup

---
 app/src/main/java/fr/neamar/kiss/IconsHandler.java           | 1 +
 app/src/main/java/fr/neamar/kiss/RootHandler.java            | 2 +-
 app/src/main/java/fr/neamar/kiss/UIColors.java               | 4 +++-
 app/src/main/java/fr/neamar/kiss/ui/KeyboardScrollHider.java | 2 +-
 app/src/main/java/fr/neamar/kiss/utils/UserHandle.java       | 1 +
 5 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/app/src/main/java/fr/neamar/kiss/IconsHandler.java b/app/src/main/java/fr/neamar/kiss/IconsHandler.java
index 9ffa7d821..cc34d7d7a 100644
--- a/app/src/main/java/fr/neamar/kiss/IconsHandler.java
+++ b/app/src/main/java/fr/neamar/kiss/IconsHandler.java
@@ -203,6 +203,7 @@ private Drawable getDefaultAppDrawable(ComponentName componentName, UserHandle u
     /**
      * Get or generate icon for an app
      */
+    @SuppressWarnings("CatchAndPrintStackTrace")
     public Drawable getDrawableIconForPackage(ComponentName componentName, UserHandle userHandle) {
         // system icons, nothing to do
         if (iconsPackPackageName.equalsIgnoreCase("default")) {
diff --git a/app/src/main/java/fr/neamar/kiss/RootHandler.java b/app/src/main/java/fr/neamar/kiss/RootHandler.java
index 852baab81..96961eea4 100644
--- a/app/src/main/java/fr/neamar/kiss/RootHandler.java
+++ b/app/src/main/java/fr/neamar/kiss/RootHandler.java
@@ -57,7 +57,7 @@ private boolean executeRootShell(String command) {
                 p.getOutputStream().write((command + "\n").getBytes(UTF_8));
             }
             //exit from su command
-            p.getOutputStream().write(("exit\n").getBytes(UTF_8));
+            p.getOutputStream().write("exit\n".getBytes(UTF_8));
             p.getOutputStream().flush();
             p.getOutputStream().close();
             int result = p.waitFor();
diff --git a/app/src/main/java/fr/neamar/kiss/UIColors.java b/app/src/main/java/fr/neamar/kiss/UIColors.java
index e2488151a..3290a4eda 100644
--- a/app/src/main/java/fr/neamar/kiss/UIColors.java
+++ b/app/src/main/java/fr/neamar/kiss/UIColors.java
@@ -10,6 +10,8 @@
 import android.view.Window;
 import android.view.WindowManager;
 
+import androidx.annotation.NonNull;
+
 public class UIColors {
     public static final int COLOR_DEFAULT = 0xFF4caf50;
     // Source: https://material.io/guidelines/style/color.html#color-color-palette
@@ -60,7 +62,7 @@ public static int getPrimaryColor(Context context) {
             String primaryColorStr = PreferenceManager.getDefaultSharedPreferences(context).getString("primary-color", COLOR_DEFAULT_STR);
 
             // Transparent can't be displayed for text color, replace with light gray.
-            if (primaryColorStr.equals("#00000000") || primaryColorStr.equals(("#AAFFFFFF"))) {
+            if (primaryColorStr.equals("#00000000") || primaryColorStr.equals("#AAFFFFFF")) {
                 primaryColor = 0xFFBDBDBD;
             }
             else {
diff --git a/app/src/main/java/fr/neamar/kiss/ui/KeyboardScrollHider.java b/app/src/main/java/fr/neamar/kiss/ui/KeyboardScrollHider.java
index d6200c0db..8644df23f 100644
--- a/app/src/main/java/fr/neamar/kiss/ui/KeyboardScrollHider.java
+++ b/app/src/main/java/fr/neamar/kiss/ui/KeyboardScrollHider.java
@@ -169,7 +169,7 @@ public boolean onTouch(View v, MotionEvent event) {
                     animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                         @Override
                         public void onAnimationUpdate(ValueAnimator animator) {
-                            int height = (int) (animator.getAnimatedValue());
+                            int height = (int) animator.getAnimatedValue();
                             KeyboardScrollHider.this.setListLayoutHeight(height);
                         }
                     });
diff --git a/app/src/main/java/fr/neamar/kiss/utils/UserHandle.java b/app/src/main/java/fr/neamar/kiss/utils/UserHandle.java
index 5c43c1c5e..8564b1634 100644
--- a/app/src/main/java/fr/neamar/kiss/utils/UserHandle.java
+++ b/app/src/main/java/fr/neamar/kiss/utils/UserHandle.java
@@ -57,6 +57,7 @@ public String addUserSuffixToString(String base, char separator) {
         }
     }
 
+    @SuppressWarnings("CatchAndPrintStackTrace")
     public boolean hasStringUserSuffix(String string, char separator) {
         long serial = 0;
 

From c72ed8f28687dc673a4f55b689897cc402c8811e Mon Sep 17 00:00:00 2001
From: Neamar <neamar@neamar.fr>
Date: Fri, 10 May 2019 19:30:16 +0200
Subject: [PATCH 24/30] Clean up notifications when listener gets disconnected

Ensure we won't get out of sync with real notifications if system is low on memory, or just if the user removes permission to read notification.
---
 .../neamar/kiss/notification/NotificationListener.java | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java b/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java
index 585a10a04..0be25347d 100644
--- a/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java
+++ b/app/src/main/java/fr/neamar/kiss/notification/NotificationListener.java
@@ -67,6 +67,16 @@ public void onListenerConnected() {
     public void onListenerDisconnected() {
         Log.i(TAG, "Notification listener disconnected");
 
+        // Clean up everything we have in memory to ensure we don't keep displaying trailing dots.
+        // We don't use .clear() to ensure listeners are properly called.
+        SharedPreferences.Editor editor = prefs.edit();
+        Set<String> packages = prefs.getAll().keySet();
+
+        for (String packageName : packages) {
+            editor.remove(packageName);
+        }
+        editor.apply();
+
         super.onListenerDisconnected();
     }
 

From b8e0cc5b1114473838bbd6ae8a4dbb5fcadebf58 Mon Sep 17 00:00:00 2001
From: Unknown <thetbog@gmail.com>
Date: Tue, 14 May 2019 13:07:33 +0300
Subject: [PATCH 25/30] Simpler favorite_item

* RelativeLayout is expensive to inflate.
* Make the design view more informative using tools
---
 app/src/main/res/layout/favorite_item.xml | 21 +++++++++++----------
 1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/app/src/main/res/layout/favorite_item.xml b/app/src/main/res/layout/favorite_item.xml
index d2fd9990e..33a1faec4 100644
--- a/app/src/main/res/layout/favorite_item.xml
+++ b/app/src/main/res/layout/favorite_item.xml
@@ -1,27 +1,28 @@
 <?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="0px"
     android:layout_height="wrap_content"
     android:layout_weight="1"
     android:background="?attr/appSelectableItemBackground"
-    android:padding="@dimen/favorite_padding">
+    android:padding="@dimen/favorite_padding"
+    tools:layout_width="wrap_content">
+
     <ImageView
-        android:contentDescription="@null"
         android:id="@+id/favorite"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:contentDescription="@null"
         tools:src="@drawable/ic_contact" />
 
     <ImageView
         android:id="@+id/item_notification_dot"
         android:layout_width="14dp"
         android:layout_height="14dp"
-        android:layout_alignTop="@id/favorite"
-        android:layout_alignEnd="@id/favorite"
-        android:layout_alignRight="@id/favorite"
-        android:visibility="gone"
+        android:layout_gravity="end|top"
         android:contentDescription="@null"
-        android:src="@drawable/notification_dot" />
-</RelativeLayout>
+        android:src="@drawable/notification_dot"
+        android:visibility="gone"
+        tools:visibility="visible" />
+</FrameLayout>

From 7e2b8eeedb67de47d34231c0d7fac1ee6661e0ba Mon Sep 17 00:00:00 2001
From: Unknown <thetbog@gmail.com>
Date: Tue, 14 May 2019 15:18:21 +0300
Subject: [PATCH 26/30] Force the image to fill the available space

I had issues with drawable tags showing very small
---
 app/src/main/res/layout/favorite_item.xml | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/app/src/main/res/layout/favorite_item.xml b/app/src/main/res/layout/favorite_item.xml
index 33a1faec4..ab3662fb2 100644
--- a/app/src/main/res/layout/favorite_item.xml
+++ b/app/src/main/res/layout/favorite_item.xml
@@ -2,16 +2,16 @@
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="0px"
-    android:layout_height="wrap_content"
+    android:layout_height="match_parent"
     android:layout_weight="1"
     android:background="?attr/appSelectableItemBackground"
     android:padding="@dimen/favorite_padding"
-    tools:layout_width="wrap_content">
+    tools:layout_width="match_parent">
 
     <ImageView
         android:id="@+id/favorite"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
         android:layout_gravity="center"
         android:contentDescription="@null"
         tools:src="@drawable/ic_contact" />

From 480e6148d7019a1befdb427afd911646c4a7c938 Mon Sep 17 00:00:00 2001
From: Unknown <thetbog@gmail.com>
Date: Wed, 15 May 2019 10:51:00 +0300
Subject: [PATCH 27/30] fix NPE

---
 app/src/main/java/fr/neamar/kiss/forwarder/Notification.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/src/main/java/fr/neamar/kiss/forwarder/Notification.java b/app/src/main/java/fr/neamar/kiss/forwarder/Notification.java
index 5b5785746..28bdac767 100644
--- a/app/src/main/java/fr/neamar/kiss/forwarder/Notification.java
+++ b/app/src/main/java/fr/neamar/kiss/forwarder/Notification.java
@@ -63,7 +63,7 @@ private void updateDots(ViewGroup vg, int childCount, String packageName) {
         for (int i = 0; i < childCount; i++) {
             View v = vg.getChildAt(i);
             final View notificationDot = v.findViewById(R.id.item_notification_dot);
-            if (notificationDot != null && notificationDot.getTag().toString().equals(packageName)) {
+            if (notificationDot != null && packageName.equals(notificationDot.getTag())) {
                 boolean hasNotification = notificationPreferences.contains(packageName);
                 animateDot(notificationDot, hasNotification);
             }

From 6a038f9a0df4e78c6dedb28c302e19b3c2db2df8 Mon Sep 17 00:00:00 2001
From: Unknown <thetbog@gmail.com>
Date: Thu, 16 May 2019 15:42:19 +0300
Subject: [PATCH 28/30] Fix dot position

---
 .../main/res/drawable/notification_dot.xml    | 33 ++++++++++-----
 app/src/main/res/layout/favorite_item.xml     |  7 ++--
 app/src/main/res/layout/item_app.xml          | 42 +++++++++----------
 3 files changed, 47 insertions(+), 35 deletions(-)

diff --git a/app/src/main/res/drawable/notification_dot.xml b/app/src/main/res/drawable/notification_dot.xml
index 0b2d04f04..dd8c9c62e 100644
--- a/app/src/main/res/drawable/notification_dot.xml
+++ b/app/src/main/res/drawable/notification_dot.xml
@@ -1,12 +1,23 @@
 <?xml version="1.0" encoding="utf-8"?>
-<shape
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="oval">
-
-    <solid
-        android:color="#666666"/>
-
-    <size
-        android:width="8dp"
-        android:height="8dp"/>
-</shape>
\ No newline at end of file
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item>
+        <shape>
+            <solid android:color="@android:color/transparent" />
+            <size
+                android:width="48dp"
+                android:height="48dp" />
+        </shape>
+    </item>
+    <item
+        android:gravity="top|right"
+        android:right="2dp"
+        android:top="2dp">
+        <shape
+            android:shape="oval">
+            <solid android:color="#666666" />
+            <size
+                android:width="14dp"
+                android:height="14dp" />
+        </shape>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/app/src/main/res/layout/favorite_item.xml b/app/src/main/res/layout/favorite_item.xml
index ab3662fb2..34cb1fd3e 100644
--- a/app/src/main/res/layout/favorite_item.xml
+++ b/app/src/main/res/layout/favorite_item.xml
@@ -18,11 +18,12 @@
 
     <ImageView
         android:id="@+id/item_notification_dot"
-        android:layout_width="14dp"
-        android:layout_height="14dp"
-        android:layout_gravity="end|top"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_gravity="center"
         android:contentDescription="@null"
         android:src="@drawable/notification_dot"
+        android:tint="#FF4caf50"
         android:visibility="gone"
         tools:visibility="visible" />
 </FrameLayout>
diff --git a/app/src/main/res/layout/item_app.xml b/app/src/main/res/layout/item_app.xml
index 59b08bd4c..b367e8b94 100644
--- a/app/src/main/res/layout/item_app.xml
+++ b/app/src/main/res/layout/item_app.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
@@ -12,37 +12,37 @@
     android:paddingRight="@dimen/result_margin_right"
     android:paddingBottom="@dimen/result_margin_bottom">
 
-    <ImageView
-        android:id="@+id/item_app_icon"
+    <FrameLayout
         android:layout_width="48dp"
         android:layout_height="48dp"
-        android:layout_centerVertical="true"
+        android:layout_gravity="start|center_vertical"
         android:layout_marginStart="@dimen/icon_margin_left"
         android:layout_marginLeft="@dimen/icon_margin_left"
         android:layout_marginTop="@dimen/icon_margin_top"
         android:layout_marginEnd="@dimen/icon_margin_right"
         android:layout_marginRight="@dimen/icon_margin_right"
-        android:layout_marginBottom="@dimen/icon_margin_bottom"
-        android:contentDescription="@null"
-        tools:src="@drawable/ic_launcher" />
+        android:layout_marginBottom="@dimen/icon_margin_bottom">
+        <ImageView
+            android:id="@+id/item_app_icon"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:contentDescription="@null"
+            tools:src="@drawable/ic_launcher" />
 
-    <ImageView
-        android:id="@+id/item_notification_dot"
-        android:layout_width="14dp"
-        android:layout_height="14dp"
-        android:layout_alignTop="@id/item_app_icon"
-        android:layout_alignEnd="@id/item_app_icon"
-        android:layout_alignRight="@id/item_app_icon"
-        android:visibility="gone"
-        android:contentDescription="@null"
-        android:src="@drawable/notification_dot" />
+        <ImageView
+            android:id="@+id/item_notification_dot"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:visibility="gone"
+            tools:visibility="visible"
+            android:contentDescription="@null"
+            android:src="@drawable/notification_dot" />
+    </FrameLayout>
 
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:layout_centerVertical="true"
-        android:layout_toEndOf="@+id/item_app_icon"
-        android:layout_toRightOf="@+id/item_app_icon"
+        android:layout_gravity="start|center_vertical"
         android:gravity="center_vertical"
         android:orientation="vertical">
 
@@ -82,4 +82,4 @@
             tools:text="@string/stub_app_tag" />
 
     </LinearLayout>
-</RelativeLayout>
+</LinearLayout>

From f7bf9714a7ef06090f26aec986b07e2025ccc697 Mon Sep 17 00:00:00 2001
From: Matthieu Bacconnier <neamar@neamar.fr>
Date: Thu, 16 May 2019 23:21:43 +0200
Subject: [PATCH 29/30] Removing tint for performance

---
 app/src/main/res/layout/favorite_item.xml | 1 -
 1 file changed, 1 deletion(-)

diff --git a/app/src/main/res/layout/favorite_item.xml b/app/src/main/res/layout/favorite_item.xml
index 34cb1fd3e..4bc892932 100644
--- a/app/src/main/res/layout/favorite_item.xml
+++ b/app/src/main/res/layout/favorite_item.xml
@@ -23,7 +23,6 @@
         android:layout_gravity="center"
         android:contentDescription="@null"
         android:src="@drawable/notification_dot"
-        android:tint="#FF4caf50"
         android:visibility="gone"
         tools:visibility="visible" />
 </FrameLayout>

From 082aa85fb7da74879a0b907b9e3a8fe6a47b3b9c Mon Sep 17 00:00:00 2001
From: Neamar <neamar@neamar.fr>
Date: Sat, 18 May 2019 16:48:16 +0200
Subject: [PATCH 30/30] Use white as dot color when using the embedded favorite
 bar

---
 .../java/fr/neamar/kiss/forwarder/Favorites.java     | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/app/src/main/java/fr/neamar/kiss/forwarder/Favorites.java b/app/src/main/java/fr/neamar/kiss/forwarder/Favorites.java
index 2a071a348..44c5feaeb 100644
--- a/app/src/main/java/fr/neamar/kiss/forwarder/Favorites.java
+++ b/app/src/main/java/fr/neamar/kiss/forwarder/Favorites.java
@@ -7,6 +7,7 @@
 import android.content.pm.ResolveInfo;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
@@ -134,6 +135,14 @@ void onFavoriteChange() {
         favCount = favoritesPojo.size();
         LayoutInflater layoutInflater = null;
 
+        int dotColor;
+        if (isExternalFavoriteBarEnabled()) {
+            dotColor = UIColors.getPrimaryColor(mainActivity);
+        }
+        else {
+            dotColor = Color.WHITE;
+        }
+
         // Don't look for items after favIds length, we won't be able to display them
         for (int i = 0; i < favoritesPojo.size(); i++) {
             View favoriteView;
@@ -171,8 +180,7 @@ void onFavoriteChange() {
 
             if (notificationPrefs != null) {
                 ImageView notificationDot = favoriteView.findViewById(R.id.item_notification_dot);
-                int primaryColor = UIColors.getPrimaryColor(mainActivity);
-                notificationDot.setColorFilter(primaryColor);
+                notificationDot.setColorFilter(dotColor);
 
                 if (result instanceof AppResult) {
                     String packageName = ((AppResult) result).getPackageName();