Browse Source

Merge branch 'develop' into crashlytics_fix

pull/46/head
oogee 6 years ago
committed by GitHub
parent
commit
0f83830638
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      README.md
  2. 8
      app/build.gradle
  3. 5
      app/src/main/AndroidManifest.xml
  4. 4
      app/src/main/java/gr/thmmy/mthmmy/activities/LoginActivity.java
  5. 4
      app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardActivity.java
  6. 1
      app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadFragment.java
  7. 1
      app/src/main/java/gr/thmmy/mthmmy/activities/settings/SettingsActivity.java
  8. 57
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java
  9. 86
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java
  10. 6
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicParser.java
  11. 1
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/DeleteTask.java
  12. 1
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/EditTask.java
  13. 1
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForEditTask.java
  14. 1
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/ReplyTask.java
  15. 7
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/TopicTask.java
  16. 3
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/TopicTaskResult.java
  17. 293
      app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadActivity.java
  18. 501
      app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadFieldsBuilderActivity.java
  19. 34
      app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsHelper.java
  20. 3
      app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java
  21. 47
      app/src/main/java/gr/thmmy/mthmmy/base/BaseApplication.java
  22. 55
      app/src/main/java/gr/thmmy/mthmmy/editorview/AutoFitGridLayout.java
  23. 283
      app/src/main/java/gr/thmmy/mthmmy/editorview/EditorView.java
  24. 241
      app/src/main/java/gr/thmmy/mthmmy/editorview/EmojiKeyboard.java
  25. 64
      app/src/main/java/gr/thmmy/mthmmy/editorview/EmojiKeyboardAdapter.java
  26. 1
      app/src/main/java/gr/thmmy/mthmmy/services/DownloadHelper.java
  27. 42
      app/src/main/java/gr/thmmy/mthmmy/utils/CrashReporter.java
  28. 17
      app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java
  29. 240
      app/src/main/res/drawable/emoji_a_eatpaper.xml
  30. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f0.png
  31. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f1.png
  32. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f10.png
  33. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f11.png
  34. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f12.png
  35. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f13.png
  36. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f14.png
  37. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f15.png
  38. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f16.png
  39. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f17.png
  40. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f18.png
  41. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f19.png
  42. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f2.png
  43. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f20.png
  44. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f21.png
  45. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f22.png
  46. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f23.png
  47. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f24.png
  48. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f25.png
  49. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f26.png
  50. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f27.png
  51. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f28.png
  52. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f29.png
  53. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f3.png
  54. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f30.png
  55. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f31.png
  56. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f32.png
  57. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f33.png
  58. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f34.png
  59. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f35.png
  60. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f36.png
  61. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f37.png
  62. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f38.png
  63. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f39.png
  64. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f4.png
  65. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f40.png
  66. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f41.png
  67. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f42.png
  68. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f43.png
  69. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f44.png
  70. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f45.png
  71. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f46.png
  72. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f47.png
  73. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f48.png
  74. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f49.png
  75. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f5.png
  76. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f50.png
  77. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f51.png
  78. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f52.png
  79. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f53.png
  80. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f54.png
  81. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f55.png
  82. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f56.png
  83. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f57.png
  84. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f58.png
  85. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f59.png
  86. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f6.png
  87. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f60.png
  88. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f61.png
  89. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f62.png
  90. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f63.png
  91. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f64.png
  92. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f65.png
  93. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f66.png
  94. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f67.png
  95. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f68.png
  96. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f69.png
  97. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f7.png
  98. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f70.png
  99. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f71.png
  100. BIN
      app/src/main/res/drawable/emoji_a_eatpaper_f72.png

2
README.md

@ -22,7 +22,7 @@ Please refer to [CONTRIBUTING.md](/CONTRIBUTING.md) for details.
## Contact
Do not hesitate to contact us for any matter, either by sending an email to `thmmynolife@gmail.com`, or by joining our [Discord server][discord-server].
Do not hesitate to contact us for any matter, either by sending an email to [thmmynolife@gmail.com](mailto:thmmynolife@gmail.com), or by joining our [Discord server][discord-server].
**Legal attribution: Google Play and the Google Play logo are trademarks of Google Inc.*

8
app/build.gradle

