Browse Source

Version 1.8.3

master v1.8.3
Ezerous 4 years ago
parent
commit
daeb5606e1
No known key found for this signature in database GPG Key ID: 262B2954BBA319E3
  1. 6
      app/build.gradle
  2. 1
      app/src/main/AndroidManifest.xml
  3. 11
      app/src/main/assets/style_dark.css
  4. 11
      app/src/main/assets/style_light.css
  5. 2
      app/src/main/java/gr/thmmy/mthmmy/activities/AboutActivity.java
  6. 16
      app/src/main/java/gr/thmmy/mthmmy/activities/LoginActivity.java
  7. 5
      app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardActivity.java
  8. 5
      app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksActivity.java
  9. 3
      app/src/main/java/gr/thmmy/mthmmy/activities/create_content/CreateContentActivity.java
  10. 2
      app/src/main/java/gr/thmmy/mthmmy/activities/downloads/DownloadsActivity.java
  11. 7
      app/src/main/java/gr/thmmy/mthmmy/activities/main/MainActivity.java
  12. 13
      app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadFragment.java
  13. 46
      app/src/main/java/gr/thmmy/mthmmy/activities/profile/ProfileActivity.java
  14. 43
      app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsAdapter.java
  15. 72
      app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsFragment.java
  16. 71
      app/src/main/java/gr/thmmy/mthmmy/activities/profile/stats/StatsFragment.java
  17. 7
      app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java
  18. 6
      app/src/main/java/gr/thmmy/mthmmy/activities/shoutbox/ShoutboxFragment.java
  19. 16
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java
  20. 20
      app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadActivity.java
  21. 9
      app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadFieldsBuilderActivity.java
  22. 3
      app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsHelper.java
  23. 10
      app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java
  24. 6
      app/src/main/java/gr/thmmy/mthmmy/base/BaseFragment.java
  25. 18
      app/src/main/res/layout/fragment_profile_latest_posts.xml
  26. 47
      app/src/main/res/layout/fragment_profile_latest_posts_row.xml
  27. 26
      app/src/main/res/layout/fragment_profile_stats.xml
  28. 2
      app/src/main/res/layout/fragment_profile_summary.xml
  29. 2
      app/src/main/res/layout/fragment_shoutbox.xml
  30. 1
      app/src/main/res/values/colors.xml
  31. 1
      app/src/main/res/values/strings.xml
  32. 2
      build.gradle

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 25 versionCode 26
versionName "1.8.2" versionName "1.8.3"
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() + "\""
@ -91,7 +91,7 @@ dependencies {
implementation 'com.google.android.material:material:1.1.0' implementation 'com.google.android.material:material:1.1.0'
implementation 'com.google.firebase:firebase-analytics:17.4.4' implementation 'com.google.firebase:firebase-analytics:17.4.4'
implementation 'com.google.firebase:firebase-crashlytics:17.1.1' implementation 'com.google.firebase:firebase-crashlytics:17.1.1'
implementation 'com.google.firebase:firebase-messaging:20.2.3' implementation 'com.google.firebase:firebase-messaging:20.2.4'
implementation 'com.snatik:storage:2.1.0' implementation 'com.snatik:storage:2.1.0'
implementation ('com.squareup.okhttp3:okhttp:3.12.12') { //TODO: Warning: OkHttp has dropped support for Android 19 since OkHttp 3.13! implementation ('com.squareup.okhttp3:okhttp:3.12.12') { //TODO: Warning: OkHttp has dropped support for Android 19 since OkHttp 3.13!
force = true //TODO: Remove when minSdkVersion >= 21 force = true //TODO: Remove when minSdkVersion >= 21

1
app/src/main/AndroidManifest.xml

@ -96,6 +96,7 @@
</activity> </activity>
<activity <activity
android:name=".activities.profile.ProfileActivity" android:name=".activities.profile.ProfileActivity"
android:configChanges="orientation|screenSize"
android:theme="@style/AppTheme.NoActionBar" /> android:theme="@style/AppTheme.NoActionBar" />
<activity <activity
android:name=".activities.board.BoardActivity" android:name=".activities.board.BoardActivity"

11
app/src/main/assets/style_dark.css

@ -0,0 +1,11 @@
body {
background: #323232 !important;
}
.post, .personalmessage {
background: #323232 !important;
}
.customSignature {
background: #323232 !important;
}

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

@ -0,0 +1,11 @@
body {
background: #434649 !important;
}
.post, .personalmessage {
background: #434649 !important;
}
.customSignature {
background: #434649 !important;
}

2
app/src/main/java/gr/thmmy/mthmmy/activities/AboutActivity.java

@ -110,7 +110,7 @@ public class AboutActivity extends BaseActivity {
} }
versionTextView.setOnLongClickListener(view -> { versionTextView.setOnLongClickListener(view -> {
Toast.makeText(getApplicationContext(), BaseApplication.getFirebaseProjectId(), Toast.LENGTH_SHORT).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), BaseApplication.getFirebaseProjectId(), Toast.LENGTH_SHORT).show();
return true; return true;
}); });
} }

16
app/src/main/java/gr/thmmy/mthmmy/activities/LoginActivity.java

