Browse Source

Version 1.8.4

master v1.8.4
Ezerous 4 years ago
parent
commit
e628c1f6db
No known key found for this signature in database GPG Key ID: 262B2954BBA319E3
  1. 6
      app/build.gradle
  2. 29
      app/src/main/AndroidManifest.xml
  3. BIN
      app/src/main/assets/YouTube_light_color_icon.png
  4. 376
      app/src/main/assets/style.css
  5. 1
      app/src/main/assets/style_light.css
  6. 13
      app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardActivity.java
  7. 7
      app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksActivity.java
  8. 4
      app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentFragment.java
  9. 125
      app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java
  10. 6
      app/src/main/java/gr/thmmy/mthmmy/activities/shoutbox/ShoutboxFragment.java
  11. 118
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java
  12. 4
      app/src/main/java/gr/thmmy/mthmmy/utils/crashreporting/CrashReportingTree.java
  13. 27
      app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseHelpers.java
  14. 19
      app/src/main/java/gr/thmmy/mthmmy/views/ReactiveWebView.java
  15. 4
      app/src/main/java/gr/thmmy/mthmmy/views/editorview/EditorView.java
  16. 2
      app/src/main/res/layout/activity_create_content.xml
  17. 3
      app/src/main/res/layout/fragment_profile_summary.xml
  18. 5
      app/src/main/res/values/strings.xml

6
app/build.gradle

@ -15,8 +15,8 @@ android {
applicationId "gr.thmmy.mthmmy" applicationId "gr.thmmy.mthmmy"
minSdkVersion 19 minSdkVersion 19
targetSdkVersion 29 targetSdkVersion 29
versionCode 26 versionCode 27
versionName "1.8.3" versionName "1.8.4"
archivesBaseName = "mTHMMY-v$versionName" archivesBaseName = "mTHMMY-v$versionName"
buildConfigField "String", "CURRENT_BRANCH", "\"" + getCurrentBranch() + "\"" buildConfigField "String", "CURRENT_BRANCH", "\"" + getCurrentBranch() + "\""
buildConfigField "String", "COMMIT_HASH", "\"" + getCommitHash() + "\"" buildConfigField "String", "COMMIT_HASH", "\"" + getCommitHash() + "\""
@ -78,7 +78,7 @@ tasks.whenTaskAdded { task ->
dependencies { dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(":emojis") 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.preference:preference:1.1.1'
implementation 'androidx.legacy:legacy-preference-v14:1.0.0' implementation 'androidx.legacy:legacy-preference-v14:1.0.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0'

29
app/src/main/AndroidManifest.xml

@ -150,6 +150,20 @@
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.main.MainActivity" /> android:value=".activities.main.MainActivity" />
</activity> </activity>
<activity
android:name=".activities.shoutbox.ShoutboxActivity"
android:configChanges="orientation|screenSize"
android:parentActivityName=".activities.main.MainActivity"
android:theme="@style/AppTheme.NoActionBar">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.main.MainActivity" />
</activity>
<activity
android:name=".activities.create_content.CreateContentActivity"
android:configChanges="orientation|screenSize"
android:parentActivityName=".activities.main.MainActivity"
android:theme="@style/AppTheme.NoActionBar" />
<provider <provider
android:name="androidx.core.content.FileProvider" android:name="androidx.core.content.FileProvider"
@ -176,20 +190,5 @@
<action android:name="gr.thmmy.mthmmy.uploadservice.broadcast.status" /> <action android:name="gr.thmmy.mthmmy.uploadservice.broadcast.status" />
</intent-filter> </intent-filter>
</receiver> </receiver>
<activity
android:name=".activities.create_content.CreateContentActivity"
android:configChanges="orientation|screenSize"
android:parentActivityName=".activities.main.MainActivity"
android:theme="@style/AppTheme.NoActionBar" />
<activity
android:name=".activities.shoutbox.ShoutboxActivity"
android:parentActivityName=".activities.main.MainActivity"
android:theme="@style/AppTheme.NoActionBar">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.main.MainActivity" />
</activity>
</application> </application>
</manifest> </manifest>

BIN
app/src/main/assets/YouTube_light_color_icon.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

376
app/src/main/assets/style.css

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

1
app/src/main/assets/style_light.css

@ -8,4 +8,5 @@ body {
.customSignature { .customSignature {
background: #434649 !important; background: #434649 !important;
margin-top: 0.5em;
} }

13
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.ProgressBar;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
@ -142,7 +143,7 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo
mainContent.addOnScrollListener(new RecyclerView.OnScrollListener() { mainContent.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override @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); super.onScrolled(recyclerView, dx, dy);
totalItemCount = layoutManager.getItemCount(); totalItemCount = layoutManager.getItemCount();
lastVisibleItem = layoutManager.findLastVisibleItemPosition(); lastVisibleItem = layoutManager.findLastVisibleItemPosition();
@ -189,7 +190,7 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo
* parameter!</p> * parameter!</p>
*/ */
private class BoardTask extends ParseTask { private class BoardTask extends ParseTask {
ArrayList<Board> tempSubboards = new ArrayList<>(); ArrayList<Board> tempSubBoards = new ArrayList<>();
ArrayList<Topic> tempTopics = new ArrayList<>(); ArrayList<Topic> tempTopics = new ArrayList<>();
@Override @Override
@ -200,7 +201,7 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo
@Override //TODO should throw ParseException @Override //TODO should throw ParseException
public void parse(Document boardPage) throws ParseException { public void parse(Document boardPage) throws ParseException {
tempSubboards.addAll(parsedSubBoards); tempSubBoards.addAll(parsedSubBoards);
tempTopics.addAll(parsedTopics); tempTopics.addAll(parsedTopics);
//Removes loading item //Removes loading item
if (isLoadingMore && tempTopics.size() > 0) if (isLoadingMore && tempTopics.size() > 0)
@ -292,7 +293,7 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo
} }
} }
if(!parsingFailed) if(!parsingFailed)
tempSubboards.add(new Board(pUrl, pTitle, pMods, pStats, pLastPost, pLastPostUrl)); tempSubBoards.add(new Board(pUrl, pTitle, pMods, pStats, pLastPost, pLastPostUrl));
else else
Timber.e("Parsing failed (pLastPost came with: \"%s\", subBoardColumns html was \"%s\")", pLastPost, subBoardColumns); Timber.e("Parsing failed (pLastPost came with: \"%s\", subBoardColumns html was \"%s\")", pLastPost, subBoardColumns);
} }
@ -333,7 +334,7 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo
continue; 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, tempTopics.add(new Topic(pTopicUrl, pSubject, pStarter, pLastUser, pLastPostDateTime, pLastPostUrl,
pStats, pLocked, pSticky, pUnread)); pStats, pLocked, pSticky, pUnread));
} }
@ -356,7 +357,7 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo
parsedTopics.clear(); parsedTopics.clear();
parsedSubBoards.clear(); parsedSubBoards.clear();
parsedTopics.addAll(tempTopics); parsedTopics.addAll(tempTopics);
parsedSubBoards.addAll(tempSubboards); parsedSubBoards.addAll(tempSubBoards);
boardAdapter.notifyDataSetChanged(); boardAdapter.notifyDataSetChanged();
//Parse was successful //Parse was successful

