From b28e402c3bf8777b37515d8cf39d2b662713f535 Mon Sep 17 00:00:00 2001 From: Ezerous Date: Mon, 24 Aug 2020 19:32:13 +0300 Subject: [PATCH 1/6] ProfileSummary optimizations --- app/src/main/assets/style_light.css | 1 + .../profile/summary/SummaryFragment.java | 127 +++++++++++------- .../res/layout/fragment_profile_summary.xml | 3 +- 3 files changed, 78 insertions(+), 53 deletions(-) diff --git a/app/src/main/assets/style_light.css b/app/src/main/assets/style_light.css index a3cafed2..b70e8585 100644 --- a/app/src/main/assets/style_light.css +++ b/app/src/main/assets/style_light.css @@ -8,4 +8,5 @@ body { .customSignature { background: #434649 !important; + margin-top: 0.5em; } diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java index d58112ea..ea257924 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java @@ -19,7 +19,8 @@ import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; -import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Objects; import gr.thmmy.mthmmy.R; @@ -35,10 +36,10 @@ public class SummaryFragment extends Fragment { */ private static final String PROFILE_DOCUMENT = "PROFILE_DOCUMENT"; /** - * {@link ArrayList} of Strings used to hold profile's information. Data are added in + * {@link HashMap} used to hold profile's information. Data are added in * {@link SummaryTask}. */ - private ArrayList parsedProfileSummaryData; + private LinkedHashMap parsedProfileSummaryData; /** * A {@link Document} holding this profile's source code. */ @@ -69,7 +70,7 @@ public class SummaryFragment extends Fragment { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); profileSummaryDocument = Jsoup.parse(requireArguments().getString(PROFILE_DOCUMENT)); - parsedProfileSummaryData = new ArrayList<>(); + parsedProfileSummaryData = new LinkedHashMap<>(); } @Override @@ -123,36 +124,42 @@ public class SummaryFragment extends Fragment { * @return ArrayList containing this profile's parsed information * @see org.jsoup.Jsoup Jsoup */ - ArrayList parseProfileSummary(Document profile) { - //Method's variables - ArrayList parsedInformation = new ArrayList<>(); + LinkedHashMap parseProfileSummary(Document profile) { + LinkedHashMap parsedInformation = new LinkedHashMap<>(); //Contains all summary's rows - Elements summaryRows = profile.select(".bordercolor > tbody:nth-child(1) > tr:nth-child(2) tr"); + Elements summaryRows = profile.select("td.windowbg > table > tbody > tr"); for (Element summaryRow : summaryRows) { - String rowText = summaryRow.text(), pHtml = ""; - - if (summaryRow.select("td").size() == 1) //Horizontal rule rows - pHtml = ""; - else if (summaryRow.text().contains("Current Status") - || summaryRow.text().contains("Κατάσταση")) continue; - else if (rowText.contains("Signature") || rowText.contains("Υπογραφή")) { - //This needs special handling since it may have css - pHtml = ParseHelpers.emojiTagToHtml(ParseHelpers.youtubeEmbeddedFix(summaryRow)); - //Add stuff to make it work in WebView - //style.css - pHtml = ("\n" + - "\n" + - "
\n" + pHtml + "\n
"); - } else if (!rowText.contains("Name") && !rowText.contains("Όνομα")) { //Doesn't add username twice - if (Objects.equals(summaryRow.select("td").get(1).text(), "")) + String key, value; + + int tdSize = summaryRow.select("td").size(); + + if (tdSize > 1){ + key = summaryRow.select("td").first().text().trim(); + + if (key.startsWith("Name") || key.startsWith("Όνομα")) continue; - //Style parsed information with html - pHtml = "" + summaryRow.select("td").first().text() + " " - + summaryRow.select("td").get(1).text(); + else if (key.startsWith("Signature") || key.startsWith("Υπογραφή")){ + key = summaryRow.selectFirst("td > table > tbody > tr > td").text().trim(); + summaryRow.selectFirst("td > table > tbody > tr").remove(); //key not needed, outer html needed for CSS + value = ParseHelpers.emojiTagToHtml(ParseHelpers.youtubeEmbeddedFix(summaryRow)); // Is emojiTagToHtml() really needed here? + if(summaryRow.text().trim().isEmpty()) + continue; + value = ("\n" + + "\n" + + "
\n" + value + "\n
"); + } + else { + if (summaryRow.select("td").get(1).text().isEmpty()) + continue; + if (key.startsWith("Date Registered") || key.startsWith("Ημερομηνία εγγραφής") || key.startsWith("Last Active") || key.startsWith("Τελευταία σύνδεση")) + value = summaryRow.select("td").get(1).text().trim(); + else + value = summaryRow.select("td").get(1).html().trim(); + } + parsedInformation.put(key, value); } - parsedInformation.add(pHtml); } return parsedInformation; } @@ -165,38 +172,54 @@ public class SummaryFragment extends Fragment { * {@link #parsedProfileSummaryData}

*/ private void populateLayout() { - for (String profileSummaryRow : parsedProfileSummaryData) { - if (profileSummaryRow.contains("Signature") - || profileSummaryRow.contains("Υπογραφή")) { //This may contain css - ReactiveWebView signatureEntry = new ReactiveWebView(this.getContext()); - signatureEntry.setBackgroundColor(Color.argb(1, 255, 255, 255)); - signatureEntry.loadDataWithBaseURL("file:///android_asset/", profileSummaryRow, - "text/html", "UTF-8", null); - mainContent.addView(signatureEntry); + for (LinkedHashMap.Entry entry : parsedProfileSummaryData.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + + if (key.startsWith("Current Status") || key.startsWith("Κατάσταση")){ + addEmptyView(); continue; } - TextView entry = new TextView(this.getContext()); - - if (profileSummaryRow.contains("@") && - (profileSummaryRow.contains("Email") || profileSummaryRow.contains("E-mail"))) { - String email = profileSummaryRow.substring(profileSummaryRow.indexOf(": ") + 6); - profileSummaryRow = profileSummaryRow.replace(email, - "" + email + ""); - entry.setMovementMethod(LinkMovementMethod.getInstance()); - } + + TextView textView = new TextView(this.getContext()); + + if (((key.startsWith("Email") || key.startsWith("E-mail")) + && value.contains("@")) || key.startsWith("Website") || key.startsWith("Ιστοτόπος")) + textView.setMovementMethod(LinkMovementMethod.getInstance()); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) - entry.setTextColor(getResources().getColor(R.color.primary_text, null)); + textView.setTextColor(getResources().getColor(R.color.primary_text, null)); else - entry.setTextColor(getResources().getColor(R.color.primary_text)); + textView.setTextColor(getResources().getColor(R.color.primary_text)); + + String textViewContent = "" + key + " " + value; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - entry.setText(Html.fromHtml(profileSummaryRow, Html.FROM_HTML_MODE_LEGACY)); - } else { - entry.setText(Html.fromHtml(profileSummaryRow)); + if (key.startsWith("Signature") || key.startsWith("Υπογραφή")){ + addEmptyView(); + textViewContent = "" + key + ""; } - mainContent.addView(entry); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) + textView.setText(Html.fromHtml(textViewContent, Html.FROM_HTML_MODE_LEGACY)); + else + textView.setText(Html.fromHtml(textViewContent)); + + mainContent.addView(textView); + + if (key.startsWith("Last Active") || key.startsWith("Τελευταία σύνδεση")) + addEmptyView(); + + if (key.startsWith("Signature") || key.startsWith("Υπογραφή")) { + ReactiveWebView signatureEntry = new ReactiveWebView(this.getContext()); + signatureEntry.setBackgroundColor(Color.argb(1, 255, 255, 255)); + signatureEntry.loadDataWithBaseURL("file:///android_asset/", value, + "text/html", "UTF-8", null); + mainContent.addView(signatureEntry); + } } } + + private void addEmptyView(){ + mainContent.addView(new TextView(this.getContext())); + } } diff --git a/app/src/main/res/layout/fragment_profile_summary.xml b/app/src/main/res/layout/fragment_profile_summary.xml index 098cad5a..65cdf1a8 100644 --- a/app/src/main/res/layout/fragment_profile_summary.xml +++ b/app/src/main/res/layout/fragment_profile_summary.xml @@ -5,8 +5,9 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/background_lighter" - android:paddingEnd="16dp" android:paddingStart="16dp" + android:paddingEnd="16dp" + android:paddingTop="16dp" android:scrollbars="none"> Date: Tue, 25 Aug 2020 14:54:50 +0300 Subject: [PATCH 2/6] BoardActivity hotfix, up version --- app/build.gradle | 6 +++--- .../thmmy/mthmmy/activities/board/BoardActivity.java | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index c73a3bd7..10c07e6e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -15,8 +15,8 @@ android { applicationId "gr.thmmy.mthmmy" minSdkVersion 19 targetSdkVersion 29 - versionCode 26 - versionName "1.8.3" + versionCode 27 + versionName "1.8.4" archivesBaseName = "mTHMMY-v$versionName" buildConfigField "String", "CURRENT_BRANCH", "\"" + getCurrentBranch() + "\"" buildConfigField "String", "COMMIT_HASH", "\"" + getCommitHash() + "\"" @@ -78,7 +78,7 @@ tasks.whenTaskAdded { task -> dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation project(":emojis") - implementation 'androidx.appcompat:appcompat:1.3.0-alpha01' + implementation 'androidx.appcompat:appcompat:1.3.0-alpha02' 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' 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 a4ede049..057b6d63 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 @@ -189,7 +189,7 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo * parameter!

*/ private class BoardTask extends ParseTask { - ArrayList tempSubboards = new ArrayList<>(); + ArrayList tempSubBoards = new ArrayList<>(); ArrayList tempTopics = new ArrayList<>(); @Override @@ -200,7 +200,7 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo @Override //TODO should throw ParseException public void parse(Document boardPage) throws ParseException { - tempSubboards.addAll(parsedSubBoards); + tempSubBoards.addAll(parsedSubBoards); tempTopics.addAll(parsedTopics); //Removes loading item if (isLoadingMore && tempTopics.size() > 0) @@ -292,7 +292,7 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo } } if(!parsingFailed) - tempSubboards.add(new Board(pUrl, pTitle, pMods, pStats, pLastPost, pLastPostUrl)); + tempSubBoards.add(new Board(pUrl, pTitle, pMods, pStats, pLastPost, pLastPostUrl)); else Timber.e("Parsing failed (pLastPost came with: \"%s\", subBoardColumns html was \"%s\")", pLastPost, subBoardColumns); } @@ -333,7 +333,7 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo continue; } - pLastPostUrl = topicColumns.last().select("a:has(img)").first().attr("href"); + pLastPostUrl = topicColumns.get(6).select("a:has(img)").first().attr("href"); tempTopics.add(new Topic(pTopicUrl, pSubject, pStarter, pLastUser, pLastPostDateTime, pLastPostUrl, pStats, pLocked, pSticky, pUnread)); } @@ -356,7 +356,7 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo parsedTopics.clear(); parsedSubBoards.clear(); parsedTopics.addAll(tempTopics); - parsedSubBoards.addAll(tempSubboards); + parsedSubBoards.addAll(tempSubBoards); boardAdapter.notifyDataSetChanged(); //Parse was successful From 5de855fe0e01ef7de648673174e25e01674212fe Mon Sep 17 00:00:00 2001 From: Ezerous Date: Tue, 25 Aug 2020 15:25:32 +0300 Subject: [PATCH 3/6] TopicAdapter crash fix --- .../profile/summary/SummaryFragment.java | 1 - .../mthmmy/activities/topic/TopicAdapter.java | 84 +++++-------------- 2 files changed, 21 insertions(+), 64 deletions(-) diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java index ea257924..749f42bc 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java @@ -21,7 +21,6 @@ import org.jsoup.select.Elements; import java.util.HashMap; import java.util.LinkedHashMap; -import java.util.Objects; import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.utils.parsing.ParseHelpers; diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java index def64530..0584192f 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java @@ -696,43 +696,13 @@ class TopicAdapter extends RecyclerView.Adapter { } holder.replyEditor.setText(replyText); holder.replyEditor.getEditText().setSelection(holder.replyEditor.getText().length()); - holder.replyEditor.getEditText().addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { - - } - - @Override - public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { - ((Post) topicItems.get(holder.getAdapterPosition())).setBbContent(charSequence.toString()); - } - - @Override - public void afterTextChanged(Editable editable) { - - } - }); + holder.replyEditor.getEditText().addTextChangedListener(createTextWatcher(holder)); if (backPressHidden) { holder.replyEditor.requestEditTextFocus(); backPressHidden = false; } - holder.quickReplySubject.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { - - } - - @Override - public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { - ((Post) topicItems.get(holder.getAdapterPosition())).setSubject(charSequence.toString()); - } - - @Override - public void afterTextChanged(Editable editable) { - - } - }); + holder.quickReplySubject.addTextChangedListener(createTextWatcher(holder)); } else if (currentHolder instanceof EditMessageViewHolder) { final EditMessageViewHolder holder = (EditMessageViewHolder) currentHolder; @@ -767,38 +737,9 @@ class TopicAdapter extends RecyclerView.Adapter { viewModel.editPost(position, holder.editSubject.getText().toString(), holder.editEditor.getText().toString()); }); - holder.editSubject.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { - - } - - @Override - public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { - ((Post) topicItems.get(holder.getAdapterPosition())).setSubject(charSequence.toString()); - } - - @Override - public void afterTextChanged(Editable editable) { - - } - }); - holder.editEditor.getEditText().addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { - - } - - @Override - public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { - ((Post) topicItems.get(holder.getAdapterPosition())).setBbContent(charSequence.toString()); - } - - @Override - public void afterTextChanged(Editable editable) { + holder.editSubject.addTextChangedListener(createTextWatcher(holder)); + holder.editEditor.getEditText().addTextChangedListener(createTextWatcher(holder)); - } - }); if (backPressHidden) { holder.editEditor.requestEditTextFocus(); backPressHidden = false; @@ -807,6 +748,23 @@ class TopicAdapter extends RecyclerView.Adapter { } } + private TextWatcher createTextWatcher(@NonNull final RecyclerView.ViewHolder holder){ + return new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { } + + @Override + public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { + int position = holder.getAdapterPosition(); + if (position >= 0 && position < topicItems.size()) + ((Post) topicItems.get(position)).setBbContent(charSequence.toString()); + } + + @Override + public void afterTextChanged(Editable editable) { } + }; + } + private void loadAvatar(String imageUrl, ImageView imageView, Context context) { if(imageUrl!=null) imageUrl = imageUrl.trim(); From 074d0055c6cf98a11f8c54aafad3793a5ce4b339 Mon Sep 17 00:00:00 2001 From: Ezerous Date: Tue, 25 Aug 2020 19:24:42 +0300 Subject: [PATCH 4/6] Cleanup, minor Shoutbox improvements --- app/src/main/AndroidManifest.xml | 29 ++++++----- .../activities/board/BoardActivity.java | 3 +- .../bookmarks/BookmarksActivity.java | 7 +-- .../main/recent/RecentFragment.java | 4 -- .../profile/summary/SummaryFragment.java | 3 +- .../activities/shoutbox/ShoutboxFragment.java | 5 +- .../mthmmy/activities/topic/TopicAdapter.java | 50 +++++++++---------- .../crashreporting/CrashReportingTree.java | 4 +- .../mthmmy/views/editorview/EditorView.java | 12 +++-- .../res/layout/activity_create_content.xml | 2 +- app/src/main/res/values/strings.xml | 5 +- 11 files changed, 62 insertions(+), 62 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8b01d869..9003cab6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -150,6 +150,20 @@ android:name="android.support.PARENT_ACTIVITY" android:value=".activities.main.MainActivity" /> + + + + - - - - - - \ No newline at end of file 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 057b6d63..76d15b4f 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 @@ -8,6 +8,7 @@ import android.view.View; import android.widget.ProgressBar; import android.widget.Toast; +import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.LinearLayoutManager; @@ -142,7 +143,7 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo mainContent.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override - public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); totalItemCount = layoutManager.getItemCount(); lastVisibleItem = layoutManager.findLastVisibleItemPosition(); 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 107ed6ce..dcf2ea84 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 @@ -4,6 +4,7 @@ import android.content.Intent; import android.os.Bundle; import android.widget.Toast; +import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentPagerAdapter; @@ -128,7 +129,7 @@ public class BookmarksActivity extends BaseActivity { * it may be best to switch to a * {@link FragmentStatePagerAdapter}. */ - private class SectionsPagerAdapter extends FragmentPagerAdapter { + private static class SectionsPagerAdapter extends FragmentPagerAdapter { private final List fragmentList = new ArrayList<>(); private final List fragmentTitleList = new ArrayList<>(); @@ -142,6 +143,7 @@ public class BookmarksActivity extends BaseActivity { notifyDataSetChanged(); } + @NonNull @Override public Fragment getItem(int position) { return fragmentList.get(position); @@ -158,8 +160,7 @@ public class BookmarksActivity extends BaseActivity { } @Override - public int getItemPosition(Object object) { - @SuppressWarnings("RedundantCast") + public int getItemPosition(@NonNull Object object) { int position = fragmentList.indexOf((Fragment) object); return position == -1 ? POSITION_NONE : position; } diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentFragment.java index 625f7440..ed4431ce 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentFragment.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentFragment.java @@ -32,8 +32,6 @@ import gr.thmmy.mthmmy.utils.parsing.ParseException; import gr.thmmy.mthmmy.views.CustomRecyclerView; import me.zhanghai.android.materialprogressbar.MaterialProgressBar; import okhttp3.Response; -import timber.log.Timber; - /** * A {@link BaseFragment} subclass. @@ -86,10 +84,8 @@ public class RecentFragment extends BaseFragment { recentTask = new RecentTask(this::onRecentTaskStarted, this::onRecentTaskFinished); recentTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, SessionManager.indexUrl.toString()); } - Timber.d("onActivityCreated"); } - @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java index 749f42bc..a0cac182 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java @@ -21,6 +21,7 @@ import org.jsoup.select.Elements; import java.util.HashMap; import java.util.LinkedHashMap; +import java.util.Objects; import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.utils.parsing.ParseHelpers; @@ -68,7 +69,7 @@ public class SummaryFragment extends Fragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - profileSummaryDocument = Jsoup.parse(requireArguments().getString(PROFILE_DOCUMENT)); + profileSummaryDocument = Jsoup.parse(Objects.requireNonNull(requireArguments().getString(PROFILE_DOCUMENT))); parsedProfileSummaryData = new LinkedHashMap<>(); } diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/shoutbox/ShoutboxFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/shoutbox/ShoutboxFragment.java index d6ab6cfd..fd7bb28e 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/shoutbox/ShoutboxFragment.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/shoutbox/ShoutboxFragment.java @@ -56,6 +56,7 @@ public class ShoutboxFragment extends Fragment { LinearLayoutManager layoutManager = new LinearLayoutManager(getContext()); layoutManager.setReverseLayout(true); recyclerView.setLayoutManager(layoutManager); + recyclerView.setItemViewCacheSize(25); recyclerView.setOnTouchListener((view, motionEvent) -> { editorView.hideMarkdown(); InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Activity.INPUT_METHOD_SERVICE); @@ -82,7 +83,7 @@ public class ShoutboxFragment extends Fragment { } @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.shoutbox_menu, menu); } @@ -100,7 +101,7 @@ public class ShoutboxFragment extends Fragment { public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); shoutboxViewModel = ViewModelProviders.of(getActivity()).get(ShoutboxViewModel.class); - shoutboxViewModel.getShoutboxMutableLiveData().observe(this, shoutbox -> { + shoutboxViewModel.getShoutboxMutableLiveData().observe(getViewLifecycleOwner(), shoutbox -> { if (shoutbox != null) { Timber.i("Shoutbox loaded successfully"); shoutAdapter.setShouts(shoutbox.getShouts()); diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java index 0584192f..9d0d4556 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java @@ -43,7 +43,7 @@ import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.content.res.AppCompatResources; import androidx.appcompat.widget.AppCompatButton; -import androidx.lifecycle.ViewModelProviders; +import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.RecyclerView; import com.bumptech.glide.Glide; @@ -113,7 +113,7 @@ class TopicAdapter extends RecyclerView.Adapter { this.postFocusListener = context; this.emojiKeyboard = emojiKeyboard; - viewModel = ViewModelProviders.of(context).get(TopicViewModel.class); + viewModel = new ViewModelProvider(context).get(TopicViewModel.class); } @Override @@ -137,7 +137,8 @@ class TopicAdapter extends RecyclerView.Adapter { quickReplyText.setFocusableInTouchMode(true); quickReplyText.setOnFocusChangeListener((v, hasFocus) -> quickReplyText.post(() -> { InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); - imm.showSoftInput(quickReplyText, InputMethodManager.SHOW_IMPLICIT); + if (imm != null) + imm.showSoftInput(quickReplyText, InputMethodManager.SHOW_IMPLICIT); })); quickReplyText.requestFocus(); @@ -150,7 +151,8 @@ class TopicAdapter extends RecyclerView.Adapter { editPostEdittext.setFocusableInTouchMode(true); editPostEdittext.setOnFocusChangeListener((v, hasFocus) -> editPostEdittext.post(() -> { InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); - imm.showSoftInput(editPostEdittext, InputMethodManager.SHOW_IMPLICIT); + if (imm != null) + imm.showSoftInput(editPostEdittext, InputMethodManager.SHOW_IMPLICIT); })); editPostEdittext.requestFocus(); @@ -215,12 +217,10 @@ class TopicAdapter extends RecyclerView.Adapter { for (Poll.Entry entry : entries) { CheckBox checkBox = new CheckBox(context); checkBox.setMovementMethod(LinkMovementMethod.getInstance()); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) checkBox.setText(Html.fromHtml(entry.getEntryName(), Html.FROM_HTML_MODE_LEGACY)); - } else { - //noinspection deprecation + else checkBox.setText(Html.fromHtml(entry.getEntryName())); - } checkBox.setTextColor(primaryTextColor); holder.optionsLayout.addView(checkBox); } @@ -236,10 +236,9 @@ class TopicAdapter extends RecyclerView.Adapter { radioButton.setMovementMethod(LinkMovementMethod.getInstance()); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { radioButton.setText(Html.fromHtml(entries[i].getEntryName(), Html.FROM_HTML_MODE_LEGACY)); - } else { - //noinspection deprecation + } else radioButton.setText(Html.fromHtml(entries[i].getEntryName())); - } + radioButton.setText(ThmmyParser.html2span(context, entries[i].getEntryName())); radioButton.setTextColor(primaryTextColor); radioGroup.addView(radioButton); @@ -255,12 +254,11 @@ class TopicAdapter extends RecyclerView.Adapter { Poll.Entry entry = entries1[i]; TextView textView = new TextView(context); textView.setMovementMethod(LinkMovementMethod.getInstance()); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) textView.setText(Html.fromHtml(entry.getEntryName(), Html.FROM_HTML_MODE_LEGACY)); - } else { - //noinspection deprecation + else textView.setText(Html.fromHtml(entry.getEntryName())); - } + textView.setTextColor(primaryTextColor); if (poll.getSelectedEntryIndex() == i) { // apply bold to the selected entry @@ -363,7 +361,6 @@ class TopicAdapter extends RecyclerView.Adapter { holder.post.setClickable(true); holder.post.setWebViewClient(new LinkLauncher()); - //noinspection ConstantConditions loadAvatar(currentPost.getThumbnailURL(), holder.thumbnail, holder.itemView.getContext()); //Sets username,submit date, index number, subject, post's and attached files texts @@ -385,7 +382,7 @@ class TopicAdapter extends RecyclerView.Adapter { int filesTextColor; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { filesTextColor = context.getResources().getColor(R.color.accent, null); - } else //noinspection deprecation + } else filesTextColor = context.getResources().getColor(R.color.accent); for (final ThmmyFile attachedFile : currentPost.getAttachedFiles()) { @@ -408,7 +405,7 @@ class TopicAdapter extends RecyclerView.Adapter { int lastEditTextColor; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { lastEditTextColor = context.getResources().getColor(R.color.white, null); - } else //noinspection deprecation + } else lastEditTextColor = context.getResources().getColor(R.color.white); final TextView lastEdit = new TextView(context); @@ -492,7 +489,7 @@ class TopicAdapter extends RecyclerView.Adapter { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { holder.cardChildLinear.setBackground(context.getResources(). getDrawable(R.drawable.mention_card, null)); - } else //noinspection deprecation + } else holder.cardChildLinear.setBackground(context.getResources(). getDrawable(R.drawable.mention_card)); } else if (mUserColor == TopicParser.USER_COLOR_PINK) { @@ -500,7 +497,7 @@ class TopicAdapter extends RecyclerView.Adapter { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { holder.cardChildLinear.setBackground(context.getResources(). getDrawable(R.drawable.member_of_the_month_card, null)); - } else //noinspection deprecation + } else holder.cardChildLinear.setBackground(context.getResources(). getDrawable(R.drawable.member_of_the_month_card)); } else holder.cardChildLinear.setBackground(null); @@ -623,7 +620,6 @@ class TopicAdapter extends RecyclerView.Adapter { popUp.showAsDropDown(holder.overflowButton); }); - //noinspection PointlessBooleanExpression,ConstantConditions if (!BaseActivity.getSessionManager().isLoggedIn() || !viewModel.canReply()) holder.quoteToggle.setVisibility(View.GONE); else { @@ -644,7 +640,6 @@ class TopicAdapter extends RecyclerView.Adapter { final QuickReplyViewHolder holder = (QuickReplyViewHolder) currentHolder; Post reply = (Post) topicItems.get(position); - //noinspection ConstantConditions loadAvatar(getSessionManager().getAvatarLink(), holder.thumbnail, holder.itemView.getContext()); holder.username.setText(getSessionManager().getUsername()); @@ -669,7 +664,9 @@ class TopicAdapter extends RecyclerView.Adapter { return; } InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(view.getWindowToken(), 0); + if (imm != null) + imm.hideSoftInputFromWindow(view.getWindowToken(), 0); + holder.itemView.setAlpha(0.5f); holder.itemView.setEnabled(false); emojiKeyboard.hide(); @@ -706,7 +703,6 @@ class TopicAdapter extends RecyclerView.Adapter { } else if (currentHolder instanceof EditMessageViewHolder) { final EditMessageViewHolder holder = (EditMessageViewHolder) currentHolder; - //noinspection ConstantConditions loadAvatar(getSessionManager().getAvatarLink(), holder.thumbnail, holder.itemView.getContext()); holder.username.setText(getSessionManager().getUsername()); @@ -729,7 +725,9 @@ class TopicAdapter extends RecyclerView.Adapter { return; } InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(view.getWindowToken(), 0); + if (imm != null) + imm.hideSoftInputFromWindow(view.getWindowToken(), 0); + holder.itemView.setAlpha(0.5f); holder.itemView.setEnabled(false); emojiKeyboard.hide(); @@ -898,9 +896,7 @@ class TopicAdapter extends RecyclerView.Adapter { * This class is used to handle link clicks in WebViews. When link url is one that the app can * handle internally, it does. Otherwise user is prompt to open the link in a browser. */ - @SuppressWarnings("unchecked") private class LinkLauncher extends WebViewClient { - @SuppressWarnings("deprecation") @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { final Uri uri = Uri.parse(url); diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/crashreporting/CrashReportingTree.java b/app/src/main/java/gr/thmmy/mthmmy/utils/crashreporting/CrashReportingTree.java index 83e55a37..77d4e352 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/crashreporting/CrashReportingTree.java +++ b/app/src/main/java/gr/thmmy/mthmmy/utils/crashreporting/CrashReportingTree.java @@ -2,6 +2,8 @@ package gr.thmmy.mthmmy.utils.crashreporting; import android.util.Log; +import androidx.annotation.NonNull; + import com.google.firebase.crashlytics.FirebaseCrashlytics; import timber.log.Timber.DebugTree; @@ -14,7 +16,7 @@ public class CrashReportingTree extends DebugTree { } @Override - protected void log(int priority, String tag, String message, Throwable t) { + protected void log(int priority, String tag, @NonNull String message, Throwable t) { if (priority == Log.VERBOSE || priority == Log.DEBUG) { return; } diff --git a/app/src/main/java/gr/thmmy/mthmmy/views/editorview/EditorView.java b/app/src/main/java/gr/thmmy/mthmmy/views/editorview/EditorView.java index 051bfdfb..df39daa3 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/views/editorview/EditorView.java +++ b/app/src/main/java/gr/thmmy/mthmmy/views/editorview/EditorView.java @@ -375,11 +375,13 @@ public class EditorView extends LinearLayout implements EmojiInputField { if (emojiKeyboard.onEmojiButtonToggle()) { //prevent system keyboard from appearing when clicking the edittext editText.setTextIsSelectable(true); - imm.hideSoftInputFromWindow(getWindowToken(), 0); + if (imm != null) + imm.hideSoftInputFromWindow(getWindowToken(), 0); } else { editText.requestFocus(); - imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT); + if (imm != null) + imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT); } editText.setSelection(selectionStart, selectionEnd); }); @@ -398,10 +400,12 @@ public class EditorView extends LinearLayout implements EmojiInputField { editText.setOnClickListener(view -> { if (!emojiKeyboard.isVisible()) { InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Activity.INPUT_METHOD_SERVICE); - imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT); + if (imm != null) + imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT); } else { InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Activity.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(getWindowToken(), 0); + if (imm != null) + imm.hideSoftInputFromWindow(getWindowToken(), 0); requestEditTextFocus(); } showMarkdown(); diff --git a/app/src/main/res/layout/activity_create_content.xml b/app/src/main/res/layout/activity_create_content.xml index 5c42f68b..279eb871 100644 --- a/app/src/main/res/layout/activity_create_content.xml +++ b/app/src/main/res/layout/activity_create_content.xml @@ -53,7 +53,7 @@ android:layout_below="@id/subject_input" android:layout_marginStart="16dp" android:layout_marginEnd="16dp" - app:hint="topic message"/> + app:hint="@string/message"/> Page next last - Quick reply… - Subject… + Subject + Message Submit - Message… Could not connect to thmmy.gr\n\nTap to retry Network error retry From e43825d7a40f4a295b61f81f52596d6d1553667f Mon Sep 17 00:00:00 2001 From: Ezerous Date: Wed, 26 Aug 2020 13:43:02 +0300 Subject: [PATCH 5/6] YouTube icon overlay fix --- .../main/assets/YouTube_light_color_icon.png | Bin 13195 -> 9567 bytes app/src/main/assets/style.css | 416 ++++++++++-------- .../activities/shoutbox/ShoutboxFragment.java | 3 +- .../mthmmy/utils/parsing/ParseHelpers.java | 13 +- 4 files changed, 242 insertions(+), 190 deletions(-) diff --git a/app/src/main/assets/YouTube_light_color_icon.png b/app/src/main/assets/YouTube_light_color_icon.png index bfd6db39af520e36914c67a9bb9d0b151f3178cd..ac1360059c2083f6fabc53126d638a61e7e2e302 100644 GIT binary patch literal 9567 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

l2P! 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 diff --git a/app/src/main/assets/style.css b/app/src/main/assets/style.css index 4a9fc801..501e1320 100644 --- a/app/src/main/assets/style.css +++ b/app/src/main/assets/style.css @@ -1,24 +1,26 @@ /* TP specific classes */ -.sitemap{ + +.sitemap { margin: 0; padding: 0; list-style: none; } -.sitemap_topheader{ + +.sitemap_topheader { background: #ECEDF3; border-bottom: solid 1px #ffffff; padding: 4px; } -.sitemap_header{ +.sitemap_header { background: #ECEDF3; border-bottom: solid 1px #ffffff; padding: 4px; display: block; font-weight: bold; - } +} -.sitemap_header_active{ +.sitemap_header_active { background: #C8D6E1; border-bottom: solid 1px #ffffff; padding: 4px; @@ -26,7 +28,8 @@ font-weight: bold; } -.sitemap_header:hover , .sitemap_header_active:hover{ +.sitemap_header:hover, +.sitemap_header_active:hover { background: #DBE4ED; border-bottom: solid 1px #ffffff; padding: 4px; @@ -34,129 +37,149 @@ text-decoration: none; } + /* TP other styles */ -ul#articlelist -{ + +ul#articlelist { margin: 0; padding: 0.5ex 0; list-style: none; } -ul#catlist -{ + +ul#catlist { margin: 0; padding: 0; list-style: none; border-top: solid 1px #d0d0d0; } -ul#articlelist li -{ +ul#articlelist li { margin: 0; display: block; padding: 0 0 0 3ex; background: url(images/divider.gif) no-repeat 5px 3px; } -ul#catlist li -{ + +ul#catlist li { display: block; padding: 0 0 0 3ex; margin: 0; } + /* Normal, standard links. */ -a:link, a:visited -{ + +a:link, +a:visited { color: #26A69A; text-decoration: none; } -a:hover -{ + +a:hover { text-decoration: underline; } + /* Navigation links - for the link tree. */ -.nav, .nav:link, .nav:visited -{ + +.nav, +.nav:link, +.nav:visited { color: #000000; text-decoration: none; } -a.nav:hover -{ + +a.nav:hover { color: #cc3333; } + /* Tables should show empty cells. */ -table -{ + +table { empty-cells: show; } + + /* The main body of the entire forum. */ -body -{ + +body { background: #3C3F41; margin: 0; padding: 0; } + + /* By default (td, body..) use verdana in black. */ -body, td, th , tr -{ + +body, +td, +th, +tr { color: #FFFFFF; font-size: small; font-family: Trebuchet, sans-serif; } - /* Input boxes - just a bit smaller than normal so they align well. */ -input, textarea, button -{ + +input, +textarea, +button { color: #FFFFFF; font-family: Trebuchet, sans-serif; border: 1px solid #aaa; } -input, button -{ + +input, +button { font-size: 90%; } -textarea -{ +textarea { font-size: 100%; color: #FFFFFF; font-family: Trebuchet, sans-serif; } + /* All input elements that are checkboxes or radio buttons. */ -input.check -{ -} + +input.check {} + /* Selects are a bit smaller, because it makes them look even better 8). */ -select -{ + +select { font-size: 90%; font-weight: normal; color: #FFFFFF; font-family: Trebuchet, sans-serif; } + /* Standard horizontal rule.. ([hr], etc.) */ -hr, .hrcolor -{ + +hr, +.hrcolor { height: 1px; border: 0; color: #666666; background-color: #666666; } + /* No image should have a border when linked */ -a img -{ + +a img { border: 0; } + + /* A quote, perhaps from another post. */ -.quote -{ + +.quote { font-family: tahoma, sans-serif; color: #FFFFFF; background-color: #404D50; @@ -167,9 +190,10 @@ a img line-height: 1.4em; } + /* A code block - maybe even PHP ;). */ -.code -{ + +.code { color: #FFFFFF; background-color: #626566; font-family: "Comic Sans MS", "times new roman", monospace; @@ -187,9 +211,11 @@ a img max-height: 24em; } + /* The "Quote:" and "Code:" header parts... */ -.quoteheader, .codeheader -{ + +.quoteheader, +.codeheader { font-family: tahoma, sans-serif; color: #26A69A; text-decoration: none; @@ -199,156 +225,185 @@ a img line-height: 1.2em; } + /* Generally, those [?] icons. This makes your cursor a help icon. */ -.help -{ + +.help { cursor: help; } + /* /me uses this a lot. (emote, try typing /me in a post.) */ -.meaction -{ + +.meaction { color: red; } + /* The main post box - this makes it as wide as possible. */ -.editor -{ + +.editor { width: 96%; } + /* Highlighted text - such as search results. */ -.highlight -{ + +.highlight { background-color: yellow; font-weight: bold; color: black; } + /* Alternating backgrounds for posts, and several other sections of the forum. */ -.windowbg -{ + +.windowbg { color: #FFFFFF; background-color: #E3E6E1; } -.windowbg2 -{ + +.windowbg2 { color: #FFFFFF; background-color: #F2F5F0; } -.windowbg3 -{ + +.windowbg3 { color: #FFFFFF; background-color: #E1E8E0; } + + /* the today container in calendar */ -.calendar_today -{ + +.calendar_today { background-color: #FFFFFF; } + /* These are used primarily for titles, but also for headers (the row that says what everything in the table is.) */ -.titlebg, tr.titlebg th, tr.titlebg td, .titlebg2, tr.titlebg2 th, tr.titlebg2 td -{ + +.titlebg, +tr.titlebg th, +tr.titlebg td, +.titlebg2, +tr.titlebg2 th, +tr.titlebg2 td { background-color: #A3A392; padding-top: 10px; } -.titlebg, tr.titlebg th, tr.titlebg td, .titlebg a:link, .titlebg a:visited, .titlebg2, tr.titlebg2 th, tr.titlebg2 td, .titlebg2 a:link, .titlebg2 a:visited -{ + +.titlebg, +tr.titlebg th, +tr.titlebg td, +.titlebg a:link, +.titlebg a:visited, +.titlebg2, +tr.titlebg2 th, +tr.titlebg2 td, +.titlebg2 a:link, +.titlebg2 a:visited { color: white; font-style: normal; } -.titlebg a:hover -{ + +.titlebg a:hover { color: #dfdfdf; } -.catbg, .catbg2, .catbg3 -{ +.catbg, +.catbg2, +.catbg3 { font-weight: bold; background-color: #e4e2e0; color: #FFFFFF; } + + /* This is used for tables that have a grid/border background color (such as the topic listing.) */ -.bordercolor -{ + +.bordercolor { background-color: white; } + /* This is used on tables that should just have a border around them. */ -.tborder -{ + +.tborder { background-color: #FFFFFF; } + /* Default font sizes: small (8pt), normal (10pt), and large (14pt). */ -.smalltext -{ + +.smalltext { font-size: x-small; font-family: tahoma, sans-serif; } -.middletext -{ + +.middletext { font-size: 90%; } -.normaltext -{ + +.normaltext { font-size: small; } -.largetext -{ + +.largetext { font-size: large; } /* Posts and personal messages displayed throughout the forum. */ -.post, .personalmessage -{ + +.post, +.personalmessage { width: 100%; overflow: auto; line-height: 1.3em; color: white; - background: #3C3F41 !important; + background: #3C3F41 !important; } + /* All the signatures used in the forum. If your forum users use Mozilla, Opera, or Safari, you might add max-height here ;). */ -.signature -{ + +.signature { width: 100%; overflow: auto; padding-bottom: 3px; line-height: 1.3em; } -#left -{ +#left { background: url(images/img2/leftbg.jpg) repeat-y white; margin: auto; } -#right -{ + +#right { background: url(images/img2/rightbg.gif) repeat-y top right; } -#top -{ + +#top { background: url(images/img2/top.jpg) repeat-x; } -#topleft -{ + +#topleft { background: url(images/img2/lefttop.jpg) no-repeat; } -#topright -{ + +#topright { background: url(images/img2/logo.jpg) no-repeat top right; } -#main -{ + +#main { padding: 100px 81px 20px 81px; } + + /* #################### */ -ul#menubox -{ +ul#menubox { padding: 0 0 44px 0; margin: 0; list-style: none; @@ -358,16 +413,15 @@ ul#menubox background: url(images/img2/leftbot.gif) no-repeat bottom left; } -ul#menubox li -{ +ul#menubox li { padding: 0 0 0 8px; width: 65px; height: 44px; margin: 0; background: url(images/img2/left.gif) repeat-y; } -ul#menubox li a -{ + +ul#menubox li a { font-family: "Comic Sans MS", serif; display: block; color: black; @@ -375,74 +429,75 @@ ul#menubox li a height: 42px; padding: 0 0 0 6px; } -ul#menubox li a span -{ + +ul#menubox li a span { display: none; } -ul#menubox li.m1 -{ +ul#menubox li.m1 { padding-left: 2px; } -ul#menubox li.m2 -{ + +ul#menubox li.m2 { padding-left: 6px; } -ul#menubox li.m3 -{ + +ul#menubox li.m3 { padding-left: 10px; } -ul#menubox li.m4 -{ + +ul#menubox li.m4 { padding-left: 14px; } -ul#menubox li.m5 -{ + +ul#menubox li.m5 { padding-left: 18px; } - -#myuser -{ +#myuser { font-size: small; padding-bottom: 1em; } -#ava -{ + +#ava { float: right; margin-right: 10px; text-align: right; font-family: "Comic Sans MS", sans-serif; } -#bodyarea -{ + +#bodyarea { border-bottom: solid 1px #ddd; margin-bottom: 1em; padding-bottom: 1em; } -.clearfix:after -{ - content: "."; - display: block; - height: 0; - clear: both; - visibility: hidden; + +.clearfix:after { + content: "."; + display: block; + height: 0; + clear: both; + visibility: hidden; } -.clearfix -{ +.clearfix { display: inline-block; } + /* Hides from IE-mac \*/ -* html .clearfix , * html .catbg, * html .catbg2, * html .catbg3 -{ + +* html .clearfix, +* html .catbg, +* html .catbg2, +* html .catbg3 { height: 1%; } + + /* End hide from IE-mac */ -ul#topmenu -{ +ul#topmenu { position: absolute; top: 45px; margin: 0 195px 0 40px; @@ -452,12 +507,12 @@ ul#topmenu font-size: 11px; border-bottom: groove 2px #EDF4ED; } -ul#topmenu li -{ + +ul#topmenu li { float: left; } -ul#topmenu li a -{ + +ul#topmenu li a { display: block; padding: 2px 5px 2px 5px; border-style: solid solid; @@ -466,31 +521,31 @@ ul#topmenu li a font-size: 11px; color: #004080; } -ul#topmenu li a:hover -{ + +ul#topmenu li a:hover { background: #E3E6E1; text-decoration: none; color: #E78E13; } -#pages -{ + +#pages { padding-top: 1em; } -#uppersection -{ + +#uppersection { padding: 1em; background: url(images/img/upper.jpg) repeat-x; } -.errorbar -{ + +.errorbar { color: white; font-size: xx-small; text-align: center; padding: 3px; border-bottom: solid 1px black; } -#errorpanel -{ + +#errorpanel { position: absolute; top: 0; left: 0; @@ -498,51 +553,46 @@ ul#topmenu li a:hover width: 100%; } + /* Additions */ -img -{ - max-width:100% !important; - height:auto !important; + +img { + max-width: 100% !important; + height: auto !important; } .yt { - position: relative; + -webkit-tap-highlight-color: transparent; } .embedded-video-play { - position: absolute; - top: 22%; - left: 10%; - width: 20%; - opacity: 0.7; - z-index: 2; + width: 25%; + padding: 15%; + background-repeat: no-repeat; + background-position: center; + background-size: 100% auto; } -.customSignature{ - background: #3C3F41; +.customSignature { + background: #3C3F41; } -[style="color: blue;"] -{ - color: #3452fe !important; +[style="color: blue;"] { + color: #3452fe !important; } -[style="color: purple;"] -{ - color: #a511a5 !important; +[style="color: purple;"] { + color: #a511a5 !important; } -[style="color: maroon;"] -{ - color: #a51111 !important; +[style="color: maroon;"] { + color: #a51111 !important; } -span[style="background-color: yellow;"] -{ - color: black !important; +span[style="background-color: yellow;"] { + color: black !important; } -[style="color: white;"] > span[style="background-color: yellow;"] -{ - color: white !important; +[style="color: white;"]>span[style="background-color: yellow;"] { + color: white !important; } diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/shoutbox/ShoutboxFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/shoutbox/ShoutboxFragment.java index fd7bb28e..0b52b9d7 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/shoutbox/ShoutboxFragment.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/shoutbox/ShoutboxFragment.java @@ -60,7 +60,8 @@ public class ShoutboxFragment extends Fragment { recyclerView.setOnTouchListener((view, motionEvent) -> { editorView.hideMarkdown(); InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Activity.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(editorView.getWindowToken(), 0); + if (imm != null) + imm.hideSoftInputFromWindow(editorView.getWindowToken(), 0); return false; }); diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseHelpers.java b/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseHelpers.java index 50ca9870..f602dfb2 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseHelpers.java +++ b/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseHelpers.java @@ -154,14 +154,15 @@ public class ParseHelpers { fixed.substring(fixed.indexOf("") + 9) , "

"); + + "/default.jpg');\">" + ); ++tmp_counter; } return fixed; From eba2c6f06713e498168c4ef2705eb7255c18cab6 Mon Sep 17 00:00:00 2001 From: Ezerous Date: Wed, 26 Aug 2020 15:00:37 +0300 Subject: [PATCH 6/6] Copy YouTube videos links onLongClick --- .../mthmmy/utils/parsing/ParseHelpers.java | 20 ++++++++++++------- .../thmmy/mthmmy/views/ReactiveWebView.java | 19 ++++++++++++++++-- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseHelpers.java b/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseHelpers.java index f602dfb2..a4acb3ec 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseHelpers.java +++ b/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseHelpers.java @@ -128,6 +128,8 @@ public class ParseHelpers { } } + public static final String VIDEO_ID_PARAMETER = "videoId"; + /** * This method fixes html so that embedded videos will render properly and be lightweight. * @@ -146,24 +148,28 @@ public class ParseHelpers { } String fixed = html.outerHtml(); - int tmp_counter = 0; + int counter = 0; while (fixed.contains(" embededVideosUrls.size()) + if (counter > embededVideosUrls.size()) break; + final String videoId = embededVideosUrls.get(counter); fixed = fixed.replace( fixed.substring(fixed.indexOf("") + 9) , "
" + "" + "
" ); - ++tmp_counter; + ++counter; } return fixed; } diff --git a/app/src/main/java/gr/thmmy/mthmmy/views/ReactiveWebView.java b/app/src/main/java/gr/thmmy/mthmmy/views/ReactiveWebView.java index 7c9efdb8..eddd6781 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/views/ReactiveWebView.java +++ b/app/src/main/java/gr/thmmy/mthmmy/views/ReactiveWebView.java @@ -3,6 +3,7 @@ package gr.thmmy.mthmmy.views; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; +import android.net.Uri; import android.util.AttributeSet; import android.view.MotionEvent; import android.webkit.WebView; @@ -13,6 +14,7 @@ import gr.thmmy.mthmmy.base.BaseApplication; import gr.thmmy.mthmmy.utils.ui.ImageDownloadDialogBuilder; import static android.content.Context.CLIPBOARD_SERVICE; +import static gr.thmmy.mthmmy.utils.parsing.ParseHelpers.VIDEO_ID_PARAMETER; import static gr.thmmy.mthmmy.utils.ui.PhotoViewUtils.displayPhotoViewImage; public class ReactiveWebView extends WebView { @@ -77,8 +79,16 @@ public class ReactiveWebView extends WebView { copyUrlToClipboard(result.getExtra()); else if(result.getType() == WebView.HitTestResult.IMAGE_TYPE) { String imageURL = result.getExtra(); - ImageDownloadDialogBuilder builder = new ImageDownloadDialogBuilder(context,imageURL); - builder.show(); + showImageDownloadDialog(imageURL); + } + else if(result.getType() == HitTestResult.SRC_IMAGE_ANCHOR_TYPE) { + final String imageURL = result.getExtra(); + Uri uri = Uri.parse(imageURL); + String videoId = uri.getQueryParameter(VIDEO_ID_PARAMETER); + if (videoId!=null) + copyUrlToClipboard("https://www.youtube.com/watch?v=" + videoId); + else + showImageDownloadDialog(imageURL); } return false; }); @@ -90,4 +100,9 @@ public class ReactiveWebView extends WebView { clipboard.setPrimaryClip(clip); Toast.makeText(BaseApplication.getInstance().getApplicationContext(),context.getString(R.string.link_copied_msg),Toast.LENGTH_SHORT).show(); } + + private void showImageDownloadDialog(String imageURL){ + ImageDownloadDialogBuilder builder = new ImageDownloadDialogBuilder(context, imageURL); + builder.show(); + } }