diff --git a/app/build.gradle b/app/build.gradle
index aafe1fe9..a4de63b5 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -31,6 +31,8 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support:design:27.1.1'
+ implementation 'com.android.support:preference-v7:27.1.1'
+ implementation 'com.android.support:preference-v14:27.1.1'
implementation 'com.android.support:support-v4:27.1.1'
implementation 'com.android.support:cardview-v7:27.1.1'
implementation 'com.android.support:recyclerview-v7:27.1.1'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 7417185a..b9789d97 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -105,6 +105,15 @@
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.main.MainActivity" />
+
+
+
0 && postsList.get(postsList.size()-1)==null)
- {
+ } else if (postsList != null && postsList.size() > 0 && postsList.get(postsList.size() - 1) == null) {
postsList.remove(postsList.size() - 1);
topicAdapter.notifyItemRemoved(postsList.size());
topicAdapter.setBackButtonHidden();
@@ -356,6 +365,11 @@ public class TopicActivity extends BaseActivity {
super.onResume();
refreshTopicBookmark();
drawer.setSelection(-1);
+
+ if (sessionManager.isLoggedIn()) {
+ SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
+ includeAppSignaturePreference = sharedPrefs.getBoolean(SettingsActivity.APP_SIGNATURE_ENABLE_KEY, true);
+ }
}
@Override
@@ -548,7 +562,8 @@ public class TopicActivity extends BaseActivity {
}
}
-//------------------------------------BOTTOM NAV BAR METHODS END------------------------------------
+
+ //------------------------------------BOTTOM NAV BAR METHODS END------------------------------------
private enum ResultCode {
SUCCESS, NETWORK_ERROR, PARSING_ERROR, OTHER_ERROR, SAME_PAGE, UNAUTHORIZED
}
@@ -632,7 +647,7 @@ public class TopicActivity extends BaseActivity {
Timber.i(e, "IO Exception");
return ResultCode.NETWORK_ERROR;
} catch (ParseException e) {
- if(isUnauthorized(document))
+ if (isUnauthorized(document))
return ResultCode.UNAUTHORIZED;
Timber.e(e, "Parsing Error");
return ResultCode.PARSING_ERROR;
@@ -673,9 +688,9 @@ public class TopicActivity extends BaseActivity {
pageIndicator.setText(String.valueOf(thisPage) + "/" + String.valueOf(numberOfPages));
pageRequestValue = thisPage;
- if(thisPage==numberOfPages){
+ if (thisPage == numberOfPages) {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- if(notificationManager!=null)
+ if (notificationManager != null)
notificationManager.cancel(NEW_POST_TAG, loadedPageTopicId);
}
@@ -702,7 +717,7 @@ public class TopicActivity extends BaseActivity {
}
}
- private void stopLoading(){
+ private void stopLoading() {
progressBar.setVisibility(ProgressBar.INVISIBLE);
if (replyPageUrl == null) {
replyFAB.hide();
@@ -718,7 +733,7 @@ public class TopicActivity extends BaseActivity {
* @param topic {@link Document} object containing this topic's source code
* @see org.jsoup.Jsoup Jsoup
*/
- private ArrayList parse(Document topic) throws ParseException{
+ private ArrayList parse(Document topic) throws ParseException {
try {
ParseHelpers.Language language = ParseHelpers.Language.getLanguage(topic);
@@ -898,7 +913,9 @@ public class TopicActivity extends BaseActivity {
@Override
protected Boolean doInBackground(String... args) {
- final String sentFrommTHMMY = "\n[right][size=7pt][i]sent from [url=https://play.google.com/store/apps/details?id=gr.thmmy.mthmmy]mTHMMY [/url][/i][/size][/right]";
+ final String sentFrommTHMMY = includeAppSignaturePreference
+ ? "\n[right][size=7pt][i]sent from [url=https://play.google.com/store/apps/details?id=gr.thmmy.mthmmy]mTHMMY [/url][/i][/size][/right]"
+ : "";
RequestBody postBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("message", args[1] + sentFrommTHMMY)
diff --git a/app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java b/app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java
index 9150065d..64096b56 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java
@@ -47,6 +47,7 @@ import gr.thmmy.mthmmy.activities.LoginActivity;
import gr.thmmy.mthmmy.activities.downloads.DownloadsActivity;
import gr.thmmy.mthmmy.activities.main.MainActivity;
import gr.thmmy.mthmmy.activities.profile.ProfileActivity;
+import gr.thmmy.mthmmy.activities.settings.SettingsActivity;
import gr.thmmy.mthmmy.model.Bookmark;
import gr.thmmy.mthmmy.model.ThmmyFile;
import gr.thmmy.mthmmy.services.DownloadHelper;
@@ -147,10 +148,11 @@ public abstract class BaseActivity extends AppCompatActivity {
protected static final int BOOKMARKS_ID = 2;
protected static final int LOG_ID = 3;
protected static final int ABOUT_ID = 4;
+ protected static final int SETTINGS_ID = 5;
private AccountHeader accountHeader;
private ProfileDrawerItem profileDrawerItem;
- private PrimaryDrawerItem downloadsItem, loginLogoutItem;
+ private PrimaryDrawerItem downloadsItem, settingsItem, loginLogoutItem;
private IconicsDrawable loginIcon, logoutIcon;
/**
@@ -162,9 +164,8 @@ public abstract class BaseActivity extends AppCompatActivity {
final int selectedSecondaryColor = ContextCompat.getColor(this, R.color.accent);
PrimaryDrawerItem homeItem, bookmarksItem, aboutItem;
- IconicsDrawable homeIcon, homeIconSelected, downloadsIcon, downloadsIconSelected,
- bookmarksIcon, bookmarksIconSelected, aboutIcon,
- aboutIconSelected;
+ IconicsDrawable homeIcon, homeIconSelected, downloadsIcon, downloadsIconSelected, settingsIcon,
+ settingsIconSelected, bookmarksIcon, bookmarksIconSelected, aboutIcon, aboutIconSelected;
//Drawer Icons
homeIcon = new IconicsDrawable(this)
@@ -188,6 +189,14 @@ public abstract class BaseActivity extends AppCompatActivity {
.color(primaryColor);
downloadsIconSelected = new IconicsDrawable(this)
+ .icon(GoogleMaterial.Icon.gmd_settings)
+ .color(selectedSecondaryColor);
+
+ settingsIcon = new IconicsDrawable(this)
+ .icon(GoogleMaterial.Icon.gmd_settings)
+ .color(primaryColor);
+
+ settingsIconSelected = new IconicsDrawable(this)
.icon(GoogleMaterial.Icon.gmd_file_download)
.color(selectedSecondaryColor);
@@ -243,6 +252,15 @@ public abstract class BaseActivity extends AppCompatActivity {
.withIcon(loginIcon)
.withSelectable(false);
+ settingsItem = new PrimaryDrawerItem()
+ .withTextColor(primaryColor)
+ .withSelectedColor(selectedPrimaryColor)
+ .withSelectedTextColor(selectedSecondaryColor)
+ .withIdentifier(SETTINGS_ID)
+ .withName(R.string.settings)
+ .withIcon(settingsIcon)
+ .withSelectedIcon(settingsIconSelected);
+
bookmarksItem = new PrimaryDrawerItem()
.withTextColor(primaryColor)
.withSelectedColor(selectedPrimaryColor)
@@ -337,7 +355,11 @@ public abstract class BaseActivity extends AppCompatActivity {
Intent i = new Intent(BaseActivity.this, AboutActivity.class);
startActivity(i);
}
-
+ } else if (drawerItem.equals(SETTINGS_ID)) {
+ if (!(BaseActivity.this instanceof SettingsActivity)) {
+ Intent intent = new Intent(BaseActivity.this, SettingsActivity.class);
+ startActivity(intent);
+ }
}
drawer.closeDrawer();
@@ -346,7 +368,7 @@ public abstract class BaseActivity extends AppCompatActivity {
});
if (sessionManager.isLoggedIn())
- drawerBuilder.addDrawerItems(homeItem, bookmarksItem, downloadsItem, loginLogoutItem, aboutItem);
+ drawerBuilder.addDrawerItems(homeItem, bookmarksItem, downloadsItem, settingsItem, loginLogoutItem, aboutItem);
else
drawerBuilder.addDrawerItems(homeItem, bookmarksItem, loginLogoutItem, aboutItem);
@@ -369,13 +391,20 @@ public abstract class BaseActivity extends AppCompatActivity {
if (!sessionManager.isLoggedIn()) //When logged out or if user is guest
{
drawer.removeItem(DOWNLOADS_ID);
+ drawer.removeItem(SETTINGS_ID);
loginLogoutItem.withName(R.string.login).withIcon(loginIcon); //Swap logout with login
profileDrawerItem.withName(sessionManager.getUsername());
setDefaultAvatar();
} else {
+ if (!drawer.getDrawerItems().contains(downloadsItem)){
+ drawer.addItemAtPosition(settingsItem, 2);
+ }
+ if (!drawer.getDrawerItems().contains(settingsItem)){
+ drawer.addItemAtPosition(settingsItem, 3);
+ }
loginLogoutItem.withName(R.string.logout).withIcon(logoutIcon); //Swap login with logout
profileDrawerItem.withName(sessionManager.getUsername());
- if(sessionManager.hasAvatar())
+ if (sessionManager.hasAvatar())
profileDrawerItem.withIcon(sessionManager.getAvatarLink());
else
setDefaultAvatar();
@@ -422,9 +451,9 @@ public abstract class BaseActivity extends AppCompatActivity {
mainActivity.updateTabs();
progressDialog.dismiss();
//if (BaseActivity.this instanceof TopicActivity){
- Intent intent = new Intent(BaseActivity.this, MainActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
+ Intent intent = new Intent(BaseActivity.this, MainActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intent);
//}
}
}
@@ -560,12 +589,12 @@ public abstract class BaseActivity extends AppCompatActivity {
else if (bookmark.matchExists(topicsBookmarked)) toggleTopicToBookmarks(bookmark);
}
- protected boolean toggleNotification(Bookmark bookmark){
- if (bookmark.matchExists(topicsBookmarked)){
+ protected boolean toggleNotification(Bookmark bookmark) {
+ if (bookmark.matchExists(topicsBookmarked)) {
topicsBookmarked.get(bookmark.findIndex(topicsBookmarked)).toggleNotificationsEnabled();
updateTopicBookmarks();
- if (topicsBookmarked.get(bookmark.findIndex(topicsBookmarked)).isNotificationsEnabled()){
+ if (topicsBookmarked.get(bookmark.findIndex(topicsBookmarked)).isNotificationsEnabled()) {
FirebaseMessaging.getInstance().subscribeToTopic(bookmark.getId());
} else {
FirebaseMessaging.getInstance().unsubscribeFromTopic(bookmark.getId());
@@ -634,9 +663,9 @@ public abstract class BaseActivity extends AppCompatActivity {
prepareDownload(tempThmmyFile);
}
- private void prepareDownload(ThmmyFile thmmyFile){
+ private void prepareDownload(ThmmyFile thmmyFile) {
String fileName = thmmyFile.getFilename();
- if(FileUtils.fileNameExists(fileName))
+ if (FileUtils.fileNameExists(fileName))
openDownloadPrompt(thmmyFile);
else
DownloadHelper.enqueueDownload(thmmyFile);
@@ -647,7 +676,7 @@ public abstract class BaseActivity extends AppCompatActivity {
final BottomSheetDialog dialog = new BottomSheetDialog(this);
dialog.setContentView(view);
TextView downloadPromptTextView = view.findViewById(R.id.downloadPromptTextView);
- downloadPromptTextView.setText(getString(R.string.downloadPromptText,thmmyFile.getFilename()));
+ downloadPromptTextView.setText(getString(R.string.downloadPromptText, thmmyFile.getFilename()));
Button cancelButton = view.findViewById(R.id.cancel);
Button openButton = view.findViewById(R.id.open);
Button downloadButton = view.findViewById(R.id.download);
@@ -661,15 +690,15 @@ public abstract class BaseActivity extends AppCompatActivity {
@Override
public void onClick(View v) {
dialog.dismiss();
- try{
+ try {
String fileName = thmmyFile.getFilename();
Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_GRANT_READ_URI_PERMISSION);
- Uri fileUri = FileProvider.getUriForFile(getApplicationContext(), getPackageName() + ".provider", new File(SAVE_DIR, fileName));
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ Uri fileUri = FileProvider.getUriForFile(getApplicationContext(), getPackageName() + ".provider", new File(SAVE_DIR, fileName));
intent.setDataAndType(fileUri, getMimeType(fileName));
BaseActivity.this.startActivity(intent);
- }catch (Exception e){
- Timber.e(e,"Couldn't open downloaded file...");
+ } catch (Exception e) {
+ Timber.e(e, "Couldn't open downloaded file...");
Toast.makeText(getBaseContext(), "Couldn't open file...", Toast.LENGTH_SHORT).show();
}
diff --git a/app/src/main/java/gr/thmmy/mthmmy/services/NotificationService.java b/app/src/main/java/gr/thmmy/mthmmy/services/NotificationService.java
index 6841252e..f6db39ad 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/services/NotificationService.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/services/NotificationService.java
@@ -6,11 +6,14 @@ import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
+import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
import android.support.annotation.RequiresApi;
import android.support.v4.app.NotificationCompat;
+import android.support.v7.preference.PreferenceManager;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
@@ -25,6 +28,9 @@ import gr.thmmy.mthmmy.model.PostNotification;
import timber.log.Timber;
import static android.support.v4.app.NotificationCompat.PRIORITY_MAX;
+import static gr.thmmy.mthmmy.activities.settings.SettingsActivity.NOTIFICATION_VIBRATION_KEY;
+import static gr.thmmy.mthmmy.activities.settings.SettingsFragment.SELECTED_RINGTONE;
+import static gr.thmmy.mthmmy.activities.settings.SettingsFragment.SETTINGS_SHARED_PREFS;
import static gr.thmmy.mthmmy.activities.topic.TopicActivity.BUNDLE_TOPIC_TITLE;
import static gr.thmmy.mthmmy.activities.topic.TopicActivity.BUNDLE_TOPIC_URL;
@@ -40,15 +46,13 @@ public class NotificationService extends FirebaseMessagingService {
try {
int userId = BaseApplication.getInstance().getSessionManager().getUserId();
//Don't notify me if the sender is me!
- if(Integer.parseInt(json.getString("posterId"))!= userId)
- {
+ if (Integer.parseInt(json.getString("posterId")) != userId) {
int topicId = Integer.parseInt(json.getString("topicId"));
int postId = Integer.parseInt(json.getString("postId"));
String topicTitle = json.getString("topicTitle");
String poster = json.getString("poster");
sendNotification(new PostNotification(postId, topicId, topicTitle, poster));
- }
- else
+ } else
Timber.v("Notification suppressed (own userID).");
} catch (JSONException e) {
Timber.e(e, "JSON Exception");
@@ -64,13 +68,26 @@ public class NotificationService extends FirebaseMessagingService {
private static final String NEW_POSTS_COUNT = "newPostsCount";
public static final String NEW_POST_TAG = "NEW_POST";
private static final String SUMMARY_TAG = "SUMMARY";
- private static final String DELETED_MESSAGES_TAG = "DELETED_MESSAGES";
/**
* Create and show a new post notification.
*/
private void sendNotification(PostNotification postNotification) {
Timber.i("Creating a notification...");
+
+ SharedPreferences settingsFile = getSharedPreferences(SETTINGS_SHARED_PREFS, Context.MODE_PRIVATE);
+ Uri notificationSoundUri = Uri.parse(settingsFile.getString(SELECTED_RINGTONE, null));
+ SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
+ boolean notificationsVibrateEnabled = sharedPrefs.getBoolean(NOTIFICATION_VIBRATION_KEY, true);
+
+ int notificationDefaultValues = Notification.DEFAULT_LIGHTS;
+ if (notificationsVibrateEnabled) {
+ notificationDefaultValues |= Notification.DEFAULT_VIBRATE;
+ }
+ if (notificationSoundUri == null) {
+ notificationDefaultValues |= Notification.DEFAULT_SOUND;
+ }
+
String topicUrl = "https://www.thmmy.gr/smf/index.php?topic=" + postNotification.getTopicId() + "." + postNotification.getPostId();
Intent intent = new Intent(this, TopicActivity.class);
Bundle extras = new Bundle();
@@ -85,10 +102,9 @@ public class NotificationService extends FirebaseMessagingService {
String contentText = "New post by " + postNotification.getPoster();
int newPostsCount = 1;
- if (buildVersion >= Build.VERSION_CODES.M){
+ if (buildVersion >= Build.VERSION_CODES.M) {
Notification existingNotification = getActiveNotification(topicId);
- if(existingNotification!=null)
- {
+ if (existingNotification != null) {
newPostsCount = existingNotification.extras.getInt(NEW_POSTS_COUNT) + 1;
contentText = newPostsCount + " new posts";
}
@@ -99,29 +115,35 @@ public class NotificationService extends FirebaseMessagingService {
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(this, CHANNEL_ID)
- .setSmallIcon(R.mipmap.ic_launcher)
+ .setSmallIcon(R.drawable.ic_notification)
.setContentTitle(postNotification.getTopicTitle())
.setContentText(contentText)
.setAutoCancel(true)
.setContentIntent(pendingIntent)
- .setDefaults(Notification.DEFAULT_ALL)
+ .setDefaults(notificationDefaultValues)
.setGroup(GROUP_KEY)
.addExtras(notificationExtras);
+ //Checks for values other than defaults and applies them
+ if (notificationSoundUri != null) {
+ notificationBuilder.setSound(notificationSoundUri);
+ }
+ if (!notificationsVibrateEnabled) {
+ notificationBuilder.setVibrate(new long[]{0L});
+ }
if (buildVersion < Build.VERSION_CODES.O)
notificationBuilder.setPriority(PRIORITY_MAX);
boolean createSummaryNotification = false;
- if(buildVersion >= Build.VERSION_CODES.M)
+ if (buildVersion >= Build.VERSION_CODES.M)
createSummaryNotification = otherNotificationsExist(topicId);
NotificationCompat.Builder summaryNotificationBuilder = null;
- if(createSummaryNotification)
- {
+ if (createSummaryNotification) {
summaryNotificationBuilder =
new NotificationCompat.Builder(this, CHANNEL_ID)
- .setSmallIcon(R.mipmap.ic_launcher)
+ .setSmallIcon(R.drawable.ic_notification)
.setGroupSummary(true)
.setGroup(GROUP_KEY)
.setAutoCancel(true)
@@ -131,8 +153,6 @@ public class NotificationService extends FirebaseMessagingService {
}
-
-
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// Since Android Oreo notification channel is needed.
@@ -141,17 +161,16 @@ public class NotificationService extends FirebaseMessagingService {
notificationManager.notify(NEW_POST_TAG, topicId, notificationBuilder.build());
- if(createSummaryNotification)
- notificationManager.notify(SUMMARY_TAG,0, summaryNotificationBuilder.build());
+ if (createSummaryNotification)
+ notificationManager.notify(SUMMARY_TAG, 0, summaryNotificationBuilder.build());
}
@RequiresApi(api = Build.VERSION_CODES.M)
private Notification getActiveNotification(int notificationId) {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- if(notificationManager!=null)
- {
+ if (notificationManager != null) {
StatusBarNotification[] barNotifications = notificationManager.getActiveNotifications();
- for(StatusBarNotification notification: barNotifications) {
+ for (StatusBarNotification notification : barNotifications) {
if (notification.getId() == notificationId)
return notification.getNotification();
}
@@ -161,13 +180,13 @@ public class NotificationService extends FirebaseMessagingService {
}
@RequiresApi(api = Build.VERSION_CODES.M)
- private boolean otherNotificationsExist(int notificationId){
+ private boolean otherNotificationsExist(int notificationId) {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- if(notificationManager!=null) {
+ if (notificationManager != null) {
StatusBarNotification[] barNotifications = notificationManager.getActiveNotifications();
for (StatusBarNotification notification : barNotifications) {
String tag = notification.getTag();
- if (tag!=null && tag.equals(NEW_POST_TAG) && notification.getId() != notificationId)
+ if (tag != null && tag.equals(NEW_POST_TAG) && notification.getId() != notificationId)
return true;
}
}
@@ -177,24 +196,8 @@ public class NotificationService extends FirebaseMessagingService {
@Override
public void onDeletedMessages() {
super.onDeletedMessages();
- NotificationCompat.Builder notificationBuilder =
- new NotificationCompat.Builder(this, CHANNEL_ID)
- .setSmallIcon(R.mipmap.ic_launcher)
- .setContentTitle("Error fetching notifications!")
- .setContentText("Some notifications may not have arrived successfully either due to" +
- "the amount of pending messages (>100) or if the device hasn't come online for more than a month.")
- .setAutoCancel(true)
- .setDefaults(Notification.DEFAULT_ALL);
-
- if (buildVersion < Build.VERSION_CODES.O)
- notificationBuilder.setPriority(Notification.PRIORITY_MAX);
-
- NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ Timber.w("onDeletedMessages");
+ }
- // Since Android Oreo notification channel is needed.
- if (buildVersion >= Build.VERSION_CODES.O)
- notificationManager.createNotificationChannel(new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH));
- notificationManager.notify(DELETED_MESSAGES_TAG, 0, notificationBuilder.build());
- }
}
diff --git a/app/src/main/res/drawable-hdpi/ic_notification.png b/app/src/main/res/drawable-hdpi/ic_notification.png
new file mode 100644
index 00000000..6b78b6e6
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_notification.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_notification.png b/app/src/main/res/drawable-mdpi/ic_notification.png
new file mode 100644
index 00000000..b8c700e0
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_notification.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_notification.png b/app/src/main/res/drawable-xhdpi/ic_notification.png
new file mode 100644
index 00000000..79982cca
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_notification.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_notification.png b/app/src/main/res/drawable-xxhdpi/ic_notification.png
new file mode 100644
index 00000000..00a4e37b
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_notification.png differ
diff --git a/app/src/main/res/layout/activity_bookmark.xml b/app/src/main/res/layout/activity_bookmark.xml
index 939fb01a..d35191dd 100644
--- a/app/src/main/res/layout/activity_bookmark.xml
+++ b/app/src/main/res/layout/activity_bookmark.xml
@@ -54,6 +54,4 @@
app:layout_anchorGravity="bottom|center"
app:mpb_indeterminateTint="@color/accent"
app:mpb_progressStyle="horizontal"/>
-
-
-
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml
new file mode 100644
index 00000000..a6a0513b
--- /dev/null
+++ b/app/src/main/res/layout/activity_settings.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml
index 72e2ac82..628b2a36 100644
--- a/app/src/main/res/values-v21/styles.xml
+++ b/app/src/main/res/values-v21/styles.xml
@@ -24,5 +24,6 @@
- true
- true
- @android:color/transparent
+ - @style/PreferenceThemeOverlay.v14.Material
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 3187395d..d3d7b1e4 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -6,6 +6,7 @@
Authenticating…
Logout
Downloads
+ Settings
About
Home
Bookmarks
@@ -74,6 +75,7 @@
Remove
You have no bookmarked boards
You have no bookmarked topics
+ Toggle Notification
@@ -91,13 +93,26 @@
-
- Toggle Notification
-
File \"%1$s\" already exists. Download again?"
Download Symbol
Cancel
Open
Download
+
+
+ Settings
+ Settings
+
+ Notifications
+
+ Vibration
+ Summary
+ Notifications sound
+ Select your preferred notification sound
+
+ Posting
+ App signature
+ If enabled, a \"Posted from mThmmy\" message will be inserted at the end of your posts
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 3f32d79c..543cc9b2 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -23,6 +23,7 @@