7
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.os.Bundle;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter; import androidx.fragment.app.FragmentPagerAdapter;
@ -128,7 +129,7 @@ public class BookmarksActivity extends BaseActivity {
* it may be best to switch to a * it may be best to switch to a
* {@link FragmentStatePagerAdapter}. * {@link FragmentStatePagerAdapter}.
*/ */
private class SectionsPagerAdapter extends FragmentPagerAdapter { private static class SectionsPagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> fragmentList = new ArrayList<>(); private final List<Fragment> fragmentList = new ArrayList<>();
private final List<String> fragmentTitleList = new ArrayList<>(); private final List<String> fragmentTitleList = new ArrayList<>();
@ -142,6 +143,7 @@ public class BookmarksActivity extends BaseActivity {
notifyDataSetChanged(); notifyDataSetChanged();
} }
@NonNull
@Override @Override
public Fragment getItem(int position) { public Fragment getItem(int position) {
return fragmentList.get(position); return fragmentList.get(position);
@ -158,8 +160,7 @@ public class BookmarksActivity extends BaseActivity {
} }
@Override @Override
public int getItemPosition(Object object) { public int getItemPosition(@NonNull Object object) {
@SuppressWarnings("RedundantCast")
int position = fragmentList.indexOf((Fragment) object); int position = fragmentList.indexOf((Fragment) object);
return position == -1 ? POSITION_NONE : position; return position == -1 ? POSITION_NONE : position;
} }

4
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 gr.thmmy.mthmmy.views.CustomRecyclerView;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar; import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
import okhttp3.Response; import okhttp3.Response;
import timber.log.Timber;
/** /**
* A {@link BaseFragment} subclass. * A {@link BaseFragment} subclass.
@ -86,10 +84,8 @@ public class RecentFragment extends BaseFragment {
recentTask = new RecentTask(this::onRecentTaskStarted, this::onRecentTaskFinished); recentTask = new RecentTask(this::onRecentTaskStarted, this::onRecentTaskFinished);
recentTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, SessionManager.indexUrl.toString()); recentTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, SessionManager.indexUrl.toString());
} }
Timber.d("onActivityCreated");
} }
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {

125
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.nodes.Element;
import org.jsoup.select.Elements; import org.jsoup.select.Elements;
import java.util.ArrayList; import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Objects; import java.util.Objects;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
@ -35,10 +36,10 @@ public class SummaryFragment extends Fragment {
*/ */
private static final String PROFILE_DOCUMENT = "PROFILE_DOCUMENT"; 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}. * {@link SummaryTask}.
*/ */
private ArrayList<String> parsedProfileSummaryData; private LinkedHashMap<String, String> parsedProfileSummaryData;
/** /**
* A {@link Document} holding this profile's source code. * A {@link Document} holding this profile's source code.
*/ */
@ -68,8 +69,8 @@ public class SummaryFragment extends Fragment {
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
profileSummaryDocument = Jsoup.parse(requireArguments().getString(PROFILE_DOCUMENT)); profileSummaryDocument = Jsoup.parse(Objects.requireNonNull(requireArguments().getString(PROFILE_DOCUMENT)));
parsedProfileSummaryData = new ArrayList<>(); parsedProfileSummaryData = new LinkedHashMap<>();
} }
@Override @Override
@ -123,36 +124,42 @@ public class SummaryFragment extends Fragment {
* @return ArrayList containing this profile's parsed information * @return ArrayList containing this profile's parsed information
* @see org.jsoup.Jsoup Jsoup * @see org.jsoup.Jsoup Jsoup
*/ */
ArrayList<String> parseProfileSummary(Document profile) { LinkedHashMap<String, String> parseProfileSummary(Document profile) {
//Method's variables LinkedHashMap<String, String> parsedInformation = new LinkedHashMap<>();
ArrayList<String> parsedInformation = new ArrayList<>();
//Contains all summary's rows //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) { for (Element summaryRow : summaryRows) {
String rowText = summaryRow.text(), pHtml = ""; String key, value;
if (summaryRow.select("td").size() == 1) //Horizontal rule rows int tdSize = summaryRow.select("td").size();
pHtml = "";
else if (summaryRow.text().contains("Current Status") if (tdSize > 1){
|| summaryRow.text().contains("Κατάσταση")) continue; key = summaryRow.select("td").first().text().trim();
else if (rowText.contains("Signature") || rowText.contains("Υπογραφή")) {
//This needs special handling since it may have css if (key.startsWith("Name") || key.startsWith("Όνομα"))
pHtml = ParseHelpers.emojiTagToHtml(ParseHelpers.youtubeEmbeddedFix(summaryRow)); continue;
//Add stuff to make it work in WebView else if (key.startsWith("Signature") || key.startsWith("Υπογραφή")){
//style.css key = summaryRow.selectFirst("td > table > tbody > tr > td").text().trim();
pHtml = ("<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />\n" + 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 = ("<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />\n" +
"<link rel=\"stylesheet\" type=\"text/css\" href=\"style_light.css\" />\n" + "<link rel=\"stylesheet\" type=\"text/css\" href=\"style_light.css\" />\n" +
"<div class=\"customSignature\">\n" + pHtml + "\n</div>"); "<div class=\"customSignature\">\n" + value + "\n</div>");
} else if (!rowText.contains("Name") && !rowText.contains("Όνομα")) { //Doesn't add username twice }
if (Objects.equals(summaryRow.select("td").get(1).text(), "")) else {
if (summaryRow.select("td").get(1).text().isEmpty())
continue; continue;
//Style parsed information with html if (key.startsWith("Date Registered") || key.startsWith("Ημερομηνία εγγραφής") || key.startsWith("Last Active") || key.startsWith("Τελευταία σύνδεση"))
pHtml = "<b>" + summaryRow.select("td").first().text() + "</b> " value = summaryRow.select("td").get(1).text().trim();
+ summaryRow.select("td").get(1).text(); else
value = summaryRow.select("td").get(1).html().trim();
}
parsedInformation.put(key, value);
} }
parsedInformation.add(pHtml);
} }
return parsedInformation; return parsedInformation;
} }
@ -165,38 +172,54 @@ public class SummaryFragment extends Fragment {
* {@link #parsedProfileSummaryData}</p> * {@link #parsedProfileSummaryData}</p>
*/ */
private void populateLayout() { private void populateLayout() {
for (String profileSummaryRow : parsedProfileSummaryData) { for (LinkedHashMap.Entry<String, String> entry : parsedProfileSummaryData.entrySet()) {
if (profileSummaryRow.contains("Signature") String key = entry.getKey();
|| profileSummaryRow.contains("Υπογραφή")) { //This may contain css String value = entry.getValue();
ReactiveWebView signatureEntry = new ReactiveWebView(this.getContext());
signatureEntry.setBackgroundColor(Color.argb(1, 255, 255, 255)); if (key.startsWith("Current Status") || key.startsWith("Κατάσταση")){
signatureEntry.loadDataWithBaseURL("file:///android_asset/", profileSummaryRow, addEmptyView();
"text/html", "UTF-8", null);
mainContent.addView(signatureEntry);
continue; continue;
} }
TextView entry = new TextView(this.getContext());
if (profileSummaryRow.contains("@") && TextView textView = new TextView(this.getContext());
(profileSummaryRow.contains("Email") || profileSummaryRow.contains("E-mail"))) {
String email = profileSummaryRow.substring(profileSummaryRow.indexOf(":</b> ") + 6); if (((key.startsWith("Email") || key.startsWith("E-mail"))
profileSummaryRow = profileSummaryRow.replace(email, && value.contains("@")) || key.startsWith("Website") || key.startsWith("Ιστοτόπος"))
"<a href=\"mailto:" + email + "\">" + email + "</a>"); textView.setMovementMethod(LinkMovementMethod.getInstance());
entry.setMovementMethod(LinkMovementMethod.getInstance());
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) 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 else
entry.setTextColor(getResources().getColor(R.color.primary_text)); textView.setTextColor(getResources().getColor(R.color.primary_text));
String textViewContent = "<b>" + key + "</b> " + value;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (key.startsWith("Signature") || key.startsWith("Υπογραφή")){
entry.setText(Html.fromHtml(profileSummaryRow, Html.FROM_HTML_MODE_LEGACY)); addEmptyView();
} else { textViewContent = "<b>" + key + "</b>";
entry.setText(Html.fromHtml(profileSummaryRow));
} }
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()));
}
}

6
app/src/main/java/gr/thmmy/mthmmy/activities/shoutbox/ShoutboxFragment.java

@ -56,9 +56,11 @@ public class ShoutboxFragment extends Fragment {
LinearLayoutManager layoutManager = new LinearLayoutManager(getContext()); LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
layoutManager.setReverseLayout(true); layoutManager.setReverseLayout(true);
recyclerView.setLayoutManager(layoutManager); recyclerView.setLayoutManager(layoutManager);
recyclerView.setItemViewCacheSize(25);
recyclerView.setOnTouchListener((view, motionEvent) -> { recyclerView.setOnTouchListener((view, motionEvent) -> {
editorView.hideMarkdown(); editorView.hideMarkdown();
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Activity.INPUT_METHOD_SERVICE); InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Activity.INPUT_METHOD_SERVICE);
if (imm != null)
imm.hideSoftInputFromWindow(editorView.getWindowToken(), 0); imm.hideSoftInputFromWindow(editorView.getWindowToken(), 0);
return false; return false;
}); });
@ -82,7 +84,7 @@ public class ShoutboxFragment extends Fragment {
} }
@Override @Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.shoutbox_menu, menu); inflater.inflate(R.menu.shoutbox_menu, menu);
} }
@ -100,7 +102,7 @@ public class ShoutboxFragment extends Fragment {
public void onActivityCreated(@Nullable Bundle savedInstanceState) { public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState); super.onActivityCreated(savedInstanceState);
shoutboxViewModel = ViewModelProviders.of(getActivity()).get(ShoutboxViewModel.class); shoutboxViewModel = ViewModelProviders.of(getActivity()).get(ShoutboxViewModel.class);
shoutboxViewModel.getShoutboxMutableLiveData().observe(this, shoutbox -> { shoutboxViewModel.getShoutboxMutableLiveData().observe(getViewLifecycleOwner(), shoutbox -> {
if (shoutbox != null) { if (shoutbox != null) {
Timber.i("Shoutbox loaded successfully"); Timber.i("Shoutbox loaded successfully");
shoutAdapter.setShouts(shoutbox.getShouts()); shoutAdapter.setShouts(shoutbox.getShouts());

118
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.app.AlertDialog;
import androidx.appcompat.content.res.AppCompatResources; import androidx.appcompat.content.res.AppCompatResources;
import androidx.appcompat.widget.AppCompatButton; import androidx.appcompat.widget.AppCompatButton;
import androidx.lifecycle.ViewModelProviders; import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
@ -113,7 +113,7 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
this.postFocusListener = context; this.postFocusListener = context;
this.emojiKeyboard = emojiKeyboard; this.emojiKeyboard = emojiKeyboard;
viewModel = ViewModelProviders.of(context).get(TopicViewModel.class); viewModel = new ViewModelProvider(context).get(TopicViewModel.class);
} }
@Override @Override
@ -137,6 +137,7 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
quickReplyText.setFocusableInTouchMode(true); quickReplyText.setFocusableInTouchMode(true);
quickReplyText.setOnFocusChangeListener((v, hasFocus) -> quickReplyText.post(() -> { quickReplyText.setOnFocusChangeListener((v, hasFocus) -> quickReplyText.post(() -> {
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null)
imm.showSoftInput(quickReplyText, InputMethodManager.SHOW_IMPLICIT); imm.showSoftInput(quickReplyText, InputMethodManager.SHOW_IMPLICIT);
})); }));
quickReplyText.requestFocus(); quickReplyText.requestFocus();
@ -150,6 +151,7 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
editPostEdittext.setFocusableInTouchMode(true); editPostEdittext.setFocusableInTouchMode(true);
editPostEdittext.setOnFocusChangeListener((v, hasFocus) -> editPostEdittext.post(() -> { editPostEdittext.setOnFocusChangeListener((v, hasFocus) -> editPostEdittext.post(() -> {
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null)
imm.showSoftInput(editPostEdittext, InputMethodManager.SHOW_IMPLICIT); imm.showSoftInput(editPostEdittext, InputMethodManager.SHOW_IMPLICIT);
})); }));
editPostEdittext.requestFocus(); editPostEdittext.requestFocus();
@ -215,12 +217,10 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
for (Poll.Entry entry : entries) { for (Poll.Entry entry : entries) {
CheckBox checkBox = new CheckBox(context); CheckBox checkBox = new CheckBox(context);
checkBox.setMovementMethod(LinkMovementMethod.getInstance()); 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)); checkBox.setText(Html.fromHtml(entry.getEntryName(), Html.FROM_HTML_MODE_LEGACY));
} else { else
//noinspection deprecation
checkBox.setText(Html.fromHtml(entry.getEntryName())); checkBox.setText(Html.fromHtml(entry.getEntryName()));
}
checkBox.setTextColor(primaryTextColor); checkBox.setTextColor(primaryTextColor);
holder.optionsLayout.addView(checkBox); holder.optionsLayout.addView(checkBox);
} }
@ -236,10 +236,9 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
radioButton.setMovementMethod(LinkMovementMethod.getInstance()); radioButton.setMovementMethod(LinkMovementMethod.getInstance());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
radioButton.setText(Html.fromHtml(entries[i].getEntryName(), Html.FROM_HTML_MODE_LEGACY)); radioButton.setText(Html.fromHtml(entries[i].getEntryName(), Html.FROM_HTML_MODE_LEGACY));
} else { } else
//noinspection deprecation
radioButton.setText(Html.fromHtml(entries[i].getEntryName())); radioButton.setText(Html.fromHtml(entries[i].getEntryName()));
}
radioButton.setText(ThmmyParser.html2span(context, entries[i].getEntryName())); radioButton.setText(ThmmyParser.html2span(context, entries[i].getEntryName()));
radioButton.setTextColor(primaryTextColor); radioButton.setTextColor(primaryTextColor);
radioGroup.addView(radioButton); radioGroup.addView(radioButton);
@ -255,12 +254,11 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
Poll.Entry entry = entries1[i]; Poll.Entry entry = entries1[i];
TextView textView = new TextView(context); TextView textView = new TextView(context);
textView.setMovementMethod(LinkMovementMethod.getInstance()); 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)); textView.setText(Html.fromHtml(entry.getEntryName(), Html.FROM_HTML_MODE_LEGACY));
} else { else
//noinspection deprecation
textView.setText(Html.fromHtml(entry.getEntryName())); textView.setText(Html.fromHtml(entry.getEntryName()));
}
textView.setTextColor(primaryTextColor); textView.setTextColor(primaryTextColor);
if (poll.getSelectedEntryIndex() == i) { if (poll.getSelectedEntryIndex() == i) {
// apply bold to the selected entry // apply bold to the selected entry
@ -363,7 +361,6 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
holder.post.setClickable(true); holder.post.setClickable(true);
holder.post.setWebViewClient(new LinkLauncher()); holder.post.setWebViewClient(new LinkLauncher());
//noinspection ConstantConditions
loadAvatar(currentPost.getThumbnailURL(), holder.thumbnail, holder.itemView.getContext()); loadAvatar(currentPost.getThumbnailURL(), holder.thumbnail, holder.itemView.getContext());
//Sets username,submit date, index number, subject, post's and attached files texts //Sets username,submit date, index number, subject, post's and attached files texts
@ -385,7 +382,7 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
int filesTextColor; int filesTextColor;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
filesTextColor = context.getResources().getColor(R.color.accent, null); filesTextColor = context.getResources().getColor(R.color.accent, null);
} else //noinspection deprecation } else
filesTextColor = context.getResources().getColor(R.color.accent); filesTextColor = context.getResources().getColor(R.color.accent);
for (final ThmmyFile attachedFile : currentPost.getAttachedFiles()) { for (final ThmmyFile attachedFile : currentPost.getAttachedFiles()) {
@ -408,7 +405,7 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
int lastEditTextColor; int lastEditTextColor;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
lastEditTextColor = context.getResources().getColor(R.color.white, null); lastEditTextColor = context.getResources().getColor(R.color.white, null);
} else //noinspection deprecation } else
lastEditTextColor = context.getResources().getColor(R.color.white); lastEditTextColor = context.getResources().getColor(R.color.white);
final TextView lastEdit = new TextView(context); final TextView lastEdit = new TextView(context);
@ -492,7 +489,7 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
holder.cardChildLinear.setBackground(context.getResources(). holder.cardChildLinear.setBackground(context.getResources().
getDrawable(R.drawable.mention_card, null)); getDrawable(R.drawable.mention_card, null));
} else //noinspection deprecation } else
holder.cardChildLinear.setBackground(context.getResources(). holder.cardChildLinear.setBackground(context.getResources().
getDrawable(R.drawable.mention_card)); getDrawable(R.drawable.mention_card));
} else if (mUserColor == TopicParser.USER_COLOR_PINK) { } else if (mUserColor == TopicParser.USER_COLOR_PINK) {
@ -500,7 +497,7 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
holder.cardChildLinear.setBackground(context.getResources(). holder.cardChildLinear.setBackground(context.getResources().
getDrawable(R.drawable.member_of_the_month_card, null)); getDrawable(R.drawable.member_of_the_month_card, null));
} else //noinspection deprecation } else
holder.cardChildLinear.setBackground(context.getResources(). holder.cardChildLinear.setBackground(context.getResources().
getDrawable(R.drawable.member_of_the_month_card)); getDrawable(R.drawable.member_of_the_month_card));
} else holder.cardChildLinear.setBackground(null); } else holder.cardChildLinear.setBackground(null);
@ -623,7 +620,6 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
popUp.showAsDropDown(holder.overflowButton); popUp.showAsDropDown(holder.overflowButton);
}); });
//noinspection PointlessBooleanExpression,ConstantConditions
if (!BaseActivity.getSessionManager().isLoggedIn() || !viewModel.canReply()) if (!BaseActivity.getSessionManager().isLoggedIn() || !viewModel.canReply())
holder.quoteToggle.setVisibility(View.GONE); holder.quoteToggle.setVisibility(View.GONE);
else { else {
@ -644,7 +640,6 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
final QuickReplyViewHolder holder = (QuickReplyViewHolder) currentHolder; final QuickReplyViewHolder holder = (QuickReplyViewHolder) currentHolder;
Post reply = (Post) topicItems.get(position); Post reply = (Post) topicItems.get(position);
//noinspection ConstantConditions
loadAvatar(getSessionManager().getAvatarLink(), holder.thumbnail, holder.itemView.getContext()); loadAvatar(getSessionManager().getAvatarLink(), holder.thumbnail, holder.itemView.getContext());
holder.username.setText(getSessionManager().getUsername()); holder.username.setText(getSessionManager().getUsername());
@ -669,7 +664,9 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
return; return;
} }
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null)
imm.hideSoftInputFromWindow(view.getWindowToken(), 0); imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
holder.itemView.setAlpha(0.5f); holder.itemView.setAlpha(0.5f);
holder.itemView.setEnabled(false); holder.itemView.setEnabled(false);
emojiKeyboard.hide(); emojiKeyboard.hide();
@ -696,47 +693,16 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
} }
holder.replyEditor.setText(replyText); holder.replyEditor.setText(replyText);
holder.replyEditor.getEditText().setSelection(holder.replyEditor.getText().length()); holder.replyEditor.getEditText().setSelection(holder.replyEditor.getText().length());
holder.replyEditor.getEditText().addTextChangedListener(new TextWatcher() { holder.replyEditor.getEditText().addTextChangedListener(createTextWatcher(holder));
@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) {
}
});
if (backPressHidden) { if (backPressHidden) {
holder.replyEditor.requestEditTextFocus(); holder.replyEditor.requestEditTextFocus();
backPressHidden = false; backPressHidden = false;
} }
holder.quickReplySubject.addTextChangedListener(new TextWatcher() { holder.quickReplySubject.addTextChangedListener(createTextWatcher(holder));
@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) {
}
});
} else if (currentHolder instanceof EditMessageViewHolder) { } else if (currentHolder instanceof EditMessageViewHolder) {
final EditMessageViewHolder holder = (EditMessageViewHolder) currentHolder; final EditMessageViewHolder holder = (EditMessageViewHolder) currentHolder;
//noinspection ConstantConditions
loadAvatar(getSessionManager().getAvatarLink(), holder.thumbnail, holder.itemView.getContext()); loadAvatar(getSessionManager().getAvatarLink(), holder.thumbnail, holder.itemView.getContext());
holder.username.setText(getSessionManager().getUsername()); holder.username.setText(getSessionManager().getUsername());
@ -759,7 +725,9 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
return; return;
} }
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null)
imm.hideSoftInputFromWindow(view.getWindowToken(), 0); imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
holder.itemView.setAlpha(0.5f); holder.itemView.setAlpha(0.5f);
holder.itemView.setEnabled(false); holder.itemView.setEnabled(false);
emojiKeyboard.hide(); emojiKeyboard.hide();
@ -767,44 +735,32 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
viewModel.editPost(position, holder.editSubject.getText().toString(), holder.editEditor.getText().toString()); viewModel.editPost(position, holder.editSubject.getText().toString(), holder.editEditor.getText().toString());
}); });
holder.editSubject.addTextChangedListener(new TextWatcher() { holder.editSubject.addTextChangedListener(createTextWatcher(holder));
@Override holder.editEditor.getEditText().addTextChangedListener(createTextWatcher(holder));
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
if (backPressHidden) {
holder.editEditor.requestEditTextFocus();
backPressHidden = false;
} }
@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) {
} }
private TextWatcher createTextWatcher(@NonNull final RecyclerView.ViewHolder holder){
return new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { }
@Override @Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
((Post) topicItems.get(holder.getAdapterPosition())).setBbContent(charSequence.toString()); int position = holder.getAdapterPosition();
if (position >= 0 && position < topicItems.size())
((Post) topicItems.get(position)).setBbContent(charSequence.toString());
} }
@Override @Override
public void afterTextChanged(Editable editable) { public void afterTextChanged(Editable editable) { }
};
}
});
if (backPressHidden) {
holder.editEditor.requestEditTextFocus();
backPressHidden = false;
}
}
}
} }
private void loadAvatar(String imageUrl, ImageView imageView, Context context) { private void loadAvatar(String imageUrl, ImageView imageView, Context context) {
@ -940,9 +896,7 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
* This class is used to handle link clicks in WebViews. When link url is one that the app can * 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. * handle internally, it does. Otherwise user is prompt to open the link in a browser.
*/ */
@SuppressWarnings("unchecked")
private class LinkLauncher extends WebViewClient { private class LinkLauncher extends WebViewClient {
@SuppressWarnings("deprecation")
@Override @Override
public boolean shouldOverrideUrlLoading(WebView view, String url) { public boolean shouldOverrideUrlLoading(WebView view, String url) {
final Uri uri = Uri.parse(url); final Uri uri = Uri.parse(url);

4
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 android.util.Log;
import androidx.annotation.NonNull;
import com.google.firebase.crashlytics.FirebaseCrashlytics; import com.google.firebase.crashlytics.FirebaseCrashlytics;
import timber.log.Timber.DebugTree; import timber.log.Timber.DebugTree;
@ -14,7 +16,7 @@ public class CrashReportingTree extends DebugTree {
} }
@Override @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) { if (priority == Log.VERBOSE || priority == Log.DEBUG) {
return; return;
} }

27
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. * This method fixes html so that embedded videos will render properly and be lightweight.
* *
@ -146,23 +148,28 @@ public class ParseHelpers {
} }
String fixed = html.outerHtml(); String fixed = html.outerHtml();
int tmp_counter = 0; int counter = 0;
while (fixed.contains("<embed")) { while (fixed.contains("<embed")) {
if (tmp_counter > embededVideosUrls.size()) if (counter > embededVideosUrls.size())
break; break;
final String videoId = embededVideosUrls.get(counter);
fixed = fixed.replace( fixed = fixed.replace(
fixed.substring(fixed.indexOf("<embed"), fixed.indexOf("/noembed>") + 9) fixed.substring(fixed.indexOf("<embed"), fixed.indexOf("/noembed>") + 9)
, "<div class=\"yt\">" , "<div class=\"yt\">"
+ "<a href=\"https://www.youtube.com/watch?v=" + "<a href=\"https://www.youtube.com/watch?v="
+ embededVideosUrls.get(tmp_counter) + "\" target=\"_blank\">" + videoId
+ "\" target=\"_blank\">"
+ "<img class=\"embedded-video-play\" " + "<img class=\"embedded-video-play\" "
+ "src=\"YouTube_light_color_icon.png\">" + "src=\"YouTube_light_color_icon.png?"
+ "</a>" + VIDEO_ID_PARAMETER
+ "<img src=\"https://img.youtube.com/vi/" + "="
+ embededVideosUrls.get(tmp_counter) + videoId // To grab it in ReactiveWebView
+ "/default.jpg\" alt=\"\" border=\"0\" width=\"40%\">" + "\" style=\"background-image: url('"
+ "</div>"); + "https://img.youtube.com/vi/"
++tmp_counter; + videoId
+ "/default.jpg');\"></a></div>"
);
++counter;
} }
return fixed; return fixed;
} }

19
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.ClipData;
import android.content.ClipboardManager; import android.content.ClipboardManager;
import android.content.Context; import android.content.Context;
import android.net.Uri;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.webkit.WebView; import android.webkit.WebView;
@ -13,6 +14,7 @@ import gr.thmmy.mthmmy.base.BaseApplication;
import gr.thmmy.mthmmy.utils.ui.ImageDownloadDialogBuilder; import gr.thmmy.mthmmy.utils.ui.ImageDownloadDialogBuilder;
import static android.content.Context.CLIPBOARD_SERVICE; 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; import static gr.thmmy.mthmmy.utils.ui.PhotoViewUtils.displayPhotoViewImage;
public class ReactiveWebView extends WebView { public class ReactiveWebView extends WebView {
@ -77,8 +79,16 @@ public class ReactiveWebView extends WebView {
copyUrlToClipboard(result.getExtra()); copyUrlToClipboard(result.getExtra());
else if(result.getType() == WebView.HitTestResult.IMAGE_TYPE) { else if(result.getType() == WebView.HitTestResult.IMAGE_TYPE) {
String imageURL = result.getExtra(); String imageURL = result.getExtra();
ImageDownloadDialogBuilder builder = new ImageDownloadDialogBuilder(context,imageURL); showImageDownloadDialog(imageURL);
builder.show(); }
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; return false;
}); });
@ -90,4 +100,9 @@ public class ReactiveWebView extends WebView {
clipboard.setPrimaryClip(clip); clipboard.setPrimaryClip(clip);
Toast.makeText(BaseApplication.getInstance().getApplicationContext(),context.getString(R.string.link_copied_msg),Toast.LENGTH_SHORT).show(); 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();
}
} }

