backlist feature

This commit is contained in:
danijoo
2016-01-08 00:36:24 +01:00
parent 6884f1b396
commit e22c4d8cc1
10 changed files with 510 additions and 23 deletions

View File

@@ -0,0 +1,15 @@
package net.headlezz.notificationlogger.logger;
import ckm.simple.sql_provider.annotation.SimpleSQLColumn;
import ckm.simple.sql_provider.annotation.SimpleSQLTable;
@SimpleSQLTable(table="blacklist", provider="NotificationProvider")
public class BlacklistItem {
@SimpleSQLColumn("_id")
public long id;
@SimpleSQLColumn("package_name")
public String packageName;
}

View File

@@ -4,6 +4,7 @@ import android.app.Notification;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.Bundle;
import android.os.IBinder;
import android.preference.PreferenceManager;
@@ -49,19 +50,38 @@ public class NotificationLoggerService extends NotificationListenerService imple
public void onNotificationPosted(StatusBarNotification sbn) {
super.onNotificationPosted(sbn);
if(!isEnabled) {
if (!isEnabled) {
Timber.d("Notification posted but logger is disabled");
return;
}
LoggedNotification ln = buildLoggedNotification(sbn);
if (isBlacklisted(ln)) {
Timber.d("App blacklisted. Notification not logged");
} else {
getApplicationContext().getContentResolver().insert(
Logged_notificationTable.CONTENT_URI,
Logged_notificationTable.getContentValues(ln, false)
);
Timber.d(String.format("Notificaton logged (id: %d)", ln.notificationId));
}
}
private boolean isBlacklisted(LoggedNotification ln) {
Cursor cursor = getApplicationContext().getContentResolver().query(
BlacklistTable.CONTENT_URI,
null,
BlacklistTable.FIELD_PACKAGE_NAME + " = ?",
new String[] { String.valueOf(ln.packageName) },
null,
null
);
boolean isBlacklisted = cursor != null && cursor.getCount() > 0;
if(cursor != null)
cursor.close();
return isBlacklisted;
}
private LoggedNotification buildLoggedNotification(StatusBarNotification sbn) {
int notificationId = sbn.getId();
@@ -85,9 +105,9 @@ public class NotificationLoggerService extends NotificationListenerService imple
String title = extras.getString("android.title", "");
String message = extras.getCharSequence("android.text", "").toString();
if(message.isEmpty() && notification.tickerText != null)
if (message.isEmpty() && notification.tickerText != null)
message = notification.tickerText.toString();
if(message.isEmpty() && extras.containsKey("android.infoText"))
if (message.isEmpty() && extras.containsKey("android.infoText"))
message = extras.get("android.infoText").toString();
LoggedNotification ln = new LoggedNotification();
@@ -104,7 +124,7 @@ public class NotificationLoggerService extends NotificationListenerService imple
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if(key.equals("pref_logging_enabled")) {
if (key.equals("pref_logging_enabled")) {
isEnabled = sharedPreferences.getBoolean("pref_logging_enabled", true);
Timber.d("Logging is now " + isEnabled);
}

View File

@@ -0,0 +1,73 @@
package net.headlezz.notificationlogger.preferences;
import android.content.pm.PackageManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import net.headlezz.notificationlogger.PackageUtils;
import net.headlezz.notificationlogger.R;
import java.util.List;
import timber.log.Timber;
public class AppsAdapter extends BaseAdapter {
private List<String> mPackageNames;
class ViewHolder {
TextView tvName;
TextView tvPackageName;
ImageView ivIcon;
}
public AppsAdapter(List<String> packageNames) {
mPackageNames = packageNames;
}
@Override
public int getCount() {
return mPackageNames.size();
}
@Override
public Object getItem(int position) {
return mPackageNames.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if(convertView == null) {
convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.apps_adapter_view, parent, false);
viewHolder = new ViewHolder();
convertView.setTag(viewHolder);
viewHolder.ivIcon = (ImageView) convertView.findViewById(android.R.id.icon1);
viewHolder.tvName = (TextView) convertView.findViewById(android.R.id.text1);
viewHolder.tvPackageName = (TextView) convertView.findViewById(android.R.id.text2);
} else
viewHolder = (ViewHolder) convertView.getTag();
String packageName = (String) getItem(position);
viewHolder.tvPackageName.setText(packageName);
try {
viewHolder.tvName.setText(PackageUtils.getAppName(convertView.getContext(), packageName));
viewHolder.ivIcon.setImageDrawable(PackageUtils.getApplicationLauncherIcon(convertView.getContext(), packageName));
} catch (PackageManager.NameNotFoundException e) {
Timber.d("app name not found", e);
viewHolder.tvName.setText("");
viewHolder.ivIcon.setImageDrawable(null);
}
return convertView;
}
}

View File

@@ -1,13 +1,200 @@
package net.headlezz.notificationlogger.preferences;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.DialogInterface;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class BlacklistFragment extends Fragment {
import net.headlezz.notificationlogger.PackageUtils;
import net.headlezz.notificationlogger.R;
import net.headlezz.notificationlogger.logger.BlacklistItem;
import net.headlezz.notificationlogger.logger.BlacklistTable;
@SuppressWarnings("ConstantConditions")
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import butterknife.Bind;
import butterknife.ButterKnife;
import timber.log.Timber;
public class BlacklistFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor>, View.OnClickListener, BlacklistListAdapter.BlacklistCallbacks {
final int LOADER_ID = 1752;
@Bind(R.id.blacklistList_emptyView)
View mEmptyView;
@Bind(R.id.blacklistList_list)
RecyclerView mBlacklistList;
@Bind(R.id.blacklistList_fab)
FloatingActionButton mFab;
BlacklistListAdapter mAdapter;
@Nullable
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.blacklist_frag, container, false);
ButterKnife.bind(this, view);
mBlacklistList.setLayoutManager(new LinearLayoutManager(getContext()));
mFab.setOnClickListener(this);
return view;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mAdapter = new BlacklistListAdapter(getContext(), null, this);
mBlacklistList.setAdapter(mAdapter);
getLoaderManager().initLoader(LOADER_ID, Bundle.EMPTY, this);
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Timber.d("blacklistloader created");
return new CursorLoader(getContext(),
BlacklistTable.CONTENT_URI,
null,
null,
null,
BlacklistTable.FIELD_PACKAGE_NAME + " DESC");
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
Timber.d("blacklistloader finished: " + data.getCount() + " items.");
mAdapter.changeCursor(data);
checkEmpty();
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
Timber.d("blacklistloader reset");
mAdapter.changeCursor(null);
checkEmpty();
}
private void checkEmpty() {
if(mAdapter.getItemCount() == 0) {
mEmptyView.setVisibility(View.VISIBLE);
mBlacklistList.setVisibility(View.GONE);
} else {
mEmptyView.setVisibility(View.GONE);
mBlacklistList.setVisibility(View.VISIBLE);
}
}
@Override
public void onClick(View v) {
loadBlacklistPickerDialog();
}
private void loadBlacklistPickerDialog() {
// disable fab until the list is loaded
mFab.setEnabled(false);
new AsyncTask<Void, Void, List<String>>() {
@Override
protected List<String> doInBackground(Void... params) {
PackageManager pm = getContext().getPackageManager();
List<ApplicationInfo> installedApps = pm.getInstalledApplications(PackageManager.GET_META_DATA);
List<String> appPackages = new ArrayList<>();
for(ApplicationInfo appInfo : installedApps) {
appPackages.add(appInfo.packageName);
}
Collections.sort(appPackages, new Comparator<String>() {
@Override
public int compare(String lhs, String rhs) {
String lhsName, rhsName;
try {
lhsName = PackageUtils.getAppName(getContext(), lhs);
rhsName = PackageUtils.getAppName(getContext(), rhs);
} catch (PackageManager.NameNotFoundException e) {
return 0;
}
return lhsName.compareTo(rhsName);
}
});
return appPackages;
}
@Override
protected void onPostExecute(final List<String> packageNames) {
mFab.setEnabled(true);
new AlertDialog.Builder(getContext())
.setTitle(getContext().getString(R.string.blacklist_add_dialog_title))
.setAdapter(new AppsAdapter(packageNames), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String packageName = packageNames.get(which);
BlacklistItem item = new BlacklistItem();
item.packageName = packageName;
ContentResolver cr = getContext().getContentResolver();
Cursor cursor = cr.query(
BlacklistTable.CONTENT_URI,
null,
BlacklistTable.FIELD_PACKAGE_NAME + " = ?",
new String[] { packageName },
null,
null
);
boolean blacklisted = cursor.getCount() > 0;
cursor.close();
if(!blacklisted) {
getContext().getContentResolver().insert(
BlacklistTable.CONTENT_URI,
BlacklistTable.getContentValues(item, false)
);
}
cursor.close();
}
})
.create().show();
}
}.execute();
}
@Override
public void onBlacklistItemLongClicked(final BlacklistItem item) {
String appName;
try {
appName = PackageUtils.getAppName(getContext(), item.packageName);
} catch (PackageManager.NameNotFoundException e) {
appName = item.packageName;
}
new AlertDialog.Builder(getContext())
.setTitle(getContext().getString(R.string.blacklist_dialog_remove_title))
.setMessage(getContext().getString(R.string.blacklist_fragment_remove_message, appName))
.setPositiveButton(getContext().getString(R.string.ok), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
getContext().getContentResolver().delete(
BlacklistTable.CONTENT_URI,
BlacklistTable.FIELD_PACKAGE_NAME + " = ?",
new String[] {String.valueOf(item.packageName)}
);
}
})
.setNegativeButton(getContext().getString(R.string.cancel), null)
.create().show();
}
}