@ -105,7 +105,7 @@ public class LoginActivity extends BaseActivity {
} }
private void onLoginFailed() { private void onLoginFailed() {
Toast.makeText(getBaseContext(), "Login failed", Toast.LENGTH_LONG).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Login failed", Toast.LENGTH_LONG).show();
btnLogin.setEnabled(true); btnLogin.setEnabled(true);
} }
@ -166,7 +166,7 @@ public class LoginActivity extends BaseActivity {
protected void onPostExecute(Integer result) { //Handle attempt result protected void onPostExecute(Integer result) { //Handle attempt result
switch (result) { switch (result) {
case SUCCESS: //Successful login case SUCCESS: //Successful login
Toast.makeText(getApplicationContext(), Toast.makeText(BaseApplication.getInstance().getApplicationContext(),
"Welcome, " + sessionManager.getUsername() + "!", Toast.LENGTH_LONG) "Welcome, " + sessionManager.getUsername() + "!", Toast.LENGTH_LONG)
.show(); .show();
BaseApplication.getInstance().logFirebaseAnalyticsEvent(FirebaseAnalytics.Event.LOGIN, null); BaseApplication.getInstance().logFirebaseAnalyticsEvent(FirebaseAnalytics.Event.LOGIN, null);
@ -180,30 +180,30 @@ public class LoginActivity extends BaseActivity {
overridePendingTransition(R.anim.push_left_in, R.anim.push_left_out); overridePendingTransition(R.anim.push_left_in, R.anim.push_left_out);
break; break;
case WRONG_USER: case WRONG_USER:
Toast.makeText(getApplicationContext(), Toast.makeText(BaseApplication.getInstance().getApplicationContext(),
"Wrong username!", Toast.LENGTH_LONG).show(); "Wrong username!", Toast.LENGTH_LONG).show();
inputUsername.requestFocus(); inputUsername.requestFocus();
break; break;
case WRONG_PASSWORD: case WRONG_PASSWORD:
Toast.makeText(getApplicationContext(), Toast.makeText(BaseApplication.getInstance().getApplicationContext(),
"Wrong password!", Toast.LENGTH_LONG).show(); "Wrong password!", Toast.LENGTH_LONG).show();
inputPassword.requestFocus(); inputPassword.requestFocus();
break; break;
case BANNED_USER: case BANNED_USER:
Toast.makeText(getApplicationContext(), Toast.makeText(BaseApplication.getInstance().getApplicationContext(),
"You are banned!", Toast.LENGTH_LONG).show(); "You are banned!", Toast.LENGTH_LONG).show();
inputPassword.requestFocus(); inputPassword.requestFocus();
break; break;
case FAILURE: case FAILURE:
Toast.makeText(getApplicationContext(), Toast.makeText(BaseApplication.getInstance().getApplicationContext(),
"Login failed...", Toast.LENGTH_LONG).show(); "Login failed...", Toast.LENGTH_LONG).show();
break; break;
case CONNECTION_ERROR: case CONNECTION_ERROR:
Toast.makeText(getApplicationContext(), Toast.makeText(BaseApplication.getInstance().getApplicationContext(),
"Connection Error", Toast.LENGTH_LONG).show(); "Connection Error", Toast.LENGTH_LONG).show();
break; break;
case EXCEPTION: case EXCEPTION:
Toast.makeText(getApplicationContext(), Toast.makeText(BaseApplication.getInstance().getApplicationContext(),
"Error", Toast.LENGTH_LONG).show(); "Error", Toast.LENGTH_LONG).show();
break; break;

5
app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardActivity.java

@ -28,6 +28,7 @@ import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.LoginActivity; import gr.thmmy.mthmmy.activities.LoginActivity;
import gr.thmmy.mthmmy.activities.create_content.CreateContentActivity; import gr.thmmy.mthmmy.activities.create_content.CreateContentActivity;
import gr.thmmy.mthmmy.base.BaseActivity; import gr.thmmy.mthmmy.base.BaseActivity;
import gr.thmmy.mthmmy.base.BaseApplication;
import gr.thmmy.mthmmy.model.Board; import gr.thmmy.mthmmy.model.Board;
import gr.thmmy.mthmmy.model.Bookmark; import gr.thmmy.mthmmy.model.Bookmark;
import gr.thmmy.mthmmy.model.ThmmyPage; import gr.thmmy.mthmmy.model.ThmmyPage;
@ -77,7 +78,7 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo
ThmmyPage.PageCategory target = ThmmyPage.resolvePageCategory(Uri.parse(boardUrl)); ThmmyPage.PageCategory target = ThmmyPage.resolvePageCategory(Uri.parse(boardUrl));
if (!target.is(ThmmyPage.PageCategory.BOARD)) { if (!target.is(ThmmyPage.PageCategory.BOARD)) {
Timber.e("Bundle came with a non board url!\nUrl:\n%s", boardUrl); Timber.e("Bundle came with a non board url!\nUrl:\n%s", boardUrl);
Toast.makeText(this, "An error has occurred\nAborting.", Toast.LENGTH_SHORT).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "An error has occurred\nAborting.", Toast.LENGTH_SHORT).show();
finish(); finish();
} }
@ -321,7 +322,7 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo
pStarter = topicColumns.get(3).text(); pStarter = topicColumns.get(3).text();
pStats = "Replies: " + topicColumns.get(4).text() + ", Views: " + topicColumns.get(5).text(); pStats = "Replies: " + topicColumns.get(4).text() + ", Views: " + topicColumns.get(5).text();
pLastPost = topicColumns.last().text(); pLastPost = topicColumns.get(6).text();
Matcher matcher = pLastPostPattern.matcher(pLastPost); Matcher matcher = pLastPostPattern.matcher(pLastPost);
if (matcher.find()){ if (matcher.find()){
pLastPostDateTime = matcher.group(1); pLastPostDateTime = matcher.group(1);

5
app/src/main/java/gr/thmmy/mthmmy/activities/bookmarks/BookmarksActivity.java

@ -19,6 +19,7 @@ import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.board.BoardActivity; import gr.thmmy.mthmmy.activities.board.BoardActivity;
import gr.thmmy.mthmmy.activities.topic.TopicActivity; import gr.thmmy.mthmmy.activities.topic.TopicActivity;
import gr.thmmy.mthmmy.base.BaseActivity; import gr.thmmy.mthmmy.base.BaseActivity;
import gr.thmmy.mthmmy.base.BaseApplication;
import gr.thmmy.mthmmy.model.Bookmark; import gr.thmmy.mthmmy.model.Bookmark;
import static gr.thmmy.mthmmy.activities.board.BoardActivity.BUNDLE_BOARD_TITLE; import static gr.thmmy.mthmmy.activities.board.BoardActivity.BUNDLE_BOARD_TITLE;
@ -90,7 +91,7 @@ public class BookmarksActivity extends BaseActivity {
return toggleNotification(bookmarkedTopic); return toggleNotification(bookmarkedTopic);
case BookmarksFragment.INTERACTION_REMOVE_TOPIC_BOOKMARK: case BookmarksFragment.INTERACTION_REMOVE_TOPIC_BOOKMARK:
removeBookmark(bookmarkedTopic); removeBookmark(bookmarkedTopic);
Toast.makeText(BookmarksActivity.this, "Bookmark removed", Toast.LENGTH_SHORT).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Bookmark removed", Toast.LENGTH_SHORT).show();
break; break;
default: default:
break; break;
@ -113,7 +114,7 @@ public class BookmarksActivity extends BaseActivity {
return toggleNotification(bookmarkedBoard); return toggleNotification(bookmarkedBoard);
case BookmarksFragment.INTERACTION_REMOVE_BOARD_BOOKMARK: case BookmarksFragment.INTERACTION_REMOVE_BOARD_BOOKMARK:
removeBookmark(bookmarkedBoard); removeBookmark(bookmarkedBoard);
Toast.makeText(BookmarksActivity.this, "Bookmark removed", Toast.LENGTH_SHORT).show(); Toast.makeText(getApplicationContext(), "Bookmark removed", Toast.LENGTH_SHORT).show();
break; break;
default: default:
break; break;

3
app/src/main/java/gr/thmmy/mthmmy/activities/create_content/CreateContentActivity.java

@ -15,6 +15,7 @@ import com.google.android.material.textfield.TextInputLayout;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.settings.SettingsActivity; import gr.thmmy.mthmmy.activities.settings.SettingsActivity;
import gr.thmmy.mthmmy.base.BaseActivity; import gr.thmmy.mthmmy.base.BaseActivity;
import gr.thmmy.mthmmy.base.BaseApplication;
import gr.thmmy.mthmmy.session.SessionManager; import gr.thmmy.mthmmy.session.SessionManager;
import gr.thmmy.mthmmy.views.editorview.EditorView; import gr.thmmy.mthmmy.views.editorview.EditorView;
import gr.thmmy.mthmmy.views.editorview.EmojiKeyboard; import gr.thmmy.mthmmy.views.editorview.EmojiKeyboard;
@ -104,7 +105,7 @@ public class CreateContentActivity extends BaseActivity implements NewTopicTask.
finish(); finish();
} else { } else {
Timber.w("New topic creation failed"); Timber.w("New topic creation failed");
Toast.makeText(getBaseContext(), "Failed to create new topic!", Toast.LENGTH_LONG).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Failed to create new topic!", Toast.LENGTH_LONG).show();
finish(); finish();
} }
} }

2
app/src/main/java/gr/thmmy/mthmmy/activities/downloads/DownloadsActivity.java

@ -78,7 +78,7 @@ public class DownloadsActivity extends BaseActivity implements DownloadsAdapter.
ThmmyPage.PageCategory target = ThmmyPage.resolvePageCategory(Uri.parse(downloadsUrl)); ThmmyPage.PageCategory target = ThmmyPage.resolvePageCategory(Uri.parse(downloadsUrl));
if (!target.is(ThmmyPage.PageCategory.DOWNLOADS)) { if (!target.is(ThmmyPage.PageCategory.DOWNLOADS)) {
Timber.e("Bundle came with a non downloads url!\nUrl:\n%s", downloadsUrl); Timber.e("Bundle came with a non downloads url!\nUrl:\n%s", downloadsUrl);
Toast.makeText(this, "An error has occurred\nAborting.", Toast.LENGTH_SHORT).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "An error has occurred\nAborting.", Toast.LENGTH_SHORT).show();
finish(); finish();
} }
} else downloadsUrl = downloadsIndexUrl; } else downloadsUrl = downloadsIndexUrl;

7
app/src/main/java/gr/thmmy/mthmmy/activities/main/MainActivity.java

@ -31,6 +31,7 @@ import gr.thmmy.mthmmy.activities.profile.ProfileActivity;
import gr.thmmy.mthmmy.activities.settings.SettingsActivity; import gr.thmmy.mthmmy.activities.settings.SettingsActivity;
import gr.thmmy.mthmmy.activities.topic.TopicActivity; import gr.thmmy.mthmmy.activities.topic.TopicActivity;
import gr.thmmy.mthmmy.base.BaseActivity; import gr.thmmy.mthmmy.base.BaseActivity;
import gr.thmmy.mthmmy.base.BaseApplication;
import gr.thmmy.mthmmy.model.Board; import gr.thmmy.mthmmy.model.Board;
import gr.thmmy.mthmmy.model.ThmmyPage; import gr.thmmy.mthmmy.model.ThmmyPage;
import gr.thmmy.mthmmy.model.TopicSummary; import gr.thmmy.mthmmy.model.TopicSummary;
@ -138,7 +139,7 @@ public class MainActivity extends BaseActivity implements RecentFragment.RecentF
super.onBackPressed(); super.onBackPressed();
return; return;
} else { } else {
Toast.makeText(getBaseContext(), "Press back again to exit!" Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Press back again to exit!"
, Toast.LENGTH_SHORT).show(); , Toast.LENGTH_SHORT).show();
} }
mBackPressed = System.currentTimeMillis(); mBackPressed = System.currentTimeMillis();
@ -278,10 +279,10 @@ public class MainActivity extends BaseActivity implements RecentFragment.RecentF
redirectIntent.putExtra(BUNDLE_DOWNLOADS_TITLE, ""); redirectIntent.putExtra(BUNDLE_DOWNLOADS_TITLE, "");
startActivity(redirectIntent); startActivity(redirectIntent);
} else if (!page.is(ThmmyPage.PageCategory.INDEX)) { } else if (!page.is(ThmmyPage.PageCategory.INDEX)) {
Toast.makeText(this, "This thmmy sector is not yet supported.", Toast.LENGTH_LONG).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "This thmmy sector is not yet supported.", Toast.LENGTH_LONG).show();
} }
} else { } else {
Toast.makeText(this, "This is not thmmy.", Toast.LENGTH_LONG).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "This is not thmmy.", Toast.LENGTH_LONG).show();
} }
} }
} }

13
app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadFragment.java

@ -26,6 +26,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseApplication;
import gr.thmmy.mthmmy.base.BaseFragment; import gr.thmmy.mthmmy.base.BaseFragment;
import gr.thmmy.mthmmy.model.TopicSummary; import gr.thmmy.mthmmy.model.TopicSummary;
import gr.thmmy.mthmmy.session.InvalidSessionException; import gr.thmmy.mthmmy.session.InvalidSessionException;
@ -247,11 +248,11 @@ public class UnreadFragment extends BaseFragment {
else{ else{
hideProgressUI(); hideProgressUI();
if (resultCode == NetworkResultCodes.NETWORK_ERROR) if (resultCode == NetworkResultCodes.NETWORK_ERROR)
Toast.makeText(getContext(), "Network error", Toast.LENGTH_SHORT).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Network error", Toast.LENGTH_SHORT).show();
else if (resultCode == SessionManager.INVALID_SESSION) else if (resultCode == SessionManager.INVALID_SESSION)
Toast.makeText(getContext(), "Session verification failed. Please try logging in again.", Toast.LENGTH_LONG).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Session verification failed. Please try logging in again.", Toast.LENGTH_LONG).show();
else else
Toast.makeText(getContext(), "Unexpected error," + Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Unexpected error," +
" please contact the developers with the details", Toast.LENGTH_LONG).show(); " please contact the developers with the details", Toast.LENGTH_LONG).show();
} }
} }
@ -322,11 +323,11 @@ public class UnreadFragment extends BaseFragment {
else{ else{
hideProgressUI(); hideProgressUI();
if (resultCode == NetworkResultCodes.NETWORK_ERROR) if (resultCode == NetworkResultCodes.NETWORK_ERROR)
Toast.makeText(getContext(), "Network error", Toast.LENGTH_SHORT).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Network error", Toast.LENGTH_SHORT).show();
else if (resultCode == SessionManager.INVALID_SESSION) else if (resultCode == SessionManager.INVALID_SESSION)
Toast.makeText(getContext(), "Session verification failed. Please try logging out and back in again", Toast.LENGTH_LONG).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Session verification failed. Please try logging out and back in again", Toast.LENGTH_LONG).show();
else else
Toast.makeText(getContext(), "Unexpected error," + Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Unexpected error," +
" please contact the developers with the details", Toast.LENGTH_LONG).show(); " please contact the developers with the details", Toast.LENGTH_LONG).show();
} }
} }

46
app/src/main/java/gr/thmmy/mthmmy/activities/profile/ProfileActivity.java

