diff --git a/app/build.gradle b/app/build.gradle index 29faec1f..777af2d6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,15 +7,15 @@ apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.firebase.crashlytics' android { - compileSdkVersion 30 + compileSdkVersion 31 buildToolsVersion = '30.0.2' defaultConfig { applicationId "gr.thmmy.mthmmy" minSdkVersion 21 targetSdkVersion 30 - versionCode 30 - versionName "2.0.0" + versionCode 31 + versionName "2.1.0" archivesBaseName = "mTHMMY-v$versionName" buildConfigField "String", "CURRENT_BRANCH", "\"" + getCurrentBranch() + "\"" buildConfigField "String", "COMMIT_HASH", "\"" + getCommitHash() + "\"" @@ -76,14 +76,14 @@ tasks.whenTaskAdded { task -> dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation project(":emojis") - implementation 'androidx.appcompat:appcompat:1.4.0-alpha03' + implementation 'androidx.appcompat:appcompat:1.4.1' implementation 'androidx.preference:preference:1.1.1' implementation 'androidx.legacy:legacy-preference-v14:1.0.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.recyclerview:recyclerview:1.2.1' implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' - implementation 'androidx.constraintlayout:constraintlayout:2.1.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.3' implementation 'androidx.exifinterface:exifinterface:1.3.3' implementation 'com.google.android.material:material:1.4.0' implementation platform('com.google.firebase:firebase-bom:28.4.0') diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9003cab6..7c7d5b18 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,8 +7,8 @@ - + @@ -41,6 +41,7 @@ android:configChanges="orientation|screenSize" android:label="@string/app_name" android:launchMode="singleTask" + android:exported="true" android:theme="@style/AppTheme.NoActionBar"> diff --git a/app/src/main/assets/style.css b/app/src/main/assets/style.css index 27c7f39f..6e6feb33 100644 --- a/app/src/main/assets/style.css +++ b/app/src/main/assets/style.css @@ -188,7 +188,7 @@ a img { margin: 1px; padding: 1px; font-size: x-small; - line-height: 1.4em; + line-height: 1.4; } @@ -199,7 +199,7 @@ a img { background-color: #626566; font-family: "Comic Sans MS", "times new roman", monospace; font-size: x-small; - line-height: 1.3em; + line-height: 1.3; /* Put a nice border around it. */ border: 1px solid #FFFFFF; margin: 1px auto 1px auto; @@ -223,7 +223,7 @@ a img { font-style: normal; font-weight: bold; font-size: x-small; - line-height: 1.2em; + line-height: 1.2; } @@ -361,7 +361,7 @@ tr.titlebg2 td, .personalmessage { width: 100%; overflow: auto; - line-height: 1.3em; + line-height: 1.3; color: white; background: #3C3F41 !important; } @@ -373,7 +373,7 @@ tr.titlebg2 td, width: 100%; overflow: auto; padding-bottom: 3px; - line-height: 1.3em; + line-height: 1.3; } #left { @@ -597,3 +597,7 @@ span[style="background-color: yellow;"] { [style="color: white;"]>span[style="background-color: yellow;"] { color: white !important; } + +span[style*="line-height: 1.3em;"] { + line-height: 1.3 !important; +} diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardActivity.java index 8c9820cb..3bed0b07 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardActivity.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardActivity.java @@ -250,7 +250,6 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo if (pStats.equals("--")) pStats = ""; } - else if (Objects.equals(subBoardCol.className(), "smalltext")) { pLastPost = subBoardCol.text(); if (pLastPost.contains(" in ") || pLastPost.contains(" σε ")) { @@ -281,7 +280,6 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo parsingFailed = true; break; } - } else if (pLastPost.contains("redirected clicks") || pLastPost.contains("N/A")) pLastPost = ""; @@ -289,10 +287,17 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo pLastPost = "No posts yet"; } else { - pUrl = subBoardCol.select("a").first().attr("href"); - pTitle = subBoardCol.select("a").first().text(); - if (subBoardCol.select("div.smalltext").first() != null) - pMods = subBoardCol.select("div.smalltext").first().text(); + Element subBoardTitleElement = subBoardCol.select("a").first(); + if (subBoardTitleElement != null) { + pUrl = subBoardTitleElement.attr("href"); + pTitle = subBoardTitleElement.text(); + if (subBoardCol.select("div.smalltext").first() != null) + pMods = subBoardCol.select("div.smalltext").first().text(); + } + else { + parsingFailed = true; + break; + } } } if (!parsingFailed) diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardAdapter.java index 3594fe79..6468d130 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardAdapter.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardAdapter.java @@ -32,11 +32,9 @@ import static gr.thmmy.mthmmy.activities.topic.TopicActivity.BUNDLE_TOPIC_URL; * {@link RecyclerView.Adapter} that can display a {@link gr.thmmy.mthmmy.model.Board}. */ class BoardAdapter extends RecyclerView.Adapter { - private final int VIEW_TYPE_SUB_BOARD_TITLE = 0; - private final int VIEW_TYPE_SUB_BOARD = 1; - private final int VIEW_TYPE_TOPIC_TITLE = 2; - private final int VIEW_TYPE_TOPIC = 3; - private final int VIEW_TYPE_LOADING = 4; + private final int VIEW_TYPE_SUB_BOARD = 0; + private final int VIEW_TYPE_TOPIC = 1; + private final int VIEW_TYPE_LOADING = 2; private final Context context; private ArrayList parsedSubBoards; @@ -56,13 +54,11 @@ class BoardAdapter extends RecyclerView.Adapter { @Override public int getItemViewType(int position) { - if (position <= parsedSubBoards.size()) { - if (position == 0) return VIEW_TYPE_SUB_BOARD_TITLE; + if (position < parsedSubBoards.size()) { return VIEW_TYPE_SUB_BOARD; } - else if (position <= parsedSubBoards.size() + parsedTopics.size() + 1) { - if (position == parsedSubBoards.size() + 1) return VIEW_TYPE_TOPIC_TITLE; - if (parsedTopics.get(position - parsedSubBoards.size() - 1 - 1) != null) + else if (position < parsedSubBoards.size() + parsedTopics.size()) { + if (parsedTopics.get(position - parsedSubBoards.size()) != null) //?? return VIEW_TYPE_TOPIC; } return VIEW_TYPE_LOADING; @@ -70,52 +66,11 @@ class BoardAdapter extends RecyclerView.Adapter { @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - if (viewType == VIEW_TYPE_SUB_BOARD_TITLE) { - TextView subBoardTitle = new TextView(context); - subBoardTitle.setLayoutParams(new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT - , LinearLayout.LayoutParams.WRAP_CONTENT)); - subBoardTitle.setText(context.getString(R.string.child_board_title)); - subBoardTitle.setTypeface(subBoardTitle.getTypeface(), Typeface.BOLD); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - subBoardTitle.setBackgroundColor(context.getColor(R.color.background_light)); - subBoardTitle.setTextColor(context.getColor(R.color.accent)); - } - else { - //noinspection deprecation - subBoardTitle.setBackgroundColor(context.getResources().getColor(R.color.background_light)); - //noinspection deprecation - subBoardTitle.setTextColor(context.getResources().getColor(R.color.accent)); - } - subBoardTitle.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); - subBoardTitle.setTextSize(20f); - - return new TitlesViewHolder(subBoardTitle); - } - else if (viewType == VIEW_TYPE_SUB_BOARD) { + if (viewType == VIEW_TYPE_SUB_BOARD) { View subBoard = LayoutInflater.from(parent.getContext()). inflate(R.layout.activity_board_sub_board_row, parent, false); return new SubBoardViewHolder(subBoard); } - else if (viewType == VIEW_TYPE_TOPIC_TITLE) { - TextView topicTitle = new TextView(context); - topicTitle.setLayoutParams(new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT - , LinearLayout.LayoutParams.WRAP_CONTENT)); - topicTitle.setText(context.getString(R.string.topic_title)); - topicTitle.setTypeface(topicTitle.getTypeface(), Typeface.BOLD); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - topicTitle.setTextColor(context.getColor(R.color.primary_text)); - } - else { - //noinspection deprecation - topicTitle.setTextColor(context.getResources().getColor(R.color.primary_text)); - } - topicTitle.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); - topicTitle.setTextSize(20f); - - return new TitlesViewHolder(topicTitle); - } else if (viewType == VIEW_TYPE_TOPIC) { View topic = LayoutInflater.from(parent.getContext()). inflate(R.layout.activity_board_topic_row, parent, false); @@ -132,7 +87,7 @@ class BoardAdapter extends RecyclerView.Adapter { @Override public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) { if (holder instanceof SubBoardViewHolder) { - final Board subBoard = parsedSubBoards.get(position - 1); + final Board subBoard = parsedSubBoards.get(position); final SubBoardViewHolder subBoardViewHolder = (SubBoardViewHolder) holder; if (boardExpandableVisibility.size() != parsedSubBoards.size()) { @@ -149,7 +104,7 @@ class BoardAdapter extends RecyclerView.Adapter { intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); }); - if (boardExpandableVisibility.get(subBoardViewHolder.getAdapterPosition() - 1)) { + if (boardExpandableVisibility.get(subBoardViewHolder.getAdapterPosition())) { subBoardViewHolder.boardExpandable.setVisibility(View.VISIBLE); subBoardViewHolder.showHideExpandable.setImageResource(R.drawable.ic_arrow_drop_up_accent_24dp); } @@ -158,7 +113,7 @@ class BoardAdapter extends RecyclerView.Adapter { subBoardViewHolder.showHideExpandable.setImageResource(R.drawable.ic_arrow_drop_down_accent_24dp); } subBoardViewHolder.showHideExpandable.setOnClickListener(view -> { - final boolean visible = boardExpandableVisibility.get(subBoardViewHolder.getAdapterPosition() - 1); + final boolean visible = boardExpandableVisibility.get(subBoardViewHolder.getAdapterPosition()); if (visible) { subBoardViewHolder.boardExpandable.setVisibility(View.GONE); subBoardViewHolder.showHideExpandable.setImageResource(R.drawable.ic_arrow_drop_down_accent_24dp); @@ -167,7 +122,7 @@ class BoardAdapter extends RecyclerView.Adapter { subBoardViewHolder.boardExpandable.setVisibility(View.VISIBLE); subBoardViewHolder.showHideExpandable.setImageResource(R.drawable.ic_arrow_drop_up_accent_24dp); } - boardExpandableVisibility.set(subBoardViewHolder.getAdapterPosition() - 1, !visible); + boardExpandableVisibility.set(subBoardViewHolder.getAdapterPosition(), !visible); }); subBoardViewHolder.boardTitle.setText(subBoard.getTitle()); String mods = subBoard.getMods(); @@ -202,7 +157,7 @@ class BoardAdapter extends RecyclerView.Adapter { } } else if (holder instanceof TopicViewHolder) { - final Topic topic = parsedTopics.get(position - parsedSubBoards.size() - 1 - 1); + final Topic topic = parsedTopics.get(position - parsedSubBoards.size()); final TopicViewHolder topicViewHolder = (TopicViewHolder) holder; if (topicExpandableVisibility.size() != parsedTopics.size()) { @@ -220,7 +175,7 @@ class BoardAdapter extends RecyclerView.Adapter { context.startActivity(intent); }); if (topicExpandableVisibility.get(topicViewHolder.getAdapterPosition() - parsedSubBoards - .size() - 2)) { + .size())) { topicViewHolder.topicExpandable.setVisibility(View.VISIBLE); topicViewHolder.showHideExpandable.setImageResource(R.drawable.ic_arrow_drop_up_accent_24dp); } @@ -230,7 +185,7 @@ class BoardAdapter extends RecyclerView.Adapter { } topicViewHolder.showHideExpandable.setOnClickListener(view -> { final boolean visible = topicExpandableVisibility.get(topicViewHolder. - getAdapterPosition() - parsedSubBoards.size() - 2); + getAdapterPosition() - parsedSubBoards.size()); if (visible) { topicViewHolder.topicExpandable.setVisibility(View.GONE); topicViewHolder.showHideExpandable.setImageResource(R.drawable.ic_arrow_drop_down_accent_24dp); @@ -240,7 +195,7 @@ class BoardAdapter extends RecyclerView.Adapter { topicViewHolder.showHideExpandable.setImageResource(R.drawable.ic_arrow_drop_up_accent_24dp); } topicExpandableVisibility.set(topicViewHolder.getAdapterPosition() - - parsedSubBoards.size() - 2, !visible); + parsedSubBoards.size(), !visible); }); topicViewHolder.topicSubject.setTypeface(Typeface.createFromAsset(context.getAssets() , "fonts/fontawesome-webfont.ttf")); @@ -280,8 +235,8 @@ class BoardAdapter extends RecyclerView.Adapter { @Override public int getItemCount() { int items = 0; - if (parsedSubBoards != null) items += parsedSubBoards.size() + 1; - if (parsedTopics != null) items += parsedTopics.size() + 1; + if (parsedSubBoards != null) items += parsedSubBoards.size(); + if (parsedTopics != null) items += parsedTopics.size(); return items; } @@ -320,12 +275,6 @@ class BoardAdapter extends RecyclerView.Adapter { } } - private static class TitlesViewHolder extends RecyclerView.ViewHolder { - TitlesViewHolder(View title) { - super(title); - } - } - private static class LoadingViewHolder extends RecyclerView.ViewHolder { final MaterialProgressBar progressBar; diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksActivity.java index 7fbdf746..6ce16ec0 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksActivity.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksActivity.java @@ -89,7 +89,9 @@ public class BookmarksActivity extends BaseActivity { startActivity(intent); break; case BookmarksFragment.INTERACTION_TOGGLE_TOPIC_NOTIFICATION: - return toggleNotification(bookmarkedTopic); + boolean notificationsEnabled = toggleNotification(bookmarkedTopic); + displayNotificationsToggleToast(notificationsEnabled); + return notificationsEnabled; case BookmarksFragment.INTERACTION_REMOVE_TOPIC_BOOKMARK: removeBookmark(bookmarkedTopic); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Bookmark removed", Toast.LENGTH_SHORT).show(); @@ -112,7 +114,9 @@ public class BookmarksActivity extends BaseActivity { startActivity(intent); break; case BookmarksFragment.INTERACTION_TOGGLE_BOARD_NOTIFICATION: - return toggleNotification(bookmarkedBoard); + boolean notificationsEnabled = toggleNotification(bookmarkedBoard); + displayNotificationsToggleToast(notificationsEnabled); + return notificationsEnabled; case BookmarksFragment.INTERACTION_REMOVE_BOARD_BOOKMARK: removeBookmark(bookmarkedBoard); Toast.makeText(getApplicationContext(), "Bookmark removed", Toast.LENGTH_SHORT).show(); @@ -123,6 +127,11 @@ public class BookmarksActivity extends BaseActivity { return true; } + private void displayNotificationsToggleToast (boolean notificationsEnabled){ + String toastText = notificationsEnabled ? "Notifications enabled" : "Notifications disabled"; + Toast.makeText(BaseApplication.getInstance().getApplicationContext(), toastText, Toast.LENGTH_SHORT).show(); + } + /** * A {@link FragmentPagerAdapter} that returns a fragment corresponding to * one of the sections/tabs/pages. If it becomes too memory intensive, diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksFragment.java index 50ecb812..3c2713d1 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksFragment.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksFragment.java @@ -1,6 +1,7 @@ package gr.thmmy.mthmmy.activities.bookmarks; import android.app.Activity; +import android.content.Intent; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.view.LayoutInflater; @@ -11,11 +12,14 @@ import android.widget.LinearLayout; import android.widget.TextView; import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.Fragment; import java.util.ArrayList; import gr.thmmy.mthmmy.R; +import gr.thmmy.mthmmy.activities.LoginActivity; +import gr.thmmy.mthmmy.activities.board.BoardActivity; import gr.thmmy.mthmmy.model.Bookmark; //TODO refactor using RecyclerView @@ -83,10 +87,8 @@ public class BookmarksFragment extends Fragment { } } - notificationsEnabledButtonImage = getResources().getDrawable(R.drawable.ic_notification_on, null); notificationsDisabledButtonImage = getResources().getDrawable(R.drawable.ic_notification_off, null); - } @Override @@ -104,7 +106,7 @@ public class BookmarksFragment extends Fragment { if (bookmark != null && bookmark.getTitle() != null) { final LinearLayout row = (LinearLayout) layoutInflater.inflate( R.layout.fragment_bookmarks_row, bookmarksLinearView, false); - row.setOnClickListener(view -> { + row.findViewById(R.id.bookmark_card).setOnClickListener(view -> { Activity activity = getActivity(); if (activity instanceof BookmarksActivity) ((BookmarksActivity) activity).onFragmentRowInteractionListener(type, interactionClick, bookmark); @@ -129,13 +131,19 @@ public class BookmarksFragment extends Fragment { (row.findViewById(R.id.remove_bookmark)).setOnClickListener(view -> { Activity activity = getActivity(); if (activity instanceof BookmarksActivity) { - ((BookmarksActivity) activity).onFragmentRowInteractionListener(type, interactionRemove, bookmark); - bookmarks.remove(bookmark); - } - row.setVisibility(View.GONE); - - if (bookmarks.isEmpty()) { - showNothingBookmarked(); + new AlertDialog.Builder(activity) + .setMessage("Are you sure that you want to remove this bookmark?") + .setPositiveButton("Yes", (dialogInterface, i) -> { + ((BookmarksActivity) activity).onFragmentRowInteractionListener(type, interactionRemove, bookmark); + bookmarks.remove(bookmark); + row.setVisibility(View.GONE); + if (bookmarks.isEmpty()) { + showNothingBookmarked(); + } + }) + .setNegativeButton("No", (dialogInterface, i) -> { + }) + .show(); } }); bookmarksLinearView.addView(row); diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java index c7e3f126..959a0939 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java @@ -633,8 +633,11 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo else { Timber.i("Post edit unsuccessful"); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Edit failed!", Toast.LENGTH_SHORT).show(); - recyclerView.getChildAt(viewModel.getPostBeingEditedPosition()).setAlpha(1); - recyclerView.getChildAt(viewModel.getPostBeingEditedPosition()).setEnabled(true); + View postBeingEditedView = recyclerView.getChildAt(viewModel.getPostBeingEditedPosition()); + if (postBeingEditedView != null) { + postBeingEditedView.setAlpha(1); + postBeingEditedView.setEnabled(true); + } } } }); diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/TopicTask.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/TopicTask.java index 8ba72b74..9b2f8e20 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/TopicTask.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/TopicTask.java @@ -125,7 +125,7 @@ public class TopicTask extends AsyncTask { 0, 0, 0, 0, null, null); } else { - Timber.e(e, "Topic parse failed"); + Timber.e(e, "Topic parsing failed (%s)!", newPageUrl); return new TopicTaskResult(ResultCode.PARSING_ERROR, null, null, null, 0, 0, 0, 0, null, null); } diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadActivity.java index e0b5b986..48ec3b5b 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadActivity.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadActivity.java @@ -10,6 +10,7 @@ import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.AsyncTask; +import android.os.Build; import android.os.Bundle; import android.text.Editable; import android.text.Spannable; @@ -327,30 +328,20 @@ public class UploadActivity extends BaseActivity { //File should be uploaded with a different name if (checkPerms()) { - if (!uploadFile.isCameraPhoto()) { - //Temporarily copies the file to a another location and renames it - tempFileUri = UploadsHelper.createTempFile(this, storage, - uploadFile.getFileUri(), - FileUtils.getFilenameWithoutExtension(editTextFilename)); - } - else { - //Renames the photo taken - String photoPath = uploadFile.getPhotoFile().getPath(); - photoPath = photoPath.substring(0, photoPath.lastIndexOf(File.separator)); - String destinationFilename = photoPath + File.separator + - FileUtils.getFilenameWithoutExtension(editTextFilename) + ".jpg"; - - if (!storage.rename(uploadFile.getPhotoFile().getAbsolutePath(), destinationFilename)) { - //Something went wrong, abort - Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Could not create temporary file for renaming", Toast.LENGTH_SHORT).show(); - progressBar.setVisibility(View.GONE); - return; - } - - //Points photoFile and fileUri to the new copied and renamed file - uploadFile.setPhotoFile(storage.getFile(destinationFilename)); - uploadFile.setFileUri(FileProvider.getUriForFile(this, getPackageName() + - ".provider", uploadFile.getPhotoFile())); + String newFileName = FileUtils.getFilenameWithoutExtension(editTextFilename); + + if(uploadFile.isCameraPhoto()) + newFileName = newFileName + ".jpg"; + + //Temporarily copies the file to another location (external cache) and renames it + tempFileUri = UploadsHelper.createTempFile(this, storage, + uploadFile.getFileUri(), + newFileName); + + //Something went wrong while creating temporary file, abort + if(tempFileUri == null){ + progressBar.setVisibility(View.GONE); + return; } } else { @@ -561,7 +552,7 @@ public class UploadActivity extends BaseActivity { UploadFile newFile = new UploadFile(true, TakePhoto.processResult(this, photoFileCreated), photoFileCreated); addFileViewToList(FileUtils.getFilenameWithoutExtension(FileUtils. - filenameFromUri(this, newFile.getFileUri()))); + filenameFromUri(this, newFile.getFileUri())) + ".jpg"); filesList.add(newFile); } else if (requestCode == AFR_REQUEST_CODE_FIELDS_BUILDER) { @@ -596,6 +587,10 @@ public class UploadActivity extends BaseActivity { case UPLOAD_REQUEST_CAMERA_CODE: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) takePhoto(); + else { + Timber.w("Take photo failed (permission denied)."); + Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Take photo failed (storage permission denied)", Toast.LENGTH_SHORT).show(); + } break; case UPLOAD_REQUEST_STORAGE_CODE: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED && @@ -609,6 +604,7 @@ public class UploadActivity extends BaseActivity { finish(); } else { + Timber.w("Zip task failed (permission denied)."); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Please retry uploading.", Toast.LENGTH_SHORT).show(); } break; @@ -720,17 +716,23 @@ public class UploadActivity extends BaseActivity { cancelIntent.setAction(UploadsReceiver.ACTION_CANCEL_UPLOAD); cancelIntent.putExtra(UploadsReceiver.UPLOAD_ID_KEY, uploadID); + int pendingIntentFlags; + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) + pendingIntentFlags = PendingIntent.FLAG_MUTABLE |PendingIntent.FLAG_UPDATE_CURRENT; + else + pendingIntentFlags = PendingIntent.FLAG_UPDATE_CURRENT; + uploadNotificationConfig.getProgress().actions.add(new UploadNotificationAction( R.drawable.ic_cancel_accent_24dp, context.getString(R.string.cancel), PendingIntent.getBroadcast(context, 0, cancelIntent, - PendingIntent.FLAG_UPDATE_CURRENT) + pendingIntentFlags) )); uploadNotificationConfig.getError().actions.add(new UploadNotificationAction( R.drawable.ic_notification, context.getString(R.string.upload_retry), PendingIntent.getBroadcast(context, 0, retryIntent, - PendingIntent.FLAG_UPDATE_CURRENT) + pendingIntentFlags) )); return uploadNotificationConfig; @@ -1069,7 +1071,7 @@ public class UploadActivity extends BaseActivity { @Override protected void onPreExecute() { assert weakActivity != null; - Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Zipping files", Toast.LENGTH_SHORT).show(); + Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Zipping files...", Toast.LENGTH_SHORT).show(); } @Override @@ -1077,7 +1079,7 @@ public class UploadActivity extends BaseActivity { if (weakActivity == null || zipFilename == null) return false; - File zipFile = UploadsHelper.createZipFile(zipFilename); + File zipFile = UploadsHelper.createZipFile(weakActivity.get(), zipFilename); if (zipFile == null) return false; diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsHelper.java b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsHelper.java index 73b7fd21..af91d72c 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsHelper.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsHelper.java @@ -2,7 +2,6 @@ package gr.thmmy.mthmmy.activities.upload; import android.content.Context; import android.net.Uri; -import android.os.Environment; import android.widget.Toast; import androidx.annotation.NonNull; @@ -27,24 +26,19 @@ import timber.log.Timber; public class UploadsHelper { private static final int BUFFER = 4096; - private static final String TEMP_FILES_DIRECTORY = "~tmp_mTHMMY_uploads"; @SuppressWarnings("ResultOfMethodCallIgnored") @Nullable static Uri createTempFile(Context context, Storage storage, Uri fileUri, String newFilename) { + Timber.i("Creating new temporary file (%s)", newFilename); String oldFilename = FileUtils.filenameFromUri(context, fileUri); String fileExtension = oldFilename.substring(oldFilename.indexOf('.')); - String destinationFilename = Environment.getExternalStorageDirectory().getPath() + - File.separatorChar + TEMP_FILES_DIRECTORY + File.separatorChar + newFilename + fileExtension; - File tempDirectory = new File(android.os.Environment.getExternalStorageDirectory().getPath() + - File.separatorChar + TEMP_FILES_DIRECTORY); - - if (!tempDirectory.exists() && !tempDirectory.mkdirs()) { - Timber.w("Temporary directory build returned false in %s", UploadActivity.class.getSimpleName()); - Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Couldn't create temporary directory", Toast.LENGTH_SHORT).show(); + File tempFilesDirectory = createTempFilesDir(context); + if (tempFilesDirectory==null) return null; - } + + String destinationFilename = tempFilesDirectory.getAbsolutePath() + File.separatorChar + newFilename + fileExtension; InputStream inputStream; BufferedInputStream bufferedInputStream = null; @@ -53,7 +47,7 @@ public class UploadsHelper { try { inputStream = context.getContentResolver().openInputStream(fileUri); if (inputStream == null) { - Timber.w("Input stream was null, %s", UploadActivity.class.getSimpleName()); + Timber.e("Input stream was null, %s", UploadActivity.class.getSimpleName()); return null; } @@ -81,20 +75,19 @@ public class UploadsHelper { } @Nullable - static File createZipFile(@NonNull String zipFilename) { - // Create a zip file name - File zipFolder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) + - File.separator + "mTHMMY"); + static File createZipFile(Context context, @NonNull String zipFilename) { + // Create a zip file in temp directory + Timber.i("Creating temporary zip file %s", zipFilename); - if (!zipFolder.exists() && !zipFolder.mkdirs()) { - Timber.w("Zip folder build returned false in %s", UploadsHelper.class.getSimpleName()); + File tempFilesDirectory = createTempFilesDir(context); + if (tempFilesDirectory==null) return null; - } - return new File(zipFolder, zipFilename); + return new File(tempFilesDirectory, zipFilename); } static void zip(Context context, Uri[] files, Uri zipFile) { + Timber.i("Adding files to %s...", zipFile.getPath()); try { BufferedInputStream origin; OutputStream dest = context.getContentResolver().openOutputStream(zipFile); @@ -117,21 +110,45 @@ public class UploadsHelper { origin.close(); } + Timber.i("Files added successfully to %s.", zipFile.getPath()); + out.close(); } catch (Exception e) { e.printStackTrace(); } } - public static void deleteTempFiles(Storage storage) { - File tempFilesDirectory = new File(Environment.getExternalStorageDirectory().getPath() + - File.separatorChar + TEMP_FILES_DIRECTORY); + private static File createTempFilesDir(Context context){ + File tempFilesDirectory = context.getExternalCacheDir(); + + if (tempFilesDirectory == null){ + Timber.e("Temporary files directory error (%s)!", UploadActivity.class.getSimpleName()); + Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Temporary files directory error (%s)!", Toast.LENGTH_SHORT).show(); + return null; + } - if (storage.isDirectoryExists(tempFilesDirectory.getAbsolutePath())) { - for (File tempFile : storage.getFiles(tempFilesDirectory.getAbsolutePath())) { - storage.deleteFile(tempFile.getAbsolutePath()); + if (!tempFilesDirectory.exists() && !tempFilesDirectory.mkdirs()) { + Timber.e("Temporary directory %s creation returned false (%s)", tempFilesDirectory.getAbsolutePath(), UploadActivity.class.getSimpleName()); + Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Couldn't create temporary directory for file renaming!", Toast.LENGTH_SHORT).show(); + return null; + } + + return tempFilesDirectory; + } + + public static void deleteTempFiles(Context context, Storage storage) { + File tempFilesDirectory = context.getExternalCacheDir(); + + if (tempFilesDirectory != null){ + if (storage.isDirectoryExists(tempFilesDirectory.getAbsolutePath())) { + for (File tempFile : storage.getFiles(tempFilesDirectory.getAbsolutePath())) { + storage.deleteFile(tempFile.getAbsolutePath()); + } + storage.deleteDirectory(tempFilesDirectory.getAbsolutePath()); + Timber.i("Deleted temp files from cache."); } - storage.deleteDirectory(tempFilesDirectory.getAbsolutePath()); } + else + Timber.e("Couldn't delete temp files!"); } } 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 f2af6b7e..21e1af95 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java +++ b/app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java @@ -694,6 +694,7 @@ public abstract class BaseActivity extends AppCompatActivity { //True if permissions are OK protected boolean checkPerms() { if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) { + Timber.i("Checking storage permissions."); String[] PERMISSIONS_STORAGE = { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}; @@ -705,12 +706,14 @@ public abstract class BaseActivity extends AppCompatActivity { } //Display popup for user to grant permission - protected void requestPerms(int code) { //Runtime permissions request for devices with API >= 23 + protected void requestPerms(int code) { + //Runtime permissions request for devices with API >= 23 if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) { String[] PERMISSIONS_STORAGE = { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}; + Timber.i("Requesting storage permissions (code %d).", code); requestPermissions(PERMISSIONS_STORAGE, code); } } @@ -722,10 +725,13 @@ public abstract class BaseActivity extends AppCompatActivity { if (permsRequestCode == DOWNLOAD_REQUEST_CODE) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) prepareDownload(tempThmmyFile); + else { + Timber.w("Download failed (permission denied)."); + Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Download failed (storage permission denied)", Toast.LENGTH_SHORT).show(); + } } } - //----------------------------------DOWNLOAD---------------------- private ThmmyFile tempThmmyFile; diff --git a/app/src/main/java/gr/thmmy/mthmmy/base/BaseApplication.java b/app/src/main/java/gr/thmmy/mthmmy/base/BaseApplication.java index e9293dd2..5b1d0154 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/base/BaseApplication.java +++ b/app/src/main/java/gr/thmmy/mthmmy/base/BaseApplication.java @@ -147,7 +147,7 @@ public class BaseApplication extends Application implements Executor{ boolean updated = task.getResult(); Timber.i("Firebase remote config params updated: %s", updated); } else - Timber.e("Fetching Firebase remote config params failed!"); + Timber.w("Fetching Firebase remote config params failed!"); }); } 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 b271e39f..482dffdb 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/services/NotificationService.java +++ b/app/src/main/java/gr/thmmy/mthmmy/services/NotificationService.java @@ -178,8 +178,15 @@ public class NotificationService extends FirebaseMessagingService { extras.putString(BUNDLE_TOPIC_TITLE, postNotification.getTopicTitle()); intent.putExtras(extras); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + + int pendingIntentFlags; + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) + pendingIntentFlags = PendingIntent.FLAG_IMMUTABLE |PendingIntent.FLAG_ONE_SHOT; + else + pendingIntentFlags = PendingIntent.FLAG_ONE_SHOT; + PendingIntent pendingIntent = PendingIntent.getActivity(this, requestCode++, intent, - PendingIntent.FLAG_ONE_SHOT); + pendingIntentFlags); int notificationId; String contentText; diff --git a/app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java b/app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java index 9823d3d3..19c52396 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java +++ b/app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java @@ -105,7 +105,7 @@ public class UploadsReceiver extends UploadServiceBroadcastReceiver { String response = serverResponse.getBodyAsString(); if (response.contains("Η προσθήκη του αρχείου ήταν επιτυχημένη.") || response.contains("The upload was successful.")) { Timber.i("Upload completed successfully (id: %s)", uploadInfo.getUploadId()); - Toast.makeText(context.getApplicationContext(), "Upload completed successfully", Toast.LENGTH_SHORT).show(); + Toast.makeText(context.getApplicationContext(), "Upload completed successfully!", Toast.LENGTH_SHORT).show(); BaseApplication.getInstance().logFirebaseAnalyticsEvent("file_upload", null); } else { @@ -118,7 +118,7 @@ public class UploadsReceiver extends UploadServiceBroadcastReceiver { storage = new Storage(context.getApplicationContext()); } - UploadsHelper.deleteTempFiles(storage); + UploadsHelper.deleteTempFiles(context, storage); } @Override @@ -130,7 +130,7 @@ public class UploadsReceiver extends UploadServiceBroadcastReceiver { storage = new Storage(context.getApplicationContext()); //cancelNotification(context, uploadInfo.getNotificationID()); - UploadsHelper.deleteTempFiles(storage); + UploadsHelper.deleteTempFiles(context, storage); } public static void setDialogDisplay(AlertDialog uploadProgressDialog, String dialogUploadID, diff --git a/app/src/main/res/drawable/ic_notification_off.xml b/app/src/main/res/drawable/ic_notification_off.xml index 336141ef..f91fda60 100644 --- a/app/src/main/res/drawable/ic_notification_off.xml +++ b/app/src/main/res/drawable/ic_notification_off.xml @@ -6,4 +6,4 @@ - + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_upload_file_list_row.xml b/app/src/main/res/layout/activity_upload_file_list_row.xml index 08b74a4a..8af02800 100644 --- a/app/src/main/res/layout/activity_upload_file_list_row.xml +++ b/app/src/main/res/layout/activity_upload_file_list_row.xml @@ -3,7 +3,9 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="horizontal"> + android:orientation="horizontal" + android:paddingStart="8dp" + android:paddingEnd="0dp"> diff --git a/app/src/main/res/layout/fragment_bookmarks.xml b/app/src/main/res/layout/fragment_bookmarks.xml index e672fab8..280a9cc4 100644 --- a/app/src/main/res/layout/fragment_bookmarks.xml +++ b/app/src/main/res/layout/fragment_bookmarks.xml @@ -9,7 +9,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="top|start" - android:background="@color/primary_lighter" + android:background="@color/primary_lighter_2" android:scrollbars="none" app:layout_behavior="@string/appbar_scrolling_view_behavior"> @@ -17,10 +17,8 @@ android:id="@+id/bookmarks_container" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical" - android:showDividers="middle" - android:divider="?android:listDivider" - android:dividerPadding="16dp" /> + android:layout_margin="2dp" + android:orientation="vertical" /> + android:layout_height="wrap_content"> - + android:foreground="?android:attr/selectableItemBackground" + card_view:cardBackgroundColor="@color/background" + card_view:cardCornerRadius="5dp" + card_view:cardElevation="2dp" + card_view:cardPreventCornerOverlap="false" + card_view:cardUseCompatPadding="true" + android:clickable="true" + android:focusable="true"> + - + - + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f203b8cf..1e83bc57 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -95,9 +95,10 @@ Eclipse Public License v1.0 libraries Other libraries Contact - Do not hesitate to contact us for any matter either by email at thmmynolife@gmail.com, or by joining our discord server at https://discord.gg/CVt3yrn. + Do not hesitate to contact us for any technical matter, either by email at thmmynolife@gmail.com, or by joining our Discord server using https://discord.gg/CVt3yrn​. + For account-related issues, please contact the administration team of thmmy.gr by email at contact@thmmy.gr. Open Source - The source code of mTHMMY can be found on Github (https://github.com/ThmmyNoLife/mTHMMY) along with further details of how one can contribute. + The source code of mTHMMY can be found on Github at https://github.com/ThmmyNoLife/mTHMMY, along with further details of how one can contribute. You should see a funny pic! Privacy policy