View File

@@ -0,0 +1,76 @@
package net.headlezz.notificationlogger.preferences;
import android.content.Context;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import net.headlezz.notificationlogger.PackageUtils;
import net.headlezz.notificationlogger.logger.BlacklistItem;
import net.headlezz.notificationlogger.logger.BlacklistTable;
import net.headlezz.notificationlogger.notificationlist.CursorRecyclerViewAdapter;
public class BlacklistListAdapter extends CursorRecyclerViewAdapter implements View.OnLongClickListener {
interface BlacklistCallbacks {
void onBlacklistItemLongClicked(BlacklistItem item);
}
class BlacklistViewHolder extends RecyclerView.ViewHolder {
TextView mPackageName;
TextView mAppName;
public BlacklistViewHolder(View itemView) {
super(itemView);
mAppName = (TextView) itemView.findViewById(android.R.id.text1);
mPackageName = (TextView) itemView.findViewById(android.R.id.text2);
}
public void setBlacklistItem(BlacklistItem item) {
mPackageName.setText(item.packageName);
String appName;
try {
appName = PackageUtils.getAppName(
itemView.getContext(),
item.packageName
);
} catch (PackageManager.NameNotFoundException e) {
appName = "";
}
mAppName.setText(appName);
itemView.setTag(item);
}
}
BlacklistCallbacks mCallbacks;
public BlacklistListAdapter(Context context, Cursor cursor, BlacklistCallbacks cb) {
super(context, cursor);
mCallbacks = cb;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, Cursor cursor) {
BlacklistItem item = BlacklistTable.getRow(cursor, false);
((BlacklistViewHolder) viewHolder).setBlacklistItem(item);
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(android.R.layout.simple_list_item_2, parent, false);
view.setOnLongClickListener(this);
return new BlacklistViewHolder(view);
}
@Override
public boolean onLongClick(View v) {
BlacklistItem item = (BlacklistItem) v.getTag();
mCallbacks.onBlacklistItemLongClicked(item);
return true;
}
}

