1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-11-27 12:26:48 +01:00

Apps Notification can now be configured to filter notification content based on black- and whitelists

Go to notification blacklist, allow an app if blacklisted, than configure it's behavior with the menu icon on the right hand side.
Should be pretty much self explanatory.

Database Scheme raised to 20
This commit is contained in:
abettenburg 2018-12-03 09:45:43 +01:00
parent a0e6ee490e
commit d6190e6e59
10 changed files with 579 additions and 24 deletions

View File

@ -45,7 +45,7 @@ public class GBDaoGenerator {
public static void main(String[] args) throws Exception {
Schema schema = new Schema(19, MAIN_PACKAGE + ".entities");
Schema schema = new Schema(20, MAIN_PACKAGE + ".entities");
Entity userAttributes = addUserAttributes(schema);
Entity user = addUserInfo(schema, userAttributes);
@ -75,6 +75,10 @@ public class GBDaoGenerator {
addCalendarSyncState(schema, device);
addAlarms(schema, user, device);
Entity notificationFilter = addNotificationFilters(schema);
addNotificationFilterEntry(schema, notificationFilter);
addBipActivitySummary(schema, user, device);
new DaoGenerator().generateAll(schema, "app/src/main/java");
@ -363,6 +367,30 @@ public class GBDaoGenerator {
alarm.addToOne(device, deviceId);
}
private static void addNotificationFilterEntry(Schema schema, Entity notificationFilterEntity) {
Entity notificatonFilterEntry = addEntity(schema, "NotificationFilterEntry");
notificatonFilterEntry.addIdProperty().autoincrement();
Property notificationFilterId = notificatonFilterEntry.addLongProperty("notificationFilterId").notNull().getProperty();
notificatonFilterEntry.addStringProperty("notificationFilterContent").notNull().getProperty();
notificatonFilterEntry.addToOne(notificationFilterEntity, notificationFilterId);
}
private static Entity addNotificationFilters(Schema schema) {
Entity notificatonFilter = addEntity(schema, "NotificationFilter");
Property appIdentifier = notificatonFilter.addStringProperty("appIdentifier").notNull().getProperty();
notificatonFilter.addIdProperty().autoincrement();
Index indexUnique = new Index();
indexUnique.addProperty(appIdentifier);
indexUnique.makeUnique();
notificatonFilter.addIndex(indexUnique);
Property notificationFilterMode = notificatonFilter.addIntProperty("notificationFilterMode").notNull().getProperty();
Property notificationFilterSubMode = notificatonFilter.addIntProperty("notificationFilterSubMode").notNull().getProperty();
return notificatonFilter;
}
private static void addBipActivitySummary(Schema schema, Entity user, Entity device) {
Entity summary = addEntity(schema, "BaseActivitySummary");
summary.implementsInterface(ACTIVITY_SUMMARY);

View File

@ -60,6 +60,7 @@ pmd {
dependencies {
// testImplementation "ch.qos.logback:logback-classic:1.1.3"
// testImplementation "ch.qos.logback:logback-core:1.1.3"
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation "junit:junit:4.12"
testImplementation "org.mockito:mockito-core:1.10.19"
testImplementation "org.robolectric:robolectric:3.6.1"

View File

@ -405,12 +405,17 @@
<activity
android:name=".activities.AlarmDetails"
android:label="@string/title_activity_alarm_details"
android:screenOrientation="portrait"
android:parentActivityName=".activities.ConfigureAlarms" />
android:parentActivityName=".activities.ConfigureAlarms"
android:screenOrientation="portrait" />
<activity
android:name=".activities.VibrationActivity"
android:label="@string/title_activity_vibration"
android:parentActivityName=".activities.ControlCenterv2" />
<activity
android:name=".activities.NotificationFilterActivity"
android:label="@string/title_activity_notification_filter"
android:windowSoftInputMode="stateHidden"
android:parentActivityName=".activities.AppBlacklistActivity" />
<activity
android:name=".activities.FindPhoneActivity"
android:label="Find Phone" />

View File

@ -0,0 +1,206 @@
package nodomain.freeyourgadget.gadgetbridge.activities;
import android.os.Bundle;
import android.view.View;
import android.widget.*;
import de.greenrobot.dao.query.Query;
import nodomain.freeyourgadget.gadgetbridge.BuildConfig;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.adapter.AppBlacklistAdapter;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.entities.NotificationFilter;
import nodomain.freeyourgadget.gadgetbridge.entities.NotificationFilterDao;
import nodomain.freeyourgadget.gadgetbridge.entities.NotificationFilterEntry;
import nodomain.freeyourgadget.gadgetbridge.entities.NotificationFilterEntryDao;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
public class NotificationFilterActivity extends AbstractGBActivity {
private static final String TAG = NotificationFilterActivity.class.getName();
public static final int NOTIFICATION_FILTER_MODE_NONE = 0;
public static final int NOTIFICATION_FILTER_MODE_WHITELIST = 1;
public static final int NOTIFICATION_FILTER_MODE_BLACKLIST = 2;
public static final int NOTIFICATION_FILTER_SUBMODE_ANY = 0;
public static final int NOTIFICATION_FILTER_SUBMODE_ALL = 1;
private Button mButtonSave;
private Spinner mSpinnerFilterMode;
private Spinner mSpinnerFilterSubMode;
private NotificationFilter mNotificationFilter;
private EditText mEditTextWords;
private DBHandler db = null;
private List<String> mWordsList = new ArrayList<>();
private List<Long> mFilterEntryIds = new ArrayList<>();
private static final Logger LOG = LoggerFactory.getLogger(NotificationFilterActivity.class);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_notification_filter);
String packageName = getIntent().getStringExtra(AppBlacklistAdapter.STRING_EXTRA_PACKAGE_NAME);
if (StringUtils.isBlank(packageName)) {
this.finish();
}
try {
db = GBApplication.acquireDB();
} catch (GBException e) {
LOG.error("Could not acquire DB.", e);
this.finish();
}
NotificationFilterDao notificationFilterDao = db.getDaoSession().getNotificationFilterDao();
NotificationFilterEntryDao notificationFilterEntryDao = db.getDaoSession().getNotificationFilterEntryDao();
Query<NotificationFilter> query = notificationFilterDao.queryBuilder().where(NotificationFilterDao.Properties.AppIdentifier.eq(packageName)).build();
mNotificationFilter = query.unique();
if (mNotificationFilter == null) {
mNotificationFilter = new NotificationFilter();
mNotificationFilter.setAppIdentifier(packageName);
LOG.debug("New Notification Filter");
} else {
LOG.debug("Loaded existing notification filter");
Query<NotificationFilterEntry> queryEntries = notificationFilterEntryDao.queryBuilder().where(NotificationFilterEntryDao.Properties.NotificationFilterId.eq(mNotificationFilter.getId())).build();
List<NotificationFilterEntry> filterEntries = queryEntries.list();
if (!filterEntries.isEmpty()) {
for (NotificationFilterEntry temp : filterEntries) {
mWordsList.add(temp.getNotificationFilterContent());
mFilterEntryIds.add(temp.getId());
LOG.debug("Loaded filter word: " + temp.getNotificationFilterContent());
}
}
}
setupView();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (db != null) {
GBApplication.releaseDB();
}
}
private void setupView() {
mSpinnerFilterMode = findViewById(R.id.spinnerFilterMode);
mSpinnerFilterMode.setSelection(mNotificationFilter.getNotificationFilterMode());
mSpinnerFilterSubMode = findViewById(R.id.spinnerSubMode);
mSpinnerFilterMode.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int pos, long id) {
switch (pos) {
case NOTIFICATION_FILTER_MODE_NONE:
mEditTextWords.setEnabled(false);
mSpinnerFilterSubMode.setEnabled(false);
break;
case NOTIFICATION_FILTER_MODE_BLACKLIST:
case NOTIFICATION_FILTER_MODE_WHITELIST:
mEditTextWords.setEnabled(true);
mSpinnerFilterSubMode.setEnabled(true);
break;
}
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
mSpinnerFilterSubMode.setSelection(mNotificationFilter.getNotificationFilterSubMode());
mEditTextWords = findViewById(R.id.editTextWords);
if (!mWordsList.isEmpty()) {
StringBuilder builder = new StringBuilder();
for (String temp : mWordsList) {
builder.append(temp);
builder.append("\n");
}
mEditTextWords.setText(builder.toString());
}
mEditTextWords.setEnabled(mSpinnerFilterMode.getSelectedItemPosition() == NOTIFICATION_FILTER_MODE_NONE);
mButtonSave = findViewById(R.id.buttonSaveFilter);
mButtonSave.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// TODO: check for modifications, only save if something changed
String words = mEditTextWords.getText().toString();
if (StringUtils.isBlank(words) && mSpinnerFilterMode.getSelectedItemPosition() != NOTIFICATION_FILTER_MODE_NONE) {
Toast.makeText(NotificationFilterActivity.this, R.string.toast_notification_filter_words_empty_hint, Toast.LENGTH_SHORT).show();
return;
}
try {
db = GBApplication.acquireDB();
NotificationFilterDao notificationFilterDao = db.getDaoSession().getNotificationFilterDao();
NotificationFilterEntryDao notificationFilterEntryDao = db.getDaoSession().getNotificationFilterEntryDao();
debugOutput(notificationFilterDao);
mNotificationFilter.setNotificationFilterMode(mSpinnerFilterMode.getSelectedItemPosition());
mNotificationFilter.setNotificationFilterSubMode(mSpinnerFilterSubMode.getSelectedItemPosition());
notificationFilterEntryDao.deleteByKeyInTx(mFilterEntryIds);
Long filterId = notificationFilterDao.insertOrReplace(mNotificationFilter);
// only save words if filter mode != none
if (mNotificationFilter.getNotificationFilterMode() != NOTIFICATION_FILTER_MODE_NONE) {
String[] wordsSplitted = words.split("\n");
for (String temp : wordsSplitted) {
temp = temp.trim();
NotificationFilterEntry notificationFilterEntry = new NotificationFilterEntry();
notificationFilterEntry.setNotificationFilterContent(temp);
notificationFilterEntry.setNotificationFilterId(filterId);
notificationFilterEntryDao.insert(notificationFilterEntry);
}
}
Toast.makeText(NotificationFilterActivity.this, R.string.toast_notification_filter_saved_successfully, Toast.LENGTH_SHORT).show();
NotificationFilterActivity.this.finish();
} catch (GBException e) {
LOG.error("Could not acquire DB.", e);
Toast.makeText(NotificationFilterActivity.this, "Database Error: " + e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
});
}
private void debugOutput(NotificationFilterDao notificationFilterDao) {
if (BuildConfig.DEBUG) {
List<NotificationFilter> filters = notificationFilterDao.loadAll();
LOG.info(TAG, "Saved filters");
for (NotificationFilter temp : filters) {
LOG.info(TAG, "Filter: " + temp.getId() + " " + temp.getAppIdentifier());
}
}
}
}

View File

@ -17,6 +17,7 @@
package nodomain.freeyourgadget.gadgetbridge.adapter;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.support.v7.widget.RecyclerView;
@ -40,11 +41,14 @@ import java.util.Set;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.NotificationFilterActivity;
import static nodomain.freeyourgadget.gadgetbridge.GBApplication.packageNameToPebbleMsgSender;
public class AppBlacklistAdapter extends RecyclerView.Adapter<AppBlacklistAdapter.AppBLViewHolder> implements Filterable {
public static final String STRING_EXTRA_PACKAGE_NAME = "packageName";
private List<ApplicationInfo> applicationInfoList;
private final int mLayoutId;
private final Context mContext;
@ -92,7 +96,7 @@ public class AppBlacklistAdapter extends RecyclerView.Adapter<AppBlacklistAdapte
}
@Override
public void onBindViewHolder(AppBlacklistAdapter.AppBLViewHolder holder, int position) {
public void onBindViewHolder(final AppBlacklistAdapter.AppBLViewHolder holder, int position) {
final ApplicationInfo appInfo = applicationInfoList.get(position);
holder.deviceAppVersionAuthorLabel.setText(appInfo.packageName);
@ -117,7 +121,7 @@ public class AppBlacklistAdapter extends RecyclerView.Adapter<AppBlacklistAdapte
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
CheckedTextView checkBox = ((CheckedTextView) v.findViewById(R.id.item_checkbox));
CheckedTextView checkBox = (v.findViewById(R.id.item_checkbox));
checkBox.toggle();
if (checkBox.isChecked()) {
GBApplication.addAppToNotifBlacklist(appInfo.packageName);
@ -130,10 +134,16 @@ public class AppBlacklistAdapter extends RecyclerView.Adapter<AppBlacklistAdapte
holder.btnConfigureApp.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(mContext, "Configure clicked for: " + mNameMap.get(appInfo), Toast.LENGTH_SHORT).show();
if (holder.blacklist_checkbox.isChecked()) {
Toast.makeText(mContext, R.string.toast_app_must_not_be_blacklisted, Toast.LENGTH_SHORT).show();
} else {
Intent intentStartNotificationFilterActivity = new Intent(mContext, NotificationFilterActivity.class);
intentStartNotificationFilterActivity.putExtra(STRING_EXTRA_PACKAGE_NAME, appInfo.packageName);
mContext.startActivity(intentStartNotificationFilterActivity);
}
}
});
}
public void blacklistAllNotif() {

View File

@ -38,6 +38,7 @@ import android.os.PowerManager;
import android.os.RemoteException;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.support.annotation.NonNull;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.RemoteInput;
import android.support.v4.content.LocalBroadcastManager;
@ -46,7 +47,24 @@ import android.support.v4.media.session.MediaControllerCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import android.support.v7.graphics.Palette;
import de.greenrobot.dao.query.Query;
import nodomain.freeyourgadget.gadgetbridge.BuildConfig;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleColor;
import nodomain.freeyourgadget.gadgetbridge.entities.NotificationFilter;
import nodomain.freeyourgadget.gadgetbridge.entities.NotificationFilterDao;
import nodomain.freeyourgadget.gadgetbridge.entities.NotificationFilterEntry;
import nodomain.freeyourgadget.gadgetbridge.entities.NotificationFilterEntryDao;
import nodomain.freeyourgadget.gadgetbridge.model.*;
import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService;
import nodomain.freeyourgadget.gadgetbridge.util.BitmapUtil;
import nodomain.freeyourgadget.gadgetbridge.util.LimitedQueue;
import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -55,21 +73,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleColor;
import nodomain.freeyourgadget.gadgetbridge.model.AppNotificationType;
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService;
import nodomain.freeyourgadget.gadgetbridge.util.BitmapUtil;
import nodomain.freeyourgadget.gadgetbridge.util.LimitedQueue;
import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
import static android.support.v4.media.app.NotificationCompat.MediaStyle.getMediaSession;
import static nodomain.freeyourgadget.gadgetbridge.activities.NotificationFilterActivity.*;
public class NotificationListener extends NotificationListenerService {
@ -290,6 +295,11 @@ public class NotificationListener extends NotificationListenerService {
dissectNotificationTo(notification, notificationSpec, preferBigText);
if (!checkNotificationContentForWhiteAndBlackList(sbn.getPackageName().toLowerCase(), notificationSpec.body)) {
return;
}
// ignore Gadgetbridge's very own notifications, except for those from the debug screen
if (getApplicationContext().getPackageName().equals(source)) {
if (!getApplicationContext().getString(R.string.test_notification).equals(notificationSpec.title)) {
@ -351,12 +361,109 @@ public class NotificationListener extends NotificationListenerService {
GBApplication.deviceService().onNotification(notificationSpec);
}
private boolean checkNotificationContentForWhiteAndBlackList(String packageName, String body) {
long start = System.currentTimeMillis();
DBHandler db;
try {
db = GBApplication.acquireDB();
} catch (GBException e) {
LOG.error("Could not acquire DB.", e);
return true;
}
List<String> wordsList = new ArrayList<>();
NotificationFilterDao notificationFilterDao = db.getDaoSession().getNotificationFilterDao();
NotificationFilterEntryDao notificationFilterEntryDao = db.getDaoSession().getNotificationFilterEntryDao();
Query<NotificationFilter> query = notificationFilterDao.queryBuilder().where(NotificationFilterDao.Properties.AppIdentifier.eq(packageName)).build();
NotificationFilter notificationFilter = query.unique();
if (notificationFilter == null) {
LOG.debug("No Notification Filter found");
return true;
}
LOG.debug("Loaded notification filter for '{}'", packageName);
Query<NotificationFilterEntry> queryEntries = notificationFilterEntryDao.queryBuilder().where(NotificationFilterEntryDao.Properties.NotificationFilterId.eq(notificationFilter.getId())).build();
List<NotificationFilterEntry> filterEntries = queryEntries.list();
if (BuildConfig.DEBUG) {
LOG.info("Database lookup took '{}' ms", System.currentTimeMillis() - start);
}
if (!filterEntries.isEmpty()) {
for (NotificationFilterEntry temp : filterEntries) {
wordsList.add(temp.getNotificationFilterContent());
LOG.debug("Loaded filter word: " + temp.getNotificationFilterContent());
}
}
return shouldContinueAfterFilter(body, wordsList, notificationFilter);
}
boolean shouldContinueAfterFilter(@NonNull String body, @NonNull List<String> wordsList, @NonNull NotificationFilter notificationFilter) {
LOG.debug("Mode: '{}' Submode: '{}' WordsList: '{}'", notificationFilter.getNotificationFilterMode(), notificationFilter.getNotificationFilterSubMode(), wordsList);
boolean allMode = notificationFilter.getNotificationFilterSubMode() == NOTIFICATION_FILTER_SUBMODE_ALL;
switch (notificationFilter.getNotificationFilterMode()) {
case NOTIFICATION_FILTER_MODE_BLACKLIST:
if (allMode) {
for (String word : wordsList) {
if (!body.contains(word)) {
LOG.info("Not every word was found, blacklist has no effect, processing continues.");
return true;
}
}
LOG.info("Every word was found, blacklist has effect, processing stops.");
return false;
} else {
boolean notContainsAny = !StringUtils.containsAny(body, wordsList.toArray(new CharSequence[0]));
if (notContainsAny) {
LOG.info("Not matching word was found, blacklist has no effect, processing continues.");
} else {
LOG.info("At least one matching word was found, blacklist has effect, processing stops.");
}
return notContainsAny;
}
case NOTIFICATION_FILTER_MODE_WHITELIST:
if (allMode) {
for (String word : wordsList) {
if (!body.contains(word)) {
LOG.info("Not every word was found, whitelist has no effect, processing stops.");
return false;
}
}
LOG.info("Every word was found, whitelist has effect, processing continues.");
return true;
} else {
boolean containsAny = StringUtils.containsAny(body, wordsList.toArray(new CharSequence[0]));
if (containsAny) {
LOG.info("At least one matching word was found, whitelist has effect, processing continues.");
} else {
LOG.info("No matching word was found, whitelist has no effect, processing stops.");
}
return containsAny;
}
default:
return true;
}
}
// Strip Unicode control sequences: some apps like Telegram add a lot of them for unknown reasons
private String sanitizeUnicode(String orig) {
return orig.replaceAll("\\p{C}", "");
}
private void dissectNotificationTo(Notification notification, NotificationSpec notificationSpec, boolean preferBigText) {
private void dissectNotificationTo(Notification notification, NotificationSpec notificationSpec,
boolean preferBigText) {
Bundle extras = NotificationCompat.getExtras(notification);

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activities.NotificationFilterActivity"
tools:layout_editor_absoluteY="81dp">
<TextView
android:text="Filter Mode"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/textView2"
android:layout_marginTop="16dp"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="16dp"/>
<Spinner
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/spinnerFilterMode" android:layout_marginTop="16dp"
app:layout_constraintTop_toBottomOf="@+id/textView2" app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="16dp" app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="16dp"
android:entries="@array/notification_filter_modes_entries"/>
<TextView
android:text="Mode Configuration"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/textView3" android:layout_marginTop="16dp"
app:layout_constraintTop_toBottomOf="@+id/spinnerFilterMode" app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="16dp"/>
<Spinner
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/spinnerSubMode" android:layout_marginTop="16dp"
app:layout_constraintTop_toBottomOf="@+id/textView3" app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="16dp" app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="16dp"
android:entries="@array/notification_filter_submodes_entries"/>
<EditText
android:gravity="start"
style="@style/Widget.AppCompat.EditText"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="0dp"
android:inputType="textMultiLine"
android:hint="@string/edittext_notification_filter_words_hint"
android:ems="10"
android:minLines="10"
android:maxLines="25"
android:id="@+id/editTextWords" android:layout_marginTop="16dp"
app:layout_constraintTop_toBottomOf="@+id/spinnerSubMode" android:layout_marginStart="16dp"
app:layout_constraintStart_toStartOf="parent" android:layout_marginEnd="16dp"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginBottom="16dp" app:layout_constraintBottom_toTopOf="@+id/buttonSaveFilter"/>
<Button
android:text="Save Configuration"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="@+id/buttonSaveFilter" android:layout_marginBottom="16dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="16dp" app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="16dp"/>
</android.support.constraint.ConstraintLayout>

View File

@ -429,4 +429,15 @@
<item>1800</item>
</string-array>
<string-array name="notification_filter_modes_entries">
<item>@string/filter_mode_none</item>
<item>@string/filter_mode_whitelist</item>
<item>@string/filter_mode_blacklist</item>
</string-array>
<string-array name="notification_filter_submodes_entries">
<item>@string/filter_submode_at_least_one</item>
<item>@string/filter_submode_all</item>
</string-array>
</resources>

View File

@ -419,7 +419,7 @@
<string name="fwinstaller_firmware_not_compatible_to_device">This firmware is not compatible with the device</string>
<string name="miband_prefs_reserve_alarm_calendar">Alarms to reserve for upcoming events</string>
<string name="miband_prefs_hr_sleep_detection">Use heart rate sensor to improve sleep detection</string>
<string name="miband_prefs_device_time_offset_hours">Device time offset in hours (for detecting sleep of shift workers)</string>
<string name="miband_prefs_device_time_offset_hours">Device time offset in hours (for detecting sleep of shift workers)</string>
<string name="miband2_prefs_dateformat">Date format</string>
<string name="dateformat_time">Time</string>
<string name="dateformat_date_time"><![CDATA[Time & date]]></string>
@ -667,4 +667,17 @@
<string name="pref_invalid_frequency_title">Invalid frequency</string>
<string name="pref_invalid_frequency_message">Please enter a frequency between 87.5 and 108.0</string>
<string name="language_and_region_prefs">Language and region settings</string>
<!--Notification Filter Black-/ Whitelist-->
<string name="title_activity_notification_filter">Notification Filter</string>
<string name="toast_app_must_not_be_blacklisted">App must not be blacklisted to be configured</string>
<string name="edittext_notification_filter_words_hint">Enter desired words, new line for each</string>
<string name="toast_notification_filter_saved_successfully">Notification Filter saved successfully</string>
<string name="filter_mode_none">Do not filter</string>
<string name="filter_mode_whitelist">Show when words are contained</string>
<string name="filter_mode_blacklist">Block when words are contained</string>
<string name="filter_submode_at_least_one">At least one of the words</string>
<string name="filter_submode_all">All of the words</string>
<string name="toast_notification_filter_words_empty_hint">Please enter at least one word</string>
</resources>

View File

@ -0,0 +1,109 @@
package nodomain.freeyourgadget.gadgetbridge.externalevents;
import nodomain.freeyourgadget.gadgetbridge.activities.NotificationFilterActivity;
import nodomain.freeyourgadget.gadgetbridge.entities.NotificationFilter;
import nodomain.freeyourgadget.gadgetbridge.test.TestBase;
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.*;
public class NotificationListenerTest extends TestBase {
private NotificationListener mNotificationListener;
private List<String> wordList = Arrays.asList("Hello", "world", "test");
@Override
public void setUp() throws Exception {
super.setUp();
mNotificationListener = new NotificationListener();
}
@Test
public void shouldContinueAfterFilter_TestBlacklistFindAnyWord_WordFound_MustReturnFalse() {
String body = "Hello world this is a test";
NotificationFilter filter = new NotificationFilter();
filter.setNotificationFilterMode(NotificationFilterActivity.NOTIFICATION_FILTER_MODE_BLACKLIST);
filter.setNotificationFilterSubMode(NotificationFilterActivity.NOTIFICATION_FILTER_SUBMODE_ANY);
assertFalse(mNotificationListener.shouldContinueAfterFilter(body, wordList, filter));
}
@Test
public void shouldContinueAfterFilter_TestWhitelistFindAnyWord_WordFound_MustReturnTrue() {
String body = "Hello world this is a test";
NotificationFilter filter = new NotificationFilter();
filter.setNotificationFilterMode(NotificationFilterActivity.NOTIFICATION_FILTER_MODE_WHITELIST);
filter.setNotificationFilterSubMode(NotificationFilterActivity.NOTIFICATION_FILTER_SUBMODE_ANY);
assertTrue(mNotificationListener.shouldContinueAfterFilter(body, wordList, filter));
}
@Test
public void shouldContinueAfterFilter_TestBlacklistFindAllWords_WordsFound_MustReturnFalse() {
String body = "Hello world this is a test";
NotificationFilter filter = new NotificationFilter();
filter.setNotificationFilterMode(NotificationFilterActivity.NOTIFICATION_FILTER_MODE_BLACKLIST);
filter.setNotificationFilterSubMode(NotificationFilterActivity.NOTIFICATION_FILTER_SUBMODE_ALL);
assertFalse(mNotificationListener.shouldContinueAfterFilter(body, wordList, filter));
}
@Test
public void shouldContinueAfterFilter_TestWhitelistFindAllWords_WordsFound_MustReturnTrue() {
String body = "Hello world this is a test";
NotificationFilter filter = new NotificationFilter();
filter.setNotificationFilterMode(NotificationFilterActivity.NOTIFICATION_FILTER_MODE_WHITELIST);
filter.setNotificationFilterSubMode(NotificationFilterActivity.NOTIFICATION_FILTER_SUBMODE_ALL);
assertTrue(mNotificationListener.shouldContinueAfterFilter(body, wordList, filter));
}
@Test
public void shouldContinueAfterFilter_TestBlacklistFindAnyWord_WordNotFound_MustReturnTrue() {
String body = "Hallo Welt das ist ein Versuch";
NotificationFilter filter = new NotificationFilter();
filter.setNotificationFilterMode(NotificationFilterActivity.NOTIFICATION_FILTER_MODE_BLACKLIST);
filter.setNotificationFilterSubMode(NotificationFilterActivity.NOTIFICATION_FILTER_SUBMODE_ANY);
assertTrue(mNotificationListener.shouldContinueAfterFilter(body, wordList, filter));
}
@Test
public void shouldContinueAfterFilter_TestWhitelistFindAnyWord_WordNotFound_MustReturnFalse() {
String body = "Hallo Welt das ist ein Versuch";
NotificationFilter filter = new NotificationFilter();
filter.setNotificationFilterMode(NotificationFilterActivity.NOTIFICATION_FILTER_MODE_WHITELIST);
filter.setNotificationFilterSubMode(NotificationFilterActivity.NOTIFICATION_FILTER_SUBMODE_ANY);
assertFalse(mNotificationListener.shouldContinueAfterFilter(body, wordList, filter));
}
@Test
public void shouldContinueAfterFilter_TestBlacklistFindAllWords_WordNotFound_MustReturnTrue() {
String body = "Hallo Welt das ist ein Versuch";
NotificationFilter filter = new NotificationFilter();
filter.setNotificationFilterMode(NotificationFilterActivity.NOTIFICATION_FILTER_MODE_BLACKLIST);
filter.setNotificationFilterSubMode(NotificationFilterActivity.NOTIFICATION_FILTER_SUBMODE_ALL);
assertTrue(mNotificationListener.shouldContinueAfterFilter(body, wordList, filter));
}
@Test
public void shouldContinueAfterFilter_TestWhitelistFindAllWords_WordNotFound_MustReturnFalse() {
String body = "Hallo Welt das ist ein Versuch";
NotificationFilter filter = new NotificationFilter();
filter.setNotificationFilterMode(NotificationFilterActivity.NOTIFICATION_FILTER_MODE_WHITELIST);
filter.setNotificationFilterSubMode(NotificationFilterActivity.NOTIFICATION_FILTER_SUBMODE_ALL);
assertFalse(mNotificationListener.shouldContinueAfterFilter(body, wordList, filter));
}
@Test
public void shouldContinueAfterFilter_TestFilterNone_MustReturnTrue() {
String body = "A text without a meaning";
NotificationFilter filter = new NotificationFilter();
filter.setNotificationFilterMode(NotificationFilterActivity.NOTIFICATION_FILTER_MODE_NONE);
assertTrue(mNotificationListener.shouldContinueAfterFilter(body, wordList, filter));
}
}