Browse Source

Update uploads to androidx, comment out retry stuff, Fix FAB disappearing

pull/63/head
Apostolos Fanakis 6 years ago
parent
commit
4709796b8a
  1. 3
      app/build.gradle
  2. 8
      app/src/main/AndroidManifest.xml
  3. 1
      app/src/main/java/gr/thmmy/mthmmy/activities/board/BoardActivity.java
  4. 68
      app/src/main/java/gr/thmmy/mthmmy/activities/downloads/DownloadsActivity.java
  5. 36
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java
  6. 711
      app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadActivity.java
  7. 342
      app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadFieldsBuilderActivity.java
  8. 176
      app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsHelper.java
  9. 188
      app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java
  10. 44
      app/src/main/java/gr/thmmy/mthmmy/model/UploadFile.java
  11. 223
      app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java
  12. 95
      app/src/main/java/gr/thmmy/mthmmy/utils/FileUtils.java
  13. 18
      app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareFABBehavior.java
  14. 174
      app/src/main/java/gr/thmmy/mthmmy/utils/TakePhoto.java
  15. 4
      app/src/main/res/drawable/ic_attach_file_white_24dp.xml
  16. 4
      app/src/main/res/drawable/ic_cached_accent_24dp.xml
  17. 4
      app/src/main/res/drawable/ic_cancel_accent_24dp.xml
  18. 4
      app/src/main/res/drawable/ic_info_outline_warning_24dp.xml
  19. 9
      app/src/main/res/layout/activity_board.xml
  20. 4
      app/src/main/res/layout/activity_downloads.xml
  21. 4
      app/src/main/res/layout/activity_topic.xml
  22. 72
      app/src/main/res/layout/activity_upload.xml
  23. 2
      app/src/main/res/layout/activity_upload_fields_builder.xml
  24. 24
      app/src/main/res/layout/activity_upload_file_list_row.xml
  25. 10
      app/src/main/res/layout/activity_upload_filename_info_popup.xml
  26. 43
      app/src/main/res/layout/dialog_upload_progress.xml
  27. 9
      app/src/main/res/layout/editor_view_color_picker.xml
  28. 1
      app/src/main/res/values/colors.xml
  29. 12
      app/src/main/res/values/strings.xml
  30. 4
      app/src/main/res/xml/app_preferences_user.xml
  31. 2
      build.gradle
  32. 4
      gradle/wrapper/gradle-wrapper.properties

3
app/build.gradle