4
app/src/main/java/gr/thmmy/mthmmy/views/editorview/EditorView.java

@ -375,10 +375,12 @@ public class EditorView extends LinearLayout implements EmojiInputField {
if (emojiKeyboard.onEmojiButtonToggle()) { if (emojiKeyboard.onEmojiButtonToggle()) {
//prevent system keyboard from appearing when clicking the edittext //prevent system keyboard from appearing when clicking the edittext
editText.setTextIsSelectable(true); editText.setTextIsSelectable(true);
if (imm != null)
imm.hideSoftInputFromWindow(getWindowToken(), 0); imm.hideSoftInputFromWindow(getWindowToken(), 0);
} }
else { else {
editText.requestFocus(); editText.requestFocus();
if (imm != null)
imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT); imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT);
} }
editText.setSelection(selectionStart, selectionEnd); editText.setSelection(selectionStart, selectionEnd);
@ -398,9 +400,11 @@ public class EditorView extends LinearLayout implements EmojiInputField {
editText.setOnClickListener(view -> { editText.setOnClickListener(view -> {
if (!emojiKeyboard.isVisible()) { if (!emojiKeyboard.isVisible()) {
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Activity.INPUT_METHOD_SERVICE); InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Activity.INPUT_METHOD_SERVICE);
if (imm != null)
imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT); imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT);
} else { } else {
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Activity.INPUT_METHOD_SERVICE); InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Activity.INPUT_METHOD_SERVICE);
if (imm != null)
imm.hideSoftInputFromWindow(getWindowToken(), 0); imm.hideSoftInputFromWindow(getWindowToken(), 0);
requestEditTextFocus(); requestEditTextFocus();
} }