@ -43,9 +43,9 @@ dependencies {
implementation 'com.android.support:support-v4:27.1.1'
implementation 'com.android.support:cardview-v7:27.1.1'
implementation 'com.android.support:recyclerview-v7:27.1.1'
implementation 'com.google.firebase:firebase-core:16.0.1'
implementation 'com.google.firebase:firebase-messaging:17.1.0'
implementation 'com.crashlytics.sdk.android:crashlytics:2.9.4'
implementation 'com.google.firebase:firebase-core:16.0.3'
implementation 'com.google.firebase:firebase-messaging:17.3.0'
implementation 'com.crashlytics.sdk.android:crashlytics:2.9.5'
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
implementation 'com.squareup.picasso:picasso:2.5.2'
implementation 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
@ -57,7 +57,7 @@ dependencies {
}
implementation 'com.mikepenz:fontawesome-typeface:4.7.0.0@aar'
implementation 'com.mikepenz:google-material-typeface:3.0.1.2.original@aar'
implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.12'
implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.15'
implementation 'com.bignerdranch.android:expandablerecyclerview:3.0.0-RC1'//TODO: deprecated!
implementation 'me.zhanghai.android.materialprogressbar:library:1.4.2'
implementation 'com.jakewharton.timber:timber:4.7.0'

5
app/src/main/AndroidManifest.xml

@ -40,15 +40,12 @@
<data
android:host="www.thmmy.gr"
android:scheme="http" />
<data
android:host="www.thmmy.gr"
android:scheme="https" />
<data
android:host="thmmy.gr"
android:scheme="https" />
<data android:host="thmmy.gr" />
</intent-filter>
</activity>
@ -124,7 +121,7 @@
<activity
android:name=".activities.settings.SettingsActivity"
android:parentActivityName=".activities.main.MainActivity"
android:theme="@style/AppTheme.NoActionBar">
android:theme="@style/AppTheme.PreferenceTheme">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.main.MainActivity" />

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

@ -12,9 +12,12 @@ import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.Toast;
import com.google.firebase.analytics.FirebaseAnalytics;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.main.MainActivity;
import gr.thmmy.mthmmy.base.BaseActivity;
import gr.thmmy.mthmmy.base.BaseApplication;
import timber.log.Timber;
import static gr.thmmy.mthmmy.session.SessionManager.BANNED_USER;
@ -163,6 +166,7 @@ public class LoginActivity extends BaseActivity {
Toast.makeText(getApplicationContext(),
"Welcome, " + sessionManager.getUsername() + "!", Toast.LENGTH_LONG)
.show();
BaseApplication.getInstance().logFirebaseAnalyticsEvent(FirebaseAnalytics.Event.LOGIN, null);
//Go to main
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
startActivity(intent);

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

@ -286,9 +286,11 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo
if (pLastPost.contains("by")) {
pLastPost = pLastPost.substring(0, pLastPost.indexOf("by")) +
"\n" + pLastPost.substring(pLastPost.indexOf("by"));
} else {
} else if (pLastPost.contains("από")) {
pLastPost = pLastPost.substring(0, pLastPost.indexOf("από")) +
"\n" + pLastPost.substring(pLastPost.indexOf("από"));
} else {
Timber.wtf("Board parsing about to fail. pLastPost came with: %s", pLastPost);
}
pLastPostUrl = topicColumns.last().select("a:has(img)").first().attr("href");
parsedTopics.add(new Topic(pTopicUrl, pSubject, pStartedBy, pLastPost, pLastPostUrl,

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

@ -6,7 +6,6 @@ import android.support.annotation.NonNull;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

1
app/src/main/java/gr/thmmy/mthmmy/activities/settings/SettingsActivity.java

@ -1,7 +1,6 @@
package gr.thmmy.mthmmy.activities.settings;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import gr.thmmy.mthmmy.R;

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

@ -23,6 +23,7 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.widget.ImageButton;
import android.widget.LinearLayout;
@ -44,6 +45,7 @@ import gr.thmmy.mthmmy.model.Bookmark;
import gr.thmmy.mthmmy.model.Post;
import gr.thmmy.mthmmy.model.ThmmyPage;
import gr.thmmy.mthmmy.utils.CustomLinearLayoutManager;
import gr.thmmy.mthmmy.editorview.EmojiKeyboard;
import gr.thmmy.mthmmy.utils.HTMLUtils;
import gr.thmmy.mthmmy.utils.parsing.ParseHelpers;
import gr.thmmy.mthmmy.viewmodel.TopicViewModel;
@ -59,7 +61,8 @@ import static gr.thmmy.mthmmy.services.NotificationService.NEW_POST_TAG;
* key {@link #BUNDLE_TOPIC_TITLE} for faster title rendering.
*/
@SuppressWarnings("unchecked")
public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFocusChangeListener {
public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFocusChangeListener,
EmojiKeyboard.EmojiKeyboardOwner{
//Activity's variables
/**
* The key to use when putting topic's url String to {@link TopicActivity}'s Bundle.
@ -112,6 +115,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo
private ImageButton lastPage;
private Snackbar snackbar;
private TopicViewModel viewModel;
private EmojiKeyboard emojiKeyboard;
//Fix for vector drawables on android <21
static {
@ -159,6 +163,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo
createDrawer();
progressBar = findViewById(R.id.progressBar);
emojiKeyboard = findViewById(R.id.emoji_keyboard);
postsList = new ArrayList<>();
@ -196,6 +201,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo
paginationEnabled(false);
Timber.i("Starting initial topic load");
viewModel.loadUrl(topicPageUrl);
}
@ -254,6 +260,19 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo
if (drawer.isDrawerOpen()) {
drawer.closeDrawer();
return;
} else if (emojiKeyboard.getVisibility() == View.VISIBLE) {
emojiKeyboard.setVisibility(View.GONE);
if (viewModel.isEditingPost()) {
TopicAdapter.EditMessageViewHolder vh = (TopicAdapter.EditMessageViewHolder)
recyclerView.findViewHolderForAdapterPosition(viewModel.getPostBeingEditedPosition());
vh.editEditor.updateEmojiKeyboardVisibility();
}
if (viewModel.isWritingReply()) {
TopicAdapter.QuickReplyViewHolder vh = (TopicAdapter.QuickReplyViewHolder)
recyclerView.findViewHolderForAdapterPosition(viewModel.postCount());
vh.replyEditor.updateEmojiKeyboardVisibility();
}
return;
} else if (viewModel.isWritingReply()) {
postsList.remove(postsList.size() - 1);
topicAdapter.notifyItemRemoved(postsList.size());
@ -293,6 +312,21 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo
recyclerView.scrollToPosition(position);
}
@Override
public void setEmojiKeyboardVisible(boolean visible) {
emojiKeyboard.setVisibility(visible ? View.VISIBLE : View.GONE);
}
@Override
public boolean isEmojiKeyboardVisible() {
return emojiKeyboard.getVisibility() == View.VISIBLE;
}
@Override
public void setEmojiKeyboardInputConnection(InputConnection ic) {
emojiKeyboard.setInputConnection(ic);
}
//--------------------------------------BOTTOM NAV BAR METHODS----------------------------------
/**
@ -468,10 +502,13 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo
public void onDeleteTaskFinished(boolean result) {
progressBar.setVisibility(ProgressBar.GONE);
if (result)
if (result) {
Timber.i("Post deleted successfully");
viewModel.reloadPage();
else
} else {
Timber.w("Failed to delete post");
Toast.makeText(getBaseContext(), "Delete failed!", Toast.LENGTH_SHORT).show();
}
}
});
viewModel.setReplyFinishListener(new ReplyTask.ReplyTaskCallbacks() {
@ -491,15 +528,18 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo
progressBar.setVisibility(ProgressBar.GONE);
if (success) {
Timber.i("Post reply successful");
replyFAB.show();
bottomNavBar.setVisibility(View.VISIBLE);
viewModel.setWritingReply(false);
if ((postsList.get(postsList.size() - 1).getPostNumber() + 1) % 15 == 0) {
Timber.i("Reply was posted in new page. Switching to last page.");
viewModel.loadUrl(ParseHelpers.getBaseURL(viewModel.getTopicUrl()) + "." + 2147483647);
} else {
viewModel.reloadPage();
}
} else {
Timber.w("Post reply unsuccessful");
Toast.makeText(getBaseContext(), "Post failed!", Toast.LENGTH_SHORT).show();
recyclerView.getChildAt(postsList.size() - 1).setAlpha(1);
recyclerView.getChildAt(postsList.size() - 1).setEnabled(true);
@ -534,6 +574,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo
progressBar.setVisibility(ProgressBar.GONE);
if (result) {
Timber.i("Post edit successful");
postsList.get(position).setPostType(Post.TYPE_POST);
topicAdapter.notifyItemChanged(position);
replyFAB.show();
@ -541,6 +582,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo
viewModel.setEditingPost(false);
viewModel.reloadPage();
} else {
Timber.i("Post edit unsuccessful");
Toast.makeText(getBaseContext(), "Edit failed!", Toast.LENGTH_SHORT).show();
recyclerView.getChildAt(viewModel.getPostBeingEditedPosition()).setAlpha(1);
recyclerView.getChildAt(viewModel.getPostBeingEditedPosition()).setEnabled(true);
@ -599,9 +641,11 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo
progressBar.setVisibility(ProgressBar.GONE);
switch (resultCode) {
case SUCCESS:
Timber.i("Successfully loaded topic with URL %s", viewModel.getTopicUrl());
paginationEnabled(true);
break;
case NETWORK_ERROR:
Timber.w("Network error on loaded page");
if (viewModel.getPostsList().getValue() == null) {
// no page has been loaded yet. Give user the ability to refresh
recyclerView.setVisibility(View.GONE);
@ -623,6 +667,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo
}
break;
case UNAUTHORIZED:
Timber.w("Requested topic was unauthorized");
recyclerView.setVisibility(View.GONE);
TextView errorTextview = findViewById(R.id.error_textview);
errorTextview.setText(getString(R.string.unauthorized_topic_error));
@ -630,7 +675,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo
break;
default:
//Parse failed - should never happen
Timber.d("Parse failed!"); //TODO report ParseException!!!
Timber.wtf("Parse failed!"); //TODO report ParseException!!!
Toast.makeText(getBaseContext(), "Fatal Error", Toast.LENGTH_SHORT).show();
finish();
break;
@ -639,6 +684,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo
viewModel.getPrepareForReplyResult().observe(this, prepareForReplyResult -> {
progressBar.setVisibility(ProgressBar.GONE);
if (prepareForReplyResult != null && prepareForReplyResult.isSuccessful()) {
Timber.i("Prepare for reply successful");
//prepare for a reply
viewModel.setWritingReply(true);
postsList.add(Post.newQuickReply());
@ -647,12 +693,14 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo
replyFAB.hide();
bottomNavBar.setVisibility(View.GONE);
} else {
Timber.i("Prepare for reply unsuccessful");
Snackbar.make(findViewById(R.id.main_content), getString(R.string.generic_network_error), Snackbar.LENGTH_SHORT).show();
}
});
viewModel.getPrepareForEditResult().observe(this, result -> {
progressBar.setVisibility(ProgressBar.GONE);
if (result != null && result.isSuccessful()) {
Timber.i("Prepare for edit successful");
viewModel.setEditingPost(true);
postsList.get(result.getPosition()).setPostType(Post.TYPE_EDIT);
topicAdapter.notifyItemChanged(result.getPosition());
@ -660,6 +708,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo
replyFAB.hide();
bottomNavBar.setVisibility(View.GONE);
} else {
Timber.i("Prepare for edit unsuccessful");
Snackbar.make(findViewById(R.id.main_content), getString(R.string.generic_network_error), Snackbar.LENGTH_SHORT).show();
}
});

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

@ -15,12 +15,12 @@ import android.support.annotation.NonNull;
import android.support.v4.content.res.ResourcesCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.content.res.AppCompatResources;
import android.support.v7.widget.AppCompatImageButton;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
@ -46,6 +46,8 @@ import gr.thmmy.mthmmy.model.Post;
import gr.thmmy.mthmmy.model.ThmmyFile;
import gr.thmmy.mthmmy.model.ThmmyPage;
import gr.thmmy.mthmmy.utils.CircleTransform;
import gr.thmmy.mthmmy.editorview.EditorView;
import gr.thmmy.mthmmy.editorview.EmojiKeyboard;
import gr.thmmy.mthmmy.utils.parsing.ParseHelpers;
import gr.thmmy.mthmmy.viewmodel.TopicViewModel;
import timber.log.Timber;
@ -71,6 +73,7 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static int THUMBNAIL_SIZE;
private final Context context;
private final OnPostFocusChangeListener postFocusListener;
private final EmojiKeyboard.EmojiKeyboardOwner emojiKeyboardOwner;
private final List<Post> postsList;
private TopicViewModel viewModel;
@ -82,6 +85,7 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
this.context = context;
this.postsList = postsList;
this.postFocusListener = context;
this.emojiKeyboardOwner = context;
viewModel = ViewModelProviders.of(context).get(TopicViewModel.class);
@ -103,9 +107,8 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
} else if (viewType == Post.TYPE_QUICK_REPLY) {
View view = LayoutInflater.from(parent.getContext()).
inflate(R.layout.activity_topic_quick_reply_row, parent, false);
view.findViewById(R.id.quick_reply_submit).setEnabled(true);
final EditText quickReplyText = view.findViewById(R.id.quick_reply_text);
final EditText quickReplyText = ((EditorView) view.findViewById(R.id.reply_editorview)).getEditText();
quickReplyText.setFocusableInTouchMode(true);
quickReplyText.setOnFocusChangeListener((v, hasFocus) -> quickReplyText.post(() -> {
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
@ -117,9 +120,8 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
} else if (viewType == Post.TYPE_EDIT) {
View view = LayoutInflater.from(parent.getContext()).
inflate(R.layout.activity_topic_edit_row, parent, false);
view.findViewById(R.id.edit_message_submit).setEnabled(true);
final EditText editPostEdittext = view.findViewById(R.id.edit_message_text);
final EditText editPostEdittext = ((EditorView) view.findViewById(R.id.edit_editorview)).getEditText();
editPostEdittext.setFocusableInTouchMode(true);
editPostEdittext.setOnFocusChangeListener((v, hasFocus) -> editPostEdittext.post(() -> {
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
@ -428,14 +430,14 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
if (viewModel.getToQuoteList().contains(currentPost.getPostIndex()))
holder.quoteToggle.setImageResource(R.drawable.ic_format_quote_checked_accent_24dp);
else
holder.quoteToggle.setImageResource(R.drawable.ic_format_quote_unchecked_grey_24dp);
holder.quoteToggle.setImageResource(R.drawable.ic_format_quote_unchecked_24dp);
//Sets graphics behavior
holder.quoteToggle.setOnClickListener(view -> {
viewModel.postIndexToggle(currentPost.getPostIndex());
if (viewModel.getToQuoteList().contains(currentPost.getPostIndex()))
holder.quoteToggle.setImageResource(R.drawable.ic_format_quote_checked_accent_24dp);
else
holder.quoteToggle.setImageResource(R.drawable.ic_format_quote_unchecked_grey_24dp);
holder.quoteToggle.setImageResource(R.drawable.ic_format_quote_unchecked_24dp);
});
}
} else if (currentHolder instanceof QuickReplyViewHolder) {
@ -455,24 +457,36 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
holder.username.setText(getSessionManager().getUsername());
holder.quickReplySubject.setText("Re: " + viewModel.getTopicTitle().getValue());
holder.quickReply.setText(viewModel.getBuildedQuotes());
holder.replyEditor.setEmojiKeyboardOwner(emojiKeyboardOwner);
InputConnection ic = holder.replyEditor.getInputConnection();
emojiKeyboardOwner.setEmojiKeyboardInputConnection(ic);
holder.replyEditor.updateEmojiKeyboardVisibility();
holder.replyEditor.getEditText().setOnFocusChangeListener((v, hasFocus) -> {
InputConnection ic12 = holder.replyEditor.getInputConnection();
emojiKeyboardOwner.setEmojiKeyboardInputConnection(ic12);
holder.replyEditor.updateEmojiKeyboardVisibility();
});
holder.submitButton.setOnClickListener(view -> {
holder.replyEditor.setText(viewModel.getBuildedQuotes());
holder.replyEditor.setOnSubmitListener(view -> {
if (holder.quickReplySubject.getText().toString().isEmpty()) return;
if (holder.quickReply.getText().toString().isEmpty()) return;
if (holder.replyEditor.getText().toString().isEmpty()) {
holder.replyEditor.setError("Required");
return;
}
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
holder.itemView.setAlpha(0.5f);
holder.itemView.setEnabled(false);
emojiKeyboardOwner.setEmojiKeyboardVisible(false);
viewModel.postReply(context, holder.quickReplySubject.getText().toString(),
holder.quickReply.getText().toString());
holder.replyEditor.getText().toString());
});
holder.replyEditor.setOnClickListener(view -> holder.replyEditor.setError(null));
if (backPressHidden) {
holder.quickReply.requestFocus();
holder.replyEditor.requestFocus();
backPressHidden = false;
}
} else if (currentHolder instanceof EditMessageViewHolder) {
@ -490,23 +504,37 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
.transform(new CircleTransform())
.into(holder.thumbnail);
holder.username.setText(getSessionManager().getUsername());
holder.editSubject.setText(postsList.get(position).getSubject());
holder.editMessage.setText(viewModel.getPostBeingEditedText());
holder.submitButton.setOnClickListener(view -> {
holder.editEditor.setEmojiKeyboardOwner(emojiKeyboardOwner);
InputConnection ic = holder.editEditor.getInputConnection();
emojiKeyboardOwner.setEmojiKeyboardInputConnection(ic);
holder.editEditor.updateEmojiKeyboardVisibility();
holder.editEditor.setText(viewModel.getPostBeingEditedText());
holder.editEditor.getEditText().setOnFocusChangeListener((v, hasFocus) -> {
if (hasFocus) {
InputConnection ic1 = holder.editEditor.getInputConnection();
emojiKeyboardOwner.setEmojiKeyboardInputConnection(ic1);
holder.editEditor.updateEmojiKeyboardVisibility();
}
});
holder.editEditor.setOnSubmitListener(view -> {
if (holder.editSubject.getText().toString().isEmpty()) return;
if (holder.editMessage.getText().toString().isEmpty()) return;
if (holder.editEditor.getText().toString().isEmpty()) {
holder.editEditor.setError("Required");
return;
}
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
holder.itemView.setAlpha(0.5f);
holder.itemView.setEnabled(false);
emojiKeyboardOwner.setEmojiKeyboardVisible(false);
viewModel.editPost(position, holder.editSubject.getText().toString(), holder.editMessage.getText().toString());
viewModel.editPost(position, holder.editSubject.getText().toString(), holder.editEditor.getText().toString());
});
if (backPressHidden) {
holder.editMessage.requestFocus();
holder.editEditor.requestFocus();
backPressHidden = false;
}
}
@ -572,36 +600,34 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
/**
* Custom {@link RecyclerView.ViewHolder} implementation
*/
private static class QuickReplyViewHolder extends RecyclerView.ViewHolder {
static class QuickReplyViewHolder extends RecyclerView.ViewHolder {
final ImageView thumbnail;
final TextView username;
final EditText quickReply, quickReplySubject;
final AppCompatImageButton submitButton;
final EditText quickReplySubject;
final EditorView replyEditor;
QuickReplyViewHolder(View quickReply) {
super(quickReply);
thumbnail = quickReply.findViewById(R.id.thumbnail);
username = quickReply.findViewById(R.id.username);
this.quickReply = quickReply.findViewById(R.id.quick_reply_text);
quickReplySubject = quickReply.findViewById(R.id.quick_reply_subject);
submitButton = quickReply.findViewById(R.id.quick_reply_submit);
replyEditor = quickReply.findViewById(R.id.reply_editorview);
}
}
private static class EditMessageViewHolder extends RecyclerView.ViewHolder {
static class EditMessageViewHolder extends RecyclerView.ViewHolder {
final ImageView thumbnail;
final TextView username;
final EditText editMessage, editSubject;
final AppCompatImageButton submitButton;
final EditText editSubject;
final EditorView editEditor;
EditMessageViewHolder(View editView) {
super(editView);
thumbnail = editView.findViewById(R.id.thumbnail);
username = editView.findViewById(R.id.username);
editMessage = editView.findViewById(R.id.edit_message_text);
editSubject = editView.findViewById(R.id.edit_message_subject);
submitButton = editView.findViewById(R.id.edit_message_submit);
editEditor = editView.findViewById(R.id.edit_editorview);
}
}

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

@ -1,9 +1,7 @@
package gr.thmmy.mthmmy.activities.topic;
import android.graphics.Color;
import android.util.Log;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
@ -249,6 +247,10 @@ public class TopicParser {
p_deletePostURL = postDelete.attr("href");
}
Element postEdit = thisRow.select("a:has(img[alt='Αλλαγή'])").first();
if (postEdit != null)
p_editPostURL = postEdit.attr("href");
//Finds post's submit date
Element postDate = thisRow.select("div.smalltext:matches(στις:)").first();
p_postDate = postDate.text();

1
app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/DeleteTask.java

@ -38,6 +38,7 @@ public class DeleteTask extends AsyncTask<String, Void, Boolean> {
//Response response = client.newCall(delete).execute();
switch (Posting.replyStatus(response)) {
case SUCCESSFUL:
BaseApplication.getInstance().logFirebaseAnalyticsEvent("post_deletion", null);
return true;
default:
Timber.e("Something went wrong. Request string: %s", delete.toString());

1
app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/EditTask.java

@ -51,6 +51,7 @@ public class EditTask extends AsyncTask<String, Void, Boolean> {
Response response = client.newCall(post).execute();
switch (replyStatus(response)) {
case SUCCESSFUL:
BaseApplication.getInstance().logFirebaseAnalyticsEvent("post_editing", null);
return true;
case NEW_REPLY_WHILE_POSTING:
//TODO this...

1
app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForEditTask.java

@ -9,7 +9,6 @@ import org.jsoup.select.Selector;
import java.io.IOException;
import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForEditResult;
import gr.thmmy.mthmmy.base.BaseApplication;
import okhttp3.OkHttpClient;
import okhttp3.Request;

1
app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/ReplyTask.java

@ -54,6 +54,7 @@ public class ReplyTask extends AsyncTask<String, Void, Boolean> {
Response response = client.newCall(post).execute();
switch (replyStatus(response)) {
case SUCCESSFUL:
BaseApplication.getInstance().logFirebaseAnalyticsEvent("post_creation", null);
return true;
case NEW_REPLY_WHILE_POSTING:
//TODO this...

7
app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/TopicTask.java

@ -1,7 +1,6 @@
package gr.thmmy.mthmmy.activities.topic.tasks;
import android.os.AsyncTask;
import android.util.SparseArray;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
@ -9,13 +8,11 @@ import org.jsoup.nodes.Element;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Objects;
import gr.thmmy.mthmmy.activities.topic.TopicParser;
import gr.thmmy.mthmmy.base.BaseApplication;
import gr.thmmy.mthmmy.model.Post;
import gr.thmmy.mthmmy.model.ThmmyPage;
import gr.thmmy.mthmmy.utils.parsing.ParseException;
import gr.thmmy.mthmmy.utils.parsing.ParseHelpers;
import okhttp3.Request;
import okhttp3.Response;
@ -110,7 +107,6 @@ public class TopicTask extends AsyncTask<String, Void, TopicTaskResult> {
return new TopicTaskResult(ResultCode.SUCCESS, topicTitle, replyPageUrl, newPostsList, loadedPageTopicId,
currentPageIndex, pageCount, focusedPostIndex, topicTreeAndMods, topicViewers);
} catch (IOException e) {
Timber.i(e, "IO Exception");
return new TopicTaskResult(ResultCode.NETWORK_ERROR, null, null, null,
0, 0, 0, 0, null, null);
} catch (Exception e) {
@ -118,7 +114,7 @@ public class TopicTask extends AsyncTask<String, Void, TopicTaskResult> {
return new TopicTaskResult(ResultCode.UNAUTHORIZED, null, null, null,
0, 0, 0, 0, null, null);
} else {
Timber.e(e, "Parsing Error");
Timber.e(e, "Topic parse failed");
return new TopicTaskResult(ResultCode.PARSING_ERROR, null, null, null,
0, 0, 0, 0, null, null);
}
@ -143,7 +139,6 @@ public class TopicTask extends AsyncTask<String, Void, TopicTaskResult> {
public interface TopicTaskObserver {
void onTopicTaskStarted();
void onTopicTaskCancelled();
}

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

@ -1,10 +1,7 @@
package gr.thmmy.mthmmy.activities.topic.tasks;
import android.util.SparseArray;
import java.util.ArrayList;
import gr.thmmy.mthmmy.activities.topic.tasks.TopicTask;
import gr.thmmy.mthmmy.model.Post;
public class TopicTaskResult {

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

@ -5,20 +5,17 @@ import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ResolveInfo;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AlertDialog;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.content.res.AppCompatResources;
import android.support.v7.preference.PreferenceManager;
import android.support.v7.widget.AppCompatButton;
import android.support.v7.widget.AppCompatTextView;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
@ -37,12 +34,8 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
@ -73,12 +66,15 @@ public class UploadActivity extends BaseActivity {
*/
public static final String BUNDLE_UPLOAD_CATEGORY = "UPLOAD_CATEGORY";
private static final String uploadIndexUrl = "https://www.thmmy.gr/smf/index.php?action=tpmod;dl=upload";
private static final String uploadedFrommThmmyPromptHtml = "<br /><div style=\"text-align: right;\"><span style=\"font-style: italic;\">uploaded from <a href=\"https://play.google.com/store/apps/details?id=gr.thmmy.mthmmy\">mTHMMY</a></span>";
private static final int REQUEST_CODE_CHOOSE_FILE = 8;
private static final int REQUEST_CODE_CAMERA = 4;
private static final int REQUEST_CODE_FIELDS_BUILDER = 74;
private static final String uploadedFromThmmyPromptHtml = "<br /><div style=\"text-align: right;\"><span style=\"font-style: italic;\">uploaded from <a href=\"https://play.google.com/store/apps/details?id=gr.thmmy.mthmmy\">mTHMMY</a></span>";
/**
* Request codes used in activities for result (AFR) calls
*/
private static final int AFR_REQUEST_CODE_CHOOSE_FILE = 8;
private static final int AFR_REQUEST_CODE_CAMERA = 4;
private static final int AFR_REQUEST_CODE_FIELDS_BUILDER = 74;
private static ArrayList<UploadCategory> uploadRootCategories = new ArrayList<>();
private ArrayList<UploadCategory> uploadRootCategories = new ArrayList<>();
private ParseUploadPageTask parseUploadPageTask;
private ArrayList<String> bundleCategory;
private String categorySelected = "-1";
@ -94,8 +90,7 @@ public class UploadActivity extends BaseActivity {
private EditText uploadTitle;
private EditText uploadDescription;
private AppCompatButton titleDescriptionBuilderButton;
private AppCompatButton selectFileButton;
//private static AppCompatButton titleDescriptionBuilderButton;
private AppCompatTextView filenameHolder;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -141,7 +136,7 @@ public class UploadActivity extends BaseActivity {
titleDescriptionBuilderButton = findViewById(R.id.upload_title_description_builder);
titleDescriptionBuilderButton.setOnClickListener(view -> {
if (categorySelected.equals("-1")) {
Toast.makeText(view.getContext(), "Please choose category first", Toast.LENGTH_SHORT).show();
Toast.makeText(view.getContext(), "Please choose a category first", Toast.LENGTH_SHORT).show();
return;
}
@ -186,65 +181,62 @@ public class UploadActivity extends BaseActivity {
}
//Fixes course and semester
maybeCourse = maybeCourse.replaceAll("-", "").replace("(ΝΠΣ)", "").trim();
maybeSemester = maybeSemester.replaceAll("-", "").trim().substring(0, 1);
String course = maybeCourse.replaceAll("-", "").replace("(ΝΠΣ)", "").trim();
String semester = maybeSemester.replaceAll("-", "").trim().substring(0, 1);
Intent intent = new Intent(UploadActivity.this, UploadFieldsBuilderActivity.class);
Bundle builderExtras = new Bundle();
builderExtras.putString(BUNDLE_UPLOAD_FIELD_BUILDER_COURSE, maybeCourse);
builderExtras.putString(BUNDLE_UPLOAD_FIELD_BUILDER_SEMESTER, maybeSemester);
builderExtras.putString(BUNDLE_UPLOAD_FIELD_BUILDER_COURSE, course);
builderExtras.putString(BUNDLE_UPLOAD_FIELD_BUILDER_SEMESTER, semester);
intent.putExtras(builderExtras);
startActivityForResult(intent, REQUEST_CODE_FIELDS_BUILDER);
startActivityForResult(intent, AFR_REQUEST_CODE_FIELDS_BUILDER);
});
titleDescriptionBuilderButton.setEnabled(false);
uploadTitle = findViewById(R.id.upload_title);
uploadDescription = findViewById(R.id.upload_description);
selectFileButton = findViewById(R.id.upload_select_file_button);
filenameHolder = findViewById(R.id.upload_filename);
Drawable filenameDrawable = AppCompatResources.getDrawable(this, R.drawable.ic_attach_file_white_24dp);
filenameHolder.setCompoundDrawablesRelativeWithIntrinsicBounds(filenameDrawable, null, null, null);
AppCompatButton selectFileButton = findViewById(R.id.upload_select_file_button);
Drawable selectStartDrawable = AppCompatResources.getDrawable(this, R.drawable.ic_insert_drive_file_white_24dp);
selectFileButton.setCompoundDrawablesRelativeWithIntrinsicBounds(selectStartDrawable, null, null, null);
selectFileButton.setOnClickListener(v -> {
final CharSequence[] options = {"Take photo", "Choose file",
"Cancel"};
AlertDialog.Builder builder = new AlertDialog.Builder(UploadActivity.this);
builder.setTitle("Upload file");
builder.setItems(options, (dialog, item) -> {
if (options[item].equals("Take photo")) {
/*Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent, REQUEST_CODE_CAMERA);*/
Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
takePhotoIntent.putExtra("return-data", true);
takePhotoIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(UploadsHelper.getTempFile(this)));
Intent targetedIntent = new Intent(takePhotoIntent);
List<ResolveInfo> resInfo = this.getPackageManager().queryIntentActivities(takePhotoIntent, 0);
for (ResolveInfo resolveInfo : resInfo) {
String packageName = resolveInfo.activityInfo.packageName;
targetedIntent.setPackage(packageName);
}
startActivityForResult(takePhotoIntent, REQUEST_CODE_CAMERA);
String[] mimeTypes = {"image/jpeg", "text/html", "image/png", "image/jpg", "image/gif",
"application/pdf", "application/rar", "application/x-tar", "application/zip",
"application/msword", "image/vnd.djvu", "application/gz", "application/tar.gz"};
} else if (options[item].equals("Choose file")) {
String[] mimeTypes = {"image/jpeg", "text/html", "image/png", "image/jpg", "image/gif",
"application/pdf", "application/rar", "application/x-tar", "application/zip",
"application/msword", "image/vnd.djvu", "application/gz", "application/tar.gz"};
Intent intent = new Intent(Intent.ACTION_GET_CONTENT)
//.setType("*/*")
.setType("image/jpeg")
.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
Intent intent = new Intent(Intent.ACTION_GET_CONTENT)
//.setType("*/*")
.setType("image/jpeg")
.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
startActivityForResult(intent, AFR_REQUEST_CODE_CHOOSE_FILE);
});
startActivityForResult(intent, REQUEST_CODE_CHOOSE_FILE);
} else if (options[item].equals("Cancel")) {
dialog.dismiss();
}
});
builder.show();
AppCompatButton takePhotoButton = findViewById(R.id.upload_take_photo_button);
Drawable takePhotoDrawable = AppCompatResources.getDrawable(this, R.drawable.ic_photo_camera_white_24dp);
takePhotoButton.setCompoundDrawablesRelativeWithIntrinsicBounds(takePhotoDrawable, null, null, null);
takePhotoButton.setOnClickListener(v -> {
Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
takePhotoIntent.putExtra("return-data", true);
takePhotoIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(UploadsHelper.getCacheFile(this)));
Intent targetedIntent = new Intent(takePhotoIntent);
List<ResolveInfo> resInfo = this.getPackageManager().queryIntentActivities(takePhotoIntent, 0);
for (ResolveInfo resolveInfo : resInfo) {
String packageName = resolveInfo.activityInfo.packageName;
targetedIntent.setPackage(packageName);
}
startActivityForResult(takePhotoIntent, AFR_REQUEST_CODE_CAMERA);
});
findViewById(R.id.upload_upload_button).setOnClickListener(view -> {
FloatingActionButton uploadFAB = findViewById(R.id.upload_fab);
uploadFAB.setOnClickListener(view -> {
progressBar.setVisibility(View.VISIBLE);
String uploadTitleText = uploadTitle.getText().toString();
String uploadDescriptionText = uploadDescription.getText().toString();
@ -252,39 +244,40 @@ public class UploadActivity extends BaseActivity {
uploadTitle.setError("Required");
}
if (fileUri == null) {
selectFileButton.setError("Required");
Toast.makeText(view.getContext(), "Please choose a file to upload or take a photo", Toast.LENGTH_LONG).show();
}
if (categorySelected.equals("-1")) {
Toast.makeText(view.getContext(), "Please choose category first", Toast.LENGTH_SHORT).show();
}
if (categorySelected.equals("-1") || uploadTitleText.equals("") || fileUri == null) {
progressBar.setVisibility(View.GONE);
return;
}
String tmpDescriptionText = uploadDescriptionText;
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(view.getContext());
if (sharedPrefs.getBoolean(UPLOADING_APP_SIGNATURE_ENABLE_KEY, true)) {
tmpDescriptionText += uploadedFrommThmmyPromptHtml;
uploadDescriptionText += uploadedFromThmmyPromptHtml;
}
String tempFilePath = null;
if (uploadFilename != null) {
//File should be uploaded with a certain name. Temporarily copies the file and renames it
tempFilePath = UploadsHelper.createTempFile(this, fileUri, uploadFilename);
if (tempFilePath == null) {
//Something went wrong, abort
Toast.makeText(this, "Could not create temporary file for renaming", Toast.LENGTH_SHORT).show();
progressBar.setVisibility(View.GONE);
return;
}
}
try {
final String finalTempFilePath = tempFilePath;
new MultipartUploadRequest(view.getContext(), uploadIndexUrl)
.setUtf8Charset()
.addParameter("tp-dluploadtitle", uploadTitleText)
.addParameter("tp-dluploadcat", categorySelected)
.addParameter("tp-dluploadtext", tmpDescriptionText)
.addParameter("tp-dluploadtext", uploadDescriptionText)
.addFileToUpload(tempFilePath == null
? fileUri.toString()
: tempFilePath
@ -302,68 +295,46 @@ public class UploadActivity extends BaseActivity {
public void onError(Context context, UploadInfo uploadInfo, ServerResponse serverResponse,
Exception exception) {
Toast.makeText(context, "Upload failed", Toast.LENGTH_SHORT).show();
if (finalTempFilePath != null) {
if (!UploadsHelper.deleteTempFile(finalTempFilePath)) {
Toast.makeText(context, "Failed to delete temporary file", Toast.LENGTH_SHORT).show();
}
}
UploadsHelper.deleteTempFiles();
progressBar.setVisibility(View.GONE);
}
@Override
public void onCompleted(Context context, UploadInfo uploadInfo, ServerResponse serverResponse) {
if (finalTempFilePath != null) {
if (!UploadsHelper.deleteTempFile(finalTempFilePath)) {
Toast.makeText(context, "Failed to delete temporary file", Toast.LENGTH_SHORT).show();
}
}
Toast.makeText(context, "Upload completed successfully", Toast.LENGTH_SHORT).show();
UploadsHelper.deleteTempFiles();
BaseApplication.getInstance().logFirebaseAnalyticsEvent("file_upload", null);
uploadTitle.setText(null);
uploadDescription.setText(null);
fileUri = null;
filenameHolder.setText(null);
filenameHolder.setVisibility(View.GONE);
progressBar.setVisibility(View.GONE);
}
@Override
public void onCancelled(Context context, UploadInfo uploadInfo) {
if (finalTempFilePath != null) {
if (!UploadsHelper.deleteTempFile(finalTempFilePath)) {
Toast.makeText(context, "Failed to delete temporary file", Toast.LENGTH_SHORT).show();
}
}
Toast.makeText(context, "Upload canceled", Toast.LENGTH_SHORT).show();
UploadsHelper.deleteTempFiles();
progressBar.setVisibility(View.GONE);
}
})
.startUpload();
} catch (Exception exception) {
Timber.e(exception, "AndroidUploadService: %s", exception.getMessage());
progressBar.setVisibility(View.GONE);
}
});
if (uploadRootCategories.isEmpty()) {
//Parses the uploads page
parseUploadPageTask = new ParseUploadPageTask();
parseUploadPageTask.execute(uploadIndexUrl);
} else {
String[] tmpSpinnerArray = new String[uploadRootCategories.size()];
for (int i = 0; i < uploadRootCategories.size(); ++i) {
tmpSpinnerArray[i] = uploadRootCategories.get(i).getCategoryTitle();
}
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(BaseApplication.getInstance().getApplicationContext(),
android.R.layout.simple_spinner_dropdown_item, tmpSpinnerArray);
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
rootCategorySpinner.setAdapter(spinnerArrayAdapter);
//Sets bundle selection
if (bundleCategory != null) {
int bundleSelectionIndex = -1, currentIndex = 0;
for (UploadCategory category : uploadRootCategories) {
if (bundleCategory.get(0).contains(category.getCategoryTitle())) {
bundleSelectionIndex = currentIndex;
break;
}
++currentIndex;
}
if (bundleSelectionIndex != -1) {
rootCategorySpinner.setSelection(bundleSelectionIndex, true);
bundleCategory.remove(0);
}
}
//Renders the already parsed data
updateUIElements();
titleDescriptionBuilderButton.setEnabled(true);
}
}
@ -392,15 +363,16 @@ public class UploadActivity extends BaseActivity {
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE_CHOOSE_FILE && data != null) {
if (resultCode == Activity.RESULT_CANCELED) {
if (requestCode == AFR_REQUEST_CODE_CHOOSE_FILE) {
if (resultCode == Activity.RESULT_CANCELED || data == null) {
return;
}
fileUri = data.getData();
if (fileUri != null) {
String filename = UploadsHelper.filenameFromUri(this, fileUri);
selectFileButton.setText(filename);
filenameHolder.setText(filename);
filenameHolder.setVisibility(View.VISIBLE);
filename = filename.toLowerCase();
if (filename.endsWith(".jpg")) {
@ -422,38 +394,40 @@ public class UploadActivity extends BaseActivity {
fileIcon = "blank.gif";
}
}
} else if (requestCode == REQUEST_CODE_CAMERA) {
} else if (requestCode == AFR_REQUEST_CODE_CAMERA) {
if (resultCode == Activity.RESULT_CANCELED) {
return;
}
Bitmap bitmap;
File cacheImageFile = UploadsHelper.getTempFile(this);
if (resultCode == Activity.RESULT_OK) {
fileUri = Uri.fromFile(cacheImageFile);
fileIcon = "jpg_image.gif";
bitmap = UploadsHelper.getImageResized(this, fileUri);
int rotation = UploadsHelper.getRotation(this, fileUri);
bitmap = UploadsHelper.rotate(bitmap, rotation);
try {
FileOutputStream out = new FileOutputStream(cacheImageFile);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
File cacheImageFile = UploadsHelper.getCacheFile(this);
String newFilename = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.FRANCE).
format(new Date());
fileUri = Uri.parse(UploadsHelper.createTempFile(this, fileUri, newFilename));
Uri cacheFileUri = Uri.fromFile(cacheImageFile);
fileIcon = "jpg_image.gif";
newFilename += ".jpg";
selectFileButton.setText(newFilename);
bitmap = UploadsHelper.getImageResized(this, cacheFileUri);
int rotation = UploadsHelper.getRotation(this, cacheFileUri);
bitmap = UploadsHelper.rotate(bitmap, rotation);
try {
FileOutputStream out = new FileOutputStream(cacheImageFile);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
} else if (requestCode == REQUEST_CODE_FIELDS_BUILDER) {
String newFilename = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.FRANCE).
format(new Date());
fileUri = Uri.parse(UploadsHelper.createTempFile(this, cacheFileUri, newFilename));
newFilename += ".jpg";
filenameHolder.setText(newFilename);
filenameHolder.setVisibility(View.VISIBLE);
UploadsHelper.deleteCacheFiles(this);
} else if (requestCode == AFR_REQUEST_CODE_FIELDS_BUILDER) {
if (resultCode == Activity.RESULT_CANCELED) {
return;
}
@ -499,8 +473,8 @@ public class UploadActivity extends BaseActivity {
}
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(getApplicationContext(),
android.R.layout.simple_spinner_dropdown_item, tmpSpinnerArray);
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
R.layout.spinner_item, tmpSpinnerArray);
spinnerArrayAdapter.setDropDownViewResource(R.layout.spinner_dropdown_item);
AppCompatSpinnerWithoutDefault subSpinner = new AppCompatSpinnerWithoutDefault(categoriesSpinners.getContext());
subSpinner.setPromptId(R.string.upload_spinners_hint);
@ -578,7 +552,7 @@ public class UploadActivity extends BaseActivity {
UploadCategory secondLevelCategory = firstLevelCategory.getSubCategories().get(firstLevelCategory.getSubCategories().size() - 1);
secondLevelCategory.addSubCategory(categoryValue, categoryText);
} else if (categoryText.startsWith("---- ")) {
//This is a level three subcategory
//This is a level four subcategory
UploadCategory rootLevelCategory = uploadRootCategories.get(uploadRootCategories.size() - 1);
UploadCategory firstLevelCategory = rootLevelCategory.getSubCategories().get(rootLevelCategory.getSubCategories().size() - 1);
UploadCategory secondLevelCategory = firstLevelCategory.getSubCategories().get(firstLevelCategory.getSubCategories().size() - 1);
@ -593,36 +567,39 @@ public class UploadActivity extends BaseActivity {
@Override
protected void postExecution(ResultCode result) {
String[] tmpSpinnerArray = new String[uploadRootCategories.size()];
for (int i = 0; i < uploadRootCategories.size(); ++i) {
tmpSpinnerArray[i] = uploadRootCategories.get(i).getCategoryTitle();
}
updateUIElements();
titleDescriptionBuilderButton.setEnabled(true);
progressBar.setVisibility(ProgressBar.GONE);
}
}
private void updateUIElements() {
String[] tmpSpinnerArray = new String[uploadRootCategories.size()];
for (int i = 0; i < uploadRootCategories.size(); ++i) {
tmpSpinnerArray[i] = uploadRootCategories.get(i).getCategoryTitle();
}
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(BaseApplication.getInstance().getApplicationContext(),
android.R.layout.simple_spinner_dropdown_item, tmpSpinnerArray);
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
R.layout.spinner_item, tmpSpinnerArray);
spinnerArrayAdapter.setDropDownViewResource(R.layout.spinner_dropdown_item);
rootCategorySpinner.setAdapter(spinnerArrayAdapter);
//Sets bundle selection
if (bundleCategory != null) {
int bundleSelectionIndex = -1, currentIndex = 0;
//Sets bundle selection
if (bundleCategory != null) {
int bundleSelectionIndex = -1, currentIndex = 0;
for (UploadCategory category : uploadRootCategories) {
if (bundleCategory.get(0).contains(category.getCategoryTitle())) {
bundleSelectionIndex = currentIndex;
break;
}
++currentIndex;
}
if (bundleSelectionIndex != -1) {
rootCategorySpinner.setSelection(bundleSelectionIndex, true);
bundleCategory.remove(0);
for (UploadCategory category : uploadRootCategories) {
if (bundleCategory.get(0).contains(category.getCategoryTitle())) {
bundleSelectionIndex = currentIndex;
break;
}
++currentIndex;
}
titleDescriptionBuilderButton.setEnabled(true);
progressBar.setVisibility(ProgressBar.GONE);
if (bundleSelectionIndex != -1) {
rootCategorySpinner.setSelection(bundleSelectionIndex, true);
bundleCategory.remove(0);
}
}
}
}

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

@ -87,7 +87,7 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
//Initialize toolbar
Toolbar toolbar = findViewById(R.id.toolbar);
toolbar.setTitle("Upload fields builder");
toolbar.setTitle(R.string.upload_fields_builder_toolbar_title);
setSupportActionBar(toolbar);
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
@ -215,263 +215,300 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
@Nullable
private String getGreeklishOrMinifiedCourseName(boolean greeklish) {
//TODO fill missing values
if (course.contains("Συστήματα Υπολογιστών (Υπολογιστικά Συστήματα)")) {
return greeklish ? "sys_ypologistwn" : "Συσ. Υπολογιστών";
} else if (course.contains("Τεχνική Μηχανική")) {
return greeklish ? "texn_mhxan" : "Τεχν. Μηχαν.";
} else if (course.contains("Διαφορικές Εξισώσεις")) {
return greeklish ? "diaforikes" : "Διαφορικές";
} else if (course.contains("Θεωρία Πιθανοτήτων και Στατιστική")) {
return greeklish ? "pithanothtes" : "Πιθανότητες";
} else if (course.contains("Εφαρμοσμένα Μαθηματικά Ι")) {
return greeklish ? "efarmosmena_math_I" : "Εφαρμοσμένα 1";
} else if (course.contains("Ηλεκτρικά Κυκλώματα ΙΙ")) {
return greeklish ? "kyklwmata_II" : "Κυκλώματα 2";
} else if (course.contains("Ηλεκτρολογικά Υλικά")) {
return greeklish ? "ylika" : "Ηλεκτρ. Υλικά";
} else if (course.contains("Ηλεκτρομαγνητικό Πεδίο Ι")) {
return greeklish ? "pedio_I" : "Πεδίο 1";
} else if (course.contains("Θεωρία Σημάτων και Γραμμικών Συστημάτων")) {
return greeklish ? "analog_shma" : "Σύματα & Συστήματα";
} else if (course.contains("Προγραμματιστικές Τεχνικές")) {
return greeklish ? "cpp" : "Προγραμματ. Τεχν.";
} else if (course.contains("Αριθμητική Ανάλυση")) {
return greeklish ? "arith_anal" : "Αριθμ. Ανάλυση";
} else if (course.contains("Αρχιτεκτονική Υπολογιστών")) {
return greeklish ? "arx_ypologistwn" : "Αρχ. Υπολογιστών";
} else if (course.contains("Εισαγωγή στην Ενεργειακή Τεχνολογία Ι")) {
return greeklish ? "EET_I" : "ΕΕΤ 1";
} else if (course.contains("Ηλεκτρικά Κυκλώματα ΙΙΙ")) {
return greeklish ? "kyklwmata_I" : "Κυκλώματα 3";
} else if (course.contains("Ηλεκτρομαγνητικό Πεδίο ΙΙ")) {
return greeklish ? "pedio_II" : "Πεδίο 2";
} else if (course.contains("Στοχαστικό Σήμα")) {
return greeklish ? "stox_shma" : "Στοχ. Σήμα";
if (course.contains("Ψηφιακή Επεξεργασία Σήματος")) {
return greeklish ? "PSES" : "ΨΕΣ";
} else if (course.contains("Ψηφιακή Επεξεργασία Εικόνας")) {
return greeklish ? "psee" : "ΨΕΕ";
} else if (course.contains("Ψηφιακές Τηλεπικοινωνίες ΙΙ")) {
return greeklish ? "pshf_thlep_II" : "Ψηφιακές Τηλεπ. 2";
} else if (course.contains("Ψηφιακές Τηλεπικοινωνίες Ι")) {
return greeklish ? "pshf_thlep_I" : "Ψηφιακές Τηλεπ. 1";
} else if (course.contains("Ψηφιακά Φίλτρα")) {
return greeklish ? "filtra" : "Φίλτρα";
} else if (course.contains("Ψηφιακά Συστήματα ΙΙΙ")) {
return greeklish ? "pshfiaka_III" : "Ψηφιακά 3";
} else if (course.contains("Ψηφιακά Συστήματα ΙΙ")) {
return greeklish ? "pshfiaka_II" : "Ψηφιακά 2";
} else if (course.contains("Ψηφιακά Συστήματα Ι")) {
return greeklish ? "pshfiaka_I" : "Ψηφιακά 1";
} else if (course.contains("Αναλογικές Τηλεπικοινωνίες (πρώην Τηλεπικοινωνιακά Συστήματα Ι)")) {
return greeklish ? "anal_thlep" : "Αναλογικές Τηλεπ.";
} else if (course.contains("Διάδοση Ηλεκτρομαγνητικού Κύματος Ι (πρώην Πεδίο ΙΙΙ)")) {
return greeklish ? "diadosi_1" : "Διάδοση 1";
} else if (course.contains("Δομές Δεδομένων")) {
return greeklish ? "dom_dedomenwn" : "Δομ. Δεδομ.";
} else if (course.contains("Εισαγωγή στην Ενεργειακή Τεχνολογία ΙΙ")) {
return greeklish ? "EET_2" : "ΕΕΤ2";
} else if (course.contains("Ηλεκτρικές Μετρήσεις Ι")) {
return greeklish ? "metrhseis_1" : "Μετρήσεις 1";
} else if (course.contains("Ηλεκτρονική ΙΙ")) {
return greeklish ? "hlektronikh_2" : "Ηλεκτρονική 2";
} else if (course.contains("Συστήματα Αυτομάτου Ελέγχου Ι")) {
return greeklish ? "SAE_1" : "ΣΑΕ 1";
} else if (course.contains("Γραμμική Άλγεβρα")) {
return greeklish ? "grammikh_algebra" : "Γραμμ. Άλγεβρ.";
} else if (course.contains("Δομημένος Προγραμματισμός")) {
return greeklish ? "C" : "Δομ. Προγραμμ.";
} else if (course.contains("Λογική Σχεδίαση")) {
return greeklish ? "logiki_sxediash" : "Λογική Σχεδίαση";
} else if (course.contains("Λογισμός Ι")) {
return greeklish ? "logismos_I" : "Λογισμός 1";
} else if (course.contains("Τεχνικές Σχεδίασης με Η/Υ")) {
return greeklish ? "sxedio" : "Σχέδιο";
} else if (course.contains("Φωτονική Τεχνολογία")) {
return greeklish ? "fwtonikh" : "Φωτονική";
} else if (course.contains("Φυσική Ι")) {
return greeklish ? "fysikh_I" : "Φυσική 1";
} else if (course.contains("Αντικειμενοστραφής Προγραμματισμός")) {
return greeklish ? "OOP" : "Αντικειμενοστραφής";
} else if (course.contains("Εφαρμοσμένη Θερμοδυναμική")) {
return greeklish ? "thermodynamikh" : "Θερμοδυναμική";
} else if (course.contains("Ηλεκτρικά Κυκλώματα Ι")) {
return greeklish ? "kyklwmata_I" : "Κυκλώματα 1";
} else if (course.contains("Λογισμός ΙΙ")) {
return greeklish ? "logismos_II" : "Λογισμός 2";
} else if (course.contains("Οργάνωση Υπολογιστών")) {
return greeklish ? "org_ypol" : "Οργάνωση Υπολ.";
} else if (course.contains("Ηλεκτρονική Ι")) {
return greeklish ? "hlektronikh_1" : "Ηλεκτρονική 1";
} else if (course.contains("Διακριτά μαθηματικά")) {
return greeklish ? "diakrita" : "Διακριτά Μαθηματικά";
} else if (course.contains("Σήματα και Συστήματα")) {
return greeklish ? "analog_shma" : "Σύματα & Συστήματα";
} else if (course.contains("Εισαγωγή στις εφαρμογές Πυρηνικής Τεχνολογίας")) {
return greeklish ? "Intro_Purhnikh_Texn" : "Εισ. Πυρηνικη Τεχν.";
} else if (course.contains("Επιχειρησιακή Έρευνα")) {
return greeklish ? "epixeirisiaki" : "Επιχειρησιακή Έρευνα";
} else if (course.contains("Ημιαγωγά Υλικά: Θεωρία-Διατάξεις")) {
return greeklish ? "Hmiagwga_Ylika" : "Ημιαγωγά Υλικά";
} else if (course.contains("Μετάδοση Θερμότητας")) {
return greeklish ? "metadosi_therm" : "Μετάδοση Θερμ.";
} else if (course.contains("Συστήματα Ηλεκτρικής Ενέργειας Ι")) {
return greeklish ? "SHE_I" : "ΣΗΕ 1";
} else if (course.contains("Υψηλές Τάσεις ΙΙΙ")) {
return greeklish ? "ypshles_III" : "Υψηλές 3";
} else if (course.contains("Υψηλές Τάσεις ΙΙ")) {
return greeklish ? "ypshles_II" : "Υψηλές 2";
} else if (course.contains("Υψηλές Τάσεις Ι")) {
return greeklish ? "ypshles_I" : "Υψηλές 1";
} else if (course.contains("Θεωρία και Τεχνολογία Πυρηνικών Αντιδραστήρων")) {
return greeklish ? "texn_antidrasthrwn" : "Τεχνολογία Αντιδραστήρων";
} else if (course.contains("Υψηλές Τάσεις 4")) {
return greeklish ? "ypshles_IV" : "Υψηλές 4";
} else if (course.contains("Υπολογιστικός Ηλεκτρομαγνητισμός")) {
return greeklish ? "ypologistikos_HM" : "Υπολογιστικός Η/Μ";
} else if (course.contains("Υπολογιστικές Μέθοδοι στα Ενεργειακά Συστήματα")) {
return greeklish ? "ymes" : "ΥΜΕΣ";
} else if (course.contains("Τηλεπικοινωνιακή Ηλεκτρονική")) {
return greeklish ? "tilep_ilektr" : "Τηλεπ. Ηλεκτρ.";
} else if (course.contains("Τηλεοπτικά Συστήματα")) {
return greeklish ? "tileoptika" : "Τηλεοπτικά";
} else if (course.contains("Τεχνολογία Λογισμικού")) {
return greeklish ? "SE" : "Τεχνολογία Λογισμικού";
} else if (course.contains("Τεχνολογία Ηλεκτροτεχνικών Υλικών")) {
return greeklish ? "Hlektrotexnika_Ylika" : "Ηλεκτροτεχνικά Υλικά";
} else if (course.contains("Ηλεκτρικές Μηχανές Α'")) {
return greeklish ? "mhxanes_A" : "Μηχανές Α";
} else if (course.contains("Σταθμοί Παραγωγής Ηλεκτρικής Ενέργειας")) {
return greeklish ? "SPHE" : "ΣΠΗΕ";
} else if (course.contains("Συστήματα Ηλεκτρικής Ενέργειας ΙΙ")) {
return greeklish ? "SHE_II" : "ΣΗΕ 2";
} else if (course.contains("Υψηλές Τάσεις ΙΙ")) {
return greeklish ? "ypshles_II" : "Υψηλές 2";
} else if (course.contains("Αρχές Οικονομίας")) {
return greeklish ? "arx_oikonomias" : "Αρχές Οικονομίας";
} else if (course.contains("Διανεμημένη Παραγωγή")) {
return greeklish ? "dian_paragwgh" : "Διανεμημένη Παραγωγή";
} else if (course.contains("Διαχείριση Συστημάτων Ηλεκτρικής Ενέργειας")) {
return greeklish ? "dshe" : "ΔΣΗΕ";
} else if (course.contains("Υψηλές Τάσεις ΙΙΙ")) {
return greeklish ? "ypshles_III" : "Υψηλές 3";
} else if (course.contains("Ανάλυση Συστημάτων Ηλεκτρικής Ενέργειας")) {
return greeklish ? "ASHE" : "ΑΣΗΕ";
} else if (course.contains("Ηλεκτρικές Μηχανές Β'")) {
return greeklish ? "mhxanes_B" : "Μηχανές Β";
} else if (course.contains("Ηλεκτρονικά Ισχύος Ι")) {
return greeklish ? "isxyos_I" : "Ισχύος 1";
} else if (course.contains("Τεχνολογία Ήχου και Εικόνας")) {
return greeklish ? "texn_hxoy_eikonas" : "Τεχνολογία Ήχου και Εικόνας";
} else if (course.contains("Τεχνική Μηχανική")) {
return greeklish ? "texn_mhxan" : "Τεχν. Μηχαν.";
} else if (course.contains("Τεχνικές μη Καταστρεπτικών Δοκιμών")) {
return greeklish ? "non_destructive_tests" : "Μη Καταστρεπτικές Δοκιμές";
} else if (course.contains("Τεχνικές Σχεδίασης με Η/Υ")) {
return greeklish ? "sxedio" : "Σχέδιο";
} else if (course.contains("Τεχνικές Κωδικοποίησης")) {
return greeklish ? "texn_kwdikopoihshs" : "Τεχνικές Κωδικοποίησης";
} else if (course.contains("Τεχνικές Βελτιστοποίησης")) {
return greeklish ? "veltistopoihsh" : "Βελτιστοποίηση";
} else if (course.contains("Σύνθεση Τηλεπικοινωνιακών Διατάξεων")) {
return greeklish ? "synth_thlep_diataksewn" : "Σύνθεση Τηλεπ. Διατάξεων";
} else if (course.contains("Σύνθεση Ενεργών και Παθητικών Κυκλωμάτων")) {
return greeklish ? "synthesh" : "Σύνθεση";
} else if (course.contains("Σχεδίαση Συστημάτων VLSI")) {
return greeklish ? "VLSI" : "VLSI";
} else if (course.contains("Συστήματα Υπολογιστών (Υπολογιστικά Συστήματα)")) {
return greeklish ? "sys_ypologistwn" : "Συσ. Υπολογιστών";
} else if (course.contains("Συστήματα Πολυμέσων και Εικονική Πραγματικότητα")) {
return greeklish ? "polymesa" : "Πολυμέσα";
} else if (course.contains("Συστήματα Μικροϋπολογιστών")) {
return greeklish ? "mikro_I" : "Μίκρο 1";
} else if (course.contains("Συστήματα Ηλεκτροκίνησης")) {
return greeklish ? "hlektrokinhsh" : "Ηλεκτροκίνηση";
} else if (course.contains("Συστήματα Ηλεκτρικής Ενέργειας ΙΙΙ")) {
return greeklish ? "SHE_III" : "ΣΗΕ 3";
} else if (course.contains("Συστήματα Ηλεκτρικής Ενέργειας ΙΙ")) {
return greeklish ? "SHE_II" : "ΣΗΕ 2";
} else if (course.contains("Συστήματα Ηλεκτρικής Ενέργειας Ι")) {
return greeklish ? "SHE_I" : "ΣΗΕ 1";
} else if (course.contains("Συστήματα Αυτομάτου Ελέγχου ΙΙI")) {
return greeklish ? "SAE_III" : "ΣΑΕ 3";
} else if (course.contains("Συστήματα Αυτομάτου Ελέγχου ΙΙ")) {
return greeklish ? "SAE_II" : "ΣΑΕ 2";
} else if (course.contains("Συστήματα Αυτομάτου Ελέγχου Ι")) {
return greeklish ? "SAE_1" : "ΣΑΕ 1";
} else if (course.contains("Στοχαστικό Σήμα")) {
return greeklish ? "stox_shma" : "Στοχ. Σήμα";
} else if (course.contains("Σταθμοί Παραγωγής Ηλεκτρικής Ενέργειας")) {
return greeklish ? "SPHE" : "ΣΠΗΕ";
} else if (course.contains("Σερβοκινητήρια Συστήματα")) {
return greeklish ? "servo" : "Σέρβο";
} else if (course.contains("Συστήματα Ηλεκτροκίνησης")) {
return greeklish ? "hlektrokinhsh" : "Ηλεκτροκίνηση";
} else if (course.contains("Υπολογιστικές Μέθοδοι στα Ενεργειακά Συστήματα")) {
return greeklish ? "ymes" : "ΥΜΕΣ";
} else if (course.contains("Υψηλές Τάσεις 4")) {
return greeklish ? "ypshles_IV" : "Υψηλές 4";
} else if (course.contains("Ηλεκτρικές Μηχανές Γ'")) {
return greeklish ? "mhxanes_C" : "Μηχανές Γ";
} else if (course.contains("Ηλεκτρική Οικονομία")) {
return greeklish ? "hlektr_oikonomia" : "Ηλεκτρική Οικονομία";
} else if (course.contains("Ηλεκτρονικά Ισχύος ΙΙ")) {
return greeklish ? "isxyos_II" : "Ισχύος 2";
} else if (course.contains("Ανάλυση και Σχεδίαση Αλγορίθμων")) {
return greeklish ? "algorithms" : "Αλγόριθμοι";
} else if (course.contains("Διακριτά Μαθηματικά")) {
return greeklish ? "diakrita" : "Διακριτά Μαθηματικά";
} else if (course.contains("Κβαντική Φυσική")) {
return greeklish ? "kvantikh" : "Κβαντική";
} else if (course.contains("Σήματα και Συστήματα")) {
return greeklish ? "analog_shma" : "Σύματα & Συστήματα";
} else if (course.contains("Ρομποτική")) {
return greeklish ? "rompotikh" : "Ρομποτική";
} else if (course.contains("Τεχνικές Βελτιστοποίησης")) {
return greeklish ? "veltistopoihsh" : "Βελτιστοποίηση";
} else if (course.contains("Ηλεκτρικές Μετρήσεις ΙΙ")) {
return greeklish ? "metrhseis_II" : "Μετρήσεις 2";
} else if (course.contains("Ηλεκτρονική ΙΙΙ")) {
return greeklish ? "hlektronikh_III" : "Ηλεκτρονική 3";
} else if (course.contains("Συστήματα Αυτομάτου Ελέγχου ΙΙ")) {
return greeklish ? "SAE_II" : "ΣΑΕ 2";
} else if (course.contains("Ψηφιακά Συστήματα ΙΙ")) {
return greeklish ? "pshfiaka_II" : "Ψηφιακά 2";
} else if (course.contains("Ανάλυση Χρονοσειρών")) {
return greeklish ? "xronoseires" : "Χρονοσειρές";
} else if (course.contains("Θεωρία Υπολογισμών και Αλγορίθμων")) {
return greeklish ? "thya" : "ΘΥΑ";
} else if (course.contains("Παράλληλα και Κατανεμημένα Συστήματα")) {
return greeklish ? "parallhla" : "Παράλληλα";
} else if (course.contains("Προγραμματιζόμενα Κυκλώματα ASIC")) {
return greeklish ? "asic" : "ASIC";
} else if (course.contains("Προσομοίωση και Μοντελοποίηση Συστημάτων")) {
return greeklish ? "montelopoihsh" : "Μοντελοποίηση";
} else if (course.contains("Συστήματα Αυτομάτου Ελέγχου ΙΙI")) {
return greeklish ? "SAE_III" : "ΣΑΕ 3";
} else if (course.contains("Σύνθεση Ενεργών και Παθητικών Κυκλωμάτων")) {
return greeklish ? "synthesh" : "Σύνθεση";
} else if (course.contains("Δίκτυα Υπολογιστών Ι")) {
return greeklish ? "diktya_I" : "Δίκτυα 1";
} else if (course.contains("Λειτουργικά Συστήματα")) {
return greeklish ? "OS" : "Λειτουργικά";
} else if (course.contains("Συστήματα Μικροϋπολογιστών")) {
return greeklish ? "mikro_I" : "Μίκρο 1";
} else if (course.contains("Ασαφή Συστήματα")) {
return greeklish ? "asafh" : "Ασαφή";
} else if (course.contains("Γραφική με Υπολογιστές")) {
return greeklish ? "grafikh" : "Γραφική";
} else if (course.contains("Ενσωματωμένα Συστήματα Πραγματικού Χρόνου")) {
return greeklish ? "enswmatwmena" : "Ενσωματωμένα";
} else if (course.contains("Τηλεπικοινωνιακή Ηλεκτρονική")) {
return greeklish ? "tilep_ilektr" : "Τηλεπ. Ηλεκτρ.";
} else if (course.contains("Ψηφιακά Συστήματα ΙΙΙ")) {
return greeklish ? "pshfiaka_III" : "Ψηφιακά 3";
} else if (course.contains("Ψηφιακή Επεξεργασία Εικόνας")) {
return greeklish ? "psee" : "ΨΕΕ";
} else if (course.contains("Δίκτυα Υπολογιστών ΙΙ")) {
return greeklish ? "diktya_II" : "Δίκτυα 2";
} else if (course.contains("Μικροεπεξεργαστές και Περιφερειακά")) {
return greeklish ? "mikro_II" : "Μίκρο 2";
} else if (course.contains("Τεχνολογία Λογισμικού")) {
return greeklish ? "SE" : "Τεχνολογία Λογισμικού";
} else if (course.contains("Ψηφιακά Φίλτρα")) {
return greeklish ? "filtra" : "Φίλτρα";
} else if (course.contains("Αναγνώριση Προτύπων")) {
return greeklish ? "protipa" : "Αναγνώριση Προτύπων";
} else if (course.contains("Ασφάλεια Πληροφοριακών Συστημάτων")) {
return greeklish ? "asfaleia" : "Ασφάλεια";
} else if (course.contains("Βάσεις Δεδομένων")) {
return greeklish ? "vaseis" : "Βάσεις";
} else if (course.contains("Βιομηχανική Πληροφορική")) {
return greeklish ? "viomix_plir" : "Βιομηχανική Πληρ";
} else if (course.contains("Ευφυή Συστήματα Ρομπότ")) {
return greeklish ? "eufuh" : "Ευφυή";
} else if (course.contains("Συστήματα Πολυμέσων και Εικονική Πραγματικότητα")) {
return greeklish ? "polymesa" : "Πολυμέσα";
} else if (course.contains("Σχεδίαση Συστημάτων VLSI")) {
return greeklish ? "VLSI" : "VLSI";
} else if (course.contains("Ακουστική Ι")) {
return greeklish ? "akoystikh_I" : "Ακουστική 1";
} else if (course.contains("Εφαρμοσμένα Μαθηματικά ΙΙ")) {
return greeklish ? "efarmosmena_math_II" : "Εφαρμοσμένα 2";
} else if (course.contains("Ηλεκτρακουστική Ι")) {
return greeklish ? "hlektroakoystikh_I" : "Ηλεκτροακουστική 1";
} else if (course.contains("Οπτική Ι")) {
return greeklish ? "optikh_I" : "Οπτική 1";
} else if (course.contains("Διάδοση Η/Μ Κύματος ΙΙ")) {
return greeklish ? "diadosi_II" : "Διάδοση 2";
} else if (course.contains("Ψηφιακές Τηλεπικοινωνίες Ι")) {
return greeklish ? "pshf_thlep_I" : "Ψηφιακές Τηλεπ. 1";
} else if (course.contains("Ακουστική ΙΙ")) {
return greeklish ? "akoystikh_II" : "Ακουστική 2";
} else if (course.contains("Βιοϊατρική Τεχνολογία")) {
return greeklish ? "vioiatriki" : "Βιοιατρική";
} else if (course.contains("Ηλεκτρακουστική ΙΙ")) {
return greeklish ? "hlektroakoystikh_II" : "Ηλεκτροακουστική 2";
} else if (course.contains("Προηγμένες Τεχνικές Επεξεργασίας Σήματος")) {
return greeklish ? "ptes" : "ΠΤΕΣ";
} else if (course.contains("Προγραμματιστικές Τεχνικές")) {
return greeklish ? "cpp" : "Προγραμματ. Τεχν.";
} else if (course.contains("Προγραμματιζόμενα Κυκλώματα ASIC")) {
return greeklish ? "asic" : "ASIC";
} else if (course.contains("Παράλληλα και Κατανεμημένα Συστήματα")) {
return greeklish ? "parallhla" : "Παράλληλα";
} else if (course.contains("Οργάνωση και Διοίκηση Εργοστασίων")) {
return greeklish ? "organ_dioik_ergostasiwn" : "Οργάνωση και Διοίκηση Εργοστασίων";
} else if (course.contains("Οργάνωση Υπολογιστών")) {
return greeklish ? "org_ypol" : "Οργάνωση Υπολ.";
} else if (course.contains("Οπτική ΙΙ")) {
return greeklish ? "optikh_II" : "Οπτική 2";
} else if (course.contains("Ασύρματος Τηλεπικοινωνία Ι")) {
return greeklish ? "asyrmatos_I" : "Ασύρματος 1";
} else if (course.contains("Οπτική Ι")) {
return greeklish ? "optikh_I" : "Οπτική 1";
} else if (course.contains("Οπτικές Επικοινωνίες")) {
return greeklish ? "optikes_thlep" : "Οπτικές Τηλεπ.";
} else if (course.contains("Μικροκύματα II")) {
return greeklish ? "mikrokymata_II" : "Μικροκύματα 2";
} else if (course.contains("Μικροκύματα I")) {
return greeklish ? "mikrokymata_I" : "Μικροκύματα 1";
} else if (course.contains("Ψηφιακές Τηλεπικοινωνίες ΙΙ")) {
return greeklish ? "pshf_thlep_II" : "Ψηφιακές Τηλεπ. 2";
} else if (course.contains("Ψηφιακή Επεξεργασία Σήματος")) {
return greeklish ? "PSES" : "ΨΕΣ";
} else if (course.contains("Εισαγωγή στην Πολιτική Οικονομία")) {
return greeklish ? "polit_oik" : "Πολιτική Οικονομία";
} else if (course.contains("Μικροκυματική Τηλεπισκόπηση")) {
return greeklish ? "thlepiskophsh" : "Τηλεπισκόπηση";
} else if (course.contains("Μικροεπεξεργαστές και Περιφερειακά")) {
return greeklish ? "mikro_II" : "Μίκρο 2";
} else if (course.contains("Μετάδοση Θερμότητας")) {
return greeklish ? "metadosi_therm" : "Μετάδοση Θερμ.";
} else if (course.contains("Λογισμός ΙΙ")) {
return greeklish ? "logismos_II" : "Λογισμός 2";
} else if (course.contains("Λογισμός Ι")) {
return greeklish ? "logismos_I" : "Λογισμός 1";
} else if (course.contains("Λογική Σχεδίαση")) {
return greeklish ? "logiki_sxediash" : "Λογική Σχεδίαση";
} else if (course.contains("Λειτουργικά Συστήματα")) {
return greeklish ? "OS" : "Λειτουργικά";
} else if (course.contains("Κινητές και Δορυφορικές Επικοινωνίες")) {
return greeklish ? "kinhtes_doryforikes_epik" : "Κινητές & Δορυφορικές Επικοινωνίες";
} else if (course.contains("Κβαντική Φυσική")) {
return greeklish ? "kvantikh" : "Κβαντική";
} else if (course.contains("Θεωρία και Τεχνολογία Πυρηνικών Αντιδραστήρων")) {
return greeklish ? "texn_antidrasthrwn" : "Τεχνολογία Αντιδραστήρων";
} else if (course.contains("Θεωρία Υπολογισμών και Αλγορίθμων")) {
return greeklish ? "thya" : "ΘΥΑ";
} else if (course.contains("Θεωρία Σκέδασης")) {
return greeklish ? "skedash" : "Σκέδαση";
} else if (course.contains("Προηγμένες Τεχνικές Επεξεργασίας Σήματος")) {
return greeklish ? "ptes" : "ΠΤΕΣ";
} else if (course.contains("Τηλεοπτικά Συστήματα")) {
return greeklish ? "tileoptika" : "Τηλεοπτικά";
} else if (course.contains("Ασύρματος Τηλεπικοινωνία ΙΙ")) {
return greeklish ? "asyrmatos_II" : "Ασύρματος 2";
} else if (course.contains("Δίκτυα Τηλεπικοινωνιών")) {
return greeklish ? "diktya_thlep" : "Δίκτυα Τηλέπ.";
} else if (course.contains("Θεωρία Σημάτων και Γραμμικών Συστημάτων")) {
return greeklish ? "analog_shma" : "Σύματα & Συστήματα";
} else if (course.contains("Θεωρία Πληροφοριών")) {
return greeklish ? "theoria_plir" : "Θεωρία Πληρ.";
} else if (course.contains("Οπτικές Επικοινωνίες")) {
return greeklish ? "optikes_thlep" : "Οπτικές Τηλεπ.";
} else if (course.contains("Θεωρία Πιθανοτήτων και Στατιστική")) {
return greeklish ? "pithanothtes" : "Πιθανότητες";
} else if (course.contains("Ημιαγωγά Υλικά: Θεωρία-Διατάξεις")) {
return greeklish ? "Hmiagwga_Ylika" : "Ημιαγωγά Υλικά";
} else if (course.contains("Ηλεκτρονική ΙΙΙ")) {
return greeklish ? "hlektronikh_III" : "Ηλεκτρονική 3";
} else if (course.contains("Ηλεκτρονική ΙΙ")) {
return greeklish ? "hlektronikh_2" : "Ηλεκτρονική 2";
} else if (course.contains("Ηλεκτρονική Ι")) {
return greeklish ? "hlektronikh_1" : "Ηλεκτρονική 1";
} else if (course.contains("Ηλεκτρονικές Διατάξεις και Μετρήσεις")) {
return greeklish ? "hlektron_diatakseis_metrhseis" : "Ηλεκτρονικές Διατάξεις και Μετρήσεις";
} else if (course.contains("Ηλεκτρονικά Ισχύος ΙΙ")) {
return greeklish ? "isxyos_II" : "Ισχύος 2";
} else if (course.contains("Ηλεκτρονικά Ισχύος Ι")) {
return greeklish ? "isxyos_I" : "Ισχύος 1";
} else if (course.contains("Ηλεκτρομαγνητικό Πεδίο ΙΙ")) {
return greeklish ? "pedio_II" : "Πεδίο 2";
} else if (course.contains("Ηλεκτρομαγνητικό Πεδίο Ι")) {
return greeklish ? "pedio_I" : "Πεδίο 1";
} else if (course.contains("Ηλεκτρομαγνητική Συμβατότητα")) {
return greeklish ? "HM_symvatothta" : "H/M Συμβατότητα";
} else if (course.contains("Ηλεκτρολογικά Υλικά")) {
return greeklish ? "ylika" : "Ηλεκτρ. Υλικά";
} else if (course.contains("Ηλεκτρική Οικονομία")) {
return greeklish ? "hlektr_oikonomia" : "Ηλεκτρική Οικονομία";
} else if (course.contains("Ηλεκτρικές Μηχανές Γ'")) {
return greeklish ? "mhxanes_C" : "Μηχανές Γ";
} else if (course.contains("Ηλεκτρικές Μηχανές Β'")) {
return greeklish ? "mhxanes_B" : "Μηχανές Β";
} else if (course.contains("Ηλεκτρικές Μηχανές Α'")) {
return greeklish ? "mhxanes_A" : "Μηχανές Α";
} else if (course.contains("Ηλεκτρικές Μετρήσεις ΙΙ")) {
return greeklish ? "metrhseis_II" : "Μετρήσεις 2";
} else if (course.contains("Ηλεκτρικές Μετρήσεις Ι")) {
return greeklish ? "metrhseis_1" : "Μετρήσεις 1";
} else if (course.contains("Ηλεκτρικά Κυκλώματα ΙΙΙ")) {
return greeklish ? "kyklwmata_I" : "Κυκλώματα 3";
} else if (course.contains("Ηλεκτρικά Κυκλώματα ΙΙ")) {
return greeklish ? "kyklwmata_II" : "Κυκλώματα 2";
} else if (course.contains("Ηλεκτρικά Κυκλώματα Ι")) {
return greeklish ? "kyklwmata_I" : "Κυκλώματα 1";
} else if (course.contains("Ηλεκτρακουστική ΙΙ")) {
return greeklish ? "hlektroakoystikh_II" : "Ηλεκτροακουστική 2";
} else if (course.contains("Ηλεκτρακουστική Ι")) {
return greeklish ? "hlektroakoystikh_I" : "Ηλεκτροακουστική 1";
} else if (course.contains("Εφαρμοσμένη Θερμοδυναμική")) {
return greeklish ? "thermodynamikh" : "Θερμοδυναμική";
} else if (course.contains("Εφαρμοσμένα Μαθηματικά ΙΙ")) {
return greeklish ? "efarmosmena_math_II" : "Εφαρμοσμένα 2";
} else if (course.contains("Εφαρμοσμένα Μαθηματικά Ι")) {
return greeklish ? "efarmosmena_math_I" : "Εφαρμοσμένα 1";
} else if (course.contains("Εφαρμογές Τηλεπικοινωνιακών Διατάξεων")) {
return greeklish ? "efarm_thlep_diataksewn" : "Εφαρμογές Τηλεπ. Διατάξεων";
} else if (course.contains("Ευφυή Συστήματα Ρομπότ")) {
return greeklish ? "eufuh" : "Ευφυή";
} else if (course.contains("Ευρυζωνικά Δίκτυα")) {
return greeklish ? "eyryzwnika" : "Ευρυζωνικά";
} else if (course.contains("Τεχνικές μη Καταστρεπτικών Δοκιμών")) {
return greeklish ? "non_destructive_tests" : "Μη Καταστρεπτικές Δοκιμές";
} else if (course.contains("Φωτονική Τεχνολογία")) {
return greeklish ? "fwtonikh" : "Φωτονική";
} else if (course.contains("Μικροκυματική Τηλεπισκόπηση")) {
return greeklish ? "thlepiskophsh" : "Τηλεπισκόπηση";
} else if (course.contains("Μικροκύματα II")) {
return greeklish ? "mikrokymata_II" : "Μικροκύματα 2";
} else if (course.contains("Επιχειρησιακή Έρευνα")) {
return greeklish ? "epixeirisiaki" : "Επιχειρησιακή Έρευνα";
} else if (course.contains("Ενσωματωμένα Συστήματα Πραγματικού Χρόνου")) {
return greeklish ? "enswmatwmena" : "Ενσωματωμένα";
} else if (course.contains("Εισαγωγή στις εφαρμογές Πυρηνικής Τεχνολογίας")) {
return greeklish ? "Intro_Purhnikh_Texn" : "Εισ. Πυρηνικη Τεχν.";
} else if (course.contains("Εισαγωγή στην Πολιτική Οικονομία")) {
return greeklish ? "polit_oik" : "Πολιτική Οικονομία";
} else if (course.contains("Εισαγωγή στην Ενεργειακή Τεχνολογία ΙΙ")) {
return greeklish ? "EET_2" : "ΕΕΤ2";
} else if (course.contains("Εισαγωγή στην Ενεργειακή Τεχνολογία Ι")) {
return greeklish ? "EET_I" : "ΕΕΤ 1";
} else if (course.contains("Ειδικές Κεραίες, Σύνθεση Κεραιών")) {
return greeklish ? "eidikes_keraies" : "Ειδικές Κεραίες, Σύνθεση Κεραιών";
} else if (course.contains("Ειδικές Αρχιτεκτονικές Υπολογιστών")) {
return greeklish ? "eidikes_arx_ypolog" : "Ειδικές Αρχιτεκτονικές Υπολογιστών";
} else if (course.contains("Ειδικά Κεφάλαια Συστημάτων Ηλεκτρικής Ενέργειας")) {
return greeklish ? "ekshe" : "ΕΚΣΗΕ";
} else if (course.contains("Ειδικά Κεφάλαια Ηλεκτρομαγνητικού Πεδίου Ι")) {
return greeklish ? "eidika_kef_HM_pedioy_I" : "Ειδικά Κεφάλαια Ηλεκτρομαγνητικού Πεδίου Ι";
} else if (course.contains("Ειδικά Κεφάλαια Διαφορικών Εξισώσεων")) {
return greeklish ? "eidika_kef_diaf_eksis" : "Ειδικά Κεφάλαια Διαφορικών Εξισώσεων";
} else if (course.contains("Δομημένος Προγραμματισμός")) {
return greeklish ? "C" : "Δομ. Προγραμμ.";
} else if (course.contains("Δομές Δεδομένων")) {
return greeklish ? "dom_dedomenwn" : "Δομ. Δεδομ.";
} else if (course.contains("Διαχείριση Συστημάτων Ηλεκτρικής Ενέργειας")) {
return greeklish ? "dshe" : "ΔΣΗΕ";
} else if (course.contains("Διαφορικές Εξισώσεις")) {
return greeklish ? "diaforikes" : "Διαφορικές";
} else if (course.contains("Διανεμημένη Παραγωγή")) {
return greeklish ? "dian_paragwgh" : "Διανεμημένη Παραγωγή";
} else if (course.contains("Διακριτά μαθηματικά")) {
return greeklish ? "diakrita" : "Διακριτά Μαθηματικά";
} else if (course.contains("Διακριτά Μαθηματικά")) {
return greeklish ? "diakrita" : "Διακριτά Μαθηματικά";
} else if (course.contains("Διάδοση Ηλεκτρομαγνητικού Κύματος Ι (πρώην Πεδίο ΙΙΙ)")) {
return greeklish ? "diadosi_1" : "Διάδοση 1";
} else if (course.contains("Διάδοση Η/Μ Κύματος ΙΙ")) {
return greeklish ? "diadosi_II" : "Διάδοση 2";
} else if (course.contains("Δίκτυα Υπολογιστών ΙΙ")) {
return greeklish ? "diktya_II" : "Δίκτυα 2";
} else if (course.contains("Δίκτυα Υπολογιστών Ι")) {
return greeklish ? "diktya_I" : "Δίκτυα 1";
} else if (course.contains("Δίκτυα Τηλεπικοινωνιών")) {
return greeklish ? "diktya_thlep" : "Δίκτυα Τηλέπ.";
} else if (course.contains("Γραφική με Υπολογιστές")) {
return greeklish ? "grafikh" : "Γραφική";
} else if (course.contains("Γραμμική Άλγεβρα")) {
return greeklish ? "grammikh_algebra" : "Γραμμ. Άλγεβρ.";
} else if (course.contains("Γεωηλεκτρομαγνητισμός")) {
return greeklish ? "geohlektromagnitismos" : "Γεωηλεκτρομαγνητισμός";
} else if (course.contains("Βιοϊατρική Τεχνολογία")) {
return greeklish ? "vioiatriki" : "Βιοιατρική";
} else if (course.contains("Βιομηχανική Πληροφορική")) {
return greeklish ? "viomix_plir" : "Βιομηχανική Πληρ";
} else if (course.contains("Βιομηχανικά Ηλεκτρονικά")) {
return greeklish ? "bhomix_hlektronika" : "Βιομηχανικά Ηλεκτρονικά";
} else if (course.contains("Βάσεις Δεδομένων")) {
return greeklish ? "vaseis" : "Βάσεις";
} else if (course.contains("Ασύρματος Τηλεπικοινωνία ΙΙ")) {
return greeklish ? "asyrmatos_II" : "Ασύρματος 2";
} else if (course.contains("Ασύρματος Τηλεπικοινωνία Ι")) {
return greeklish ? "asyrmatos_I" : "Ασύρματος 1";
} else if (course.contains("Ασφάλεια Πληροφοριακών Συστημάτων")) {
return greeklish ? "asfaleia" : "Ασφάλεια";
} else if (course.contains("Ασαφή Συστήματα")) {
return greeklish ? "asafh" : "Ασαφή";
} else if (course.contains("Αρχιτεκτονική Υπολογιστών")) {
return greeklish ? "arx_ypologistwn" : "Αρχ. Υπολογιστών";
} else if (course.contains("Αρχές Παράλληλης Επεξεργασίας")) {
return greeklish ? "arxes_parall_epeksergasias" : "Αρχές Παράλληλης Επεξεργασίας";
} else if (course.contains("Αρχές Οικονομίας")) {
return greeklish ? "arx_oikonomias" : "Αρχές Οικονομίας";
} else if (course.contains("Αριθμητική Ανάλυση")) {
return greeklish ? "arith_anal" : "Αριθμ. Ανάλυση";
} else if (course.contains("Αξιοπιστία Συστημάτων")) {
return greeklish ? "aksiopistia_systhmatwn" : "Αξιοπιστία Συστημάτων";
} else if (course.contains("Αντικειμενοστραφής Προγραμματισμός")) {
return greeklish ? "OOP" : "Αντικειμενοστραφής";
} else if (course.contains("Αναλογικές Τηλεπικοινωνίες (πρώην Τηλεπικοινωνιακά Συστήματα Ι)")) {
return greeklish ? "anal_thlep" : "Αναλογικές Τηλεπ.";
} else if (course.contains("Αναγνώριση Προτύπων")) {
return greeklish ? "protipa" : "Αναγνώριση Προτύπων";
} else if (course.contains("Ανάλυση και Σχεδίαση Αλγορίθμων")) {
return greeklish ? "algorithms" : "Αλγόριθμοι";
} else if (course.contains("Ανάλυση Χρονοσειρών")) {
return greeklish ? "xronoseires" : "Χρονοσειρές";
} else if (course.contains("Ανάλυση Συστημάτων Ηλεκτρικής Ενέργειας")) {
return greeklish ? "ASHE" : "ΑΣΗΕ";
} else if (course.contains("Ανάλυση Ηλεκτρικών Κυκλωμάτων με Υπολογιστή")) {
return greeklish ? "analysh_hlektr_kykl" : "Ανάλυση Ηλεκτρικ. Κυκλ. με Υπολογιστή";
} else if (course.contains("Ακουστική ΙΙ")) {
return greeklish ? "akoystikh_II" : "Ακουστική 2";
} else if (course.contains("Ακουστική Ι")) {
return greeklish ? "akoystikh_I" : "Ακουστική 1";
} else {
return null;
}

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

@ -8,6 +8,7 @@ import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Environment;
import android.provider.OpenableColumns;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@ -25,7 +26,7 @@ import timber.log.Timber;
class UploadsHelper {
private static final int DEFAULT_MIN_WIDTH_QUALITY = 400;
private static final String TEMP_IMAGE_NAME = "tempUploadFile.jpg";
private static final String CACHE_IMAGE_NAME = "tempUploadFile.jpg";
@NonNull
static String filenameFromUri(Context context, Uri uri) {
@ -53,7 +54,7 @@ class UploadsHelper {
static String createTempFile(Context context, Uri fileUri, String newFilename) {
String oldFilename = filenameFromUri(context, fileUri);
String fileExtension = oldFilename.substring(oldFilename.indexOf("."));
String destinationFilename = android.os.Environment.getExternalStorageDirectory().getPath() +
String destinationFilename = Environment.getExternalStorageDirectory().getPath() +
File.separatorChar + "~tmp_mThmmy_uploads" + File.separatorChar + newFilename + fileExtension;
File tempDirectory = new File(android.os.Environment.getExternalStorageDirectory().getPath() +
@ -99,16 +100,35 @@ class UploadsHelper {
return destinationFilename;
}
static File getTempFile(Context context) {
File imageFile = new File(context.getExternalCacheDir(), TEMP_IMAGE_NAME);
static File getCacheFile(Context context) {
File imageFile = new File(context.getExternalCacheDir(), CACHE_IMAGE_NAME);
//noinspection ResultOfMethodCallIgnored
imageFile.getParentFile().mkdirs();
return imageFile;
}
static boolean deleteTempFile(String destinationFilename) {
File file = new File(destinationFilename);
return file.delete();
@SuppressWarnings("ResultOfMethodCallIgnored")
static void deleteTempFiles() {
File tempFilesDirectory = new File(Environment.getExternalStorageDirectory().getPath() +
File.separatorChar + "~tmp_mThmmy_uploads");
if (tempFilesDirectory.isDirectory()) {
String[] tempFilesArray = tempFilesDirectory.list();
for (String tempFile : tempFilesArray) {
new File(tempFilesDirectory, tempFile).delete();
}
tempFilesDirectory.delete();
}
}
@SuppressWarnings("ResultOfMethodCallIgnored")
static void deleteCacheFiles(Context context) {
File cacheFilesDirectory = context.getExternalCacheDir();
assert cacheFilesDirectory != null;
String[] tempFilesArray = cacheFilesDirectory.list();
for (String tempFile : tempFilesArray) {
new File(cacheFilesDirectory, tempFile).delete();
}
}
/**

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

@ -7,7 +7,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
@ -45,12 +44,12 @@ import java.util.ArrayList;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.AboutActivity;
import gr.thmmy.mthmmy.activities.LoginActivity;
import gr.thmmy.mthmmy.activities.upload.UploadActivity;
import gr.thmmy.mthmmy.activities.bookmarks.BookmarkActivity;
import gr.thmmy.mthmmy.activities.downloads.DownloadsActivity;
import gr.thmmy.mthmmy.activities.main.MainActivity;
import gr.thmmy.mthmmy.activities.profile.ProfileActivity;
import gr.thmmy.mthmmy.activities.settings.SettingsActivity;
import gr.thmmy.mthmmy.activities.upload.UploadActivity;
import gr.thmmy.mthmmy.model.Bookmark;
import gr.thmmy.mthmmy.model.ThmmyFile;
import gr.thmmy.mthmmy.services.DownloadHelper;

47
app/src/main/java/gr/thmmy/mthmmy/base/BaseApplication.java

@ -5,6 +5,7 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.util.DisplayMetrics;
import android.widget.ImageView;
@ -14,6 +15,7 @@ import com.crashlytics.android.core.CrashlyticsCore;
import com.franmontiel.persistentcookiejar.PersistentCookieJar;
import com.franmontiel.persistentcookiejar.cache.SetCookieCache;
import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor;
import com.google.firebase.analytics.FirebaseAnalytics;
import com.jakewharton.picasso.OkHttp3Downloader;
import com.mikepenz.fontawesome_typeface_library.FontAwesome;
import com.mikepenz.iconics.IconicsDrawable;
@ -24,7 +26,6 @@ import com.squareup.picasso.Picasso;
import net.gotev.uploadservice.UploadService;
import net.gotev.uploadservice.okhttp.OkHttpStack;
import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
@ -34,16 +35,17 @@ import gr.thmmy.mthmmy.session.SessionManager;
import gr.thmmy.mthmmy.utils.CrashReportingTree;
import io.fabric.sdk.android.Fabric;
import okhttp3.HttpUrl;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import timber.log.Timber;
public class BaseApplication extends Application {
private static BaseApplication baseApplication; //BaseApplication singleton
// Client & SessionManager
//FirebaseAnalytics
private FirebaseAnalytics firebaseAnalytics;
//Client & SessionManager
private OkHttpClient client;
private SessionManager sessionManager;
@ -69,32 +71,31 @@ public class BaseApplication extends Application {
// Initialize Fabric with the debug-disabled crashlytics.
Fabric.with(this, crashlyticsKit);
// Initialize timber
if (BuildConfig.DEBUG) {
if (BuildConfig.DEBUG)
Timber.plant(new Timber.DebugTree());
} else {
else
Timber.plant(new CrashReportingTree());
}
// Analytics init
firebaseAnalytics = FirebaseAnalytics.getInstance(this);
SharedPreferences sharedPrefs = getSharedPreferences(SHARED_PREFS_NAME, MODE_PRIVATE);
SharedPrefsCookiePersistor sharedPrefsCookiePersistor = new SharedPrefsCookiePersistor(getApplicationContext());
PersistentCookieJar cookieJar = new PersistentCookieJar(new SetCookieCache(), sharedPrefsCookiePersistor);
client = new OkHttpClient.Builder()
.cookieJar(cookieJar)
.addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
HttpUrl oldUrl = chain.request().url();
if (Objects.equals(chain.request().url().host(), "www.thmmy.gr")) {
if (!oldUrl.toString().contains("theme=4")) {
//Probably works but needs more testing:
HttpUrl newUrl = oldUrl.newBuilder().addQueryParameter("theme", "4").build();
request = request.newBuilder().url(newUrl).build();
}
.addInterceptor(chain -> {
Request request = chain.request();
HttpUrl oldUrl = chain.request().url();
if (Objects.equals(chain.request().url().host(), "www.thmmy.gr")) {
if (!oldUrl.toString().contains("theme=4")) {
//Probably works but needs more testing:
HttpUrl newUrl = oldUrl.newBuilder().addQueryParameter("theme", "4").build();
request = request.newBuilder().url(newUrl).build();
}
return chain.proceed(request);
}
return chain.proceed(request);
})
.connectTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
@ -140,6 +141,12 @@ public class BaseApplication extends Application {
dpWidth = displayMetrics.widthPixels / displayMetrics.density;
}
public void logFirebaseAnalyticsEvent(String event, Bundle params) {
firebaseAnalytics.logEvent(event, params);
}
//Getters
public OkHttpClient getClient() {
return client;
}

55
app/src/main/java/gr/thmmy/mthmmy/editorview/AutoFitGridLayout.java

@ -0,0 +1,55 @@
package gr.thmmy.mthmmy.editorview;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.widget.GridLayout;
import gr.thmmy.mthmmy.R;
public class AutoFitGridLayout extends GridLayout {
private int columnWidth;
private int defaultColumnCount;
public AutoFitGridLayout(Context context) {
super(context);
init(context, null, 0);
}
public AutoFitGridLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0);
}
public AutoFitGridLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
}
public void init(Context context, AttributeSet attrs, int defStyleAttr) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AutoFitGridLayout, 0, defStyleAttr);
try {
columnWidth = a.getDimensionPixelSize(R.styleable.AutoFitGridLayout_columnWidth, 0);
int[] set = {android.R.attr.columnCount};
a = context.obtainStyledAttributes(attrs, set, 0, defStyleAttr);
defaultColumnCount = a.getInt(0, 6);
} finally {
a.recycle();
}
setColumnCount(1);
}
@Override
protected void onMeasure(int widthSpec, int heightSpec) {
super.onMeasure(widthSpec, heightSpec);
int width = MeasureSpec.getSize(widthSpec);
if (columnWidth > 0 && width > 0) {
int totalSpace = width - getPaddingRight() - getPaddingLeft();
int columnCount = Math.max(1, totalSpace / columnWidth);
setColumnCount(columnCount);
} else {
setColumnCount(defaultColumnCount);
}
}
}

283
app/src/main/java/gr/thmmy/mthmmy/editorview/EditorView.java

@ -0,0 +1,283 @@
package gr.thmmy.mthmmy.editorview;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable;
import android.support.design.widget.TextInputEditText;
import android.support.design.widget.TextInputLayout;
import android.support.v7.widget.AppCompatImageButton;
import android.text.Editable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.ScrollView;
import java.util.Objects;
import gr.thmmy.mthmmy.R;
public class EditorView extends LinearLayout {
private SparseArray<String> colors = new SparseArray<>();
private TextInputLayout edittextWrapper;
private TextInputEditText editText;
private AppCompatImageButton emojiButton;
private AppCompatImageButton submitButton;
private EmojiKeyboard.EmojiKeyboardOwner emojiKeyboardOwner;
public EditorView(Context context) {
super(context);
init(context, null);
}
public EditorView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public EditorView(Context context, AttributeSet attrs, int defStyleAttrs) {
super(context, attrs, defStyleAttrs);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
LayoutInflater.from(context).inflate(R.layout.editor_view, this, true);
setOrientation(VERTICAL);
edittextWrapper = findViewById(R.id.editor_edittext_wrapper);
editText = findViewById(R.id.editor_edittext);
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.EditorView, 0, 0);
try {
editText.setHint(a.getString(R.styleable.EditorView_hint));
} finally {
a.recycle();
}
// without this, the editor gets default window background
Drawable background = getBackground();
for (int i = 0; i < getChildCount(); i++) {
getChildAt(i).setBackground(background);
}
emojiButton = findViewById(R.id.emoji_keyboard_button);
editText.setOnTouchListener((v, event) -> {
if (emojiKeyboardOwner.isEmojiKeyboardVisible()) return true;
return false;
});
emojiButton.setOnClickListener(view -> {
InputMethodManager imm = (InputMethodManager) context.getSystemService(Activity.INPUT_METHOD_SERVICE);
assert imm != null;
if (emojiKeyboardOwner.isEmojiKeyboardVisible()) {
editText.requestFocus();
imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT);
emojiButton.setImageResource(R.drawable.ic_tag_faces_24dp);
} else {
imm.hideSoftInputFromWindow(getWindowToken(), 0);
view.clearFocus();
emojiButton.setImageResource(R.drawable.ic_keyboard_24dp);
}
emojiKeyboardOwner.setEmojiKeyboardVisible(!emojiKeyboardOwner.isEmojiKeyboardVisible());
});
submitButton = findViewById(R.id.submit_button);
findViewById(R.id.bold_button).setOnClickListener(view -> {
boolean hadTextSelection = editText.hasSelection();
getText().insert(editText.getSelectionStart(), "[b]");
getText().insert(editText.getSelectionEnd(), "[/b]");
editText.setSelection(hadTextSelection ? editText.getSelectionEnd() : editText.getSelectionStart() - 4);
});
findViewById(R.id.italic_button).setOnClickListener(view -> {
boolean hadTextSelection = editText.hasSelection();
getText().insert(editText.getSelectionStart(), "[i]");
getText().insert(editText.getSelectionEnd(), "[/i]");
editText.setSelection(hadTextSelection ? editText.getSelectionEnd() : editText.getSelectionStart() - 4);
});
findViewById(R.id.underline_button).setOnClickListener(view -> {
boolean hadTextSelection = editText.hasSelection();
getText().insert(editText.getSelectionStart(), "[u]");
getText().insert(editText.getSelectionEnd(), "[/u]");
editText.setSelection(hadTextSelection ? editText.getSelectionEnd() : editText.getSelectionStart() - 4);
});
findViewById(R.id.strikethrough_button).setOnClickListener(view -> {
boolean hadTextSelection = editText.hasSelection();
getText().insert(editText.getSelectionStart(), "[s]");
getText().insert(editText.getSelectionEnd(), "[/s]");
editText.setSelection(hadTextSelection ? editText.getSelectionEnd() : editText.getSelectionStart() - 4);
});
colors.append(R.id.black, "black");
colors.append(R.id.red, "red");
colors.append(R.id.yellow, "yellow");
colors.append(R.id.pink, "pink");
colors.append(R.id.green, "green");
colors.append(R.id.orange, "orange");
colors.append(R.id.purple, "purple");
colors.append(R.id.blue, "blue");
colors.append(R.id.beige, "beige");
colors.append(R.id.brown, "brown");
colors.append(R.id.teal, "teal");
colors.append(R.id.navy, "navy");
colors.append(R.id.maroon, "maroon");
colors.append(R.id.lime_green, "limegreen");
findViewById(R.id.text_color_button).setOnClickListener(view -> {
PopupWindow popupWindow = new PopupWindow(view.getContext());
popupWindow.setHeight(LayoutParams.WRAP_CONTENT);
popupWindow.setWidth(LayoutParams.WRAP_CONTENT);
popupWindow.setFocusable(true);
ScrollView colorPickerScrollview = (ScrollView) LayoutInflater.from(context).inflate(R.layout.editor_view_color_picker, null);
LinearLayout colorPicker = (LinearLayout) colorPickerScrollview.getChildAt(0);
popupWindow.setContentView(colorPickerScrollview);
for (int i = 0; i < colorPicker.getChildCount(); i++) {
colorPicker.getChildAt(i).setOnClickListener(v -> {
boolean hadTextSelection = editText.hasSelection();
getText().insert(editText.getSelectionStart(), "[color=" + colors.get(v.getId()) + "]");
getText().insert(editText.getSelectionEnd(), "[/color]");
editText.setSelection(hadTextSelection ? editText.getSelectionEnd() : editText.getSelectionStart() - 8);
popupWindow.dismiss();
});
}
popupWindow.showAsDropDown(view);
});
findViewById(R.id.text_size_button).setOnClickListener(view -> {
boolean hadTextSelection = editText.hasSelection();
getText().insert(editText.getSelectionStart(), "[size=10pt]");
getText().insert(editText.getSelectionEnd(), "[/size]");
editText.setSelection(hadTextSelection ? editText.getSelectionEnd() : editText.getSelectionStart() - 7);
});
findViewById(R.id.font_button).setOnClickListener(view -> {
boolean hadTextSelection = editText.hasSelection();
getText().insert(editText.getSelectionStart(), "[font=Verdana]");
getText().insert(editText.getSelectionEnd(), "[/font]");
editText.setSelection(hadTextSelection ? editText.getSelectionEnd() : editText.getSelectionStart() - 7);
});
findViewById(R.id.unordered_list_button).setOnClickListener(view -> {
boolean hadTextSelection = editText.hasSelection();
getText().insert(editText.getSelectionStart(), "[list]\n[li]");
getText().insert(editText.getSelectionEnd(), "[/li]\n[li][/li]\n[/list]");
editText.setSelection(hadTextSelection ? editText.getSelectionEnd() - 13 : editText.getSelectionStart() - 23);
});
findViewById(R.id.align_left_button).setOnClickListener(view -> {
boolean hadTextSelection = editText.hasSelection();
getText().insert(editText.getSelectionStart(), "[left]");
getText().insert(editText.getSelectionEnd(), "[/left]");
editText.setSelection(hadTextSelection ? editText.getSelectionEnd() : editText.getSelectionStart() - 7);
});
findViewById(R.id.align_center_button).setOnClickListener(view -> {
boolean hadTextSelection = editText.hasSelection();
getText().insert(editText.getSelectionStart(), "[center]");
getText().insert(editText.getSelectionEnd(), "[/center]");
editText.setSelection(hadTextSelection ? editText.getSelectionEnd() : editText.getSelectionStart() - 9);
});
findViewById(R.id.align_right_button).setOnClickListener(view -> {
boolean hadTextSelection = editText.hasSelection();
getText().insert(editText.getSelectionStart(), "[right]");
getText().insert(editText.getSelectionEnd(), "[/right]");
editText.setSelection(hadTextSelection ? editText.getSelectionEnd() : editText.getSelectionStart() - 8);
});
findViewById(R.id.link_button).setOnClickListener(view -> {
LinearLayout dialogBody = (LinearLayout) LayoutInflater.from(context)
.inflate(R.layout.dialog_create_link, null);
TextInputLayout linkUrl = dialogBody.findViewById(R.id.link_url_input);
linkUrl.setOnClickListener(view1 -> linkUrl.setError(null));
TextInputLayout linkText = dialogBody.findViewById(R.id.link_text_input);
linkText.setOnClickListener(view2 -> linkText.setError(null));
boolean hadTextSelection = editText.hasSelection();
int start = editText.getSelectionStart(), end = editText.getSelectionEnd();
if (editText.hasSelection()) {
linkText.getEditText().setText(
editText.getText().toString().substring(editText.getSelectionStart(), editText.getSelectionEnd()));
}
new AlertDialog.Builder(context, R.style.AppCompatAlertDialogStyleAccent)
.setTitle(R.string.dialog_create_link_title)
.setView(dialogBody)
.setPositiveButton(R.string.ok, (dialog, which) -> {
if (TextUtils.isEmpty(Objects.requireNonNull(linkUrl.getEditText()).getText().toString())) {
linkUrl.setError(context.getString(R.string.input_field_required));
return;
}
if (TextUtils.isEmpty(Objects.requireNonNull(linkText.getEditText()).getText().toString())) {
linkUrl.setError(context.getString(R.string.input_field_required));
return;
}
if (hadTextSelection) editText.getText().delete(start, end);
getText().insert(editText.getSelectionStart(), "[url=" +
Objects.requireNonNull(linkUrl.getEditText()).getText().toString() + "]" +
Objects.requireNonNull(linkText.getEditText()).getText().toString() + "[/url]");
})
.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss())
.show();
});
findViewById(R.id.quote_button).setOnClickListener(view -> {
boolean hadTextSelection = editText.hasSelection();
getText().insert(editText.getSelectionStart(), "[quote]");
getText().insert(editText.getSelectionEnd(), "[/quote]");
editText.setSelection(hadTextSelection ? editText.getSelectionEnd() : editText.getSelectionStart() - 8);
});
findViewById(R.id.code_button).setOnClickListener(view -> {
boolean hadTextSelection = editText.hasSelection();
getText().insert(editText.getSelectionStart(), "[code]");
getText().insert(editText.getSelectionEnd(), "[/code]");
editText.setSelection(hadTextSelection ? editText.getSelectionEnd() : editText.getSelectionStart() - 7);
});
findViewById(R.id.math_button).setOnClickListener(view -> {
boolean hadTextSelection = editText.hasSelection();
getText().insert(editText.getSelectionStart(), "[tex]");
getText().insert(editText.getSelectionEnd(), "[/tex]");
editText.setSelection(hadTextSelection ? editText.getSelectionEnd() : editText.getSelectionStart() - 6);
});
}
public TextInputEditText getEditText() {
return editText;
}
public Editable getText() {
return editText.getText();
}
public void setText(Editable text) {
editText.setText(text);
}
public void setText(CharSequence text) {
editText.setText(text);
}
public void setError(@Nullable CharSequence text) {
edittextWrapper.setError(text);
}
public void setOnSubmitListener(OnClickListener onSubmitListener) {
submitButton.setOnClickListener(onSubmitListener);
}
public void setEmojiKeyboardOwner(EmojiKeyboard.EmojiKeyboardOwner emojiKeyboardOwner) {
this.emojiKeyboardOwner = emojiKeyboardOwner;
}
public InputConnection getInputConnection() {
return editText.onCreateInputConnection(new EditorInfo());
}
public void updateEmojiKeyboardVisibility() {
if (emojiKeyboardOwner.isEmojiKeyboardVisible())
emojiButton.setImageResource(R.drawable.ic_keyboard_24dp);
else
emojiButton.setImageResource(R.drawable.ic_tag_faces_24dp);
}
}

241
app/src/main/java/gr/thmmy/mthmmy/editorview/EmojiKeyboard.java

@ -0,0 +1,241 @@
package gr.thmmy.mthmmy.editorview;
import android.content.Context;
import android.os.Handler;
import android.support.v7.widget.AppCompatImageButton;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.inputmethod.InputConnection;
import android.widget.LinearLayout;
import gr.thmmy.mthmmy.R;
public class EmojiKeyboard extends LinearLayout {
// TODO: Sort emojis in a way that makes sense
private final Emoji[] emojis = {new Emoji(R.drawable.emoji_smiley, ":)"),
new Emoji(R.drawable.emoji_wink, ";)"),
new Emoji(R.drawable.emoji_cheesy, ":D"),
new Emoji(R.drawable.emoji_grin, ";D"),
// removed repeated angry emoji
new Emoji(R.drawable.emoji_angry, ">:("),
new Emoji(R.drawable.emoji_sad, ":("),
new Emoji(R.drawable.emoji_shocked, ":o"),
new Emoji(R.drawable.emoji_cool, "8))"),
new Emoji(R.drawable.emoji_huh, ":???:"),
new Emoji(R.drawable.emoji_rolleyes, "::)"),
new Emoji(R.drawable.emoji_tongue, ":P"),
new Emoji(R.drawable.emoji_embarrassed, ":-["),
new Emoji(R.drawable.emoji_lipsrsealed, ":-X"),
new Emoji(R.drawable.emoji_undecided, ":-\\\\"),
new Emoji(R.drawable.emoji_kiss, ":-*"),
new Emoji(R.drawable.emoji_cry, ":'("),
new Emoji(R.drawable.emoji_heart, "<3"),
// removed repeated lock emoji
new Emoji(R.drawable.emoji_locked, "^lock^"),
new Emoji(R.drawable.emoji_roll_over, "^rollover^"),
new Emoji(R.drawable.emoji_redface, "^redface^"),
new Emoji(R.drawable.emoji_confused, "^confused^"),
new Emoji(R.drawable.emoji_innocent, "^innocent^"),
new Emoji(R.drawable.emoji_sleep, "^sleep^"),
new Emoji(R.drawable.emoji_lips_sealed, "^sealed^"),
new Emoji(R.drawable.emoji_cool2, "^cool^"),
new Emoji(R.drawable.emoji_monster, "^monster^"),
new Emoji(R.drawable.emoji_crazy, "^crazy^"),
new Emoji(R.drawable.emoji_mad, "^mad^"),
new Emoji(R.drawable.emoji_wav, "^wav^"),
new Emoji(R.drawable.emoji_binkybaby, "^binkybaby^"),
new Emoji(R.drawable.emoji_police, "^police^"),
new Emoji(R.drawable.emoji_dontknow, "^dontknow^"),
//removed repeated angry hot emoji
new Emoji(R.drawable.emoji_angry_hot, "^angryhot^"),
new Emoji(R.drawable.emoji_foyska, "^fouska^"),
new Emoji(R.drawable.emoji_e10_7_3e, "^sfinaki^"),
new Emoji(R.drawable.emoji_bang_head, "^banghead^"),
new Emoji(R.drawable.emoji_crybaby, "^crybaby^"),
new Emoji(R.drawable.emoji_hello, "^hello^"),
new Emoji(R.drawable.emoji_jerk, "^jerk^"),
new Emoji(R.drawable.emoji_nono, "^nono^"),
new Emoji(R.drawable.emoji_notworthy, "^notworthy^"),
new Emoji(R.drawable.emoji_off_topic, "^off-topic^"),
new Emoji(R.drawable.emoji_puke, "^puke^"),
new Emoji(R.drawable.emoji_shout, "^shout^"),
new Emoji(R.drawable.emoji_slurp, "^slurp^"),
new Emoji(R.drawable.emoji_superconfused, "^superconfused^"),
new Emoji(R.drawable.emoji_superinnocent, "^superinnocent^"),
new Emoji(R.drawable.emoji_cell_phone, "^cellPhone^"),
new Emoji(R.drawable.emoji_idiot, "^idiot^"),
new Emoji(R.drawable.emoji_knuppel, "^knuppel^"),
new Emoji(R.drawable.emoji_tickedoff, "^tickedOff^"),
new Emoji(R.drawable.emoji_peace, "^peace^"),
new Emoji(R.drawable.emoji_suspicious, "^suspicious^"),
new Emoji(R.drawable.emoji_caffine, "^caffine^"),
new Emoji(R.drawable.emoji_argue, "^argue^"),
new Emoji(R.drawable.emoji_banned2, "^banned2^"),
new Emoji(R.drawable.emoji_banned, "^banned^"),
new Emoji(R.drawable.emoji_bath, "^bath^"),
new Emoji(R.drawable.emoji_beg, "^beg^"),
new Emoji(R.drawable.emoji_bluescreen, "^bluescreen^"),
new Emoji(R.drawable.emoji_boil, "^boil^"),
new Emoji(R.drawable.emoji_bye, "^bye^"),
new Emoji(R.drawable.emoji_callmerip, "^callmerip^"),
new Emoji(R.drawable.emoji_carnaval, "^carnaval^"),
new Emoji(R.drawable.emoji_clap, "^clap^"),
new Emoji(R.drawable.emoji_coffeepot, "^coffepot^"),
new Emoji(R.drawable.emoji_crap, "^crap^"),
new Emoji(R.drawable.emoji_curses, "^curses^"),
new Emoji(R.drawable.emoji_funny, "^funny^"),
new Emoji(R.drawable.emoji_guitar1, "^guitar^"),
new Emoji(R.drawable.emoji_icon_kissy, "^kissy^"),
new Emoji(R.drawable.emoji_band, "^band^"),
new Emoji(R.drawable.emoji_ivres, "^ivres^"),
new Emoji(R.drawable.emoji_kaloe, "^kaloe^"),
new Emoji(R.drawable.emoji_kremala, "^kremala^"),
new Emoji(R.drawable.emoji_moon, "^moon^"),
new Emoji(R.drawable.emoji_mopping, "^mopping^"),
new Emoji(R.drawable.emoji_mountza, "^mountza^"),
new Emoji(R.drawable.emoji_pcsleep, "^pcsleep^"),
new Emoji(R.drawable.emoji_pinokio, "^pinokio^"),
new Emoji(R.drawable.emoji_poke, "^poke^"),
new Emoji(R.drawable.emoji_seestars, "^seestars^"),
new Emoji(R.drawable.emoji_sfyri, "^sfyri^"),
new Emoji(R.drawable.emoji_spam2, "^spam^"),
new Emoji(R.drawable.emoji_esuper, "^super^"),
new Emoji(R.drawable.emoji_tafos, "^tafos^"),
new Emoji(R.drawable.emoji_tomatomourh, "^tomato^"),
new Emoji(R.drawable.emoji_ytold, "^ytold^"),
new Emoji(R.drawable.emoji_beer2, "^beer^"),
new Emoji(R.drawable.emoji_yu, "^yue^"),
new Emoji(R.drawable.emoji_a_eatpaper, "^eatpaper^"),
new Emoji(R.drawable.emoji_fritz, "^fritz^"),
new Emoji(R.drawable.emoji_wade, "^wade^"),
new Emoji(R.drawable.emoji_lypi, "^lypi^"),
new Emoji(R.drawable.emoji_megashok1wq, "^aytoxeir^"),
new Emoji(R.drawable.emoji_victory, "^victory^"),
new Emoji(R.drawable.emoji_filarakia, "^filarakia^"),
new Emoji(R.drawable.emoji_bonjour_97213, "^hat^"),
new Emoji(R.drawable.emoji_curtseyqi9, "^miss^"),
new Emoji(R.drawable.emoji_rofl, "^rolfmao^"),
new Emoji(R.drawable.emoji_question, "^que^"),
new Emoji(R.drawable.emoji_shifty, "^shifty^"),
new Emoji(R.drawable.emoji_shy, "^shy^"),
new Emoji(R.drawable.emoji_music, "^music_listen^"),
new Emoji(R.drawable.emoji_shamed_bag, "^bagface^"),
new Emoji(R.drawable.emoji_rotfl, "^rotate^"),
new Emoji(R.drawable.emoji_love, "^love^"),
new Emoji(R.drawable.emoji_speech, "^speech^"),
new Emoji(R.drawable.emoji_facepalm, "^facepalm^"),
new Emoji(R.drawable.emoji_shocked2, "^shocked^"),
new Emoji(R.drawable.emoji_extremely_shocked, "^ex_shocked^"),
new Emoji(R.drawable.emoji_smurf, "^smurf^")
};
InputConnection inputConnection;
public EmojiKeyboard(Context context) {
this(context, null, 0);
}
public EmojiKeyboard(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public EmojiKeyboard(Context context, AttributeSet attrs, int defStyleAttrs) {
super(context, attrs, defStyleAttrs);
init(context, attrs);
}
public void init(Context context, AttributeSet attrs) {
LayoutInflater.from(context).inflate(R.layout.emoji_keyboard, this, true);
setOrientation(VERTICAL);
RecyclerView emojiRecyclerview = findViewById(R.id.emoji_recyclerview);
emojiRecyclerview.setHasFixedSize(true);
GridLayoutManager emojiLayoutManager = new GridLayoutManager(context, 6);
emojiLayoutManager.setSpanSizeLookup(new EmojiColumnSpanLookup());
emojiRecyclerview.setLayoutManager(emojiLayoutManager);
EmojiKeyboardAdapter emojiKeyboardAdapter = new EmojiKeyboardAdapter(emojis);
emojiKeyboardAdapter.setOnEmojiClickListener((view, position) -> {
if (inputConnection == null) return;
String bbcode = emojis[position].getBbcode();
inputConnection.commitText(" " + bbcode, 1);
});
emojiRecyclerview.setAdapter(emojiKeyboardAdapter);
AppCompatImageButton backspaceButton = findViewById(R.id.backspace_button);
// backspace behavior
final Handler handler = new Handler();
Runnable longPressed = new Runnable() {
@Override
public void run() {
inputConnection.deleteSurroundingText(1, 0);
handler.postDelayed(this, 50);
}
};
backspaceButton.setOnTouchListener((v, event) -> {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
CharSequence selectedText = inputConnection.getSelectedText(0);
if (TextUtils.isEmpty(selectedText))
inputConnection.deleteSurroundingText(1, 0);
else
inputConnection.commitText("", 1);
handler.postDelayed(longPressed, 400);
break;
case MotionEvent.ACTION_UP:
handler.removeCallbacks(longPressed);
break;
}
return true;
});
}
public void setInputConnection(InputConnection inputConnection) {
this.inputConnection = inputConnection;
}
public interface EmojiKeyboardOwner {
void setEmojiKeyboardVisible(boolean visible);
boolean isEmojiKeyboardVisible();
void setEmojiKeyboardInputConnection(InputConnection ic);
}
class Emoji {
final int src;
final String bbcode;
public Emoji(int src, String bbcode) {
this.src = src;
this.bbcode = bbcode;
}
public int getSrc() {
return src;
}
public String getBbcode() {
return bbcode;
}
}
class EmojiColumnSpanLookup extends GridLayoutManager.SpanSizeLookup {
@Override
public int getSpanSize(int position) {
switch (emojis[position].getSrc()) {
case R.drawable.emoji_wav:
return 4;
case R.drawable.emoji_band:
return 3;
case R.drawable.emoji_pcsleep:
return 2;
default:
return 1;
}
}
}
}

64
app/src/main/java/gr/thmmy/mthmmy/editorview/EmojiKeyboardAdapter.java

@ -0,0 +1,64 @@
package gr.thmmy.mthmmy.editorview;
import android.graphics.drawable.AnimationDrawable;
import android.support.annotation.NonNull;
import android.support.v7.widget.AppCompatImageButton;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import gr.thmmy.mthmmy.R;
public class EmojiKeyboardAdapter extends RecyclerView.Adapter<EmojiKeyboardAdapter.EmojiViewHolder> {
private EmojiKeyboard.Emoji[] emojiIds;
private OnEmojiClickListener listener;
public EmojiKeyboardAdapter(EmojiKeyboard.Emoji[] emojiIds) {
this.emojiIds = emojiIds;
}
public void setOnEmojiClickListener(OnEmojiClickListener listener) {
this.listener = listener;
}
@NonNull
@Override
public EmojiViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
AppCompatImageButton emojiButton = (AppCompatImageButton) LayoutInflater.from(parent.getContext())
.inflate(R.layout.emoji_keyboard_grid_cell, parent, false);
return new EmojiViewHolder(emojiButton);
}
@Override
public void onBindViewHolder(@NonNull EmojiViewHolder holder, int position) {
holder.emojiButton.setOnClickListener(view -> listener.onEmojiClick(view, position));
}
@Override
public void onViewAttachedToWindow(@NonNull EmojiViewHolder holder) {
holder.emojiButton.setImageResource(emojiIds[holder.getAdapterPosition()].getSrc());
if (holder.emojiButton.getDrawable() instanceof AnimationDrawable) {
AnimationDrawable emojiAnimation = (AnimationDrawable) holder.emojiButton.getDrawable();
emojiAnimation.start();
}
}
@Override
public int getItemCount() {
return emojiIds.length;
}
static class EmojiViewHolder extends RecyclerView.ViewHolder {
AppCompatImageButton emojiButton;
EmojiViewHolder(AppCompatImageButton emojiButton) {
super(emojiButton);
this.emojiButton = emojiButton;
}
}
interface OnEmojiClickListener {
void onEmojiClick(View view, int position);
}
}

1
app/src/main/java/gr/thmmy/mthmmy/services/DownloadHelper.java

@ -41,6 +41,7 @@ public class DownloadHelper {
request.setDestinationInExternalPublicDir(SAVE_DIR.getName(), fileName);
request.allowScanningByMediaScanner();
BaseApplication.getInstance().logFirebaseAnalyticsEvent("file_download", null);
downloadManager.enqueue(request);
} catch (Exception e) {
Toast.makeText(applicationContext, "Download failed...", Toast.LENGTH_SHORT).show();

42
app/src/main/java/gr/thmmy/mthmmy/utils/CrashReporter.java

@ -0,0 +1,42 @@
package gr.thmmy.mthmmy.utils;
import com.crashlytics.android.Crashlytics;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import gr.thmmy.mthmmy.utils.parsing.ParseHelpers;
public class CrashReporter {
private static final int STRING_BATCH_LENGTH = 250;
private CrashReporter() {}
public static void reportDocument(Document document, String key) {
String documentString = document.toString();
ParseHelpers.Language language = ParseHelpers.Language.getLanguage(document);
Elements postRows;
if (language.is(ParseHelpers.Language.GREEK))
postRows = document.select("form[id=quickModForm]>table>tbody>tr:matches(στις)");
else
postRows = document.select("form[id=quickModForm]>table>tbody>tr:matches(on)");
for (Element thisRow : postRows) {
String subject = thisRow.select("div[id^=subject_]").first().select("a").first().text();
documentString = documentString.replace(subject, "subject");
String post = thisRow.select("div").select(".post").first().text();
documentString = documentString.replace(post, "post");
}
int batchCount = documentString.length() / STRING_BATCH_LENGTH;
for (int i = 0; i < batchCount; i++) {
String batch;
if (i != batchCount - 1)
batch = documentString.substring(i * STRING_BATCH_LENGTH, (i + 1) * STRING_BATCH_LENGTH);
else
batch = documentString.substring(i * STRING_BATCH_LENGTH);
Crashlytics.setString(key + "_" + i + 1, batch);
}
}
}

17
app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java

@ -22,6 +22,7 @@ import gr.thmmy.mthmmy.base.BaseActivity;
import gr.thmmy.mthmmy.model.Post;
import gr.thmmy.mthmmy.session.SessionManager;
import gr.thmmy.mthmmy.utils.parsing.ParseHelpers;
import timber.log.Timber;
public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTaskCompleted,
PrepareForReply.OnPrepareForReplyFinished, PrepareForEditTask.OnPrepareEditFinished {
@ -85,6 +86,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa
public void reloadPage() {
if (topicUrl == null) throw new NullPointerException("No topic task has been requested yet!");
Timber.i("Reloading page");
loadUrl(topicUrl);
}
@ -93,6 +95,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa
throw new NullPointerException("No page has been loaded yet!");
int pageRequested = pageIndicatorIndex.getValue() - 1;
if (pageRequested != currentPageIndex - 1) {
Timber.i("Changing to page " + pageRequested + 1);
loadUrl(ParseHelpers.getBaseURL(topicUrl) + "." + String.valueOf(pageRequested * 15));
pageIndicatorIndex.setValue(pageRequested + 1);
} else {
@ -105,6 +108,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa
throw new NullPointerException("Topic task has not finished yet!");
stopLoading();
setPageIndicatorIndex(pageCount, true);
Timber.i("Preparing for reply");
currentPrepareForReplyTask = new PrepareForReply(prepareForReplyCallbacks, this,
replyPageUrl.getValue());
currentPrepareForReplyTask.execute(toQuoteList.toArray(new Integer[0]));
@ -122,11 +126,13 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa
includeAppSignature = prefs.getBoolean(SettingsActivity.POSTING_APP_SIGNATURE_ENABLE_KEY, true);
}
toQuoteList.clear();
Timber.i("Posting reply");
new ReplyTask(replyFinishListener, includeAppSignature).execute(subject, reply,
replyForm.getNumReplies(), replyForm.getSeqnum(), replyForm.getSc(), replyForm.getTopic());
}
public void deletePost(String postDeleteUrl) {
Timber.i("Deleting post");
new DeleteTask(deleteTaskCallbacks).execute(postDeleteUrl);
}
@ -134,6 +140,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa
if (replyPageUrl.getValue() == null)
throw new NullPointerException("Topic task has not finished yet!");
stopLoading();
Timber.i("Preparing for edit");
currentPrepareForEditTask = new PrepareForEditTask(prepareForEditCallbacks, this, position,
replyPageUrl.getValue());
currentPrepareForEditTask.execute(postEditURL);
@ -143,6 +150,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa
if (prepareForEditResult.getValue() == null)
throw new NullPointerException("Edit preparation was not found!");
PrepareForEditResult editResult = prepareForEditResult.getValue();
Timber.i("Editing post");
new EditTask(editTaskCallbacks, position).execute(editResult.getCommitEditUrl(), message,
editResult.getNumReplies(), editResult.getSeqnum(), editResult.getSc(), subject, editResult.getTopic());
}
@ -154,15 +162,18 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa
*/
public void stopLoading() {
if (currentTopicTask != null && currentTopicTask.getStatus() == AsyncTask.Status.RUNNING) {
Timber.i("Canceling topic task");
currentTopicTask.cancel(true);
pageIndicatorIndex.setValue(currentPageIndex);
topicTaskObserver.onTopicTaskCancelled();
}
if (currentPrepareForEditTask != null && currentPrepareForEditTask.getStatus() == AsyncTask.Status.RUNNING) {
Timber.i("Canceling prepare for edit task");
currentPrepareForEditTask.cancel(true);
prepareForEditCallbacks.onPrepareEditCancelled();
}
if (currentPrepareForReplyTask != null && currentPrepareForReplyTask.getStatus() == AsyncTask.Status.RUNNING) {
Timber.i("Canceling prepare for reply task");
currentPrepareForReplyTask.cancel(true);
prepareForReplyCallbacks.onPrepareForReplyCancelled();
}
@ -375,4 +386,10 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa
throw new NullPointerException("Reply preparation was not found");
return prepareForReplyResult.getValue().getBuildedQuotes();
}
public int postCount() {
if (postsList.getValue() == null)
throw new NullPointerException("No page has been loaded yet!");
return postsList.getValue().size();
}
}

240
app/src/main/res/drawable/emoji_a_eatpaper.xml

@ -0,0 +1,240 @@
<?xml version="1.0" encoding="utf-8"?>
<animation-list
xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false" >
<item
android:drawable="@drawable/emoji_a_eatpaper_f0"
android:duration="500" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f1"
android:duration="500" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f2"
android:duration="200" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f3"
android:duration="200" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f4"
android:duration="200" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f5"
android:duration="500" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f6"
android:duration="200" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f7"
android:duration="200" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f8"
android:duration="200" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f9"
android:duration="200" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f10"
android:duration="200" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f11"
android:duration="500" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f12"
android:duration="200" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f13"
android:duration="200" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f14"
android:duration="200" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f15"
android:duration="200" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f16"
android:duration="200" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f17"
android:duration="100" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f18"
android:duration="200" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f19"
android:duration="100" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f20"
android:duration="200" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f21"
android:duration="500" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f22"
android:duration="150" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f23"
android:duration="150" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f24"
android:duration="150" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f25"
android:duration="150" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f26"
android:duration="400" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f27"
android:duration="150" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f28"
android:duration="400" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f29"
android:duration="300" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f30"
android:duration="100" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f31"
android:duration="100" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f32"
android:duration="100" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f33"
android:duration="100" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f34"
android:duration="100" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f35"
android:duration="100" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f36"
android:duration="100" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f37"
android:duration="100" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f38"
android:duration="100" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f39"
android:duration="100" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f40"
android:duration="300" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f41"
android:duration="50" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f42"
android:duration="150" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f43"
android:duration="10" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f44"
android:duration="10" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f45"
android:duration="300" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f46"
android:duration="200" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f47"
android:duration="200" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f48"
android:duration="150" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f49"
android:duration="150" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f50"
android:duration="150" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f51"
android:duration="150" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f52"
android:duration="150" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f53"
android:duration="150" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f54"
android:duration="100" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f55"
android:duration="150" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f56"
android:duration="150" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f57"
android:duration="200" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f58"
android:duration="200" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f59"
android:duration="200" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f60"
android:duration="200" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f61"
android:duration="200" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f62"
android:duration="200" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f63"
android:duration="200" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f64"
android:duration="150" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f65"
android:duration="150" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f66"
android:duration="300" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f67"
android:duration="150" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f68"
android:duration="100" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f69"
android:duration="150" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f70"
android:duration="150" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f71"
android:duration="150" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f72"
android:duration="150" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f73"
android:duration="700" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f74"
android:duration="100" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f75"
android:duration="700" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f76"
android:duration="100" />
<item
android:drawable="@drawable/emoji_a_eatpaper_f77"
android:duration="1000" />
</animation-list>

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f0.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 636 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f10.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 618 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f11.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f12.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f13.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f14.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f15.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f16.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f17.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 629 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f18.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f19.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 629 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 617 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f20.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f21.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 629 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f22.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 633 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f23.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 570 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f24.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 614 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f25.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 600 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f26.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f27.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 641 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f28.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f29.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f30.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 631 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f31.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f32.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 631 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f33.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f34.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 631 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f35.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f36.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 631 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f37.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f38.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 631 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f39.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 617 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f40.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f41.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 620 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f42.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 565 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f43.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 555 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f44.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f45.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 604 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f46.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 573 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f47.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 605 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f48.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 536 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f49.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 517 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 614 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f50.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 527 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f51.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 516 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f52.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f53.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 517 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f54.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 517 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f55.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 465 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f56.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 466 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f57.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 534 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f58.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 521 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f59.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 525 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f6.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 618 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f60.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 521 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f61.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 536 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f62.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 521 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f63.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 525 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f64.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 521 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f65.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f66.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 524 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f67.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 514 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f68.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 520 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f69.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 527 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f7.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 614 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f70.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 523 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f71.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 B

BIN
app/src/main/res/drawable/emoji_a_eatpaper_f72.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 521 B

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save