@ -44,7 +44,7 @@ android {
tasks.whenTaskAdded { task -> tasks.whenTaskAdded { task ->
if (task.name.contains("assembleRelease")) { if (task.name.contains("assembleRelease")) {
task.getDependsOn().add({ task.getDependsOn().add({
def inputFile = new File("app/google-services.json") def inputFile = new File("google-services.json")
def json = new JsonSlurper().parseText(inputFile.text) def json = new JsonSlurper().parseText(inputFile.text)
if (json.project_info.project_id != "mthmmy-release-3aef0") if (json.project_info.project_id != "mthmmy-release-3aef0")
throw new GradleException('Please supply the correct google-services.json for release (or manually change the id above)!') throw new GradleException('Please supply the correct google-services.json for release (or manually change the id above)!')
@ -73,6 +73,7 @@ dependencies {
implementation 'com.google.firebase:firebase-core:16.0.6' implementation 'com.google.firebase:firebase-core:16.0.6'
implementation 'com.google.firebase:firebase-messaging:17.3.4' implementation 'com.google.firebase:firebase-messaging:17.3.4'
implementation 'com.crashlytics.sdk.android:crashlytics:2.9.8' implementation 'com.crashlytics.sdk.android:crashlytics:2.9.8'
implementation 'com.snatik:storage:2.1.0'
implementation 'com.squareup.okhttp3:okhttp:3.12.0' implementation 'com.squareup.okhttp3:okhttp:3.12.0'
implementation 'com.squareup.picasso:picasso:2.5.2' implementation 'com.squareup.picasso:picasso:2.5.2'
implementation 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0' implementation 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'

8
app/src/main/AndroidManifest.xml

@ -160,6 +160,14 @@
</intent-filter> </intent-filter>
</service> </service>
<receiver
android:name=".services.UploadsReceiver"
android:exported="false">
<intent-filter>
<action android:name="gr.thmmy.mthmmy.uploadservice.broadcast.status" />
</intent-filter>
</receiver>
<activity <activity
android:name=".activities.create_content.CreateContentActivity" android:name=".activities.create_content.CreateContentActivity"
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"

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

@ -100,6 +100,7 @@ public class BoardActivity extends BaseActivity implements BoardAdapter.OnLoadMo
progressBar = findViewById(R.id.progressBar); progressBar = findViewById(R.id.progressBar);
newTopicFAB = findViewById(R.id.board_fab); newTopicFAB = findViewById(R.id.board_fab);
newTopicFAB.setTag(true);
if (!sessionManager.isLoggedIn()) newTopicFAB.hide(); if (!sessionManager.isLoggedIn()) newTopicFAB.hide();
else { else {
newTopicFAB.setOnClickListener(view -> { newTopicFAB.setOnClickListener(view -> {

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

@ -1,8 +1,12 @@
package gr.thmmy.mthmmy.activities.downloads; package gr.thmmy.mthmmy.activities.downloads;
import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.Toast; import android.widget.Toast;
@ -16,7 +20,11 @@ import java.util.Objects;
import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.upload.UploadActivity;
import gr.thmmy.mthmmy.base.BaseActivity; import gr.thmmy.mthmmy.base.BaseActivity;
import gr.thmmy.mthmmy.base.BaseApplication; import gr.thmmy.mthmmy.base.BaseApplication;
import gr.thmmy.mthmmy.model.Download; import gr.thmmy.mthmmy.model.Download;
@ -29,6 +37,8 @@ import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
import timber.log.Timber; import timber.log.Timber;
import static gr.thmmy.mthmmy.activities.upload.UploadActivity.BUNDLE_UPLOAD_CATEGORY;
public class DownloadsActivity extends BaseActivity implements DownloadsAdapter.OnLoadMoreListener { public class DownloadsActivity extends BaseActivity implements DownloadsAdapter.OnLoadMoreListener {
/** /**
* The key to use when putting download's url String to {@link DownloadsActivity}'s Bundle. * The key to use when putting download's url String to {@link DownloadsActivity}'s Bundle.
@ -47,7 +57,7 @@ public class DownloadsActivity extends BaseActivity implements DownloadsAdapter.
private MaterialProgressBar progressBar; private MaterialProgressBar progressBar;
private RecyclerView recyclerView; private RecyclerView recyclerView;
private DownloadsAdapter downloadsAdapter; private DownloadsAdapter downloadsAdapter;
//private FloatingActionButton uploadFAB; private FloatingActionButton uploadFAB;
private ParseDownloadPageTask parseDownloadPageTask; private ParseDownloadPageTask parseDownloadPageTask;
private int numberOfPages = -1; private int numberOfPages = -1;
@ -113,37 +123,37 @@ public class DownloadsActivity extends BaseActivity implements DownloadsAdapter.
} }
}); });
// uploadFAB = findViewById(R.id.upload_fab); uploadFAB = findViewById(R.id.upload_fab);
// uploadFAB.setEnabled(false); uploadFAB.setEnabled(false);
// uploadFAB.hide(); uploadFAB.hide();
parseDownloadPageTask = new ParseDownloadPageTask(); parseDownloadPageTask = new ParseDownloadPageTask();
parseDownloadPageTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, downloadsUrl); parseDownloadPageTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, downloadsUrl);
} }
// @Override @Override
// public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
// // Inflates the menu; this adds items to the action bar if it is present. // Inflates the menu; this adds items to the action bar if it is present.
// getMenuInflater().inflate(R.menu.downloads_menu, menu); getMenuInflater().inflate(R.menu.downloads_menu, menu);
// super.onCreateOptionsMenu(menu); super.onCreateOptionsMenu(menu);
// return true; return true;
// } }
//
// @Override @Override
// public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
// // Handle presses on the action bar items // Handle presses on the action bar items
// switch (item.getItemId()) { switch (item.getItemId()) {
// case R.id.menu_upload: case R.id.menu_upload:
// Intent intent = new Intent(DownloadsActivity.this, UploadActivity.class); Intent intent = new Intent(DownloadsActivity.this, UploadActivity.class);
// Bundle extras = new Bundle(); Bundle extras = new Bundle();
// extras.putString(BUNDLE_UPLOAD_CATEGORY, downloadsNav); extras.putString(BUNDLE_UPLOAD_CATEGORY, downloadsNav);
// intent.putExtras(extras); intent.putExtras(extras);
// startActivity(intent); startActivity(intent);
// return true; return true;
// default: default:
// return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
// } }
// } }
@Override @Override
public void onLoadMore() { public void onLoadMore() {
@ -198,7 +208,7 @@ public class DownloadsActivity extends BaseActivity implements DownloadsAdapter.
@Override @Override
protected void onPreExecute() { protected void onPreExecute() {
if (!isLoadingMore) progressBar.setVisibility(ProgressBar.VISIBLE); if (!isLoadingMore) progressBar.setVisibility(ProgressBar.VISIBLE);
//if (uploadFAB.getVisibility() != View.GONE) uploadFAB.setEnabled(false); if (uploadFAB.getVisibility() != View.GONE) uploadFAB.setEnabled(false);
} }
@Override @Override
@ -296,7 +306,7 @@ public class DownloadsActivity extends BaseActivity implements DownloadsAdapter.
toolbar.setTitle(downloadsTitle); toolbar.setTitle(downloadsTitle);
++pagesLoaded; ++pagesLoaded;
//if (uploadFAB.getVisibility() != View.GONE) uploadFAB.setEnabled(true); if (uploadFAB.getVisibility() != View.GONE) uploadFAB.setEnabled(true);
progressBar.setVisibility(ProgressBar.INVISIBLE); progressBar.setVisibility(ProgressBar.INVISIBLE);
downloadsAdapter.notifyDataSetChanged(); downloadsAdapter.notifyDataSetChanged();
isLoadingMore = false; isLoadingMore = false;

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

@ -87,6 +87,7 @@ import static gr.thmmy.mthmmy.activities.topic.TopicActivity.BUNDLE_TOPIC_URL;
import static gr.thmmy.mthmmy.activities.topic.TopicParser.USER_COLOR_WHITE; import static gr.thmmy.mthmmy.activities.topic.TopicParser.USER_COLOR_WHITE;
import static gr.thmmy.mthmmy.activities.topic.TopicParser.USER_COLOR_YELLOW; import static gr.thmmy.mthmmy.activities.topic.TopicParser.USER_COLOR_YELLOW;
import static gr.thmmy.mthmmy.base.BaseActivity.getSessionManager; import static gr.thmmy.mthmmy.base.BaseActivity.getSessionManager;
import static gr.thmmy.mthmmy.utils.FileUtils.faIconFromFilename;
/** /**
* Custom {@link RecyclerView.Adapter} used for topics. * Custom {@link RecyclerView.Adapter} used for topics.
@ -392,7 +393,7 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
attached.setClickable(true); attached.setClickable(true);
attached.setTypeface(Typeface.createFromAsset(context.getAssets() attached.setTypeface(Typeface.createFromAsset(context.getAssets()
, "fonts/fontawesome-webfont.ttf")); , "fonts/fontawesome-webfont.ttf"));
attached.setText(faIconFromFilename(attachedFile.getFilename()) + " " attached.setText(faIconFromFilename(context, attachedFile.getFilename()) + " "
+ attachedFile.getFilename() + attachedFile.getFileInfo()); + attachedFile.getFilename() + attachedFile.getFileInfo());
attached.setTextColor(filesTextColor); attached.setTextColor(filesTextColor);
attached.setPadding(0, 3, 0, 3); attached.setPadding(0, 3, 0, 3);
@ -1035,37 +1036,4 @@ class TopicAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
public interface OnPostFocusChangeListener { public interface OnPostFocusChangeListener {
void onPostFocusChange(int position); void onPostFocusChange(int position);
} }
/**
* Returns a String with a single FontAwesome typeface character corresponding to this file's
* extension.
*
* @param filename String with filename <b>containing file's extension</b>
* @return FontAwesome character according to file's type
* @see <a href="http://fontawesome.io/">FontAwesome</a>
*/
@NonNull
private String faIconFromFilename(String filename) {
filename = filename.toLowerCase();
if (filename.contains("jpg") || filename.contains("gif") || filename.contains("jpeg")
|| filename.contains("png"))
return context.getResources().getString(R.string.fa_file_image_o);
else if (filename.contains("pdf"))
return context.getResources().getString(R.string.fa_file_pdf_o);
else if (filename.contains("zip") || filename.contains("rar") || filename.contains("tar.gz"))
return context.getResources().getString(R.string.fa_file_zip_o);
else if (filename.contains("txt"))
return context.getResources().getString(R.string.fa_file_text_o);
else if (filename.contains("doc") || filename.contains("docx"))
return context.getResources().getString(R.string.fa_file_word_o);
else if (filename.contains("xls") || filename.contains("xlsx"))
return context.getResources().getString(R.string.fa_file_excel_o);
else if (filename.contains("pps"))
return context.getResources().getString(R.string.fa_file_powerpoint_o);
else if (filename.contains("mpg"))
return context.getResources().getString(R.string.fa_file_video_o);
return context.getResources().getString(R.string.fa_file);
}
} }

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

@ -1,54 +1,68 @@
package gr.thmmy.mthmmy.activities.upload; package gr.thmmy.mthmmy.activities.upload;
import android.app.Activity; import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.ResolveInfo; import android.content.pm.PackageManager;
import android.graphics.Bitmap; import android.graphics.Typeface;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.provider.MediaStore; import androidx.annotation.NonNull;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import androidx.core.content.FileProvider;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.preference.PreferenceManager;
import androidx.appcompat.widget.AppCompatButton;
import androidx.appcompat.widget.AppCompatImageButton;
import android.text.Editable;
import android.text.Spannable;
import android.text.TextWatcher;
import android.text.method.LinkMovementMethod;
import android.text.style.ForegroundColorSpan;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.EditText; import android.widget.EditText;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import net.gotev.uploadservice.MultipartUploadRequest; import net.gotev.uploadservice.MultipartUploadRequest;
import net.gotev.uploadservice.ServerResponse; import net.gotev.uploadservice.UploadNotificationAction;
import net.gotev.uploadservice.UploadInfo;
import net.gotev.uploadservice.UploadNotificationConfig; import net.gotev.uploadservice.UploadNotificationConfig;
import net.gotev.uploadservice.UploadStatusDelegate;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element; import org.jsoup.nodes.Element;
import org.jsoup.select.Elements; import org.jsoup.select.Elements;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.lang.ref.WeakReference;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.UUID;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.appcompat.widget.AppCompatButton;
import androidx.appcompat.widget.AppCompatTextView;
import androidx.preference.PreferenceManager;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseActivity; import gr.thmmy.mthmmy.base.BaseActivity;
import gr.thmmy.mthmmy.base.BaseApplication; import gr.thmmy.mthmmy.base.BaseApplication;
import gr.thmmy.mthmmy.model.UploadCategory; import gr.thmmy.mthmmy.model.UploadCategory;
import gr.thmmy.mthmmy.model.UploadFile;
import gr.thmmy.mthmmy.services.UploadsReceiver;
import gr.thmmy.mthmmy.utils.AppCompatSpinnerWithoutDefault; import gr.thmmy.mthmmy.utils.AppCompatSpinnerWithoutDefault;
import gr.thmmy.mthmmy.utils.FileUtils;
import gr.thmmy.mthmmy.utils.TakePhoto;
import gr.thmmy.mthmmy.utils.parsing.ParseException; import gr.thmmy.mthmmy.utils.parsing.ParseException;
import gr.thmmy.mthmmy.utils.parsing.ParseTask; import gr.thmmy.mthmmy.utils.parsing.ParseTask;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar; import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
@ -60,6 +74,7 @@ import static gr.thmmy.mthmmy.activities.upload.UploadFieldsBuilderActivity.BUND
import static gr.thmmy.mthmmy.activities.upload.UploadFieldsBuilderActivity.RESULT_DESCRIPTION; import static gr.thmmy.mthmmy.activities.upload.UploadFieldsBuilderActivity.RESULT_DESCRIPTION;
import static gr.thmmy.mthmmy.activities.upload.UploadFieldsBuilderActivity.RESULT_FILENAME; import static gr.thmmy.mthmmy.activities.upload.UploadFieldsBuilderActivity.RESULT_FILENAME;
import static gr.thmmy.mthmmy.activities.upload.UploadFieldsBuilderActivity.RESULT_TITLE; import static gr.thmmy.mthmmy.activities.upload.UploadFieldsBuilderActivity.RESULT_TITLE;
import static gr.thmmy.mthmmy.utils.FileUtils.faIconFromFilename;
public class UploadActivity extends BaseActivity { public class UploadActivity extends BaseActivity {
/** /**
@ -71,27 +86,40 @@ public class UploadActivity extends BaseActivity {
/** /**
* Request codes used in activities for result (AFR) calls * 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_CHOOSE_FILE = 8; //Arbitrary, application specific
private static final int AFR_REQUEST_CODE_CAMERA = 4; private static final int AFR_REQUEST_CODE_CAMERA = 4; //Arbitrary, application specific
private static final int AFR_REQUEST_CODE_FIELDS_BUILDER = 74; private static final int AFR_REQUEST_CODE_FIELDS_BUILDER = 74; //Arbitrary, application specific
/**
* Request code to gain read/write permission
*/
private static final int UPLOAD_REQUEST_CODE = 42; //Arbitrary, application specific
private static final int MAX_FILE_SIZE_SUPPORTED = 45000000;
//private UploadsReceiver uploadsReceiver = new UploadsReceiver();
private ArrayList<UploadCategory> uploadRootCategories = new ArrayList<>(); private ArrayList<UploadCategory> uploadRootCategories = new ArrayList<>();
private ParseUploadPageTask parseUploadPageTask; private ParseUploadPageTask parseUploadPageTask;
private ArrayList<String> bundleCategory; private ArrayList<String> bundleCategory;
private String categorySelected = "-1"; private String categorySelected = "-1";
private String uploaderProfileIndex = "1"; private String uploaderProfileIndex = "1";
private String uploadFilename;
private Uri fileUri; private ArrayList<UploadFile> filesList = new ArrayList<>();
private File photoFileCreated = null;
private String fileIcon; private String fileIcon;
private AppCompatImageButton uploadFilenameInfo;
private CustomTextWatcher textWatcher;
private boolean hasModifiedFilename = false;
//UI elements //UI elements
private MaterialProgressBar progressBar; private MaterialProgressBar progressBar;
private LinearLayout categoriesSpinners; private LinearLayout categoriesSpinners;
private AppCompatSpinnerWithoutDefault rootCategorySpinner; private AppCompatSpinnerWithoutDefault rootCategorySpinner;
private EditText uploadTitle; private EditText uploadTitle;
private EditText uploadFilename;
private EditText uploadDescription; private EditText uploadDescription;
private AppCompatButton titleDescriptionBuilderButton; private AppCompatButton titleDescriptionBuilderButton;
private AppCompatTextView filenameHolder; private LinearLayout filesListView;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -197,9 +225,36 @@ public class UploadActivity extends BaseActivity {
uploadTitle = findViewById(R.id.upload_title); uploadTitle = findViewById(R.id.upload_title);
uploadDescription = findViewById(R.id.upload_description); uploadDescription = findViewById(R.id.upload_description);
filenameHolder = findViewById(R.id.upload_filename); uploadFilenameInfo = findViewById(R.id.upload_filename_info);
Drawable filenameDrawable = AppCompatResources.getDrawable(this, R.drawable.ic_attach_file_white_24dp); uploadFilenameInfo.setOnClickListener(view -> {
filenameHolder.setCompoundDrawablesRelativeWithIntrinsicBounds(filenameDrawable, null, null, null); //Inflates the popup menu content
LayoutInflater layoutInflater = (LayoutInflater) view.getContext().
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (layoutInflater == null) {
return;
}
Context wrapper = new ContextThemeWrapper(this, R.style.PopupWindow);
View popUpContent = layoutInflater.inflate(R.layout.activity_upload_filename_info_popup, null);
//Creates the PopupWindow
PopupWindow popUp = new PopupWindow(wrapper);
popUp.setContentView(popUpContent);
popUp.setWidth(LinearLayout.LayoutParams.WRAP_CONTENT);
popUp.setHeight(LinearLayout.LayoutParams.WRAP_CONTENT);
popUp.setFocusable(true);
((TextView) popUpContent.findViewById(R.id.upload_filename_info_text)).
setMovementMethod(LinkMovementMethod.getInstance());
//Displays the popup
popUp.showAsDropDown(view);
});
uploadFilename = findViewById(R.id.upload_filename);
textWatcher = new CustomTextWatcher();
uploadFilename.addTextChangedListener(textWatcher);
filesListView = findViewById(R.id.upload_files_list);
AppCompatButton selectFileButton = findViewById(R.id.upload_select_file_button); AppCompatButton selectFileButton = findViewById(R.id.upload_select_file_button);
Drawable selectStartDrawable = AppCompatResources.getDrawable(this, R.drawable.ic_insert_drive_file_white_24dp); Drawable selectStartDrawable = AppCompatResources.getDrawable(this, R.drawable.ic_insert_drive_file_white_24dp);
@ -212,7 +267,8 @@ public class UploadActivity extends BaseActivity {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT) Intent intent = new Intent(Intent.ACTION_GET_CONTENT)
//.setType("*/*") //.setType("*/*")
.setType("image/jpeg") .setType("image/jpeg")
.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes); .putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes)
.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
startActivityForResult(intent, AFR_REQUEST_CODE_CHOOSE_FILE); startActivityForResult(intent, AFR_REQUEST_CODE_CHOOSE_FILE);
}); });
@ -221,118 +277,153 @@ public class UploadActivity extends BaseActivity {
Drawable takePhotoDrawable = AppCompatResources.getDrawable(this, R.drawable.ic_photo_camera_white_24dp); Drawable takePhotoDrawable = AppCompatResources.getDrawable(this, R.drawable.ic_photo_camera_white_24dp);
takePhotoButton.setCompoundDrawablesRelativeWithIntrinsicBounds(takePhotoDrawable, null, null, null); takePhotoButton.setCompoundDrawablesRelativeWithIntrinsicBounds(takePhotoDrawable, null, null, null);
takePhotoButton.setOnClickListener(v -> { takePhotoButton.setOnClickListener(v -> {
Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (checkPerms())
takePhotoIntent.putExtra("return-data", true); takePhoto();
takePhotoIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(UploadsHelper.getCacheFile(this))); else
requestPerms(UPLOAD_REQUEST_CODE);
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);
}); });
FloatingActionButton uploadFAB = findViewById(R.id.upload_fab); FloatingActionButton uploadFAB = findViewById(R.id.upload_fab);
uploadFAB.setTag(true);
uploadFAB.setOnClickListener(view -> { uploadFAB.setOnClickListener(view -> {
//Attempts upload
progressBar.setVisibility(View.VISIBLE); progressBar.setVisibility(View.VISIBLE);
String uploadTitleText = uploadTitle.getText().toString(); String uploadTitleText = uploadTitle.getText().toString();
String uploadDescriptionText = uploadDescription.getText().toString(); String editTextFilename = uploadFilename.getText().toString();
final String[] uploadDescriptionText = {uploadDescription.getText().toString()};
//Checks if all required fields are filled
{
boolean shouldReturn = false;
if (uploadTitleText.equals("")) { if (uploadTitleText.equals("")) {
uploadTitle.setError("Required"); uploadTitle.setError("Required");
shouldReturn = true;
} }
if (fileUri == null) { if (filesList.isEmpty()) {
Toast.makeText(view.getContext(), "Please choose a file to upload or take a photo", Toast.LENGTH_LONG).show(); Toast.makeText(view.getContext(), "Please choose a file to upload or take a photo", Toast.LENGTH_LONG).show();
shouldReturn = true;
} }
if (categorySelected.equals("-1")) { if (categorySelected.equals("-1")) {
Toast.makeText(view.getContext(), "Please choose category first", Toast.LENGTH_SHORT).show(); Toast.makeText(view.getContext(), "Please choose category first", Toast.LENGTH_SHORT).show();
shouldReturn = true;
}
if (!filesList.isEmpty()) {
long totalFilesSize = 0;
for (UploadFile file : filesList) {
totalFilesSize += FileUtils.sizeFromUri(this, file.getFileUri());
} }
if (categorySelected.equals("-1") || uploadTitleText.equals("") || fileUri == null) { if (totalFilesSize > MAX_FILE_SIZE_SUPPORTED) {
Toast.makeText(view.getContext(), "Your files are too powerful for thmmy. Reduce size or split!", Toast.LENGTH_LONG).show();
shouldReturn = true;
}
}
if (!editTextFilename.matches("(.+\\.)+.+") ||
!FileUtils.getFilenameWithoutExtension(editTextFilename).
matches("[0-9a-zA-Zα-ωΑ-Ω~!@#$%^&()_+=\\-`\\[\\]{};',.]+")) {
uploadFilename.setError("Invalid filename");
shouldReturn = true;
}
if (shouldReturn) {
progressBar.setVisibility(View.GONE); progressBar.setVisibility(View.GONE);
return; return;
} }
}
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Upload to thmmy");
builder.setMessage("Are you sure?");
builder.setPositiveButton("YES, FIRE AWAY", (dialog, which) -> {
//Checks settings and possibly adds "Uploaded from mTHMMY" string to description
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(view.getContext()); SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(view.getContext());
if (sharedPrefs.getBoolean(UPLOADING_APP_SIGNATURE_ENABLE_KEY, true)) { if (sharedPrefs.getBoolean(UPLOADING_APP_SIGNATURE_ENABLE_KEY, true)) {
uploadDescriptionText += uploadedFromThmmyPromptHtml; uploadDescriptionText[0] += uploadedFromThmmyPromptHtml;
}
for (UploadFile file : filesList) {
if (file.isCameraPhoto()) {
TakePhoto.galleryAddPic(this, file.getPhotoFile());
}
} }
String tempFilePath = null; Uri tempFileUri = null;
if (uploadFilename != null) { if (filesList.size() == 1) {
//File should be uploaded with a certain name. Temporarily copies the file and renames it //Checks if the file needs renaming
tempFilePath = UploadsHelper.createTempFile(this, fileUri, uploadFilename); UploadFile uploadFile = filesList.get(0);
if (tempFilePath == null) { String selectedFileFilename = FileUtils.filenameFromUri(this, uploadFile.getFileUri());
if (!editTextFilename.equals(selectedFileFilename)) {
//File should be uploaded with a different name
if (!uploadFile.isCameraPhoto()) {
//Temporarily copies the file to a another location and renames it
tempFileUri = UploadsHelper.createTempFile(this, storage,
uploadFile.getFileUri(),
FileUtils.getFilenameWithoutExtension(editTextFilename));
} else {
//Renames the photo taken
String photoPath = uploadFile.getPhotoFile().getPath();
photoPath = photoPath.substring(0, photoPath.lastIndexOf(File.separator));
String destinationFilename = photoPath + File.separator +
FileUtils.getFilenameWithoutExtension(editTextFilename) + ".jpg";
if (!storage.rename(uploadFile.getPhotoFile().getAbsolutePath(), destinationFilename)) {
//Something went wrong, abort //Something went wrong, abort
Toast.makeText(this, "Could not create temporary file for renaming", Toast.LENGTH_SHORT).show(); Toast.makeText(this, "Could not create temporary file for renaming", Toast.LENGTH_SHORT).show();
progressBar.setVisibility(View.GONE); progressBar.setVisibility(View.GONE);
return; return;
} }
}
try { //Points photoFile and fileUri to the new copied and renamed file
new MultipartUploadRequest(view.getContext(), uploadIndexUrl) uploadFile.setPhotoFile(storage.getFile(destinationFilename));
.setUtf8Charset() uploadFile.setFileUri(FileProvider.getUriForFile(this, getPackageName() +
.addParameter("tp-dluploadtitle", uploadTitleText) ".provider", uploadFile.getPhotoFile()));
.addParameter("tp-dluploadcat", categorySelected)
.addParameter("tp-dluploadtext", uploadDescriptionText)
.addFileToUpload(tempFilePath == null
? fileUri.toString()
: tempFilePath
, "tp-dluploadfile")
.addParameter("tp_dluploadicon", fileIcon)
.addParameter("tp-uploaduser", uploaderProfileIndex)
.setNotificationConfig(new UploadNotificationConfig())
.setMaxRetries(2)
.setDelegate(new UploadStatusDelegate() {
@Override
public void onProgress(Context context, UploadInfo uploadInfo) {
} }
}
@Override } else {
public void onError(Context context, UploadInfo uploadInfo, ServerResponse serverResponse, Uri[] filesListArray = new Uri[filesList.size()];
Exception exception) { for (int i = 0; i < filesList.size(); ++i) {
Toast.makeText(context, "Upload failed", Toast.LENGTH_SHORT).show(); filesListArray[i] = filesList.get(i).getFileUri();
UploadsHelper.deleteTempFiles();
progressBar.setVisibility(View.GONE);
} }
@Override new ZipTask(this, editTextFilename, categorySelected,
public void onCompleted(Context context, UploadInfo uploadInfo, ServerResponse serverResponse) { uploadTitleText, uploadDescriptionText[0], fileIcon,
Toast.makeText(context, "Upload completed successfully", Toast.LENGTH_SHORT).show(); uploaderProfileIndex).execute(filesListArray);
UploadsHelper.deleteTempFiles(); finish();
BaseApplication.getInstance().logFirebaseAnalyticsEvent("file_upload", null); return;
uploadTitle.setText(null);
uploadDescription.setText(null);
fileUri = null;
filenameHolder.setText(null);
filenameHolder.setVisibility(View.GONE);
progressBar.setVisibility(View.GONE);
} }
@Override String uploadID = UUID.randomUUID().toString();
public void onCancelled(Context context, UploadInfo uploadInfo) { if (uploadFile(this, uploadID, getConfigForUpload(this, uploadID,
Toast.makeText(context, "Upload canceled", Toast.LENGTH_SHORT).show(); editTextFilename),
categorySelected, uploadTitleText,
uploadDescriptionText[0], fileIcon, uploaderProfileIndex,
tempFileUri == null
? filesList.get(0).getFileUri()
: tempFileUri)) {
finish();
} else {
Toast.makeText(this, "Couldn't initiate upload.", Toast.LENGTH_SHORT).show();
}
});
UploadsHelper.deleteTempFiles(); builder.setNegativeButton("NOPE", (dialog, which) -> {
progressBar.setVisibility(View.GONE); progressBar.setVisibility(View.GONE);
} dialog.dismiss();
}) });
.startUpload();
} catch (Exception exception) { AlertDialog alert = builder.create();
Timber.e(exception, "AndroidUploadService: %s", exception.getMessage()); alert.setOnCancelListener(dialog -> {
progressBar.setVisibility(View.GONE); progressBar.setVisibility(View.GONE);
} dialog.dismiss();
});
alert.show();
}); });
if (uploadRootCategories.isEmpty()) { if (uploadRootCategories.isEmpty()) {
//Parses the uploads page //Parses the uploads page
parseUploadPageTask = new ParseUploadPageTask(); parseUploadPageTask = new ParseUploadPageTask();
parseUploadPageTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, uploadIndexUrl); parseUploadPageTask.execute(uploadIndexUrl);
} else { } else {
//Renders the already parsed data //Renders the already parsed data
updateUIElements(); updateUIElements();
@ -355,12 +446,18 @@ public class UploadActivity extends BaseActivity {
super.onResume(); super.onResume();
} }
@Override
protected void onPause() {
super.onPause();
}
@Override @Override
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
if (parseUploadPageTask != null && parseUploadPageTask.getStatus() != AsyncTask.Status.RUNNING) if (parseUploadPageTask != null && parseUploadPageTask.getStatus() != AsyncTask.Status.RUNNING) {
parseUploadPageTask.cancel(true); parseUploadPageTask.cancel(true);
} }
}
@Override @Override
public void onActivityResult(int requestCode, int resultCode, Intent data) { public void onActivityResult(int requestCode, int resultCode, Intent data) {
@ -369,11 +466,35 @@ public class UploadActivity extends BaseActivity {
return; return;
} }
fileUri = data.getData(); if (data.getClipData() != null) {
if (fileUri != null) { fileIcon = "archive.gif";
String filename = UploadsHelper.filenameFromUri(this, fileUri); textWatcher.setFileExtension(".zip");
filenameHolder.setText(filename);
filenameHolder.setVisibility(View.VISIBLE); if (!hasModifiedFilename) {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.FRANCE).format(new Date());
String zipFilename = "mThmmy_" + timeStamp + ".zip";
uploadFilename.setText(zipFilename);
hasModifiedFilename = false;
}
for (int fileIndex = 0; fileIndex < data.getClipData().getItemCount(); ++fileIndex) {
Uri newFileUri = data.getClipData().getItemAt(fileIndex).getUri();
String filename = FileUtils.filenameFromUri(this, newFileUri);
addFileViewToList(filename);
filesList.add(new UploadFile(false, newFileUri, null));
}
} else {
Uri newFileUri = data.getData();
if (newFileUri != null) {
String filename = FileUtils.filenameFromUri(this, newFileUri);
if (filesList.isEmpty()) {
textWatcher.setFileExtension(FileUtils.getFileExtension(filename));
if (!hasModifiedFilename) {
uploadFilename.setText(filename);
hasModifiedFilename = false;
}
filename = filename.toLowerCase(); filename = filename.toLowerCase();
if (filename.endsWith(".jpg")) { if (filename.endsWith(".jpg")) {
@ -394,46 +515,70 @@ public class UploadActivity extends BaseActivity {
} else { } else {
fileIcon = "blank.gif"; fileIcon = "blank.gif";
} }
} else {
fileIcon = "archive.gif";
textWatcher.setFileExtension(".zip");
if (!hasModifiedFilename) {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.FRANCE).format(new Date());
String zipFilename = "mThmmy_" + timeStamp + ".zip";
uploadFilename.setText(zipFilename);
hasModifiedFilename = false;
}
}
addFileViewToList(filename);
filesList.add(new UploadFile(false, newFileUri, null));
}
} }
} else if (requestCode == AFR_REQUEST_CODE_CAMERA) { } else if (requestCode == AFR_REQUEST_CODE_CAMERA) {
if (resultCode == Activity.RESULT_CANCELED) { if (resultCode == Activity.RESULT_CANCELED) {
//Deletes image file
storage.deleteFile(photoFileCreated.getAbsolutePath());
return; return;
} }
Bitmap bitmap; if (filesList.isEmpty()) {
File cacheImageFile = UploadsHelper.getCacheFile(this); textWatcher.setFileExtension(FileUtils.getFileExtension(photoFileCreated.getName()));
Uri cacheFileUri = Uri.fromFile(cacheImageFile);
fileIcon = "jpg_image.gif";
bitmap = UploadsHelper.getImageResized(this, cacheFileUri); if (!hasModifiedFilename) {
int rotation = UploadsHelper.getRotation(this, cacheFileUri); uploadFilename.setText(photoFileCreated.getName());
bitmap = UploadsHelper.rotate(bitmap, rotation); hasModifiedFilename = false;
try {
FileOutputStream out = new FileOutputStream(cacheImageFile);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
} }
String newFilename = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.FRANCE). fileIcon = "jpg_image.gif";
format(new Date()); } else {
fileUri = Uri.parse(UploadsHelper.createTempFile(this, cacheFileUri, newFilename)); fileIcon = "archive.gif";
textWatcher.setFileExtension(".zip");
newFilename += ".jpg"; if (!hasModifiedFilename) {
filenameHolder.setText(newFilename); String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.FRANCE).format(new Date());
filenameHolder.setVisibility(View.VISIBLE); String zipFilename = "mThmmy_" + timeStamp + ".zip";
uploadFilename.setText(zipFilename);
hasModifiedFilename = false;
}
}
UploadsHelper.deleteCacheFiles(this); UploadFile newFile = new UploadFile(true, TakePhoto.processResult(this,
photoFileCreated), photoFileCreated);
addFileViewToList(FileUtils.getFilenameWithoutExtension(FileUtils.
filenameFromUri(this, newFile.getFileUri())));
filesList.add(newFile);
} else if (requestCode == AFR_REQUEST_CODE_FIELDS_BUILDER) { } else if (requestCode == AFR_REQUEST_CODE_FIELDS_BUILDER) {
if (resultCode == Activity.RESULT_CANCELED) { if (resultCode == Activity.RESULT_CANCELED) {
return; return;
} }
uploadFilename = data.getStringExtra(RESULT_FILENAME); String previousName = uploadFilename.getText().toString();
if (previousName.isEmpty()) {
uploadFilename.setText(data.getStringExtra(RESULT_FILENAME));
} else {
String filenameWithExtension = data.getStringExtra(RESULT_FILENAME) +
FileUtils.getFileExtension(previousName);
uploadFilename.setText(filenameWithExtension);
}
hasModifiedFilename = true;
uploadTitle.setText(data.getStringExtra(RESULT_TITLE)); uploadTitle.setText(data.getStringExtra(RESULT_TITLE));
uploadDescription.setText(data.getStringExtra(RESULT_DESCRIPTION)); uploadDescription.setText(data.getStringExtra(RESULT_DESCRIPTION));
} else { } else {
@ -441,6 +586,243 @@ public class UploadActivity extends BaseActivity {
} }
} }
@Override
public void onRequestPermissionsResult(int permsRequestCode, @NonNull String[] permissions
, @NonNull int[] grantResults) {
switch (permsRequestCode) {
case UPLOAD_REQUEST_CODE:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
takePhoto();
break;
}
}
// Should only be called after making sure permissions are granted
private void takePhoto() {
// Create the File where the photo should go
photoFileCreated = TakePhoto.createImageFile(this);
// Continue only if the File was successfully created
if (photoFileCreated != null) {
startActivityForResult(TakePhoto.getIntent(this, photoFileCreated),
AFR_REQUEST_CODE_CAMERA);
}
}
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(),
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;
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);
}
}
}
private void addFileViewToList(String filename) {
LayoutInflater layoutInflater = getLayoutInflater();
LinearLayout newFileRow = (LinearLayout) layoutInflater.
inflate(R.layout.activity_upload_file_list_row, null);
TextView itemText = newFileRow.findViewById(R.id.upload_file_item_text);
itemText.setTypeface(Typeface.createFromAsset(this.getAssets()
, "fonts/fontawesome-webfont.ttf"));
String filenameWithIcon = faIconFromFilename(this, filename) + " " + filename;
itemText.setText(filenameWithIcon);
newFileRow.findViewById(R.id.upload_file_item_remove).setOnClickListener(view -> {
int fileIndex = filesListView.indexOfChild(newFileRow);
filesListView.removeViewAt(fileIndex);
if (filesList.get(fileIndex).isCameraPhoto()) {
storage.deleteFile(filesList.get(fileIndex).getPhotoFile().getAbsolutePath());
}
filesList.remove(fileIndex);
if (filesList.isEmpty()) {
filesListView.setVisibility(View.GONE);
} else if (filesList.size() == 1) {
textWatcher.setFileExtension(FileUtils.getFileExtension(FileUtils.
filenameFromUri(this, filesList.get(0).getFileUri())));
}
});
filesListView.addView(newFileRow);
filesListView.setVisibility(View.VISIBLE);
}
public static UploadNotificationConfig getConfigForUpload(Context context, String uploadID,
String filename) {
UploadNotificationConfig uploadNotificationConfig = new UploadNotificationConfig();
uploadNotificationConfig.setIconForAllStatuses(android.R.drawable.stat_sys_upload);
uploadNotificationConfig.setTitleForAllStatuses("Uploading " + filename);
uploadNotificationConfig.getProgress().iconResourceID = android.R.drawable.stat_sys_upload;
uploadNotificationConfig.getCompleted().iconResourceID = android.R.drawable.stat_sys_upload_done;
uploadNotificationConfig.getError().iconResourceID = android.R.drawable.stat_sys_upload_done;
uploadNotificationConfig.getError().iconColorResourceID = R.color.error_red;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
uploadNotificationConfig.getError().message = "Error during upload. Click for options";
}
uploadNotificationConfig.getCancelled().iconColorResourceID = android.R.drawable.stat_sys_upload_done;
uploadNotificationConfig.getCancelled().autoClear = true;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
Intent combinedActionsIntent = new Intent(UploadsReceiver.ACTION_COMBINED_UPLOAD);
combinedActionsIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
combinedActionsIntent.putExtra(UploadsReceiver.UPLOAD_ID_KEY, uploadID);
/*combinedActionsIntent.putExtra(UploadsReceiver.UPLOAD_RETRY_FILENAME, filename);
combinedActionsIntent.putExtra(UploadsReceiver.UPLOAD_RETRY_CATEGORY, retryCategory);
combinedActionsIntent.putExtra(UploadsReceiver.UPLOAD_RETRY_TITLE, retryTitleText);
combinedActionsIntent.putExtra(UploadsReceiver.UPLOAD_RETRY_DESCRIPTION, retryDescription);
combinedActionsIntent.putExtra(UploadsReceiver.UPLOAD_RETRY_ICON, retryIcon);
combinedActionsIntent.putExtra(UploadsReceiver.UPLOAD_RETRY_UPLOADER, retryUploaderProfile);
combinedActionsIntent.putExtra(UploadsReceiver.UPLOAD_RETRY_FILE_URI, retryFileUri);*/
uploadNotificationConfig.setClickIntentForAllStatuses(PendingIntent.getBroadcast(context,
1, combinedActionsIntent, PendingIntent.FLAG_UPDATE_CURRENT));
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Intent retryIntent = new Intent(context, UploadsReceiver.class);
retryIntent.setAction(UploadsReceiver.ACTION_RETRY_UPLOAD);
retryIntent.putExtra(UploadsReceiver.UPLOAD_ID_KEY, uploadID);
Intent cancelIntent = new Intent(context, UploadsReceiver.class);
cancelIntent.setAction(UploadsReceiver.ACTION_CANCEL_UPLOAD);
cancelIntent.putExtra(UploadsReceiver.UPLOAD_ID_KEY, uploadID);
uploadNotificationConfig.getProgress().actions.add(new UploadNotificationAction(
R.drawable.ic_cancel_accent_24dp,
context.getString(R.string.upload_notification_cancel),
PendingIntent.getBroadcast(context, 0, cancelIntent,
PendingIntent.FLAG_UPDATE_CURRENT)
));
uploadNotificationConfig.getError().actions.add(new UploadNotificationAction(
R.drawable.ic_notification,
context.getString(R.string.upload_notification_retry),
PendingIntent.getBroadcast(context, 0, retryIntent,
PendingIntent.FLAG_UPDATE_CURRENT)
));
}
return uploadNotificationConfig;
}
public static boolean uploadFile(Context context, String uploadID,
UploadNotificationConfig uploadNotificationConfig,
String categorySelected, String uploadTitleText,
String uploadDescriptionText, String fileIcon,
String uploaderProfileIndex, Uri fileUri) {
try {
new MultipartUploadRequest(context, uploadID, uploadIndexUrl)
.setUtf8Charset()
.setNotificationConfig(uploadNotificationConfig)
.addParameter("tp-dluploadtitle", uploadTitleText)
.addParameter("tp-dluploadcat", categorySelected)
.addParameter("tp-dluploadtext", uploadDescriptionText)
.addFileToUpload(fileUri.toString()
, "tp-dluploadfile")
.addParameter("tp_dluploadicon", fileIcon)
.addParameter("tp-uploaduser", uploaderProfileIndex)
.setNotificationConfig(uploadNotificationConfig)
.setMaxRetries(2)
.startUpload();
Toast.makeText(context, "Uploading files in the background.", Toast.LENGTH_SHORT).show();
return true;
} catch (Exception exception) {
Timber.e(exception, "AndroidUploadService: %s", exception.getMessage());
return false;
}
}
private class CustomTextWatcher implements TextWatcher {
String oldFilename, fileExtension;
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
//Saves an instance of the filename before changing
oldFilename = s.toString();
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
//Warns user for bad filenames
String filenameWithoutExtension = FileUtils.getFilenameWithoutExtension(s.toString());
if (filenameWithoutExtension != null && !filenameWithoutExtension.isEmpty() &&
!filenameWithoutExtension.matches("[0-9a-zA-Z~!@#$%^&()_+=\\-`\\[\\]{};',.]+")) {
uploadFilenameInfo.setImageResource(R.drawable.ic_info_outline_warning_24dp);
} else {
uploadFilenameInfo.setImageResource(R.drawable.ic_info_outline_white_24dp);
}
if (fileExtension == null) {
hasModifiedFilename = !s.toString().isEmpty();
return;
}
if (!s.toString().endsWith(fileExtension)) {
//User tried to alter the extension
//Prevents the change
uploadFilename.setText(oldFilename);
return;
}
//User has modified the filename
hasModifiedFilename = true;
if (s.toString().isEmpty() || (filesList.size() == 1 && s.toString().equals(FileUtils.
filenameFromUri(getApplicationContext(), filesList.get(0).getFileUri())))) {
//After modification the filename falls back to the original
hasModifiedFilename = false;
}
//Adds the grey colored span to the extension
s.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.secondary_text)),
s.length() - fileExtension.length(), s.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
void setFileExtension(String extension) {
boolean oldHasModifiedFilename = hasModifiedFilename;
oldFilename = uploadFilename.getText().toString();
fileExtension = extension;
String newFilename;
if (!oldFilename.isEmpty()) {
newFilename = FileUtils.getFilenameWithoutExtension(oldFilename) + extension;
} else {
newFilename = extension;
}
uploadFilename.setText(newFilename);
hasModifiedFilename = oldHasModifiedFilename;
}
}
private class CustomOnItemSelectedListener implements AdapterView.OnItemSelectedListener { private class CustomOnItemSelectedListener implements AdapterView.OnItemSelectedListener {
private ArrayList<UploadCategory> parentCategories, childCategories; private ArrayList<UploadCategory> parentCategories, childCategories;
@ -574,32 +956,79 @@ public class UploadActivity extends BaseActivity {
} }
} }
private void updateUIElements() { public static class ZipTask extends AsyncTask<Uri, Void, Boolean> {
String[] tmpSpinnerArray = new String[uploadRootCategories.size()]; // Weak references will still allow the Activity to be garbage-collected
for (int i = 0; i < uploadRootCategories.size(); ++i) { private final WeakReference<Activity> weakActivity;
tmpSpinnerArray[i] = uploadRootCategories.get(i).getCategoryTitle(); final String zipFilename, categorySelected, uploadTitleText, uploadDescriptionText,
fileIcon, uploaderProfileIndex;
Uri zipFileUri;
// Suppresses default constructor
@SuppressWarnings("unused")
private ZipTask() {
weakActivity = null;
this.zipFilename = null;
this.categorySelected = null;
this.uploadTitleText = null;
this.uploadDescriptionText = null;
this.fileIcon = null;
this.uploaderProfileIndex = null;
}
ZipTask(Activity uploadsActivity, @NonNull String zipFilename,
@NonNull String categorySelected, @NonNull String uploadTitleText,
@NonNull String uploadDescriptionText, @NonNull String fileIcon,
@NonNull String uploaderProfileIndex) {
weakActivity = new WeakReference<>(uploadsActivity);
this.zipFilename = zipFilename;
this.categorySelected = categorySelected;
this.uploadTitleText = uploadTitleText;
this.uploadDescriptionText = uploadDescriptionText;
this.fileIcon = fileIcon;
this.uploaderProfileIndex = uploaderProfileIndex;
} }
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(BaseApplication.getInstance().getApplicationContext(), @Override
R.layout.spinner_item, tmpSpinnerArray); protected void onPreExecute() {
spinnerArrayAdapter.setDropDownViewResource(R.layout.spinner_dropdown_item); assert weakActivity != null;
rootCategorySpinner.setAdapter(spinnerArrayAdapter); Toast.makeText(weakActivity.get(), "Zipping files", Toast.LENGTH_SHORT).show();
}
//Sets bundle selection @Override
if (bundleCategory != null) { protected Boolean doInBackground(Uri... filesToZip) {
int bundleSelectionIndex = -1, currentIndex = 0; if (weakActivity == null || zipFilename == null) {
return false;
}
File zipFile = UploadsHelper.createZipFile(zipFilename);
for (UploadCategory category : uploadRootCategories) { if (zipFile == null) {
if (bundleCategory.get(0).contains(category.getCategoryTitle())) { return false;
bundleSelectionIndex = currentIndex;
break;
} }
++currentIndex; zipFileUri = FileProvider.getUriForFile(weakActivity.get(),
weakActivity.get().getPackageName() +
".provider", zipFile);
UploadsHelper.zip(weakActivity.get(), filesToZip, zipFileUri);
return true;
} }
if (bundleSelectionIndex != -1) { @Override
rootCategorySpinner.setSelection(bundleSelectionIndex, true); protected void onPostExecute(Boolean result) {
bundleCategory.remove(0); if (weakActivity == null) {
return;
}
if (!result) {
Toast.makeText(weakActivity.get(), "Couldn't create zip!", Toast.LENGTH_SHORT).show();
return;
}
String uploadID = UUID.randomUUID().toString();
if (!uploadFile(weakActivity.get(), uploadID,
getConfigForUpload(weakActivity.get(), uploadID, zipFilename), categorySelected,
uploadTitleText, uploadDescriptionText, fileIcon, uploaderProfileIndex,
zipFileUri)) {
Toast.makeText(weakActivity.get(), "Couldn't initiate upload.", Toast.LENGTH_SHORT).show();
} }
} }
} }

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

