Browse Source

Version 1.7.4

master v1.7.4
Ezerous 6 years ago
parent
commit
02bf5fcfc3
No known key found for this signature in database GPG Key ID: 262B2954BBA319E3
  1. 20
      app/build.gradle
  2. 5
      app/src/main/java/gr/thmmy/mthmmy/activities/main/recent/RecentFragment.java
  3. 7
      app/src/main/java/gr/thmmy/mthmmy/activities/main/unread/UnreadFragment.java
  4. 35
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/Posting.java
  5. 4
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java
  6. 16
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForReplyTask.java
  7. 87
      app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadActivity.java
  8. 6
      app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadFieldsBuilderActivity.java
  9. 10
      app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsCourse.java
  10. 8
      app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadException.java
  11. 99
      app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadRequest.java
  12. 160
      app/src/main/java/gr/thmmy/mthmmy/activities/upload/multipart/MultipartUploadTask.java
  13. 10
      app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java
  14. 62
      app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java
  15. 5
      app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseException.java
  16. 12
      app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java
  17. 8
      app/src/main/res/values/strings.xml
  18. 7
      app/src/main/res/values/uploads_courses.xml
  19. 4
      build.gradle
  20. 4
      gradle/wrapper/gradle-wrapper.properties

20
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'

5
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();
}

7
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 {

35
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("<br>");
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("<br>");
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;
}

4
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);

16
app/src/main/java/gr/thmmy/mthmmy/activities/topic/tasks/PrepareForReply.java → 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<Integer, Void, PrepareForReplyResult> {
public class PrepareForReplyTask extends AsyncTask<Integer, Void, PrepareForReplyResult> {
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<Integer, Void, PrepareForReplyRes
seqnum = document.select("input[name=seqnum]").first().attr("value");
sc = document.select("input[name=sc]").first().attr("value");
topic = document.select("input[name=topic]").first().attr("value");
} catch (IOException | Selector.SelectorParseException e) {
Timber.e(e, "Prepare failed.");
} catch (NullPointerException e) {
// TODO: Convert this task to (New)ParseTask (?) / handle parsing errors in a better way
Timber.e(e, "Prepare failed (1)");
return new PrepareForReplyResult(false, null, null, null, null, null);
} catch (IOException | Selector.SelectorParseException e){
Timber.e(e, "Prepare failed (2)");
return new PrepareForReplyResult(false, null, null, null, null, null);
}
StringBuilder buildedQuotes = new StringBuilder("");
StringBuilder buildedQuotes = new StringBuilder();
for (Integer postIndex : postIndices) {
request = new Request.Builder()
.url("https://www.thmmy.gr/smf/index.php?action=quotefast;quote=" +

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

@ -40,7 +40,6 @@ import androidx.preference.PreferenceManager;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import net.gotev.uploadservice.MultipartUploadRequest;
import net.gotev.uploadservice.UploadNotificationAction;
import net.gotev.uploadservice.UploadNotificationConfig;
@ -59,6 +58,7 @@ import java.util.Locale;
import java.util.UUID;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.upload.multipart.MultipartUploadRequest;
import gr.thmmy.mthmmy.base.BaseActivity;
import gr.thmmy.mthmmy.base.BaseApplication;
import gr.thmmy.mthmmy.model.UploadCategory;
@ -130,7 +130,7 @@ public class UploadActivity extends BaseActivity {
private EditText uploadTitle;
private EditText uploadFilename;
private EditText uploadDescription;
private AppCompatButton titleDescriptionBuilderButton;
private AppCompatButton generateFieldsButton;
private LinearLayout filesListView;
@Override
@ -174,9 +174,9 @@ public class UploadActivity extends BaseActivity {
rootCategorySpinner = findViewById(R.id.upload_spinner_category_root);
rootCategorySpinner.setOnItemSelectedListener(new CustomOnItemSelectedListener(uploadRootCategories));
titleDescriptionBuilderButton = findViewById(R.id.upload_title_description_builder);
titleDescriptionBuilderButton.setEnabled(false);
titleDescriptionBuilderButton.setOnClickListener(view -> {
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<Uri, Void, Boolean> {
public class ZipTask extends AsyncTask<Uri, Void, Boolean> {
// Weak references will still allow the Activity to be garbage-collected
private final WeakReference<Activity> weakActivity;
final String zipFilename, categorySelected, uploadTitleText, uploadDescriptionText,

6
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;
}

10
app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsCourse.java

@ -52,15 +52,17 @@ class UploadsCourse {
String foundKey = null;
for (Map.Entry<String, UploadsCourse> 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);

8
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);
}
}

99
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<MultipartUploadRequest> {
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<? extends UploadTask> 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;
}
}

160
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();
}
}

10
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 {

62
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);
}
}

5
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);
}

