Hi guys!
I’m continuing my series of posts about creating Android app for resending notifications. First part you can read here: http://dbondarchuk.com/2016/09/22/notification-resender-part-1/
So, we have already created our project in Android studio, with MainActivity layout and Java class.
Lets change Main Activity to show our current settings and allow to add/edit/remove them.
For displaying settings the best option is to use a ListView. But we don’t need just a text in an item. The good ListView item will contain setting name, control to enable/disable setting and remove button:
So, we need to add our ListView to the MainActivity. Open main_content.xml layout file and replace its content with following code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout 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" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context="ua.com.revi.notificationresender.MainActivity" tools:showIn="@layout/activity_main"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" android:text="@string/settings_list" android:id="@+id/textView" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" /> <ListView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/listView" android:layout_alignParentStart="@+id/textView" android:layout_alignParentLeft="true" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:choiceMode="none" android:clickable="true" android:contextClickable="false" android:dividerHeight="0dp" android:divider="@null" android:touchscreenBlocksFocus="false" android:paddingLeft="5dp" android:paddingTop="5dp" android:paddingRight="5dp" android:paddingBottom="5dp" android:layout_below="@+id/textView" /> </RelativeLayout> |
Here we added a text label with text “Your settings:” (add it to the strings.xml file with name “settings_list“: <string name="settings_list">Your settings</string> . Here and below don’t forget add string values (starts with @string/some_id to the strings.xml file)) and id “textView” and a ListView with id “listView“, wich should be placed below textView( android:layout_below="@+id/textView" )
The next step is to create custom view for the ListView item. Create a new Layout Resource file with name “resendsetting_listview_item.xml” and place such code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent" android:weightSum="1" android:paddingBottom="5dp"> <TextView android:layout_weight="1" android:layout_width="0dp" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" android:text="" android:id="@+id/name" android:paddingBottom="5dp" /> <Switch android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="" android:id="@+id/enabled" android:layout_gravity="right" /> <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/remove" android:background="@android:drawable/ic_delete" android:layout_gravity="right" /> </LinearLayout> |
Here we are saying, that our item will be contained inside horizontal LinearLayout and will contain TextView for setting name, Switch for enabling/disabling setting and ImageButton for removing with built Android image for red cross sign ( android:background="@android:drawable/ic_delete" )
Okay, we have our custom layout for ListView item and now we need to connect it to our ListView.
First of all, we should create a class for setting model. Lets create a Java class named “ResendSetting“:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
public class ResendSetting implements Serializable { /* Setting id */ public int id; /* Setting name */ public String name; /* Determines whether is setting enabled */ public boolean enabled; /* Selected apps */ public String apps; /* Notification title */ public String title; /* Notification body */ public String body; /* Regex to exclude */ public String excludeRegex; /* Determines whether to use regex on exclusion or not */ public boolean useRegexForExclude; /* Remove delay in seconds. 0 - none */ public int removeDelay; public ResendSetting(int id, String name, boolean enabled, String apps, String title, String body, String excludeRegex, boolean useRegexForExclude, int removeDelay){ this.name = name; this.enabled = enabled; this.id = id; this.apps = apps; this.title = title; this.body = body; this.excludeRegex = excludeRegex; this.useRegexForExclude = useRegexForExclude; this.removeDelay = removeDelay; } } |
Our ResendSetting will contain such properties: id, name, is it enabled or not, title and body for resended notification, some text filter for check if original notification should be ignored, flag that says that this filter should be a Regex filter, delay time for resended notification removal and list of apps, from which notifications could be accepted. It’s pretty simple 🙂
Now, we need create an Adapter, that will allow us to apply our ResendSetting model to our ListView item. Lets create another Java class, called ResendSettingArrayAdapter:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
public class ResendSettingArrayAdapter extends ArrayAdapter<ResendSetting> { private Context context; private int resource; private ArrayList<ResendSetting> objects; public ResendSettingArrayAdapter(Context context, int resource, ArrayList<ResendSetting> objects) { super(context, resource, objects); this.context=context; this.resource=resource; this.objects=objects; } @Override public View getView(final int position, final View convertView, final ViewGroup parent) { LayoutInflater inflater=((Activity) context).getLayoutInflater(); final View row=inflater.inflate(resource,parent,false); TextView nameTextView = (TextView)row.findViewById(R.id.name); final Switch enabledSwitch = (Switch)row.findViewById(R.id.enabled); nameTextView.setText(objects.get(position).name); enabledSwitch.setChecked(objects.get(position).enabled); final ImageButton removeButton = (ImageButton)row.findViewById(R.id.remove); removeButton.setTag(position); final ResendSettingArrayAdapter adapter = this; enabledSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { objects.get(position).enabled = isChecked; Snackbar.make(row, context.getString(isChecked ? R.string.setting_was_enabled : R.string.setting_was_disabled, objects.get(position).name) , Snackbar.LENGTH_LONG).show(); } }); removeButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { final String name = objects.get(position).name; new AlertDialog.Builder(context) .setTitle(context.getString(R.string.are_you_sure_title)) .setMessage(context.getString(R.string.are_you_sure_delete_setting_message, name)) .setIcon(android.R.drawable.ic_dialog_alert) .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { objects.remove(position); adapter.notifyDataSetChanged(); Snackbar.make(row, context.getString(R.string.setting_was_removed, name) , Snackbar.LENGTH_LONG).show(); }}) .setNegativeButton(android.R.string.no, null).show(); } }); return row; } } |
Lets break this on the parts. First of all, we have a class ResendSettingArrayAdapter that extends ArrayAdapter for ResendSetting class. It has a constructor, that takes Context, Resource, and list of Objects (our settings).
When the new item is created, getView method is called and position number of the current item in objects array is passed. Using it, we are loading our item template and assign name and enabled values to corresponding TextView and Switch:
1 2 3 4 5 6 |
LayoutInflater inflater=((Activity) context).getLayoutInflater(); final View row=inflater.inflate(resource,parent,false); TextView nameTextView = (TextView)row.findViewById(R.id.name); final Switch enabledSwitch = (Switch)row.findViewById(R.id.enabled); nameTextView.setText(objects.get(position).name); enabledSwitch.setChecked(objects.get(position).enabled); |
Then, we can add handlers for Switch and Remove buttons:
1 2 3 4 5 6 7 |
enabledSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { objects.get(position).enabled = isChecked; Snackbar.make(row, context.getString(isChecked ? R.string.setting_was_enabled : R.string.setting_was_disabled, objects.get(position).name) , Snackbar.LENGTH_LONG).show(); } }); |
When, Switch state was chanded, this handler gets an event and process it by setting enabled value for object in position position new value and shows a Snackbar with text, that setting with name ‘objects.get(position).name‘ was enabled/disabled.
Please take attention on how string value is getting on showing Snackbar:
1 |
context.getString(isChecked ? R.string.setting_was_enabled : R.string.setting_was_disabled, objects.get(position).name) |
In a context, getString method is called and depends on isChecked value, setting_was_enabled or setting_was_disabled value is taken. Also the name of the setting is passed as argument to these strings. Here an example, how this strings should be defined in strings.xml:
1 2 |
<string name="setting_was_enabled">Setting \'%1$s\' was enabled</string> <string name="setting_was_disabled">Setting \'%1$s\' was disabled</string> |
Here, apostrophes ( ‘ ) should be escaped by backslash: \’ .
%1$s – it’s a mask for replacing argument (our setting name), where
%1 means number of argument, and
$s means type of the argument (s – for string, d – for digital etc)
For the Remove Button our handler will look like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
removeButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { final String name = objects.get(position).name; new AlertDialog.Builder(context) .setTitle(context.getString(R.string.are_you_sure_title)) .setMessage(context.getString(R.string.are_you_sure_delete_setting_message, name)) .setIcon(android.R.drawable.ic_dialog_alert) .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { objects.remove(position); adapter.notifyDataSetChanged(); Snackbar.make(row, context.getString(R.string.setting_was_removed, name) , Snackbar.LENGTH_LONG).show(); }}) .setNegativeButton(android.R.string.no, null).show(); } }); |
When we will click on the Remove Button, a new AlertDialog will be shown with title R.string.are_you_sure_title, message R.string.are_you_sure_delete_setting_message, name, default android icon for dialog alert android.R.drawable.ic_dialog_alert, and two buttons: Yes or No.
For this buttons’ captions we will use default android strings: android.R.string.yes and android.R.string.no instead of creating ours.
When user will click on No button, nothing will happen, just alert dialog will close. On the Yes button, we will remove this item from the objects array and show a Snackbar. adapter.notifyDataSetChanged(); is needed to notify ListView, that items were changed and we need to redraw them.
Ok, we have model, layout and adapter. Now we should connect them into the one thing.
Open the MainActivity.java file and add such code to the onCreate method:
1 2 3 |
ResendSettingArrayAdapter resendSettingArrayAdapter = new ResendSettingArrayAdapter(this, R.layout.resendsetting_listview_item, settings); ListView listView=(ListView) findViewById(R.id.listView); listView.setAdapter(resendSettingArrayAdapter); |
Here, we will create our adapter object with passing current context, id of the ListView item layout and settings into it. Then we will take our ListView by its id and set our adapter to it.
The one thing, that has left – is creating an array of settings. In the next posts, I will tell about working with local database to save/edit/remove/get our settings from it. But now, we can fake it and create a simple ArrayList with settings:
1 2 3 4 5 |
ResendSetting s1 = new ResendSetting(1, "SMS", true, "", "", "", "", false, 0); ResendSetting s2 = new ResendSetting(2, "FB", false, "", "", "", "", false, 0); ArrayList<ResendSetting> settings = new ArrayList<>(); settings.add(s1); settings.add(s2); |
That’s it 🙂 Now we have our main screen with list of our settings 🙂
In the next posts I will tell about adding a new settings, working with database and receiving notifications.
Thanks!
P.S. #1 All source code you can find in my GitHub – https://github.com/dbondarchuk/Notification-Resender
P.S. #2 You can also download app for your bracelet here – https://github.com/dbondarchuk/Notification-Resender/releases