@ -3,6 +3,7 @@ package gr.thmmy.mthmmy.activities.upload;
import android.app.Activity; import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.Nullable;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.view.View; import android.view.View;
@ -12,14 +13,14 @@ import android.widget.RadioGroup;
import android.widget.Toast; import android.widget.Toast;
import java.util.Calendar; import java.util.Calendar;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseActivity;
import timber.log.Timber; import timber.log.Timber;
public class UploadFieldsBuilderActivity extends AppCompatActivity { public class UploadFieldsBuilderActivity extends BaseActivity {
static final String BUNDLE_UPLOAD_FIELD_BUILDER_COURSE = "UPLOAD_FIELD_BUILDER_COURSE"; static final String BUNDLE_UPLOAD_FIELD_BUILDER_COURSE = "UPLOAD_FIELD_BUILDER_COURSE";
static final String BUNDLE_UPLOAD_FIELD_BUILDER_SEMESTER = "UPLOAD_FIELD_BUILDER_SEMESTER"; static final String BUNDLE_UPLOAD_FIELD_BUILDER_SEMESTER = "UPLOAD_FIELD_BUILDER_SEMESTER";
@ -28,6 +29,7 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
static final String RESULT_DESCRIPTION = "RESULT_DESCRIPTION"; static final String RESULT_DESCRIPTION = "RESULT_DESCRIPTION";
private String course, semester; private String course, semester;
private boolean isValidYear;
private LinearLayout semesterChooserLinear; private LinearLayout semesterChooserLinear;
private RadioGroup typeRadio, semesterRadio; private RadioGroup typeRadio, semesterRadio;
@ -38,18 +40,17 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
@Override @Override
public void onTextChanged(CharSequence s, int start, int before, int count) { public void onTextChanged(CharSequence s, int start, int before, int count) {
String working = s.toString(); String working = s.toString();
boolean isValid;
if (working.length() == 4) { if (working.length() == 4) {
int currentYear = Calendar.getInstance().get(Calendar.YEAR); int currentYear = Calendar.getInstance().get(Calendar.YEAR);
int inputYear = Integer.parseInt(working); int inputYear = Integer.parseInt(working);
isValid = inputYear <= currentYear && inputYear > 2000; isValidYear = inputYear <= currentYear && inputYear > 1980;
} else { } else {
isValid = false; isValidYear = false;
} }
if (!isValid) { if (!isValidYear) {
year.setError("Please enter a valid year"); year.setError("Please enter a valid year");
} else { } else {
year.setError(null); year.setError(null);
@ -86,7 +87,7 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
} }
//Initialize toolbar //Initialize toolbar
Toolbar toolbar = findViewById(R.id.toolbar); toolbar = findViewById(R.id.toolbar);
toolbar.setTitle(R.string.upload_fields_builder_toolbar_title); toolbar.setTitle(R.string.upload_fields_builder_toolbar_title);
setSupportActionBar(toolbar); setSupportActionBar(toolbar);
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
@ -94,6 +95,9 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
getSupportActionBar().setDisplayShowHomeEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true);
} }
createDrawer();
drawer.setSelection(UPLOAD_ID, false);
semesterChooserLinear = findViewById(R.id.upload_fields_builder_choose_semester); semesterChooserLinear = findViewById(R.id.upload_fields_builder_choose_semester);
semesterRadio = findViewById(R.id.upload_fields_builder_semester_radio_group); semesterRadio = findViewById(R.id.upload_fields_builder_semester_radio_group);
semesterRadio.check(Integer.parseInt(semester) % 2 == 0 semesterRadio.check(Integer.parseInt(semester) % 2 == 0
@ -121,8 +125,8 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
} else if (semesterChooserLinear.getVisibility() == View.VISIBLE && semesterId == -1) { } else if (semesterChooserLinear.getVisibility() == View.VISIBLE && semesterId == -1) {
Toast.makeText(view.getContext(), "Please choose a semester for the upload", Toast.LENGTH_SHORT).show(); Toast.makeText(view.getContext(), "Please choose a semester for the upload", Toast.LENGTH_SHORT).show();
return; return;
} else if (year.getText().toString().isEmpty()) { } else if (year.getText().toString().isEmpty() || !isValidYear) {
Toast.makeText(view.getContext(), "Please choose a year for the upload", Toast.LENGTH_SHORT).show(); Toast.makeText(view.getContext(), "Please choose a valid year for the upload", Toast.LENGTH_SHORT).show();
return; return;
} }
@ -212,304 +216,324 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
return getGreeklishOrMinifiedCourseName(false); return getGreeklishOrMinifiedCourseName(false);
} }
private String normalizeLatinNumbers(String stringWithLatinNumbers) {
String greekLatinOne = "Ι", englishLatinOne = "I";
String normalisedString;
//Separates the latin number suffix from the course name
final String regex = "(.+)\\ ([IΙ]+)";
final Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
final Matcher matcher = pattern.matcher(stringWithLatinNumbers);
if (matcher.matches() && matcher.groupCount() == 2) {
normalisedString = matcher.group(1) + " " + matcher.group(2).replaceAll(greekLatinOne, englishLatinOne);
} else {
normalisedString = stringWithLatinNumbers;
}
return normalisedString;
}
@Nullable @Nullable
private String getGreeklishOrMinifiedCourseName(boolean greeklish) { private String getGreeklishOrMinifiedCourseName(boolean greeklish) {
if (course.contains("Ψηφιακή Επεξεργασία Σήματος")) { String normalisedCourse = normalizeLatinNumbers(course);
if (normalisedCourse.contains(("Ψηφιακή Επεξεργασία Σήματος"))) {
return greeklish ? "PSES" : "ΨΕΣ"; return greeklish ? "PSES" : "ΨΕΣ";
} else if (course.contains("Ψηφιακή Επεξεργασία Εικόνας")) { } else if (normalisedCourse.contains(("Ψηφιακή Επεξεργασία Εικόνας"))) {
return greeklish ? "psee" : "ΨΕΕ"; return greeklish ? "psee" : "ΨΕΕ";
} else if (course.contains("Ψηφιακές Τηλεπικοινωνίες ΙΙ")) { } else if (normalisedCourse.contains(("Ψηφιακές Τηλεπικοινωνίες II"))) {
return greeklish ? "pshf_thlep_II" : "Ψηφιακές Τηλεπ. 2"; return greeklish ? "pshf_thlep_II" : "Ψηφιακές Τηλεπ. 2";
} else if (course.contains("Ψηφιακές Τηλεπικοινωνίες Ι")) { } else if (normalisedCourse.contains(("Ψηφιακές Τηλεπικοινωνίες I"))) {
return greeklish ? "pshf_thlep_I" : "Ψηφιακές Τηλεπ. 1"; return greeklish ? "pshf_thlep_I" : "Ψηφιακές Τηλεπ. 1";
} else if (course.contains("Ψηφιακά Φίλτρα")) { } else if (normalisedCourse.contains(("Ψηφιακά Φίλτρα"))) {
return greeklish ? "filtra" : "Φίλτρα"; return greeklish ? "filtra" : "Φίλτρα";
} else if (course.contains("Ψηφιακά Συστήματα ΙΙΙ")) { } else if (normalisedCourse.contains(("Ψηφιακά Συστήματα III"))) {
return greeklish ? "pshfiaka_III" : "Ψηφιακά 3"; return greeklish ? "pshfiaka_III" : "Ψηφιακά 3";
} else if (course.contains("Ψηφιακά Συστήματα ΙΙ")) { } else if (normalisedCourse.contains(("Ψηφιακά Συστήματα II"))) {
return greeklish ? "pshfiaka_II" : "Ψηφιακά 2"; return greeklish ? "pshfiaka_II" : "Ψηφιακά 2";
} else if (course.contains("Ψηφιακά Συστήματα Ι")) { } else if (normalisedCourse.contains(("Ψηφιακά Συστήματα I"))) {
return greeklish ? "pshfiaka_I" : "Ψηφιακά 1"; return greeklish ? "pshfiaka_I" : "Ψηφιακά 1";
} else if (course.contains("Φωτονική Τεχνολογία")) { } else if (normalisedCourse.contains(("Φωτονική Τεχνολογία"))) {
return greeklish ? "fwtonikh" : "Φωτονική"; return greeklish ? "fwtonikh" : "Φωτονική";
} else if (course.contains("Φυσική Ι")) { } else if (normalisedCourse.contains(("Φυσική I"))) {
return greeklish ? "fysikh_I" : "Φυσική 1"; return greeklish ? "fysikh_I" : "Φυσική 1";
} else if (course.contains("Υψηλές Τάσεις ΙΙΙ")) { } else if (normalisedCourse.contains(("Υψηλές Τάσεις III"))) {
return greeklish ? "ypshles_III" : "Υψηλές 3"; return greeklish ? "ypshles_III" : "Υψηλές 3";
} else if (course.contains("Υψηλές Τάσεις ΙΙ")) { } else if (normalisedCourse.contains(("Υψηλές Τάσεις II"))) {
return greeklish ? "ypshles_II" : "Υψηλές 2"; return greeklish ? "ypshles_II" : "Υψηλές 2";
} else if (course.contains("Υψηλές Τάσεις Ι")) { } else if (normalisedCourse.contains(("Υψηλές Τάσεις I"))) {
return greeklish ? "ypshles_I" : "Υψηλές 1"; return greeklish ? "ypshles_I" : "Υψηλές 1";
} else if (course.contains("Υψηλές Τάσεις 4")) { } else if (normalisedCourse.contains(("Υψηλές Τάσεις 4"))) {
return greeklish ? "ypshles_IV" : "Υψηλές 4"; return greeklish ? "ypshles_IV" : "Υψηλές 4";
} else if (course.contains("Υπολογιστικός Ηλεκτρομαγνητισμός")) { } else if (normalisedCourse.contains(("Υπολογιστικός Ηλεκτρομαγνητισμός"))) {
return greeklish ? "ypologistikos_HM" : "Υπολογιστικός Η/Μ"; return greeklish ? "ypologistikos_HM" : "Υπολογιστικός Η/Μ";
} else if (course.contains("Υπολογιστικές Μέθοδοι στα Ενεργειακά Συστήματα")) { } else if (normalisedCourse.contains(("Υπολογιστικές Μέθοδοι στα Ενεργειακά Συστήματα"))) {
return greeklish ? "ymes" : "ΥΜΕΣ"; return greeklish ? "ymes" : "ΥΜΕΣ";
} else if (course.contains("Τηλεπικοινωνιακή Ηλεκτρονική")) { } else if (normalisedCourse.contains(("Τηλεπικοινωνιακή Ηλεκτρονική"))) {
return greeklish ? "tilep_ilektr" : "Τηλεπ. Ηλεκτρ."; return greeklish ? "tilep_ilektr" : "Τηλεπ. Ηλεκτρ.";
} else if (course.contains("Τηλεοπτικά Συστήματα")) { } else if (normalisedCourse.contains(("Τηλεοπτικά Συστήματα"))) {
return greeklish ? "tileoptika" : "Τηλεοπτικά"; return greeklish ? "tileoptika" : "Τηλεοπτικά";
} else if (course.contains("Τεχνολογία Λογισμικού")) { } else if (normalisedCourse.contains(("Τεχνολογία Λογισμικού"))) {
return greeklish ? "SE" : "Τεχνολογία Λογισμικού"; return greeklish ? "SE" : "Τεχνολογία Λογισμικού";
} else if (course.contains("Τεχνολογία Ηλεκτροτεχνικών Υλικών")) { } else if (normalisedCourse.contains(("Τεχνολογία Ηλεκτροτεχνικών Υλικών"))) {
return greeklish ? "Hlektrotexnika_Ylika" : "Ηλεκτροτεχνικά Υλικά"; return greeklish ? "Hlektrotexnika_Ylika" : "Ηλεκτροτεχνικά Υλικά";
} else if (course.contains("Τεχνολογία Ήχου και Εικόνας")) { } else if (normalisedCourse.contains(("Τεχνολογία Ήχου και Εικόνας"))) {
return greeklish ? "texn_hxoy_eikonas" : "Τεχνολογία Ήχου και Εικόνας"; return greeklish ? "texn_hxoy_eikonas" : "Τεχνολογία Ήχου και Εικόνας";
} else if (course.contains("Τεχνική Μηχανική")) { } else if (normalisedCourse.contains(("Τεχνική Μηχανική"))) {
return greeklish ? "texn_mhxan" : "Τεχν. Μηχαν."; return greeklish ? "texn_mhxan" : "Τεχν. Μηχαν.";
} else if (course.contains("Τεχνικές μη Καταστρεπτικών Δοκιμών")) { } else if (normalisedCourse.contains(("Τεχνικές μη Καταστρεπτικών Δοκιμών"))) {
return greeklish ? "non_destructive_tests" : "Μη Καταστρεπτικές Δοκιμές"; return greeklish ? "non_destructive_tests" : "Μη Καταστρεπτικές Δοκιμές";
} else if (course.contains("Τεχνικές Σχεδίασης με Η/Υ")) { } else if (normalisedCourse.contains(("Τεχνικές Σχεδίασης με Η/Υ"))) {
return greeklish ? "sxedio" : "Σχέδιο"; return greeklish ? "sxedio" : "Σχέδιο";
} else if (course.contains("Τεχνικές Κωδικοποίησης")) { } else if (normalisedCourse.contains(("Τεχνικές Κωδικοποίησης"))) {
return greeklish ? "texn_kwdikopoihshs" : "Τεχνικές Κωδικοποίησης"; return greeklish ? "texn_kwdikopoihshs" : "Τεχνικές Κωδικοποίησης";
} else if (course.contains("Τεχνικές Βελτιστοποίησης")) { } else if (normalisedCourse.contains(("Τεχνικές Βελτιστοποίησης"))) {
return greeklish ? "veltistopoihsh" : "Βελτιστοποίηση"; return greeklish ? "veltistopoihsh" : "Βελτιστοποίηση";
} else if (course.contains("Σύνθεση Τηλεπικοινωνιακών Διατάξεων")) { } else if (normalisedCourse.contains(("Σύνθεση Τηλεπικοινωνιακών Διατάξεων"))) {
return greeklish ? "synth_thlep_diataksewn" : "Σύνθεση Τηλεπ. Διατάξεων"; return greeklish ? "synth_thlep_diataksewn" : "Σύνθεση Τηλεπ. Διατάξεων";
} else if (course.contains("Σύνθεση Ενεργών και Παθητικών Κυκλωμάτων")) { } else if (normalisedCourse.contains(("Σύνθεση Ενεργών και Παθητικών Κυκλωμάτων"))) {
return greeklish ? "synthesh" : "Σύνθεση"; return greeklish ? "synthesh" : "Σύνθεση";
} else if (course.contains("Σχεδίαση Συστημάτων VLSI")) { } else if (normalisedCourse.contains(("Σχεδίαση Συστημάτων VLSI"))) {
return greeklish ? "VLSI" : "VLSI"; return greeklish ? "VLSI" : "VLSI";
} else if (course.contains("Συστήματα Υπολογιστών (Υπολογιστικά Συστήματα)")) { } else if (normalisedCourse.contains(("Συστήματα Υπολογιστών (Υπολογιστικά Συστήματα)"))) {
return greeklish ? "sys_ypologistwn" : "Συσ. Υπολογιστών"; return greeklish ? "sys_ypologistwn" : "Συσ. Υπολογιστών";
} else if (course.contains("Συστήματα Πολυμέσων και Εικονική Πραγματικότητα")) { } else if (normalisedCourse.contains(("Συστήματα Πολυμέσων και Εικονική Πραγματικότητα"))) {
return greeklish ? "polymesa" : "Πολυμέσα"; return greeklish ? "polymesa" : "Πολυμέσα";
} else if (course.contains("Συστήματα Μικροϋπολογιστών")) { } else if (normalisedCourse.contains(("Συστήματα Μικροϋπολογιστών"))) {
return greeklish ? "mikro_I" : "Μίκρο 1"; return greeklish ? "mikro_I" : "Μίκρο 1";
} else if (course.contains("Συστήματα Ηλεκτροκίνησης")) { } else if (normalisedCourse.contains(("Συστήματα Ηλεκτροκίνησης"))) {
return greeklish ? "hlektrokinhsh" : "Ηλεκτροκίνηση"; return greeklish ? "hlektrokinhsh" : "Ηλεκτροκίνηση";
} else if (course.contains("Συστήματα Ηλεκτρικής Ενέργειας ΙΙΙ")) { } else if (normalisedCourse.contains(("Συστήματα Ηλεκτρικής Ενέργειας III"))) {
return greeklish ? "SHE_III" : "ΣΗΕ 3"; return greeklish ? "SHE_III" : "ΣΗΕ 3";
} else if (course.contains("Συστήματα Ηλεκτρικής Ενέργειας ΙΙ")) { } else if (normalisedCourse.contains(("Συστήματα Ηλεκτρικής Ενέργειας II"))) {
return greeklish ? "SHE_II" : "ΣΗΕ 2"; return greeklish ? "SHE_II" : "ΣΗΕ 2";
} else if (course.contains("Συστήματα Ηλεκτρικής Ενέργειας Ι")) { } else if (normalisedCourse.contains(("Συστήματα Ηλεκτρικής Ενέργειας I"))) {
return greeklish ? "SHE_I" : "ΣΗΕ 1"; return greeklish ? "SHE_I" : "ΣΗΕ 1";
} else if (course.contains("Συστήματα Αυτομάτου Ελέγχου ΙΙI")) { } else if (normalisedCourse.contains(("Συστήματα Αυτομάτου Ελέγχου III"))) {
return greeklish ? "SAE_III" : "ΣΑΕ 3"; return greeklish ? "SAE_III" : "ΣΑΕ 3";
} else if (course.contains("Συστήματα Αυτομάτου Ελέγχου ΙΙ")) { } else if (normalisedCourse.contains(("Συστήματα Αυτομάτου Ελέγχου II"))) {
return greeklish ? "SAE_II" : "ΣΑΕ 2"; return greeklish ? "SAE_II" : "ΣΑΕ 2";
} else if (course.contains("Συστήματα Αυτομάτου Ελέγχου Ι")) { } else if (normalisedCourse.contains(("Συστήματα Αυτομάτου Ελέγχου I"))) {
return greeklish ? "SAE_1" : "ΣΑΕ 1"; return greeklish ? "SAE_1" : "ΣΑΕ 1";
} else if (course.contains("Στοχαστικό Σήμα")) { } else if (normalisedCourse.contains(("Στοχαστικό Σήμα"))) {
return greeklish ? "stox_shma" : "Στοχ. Σήμα"; return greeklish ? "stox_shma" : "Στοχ. Σήμα";
} else if (course.contains("Σταθμοί Παραγωγής Ηλεκτρικής Ενέργειας")) { } else if (normalisedCourse.contains(("Σταθμοί Παραγωγής Ηλεκτρικής Ενέργειας"))) {
return greeklish ? "SPHE" : "ΣΠΗΕ"; return greeklish ? "SPHE" : "ΣΠΗΕ";
} else if (course.contains("Σερβοκινητήρια Συστήματα")) { } else if (normalisedCourse.contains(("Σερβοκινητήρια Συστήματα"))) {
return greeklish ? "servo" : "Σέρβο"; return greeklish ? "servo" : "Σέρβο";
} else if (course.contains("Σήματα και Συστήματα")) { } else if (normalisedCourse.contains(("Σήματα και Συστήματα"))) {
return greeklish ? "analog_shma" : "Σύματα & Συστήματα"; return greeklish ? "analog_shma" : "Σύματα & Συστήματα";
} else if (course.contains("Ρομποτική")) { } else if (normalisedCourse.contains(("Ρομποτική"))) {
return greeklish ? "rompotikh" : "Ρομποτική"; return greeklish ? "rompotikh" : "Ρομποτική";
} else if (course.contains("Προσομοίωση και Μοντελοποίηση Συστημάτων")) { } else if (normalisedCourse.contains(("Προσομοίωση και Μοντελοποίηση Συστημάτων"))) {
return greeklish ? "montelopoihsh" : "Μοντελοποίηση"; return greeklish ? "montelopoihsh" : "Μοντελοποίηση";
} else if (course.contains("Προηγμένες Τεχνικές Επεξεργασίας Σήματος")) { } else if (normalisedCourse.contains(("Προηγμένες Τεχνικές Επεξεργασίας Σήματος"))) {
return greeklish ? "ptes" : "ΠΤΕΣ"; return greeklish ? "ptes" : "ΠΤΕΣ";
} else if (course.contains("Προγραμματιστικές Τεχνικές")) { } else if (normalisedCourse.contains(("Προγραμματιστικές Τεχνικές"))) {
return greeklish ? "cpp" : "Προγραμματ. Τεχν."; return greeklish ? "cpp" : "Προγραμματ. Τεχν.";
} else if (course.contains("Προγραμματιζόμενα Κυκλώματα ASIC")) { } else if (normalisedCourse.contains(("Προγραμματιζόμενα Κυκλώματα ASIC"))) {
return greeklish ? "asic" : "ASIC"; return greeklish ? "asic" : "ASIC";
} else if (course.contains("Παράλληλα και Κατανεμημένα Συστήματα")) { } else if (normalisedCourse.contains(("Παράλληλα και Κατανεμημένα Συστήματα"))) {
return greeklish ? "parallhla" : "Παράλληλα"; return greeklish ? "parallhla" : "Παράλληλα";
} else if (course.contains("Οργάνωση και Διοίκηση Εργοστασίων")) { } else if (normalisedCourse.contains(("Οργάνωση και Διοίκηση Εργοστασίων"))) {
return greeklish ? "organ_dioik_ergostasiwn" : "Οργάνωση και Διοίκηση Εργοστασίων"; return greeklish ? "organ_dioik_ergostasiwn" : "Οργάνωση και Διοίκηση Εργοστασίων";
} else if (course.contains("Οργάνωση Υπολογιστών")) { } else if (normalisedCourse.contains(("Οργάνωση Υπολογιστών"))) {
return greeklish ? "org_ypol" : "Οργάνωση Υπολ."; return greeklish ? "org_ypol" : "Οργάνωση Υπολ.";
} else if (course.contains("Οπτική ΙΙ")) { } else if (normalisedCourse.contains(("Οπτική II"))) {
return greeklish ? "optikh_II" : "Οπτική 2"; return greeklish ? "optikh_II" : "Οπτική 2";
} else if (course.contains("Οπτική Ι")) { } else if (normalisedCourse.contains(("Οπτική I"))) {
return greeklish ? "optikh_I" : "Οπτική 1"; return greeklish ? "optikh_I" : "Οπτική 1";
} else if (course.contains("Οπτικές Επικοινωνίες")) { } else if (normalisedCourse.contains(("Οπτικές Επικοινωνίες"))) {
return greeklish ? "optikes_thlep" : "Οπτικές Τηλεπ."; return greeklish ? "optikes_thlep" : "Οπτικές Τηλεπ.";
} else if (course.contains("Μικροκύματα II")) { } else if (normalisedCourse.contains(("Μικροκύματα II"))) {
return greeklish ? "mikrokymata_II" : "Μικροκύματα 2"; return greeklish ? "mikrokymata_II" : "Μικροκύματα 2";
} else if (course.contains("Μικροκύματα I")) { } else if (normalisedCourse.contains(("Μικροκύματα I"))) {
return greeklish ? "mikrokymata_I" : "Μικροκύματα 1"; return greeklish ? "mikrokymata_I" : "Μικροκύματα 1";
} else if (course.contains("Μικροκυματική Τηλεπισκόπηση")) { } else if (normalisedCourse.contains(("Μικροκυματική Τηλεπισκόπηση"))) {
return greeklish ? "thlepiskophsh" : "Τηλεπισκόπηση"; return greeklish ? "thlepiskophsh" : "Τηλεπισκόπηση";
} else if (course.contains("Μικροεπεξεργαστές και Περιφερειακά")) { } else if (normalisedCourse.contains(("Μικροεπεξεργαστές και Περιφερειακά"))) {
return greeklish ? "mikro_II" : "Μίκρο 2"; return greeklish ? "mikro_II" : "Μίκρο 2";
} else if (course.contains("Μετάδοση Θερμότητας")) { } else if (normalisedCourse.contains(("Μετάδοση Θερμότητας"))) {
return greeklish ? "metadosi_therm" : "Μετάδοση Θερμ."; return greeklish ? "metadosi_therm" : "Μετάδοση Θερμ.";
} else if (course.contains("Λογισμός ΙΙ")) { } else if (normalisedCourse.contains(("Λογισμός II"))) {
return greeklish ? "logismos_II" : "Λογισμός 2"; return greeklish ? "logismos_II" : "Λογισμός 2";
} else if (course.contains("Λογισμός Ι")) { } else if (normalisedCourse.contains(("Λογισμός I"))) {
return greeklish ? "logismos_I" : "Λογισμός 1"; return greeklish ? "logismos_I" : "Λογισμός 1";
} else if (course.contains("Λογική Σχεδίαση")) { } else if (normalisedCourse.contains(("Λογική Σχεδίαση"))) {
return greeklish ? "logiki_sxediash" : "Λογική Σχεδίαση"; return greeklish ? "logiki_sxediash" : "Λογική Σχεδίαση";
} else if (course.contains("Λειτουργικά Συστήματα")) { } else if (normalisedCourse.contains(("Λειτουργικά Συστήματα"))) {
return greeklish ? "OS" : "Λειτουργικά"; return greeklish ? "OS" : "Λειτουργικά";
} else if (course.contains("Κινητές και Δορυφορικές Επικοινωνίες")) { } else if (normalisedCourse.contains(("Κινητές και Δορυφορικές Επικοινωνίες"))) {
return greeklish ? "kinhtes_doryforikes_epik" : "Κινητές & Δορυφορικές Επικοινωνίες"; return greeklish ? "kinhtes_doryforikes_epik" : "Κινητές & Δορυφορικές Επικοινωνίες";
} else if (course.contains("Κβαντική Φυσική")) { } else if (normalisedCourse.contains(("Κβαντική Φυσική"))) {
return greeklish ? "kvantikh" : "Κβαντική"; return greeklish ? "kvantikh" : "Κβαντική";
} else if (course.contains("Θεωρία και Τεχνολογία Πυρηνικών Αντιδραστήρων")) { } else if (normalisedCourse.contains(("Θεωρία και Τεχνολογία Πυρηνικών Αντιδραστήρων"))) {
return greeklish ? "texn_antidrasthrwn" : "Τεχνολογία Αντιδραστήρων"; return greeklish ? "texn_antidrasthrwn" : "Τεχνολογία Αντιδραστήρων";
} else if (course.contains("Θεωρία Υπολογισμών και Αλγορίθμων")) { } else if (normalisedCourse.contains(("Θεωρία Υπολογισμών και Αλγορίθμων"))) {
return greeklish ? "thya" : "ΘΥΑ"; return greeklish ? "thya" : "ΘΥΑ";
} else if (course.contains("Θεωρία Σκέδασης")) { } else if (normalisedCourse.contains(("Θεωρία Σκέδασης"))) {
return greeklish ? "skedash" : "Σκέδαση"; return greeklish ? "skedash" : "Σκέδαση";
} else if (course.contains("Θεωρία Σημάτων και Γραμμικών Συστημάτων")) { } else if (normalisedCourse.contains(("Θεωρία Σημάτων και Γραμμικών Συστημάτων"))) {
return greeklish ? "analog_shma" : "Σύματα & Συστήματα"; return greeklish ? "analog_shma" : "Σύματα & Συστήματα";
} else if (course.contains("Θεωρία Πληροφοριών")) { } else if (normalisedCourse.contains(("Θεωρία Πληροφοριών"))) {
return greeklish ? "theoria_plir" : "Θεωρία Πληρ."; return greeklish ? "theoria_plir" : "Θεωρία Πληρ.";
} else if (course.contains("Θεωρία Πιθανοτήτων και Στατιστική")) { } else if (normalisedCourse.contains(("Θεωρία Πιθανοτήτων και Στατιστική"))) {
return greeklish ? "pithanothtes" : "Πιθανότητες"; return greeklish ? "pithanothtes" : "Πιθανότητες";
} else if (course.contains("Ημιαγωγά Υλικά: Θεωρία-Διατάξεις")) { } else if (normalisedCourse.contains(("Ημιαγωγά Υλικά: Θεωρία-Διατάξεις"))) {
return greeklish ? "Hmiagwga_Ylika" : "Ημιαγωγά Υλικά"; return greeklish ? "Hmiagwga_Ylika" : "Ημιαγωγά Υλικά";
} else if (course.contains("Ηλεκτρονική ΙΙΙ")) { } else if (normalisedCourse.contains(("Ηλεκτρονική III"))) {
return greeklish ? "hlektronikh_III" : "Ηλεκτρονική 3"; return greeklish ? "hlektronikh_III" : "Ηλεκτρονική 3";
} else if (course.contains("Ηλεκτρονική ΙΙ")) { } else if (normalisedCourse.contains(("Ηλεκτρονική II"))) {
return greeklish ? "hlektronikh_2" : "Ηλεκτρονική 2"; return greeklish ? "hlektronikh_2" : "Ηλεκτρονική 2";
} else if (course.contains("Ηλεκτρονική Ι")) { } else if (normalisedCourse.contains(("Ηλεκτρονική I"))) {
return greeklish ? "hlektronikh_1" : "Ηλεκτρονική 1"; return greeklish ? "hlektronikh_1" : "Ηλεκτρονική 1";
} else if (course.contains("Ηλεκτρονικές Διατάξεις και Μετρήσεις")) { } else if (normalisedCourse.contains(("Ηλεκτρονικές Διατάξεις και Μετρήσεις"))) {
return greeklish ? "hlektron_diatakseis_metrhseis" : "Ηλεκτρονικές Διατάξεις και Μετρήσεις"; return greeklish ? "hlektron_diatakseis_metrhseis" : "Ηλεκτρονικές Διατάξεις και Μετρήσεις";
} else if (course.contains("Ηλεκτρονικά Ισχύος ΙΙ")) { } else if (normalisedCourse.contains(("Ηλεκτρονικά Iσχύος II"))) {
return greeklish ? "isxyos_II" : "Ισχύος 2"; return greeklish ? "isxyos_II" : "Ισχύος 2";
} else if (course.contains("Ηλεκτρονικά Ισχύος Ι")) { } else if (normalisedCourse.contains(("Ηλεκτρονικά Iσχύος I"))) {
return greeklish ? "isxyos_I" : "Ισχύος 1"; return greeklish ? "isxyos_I" : "Ισχύος 1";
} else if (course.contains("Ηλεκτρομαγνητικό Πεδίο ΙΙ")) { } else if (normalisedCourse.contains(("Ηλεκτρομαγνητικό Πεδίο II"))) {
return greeklish ? "pedio_II" : "Πεδίο 2"; return greeklish ? "pedio_II" : "Πεδίο 2";
} else if (course.contains("Ηλεκτρομαγνητικό Πεδίο Ι")) { } else if (normalisedCourse.contains(("Ηλεκτρομαγνητικό Πεδίο I"))) {
return greeklish ? "pedio_I" : "Πεδίο 1"; return greeklish ? "pedio_I" : "Πεδίο 1";
} else if (course.contains("Ηλεκτρομαγνητική Συμβατότητα")) { } else if (normalisedCourse.contains(("Ηλεκτρομαγνητική Συμβατότητα"))) {
return greeklish ? "HM_symvatothta" : "H/M Συμβατότητα"; return greeklish ? "HM_symvatothta" : "H/M Συμβατότητα";
} else if (course.contains("Ηλεκτρολογικά Υλικά")) { } else if (normalisedCourse.contains(("Ηλεκτρολογικά Υλικά"))) {
return greeklish ? "ylika" : "Ηλεκτρ. Υλικά"; return greeklish ? "ylika" : "Ηλεκτρ. Υλικά";
} else if (course.contains("Ηλεκτρική Οικονομία")) { } else if (normalisedCourse.contains(("Ηλεκτρική Οικονομία"))) {
return greeklish ? "hlektr_oikonomia" : "Ηλεκτρική Οικονομία"; return greeklish ? "hlektr_oikonomia" : "Ηλεκτρική Οικονομία";
} else if (course.contains("Ηλεκτρικές Μηχανές Γ'")) { } else if (normalisedCourse.contains(("Ηλεκτρικές Μηχανές Γ'"))) {
return greeklish ? "mhxanes_C" : "Μηχανές Γ"; return greeklish ? "mhxanes_C" : "Μηχανές Γ";
} else if (course.contains("Ηλεκτρικές Μηχανές Β'")) { } else if (normalisedCourse.contains(("Ηλεκτρικές Μηχανές Β'"))) {
return greeklish ? "mhxanes_B" : "Μηχανές Β"; return greeklish ? "mhxanes_B" : "Μηχανές Β";
} else if (course.contains("Ηλεκτρικές Μηχανές Α'")) { } else if (normalisedCourse.contains(("Ηλεκτρικές Μηχανές Α'"))) {
return greeklish ? "mhxanes_A" : "Μηχανές Α"; return greeklish ? "mhxanes_A" : "Μηχανές Α";
} else if (course.contains("Ηλεκτρικές Μετρήσεις ΙΙ")) { } else if (normalisedCourse.contains(("Ηλεκτρικές Μετρήσεις II"))) {
return greeklish ? "metrhseis_II" : "Μετρήσεις 2"; return greeklish ? "metrhseis_II" : "Μετρήσεις 2";
} else if (course.contains("Ηλεκτρικές Μετρήσεις Ι")) { } else if (normalisedCourse.contains(("Ηλεκτρικές Μετρήσεις I"))) {
return greeklish ? "metrhseis_1" : "Μετρήσεις 1"; return greeklish ? "metrhseis_1" : "Μετρήσεις 1";
} else if (course.contains("Ηλεκτρικά Κυκλώματα ΙΙΙ")) { } else if (normalisedCourse.contains(("Ηλεκτρικά Κυκλώματα III"))) {
return greeklish ? "kyklwmata_I" : "Κυκλώματα 3"; return greeklish ? "kyklwmata_I" : "Κυκλώματα 3";
} else if (course.contains("Ηλεκτρικά Κυκλώματα ΙΙ")) { } else if (normalisedCourse.contains(("Ηλεκτρικά Κυκλώματα II"))) {
return greeklish ? "kyklwmata_II" : "Κυκλώματα 2"; return greeklish ? "kyklwmata_II" : "Κυκλώματα 2";
} else if (course.contains("Ηλεκτρικά Κυκλώματα Ι")) { } else if (normalisedCourse.contains(("Ηλεκτρικά Κυκλώματα I"))) {
return greeklish ? "kyklwmata_I" : "Κυκλώματα 1"; return greeklish ? "kyklwmata_I" : "Κυκλώματα 1";
} else if (course.contains("Ηλεκτρακουστική ΙΙ")) { } else if (normalisedCourse.contains(("Ηλεκτρακουστική II"))) {
return greeklish ? "hlektroakoystikh_II" : "Ηλεκτροακουστική 2"; return greeklish ? "hlektroakoystikh_II" : "Ηλεκτροακουστική 2";
} else if (course.contains("Ηλεκτρακουστική Ι")) { } else if (normalisedCourse.contains(("Ηλεκτρακουστική I"))) {
return greeklish ? "hlektroakoystikh_I" : "Ηλεκτροακουστική 1"; return greeklish ? "hlektroakoystikh_I" : "Ηλεκτροακουστική 1";
} else if (course.contains("Εφαρμοσμένη Θερμοδυναμική")) { } else if (normalisedCourse.contains(("Εφαρμοσμένη Θερμοδυναμική"))) {
return greeklish ? "thermodynamikh" : "Θερμοδυναμική"; return greeklish ? "thermodynamikh" : "Θερμοδυναμική";
} else if (course.contains("Εφαρμοσμένα Μαθηματικά ΙΙ")) { } else if (normalisedCourse.contains(("Εφαρμοσμένα Μαθηματικά II"))) {
return greeklish ? "efarmosmena_math_II" : "Εφαρμοσμένα 2"; return greeklish ? "efarmosmena_math_II" : "Εφαρμοσμένα 2";
} else if (course.contains("Εφαρμοσμένα Μαθηματικά Ι")) { } else if (normalisedCourse.contains(("Εφαρμοσμένα Μαθηματικά I"))) {
return greeklish ? "efarmosmena_math_I" : "Εφαρμοσμένα 1"; return greeklish ? "efarmosmena_math_I" : "Εφαρμοσμένα 1";
} else if (course.contains("Εφαρμογές Τηλεπικοινωνιακών Διατάξεων")) { } else if (normalisedCourse.contains(("Εφαρμογές Τηλεπικοινωνιακών Διατάξεων"))) {
return greeklish ? "efarm_thlep_diataksewn" : "Εφαρμογές Τηλεπ. Διατάξεων"; return greeklish ? "efarm_thlep_diataksewn" : "Εφαρμογές Τηλεπ. Διατάξεων";
} else if (course.contains("Ευφυή Συστήματα Ρομπότ")) { } else if (normalisedCourse.contains(("Ευφυή Συστήματα Ρομπότ"))) {
return greeklish ? "eufuh" : "Ευφυή"; return greeklish ? "eufuh" : "Ευφυή";
} else if (course.contains("Ευρυζωνικά Δίκτυα")) { } else if (normalisedCourse.contains(("Ευρυζωνικά Δίκτυα"))) {
return greeklish ? "eyryzwnika" : "Ευρυζωνικά"; return greeklish ? "eyryzwnika" : "Ευρυζωνικά";
} else if (course.contains("Επιχειρησιακή Έρευνα")) { } else if (normalisedCourse.contains(("Επιχειρησιακή Έρευνα"))) {
return greeklish ? "epixeirisiaki" : "Επιχειρησιακή Έρευνα"; return greeklish ? "epixeirisiaki" : "Επιχειρησιακή Έρευνα";
} else if (course.contains("Ενσωματωμένα Συστήματα Πραγματικού Χρόνου")) { } else if (normalisedCourse.contains(("Ενσωματωμένα Συστήματα Πραγματικού Χρόνου"))) {
return greeklish ? "enswmatwmena" : "Ενσωματωμένα"; return greeklish ? "enswmatwmena" : "Ενσωματωμένα";
} else if (course.contains("Εισαγωγή στις εφαρμογές Πυρηνικής Τεχνολογίας")) { } else if (normalisedCourse.contains(("Εισαγωγή στις εφαρμογές Πυρηνικής Τεχνολογίας"))) {
return greeklish ? "Intro_Purhnikh_Texn" : "Εισ. Πυρηνικη Τεχν."; return greeklish ? "Intro_Purhnikh_Texn" : "Εισ. Πυρηνικη Τεχν.";
} else if (course.contains("Εισαγωγή στην Πολιτική Οικονομία")) { } else if (normalisedCourse.contains(("Εισαγωγή στην Πολιτική Οικονομία"))) {
return greeklish ? "polit_oik" : "Πολιτική Οικονομία"; return greeklish ? "polit_oik" : "Πολιτική Οικονομία";
} else if (course.contains("Εισαγωγή στην Ενεργειακή Τεχνολογία ΙΙ")) { } else if (normalisedCourse.contains(("Εισαγωγή στην Ενεργειακή Τεχνολογία II"))) {
return greeklish ? "EET_2" : "ΕΕΤ2"; return greeklish ? "EET_2" : "ΕΕΤ2";
} else if (course.contains("Εισαγωγή στην Ενεργειακή Τεχνολογία Ι")) { } else if (normalisedCourse.contains(("Εισαγωγή στην Ενεργειακή Τεχνολογία I"))) {
return greeklish ? "EET_I" : "ΕΕΤ 1"; return greeklish ? "EET_I" : "ΕΕΤ 1";
} else if (course.contains("Ειδικές Κεραίες, Σύνθεση Κεραιών")) { } else if (normalisedCourse.contains(("Ειδικές Κεραίες, Σύνθεση Κεραιών"))) {
return greeklish ? "eidikes_keraies" : "Ειδικές Κεραίες, Σύνθεση Κεραιών"; return greeklish ? "eidikes_keraies" : "Ειδικές Κεραίες, Σύνθεση Κεραιών";
} else if (course.contains("Ειδικές Αρχιτεκτονικές Υπολογιστών")) { } else if (normalisedCourse.contains(("Ειδικές Αρχιτεκτονικές Υπολογιστών"))) {
return greeklish ? "eidikes_arx_ypolog" : "Ειδικές Αρχιτεκτονικές Υπολογιστών"; return greeklish ? "eidikes_arx_ypolog" : "Ειδικές Αρχιτεκτονικές Υπολογιστών";
} else if (course.contains("Ειδικά Κεφάλαια Συστημάτων Ηλεκτρικής Ενέργειας")) { } else if (normalisedCourse.contains(("Ειδικά Κεφάλαια Συστημάτων Ηλεκτρικής Ενέργειας"))) {
return greeklish ? "ekshe" : "ΕΚΣΗΕ"; return greeklish ? "ekshe" : "ΕΚΣΗΕ";
} else if (course.contains("Ειδικά Κεφάλαια Ηλεκτρομαγνητικού Πεδίου Ι")) { } else if (normalisedCourse.contains(("Ειδικά Κεφάλαια Ηλεκτρομαγνητικού Πεδίου I"))) {
return greeklish ? "eidika_kef_HM_pedioy_I" : "Ειδικά Κεφάλαια Ηλεκτρομαγνητικού Πεδίου Ι"; return greeklish ? "eidika_kef_HM_pedioy_I" : "Ειδικά Κεφάλαια Ηλεκτρομαγνητικού Πεδίου I";
} else if (course.contains("Ειδικά Κεφάλαια Διαφορικών Εξισώσεων")) { } else if (normalisedCourse.contains(("Ειδικά Κεφάλαια Διαφορικών Εξισώσεων"))) {
return greeklish ? "eidika_kef_diaf_eksis" : "Ειδικά Κεφάλαια Διαφορικών Εξισώσεων"; return greeklish ? "eidika_kef_diaf_eksis" : "Ειδικά Κεφάλαια Διαφορικών Εξισώσεων";
} else if (course.contains("Δομημένος Προγραμματισμός")) { } else if (normalisedCourse.contains(("Δομημένος Προγραμματισμός"))) {
return greeklish ? "C" : "Δομ. Προγραμμ."; return greeklish ? "C" : "Δομ. Προγραμμ.";
} else if (course.contains("Δομές Δεδομένων")) { } else if (normalisedCourse.contains(("Δομές Δεδομένων"))) {
return greeklish ? "dom_dedomenwn" : "Δομ. Δεδομ."; return greeklish ? "dom_dedomenwn" : "Δομ. Δεδομ.";
} else if (course.contains("Διαχείριση Συστημάτων Ηλεκτρικής Ενέργειας")) { } else if (normalisedCourse.contains(("Διαχείριση Συστημάτων Ηλεκτρικής Ενέργειας"))) {
return greeklish ? "dshe" : "ΔΣΗΕ"; return greeklish ? "dshe" : "ΔΣΗΕ";
} else if (course.contains("Διαφορικές Εξισώσεις")) { } else if (normalisedCourse.contains(("Διαφορικές Εξισώσεις"))) {
return greeklish ? "diaforikes" : "Διαφορικές"; return greeklish ? "diaforikes" : "Διαφορικές";
} else if (course.contains("Διανεμημένη Παραγωγή")) { } else if (normalisedCourse.contains(("Διανεμημένη Παραγωγή"))) {
return greeklish ? "dian_paragwgh" : "Διανεμημένη Παραγωγή"; return greeklish ? "dian_paragwgh" : "Διανεμημένη Παραγωγή";
} else if (course.contains("Διακριτά μαθηματικά")) { } else if (normalisedCourse.contains(("Διακριτά μαθηματικά"))) {
return greeklish ? "diakrita" : "Διακριτά Μαθηματικά"; return greeklish ? "diakrita" : "Διακριτά Μαθηματικά";
} else if (course.contains("Διακριτά Μαθηματικά")) { } else if (normalisedCourse.contains(("Διακριτά Μαθηματικά"))) {
return greeklish ? "diakrita" : "Διακριτά Μαθηματικά"; return greeklish ? "diakrita" : "Διακριτά Μαθηματικά";
} else if (course.contains("Διάδοση Ηλεκτρομαγνητικού Κύματος Ι (πρώην Πεδίο ΙΙΙ)")) { } else if (normalisedCourse.contains(("Διάδοση Ηλεκτρομαγνητικού Κύματος I (πρώην Πεδίο III)"))) {
return greeklish ? "diadosi_1" : "Διάδοση 1"; return greeklish ? "diadosi_1" : "Διάδοση 1";
} else if (course.contains("Διάδοση Η/Μ Κύματος ΙΙ")) { } else if (normalisedCourse.contains(("Διάδοση Η/Μ Κύματος II"))) {
return greeklish ? "diadosi_II" : "Διάδοση 2"; return greeklish ? "diadosi_II" : "Διάδοση 2";
} else if (course.contains("Δίκτυα Υπολογιστών ΙΙ")) { } else if (normalisedCourse.contains(("Δίκτυα Υπολογιστών II"))) {
return greeklish ? "diktya_II" : "Δίκτυα 2"; return greeklish ? "diktya_II" : "Δίκτυα 2";
} else if (course.contains("Δίκτυα Υπολογιστών Ι")) { } else if (normalisedCourse.contains(("Δίκτυα Υπολογιστών I"))) {
return greeklish ? "diktya_I" : "Δίκτυα 1"; return greeklish ? "diktya_I" : "Δίκτυα 1";
} else if (course.contains("Δίκτυα Τηλεπικοινωνιών")) { } else if (normalisedCourse.contains(("Δίκτυα Τηλεπικοινωνιών"))) {
return greeklish ? "diktya_thlep" : "Δίκτυα Τηλέπ."; return greeklish ? "diktya_thlep" : "Δίκτυα Τηλέπ.";
} else if (course.contains("Γραφική με Υπολογιστές")) { } else if (normalisedCourse.contains(("Γραφική με Υπολογιστές"))) {
return greeklish ? "grafikh" : "Γραφική"; return greeklish ? "grafikh" : "Γραφική";
} else if (course.contains("Γραμμική Άλγεβρα")) { } else if (normalisedCourse.contains(("Γραμμική Άλγεβρα"))) {
return greeklish ? "grammikh_algebra" : "Γραμμ. Άλγεβρ."; return greeklish ? "grammikh_algebra" : "Γραμμ. Άλγεβρ.";
} else if (course.contains("Γεωηλεκτρομαγνητισμός")) { } else if (normalisedCourse.contains(("Γεωηλεκτρομαγνητισμός"))) {
return greeklish ? "geohlektromagnitismos" : "Γεωηλεκτρομαγνητισμός"; return greeklish ? "geohlektromagnitismos" : "Γεωηλεκτρομαγνητισμός";
} else if (course.contains("Βιοϊατρική Τεχνολογία")) { } else if (normalisedCourse.contains(("Βιοϊατρική Τεχνολογία"))) {
return greeklish ? "vioiatriki" : "Βιοιατρική"; return greeklish ? "vioiatriki" : "Βιοιατρική";
} else if (course.contains("Βιομηχανική Πληροφορική")) { } else if (normalisedCourse.contains(("Βιομηχανική Πληροφορική"))) {
return greeklish ? "viomix_plir" : "Βιομηχανική Πληρ"; return greeklish ? "viomix_plir" : "Βιομηχανική Πληρ";
} else if (course.contains("Βιομηχανικά Ηλεκτρονικά")) { } else if (normalisedCourse.contains(("Βιομηχανικά Ηλεκτρονικά"))) {
return greeklish ? "bhomix_hlektronika" : "Βιομηχανικά Ηλεκτρονικά"; return greeklish ? "bhomix_hlektronika" : "Βιομηχανικά Ηλεκτρονικά";
} else if (course.contains("Βάσεις Δεδομένων")) { } else if (normalisedCourse.contains(("Βάσεις Δεδομένων"))) {
return greeklish ? "vaseis" : "Βάσεις"; return greeklish ? "vaseis" : "Βάσεις";
} else if (course.contains("Ασύρματος Τηλεπικοινωνία ΙΙ")) { } else if (normalisedCourse.contains(("Ασύρματος Τηλεπικοινωνία II"))) {
return greeklish ? "asyrmatos_II" : "Ασύρματος 2"; return greeklish ? "asyrmatos_II" : "Ασύρματος 2";
} else if (course.contains("Ασύρματος Τηλεπικοινωνία Ι")) { } else if (normalisedCourse.contains(("Ασύρματος Τηλεπικοινωνία I"))) {
return greeklish ? "asyrmatos_I" : "Ασύρματος 1"; return greeklish ? "asyrmatos_I" : "Ασύρματος 1";
} else if (course.contains("Ασφάλεια Πληροφοριακών Συστημάτων")) { } else if (normalisedCourse.contains(("Ασφάλεια Πληροφοριακών Συστημάτων"))) {
return greeklish ? "asfaleia" : "Ασφάλεια"; return greeklish ? "asfaleia" : "Ασφάλεια";
} else if (course.contains("Ασαφή Συστήματα")) { } else if (normalisedCourse.contains(("Ασαφή Συστήματα"))) {
return greeklish ? "asafh" : "Ασαφή"; return greeklish ? "asafh" : "Ασαφή";
} else if (course.contains("Αρχιτεκτονική Υπολογιστών")) { } else if (normalisedCourse.contains(("Αρχιτεκτονική Υπολογιστών"))) {
return greeklish ? "arx_ypologistwn" : "Αρχ. Υπολογιστών"; return greeklish ? "arx_ypologistwn" : "Αρχ. Υπολογιστών";
} else if (course.contains("Αρχές Παράλληλης Επεξεργασίας")) { } else if (normalisedCourse.contains(("Αρχές Παράλληλης Επεξεργασίας"))) {
return greeklish ? "arxes_parall_epeksergasias" : "Αρχές Παράλληλης Επεξεργασίας"; return greeklish ? "arxes_parall_epeksergasias" : "Αρχές Παράλληλης Επεξεργασίας";
} else if (course.contains("Αρχές Οικονομίας")) { } else if (normalisedCourse.contains(("Αρχές Οικονομίας"))) {
return greeklish ? "arx_oikonomias" : "Αρχές Οικονομίας"; return greeklish ? "arx_oikonomias" : "Αρχές Οικονομίας";
} else if (course.contains("Αριθμητική Ανάλυση")) { } else if (normalisedCourse.contains(("Αριθμητική Ανάλυση"))) {
return greeklish ? "arith_anal" : "Αριθμ. Ανάλυση"; return greeklish ? "arith_anal" : "Αριθμ. Ανάλυση";
} else if (course.contains("Αξιοπιστία Συστημάτων")) { } else if (normalisedCourse.contains(("Αξιοπιστία Συστημάτων"))) {
return greeklish ? "aksiopistia_systhmatwn" : "Αξιοπιστία Συστημάτων"; return greeklish ? "aksiopistia_systhmatwn" : "Αξιοπιστία Συστημάτων";
} else if (course.contains("Αντικειμενοστραφής Προγραμματισμός")) { } else if (normalisedCourse.contains(("Αντικειμενοστραφής Προγραμματισμός"))) {
return greeklish ? "OOP" : "Αντικειμενοστραφής"; return greeklish ? "OOP" : "Αντικειμενοστραφής";
} else if (course.contains("Αναλογικές Τηλεπικοινωνίες (πρώην Τηλεπικοινωνιακά Συστήματα Ι)")) { } else if (normalisedCourse.contains(("Αναλογικές Τηλεπικοινωνίες (πρώην Τηλεπικοινωνιακά Συστήματα I)"))) {
return greeklish ? "anal_thlep" : "Αναλογικές Τηλεπ."; return greeklish ? "anal_thlep" : "Αναλογικές Τηλεπ.";
} else if (course.contains("Αναγνώριση Προτύπων")) { } else if (normalisedCourse.contains(("Αναγνώριση Προτύπων"))) {
return greeklish ? "protipa" : "Αναγνώριση Προτύπων"; return greeklish ? "protipa" : "Αναγνώριση Προτύπων";
} else if (course.contains("Ανάλυση και Σχεδίαση Αλγορίθμων")) { } else if (normalisedCourse.contains(("Ανάλυση και Σχεδίαση Αλγορίθμων"))) {
return greeklish ? "algorithms" : "Αλγόριθμοι"; return greeklish ? "algorithms" : "Αλγόριθμοι";
} else if (course.contains("Ανάλυση Χρονοσειρών")) { } else if (normalisedCourse.contains(("Ανάλυση Χρονοσειρών"))) {
return greeklish ? "xronoseires" : "Χρονοσειρές"; return greeklish ? "xronoseires" : "Χρονοσειρές";
} else if (course.contains("Ανάλυση Συστημάτων Ηλεκτρικής Ενέργειας")) { } else if (normalisedCourse.contains(("Ανάλυση Συστημάτων Ηλεκτρικής Ενέργειας"))) {
return greeklish ? "ASHE" : "ΑΣΗΕ"; return greeklish ? "ASHE" : "ΑΣΗΕ";
} else if (course.contains("Ανάλυση Ηλεκτρικών Κυκλωμάτων με Υπολογιστή")) { } else if (normalisedCourse.contains(("Ανάλυση Ηλεκτρικών Κυκλωμάτων με Υπολογιστή"))) {
return greeklish ? "analysh_hlektr_kykl" : "Ανάλυση Ηλεκτρικ. Κυκλ. με Υπολογιστή"; return greeklish ? "analysh_hlektr_kykl" : "Ανάλυση Ηλεκτρικ. Κυκλ. με Υπολογιστή";
} else if (course.contains("Ακουστική ΙΙ")) { } else if (normalisedCourse.contains(("Ακουστική II"))) {
return greeklish ? "akoystikh_II" : "Ακουστική 2"; return greeklish ? "akoystikh_II" : "Ακουστική 2";
} else if (course.contains("Ακουστική Ι")) { } else if (normalisedCourse.contains(("Ακουστική I"))) {
return greeklish ? "akoystikh_I" : "Ακουστική 1"; return greeklish ? "akoystikh_I" : "Ακουστική 1";
} else { } else {
Timber.wtf("Unrecognised course came in the upload fields generator! Course string = %s", course);
return null; return null;
} }
} }

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