@ -18,6 +18,7 @@ import android.widget.ProgressBar;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatDelegate; import androidx.appcompat.app.AppCompatDelegate;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
@ -43,6 +44,7 @@ import gr.thmmy.mthmmy.activities.profile.stats.StatsFragment;
import gr.thmmy.mthmmy.activities.profile.summary.SummaryFragment; import gr.thmmy.mthmmy.activities.profile.summary.SummaryFragment;
import gr.thmmy.mthmmy.activities.topic.TopicActivity; import gr.thmmy.mthmmy.activities.topic.TopicActivity;
import gr.thmmy.mthmmy.base.BaseActivity; import gr.thmmy.mthmmy.base.BaseActivity;
import gr.thmmy.mthmmy.base.BaseApplication;
import gr.thmmy.mthmmy.model.PostSummary; import gr.thmmy.mthmmy.model.PostSummary;
import gr.thmmy.mthmmy.model.ThmmyPage; import gr.thmmy.mthmmy.model.ThmmyPage;
import gr.thmmy.mthmmy.utils.Parcel; import gr.thmmy.mthmmy.utils.Parcel;
@ -66,7 +68,7 @@ import static gr.thmmy.mthmmy.utils.ui.PhotoViewUtils.displayPhotoViewImage;
* this user's avatar url using the key {@link #BUNDLE_PROFILE_THUMBNAIL_URL} and a <b>String</b> containing * this user's avatar url using the key {@link #BUNDLE_PROFILE_THUMBNAIL_URL} and a <b>String</b> containing
* the username using the key {@link #BUNDLE_PROFILE_USERNAME}. * the username using the key {@link #BUNDLE_PROFILE_USERNAME}.
*/ */
public class ProfileActivity extends BaseActivity implements LatestPostsFragment.LatestPostsFragmentInteractionListener { public class ProfileActivity extends BaseActivity implements LatestPostsFragment.LatestPostsFragmentInteractionListener, LatestPostsFragment.OnLoadingListener, StatsFragment.OnLoadingListener{
/** /**
* The key to use when putting profile's url String to {@link ProfileActivity}'s Bundle. * The key to use when putting profile's url String to {@link ProfileActivity}'s Bundle.
*/ */
@ -177,7 +179,7 @@ public class ProfileActivity extends BaseActivity implements LatestPostsFragment
ThmmyPage.PageCategory target = ThmmyPage.resolvePageCategory(Uri.parse(profileUrl)); ThmmyPage.PageCategory target = ThmmyPage.resolvePageCategory(Uri.parse(profileUrl));
if (!target.is(ThmmyPage.PageCategory.PROFILE)) { if (!target.is(ThmmyPage.PageCategory.PROFILE)) {
Timber.e("Bundle came with a non profile url!\nUrl:\n%s", profileUrl); Timber.e("Bundle came with a non profile url!\nUrl:\n%s", profileUrl);
Toast.makeText(this, "An error has occurred\n Aborting.", Toast.LENGTH_SHORT).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "An error has occurred\n Aborting.", Toast.LENGTH_SHORT).show();
finish(); finish();
} }
if (target.is(ThmmyPage.PageCategory.PROFILE_STATS)) { if (target.is(ThmmyPage.PageCategory.PROFILE_STATS)) {
@ -208,6 +210,24 @@ public class ProfileActivity extends BaseActivity implements LatestPostsFragment
startActivity(i); startActivity(i);
} }
@Override
public void onLoadingLatestPosts(boolean loading) {
setBarVisibility(loading);
}
@Override
public void onLoadingStats(boolean loading) {
setBarVisibility(loading);
}
private void setBarVisibility (boolean visible){
if(visible)
progressBar.setVisibility(ProgressBar.VISIBLE);
else
progressBar.setVisibility(ProgressBar.INVISIBLE);
}
public void onProfileTaskStarted() { public void onProfileTaskStarted() {
progressBar.setVisibility(ProgressBar.VISIBLE); progressBar.setVisibility(ProgressBar.VISIBLE);
if (pmFAB.getVisibility() != View.GONE) pmFAB.setEnabled(false); if (pmFAB.getVisibility() != View.GONE) pmFAB.setEnabled(false);
@ -232,7 +252,7 @@ public class ProfileActivity extends BaseActivity implements LatestPostsFragment
.into(avatarView); .into(avatarView);
} }
else else
Timber.d("Will not load Glide image (invalid context)"); Timber.i("Will not load Glide image (invalid context)");
} }
/** /**
@ -331,12 +351,11 @@ public class ProfileActivity extends BaseActivity implements LatestPostsFragment
} }
} else if (result == NetworkResultCodes.NETWORK_ERROR) { } else if (result == NetworkResultCodes.NETWORK_ERROR) {
Timber.w("Network error while excecuting profile activity"); Timber.w("Network error while excecuting profile activity");
Toast.makeText(getBaseContext(), "Network error" Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Network error"
, Toast.LENGTH_LONG).show(); , Toast.LENGTH_LONG).show();
finish(); finish();
} else { } else {
Timber.d("Parse failed!"); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Fatal error!\n Aborting..."
Toast.makeText(getBaseContext(), "Fatal error!\n Aborting..."
, Toast.LENGTH_LONG).show(); , Toast.LENGTH_LONG).show();
finish(); finish();
} }
@ -356,13 +375,17 @@ public class ProfileActivity extends BaseActivity implements LatestPostsFragment
*/ */
private void setupViewPager(ViewPager viewPager, Document profilePage) { private void setupViewPager(ViewPager viewPager, Document profilePage) {
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager()); ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
adapter.addFrag(SummaryFragment.newInstance(profilePage), "SUMMARY"); adapter.addFragment(SummaryFragment.newInstance(profilePage), "SUMMARY");
adapter.addFrag(LatestPostsFragment.newInstance(profileUrl), "LATEST POSTS"); LatestPostsFragment latestPostsFragment = LatestPostsFragment.newInstance(profileUrl);
adapter.addFrag(StatsFragment.newInstance(profileUrl), "STATS"); latestPostsFragment.setOnLoadingListener(this);
adapter.addFragment(latestPostsFragment, "LATEST POSTS");
StatsFragment statsFragment = StatsFragment.newInstance(profileUrl);
statsFragment.setOnLoadingListener(this);
adapter.addFragment(statsFragment, "STATS");
viewPager.setAdapter(adapter); viewPager.setAdapter(adapter);
} }
private class ViewPagerAdapter extends FragmentPagerAdapter { private static class ViewPagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>(); private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>(); private final List<String> mFragmentTitleList = new ArrayList<>();
@ -370,6 +393,7 @@ public class ProfileActivity extends BaseActivity implements LatestPostsFragment
super(manager); super(manager);
} }
@NonNull
@Override @Override
public Fragment getItem(int position) { public Fragment getItem(int position) {
return mFragmentList.get(position); return mFragmentList.get(position);
@ -380,7 +404,7 @@ public class ProfileActivity extends BaseActivity implements LatestPostsFragment
return mFragmentList.size(); return mFragmentList.size();
} }
void addFrag(Fragment fragment, String title) { void addFragment(Fragment fragment, String title) {
mFragmentList.add(fragment); mFragmentList.add(fragment);
mFragmentTitleList.add(title); mFragmentTitleList.add(title);
} }

43
app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsAdapter.java