12
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<Void> 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;
}

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

@ -13,7 +13,7 @@
<string name="bookmark">Bookmarks</string>
<string name="info">Info</string>
<string name="ok">OK</string>
<string name="cancel">Cancel</string>
<string name="cancel">"Cancel"</string>
<string name="user_agreement_dialog_text">"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.</string>
<string name="user_consent_shared_preference_key">user_consent_shared_preference_key</string>
@ -144,8 +144,10 @@
\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>
<string name="upload_failed">"Upload failed"</string>
<string name="upload_cancelled">"Upload canceled"</string>
<string name="upload_resume_in_background">"Resume in background"</string>
<string name="upload_retry">"Retry"</string>
<!--Upload Fields Builder Activity-->
<string name="upload_fields_builder_type_radio_buttons_title">Select type of upload</string>

7
app/src/main/res/values/uploads_courses.xml

@ -35,6 +35,7 @@
<item>Διακριτά Μαθηματικά:Διακριτά Μαθηματικά:Diakrita</item>
<item>Διακριτά μαθηματικά:Διακριτά Μαθηματικά:Diakrita</item>
<item>Διανεμημένη Παραγωγή:Διανεμημένη Παραγωγή:Dian_Paragogi</item>
<item>Διατάξεις Υψηλών Συχνοτήτων:ΔΥΣ:DYS</item>
<item>Διαφορικές Εξισώσεις:Διαφορικές:Diaforikes</item>
<item>Διαχείριση Συστημάτων Ηλεκτρικής Ενέργειας:ΔΣΗΕ:DSHE</item>
<item>Δομές Δεδομένων:Δομ. Δεδομ.:Domes_Dedomenon</item>
@ -63,6 +64,7 @@
<item>Ηλεκτρικά Κυκλώματα III:Κυκλώματα 3:Kyklomata_I</item>
<item>Ηλεκτρικές Μετρήσεις I:Μετρήσεις 1:Metriseis_I</item>
<item>Ηλεκτρικές Μετρήσεις II:Μετρήσεις 2:Metriseis_II</item>
<item>Ηλεκτρικές Μηχανές I:Μηχανές I:Mixanes_I</item>
<item>Ηλεκτρικές Μηχανές Α\':Μηχανές Α:Mixanes_A</item>
<item>Ηλεκτρικές Μηχανές Β\':Μηχανές Β:Mixanes_B</item>
<item>Ηλεκτρικές Μηχανές Γ\':Μηχανές Γ:Mixanes_C</item>
@ -109,7 +111,8 @@
<item>Σήματα και Συστήματα:Σήματα &amp; Συστήματα:Analog_Sima</item>
<item>Σερβοκινητήρια Συστήματα:Σέρβο:Servo</item>
<item>Σταθμοί Παραγωγής Ηλεκτρικής Ενέργειας:ΣΠΗΕ:SPHE</item>
<item>Στοχαστικό Σήμα:Στοχ. Σήμα:Stox_Sima</item>
<item>Στοχαστικά Σήματα και Διαδικασίες:Στοχαστικό:Stochastic</item>
<item>Στοχαστικό Σήμα:Στοχαστικό:Stochastic</item>
<item>Συστήματα Αυτομάτου Ελέγχου I:ΣΑΕ 1:SAE_I</item>
<item>Συστήματα Αυτομάτου Ελέγχου II:ΣΑΕ 2:SAE_II</item>
<item>Συστήματα Αυτομάτου Ελέγχου III:ΣΑΕ 3:SAE_III</item>
@ -132,6 +135,8 @@
<item>Τεχνολογία Ηλεκτροτεχνικών Υλικών:Ηλεκτροτεχνικά Υλικά:Ilektrotexnika_Ylika</item>
<item>Τεχνολογία Λογισμικού:Τεχνολογία Λογισμικού:SE</item>
<item>Τηλεοπτικά Συστήματα:Τηλεοπτικά:Tileoptika</item>
<item>Τηλεπικοινωνιακά Συστήματα I:Τηλεπικοινωνιακά I:Tilepikoinoniaka_I</item>
<item>Τηλεπικοινωνιακά Συστήματα II:Τηλεπικοινωνιακά II:Tilepikoinoniaka_II</item>
<item>Τηλεπικοινωνιακή Ηλεκτρονική:Τηλεπ. Ηλεκτρ.:Tilep_Ilektr</item>
<item>Υπολογιστικές Μέθοδοι στα Ενεργειακά Συστήματα:ΥΜΕΣ:YMES</item>
<item>Υπολογιστικός Ηλεκτρομαγνητισμός:Υπολογιστικός Η/Μ:Ypologistikos_HM</item>

4
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"

4
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

Loading…
Cancel
Save