@ -1,64 +1,44 @@
package gr.thmmy.mthmmy.activities.upload; package gr.thmmy.mthmmy.activities.upload;
import android.content.Context; import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.net.Uri; import android.net.Uri;
import android.os.Environment; import android.os.Environment;
import android.provider.OpenableColumns; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.FileProvider;
import android.util.Log;
import android.widget.Toast; import android.widget.Toast;
import com.snatik.storage.Storage;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import androidx.annotation.NonNull; import gr.thmmy.mthmmy.utils.FileUtils;
import androidx.annotation.Nullable;
import timber.log.Timber; import timber.log.Timber;
class UploadsHelper { public class UploadsHelper {
private static final int DEFAULT_MIN_WIDTH_QUALITY = 400; private final static int BUFFER = 4096;
private static final String CACHE_IMAGE_NAME = "tempUploadFile.jpg"; private static final String TEMP_FILES_DIRECTORY = "~tmp_mThmmy_uploads";
@NonNull
static String filenameFromUri(Context context, Uri uri) {
String filename = null;
if (uri.getScheme().equals("content")) {
try (Cursor cursor = context.getContentResolver().query(uri, null, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
}
}
}
if (filename == null) {
filename = uri.getPath();
int cut = filename.lastIndexOf('/');
if (cut != -1) {
filename = filename.substring(cut + 1);
}
}
return filename;
}
@SuppressWarnings("ResultOfMethodCallIgnored") @SuppressWarnings("ResultOfMethodCallIgnored")
@Nullable @Nullable
static String createTempFile(Context context, Uri fileUri, String newFilename) { static Uri createTempFile(Context context, Storage storage, Uri fileUri, String newFilename) {
String oldFilename = filenameFromUri(context, fileUri); String oldFilename = FileUtils.filenameFromUri(context, fileUri);
String fileExtension = oldFilename.substring(oldFilename.indexOf(".")); String fileExtension = oldFilename.substring(oldFilename.indexOf("."));
String destinationFilename = Environment.getExternalStorageDirectory().getPath() + String destinationFilename = Environment.getExternalStorageDirectory().getPath() +
File.separatorChar + "~tmp_mThmmy_uploads" + File.separatorChar + newFilename + fileExtension; File.separatorChar + TEMP_FILES_DIRECTORY + File.separatorChar + newFilename + fileExtension;
File tempDirectory = new File(android.os.Environment.getExternalStorageDirectory().getPath() + File tempDirectory = new File(android.os.Environment.getExternalStorageDirectory().getPath() +
File.separatorChar + "~tmp_mThmmy_uploads"); File.separatorChar + TEMP_FILES_DIRECTORY);
if (!tempDirectory.exists()) { if (!tempDirectory.exists()) {
if (!tempDirectory.mkdirs()) { if (!tempDirectory.mkdirs()) {
@ -97,103 +77,65 @@ class UploadsHelper {
} }
} }
return destinationFilename; return FileProvider.getUriForFile(context, context.getPackageName() +
} ".provider", storage.getFile(destinationFilename));
static File getCacheFile(Context context) {
File imageFile = new File(context.getExternalCacheDir(), CACHE_IMAGE_NAME);
//noinspection ResultOfMethodCallIgnored
imageFile.getParentFile().mkdirs();
return imageFile;
} }
@SuppressWarnings("ResultOfMethodCallIgnored") @Nullable
static void deleteTempFiles() { public static File createZipFile(@NonNull String zipFilename) {
File tempFilesDirectory = new File(Environment.getExternalStorageDirectory().getPath() + // Create a zip file name
File.separatorChar + "~tmp_mThmmy_uploads"); File zipFolder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) +
File.separator + "mThmmy");
if (tempFilesDirectory.isDirectory()) {
String[] tempFilesArray = tempFilesDirectory.list(); if (!zipFolder.exists()) {
for (String tempFile : tempFilesArray) { if (!zipFolder.mkdirs()) {
new File(tempFilesDirectory, tempFile).delete(); Timber.w("Zip folder build returned false in %s", UploadsHelper.class.getSimpleName());
} return null;
tempFilesDirectory.delete();
} }
} }
@SuppressWarnings("ResultOfMethodCallIgnored") return new File(zipFolder, zipFilename);
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();
}
}
/**
* Resize to avoid using too much memory loading big images (e.g.: 2560*1920)
**/
static Bitmap getImageResized(Context context, Uri selectedImage) {
Bitmap bm;
int[] sampleSizes = new int[]{5, 3, 2, 1};
int i = 0;
do {
bm = decodeBitmap(context, selectedImage, sampleSizes[i]);
i++;
} while (bm.getWidth() < DEFAULT_MIN_WIDTH_QUALITY && i < sampleSizes.length);
return bm;
} }
private static Bitmap decodeBitmap(Context context, Uri theUri, int sampleSize) { public static void zip(Context context, Uri[] files, Uri zipFile) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = sampleSize;
AssetFileDescriptor fileDescriptor = null;
try { try {
fileDescriptor = context.getContentResolver().openAssetFileDescriptor(theUri, "r"); BufferedInputStream origin;
} catch (FileNotFoundException e) { OutputStream dest = context.getContentResolver().openOutputStream(zipFile);
e.printStackTrace(); assert dest != null;
} ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(dest));
byte data[] = new byte[BUFFER];
assert fileDescriptor != null; for (Uri file : files) {
return BitmapFactory.decodeFileDescriptor( InputStream inputStream = context.getContentResolver().openInputStream(file);
fileDescriptor.getFileDescriptor(), null, options); assert inputStream != null;
} origin = new BufferedInputStream(inputStream, BUFFER);
static int getRotation(Context context, Uri imageUri) { ZipEntry entry = new ZipEntry(FileUtils.filenameFromUri(context, file));
int rotation = 0; out.putNextEntry(entry);
try { int count;
context.getContentResolver().notifyChange(imageUri, null); while ((count = origin.read(data, 0, BUFFER)) != -1) {
ExifInterface exif = new ExifInterface(imageUri.getPath()); out.write(data, 0, count);
int orientation = exif.getAttributeInt( }
ExifInterface.TAG_ORIENTATION, origin.close();
ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_270:
rotation = 270;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
rotation = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_90:
rotation = 90;
break;
} }
out.close();
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
return rotation;
} }
static Bitmap rotate(Bitmap bm, int rotation) { public static void deleteTempFiles(Storage storage) {
if (rotation != 0) { File tempFilesDirectory = new File(Environment.getExternalStorageDirectory().getPath() +
Matrix matrix = new Matrix(); File.separatorChar + TEMP_FILES_DIRECTORY);
matrix.postRotate(rotation);
return Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true); if (storage.isDirectoryExists(tempFilesDirectory.getAbsolutePath())) {
for (File tempFile : storage.getFiles(tempFilesDirectory.getAbsolutePath())) {
storage.deleteFile(tempFile.getAbsolutePath());
}
storage.deleteDirectory(tempFilesDirectory.getAbsolutePath());
} }
return bm;
} }
} }

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

