diff --git a/app/build.gradle b/app/build.gradle index b47ae972..a4f3e951 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -6,15 +6,16 @@ apply plugin: 'com.android.application' apply plugin: 'io.fabric' android { - compileSdkVersion 28 + compileSdkVersion 29 + buildToolsVersion = '29.0.2' defaultConfig { vectorDrawables.useSupportLibrary = true applicationId "gr.thmmy.mthmmy" minSdkVersion 19 - targetSdkVersion 28 - versionCode 21 - versionName "1.7.3" + targetSdkVersion 29 + versionCode 22 + versionName "1.7.4" archivesBaseName = "mTHMMY-v$versionName" buildConfigField "String", "CURRENT_BRANCH", "\"" + getCurrentBranch() + "\"" buildConfigField "String", "COMMIT_HASH", "\"" + getCommitHash() + "\"" @@ -71,15 +72,15 @@ tasks.whenTaskAdded { task -> dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'androidx.appcompat:appcompat:1.1.0-beta01' - implementation 'androidx.preference:preference:1.1.0-beta01' + implementation 'androidx.appcompat:appcompat:1.1.0' + implementation 'androidx.preference:preference:1.1.0' implementation 'androidx.legacy:legacy-preference-v14:1.0.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.recyclerview:recyclerview:1.0.0' - implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0-alpha01' + implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0-alpha04' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' - implementation 'androidx.exifinterface:exifinterface:1.1.0-alpha01' + implementation 'androidx.exifinterface:exifinterface:1.1.0-beta01' implementation 'com.google.android.material:material:1.0.0' implementation 'com.google.firebase:firebase-core:17.0.0' implementation 'com.google.firebase:firebase-messaging:19.0.1' @@ -101,8 +102,7 @@ dependencies { implementation 'ru.noties:markwon:2.0.2' implementation 'net.gotev:uploadservice:3.5.2' implementation 'net.gotev:uploadservice-okhttp:3.4.2' //TODO: Warning: v.3.5 depends on okhttp 3.13! - implementation 'com.itkacher.okhttpprofiler:okhttpprofiler:1.0.5' -//Plugin: https://plugins.jetbrains.com/plugin/11249-okhttp-profiler + implementation 'com.itkacher.okhttpprofiler:okhttpprofiler:1.0.5' //Plugin: https://plugins.jetbrains.com/plugin/11249-okhttp-profiler } apply plugin: 'com.google.gms.google-services' diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentFragment.java index 4f4517d1..6e14b79b 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentFragment.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentFragment.java @@ -22,6 +22,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import gr.thmmy.mthmmy.R; +import gr.thmmy.mthmmy.base.BaseApplication; import gr.thmmy.mthmmy.base.BaseFragment; import gr.thmmy.mthmmy.model.TopicSummary; import gr.thmmy.mthmmy.session.SessionManager; @@ -146,9 +147,9 @@ public class RecentFragment extends BaseFragment { topicSummaries.addAll(fetchedRecent); recentAdapter.notifyDataSetChanged(); } else if (resultCode == NetworkResultCodes.NETWORK_ERROR) { - Toast.makeText(getContext(), "Network error", Toast.LENGTH_SHORT).show(); + Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Network error", Toast.LENGTH_SHORT).show(); } else { - Toast.makeText(getContext(), "Unexpected error," + + Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Unexpected error," + " please contact the developers with the details", Toast.LENGTH_LONG).show(); } diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadFragment.java index cec14e97..1db4131e 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadFragment.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadFragment.java @@ -143,11 +143,12 @@ public class UnreadFragment extends BaseFragment { @Override public void onDestroy() { super.onDestroy(); - if (unreadTask.isRunning()) + if (unreadTask!=null && unreadTask.isRunning()) unreadTask.cancel(true); - if (markReadTask.isRunning()) + if (markReadTask!=null && markReadTask.isRunning()) markReadTask.cancel(true); - topicSummaries.clear(); + if(topicSummaries!=null) + topicSummaries.clear(); } public interface UnreadFragmentInteractionListener extends FragmentInteractionListener { diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/Posting.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/Posting.java index 88c2f8a3..6fb1cc3f 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/Posting.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/Posting.java @@ -2,6 +2,7 @@ package gr.thmmy.mthmmy.activities.topic; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; import java.io.IOException; @@ -60,22 +61,24 @@ public class Posting { String finalUrl = response.request().url().toString(); if (finalUrl.contains("action=post")) { Document postErrorPage = Jsoup.parse(response.body().string()); - String[] errors = postErrorPage.select("tr[id=errors] div[id=error_list]").first() - .toString().split("
"); - for (int i = 0; i < errors.length; ++i) { //TODO test - Timber.d(String.valueOf(i)); - Timber.d(errors[i]); - } - for (String error : errors) { - if (error.contains("Your session timed out while posting") || - error.contains("Υπερβήκατε τον μέγιστο χρόνο σύνδεσης κατά την αποστολή")) - return REPLY_STATUS.SESSION_ENDED; - if (error.contains("No subject was filled in") - || error.contains("Δεν δόθηκε τίτλος")) - return REPLY_STATUS.NO_SUBJECT; - if (error.contains("The message body was left empty") - || error.contains("Δεν δόθηκε κείμενο για το μήνυμα")) - return REPLY_STATUS.EMPTY_BODY; + Element errorsElement = postErrorPage.select("tr[id=errors] div[id=error_list]").first(); + if(errorsElement!=null){ + String[] errors = errorsElement.toString().split("
"); + for (int i = 0; i < errors.length; ++i) { //TODO test + Timber.d(String.valueOf(i)); + Timber.d(errors[i]); + } + for (String error : errors) { + if (error.contains("Your session timed out while posting") || + error.contains("Υπερβήκατε τον μέγιστο χρόνο σύνδεσης κατά την αποστολή")) + return REPLY_STATUS.SESSION_ENDED; + if (error.contains("No subject was filled in") + || error.contains("Δεν δόθηκε τίτλος")) + return REPLY_STATUS.NO_SUBJECT; + if (error.contains("The message body was left empty") + || error.contains("Δεν δόθηκε κείμενο για το μήνυμα")) + return REPLY_STATUS.EMPTY_BODY; + } } return REPLY_STATUS.NEW_REPLY_WHILE_POSTING; } diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java index d3d97663..e0e52b3e 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java @@ -42,7 +42,7 @@ import java.util.ArrayList; import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.activities.topic.tasks.EditTask; import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForEditTask; -import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForReply; +import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForReplyTask; import gr.thmmy.mthmmy.activities.topic.tasks.ReplyTask; import gr.thmmy.mthmmy.activities.topic.tasks.TopicTask; import gr.thmmy.mthmmy.base.BaseActivity; @@ -618,7 +618,7 @@ public class TopicActivity extends BaseActivity implements TopicAdapter.OnPostFo } } }); - viewModel.setPrepareForReplyCallbacks(new PrepareForReply.PrepareForReplyCallbacks() { + viewModel.setPrepareForReplyCallbacks(new PrepareForReplyTask.PrepareForReplyCallbacks() { @Override public void onPrepareForReplyStarted() { progressBar.setVisibility(ProgressBar.VISIBLE); diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForReply.java b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForReplyTask.java similarity index 83% rename from app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForReply.java rename to app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForReplyTask.java index bf5e12e0..36024a7e 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForReply.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForReplyTask.java @@ -15,13 +15,13 @@ import okhttp3.Request; import okhttp3.Response; import timber.log.Timber; -public class PrepareForReply extends AsyncTask { +public class PrepareForReplyTask extends AsyncTask { private PrepareForReplyCallbacks listener; private OnPrepareForReplyFinished finishListener; private String replyPageUrl; - public PrepareForReply(PrepareForReplyCallbacks listener, OnPrepareForReplyFinished finishListener, - String replyPageUrl) { + public PrepareForReplyTask(PrepareForReplyCallbacks listener, OnPrepareForReplyFinished finishListener, + String replyPageUrl) { this.listener = listener; this.finishListener = finishListener; this.replyPageUrl = replyPageUrl; @@ -49,12 +49,16 @@ public class PrepareForReply extends AsyncTask { + generateFieldsButton = findViewById(R.id.upload_title_description_builder); + generateFieldsButton.setEnabled(false); + generateFieldsButton.setOnClickListener(view -> { if(uploadsCourse!=null && !uploadsCourse.getName().equals("") && !semester.equals("")){ Intent intent = new Intent(UploadActivity.this, UploadFieldsBuilderActivity.class); Bundle builderExtras = new Bundle(); @@ -372,7 +372,6 @@ public class UploadActivity extends BaseActivity { requestPerms(UPLOAD_REQUEST_STORAGE_CODE); dialog.cancel(); } - return; } @@ -410,7 +409,7 @@ public class UploadActivity extends BaseActivity { } else { //Renders the already parsed data updateUIElements(); - titleDescriptionBuilderButton.setEnabled(true); + generateFieldsButton.setEnabled(true); } Resources res = getResources(); @@ -667,7 +666,7 @@ public class UploadActivity extends BaseActivity { filesListView.setVisibility(View.VISIBLE); } - public static UploadNotificationConfig getConfigForUpload(Context context, String uploadID, + public UploadNotificationConfig getConfigForUpload(Context context, String uploadID, String filename) { UploadNotificationConfig uploadNotificationConfig = new UploadNotificationConfig(); uploadNotificationConfig.setIconForAllStatuses(android.R.drawable.stat_sys_upload); @@ -699,7 +698,7 @@ public class UploadActivity extends BaseActivity { uploadNotificationConfig.setClickIntentForAllStatuses(PendingIntent.getBroadcast(context, 1, combinedActionsIntent, PendingIntent.FLAG_UPDATE_CURRENT)); } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + else { Intent retryIntent = new Intent(context, UploadsReceiver.class); retryIntent.setAction(UploadsReceiver.ACTION_RETRY_UPLOAD); retryIntent.putExtra(UploadsReceiver.UPLOAD_ID_KEY, uploadID); @@ -710,13 +709,13 @@ public class UploadActivity extends BaseActivity { uploadNotificationConfig.getProgress().actions.add(new UploadNotificationAction( R.drawable.ic_cancel_accent_24dp, - context.getString(R.string.upload_notification_cancel), + context.getString(R.string.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), + context.getString(R.string.upload_retry), PendingIntent.getBroadcast(context, 0, retryIntent, PendingIntent.FLAG_UPDATE_CURRENT) )); @@ -731,7 +730,7 @@ public class UploadActivity extends BaseActivity { String uploadDescriptionText, String fileIcon, String uploaderProfileIndex, Uri fileUri) { try { - new MultipartUploadRequest(context, uploadID, uploadIndexUrl) + MultipartUploadRequest multipartUploadRequest = new MultipartUploadRequest(context, uploadID, uploadIndexUrl) .setUtf8Charset() .setNotificationConfig(uploadNotificationConfig) .addParameter("tp-dluploadtitle", uploadTitleText) @@ -740,12 +739,14 @@ public class UploadActivity extends BaseActivity { .addFileToUpload(fileUri.toString() , "tp-dluploadfile") .addParameter("tp_dluploadicon", fileIcon) + .addParameter("tp_dluploadpic", "") .addParameter("tp-uploaduser", uploaderProfileIndex) .setNotificationConfig(uploadNotificationConfig) - .setMaxRetries(2) - .startUpload(); - - Toast.makeText(context, "Uploading files in the background.", Toast.LENGTH_SHORT).show(); + .setMaxRetries(2); + Timber.d("Uploading a file with properties: \nTitle: %s\nCategory: %s\nDescription: %s\nIcon: %s\nUploader: %s", + uploadTitleText, categorySelected, uploadDescriptionText, fileIcon, uploaderProfileIndex); + multipartUploadRequest.startUpload(); + Toast.makeText(context, "Uploading file(s) in the background...", Toast.LENGTH_SHORT).show(); return true; } catch (Exception exception) { Timber.e(exception, "AndroidUploadService: %s", exception.getMessage()); @@ -841,7 +842,7 @@ public class UploadActivity extends BaseActivity { } categorySelected = parentCategories.get(position).getValue(); - setCourseAndSemester(); + generateFieldsButton.setEnabled(false); //Adds new sub-category spinner if (parentCategories.get(position).hasSubCategories()) { @@ -882,6 +883,8 @@ public class UploadActivity extends BaseActivity { } } } + else + setCourseAndSemester(); } @Override @@ -890,21 +893,13 @@ public class UploadActivity extends BaseActivity { private void setCourseAndSemester(){ uploadsCourse = null; semester = ""; - - if (categorySelected.equals("-1")) { - titleDescriptionBuilderButton.setEnabled(false); - return; - } + if (categorySelected.equals("-1")) return; int numberOfSpinners = categoriesSpinners.getChildCount(); - if (numberOfSpinners < 3) { - titleDescriptionBuilderButton.setEnabled(false); - return; - } + if (numberOfSpinners < 3) return; - String maybeSemester = ""; - String maybeCourse = ""; + String maybeSemester, maybeCourse; if (numberOfSpinners == 5) { if (((AppCompatSpinnerWithoutDefault) categoriesSpinners.getChildAt(numberOfSpinners - 1)). @@ -927,30 +922,24 @@ public class UploadActivity extends BaseActivity { categoriesSpinners.getChildAt(numberOfSpinners - 1)).getSelectedItem(); } - if (!maybeSemester.contains("εξάμηνο") && !maybeSemester.contains("Εξάμηνο")) { - titleDescriptionBuilderButton.setEnabled(false); - return; - } - - if (maybeCourse == null) { - titleDescriptionBuilderButton.setEnabled(false); - return; - } + if (!maybeSemester.contains("εξάμηνο") && !maybeSemester.contains("Εξάμηνο")) return; + if (maybeCourse == null) return; - String retrievedCourse = maybeCourse.replaceAll("-", "").replace("(ΝΠΣ)", "").trim(); - String retrievedSemester = maybeSemester.replaceAll("-", "").trim().substring(0, 1); + String retrievedCourse = maybeCourse.replaceAll("-", "") + .replaceAll("\\((πρώην|πρωην).*\\)","") + .replace("(ΝΠΣ)", "") + .trim(); - UploadsCourse foundUploadsCourse = UploadsCourse.findCourse(retrievedCourse, uploadsCourses); + if(!retrievedCourse.isEmpty()){ + UploadsCourse foundUploadsCourse = UploadsCourse.findCourse(retrievedCourse, uploadsCourses); - if(foundUploadsCourse != null){ - uploadsCourse = foundUploadsCourse; - semester = retrievedSemester; - Timber.d("Selected course: %s, semester: %s", uploadsCourse.getName(), semester); - titleDescriptionBuilderButton.setEnabled(true); - return; + if(foundUploadsCourse != null){ + uploadsCourse = foundUploadsCourse; + semester = maybeSemester.replaceAll("-", "").trim().substring(0, 1); + Timber.d("Selected course: %s, semester: %s", uploadsCourse.getName(), semester); + generateFieldsButton.setEnabled(true); + } } - - titleDescriptionBuilderButton.setEnabled(false); } } @@ -1017,7 +1006,7 @@ public class UploadActivity extends BaseActivity { } } - public static class ZipTask extends AsyncTask { + public class ZipTask extends AsyncTask { // Weak references will still allow the Activity to be garbage-collected private final WeakReference weakActivity; final String zipFilename, categorySelected, uploadTitleText, uploadDescriptionText, diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadFieldsBuilderActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadFieldsBuilderActivity.java index ffa6f26e..f7eb6e0d 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadFieldsBuilderActivity.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadFieldsBuilderActivity.java @@ -156,11 +156,11 @@ public class UploadFieldsBuilderActivity extends BaseActivity { private String buildTitle() { switch (typeRadio.getCheckedRadioButtonId()) { case R.id.upload_fields_builder_radio_button_exams: - return courseMinifiedName + " - " + "Θέματα εξετάσεων " + getPeriod() + " " + year.getText().toString(); + return "[" + courseMinifiedName + "] - " + "Θέματα εξετάσεων " + getPeriod() + " " + year.getText().toString(); case R.id.upload_fields_builder_radio_button_exam_solutions: - return courseMinifiedName + " - " + "Λύσεις θεμάτων " + getPeriod() + " " + year.getText().toString(); + return "[" + courseMinifiedName + "] - " + "Λύσεις θεμάτων " + getPeriod() + " " + year.getText().toString(); case R.id.upload_fields_builder_radio_button_notes: - return courseMinifiedName + " - " + "Σημειώσεις παραδόσεων " + year.getText().toString(); + return "[" + courseMinifiedName + "] - " + "Σημειώσεις παραδόσεων " + year.getText().toString(); default: return null; } diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsCourse.java b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsCourse.java index 8fe986c9..12f0475f 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsCourse.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsCourse.java @@ -52,15 +52,17 @@ class UploadsCourse { String foundKey = null; for (Map.Entry entry : uploadsCourses.entrySet()) { String key = entry.getKey(); - if ((key.contains(retrievedCourse))&& (foundKey==null || key.length()>foundKey.length())) - foundKey = key; + if ((key.contains(retrievedCourse) || retrievedCourse.contains(key)) + && (foundKey==null || key.length()>foundKey.length())) + foundKey = key; } if(foundKey==null){ Timber.w("Couldn't find course that matches %s", retrievedCourse); Bundle bundle = new Bundle(); - bundle.putString("COURSE_NAME", retrievedCourse); - BaseApplication.getInstance().logFirebaseAnalyticsEvent("UNSUPPORTED_UPLOADS_COURSE", bundle); + bundle.putString("course_name", retrievedCourse); + BaseApplication.getInstance().logFirebaseAnalyticsEvent("unsupported_uploads_course", bundle); + return null; } return uploadsCourses.get(foundKey); diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadException.java b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadException.java new file mode 100644 index 00000000..0b8cdc29 --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadException.java @@ -0,0 +1,8 @@ +package gr.thmmy.mthmmy.activities.upload.multipart; + +public class MultipartUploadException extends RuntimeException { + public MultipartUploadException(String message) { + super(message); + } + +} diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadRequest.java b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadRequest.java new file mode 100644 index 00000000..f6126523 --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadRequest.java @@ -0,0 +1,99 @@ +package gr.thmmy.mthmmy.activities.upload.multipart; + +import android.content.Context; +import android.content.Intent; + +import net.gotev.uploadservice.HttpUploadRequest; +import net.gotev.uploadservice.Logger; +import net.gotev.uploadservice.UploadFile; +import net.gotev.uploadservice.UploadTask; + +import java.io.FileNotFoundException; +import java.net.MalformedURLException; + +/** + From MultipartUploadRequest gotev/android-upload-service in order to use the local custom + MultipartUploadTask. + */ +public class MultipartUploadRequest extends HttpUploadRequest { + + private static final String LOG_TAG = MultipartUploadRequest.class.getSimpleName(); + private boolean isUtf8Charset = false; + + + public MultipartUploadRequest(final Context context, final String uploadId, final String serverUrl) + throws IllegalArgumentException, MalformedURLException { + super(context, uploadId, serverUrl); + } + + public MultipartUploadRequest(final Context context, final String serverUrl) + throws MalformedURLException, IllegalArgumentException { + this(context, null, serverUrl); + } + + @Override + protected void initializeIntent(Intent intent) { + super.initializeIntent(intent); + intent.putExtra(MultipartUploadTask.PARAM_UTF8_CHARSET, isUtf8Charset); + } + + @Override + protected Class getTaskClass() { + return MultipartUploadTask.class; + } + + public MultipartUploadRequest addFileToUpload(String filePath, + String parameterName, + String fileName, String contentType) + throws FileNotFoundException, IllegalArgumentException { + + UploadFile file = new UploadFile(filePath); + filePath = file.getPath(); + + if (parameterName == null || "".equals(parameterName)) { + throw new IllegalArgumentException("Please specify parameterName value for file: " + + filePath); + } + + file.setProperty(MultipartUploadTask.PROPERTY_PARAM_NAME, parameterName); + + if (contentType == null || contentType.isEmpty()) { + contentType = file.getContentType(context); + Logger.debug(LOG_TAG, "Auto-detected MIME type for " + filePath + + " is: " + contentType); + } else { + Logger.debug(LOG_TAG, "Content Type set for " + filePath + + " is: " + contentType); + } + + file.setProperty(MultipartUploadTask.PROPERTY_CONTENT_TYPE, contentType); + + if (fileName == null || "".equals(fileName)) { + fileName = file.getName(context); + Logger.debug(LOG_TAG, "Using original file name: " + fileName); + } else { + Logger.debug(LOG_TAG, "Using custom file name: " + fileName); + } + + file.setProperty(MultipartUploadTask.PROPERTY_REMOTE_FILE_NAME, fileName); + + params.files.add(file); + return this; + } + + public MultipartUploadRequest addFileToUpload(final String path, final String parameterName, + final String fileName) + throws FileNotFoundException, IllegalArgumentException { + return addFileToUpload(path, parameterName, fileName, null); + } + + public MultipartUploadRequest addFileToUpload(final String path, final String parameterName) + throws FileNotFoundException, IllegalArgumentException { + return addFileToUpload(path, parameterName, null, null); + } + + public MultipartUploadRequest setUtf8Charset() { + isUtf8Charset = true; + return this; + } +} \ No newline at end of file diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadTask.java b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadTask.java new file mode 100644 index 00000000..be9bb0f6 --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadTask.java @@ -0,0 +1,160 @@ +package gr.thmmy.mthmmy.activities.upload.multipart; + +import android.content.Intent; + +import net.gotev.uploadservice.HttpUploadTask; +import net.gotev.uploadservice.NameValue; +import net.gotev.uploadservice.UploadFile; +import net.gotev.uploadservice.UploadService; +import net.gotev.uploadservice.http.BodyWriter; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; + +/** + Extended MultipartUploadTask from gotev/android-upload-service to include a fix for the parameter + tp_dluploadpic. Also changed Connection to keep-alive. + */ +public class MultipartUploadTask extends HttpUploadTask { + + static final String PARAM_UTF8_CHARSET = "multipartUtf8Charset"; + + private static final String BOUNDARY_SIGNATURE = "-------AndroidUploadService"; + private static final Charset US_ASCII = Charset.forName("US-ASCII"); + private static final String NEW_LINE = "\r\n"; + private static final String TWO_HYPHENS = "--"; + + // properties associated to each file + static final String PROPERTY_REMOTE_FILE_NAME = "httpRemoteFileName"; + static final String PROPERTY_CONTENT_TYPE = "httpContentType"; + static final String PROPERTY_PARAM_NAME = "httpParamName"; + + private byte[] boundaryBytes; + private byte[] trailerBytes; + private Charset charset; + + @Override + protected void init(UploadService service, Intent intent) throws IOException { + super.init(service, intent); + + String boundary = BOUNDARY_SIGNATURE + System.nanoTime(); + boundaryBytes = (TWO_HYPHENS + boundary + NEW_LINE).getBytes(US_ASCII); + trailerBytes = (TWO_HYPHENS + boundary + TWO_HYPHENS + NEW_LINE).getBytes(US_ASCII); + charset = intent.getBooleanExtra(PARAM_UTF8_CHARSET, false) ? + Charset.forName("UTF-8") : US_ASCII; + + httpParams.addHeader("Connection", "Keep-Alive"); + httpParams.addHeader("Content-Type", "multipart/form-data; boundary=" + boundary); + } + + @Override + protected long getBodyLength() throws UnsupportedEncodingException { + return (getRequestParametersLength() + getFilesLength() + trailerBytes.length); + } + + @Override + public void onBodyReady(BodyWriter bodyWriter) throws IOException { + //reset uploaded bytes when the body is ready to be written + //because sometimes this gets invoked when network changes + uploadedBytes = 0; + writeRequestParameters(bodyWriter); + writeFiles(bodyWriter); + bodyWriter.write(trailerBytes); + uploadedBytes += trailerBytes.length; + broadcastProgress(uploadedBytes, totalBytes); + } + + private long getFilesLength() throws UnsupportedEncodingException { + long total = 0; + + for (UploadFile file : params.files) { + total += getTotalMultipartBytes(file); + } + + return total; + } + + private long getRequestParametersLength() throws UnsupportedEncodingException { + long parametersBytes = 0; + + if (!httpParams.getRequestParameters().isEmpty()) { + for (final NameValue parameter : httpParams.getRequestParameters()) { + // the bytes needed for every parameter are the sum of the boundary bytes + // and the bytes occupied by the parameter + parametersBytes += boundaryBytes.length + getMultipartBytes(parameter).length; + } + } + + return parametersBytes; + } + + private byte[] getMultipartBytes(NameValue parameter) throws UnsupportedEncodingException { + if(parameter.getName().equals("tp_dluploadpic")){ + String header = "Content-Disposition: form-data; name=\"" + + parameter.getName() + "\"; filename=\"\"" + NEW_LINE + + "Content-Type: application/octet-stream" + NEW_LINE + NEW_LINE; + return header.getBytes(charset); + } + else + return ("Content-Disposition: form-data; name=\"" + parameter.getName() + "\"" + + NEW_LINE + NEW_LINE + parameter.getValue() + NEW_LINE).getBytes(charset); + } + + private byte[] getMultipartHeader(UploadFile file) + throws UnsupportedEncodingException { + String header = "Content-Disposition: form-data; name=\"" + + file.getProperty(PROPERTY_PARAM_NAME) + "\"; filename=\"" + + file.getProperty(PROPERTY_REMOTE_FILE_NAME) + "\"" + NEW_LINE + + "Content-Type: " + file.getProperty(PROPERTY_CONTENT_TYPE) + + NEW_LINE + NEW_LINE; + + return header.getBytes(charset); + } + + private long getTotalMultipartBytes(UploadFile file) + throws UnsupportedEncodingException { + return boundaryBytes.length + getMultipartHeader(file).length + file.length(service) + + NEW_LINE.getBytes(charset).length; + } + + private void writeRequestParameters(BodyWriter bodyWriter) throws IOException { + if (!httpParams.getRequestParameters().isEmpty()) { + for (final NameValue parameter : httpParams.getRequestParameters()) { + bodyWriter.write(boundaryBytes); + byte[] formItemBytes = getMultipartBytes(parameter); + bodyWriter.write(formItemBytes); + + uploadedBytes += boundaryBytes.length + formItemBytes.length; + broadcastProgress(uploadedBytes, totalBytes); + } + } + } + + private void writeFiles(BodyWriter bodyWriter) throws IOException { + for (UploadFile file : params.files) { + if (!shouldContinue) + break; + + bodyWriter.write(boundaryBytes); + byte[] headerBytes = getMultipartHeader(file); + bodyWriter.write(headerBytes); + + uploadedBytes += boundaryBytes.length + headerBytes.length; + broadcastProgress(uploadedBytes, totalBytes); + + bodyWriter.writeStream(file.getStream(service), this); + + byte[] newLineBytes = NEW_LINE.getBytes(charset); + bodyWriter.write(newLineBytes); + uploadedBytes += newLineBytes.length; + } + } + + @Override + protected void onSuccessfulUpload() { + addAllFilesToSuccessfullyUploadedFiles(); + } + +} + diff --git a/app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java b/app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java index 0671b7e3..a4ea57dd 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java +++ b/app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java @@ -921,22 +921,22 @@ public abstract class BaseActivity extends AppCompatActivity { progressDialogBuilder.setView(progressDialogLayout); uploadsProgressDialog = progressDialogBuilder.create(); - if (!UploadService.getTaskList().contains("" + dialogUploadID)) { + if (!UploadService.getTaskList().contains(dialogUploadID)) { //Upload probably failed at this point - uploadsProgressDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "Retry", (progressDialog, progressWhich) -> { + uploadsProgressDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "", (progressDialog, progressWhich) -> { /*LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context.getApplicationContext()); localBroadcastManager.sendBroadcast(multipartUploadRetryIntent);*/ - uploadsProgressDialog.dismiss(); + //uploadsProgressDialog.dismiss(); //context.sendBroadcast(retryIntent); }); - uploadsProgressDialog.setButton(AlertDialog.BUTTON_NEGATIVE, "Cancel", (progressDialog, progressWhich) -> { + uploadsProgressDialog.setButton(AlertDialog.BUTTON_NEGATIVE, getString(R.string.cancel), (progressDialog, progressWhich) -> { uploadsProgressDialog.dismiss(); }); TextView dialogProgressText = progressDialogLayout.findViewById(R.id.dialog_upload_progress_text); dialogProgressBar.setVisibility(View.GONE); - dialogProgressText.setText("Upload failed."); + dialogProgressText.setText(getString(R.string.upload_failed)); uploadsProgressDialog.show(); } else { diff --git a/app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java b/app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java index f8b06e28..1406eb57 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java +++ b/app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java @@ -22,8 +22,10 @@ import net.gotev.uploadservice.UploadServiceBroadcastReceiver; import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.activities.upload.UploadsHelper; +import gr.thmmy.mthmmy.activities.upload.multipart.MultipartUploadException; import gr.thmmy.mthmmy.base.BaseApplication; import me.zhanghai.android.materialprogressbar.MaterialProgressBar; +import timber.log.Timber; public class UploadsReceiver extends UploadServiceBroadcastReceiver { public static final String UPLOAD_ID_KEY = "UPLOAD_ID_KEY"; @@ -57,6 +59,7 @@ public class UploadsReceiver extends UploadServiceBroadcastReceiver { switch (intentAction) { case ACTION_CANCEL_UPLOAD: String uploadID = intentBundle.getString(UPLOAD_ID_KEY); + Timber.d("Received ACTION_CANCEL_UPLOAD (id: %s)", uploadID); UploadService.stopUpload(uploadID); break; /*case ACTION_RETRY_UPLOAD: @@ -84,16 +87,18 @@ public class UploadsReceiver extends UploadServiceBroadcastReceiver { @Override public void onProgress(Context context, UploadInfo uploadInfo) { + Timber.i("Upload in progress (id: %s)",uploadInfo.getUploadId()); 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.setText(R.string.upload_resume_in_background); alertDialogNeutral.setOnClickListener(v -> uploadProgressDialog.dismiss()); Button alertDialogNegative = uploadProgressDialog.getButton(AlertDialog.BUTTON_NEGATIVE); - alertDialogNegative.setText("Cancel"); + alertDialogNegative.setText(R.string.cancel); alertDialogNegative.setOnClickListener(v -> { + Timber.d("Cancelling upload (id: %s)", dialogUploadID); UploadService.stopUpload(dialogUploadID); uploadProgressDialog.dismiss(); }); @@ -122,6 +127,7 @@ public class UploadsReceiver extends UploadServiceBroadcastReceiver { @Override public void onError(Context context, UploadInfo uploadInfo, ServerResponse serverResponse, Exception exception) { + Timber.i("Error while uploading (id: %s)",uploadInfo.getUploadId()); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP && uploadInfo.getUploadId().equals(dialogUploadID) && uploadProgressDialog != null) { @@ -135,13 +141,9 @@ public class UploadsReceiver extends UploadServiceBroadcastReceiver { });*/ Button alertDialogNegative = uploadProgressDialog.getButton(AlertDialog.BUTTON_NEGATIVE); - alertDialogNegative.setText("Cancel"); + alertDialogNegative.setText(R.string.cancel); alertDialogNegative.setOnClickListener(v -> { - NotificationManager notificationManager = (NotificationManager) context.getApplicationContext(). - getSystemService(Context.NOTIFICATION_SERVICE); - if (notificationManager != null) { - notificationManager.cancel(uploadInfo.getNotificationID()); - } + cancelNotification(context, uploadInfo.getNotificationID()); UploadsHelper.deleteTempFiles(storage); uploadProgressDialog.dismiss(); }); @@ -153,7 +155,7 @@ public class UploadsReceiver extends UploadServiceBroadcastReceiver { TextView dialogProgressText = progressWindow.findViewById(R.id.dialog_upload_progress_text); dialogProgressBar.setVisibility(View.GONE); - dialogProgressText.setText("Upload failed."); + dialogProgressText.setText(R.string.upload_failed); } if (uploadInfo.getUploadedBytes() == uploadInfo.getTotalBytes()) { @@ -161,18 +163,13 @@ public class UploadsReceiver extends UploadServiceBroadcastReceiver { } } } else { - NotificationManager notificationManager = (NotificationManager) context.getApplicationContext(). - getSystemService(Context.NOTIFICATION_SERVICE); - if (notificationManager != null) { - notificationManager.cancel(uploadInfo.getNotificationID()); - } - + cancelNotification(context, 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(); + Toast.makeText(context.getApplicationContext(), R.string.upload_failed, Toast.LENGTH_SHORT).show(); if (storage == null) { storage = new Storage(context.getApplicationContext()); } @@ -185,32 +182,38 @@ public class UploadsReceiver extends UploadServiceBroadcastReceiver { dialogUploadID = null; } - Toast.makeText(context.getApplicationContext(), "Upload completed successfully", Toast.LENGTH_SHORT).show(); + String response = serverResponse.getBodyAsString(); + if(response.contains("Η προσθήκη του αρχείου ήταν επιτυχημένη.")||response.contains("The upload was successful.")){ + Timber.i("Upload completed successfully (id: %s)",uploadInfo.getUploadId()); + Toast.makeText(context.getApplicationContext(), "Upload completed successfully", Toast.LENGTH_SHORT).show(); + BaseApplication.getInstance().logFirebaseAnalyticsEvent("file_upload", null); + } + else { + MultipartUploadException multipartUploadException = new MultipartUploadException(response); + Timber.e(multipartUploadException); + onError(context,uploadInfo,serverResponse,multipartUploadException); + } + 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) { + Timber.i("Upload cancelled (id: %s)", uploadInfo.getUploadId()); 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) { + Toast.makeText(context.getApplicationContext(), R.string.upload_cancelled, 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()); - }*/ + //cancelNotification(context, uploadInfo.getNotificationID()); UploadsHelper.deleteTempFiles(storage); } @@ -220,4 +223,11 @@ public class UploadsReceiver extends UploadServiceBroadcastReceiver { UploadsReceiver.dialogUploadID = dialogUploadID; //UploadsReceiver.multipartUploadRetryIntent = multipartUploadRetryIntent; } + + private void cancelNotification(Context context, int notificationId){ + NotificationManager notificationManager = (NotificationManager) context.getApplicationContext(). + getSystemService(Context.NOTIFICATION_SERVICE); + if (notificationManager != null) + notificationManager.cancel(notificationId); + } } \ No newline at end of file diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseException.java b/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseException.java index 942e789b..5fc06664 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseException.java +++ b/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseException.java @@ -1,13 +1,12 @@ package gr.thmmy.mthmmy.utils.parsing; /** - * ParseException is to be used for errors while parsing. + * Use ParseException for errors while parsing. */ public class ParseException extends RuntimeException { public ParseException() {} - public ParseException(String message) - { + public ParseException(String message) { super(message); } diff --git a/app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java b/app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java index 750e6a34..bac3952a 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java +++ b/app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java @@ -17,8 +17,8 @@ import gr.thmmy.mthmmy.activities.topic.tasks.DeleteTask; import gr.thmmy.mthmmy.activities.topic.tasks.EditTask; import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForEditResult; import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForEditTask; -import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForReply; import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForReplyResult; +import gr.thmmy.mthmmy.activities.topic.tasks.PrepareForReplyTask; import gr.thmmy.mthmmy.activities.topic.tasks.RemoveVoteTask; import gr.thmmy.mthmmy.activities.topic.tasks.ReplyTask; import gr.thmmy.mthmmy.activities.topic.tasks.SubmitVoteTask; @@ -35,7 +35,7 @@ import gr.thmmy.mthmmy.utils.parsing.ParseHelpers; import timber.log.Timber; public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTaskCompleted, - PrepareForReply.OnPrepareForReplyFinished, PrepareForEditTask.OnPrepareEditFinished { + PrepareForReplyTask.OnPrepareForReplyFinished, PrepareForEditTask.OnPrepareEditFinished { /** * topic state */ @@ -56,7 +56,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa private TopicTask currentTopicTask; private PrepareForEditTask currentPrepareForEditTask; - private PrepareForReply currentPrepareForReplyTask; + private PrepareForReplyTask currentPrepareForReplyTask; //callbacks for topic activity private TopicTask.TopicTaskObserver topicTaskObserver; @@ -65,7 +65,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa private ReplyTask.ReplyTaskCallbacks replyFinishListener; private PrepareForEditTask.PrepareForEditCallbacks prepareForEditCallbacks; private EditTask.EditTaskCallbacks editTaskCallbacks; - private PrepareForReply.PrepareForReplyCallbacks prepareForReplyCallbacks; + private PrepareForReplyTask.PrepareForReplyCallbacks prepareForReplyCallbacks; private ExternalAsyncTask.OnTaskStartedListener voteTaskStartedListener; private NetworkTask.OnNetworkTaskFinishedListener voteTaskFinishedListener; private ExternalAsyncTask.OnTaskStartedListener removeVoteTaskStartedListener; @@ -177,7 +177,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa stopLoading(); setPageIndicatorIndex(pageCount, true); Timber.i("Preparing for reply"); - currentPrepareForReplyTask = new PrepareForReply(prepareForReplyCallbacks, this, + currentPrepareForReplyTask = new PrepareForReplyTask(prepareForReplyCallbacks, this, replyPageUrl.getValue()); currentPrepareForReplyTask.execute(toQuoteList.toArray(new Integer[0])); } @@ -424,7 +424,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa this.editTaskCallbacks = editTaskCallbacks; } - public void setPrepareForReplyCallbacks(PrepareForReply.PrepareForReplyCallbacks prepareForReplyCallbacks) { + public void setPrepareForReplyCallbacks(PrepareForReplyTask.PrepareForReplyCallbacks prepareForReplyCallbacks) { this.prepareForReplyCallbacks = prepareForReplyCallbacks; } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5d856f19..fabd3e8a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -13,7 +13,7 @@ Bookmarks Info OK - Cancel + "Cancel" "To use mTHMMY you have to agree to our Privacy Policy by choosing one of the options below. Choose \"Yes, I want to help\", if you consent to the collection of anonymized data that will help us improve the app. Otherwise, choose \"Nope, leave me alone\". You can change your preferences any time through the app's Settings. user_consent_shared_preference_key @@ -144,8 +144,10 @@ \nThis does not rename your local files. Uploading Uploading: %1$.2f Kbit/s, %2$d/%3$d KBytes - "Cancel" - "Retry" + "Upload failed" + "Upload canceled" + "Resume in background" + "Retry" Select type of upload diff --git a/app/src/main/res/values/uploads_courses.xml b/app/src/main/res/values/uploads_courses.xml index 8f872389..51781f41 100644 --- a/app/src/main/res/values/uploads_courses.xml +++ b/app/src/main/res/values/uploads_courses.xml @@ -35,6 +35,7 @@ Διακριτά Μαθηματικά:Διακριτά Μαθηματικά:Diakrita Διακριτά μαθηματικά:Διακριτά Μαθηματικά:Diakrita Διανεμημένη Παραγωγή:Διανεμημένη Παραγωγή:Dian_Paragogi + Διατάξεις Υψηλών Συχνοτήτων:ΔΥΣ:DYS Διαφορικές Εξισώσεις:Διαφορικές:Diaforikes Διαχείριση Συστημάτων Ηλεκτρικής Ενέργειας:ΔΣΗΕ:DSHE Δομές Δεδομένων:Δομ. Δεδομ.:Domes_Dedomenon @@ -63,6 +64,7 @@ Ηλεκτρικά Κυκλώματα III:Κυκλώματα 3:Kyklomata_I Ηλεκτρικές Μετρήσεις I:Μετρήσεις 1:Metriseis_I Ηλεκτρικές Μετρήσεις II:Μετρήσεις 2:Metriseis_II + Ηλεκτρικές Μηχανές I:Μηχανές I:Mixanes_I Ηλεκτρικές Μηχανές Α\':Μηχανές Α:Mixanes_A Ηλεκτρικές Μηχανές Β\':Μηχανές Β:Mixanes_B Ηλεκτρικές Μηχανές Γ\':Μηχανές Γ:Mixanes_C @@ -109,7 +111,8 @@ Σήματα και Συστήματα:Σήματα & Συστήματα:Analog_Sima Σερβοκινητήρια Συστήματα:Σέρβο:Servo Σταθμοί Παραγωγής Ηλεκτρικής Ενέργειας:ΣΠΗΕ:SPHE - Στοχαστικό Σήμα:Στοχ. Σήμα:Stox_Sima + Στοχαστικά Σήματα και Διαδικασίες:Στοχαστικό:Stochastic + Στοχαστικό Σήμα:Στοχαστικό:Stochastic Συστήματα Αυτομάτου Ελέγχου I:ΣΑΕ 1:SAE_I Συστήματα Αυτομάτου Ελέγχου II:ΣΑΕ 2:SAE_II Συστήματα Αυτομάτου Ελέγχου III:ΣΑΕ 3:SAE_III @@ -132,6 +135,8 @@ Τεχνολογία Ηλεκτροτεχνικών Υλικών:Ηλεκτροτεχνικά Υλικά:Ilektrotexnika_Ylika Τεχνολογία Λογισμικού:Τεχνολογία Λογισμικού:SE Τηλεοπτικά Συστήματα:Τηλεοπτικά:Tileoptika + Τηλεπικοινωνιακά Συστήματα I:Τηλεπικοινωνιακά I:Tilepikoinoniaka_I + Τηλεπικοινωνιακά Συστήματα II:Τηλεπικοινωνιακά II:Tilepikoinoniaka_II Τηλεπικοινωνιακή Ηλεκτρονική:Τηλεπ. Ηλεκτρ.:Tilep_Ilektr Υπολογιστικές Μέθοδοι στα Ενεργειακά Συστήματα:ΥΜΕΣ:YMES Υπολογιστικός Ηλεκτρομαγνητισμός:Υπολογιστικός Η/Μ:Ypologistikos_HM diff --git a/build.gradle b/build.gradle index 65830711..70b74ad6 100644 --- a/build.gradle +++ b/build.gradle @@ -9,8 +9,8 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.4.1' - classpath 'com.google.gms:google-services:4.3.0' + classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.google.gms:google-services:4.3.2' classpath 'io.fabric.tools:gradle:1.29.0' classpath 'org.ajoberstar.grgit:grgit-core:3.1.1' // Also change in app/gradle/grgit.gradle classpath "com.github.ben-manes:gradle-versions-plugin:0.21.0" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2e43564c..7076a01e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sat Jun 15 18:56:13 EEST 2019 +#Tue Sep 17 12:32:34 EEST 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip