Browse Source

Version 1.3.3

master v1.3.3
Ezerous 7 years ago
parent
commit
4f2a19c672
  1. 4
      .gitignore
  2. 38
      .gitlab-ci.yml
  3. 2
      VERSION
  4. 37
      app/build.gradle
  5. 42
      app/src/debug/google-services.json
  6. 1
      app/src/main/AndroidManifest.xml
  7. 8
      app/src/main/assets/apache_libraries.html
  8. 2
      app/src/main/assets/mit_libraries.html
  9. 9
      app/src/main/java/gr/thmmy/mthmmy/activities/AboutActivity.java
  10. 4
      app/src/main/java/gr/thmmy/mthmmy/activities/main/forum/ForumFragment.java
  11. 2
      app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentAdapter.java
  12. 19
      app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentFragment.java
  13. 2
      app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadAdapter.java
  14. 8
      app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadFragment.java
  15. 27
      app/src/main/java/gr/thmmy/mthmmy/activities/profile/ProfileActivity.java
  16. 7
      app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsAdapter.java
  17. 14
      app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsFragment.java
  18. 20
      app/src/main/java/gr/thmmy/mthmmy/activities/profile/stats/StatsFragment.java
  19. 233
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/Posting.java
  20. 239
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java
  21. 129
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java
  22. 47
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAnimations.java
  23. 3
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicParser.java
  24. 50
      app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java
  25. 26
      app/src/main/java/gr/thmmy/mthmmy/services/DownloadService.java
  26. 7
      app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareFABBehavior.java
  27. 7
      app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareLinearBehavior.java
  28. 12
      app/src/main/res/layout-v21/activity_topic_post_row.xml
  29. 83
      app/src/main/res/layout/activity_about.xml
  30. 14
      app/src/main/res/layout/activity_board_sub_board.xml
  31. 14
      app/src/main/res/layout/activity_board_topic.xml
  32. 12
      app/src/main/res/layout/activity_topic_post_row.xml
  33. 17
      app/src/main/res/layout/activity_topic_quick_reply_row.xml
  34. 13
      app/src/main/res/layout/fragment_forum_board_row.xml
  35. 14
      app/src/main/res/layout/fragment_latest_posts_empty_message.xml
  36. 2
      app/src/main/res/layout/fragment_recent_row.xml
  37. 11
      app/src/main/res/layout/fragment_unread_mark_read_row.xml
  38. 2
      app/src/main/res/layout/fragment_unread_row.xml
  39. 4
      app/src/main/res/values/strings.xml
  40. 3
      build.gradle
  41. 4
      gradle/wrapper/gradle-wrapper.properties

4
.gitignore

@ -46,5 +46,5 @@ captures/
### Android Patch ###
gen-external-apklibs
# Google Services (release build)
app/src/release/google-services.json
# Google Services JSON
google-services.json

38
.gitlab-ci.yml

@ -2,30 +2,38 @@ image: openjdk:8-jdk
variables:
ANDROID_TARGET_SDK: "26"
ANDROID_BUILD_TOOLS: "26.0.1"
ANDROID_SDK_TOOLS: "25.2.5"
ANDROID_BUILD_TOOLS: "27.0.0"
ANDROID_SDK_TOOLS_REV: "3859397"
before_script:
- apt-get --quiet update --yes
- apt-get --quiet install --yes wget tar unzip lib32stdc++6 lib32z1
- wget --quiet --output-document=android-sdk.zip https://dl.google.com/android/repository/tools_r${ANDROID_SDK_TOOLS}-linux.zip
- unzip android-sdk.zip -d android-sdk-linux
- echo y | android-sdk-linux/tools/android --silent update sdk --no-ui --all --filter android-${ANDROID_TARGET_SDK}
- echo y | android-sdk-linux/tools/android --silent update sdk --no-ui --all --filter platform-tools
- echo y | android-sdk-linux/tools/android --silent update sdk --no-ui --all --filter build-tools-${ANDROID_BUILD_TOOLS}
- echo y | android-sdk-linux/tools/android --silent update sdk --no-ui --all --filter extra-android-m2repository
- echo y | android-sdk-linux/tools/android --silent update sdk --no-ui --all --filter extra-google-google_play_services
- echo y | android-sdk-linux/tools/android --silent update sdk --no-ui --all --filter extra-google-m2repository
- apt-get --quiet install --yes wget unzip lib32stdc++6 lib32z1
- mkdir $HOME/.android # for sdkmanager configs
- echo 'count=0' > $HOME/.android/repositories.cfg # avoid warning
- wget --quiet --output-document=android-sdk.zip https://dl.google.com/android/repository/sdk-tools-linux-${ANDROID_SDK_TOOLS_REV}.zip
- mkdir $PWD/android-sdk-linux
- unzip -qq android-sdk.zip -d $PWD/android-sdk-linux
- export ANDROID_HOME=$PWD/android-sdk-linux
- export PATH=$PATH:$PWD/android-sdk-linux/platform-tools/
- echo y | $ANDROID_HOME/tools/bin/sdkmanager --update > /dev/null
- echo y | $ANDROID_HOME/tools/bin/sdkmanager 'tools' > /dev/null
- echo y | $ANDROID_HOME/tools/bin/sdkmanager 'platform-tools' > /dev/null
- echo y | $ANDROID_HOME/tools/bin/sdkmanager 'build-tools;'$ANDROID_BUILD_TOOLS > /dev/null
- echo y | $ANDROID_HOME/tools/bin/sdkmanager 'platforms;android-'$ANDROID_TARGET_SDK > /dev/null
- echo y | $ANDROID_HOME/tools/bin/sdkmanager 'extras;android;m2repository' > /dev/null
- echo y | $ANDROID_HOME/tools/bin/sdkmanager 'extras;google;google_play_services' > /dev/null
- echo y | $ANDROID_HOME/tools/bin/sdkmanager 'extras;google;m2repository' > /dev/null
- chmod +x ./gradlew
build_develop:
stages:
- build
build:
stage: build
script:
- ./gradlew assembleDebug
- ./gradlew assembleDebug > /dev/null
except:
- master
artifacts:
name: "${CI_BUILD_NAME}"
paths:
- app/build/outputs/apk
- app/build/outputs/

2
VERSION

@ -1 +1 @@
1.3.2
1.3.3

37
app/build.gradle

@ -2,15 +2,15 @@ apply plugin: 'com.android.application'
android {
compileSdkVersion 26
buildToolsVersion "26.0.1"
buildToolsVersion "27.0.0"
defaultConfig {
vectorDrawables.useSupportLibrary = true
applicationId "gr.thmmy.mthmmy"
minSdkVersion 19
targetSdkVersion 26
versionCode 10
versionName "1.3.2"
versionCode 11
versionName "1.3.3"
archivesBaseName = "mTHMMY-v$versionName"
}
@ -19,35 +19,38 @@ android {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
/*debug {
def date = new Date().format('ddMMyy_HH');
debug {
def date = new Date().format('ddMMyy_HHmmss')
archivesBaseName = archivesBaseName + "-$date"
}*/
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:26.0.0'
compile 'com.android.support:design:26.0.0'
compile 'com.android.support:support-v4:26.0.0'
compile 'com.android.support:cardview-v7:26.0.0'
compile 'com.android.support:recyclerview-v7:26.0.0'
compile 'com.google.firebase:firebase-crash:10.2.6'
compile 'com.squareup.okhttp3:okhttp:3.8.0'
compile 'com.android.support:appcompat-v7:26.1.0'
compile 'com.android.support:design:26.1.0'
compile 'com.android.support:support-v4:26.1.0'
compile 'com.android.support:cardview-v7:26.1.0'
compile 'com.android.support:recyclerview-v7:26.1.0'
compile 'com.google.firebase:firebase-crash:11.4.2'
compile 'com.squareup.okhttp3:okhttp:3.9.0'
compile 'com.squareup.picasso:picasso:2.5.2'
compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
compile 'org.jsoup:jsoup:1.10.2'
compile 'org.jsoup:jsoup:1.10.3'
compile 'com.github.franmontiel:PersistentCookieJar:v1.0.1'
compile 'com.github.PhilJay:MPAndroidChart:v3.0.2'
compile('com.mikepenz:materialdrawer:5.9.2@aar') {
compile('com.mikepenz:materialdrawer:5.9.3@aar') {
transitive = true
}
compile 'com.mikepenz:fontawesome-typeface:4.7.0.0@aar'
compile 'com.mikepenz:google-material-typeface:3.0.1.2.original@aar'
compile 'pl.droidsonroids.gif:android-gif-drawable:1.2.7'
compile 'com.bignerdranch.android:expandablerecyclerview:3.0.0-RC1' //TODO: Deprecated - needs replacement!
compile 'me.zhanghai.android.materialprogressbar:library:1.4.1'
compile 'me.zhanghai.android.materialprogressbar:library:1.4.2'
compile 'com.jakewharton.timber:timber:4.5.1'
}
apply plugin: 'com.google.gms.google-services'
if (getGradle().getStartParameter().getTaskRequests().toString().contains("Release")){
apply plugin: 'com.google.gms.google-services'
}

42
app/src/debug/google-services.json

@ -1,42 +0,0 @@
{
"project_info": {
"project_number": "934432863001",
"firebase_url": "https://mthmmy-debug.firebaseio.com",
"project_id": "mthmmy-debug",
"storage_bucket": "mthmmy-debug.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:934432863001:android:088be0537ff6b292",
"android_client_info": {
"package_name": "gr.thmmy.mthmmy"
}
},
"oauth_client": [
{
"client_id": "934432863001-d5oocs1vdi0pcepesi55a41p7enphfcv.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyD4-gwVcb2Rc8zeT8l1v2Lg1DU0QgfGtk8"
}
],
"services": {
"analytics_service": {
"status": 1
},
"appinvite_service": {
"status": 1,
"other_platform_oauth_client": []
},
"ads_service": {
"status": 2
}
}
}
],
"configuration_version": "1"
}

1
app/src/main/AndroidManifest.xml

@ -7,6 +7,7 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
<application
android:name=".base.BaseApplication"

8
app/src/main/assets/apache_libraries.html

@ -39,7 +39,7 @@
<body>
<ul>
<li>
<h5><a href="https://square.github.io/okhttp/">OkHttp</a>&nbsp;v3.8.0 (Copyright ©2016 Square, Inc.)</h5>
<h5><a href="https://square.github.io/okhttp/">OkHttp</a>&nbsp;v3.9.0 (Copyright ©2016 Square, Inc.)</h5>
</li>
<li>
<h5><a href="https://square.github.io/picasso/">Picasso</a>&nbsp;v2.5.2 (Copyright ©2013 Square, Inc.)</h5>
@ -51,13 +51,13 @@
<h5><a href="https://github.com/PhilJay/MPAndroidChart">MPAndroidChart</a>&nbsp;v3.0.2 (Copyright ©2016 Philipp Jahoda)</h5>
</li>
<li>
<h5><a href="https://github.com/mikepenz/MaterialDrawer">MaterialDrawer</a>&nbsp;v5.9.2 (Copyright ©2016 Mike Penz)</h5>
<h5><a href="https://github.com/mikepenz/MaterialDrawer">MaterialDrawer</a>&nbsp;v5.9.3 (Copyright ©2016 Mike Penz)</h5>
</li>
<li>
<h5><a href="https://github.com/mikepenz/Android-Iconics/tree/develop/fontawesome-typeface-library">Fontawesome Typeface Library</a>&nbsp;v4.7.0.0 (Copyright ©2016 Mike Penz)</h5>
<h5><a href="https://github.com/mikepenz/Android-Iconics">Android-Iconics</a>&nbsp;v2.9.5 (Copyright ©2016 Mike Penz)</h5>
</li>
<li>
<h5><a href="https://github.com/DreaminginCodeZH/MaterialProgressBar">MaterialProgressBar</a>&nbsp;v1.4.1 (Copyright ©2015 Zhang Hai)</h5>
<h5><a href="https://github.com/DreaminginCodeZH/MaterialProgressBar">MaterialProgressBar</a>&nbsp;v1.4.2 (Copyright ©2015 Zhang Hai)</h5>
</li>
</ul>

