Merge branch 'notification_logger'
This commit is contained in:
@@ -9,7 +9,7 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId "net.headlezz.notificationlogger"
|
||||
minSdkVersion 18
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 23
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
package="net.headlezz.notificationlogger">
|
||||
|
||||
<application
|
||||
android:name=".Application"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
@@ -14,15 +15,31 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name=".PreferenceActivity"
|
||||
<activity
|
||||
android:name=".PreferenceActivity"
|
||||
android:parentActivityName=".MainActivity" />
|
||||
|
||||
<receiver android:name=".createnotification.NotificationAlarmReceiver"
|
||||
<receiver
|
||||
android:name=".createnotification.NotificationAlarmReceiver"
|
||||
android:enabled="true">
|
||||
<intent-filter>
|
||||
<action android:name="net.headlezz.notificationbroadcast" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<service
|
||||
android:name=".logger.NotificationLoggerService"
|
||||
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
|
||||
<intent-filter>
|
||||
<action android:name="android.service.notification.NotificationListenerService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<provider
|
||||
android:authorities="net.headlezz.notificationlogger.logger.authority"
|
||||
android:name="net.headlezz.notificationlogger.logger.NotificationProvider"
|
||||
android:exported="true"
|
||||
/>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package net.headlezz.notificationlogger;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
public class Application extends android.app.Application {
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
Timber.plant(new Timber.DebugTree());
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,26 @@
|
||||
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.logger.LoggerUtils;
|
||||
import net.headlezz.notificationlogger.presenter.LoggerWarningPresenter;
|
||||
|
||||
import butterknife.Bind;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
public class MainActivity extends AppCompatActivity implements SharedPreferences.OnSharedPreferenceChangeListener, LoggerWarningPresenter.LoggerWarningView {
|
||||
|
||||
@Bind(R.id.toolbar)
|
||||
Toolbar mToolbar;
|
||||
@@ -23,14 +28,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) {
|
||||
@@ -48,10 +60,25 @@ public class MainActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
if(!LoggerUtils.isLoggerRunning())
|
||||
LoggerUtils.buildNotificationAccessDialog(this).show();
|
||||
if(getSupportFragmentManager().getBackStackEntryCount() > 0)
|
||||
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);
|
||||
@@ -93,4 +120,41 @@ public class MainActivity extends AppCompatActivity {
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package net.headlezz.notificationlogger.logger;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.service.notification.NotificationListenerService;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
|
||||
import net.headlezz.notificationlogger.PackageUtils;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
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;
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
isRunning = true;
|
||||
mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||
isEnabled = mSharedPrefs.getBoolean("pref_logging_enabled", true);
|
||||
mSharedPrefs.registerOnSharedPreferenceChangeListener(this);
|
||||
Timber.d(String.format("Logger started (running: %s, enabled: %s)", isRunning, isEnabled));
|
||||
return super.onBind(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onUnbind(Intent intent) {
|
||||
mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
|
||||
Timber.d("Logger stopped");
|
||||
return super.onUnbind(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotificationPosted(StatusBarNotification sbn) {
|
||||
super.onNotificationPosted(sbn);
|
||||
|
||||
if(!isEnabled) {
|
||||
Timber.d("Notification posted but logger is disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
LoggedNotification ln = buildLoggedNotification(sbn);
|
||||
getApplicationContext().getContentResolver().insert(
|
||||
Logged_notificationTable.CONTENT_URI,
|
||||
Logged_notificationTable.getContentValues(ln, false)
|
||||
);
|
||||
Timber.d(String.format("Notificaton logged (id: %d)", ln.notificationId));
|
||||
}
|
||||
|
||||
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) {
|
||||
Timber.w("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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
if(key.equals("pref_logging_enabled")) {
|
||||
isEnabled = sharedPreferences.getBoolean("pref_logging_enabled", true);
|
||||
Timber.d("Logging is now " + isEnabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/viewholder"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
@@ -3,6 +3,13 @@
|
||||
<string name="menu_item_settings">Settings</string>
|
||||
<string name="title_settings">Settings</string>
|
||||
|
||||
<string name="main_snack_logging_disabled_warning">Logging is disabled.</string>
|
||||
<string name="main_snack_logging_disabled_warning_action">Enable</string>
|
||||
<string name="main_dialog_enable_notification_access_title">Enable Notification Access</string>
|
||||
<string name="main_dialog_enable_notification_access_message">In order to log notifcations, this app needs access to your notification system.</string>
|
||||
<string name="main_dialog_enable_notification_access_positive">Open settings</string>
|
||||
<string name="main_dialog_enable_notification_access_negative">Cancel</string>
|
||||
|
||||
<string name="pref_category_notification_settings">Notification Settings</string>
|
||||
<string name="pref_category_support">Support</string>
|
||||
<string name="pref_logging_enabled_title">Notification Logging</string>
|
||||
@@ -13,6 +20,12 @@
|
||||
<string name="pref_clear_database_summary">Remove all logged notifications</string>
|
||||
<string name="pref_export_title">Export to sdcard</string>
|
||||
<string name="pref_about_title">About</string>
|
||||
<string name="pref_clear_database_dialog_title">Clear database</string>
|
||||
<string name="pref_clear_database_dialog_message">Delete all logged notifications?</string>
|
||||
<string name="pref_clear_database_dialog_negative">Cancel</string>
|
||||
<string name="pref_clear_database_dialog_positive">Yes</string>
|
||||
<string name="pref_clear_database_snackbar_cleared">Database cleared.</string>
|
||||
|
||||
<string name="fab_cd_create_notification">FAB: Create notification</string>
|
||||
<string name="title_create_notification">Create notification</string>
|
||||
<string name="title_blacklist">Blacklist</string>
|
||||
|
||||
Reference in New Issue
Block a user