From 5c625710a1ae3fb60bcb7c4d6e02ebe0b6dec214 Mon Sep 17 00:00:00 2001 From: danijoo Date: Sat, 2 Jan 2016 12:58:47 +0100 Subject: [PATCH 1/5] notificationlistenerservice --- app/build.gradle | 2 +- app/src/main/AndroidManifest.xml | 22 +++++- .../notificationlogger/MainActivity.java | 1 + .../notificationlogger/PackageUtils.java | 16 +++++ .../logger/LoggedNotification.java | 36 ++++++++++ .../logger/NotificationLoggerService.java | 69 +++++++++++++++++++ .../logger/NotificationProviderConfig.java | 18 +++++ 7 files changed, 160 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/net/headlezz/notificationlogger/PackageUtils.java create mode 100644 app/src/main/java/net/headlezz/notificationlogger/logger/LoggedNotification.java create mode 100644 app/src/main/java/net/headlezz/notificationlogger/logger/NotificationLoggerService.java create mode 100644 app/src/main/java/net/headlezz/notificationlogger/logger/NotificationProviderConfig.java diff --git a/app/build.gradle b/app/build.gradle index cadb5a5..46c9486 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,7 +9,7 @@ android { defaultConfig { applicationId "net.headlezz.notificationlogger" - minSdkVersion 18 + minSdkVersion 19 targetSdkVersion 23 versionCode 1 versionName "1.0" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 292c1b9..ca006b4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -14,15 +14,31 @@ - + - + + + + + + + + diff --git a/app/src/main/java/net/headlezz/notificationlogger/MainActivity.java b/app/src/main/java/net/headlezz/notificationlogger/MainActivity.java index e3f7d4b..169eff9 100644 --- a/app/src/main/java/net/headlezz/notificationlogger/MainActivity.java +++ b/app/src/main/java/net/headlezz/notificationlogger/MainActivity.java @@ -14,6 +14,7 @@ import net.headlezz.notificationlogger.createnotification.NotificationCreationFr import butterknife.Bind; import butterknife.ButterKnife; +import timber.log.Timber; public class MainActivity extends AppCompatActivity { diff --git a/app/src/main/java/net/headlezz/notificationlogger/PackageUtils.java b/app/src/main/java/net/headlezz/notificationlogger/PackageUtils.java new file mode 100644 index 0000000..0d32c08 --- /dev/null +++ b/app/src/main/java/net/headlezz/notificationlogger/PackageUtils.java @@ -0,0 +1,16 @@ +package net.headlezz.notificationlogger; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; + +public class PackageUtils { + + public static String getAppName(Context context, String packageName) throws PackageManager.NameNotFoundException { + PackageManager pm = context.getPackageManager(); + ApplicationInfo appInfo = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA); + return (String) pm.getApplicationLabel(appInfo); + } + + +} diff --git a/app/src/main/java/net/headlezz/notificationlogger/logger/LoggedNotification.java b/app/src/main/java/net/headlezz/notificationlogger/logger/LoggedNotification.java new file mode 100644 index 0000000..ac2d56c --- /dev/null +++ b/app/src/main/java/net/headlezz/notificationlogger/logger/LoggedNotification.java @@ -0,0 +1,36 @@ +package net.headlezz.notificationlogger.logger; + +import java.util.Date; + +import ckm.simple.sql_provider.annotation.SimpleSQLColumn; +import ckm.simple.sql_provider.annotation.SimpleSQLTable; + +@SimpleSQLTable(table="logged_notification", provider="NotificationProvider") +public class LoggedNotification { + + + @SimpleSQLColumn("title") + public String title; + + @SimpleSQLColumn("message") + public String message; + + @SimpleSQLColumn("date") + public Date date; + + @SimpleSQLColumn("app_name") + public String appName; + + @SimpleSQLColumn("package_name") + public String packageName; + + @SimpleSQLColumn("notification_id") + public int notificationId; + + @SimpleSQLColumn("user_id") + public int userId; + + @SimpleSQLColumn("small_icon_id") + public int smallIconId; + +} diff --git a/app/src/main/java/net/headlezz/notificationlogger/logger/NotificationLoggerService.java b/app/src/main/java/net/headlezz/notificationlogger/logger/NotificationLoggerService.java new file mode 100644 index 0000000..c2bee81 --- /dev/null +++ b/app/src/main/java/net/headlezz/notificationlogger/logger/NotificationLoggerService.java @@ -0,0 +1,69 @@ +package net.headlezz.notificationlogger.logger; + +import android.app.Notification; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.service.notification.NotificationListenerService; +import android.service.notification.StatusBarNotification; +import android.util.Log; + +import net.headlezz.notificationlogger.PackageUtils; + +import java.util.Date; + +public class NotificationLoggerService extends NotificationListenerService { + + public String TAG = NotificationLoggerService.class.getSimpleName(); + + @Override + public void onNotificationPosted(StatusBarNotification sbn) { + super.onNotificationPosted(sbn); + Log.d(TAG, "Notificaton logged (id: " + sbn.getId() + ")"); + + LoggedNotification ln = buildLoggedNotification(sbn); + getApplicationContext().getContentResolver().insert( + Logged_notificationTable.CONTENT_URI, + Logged_notificationTable.getContentValues(ln, false) + ); + } + + private LoggedNotification buildLoggedNotification(StatusBarNotification sbn) { + int notificationId = sbn.getId(); + + int userId = sbn.getUserId(); + Date date = new Date(sbn.getPostTime()); + String packageName = sbn.getPackageName(); + int iconId = sbn.getNotification().icon; + + String appName; + try { + appName = PackageUtils.getAppName(getApplicationContext(), packageName); + } catch (PackageManager.NameNotFoundException e) { + Log.w(TAG, "App name not found for " + packageName); + appName = ""; + } + + Notification notification = sbn.getNotification(); + Bundle extras = notification.extras; + + String title = extras.getString("android.title", ""); + String message = extras.getCharSequence("android.text", "").toString(); + + if(message.isEmpty() && notification.tickerText != null) + message = notification.tickerText.toString(); + if(message.isEmpty() && extras.containsKey("android.infoText")) + message = extras.get("android.infoText").toString(); + + LoggedNotification ln = new LoggedNotification(); + ln.title = title; + ln.message = message; + ln.date = date; + ln.appName = appName; + ln.packageName = packageName; + ln.notificationId = notificationId; + ln.userId = userId; + ln.smallIconId = iconId; + return ln; + } + +} diff --git a/app/src/main/java/net/headlezz/notificationlogger/logger/NotificationProviderConfig.java b/app/src/main/java/net/headlezz/notificationlogger/logger/NotificationProviderConfig.java new file mode 100644 index 0000000..ffd6084 --- /dev/null +++ b/app/src/main/java/net/headlezz/notificationlogger/logger/NotificationProviderConfig.java @@ -0,0 +1,18 @@ +package net.headlezz.notificationlogger.logger; + +import ckm.simple.sql_provider.UpgradeScript; +import ckm.simple.sql_provider.annotation.ProviderConfig; +import ckm.simple.sql_provider.annotation.SimpleSQLConfig; + +@SimpleSQLConfig( + name="NotificationProvider", + authority = "net.headlezz.notificationlogger.logger.authority", + database = "notifications.db", + version = 1 +) +public class NotificationProviderConfig implements ProviderConfig { + @Override + public UpgradeScript[] getUpdateScripts() { + return new UpgradeScript[0]; + } +} From 33d78ee5f1fbd30b626066fa7fb02038a9b01d4f Mon Sep 17 00:00:00 2001 From: danijoo Date: Sat, 2 Jan 2016 13:11:28 +0100 Subject: [PATCH 2/5] disable notification setting implemented --- app/src/main/AndroidManifest.xml | 1 + .../notificationlogger/Application.java | 12 +++++ .../logger/NotificationLoggerService.java | 48 +++++++++++++++++-- 3 files changed, 56 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/net/headlezz/notificationlogger/Application.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ca006b4..527db7a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,7 @@ package="net.headlezz.notificationlogger"> Date: Sat, 2 Jan 2016 14:05:41 +0100 Subject: [PATCH 3/5] mvp for logger warning --- .../notificationlogger/MainActivity.java | 74 +++++++++++++++++-- .../presenter/LoggerWarningPresenter.java | 23 ++++++ app/src/main/res/layout/main_activity.xml | 1 + app/src/main/res/values/strings.xml | 3 + 4 files changed, 94 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/net/headlezz/notificationlogger/presenter/LoggerWarningPresenter.java diff --git a/app/src/main/java/net/headlezz/notificationlogger/MainActivity.java b/app/src/main/java/net/headlezz/notificationlogger/MainActivity.java index 169eff9..a95025d 100644 --- a/app/src/main/java/net/headlezz/notificationlogger/MainActivity.java +++ b/app/src/main/java/net/headlezz/notificationlogger/MainActivity.java @@ -1,22 +1,25 @@ package net.headlezz.notificationlogger; import android.content.Intent; +import android.content.SharedPreferences; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.design.widget.FloatingActionButton; +import android.support.design.widget.Snackbar; import android.support.v7.app.AppCompatActivity; +import android.support.v7.preference.PreferenceManager; import android.support.v7.widget.Toolbar; import android.view.Menu; import android.view.MenuItem; import android.view.View; import net.headlezz.notificationlogger.createnotification.NotificationCreationFragment; +import net.headlezz.notificationlogger.presenter.LoggerWarningPresenter; import butterknife.Bind; import butterknife.ButterKnife; -import timber.log.Timber; -public class MainActivity extends AppCompatActivity { +public class MainActivity extends AppCompatActivity implements SharedPreferences.OnSharedPreferenceChangeListener, LoggerWarningPresenter.LoggerWarningView { @Bind(R.id.toolbar) Toolbar mToolbar; @@ -24,14 +27,21 @@ public class MainActivity extends AppCompatActivity { @Bind(R.id.main_menu_fab_add_notification) FloatingActionButton mFAB; + SharedPreferences mSharedPrefs; + + LoggerWarningPresenter mLoggerWarningPresenter; + Snackbar mLoggerWarningView; + @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main_activity); ButterKnife.bind(this); - setSupportActionBar(mToolbar); + mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); + mLoggerWarningPresenter = new LoggerWarningPresenter(this); + mFAB.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -39,7 +49,7 @@ public class MainActivity extends AppCompatActivity { } }); - if(getSupportFragmentManager().findFragmentById(R.id.main_menu_fragment_container) == null) { + if (getSupportFragmentManager().findFragmentById(R.id.main_menu_fragment_container) == null) { getSupportFragmentManager().beginTransaction() .add(R.id.main_menu_fragment_container, new NotificationListFragment()) .commit(); @@ -53,6 +63,19 @@ public class MainActivity extends AppCompatActivity { mFAB.setVisibility(View.GONE); } + @Override + protected void onStart() { + super.onStart(); + mSharedPrefs.registerOnSharedPreferenceChangeListener(this); + mLoggerWarningPresenter.setCurrentLoggerPreference(mSharedPrefs.getBoolean("pref_logging_enabled", true)); + } + + @Override + protected void onStop() { + super.onStop(); + mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this); + } + @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main_activity, menu); @@ -61,11 +84,11 @@ public class MainActivity extends AppCompatActivity { @Override public boolean onOptionsItemSelected(MenuItem item) { - if(item.getItemId() == R.id.menu_settings) { + if (item.getItemId() == R.id.menu_settings) { Intent intent = new Intent(this, PreferenceActivity.class); startActivity(intent); return true; - } else if(item.getItemId() == android.R.id.home) { + } else if (item.getItemId() == android.R.id.home) { onBackPressed(); } return super.onOptionsItemSelected(item); @@ -87,11 +110,48 @@ public class MainActivity extends AppCompatActivity { @Override public void onBackPressed() { // make fab visible and change title if we come back to first fragment - if(getSupportFragmentManager().getBackStackEntryCount() == 1) { + if (getSupportFragmentManager().getBackStackEntryCount() == 1) { mFAB.setVisibility(View.VISIBLE); getSupportActionBar().setTitle(getString(R.string.app_name)); getSupportActionBar().setDisplayHomeAsUpEnabled(false); } super.onBackPressed(); } + + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + if (key.equals("pref_logging_enabled")) + mLoggerWarningPresenter.setCurrentLoggerPreference(sharedPreferences.getBoolean("pref_logging_enabled", true)); + } + + @Override + public void showLoggerWarning() { + if (mLoggerWarningView == null) { + mLoggerWarningView = Snackbar.make(findViewById(R.id.viewholder), R.string.main_snack_logging_disabled_warning, Snackbar.LENGTH_INDEFINITE); + mLoggerWarningView.setAction(R.string.main_snack_logging_disabled_warning_action, new View.OnClickListener() { + @Override + public void onClick(View v) { + mSharedPrefs.edit().putBoolean("pref_logging_enabled", true).apply(); + } + }); + // fix for FAB not moving down after dismiss + mLoggerWarningView.getView().addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View v) { + } + + @Override + public void onViewDetachedFromWindow(View v) { + mFAB.setTranslationY(0); + } + }); + } + mLoggerWarningView.show(); + } + + @Override + public void hideLoggerWarning() { + if (mLoggerWarningView != null && mLoggerWarningView.isShownOrQueued()) + mLoggerWarningView.dismiss(); + } } diff --git a/app/src/main/java/net/headlezz/notificationlogger/presenter/LoggerWarningPresenter.java b/app/src/main/java/net/headlezz/notificationlogger/presenter/LoggerWarningPresenter.java new file mode 100644 index 0000000..3cfdd17 --- /dev/null +++ b/app/src/main/java/net/headlezz/notificationlogger/presenter/LoggerWarningPresenter.java @@ -0,0 +1,23 @@ +package net.headlezz.notificationlogger.presenter; + +public class LoggerWarningPresenter { + + public interface LoggerWarningView { + void showLoggerWarning(); + void hideLoggerWarning(); + } + + LoggerWarningView mView; + + public LoggerWarningPresenter(LoggerWarningView lwv) { + mView = lwv; + } + + public void setCurrentLoggerPreference(boolean loggingEnabled) { + if(loggingEnabled) + mView.hideLoggerWarning(); + else + mView.showLoggerWarning(); + } + +} diff --git a/app/src/main/res/layout/main_activity.xml b/app/src/main/res/layout/main_activity.xml index 12ae9a6..82e5d94 100644 --- a/app/src/main/res/layout/main_activity.xml +++ b/app/src/main/res/layout/main_activity.xml @@ -1,5 +1,6 @@ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1c84b09..50b4e71 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -3,6 +3,9 @@ Settings Settings + Logging is disabled. + Enable + Notification Settings Support Notification Logging From e91f4c86bce709902081a593074d30c20b416ef4 Mon Sep 17 00:00:00 2001 From: danijoo Date: Sat, 2 Jan 2016 14:40:06 +0100 Subject: [PATCH 4/5] dialog to enable notification access --- .../notificationlogger/MainActivity.java | 3 ++ .../logger/LoggerUtils.java | 36 +++++++++++++++++++ .../logger/NotificationLoggerService.java | 7 ++++ app/src/main/res/values/strings.xml | 4 +++ 4 files changed, 50 insertions(+) create mode 100644 app/src/main/java/net/headlezz/notificationlogger/logger/LoggerUtils.java diff --git a/app/src/main/java/net/headlezz/notificationlogger/MainActivity.java b/app/src/main/java/net/headlezz/notificationlogger/MainActivity.java index a95025d..0e0748c 100644 --- a/app/src/main/java/net/headlezz/notificationlogger/MainActivity.java +++ b/app/src/main/java/net/headlezz/notificationlogger/MainActivity.java @@ -14,6 +14,7 @@ import android.view.MenuItem; import android.view.View; import net.headlezz.notificationlogger.createnotification.NotificationCreationFragment; +import net.headlezz.notificationlogger.logger.LoggerUtils; import net.headlezz.notificationlogger.presenter.LoggerWarningPresenter; import butterknife.Bind; @@ -59,6 +60,8 @@ public class MainActivity extends AppCompatActivity implements SharedPreferences @Override protected void onResume() { super.onResume(); + if(!LoggerUtils.isLoggerRunning()) + LoggerUtils.buildNotificationAccessDialog(this).show(); if(getSupportFragmentManager().getBackStackEntryCount() > 0) mFAB.setVisibility(View.GONE); } diff --git a/app/src/main/java/net/headlezz/notificationlogger/logger/LoggerUtils.java b/app/src/main/java/net/headlezz/notificationlogger/logger/LoggerUtils.java new file mode 100644 index 0000000..1f64635 --- /dev/null +++ b/app/src/main/java/net/headlezz/notificationlogger/logger/LoggerUtils.java @@ -0,0 +1,36 @@ +package net.headlezz.notificationlogger.logger; + +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.support.v7.app.AlertDialog; + +import net.headlezz.notificationlogger.R; + +public class LoggerUtils { + + public static void openNotificationSystemSettings(Context context) { + Intent intent = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); + context.startActivity(intent); + } + + public static boolean isLoggerRunning() { + return NotificationLoggerService.isRunning; + } + + public static AlertDialog buildNotificationAccessDialog(final Context context) { + return new AlertDialog.Builder(context) + .setTitle(context.getString(R.string.main_dialog_enable_notification_access_title)) + .setMessage(context.getString(R.string.main_dialog_enable_notification_access_message)) + .setPositiveButton(R.string.main_dialog_enable_notification_access_positive, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + openNotificationSystemSettings(context); + } + }) + .setNegativeButton(R.string.main_dialog_enable_notification_access_negative, null) + .create(); + } + +} diff --git a/app/src/main/java/net/headlezz/notificationlogger/logger/NotificationLoggerService.java b/app/src/main/java/net/headlezz/notificationlogger/logger/NotificationLoggerService.java index 246f2c8..73d5238 100644 --- a/app/src/main/java/net/headlezz/notificationlogger/logger/NotificationLoggerService.java +++ b/app/src/main/java/net/headlezz/notificationlogger/logger/NotificationLoggerService.java @@ -18,7 +18,14 @@ import timber.log.Timber; public class NotificationLoggerService extends NotificationListenerService implements SharedPreferences.OnSharedPreferenceChangeListener { + /** + * true if logger is enabled in system settings + */ public static boolean isRunning = false; + + /** + * true if logger is enabled in app settings + */ public static boolean isEnabled = true; SharedPreferences mSharedPrefs; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 50b4e71..6167c86 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -5,6 +5,10 @@ Logging is disabled. Enable + Enable Notification Access + In order to log notifcations, this app needs access to your notification system. + Open settings + Cancel Notification Settings Support From 6b31036abdeb96d484695487454c1fab95293213 Mon Sep 17 00:00:00 2001 From: danijoo Date: Sat, 2 Jan 2016 15:00:10 +0100 Subject: [PATCH 5/5] clear database dialog --- .../PreferenceFragment.java | 30 +++++++++++++++++-- app/src/main/res/values/strings.xml | 6 ++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/net/headlezz/notificationlogger/PreferenceFragment.java b/app/src/main/java/net/headlezz/notificationlogger/PreferenceFragment.java index c5a7036..625bcd0 100644 --- a/app/src/main/java/net/headlezz/notificationlogger/PreferenceFragment.java +++ b/app/src/main/java/net/headlezz/notificationlogger/PreferenceFragment.java @@ -1,17 +1,23 @@ package net.headlezz.notificationlogger; import android.app.Activity; +import android.content.ContentResolver; +import android.content.DialogInterface; import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; +import android.support.design.widget.Snackbar; +import android.support.v7.app.AlertDialog; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceFragmentCompat; +import net.headlezz.notificationlogger.logger.Logged_notificationTable; + public class PreferenceFragment extends PreferenceFragmentCompat implements Preference.OnPreferenceClickListener { private PreferenceCallbacks mCallbacks; final String PREF_ABOUT = "pref_about"; final String PREF_BLACKLIST = "pref_blacklist"; + final String PREF_CLEAR_DATABASE = "pref_clear_database"; interface PreferenceCallbacks { @@ -24,6 +30,7 @@ public class PreferenceFragment extends PreferenceFragmentCompat implements Pref addPreferencesFromResource(R.xml.preferences); findPreference(PREF_ABOUT).setOnPreferenceClickListener(this); findPreference(PREF_BLACKLIST).setOnPreferenceClickListener(this); + findPreference(PREF_CLEAR_DATABASE).setOnPreferenceClickListener(this); } @Override @@ -35,10 +42,29 @@ public class PreferenceFragment extends PreferenceFragmentCompat implements Pref } else if(key.equals(PREF_BLACKLIST)) { mCallbacks.showBlacklistScreen(); return true; - } + } else if(key.equals(PREF_CLEAR_DATABASE)) + askClearDatabase(); return false; } + private void askClearDatabase() { + new AlertDialog.Builder(getContext()) + .setTitle(R.string.pref_clear_database_dialog_title) + .setMessage(R.string.pref_clear_database_dialog_message) + .setNegativeButton(R.string.pref_clear_database_dialog_negative, null) + .setPositiveButton(R.string.pref_clear_database_dialog_positive, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + ContentResolver resolver = getContext().getContentResolver(); + // there are no notifications with negative ids, so we use this to select all rows + String whereClause = Logged_notificationTable.FIELD_NOTIFICATION_ID + " != -1"; + resolver.delete(Logged_notificationTable.CONTENT_URI, whereClause, new String[0]); + Snackbar.make(getActivity().findViewById(android.R.id.content), R.string.pref_clear_database_snackbar_cleared, Snackbar.LENGTH_SHORT).show(); + } + }) + .create().show(); + } + @SuppressWarnings("ConstantConditions") @Override public void onAttach(Activity activity) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6167c86..501025c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -20,6 +20,12 @@ Remove all logged notifications Export to sdcard About + Clear database + Delete all logged notifications? + Cancel + Yes + Database cleared. + FAB: Create notification Create notification Blacklist