2
app/src/main/assets/mit_libraries.html

@ -39,7 +39,7 @@
<body>
<ul>
<li>
<h5><a href="https://jsoup.org//">jsoup</a>&nbsp;v1.10.2 (Copyright ©2009-2017, Jonathan Hedley &lt;jonathan@hedley.net&gt;)</h5>
<h5><a href="https://jsoup.org//">jsoup</a>&nbsp;v1.10.3 (Copyright ©2009-2017, Jonathan Hedley &lt;jonathan@hedley.net&gt;)</h5>
</li>
<li>
<h5><a href="https://github.com/koral--/android-gif-drawable">android-gif-drawable</a>&nbsp;v1.2.7 (Copyright ©2016 Karol Wrótniak, Droids on Roids)</h5>

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

@ -53,9 +53,13 @@ public class AboutActivity extends BaseActivity {
trollGif = findViewById(R.id.trollPicFrame);
TextView tv = findViewById(R.id.version);
if (tv != null)
if (tv != null) {
if (BuildConfig.DEBUG)
tv.setText(getString(R.string.version, versionName + "-debug"));
else
tv.setText(getString(R.string.version, versionName));
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
@ -75,6 +79,7 @@ public class AboutActivity extends BaseActivity {
}
}
});
}
}
@ -95,6 +100,7 @@ public class AboutActivity extends BaseActivity {
.setView(webView)
.setPositiveButton(android.R.string.ok, null)
.show();
if(alertDialog.getWindow()!=null)
alertDialog.getWindow().setLayout(width, height);
}
@ -109,6 +115,7 @@ public class AboutActivity extends BaseActivity {
.setView(webView)
.setPositiveButton(android.R.string.ok, null)
.show();
if(alertDialog.getWindow()!=null)
alertDialog.getWindow().setLayout(width, height);
}

4
app/src/main/java/gr/thmmy/mthmmy/activities/main/forum/ForumFragment.java