@ -2,21 +2,34 @@ package gr.thmmy.mthmmy.base;
import android.Manifest; import android.Manifest;
import android.app.ProgressDialog; import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.Button; import android.widget.Button;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import androidx.lifecycle.ViewModelProviders;
import androidx.preference.PreferenceManager;
import com.google.android.material.bottomsheet.BottomSheetDialog; import com.google.android.material.bottomsheet.BottomSheetDialog;
import com.google.firebase.messaging.FirebaseMessaging; import com.google.firebase.messaging.FirebaseMessaging;
import com.mikepenz.fontawesome_typeface_library.FontAwesome; import com.mikepenz.fontawesome_typeface_library.FontAwesome;
@ -28,6 +41,9 @@ import com.mikepenz.materialdrawer.Drawer;
import com.mikepenz.materialdrawer.DrawerBuilder; import com.mikepenz.materialdrawer.DrawerBuilder;
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem; import com.mikepenz.materialdrawer.model.PrimaryDrawerItem;
import com.mikepenz.materialdrawer.model.ProfileDrawerItem; import com.mikepenz.materialdrawer.model.ProfileDrawerItem;
import com.snatik.storage.Storage;
import net.gotev.uploadservice.UploadService;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
@ -35,14 +51,6 @@ import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.util.ArrayList; import java.util.ArrayList;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import androidx.lifecycle.ViewModelProviders;
import androidx.preference.PreferenceManager;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.AboutActivity; import gr.thmmy.mthmmy.activities.AboutActivity;
import gr.thmmy.mthmmy.activities.LoginActivity; import gr.thmmy.mthmmy.activities.LoginActivity;
@ -52,12 +60,15 @@ import gr.thmmy.mthmmy.activities.main.MainActivity;
import gr.thmmy.mthmmy.activities.profile.ProfileActivity; import gr.thmmy.mthmmy.activities.profile.ProfileActivity;
import gr.thmmy.mthmmy.activities.settings.SettingsActivity; import gr.thmmy.mthmmy.activities.settings.SettingsActivity;
import gr.thmmy.mthmmy.activities.shoutbox.ShoutboxActivity; import gr.thmmy.mthmmy.activities.shoutbox.ShoutboxActivity;
import gr.thmmy.mthmmy.activities.upload.UploadActivity;
import gr.thmmy.mthmmy.model.Bookmark; import gr.thmmy.mthmmy.model.Bookmark;
import gr.thmmy.mthmmy.model.ThmmyFile; import gr.thmmy.mthmmy.model.ThmmyFile;
import gr.thmmy.mthmmy.services.DownloadHelper; import gr.thmmy.mthmmy.services.DownloadHelper;
import gr.thmmy.mthmmy.services.UploadsReceiver;
import gr.thmmy.mthmmy.session.SessionManager; import gr.thmmy.mthmmy.session.SessionManager;
import gr.thmmy.mthmmy.utils.FileUtils; import gr.thmmy.mthmmy.utils.FileUtils;
import gr.thmmy.mthmmy.viewmodel.BaseViewModel; import gr.thmmy.mthmmy.viewmodel.BaseViewModel;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import ru.noties.markwon.LinkResolverDef; import ru.noties.markwon.LinkResolverDef;
import ru.noties.markwon.Markwon; import ru.noties.markwon.Markwon;
@ -72,6 +83,7 @@ import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_
import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_USERNAME; import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_PROFILE_USERNAME;
import static gr.thmmy.mthmmy.activities.settings.SettingsActivity.DEFAULT_HOME_TAB; import static gr.thmmy.mthmmy.activities.settings.SettingsActivity.DEFAULT_HOME_TAB;
import static gr.thmmy.mthmmy.services.DownloadHelper.SAVE_DIR; import static gr.thmmy.mthmmy.services.DownloadHelper.SAVE_DIR;
import static gr.thmmy.mthmmy.services.UploadsReceiver.UPLOAD_ID_KEY;
import static gr.thmmy.mthmmy.session.SessionManager.SUCCESS; import static gr.thmmy.mthmmy.session.SessionManager.SUCCESS;
import static gr.thmmy.mthmmy.utils.FileUtils.getMimeType; import static gr.thmmy.mthmmy.utils.FileUtils.getMimeType;
@ -82,6 +94,9 @@ public abstract class BaseActivity extends AppCompatActivity {
//SessionManager //SessionManager
protected static SessionManager sessionManager; protected static SessionManager sessionManager;
//Storage manager
protected Storage storage;
//Bookmarks //Bookmarks
public static final String BOOKMARKS_SHARED_PREFS = "bookmarksSharedPrefs"; public static final String BOOKMARKS_SHARED_PREFS = "bookmarksSharedPrefs";
public static final String BOOKMARKED_TOPICS_KEY = "bookmarkedTopicsKey"; public static final String BOOKMARKED_TOPICS_KEY = "bookmarkedTopicsKey";
@ -96,6 +111,9 @@ public abstract class BaseActivity extends AppCompatActivity {
//Common UI elements //Common UI elements
protected Toolbar toolbar; protected Toolbar toolbar;
protected Drawer drawer; protected Drawer drawer;
//Uploads progress dialog
UploadsShowDialogReceiver uploadsShowDialogReceiver;
AlertDialog uploadsProgressDialog;
private MainActivity mainActivity; private MainActivity mainActivity;
private boolean isMainActivity; private boolean isMainActivity;
@ -123,6 +141,8 @@ public abstract class BaseActivity extends AppCompatActivity {
BaseViewModel baseViewModel = ViewModelProviders.of(this).get(BaseViewModel.class); BaseViewModel baseViewModel = ViewModelProviders.of(this).get(BaseViewModel.class);
baseViewModel.getCurrentPageBookmark().observe(this, thisPageBookmark -> setTopicBookmark(thisPageBookmarkMenuButton)); baseViewModel.getCurrentPageBookmark().observe(this, thisPageBookmark -> setTopicBookmark(thisPageBookmarkMenuButton));
storage = new Storage(getApplicationContext());
} }
@Override @Override
@ -133,6 +153,13 @@ public abstract class BaseActivity extends AppCompatActivity {
isUserConsentDialogShown = true; isUserConsentDialogShown = true;
showUserConsentDialog(); showUserConsentDialog();
} }
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
if (uploadsShowDialogReceiver == null) {
uploadsShowDialogReceiver = new UploadsShowDialogReceiver(this);
}
this.registerReceiver(uploadsShowDialogReceiver, new IntentFilter(UploadsReceiver.ACTION_COMBINED_UPLOAD));
}
} }
@Override @Override
@ -140,6 +167,10 @@ public abstract class BaseActivity extends AppCompatActivity {
super.onPause(); super.onPause();
if (drawer != null) //close drawer animation after returning to activity if (drawer != null) //close drawer animation after returning to activity
drawer.closeDrawer(); drawer.closeDrawer();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP && uploadsShowDialogReceiver != null) {
this.unregisterReceiver(uploadsShowDialogReceiver);
}
} }
@ -151,6 +182,10 @@ public abstract class BaseActivity extends AppCompatActivity {
return sessionManager; return sessionManager;
} }
public Storage getStorage() {
return storage;
}
//TODO: move stuff below (?) //TODO: move stuff below (?)
//------------------------------------------DRAWER STUFF---------------------------------------- //------------------------------------------DRAWER STUFF----------------------------------------
protected static final int HOME_ID = 0; protected static final int HOME_ID = 0;
@ -254,14 +289,15 @@ public abstract class BaseActivity extends AppCompatActivity {
.withName(R.string.downloads) .withName(R.string.downloads)
.withIcon(downloadsIcon) .withIcon(downloadsIcon)
.withSelectedIcon(downloadsIconSelected); .withSelectedIcon(downloadsIconSelected);
// uploadItem = new PrimaryDrawerItem()
// .withTextColor(primaryColor) uploadItem = new PrimaryDrawerItem()
// .withSelectedColor(selectedPrimaryColor) .withTextColor(primaryColor)
// .withSelectedTextColor(selectedSecondaryColor) .withSelectedColor(selectedPrimaryColor)
// .withIdentifier(UPLOAD_ID) .withSelectedTextColor(selectedSecondaryColor)
// .withName(R.string.upload) .withIdentifier(UPLOAD_ID)
// .withIcon(uploadIcon) .withName(R.string.upload)
// .withSelectedIcon(uploadIconSelected); .withIcon(uploadIcon)
.withSelectedIcon(uploadIconSelected);
shoutboxItem = new PrimaryDrawerItem() shoutboxItem = new PrimaryDrawerItem()
.withTextColor(primaryColor) .withTextColor(primaryColor)
@ -377,11 +413,11 @@ public abstract class BaseActivity extends AppCompatActivity {
intent.putExtras(extras); intent.putExtras(extras);
startActivity(intent); startActivity(intent);
} }
// } else if (drawerItem.equals(UPLOAD_ID)) { } else if (drawerItem.equals(UPLOAD_ID)) {
// if (!(BaseActivity.this instanceof UploadActivity)) { if (!(BaseActivity.this instanceof UploadActivity)) {
// Intent intent = new Intent(BaseActivity.this, UploadActivity.class); Intent intent = new Intent(BaseActivity.this, UploadActivity.class);
// startActivity(intent); startActivity(intent);
// } }
} else if (drawerItem.equals(BOOKMARKS_ID)) { } else if (drawerItem.equals(BOOKMARKS_ID)) {
if (!(BaseActivity.this instanceof BookmarksActivity)) { if (!(BaseActivity.this instanceof BookmarksActivity)) {
Intent intent = new Intent(BaseActivity.this, BookmarksActivity.class); Intent intent = new Intent(BaseActivity.this, BookmarksActivity.class);
@ -432,7 +468,7 @@ public abstract class BaseActivity extends AppCompatActivity {
if (!sessionManager.isLoggedIn()) //When logged out or if user is guest if (!sessionManager.isLoggedIn()) //When logged out or if user is guest
{ {
drawer.removeItem(DOWNLOADS_ID); drawer.removeItem(DOWNLOADS_ID);
// drawer.removeItem(UPLOAD_ID); drawer.removeItem(UPLOAD_ID);
loginLogoutItem.withName(R.string.login).withIcon(loginIcon); //Swap logout with login loginLogoutItem.withName(R.string.login).withIcon(loginIcon); //Swap logout with login
profileDrawerItem.withName(sessionManager.getUsername()); profileDrawerItem.withName(sessionManager.getUsername());
setDefaultAvatar(); setDefaultAvatar();
@ -440,9 +476,9 @@ public abstract class BaseActivity extends AppCompatActivity {
if (!drawer.getDrawerItems().contains(downloadsItem)) { if (!drawer.getDrawerItems().contains(downloadsItem)) {
drawer.addItemAtPosition(downloadsItem, 4); drawer.addItemAtPosition(downloadsItem, 4);
} }
// if (!drawer.getDrawerItems().contains(uploadItem)) { if (!drawer.getDrawerItems().contains(uploadItem)) {
// drawer.addItemAtPosition(uploadItem, 5); drawer.addItemAtPosition(uploadItem, 5);
// } }
loginLogoutItem.withName(R.string.logout).withIcon(logoutIcon); //Swap login with logout loginLogoutItem.withName(R.string.logout).withIcon(logoutIcon); //Swap login with logout
profileDrawerItem.withName(sessionManager.getUsername()); profileDrawerItem.withName(sessionManager.getUsername());
if (sessionManager.hasAvatar()) if (sessionManager.hasAvatar())
@ -667,10 +703,10 @@ public abstract class BaseActivity extends AppCompatActivity {
//-------------------------------------------BOOKMARKS END------------------------------------------ //-------------------------------------------BOOKMARKS END------------------------------------------
//-------PERMS--------- //-------PERMS---------
private static final int PERMISSIONS_REQUEST_CODE = 69; private static final int DOWNLOAD_REQUEST_CODE = 69; //Arbitrary, application specific
//True if permissions are OK //True if permissions are OK
private boolean checkPerms() { protected boolean checkPerms() {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) { if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
String[] PERMISSIONS_STORAGE = { String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE,
@ -683,13 +719,13 @@ public abstract class BaseActivity extends AppCompatActivity {
} }
//Display popup for user to grant permission //Display popup for user to grant permission
private void requestPerms() { //Runtime permissions request for devices with API >= 23 protected void requestPerms(int code) { //Runtime permissions request for devices with API >= 23
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) { if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
String[] PERMISSIONS_STORAGE = { String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE}; Manifest.permission.WRITE_EXTERNAL_STORAGE};
requestPermissions(PERMISSIONS_STORAGE, PERMISSIONS_REQUEST_CODE); requestPermissions(PERMISSIONS_STORAGE, code);
} }
} }
@ -698,8 +734,9 @@ public abstract class BaseActivity extends AppCompatActivity {
public void onRequestPermissionsResult(int permsRequestCode, @NonNull String[] permissions public void onRequestPermissionsResult(int permsRequestCode, @NonNull String[] permissions
, @NonNull int[] grantResults) { , @NonNull int[] grantResults) {
switch (permsRequestCode) { switch (permsRequestCode) {
case PERMISSIONS_REQUEST_CODE: case DOWNLOAD_REQUEST_CODE:
downloadFile(); if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
prepareDownload(tempThmmyFile);
break; break;
} }
} }
@ -713,7 +750,7 @@ public abstract class BaseActivity extends AppCompatActivity {
prepareDownload(thmmyFile); prepareDownload(thmmyFile);
else { else {
tempThmmyFile = thmmyFile; tempThmmyFile = thmmyFile;
requestPerms(); requestPerms(DOWNLOAD_REQUEST_CODE);
} }
} }
@ -835,6 +872,93 @@ public abstract class BaseActivity extends AppCompatActivity {
editor.putBoolean(getString(R.string.pref_privacy_analytics_enable_key), enabled).apply(); editor.putBoolean(getString(R.string.pref_privacy_analytics_enable_key), enabled).apply();
} }
//------------------------------------------ UPLOADS -------------------------------------------
private class UploadsShowDialogReceiver extends BroadcastReceiver {
private final Context activityContext;
UploadsShowDialogReceiver(Context activityContext) {
this.activityContext = activityContext;
}
@Override
public void onReceive(Context context, Intent intent) {
Bundle intentBundle = intent.getExtras();
if (intentBundle == null) {
return;
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
String dialogUploadID = intentBundle.getString(UPLOAD_ID_KEY);
/*String retryFilename = intentBundle.getString(UPLOAD_RETRY_FILENAME);
String retryCategory = intentBundle.getString(UPLOAD_RETRY_CATEGORY);
String retryTitleText = intentBundle.getString(UPLOAD_RETRY_TITLE);
String retryDescription = intentBundle.getString(UPLOAD_RETRY_DESCRIPTION);
String retryIcon = intentBundle.getString(UPLOAD_RETRY_ICON);
String retryUploaderProfile = intentBundle.getString(UPLOAD_RETRY_UPLOADER);
Uri retryFileUri = (Uri) intentBundle.get(UPLOAD_RETRY_FILE_URI);
Intent retryIntent = new Intent(context, UploadsReceiver.class);
retryIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
retryIntent.setAction(UploadsReceiver.ACTION_RETRY_UPLOAD);
retryIntent.putExtra(UPLOAD_RETRY_FILENAME, retryFilename);
retryIntent.putExtra(UPLOAD_RETRY_CATEGORY, retryCategory);
retryIntent.putExtra(UPLOAD_RETRY_TITLE, retryTitleText);
retryIntent.putExtra(UPLOAD_RETRY_DESCRIPTION, retryDescription);
retryIntent.putExtra(UPLOAD_RETRY_ICON, retryIcon);
retryIntent.putExtra(UPLOAD_RETRY_UPLOADER, retryUploaderProfile);
retryIntent.putExtra(UPLOAD_RETRY_FILE_URI, retryFileUri);*/
if (uploadsProgressDialog == null) {
AlertDialog.Builder progressDialogBuilder = new AlertDialog.Builder(activityContext);
LayoutInflater inflater = LayoutInflater.from(activityContext);
LinearLayout progressDialogLayout = (LinearLayout) inflater.inflate(R.layout.dialog_upload_progress, null);
MaterialProgressBar dialogProgressBar = progressDialogLayout.findViewById(R.id.dialogProgressBar);
dialogProgressBar.setMax(100);
progressDialogBuilder.setView(progressDialogLayout);
uploadsProgressDialog = progressDialogBuilder.create();
if (!UploadService.getTaskList().contains("" + dialogUploadID)) {
//Upload probably failed at this point
uploadsProgressDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "Retry", (progressDialog, progressWhich) -> {
/*LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context.getApplicationContext());
localBroadcastManager.sendBroadcast(multipartUploadRetryIntent);*/
uploadsProgressDialog.dismiss();
//context.sendBroadcast(retryIntent);
});
uploadsProgressDialog.setButton(AlertDialog.BUTTON_NEGATIVE, "Cancel", (progressDialog, progressWhich) -> {
uploadsProgressDialog.dismiss();
});
TextView dialogProgressText = progressDialogLayout.findViewById(R.id.dialog_upload_progress_text);
dialogProgressBar.setVisibility(View.GONE);
dialogProgressText.setText("Upload failed.");
uploadsProgressDialog.show();
} else {
//Empty buttons are needed, they are updated with correct values in the receiver
uploadsProgressDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "placeholder", (progressDialog, progressWhich) -> {
});
uploadsProgressDialog.setButton(AlertDialog.BUTTON_NEGATIVE, "placeholder", (progressDialog, progressWhich) -> {
});
UploadsReceiver.setDialogDisplay(uploadsProgressDialog, dialogUploadID, null);
//UploadsReceiver.setDialogDisplay(uploadsProgressDialog, dialogUploadID, retryIntent);
uploadsProgressDialog.show();
}
} else {
UploadsReceiver.setDialogDisplay(uploadsProgressDialog, dialogUploadID, null);
//UploadsReceiver.setDialogDisplay(uploadsProgressDialog, dialogUploadID, retryIntent);
uploadsProgressDialog.show();
}
}
}
}
//----------------------------------MISC---------------------- //----------------------------------MISC----------------------
protected void setMainActivity(MainActivity mainActivity) { protected void setMainActivity(MainActivity mainActivity) {
this.mainActivity = mainActivity; this.mainActivity = mainActivity;

44
app/src/main/java/gr/thmmy/mthmmy/model/UploadFile.java

@ -0,0 +1,44 @@
package gr.thmmy.mthmmy.model;
import android.net.Uri;
import androidx.annotation.Nullable;
import java.io.File;
public class UploadFile {
private final boolean isCameraPhoto;
private Uri fileUri;
private File photoFile;
private UploadFile() {
isCameraPhoto = false;
fileUri = null;
photoFile = null;
}
public UploadFile(boolean isCameraPhoto, Uri fileUri, @Nullable File photoFile) {
this.isCameraPhoto = isCameraPhoto;
this.fileUri = fileUri;
this.photoFile = photoFile;
}
public boolean isCameraPhoto() {
return isCameraPhoto;
}
public Uri getFileUri() {
return fileUri;
}
public File getPhotoFile() {
return photoFile;
}
public void setFileUri(Uri fileUri) {
this.fileUri = fileUri;
}
public void setPhotoFile(File photoFile) {
this.photoFile = photoFile;
}
}

223
app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java

@ -0,0 +1,223 @@
package gr.thmmy.mthmmy.services;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import androidx.appcompat.app.AlertDialog;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.snatik.storage.Storage;
import net.gotev.uploadservice.ServerResponse;
import net.gotev.uploadservice.UploadInfo;
import net.gotev.uploadservice.UploadService;
import net.gotev.uploadservice.UploadServiceBroadcastReceiver;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.upload.UploadsHelper;
import gr.thmmy.mthmmy.base.BaseApplication;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
public class UploadsReceiver extends UploadServiceBroadcastReceiver {
public static final String UPLOAD_ID_KEY = "UPLOAD_ID_KEY";
public static final String ACTION_COMBINED_UPLOAD = "ACTION_COMBINED_UPLOAD";
public static final String ACTION_CANCEL_UPLOAD = "ACTION_CANCEL_UPLOAD";
public static final String ACTION_RETRY_UPLOAD = "ACTION_RETRY_UPLOAD";
/*public static final String UPLOAD_RETRY_FILENAME = "UPLOAD_RETRY_FILENAME";
public static final String UPLOAD_RETRY_CATEGORY = "UPLOAD_RETRY_CATEGORY";
public static final String UPLOAD_RETRY_TITLE = "UPLOAD_RETRY_TITLE";
public static final String UPLOAD_RETRY_DESCRIPTION = "UPLOAD_RETRY_DESCRIPTION";
public static final String UPLOAD_RETRY_ICON = "UPLOAD_RETRY_ICON";
public static final String UPLOAD_RETRY_UPLOADER = "UPLOAD_RETRY_UPLOADER";
public static final String UPLOAD_RETRY_FILE_URI = "UPLOAD_RETRY_FILE_URI";*/
private Storage storage;
private static AlertDialog uploadProgressDialog;
private static String dialogUploadID;
//private static Intent multipartUploadRetryIntent;
@Override
public void onReceive(Context context, Intent intent) {
String intentAction = intent.getAction();
Bundle intentBundle = intent.getExtras();
if (intentAction == null || intentBundle == null) {
super.onReceive(context, intent);
return;
}
switch (intentAction) {
case ACTION_CANCEL_UPLOAD:
String uploadID = intentBundle.getString(UPLOAD_ID_KEY);
UploadService.stopUpload(uploadID);
break;
/*case ACTION_RETRY_UPLOAD:
String retryFilename = intentBundle.getString(UPLOAD_RETRY_FILENAME);
String retryCategory = intentBundle.getString(UPLOAD_RETRY_CATEGORY);
String retryTitleText = intentBundle.getString(UPLOAD_RETRY_TITLE);
String retryDescription = intentBundle.getString(UPLOAD_RETRY_DESCRIPTION);
String retryIcon = intentBundle.getString(UPLOAD_RETRY_ICON);
String retryUploaderProfile = intentBundle.getString(UPLOAD_RETRY_UPLOADER);
Uri retryFileUri = (Uri) intentBundle.get(UPLOAD_RETRY_FILE_URI);
String retryUploadID = UUID.randomUUID().toString();
UploadActivity.uploadFile(context, retryUploadID,
UploadActivity.getConfigForUpload(context, retryUploadID, retryFilename, retryCategory,
retryTitleText, retryDescription, retryIcon, retryUploaderProfile, retryFileUri),
retryCategory, retryTitleText, retryDescription, retryIcon,
retryUploaderProfile, retryFileUri);
break;*/
default:
super.onReceive(context, intent);
break;
}
}
@Override
public void onProgress(Context context, UploadInfo uploadInfo) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP &&
uploadInfo.getUploadId().equals(dialogUploadID) &&
uploadProgressDialog != null) {
Button alertDialogNeutral = uploadProgressDialog.getButton(AlertDialog.BUTTON_NEUTRAL);
alertDialogNeutral.setText("Resume on background");
alertDialogNeutral.setOnClickListener(v -> uploadProgressDialog.dismiss());
Button alertDialogNegative = uploadProgressDialog.getButton(AlertDialog.BUTTON_NEGATIVE);
alertDialogNegative.setText("Cancel");
alertDialogNegative.setOnClickListener(v -> {
UploadService.stopUpload(dialogUploadID);
uploadProgressDialog.dismiss();
});
if (uploadProgressDialog.isShowing()) {
Window progressWindow = uploadProgressDialog.getWindow();
if (progressWindow != null) {
MaterialProgressBar dialogProgressBar = progressWindow.findViewById(R.id.dialogProgressBar);
TextView dialogProgressText = progressWindow.findViewById(R.id.dialog_upload_progress_text);
dialogProgressBar.setProgress(uploadInfo.getProgressPercent());
dialogProgressText.setText(context.getResources().getString(
R.string.upload_progress_dialog_bytes_uploaded,
(float) uploadInfo.getUploadRate(),
(int) uploadInfo.getUploadedBytes() / 1000,
(int) uploadInfo.getTotalBytes() / 1000));
}
if (uploadInfo.getUploadedBytes() == uploadInfo.getTotalBytes()) {
uploadProgressDialog.dismiss();
}
}
}
}
@Override
public void onError(Context context, UploadInfo uploadInfo, ServerResponse serverResponse,
Exception exception) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP &&
uploadInfo.getUploadId().equals(dialogUploadID) &&
uploadProgressDialog != null) {
/*Button alertDialogNeutral = uploadProgressDialog.getButton(AlertDialog.BUTTON_NEUTRAL);
alertDialogNeutral.setText("Retry");
alertDialogNeutral.setOnClickListener(v -> {
if (multipartUploadRetryIntent != null) {
context.sendBroadcast(multipartUploadRetryIntent);
}
uploadProgressDialog.dismiss();
});*/
Button alertDialogNegative = uploadProgressDialog.getButton(AlertDialog.BUTTON_NEGATIVE);
alertDialogNegative.setText("Cancel");
alertDialogNegative.setOnClickListener(v -> {
NotificationManager notificationManager = (NotificationManager) context.getApplicationContext().
getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager != null) {
notificationManager.cancel(uploadInfo.getNotificationID());
}
UploadsHelper.deleteTempFiles(storage);
uploadProgressDialog.dismiss();
});
if (uploadProgressDialog.isShowing()) {
Window progressWindow = uploadProgressDialog.getWindow();
if (progressWindow != null) {
MaterialProgressBar dialogProgressBar = progressWindow.findViewById(R.id.dialogProgressBar);
TextView dialogProgressText = progressWindow.findViewById(R.id.dialog_upload_progress_text);
dialogProgressBar.setVisibility(View.GONE);
dialogProgressText.setText("Upload failed.");
}
if (uploadInfo.getUploadedBytes() == uploadInfo.getTotalBytes()) {
uploadProgressDialog.dismiss();
}
}
} else {
NotificationManager notificationManager = (NotificationManager) context.getApplicationContext().
getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager != null) {
notificationManager.cancel(uploadInfo.getNotificationID());
}
Intent combinedActionsIntent = new Intent(UploadsReceiver.ACTION_COMBINED_UPLOAD);
combinedActionsIntent.putExtra(UploadsReceiver.UPLOAD_ID_KEY, uploadInfo.getUploadId());
context.sendBroadcast(combinedActionsIntent);
}
Toast.makeText(context.getApplicationContext(), "Upload failed", Toast.LENGTH_SHORT).show();
if (storage == null) {
storage = new Storage(context.getApplicationContext());
}
}
@Override
public void onCompleted(Context context, UploadInfo uploadInfo, ServerResponse serverResponse) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
uploadProgressDialog = null;
dialogUploadID = null;
}
Toast.makeText(context.getApplicationContext(), "Upload completed successfully", Toast.LENGTH_SHORT).show();
if (storage == null) {
storage = new Storage(context.getApplicationContext());
}
BaseApplication.getInstance().logFirebaseAnalyticsEvent("file_upload", null);
UploadsHelper.deleteTempFiles(storage);
}
@Override
public void onCancelled(Context context, UploadInfo uploadInfo) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
uploadProgressDialog = null;
dialogUploadID = null;
}
Toast.makeText(context.getApplicationContext(), "Upload canceled", Toast.LENGTH_SHORT).show();
if (storage == null) {
storage = new Storage(context.getApplicationContext());
}
/*NotificationManager notificationManager = (NotificationManager) context.getApplicationContext().
getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager != null) {
notificationManager.cancel(uploadInfo.getNotificationID());
}*/
UploadsHelper.deleteTempFiles(storage);
}
public static void setDialogDisplay(AlertDialog uploadProgressDialog, String dialogUploadID,
Intent multipartUploadRetryIntent) {
UploadsReceiver.uploadProgressDialog = uploadProgressDialog;
UploadsReceiver.dialogUploadID = dialogUploadID;
//UploadsReceiver.multipartUploadRetryIntent = multipartUploadRetryIntent;
}
}