View File

@@ -3,14 +3,17 @@ package net.headlezz.notificationlogger.preferences;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.DialogInterface;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Bundle;
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 android.text.Html;
import net.headlezz.notificationlogger.R;
import net.headlezz.notificationlogger.logger.BlacklistTable;
import net.headlezz.notificationlogger.logger.Logged_notificationTable;
public class PreferenceFragment extends PreferenceFragmentCompat implements Preference.OnPreferenceClickListener {
@@ -24,6 +27,7 @@ public class PreferenceFragment extends PreferenceFragmentCompat implements Pref
interface PreferenceCallbacks {
void showAboutScreen();
void showBlacklistScreen();
}
@@ -33,18 +37,47 @@ public class PreferenceFragment extends PreferenceFragmentCompat implements Pref
findPreference(PREF_ABOUT).setOnPreferenceClickListener(this);
findPreference(PREF_BLACKLIST).setOnPreferenceClickListener(this);
findPreference(PREF_CLEAR_DATABASE).setOnPreferenceClickListener(this);
setBlacklistItemCount();
}
private void setBlacklistItemCount() {
new AsyncTask<Void, Void, Integer>() {
@Override
protected Integer doInBackground(Void... params) {
Cursor cursor = getContext().getContentResolver().query(
BlacklistTable.CONTENT_URI,
null,
null,
null,
null
);
cursor.moveToFirst();
return cursor.getCount();
}
@Override
protected void onPostExecute(Integer blacklistItemCount) {
Preference blacklistPref = findPreference(PREF_BLACKLIST);
if (blacklistItemCount > 0)
blacklistPref.setSummary(Html.fromHtml(getString(R.string.pref_blacklist_summary_filled, blacklistItemCount)));
else
blacklistPref.setSummary(getString(R.string.pref_blacklist_summary_empty));
}
}.execute();
}
@Override
public boolean onPreferenceClick(Preference preference) {
String key = preference.getKey();
if(key.equals(PREF_ABOUT)) {
if (key.equals(PREF_ABOUT)) {
mCallbacks.showAboutScreen();
return true;
} else if(key.equals(PREF_BLACKLIST)) {
} else if (key.equals(PREF_BLACKLIST)) {
mCallbacks.showBlacklistScreen();
return true;
} else if(key.equals(PREF_CLEAR_DATABASE))
} else if (key.equals(PREF_CLEAR_DATABASE))
askClearDatabase();
return false;
}
@@ -77,7 +110,7 @@ public class PreferenceFragment extends PreferenceFragmentCompat implements Pref
@Override
protected void onPostExecute(Void aVoid) {
if(getActivity() != null)
if (getActivity() != null)
Snackbar.make(getActivity().findViewById(android.R.id.content), R.string.pref_clear_database_snackbar_cleared, Snackbar.LENGTH_SHORT).show();
}
}.execute();

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp">
<ImageView
android:id="@android:id/icon1"
android:layout_marginEnd="16dp"
tools:src="@mipmap/ic_launcher"
android:layout_width="46dp"
android:layout_height="48dp"
android:layout_alignBottom="@+id/include"
android:layout_alignParentTop="true" />
<LinearLayout
android:layout_gravity="center_vertical"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@android:id/text1"
android:layout_marginBottom="4dp"
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
tools:text="App name"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@android:id/text2"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
tools:text="Package name"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
tools:listitem="@android:layout/simple_list_item_2"
android:clipToPadding="false"
android:paddingBottom="56dp"
android:visibility="gone"
tools:visibility="visible"
android:id="@+id/blacklistList_list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView
android:id="@+id/blacklistList_emptyView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/blacklist_list_empty" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/blacklistList_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginBottom="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:contentDescription="@string/fab_cd_create_blacklist_item"
android:src="@drawable/ic_action_add"
app:fabSize="normal" />
</FrameLayout>