@ -9,6 +9,7 @@ import android.view.ViewGroup;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList; import java.util.ArrayList;
@ -18,7 +19,6 @@ import gr.thmmy.mthmmy.base.BaseFragment;
import gr.thmmy.mthmmy.model.PostSummary; import gr.thmmy.mthmmy.model.PostSummary;
import gr.thmmy.mthmmy.model.TopicSummary; import gr.thmmy.mthmmy.model.TopicSummary;
import gr.thmmy.mthmmy.views.ReactiveWebView; import gr.thmmy.mthmmy.views.ReactiveWebView;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
/** /**
* {@link RecyclerView.Adapter} that can display a {@link TopicSummary} and makes a call to the * {@link RecyclerView.Adapter} that can display a {@link TopicSummary} and makes a call to the
@ -27,7 +27,6 @@ import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
class LatestPostsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { class LatestPostsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int VIEW_TYPE_EMPTY = -1; private static final int VIEW_TYPE_EMPTY = -1;
private static final int VIEW_TYPE_ITEM = 0; private static final int VIEW_TYPE_ITEM = 0;
private static final int VIEW_TYPE_LOADING = 1;
private final Context context; private final Context context;
private final LatestPostsFragment.LatestPostsFragmentInteractionListener interactionListener; private final LatestPostsFragment.LatestPostsFragmentInteractionListener interactionListener;
private final ArrayList<PostSummary> parsedTopicSummaries; private final ArrayList<PostSummary> parsedTopicSummaries;
@ -39,42 +38,31 @@ class LatestPostsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
this.parsedTopicSummaries = parsedTopicSummaries; this.parsedTopicSummaries = parsedTopicSummaries;
} }
interface OnLoadMoreListener {
void onLoadMore();
}
@Override @Override
public int getItemViewType(int position) { public int getItemViewType(int position) {
if (parsedTopicSummaries.get(position) == null && position == 0) return VIEW_TYPE_EMPTY; if (parsedTopicSummaries.get(position) == null && position == 0) return VIEW_TYPE_EMPTY;
return parsedTopicSummaries.get(position) == null ? VIEW_TYPE_LOADING : VIEW_TYPE_ITEM; return VIEW_TYPE_ITEM;
} }
@NonNull
@Override @Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_EMPTY) {
View view = LayoutInflater.from(parent.getContext()).
inflate(R.layout.fragment_profile_latest_posts_empty_message, parent, false);
return new RecyclerView.ViewHolder(view){};
}
if (viewType == VIEW_TYPE_ITEM) { if (viewType == VIEW_TYPE_ITEM) {
View view = LayoutInflater.from(parent.getContext()). View view = LayoutInflater.from(parent.getContext()).
inflate(R.layout.fragment_profile_latest_posts_row, parent, false); inflate(R.layout.fragment_profile_latest_posts_row, parent, false);
return new LatestPostViewHolder(view); return new LatestPostViewHolder(view);
} else if (viewType == VIEW_TYPE_LOADING) { } else { // viewType == VIEW_TYPE_EMPTY
View view = LayoutInflater.from(parent.getContext()). View view = LayoutInflater.from(parent.getContext()).
inflate(R.layout.recycler_loading_item, parent, false); inflate(R.layout.fragment_profile_latest_posts_empty_message, parent, false);
return new LoadingViewHolder(view); return new RecyclerView.ViewHolder(view){};
} }
return null;
} }
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
@Override @Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) { public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, int position) {
if (holder instanceof LatestPostViewHolder) {
PostSummary topic = parsedTopicSummaries.get(position); PostSummary topic = parsedTopicSummaries.get(position);
final LatestPostViewHolder latestPostViewHolder = (LatestPostViewHolder) holder; final LatestPostViewHolder latestPostViewHolder = (LatestPostViewHolder) holder;
latestPostViewHolder.postTitle.setText(topic.getSubject()); latestPostViewHolder.postTitle.setText(topic.getSubject());
latestPostViewHolder.postDate.setText(topic.getDateTime()); latestPostViewHolder.postDate.setText(topic.getDateTime());
latestPostViewHolder.post.setBackgroundColor(Color.argb(1, 255, 255, 255)); latestPostViewHolder.post.setBackgroundColor(Color.argb(1, 255, 255, 255));
@ -89,10 +77,6 @@ class LatestPostsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
parsedTopicSummaries.get(holder.getAdapterPosition())); parsedTopicSummaries.get(holder.getAdapterPosition()));
} }
}); });
} else if (holder instanceof LoadingViewHolder) {
LoadingViewHolder loadingViewHolder = (LoadingViewHolder) holder;
loadingViewHolder.progressBar.setIndeterminate(true);
}
} }
@Override @Override
@ -108,19 +92,10 @@ class LatestPostsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
LatestPostViewHolder(View itemView) { LatestPostViewHolder(View itemView) {
super(itemView); super(itemView);
latestPostsRow = itemView.findViewById(R.id.latest_posts_row); latestPostsRow = itemView.findViewById(R.id.latest_posts_row_head);
postTitle = itemView.findViewById(R.id.title); postTitle = itemView.findViewById(R.id.title);
postDate = itemView.findViewById(R.id.date); postDate = itemView.findViewById(R.id.date);
post = itemView.findViewById(R.id.post); post = itemView.findViewById(R.id.post);
} }
} }
private static class LoadingViewHolder extends RecyclerView.ViewHolder {
final MaterialProgressBar progressBar;
LoadingViewHolder(View itemView) {
super(itemView);
progressBar = itemView.findViewById(R.id.recycler_progress_bar);
}
}
} }

72
app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsFragment.java

@ -5,9 +5,8 @@ import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ProgressBar;
import androidx.recyclerview.widget.DividerItemDecoration; import androidx.annotation.NonNull;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
@ -26,7 +25,6 @@ import gr.thmmy.mthmmy.base.BaseFragment;
import gr.thmmy.mthmmy.model.PostSummary; import gr.thmmy.mthmmy.model.PostSummary;
import gr.thmmy.mthmmy.utils.parsing.ParseException; import gr.thmmy.mthmmy.utils.parsing.ParseException;
import gr.thmmy.mthmmy.utils.parsing.ParseHelpers; import gr.thmmy.mthmmy.utils.parsing.ParseHelpers;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
import timber.log.Timber; import timber.log.Timber;
@ -34,7 +32,7 @@ import timber.log.Timber;
/** /**
* Use the {@link LatestPostsFragment#newInstance} factory method to create an instance of this fragment. * Use the {@link LatestPostsFragment#newInstance} factory method to create an instance of this fragment.
*/ */
public class LatestPostsFragment extends BaseFragment implements LatestPostsAdapter.OnLoadMoreListener { public class LatestPostsFragment extends BaseFragment {
/** /**
* The key to use when putting profile's url String to {@link LatestPostsFragment}'s Bundle. * The key to use when putting profile's url String to {@link LatestPostsFragment}'s Bundle.
*/ */
@ -49,12 +47,13 @@ public class LatestPostsFragment extends BaseFragment implements LatestPostsAdap
private int pagesLoaded = 0; private int pagesLoaded = 0;
private String profileUrl; private String profileUrl;
private LatestPostsTask profileLatestPostsTask; private LatestPostsTask profileLatestPostsTask;
private MaterialProgressBar progressBar;
private boolean isLoadingMore; private boolean isLoadingMore;
private boolean userHasPosts = true; private boolean userHasPosts = true;
private static final int visibleThreshold = 5; private static final int visibleThreshold = 5;
private int lastVisibleItem, totalItemCount; private int lastVisibleItem, totalItemCount;
private OnLoadingListener onLoadingListener;
public LatestPostsFragment() { public LatestPostsFragment() {
// Required empty public constructor // Required empty public constructor
} }
@ -86,18 +85,15 @@ public class LatestPostsFragment extends BaseFragment implements LatestPostsAdap
Bundle savedInstanceState) { Bundle savedInstanceState) {
final View rootView = inflater.inflate(R.layout.fragment_profile_latest_posts, container, false); final View rootView = inflater.inflate(R.layout.fragment_profile_latest_posts, container, false);
latestPostsAdapter = new LatestPostsAdapter(this.getContext(), fragmentInteractionListener, parsedTopicSummaries); latestPostsAdapter = new LatestPostsAdapter(this.getContext(), fragmentInteractionListener, parsedTopicSummaries);
RecyclerView mainContent = rootView.findViewById(R.id.profile_latest_posts_recycler); RecyclerView recyclerView = rootView.findViewById(R.id.profile_latest_posts_recycler);
mainContent.setAdapter(latestPostsAdapter); recyclerView.setAdapter(latestPostsAdapter);
final LinearLayoutManager layoutManager = new LinearLayoutManager(getContext()); final LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
mainContent.setLayoutManager(layoutManager); recyclerView.setLayoutManager(layoutManager);
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(mainContent.getContext(), recyclerView.setItemViewCacheSize(15);
layoutManager.getOrientation());
mainContent.addItemDecoration(dividerItemDecoration);
//latestPostsAdapter.setOnLoadMoreListener(); recyclerView.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();
@ -109,20 +105,15 @@ public class LatestPostsFragment extends BaseFragment implements LatestPostsAdap
} }
} }
}); });
progressBar = rootView.findViewById(R.id.progressBar);
return rootView; return rootView;
} }
@Override
public void onLoadMore() { public void onLoadMore() {
if (pagesLoaded < numberOfPages) { if (pagesLoaded < numberOfPages) {
parsedTopicSummaries.add(null);
latestPostsAdapter.notifyItemInserted(parsedTopicSummaries.size() - 1);
//Load data //Load data
profileLatestPostsTask = new LatestPostsTask(); profileLatestPostsTask = new LatestPostsTask();
profileLatestPostsTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, profileUrl + ";sa=showPosts;start=" + pagesLoaded * 15); profileLatestPostsTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, profileUrl + ";sa=showPosts;start=" + pagesLoaded * 15);
++pagesLoaded; pagesLoaded++;
} }
} }
@ -147,6 +138,14 @@ public class LatestPostsFragment extends BaseFragment implements LatestPostsAdap
void onLatestPostsFragmentInteraction(PostSummary postSummary); void onLatestPostsFragmentInteraction(PostSummary postSummary);
} }
public interface OnLoadingListener {
void onLoadingLatestPosts(boolean loading);
}
public void setOnLoadingListener(OnLoadingListener onLoadingListener) {
this.onLoadingListener = onLoadingListener;
}
/** /**
* An {@link AsyncTask} that handles asynchronous fetching of a profile page and parsing this * An {@link AsyncTask} that handles asynchronous fetching of a profile page and parsing this
* user's latest posts. * user's latest posts.
@ -154,8 +153,16 @@ public class LatestPostsFragment extends BaseFragment implements LatestPostsAdap
* parameter!</p> * parameter!</p>
*/ */
private class LatestPostsTask extends AsyncTask<String, Void, Boolean> { private class LatestPostsTask extends AsyncTask<String, Void, Boolean> {
private ArrayList<PostSummary> fetchedParsedTopicSummaries;
@Override
protected void onPreExecute() { protected void onPreExecute() {
if (!isLoadingMore) progressBar.setVisibility(ProgressBar.VISIBLE); onLoadingListener.onLoadingLatestPosts(true);
}
public LatestPostsTask() {
super();
fetchedParsedTopicSummaries = new ArrayList<>();
} }
protected Boolean doInBackground(String... profileUrl) { protected Boolean doInBackground(String... profileUrl) {
@ -175,11 +182,12 @@ public class LatestPostsFragment extends BaseFragment implements LatestPostsAdap
protected void onPostExecute(Boolean result) { protected void onPostExecute(Boolean result) {
if (Boolean.FALSE.equals(result)) if (Boolean.FALSE.equals(result))
Timber.e(new ParseException("Parsing failed(latest posts)"),"ParseException"); Timber.e(new ParseException("Parsing failed (latest posts)"),"ParseException"); //TODO: This is inaccurate (e.g. can also have an I/O cause)
int prevSize = parsedTopicSummaries.size();
progressBar.setVisibility(ProgressBar.INVISIBLE); parsedTopicSummaries.addAll(fetchedParsedTopicSummaries);
latestPostsAdapter.notifyDataSetChanged(); latestPostsAdapter.notifyItemRangeInserted(prevSize, parsedTopicSummaries.size() - prevSize);
isLoadingMore = false; isLoadingMore = false;
onLoadingListener.onLoadingLatestPosts(false);
} }
//TODO: better parse error handling (ParseException etc.) //TODO: better parse error handling (ParseException etc.)
@ -191,14 +199,10 @@ public class LatestPostsFragment extends BaseFragment implements LatestPostsAdap
latestPostsRows = latestPostsPage. latestPostsRows = latestPostsPage.
select("td:has(table:Contains(Εμφάνιση μηνυμάτων)):not([style]) > table"); select("td:has(table:Contains(Εμφάνιση μηνυμάτων)):not([style]) > table");
//Removes loading item
if (isLoadingMore)
parsedTopicSummaries.remove(parsedTopicSummaries.size() - 1);
if (!latestPostsRows.select("td:contains(Sorry, no matches were found)").isEmpty() || if (!latestPostsRows.select("td:contains(Sorry, no matches were found)").isEmpty() ||
!latestPostsRows.select("td:contains(Δυστυχώς δεν βρέθηκε τίποτα)").isEmpty()){ !latestPostsRows.select("td:contains(Δυστυχώς δεν βρέθηκε τίποτα)").isEmpty()){
userHasPosts = false; userHasPosts = false;
parsedTopicSummaries.add(null); fetchedParsedTopicSummaries.add(null);
return true; return true;
} }
@ -219,15 +223,17 @@ public class LatestPostsFragment extends BaseFragment implements LatestPostsAdap
else { else {
pTopicTitle = rowHeader.first().text().replaceAll("\\u00a0","").trim(); pTopicTitle = rowHeader.first().text().replaceAll("\\u00a0","").trim();
pTopicUrl = rowHeader.first().select("a").last().attr("href"); pTopicUrl = rowHeader.first().select("a").last().attr("href");
pDateTime = rowHeader.last().text(); pDateTime = rowHeader.last().text().replaceAll("(on: )|(στις: )","");
} }
pPost = ParseHelpers.youtubeEmbeddedFix(row.select("div.post").first()); pPost = ParseHelpers.youtubeEmbeddedFix(row.select("div.post").first());
//Add stuff to make it work in WebView //Add stuff to make it work in WebView
//style.css //style.css
pPost = ("<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />" + pPost); pPost = ("<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />"
+ "<link rel=\"stylesheet\" type=\"text/css\" href=\"style_dark.css\" />"
+ pPost);
parsedTopicSummaries.add(new PostSummary(pTopicUrl, pTopicTitle, pDateTime, pPost)); fetchedParsedTopicSummaries.add(new PostSummary(pTopicUrl, pTopicTitle, pDateTime, pPost));
} }
} }
return true; return true;