95
app/src/main/java/gr/thmmy/mthmmy/utils/FileUtils.java

@ -1,10 +1,16 @@
package gr.thmmy.mthmmy.utils; package gr.thmmy.mthmmy.utils;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.OpenableColumns;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.webkit.MimeTypeMap; import android.webkit.MimeTypeMap;
import java.io.File; import java.io.File;
import androidx.annotation.NonNull; import gr.thmmy.mthmmy.R;
import static gr.thmmy.mthmmy.services.DownloadHelper.SAVE_DIR; import static gr.thmmy.mthmmy.services.DownloadHelper.SAVE_DIR;
@ -24,4 +30,91 @@ public class FileUtils {
public static boolean fileNameExists(String fileName) { public static boolean fileNameExists(String fileName) {
return fileName != null && (new File(SAVE_DIR.getAbsolutePath(), fileName)).isFile(); return fileName != null && (new File(SAVE_DIR.getAbsolutePath(), fileName)).isFile();
} }
@Nullable
public static String getFileExtension(@NonNull String filename) {
String fileExtension;
if (!filename.contains(".")) {
return null;
}
if (filename.toLowerCase().endsWith(".tar.gz")) {
fileExtension = filename.substring(filename.length() - 7);
} else {
fileExtension = filename.substring(filename.lastIndexOf("."));
}
return fileExtension;
}
public static String getFilenameWithoutExtension(String filename) {
String fileExtension = getFileExtension(filename);
return fileExtension == null
? filename
: filename.substring(0, filename.indexOf(fileExtension));
}
@NonNull
public static String filenameFromUri(Context context, Uri uri) {
String filename = null;
if (uri.getScheme().equals("content")) {
try (Cursor cursor = context.getContentResolver().query(uri, null, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
}
}
}
if (filename == null) {
filename = uri.getPath();
int cut = filename.lastIndexOf('/');
if (cut != -1) {
filename = filename.substring(cut + 1);
}
}
return filename;
}
public static long sizeFromUri(Context context, @NonNull Uri uri) {
try (Cursor cursor = context.getContentResolver().query(uri, null, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
return cursor.getLong(cursor.getColumnIndex(OpenableColumns.SIZE));
}
}
return -1;
}
/**
* Returns a String with a single FontAwesome typeface character corresponding to this file's
* extension.
*
* @param filename String with filename <b>containing file's extension</b>
* @return FontAwesome character according to file's type
* @see <a href="http://fontawesome.io/">FontAwesome</a>
*/
@NonNull
public static String faIconFromFilename(Context context, String filename) {
filename = filename.toLowerCase();
if (filename.contains("jpg") || filename.contains("gif") || filename.contains("jpeg")
|| filename.contains("png"))
return context.getResources().getString(R.string.fa_file_image_o);
else if (filename.contains("pdf"))
return context.getResources().getString(R.string.fa_file_pdf_o);
else if (filename.contains("zip") || filename.contains("rar") || filename.contains("tar.gz"))
return context.getResources().getString(R.string.fa_file_zip_o);
else if (filename.contains("txt"))
return context.getResources().getString(R.string.fa_file_text_o);
else if (filename.contains("doc") || filename.contains("docx"))
return context.getResources().getString(R.string.fa_file_word_o);
else if (filename.contains("xls") || filename.contains("xlsx"))
return context.getResources().getString(R.string.fa_file_excel_o);
else if (filename.contains("pps"))
return context.getResources().getString(R.string.fa_file_powerpoint_o);
else if (filename.contains("mpg"))
return context.getResources().getString(R.string.fa_file_video_o);
return context.getResources().getString(R.string.fa_file);
}
} }

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