View File

@@ -15,21 +15,22 @@
<string name="main_notification_details_title"><b>Title:</b> %1$s</string>
<string name="main_notification_details_message"><b>Message:</b> %1$s</string>
<string name="main_notification_details_button_app_info">App Info</string>
<string name="main_notification_details_button_ok">OK</string>
<string name="main_notification_details_button_ok">@string/ok</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>
<string name="pref_logging_enabled_summary">Enable/Disable notification logging</string>
<string name="pref_blacklist_title">Blacklist</string>
<string name="pref_blacklist_summary">Exclude apps from being logged</string>
<string name="pref_blacklist_summary_empty">Exclude apps from being logged</string>
<string name="pref_blacklist_summary_filled"><b>%1$s</b> apps blacklisted.</string>
<string name="pref_clear_database_title">Clear database</string>
<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_negative">@string/cancel</string>
<string name="pref_clear_database_dialog_positive">Yes</string>
<string name="pref_clear_database_snackbar_cleared">Database cleared.</string>
@@ -57,13 +58,20 @@
<string name="notification_info_dialog_app_info_error">Failed to display app info.</string>
<string name="menu_item_filter">Filter</string>
<string name="menu_item_clear_filter">Clear filter</string>
<string name="filter_dialog_ok">OK</string>
<string name="filter_dialog_cancel">Cancel</string>
<string name="filter_dialog_ok">@string/ok</string>
<string name="filter_dialog_cancel">@string/cancel</string>
<string name="filter_dialog_title">Filter</string>
<string name="notification_filter_dialog_title">Title</string>
<string name="notification_filter_dialog_message">Message</string>
<string name="notification_filter_dialog_application_name">Application name</string>
<string name="notification_filter_dialog_package_name">Package name</string>
<string name="blacklist_list_empty">No application is blacklisted.</string>
<string name="fab_cd_create_blacklist_item">FAB: Create new blacklist item</string>
<string name="blacklist_add_dialog_title">Choose app to blacklist</string>
<string name="blacklist_dialog_remove_title">Remove from blacklist</string>
<string name="blacklist_fragment_remove_message">Remove %s from blacklist?</string>
<string name="ok">OK</string>
<string name="cancel">Cancel</string>
<string-array name="create_categories">
<item>Alarm</item>

View File

@@ -11,7 +11,7 @@
android:title="@string/pref_logging_enabled_title" />
<Preference
android:key="pref_blacklist"
android:summary="@string/pref_blacklist_summary"
android:summary="@string/pref_blacklist_summary_empty"
android:title="@string/pref_blacklist_title" />
<Preference
android:key="pref_clear_database"