@ -125,7 +125,7 @@ public class ForumFragment extends BaseFragment {
});
CustomRecyclerView recyclerView = rootView.findViewById(R.id.list);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(rootView.findViewById(R.id.list).getContext());
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(recyclerView.getContext());
recyclerView.setLayoutManager(linearLayoutManager);
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(),
linearLayoutManager.getOrientation());
@ -133,6 +133,8 @@ public class ForumFragment extends BaseFragment {
recyclerView.setAdapter(forumAdapter);
swipeRefreshLayout = rootView.findViewById(R.id.swiperefresh);
swipeRefreshLayout.setProgressBackgroundColorSchemeResource(R.color.primary);
swipeRefreshLayout.setColorSchemeResources(R.color.accent);
swipeRefreshLayout.setOnRefreshListener(
new SwipeRefreshLayout.OnRefreshListener() {
@Override

2
app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentAdapter.java

@ -43,7 +43,7 @@ class RecentAdapter extends RecyclerView.Adapter<RecentAdapter.ViewHolder> {
public void onBindViewHolder(final ViewHolder holder, final int position) {
holder.mTitleView.setText(recentList.get(position).getSubject());
holder.mDateTimeView.setText(recentList.get(position).getDateTimeModified());
holder.mUserView.setText(context.getString(R.string.byUser, recentList.get(position).getLastUser()));
holder.mUserView.setText(recentList.get(position).getLastUser());
holder.topic = recentList.get(position);

19
app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentFragment.java

@ -99,7 +99,7 @@ public class RecentFragment extends BaseFragment {
recentAdapter = new RecentAdapter(getActivity(), topicSummaries, fragmentInteractionListener);
CustomRecyclerView recyclerView = rootView.findViewById(R.id.list);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(rootView.findViewById(R.id.list).getContext());
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(recyclerView.getContext());
recyclerView.setLayoutManager(linearLayoutManager);
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(),
linearLayoutManager.getOrientation());
@ -107,6 +107,8 @@ public class RecentFragment extends BaseFragment {
recyclerView.setAdapter(recentAdapter);
swipeRefreshLayout = rootView.findViewById(R.id.swiperefresh);
swipeRefreshLayout.setProgressBackgroundColorSchemeResource(R.color.primary);
swipeRefreshLayout.setColorSchemeResources(R.color.accent);
swipeRefreshLayout.setOnRefreshListener(
new SwipeRefreshLayout.OnRefreshListener() {
@Override
@ -161,11 +163,20 @@ public class RecentFragment extends BaseFragment {
throw new ParseException("Parsing failed (lastUser)");
String dateTime = recent.get(i + 2).text();
pattern = Pattern.compile("\\[(.*)\\]");
pattern = Pattern.compile("\\[(.*)]");
matcher = pattern.matcher(dateTime);
if (matcher.find())
if (matcher.find()) {
dateTime = matcher.group(1);
else
if (dateTime.contains(" am") || dateTime.contains(" pm") ||
dateTime.contains(" πμ") || dateTime.contains(" μμ")) {
dateTime = dateTime.replaceAll(":[0-5][0-9] ", " ");
} else {
dateTime=dateTime.substring(0,dateTime.lastIndexOf(":"));
}
if (!dateTime.contains(",")) {
dateTime = dateTime.replaceAll(".+? ([0-9])", "$1");
}
} else
throw new ParseException("Parsing failed (dateTime)");
topicSummaries.add(new TopicSummary(link, title, lastUser, dateTime));

2
app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadAdapter.java

@ -67,7 +67,7 @@ class UnreadAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
viewHolder.mTitleView.setText(unreadList.get(holder.getAdapterPosition()).getSubject());
viewHolder.mDateTimeView.setText(unreadList.get(holder.getAdapterPosition()).getDateTimeModified());
viewHolder.mUserView.setText(context.getString(R.string.byUser, unreadList.get(position).getLastUser()));
viewHolder.mUserView.setText(unreadList.get(position).getLastUser());
viewHolder.topic = unreadList.get(holder.getAdapterPosition());

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

@ -111,7 +111,7 @@ public class UnreadFragment extends BaseFragment {
});
CustomRecyclerView recyclerView = rootView.findViewById(R.id.list);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(rootView.findViewById(R.id.list).getContext());
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(recyclerView.getContext());
recyclerView.setLayoutManager(linearLayoutManager);
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(),
linearLayoutManager.getOrientation());
@ -119,6 +119,8 @@ public class UnreadFragment extends BaseFragment {
recyclerView.setAdapter(unreadAdapter);
swipeRefreshLayout = rootView.findViewById(R.id.swiperefresh);
swipeRefreshLayout.setProgressBackgroundColorSchemeResource(R.color.primary);
swipeRefreshLayout.setColorSchemeResources(R.color.accent);
swipeRefreshLayout.setOnRefreshListener(
new SwipeRefreshLayout.OnRefreshListener() {
@Override
@ -172,6 +174,10 @@ public class UnreadFragment extends BaseFragment {
dateTime = dateTime.substring(0, dateTime.indexOf("<br>"));
dateTime = dateTime.replace("<b>", "");
dateTime = dateTime.replace("</b>", "");
dateTime = dateTime.replaceAll(":[0-5][0-9] ", " ");
if (!dateTime.contains(",")) {
dateTime = dateTime.replaceAll(".+? ([0-9])", "$1");
}
topicSummaries.add(new TopicSummary(link, title, lastUser, dateTime));
}

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

@ -172,7 +172,7 @@ public class ProfileActivity extends BaseActivity implements LatestPostsFragment
ThmmyPage.PageCategory target = ThmmyPage.resolvePageCategory(Uri.parse(profileUrl));
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();
finish();
}
@ -217,6 +217,7 @@ public class ProfileActivity extends BaseActivity implements LatestPostsFragment
//Class variables
Document profilePage;
Spannable usernameSpan;
Boolean isOnline = false;
protected void onPreExecute() {
progressBar.setVisibility(ProgressBar.VISIBLE);
@ -261,13 +262,17 @@ public class ProfileActivity extends BaseActivity implements LatestPostsFragment
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
usernameSpan.setSpan(new RelativeSizeSpan(0.45f)
, 0, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
if (contentsTable.toString().contains("Online")
|| contentsTable.toString().contains("Συνδεδεμένος")) {
usernameSpan.setSpan(new ForegroundColorSpan(Color.parseColor("#4CAF50"))
, 0, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
} else
usernameSpan.setSpan(new ForegroundColorSpan(Color.GRAY)
, 0, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
isOnline = true;
} else {
isOnline = false;
/*usernameSpan.setSpan(new ForegroundColorSpan(Color.GRAY)
, 0, 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);*/
}
usernameSpan.setSpan(new ForegroundColorSpan(Color.parseColor("#26A69A"))
, 2, usernameSpan.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
return true;
} catch (SSLHandshakeException e) {
@ -290,8 +295,14 @@ public class ProfileActivity extends BaseActivity implements LatestPostsFragment
if (pmFAB.getVisibility() != View.GONE) pmFAB.setEnabled(true);
progressBar.setVisibility(ProgressBar.INVISIBLE);
if (usernameSpan != null) usernameView.setText(usernameSpan);
else if (usernameView.getText() != username) usernameView.setText(username);
if (usernameSpan != null) {
if (isOnline) {
usernameView.setTextColor(Color.parseColor("#4CAF50"));
} else {
usernameView.setTextColor(Color.GRAY);
}
usernameView.setText(usernameSpan);
} else if (usernameView.getText() != username) usernameView.setText(username);
if (thumbnailUrl != null && !Objects.equals(thumbnailUrl, ""))
//noinspection ConstantConditions
Picasso.with(getApplicationContext())

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

@ -21,6 +21,7 @@ import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
* specified {@link LatestPostsFragment.LatestPostsFragmentInteractionListener}.
*/
class LatestPostsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final int VIEW_TYPE_EMPTY = -1;
private final int VIEW_TYPE_ITEM = 0;
private final int VIEW_TYPE_LOADING = 1;
final private LatestPostsFragment.LatestPostsFragmentInteractionListener interactionListener;
@ -38,11 +39,17 @@ class LatestPostsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
@Override
public int getItemViewType(int position) {
if (parsedTopicSummaries.get(position) == null && position == 0) return VIEW_TYPE_EMPTY;
return parsedTopicSummaries.get(position) == null ? VIEW_TYPE_LOADING : VIEW_TYPE_ITEM;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_EMPTY) {
View view = LayoutInflater.from(parent.getContext()).
inflate(R.layout.fragment_latest_posts_empty_message, parent, false);
return new RecyclerView.ViewHolder(view){};
}
if (viewType == VIEW_TYPE_ITEM) {
View view = LayoutInflater.from(parent.getContext()).
inflate(R.layout.fragment_latest_posts_row, parent, false);

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

@ -50,6 +50,7 @@ public class LatestPostsFragment extends BaseFragment implements LatestPostsAdap
private LatestPostsTask profileLatestPostsTask;
private MaterialProgressBar progressBar;
private boolean isLoadingMore;
private boolean userHasPosts = true;
private static final int visibleThreshold = 5;
private int lastVisibleItem, totalItemCount;
@ -100,7 +101,8 @@ public class LatestPostsFragment extends BaseFragment implements LatestPostsAdap
totalItemCount = layoutManager.getItemCount();
lastVisibleItem = layoutManager.findLastVisibleItemPosition();
if (!isLoadingMore && totalItemCount <= (lastVisibleItem + visibleThreshold)) {
if (userHasPosts && !isLoadingMore &&
totalItemCount <= (lastVisibleItem + visibleThreshold)) {
isLoadingMore = true;
onLoadMore();
}
@ -126,7 +128,7 @@ public class LatestPostsFragment extends BaseFragment implements LatestPostsAdap
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (parsedTopicSummaries.isEmpty()) {
if (parsedTopicSummaries.isEmpty() && userHasPosts) {
profileLatestPostsTask = new LatestPostsTask();
profileLatestPostsTask.execute(profileUrl + ";sa=showPosts");
pagesLoaded = 1;
@ -186,6 +188,7 @@ public class LatestPostsFragment extends BaseFragment implements LatestPostsAdap
//TODO: better parse error handling (ParseException etc.)
private boolean parseLatestPosts(Document latestPostsPage) {
//td:contains( Sorry, no matches were found)
Elements latestPostsRows = latestPostsPage.
select("td:has(table:Contains(Show Posts)):not([style]) > table");
if (latestPostsRows.isEmpty()) {
@ -197,6 +200,13 @@ public class LatestPostsFragment extends BaseFragment implements LatestPostsAdap
parsedTopicSummaries.remove(parsedTopicSummaries.size() - 1);
}
if (!latestPostsRows.select("td:contains(Sorry, no matches were found)").isEmpty() ||
!latestPostsRows.select("td:contains(Δυστυχώς δεν βρέθηκε τίποτα)").isEmpty()){
userHasPosts = false;
parsedTopicSummaries.add(null);
return true;
}
for (Element row : latestPostsRows) {
String pTopicUrl, pTopicTitle, pDateTime, pPost;
if (Integer.parseInt(row.attr("cellpadding")) == 4) {

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

@ -56,6 +56,7 @@ public class StatsFragment extends Fragment {
private MaterialProgressBar progressBar;
private boolean haveParsed = false;
private boolean userHasPosts = true;
private String generalStatisticsTitle = "", generalStatistics = "", postingActivityByTimeTitle = "", mostPopularBoardsByPostsTitle = "", mostPopularBoardsByActivityTitle = "";
final private List<Entry> postingActivityByTime = new ArrayList<>();
final private List<BarEntry> mostPopularBoardsByPosts = new ArrayList<>(), mostPopularBoardsByActivity = new ArrayList<>();
@ -123,6 +124,7 @@ public class StatsFragment extends Fragment {
* as String parameter!</p>
*/
private class ProfileStatsTask extends AsyncTask<String, Void, Boolean> {
@Override
protected void onPreExecute() {
progressBar.setVisibility(ProgressBar.VISIBLE);
@ -160,15 +162,24 @@ public class StatsFragment extends Fragment {
}
private boolean parseStats(Document statsPage) {
//Doesn't go through all the parsing if this user has no posts
if (!statsPage.select("td:contains(No posts to speak of!)").isEmpty()) {
userHasPosts = false;
}
if (!statsPage.select("td:contains(Δεν υπάρχει καμία αποστολή μηνύματος!)").isEmpty()) {
userHasPosts = false;
}
if (statsPage.select("table.bordercolor[align]>tbody>tr").size() != 6)
return false;
{
Elements titleRows = statsPage.select("table.bordercolor[align]>tbody>tr.titlebg");
generalStatisticsTitle = titleRows.first().text();
if (userHasPosts) {
postingActivityByTimeTitle = titleRows.get(1).text();
mostPopularBoardsByPostsTitle = titleRows.last().select("td").first().text();
mostPopularBoardsByActivityTitle = titleRows.last().select("td").last().text();
}
}
{
Elements statsRows = statsPage.select("table.bordercolor[align]>tbody>tr:not(.titlebg)");
{
@ -177,6 +188,7 @@ public class StatsFragment extends Fragment {
generalStatistics += generalStatisticsRow.text() + "\n";
generalStatistics = generalStatistics.trim();
}
if (userHasPosts) {
{
Elements postingActivityByTimeCols = statsRows.get(1).select(">td").last()
.select("tr").first().select("td[width=4%]");
@ -212,6 +224,7 @@ public class StatsFragment extends Fragment {
Collections.reverse(mostPopularBoardsByActivityLabels);
}
}
}
return true;
}
}
@ -221,6 +234,13 @@ public class StatsFragment extends Fragment {
.setText(generalStatisticsTitle);
((TextView) mainContent.findViewById(R.id.general_statistics))
.setText(generalStatistics);
if (!userHasPosts) {
mainContent.removeViews(2, mainContent.getChildCount() - 2);
//mainContent.removeViews(2, 6);
return;
}
((TextView) mainContent.findViewById(R.id.posting_activity_by_time_title))
.setText(postingActivityByTimeTitle);

233
app/src/main/java/gr/thmmy/mthmmy/activities/topic/Posting.java

@ -1,14 +1,9 @@
package gr.thmmy.mthmmy.activities.topic;
import android.util.Log;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import okhttp3.Response;
import timber.log.Timber;
@ -86,232 +81,4 @@ class Posting {
}
return REPLY_STATUS.SUCCESSFUL;
}
/**
* This is a fucked up method.. Just don't waste your time here unless you have suicidal
* tendencies.
*
* @param html the html string to be transformed to BBcode
* @return the BBcode string
*/
static String htmlToBBcode(String html) {
Log.d("Cancer", html);
Map<String, String> bbMap = new HashMap<>();
Map<String, String> smileysMap1 = new HashMap<>();
Map<String, String> smileysMap2 = new HashMap<>();
smileysMap1.put("Smiley", ":)");
smileysMap1.put("Wink", ";)");
smileysMap1.put("Cheesy", ":D");
smileysMap1.put("Grin", ";D");
smileysMap1.put("Angry", ">:(");
smileysMap1.put("Sad", ":(");
smileysMap1.put("Shocked", ":o");
smileysMap1.put("Cool", "8))");
smileysMap1.put("Huh", ":???:");
smileysMap1.put("Roll Eyes", "::)");
smileysMap1.put("Tongue", ":P");
smileysMap1.put("Embarrassed", ":-[");
smileysMap1.put("Lips Sealed", ":-X");
smileysMap1.put("Kiss", ":-*");
smileysMap1.put("Cry", ":'(");
smileysMap1.put("heart", "<3");
smileysMap1.put("kleidaria", "^locked^");
smileysMap1.put("roll_over", "^rollover^");
smileysMap1.put("redface", "^redface^");
smileysMap1.put("confused", "^confused^");
smileysMap1.put("innocent", "^innocent^");
smileysMap1.put("sleep", "^sleep^");
smileysMap1.put("lips_sealed", "^sealed^");
smileysMap1.put("cool", "^cool^");
smileysMap1.put("crazy", "^crazy^");
smileysMap1.put("mad", "^mad^");
smileysMap1.put("wav", "^wav^");
smileysMap1.put("BinkyBaby", "^binkybaby^");
smileysMap1.put("DontKnow", "^dontknow^");
smileysMap1.put("angry4", ":angry4:");
smileysMap1.put("angryAndHot", "^angryhot^");
smileysMap1.put("angry", "^angry^");
smileysMap1.put("bang_head", "^banghead^");
smileysMap1.put("CryBaby", "^crybaby^");
smileysMap1.put("Hello", "^hello^");
smileysMap1.put("jerk", "^jerk^");
smileysMap1.put("NoNo", "^nono^");
smileysMap1.put("NotWorthy", "^notworthy^");
smileysMap1.put("Off-topic", "^off-topic^");
smileysMap1.put("Puke", "^puke^");
smileysMap1.put("Shout", "^shout^");
smileysMap1.put("Slurp", "^slurp^");
smileysMap1.put("SuperConfused", "^superconfused^");
smileysMap1.put("SuperInnocent", "^superinnocent^");
smileysMap1.put("CellPhone", "^cellPhone^");
smileysMap1.put("Idiot", "^idiot^");
smileysMap1.put("Knuppel", "^knuppel^");
smileysMap1.put("TickedOff", "^tickedOff^");
smileysMap1.put("Peace", "^peace^");
smileysMap1.put("Suspicious", "^suspicious^");
smileysMap1.put("Caffine", "^caffine^");
smileysMap1.put("argue", "^argue^");
smileysMap1.put("banned2", "^banned2^");
smileysMap1.put("banned", "^banned^");
smileysMap1.put("bath", "^bath^");
smileysMap1.put("beg", "^beg^");
smileysMap1.put("bluescreen", "^bluescreen^");
smileysMap1.put("boil", "^boil^");
smileysMap1.put("bye", "^bye^");
smileysMap1.put("callmerip", "^callmerip^");
smileysMap1.put("carnaval", "^carnaval^");
smileysMap1.put("clap", "^clap^");
smileysMap1.put("coffepot", "^coffepot^");
smileysMap1.put("crap", "^crap^");
smileysMap1.put("curses", "^curses^");
smileysMap1.put("funny", "^funny^");
smileysMap1.put("guitar", "^guitar^");
smileysMap1.put("kissy", "^kissy^");
smileysMap1.put("band", "^band^");
smileysMap1.put("ivres", "^ivres^");
smileysMap1.put("kaloe", "^kaloe^");
smileysMap1.put("kremala", "^kremala^");
smileysMap1.put("moon", "^moon^");
smileysMap1.put("mopping", "^mopping^");
smileysMap1.put("mountza", "^mountza^");
smileysMap1.put("pcsleep", "^pcsleep^");
smileysMap1.put("pinokio", "^pinokio^");
smileysMap1.put("poke", "^poke^");
smileysMap1.put("seestars", "^seestars^");
smileysMap1.put("sfyri", "^sfyri^");
smileysMap1.put("spam", "^spam^");
smileysMap1.put("super", "^super^");
smileysMap1.put("tafos", "^tafos^");
smileysMap1.put("tomato", "^tomato^");
smileysMap1.put("ytold", "^ytold^");
smileysMap1.put("beer", "^beer^");
smileysMap1.put("ο fritz!!!", "^fritz^");
smileysMap1.put("o Wade!!!", "^wade^");
smileysMap1.put("bonjour", "^hat^");
smileysMap1.put("bonjour2", "^miss^");
smileysMap1.put("question", "^que^");
smileysMap1.put("shifty", "^shifty^");
smileysMap1.put("shy", "^shy^");
smileysMap1.put("music_listenning", "^music_listen^");
smileysMap1.put("bag_face", "^bagface^");
smileysMap1.put("rotation", "^rotate^");
smileysMap1.put("love", "^love^");
smileysMap1.put("speech", "^speech^");
smileysMap1.put("shocked", "^shocked^");
smileysMap1.put("extremely_shocked", "^ex_shocked^");
smileysMap1.put("smurf", "^smurf^");
smileysMap1.put("monster", "^monster^");
smileysMap1.put("pig", "^pig^");
smileysMap1.put("lol", "^lol^");
smileysMap2.put("Police", "^Police^");
smileysMap2.put("foyska", "^fouska^");
smileysMap2.put("nista", "^nysta^");
smileysMap2.put("10_7_3", "^sfinaki^");
smileysMap2.put("yu", "^yue^");
smileysMap2.put("a-eatpaper", "^eatpaper^");
smileysMap2.put("lypi", "^lypi^");
smileysMap2.put("megashok1wq", "^aytoxeir^");
smileysMap2.put("victory", "^victory^");
smileysMap2.put("filarakia", "^filarakia^");
smileysMap2.put("rofl", "^rolfmao^");
smileysMap2.put("locked", "^lock^");
smileysMap2.put("facepalm", "^facepalm^");
//html stuff on the beginning
bbMap.put("<link rel=.+\">\n ", "");
//quotes and code headers
bbMap.put("\\s*?<div class=\"quoteheader\">(.*?(\\n))*?.*?<\\/div>", "");
bbMap.put("\\s*?<div class=\"codeheader\">(.*?(\\n))+?.*?<\\/div>", "");
bbMap.put("\\s*?<div class=\"quote\">(.*?(\\n))+?.*?<\\/div>", "");
bbMap.put("<br>", "\\\n");
//Non-breaking space
bbMap.put("&nbsp;", " ");
//bold
bbMap.put("\\s*?<b>([\\S\\s]+?)<\\/b>", "\\[b\\]$1\\[/b\\]");
//italics
bbMap.put("\\s*?<i>([\\S\\s]+?)<\\/i>", "\\[i\\]$1\\[/i\\]");
//underline
bbMap.put("\\s*?<span style=\"text-decoration: underline;\">([\\S\\s]+?)<\\/span>", "\\[u\\]$1\\[/u\\]");
//deleted
bbMap.put("\\s*?<del>([\\S\\s]+?)<\\/del>", "\\[s\\]$1\\[/s\\]");
//text color
bbMap.put("\\s*?<span style=\"color: (.+?);\">([\\S\\s]+?)<\\/span>", "\\[color=$1\\]$2\\[/color\\]");
//glow
bbMap.put("\\s*?<span style=\"background-color: (.+?);\">([\\S\\s]+?)<\\/span>", "\\[glow=$1,2,300\\]$2\\[/glow\\]");
//shadow
bbMap.put("\\s*?<span style=\"text-shadow: (.+?) (.+?)\">([\\S\\s]+?)<\\/span>", "\\[shadow=$1,$2\\]$3\\[/shadow\\]");
//running text
bbMap.put("\\s*?<marquee>\n ([\\S\\s]+?)\n <\\/marquee>", "\\[move\\]$1\\[/move\\]");
//alignment
bbMap.put("\\s*?<div align=\"center\">\n ([\\S\\s]+?)\n <\\/div>", "\\[center\\]$1\\[/center\\]");
bbMap.put("\\s*?<div style=\"text-align: (.+?);\">\n ([\\S\\s]+?)\n <\\/div>", "\\[$1\\]$2\\[/$1\\]");
//preformated
bbMap.put("\\s*?<pre>([\\S\\s]+?)<\\/pre>", "\\[pre\\]$1\\[/pre\\]");
//horizontal rule
bbMap.put("\\s*?<hr>", "\\[hr\\]");
//resize
bbMap.put("\\s*?<span style=\"font-size: (.+?);(.+?)\">([\\S\\s]+?)<\\/span>", "\\[size=$1\\]$3\\[/size\\]");
//font
bbMap.put("\\s*?<span style=\"font-family: (.+?);\">([\\S\\s]+?)<\\/span>", "\\[font=$1\\]$2\\[/font\\]");
//lists
bbMap.put("\\s+<li>(.+?)<\\/li>", "\\[li\\]$1\\[/li\\]");
bbMap.put("\n\\s+<ul style=\"margin-top: 0; margin-bottom: 0;\">([\\S\\s]+?)\n\\s+<\\/ul>",
"\\[list\\]\n$1\n\\[/list\\]");
//latex code
bbMap.put("\\s*?<img src=\".+?eq=([\\S\\s]+?)\" .+?\">", "\\[tex\\]$1\\[/tex\\]");
//code
bbMap.put("\\s*?<div class=\"code\">((.*?(\\n))+?.*?)<\\/div>", "\\[code\\]$1\\[/code\\]");
//teletype
bbMap.put("\\s*?<tt>([\\S\\s]+?)<\\/tt>", "\\[tt\\]$1\\[/tt\\]");
//superscript/subscript
bbMap.put("\\s*?<sub>([\\S\\s]+?)<\\/sub>", "\\[sub\\]$1\\[/sub\\]");
bbMap.put("\\s*?<sup>([\\S\\s]+?)<\\/sup>", "\\[sup\\]$1\\[/sup\\]");
//tables
bbMap.put("\\s*?<td.+?>([\\S\\s]+?)<\\/td>", "\\[td\\]$1\\[/td\\]");
bbMap.put("<tr>([\\S\\s]+?)\n <\\/tr>", "\\[tr\\]$1\\[/tr\\]");
bbMap.put("\\s*?<table style=\"(.+?)\">\n <tbody>\n ([\\S\\s]+?)\n <\\/tbody>\n <\\/table>"
, "\\[table\\]$2\\[/table\\]");
//videos
bbMap.put("\\s*?<div class=\"yt\">.+?watch\\?v=(.+?)\"((.|\\n)*?)/div>\n",
"[youtube]https://www.youtube.com/watch?v=$1[/youtube]");
//ftp
bbMap.put("<a href=\"ftp:(.+?)\" .+?>([\\S\\s]+?)<\\/a>", "\\[fpt=ftp:$1\\]$2\\[/ftp\\]");
//mailto
bbMap.put("\\s*?<a href=\"mailto:(.+?)\">([\\S\\s]+?)<\\/a>", "\\[email\\]$2\\[/email\\]");
//links
bbMap.put("\\s*?<a href=\"(.+?)\" .+?>([\\S\\s]+?)</a>", "\\[url=$1\\]$2\\[/url\\]");
//smileys
for (Map.Entry entry : smileysMap1.entrySet()) {
bbMap.put("\n <img src=\"(.+?)//www.thmmy.gr/smf/Smileys/default_dither/(.+?) alt=\""
+ entry.getKey().toString() + "\" .+?\">", entry.getValue().toString());
}
for (Map.Entry entry : smileysMap2.entrySet()) { //Those that have empty alt tag
bbMap.put("\n <img src=\"(.+?)//www.thmmy.gr/smf/Smileys/default_dither/"
+ entry.getKey().toString() + ".gif\" .+?\">", entry.getValue().toString());
}
bbMap.put("\n <img src=\"(.+?)//www.thmmy.gr/smf/Smileys/default_dither/undecided.gif\" alt=\"Undecided\" border=\"0\">"
, Matcher.quoteReplacement(":-\\"));
//html stuff on the end
bbMap.put("\n</div>", "");
for (Map.Entry entry : bbMap.entrySet()) {
html = html.replaceAll(entry.getKey().toString(), entry.getValue().toString());
}
//img need to be done last or it messes up everything else
html = html.replaceAll("\\s+<img src=\"(.+?)\" .+? width=\"(.+?)\" .+? height=\"(.+?)\" .+?>",
"\\[img width=$2 height=$3\\]$1\\[/img\\]");
html = html.replaceAll("\\s+<img src=\"(.+?)\" .+? height=\"(.+?)\" .+? width=\"(.+?)\" .+?>",
"\\[img height=$2 width=$3\\]$1\\[/img\\]");
html = html.replaceAll("\\s+<img src=\"(.+?)\" .+? width=\"(.+?)\" .+?>", "\\[img width=$2\\]$1\\[/img\\]");
html = html.replaceAll("\\s+<img src=\"(.+?)\" .+? height=\"(.+?)\" .+?>", "\\[img height=$2\\]$1\\[/img\\]");
html = html.replaceAll("\\s+<img src=\"(.+?)\".+?>", "\\[img\\]$1\\[/img\\]");
Log.d("Cancer", html);
return html;
}
}

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

@ -65,13 +65,14 @@ import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_
import static gr.thmmy.mthmmy.activities.topic.Posting.replyStatus;
/**
* Activity for topics. When creating an Intent of this activity you need to bundle a <b>String</b>
* containing this topics's url using the key {@link #BUNDLE_TOPIC_URL} and a <b>String</b> containing
* this topic's title using the key {@link #BUNDLE_TOPIC_TITLE}.
* Activity for parsing and rendering topics. When creating an Intent of this activity you need to
* bundle a <b>String</b> containing this topic's url using the key {@link #BUNDLE_TOPIC_URL}.
* You can also bundle a <b>String</b> containing this topic's title, if its available, using the
* key {@link #BUNDLE_TOPIC_TITLE} for faster title rendering.
*/
@SuppressWarnings("unchecked")
public class TopicActivity extends BaseActivity {
//Class variables
//Activity's variables
/**
* The key to use when putting topic's url String to {@link TopicActivity}'s Bundle.
*/
@ -81,46 +82,110 @@ public class TopicActivity extends BaseActivity {
*/
public static final String BUNDLE_TOPIC_TITLE = "TOPIC_TITLE";
private static TopicTask topicTask;
//About posts
private MaterialProgressBar progressBar;
private TextView toolbarTitle;
/**
* Holds this topic's base url. For example a topic with url similar to
* "https://www.thmmy.gr/smf/index.php?topic=1.15;topicseen" or
* "https://www.thmmy.gr/smf/index.php?topic=1.msg1#msg1"
* has the base url "https://www.thmmy.gr/smf/index.php?topic=1"
*/
private static String base_url = "";
/**
* Holds this topic's title. At first this gets the value of the topic title that came with
* bundle and is rendered in the toolbar while parsing this topic. Later, after topic's parsing
* is done, it gets the value of {@link #parsedTitle} if bundle title and parsed title differ.
*/
private String topicTitle;
/**
* Holds this topic's title as parsed from the html source. If this (parsed) title is different
* than the one that came with activity's bundle then the parsed title is preferred over the
* bundle one and gets rendered in the toolbar.
*/
private String parsedTitle;
private RecyclerView recyclerView;
/**
* Holds the url of this page
*/
private String loadedPageUrl = "";
/**
* Becomes true after user has posted in this topic and the page is being reloaded and false
* when topic's reloading is done
*/
private boolean reloadingPage = false;
//Posts related
private TopicAdapter topicAdapter;
/**
* Holds a list of this topic's posts
*/
private ArrayList<Post> postsList;
/**
* Gets assigned to {@link #postFocus} when there is no post focus information in the url
*/
private static final int NO_POST_FOCUS = -1;
/**
* Holds the index of the post that has focus
*/
private int postFocus = NO_POST_FOCUS;
/**
* Holds the position in the {@link #postsList} of the post with focus
*/
private static int postFocusPosition = 0;
//Reply
//Reply related
private FloatingActionButton replyFAB;
/**
* Holds this topic's reply url
*/
private String replyPageUrl = null;
//Topic's pages
//Topic's pages related
/**
* Holds current page's index (starting from 1, not 0)
*/
private int thisPage = 1;
/**
* Holds this topic's number of pages
*/
private int numberOfPages = 1;
/**
* Holds a list of this topic's pages urls
*/
private final SparseArray<String> pagesUrls = new SparseArray<>();
//Page select
//Page select related
/**
* Used for handling bottom navigation bar's buttons long click user interactions
*/
private final Handler repeatUpdateHandler = new Handler();
/**
* Holds the initial time delay before a click on bottom navigation bar is considered long
*/
private final long INITIAL_DELAY = 500;
private boolean autoIncrement = false;
private boolean autoDecrement = false;
/**
* Holds the number of pages to be added or subtracted from current page on each step while a
* long click is held in either next or previous buttons
*/
private static final int SMALL_STEP = 1;
/**
* Holds the number of pages to be added or subtracted from current page on each step while a
* long click is held in either first or last buttons
*/
private static final int LARGE_STEP = 10;
/**
* Holds the value (index) of the page to be requested when a user interaction with bottom
* navigation bar occurs
*/
private Integer pageRequestValue;
//Bottom navigation graphics
//Bottom navigation bar graphics related
private LinearLayout bottomNavBar;
private ImageButton firstPage;
private ImageButton previousPage;
private TextView pageIndicator;
private ImageButton nextPage;
private ImageButton lastPage;
//Topic's info
//Topic's info related
private SpannableStringBuilder topicTreeAndMods = new SpannableStringBuilder("Loading..."),
topicViewers = new SpannableStringBuilder("Loading...");
//Other variables
private MaterialProgressBar progressBar;
private TextView toolbarTitle;
private static String base_url = "";
private String topicTitle;
private String parsedTitle;
private RecyclerView recyclerView;
private String loadedPageUrl = "";
private boolean reloadingPage = false;
@Override
@ -149,6 +214,7 @@ public class TopicActivity extends BaseActivity {
toolbarTitle.setMarqueeRepeatLimit(-1);
toolbarTitle.setText(topicTitle);
toolbarTitle.setSelected(true);
toolbarTitle.setEnabled(false);
setSupportActionBar(toolbar);
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
@ -171,6 +237,7 @@ public class TopicActivity extends BaseActivity {
new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
v.performClick();
return topicTask != null && topicTask.getStatus() == AsyncTask.Status.RUNNING;
}
}
@ -179,7 +246,7 @@ public class TopicActivity extends BaseActivity {
CustomLinearLayoutManager layoutManager = new CustomLinearLayoutManager(
getApplicationContext(), loadedPageUrl);
recyclerView.setLayoutManager(layoutManager);
topicAdapter = new TopicAdapter(this, postsList, topicTask);
topicAdapter = new TopicAdapter(this, postsList, base_url, topicTask);
recyclerView.setAdapter(topicAdapter);
replyFAB = findViewById(R.id.topic_fab);
@ -191,12 +258,8 @@ public class TopicActivity extends BaseActivity {
@Override
public void onClick(View view) {
if (sessionManager.isLoggedIn()) {
postsList.add(null);
topicAdapter.notifyItemInserted(postsList.size());
topicAdapter.prepareForReply(new ReplyTask(), topicTitle, loadedPageUrl);
replyFAB.hide();
bottomNavBar.setVisibility(View.GONE);
recyclerView.scrollToPosition(postsList.size() - 1);
PrepareForReply prepareForReply = new PrepareForReply();
prepareForReply.execute(topicAdapter.getToQuoteList());
}
}
});
@ -464,12 +527,10 @@ public class TopicActivity extends BaseActivity {
//------------------------------------BOTTOM NAV BAR METHODS END------------------------------------
/**
* An {@link AsyncTask} that handles asynchronous fetching of a topic page and parsing it's
* data. {@link AsyncTask#onPostExecute(Object) OnPostExecute} method calls {@link RecyclerView#swapAdapter}
* to build graphics.
* <p>
* <p>Calling TopicTask's {@link AsyncTask#execute execute} method needs to have profile's url
* as String parameter!</p>
* An {@link AsyncTask} that handles asynchronous fetching of this topic page and parsing of it's
* data.
* <p>TopicTask's {@link AsyncTask#execute execute} method needs a topic's url as String
* parameter.</p>
*/
class TopicTask extends AsyncTask<String, Void, Integer> {
private static final int SUCCESS = 0;
@ -477,6 +538,8 @@ public class TopicActivity extends BaseActivity {
private static final int OTHER_ERROR = 2;
private static final int SAME_PAGE = 3;
ArrayList<Post> localPostsList;
@Override
protected void onPreExecute() {
progressBar.setVisibility(ProgressBar.VISIBLE);
@ -530,7 +593,15 @@ public class TopicActivity extends BaseActivity {
try {
Response response = client.newCall(request).execute();
document = Jsoup.parse(response.body().string());
parse(document);
localPostsList = parse(document);
//Finds the position of the focused message if present
for (int i = 0; i < localPostsList.size(); ++i) {
if (localPostsList.get(i).getPostIndex() == postFocus) {
postFocusPosition = i;
break;
}
}
return SUCCESS;
} catch (IOException e) {
Timber.i(e, "IO Exception");
@ -542,14 +613,6 @@ public class TopicActivity extends BaseActivity {
}
protected void onPostExecute(Integer parseResult) {
//Finds the position of the focused message if present
for (int i = 0; i < postsList.size(); ++i) {
if (postsList.get(i).getPostIndex() == postFocus) {
postFocusPosition = i;
break;
}
}
switch (parseResult) {
case SUCCESS:
if (topicTitle == null || Objects.equals(topicTitle, "")
@ -560,12 +623,19 @@ public class TopicActivity extends BaseActivity {
invalidateOptionsMenu();
}
progressBar.setVisibility(ProgressBar.INVISIBLE);
if (!(postsList.isEmpty() || postsList.size() == 0)) {
recyclerView.getRecycledViewPool().clear(); //Avoid inconsistency detected bug
postsList.clear();
topicAdapter.notifyItemRangeRemoved(0, postsList.size() - 1);
}
postsList.addAll(localPostsList);
topicAdapter.notifyItemRangeInserted(0, postsList.size());
progressBar.setVisibility(ProgressBar.INVISIBLE);
if (replyPageUrl == null) {
replyFAB.hide();
topicAdapter.customNotifyDataSetChanged(new TopicTask(), false);
} else topicAdapter.customNotifyDataSetChanged(new TopicTask(), true);
topicAdapter.resetTopic(base_url, new TopicTask(), false);
} else topicAdapter.resetTopic(base_url, new TopicTask(), true);
if (replyFAB.getVisibility() != View.GONE) replyFAB.setEnabled(true);
@ -582,8 +652,8 @@ public class TopicActivity extends BaseActivity {
progressBar.setVisibility(ProgressBar.INVISIBLE);
if (replyPageUrl == null) {
replyFAB.hide();
topicAdapter.customNotifyDataSetChanged(new TopicTask(), false);
} else topicAdapter.customNotifyDataSetChanged(new TopicTask(), true);
topicAdapter.resetTopic(base_url, new TopicTask(), false);
} else topicAdapter.resetTopic(base_url, new TopicTask(), true);
if (replyFAB.getVisibility() != View.GONE) replyFAB.setEnabled(true);
paginationEnabled(true);
Toast.makeText(TopicActivity.this, "That's the same page.", Toast.LENGTH_SHORT).show();
@ -604,7 +674,7 @@ public class TopicActivity extends BaseActivity {
* @param topic {@link Document} object containing this topic's source code
* @see org.jsoup.Jsoup Jsoup
*/
private void parse(Document topic) {
private ArrayList<Post> parse(Document topic) {
ParseHelpers.Language language = ParseHelpers.Language.getLanguage(topic);
//Finds topic's tree, mods and users viewing
@ -646,11 +716,7 @@ public class TopicActivity extends BaseActivity {
}
}
postsList.clear();
int oldSize = postsList.size();
topicAdapter.notifyItemRangeRemoved(0, oldSize);
recyclerView.getRecycledViewPool().clear(); //Avoid inconsistency detected bug
postsList.addAll(TopicParser.parseTopic(topic, language));
return TopicParser.parseTopic(topic, language);
}
private void makeLinkClickable(SpannableStringBuilder strBuilder, final URLSpan span) {
@ -703,23 +769,25 @@ public class TopicActivity extends BaseActivity {
}
}
class ReplyTask extends AsyncTask<String, Void, Boolean> {
class PrepareForReply extends AsyncTask<ArrayList<Integer>, Void, Boolean> {
String numReplies, seqnum, sc, topic, buildedQuotes = "";
@Override
protected void onPreExecute() {
progressBar.setVisibility(ProgressBar.VISIBLE);
paginationEnabled(false);
replyFAB.setEnabled(false);
replyFAB.hide();
bottomNavBar.setVisibility(View.GONE);
}
@Override
protected Boolean doInBackground(String... message) {
protected Boolean doInBackground(ArrayList<Integer>... quoteList) {
Document document;
String numReplies, seqnum, sc, topic;
Request request = new Request.Builder()
.url(replyPageUrl + ";wap2")
.build();
try {
Response response = client.newCall(request).execute();
document = Jsoup.parse(response.body().string());
@ -728,22 +796,61 @@ public class TopicActivity extends BaseActivity {
seqnum = document.select("input[name=seqnum]").first().attr("value");
sc = document.select("input[name=sc]").first().attr("value");
topic = document.select("input[name=topic]").first().attr("value");
} catch (IOException e) {
Timber.e(e, "Post failed.");
} catch (IOException | Selector.SelectorParseException e) {
Timber.e(e, "Prepare failed.");
return false;
} catch (Selector.SelectorParseException e) {
Timber.e(e, "Post failed.");
}
for (Integer quotePosition : quoteList[0]) {
request = new Request.Builder()
.url("https://www.thmmy.gr/smf/index.php?action=quotefast;quote=" +
postsList.get(quotePosition).getPostIndex() +
";" + "sesc=" + sc + ";xml")
.build();
try {
Response response = client.newCall(request).execute();
String body = response.body().string();
buildedQuotes += body.substring(body.indexOf("<quote>") + 7, body.indexOf("</quote>"));
buildedQuotes += "\n\n";
} catch (IOException | Selector.SelectorParseException e) {
Timber.e(e, "Quote building failed.");
return false;
}
}
return true;
}
@Override
protected void onPostExecute(Boolean result) {
postsList.add(null);
topicAdapter.notifyItemInserted(postsList.size());
topicAdapter.prepareForReply(new ReplyTask(), topicTitle, numReplies, seqnum, sc,
topic, buildedQuotes);
recyclerView.scrollToPosition(postsList.size() - 1);
progressBar.setVisibility(ProgressBar.GONE);
}
}
class ReplyTask extends AsyncTask<String, Void, Boolean> {
@Override
protected void onPreExecute() {
progressBar.setVisibility(ProgressBar.VISIBLE);
paginationEnabled(false);
replyFAB.setEnabled(false);
}
@Override
protected Boolean doInBackground(String... args) {
RequestBody postBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("message", message[1])
.addFormDataPart("num_replies", numReplies)
.addFormDataPart("seqnum", seqnum)
.addFormDataPart("sc", sc)
.addFormDataPart("subject", message[0])
.addFormDataPart("topic", topic)
.addFormDataPart("message", args[1])
.addFormDataPart("num_replies", args[2])
.addFormDataPart("seqnum", args[3])
.addFormDataPart("sc", args[4])
.addFormDataPart("subject", args[0])
.addFormDataPart("topic", args[5])
.build();
Request post = new Request.Builder()

129
app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java

@ -10,10 +10,8 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.res.ResourcesCompat;
import android.support.v7.widget.AppCompatImageButton;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.text.Editable;
import android.text.TextUtils;
@ -26,7 +24,6 @@ import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
@ -35,14 +32,8 @@ import android.widget.TextView;
import com.squareup.picasso.Picasso;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import gr.thmmy.mthmmy.R;
@ -61,7 +52,9 @@ import static gr.thmmy.mthmmy.activities.board.BoardActivity.BUNDLE_BOARD_URL;
import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_THUMBNAIL_URL;
import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_URL;
import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_USERNAME;
import static gr.thmmy.mthmmy.activities.topic.Posting.htmlToBBcode;
import static gr.thmmy.mthmmy.activities.topic.TopicActivity.BUNDLE_TOPIC_URL;
import static gr.thmmy.mthmmy.activities.topic.TopicParser.USER_COLOR_WHITE;
import static gr.thmmy.mthmmy.activities.topic.TopicParser.USER_COLOR_YELLOW;
import static gr.thmmy.mthmmy.base.BaseActivity.getSessionManager;
/**
@ -74,6 +67,7 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static int THUMBNAIL_SIZE;
private final Context context;
private String topicTitle;
private String baseUrl;
private final ArrayList<Integer> toQuoteList = new ArrayList<>();
private final List<Post> postsList;
/**
@ -98,16 +92,18 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final String[] replyDataHolder = new String[2];
private final int replySubject = 0, replyText = 1;
private String loadedPageUrl = "";
private String numReplies, seqnum, sc, topic, buildedQuotes;
private boolean canReply = false;
/**
* @param context the context of the {@link RecyclerView}
* @param postsList List of {@link Post} objects to use
*/
TopicAdapter(Context context, List<Post> postsList, TopicActivity.TopicTask topicTask) {
TopicAdapter(Context context, List<Post> postsList, String baseUrl,
TopicActivity.TopicTask topicTask) {
this.context = context;
this.postsList = postsList;
this.baseUrl = baseUrl;
THUMBNAIL_SIZE = (int) context.getResources().getDimension(R.dimen.thumbnail_size);
for (int i = 0; i < postsList.size(); ++i) {
@ -117,10 +113,19 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
this.topicTask = topicTask;
}
void prepareForReply(TopicActivity.ReplyTask replyTask, String topicTitle, String loadedPageUrl) {
ArrayList<Integer> getToQuoteList(){
return toQuoteList;
}
void prepareForReply(TopicActivity.ReplyTask replyTask, String topicTitle, String numReplies,
String seqnum, String sc, String topic, String buildedQuotes) {
this.replyTask = replyTask;
this.topicTitle = topicTitle;
this.loadedPageUrl = loadedPageUrl;
this.numReplies = numReplies;
this.seqnum = seqnum;
this.sc = sc;
this.topic = topic;
this.buildedQuotes = buildedQuotes;
}
@Override
@ -158,12 +163,8 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
//Default post subject
replyDataHolder[replySubject] = "Re: " + topicTitle;
//Build quotes
String quotes = "";
for (int quotePosition : toQuoteList) {
quotes += buildQuote(quotePosition);
}
if (!Objects.equals(quotes, ""))
replyDataHolder[replyText] = htmlToBBcode(quotes);
if (!Objects.equals(buildedQuotes, ""))
replyDataHolder[replyText] = buildedQuotes;
return new QuickReplyViewHolder(view, new CustomEditTextListener(replySubject),
new CustomEditTextListener(replyText));
}
@ -309,6 +310,11 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
holder.personalText.setVisibility(View.VISIBLE);
} else
holder.personalText.setVisibility(View.GONE);
if (mUserColor != USER_COLOR_YELLOW) {
holder.username.setTextColor(mUserColor);
} else {
holder.username.setTextColor(USER_COLOR_WHITE);
}
if (mNumberOfStars > 0) {
holder.stars.setTypeface(Typeface.createFromAsset(context.getAssets()
, "fonts/fontawesome-webfont.ttf"));
@ -396,7 +402,7 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
TopicAnimations.animateUserExtraInfoVisibility(holder.username,
holder.subject, Color.parseColor("#FFFFFF"),
Color.parseColor("#757575"), v);
Color.parseColor("#757575"), (LinearLayout) v);
}
});
} else {
@ -460,7 +466,7 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
if (holder.quickReply.getText().toString().isEmpty()) return;
holder.submitButton.setEnabled(false);
replyTask.execute(holder.quickReplySubject.getText().toString(),
holder.quickReply.getText().toString());
holder.quickReply.getText().toString(), numReplies, seqnum, sc, topic);
holder.quickReplySubject.getText().clear();
holder.quickReplySubject.setText("Re: " + topicTitle);
@ -471,7 +477,8 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
}
}
void customNotifyDataSetChanged(TopicActivity.TopicTask topicTask, boolean canReply) {
void resetTopic(String baseUrl, TopicActivity.TopicTask topicTask, boolean canReply) {
this.baseUrl = baseUrl;
this.topicTask = topicTask;
this.canReply = canReply;
viewProperties.clear();
@ -479,8 +486,6 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
//Initializes properties, array's values will be false by default
viewProperties.add(new boolean[3]);
}
notifyItemRangeInserted(0, postsList.size());
//notifyDataSetChanged();
}
@Override
@ -492,9 +497,7 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
* Custom {@link RecyclerView.ViewHolder} implementation
*/
private class PostViewHolder extends RecyclerView.ViewHolder {
final CardView cardView;
final LinearLayout cardChildLinear;
final FrameLayout postDateAndNumber;
final TextView postDate, postNum, username, subject;
final ImageView thumbnail;
final public WebView post;
@ -510,9 +513,7 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
super(view);
//Initializes layout's graphic elements
//Standard stuff
cardView = view.findViewById(R.id.card_view);
cardChildLinear = view.findViewById(R.id.card_child_linear);
postDateAndNumber = view.findViewById(R.id.post_date_and_number_exp);
postDate = view.findViewById(R.id.post_date);
postNum = view.findViewById(R.id.post_number);
thumbnail = view.findViewById(R.id.thumbnail);
@ -544,7 +545,6 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
final TextView username;
final EditText quickReply, quickReplySubject;
final AppCompatImageButton submitButton;
final CustomEditTextListener replySubject, replyText;
QuickReplyViewHolder(View quickReply, CustomEditTextListener replySubject
, CustomEditTextListener replyText) {
@ -552,10 +552,8 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
thumbnail = quickReply.findViewById(R.id.thumbnail);
username = quickReply.findViewById(R.id.username);
this.quickReply = quickReply.findViewById(R.id.quick_reply_text);
this.replyText = replyText;
this.quickReply.addTextChangedListener(replyText);
quickReplySubject = quickReply.findViewById(R.id.quick_reply_subject);
this.replySubject = replySubject;
quickReplySubject.addTextChangedListener(replySubject);
submitButton = quickReply.findViewById(R.id.quick_reply_submit);
}
@ -589,7 +587,7 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
if (target.is(ThmmyPage.PageCategory.TOPIC)) {
//This url points to a topic
//Checks if this is the current topic
/*if (Objects.equals(uriString.substring(0, uriString.lastIndexOf(".")), base_url)) {
if (Objects.equals(uriString.substring(0, uriString.lastIndexOf(".")), baseUrl)) {
//Gets uri's targeted message's index number
String msgIndexReq = uriString.substring(uriString.indexOf("msg") + 3);
if (msgIndexReq.contains("#"))
@ -604,9 +602,16 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
return true;
}
}
}*/
topicTask.execute(uri.toString());
}
Intent intent = new Intent(context, TopicActivity.class);
Bundle extras = new Bundle();
extras.putString(BUNDLE_TOPIC_URL, uriString);
intent.putExtras(extras);
intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
return true;
} else if (target.is(ThmmyPage.PageCategory.BOARD)) {
Intent intent = new Intent(context, BoardActivity.class);
@ -660,62 +665,6 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
}
}
@Nullable
private String buildQuote(int quotePosition) {
Date postDate = null;
{
String date = postsList.get(quotePosition).getPostDate();
if (date != null) {
DateFormat format = new SimpleDateFormat("MMMM d, yyyy, h:m:s a", Locale.ENGLISH);
date = date.replace("Ιανουαρίου", "January");
date = date.replace("Φεβρουαρίου", "February");
date = date.replace("Μαρτίου", "March");
date = date.replace("Απριλίου", "April");
date = date.replace("Μαΐου", "May");
date = date.replace("Ιουνίου", "June");
date = date.replace("Ιουλίου", "July");
date = date.replace("Αυγούστου", "August");
date = date.replace("Σεπτεμβρίου", "September");
date = date.replace("Οκτωβρίου", "October");
date = date.replace("Νοεμβρίου", "November");
date = date.replace("Δεκεμβρίου", "December");
if (date.contains("Today")) {
date = date.replace("Today at",
Calendar.getInstance().getDisplayName(Calendar.MONTH,
Calendar.LONG, Locale.ENGLISH)
+ " " + Calendar.getInstance().get(Calendar.DAY_OF_MONTH)
+ ", " + Calendar.getInstance().get(Calendar.YEAR) + ",");
} else if (date.contains("Σήμερα")) {
date = date.replace("Σήμερα στις",
Calendar.getInstance().getDisplayName(Calendar.MONTH,
Calendar.LONG, Locale.ENGLISH)
+ " " + Calendar.getInstance().get(Calendar.DAY_OF_MONTH)
+ ", " + Calendar.getInstance().get(Calendar.YEAR) + ",");
if (date.contains("πμ")) date = date.replace("πμ", "am");
if (date.contains("μμ")) date = date.replace("μμ", "pm");
}
try {
postDate = format.parse(date);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
if (postsList.get(quotePosition).getPostIndex() != 0) {
if (postDate != null) {
return "[quote author=" + postsList.get(quotePosition).getAuthor()
+ " link=topic=" + ThmmyPage.getTopicId(loadedPageUrl) + ".msg"
+ postsList.get(quotePosition).getPostIndex()
+ "#msg" + postsList.get(quotePosition).getPostIndex()
+ " date=" + postDate.getTime() / 1000 + "]"
+ "\n" + postsList.get(quotePosition).getContent()
+ "\n" + "[/quote]" + "\n\n";
}
}
return null;
}
/**
* Returns a String with a single FontAwesome typeface character corresponding to this file's
* extension.

47
app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAnimations.java

@ -4,68 +4,67 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.text.TextUtils;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
class TopicAnimations {
//--------------------------USER'S INFO VISIBILITY CHANGE ANIMATION METHOD--------------------------
/**
* Method that animates view's visibility changes for user's extra info
*/
static void animateUserExtraInfoVisibility(TextView username, TextView subject,
int expandedColor, int collapsedColor,
final View userExtra) {
//If the view is gone fade it in
if (userExtra.getVisibility() == View.GONE) {
userExtra.clearAnimation();
userExtra.setVisibility(View.VISIBLE);
userExtra.setAlpha(0.0f);
static void animateUserExtraInfoVisibility(final TextView username, final TextView subject,
int expandedColor, final int collapsedColor,
final LinearLayout userExtraInfo) {
//If the view is currently gone it fades it in
if (userExtraInfo.getVisibility() == View.GONE) {
userExtraInfo.clearAnimation();
userExtraInfo.setVisibility(View.VISIBLE);
userExtraInfo.setAlpha(0.0f);
// Start the animation
userExtra.animate()
//Animation start
userExtraInfo.animate()
.alpha(1.0f)
.setDuration(300)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
userExtra.setVisibility(View.VISIBLE);
userExtraInfo.setVisibility(View.VISIBLE);
}
});
//Show full username
//Shows full username
username.setMaxLines(Integer.MAX_VALUE); //As in the android sourcecode
username.setEllipsize(null);
//Show full subject
//Shows full subject
subject.setTextColor(expandedColor);
subject.setMaxLines(Integer.MAX_VALUE); //As in the android sourcecode
subject.setEllipsize(null);
}
//If the view is visible fade it out
//If the view is currently visible then it fades it out
else {
userExtra.clearAnimation();
userExtraInfo.clearAnimation();
// Start the animation
userExtra.animate()
//Animation start
userExtraInfo.animate()
.alpha(0.0f)
.setDuration(300)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
userExtra.setVisibility(View.GONE);
}
});
userExtraInfo.setVisibility(View.GONE);
//Ellipsizes username
username.setMaxLines(1); //As in the android sourcecode
username.setEllipsize(TextUtils.TruncateAt.END);
//Ellipsizes subject
subject.setTextColor(collapsedColor);
subject.setMaxLines(1); //As in the android sourcecode
subject.setEllipsize(TextUtils.TruncateAt.END);
}
});
}
}
//------------------------POST'S INFO VISIBILITY CHANGE ANIMATION METHOD END------------------------
}

3
app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicParser.java

@ -35,7 +35,8 @@ class TopicParser {
private static final int USER_COLOR_GREEN = Color.parseColor("#4CAF50");
private static final int USER_COLOR_BLUE = Color.parseColor("#536DFE");
static final int USER_COLOR_PINK = Color.parseColor("#FF4081");
private static final int USER_COLOR_YELLOW = Color.parseColor("#FFEB3B");
static final int USER_COLOR_YELLOW = Color.parseColor("#FFEB3B");
static final int USER_COLOR_WHITE = Color.WHITE;
/**
* Returns users currently viewing this topic.

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

@ -20,6 +20,7 @@ import android.widget.ImageButton;
import android.widget.Toast;
import com.mikepenz.fontawesome_typeface_library.FontAwesome;
import com.mikepenz.google_material_typeface_library.GoogleMaterial;
import com.mikepenz.iconics.IconicsDrawable;
import com.mikepenz.materialdrawer.AccountHeader;
import com.mikepenz.materialdrawer.AccountHeaderBuilder;
@ -156,27 +157,27 @@ public abstract class BaseActivity extends AppCompatActivity {
//Drawer Icons
homeIcon = new IconicsDrawable(this)
.icon(FontAwesome.Icon.faw_home)
.icon(GoogleMaterial.Icon.gmd_home)
.color(primaryColor);
homeIconSelected = new IconicsDrawable(this)
.icon(FontAwesome.Icon.faw_home)
.icon(GoogleMaterial.Icon.gmd_home)
.color(selectedSecondaryColor);
downloadsIcon = new IconicsDrawable(this)
.icon(FontAwesome.Icon.faw_download)
bookmarksIcon = new IconicsDrawable(this)
.icon(GoogleMaterial.Icon.gmd_bookmark)
.color(primaryColor);
downloadsIconSelected = new IconicsDrawable(this)
.icon(FontAwesome.Icon.faw_download)
bookmarksIconSelected = new IconicsDrawable(this)
.icon(GoogleMaterial.Icon.gmd_bookmark)
.color(selectedSecondaryColor);
bookmarksIcon = new IconicsDrawable(this)
.icon(FontAwesome.Icon.faw_bookmark)
downloadsIcon = new IconicsDrawable(this)
.icon(GoogleMaterial.Icon.gmd_file_download)
.color(primaryColor);
bookmarksIconSelected = new IconicsDrawable(this)
.icon(FontAwesome.Icon.faw_bookmark)
downloadsIconSelected = new IconicsDrawable(this)
.icon(GoogleMaterial.Icon.gmd_file_download)
.color(selectedSecondaryColor);
loginIcon = new IconicsDrawable(this)
@ -188,11 +189,11 @@ public abstract class BaseActivity extends AppCompatActivity {
.color(primaryColor);
aboutIcon = new IconicsDrawable(this)
.icon(FontAwesome.Icon.faw_info_circle)
.icon(GoogleMaterial.Icon.gmd_info)
.color(primaryColor);
aboutIconSelected = new IconicsDrawable(this)
.icon(FontAwesome.Icon.faw_info_circle)
.icon(GoogleMaterial.Icon.gmd_info)
.color(selectedSecondaryColor);
//Drawer Items
@ -358,14 +359,15 @@ public abstract class BaseActivity extends AppCompatActivity {
{
drawer.removeItem(DOWNLOADS_ID);
loginLogoutItem.withName(R.string.login).withIcon(loginIcon); //Swap logout with login
profileDrawerItem.withName(sessionManager.getUsername()).withIcon(new IconicsDrawable(this)
.icon(FontAwesome.Icon.faw_user)
.paddingDp(10)
.color(ContextCompat.getColor(this, R.color.primary_light))
.backgroundColor(ContextCompat.getColor(this, R.color.primary)));
profileDrawerItem.withName(sessionManager.getUsername());
setDefaultAvatar();
} else {
loginLogoutItem.withName(R.string.logout).withIcon(logoutIcon); //Swap login with logout
profileDrawerItem.withName(sessionManager.getUsername()).withIcon(sessionManager.getAvatarLink());
profileDrawerItem.withName(sessionManager.getUsername());
if(sessionManager.hasAvatar())
profileDrawerItem.withIcon(sessionManager.getAvatarLink());
else
setDefaultAvatar();
}
accountHeader.updateProfile(profileDrawerItem);
drawer.updateItem(loginLogoutItem);
@ -373,6 +375,13 @@ public abstract class BaseActivity extends AppCompatActivity {
}
}
private void setDefaultAvatar() {
profileDrawerItem.withIcon(new IconicsDrawable(this)
.icon(FontAwesome.Icon.faw_user)
.paddingDp(10)
.color(ContextCompat.getColor(this, R.color.primary_light))
.backgroundColor(ContextCompat.getColor(this, R.color.primary)));
}
//-------------------------------------------LOGOUT-------------------------------------------------
@ -402,6 +411,11 @@ public abstract class BaseActivity extends AppCompatActivity {
if (mainActivity != null)
mainActivity.updateTabs();
progressDialog.dismiss();
//if (BaseActivity.this instanceof TopicActivity){
Intent intent = new Intent(BaseActivity.this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
//}
}
}
//-----------------------------------------LOGOUT END-----------------------------------------------

26
app/src/main/java/gr/thmmy/mthmmy/services/DownloadService.java

@ -1,11 +1,13 @@
package gr.thmmy.mthmmy.services;
import android.app.DownloadManager;
import android.app.IntentService;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.webkit.MimeTypeMap;
import java.io.File;
import java.io.FileNotFoundException;
@ -107,7 +109,7 @@ public class DownloadService extends IntentService {
Response response = client.newCall(request).execute();
String contentDisposition = response.headers("Content-Disposition").toString(); //check if link provides an attachment
if (contentDisposition.contains("attachment")){
if (contentDisposition.contains("attachment")) {
fileName = contentDisposition.split("\"")[1];
File dirPath = new File(SAVE_DIR);
@ -141,7 +143,7 @@ public class DownloadService extends IntentService {
fileName = file.getName();
Timber.v("Started saving file %s" , fileName);
Timber.v("Started saving file %s", fileName);
sendNotification(downloadId, STARTED, fileName);
sink = Okio.buffer(Okio.sink(file));
@ -149,6 +151,12 @@ public class DownloadService extends IntentService {
sink.flush();
Timber.i("Download OK!");
sendNotification(downloadId, COMPLETED, fileName);
// Register download
DownloadManager mManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
long length = file.length();
mManager.addCompletedDownload(fileName, "edo mporei na mpei ena description", false, getMimeType(file), SAVE_DIR +File.separator+ fileName, length, false);
} else
Timber.e("No attachment in response!");
} catch (FileNotFoundException e) {
@ -203,4 +211,18 @@ public class DownloadService extends IntentService {
}
@NonNull
static String getMimeType(@NonNull File file) {
String type = null;
final String url = file.toString();
final String extension = MimeTypeMap.getFileExtensionFromUrl(url);
if (extension != null) {
type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.toLowerCase());
}
if (type == null) {
type = ""; // fallback type. You might set it to */*
}
return type;
}
}

7
app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareFABBehavior.java

@ -34,8 +34,8 @@ public class ScrollAwareFABBehavior extends CoordinatorLayout.Behavior<FloatingA
final int dxUnconsumed, final int dyUnconsumed, int type) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed,
dyUnconsumed, type);
if ((dyConsumed > 0 || (!target.canScrollVertically(-1) && dyConsumed == 0
&& dyUnconsumed < 40)) && child.getVisibility() == View.VISIBLE) {
if (child.getVisibility() == View.VISIBLE && (dyConsumed > 0
|| (!target.canScrollVertically(-1) && dyConsumed == 0 && dyUnconsumed > 50))) {
child.hide(new FloatingActionButton.OnVisibilityChangedListener() {
@Override
public void onHidden(FloatingActionButton fab) {
@ -43,7 +43,8 @@ public class ScrollAwareFABBehavior extends CoordinatorLayout.Behavior<FloatingA
fab.setVisibility(View.INVISIBLE);
}
});
} else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE) {
} else if (child.getVisibility() == View.INVISIBLE && (dyConsumed < 0
|| (!target.canScrollVertically(-1) && dyConsumed == 0 && dyUnconsumed < -50))) {
child.show();
}
}

7
app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareLinearBehavior.java

@ -37,10 +37,11 @@ public class ScrollAwareLinearBehavior extends CoordinatorLayout.Behavior<View>
int dxUnconsumed, int dyUnconsumed, int type) {
super.onNestedScroll(coordinatorLayout, bottomNavBar, target, dxConsumed, dyConsumed,
dxUnconsumed, dyUnconsumed, type);
if ((dyConsumed > 0 || (!target.canScrollVertically(-1) && dyConsumed == 0
&& dyUnconsumed < 40)) && bottomNavBar.getVisibility() == View.VISIBLE) {
if (bottomNavBar.getVisibility() == View.VISIBLE && (dyConsumed > 0
|| (!target.canScrollVertically(-1) && dyConsumed == 0 && dyUnconsumed > 50))) {
hide(bottomNavBar);
} else if (dyConsumed < 0 && bottomNavBar.getVisibility() != View.VISIBLE) {
} else if (bottomNavBar.getVisibility() == View.INVISIBLE && (dyConsumed < 0
|| (!target.canScrollVertically(-1) && dyConsumed == 0 && dyUnconsumed < -50))) {
show(bottomNavBar);
}
}

12
app/src/main/res/layout-v21/activity_topic_post_row.xml

@ -37,6 +37,7 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:clickable="true"
android:focusable="true"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="16dp">
@ -94,6 +95,7 @@
android:background="@color/card_background"
android:clickable="true"
android:contentDescription="@string/post_quote_button"
android:focusable="true"
android:src="@drawable/ic_format_quote_unchecked" />
</LinearLayout>
@ -102,6 +104,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true"
android:orientation="vertical"
android:paddingLeft="16dp"
android:paddingRight="16dp"
@ -174,8 +177,8 @@
android:layout_height="wrap_content"
android:gravity="start"
android:text=""
android:textColor="@color/card_expand_text_color"
android:textSize="10sp" />
android:textColor="@color/accent"
android:textSize="11sp" />
<TextView
android:id="@+id/post_number"
@ -183,8 +186,8 @@
android:layout_height="wrap_content"
android:gravity="end"
android:text=""
android:textColor="@color/card_expand_text_color"
android:textSize="10sp" />
android:textColor="@color/accent"
android:textSize="11sp" />
</FrameLayout>
@ -212,6 +215,7 @@
android:layout_marginRight="16dp"
android:background="@color/card_background"
android:clickable="true"
android:focusable="true"
android:text="@string/post" />
</FrameLayout>

83
app/src/main/res/layout/activity_about.xml

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_content"
@ -21,8 +20,7 @@
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/ToolbarTheme">
</android.support.v7.widget.Toolbar>
app:popupTheme="@style/ToolbarTheme" />
</android.support.design.widget.AppBarLayout>
@ -30,11 +28,10 @@
android:id="@+id/scrollview"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:background="@color/background">
android:background="@color/primary_light"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -44,19 +41,15 @@
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.eternalpixels.toinfinity.Info">
<pl.droidsonroids.gif.GifImageView
<ImageView
android:id="@+id/logoView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_margin="15dp"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"
android:contentDescription="@string/logo"
android:src="@drawable/logo_animated"
/>
android:layout_alignParentTop="true"
android:src="@mipmap/ic_launcher"
android:contentDescription="@string/logo" />
<TextView
android:id="@+id/version"
@ -65,73 +58,74 @@
android:layout_below="@+id/logoView"
android:layout_centerHorizontal="true"
android:clickable="true"
android:focusable="true"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="@color/accent"
android:textStyle="italic"/>
android:textStyle="italic" />
<TextView
android:id="@+id/open_source_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_below="@+id/version"
android:layout_marginTop="20dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/open_source"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="@color/accent"
android:layout_below="@+id/version"
android:layout_alignParentStart="true"
android:textStyle="bold" />
<TextView
android:id="@+id/open_source_text"
android:layout_below="@+id/open_source_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_below="@+id/open_source_header"
android:autoLink="web"
android:text="@string/open_source_text"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="@color/iron"
android:layout_alignParentStart="true" />
android:textColor="@color/iron" />
<TextView
android:id="@+id/libraries_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_below="@+id/open_source_text"
android:layout_marginTop="20dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/libraries"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="@color/accent"
android:layout_below="@+id/open_source_text"
android:layout_alignParentStart="true"
android:textStyle="bold" />
<TextView
android:id="@+id/libraries_text"
android:layout_below="@+id/libraries_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_below="@+id/libraries_header"
android:text="@string/libraries_text"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="@color/iron"
android:layout_alignParentStart="true" />
android:textColor="@color/iron" />
<TextView
android:layout_below="@+id/libraries_text"
android:onClick="displayApacheLibraries"
android:id="@+id/apache_libs"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/apache_v2_0_libraries"
android:id="@+id/apache_libs"
android:layout_alignParentStart="true"
android:layout_below="@+id/libraries_text"
android:onClick="displayApacheLibraries"
android:text="@string/apache_v2_0_libraries"
android:textColor="@color/accent" />
<TextView
android:id="@+id/mit_libs"
android:onClick="displayMITLibraries"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/the_mit_libraries"
android:layout_below="@+id/apache_libs"
android:layout_alignParentStart="true"
android:layout_below="@+id/apache_libs"
android:onClick="displayMITLibraries"
android:text="@string/the_mit_libraries"
android:textColor="@color/accent" />
@ -139,24 +133,24 @@
android:id="@+id/contact_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_below="@+id/mit_libs"
android:layout_marginTop="20dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/contact"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="@color/accent"
android:layout_below="@+id/mit_libs"
android:layout_alignParentStart="true"
android:textStyle="bold" />
<TextView
android:id="@+id/contact_text"
android:layout_below="@+id/contact_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_below="@+id/contact_header"
android:autoLink="email|web"
android:text="@string/contact_text"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="@color/iron"
android:layout_alignParentStart="true" />
android:textColor="@color/iron" />
</RelativeLayout>
</ScrollView>
@ -175,7 +169,6 @@
android:layout_gravity="center"
android:contentDescription="@string/trollPic"
android:foregroundGravity="center"
android:src="@drawable/fun"
/>
android:src="@drawable/fun" />
</FrameLayout>
</android.support.design.widget.CoordinatorLayout>

14
app/src/main/res/layout/activity_board_sub_board.xml

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/card_background"
@ -33,7 +32,7 @@
android:paddingBottom="2dp"
android:text="@string/child_board_title"
android:textColor="@color/accent"
android:textSize="22sp"/>
android:textSize="22sp" />
<ImageButton
android:id="@+id/child_board_expand_collapse_button"
@ -41,7 +40,7 @@
android:layout_height="wrap_content"
android:background="@null"
android:contentDescription="@string/child_board_button"
android:src="@drawable/ic_arrow_drop_down"/>
android:src="@drawable/ic_arrow_drop_down" />
</LinearLayout>
<LinearLayout
@ -61,7 +60,7 @@
android:text="@string/child_board_mods"
android:textColor="@color/secondary_text"
android:textSize="12sp"
android:textStyle="italic"/>
android:textStyle="italic" />
<TextView
android:id="@+id/child_board_stats"
@ -71,7 +70,7 @@
android:layout_marginTop="1dp"
android:text="@string/child_board_stats"
android:textColor="@color/secondary_text"
android:textSize="12sp"/>
android:textSize="12sp" />
<TextView
android:id="@+id/child_board_last_post"
@ -80,9 +79,10 @@
android:layout_marginBottom="1dp"
android:layout_marginTop="1dp"
android:clickable="true"
android:focusable="true"
android:text="@string/child_board_last_post"
android:textColor="@color/primary_text"
android:textSize="12sp"/>
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>

14
app/src/main/res/layout/activity_board_topic.xml

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/topic_row_linear"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -27,7 +26,7 @@
android:paddingBottom="2dp"
android:text="@string/topic_subject"
android:textColor="@color/primary_text"
android:textSize="18sp"/>
android:textSize="18sp" />
<ImageButton
android:id="@+id/topic_expand_collapse_button"
@ -35,7 +34,7 @@
android:layout_height="wrap_content"
android:background="@null"
android:contentDescription="@string/child_board_button"
android:src="@drawable/ic_arrow_drop_down"/>
android:src="@drawable/ic_arrow_drop_down" />
</LinearLayout>
<LinearLayout
@ -54,7 +53,7 @@
android:layout_marginTop="1dp"
android:text="@string/topic_started_by"
android:textColor="@color/secondary_text"
android:textSize="12sp"/>
android:textSize="12sp" />
<TextView
android:id="@+id/topic_stats"
@ -64,7 +63,7 @@
android:layout_marginTop="1dp"
android:text="@string/topic_stats"
android:textColor="@color/secondary_text"
android:textSize="12sp"/>
android:textSize="12sp" />
<TextView
android:id="@+id/topic_last_post"
@ -73,8 +72,9 @@
android:layout_marginBottom="1dp"
android:layout_marginTop="1dp"
android:clickable="true"
android:focusable="true"
android:text="@string/topic_last_post"
android:textColor="@color/primary_text"
android:textSize="12sp"/>
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>

12
app/src/main/res/layout/activity_topic_post_row.xml

@ -37,6 +37,7 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:clickable="true"
android:focusable="true"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="16dp">
@ -93,6 +94,7 @@
android:background="@color/card_background"
android:clickable="true"
android:contentDescription="@string/post_quote_button"
android:focusable="true"
android:src="@drawable/ic_format_quote_unchecked" />
</LinearLayout>
@ -101,6 +103,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true"
android:orientation="vertical"
android:paddingLeft="16dp"
android:paddingRight="16dp"
@ -173,8 +176,8 @@
android:layout_height="wrap_content"
android:gravity="start"
android:text=""
android:textColor="@color/card_expand_text_color"
android:textSize="10sp" />
android:textColor="@color/accent"
android:textSize="11sp" />
<TextView
android:id="@+id/post_number"
@ -182,8 +185,8 @@
android:layout_height="wrap_content"
android:gravity="end"
android:text=""
android:textColor="@color/card_expand_text_color"
android:textSize="10sp" />
android:textColor="@color/accent"
android:textSize="11sp" />
</FrameLayout>
@ -211,6 +214,7 @@
android:layout_marginRight="16dp"
android:background="@color/card_background"
android:clickable="true"
android:focusable="true"
android:text="@string/post" />
</FrameLayout>

17
app/src/main/res/layout/activity_topic_quick_reply_row.xml

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -8,8 +7,7 @@
android:paddingEnd="4dp"
android:paddingStart="4dp">
<android.support.v7.widget.CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
<android.support.v7.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"
@ -32,6 +30,7 @@
android:layout_height="0dp"
android:layout_weight="1"
android:clickable="true"
android:focusable="true"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="16dp">
@ -53,7 +52,7 @@
android:contentDescription="@string/post_thumbnail"
android:maxHeight="@dimen/thumbnail_size"
android:maxWidth="@dimen/thumbnail_size"
android:src="@drawable/ic_default_user_thumbnail"/>
android:src="@drawable/ic_default_user_thumbnail" />
</FrameLayout>
<TextView
@ -66,7 +65,7 @@
android:maxLines="1"
android:text="@string/post_author"
android:textColor="@color/primary_text"
android:textStyle="bold"/>
android:textStyle="bold" />
<EditText
android:id="@+id/quick_reply_subject"
@ -78,7 +77,7 @@
android:inputType="textMultiLine"
android:maxLength="80"
android:textSize="10sp"
tools:ignore="SmallSp"/>
tools:ignore="SmallSp" />
</RelativeLayout>
<LinearLayout
@ -99,7 +98,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/quick_reply"
android:inputType="textMultiLine"/>
android:inputType="textMultiLine" />
</android.support.design.widget.TextInputLayout>
<android.support.v7.widget.AppCompatImageButton
@ -111,7 +110,7 @@
android:layout_marginEnd="5dp"
android:background="@color/card_background"
android:contentDescription="@string/quick_reply_submit"
android:src="@drawable/ic_send"/>
android:src="@drawable/ic_send" />
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>

13
app/src/main/res/layout/fragment_forum_board_row.xml

@ -2,19 +2,20 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@color/primary_lighter">
android:background="@color/primary_lighter"
android:orientation="vertical">
<TextView
android:id="@+id/board"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?attr/textAppearanceListItem"
android:textColor="@color/accent"
android:textSize="16sp"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:gravity="center_horizontal"
android:padding="8dp"/>
android:padding="8dp"
android:textAppearance="?attr/textAppearanceListItem"
android:textColor="@color/accent"
android:textSize="16sp" />
</LinearLayout>

14
app/src/main/res/layout/fragment_latest_posts_empty_message.xml

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAlignment="center"
android:textSize="@dimen/big_text"
android:layout_marginTop="8dp"
android:textColor="@color/accent"
android:text="@string/latest_posts_empty_message"/>
</LinearLayout>

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

@ -38,7 +38,7 @@
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_below="@+id/spacer"
android:textColor="@color/secondary_text"/>
android:textColor="@color/accent"/>
<TextView
android:id="@+id/lastUser"

11
app/src/main/res/layout/fragment_unread_mark_read_row.xml

@ -2,15 +2,16 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingEnd="10dp"
android:paddingStart="10dp"
android:paddingTop="7dp">
android:orientation="vertical">
<TextView
android:id="@+id/mark_read"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAlignment="textEnd"
android:paddingEnd="10dp"
android:paddingStart="10dp"
android:paddingTop="7dp"
android:paddingBottom="7dp"
android:textAlignment="center"
android:textColor="@color/accent" />
</LinearLayout>

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

@ -38,7 +38,7 @@
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_below="@+id/spacer"
android:textColor="@color/secondary_text"/>
android:textColor="@color/accent"/>
<TextView
android:id="@+id/lastUser"

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

@ -18,9 +18,6 @@
<string name="btn_login">LOGIN</string>
<string name="btn_continue_as_guest">Don\'t have an account? Continue as guest!</string>
<!--Main Activity-->
<string name="byUser">by %1$s</string>
<!--Board Activity-->
<string name="child_board_title">Child Boards</string>
<string name="child_board_button">Exp/Coll</string>
@ -51,6 +48,7 @@
<!--Profile Activity-->
<string name="username">Username</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">Statistics</string>
<string name="posting_activity_by_time_title">Posting Activity</string>

3
build.gradle

@ -4,9 +4,10 @@ buildscript {
repositories {
jcenter()
maven { url "https://jitpack.io" }
maven { url "https://maven.google.com" }
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
classpath 'com.android.tools.build:gradle:3.0.0'
classpath 'com.google.gms:google-services:3.1.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files

4
gradle/wrapper/gradle-wrapper.properties

@ -1,6 +1,6 @@
#Wed Mar 08 11:25:21 EET 2017
#Thu Oct 26 11:21:06 EEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip

Loading…
Cancel
Save