@ -2,15 +2,16 @@ package gr.thmmy.mthmmy.utils;
import android.content.Context; import android.content.Context;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log;
import android.view.View; import android.view.View;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.view.ViewCompat; import androidx.core.view.ViewCompat;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
/** /**
* Extends FloatingActionButton's behavior so the button will hide when scrolling down and show * Extends FloatingActionButton's behavior so the button will hide when scrolling down and show
* otherwise. It also lifts the {@link FloatingActionButton} when a {@link Snackbar} is shown. * otherwise. It also lifts the {@link FloatingActionButton} when a {@link Snackbar} is shown.
@ -37,7 +38,9 @@ public class ScrollAwareFABBehavior extends CoordinatorLayout.Behavior<FloatingA
final int dxUnconsumed, final int dyUnconsumed, int type) { final int dxUnconsumed, final int dyUnconsumed, int type) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed,
dyUnconsumed, type); dyUnconsumed, type);
Log.d("THISSSS", "GOT");
if (dyConsumed > 0 || (!target.canScrollVertically(-1) && dyConsumed == 0 && dyUnconsumed > 50)) { if (dyConsumed > 0 || (!target.canScrollVertically(-1) && dyConsumed == 0 && dyUnconsumed > 50)) {
Log.d("THISSSS", "GOT_HIDE");
child.hide(new FloatingActionButton.OnVisibilityChangedListener() { child.hide(new FloatingActionButton.OnVisibilityChangedListener() {
@Override @Override
public void onHidden(FloatingActionButton fab) { public void onHidden(FloatingActionButton fab) {
@ -47,7 +50,14 @@ public class ScrollAwareFABBehavior extends CoordinatorLayout.Behavior<FloatingA
}); });
} else if (child.getTag() != null && (boolean) child.getTag() && (dyConsumed < 0 || } else if (child.getTag() != null && (boolean) child.getTag() && (dyConsumed < 0 ||
!target.canScrollVertically(-1) && dyUnconsumed < -50)) { !target.canScrollVertically(-1) && dyUnconsumed < -50)) {
child.show(); Log.d("THISSSS", "GOT_SHOW");
child.show(new FloatingActionButton.OnVisibilityChangedListener() {
@Override
public void onShown(FloatingActionButton fab) {
super.onShown(fab);
fab.setVisibility(View.VISIBLE);
}
});
} }
} }

174
app/src/main/java/gr/thmmy/mthmmy/utils/TakePhoto.java

@ -0,0 +1,174 @@
package gr.thmmy.mthmmy.utils;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.AssetFileDescriptor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.FileProvider;
import android.widget.Toast;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import timber.log.Timber;
public class TakePhoto {
private static final int DEFAULT_MIN_WIDTH_QUALITY = 400;
private static final String IMAGE_CONTENT_DESCRIPTION = "mThmmy uploads image";
@Nullable
public static Intent getIntent(Context context, @NonNull File photoFile) {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//Ensures that there's a camera activity to handle the intent
if (takePictureIntent.resolveActivity(context.getPackageManager()) != null) {
Uri photoURI = FileProvider.getUriForFile(context, context.getPackageName() +
".provider", photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
//Grants necessary permissions for Gallery to use the Uri
List<ResolveInfo> resInfoList = context.getPackageManager().
queryIntentActivities(takePictureIntent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
String packageName = resolveInfo.activityInfo.packageName;
context.grantUriPermission(packageName, photoURI,
Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
return takePictureIntent;
}
return null;
}
public static Uri processResult(Context context, File photoFile) {
Bitmap bitmap;
Uri fileUri = FileProvider.getUriForFile(context, context.getPackageName() + ".provider", photoFile);
bitmap = getImageResized(context, fileUri);
int rotation = getRotation(context, fileUri);
bitmap = rotate(bitmap, rotation);
try {
FileOutputStream out = new FileOutputStream(photoFile);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
return fileUri;
}
@Nullable
public static File createImageFile(Context context) {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.FRANCE).format(new Date());
String imageFileName = "mThmmy_" + timeStamp + ".jpg";
File imageFolder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) +
File.separator + "mThmmy");
if (!imageFolder.exists()) {
if (!imageFolder.mkdirs()) {
Timber.w("Photos folder build returned false in %s", TakePhoto.class.getSimpleName());
Toast.makeText(context, "Couldn't create photos directory", Toast.LENGTH_SHORT).show();
return null;
}
}
return new File(imageFolder, imageFileName);
}
public static void galleryAddPic(Context context, File photoFile) {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.TITLE, photoFile.getName());
values.put(MediaStore.Images.Media.DESCRIPTION, IMAGE_CONTENT_DESCRIPTION);
values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis());
values.put(MediaStore.Images.ImageColumns.BUCKET_ID, photoFile.toString().toLowerCase(Locale.US).hashCode());
values.put(MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME, photoFile.getName().toLowerCase(Locale.US));
values.put("_data", photoFile.getAbsolutePath());
ContentResolver cr = context.getContentResolver();
cr.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
}
private static Bitmap getImageResized(Context context, Uri selectedImage) {
Bitmap bm;
int[] sampleSizes = new int[]{5, 3, 2, 1};
int i = 0;
do {
bm = decodeBitmap(context, selectedImage, sampleSizes[i]);
i++;
} while (bm.getWidth() < DEFAULT_MIN_WIDTH_QUALITY && i < sampleSizes.length);
return bm;
}
private static Bitmap decodeBitmap(Context context, Uri theUri, int sampleSize) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = sampleSize;
AssetFileDescriptor fileDescriptor = null;
try {
fileDescriptor = context.getContentResolver().openAssetFileDescriptor(theUri, "r");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
assert fileDescriptor != null;
return BitmapFactory.decodeFileDescriptor(
fileDescriptor.getFileDescriptor(), null, options);
}
private static int getRotation(Context context, Uri imageUri) {
int rotation = 0;
try {
context.getContentResolver().notifyChange(imageUri, null);
ExifInterface exif = new ExifInterface(imageUri.getPath());
int orientation = exif.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_270:
rotation = 270;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
rotation = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_90:
rotation = 90;
break;
}
} catch (Exception e) {
e.printStackTrace();
}
return rotation;
}
private static Bitmap rotate(Bitmap bm, int rotation) {
if (rotation != 0) {
Matrix matrix = new Matrix();
matrix.postRotate(rotation);
return Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true);
}
return bm;
}
}

4
app/src/main/res/drawable/ic_attach_file_white_24dp.xml

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="24.0" android:viewportWidth="24.0" android:width="24dp">
<path android:fillColor="#FFFFFF" android:pathData="M16.5,6v11.5c0,2.21 -1.79,4 -4,4s-4,-1.79 -4,-4V5c0,-1.38 1.12,-2.5 2.5,-2.5s2.5,1.12 2.5,2.5v10.5c0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1V6H10v9.5c0,1.38 1.12,2.5 2.5,2.5s2.5,-1.12 2.5,-2.5V5c0,-2.21 -1.79,-4 -4,-4S7,2.79 7,5v12.5c0,3.04 2.46,5.5 5.5,5.5s5.5,-2.46 5.5,-5.5V6h-1.5z"/>
</vector>

4
app/src/main/res/drawable/ic_cached_accent_24dp.xml

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="24.0" android:viewportWidth="24.0" android:width="24dp">
<path android:fillColor="#26A69A" android:pathData="M19,8l-4,4h3c0,3.31 -2.69,6 -6,6 -1.01,0 -1.97,-0.25 -2.8,-0.7l-1.46,1.46C8.97,19.54 10.43,20 12,20c4.42,0 8,-3.58 8,-8h3l-4,-4zM6,12c0,-3.31 2.69,-6 6,-6 1.01,0 1.97,0.25 2.8,0.7l1.46,-1.46C15.03,4.46 13.57,4 12,4c-4.42,0 -8,3.58 -8,8H1l4,4 4,-4H6z"/>
</vector>

4
app/src/main/res/drawable/ic_cancel_accent_24dp.xml

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="24.0" android:viewportWidth="24.0" android:width="24dp">
<path android:fillColor="#26A69A" android:pathData="M12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zM17,15.59L15.59,17 12,13.41 8.41,17 7,15.59 10.59,12 7,8.41 8.41,7 12,10.59 15.59,7 17,8.41 13.41,12 17,15.59z"/>
</vector>

4
app/src/main/res/drawable/ic_info_outline_warning_24dp.xml

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="24.0" android:viewportWidth="24.0" android:width="24dp">
<path android:fillColor="#FF9800" android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z"/>
</vector>

9
app/src/main/res/layout/activity_board.xml

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_content" android:id="@+id/main_content"
@ -43,8 +42,8 @@
android:layout_marginTop="64dp" android:layout_marginTop="64dp"
android:background="@color/background" android:background="@color/background"
android:scrollbars="none" android:scrollbars="none"
tools:context="gr.thmmy.mthmmy.activities.topic.TopicActivity"> app:layout_behavior="@string/appbar_scrolling_view_behavior"
</androidx.recyclerview.widget.RecyclerView> tools:context="gr.thmmy.mthmmy.activities.topic.TopicActivity" />
<me.zhanghai.android.materialprogressbar.MaterialProgressBar <me.zhanghai.android.materialprogressbar.MaterialProgressBar
android:id="@+id/progressBar" android:id="@+id/progressBar"
@ -67,5 +66,3 @@
app:layout_behavior="gr.thmmy.mthmmy.utils.ScrollAwareFABBehavior" app:layout_behavior="gr.thmmy.mthmmy.utils.ScrollAwareFABBehavior"
app:srcCompat="@drawable/ic_add_fab" /> app:srcCompat="@drawable/ic_add_fab" />
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

4
app/src/main/res/layout/activity_downloads.xml

@ -48,7 +48,7 @@
app:mpb_indeterminateTint="@color/accent" app:mpb_indeterminateTint="@color/accent"
app:mpb_progressStyle="horizontal"/> app:mpb_progressStyle="horizontal"/>
<!--<com.google.android.material.floatingactionbutton.FloatingActionButton <com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/upload_fab" android:id="@+id/upload_fab"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -56,5 +56,5 @@
android:layout_marginBottom="@dimen/fab_margins" android:layout_marginBottom="@dimen/fab_margins"
android:layout_marginEnd="@dimen/fab_margins" android:layout_marginEnd="@dimen/fab_margins"
app:layout_behavior="gr.thmmy.mthmmy.utils.ScrollAwareFABBehavior" app:layout_behavior="gr.thmmy.mthmmy.utils.ScrollAwareFABBehavior"
app:srcCompat="@drawable/ic_file_upload_white_24dp"/>--> app:srcCompat="@drawable/ic_file_upload_white_24dp"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

4
app/src/main/res/layout/activity_topic.xml

@ -45,8 +45,8 @@
android:layout_below="@id/appbar" android:layout_below="@id/appbar"
android:layout_gravity="top|start" android:layout_gravity="top|start"
android:clipToPadding="false" android:clipToPadding="false"
android:paddingBottom="4dp"
android:paddingTop="4dp" android:paddingTop="4dp"
android:paddingBottom="4dp"
android:scrollbars="none" android:scrollbars="none"
app:layout_behavior="@string/appbar_scrolling_view_behavior" app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="gr.thmmy.mthmmy.activities.topic.TopicActivity" /> tools:context="gr.thmmy.mthmmy.activities.topic.TopicActivity" />
@ -146,8 +146,8 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom|end" android:layout_gravity="bottom|end"
android:layout_marginBottom="50dp"
android:layout_marginEnd="@dimen/fab_margins" android:layout_marginEnd="@dimen/fab_margins"
android:layout_marginBottom="50dp"
app:layout_behavior="gr.thmmy.mthmmy.utils.ScrollAwareFABBehavior" app:layout_behavior="gr.thmmy.mthmmy.utils.ScrollAwareFABBehavior"
app:srcCompat="@drawable/ic_reply" /> app:srcCompat="@drawable/ic_reply" />
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

72
app/src/main/res/layout/activity_upload.xml

@ -28,16 +28,16 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_gravity="top|start" android:layout_gravity="top|start"
android:background="@color/background" android:background="@color/primary_light"
android:paddingEnd="@dimen/activity_horizontal_margin"
android:paddingStart="@dimen/activity_horizontal_margin" android:paddingStart="@dimen/activity_horizontal_margin"
android:paddingEnd="@dimen/activity_horizontal_margin"
app:layout_behavior="@string/appbar_scrolling_view_behavior" app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="gr.thmmy.mthmmy.activities.upload.UploadActivity"> tools:context="gr.thmmy.mthmmy.activities.upload.UploadActivity">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@color/background" android:background="@color/primary_light"
android:focusableInTouchMode="true" android:focusableInTouchMode="true"
android:orientation="vertical"> android:orientation="vertical">
@ -45,15 +45,16 @@
android:id="@+id/upload_spinners" android:id="@+id/upload_spinners"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@color/background" android:layout_marginTop="8dp"
android:background="@color/primary_light"
android:orientation="vertical"> android:orientation="vertical">
<gr.thmmy.mthmmy.utils.AppCompatSpinnerWithoutDefault <gr.thmmy.mthmmy.utils.AppCompatSpinnerWithoutDefault
android:id="@+id/upload_spinner_category_root" android:id="@+id/upload_spinner_category_root"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="6dp"
android:layout_marginTop="6dp" android:layout_marginTop="6dp"
android:layout_marginBottom="6dp"
android:popupBackground="@color/primary" android:popupBackground="@color/primary"
android:prompt="@string/upload_spinners_hint" /> android:prompt="@string/upload_spinners_hint" />
</LinearLayout> </LinearLayout>
@ -61,8 +62,8 @@
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="6dp" android:layout_marginTop="6dp"
android:layout_marginTop="6dp"> android:layout_marginBottom="6dp">
<EditText <EditText
android:id="@+id/upload_title" android:id="@+id/upload_title"
@ -73,11 +74,41 @@
android:maxLength="500" /> android:maxLength="500" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal">
<com.google.android.material.textfield.TextInputLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:layout_marginBottom="6dp" android:layout_marginBottom="6dp"
android:layout_marginTop="6dp"> android:layout_weight="1">
<EditText
android:id="@+id/upload_filename"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/upload_filename"
android:inputType="textNoSuggestions"
android:maxLength="500" />
</com.google.android.material.textfield.TextInputLayout>
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/upload_filename_info"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginTop="12dp"
android:theme="@style/LightBackgroundColoredButton"
app:srcCompat="@drawable/ic_info_outline_white_24dp" />
</LinearLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:layout_marginBottom="6dp">
<EditText <EditText
android:id="@+id/upload_description" android:id="@+id/upload_description"
@ -97,16 +128,13 @@
android:textAlignment="center" android:textAlignment="center"
android:textColor="@color/accent" /> android:textColor="@color/accent" />
<androidx.appcompat.widget.AppCompatTextView <LinearLayout
android:id="@+id/upload_filename" android:id="@+id/upload_files_list"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="6dp"
android:layout_marginTop="6dp" android:layout_marginTop="6dp"
android:drawablePadding="5dp" android:layout_marginBottom="6dp"
android:ellipsize="marquee" android:orientation="vertical"
android:singleLine="true"
android:textColor="@color/primary_text"
android:visibility="gone" /> android:visibility="gone" />
<LinearLayout <LinearLayout
@ -129,11 +157,6 @@
android:text="@string/upload_select_file" android:text="@string/upload_select_file"
android:textColor="@color/primary_text" /> android:textColor="@color/primary_text" />
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
<androidx.appcompat.widget.AppCompatButton <androidx.appcompat.widget.AppCompatButton
android:id="@+id/upload_take_photo_button" android:id="@+id/upload_take_photo_button"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -144,6 +167,11 @@
android:gravity="center_vertical" android:gravity="center_vertical"
android:text="@string/upload_take_photo" android:text="@string/upload_take_photo"
android:textColor="@color/primary_text" /> android:textColor="@color/primary_text" />
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>
@ -165,8 +193,8 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom|end" android:layout_gravity="bottom|end"
android:layout_marginBottom="@dimen/fab_margins"
android:layout_marginEnd="@dimen/fab_margins" android:layout_marginEnd="@dimen/fab_margins"
android:layout_marginBottom="@dimen/fab_margins"
app:layout_behavior="gr.thmmy.mthmmy.utils.ScrollAwareFABBehavior" app:layout_behavior="gr.thmmy.mthmmy.utils.ScrollAwareFABBehavior"
app:srcCompat="@drawable/ic_file_upload_white_24dp" /> app:srcCompat="@drawable/ic_file_upload_white_24dp" />

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

@ -27,7 +27,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_gravity="top|start" android:layout_gravity="top|start"
android:background="@color/background" android:background="@color/primary_light"
android:paddingEnd="@dimen/activity_horizontal_margin" android:paddingEnd="@dimen/activity_horizontal_margin"
android:paddingStart="@dimen/activity_horizontal_margin" android:paddingStart="@dimen/activity_horizontal_margin"
app:layout_behavior="@string/appbar_scrolling_view_behavior" app:layout_behavior="@string/appbar_scrolling_view_behavior"

24
app/src/main/res/layout/activity_upload_file_list_row.xml

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/upload_file_item_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="1"
android:ellipsize="marquee"
android:singleLine="true"
android:textColor="@color/primary_text" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/upload_file_item_remove"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:theme="@style/LightBackgroundColoredButton"
app:srcCompat="@drawable/ic_delete_accent_24dp" />
</LinearLayout>

10
app/src/main/res/layout/activity_upload_filename_info_popup.xml

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/upload_filename_info_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/primary_light"
android:linksClickable="true"
android:padding="8dp"
android:text="@string/upload_filename_info"
android:textColor="@color/primary_text" />

43
app/src/main/res/layout/dialog_upload_progress.xml

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingEnd="24dp"
android:paddingStart="24dp">
<TextView
android:id="@+id/dialog_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginBottom="5dp"
android:text="@string/upload_progress_dialog_title"
android:textColor="@color/accent"
android:textSize="20sp" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginBottom="15dp"
android:background="@color/secondary_text" />
<me.zhanghai.android.materialprogressbar.MaterialProgressBar
android:id="@+id/dialogProgressBar"
style="@style/Widget.MaterialProgressBar.ProgressBar.Horizontal.NoPadding"
android:layout_width="match_parent"
android:layout_height="@dimen/progress_bar_height"
android:indeterminate="false"
app:mpb_progressStyle="horizontal"
app:mpb_progressTint="@color/accent" />
<TextView
android:id="@+id/dialog_upload_progress_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:layout_marginTop="12dp"
android:minEms="64"
android:textColor="@color/white" />
</LinearLayout>

9
app/src/main/res/layout/editor_view_color_picker.xml

@ -1,11 +1,10 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<ScrollView <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
android:background="@color/card_background">
<LinearLayout <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">

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

@ -18,6 +18,7 @@
<color name="divider">#8B8B8B</color> <color name="divider">#8B8B8B</color>
<color name="link_color">#FF9800</color> <color name="link_color">#FF9800</color>
<color name="mention_color">#FAA61A</color> <color name="mention_color">#FAA61A</color>
<color name="error_red">#890d0d</color>
<color name="white">#FFFFFF</color> <color name="white">#FFFFFF</color>
<color name="iron">#CCCCCC</color> <color name="iron">#CCCCCC</color>

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

@ -132,12 +132,20 @@
<string name="upload_button">Upload</string> <string name="upload_button">Upload</string>
<!--Upload Activity--> <!--Upload Activity-->
<string name="upload_title_description_builder">Generate title and description</string> <string name="upload_title_description_builder">Generate fields</string>
<string name="upload_title_hint">Title</string> <string name="upload_title_hint">Title</string>
<string name="upload_filename">Upload as (filename)</string>
<string name="upload_description_hint">Description</string> <string name="upload_description_hint">Description</string>
<string name="upload_select_file">Select file</string> <string name="upload_select_file">Add files</string>
<string name="upload_take_photo">Take photo</string> <string name="upload_take_photo">Take photo</string>
<string name="upload_spinners_hint">Select a category</string> <string name="upload_spinners_hint">Select a category</string>
<string name="upload_filename_info">Please follow the filename rules as\ndescribed
in&#160;<a href="https://www.thmmy.gr/smf/index.php?topic=34294.0">this topic</a>.\n
\nThis does not rename your local files.</string>
<string name="upload_progress_dialog_title">Uploading</string>
<string name="upload_progress_dialog_bytes_uploaded">Uploading: %1$.2f Kbit/s, %2$d/%3$d KBytes</string>
<string name="upload_notification_cancel">"Cancel"</string>
<string name="upload_notification_retry">"Retry"</string>
<!--Upload Fields Builder Activity--> <!--Upload Fields Builder Activity-->
<string name="upload_fields_builder_type_radio_buttons_title">Select type of upload</string> <string name="upload_fields_builder_type_radio_buttons_title">Select type of upload</string>

4
app/src/main/res/xml/app_preferences_user.xml

@ -49,7 +49,7 @@
app:iconSpaceReserved="false" /> app:iconSpaceReserved="false" />
</androidx.preference.PreferenceCategory> </androidx.preference.PreferenceCategory>
<!--<androidx.preference.PreferenceCategory <androidx.preference.PreferenceCategory
android:key="pref_category_uploading_key" android:key="pref_category_uploading_key"
android:title="@string/pref_category_uploading" android:title="@string/pref_category_uploading"
app:iconSpaceReserved="false"> app:iconSpaceReserved="false">
@ -59,7 +59,7 @@
android:title="@string/pref_title_uploading_app_signature_enable" android:title="@string/pref_title_uploading_app_signature_enable"
android:summary="@string/pref_summary_uploading_app_signature_enable" android:summary="@string/pref_summary_uploading_app_signature_enable"
app:iconSpaceReserved="false" /> app:iconSpaceReserved="false" />
</androidx.preference.PreferenceCategory>--> </androidx.preference.PreferenceCategory>
<androidx.preference.PreferenceCategory <androidx.preference.PreferenceCategory
android:key="pref_category_privacy_key" android:key="pref_category_privacy_key"

2
build.gradle

@ -8,7 +8,7 @@ buildscript {
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.2.1' classpath 'com.android.tools.build:gradle:3.4.1'
classpath 'com.google.gms:google-services:4.2.0' classpath 'com.google.gms:google-services:4.2.0'
classpath 'io.fabric.tools:gradle:1.26.1' classpath 'io.fabric.tools:gradle:1.26.1'
classpath 'org.ajoberstar.grgit:grgit-core:3.0.0' // Also change in app/gradle/grgit.gradle classpath 'org.ajoberstar.grgit:grgit-core:3.0.0' // Also change in app/gradle/grgit.gradle

4
gradle/wrapper/gradle-wrapper.properties

@ -1,6 +1,6 @@
#Fri Sep 28 13:21:54 EEST 2018 #Sat Jun 15 18:56:13 EEST 2019
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip

Loading…
Cancel
Save