diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml new file mode 100644 index 0000000..d8f708f --- /dev/null +++ b/.idea/codeStyleSettings.xml @@ -0,0 +1,229 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index b78652a..5d19981 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,5 +1,32 @@ + + + + + + + @@ -10,7 +37,7 @@ - + diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..0f32531 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/ru/vfilippov/electronreminder/ElectronReminderService.java b/app/src/main/java/ru/vfilippov/electronreminder/ElectronReminderService.java new file mode 100644 index 0000000..27fe1e6 --- /dev/null +++ b/app/src/main/java/ru/vfilippov/electronreminder/ElectronReminderService.java @@ -0,0 +1,145 @@ +package ru.vfilippov.electronreminder; + +import android.app.IntentService; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.support.v4.app.NotificationCompat; +import android.util.Log; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; + +/** + * This {@code IntentService} does the app's actual work. + * {@code RecheckAlarmReceiver} (a {@code WakefulBroadcastReceiver}) holds a + * partial wake lock for this service while the service does its work. When the + * service is finished, it calls {@code completeWakefulIntent()} to release the + * wake lock. + */ +public class ElectronReminderService extends IntentService +{ + public ElectronReminderService() + { + super("ElectronReminderService"); + } + + public static final String TAG = "Electron Reminder"; + // An ID used to post the notification. + public static final int NOTIFICATION_ID = 1; + + private NotificationManager mNotificationManager; + NotificationCompat.Builder builder; + + @Override + protected void onHandleIntent(Intent intent) + { + String urlString = "https://www.google.com"; + String result = ""; + // Try to connect to the Google homepage and download content. + try + { + result = loadFromNetwork(urlString); + } + catch (IOException e) + { + Log.i(TAG, "Connection error"); + } + + // If the app finds the string "doodle" in the Google home page content, it + // indicates the presence of a doodle. Post a "Doodle Alert" notification. + if (result.indexOf("doodle") != -1) + { + sendNotification(/*getString(R.string.doodle_found)*/"doodle found"); + Log.i(TAG, "Found doodle!!"); + } + else + { + sendNotification("no doodle"); + Log.i(TAG, "No doodle found. :-("); + } + // Release the wake lock provided by the BroadcastReceiver. + RecheckAlarmReceiver.completeWakefulIntent(intent); + } + + // Post a notification indicating suburban changes + private void sendNotification(String msg) + { + mNotificationManager = (NotificationManager) + this.getSystemService(Context.NOTIFICATION_SERVICE); + + PendingIntent contentIntent = PendingIntent.getActivity(this, 0, + new Intent(this, MainActivity.class), 0); + + NotificationCompat.Builder mBuilder = + new NotificationCompat.Builder(this) + .setSmallIcon(R.mipmap.ic_launcher) + .setContentTitle("doodle alert") + .setStyle(new NotificationCompat.BigTextStyle() + .bigText(msg)) + .setContentText(msg); + + mBuilder.setContentIntent(contentIntent); + mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build()); + } + + // Given a URL string, initiate a fetch operation. + private String loadFromNetwork(String urlString) throws IOException + { + InputStream stream = null; + String str = ""; + try + { + stream = downloadUrl(urlString); + str = readIt(stream); + } + finally + { + if (stream != null) + stream.close(); + } + return str; + } + + /** + * Given a string representation of a URL, sets up a connection and gets + * an input stream. + * @param urlString A string representation of a URL. + * @return An InputStream retrieved from a successful HttpURLConnection. + * @throws IOException + */ + private InputStream downloadUrl(String urlString) throws IOException + { + URL url = new URL(urlString); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setReadTimeout(10000 /* milliseconds */); + conn.setConnectTimeout(15000 /* milliseconds */); + conn.setRequestMethod("GET"); + conn.setDoInput(true); + // Start the query + conn.connect(); + InputStream stream = conn.getInputStream(); + return stream; + } + + /** + * Reads an InputStream and converts it to a String. + * @param stream InputStream containing HTML from www.google.com. + * @return String version of InputStream. + * @throws IOException + */ + private String readIt(InputStream stream) throws IOException + { + StringBuilder builder = new StringBuilder(); + BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); + for (String line = reader.readLine(); line != null; line = reader.readLine()) + builder.append(line); + reader.close(); + return builder.toString(); + } +} diff --git a/app/src/main/java/ru/vfilippov/electronreminder/MainActivity.java b/app/src/main/java/ru/vfilippov/electronreminder/MainActivity.java new file mode 100644 index 0000000..5ab4b3a --- /dev/null +++ b/app/src/main/java/ru/vfilippov/electronreminder/MainActivity.java @@ -0,0 +1,45 @@ +package ru.vfilippov.electronreminder; + +import android.content.Intent; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; + +public class MainActivity extends AppCompatActivity +{ + + @Override + protected void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) + { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_main, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) + { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + // noinspection SimplifiableIfStatement + if (id == R.id.action_settings) + { + Intent intent = new Intent(this, SettingsActivity.class); + startActivity(intent); + return true; + } + + return super.onOptionsItemSelected(item); + } +} diff --git a/app/src/main/java/ru/vfilippov/electronreminder/RecheckAlarmReceiver.java b/app/src/main/java/ru/vfilippov/electronreminder/RecheckAlarmReceiver.java new file mode 100644 index 0000000..acf9468 --- /dev/null +++ b/app/src/main/java/ru/vfilippov/electronreminder/RecheckAlarmReceiver.java @@ -0,0 +1,82 @@ +package ru.vfilippov.electronreminder; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.support.v4.content.WakefulBroadcastReceiver; + +import java.util.Calendar; + +/** + * When the alarm fires, this WakefulBroadcastReceiver receives the broadcast Intent + * and then starts the service to do some work. + */ +public class RecheckAlarmReceiver extends WakefulBroadcastReceiver +{ + private AlarmManager alarmMgr; + private PendingIntent alarmIntent; + + @Override + public void onReceive(Context context, Intent intent) + { + /* For different suburban rechecks: + * + * ComponentName comp = new ComponentName(context.getPackageName(), MyService.class.getName()); + * startWakefulService(context, (intent.setComponent(comp))); + */ + Intent service = new Intent(context, ElectronReminderService.class); + // Start the service, keeping the device awake while it is launching. + startWakefulService(context, service); + } + + /** + * Sets a repeating alarm that runs once a day at approximately 8:30 a.m. When the + * alarm fires, the app broadcasts an Intent to this WakefulBroadcastReceiver. + * @param context + */ + public void setAlarm(Context context) + { + alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); + Intent intent = new Intent(context, RecheckAlarmReceiver.class); + alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0); + + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(System.currentTimeMillis()); + // Set the alarm's trigger time to 8:30 a.m. + calendar.set(Calendar.HOUR_OF_DAY, 8); + calendar.set(Calendar.MINUTE, 30); + + // Set the alarm to fire at approximately 8:30 a.m., according to the device's + // clock, and to repeat once a day. + alarmMgr.setInexactRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, alarmIntent); + + // Enable {@code RecheckBootReceiver} to automatically restart the alarm when the device is rebooted. + ComponentName receiver = new ComponentName(context, RecheckBootReceiver.class); + PackageManager pm = context.getPackageManager(); + pm.setComponentEnabledSetting(receiver, + PackageManager.COMPONENT_ENABLED_STATE_ENABLED, + PackageManager.DONT_KILL_APP); + } + + /** + * Cancels the alarm. + * @param context + */ + public void cancelAlarm(Context context) + { + // If the alarm has been set, cancel it. + if (alarmMgr != null) + alarmMgr.cancel(alarmIntent); + + // Disable {@code RecheckBootReceiver} so that it doesn't automatically restart the + // alarm when the device is rebooted. + ComponentName receiver = new ComponentName(context, RecheckBootReceiver.class); + PackageManager pm = context.getPackageManager(); + pm.setComponentEnabledSetting(receiver, + PackageManager.COMPONENT_ENABLED_STATE_DISABLED, + PackageManager.DONT_KILL_APP); + } +} diff --git a/app/src/main/java/ru/vfilippov/electronreminder/RecheckBootReceiver.java b/app/src/main/java/ru/vfilippov/electronreminder/RecheckBootReceiver.java new file mode 100644 index 0000000..4d53fca --- /dev/null +++ b/app/src/main/java/ru/vfilippov/electronreminder/RecheckBootReceiver.java @@ -0,0 +1,24 @@ +package ru.vfilippov.electronreminder; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +/** + * This BroadcastReceiver automatically (re)starts the alarm when the device is + * rebooted. This receiver is set to be disabled (android:enabled="false") in the + * application's manifest file. When the user sets the alarm, the receiver is enabled. + * When the user cancels the alarm, the receiver is disabled, so that rebooting the + * device will not trigger this receiver. + */ +public class RecheckBootReceiver extends BroadcastReceiver +{ + RecheckAlarmReceiver alarm = new RecheckAlarmReceiver(); + + @Override + public void onReceive(Context context, Intent intent) + { + if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) + alarm.setAlarm(context); + } +} diff --git a/app/src/main/java/ru/vfilippov/electronreminder/SettingsActivity.java b/app/src/main/java/ru/vfilippov/electronreminder/SettingsActivity.java new file mode 100644 index 0000000..9248e3a --- /dev/null +++ b/app/src/main/java/ru/vfilippov/electronreminder/SettingsActivity.java @@ -0,0 +1,194 @@ +package ru.vfilippov.electronreminder; + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.res.Configuration; +import android.media.Ringtone; +import android.media.RingtoneManager; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.preference.ListPreference; +import android.preference.Preference; +import android.preference.PreferenceActivity; +import android.preference.PreferenceCategory; +import android.preference.PreferenceFragment; +import android.preference.PreferenceManager; +import android.preference.RingtonePreference; +import android.text.TextUtils; + +import java.util.List; + + +/** + * A {@link PreferenceActivity} that presents a set of application settings. On + * handset devices, settings are presented as a single list. On tablets, + * settings are split by category, with category headers shown to the left of + * the list of settings. + *

+ * See + * Android Design: Settings for design guidelines and the Settings + * API Guide for more information on developing a Settings UI. + */ +public class SettingsActivity extends PreferenceActivity { + + + /** + * {@inheritDoc} + */ + @Override + public boolean onIsMultiPane() { + return isXLargeTablet(this); + } + + /** + * Helper method to determine if the device has an extra-large screen. For + * example, 10" tablets are extra-large. + */ + private static boolean isXLargeTablet(Context context) { + return (context.getResources().getConfiguration().screenLayout + & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_XLARGE; + } + + + /** + * {@inheritDoc} + */ + @Override + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public void onBuildHeaders(List

target) { + loadHeadersFromResource(R.xml.pref_headers, target); + } + + /** + * A preference value change listener that updates the preference's summary + * to reflect its new value. + */ + private static Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object value) { + String stringValue = value.toString(); + + if (preference instanceof ListPreference) { + // For list preferences, look up the correct display value in + // the preference's 'entries' list. + ListPreference listPreference = (ListPreference) preference; + int index = listPreference.findIndexOfValue(stringValue); + + // Set the summary to reflect the new value. + preference.setSummary( + index >= 0 + ? listPreference.getEntries()[index] + : null); + + } else if (preference instanceof RingtonePreference) { + // For ringtone preferences, look up the correct display value + // using RingtoneManager. + if (TextUtils.isEmpty(stringValue)) { + // Empty values correspond to 'silent' (no ringtone). + preference.setSummary(R.string.pref_ringtone_silent); + + } else { + Ringtone ringtone = RingtoneManager.getRingtone( + preference.getContext(), Uri.parse(stringValue)); + + if (ringtone == null) { + // Clear the summary if there was a lookup error. + preference.setSummary(null); + } else { + // Set the summary to reflect the new ringtone display + // name. + String name = ringtone.getTitle(preference.getContext()); + preference.setSummary(name); + } + } + + } else { + // For all other preferences, set the summary to the value's + // simple string representation. + preference.setSummary(stringValue); + } + return true; + } + }; + + /** + * Binds a preference's summary to its value. More specifically, when the + * preference's value is changed, its summary (line of text below the + * preference title) is updated to reflect the value. The summary is also + * immediately updated upon calling this method. The exact display format is + * dependent on the type of preference. + * + * @see #sBindPreferenceSummaryToValueListener + */ + private static void bindPreferenceSummaryToValue(Preference preference) { + // Set the listener to watch for value changes. + preference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener); + + // Trigger the listener immediately with the preference's + // current value. + sBindPreferenceSummaryToValueListener.onPreferenceChange(preference, + PreferenceManager + .getDefaultSharedPreferences(preference.getContext()) + .getString(preference.getKey(), "")); + } + + /** + * This fragment shows general preferences only. It is used when the + * activity is showing a two-pane settings UI. + */ + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public static class GeneralPreferenceFragment extends PreferenceFragment { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.pref_general); + + // Bind the summaries of EditText/List/Dialog/Ringtone preferences + // to their values. When their values change, their summaries are + // updated to reflect the new value, per the Android Design + // guidelines. + bindPreferenceSummaryToValue(findPreference("example_text")); + bindPreferenceSummaryToValue(findPreference("example_list")); + } + } + + /** + * This fragment shows notification preferences only. It is used when the + * activity is showing a two-pane settings UI. + */ + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public static class NotificationPreferenceFragment extends PreferenceFragment { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.pref_notification); + + // Bind the summaries of EditText/List/Dialog/Ringtone preferences + // to their values. When their values change, their summaries are + // updated to reflect the new value, per the Android Design + // guidelines. + bindPreferenceSummaryToValue(findPreference("notifications_new_message_ringtone")); + } + } + + /** + * This fragment shows data and sync preferences only. It is used when the + * activity is showing a two-pane settings UI. + */ + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public static class DataSyncPreferenceFragment extends PreferenceFragment { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.pref_data_sync); + + // Bind the summaries of EditText/List/Dialog/Ringtone preferences + // to their values. When their values change, their summaries are + // updated to reflect the new value, per the Android Design + // guidelines. + bindPreferenceSummaryToValue(findPreference("sync_frequency")); + } + } +} diff --git a/app/src/main/java/simple/SimpleXml.java b/app/src/main/java/simple/SimpleXml.java new file mode 100644 index 0000000..cf42858 --- /dev/null +++ b/app/src/main/java/simple/SimpleXml.java @@ -0,0 +1,60 @@ +package simple; + +import android.util.Xml; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; + +public class SimpleXml +{ + public SimpleXmlNode parse(InputStream in) throws IOException, XmlPullParserException + { + try + { + XmlPullParser parser = Xml.newPullParser(); + parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); + parser.setInput(in, null); + return readFeed(parser); + } + finally + { + in.close(); + } + } + + public SimpleXmlNode readFeed(XmlPullParser parser) throws IOException, XmlPullParserException + { + ArrayList stack = new ArrayList(); + while (parser.next() != XmlPullParser.START_TAG) {} + while (parser.next() != XmlPullParser.END_DOCUMENT) + { + if (parser.getEventType() == XmlPullParser.START_TAG) + { + SimpleXmlNode node = new SimpleXmlNode(); + node.name = parser.getName(); + for (int i = 0; i < parser.getAttributeCount(); i++) + { + String p = parser.getAttributePrefix(i); + if (p == null) + p = ""; + node.attributes.put(p + parser.getAttributeName(i), parser.getAttributeValue(i)); + } + if (stack.size() > 0) + stack.get(stack.size()-1).appendChild(node); + stack.add(node); + } + else if (parser.getEventType() == XmlPullParser.END_TAG) + { + if (stack.size() > 1) + stack.remove(stack.size() - 1); + else + return stack.get(0); + } + } + return null; + } +} diff --git a/app/src/main/java/simple/SimpleXmlNode.java b/app/src/main/java/simple/SimpleXmlNode.java new file mode 100644 index 0000000..d61f359 --- /dev/null +++ b/app/src/main/java/simple/SimpleXmlNode.java @@ -0,0 +1,30 @@ +package simple; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class SimpleXmlNode +{ + public String name; + public Map attributes; + public List children; + public Map> childrenByName; + + public SimpleXmlNode() + { + attributes = new HashMap(); + children = new ArrayList(); + childrenByName = new HashMap>(); + } + + public void appendChild(SimpleXmlNode child) + { + children.add(child); + List l = childrenByName.get(child.name); + if (l == null) + childrenByName.put(child.name, (l = new ArrayList())); + l.add(child); + } +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..29c13b0 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml new file mode 100644 index 0000000..965ea57 --- /dev/null +++ b/app/src/main/res/menu/menu_main.xml @@ -0,0 +1,7 @@ + + + diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..cde69bc Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..c133a0c Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..bfa42f0 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..324e72c Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/values-w820dp/dimens.xml b/app/src/main/res/values-w820dp/dimens.xml new file mode 100644 index 0000000..63fc816 --- /dev/null +++ b/app/src/main/res/values-w820dp/dimens.xml @@ -0,0 +1,6 @@ + + + 64dp + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000..47c8224 --- /dev/null +++ b/app/src/main/res/values/dimens.xml @@ -0,0 +1,5 @@ + + + 16dp + 16dp + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..f445de4 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,5 @@ + + Electron Reminder + MainActivity + Settings + diff --git a/app/src/main/res/values/strings_activity_settings.xml b/app/src/main/res/values/strings_activity_settings.xml new file mode 100644 index 0000000..2686b04 --- /dev/null +++ b/app/src/main/res/values/strings_activity_settings.xml @@ -0,0 +1,60 @@ + + + + + + General + + Enable social recommendations + Recommendations for people to contact + based on your message history + + + Display name + John Smith + + Add friends to messages + + Always + When possible + Never + + + 1 + 0 + -1 + + + + Data & sync + + Sync frequency + + 15 minutes + 30 minutes + 1 hour + 3 hours + 6 hours + Never + + + 15 + 30 + 60 + 180 + 360 + -1 + + + System sync settings + + + Notifications + + New message notifications + + Ringtone + Silent + + Vibrate + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..766ab99 --- /dev/null +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/app/src/main/res/xml/pref_data_sync.xml b/app/src/main/res/xml/pref_data_sync.xml new file mode 100644 index 0000000..ffda831 --- /dev/null +++ b/app/src/main/res/xml/pref_data_sync.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/xml/pref_general.xml b/app/src/main/res/xml/pref_general.xml new file mode 100644 index 0000000..c49cbed --- /dev/null +++ b/app/src/main/res/xml/pref_general.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/pref_headers.xml b/app/src/main/res/xml/pref_headers.xml new file mode 100644 index 0000000..bc7aed5 --- /dev/null +++ b/app/src/main/res/xml/pref_headers.xml @@ -0,0 +1,17 @@ + + + + +
+ +
+ +
+ + diff --git a/app/src/main/res/xml/pref_notification.xml b/app/src/main/res/xml/pref_notification.xml new file mode 100644 index 0000000..b4b8cae --- /dev/null +++ b/app/src/main/res/xml/pref_notification.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + +