diff --git a/res/layout/activity_notification_filter.xml b/res/layout/activity_notification_filter.xml index 9c2f64d5..2c178011 100644 --- a/res/layout/activity_notification_filter.xml +++ b/res/layout/activity_notification_filter.xml @@ -15,10 +15,18 @@ android:paddingBottom="5dp" android:id="@+id/tFilter"/> + + diff --git a/src/org/kde/kdeconnect/Helpers/StringsHelper.java b/src/org/kde/kdeconnect/Helpers/StringsHelper.java index 5e567855..2c00c9b2 100644 --- a/src/org/kde/kdeconnect/Helpers/StringsHelper.java +++ b/src/org/kde/kdeconnect/Helpers/StringsHelper.java @@ -6,4 +6,11 @@ public class StringsHelper { public static final Charset UTF8 = Charset.forName("UTF-8"); + public static int compare(String a, String b) { + if (a == b) return 0; + if (a == null) return -1; + if (b == null) return 1; + return a.compareToIgnoreCase(b); + } + } diff --git a/src/org/kde/kdeconnect/Plugins/NotificationsPlugin/AppDatabase.java b/src/org/kde/kdeconnect/Plugins/NotificationsPlugin/AppDatabase.java index 45bc9ab9..404199c8 100644 --- a/src/org/kde/kdeconnect/Plugins/NotificationsPlugin/AppDatabase.java +++ b/src/org/kde/kdeconnect/Plugins/NotificationsPlugin/AppDatabase.java @@ -26,26 +26,32 @@ import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; +import java.util.HashSet; + public class AppDatabase { - public static final String KEY_ROW_ID = "id"; - public static final String KEY_NAME = "app"; - public static final String KEY_PACKAGE_NAME = "packageName"; - public static final String KEY_IS_ENABLED = "isEnabled"; + static final private HashSet disabledByDefault = new HashSet<>(); + static { + disabledByDefault.add("com.android.messaging"); //We already have sms notifications in the telephony plugin + disabledByDefault.add("com.google.android.googlequicksearchbox"); //Google Now notifications re-spawn every few minutes + } - private static final String DATABASE_NAME = "Applications"; - private static final String DATABASE_TABLE = "Applications"; - private static final int DATABASE_VERSION = 1; + static final String KEY_PACKAGE_NAME = "packageName"; + static final String KEY_IS_ENABLED = "isEnabled"; - private final Context ourContext; - private SQLiteDatabase ourDatabase; - private DbHelper ourHelper; + static final String DATABASE_NAME = "Applications"; + static final String DATABASE_TABLE = "Applications"; + static final int DATABASE_VERSION = 2; + + final Context ourContext; + SQLiteDatabase ourDatabase; + DbHelper ourHelper; public AppDatabase(Context c) { ourContext = c; } - private static class DbHelper extends SQLiteOpenHelper{ + private static class DbHelper extends SQLiteOpenHelper { public DbHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); @@ -53,73 +59,57 @@ public class AppDatabase { @Override public void onCreate(SQLiteDatabase db) { - db.execSQL("CREATE TABLE " + DATABASE_TABLE + "(" + KEY_ROW_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," - + KEY_NAME + " TEXT NOT NULL, " + KEY_PACKAGE_NAME + " TEXT NOT NULL, " + KEY_IS_ENABLED + " TEXT NOT NULL); "); + db.execSQL("CREATE TABLE " + DATABASE_TABLE + "(" + KEY_PACKAGE_NAME + " TEXT PRIMARY KEY NOT NULL, " + KEY_IS_ENABLED + " TEXT NOT NULL); "); } @Override public void onUpgrade(SQLiteDatabase db, int i, int i2) { db.execSQL("DROP TABLE IF EXISTS "+ DATABASE_TABLE); onCreate(db); - } } - public void open(){ + public void open() { ourHelper = new DbHelper(ourContext); ourDatabase = ourHelper.getWritableDatabase(); } - - public void close(){ + public void close() { ourHelper.close(); } - public Cursor getAllApplications() - { - String[] columns = new String []{KEY_ROW_ID,KEY_NAME,KEY_PACKAGE_NAME,KEY_IS_ENABLED}; - Cursor res = ourDatabase.query(DATABASE_TABLE,columns,null,null,null,null,KEY_NAME); - return res; - } + public void setEnabled(String packageName, boolean isEnabled) { + String[] columns = new String []{KEY_IS_ENABLED}; + Cursor res = ourDatabase.query(DATABASE_TABLE, columns, KEY_PACKAGE_NAME + " =? ",new String[]{packageName},null,null,null); - public long create(String appName, String packageName, boolean isEnabled) { ContentValues cv = new ContentValues(); - cv.put(KEY_NAME, appName); - cv.put(KEY_PACKAGE_NAME, packageName); cv.put(KEY_IS_ENABLED, isEnabled?"true":"false"); - return ourDatabase.insert(DATABASE_TABLE, null, cv); - } - - public long update(String packageName, boolean isEnabled) { - ContentValues cvUpdate = new ContentValues(); - cvUpdate.put(KEY_IS_ENABLED, isEnabled?"true":"false"); - return ourDatabase.update(DATABASE_TABLE,cvUpdate,KEY_PACKAGE_NAME + "=?",new String[]{packageName}); - } - - public boolean exists(String packageName) { - String[] columns = new String []{KEY_ROW_ID}; - Cursor res = ourDatabase.query(DATABASE_TABLE,columns,KEY_PACKAGE_NAME + " =? ",new String[]{packageName},null,null,null); - int count = res.getCount(); + if (res.getCount() > 0) { + cv.put(KEY_PACKAGE_NAME, packageName); + ourDatabase.insert(DATABASE_TABLE, null, cv); + } else { + ourDatabase.update(DATABASE_TABLE, cv, KEY_PACKAGE_NAME + "=?",new String[]{packageName}); + } res.close(); - return (count != 0); } - public boolean isEnabled(String packageName){ + public boolean isEnabled(String packageName) { String[] columns = new String []{KEY_IS_ENABLED}; Cursor res = ourDatabase.query(DATABASE_TABLE,columns,KEY_PACKAGE_NAME + " =? ",new String[]{packageName},null,null,null); - boolean result = true; //Apps are enabled by default + boolean result; if (res.getCount() > 0) { res.moveToFirst(); result = (res.getString(res.getColumnIndex(KEY_IS_ENABLED))).equals("true"); + } else { + result = getDefaultStatus(packageName); } res.close(); return result; } - public void delete(String packageName){ - ourDatabase.delete(DATABASE_TABLE,KEY_PACKAGE_NAME + " =? ",new String[]{packageName} ); + private boolean getDefaultStatus(String packageName) { + return !disabledByDefault.contains(packageName); } - } diff --git a/src/org/kde/kdeconnect/Plugins/NotificationsPlugin/NotificationFilterActivity.java b/src/org/kde/kdeconnect/Plugins/NotificationsPlugin/NotificationFilterActivity.java index a355cbe7..6355ad3f 100644 --- a/src/org/kde/kdeconnect/Plugins/NotificationsPlugin/NotificationFilterActivity.java +++ b/src/org/kde/kdeconnect/Plugins/NotificationsPlugin/NotificationFilterActivity.java @@ -22,142 +22,144 @@ package org.kde.kdeconnect.Plugins.NotificationsPlugin; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; -import android.database.Cursor; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; import android.os.Bundle; import android.support.v7.app.ActionBarActivity; +import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import android.widget.AdapterView; -import android.widget.ArrayAdapter; +import android.widget.BaseAdapter; +import android.widget.CheckedTextView; import android.widget.ListView; import org.kde.kdeconnect.BackgroundService; +import org.kde.kdeconnect.Helpers.StringsHelper; import org.kde.kdeconnect_tp.R; +import java.util.Arrays; +import java.util.Comparator; import java.util.List; public class NotificationFilterActivity extends ActionBarActivity { - private AppDatabase appDatabase; + AppDatabase appDatabase; + + static class AppListInfo { + String pkg; + String name; + Drawable icon; + boolean isEnabled; + } + + AppListInfo[] apps; + + class AppListAdapter extends BaseAdapter { + + @Override + public int getCount() { + return apps.length; + } + + @Override + public AppListInfo getItem(int position) { + return apps[position]; + } + + @Override + public long getItemId(int position) { + return position; + } + + public View getView(int position, View view, ViewGroup parent) { + if (view == null) { + LayoutInflater inflater = getLayoutInflater(); + view = inflater.inflate(android.R.layout.simple_list_item_multiple_choice, null, true); + } + CheckedTextView checkedTextView = (CheckedTextView)view; + checkedTextView.setText(apps[position].name); + checkedTextView.setCompoundDrawablesWithIntrinsicBounds(apps[position].icon, null, null, null); + checkedTextView.setCompoundDrawablePadding((int)(8*getResources().getDisplayMetrics().density)); + + return view; + } + + } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_notification_filter); - final ListView listView = (ListView)findViewById(R.id.lvFilterApps); - appDatabase = new AppDatabase(this); + appDatabase = new AppDatabase(NotificationFilterActivity.this); - deleteUninstalledApps(); - addNewlyInstalledApps(); + new Thread(new Runnable() { + @Override + public void run() { - appDatabase.open(); - Cursor res = appDatabase.getAllApplications(); - res.moveToFirst(); + PackageManager packageManager = getPackageManager(); + List appList = packageManager.getInstalledApplications(0); + int count = appList.size(); - String[] appName = new String[res.getCount()]; - final String[] pkgName = new String[res.getCount()]; - Boolean[] isFiltered = new Boolean[res.getCount()]; + apps = new AppListInfo[count]; + appDatabase.open(); + for (int i = 0; i < count; i++) { + ApplicationInfo appInfo = appList.get(i); + apps[i] = new AppListInfo(); + apps[i].pkg = appInfo.packageName; + apps[i].name = appInfo.loadLabel(packageManager).toString(); + apps[i].icon = resizeIcon(appInfo.loadIcon(packageManager), 48); + apps[i].isEnabled = appDatabase.isEnabled(appInfo.packageName); + } + appDatabase.close(); - int i = 0; - while(!res.isAfterLast()){ - appName[i] = res.getString(res.getColumnIndex(AppDatabase.KEY_NAME)); - pkgName[i] = res.getString(res.getColumnIndex(AppDatabase.KEY_PACKAGE_NAME)); - isFiltered[i] = res.getString(res.getColumnIndex(AppDatabase.KEY_IS_ENABLED)).equals("true"); - res.moveToNext(); - i++; - } - res.close(); - appDatabase.close(); + Arrays.sort(apps, new Comparator() { + @Override + public int compare(AppListInfo lhs, AppListInfo rhs) { + return StringsHelper.compare(lhs.name, rhs.name); + } + }); - ArrayAdapter adapter = new ArrayAdapter<>(this, - android.R.layout.simple_list_item_multiple_choice, android.R.id.text1, appName); + runOnUiThread(new Runnable() { + @Override + public void run() { + displayAppList(); + } + }); + } + }).start(); + + } + + void displayAppList() { + + final ListView listView = (ListView) findViewById(R.id.lvFilterApps); + AppListAdapter adapter = new AppListAdapter(); listView.setAdapter(adapter); listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); - for (i = 0 ; i < isFiltered.length; i++){ - if (isFiltered[i]) { - listView.setItemChecked(i, true); - } - } listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView adapterView, View view, int i, long l) { boolean checked = listView.isItemChecked(i); - //Log.e("NotificationFilterActivity", pkgName[i] + ":" + checked); appDatabase.open(); - appDatabase.update(pkgName[i], checked); + appDatabase.setEnabled(apps[i].pkg, checked); appDatabase.close(); + apps[i].isEnabled = checked; } }); - } - - // Delete apps from database which are uninstalled - private void deleteUninstalledApps(){ - Cursor res; - appDatabase.open(); - res = appDatabase.getAllApplications(); - if (res != null) { - res.moveToFirst(); - while (!res.isAfterLast()) { - String packageName = res.getString(res.getColumnIndex(AppDatabase.KEY_PACKAGE_NAME)); - if (!isPackageInstalled(packageName)) { - appDatabase.delete(packageName); - } - res.moveToNext(); - } - res.close(); - } - appDatabase.close(); - - } - - // Adding newly installed apps in database - private void addNewlyInstalledApps() { - - List PackList = getPackageManager().getInstalledApplications(0); - appDatabase.open(); - - for (int i=0; i < PackList.size(); i++) - { - ApplicationInfo PackInfo = PackList.get(i); - - String appName = PackInfo.loadLabel(getPackageManager()).toString(); - String packageName = PackInfo.packageName; - - if ( (PackInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0 ) { - - if (!appDatabase.exists(packageName)) { - appDatabase.create(appName, packageName, true); - } - //Log.e("App FLAG_UPDATED_SYSTEM_APP: " + Integer.toString(i), appName); - - } else if ( (PackInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { - - //ignore these apps - - } else { - - if (!appDatabase.exists(packageName)) { - appDatabase.create(appName, packageName, true); - } - //Log.e("App : " + Integer.toString(i), appName); - - } + for (int i = 0 ; i < apps.length; i++) { + listView.setItemChecked(i, apps[i].isEnabled); } - appDatabase.close(); + listView.setVisibility(View.VISIBLE); + findViewById(R.id.spinner).setVisibility(View.GONE); } - private boolean isPackageInstalled(String packageName){ - PackageManager pm = getPackageManager(); - try { - pm.getPackageInfo(packageName, PackageManager.GET_META_DATA); - } catch (Exception e) { - return false; - } - return true; - } - @Override protected void onStart() { super.onStart(); @@ -170,4 +172,19 @@ public class NotificationFilterActivity extends ActionBarActivity { BackgroundService.removeGuiInUseCounter(this); } + Drawable resizeIcon(Drawable icon, int maxSize) { + Resources res = getResources(); + + //Convert to display pixels + maxSize = (int)(maxSize*res.getDisplayMetrics().density); + + Bitmap bitmap = Bitmap.createBitmap(maxSize, maxSize, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + icon.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + icon.draw(canvas); + + return new BitmapDrawable(res, bitmap); + + + } } diff --git a/src/org/kde/kdeconnect/Plugins/NotificationsPlugin/NotificationsPlugin.java b/src/org/kde/kdeconnect/Plugins/NotificationsPlugin/NotificationsPlugin.java index f30707f0..3feb2f36 100644 --- a/src/org/kde/kdeconnect/Plugins/NotificationsPlugin/NotificationsPlugin.java +++ b/src/org/kde/kdeconnect/Plugins/NotificationsPlugin/NotificationsPlugin.java @@ -170,11 +170,6 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver. return; } - if (packageName.equals("com.google.android.googlequicksearchbox")) { - //HACK: Hide Google Now notifications that keep constantly popping up (and without text because we don't know how to read them properly) - return; - } - NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_NOTIFICATION); if (packageName.equals("org.kde.kdeconnect_tp"))