71
app/src/main/java/gr/thmmy/mthmmy/activities/profile/stats/StatsFragment.java

@ -8,9 +8,7 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
@ -43,7 +41,7 @@ import javax.net.ssl.SSLHandshakeException;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseActivity; import gr.thmmy.mthmmy.base.BaseActivity;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar; import gr.thmmy.mthmmy.utils.parsing.ParseException;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
import timber.log.Timber; import timber.log.Timber;
@ -56,7 +54,6 @@ public class StatsFragment extends Fragment {
private String profileUrl; private String profileUrl;
private ProfileStatsTask profileStatsTask; private ProfileStatsTask profileStatsTask;
private LinearLayout mainContent; private LinearLayout mainContent;
private MaterialProgressBar progressBar;
private boolean userHasPosts = true; private boolean userHasPosts = true;
private String generalStatisticsTitle = "", generalStatistics = "", postingActivityByTimeTitle = "", mostPopularBoardsByPostsTitle = "", mostPopularBoardsByActivityTitle = ""; private String generalStatisticsTitle = "", generalStatistics = "", postingActivityByTimeTitle = "", mostPopularBoardsByPostsTitle = "", mostPopularBoardsByActivityTitle = "";
@ -64,6 +61,8 @@ public class StatsFragment extends Fragment {
private final List<BarEntry> mostPopularBoardsByPosts = new ArrayList<>(), mostPopularBoardsByActivity = new ArrayList<>(); private final List<BarEntry> mostPopularBoardsByPosts = new ArrayList<>(), mostPopularBoardsByActivity = new ArrayList<>();
private final ArrayList<String> mostPopularBoardsByPostsLabels = new ArrayList<>(), mostPopularBoardsByActivityLabels = new ArrayList<>(); private final ArrayList<String> mostPopularBoardsByPostsLabels = new ArrayList<>(), mostPopularBoardsByActivityLabels = new ArrayList<>();
private OnLoadingListener onLoadingListener;
public StatsFragment() { public StatsFragment() {
// Required empty public constructor // Required empty public constructor
} }
@ -94,7 +93,6 @@ public class StatsFragment extends Fragment {
Bundle savedInstanceState) { Bundle savedInstanceState) {
final View rootView = inflater.inflate(R.layout.fragment_profile_stats, container, false); final View rootView = inflater.inflate(R.layout.fragment_profile_stats, container, false);
mainContent = rootView.findViewById(R.id.main_content); mainContent = rootView.findViewById(R.id.main_content);
progressBar = rootView.findViewById(R.id.progressBar);
if (profileStatsTask!=null && profileStatsTask.getStatus() == AsyncTask.Status.FINISHED) if (profileStatsTask!=null && profileStatsTask.getStatus() == AsyncTask.Status.FINISHED)
populateLayout(); populateLayout();
return rootView; return rootView;
@ -117,6 +115,14 @@ public class StatsFragment extends Fragment {
profileStatsTask.cancel(true); profileStatsTask.cancel(true);
} }
public interface OnLoadingListener {
void onLoadingStats(boolean loading);
}
public void setOnLoadingListener(OnLoadingListener onLoadingListener) {
this.onLoadingListener = onLoadingListener;
}
/** /**
* An {@link AsyncTask} that handles asynchronous parsing of a profile page's data. * An {@link AsyncTask} that handles asynchronous parsing of a profile page's data.
* {@link AsyncTask#onPostExecute(Object) OnPostExecute} method calls {@link #()} * {@link AsyncTask#onPostExecute(Object) OnPostExecute} method calls {@link #()}
@ -129,7 +135,7 @@ public class StatsFragment extends Fragment {
@Override @Override
protected void onPreExecute() { protected void onPreExecute() {
progressBar.setVisibility(ProgressBar.VISIBLE); onLoadingListener.onLoadingStats(true);
} }
@Override @Override
@ -148,30 +154,24 @@ public class StatsFragment extends Fragment {
return false; return false;
} }
//TODO: better parse error handling (ParseException etc.)
@Override @Override
protected void onPostExecute(Boolean result) { protected void onPostExecute(Boolean result) {
if (!result) { //Parse failed! onLoadingListener.onLoadingStats(false);
Timber.d("Parse failed!"); if (!result)
Toast.makeText(getContext() Timber.e(new ParseException("Parsing failed (user stats)"),"ParseException"); //TODO: This is inaccurate (e.g. can also have an I/O cause)
, "Fatal error!\n Aborting...", Toast.LENGTH_LONG).show(); else
getActivity().finish();
}
//Parse was successful
populateLayout(); populateLayout();
} }
//TODO: better parse error handling (ParseException etc.)
private boolean parseStats(Document statsPage) { private boolean parseStats(Document statsPage) {
//Doesn't go through all the parsing if this user has no posts //Doesn't go through all the parsing if this user has no posts
if (!statsPage.select("td:contains(No posts to speak of!)").isEmpty()) { if (!statsPage.select("td:contains(No posts to speak of!), td:contains(Δεν υπάρχει καμία αποστολή μηνύματος!)").isEmpty())
userHasPosts = false;
}
if (!statsPage.select("td:contains(Δεν υπάρχει καμία αποστολή μηνύματος!)").isEmpty()) {
userHasPosts = false; userHasPosts = false;
}
if (statsPage.select("table.bordercolor[align]>tbody>tr").size() != 6) if (statsPage.select("table.bordercolor[align]>tbody>tr").size() != 6)
return false; return false;
{
Elements titleRows = statsPage.select("table.bordercolor[align]>tbody>tr.titlebg"); Elements titleRows = statsPage.select("table.bordercolor[align]>tbody>tr.titlebg");
generalStatisticsTitle = titleRows.first().text(); generalStatisticsTitle = titleRows.first().text();
Pattern pattern = Pattern.compile("(.+)\\s-"); Pattern pattern = Pattern.compile("(.+)\\s-");
@ -184,29 +184,24 @@ public class StatsFragment extends Fragment {
mostPopularBoardsByPostsTitle = titleRows.last().select("td").first().text(); mostPopularBoardsByPostsTitle = titleRows.last().select("td").first().text();
mostPopularBoardsByActivityTitle = titleRows.last().select("td").last().text(); mostPopularBoardsByActivityTitle = titleRows.last().select("td").last().text();
} }
}
{
Elements statsRows = statsPage.select("table.bordercolor[align]>tbody>tr:not(.titlebg)"); Elements statsRows = statsPage.select("table.bordercolor[align]>tbody>tr:not(.titlebg)");
{
Elements generalStatisticsRows = statsRows.first().select("tbody>tr"); Elements generalStatisticsRows = statsRows.first().select("tbody>tr");
for (Element generalStatisticsRow : generalStatisticsRows) for (Element generalStatisticsRow : generalStatisticsRows)
generalStatistics += generalStatisticsRow.text() + "\n"; generalStatistics += generalStatisticsRow.text() + "\n";
generalStatistics = generalStatistics.trim(); generalStatistics = generalStatistics.trim();
}
if (userHasPosts) { if (userHasPosts) {
{
Elements postingActivityByTimeCols = statsRows.get(1).select(">td").last() Elements postingActivityByTimeCols = statsRows.get(1).select(">td").last()
.select("tr").first().select("td[width=4%]"); .select("tr").first().select("td[width=4%]");
int i = -1; int i = -1;
for (Element postingActivityByTimeColumn : postingActivityByTimeCols) { for (Element postingActivityByTimeColumn : postingActivityByTimeCols)
postingActivityByTime.add(new Entry(++i, Float.parseFloat(postingActivityByTimeColumn postingActivityByTime.add(new Entry(++i, Float.parseFloat(postingActivityByTimeColumn
.select("img").first().attr("height")))); .select("img").first().attr("height"))));
}
}
{
Elements mostPopularBoardsByPostsRows = statsRows.last().select(">td").get(1) Elements mostPopularBoardsByPostsRows = statsRows.last().select(">td").get(1)
.select(">table>tbody>tr"); .select(">table>tbody>tr");
int i = mostPopularBoardsByPostsRows.size(); i = mostPopularBoardsByPostsRows.size();
for (Element mostPopularBoardsByPostsRow : mostPopularBoardsByPostsRows) { for (Element mostPopularBoardsByPostsRow : mostPopularBoardsByPostsRows) {
Elements dataCols = mostPopularBoardsByPostsRow.select("td"); Elements dataCols = mostPopularBoardsByPostsRow.select("td");
mostPopularBoardsByPosts.add(new BarEntry(--i, mostPopularBoardsByPosts.add(new BarEntry(--i,
@ -214,11 +209,10 @@ public class StatsFragment extends Fragment {
mostPopularBoardsByPostsLabels.add(dataCols.first().text()); mostPopularBoardsByPostsLabels.add(dataCols.first().text());
} }
Collections.reverse(mostPopularBoardsByPostsLabels); Collections.reverse(mostPopularBoardsByPostsLabels);
}
{
Elements mostPopularBoardsByActivityRows = statsRows.last().select(">td").last() Elements mostPopularBoardsByActivityRows = statsRows.last().select(">td").last()
.select(">table>tbody>tr"); .select(">table>tbody>tr");
int i = mostPopularBoardsByActivityRows.size(); i = mostPopularBoardsByActivityRows.size();
for (Element mostPopularBoardsByActivityRow : mostPopularBoardsByActivityRows) { for (Element mostPopularBoardsByActivityRow : mostPopularBoardsByActivityRows) {
Elements dataCols = mostPopularBoardsByActivityRow.select("td"); Elements dataCols = mostPopularBoardsByActivityRow.select("td");
String tmp = dataCols.last().text(); String tmp = dataCols.last().text();
@ -228,14 +222,12 @@ public class StatsFragment extends Fragment {
} }
Collections.reverse(mostPopularBoardsByActivityLabels); Collections.reverse(mostPopularBoardsByActivityLabels);
} }
}
}
return true; return true;
} }
} }
private void populateLayout() { private void populateLayout() {
progressBar.setVisibility(ProgressBar.VISIBLE); onLoadingListener.onLoadingStats(true);;
((TextView) mainContent.findViewById(R.id.general_statistics_title)) ((TextView) mainContent.findViewById(R.id.general_statistics_title))
.setText(generalStatisticsTitle); .setText(generalStatisticsTitle);
((TextView) mainContent.findViewById(R.id.general_statistics)) ((TextView) mainContent.findViewById(R.id.general_statistics))
@ -243,7 +235,7 @@ public class StatsFragment extends Fragment {
if (!userHasPosts) { if (!userHasPosts) {
mainContent.removeViews(2, mainContent.getChildCount() - 2); mainContent.removeViews(2, mainContent.getChildCount() - 2);
//mainContent.removeViews(2, 6); onLoadingListener.onLoadingStats(false);
return; return;
} }
@ -361,7 +353,7 @@ public class StatsFragment extends Fragment {
mostPopularBoardsByActivityData.setValueTextColor(Color.WHITE); mostPopularBoardsByActivityData.setValueTextColor(Color.WHITE);
mostPopularBoardsByActivityChart.setData(mostPopularBoardsByActivityData); mostPopularBoardsByActivityChart.setData(mostPopularBoardsByActivityData);
mostPopularBoardsByActivityChart.invalidate(); mostPopularBoardsByActivityChart.invalidate();
progressBar.setVisibility(ProgressBar.INVISIBLE); onLoadingListener.onLoadingStats(false);
} }
private class MyXAxisValueFormatter implements IAxisValueFormatter { private class MyXAxisValueFormatter implements IAxisValueFormatter {
@ -373,7 +365,10 @@ public class StatsFragment extends Fragment {
@Override @Override
public String getFormattedValue(float value, AxisBase axis) { public String getFormattedValue(float value, AxisBase axis) {
if (((int) value) < mValues.size())
return mValues.get((int) value); return mValues.get((int) value);
else
return "0";
} }
} }
} }

7
app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java

@ -25,7 +25,6 @@ import java.util.Objects;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.utils.parsing.ParseHelpers; import gr.thmmy.mthmmy.utils.parsing.ParseHelpers;
import gr.thmmy.mthmmy.views.ReactiveWebView; import gr.thmmy.mthmmy.views.ReactiveWebView;
import timber.log.Timber;
/** /**
* Use the {@link SummaryFragment#newInstance} factory method to create an instance of this fragment. * Use the {@link SummaryFragment#newInstance} factory method to create an instance of this fragment.
@ -69,7 +68,7 @@ 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(getArguments().getString(PROFILE_DOCUMENT)); profileSummaryDocument = Jsoup.parse(requireArguments().getString(PROFILE_DOCUMENT));
parsedProfileSummaryData = new ArrayList<>(); parsedProfileSummaryData = new ArrayList<>();
} }
@ -90,7 +89,6 @@ public class SummaryFragment extends Fragment {
summaryTask = new SummaryTask(); summaryTask = new SummaryTask();
summaryTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, profileSummaryDocument); summaryTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, profileSummaryDocument);
} }
Timber.d("onActivityCreated");
} }
@Override @Override
@ -145,6 +143,7 @@ public class SummaryFragment extends Fragment {
//Add stuff to make it work in WebView //Add stuff to make it work in WebView
//style.css //style.css
pHtml = ("<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />\n" + pHtml = ("<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />\n" +
"<link rel=\"stylesheet\" type=\"text/css\" href=\"style_light.css\" />\n" +
"<div class=\"customSignature\">\n" + pHtml + "\n</div>"); "<div class=\"customSignature\">\n" + pHtml + "\n</div>");
} else if (!rowText.contains("Name") && !rowText.contains("Όνομα")) { //Doesn't add username twice } else if (!rowText.contains("Name") && !rowText.contains("Όνομα")) { //Doesn't add username twice
if (Objects.equals(summaryRow.select("td").get(1).text(), "")) if (Objects.equals(summaryRow.select("td").get(1).text(), ""))
@ -189,13 +188,11 @@ public class SummaryFragment extends Fragment {
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)); entry.setTextColor(getResources().getColor(R.color.primary_text, null));
else else
//noinspection deprecation
entry.setTextColor(getResources().getColor(R.color.primary_text)); entry.setTextColor(getResources().getColor(R.color.primary_text));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
entry.setText(Html.fromHtml(profileSummaryRow, Html.FROM_HTML_MODE_LEGACY)); entry.setText(Html.fromHtml(profileSummaryRow, Html.FROM_HTML_MODE_LEGACY));
} else { } else {
//noinspection deprecation
entry.setText(Html.fromHtml(profileSummaryRow)); entry.setText(Html.fromHtml(profileSummaryRow));
} }

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

@ -142,7 +142,7 @@ public class ShoutboxFragment extends Fragment {
shoutboxViewModel.loadShoutbox(true); shoutboxViewModel.loadShoutbox(true);
} else if (resultCode == NetworkResultCodes.NETWORK_ERROR) { } else if (resultCode == NetworkResultCodes.NETWORK_ERROR) {
Timber.w("Failed to send shout"); Timber.w("Failed to send shout");
Toast.makeText(getContext(), "NetworkError", Toast.LENGTH_SHORT).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "NetworkError", Toast.LENGTH_SHORT).show();
} }
} }
@ -154,10 +154,10 @@ public class ShoutboxFragment extends Fragment {
editorView.setVisibility(View.VISIBLE); editorView.setVisibility(View.VISIBLE);
} else if (resultCode == NetworkResultCodes.NETWORK_ERROR) { } else if (resultCode == NetworkResultCodes.NETWORK_ERROR) {
Timber.w("Failed to retreive shoutbox due to network error"); Timber.w("Failed to retreive shoutbox due to network error");
Toast.makeText(getContext(), "NetworkError", Toast.LENGTH_SHORT).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "NetworkError", Toast.LENGTH_SHORT).show();
} else { } else {
Timber.wtf("Failed to retreive shoutbox due to unknown error"); Timber.wtf("Failed to retreive shoutbox due to unknown error");
Toast.makeText(getContext(), "Failed to retrieve shoutbox, please contact mthmmy developer team", Toast.LENGTH_LONG).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Failed to retrieve shoutbox, please contact mthmmy developer team", Toast.LENGTH_LONG).show();
} }
} }

16
app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java

@ -150,7 +150,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo
Uri.parse(topicPageUrl)); Uri.parse(topicPageUrl));
if (!target.is(ThmmyPage.PageCategory.TOPIC)) { if (!target.is(ThmmyPage.PageCategory.TOPIC)) {
Timber.e("Bundle came with a non topic url!\nUrl: %s", topicPageUrl); Timber.e("Bundle came with a non topic url!\nUrl: %s", topicPageUrl);
Toast.makeText(this, "An error has occurred\n Aborting.", Toast.LENGTH_SHORT).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "An error has occurred\n Aborting.", Toast.LENGTH_SHORT).show();
finish(); finish();
} }
@ -516,7 +516,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo
viewModel.reloadPage(); viewModel.reloadPage();
} else { } else {
Timber.w("Failed to delete post"); Timber.w("Failed to delete post");
Toast.makeText(getBaseContext(), "Delete failed!", Toast.LENGTH_SHORT).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Delete failed!", Toast.LENGTH_SHORT).show();
} }
}); });
viewModel.setReplyFinishListener(new ReplyTask.ReplyTaskCallbacks() { viewModel.setReplyFinishListener(new ReplyTask.ReplyTaskCallbacks() {
@ -579,7 +579,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo
break; break;
default: default:
Timber.w("Post reply unsuccessful"); Timber.w("Post reply unsuccessful");
Toast.makeText(getBaseContext(), "Post failed!", Toast.LENGTH_SHORT).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Post failed!", Toast.LENGTH_SHORT).show();
recyclerView.getChildAt(recyclerView.getChildCount() - 1).setAlpha(1f); recyclerView.getChildAt(recyclerView.getChildCount() - 1).setAlpha(1f);
recyclerView.getChildAt(recyclerView.getChildCount() - 1).setEnabled(true); recyclerView.getChildAt(recyclerView.getChildCount() - 1).setEnabled(true);
} }
@ -623,7 +623,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo
viewModel.reloadPage(); viewModel.reloadPage();
} else { } else {
Timber.i("Post edit unsuccessful"); Timber.i("Post edit unsuccessful");
Toast.makeText(getBaseContext(), "Edit failed!", Toast.LENGTH_SHORT).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Edit failed!", Toast.LENGTH_SHORT).show();
recyclerView.getChildAt(viewModel.getPostBeingEditedPosition()).setAlpha(1); recyclerView.getChildAt(viewModel.getPostBeingEditedPosition()).setAlpha(1);
recyclerView.getChildAt(viewModel.getPostBeingEditedPosition()).setEnabled(true); recyclerView.getChildAt(viewModel.getPostBeingEditedPosition()).setEnabled(true);
} }
@ -649,7 +649,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo
} }
else { else {
Timber.w("Failed to send vote"); Timber.w("Failed to send vote");
Toast.makeText(this, "Failed to send vote", Toast.LENGTH_LONG).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Failed to send vote", Toast.LENGTH_LONG).show();
} }
}); });
viewModel.setRemoveVoteTaskStartedListener(() -> progressBar.setVisibility(ProgressBar.VISIBLE)); viewModel.setRemoveVoteTaskStartedListener(() -> progressBar.setVisibility(ProgressBar.VISIBLE));
@ -661,7 +661,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo
} }
else { else {
Timber.w("Failed to remove vote"); Timber.w("Failed to remove vote");
Toast.makeText(this, "Failed to remove vote", Toast.LENGTH_LONG).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Failed to remove vote", Toast.LENGTH_LONG).show();
} }
}); });
// observe the changes in data // observe the changes in data
@ -770,7 +770,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo
default: default:
//Parse failed - should never happen //Parse failed - should never happen
Timber.wtf("Parse failed!"); //TODO report ParseException!!! Timber.wtf("Parse failed!"); //TODO report ParseException!!!
Toast.makeText(getBaseContext(), "Fatal Error", Toast.LENGTH_SHORT).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Fatal Error", Toast.LENGTH_SHORT).show();
finish(); finish();
break; break;
} }
@ -824,7 +824,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo
//Make a toast to inform the user that the url was copied //Make a toast to inform the user that the url was copied
Toast.makeText( Toast.makeText(
TopicActivity.this, BaseApplication.getInstance().getApplicationContext(),
TopicActivity.this.getString(R.string.link_copied_msg), TopicActivity.this.getString(R.string.link_copied_msg),
Toast.LENGTH_SHORT).show(); Toast.LENGTH_SHORT).show();
} }

20
app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadActivity.java

@ -268,11 +268,11 @@ public class UploadActivity extends BaseActivity {
shouldReturn = true; shouldReturn = true;
} }
if (filesList.isEmpty()) { if (filesList.isEmpty()) {
Toast.makeText(view.getContext(), "Please choose a file to upload or take a photo", Toast.LENGTH_LONG).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Please choose a file to upload or take a photo", Toast.LENGTH_LONG).show();
shouldReturn = true; shouldReturn = true;
} }
if (categorySelected.equals("-1")) { if (categorySelected.equals("-1")) {
Toast.makeText(view.getContext(), "Please choose category first", Toast.LENGTH_SHORT).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Please choose category first", Toast.LENGTH_SHORT).show();
shouldReturn = true; shouldReturn = true;
} }
if (!filesList.isEmpty()) { if (!filesList.isEmpty()) {
@ -282,7 +282,7 @@ public class UploadActivity extends BaseActivity {
} }
if (totalFilesSize > MAX_FILE_SIZE_SUPPORTED) { if (totalFilesSize > MAX_FILE_SIZE_SUPPORTED) {
Toast.makeText(view.getContext(), "Your files are too powerful for thmmy. Reduce size or split!", Toast.LENGTH_LONG).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Your files are too powerful for thmmy. Reduce size or split!", Toast.LENGTH_LONG).show();
shouldReturn = true; shouldReturn = true;
} }
} }
@ -338,7 +338,7 @@ public class UploadActivity extends BaseActivity {
if (!storage.rename(uploadFile.getPhotoFile().getAbsolutePath(), destinationFilename)) { if (!storage.rename(uploadFile.getPhotoFile().getAbsolutePath(), destinationFilename)) {
//Something went wrong, abort //Something went wrong, abort
Toast.makeText(this, "Could not create temporary file for renaming", Toast.LENGTH_SHORT).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Could not create temporary file for renaming", Toast.LENGTH_SHORT).show();
progressBar.setVisibility(View.GONE); progressBar.setVisibility(View.GONE);
return; return;
} }
@ -385,7 +385,7 @@ public class UploadActivity extends BaseActivity {
: tempFileUri)) { : tempFileUri)) {
finish(); finish();
} else { } else {
Toast.makeText(this, "Couldn't initiate upload.", Toast.LENGTH_SHORT).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Couldn't initiate upload.", Toast.LENGTH_SHORT).show();
} }
}); });
@ -580,7 +580,7 @@ public class UploadActivity extends BaseActivity {
zipTask.execute(filesListArray); zipTask.execute(filesListArray);
finish(); finish();
} else { } else {
Toast.makeText(this, "Please retry uploading.", Toast.LENGTH_SHORT).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Please retry uploading.", Toast.LENGTH_SHORT).show();
} }
break; break;
} }
@ -746,7 +746,7 @@ public class UploadActivity extends BaseActivity {
Timber.d("Uploading a file with properties: \nTitle: %s\nCategory: %s\nDescription: %s\nIcon: %s\nUploader: %s", Timber.d("Uploading a file with properties: \nTitle: %s\nCategory: %s\nDescription: %s\nIcon: %s\nUploader: %s",
uploadTitleText, categorySelected, uploadDescriptionText, fileIcon, uploaderProfileIndex); uploadTitleText, categorySelected, uploadDescriptionText, fileIcon, uploaderProfileIndex);
multipartUploadRequest.startUpload(); multipartUploadRequest.startUpload();
Toast.makeText(context, "Uploading file(s) in the background...", Toast.LENGTH_SHORT).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Uploading file(s) in the background...", Toast.LENGTH_SHORT).show();
return true; return true;
} catch (Exception exception) { } catch (Exception exception) {
Timber.e(exception, "AndroidUploadService: %s", exception.getMessage()); Timber.e(exception, "AndroidUploadService: %s", exception.getMessage());
@ -1041,7 +1041,7 @@ public class UploadActivity extends BaseActivity {
@Override @Override
protected void onPreExecute() { protected void onPreExecute() {
assert weakActivity != null; assert weakActivity != null;
Toast.makeText(weakActivity.get(), "Zipping files", Toast.LENGTH_SHORT).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Zipping files", Toast.LENGTH_SHORT).show();
} }
@Override @Override
@ -1068,7 +1068,7 @@ public class UploadActivity extends BaseActivity {
return; return;
if (!result) { if (!result) {
Toast.makeText(weakActivity.get(), "Couldn't create zip!", Toast.LENGTH_SHORT).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Couldn't create zip!", Toast.LENGTH_SHORT).show();
return; return;
} }
@ -1077,7 +1077,7 @@ public class UploadActivity extends BaseActivity {
getConfigForUpload(weakActivity.get(), uploadID, zipFilename), categorySelected, getConfigForUpload(weakActivity.get(), uploadID, zipFilename), categorySelected,
uploadTitleText, uploadDescriptionText, fileIcon, uploaderProfileIndex, uploadTitleText, uploadDescriptionText, fileIcon, uploaderProfileIndex,
zipFileUri)) { zipFileUri)) {
Toast.makeText(weakActivity.get(), "Couldn't initiate upload.", Toast.LENGTH_SHORT).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Couldn't initiate upload.", Toast.LENGTH_SHORT).show();
} }
} }
} }

9
app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadFieldsBuilderActivity.java

@ -17,6 +17,7 @@ import java.util.Calendar;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseActivity; import gr.thmmy.mthmmy.base.BaseActivity;
import gr.thmmy.mthmmy.base.BaseApplication;
import timber.log.Timber; import timber.log.Timber;
public class UploadFieldsBuilderActivity extends BaseActivity { public class UploadFieldsBuilderActivity extends BaseActivity {
@ -76,7 +77,7 @@ public class UploadFieldsBuilderActivity extends BaseActivity {
courseGreeklishName = extras.getString(BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_GREEKLISH_NAME); courseGreeklishName = extras.getString(BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_GREEKLISH_NAME);
semester = extras.getString(BUNDLE_UPLOAD_FIELD_BUILDER_SEMESTER); semester = extras.getString(BUNDLE_UPLOAD_FIELD_BUILDER_SEMESTER);
if (courseName == null || courseName.equals("") || semester == null || semester.equals("")) { if (courseName == null || courseName.equals("") || semester == null || semester.equals("")) {
Toast.makeText(this, "Something went wrong!", Toast.LENGTH_SHORT).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Something went wrong!", Toast.LENGTH_SHORT).show();
Timber.e("Bundle came empty in %s", UploadFieldsBuilderActivity.class.getSimpleName()); Timber.e("Bundle came empty in %s", UploadFieldsBuilderActivity.class.getSimpleName());
Intent returnIntent = new Intent(); Intent returnIntent = new Intent();
@ -119,13 +120,13 @@ public class UploadFieldsBuilderActivity extends BaseActivity {
int typeId = typeRadio.getCheckedRadioButtonId(), int typeId = typeRadio.getCheckedRadioButtonId(),
semesterId = semesterRadio.getCheckedRadioButtonId(); semesterId = semesterRadio.getCheckedRadioButtonId();
if (typeId == -1) { if (typeId == -1) {
Toast.makeText(view.getContext(), "Please choose a type for the upload", Toast.LENGTH_SHORT).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Please choose a type for the upload", Toast.LENGTH_SHORT).show();
return; return;
} else if (semesterChooserLinear.getVisibility() == View.VISIBLE && semesterId == -1) { } else if (semesterChooserLinear.getVisibility() == View.VISIBLE && semesterId == -1) {
Toast.makeText(view.getContext(), "Please choose a semester for the upload", Toast.LENGTH_SHORT).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Please choose a semester for the upload", Toast.LENGTH_SHORT).show();
return; return;
} else if (year.getText().toString().isEmpty() || !isValidYear) { } else if (year.getText().toString().isEmpty() || !isValidYear) {
Toast.makeText(view.getContext(), "Please choose a valid year for the upload", Toast.LENGTH_SHORT).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Please choose a valid year for the upload", Toast.LENGTH_SHORT).show();
return; return;
} }

3
app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsHelper.java

@ -21,6 +21,7 @@ import java.io.OutputStream;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
import gr.thmmy.mthmmy.base.BaseApplication;
import gr.thmmy.mthmmy.utils.FileUtils; import gr.thmmy.mthmmy.utils.FileUtils;
import timber.log.Timber; import timber.log.Timber;
@ -41,7 +42,7 @@ public class UploadsHelper {
if (!tempDirectory.exists() && !tempDirectory.mkdirs()) { if (!tempDirectory.exists() && !tempDirectory.mkdirs()) {
Timber.w("Temporary directory build returned false in %s", UploadActivity.class.getSimpleName()); Timber.w("Temporary directory build returned false in %s", UploadActivity.class.getSimpleName());
Toast.makeText(context, "Couldn't create temporary directory", Toast.LENGTH_SHORT).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Couldn't create temporary directory", Toast.LENGTH_SHORT).show();
return null; return null;
} }

10
app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java

@ -569,11 +569,11 @@ public abstract class BaseActivity extends AppCompatActivity {
if (thisPageBookmark.matchExists(topicsBookmarked)) { if (thisPageBookmark.matchExists(topicsBookmarked)) {
thisPageBookmarkMenuButton.setIcon(R.drawable.ic_bookmark_false_accent_24dp); thisPageBookmarkMenuButton.setIcon(R.drawable.ic_bookmark_false_accent_24dp);
toggleTopicToBookmarks(thisPageBookmark); toggleTopicToBookmarks(thisPageBookmark);
Toast.makeText(getBaseContext(), "Bookmark removed", Toast.LENGTH_SHORT).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Bookmark removed", Toast.LENGTH_SHORT).show();
} else { } else {
thisPageBookmarkMenuButton.setIcon(R.drawable.ic_bookmark_true_accent_24dp); thisPageBookmarkMenuButton.setIcon(R.drawable.ic_bookmark_true_accent_24dp);
toggleTopicToBookmarks(thisPageBookmark); toggleTopicToBookmarks(thisPageBookmark);
Toast.makeText(getBaseContext(), "Bookmark added", Toast.LENGTH_SHORT).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Bookmark added", Toast.LENGTH_SHORT).show();
} }
} }
@ -586,10 +586,10 @@ public abstract class BaseActivity extends AppCompatActivity {
thisPageBookmarkImageButton.setOnClickListener(view -> { thisPageBookmarkImageButton.setOnClickListener(view -> {
if (thisPageBookmark.matchExists(boardsBookmarked)) { if (thisPageBookmark.matchExists(boardsBookmarked)) {
thisPageBookmarkImageButton.setImageResource(R.drawable.ic_bookmark_false_accent_24dp); thisPageBookmarkImageButton.setImageResource(R.drawable.ic_bookmark_false_accent_24dp);
Toast.makeText(getBaseContext(), "Bookmark removed", Toast.LENGTH_SHORT).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Bookmark removed", Toast.LENGTH_SHORT).show();
} else { } else {
thisPageBookmarkImageButton.setImageResource(R.drawable.ic_bookmark_true_accent_24dp); thisPageBookmarkImageButton.setImageResource(R.drawable.ic_bookmark_true_accent_24dp);
Toast.makeText(getBaseContext(), "Bookmark added", Toast.LENGTH_SHORT).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Bookmark added", Toast.LENGTH_SHORT).show();
} }
toggleBoardToBookmarks(thisPageBookmark); toggleBoardToBookmarks(thisPageBookmark);
}); });
@ -771,7 +771,7 @@ public abstract class BaseActivity extends AppCompatActivity {
BaseActivity.this.startActivity(intent); BaseActivity.this.startActivity(intent);
} catch (Exception e) { } catch (Exception e) {
Timber.e(e, "Couldn't open downloaded file..."); Timber.e(e, "Couldn't open downloaded file...");
Toast.makeText(getBaseContext(), "Couldn't open file...", Toast.LENGTH_SHORT).show(); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Couldn't open file...", Toast.LENGTH_SHORT).show();
} }
}); });

6
app/src/main/java/gr/thmmy/mthmmy/base/BaseFragment.java

@ -28,14 +28,12 @@ public abstract class BaseFragment extends Fragment {
@Override @Override
public void onAttach(Context context) { public void onAttach(Context context) {
super.onAttach(context); super.onAttach(context);
if (context instanceof FragmentInteractionListener) { if (context instanceof FragmentInteractionListener)
fragmentInteractionListener = (FragmentInteractionListener) context; fragmentInteractionListener = (FragmentInteractionListener) context;
else
} else {
throw new RuntimeException(context.toString() throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener"); + " must implement OnFragmentInteractionListener");
} }
}
@Override @Override
public void onDetach() { public void onDetach() {

18
app/src/main/res/layout/fragment_profile_latest_posts.xml

@ -1,26 +1,16 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout <RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
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">
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/profile_latest_posts_recycler" android:id="@+id/profile_latest_posts_recycler"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/background" android:padding="4dp"
android:clipToPadding = "false"
android:scrollbars="none"> android:scrollbars="none">
</androidx.recyclerview.widget.RecyclerView> </androidx.recyclerview.widget.RecyclerView>
<me.zhanghai.android.materialprogressbar.MaterialProgressBar
android:id="@+id/progressBar"
style="@style/Widget.MaterialProgressBar.ProgressBar.Horizontal.NoPadding"
android:layout_width="match_parent"
android:layout_height="@dimen/progress_bar_height"
android:layout_alignParentTop="true"
android:indeterminate="true"
android:visibility="invisible"
app:mpb_indeterminateTint="@color/accent"
app:mpb_progressStyle="horizontal"/>
</RelativeLayout> </RelativeLayout>

47
app/src/main/res/layout/fragment_profile_latest_posts_row.xml

@ -1,21 +1,38 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout <RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/latest_posts_row"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@color/background_light"> android:layout_marginBottom="2dp">
<androidx.cardview.widget.CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="@+id/card_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:foreground="?android:attr/selectableItemBackground"
card_view:cardBackgroundColor="@color/background"
card_view:cardCornerRadius="5dp"
card_view:cardElevation="2dp"
card_view:cardPreventCornerOverlap="false"
card_view:cardUseCompatPadding="true">
<RelativeLayout <RelativeLayout
android:id="@+id/latest_posts_row" android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:id="@+id/latest_posts_row_head"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground" android:background="?android:attr/selectableItemBackground"
android:clickable="true" android:clickable="true"
android:focusable="true" android:focusable="true"
android:paddingBottom="6dp" android:paddingStart="10dp"
android:paddingLeft="10dp" android:paddingEnd="10dp"
android:paddingRight="10dp" android:paddingTop="8dp">
android:paddingTop="6dp">
<TextView <TextView
android:id="@+id/title" android:id="@+id/title"
@ -34,21 +51,24 @@
android:layout_below="@+id/title" android:layout_below="@+id/title"
android:textColor="@color/secondary_text"/> android:textColor="@color/secondary_text"/>
<View <View
android:id="@+id/spacer_divider" android:id="@+id/spacer_divider"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="1dp" android:layout_height="1dp"
android:layout_below="@+id/date" android:layout_below="@+id/date"
android:layout_marginBottom="3dp" android:layout_marginTop="6dp"
android:layout_marginTop="3dp" android:background="@color/secondary_text"/>
android:background="@color/divider"/> </RelativeLayout>
<FrameLayout <FrameLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_alignParentStart="true" android:layout_alignParentStart="true"
android:layout_below="@+id/spacer_divider"> android:layout_below="@+id/latest_posts_row_head"
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:paddingTop="6dp"
android:paddingBottom="8dp">
<gr.thmmy.mthmmy.views.ReactiveWebView <gr.thmmy.mthmmy.views.ReactiveWebView
android:id="@+id/post" android:id="@+id/post"
@ -58,4 +78,5 @@
android:text="@string/post"/> android:text="@string/post"/>
</FrameLayout> </FrameLayout>
</RelativeLayout> </RelativeLayout>
</FrameLayout> </androidx.cardview.widget.CardView>
</RelativeLayout>

26
app/src/main/res/layout/fragment_profile_stats.xml

@ -1,19 +1,17 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout <RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent">
android:background="@color/background_light">
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginBottom="4dp" android:paddingTop="4dp"
android:layout_marginEnd="16dp" android:paddingStart="16dp"
android:layout_marginStart="16dp" android:paddingEnd="16dp"
android:layout_marginTop="4dp" android:paddingBottom="4dp"
android:background="@color/background_light"> android:background="@color/background_lighter">
<LinearLayout <LinearLayout
android:id="@+id/main_content" android:id="@+id/main_content"
@ -36,7 +34,6 @@
android:id="@+id/general_statistics" android:id="@+id/general_statistics"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/general_statistics"
android:textColor="@color/primary_text"/> android:textColor="@color/primary_text"/>
<TextView <TextView
@ -88,15 +85,4 @@
android:layout_marginBottom="14dp"/> android:layout_marginBottom="14dp"/>
</LinearLayout> </LinearLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>
<me.zhanghai.android.materialprogressbar.MaterialProgressBar
android:id="@+id/progressBar"
style="@style/Widget.MaterialProgressBar.ProgressBar.Horizontal.NoPadding"
android:layout_width="match_parent"
android:layout_height="@dimen/progress_bar_height"
android:layout_alignParentTop="true"
android:indeterminate="true"
android:visibility="invisible"
app:mpb_indeterminateTint="@color/accent"
app:mpb_progressStyle="horizontal"/>
</RelativeLayout> </RelativeLayout>

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

@ -4,7 +4,7 @@
android:id="@+id/nested_scroll" android:id="@+id/nested_scroll"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/background_light" android:background="@color/background_lighter"
android:paddingEnd="16dp" android:paddingEnd="16dp"
android:paddingStart="16dp" android:paddingStart="16dp"
android:scrollbars="none"> android:scrollbars="none">

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

@ -21,6 +21,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1" android:layout_weight="1"
android:paddingBottom="8dp"
tools:listitem="@layout/fragment_shoutbox_shout_row" /> tools:listitem="@layout/fragment_shoutbox_shout_row" />
<gr.thmmy.mthmmy.views.editorview.EditorView <gr.thmmy.mthmmy.views.editorview.EditorView
@ -30,7 +31,6 @@
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp" android:layout_marginBottom="8dp"
android:paddingTop="8dp"
android:visibility="gone"/> android:visibility="gone"/>
<gr.thmmy.mthmmy.views.editorview.EmojiKeyboard <gr.thmmy.mthmmy.views.editorview.EmojiKeyboard

1
app/src/main/res/values/colors.xml

@ -16,6 +16,7 @@
<color name="secondary_text">#757575</color> <color name="secondary_text">#757575</color>
<color name="background">#323232</color> <color name="background">#323232</color>
<color name="background_light">#3C3F41</color> <color name="background_light">#3C3F41</color>
<color name="background_lighter">#434649</color>
<color name="divider">#8B8B8B</color> <color name="divider">#8B8B8B</color>
<color name="link_color">#FF9800</color> <color name="link_color">#FF9800</color>
<color name="mention_color">#FAA61A</color> <color name="mention_color">#FAA61A</color>

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

@ -82,7 +82,6 @@
<string name="username">Username</string> <string name="username">Username</string>
<string name="latest_posts_empty_message">This user has no posts yet</string> <string name="latest_posts_empty_message">This user has no posts yet</string>
<string name="general_statistics_title">General Statistics</string> <string name="general_statistics_title">General Statistics</string>
<string name="general_statistics">Statistics</string>
<string name="posting_activity_by_time_title">Posting Activity</string> <string name="posting_activity_by_time_title">Posting Activity</string>
<string name="most_popular_boards_by_posts_title">Most Popular Boards By Posts</string> <string name="most_popular_boards_by_posts_title">Most Popular Boards By Posts</string>
<string name="most_popular_boards_by_activity_title">Most Popular Boards By Activity</string> <string name="most_popular_boards_by_activity_title">Most Popular Boards By Activity</string>

2
build.gradle

@ -10,7 +10,7 @@ buildscript {
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:4.0.1' classpath 'com.android.tools.build:gradle:4.0.1'
classpath 'com.google.gms:google-services:4.3.3' classpath 'com.google.gms:google-services:4.3.3'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.1.1' classpath 'com.google.firebase:firebase-crashlytics-gradle:2.2.0'
classpath 'org.ajoberstar.grgit:grgit-core:3.1.1' // Also change in app/gradle/grgit.gradle classpath 'org.ajoberstar.grgit:grgit-core:3.1.1' // Also change in app/gradle/grgit.gradle
classpath "com.github.ben-manes:gradle-versions-plugin:0.21.0" classpath "com.github.ben-manes:gradle-versions-plugin:0.21.0"
} }

Loading…
Cancel
Save