2
app/src/main/res/layout/activity_create_content.xml

@ -53,7 +53,7 @@
android:layout_below="@id/subject_input" android:layout_below="@id/subject_input"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
app:hint="topic message"/> app:hint="@string/message"/>
<gr.thmmy.mthmmy.views.editorview.EmojiKeyboard <gr.thmmy.mthmmy.views.editorview.EmojiKeyboard
android:id="@+id/emoji_keyboard" android:id="@+id/emoji_keyboard"

3
app/src/main/res/layout/fragment_profile_summary.xml

@ -5,8 +5,9 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/background_lighter" android:background="@color/background_lighter"
android:paddingEnd="16dp"
android:paddingStart="16dp" android:paddingStart="16dp"
android:paddingEnd="16dp"
android:paddingTop="16dp"
android:scrollbars="none"> android:scrollbars="none">
<LinearLayout <LinearLayout

5
app/src/main/res/values/strings.xml

@ -61,10 +61,9 @@
<string name="button_page">Page</string> <string name="button_page">Page</string>
<string name="button_next">next</string> <string name="button_next">next</string>
<string name="button_last">last</string> <string name="button_last">last</string>
<string name="quick_reply">Quick reply&#8230;</string> <string name="subject">Subject</string>
<string name="subject">Subject&#8230;</string> <string name="message">Message</string>
<string name="submit">Submit</string> <string name="submit">Submit</string>
<string name="post_message">Message&#8230;</string>
<string name="network_error_retry_prompt">Could not connect to thmmy.gr\n\nTap to retry</string> <string name="network_error_retry_prompt">Could not connect to thmmy.gr\n\nTap to retry</string>
<string name="generic_network_error">Network error</string> <string name="generic_network_error">Network error</string>
<string name="retry">retry</string> <string name="retry">retry</string>

Loading…
Cancel
Save