uploaded from mTHMMY";
/**
* Request codes used in activities for result (AFR) calls
*/
- private static final int AFR_REQUEST_CODE_CHOOSE_FILE = 8;
- private static final int AFR_REQUEST_CODE_CAMERA = 4;
- private static final int AFR_REQUEST_CODE_FIELDS_BUILDER = 74;
+ private static final int AFR_REQUEST_CODE_CHOOSE_FILE = 8; //Arbitrary, application specific
+ private static final int AFR_REQUEST_CODE_CAMERA = 4; //Arbitrary, application specific
+ private static final int AFR_REQUEST_CODE_FIELDS_BUILDER = 74; //Arbitrary, application specific
+
+ /**
+ * Request codes to gain camera and read/write permission
+ */
+ private static final int UPLOAD_REQUEST_CAMERA_CODE = 42; //Arbitrary, application specific
+ private static final int UPLOAD_REQUEST_STORAGE_CODE = 12; //Arbitrary, application specific
+
+ private static final int MAX_FILE_SIZE_SUPPORTED = 45000000;
+
+ private HashMap
uploadsCourses;
private ArrayList uploadRootCategories = new ArrayList<>();
private ParseUploadPageTask parseUploadPageTask;
private ArrayList bundleCategory;
private String categorySelected = "-1";
private String uploaderProfileIndex = "1";
- private String uploadFilename;
- private Uri fileUri;
+ private UploadsCourse uploadsCourse;
+ private String semester = "";
+
+ private ArrayList filesList = new ArrayList<>();
+ private File photoFileCreated = null;
private String fileIcon;
+ private AppCompatImageButton uploadFilenameInfo;
+ private CustomTextWatcher textWatcher;
+ private boolean hasModifiedFilename = false;
+
+ private ZipTask zipTask;
//UI elements
private MaterialProgressBar progressBar;
private LinearLayout categoriesSpinners;
private AppCompatSpinnerWithoutDefault rootCategorySpinner;
private EditText uploadTitle;
+ private EditText uploadFilename;
private EditText uploadDescription;
private AppCompatButton titleDescriptionBuilderButton;
- private AppCompatTextView filenameHolder;
+ private LinearLayout filesListView;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -135,71 +175,53 @@ public class UploadActivity extends BaseActivity {
rootCategorySpinner.setOnItemSelectedListener(new CustomOnItemSelectedListener(uploadRootCategories));
titleDescriptionBuilderButton = findViewById(R.id.upload_title_description_builder);
+ titleDescriptionBuilderButton.setEnabled(false);
titleDescriptionBuilderButton.setOnClickListener(view -> {
- if (categorySelected.equals("-1")) {
- Toast.makeText(view.getContext(), "Please choose a category first", Toast.LENGTH_SHORT).show();
- return;
+ if(uploadsCourse!=null && !uploadsCourse.getName().equals("") && !semester.equals("")){
+ Intent intent = new Intent(UploadActivity.this, UploadFieldsBuilderActivity.class);
+ Bundle builderExtras = new Bundle();
+ builderExtras.putString(BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_NAME, uploadsCourse.getName());
+ builderExtras.putString(BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_MINIFIED_NAME, uploadsCourse.getMinifiedName());
+ builderExtras.putString(BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_GREEKLISH_NAME, uploadsCourse.getGreeklishName());
+ builderExtras.putString(BUNDLE_UPLOAD_FIELD_BUILDER_SEMESTER, semester);
+ intent.putExtras(builderExtras);
+ startActivityForResult(intent, AFR_REQUEST_CODE_FIELDS_BUILDER);
}
+ });
- int numberOfSpinners = categoriesSpinners.getChildCount();
+ uploadTitle = findViewById(R.id.upload_title);
+ uploadDescription = findViewById(R.id.upload_description);
- if (numberOfSpinners < 3) {
- Toast.makeText(view.getContext(), "Please choose a course category", Toast.LENGTH_SHORT).show();
+ uploadFilenameInfo = findViewById(R.id.upload_filename_info);
+ uploadFilenameInfo.setOnClickListener(view -> {
+ //Inflates the popup menu content
+ LayoutInflater layoutInflater = (LayoutInflater) view.getContext().
+ getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ if (layoutInflater == null) {
return;
}
- String maybeSemester = "", maybeCourse = "";
-
- if (numberOfSpinners == 5) {
- if (((AppCompatSpinnerWithoutDefault) categoriesSpinners.getChildAt(numberOfSpinners - 1)).
- getSelectedItemPosition() == -1) {
- maybeSemester = (String) ((AppCompatSpinnerWithoutDefault)
- categoriesSpinners.getChildAt(numberOfSpinners - 4)).getSelectedItem();
- maybeCourse = (String) ((AppCompatSpinnerWithoutDefault)
- categoriesSpinners.getChildAt(numberOfSpinners - 2)).getSelectedItem();
- } else {
- Toast.makeText(view.getContext(), "Please choose a course category", Toast.LENGTH_SHORT).show();
- }
- } else if (numberOfSpinners == 4) {
- maybeSemester = (String) ((AppCompatSpinnerWithoutDefault)
- categoriesSpinners.getChildAt(numberOfSpinners - 3)).getSelectedItem();
- maybeCourse = (String) ((AppCompatSpinnerWithoutDefault)
- categoriesSpinners.getChildAt(numberOfSpinners - 1)).getSelectedItem();
- } else {
- maybeSemester = (String) ((AppCompatSpinnerWithoutDefault)
- categoriesSpinners.getChildAt(numberOfSpinners - 2)).getSelectedItem();
- maybeCourse = (String) ((AppCompatSpinnerWithoutDefault)
- categoriesSpinners.getChildAt(numberOfSpinners - 1)).getSelectedItem();
- }
-
- if (!maybeSemester.contains("εξάμηνο") && !maybeSemester.contains("Εξάμηνο")) {
- Toast.makeText(view.getContext(), "Please choose a course category", Toast.LENGTH_SHORT).show();
- return;
- }
- if (maybeCourse == null) {
- Toast.makeText(view.getContext(), "Please choose a course", Toast.LENGTH_SHORT).show();
- return;
- }
+ Context wrapper = new ContextThemeWrapper(this, R.style.PopupWindow);
+ View popUpContent = layoutInflater.inflate(R.layout.activity_upload_filename_info_popup, null);
- //Fixes course and semester
- String course = maybeCourse.replaceAll("-", "").replace("(ΝΠΣ)", "").trim();
- String semester = maybeSemester.replaceAll("-", "").trim().substring(0, 1);
+ //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);
- Intent intent = new Intent(UploadActivity.this, UploadFieldsBuilderActivity.class);
- Bundle builderExtras = new Bundle();
- builderExtras.putString(BUNDLE_UPLOAD_FIELD_BUILDER_COURSE, course);
- builderExtras.putString(BUNDLE_UPLOAD_FIELD_BUILDER_SEMESTER, semester);
- intent.putExtras(builderExtras);
- startActivityForResult(intent, AFR_REQUEST_CODE_FIELDS_BUILDER);
+ ((TextView) popUpContent.findViewById(R.id.upload_filename_info_text)).
+ setMovementMethod(LinkMovementMethod.getInstance());
+ //Displays the popup
+ popUp.showAsDropDown(view);
});
- titleDescriptionBuilderButton.setEnabled(false);
- uploadTitle = findViewById(R.id.upload_title);
- uploadDescription = findViewById(R.id.upload_description);
+ uploadFilename = findViewById(R.id.upload_filename);
+ textWatcher = new CustomTextWatcher();
+ uploadFilename.addTextChangedListener(textWatcher);
- filenameHolder = findViewById(R.id.upload_filename);
- Drawable filenameDrawable = AppCompatResources.getDrawable(this, R.drawable.ic_attach_file_white_24dp);
- filenameHolder.setCompoundDrawablesRelativeWithIntrinsicBounds(filenameDrawable, null, null, null);
+ filesListView = findViewById(R.id.upload_files_list);
AppCompatButton selectFileButton = findViewById(R.id.upload_select_file_button);
Drawable selectStartDrawable = AppCompatResources.getDrawable(this, R.drawable.ic_insert_drive_file_white_24dp);
@@ -210,9 +232,10 @@ public class UploadActivity extends BaseActivity {
"application/msword", "image/vnd.djvu", "application/gz", "application/tar.gz"};
Intent intent = new Intent(Intent.ACTION_GET_CONTENT)
- //.setType("*/*")
- .setType("image/jpeg")
- .putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
+ .setType("*/*")
+ //.setType("image/jpeg")
+ .putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes)
+ .putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
startActivityForResult(intent, AFR_REQUEST_CODE_CHOOSE_FILE);
});
@@ -221,123 +244,178 @@ public class UploadActivity extends BaseActivity {
Drawable takePhotoDrawable = AppCompatResources.getDrawable(this, R.drawable.ic_photo_camera_white_24dp);
takePhotoButton.setCompoundDrawablesRelativeWithIntrinsicBounds(takePhotoDrawable, null, null, null);
takePhotoButton.setOnClickListener(v -> {
- Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
- takePhotoIntent.putExtra("return-data", true);
- takePhotoIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(UploadsHelper.getCacheFile(this)));
-
- Intent targetedIntent = new Intent(takePhotoIntent);
- List resInfo = this.getPackageManager().queryIntentActivities(takePhotoIntent, 0);
- for (ResolveInfo resolveInfo : resInfo) {
- String packageName = resolveInfo.activityInfo.packageName;
- targetedIntent.setPackage(packageName);
- }
- startActivityForResult(takePhotoIntent, AFR_REQUEST_CODE_CAMERA);
+ if (checkPerms())
+ takePhoto();
+ else
+ requestPerms(UPLOAD_REQUEST_CAMERA_CODE);
});
FloatingActionButton uploadFAB = findViewById(R.id.upload_fab);
+ uploadFAB.setTag(true);
uploadFAB.setOnClickListener(view -> {
+ //Attempts upload
progressBar.setVisibility(View.VISIBLE);
String uploadTitleText = uploadTitle.getText().toString();
- String uploadDescriptionText = uploadDescription.getText().toString();
-
- if (uploadTitleText.equals("")) {
- uploadTitle.setError("Required");
- }
- if (fileUri == null) {
- Toast.makeText(view.getContext(), "Please choose a file to upload or take a photo", Toast.LENGTH_LONG).show();
- }
- if (categorySelected.equals("-1")) {
- Toast.makeText(view.getContext(), "Please choose category first", Toast.LENGTH_SHORT).show();
- }
-
- if (categorySelected.equals("-1") || uploadTitleText.equals("") || fileUri == null) {
- progressBar.setVisibility(View.GONE);
- return;
- }
-
- SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(view.getContext());
- if (sharedPrefs.getBoolean(UPLOADING_APP_SIGNATURE_ENABLE_KEY, true)) {
- uploadDescriptionText += uploadedFromThmmyPromptHtml;
- }
+ String editTextFilename = uploadFilename.getText().toString();
+ final String[] uploadDescriptionText = {uploadDescription.getText().toString()};
+
+ //Checks if all required fields are filled
+ {
+ boolean shouldReturn = false;
+ if (uploadTitleText.equals("")) {
+ uploadTitle.setError("Required");
+ shouldReturn = true;
+ }
+ if (filesList.isEmpty()) {
+ Toast.makeText(view.getContext(), "Please choose a file to upload or take a photo", Toast.LENGTH_LONG).show();
+ shouldReturn = true;
+ }
+ if (categorySelected.equals("-1")) {
+ 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());
+ }
- String tempFilePath = null;
- if (uploadFilename != null) {
- //File should be uploaded with a certain name. Temporarily copies the file and renames it
- tempFilePath = UploadsHelper.createTempFile(this, fileUri, uploadFilename);
- if (tempFilePath == null) {
- //Something went wrong, abort
- Toast.makeText(this, "Could not create temporary file for renaming", Toast.LENGTH_SHORT).show();
+ 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);
return;
}
}
- try {
- new MultipartUploadRequest(view.getContext(), uploadIndexUrl)
- .setUtf8Charset()
- .addParameter("tp-dluploadtitle", uploadTitleText)
- .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) {
- }
+ 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());
+ if (sharedPrefs.getBoolean(UPLOADING_APP_SIGNATURE_ENABLE_KEY, true)) {
+ uploadDescriptionText[0] += uploadedFromTHMMYPromptHtml;
+ }
- @Override
- public void onError(Context context, UploadInfo uploadInfo, ServerResponse serverResponse,
- Exception exception) {
- Toast.makeText(context, "Upload failed", Toast.LENGTH_SHORT).show();
- UploadsHelper.deleteTempFiles();
- progressBar.setVisibility(View.GONE);
- }
+ for (UploadFile file : filesList) {
+ if (file.isCameraPhoto()) {
+ TakePhoto.galleryAddPic(this, file.getPhotoFile());
+ }
+ }
- @Override
- public void onCompleted(Context context, UploadInfo uploadInfo, ServerResponse serverResponse) {
- Toast.makeText(context, "Upload completed successfully", Toast.LENGTH_SHORT).show();
- UploadsHelper.deleteTempFiles();
- BaseApplication.getInstance().logFirebaseAnalyticsEvent("file_upload", null);
-
- uploadTitle.setText(null);
- uploadDescription.setText(null);
- fileUri = null;
- filenameHolder.setText(null);
- filenameHolder.setVisibility(View.GONE);
- progressBar.setVisibility(View.GONE);
+ Uri tempFileUri = null;
+ if (filesList.size() == 1) {
+ //Checks if the file needs renaming
+ UploadFile uploadFile = filesList.get(0);
+ String selectedFileFilename = FileUtils.filenameFromUri(this, uploadFile.getFileUri());
+
+ if (!editTextFilename.equals(selectedFileFilename)) {
+ //File should be uploaded with a different name
+
+ if (checkPerms()) {
+ 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
+ Toast.makeText(this, "Could not create temporary file for renaming", Toast.LENGTH_SHORT).show();
+ progressBar.setVisibility(View.GONE);
+ return;
+ }
+
+ //Points photoFile and fileUri to the new copied and renamed file
+ uploadFile.setPhotoFile(storage.getFile(destinationFilename));
+ uploadFile.setFileUri(FileProvider.getUriForFile(this, getPackageName() +
+ ".provider", uploadFile.getPhotoFile()));
}
+ } else {
+ requestPerms(UPLOAD_REQUEST_STORAGE_CODE);
+ zipTask = null;
+ dialog.cancel();
+ return;
+ }
+ }
+ } else {
+ Uri[] filesListArray = new Uri[filesList.size()];
+ for (int i = 0; i < filesList.size(); ++i) {
+ filesListArray[i] = filesList.get(i).getFileUri();
+ }
- @Override
- public void onCancelled(Context context, UploadInfo uploadInfo) {
- Toast.makeText(context, "Upload canceled", Toast.LENGTH_SHORT).show();
+ zipTask = new ZipTask(this, editTextFilename, categorySelected,
+ uploadTitleText, uploadDescriptionText[0], fileIcon,
+ uploaderProfileIndex);
- UploadsHelper.deleteTempFiles();
- progressBar.setVisibility(View.GONE);
- }
- })
- .startUpload();
- } catch (Exception exception) {
- Timber.e(exception, "AndroidUploadService: %s", exception.getMessage());
+ if (checkPerms()) {
+ zipTask.execute(filesListArray);
+ finish();
+ } else {
+ requestPerms(UPLOAD_REQUEST_STORAGE_CODE);
+ dialog.cancel();
+ }
+
+ return;
+ }
+
+ String uploadID = UUID.randomUUID().toString();
+ if (uploadFile(this, uploadID, getConfigForUpload(this, uploadID,
+ 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();
+ }
+ });
+
+ builder.setNegativeButton("NOPE", (dialog, which) -> {
progressBar.setVisibility(View.GONE);
- }
+ dialog.dismiss();
+ });
+
+ AlertDialog alert = builder.create();
+ alert.setOnCancelListener(dialog -> {
+ progressBar.setVisibility(View.GONE);
+ dialog.dismiss();
+ });
+ alert.show();
});
if (uploadRootCategories.isEmpty()) {
//Parses the uploads page
parseUploadPageTask = new ParseUploadPageTask();
- parseUploadPageTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, uploadIndexUrl);
+ parseUploadPageTask.execute(uploadIndexUrl);
} else {
//Renders the already parsed data
updateUIElements();
titleDescriptionBuilderButton.setEnabled(true);
}
+
+ Resources res = getResources();
+ uploadsCourses = new HashMap<>(UploadsCourse
+ .generateUploadsCourses(res.getStringArray(R.array.string_array_uploads_courses)));
}
@Override
@@ -355,11 +433,17 @@ public class UploadActivity extends BaseActivity {
super.onResume();
}
+ @Override
+ protected void onPause() {
+ super.onPause();
+ }
+
@Override
protected void onDestroy() {
super.onDestroy();
- if (parseUploadPageTask != null && parseUploadPageTask.getStatus() != AsyncTask.Status.RUNNING)
+ if (parseUploadPageTask != null && parseUploadPageTask.getStatus() != AsyncTask.Status.RUNNING) {
parseUploadPageTask.cancel(true);
+ }
}
@Override
@@ -369,71 +453,108 @@ public class UploadActivity extends BaseActivity {
return;
}
- fileUri = data.getData();
- if (fileUri != null) {
- String filename = UploadsHelper.filenameFromUri(this, fileUri);
- filenameHolder.setText(filename);
- filenameHolder.setVisibility(View.VISIBLE);
-
- filename = filename.toLowerCase();
- if (filename.endsWith(".jpg")) {
- fileIcon = "jpg_image.gif";
- } else if (filename.endsWith(".gif")) {
- fileIcon = "gif_image.gif";
- } else if (filename.endsWith(".png")) {
- fileIcon = "png_image.gif";
- } else if (filename.endsWith(".html") || filename.endsWith(".htm")) {
- fileIcon = "html_file.gif";
- } else if (filename.endsWith(".pdf") || filename.endsWith(".doc") ||
- filename.endsWith("djvu")) {
- fileIcon = "text_file.gif";
- } else if (filename.endsWith(".zip") || filename.endsWith(".rar") ||
- filename.endsWith(".tar") || filename.endsWith(".tar.gz") ||
- filename.endsWith(".gz")) {
- fileIcon = "archive.gif";
- } else {
- fileIcon = "blank.gif";
+ if (data.getClipData() != null) {
+ fileIcon = "archive.gif";
+ textWatcher.setFileExtension(".zip");
+
+ if (!hasModifiedFilename)
+ setZipUploadFilename();
+
+ 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();
+ if (filename.endsWith(".jpg")) {
+ fileIcon = "jpg_image.gif";
+ } else if (filename.endsWith(".gif")) {
+ fileIcon = "gif_image.gif";
+ } else if (filename.endsWith(".png")) {
+ fileIcon = "png_image.gif";
+ } else if (filename.endsWith(".html") || filename.endsWith(".htm")) {
+ fileIcon = "html_file.gif";
+ } else if (filename.endsWith(".pdf") || filename.endsWith(".doc") ||
+ filename.endsWith("djvu")) {
+ fileIcon = "text_file.gif";
+ } else if (filename.endsWith(".zip") || filename.endsWith(".rar") ||
+ filename.endsWith(".tar") || filename.endsWith(".tar.gz") ||
+ filename.endsWith(".gz")) {
+ fileIcon = "archive.gif";
+ } else {
+ fileIcon = "blank.gif";
+ }
+ } else {
+ fileIcon = "archive.gif";
+ textWatcher.setFileExtension(".zip");
+
+ if (!hasModifiedFilename)
+ setZipUploadFilename();
+ }
+
+ addFileViewToList(filename);
+ filesList.add(new UploadFile(false, newFileUri, null));
}
}
} else if (requestCode == AFR_REQUEST_CODE_CAMERA) {
if (resultCode == Activity.RESULT_CANCELED) {
+ //Deletes image file
+ storage.deleteFile(photoFileCreated.getAbsolutePath());
return;
}
- Bitmap bitmap;
- File cacheImageFile = UploadsHelper.getCacheFile(this);
+ if (filesList.isEmpty()) {
+ textWatcher.setFileExtension(FileUtils.getFileExtension(photoFileCreated.getName()));
- Uri cacheFileUri = Uri.fromFile(cacheImageFile);
- fileIcon = "jpg_image.gif";
+ if (!hasModifiedFilename) {
+ uploadFilename.setText(photoFileCreated.getName());
+ hasModifiedFilename = false;
+ }
- bitmap = UploadsHelper.getImageResized(this, cacheFileUri);
- int rotation = UploadsHelper.getRotation(this, cacheFileUri);
- bitmap = UploadsHelper.rotate(bitmap, rotation);
+ fileIcon = "jpg_image.gif";
+ } else {
+ fileIcon = "archive.gif";
+ textWatcher.setFileExtension(".zip");
- try {
- FileOutputStream out = new FileOutputStream(cacheImageFile);
- bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
- out.flush();
- out.close();
- } catch (Exception e) {
- e.printStackTrace();
+ if (!hasModifiedFilename)
+ setZipUploadFilename();
}
- String newFilename = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.FRANCE).
- format(new Date());
- fileUri = Uri.parse(UploadsHelper.createTempFile(this, cacheFileUri, newFilename));
-
- newFilename += ".jpg";
- filenameHolder.setText(newFilename);
- filenameHolder.setVisibility(View.VISIBLE);
-
- UploadsHelper.deleteCacheFiles(this);
+ 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) {
if (resultCode == Activity.RESULT_CANCELED) {
return;
}
- uploadFilename = data.getStringExtra(RESULT_FILENAME);
+ String previousName = uploadFilename.getText().toString();
+ if (previousName.isEmpty()) {
+ uploadFilename.setText(data.getStringExtra(RESULT_FILENAME));
+ } else {
+ String extractedExtension = FileUtils.getFileExtension(previousName);
+ String filenameWithExtension = data.getStringExtra(RESULT_FILENAME) +
+ (extractedExtension != null ? extractedExtension : "");
+ uploadFilename.setText(filenameWithExtension);
+ }
+ hasModifiedFilename = true;
+
uploadTitle.setText(data.getStringExtra(RESULT_TITLE));
uploadDescription.setText(data.getStringExtra(RESULT_DESCRIPTION));
} else {
@@ -441,6 +562,263 @@ public class UploadActivity extends BaseActivity {
}
}
+ @Override
+ public void onRequestPermissionsResult(int permsRequestCode, @NonNull String[] permissions
+ , @NonNull int[] grantResults) {
+ switch (permsRequestCode) {
+ case UPLOAD_REQUEST_CAMERA_CODE:
+ if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
+ takePhoto();
+ break;
+ case UPLOAD_REQUEST_STORAGE_CODE:
+ if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED &&
+ zipTask != null) {
+ Uri[] filesListArray = new Uri[filesList.size()];
+ for (int i = 0; i < filesList.size(); ++i) {
+ filesListArray[i] = filesList.get(i).getFileUri();
+ }
+
+ zipTask.execute(filesListArray);
+ finish();
+ } else {
+ Toast.makeText(this, "Please retry uploading.", Toast.LENGTH_SHORT).show();
+ }
+ break;
+ }
+ }
+
+ private void setZipUploadFilename(){
+ String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.FRANCE).format(new Date());
+ String zipFilename = "mTHMMY_" + timeStamp + ".zip";
+ uploadFilename.setText(zipFilename);
+ hasModifiedFilename = false;
+ }
+
+ // 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 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 ArrayList parentCategories, childCategories;
@@ -463,6 +841,7 @@ public class UploadActivity extends BaseActivity {
}
categorySelected = parentCategories.get(position).getValue();
+ setCourseAndSemester();
//Adds new sub-category spinner
if (parentCategories.get(position).hasSubCategories()) {
@@ -506,7 +885,72 @@ public class UploadActivity extends BaseActivity {
}
@Override
- public void onNothingSelected(AdapterView> parent) {
+ public void onNothingSelected(AdapterView> parent) { }
+
+ private void setCourseAndSemester(){
+ uploadsCourse = null;
+ semester = "";
+
+ if (categorySelected.equals("-1")) {
+ titleDescriptionBuilderButton.setEnabled(false);
+ return;
+ }
+
+ int numberOfSpinners = categoriesSpinners.getChildCount();
+
+ if (numberOfSpinners < 3) {
+ titleDescriptionBuilderButton.setEnabled(false);
+ return;
+ }
+
+ String maybeSemester = "";
+ String maybeCourse = "";
+
+ if (numberOfSpinners == 5) {
+ if (((AppCompatSpinnerWithoutDefault) categoriesSpinners.getChildAt(numberOfSpinners - 1)).
+ getSelectedItemPosition() == -1) {
+ maybeSemester = (String) ((AppCompatSpinnerWithoutDefault)
+ categoriesSpinners.getChildAt(numberOfSpinners - 4)).getSelectedItem();
+ maybeCourse = (String) ((AppCompatSpinnerWithoutDefault)
+ categoriesSpinners.getChildAt(numberOfSpinners - 2)).getSelectedItem();
+ }
+ else return;
+ } else if (numberOfSpinners == 4) {
+ maybeSemester = (String) ((AppCompatSpinnerWithoutDefault)
+ categoriesSpinners.getChildAt(numberOfSpinners - 3)).getSelectedItem();
+ maybeCourse = (String) ((AppCompatSpinnerWithoutDefault)
+ categoriesSpinners.getChildAt(numberOfSpinners - 1)).getSelectedItem();
+ } else {
+ maybeSemester = (String) ((AppCompatSpinnerWithoutDefault)
+ categoriesSpinners.getChildAt(numberOfSpinners - 2)).getSelectedItem();
+ maybeCourse = (String) ((AppCompatSpinnerWithoutDefault)
+ categoriesSpinners.getChildAt(numberOfSpinners - 1)).getSelectedItem();
+ }
+
+ if (!maybeSemester.contains("εξάμηνο") && !maybeSemester.contains("Εξάμηνο")) {
+ titleDescriptionBuilderButton.setEnabled(false);
+ return;
+ }
+
+ if (maybeCourse == null) {
+ titleDescriptionBuilderButton.setEnabled(false);
+ return;
+ }
+
+ String retrievedCourse = maybeCourse.replaceAll("-", "").replace("(ΝΠΣ)", "").trim();
+ String retrievedSemester = maybeSemester.replaceAll("-", "").trim().substring(0, 1);
+
+ 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;
+ }
+
+ titleDescriptionBuilderButton.setEnabled(false);
}
}
@@ -569,37 +1013,82 @@ public class UploadActivity extends BaseActivity {
@Override
protected void postExecution(ResultCode result) {
updateUIElements();
- titleDescriptionBuilderButton.setEnabled(true);
progressBar.setVisibility(ProgressBar.GONE);
}
}
- private void updateUIElements() {
- String[] tmpSpinnerArray = new String[uploadRootCategories.size()];
- for (int i = 0; i < uploadRootCategories.size(); ++i) {
- tmpSpinnerArray[i] = uploadRootCategories.get(i).getCategoryTitle();
+ public static 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,
+ 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;
}
- ArrayAdapter spinnerArrayAdapter = new ArrayAdapter<>(BaseApplication.getInstance().getApplicationContext(),
- R.layout.spinner_item, tmpSpinnerArray);
- spinnerArrayAdapter.setDropDownViewResource(R.layout.spinner_dropdown_item);
- rootCategorySpinner.setAdapter(spinnerArrayAdapter);
+ 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;
+ }
- //Sets bundle selection
- if (bundleCategory != null) {
- int bundleSelectionIndex = -1, currentIndex = 0;
+ @Override
+ protected void onPreExecute() {
+ assert weakActivity != null;
+ Toast.makeText(weakActivity.get(), "Zipping files", Toast.LENGTH_SHORT).show();
+ }
- for (UploadCategory category : uploadRootCategories) {
- if (bundleCategory.get(0).contains(category.getCategoryTitle())) {
- bundleSelectionIndex = currentIndex;
- break;
- }
- ++currentIndex;
+ @Override
+ protected Boolean doInBackground(Uri... filesToZip) {
+ if (weakActivity == null || zipFilename == null)
+ return false;
+
+ File zipFile = UploadsHelper.createZipFile(zipFilename);
+
+ if (zipFile == null)
+ return false;
+
+ zipFileUri = FileProvider.getUriForFile(weakActivity.get(),
+ weakActivity.get().getPackageName() +
+ ".provider", zipFile);
+
+ UploadsHelper.zip(weakActivity.get(), filesToZip, zipFileUri);
+ return true;
+ }
+
+ @Override
+ protected void onPostExecute(Boolean result) {
+ if (weakActivity == null)
+ return;
+
+ if (!result) {
+ Toast.makeText(weakActivity.get(), "Couldn't create zip!", Toast.LENGTH_SHORT).show();
+ return;
}
- if (bundleSelectionIndex != -1) {
- rootCategorySpinner.setSelection(bundleSelectionIndex, true);
- bundleCategory.remove(0);
+ 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();
}
}
}
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 f9fd9407..ffa6f26e 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
@@ -11,23 +11,27 @@ import android.widget.LinearLayout;
import android.widget.RadioGroup;
import android.widget.Toast;
+import androidx.annotation.Nullable;
+
import java.util.Calendar;
-import androidx.annotation.Nullable;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.widget.Toolbar;
import gr.thmmy.mthmmy.R;
+import gr.thmmy.mthmmy.base.BaseActivity;
import timber.log.Timber;
-public class UploadFieldsBuilderActivity extends AppCompatActivity {
- static final String BUNDLE_UPLOAD_FIELD_BUILDER_COURSE = "UPLOAD_FIELD_BUILDER_COURSE";
+public class UploadFieldsBuilderActivity extends BaseActivity {
+
+ static final String BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_NAME = "BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_NAME";
+ static final String BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_MINIFIED_NAME = "BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_MINIFIED_NAME";
+ static final String BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_GREEKLISH_NAME = "BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_GREEKLISH_NAME";
static final String BUNDLE_UPLOAD_FIELD_BUILDER_SEMESTER = "UPLOAD_FIELD_BUILDER_SEMESTER";
static final String RESULT_FILENAME = "RESULT_FILENAME";
static final String RESULT_TITLE = "RESULT_TITLE";
static final String RESULT_DESCRIPTION = "RESULT_DESCRIPTION";
- private String course, semester;
+ private String courseName, courseMinifiedName, courseGreeklishName, semester;
+ private boolean isValidYear;
private LinearLayout semesterChooserLinear;
private RadioGroup typeRadio, semesterRadio;
@@ -38,32 +42,26 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
String working = s.toString();
- boolean isValid;
if (working.length() == 4) {
int currentYear = Calendar.getInstance().get(Calendar.YEAR);
int inputYear = Integer.parseInt(working);
- isValid = inputYear <= currentYear && inputYear > 2000;
- } else {
- isValid = false;
- }
+ isValidYear = inputYear <= currentYear && inputYear > 1980;
+ } else
+ isValidYear = false;
- if (!isValid) {
+ if (!isValidYear)
year.setError("Please enter a valid year");
- } else {
+ else
year.setError(null);
- }
-
}
@Override
- public void afterTextChanged(Editable s) {
- }
+ public void afterTextChanged(Editable s) { }
@Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
};
@Override
@@ -73,9 +71,11 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
Bundle extras = getIntent().getExtras();
if (extras != null) {
- course = extras.getString(BUNDLE_UPLOAD_FIELD_BUILDER_COURSE);
+ courseName = extras.getString(BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_NAME);
+ courseMinifiedName = extras.getString(BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_MINIFIED_NAME);
+ courseGreeklishName = extras.getString(BUNDLE_UPLOAD_FIELD_BUILDER_COURSE_GREEKLISH_NAME);
semester = extras.getString(BUNDLE_UPLOAD_FIELD_BUILDER_SEMESTER);
- if (course == null || course.equals("") || semester == null || semester.equals("")) {
+ if (courseName == null || courseName.equals("") || semester == null || semester.equals("")) {
Toast.makeText(this, "Something went wrong!", Toast.LENGTH_SHORT).show();
Timber.e("Bundle came empty in %s", UploadFieldsBuilderActivity.class.getSimpleName());
@@ -86,7 +86,7 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
}
//Initialize toolbar
- Toolbar toolbar = findViewById(R.id.toolbar);
+ toolbar = findViewById(R.id.toolbar);
toolbar.setTitle(R.string.upload_fields_builder_toolbar_title);
setSupportActionBar(toolbar);
if (getSupportActionBar() != null) {
@@ -94,6 +94,9 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
getSupportActionBar().setDisplayShowHomeEnabled(true);
}
+ createDrawer();
+ drawer.setSelection(UPLOAD_ID, false);
+
semesterChooserLinear = findViewById(R.id.upload_fields_builder_choose_semester);
semesterRadio = findViewById(R.id.upload_fields_builder_semester_radio_group);
semesterRadio.check(Integer.parseInt(semester) % 2 == 0
@@ -121,8 +124,8 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
} else if (semesterChooserLinear.getVisibility() == View.VISIBLE && semesterId == -1) {
Toast.makeText(view.getContext(), "Please choose a semester for the upload", Toast.LENGTH_SHORT).show();
return;
- } else if (year.getText().toString().isEmpty()) {
- Toast.makeText(view.getContext(), "Please choose a year for the upload", Toast.LENGTH_SHORT).show();
+ } else if (year.getText().toString().isEmpty() || !isValidYear) {
+ Toast.makeText(view.getContext(), "Please choose a valid year for the upload", Toast.LENGTH_SHORT).show();
return;
}
@@ -139,11 +142,11 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
private String buildFilename() {
switch (typeRadio.getCheckedRadioButtonId()) {
case R.id.upload_fields_builder_radio_button_exams:
- return getGreeklishCourseName() + "_" + getGreeklishPeriod() + "_" + year.getText().toString();
+ return courseGreeklishName + "_" + getGreeklishPeriod() + "_" + year.getText().toString();
case R.id.upload_fields_builder_radio_button_exam_solutions:
- return getGreeklishCourseName() + "_" + getGreeklishPeriod() + "_" + year.getText().toString() + "_Lyseis";
+ return courseGreeklishName + "_" + getGreeklishPeriod() + "_" + year.getText().toString() + "_Lyseis";
case R.id.upload_fields_builder_radio_button_notes:
- return getGreeklishCourseName() + "_" + year.getText().toString() + "_Shmeiwseis";
+ return courseGreeklishName + "_" + year.getText().toString() + "_Simeioseis";
default:
return null;
}
@@ -153,11 +156,11 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
private String buildTitle() {
switch (typeRadio.getCheckedRadioButtonId()) {
case R.id.upload_fields_builder_radio_button_exams:
- return getMinifiedCourseName() + " - " + "Θέματα εξετάσεων " + getPeriod() + " " + year.getText().toString();
+ return courseMinifiedName + " - " + "Θέματα εξετάσεων " + getPeriod() + " " + year.getText().toString();
case R.id.upload_fields_builder_radio_button_exam_solutions:
- return getMinifiedCourseName() + " - " + "Λύσεις θεμάτων " + getPeriod() + " " + year.getText().toString();
+ return courseMinifiedName + " - " + "Λύσεις θεμάτων " + getPeriod() + " " + year.getText().toString();
case R.id.upload_fields_builder_radio_button_notes:
- return getMinifiedCourseName() + " - " + "Σημειώσεις παραδόσεων " + year.getText().toString();
+ return courseMinifiedName + " - " + "Σημειώσεις παραδόσεων " + year.getText().toString();
default:
return null;
}
@@ -166,11 +169,11 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
private String buildDescription() {
switch (typeRadio.getCheckedRadioButtonId()) {
case R.id.upload_fields_builder_radio_button_exams:
- return "Θέματα εξετάσεων " + getPeriod() + " " + year.getText().toString() + " του μαθήματος \"" + course + "\"";
+ return "Θέματα εξετάσεων " + getPeriod() + " " + year.getText().toString() + " του μαθήματος \"" + courseName + "\"";
case R.id.upload_fields_builder_radio_button_exam_solutions:
- return "Λύσεις των θεμάτων των εξετάσεων " + getPeriod() + " " + year.getText().toString() + " του μαθήματος \"" + course + "\"";
+ return "Λύσεις των θεμάτων των εξετάσεων " + getPeriod() + " " + year.getText().toString() + " του μαθήματος \"" + courseName + "\"";
case R.id.upload_fields_builder_radio_button_notes:
- return "Σημειώσεις των παραδόσεων του μαθήματος \"" + course + "\" από το " + year.getText().toString();
+ return "Σημειώσεις των παραδόσεων του μαθήματος \"" + courseName + "\" από το " + year.getText().toString();
default:
return null;
}
@@ -201,316 +204,4 @@ public class UploadFieldsBuilderActivity extends AppCompatActivity {
return null;
}
}
-
- @Nullable
- private String getGreeklishCourseName() {
- return getGreeklishOrMinifiedCourseName(true);
- }
-
- @Nullable
- private String getMinifiedCourseName() {
- return getGreeklishOrMinifiedCourseName(false);
- }
-
-
- @Nullable
- private String getGreeklishOrMinifiedCourseName(boolean greeklish) {
- if (course.contains("Ψηφιακή Επεξεργασία Σήματος")) {
- return greeklish ? "PSES" : "ΨΕΣ";
- } else if (course.contains("Ψηφιακή Επεξεργασία Εικόνας")) {
- return greeklish ? "psee" : "ΨΕΕ";
- } else if (course.contains("Ψηφιακές Τηλεπικοινωνίες ΙΙ")) {
- return greeklish ? "pshf_thlep_II" : "Ψηφιακές Τηλεπ. 2";
- } else if (course.contains("Ψηφιακές Τηλεπικοινωνίες Ι")) {
- return greeklish ? "pshf_thlep_I" : "Ψηφιακές Τηλεπ. 1";
- } else if (course.contains("Ψηφιακά Φίλτρα")) {
- return greeklish ? "filtra" : "Φίλτρα";
- } else if (course.contains("Ψηφιακά Συστήματα ΙΙΙ")) {
- return greeklish ? "pshfiaka_III" : "Ψηφιακά 3";
- } else if (course.contains("Ψηφιακά Συστήματα ΙΙ")) {
- return greeklish ? "pshfiaka_II" : "Ψηφιακά 2";
- } else if (course.contains("Ψηφιακά Συστήματα Ι")) {
- return greeklish ? "pshfiaka_I" : "Ψηφιακά 1";
- } else if (course.contains("Φωτονική Τεχνολογία")) {
- return greeklish ? "fwtonikh" : "Φωτονική";
- } else if (course.contains("Φυσική Ι")) {
- return greeklish ? "fysikh_I" : "Φυσική 1";
- } else if (course.contains("Υψηλές Τάσεις ΙΙΙ")) {
- return greeklish ? "ypshles_III" : "Υψηλές 3";
- } else if (course.contains("Υψηλές Τάσεις ΙΙ")) {
- return greeklish ? "ypshles_II" : "Υψηλές 2";
- } else if (course.contains("Υψηλές Τάσεις Ι")) {
- return greeklish ? "ypshles_I" : "Υψηλές 1";
- } else if (course.contains("Υψηλές Τάσεις 4")) {
- return greeklish ? "ypshles_IV" : "Υψηλές 4";
- } else if (course.contains("Υπολογιστικός Ηλεκτρομαγνητισμός")) {
- return greeklish ? "ypologistikos_HM" : "Υπολογιστικός Η/Μ";
- } else if (course.contains("Υπολογιστικές Μέθοδοι στα Ενεργειακά Συστήματα")) {
- return greeklish ? "ymes" : "ΥΜΕΣ";
- } else if (course.contains("Τηλεπικοινωνιακή Ηλεκτρονική")) {
- return greeklish ? "tilep_ilektr" : "Τηλεπ. Ηλεκτρ.";
- } else if (course.contains("Τηλεοπτικά Συστήματα")) {
- return greeklish ? "tileoptika" : "Τηλεοπτικά";
- } else if (course.contains("Τεχνολογία Λογισμικού")) {
- return greeklish ? "SE" : "Τεχνολογία Λογισμικού";
- } else if (course.contains("Τεχνολογία Ηλεκτροτεχνικών Υλικών")) {
- return greeklish ? "Hlektrotexnika_Ylika" : "Ηλεκτροτεχνικά Υλικά";
- } else if (course.contains("Τεχνολογία Ήχου και Εικόνας")) {
- return greeklish ? "texn_hxoy_eikonas" : "Τεχνολογία Ήχου και Εικόνας";
- } else if (course.contains("Τεχνική Μηχανική")) {
- return greeklish ? "texn_mhxan" : "Τεχν. Μηχαν.";
- } else if (course.contains("Τεχνικές μη Καταστρεπτικών Δοκιμών")) {
- return greeklish ? "non_destructive_tests" : "Μη Καταστρεπτικές Δοκιμές";
- } else if (course.contains("Τεχνικές Σχεδίασης με Η/Υ")) {
- return greeklish ? "sxedio" : "Σχέδιο";
- } else if (course.contains("Τεχνικές Κωδικοποίησης")) {
- return greeklish ? "texn_kwdikopoihshs" : "Τεχνικές Κωδικοποίησης";
- } else if (course.contains("Τεχνικές Βελτιστοποίησης")) {
- return greeklish ? "veltistopoihsh" : "Βελτιστοποίηση";
- } else if (course.contains("Σύνθεση Τηλεπικοινωνιακών Διατάξεων")) {
- return greeklish ? "synth_thlep_diataksewn" : "Σύνθεση Τηλεπ. Διατάξεων";
- } else if (course.contains("Σύνθεση Ενεργών και Παθητικών Κυκλωμάτων")) {
- return greeklish ? "synthesh" : "Σύνθεση";
- } else if (course.contains("Σχεδίαση Συστημάτων VLSI")) {
- return greeklish ? "VLSI" : "VLSI";
- } else if (course.contains("Συστήματα Υπολογιστών (Υπολογιστικά Συστήματα)")) {
- return greeklish ? "sys_ypologistwn" : "Συσ. Υπολογιστών";
- } else if (course.contains("Συστήματα Πολυμέσων και Εικονική Πραγματικότητα")) {
- return greeklish ? "polymesa" : "Πολυμέσα";
- } else if (course.contains("Συστήματα Μικροϋπολογιστών")) {
- return greeklish ? "mikro_I" : "Μίκρο 1";
- } else if (course.contains("Συστήματα Ηλεκτροκίνησης")) {
- return greeklish ? "hlektrokinhsh" : "Ηλεκτροκίνηση";
- } else if (course.contains("Συστήματα Ηλεκτρικής Ενέργειας ΙΙΙ")) {
- return greeklish ? "SHE_III" : "ΣΗΕ 3";
- } else if (course.contains("Συστήματα Ηλεκτρικής Ενέργειας ΙΙ")) {
- return greeklish ? "SHE_II" : "ΣΗΕ 2";
- } else if (course.contains("Συστήματα Ηλεκτρικής Ενέργειας Ι")) {
- return greeklish ? "SHE_I" : "ΣΗΕ 1";
- } else if (course.contains("Συστήματα Αυτομάτου Ελέγχου ΙΙI")) {
- return greeklish ? "SAE_III" : "ΣΑΕ 3";
- } else if (course.contains("Συστήματα Αυτομάτου Ελέγχου ΙΙ")) {
- return greeklish ? "SAE_II" : "ΣΑΕ 2";
- } else if (course.contains("Συστήματα Αυτομάτου Ελέγχου Ι")) {
- return greeklish ? "SAE_1" : "ΣΑΕ 1";
- } else if (course.contains("Στοχαστικό Σήμα")) {
- return greeklish ? "stox_shma" : "Στοχ. Σήμα";
- } else if (course.contains("Σταθμοί Παραγωγής Ηλεκτρικής Ενέργειας")) {
- return greeklish ? "SPHE" : "ΣΠΗΕ";
- } else if (course.contains("Σερβοκινητήρια Συστήματα")) {
- return greeklish ? "servo" : "Σέρβο";
- } else if (course.contains("Σήματα και Συστήματα")) {
- return greeklish ? "analog_shma" : "Σύματα & Συστήματα";
- } else if (course.contains("Ρομποτική")) {
- return greeklish ? "rompotikh" : "Ρομποτική";
- } else if (course.contains("Προσομοίωση και Μοντελοποίηση Συστημάτων")) {
- return greeklish ? "montelopoihsh" : "Μοντελοποίηση";
- } else if (course.contains("Προηγμένες Τεχνικές Επεξεργασίας Σήματος")) {
- return greeklish ? "ptes" : "ΠΤΕΣ";
- } else if (course.contains("Προγραμματιστικές Τεχνικές")) {
- return greeklish ? "cpp" : "Προγραμματ. Τεχν.";
- } else if (course.contains("Προγραμματιζόμενα Κυκλώματα ASIC")) {
- return greeklish ? "asic" : "ASIC";
- } else if (course.contains("Παράλληλα και Κατανεμημένα Συστήματα")) {
- return greeklish ? "parallhla" : "Παράλληλα";
- } else if (course.contains("Οργάνωση και Διοίκηση Εργοστασίων")) {
- return greeklish ? "organ_dioik_ergostasiwn" : "Οργάνωση και Διοίκηση Εργοστασίων";
- } else if (course.contains("Οργάνωση Υπολογιστών")) {
- return greeklish ? "org_ypol" : "Οργάνωση Υπολ.";
- } else if (course.contains("Οπτική ΙΙ")) {
- return greeklish ? "optikh_II" : "Οπτική 2";
- } else if (course.contains("Οπτική Ι")) {
- return greeklish ? "optikh_I" : "Οπτική 1";
- } else if (course.contains("Οπτικές Επικοινωνίες")) {
- return greeklish ? "optikes_thlep" : "Οπτικές Τηλεπ.";
- } else if (course.contains("Μικροκύματα II")) {
- return greeklish ? "mikrokymata_II" : "Μικροκύματα 2";
- } else if (course.contains("Μικροκύματα I")) {
- return greeklish ? "mikrokymata_I" : "Μικροκύματα 1";
- } else if (course.contains("Μικροκυματική Τηλεπισκόπηση")) {
- return greeklish ? "thlepiskophsh" : "Τηλεπισκόπηση";
- } else if (course.contains("Μικροεπεξεργαστές και Περιφερειακά")) {
- return greeklish ? "mikro_II" : "Μίκρο 2";
- } else if (course.contains("Μετάδοση Θερμότητας")) {
- return greeklish ? "metadosi_therm" : "Μετάδοση Θερμ.";
- } else if (course.contains("Λογισμός ΙΙ")) {
- return greeklish ? "logismos_II" : "Λογισμός 2";
- } else if (course.contains("Λογισμός Ι")) {
- return greeklish ? "logismos_I" : "Λογισμός 1";
- } else if (course.contains("Λογική Σχεδίαση")) {
- return greeklish ? "logiki_sxediash" : "Λογική Σχεδίαση";
- } else if (course.contains("Λειτουργικά Συστήματα")) {
- return greeklish ? "OS" : "Λειτουργικά";
- } else if (course.contains("Κινητές και Δορυφορικές Επικοινωνίες")) {
- return greeklish ? "kinhtes_doryforikes_epik" : "Κινητές & Δορυφορικές Επικοινωνίες";
- } else if (course.contains("Κβαντική Φυσική")) {
- return greeklish ? "kvantikh" : "Κβαντική";
- } else if (course.contains("Θεωρία και Τεχνολογία Πυρηνικών Αντιδραστήρων")) {
- return greeklish ? "texn_antidrasthrwn" : "Τεχνολογία Αντιδραστήρων";
- } else if (course.contains("Θεωρία Υπολογισμών και Αλγορίθμων")) {
- return greeklish ? "thya" : "ΘΥΑ";
- } else if (course.contains("Θεωρία Σκέδασης")) {
- return greeklish ? "skedash" : "Σκέδαση";
- } else if (course.contains("Θεωρία Σημάτων και Γραμμικών Συστημάτων")) {
- return greeklish ? "analog_shma" : "Σύματα & Συστήματα";
- } else if (course.contains("Θεωρία Πληροφοριών")) {
- return greeklish ? "theoria_plir" : "Θεωρία Πληρ.";
- } else if (course.contains("Θεωρία Πιθανοτήτων και Στατιστική")) {
- return greeklish ? "pithanothtes" : "Πιθανότητες";
- } else if (course.contains("Ημιαγωγά Υλικά: Θεωρία-Διατάξεις")) {
- return greeklish ? "Hmiagwga_Ylika" : "Ημιαγωγά Υλικά";
- } else if (course.contains("Ηλεκτρονική ΙΙΙ")) {
- return greeklish ? "hlektronikh_III" : "Ηλεκτρονική 3";
- } else if (course.contains("Ηλεκτρονική ΙΙ")) {
- return greeklish ? "hlektronikh_2" : "Ηλεκτρονική 2";
- } else if (course.contains("Ηλεκτρονική Ι")) {
- return greeklish ? "hlektronikh_1" : "Ηλεκτρονική 1";
- } else if (course.contains("Ηλεκτρονικές Διατάξεις και Μετρήσεις")) {
- return greeklish ? "hlektron_diatakseis_metrhseis" : "Ηλεκτρονικές Διατάξεις και Μετρήσεις";
- } else if (course.contains("Ηλεκτρονικά Ισχύος ΙΙ")) {
- return greeklish ? "isxyos_II" : "Ισχύος 2";
- } else if (course.contains("Ηλεκτρονικά Ισχύος Ι")) {
- return greeklish ? "isxyos_I" : "Ισχύος 1";
- } else if (course.contains("Ηλεκτρομαγνητικό Πεδίο ΙΙ")) {
- return greeklish ? "pedio_II" : "Πεδίο 2";
- } else if (course.contains("Ηλεκτρομαγνητικό Πεδίο Ι")) {
- return greeklish ? "pedio_I" : "Πεδίο 1";
- } else if (course.contains("Ηλεκτρομαγνητική Συμβατότητα")) {
- return greeklish ? "HM_symvatothta" : "H/M Συμβατότητα";
- } else if (course.contains("Ηλεκτρολογικά Υλικά")) {
- return greeklish ? "ylika" : "Ηλεκτρ. Υλικά";
- } else if (course.contains("Ηλεκτρική Οικονομία")) {
- return greeklish ? "hlektr_oikonomia" : "Ηλεκτρική Οικονομία";
- } else if (course.contains("Ηλεκτρικές Μηχανές Γ'")) {
- return greeklish ? "mhxanes_C" : "Μηχανές Γ";
- } else if (course.contains("Ηλεκτρικές Μηχανές Β'")) {
- return greeklish ? "mhxanes_B" : "Μηχανές Β";
- } else if (course.contains("Ηλεκτρικές Μηχανές Α'")) {
- return greeklish ? "mhxanes_A" : "Μηχανές Α";
- } else if (course.contains("Ηλεκτρικές Μετρήσεις ΙΙ")) {
- return greeklish ? "metrhseis_II" : "Μετρήσεις 2";
- } else if (course.contains("Ηλεκτρικές Μετρήσεις Ι")) {
- return greeklish ? "metrhseis_1" : "Μετρήσεις 1";
- } else if (course.contains("Ηλεκτρικά Κυκλώματα ΙΙΙ")) {
- return greeklish ? "kyklwmata_I" : "Κυκλώματα 3";
- } else if (course.contains("Ηλεκτρικά Κυκλώματα ΙΙ")) {
- return greeklish ? "kyklwmata_II" : "Κυκλώματα 2";
- } else if (course.contains("Ηλεκτρικά Κυκλώματα Ι")) {
- return greeklish ? "kyklwmata_I" : "Κυκλώματα 1";
- } else if (course.contains("Ηλεκτρακουστική ΙΙ")) {
- return greeklish ? "hlektroakoystikh_II" : "Ηλεκτροακουστική 2";
- } else if (course.contains("Ηλεκτρακουστική Ι")) {
- return greeklish ? "hlektroakoystikh_I" : "Ηλεκτροακουστική 1";
- } else if (course.contains("Εφαρμοσμένη Θερμοδυναμική")) {
- return greeklish ? "thermodynamikh" : "Θερμοδυναμική";
- } else if (course.contains("Εφαρμοσμένα Μαθηματικά ΙΙ")) {
- return greeklish ? "efarmosmena_math_II" : "Εφαρμοσμένα 2";
- } else if (course.contains("Εφαρμοσμένα Μαθηματικά Ι")) {
- return greeklish ? "efarmosmena_math_I" : "Εφαρμοσμένα 1";
- } else if (course.contains("Εφαρμογές Τηλεπικοινωνιακών Διατάξεων")) {
- return greeklish ? "efarm_thlep_diataksewn" : "Εφαρμογές Τηλεπ. Διατάξεων";
- } else if (course.contains("Ευφυή Συστήματα Ρομπότ")) {
- return greeklish ? "eufuh" : "Ευφυή";
- } else if (course.contains("Ευρυζωνικά Δίκτυα")) {
- return greeklish ? "eyryzwnika" : "Ευρυζωνικά";
- } else if (course.contains("Επιχειρησιακή Έρευνα")) {
- return greeklish ? "epixeirisiaki" : "Επιχειρησιακή Έρευνα";
- } else if (course.contains("Ενσωματωμένα Συστήματα Πραγματικού Χρόνου")) {
- return greeklish ? "enswmatwmena" : "Ενσωματωμένα";
- } else if (course.contains("Εισαγωγή στις εφαρμογές Πυρηνικής Τεχνολογίας")) {
- return greeklish ? "Intro_Purhnikh_Texn" : "Εισ. Πυρηνικη Τεχν.";
- } else if (course.contains("Εισαγωγή στην Πολιτική Οικονομία")) {
- return greeklish ? "polit_oik" : "Πολιτική Οικονομία";
- } else if (course.contains("Εισαγωγή στην Ενεργειακή Τεχνολογία ΙΙ")) {
- return greeklish ? "EET_2" : "ΕΕΤ2";
- } else if (course.contains("Εισαγωγή στην Ενεργειακή Τεχνολογία Ι")) {
- return greeklish ? "EET_I" : "ΕΕΤ 1";
- } else if (course.contains("Ειδικές Κεραίες, Σύνθεση Κεραιών")) {
- return greeklish ? "eidikes_keraies" : "Ειδικές Κεραίες, Σύνθεση Κεραιών";
- } else if (course.contains("Ειδικές Αρχιτεκτονικές Υπολογιστών")) {
- return greeklish ? "eidikes_arx_ypolog" : "Ειδικές Αρχιτεκτονικές Υπολογιστών";
- } else if (course.contains("Ειδικά Κεφάλαια Συστημάτων Ηλεκτρικής Ενέργειας")) {
- return greeklish ? "ekshe" : "ΕΚΣΗΕ";
- } else if (course.contains("Ειδικά Κεφάλαια Ηλεκτρομαγνητικού Πεδίου Ι")) {
- return greeklish ? "eidika_kef_HM_pedioy_I" : "Ειδικά Κεφάλαια Ηλεκτρομαγνητικού Πεδίου Ι";
- } else if (course.contains("Ειδικά Κεφάλαια Διαφορικών Εξισώσεων")) {
- return greeklish ? "eidika_kef_diaf_eksis" : "Ειδικά Κεφάλαια Διαφορικών Εξισώσεων";
- } else if (course.contains("Δομημένος Προγραμματισμός")) {
- return greeklish ? "C" : "Δομ. Προγραμμ.";
- } else if (course.contains("Δομές Δεδομένων")) {
- return greeklish ? "dom_dedomenwn" : "Δομ. Δεδομ.";
- } else if (course.contains("Διαχείριση Συστημάτων Ηλεκτρικής Ενέργειας")) {
- return greeklish ? "dshe" : "ΔΣΗΕ";
- } else if (course.contains("Διαφορικές Εξισώσεις")) {
- return greeklish ? "diaforikes" : "Διαφορικές";
- } else if (course.contains("Διανεμημένη Παραγωγή")) {
- return greeklish ? "dian_paragwgh" : "Διανεμημένη Παραγωγή";
- } else if (course.contains("Διακριτά μαθηματικά")) {
- return greeklish ? "diakrita" : "Διακριτά Μαθηματικά";
- } else if (course.contains("Διακριτά Μαθηματικά")) {
- return greeklish ? "diakrita" : "Διακριτά Μαθηματικά";
- } else if (course.contains("Διάδοση Ηλεκτρομαγνητικού Κύματος Ι (πρώην Πεδίο ΙΙΙ)")) {
- return greeklish ? "diadosi_1" : "Διάδοση 1";
- } else if (course.contains("Διάδοση Η/Μ Κύματος ΙΙ")) {
- return greeklish ? "diadosi_II" : "Διάδοση 2";
- } else if (course.contains("Δίκτυα Υπολογιστών ΙΙ")) {
- return greeklish ? "diktya_II" : "Δίκτυα 2";
- } else if (course.contains("Δίκτυα Υπολογιστών Ι")) {
- return greeklish ? "diktya_I" : "Δίκτυα 1";
- } else if (course.contains("Δίκτυα Τηλεπικοινωνιών")) {
- return greeklish ? "diktya_thlep" : "Δίκτυα Τηλέπ.";
- } else if (course.contains("Γραφική με Υπολογιστές")) {
- return greeklish ? "grafikh" : "Γραφική";
- } else if (course.contains("Γραμμική Άλγεβρα")) {
- return greeklish ? "grammikh_algebra" : "Γραμμ. Άλγεβρ.";
- } else if (course.contains("Γεωηλεκτρομαγνητισμός")) {
- return greeklish ? "geohlektromagnitismos" : "Γεωηλεκτρομαγνητισμός";
- } else if (course.contains("Βιοϊατρική Τεχνολογία")) {
- return greeklish ? "vioiatriki" : "Βιοιατρική";
- } else if (course.contains("Βιομηχανική Πληροφορική")) {
- return greeklish ? "viomix_plir" : "Βιομηχανική Πληρ";
- } else if (course.contains("Βιομηχανικά Ηλεκτρονικά")) {
- return greeklish ? "bhomix_hlektronika" : "Βιομηχανικά Ηλεκτρονικά";
- } else if (course.contains("Βάσεις Δεδομένων")) {
- return greeklish ? "vaseis" : "Βάσεις";
- } else if (course.contains("Ασύρματος Τηλεπικοινωνία ΙΙ")) {
- return greeklish ? "asyrmatos_II" : "Ασύρματος 2";
- } else if (course.contains("Ασύρματος Τηλεπικοινωνία Ι")) {
- return greeklish ? "asyrmatos_I" : "Ασύρματος 1";
- } else if (course.contains("Ασφάλεια Πληροφοριακών Συστημάτων")) {
- return greeklish ? "asfaleia" : "Ασφάλεια";
- } else if (course.contains("Ασαφή Συστήματα")) {
- return greeklish ? "asafh" : "Ασαφή";
- } else if (course.contains("Αρχιτεκτονική Υπολογιστών")) {
- return greeklish ? "arx_ypologistwn" : "Αρχ. Υπολογιστών";
- } else if (course.contains("Αρχές Παράλληλης Επεξεργασίας")) {
- return greeklish ? "arxes_parall_epeksergasias" : "Αρχές Παράλληλης Επεξεργασίας";
- } else if (course.contains("Αρχές Οικονομίας")) {
- return greeklish ? "arx_oikonomias" : "Αρχές Οικονομίας";
- } else if (course.contains("Αριθμητική Ανάλυση")) {
- return greeklish ? "arith_anal" : "Αριθμ. Ανάλυση";
- } else if (course.contains("Αξιοπιστία Συστημάτων")) {
- return greeklish ? "aksiopistia_systhmatwn" : "Αξιοπιστία Συστημάτων";
- } else if (course.contains("Αντικειμενοστραφής Προγραμματισμός")) {
- return greeklish ? "OOP" : "Αντικειμενοστραφής";
- } else if (course.contains("Αναλογικές Τηλεπικοινωνίες (πρώην Τηλεπικοινωνιακά Συστήματα Ι)")) {
- return greeklish ? "anal_thlep" : "Αναλογικές Τηλεπ.";
- } else if (course.contains("Αναγνώριση Προτύπων")) {
- return greeklish ? "protipa" : "Αναγνώριση Προτύπων";
- } else if (course.contains("Ανάλυση και Σχεδίαση Αλγορίθμων")) {
- return greeklish ? "algorithms" : "Αλγόριθμοι";
- } else if (course.contains("Ανάλυση Χρονοσειρών")) {
- return greeklish ? "xronoseires" : "Χρονοσειρές";
- } else if (course.contains("Ανάλυση Συστημάτων Ηλεκτρικής Ενέργειας")) {
- return greeklish ? "ASHE" : "ΑΣΗΕ";
- } else if (course.contains("Ανάλυση Ηλεκτρικών Κυκλωμάτων με Υπολογιστή")) {
- return greeklish ? "analysh_hlektr_kykl" : "Ανάλυση Ηλεκτρικ. Κυκλ. με Υπολογιστή";
- } else if (course.contains("Ακουστική ΙΙ")) {
- return greeklish ? "akoystikh_II" : "Ακουστική 2";
- } else if (course.contains("Ακουστική Ι")) {
- return greeklish ? "akoystikh_I" : "Ακουστική 1";
- } else {
- return null;
- }
- }
}
\ No newline at end of file
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
new file mode 100644
index 00000000..8fe986c9
--- /dev/null
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsCourse.java
@@ -0,0 +1,77 @@
+package gr.thmmy.mthmmy.activities.upload;
+
+import android.os.Bundle;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import gr.thmmy.mthmmy.base.BaseApplication;
+import timber.log.Timber;
+
+class UploadsCourse {
+ private String name;
+ private String minifiedName;
+ private String greeklishName;
+
+ private UploadsCourse(String fullName, String minifiedName, String greeklishName) {
+ this.name = fullName;
+ this.minifiedName = minifiedName;
+ this.greeklishName = greeklishName;
+ }
+
+ String getName() {
+ return name;
+ }
+
+ String getMinifiedName() {
+ return minifiedName;
+ }
+
+ String getGreeklishName() {
+ return greeklishName;
+ }
+
+ static Map generateUploadsCourses(String[] uploadsCoursesRes){
+ Map uploadsCourses = new HashMap<>();
+ for(String uploadsCourseStr:uploadsCoursesRes) {
+ String[] split = uploadsCourseStr.split(":");
+ UploadsCourse uploadsCourse = new UploadsCourse(split[0], split[1], split[2]);
+ uploadsCourses.put(uploadsCourse.getName(),uploadsCourse);
+ }
+ return uploadsCourses;
+ }
+
+ static UploadsCourse findCourse(String retrievedCourse,
+ Map uploadsCourses){
+ retrievedCourse = normalizeGreekNumbers(retrievedCourse);
+ UploadsCourse uploadsCourse = uploadsCourses.get(retrievedCourse);
+ if(uploadsCourse != null) return 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(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);
+ }
+
+ return uploadsCourses.get(foundKey);
+ }
+
+ private static String normalizeGreekNumbers(String stringWithGreekNumbers) {
+ StringBuilder normalizedStrBuilder = new StringBuilder(stringWithGreekNumbers);
+ Pattern pattern = Pattern.compile("(Ι+)(?:\\s|\\(|\\)|$)");
+ Matcher matcher = pattern.matcher(stringWithGreekNumbers);
+ while (matcher.find())
+ normalizedStrBuilder.replace(matcher.start(1), matcher.end(1), matcher.group(1).replaceAll("Ι", "I"));
+ return normalizedStrBuilder.toString();
+ }
+}
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsHelper.java b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsHelper.java
index e5f5301b..92b71cb0 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsHelper.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadsHelper.java
@@ -1,71 +1,48 @@
package gr.thmmy.mthmmy.activities.upload;
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.os.Environment;
-import android.provider.OpenableColumns;
import android.widget.Toast;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.content.FileProvider;
+
+import com.snatik.storage.Storage;
+
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
-import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
+import gr.thmmy.mthmmy.utils.FileUtils;
import timber.log.Timber;
-class UploadsHelper {
- private static final int DEFAULT_MIN_WIDTH_QUALITY = 400;
- private static final String CACHE_IMAGE_NAME = "tempUploadFile.jpg";
-
- @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;
- }
+public class UploadsHelper {
+ private static final int BUFFER = 4096;
+ private static final String TEMP_FILES_DIRECTORY = "~tmp_mTHMMY_uploads";
@SuppressWarnings("ResultOfMethodCallIgnored")
@Nullable
- static String createTempFile(Context context, Uri fileUri, String newFilename) {
- String oldFilename = filenameFromUri(context, fileUri);
- String fileExtension = oldFilename.substring(oldFilename.indexOf("."));
+ static Uri createTempFile(Context context, Storage storage, Uri fileUri, String newFilename) {
+ String oldFilename = FileUtils.filenameFromUri(context, fileUri);
+ String fileExtension = oldFilename.substring(oldFilename.indexOf('.'));
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.separatorChar + "~tmp_mThmmy_uploads");
+ File.separatorChar + TEMP_FILES_DIRECTORY);
- if (!tempDirectory.exists()) {
- if (!tempDirectory.mkdirs()) {
- Timber.w("Temporary directory build returned false in %s", UploadActivity.class.getSimpleName());
- Toast.makeText(context, "Couldn't create temporary directory", Toast.LENGTH_SHORT).show();
- return null;
- }
+ if (!tempDirectory.exists() && !tempDirectory.mkdirs()) {
+ Timber.w("Temporary directory build returned false in %s", UploadActivity.class.getSimpleName());
+ Toast.makeText(context, "Couldn't create temporary directory", Toast.LENGTH_SHORT).show();
+ return null;
}
InputStream inputStream;
@@ -97,103 +74,63 @@ 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")
- static void deleteTempFiles() {
- File tempFilesDirectory = new File(Environment.getExternalStorageDirectory().getPath() +
- File.separatorChar + "~tmp_mThmmy_uploads");
-
- if (tempFilesDirectory.isDirectory()) {
- String[] tempFilesArray = tempFilesDirectory.list();
- for (String tempFile : tempFilesArray) {
- new File(tempFilesDirectory, tempFile).delete();
- }
- tempFilesDirectory.delete();
- }
- }
-
- @SuppressWarnings("ResultOfMethodCallIgnored")
- static void deleteCacheFiles(Context context) {
- File cacheFilesDirectory = context.getExternalCacheDir();
- assert cacheFilesDirectory != null;
- String[] tempFilesArray = cacheFilesDirectory.list();
- for (String tempFile : tempFilesArray) {
- new File(cacheFilesDirectory, tempFile).delete();
- }
- }
-
- /**
- * 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) {
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inSampleSize = sampleSize;
+ @Nullable
+ static File createZipFile(@NonNull String zipFilename) {
+ // Create a zip file name
+ File zipFolder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) +
+ File.separator + "mTHMMY");
- AssetFileDescriptor fileDescriptor = null;
- try {
- fileDescriptor = context.getContentResolver().openAssetFileDescriptor(theUri, "r");
- } catch (FileNotFoundException e) {
- e.printStackTrace();
+ if (!zipFolder.exists() && !zipFolder.mkdirs()) {
+ Timber.w("Zip folder build returned false in %s", UploadsHelper.class.getSimpleName());
+ return null;
}
- assert fileDescriptor != null;
- return BitmapFactory.decodeFileDescriptor(
- fileDescriptor.getFileDescriptor(), null, options);
+ return new File(zipFolder, zipFilename);
}
- static int getRotation(Context context, Uri imageUri) {
- int rotation = 0;
+ static void zip(Context context, Uri[] files, Uri zipFile) {
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;
+ BufferedInputStream origin;
+ OutputStream dest = context.getContentResolver().openOutputStream(zipFile);
+ assert dest != null;
+ ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(dest));
+ byte[] data = new byte[BUFFER];
+
+ for (Uri file : files) {
+ InputStream inputStream = context.getContentResolver().openInputStream(file);
+ assert inputStream != null;
+ origin = new BufferedInputStream(inputStream, BUFFER);
+
+ ZipEntry entry = new ZipEntry(FileUtils.filenameFromUri(context, file));
+ out.putNextEntry(entry);
+ int count;
+
+ while ((count = origin.read(data, 0, BUFFER)) != -1) {
+ out.write(data, 0, count);
+ }
+ origin.close();
}
+
+ out.close();
} catch (Exception e) {
e.printStackTrace();
}
- return rotation;
}
- 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);
+ public static void deleteTempFiles(Storage storage) {
+ File tempFilesDirectory = new File(Environment.getExternalStorageDirectory().getPath() +
+ File.separatorChar + TEMP_FILES_DIRECTORY);
+
+ if (storage.isDirectoryExists(tempFilesDirectory.getAbsolutePath())) {
+ for (File tempFile : storage.getFiles(tempFilesDirectory.getAbsolutePath())) {
+ storage.deleteFile(tempFile.getAbsolutePath());
+ }
+ storage.deleteDirectory(tempFilesDirectory.getAbsolutePath());
}
- return bm;
}
}
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 d8019545..0671b7e3 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java
@@ -2,21 +2,34 @@ package gr.thmmy.mthmmy.base;
import android.Manifest;
import android.app.ProgressDialog;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
+import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
+import android.widget.LinearLayout;
import android.widget.TextView;
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.firebase.messaging.FirebaseMessaging;
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.model.PrimaryDrawerItem;
import com.mikepenz.materialdrawer.model.ProfileDrawerItem;
+import com.snatik.storage.Storage;
+
+import net.gotev.uploadservice.UploadService;
import java.io.BufferedReader;
import java.io.File;
@@ -35,14 +51,6 @@ import java.io.IOException;
import java.io.InputStreamReader;
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.activities.AboutActivity;
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.settings.SettingsActivity;
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.ThmmyFile;
import gr.thmmy.mthmmy.services.DownloadHelper;
+import gr.thmmy.mthmmy.services.UploadsReceiver;
import gr.thmmy.mthmmy.session.SessionManager;
import gr.thmmy.mthmmy.utils.FileUtils;
import gr.thmmy.mthmmy.viewmodel.BaseViewModel;
+import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
import okhttp3.OkHttpClient;
import ru.noties.markwon.LinkResolverDef;
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.settings.SettingsActivity.DEFAULT_HOME_TAB;
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.utils.FileUtils.getMimeType;
@@ -82,6 +94,9 @@ public abstract class BaseActivity extends AppCompatActivity {
//SessionManager
protected static SessionManager sessionManager;
+ //Storage manager
+ protected Storage storage;
+
//Bookmarks
public static final String BOOKMARKS_SHARED_PREFS = "bookmarksSharedPrefs";
public static final String BOOKMARKED_TOPICS_KEY = "bookmarkedTopicsKey";
@@ -96,6 +111,9 @@ public abstract class BaseActivity extends AppCompatActivity {
//Common UI elements
protected Toolbar toolbar;
protected Drawer drawer;
+ //Uploads progress dialog
+ UploadsShowDialogReceiver uploadsShowDialogReceiver;
+ AlertDialog uploadsProgressDialog;
private MainActivity mainActivity;
private boolean isMainActivity;
@@ -123,16 +141,25 @@ public abstract class BaseActivity extends AppCompatActivity {
BaseViewModel baseViewModel = ViewModelProviders.of(this).get(BaseViewModel.class);
baseViewModel.getCurrentPageBookmark().observe(this, thisPageBookmark -> setTopicBookmark(thisPageBookmarkMenuButton));
+
+ storage = new Storage(getApplicationContext());
}
@Override
protected void onResume() {
super.onResume();
updateDrawer();
- if (!sharedPreferences.getBoolean(getString(R.string.user_consent_shared_preference_key), false) && !isUserConsentDialogShown){
- isUserConsentDialogShown=true;
+ if (!sharedPreferences.getBoolean(getString(R.string.user_consent_shared_preference_key), false) && !isUserConsentDialogShown) {
+ isUserConsentDialogShown = true;
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
@@ -140,6 +167,10 @@ public abstract class BaseActivity extends AppCompatActivity {
super.onPause();
if (drawer != null) //close drawer animation after returning to activity
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;
}
+ public Storage getStorage() {
+ return storage;
+ }
+
//TODO: move stuff below (?)
//------------------------------------------DRAWER STUFF----------------------------------------
protected static final int HOME_ID = 0;
@@ -254,14 +289,15 @@ public abstract class BaseActivity extends AppCompatActivity {
.withName(R.string.downloads)
.withIcon(downloadsIcon)
.withSelectedIcon(downloadsIconSelected);
-// uploadItem = new PrimaryDrawerItem()
-// .withTextColor(primaryColor)
-// .withSelectedColor(selectedPrimaryColor)
-// .withSelectedTextColor(selectedSecondaryColor)
-// .withIdentifier(UPLOAD_ID)
-// .withName(R.string.upload)
-// .withIcon(uploadIcon)
-// .withSelectedIcon(uploadIconSelected);
+
+ uploadItem = new PrimaryDrawerItem()
+ .withTextColor(primaryColor)
+ .withSelectedColor(selectedPrimaryColor)
+ .withSelectedTextColor(selectedSecondaryColor)
+ .withIdentifier(UPLOAD_ID)
+ .withName(R.string.upload)
+ .withIcon(uploadIcon)
+ .withSelectedIcon(uploadIconSelected);
shoutboxItem = new PrimaryDrawerItem()
.withTextColor(primaryColor)
@@ -377,11 +413,11 @@ public abstract class BaseActivity extends AppCompatActivity {
intent.putExtras(extras);
startActivity(intent);
}
-// } else if (drawerItem.equals(UPLOAD_ID)) {
-// if (!(BaseActivity.this instanceof UploadActivity)) {
-// Intent intent = new Intent(BaseActivity.this, UploadActivity.class);
-// startActivity(intent);
-// }
+ } else if (drawerItem.equals(UPLOAD_ID)) {
+ if (!(BaseActivity.this instanceof UploadActivity)) {
+ Intent intent = new Intent(BaseActivity.this, UploadActivity.class);
+ startActivity(intent);
+ }
} else if (drawerItem.equals(BOOKMARKS_ID)) {
if (!(BaseActivity.this instanceof BookmarksActivity)) {
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
{
drawer.removeItem(DOWNLOADS_ID);
-// drawer.removeItem(UPLOAD_ID);
+ drawer.removeItem(UPLOAD_ID);
loginLogoutItem.withName(R.string.login).withIcon(loginIcon); //Swap logout with login
profileDrawerItem.withName(sessionManager.getUsername());
setDefaultAvatar();
@@ -440,9 +476,9 @@ public abstract class BaseActivity extends AppCompatActivity {
if (!drawer.getDrawerItems().contains(downloadsItem)) {
drawer.addItemAtPosition(downloadsItem, 4);
}
-// if (!drawer.getDrawerItems().contains(uploadItem)) {
-// drawer.addItemAtPosition(uploadItem, 5);
-// }
+ if (!drawer.getDrawerItems().contains(uploadItem)) {
+ drawer.addItemAtPosition(uploadItem, 5);
+ }
loginLogoutItem.withName(R.string.logout).withIcon(logoutIcon); //Swap login with logout
profileDrawerItem.withName(sessionManager.getUsername());
if (sessionManager.hasAvatar())
@@ -667,10 +703,10 @@ public abstract class BaseActivity extends AppCompatActivity {
//-------------------------------------------BOOKMARKS END------------------------------------------
//-------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
- private boolean checkPerms() {
+ protected boolean checkPerms() {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,
@@ -683,13 +719,13 @@ public abstract class BaseActivity extends AppCompatActivity {
}
//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) {
String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_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
, @NonNull int[] grantResults) {
switch (permsRequestCode) {
- case PERMISSIONS_REQUEST_CODE:
- downloadFile();
+ case DOWNLOAD_REQUEST_CODE:
+ if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
+ prepareDownload(tempThmmyFile);
break;
}
}
@@ -713,7 +750,7 @@ public abstract class BaseActivity extends AppCompatActivity {
prepareDownload(thmmyFile);
else {
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();
}
+ //------------------------------------------ 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----------------------
protected void setMainActivity(MainActivity mainActivity) {
this.mainActivity = mainActivity;
diff --git a/app/src/main/java/gr/thmmy/mthmmy/base/BaseApplication.java b/app/src/main/java/gr/thmmy/mthmmy/base/BaseApplication.java
index ce4e4068..6ba7d3cc 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/base/BaseApplication.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/base/BaseApplication.java
@@ -10,6 +10,9 @@ import android.os.Bundle;
import android.util.DisplayMetrics;
import android.widget.ImageView;
+import androidx.core.content.ContextCompat;
+import androidx.preference.PreferenceManager;
+
import com.crashlytics.android.Crashlytics;
import com.crashlytics.android.core.CrashlyticsCore;
import com.franmontiel.persistentcookiejar.PersistentCookieJar;
@@ -33,8 +36,6 @@ import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
-import androidx.core.content.ContextCompat;
-import androidx.preference.PreferenceManager;
import gr.thmmy.mthmmy.BuildConfig;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.session.SessionManager;
diff --git a/app/src/main/java/gr/thmmy/mthmmy/base/BaseFragment.java b/app/src/main/java/gr/thmmy/mthmmy/base/BaseFragment.java
index 03622e83..c2c1a578 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/base/BaseFragment.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/base/BaseFragment.java
@@ -5,6 +5,7 @@ import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
+
import okhttp3.OkHttpClient;
public abstract class BaseFragment extends Fragment {
diff --git a/app/src/main/java/gr/thmmy/mthmmy/editorview/EditorView.java b/app/src/main/java/gr/thmmy/mthmmy/editorview/EditorView.java
index 3b41d07c..97f7d42f 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/editorview/EditorView.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/editorview/EditorView.java
@@ -26,16 +26,17 @@ import android.widget.PopupWindow;
import android.widget.ScrollView;
import android.widget.TextView;
-import com.google.android.material.textfield.TextInputEditText;
-import com.google.android.material.textfield.TextInputLayout;
-
-import java.util.Objects;
-
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatImageButton;
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+
+import com.google.android.material.textfield.TextInputEditText;
+import com.google.android.material.textfield.TextInputLayout;
+
+import java.util.Objects;
+
import gr.thmmy.mthmmy.R;
import timber.log.Timber;
diff --git a/app/src/main/java/gr/thmmy/mthmmy/editorview/EmojiKeyboard.java b/app/src/main/java/gr/thmmy/mthmmy/editorview/EmojiKeyboard.java
index cd57fb5e..903ff5fe 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/editorview/EmojiKeyboard.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/editorview/EmojiKeyboard.java
@@ -9,11 +9,12 @@ import android.view.MotionEvent;
import android.view.inputmethod.InputConnection;
import android.widget.LinearLayout;
-import java.util.HashSet;
-
import androidx.appcompat.widget.AppCompatImageButton;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+
+import java.util.HashSet;
+
import gr.thmmy.mthmmy.R;
public class EmojiKeyboard extends LinearLayout implements IEmojiKeyboard {
diff --git a/app/src/main/java/gr/thmmy/mthmmy/editorview/EmojiKeyboardAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/editorview/EmojiKeyboardAdapter.java
index c3b0758b..efcb9519 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/editorview/EmojiKeyboardAdapter.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/editorview/EmojiKeyboardAdapter.java
@@ -8,6 +8,7 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.AppCompatImageButton;
import androidx.recyclerview.widget.RecyclerView;
+
import gr.thmmy.mthmmy.R;
public class EmojiKeyboardAdapter extends RecyclerView.Adapter {
diff --git a/app/src/main/java/gr/thmmy/mthmmy/editorview/FormatButtonsAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/editorview/FormatButtonsAdapter.java
index 98277550..732149ae 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/editorview/FormatButtonsAdapter.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/editorview/FormatButtonsAdapter.java
@@ -7,6 +7,7 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.AppCompatImageButton;
import androidx.recyclerview.widget.RecyclerView;
+
import gr.thmmy.mthmmy.R;
public class FormatButtonsAdapter extends RecyclerView.Adapter {
diff --git a/app/src/main/java/gr/thmmy/mthmmy/model/Bookmark.java b/app/src/main/java/gr/thmmy/mthmmy/model/Bookmark.java
index ac622a64..1da43458 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/model/Bookmark.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/model/Bookmark.java
@@ -1,11 +1,11 @@
package gr.thmmy.mthmmy.model;
-import java.util.ArrayList;
-import java.util.Objects;
-
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.Objects;
+
public class Bookmark implements java.io.Serializable {
private final String title, id;
private boolean isNotificationsEnabled;
diff --git a/app/src/main/java/gr/thmmy/mthmmy/model/Post.java b/app/src/main/java/gr/thmmy/mthmmy/model/Post.java
index 0c66ba83..7e0b7f55 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/model/Post.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/model/Post.java
@@ -1,10 +1,10 @@
package gr.thmmy.mthmmy.model;
+import androidx.annotation.Nullable;
+
import java.util.ArrayList;
import java.util.Objects;
-import androidx.annotation.Nullable;
-
/**
* Class that defines a topic's post. All member variables are declared final (thus no setters are
* supplied). Class has two constructors and getter methods for all variables.
diff --git a/app/src/main/java/gr/thmmy/mthmmy/model/UploadFile.java b/app/src/main/java/gr/thmmy/mthmmy/model/UploadFile.java
new file mode 100644
index 00000000..fb5c2161
--- /dev/null
+++ b/app/src/main/java/gr/thmmy/mthmmy/model/UploadFile.java
@@ -0,0 +1,45 @@
+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;
+ }
+}
diff --git a/app/src/main/java/gr/thmmy/mthmmy/services/NotificationService.java b/app/src/main/java/gr/thmmy/mthmmy/services/NotificationService.java
index 5631b325..79c36d4b 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/services/NotificationService.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/services/NotificationService.java
@@ -13,6 +13,10 @@ import android.os.Build;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
+import androidx.annotation.RequiresApi;
+import androidx.core.app.NotificationCompat;
+import androidx.preference.PreferenceManager;
+
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
@@ -23,9 +27,6 @@ import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import androidx.annotation.RequiresApi;
-import androidx.core.app.NotificationCompat;
-import androidx.preference.PreferenceManager;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.topic.TopicActivity;
import gr.thmmy.mthmmy.base.BaseApplication;
@@ -260,10 +261,9 @@ public class NotificationService extends FirebaseMessagingService {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// Since Android Oreo notification channel is needed.
- if (buildVersion >= Build.VERSION_CODES.O){
- if (notificationManager.getNotificationChannel(CHANNEL_ID) == null)
+ if (buildVersion >= Build.VERSION_CODES.O && notificationManager.getNotificationChannel(CHANNEL_ID) == null)
notificationManager.createNotificationChannel(new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH));
- }
+
notificationManager.notify(NEW_POST_TAG, notificationId, notificationBuilder.build());
diff --git a/app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java b/app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java
new file mode 100644
index 00000000..f8b06e28
--- /dev/null
+++ b/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 android.view.View;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AlertDialog;
+
+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;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/gr/thmmy/mthmmy/session/SessionManager.java b/app/src/main/java/gr/thmmy/mthmmy/session/SessionManager.java
index 42f02375..4e91bc72 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/session/SessionManager.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/session/SessionManager.java
@@ -2,6 +2,9 @@ package gr.thmmy.mthmmy.session;
import android.content.SharedPreferences;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.franmontiel.persistentcookiejar.PersistentCookieJar;
import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor;
@@ -14,8 +17,6 @@ import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import gr.thmmy.mthmmy.utils.parsing.ParseException;
import gr.thmmy.mthmmy.utils.parsing.ParseHelpers;
import okhttp3.Cookie;
diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/AppCompatSpinnerWithoutDefault.java b/app/src/main/java/gr/thmmy/mthmmy/utils/AppCompatSpinnerWithoutDefault.java
index b64bb57d..77293dfe 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/utils/AppCompatSpinnerWithoutDefault.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/utils/AppCompatSpinnerWithoutDefault.java
@@ -10,12 +10,12 @@ import android.widget.AdapterView;
import android.widget.SpinnerAdapter;
import android.widget.TextView;
+import androidx.appcompat.widget.AppCompatSpinner;
+
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
-import androidx.appcompat.widget.AppCompatSpinner;
-
public class AppCompatSpinnerWithoutDefault extends AppCompatSpinner {
public AppCompatSpinnerWithoutDefault(Context context) {
super(context);
diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/CrashReportingTree.java b/app/src/main/java/gr/thmmy/mthmmy/utils/CrashReportingTree.java
index c4674134..8f85459a 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/utils/CrashReportingTree.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/utils/CrashReportingTree.java
@@ -27,8 +27,7 @@ public class CrashReportingTree extends DebugTree {
Crashlytics.log(level + "/" + tag + ": " + message);
- if(priority == Log.ERROR)
- {
+ if(priority == Log.ERROR) {
if (t!=null)
Crashlytics.logException(t);
else
diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/CustomLinearLayoutManager.java b/app/src/main/java/gr/thmmy/mthmmy/utils/CustomLinearLayoutManager.java
index de9abfd0..f4dd5042 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/utils/CustomLinearLayoutManager.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/utils/CustomLinearLayoutManager.java
@@ -4,6 +4,7 @@ import android.content.Context;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+
import timber.log.Timber;
public class CustomLinearLayoutManager extends LinearLayoutManager {
diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/FileUtils.java b/app/src/main/java/gr/thmmy/mthmmy/utils/FileUtils.java
index 1c8eb0b0..e1ef6f9e 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/utils/FileUtils.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/utils/FileUtils.java
@@ -1,10 +1,17 @@
package gr.thmmy.mthmmy.utils;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.OpenableColumns;
import android.webkit.MimeTypeMap;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import java.io.File;
-import androidx.annotation.NonNull;
+import gr.thmmy.mthmmy.R;
import static gr.thmmy.mthmmy.services.DownloadHelper.SAVE_DIR;
@@ -21,7 +28,94 @@ public class FileUtils {
return type;
}
- public static boolean fileNameExists (String fileName) {
+ public static boolean fileNameExists(String fileName) {
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 containing file's extension
+ * @return FontAwesome character according to file's type
+ * @see FontAwesome
+ */
+ @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);
+ }
}
diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareFABBehavior.java b/app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareFABBehavior.java
index a45e1a3f..a77502f3 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareFABBehavior.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareFABBehavior.java
@@ -4,13 +4,13 @@ import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
-import com.google.android.material.floatingactionbutton.FloatingActionButton;
-import com.google.android.material.snackbar.Snackbar;
-
import androidx.annotation.NonNull;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
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
* otherwise. It also lifts the {@link FloatingActionButton} when a {@link Snackbar} is shown.
@@ -47,7 +47,13 @@ public class ScrollAwareFABBehavior extends CoordinatorLayout.BehaviorWhen a nested ScrollView is scrolled down, the view will disappear.
diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/TakePhoto.java b/app/src/main/java/gr/thmmy/mthmmy/utils/TakePhoto.java
new file mode 100644
index 00000000..c5b4c490
--- /dev/null
+++ b/app/src/main/java/gr/thmmy/mthmmy/utils/TakePhoto.java
@@ -0,0 +1,173 @@
+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.net.Uri;
+import android.os.Environment;
+import android.provider.MediaStore;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.content.FileProvider;
+import androidx.exifinterface.media.ExifInterface;
+
+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 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()&&!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;
+ }
+}
diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/ToggledBackgroundButton.java b/app/src/main/java/gr/thmmy/mthmmy/utils/ToggledBackgroundButton.java
new file mode 100644
index 00000000..6c189e87
--- /dev/null
+++ b/app/src/main/java/gr/thmmy/mthmmy/utils/ToggledBackgroundButton.java
@@ -0,0 +1,27 @@
+package gr.thmmy.mthmmy.utils;
+import android.content.Context;
+import android.util.AttributeSet;
+
+import androidx.appcompat.widget.AppCompatButton;
+
+public class ToggledBackgroundButton extends AppCompatButton {
+
+ public ToggledBackgroundButton(Context context) {
+ super(context);
+ }
+
+ public ToggledBackgroundButton(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public ToggledBackgroundButton(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ setAlpha(enabled ? 1 : 0.5f);
+ super.setEnabled(enabled);
+ }
+}
+
diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseHelpers.java b/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseHelpers.java
index f937578a..62380f80 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseHelpers.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseHelpers.java
@@ -235,7 +235,7 @@ public class ParseHelpers {
stringBuilder.append(Character.toString((char) i));
}
- Timber.i("Email deobfuscated.");
+ Timber.d("Email deobfuscated.");
return stringBuilder.toString();
}
diff --git a/app/src/main/java/gr/thmmy/mthmmy/viewmodel/BaseViewModel.java b/app/src/main/java/gr/thmmy/mthmmy/viewmodel/BaseViewModel.java
index a038c033..5787b36d 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/viewmodel/BaseViewModel.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/viewmodel/BaseViewModel.java
@@ -3,6 +3,7 @@ package gr.thmmy.mthmmy.viewmodel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
+
import gr.thmmy.mthmmy.model.Bookmark;
public class BaseViewModel extends ViewModel {
diff --git a/app/src/main/java/gr/thmmy/mthmmy/viewmodel/ShoutboxViewModel.java b/app/src/main/java/gr/thmmy/mthmmy/viewmodel/ShoutboxViewModel.java
index 25092578..c3f4dce5 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/viewmodel/ShoutboxViewModel.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/viewmodel/ShoutboxViewModel.java
@@ -4,6 +4,7 @@ import android.os.AsyncTask;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
+
import gr.thmmy.mthmmy.activities.shoutbox.SendShoutTask;
import gr.thmmy.mthmmy.activities.shoutbox.ShoutboxTask;
import gr.thmmy.mthmmy.model.Shoutbox;
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 a7e72883..750e6a34 100644
--- a/app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java
+++ b/app/src/main/java/gr/thmmy/mthmmy/viewmodel/TopicViewModel.java
@@ -8,9 +8,10 @@ import android.widget.CheckBox;
import android.widget.LinearLayout;
import android.widget.RadioGroup;
+import androidx.lifecycle.MutableLiveData;
+
import java.util.ArrayList;
-import androidx.lifecycle.MutableLiveData;
import gr.thmmy.mthmmy.activities.settings.SettingsActivity;
import gr.thmmy.mthmmy.activities.topic.tasks.DeleteTask;
import gr.thmmy.mthmmy.activities.topic.tasks.EditTask;
@@ -219,7 +220,7 @@ public class TopicViewModel extends BaseViewModel implements TopicTask.OnTopicTa
PrepareForEditResult editResult = prepareForEditResult.getValue();
Timber.i("Editing post");
new EditTask(editTaskCallbacks, position).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, editResult.getCommitEditUrl(), message,
- editResult.getNumReplies(), editResult.getSeqnum(), editResult.getSc(), subject, editResult.getTopic());
+ editResult.getNumReplies(), editResult.getSeqnum(), editResult.getSc(), subject, editResult.getTopic(), editResult.getIcon());
}
/**
diff --git a/app/src/main/res/drawable/ic_attach_file_white_24dp.xml b/app/src/main/res/drawable/ic_attach_file_white_24dp.xml
deleted file mode 100644
index 4834305b..00000000
--- a/app/src/main/res/drawable/ic_attach_file_white_24dp.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
diff --git a/app/src/main/res/drawable/ic_cached_accent_24dp.xml b/app/src/main/res/drawable/ic_cached_accent_24dp.xml
new file mode 100644
index 00000000..b4f3f7dd
--- /dev/null
+++ b/app/src/main/res/drawable/ic_cached_accent_24dp.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_cancel_accent_24dp.xml b/app/src/main/res/drawable/ic_cancel_accent_24dp.xml
new file mode 100644
index 00000000..4c1d6837
--- /dev/null
+++ b/app/src/main/res/drawable/ic_cancel_accent_24dp.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_info_outline_warning_24dp.xml b/app/src/main/res/drawable/ic_info_outline_warning_24dp.xml
new file mode 100644
index 00000000..1c3b206c
--- /dev/null
+++ b/app/src/main/res/drawable/ic_info_outline_warning_24dp.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/app/src/main/res/layout/activity_board.xml b/app/src/main/res/layout/activity_board.xml
index 9a51aa92..0a61658e 100644
--- a/app/src/main/res/layout/activity_board.xml
+++ b/app/src/main/res/layout/activity_board.xml
@@ -1,6 +1,5 @@
-
+ app:srcCompat="@drawable/ic_bookmark_false_accent_24dp" />
@@ -43,8 +42,7 @@
android:layout_marginTop="64dp"
android:background="@color/background"
android:scrollbars="none"
- tools:context="gr.thmmy.mthmmy.activities.topic.TopicActivity">
-
+ tools:context="gr.thmmy.mthmmy.activities.topic.TopicActivity" />
+ app:mpb_progressStyle="horizontal" />
+ app:srcCompat="@drawable/ic_add_fab" />
-
-
diff --git a/app/src/main/res/layout/activity_downloads.xml b/app/src/main/res/layout/activity_downloads.xml
index dc92a274..97e0afbc 100644
--- a/app/src/main/res/layout/activity_downloads.xml
+++ b/app/src/main/res/layout/activity_downloads.xml
@@ -48,7 +48,7 @@
app:mpb_indeterminateTint="@color/accent"
app:mpb_progressStyle="horizontal"/>
-
+ app:srcCompat="@drawable/ic_file_upload_white_24dp"/>
diff --git a/app/src/main/res/layout/activity_topic.xml b/app/src/main/res/layout/activity_topic.xml
index aaa13e4b..b61c41cf 100644
--- a/app/src/main/res/layout/activity_topic.xml
+++ b/app/src/main/res/layout/activity_topic.xml
@@ -45,8 +45,8 @@
android:layout_below="@id/appbar"
android:layout_gravity="top|start"
android:clipToPadding="false"
- android:paddingBottom="4dp"
android:paddingTop="4dp"
+ android:paddingBottom="4dp"
android:scrollbars="none"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="gr.thmmy.mthmmy.activities.topic.TopicActivity" />
@@ -146,8 +146,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
- android:layout_marginBottom="50dp"
android:layout_marginEnd="@dimen/fab_margins"
+ android:layout_marginBottom="50dp"
app:layout_behavior="gr.thmmy.mthmmy.utils.ScrollAwareFABBehavior"
app:srcCompat="@drawable/ic_reply" />
diff --git a/app/src/main/res/layout/activity_upload.xml b/app/src/main/res/layout/activity_upload.xml
index 8639fd8c..0eea2f63 100644
--- a/app/src/main/res/layout/activity_upload.xml
+++ b/app/src/main/res/layout/activity_upload.xml
@@ -28,16 +28,16 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="top|start"
- android:background="@color/background"
- android:paddingEnd="@dimen/activity_horizontal_margin"
+ android:background="@color/primary_light"
android:paddingStart="@dimen/activity_horizontal_margin"
+ android:paddingEnd="@dimen/activity_horizontal_margin"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="gr.thmmy.mthmmy.activities.upload.UploadActivity">
@@ -45,15 +45,16 @@
android:id="@+id/upload_spinners"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@color/background"
+ android:layout_marginTop="8dp"
+ android:background="@color/primary_light"
android:orientation="vertical">
@@ -61,8 +62,8 @@
+ android:layout_marginTop="6dp"
+ android:layout_marginBottom="6dp">
+
+
+
+
+
+
+
+
+
+
+ android:layout_marginTop="6dp"
+ android:layout_marginBottom="6dp">
-
-
-
-
+
+
@@ -165,8 +193,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
- android:layout_marginBottom="@dimen/fab_margins"
android:layout_marginEnd="@dimen/fab_margins"
+ android:layout_marginBottom="@dimen/fab_margins"
app:layout_behavior="gr.thmmy.mthmmy.utils.ScrollAwareFABBehavior"
app:srcCompat="@drawable/ic_file_upload_white_24dp" />
diff --git a/app/src/main/res/layout/activity_upload_fields_builder.xml b/app/src/main/res/layout/activity_upload_fields_builder.xml
index 0583b601..601cdeeb 100644
--- a/app/src/main/res/layout/activity_upload_fields_builder.xml
+++ b/app/src/main/res/layout/activity_upload_fields_builder.xml
@@ -27,7 +27,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
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"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
diff --git a/app/src/main/res/layout/activity_upload_file_list_row.xml b/app/src/main/res/layout/activity_upload_file_list_row.xml
new file mode 100644
index 00000000..08b74a4a
--- /dev/null
+++ b/app/src/main/res/layout/activity_upload_file_list_row.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_upload_filename_info_popup.xml b/app/src/main/res/layout/activity_upload_filename_info_popup.xml
new file mode 100644
index 00000000..bb6a9704
--- /dev/null
+++ b/app/src/main/res/layout/activity_upload_filename_info_popup.xml
@@ -0,0 +1,10 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_upload_progress.xml b/app/src/main/res/layout/dialog_upload_progress.xml
new file mode 100644
index 00000000..18444cb4
--- /dev/null
+++ b/app/src/main/res/layout/dialog_upload_progress.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/editor_view_color_picker.xml b/app/src/main/res/layout/editor_view_color_picker.xml
index b71214e1..be1ce295 100644
--- a/app/src/main/res/layout/editor_view_color_picker.xml
+++ b/app/src/main/res/layout/editor_view_color_picker.xml
@@ -1,11 +1,10 @@
-
+ android:layout_height="wrap_content"
+ android:background="@color/card_background">
-
@@ -14,84 +13,84 @@
android:id="@+id/black"
style="@style/PopupMenuItem.TopItem"
android:text="@string/black"
- android:textColor="@color/black"/>
+ android:textColor="@color/black" />
+ android:textColor="@color/red" />
+ android:textColor="@color/yellow" />
+ android:textColor="@color/pink" />
+ android:textColor="@color/green" />
+ android:textColor="@color/orange" />
+ android:textColor="@color/purple" />
+ android:textColor="@color/blue" />
+ android:textColor="@color/beige" />
+ android:textColor="@color/brown" />
+ android:textColor="@color/teal" />
+ android:textColor="@color/navy" />
+ android:textColor="@color/maroon" />
+ android:textColor="@color/lime_green" />
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index f2209027..6a76e274 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -18,6 +18,7 @@
#8B8B8B
#FF9800
#FAA61A
+ #890d0d
#FFFFFF
#CCCCCC
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index bf3d9e5f..5d856f19 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -132,12 +132,20 @@
Upload
- Generate title and description
+ Generate fields
Title
+ Upload as (filename)
Description
- Select file
+ Add files
Take photo
Select a category
+ Please follow the filename rules as\ndescribed
+ in this topic.\n
+ \nThis does not rename your local files.
+ Uploading
+ Uploading: %1$.2f Kbit/s, %2$d/%3$d KBytes
+ "Cancel"
+ "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
new file mode 100644
index 00000000..8f872389
--- /dev/null
+++ b/app/src/main/res/values/uploads_courses.xml
@@ -0,0 +1,153 @@
+
+
+
+
+ - Ακουστική I:Ακουστική 1:Akoustiki_I
+ - Ακουστική II:Ακουστική 2:Akoustiki_II
+ - Ανάλυση Ηλεκτρικών Κυκλωμάτων με Υπολογιστή:Ανάλυση Ηλεκτρικ. Κυκλ. με Υπολογιστή:Analysi_Ilektr_Kykl
+ - Ανάλυση Συστημάτων Ηλεκτρικής Ενέργειας:ΑΣΗΕ:ASHE
+ - Ανάλυση Χρονοσειρών:Χρονοσειρές:Xronoseires
+ - Ανάλυση και Σχεδίαση Αλγορίθμων:Αλγόριθμοι:Algorithms
+ - Αναγνώριση Προτύπων:Αναγνώριση Προτύπων:protipa
+ - Αναλογικές Τηλεπικοινωνίες (πρώην Τηλεπικοινωνιακά Συστήματα I):Αναλογικές Τηλεπ.:Anal_Tilep
+ - Αντικειμενοστραφής Προγραμματισμός:Αντικειμενοστραφής:OOP
+ - Αξιοπιστία Συστημάτων:Αξιοπιστία Συστημάτων:Aksiopistia_Systimaton
+ - Αριθμητική Ανάλυση:Αριθμ. Ανάλυση:Arith_Anal
+ - Αρχές Οικονομίας:Αρχές Οικονομίας:Arx_Oikonomias
+ - Αρχές Παράλληλης Επεξεργασίας:Αρχές Παράλληλης Επεξεργασίας:Arxes_Parall_Epeksergasias
+ - Αρχιτεκτονική Υπολογιστών:Αρχ. Υπολογιστών:Arx_Ypologiston
+ - Ασαφή Συστήματα:Ασαφή:Asafi
+ - Ασφάλεια Πληροφοριακών Συστημάτων:Ασφάλεια:Asfaleia
+ - Ασύρματος Τηλεπικοινωνία I:Ασύρματος 1:Asyrmatos_I
+ - Ασύρματος Τηλεπικοινωνία II:Ασύρματος 2:Asyrmatos_II
+ - Βάσεις Δεδομένων:Βάσεις:Vaseis
+ - Βιομηχανικά Ηλεκτρονικά:Βιομηχανικά Ηλεκτρονικά:Viomix_Ilektronika
+ - Βιομηχανική Πληροφορική:Βιομηχανική Πληρ:Viomix_Plir
+ - Βιοϊατρική Τεχνολογία:Βιοιατρική:Vioiatriki
+ - Γεωηλεκτρομαγνητισμός:Γεωηλεκτρομαγνητισμός:Geoilektromagnitismos
+ - Γραμμική Άλγεβρα:Γραμμ. Άλγεβρ.:Grammiki_Algevra
+ - Γραφική με Υπολογιστές:Γραφική:Grafiki
+ - Δίκτυα Τηλεπικοινωνιών:Δίκτυα Τηλέπ.:Diktya_Tilep
+ - Δίκτυα Υπολογιστών I:Δίκτυα 1:Diktya_I
+ - Δίκτυα Υπολογιστών II:Δίκτυα 2:Diktya_II
+ - Διάδοση Η/Μ Κύματος II:Διάδοση 2:Diadosi_II
+ - Διάδοση Ηλεκτρομαγνητικού Κύματος I (πρώην Πεδίο III):Διάδοση 1:Diadosi_I
+ - Διακριτά Μαθηματικά:Διακριτά Μαθηματικά:Diakrita
+ - Διακριτά μαθηματικά:Διακριτά Μαθηματικά:Diakrita
+ - Διανεμημένη Παραγωγή:Διανεμημένη Παραγωγή:Dian_Paragogi
+ - Διαφορικές Εξισώσεις:Διαφορικές:Diaforikes
+ - Διαχείριση Συστημάτων Ηλεκτρικής Ενέργειας:ΔΣΗΕ:DSHE
+ - Δομές Δεδομένων:Δομ. Δεδομ.:Domes_Dedomenon
+ - Δομημένος Προγραμματισμός:Δομ. Προγραμμ.:C
+ - Ειδικά Κεφάλαια Διαφορικών Εξισώσεων:Ειδικά Κεφάλαια Διαφορικών Εξισώσεων:Eidika_Kef_Diaf_Eksis
+ - Ειδικά Κεφάλαια Ηλεκτρομαγνητικού Πεδίου I:Ειδικά Κεφάλαια Ηλεκτρομαγνητικού Πεδίου I:Eidika_Kef_HM_Pediou_I
+ - Ειδικά Κεφάλαια Συστημάτων Ηλεκτρικής Ενέργειας:ΕΚΣΗΕ:EKSHE
+ - Ειδικές Αρχιτεκτονικές Υπολογιστών:Ειδικές Αρχιτεκτονικές Υπολογιστών:Eidikes_Arx_Ypolog
+ - Ειδικές Κεραίες, Σύνθεση Κεραιών:Ειδικές Κεραίες, Σύνθεση Κεραιών:Eidikes_Keraies
+ - Εισαγωγή στην Ενεργειακή Τεχνολογία I:ΕΕΤ 1:EET_I
+ - Εισαγωγή στην Ενεργειακή Τεχνολογία II:ΕΕΤ2:EET_II
+ - Εισαγωγή στην Πολιτική Οικονομία:Πολιτική Οικονομία:Polit_Oik
+ - Εισαγωγή στις εφαρμογές Πυρηνικής Τεχνολογίας:Εισ. Πυρηνικη Τεχν.:Intro_Pyriniki_Texn
+ - Ενσωματωμένα Συστήματα Πραγματικού Χρόνου:Ενσωματωμένα:Enswmatwmena
+ - Επιχειρησιακή Έρευνα:Επιχειρησιακή Έρευνα:Epixeirisiaki
+ - Ευρυζωνικά Δίκτυα:Ευρυζωνικά:Evryzonika
+ - Ευφυή Συστήματα Ρομπότ:Ευφυή:eufuh
+ - Εφαρμογές Τηλεπικοινωνιακών Διατάξεων:Εφαρμογές Τηλεπ. Διατάξεων:Efarm_Tilep_Diatakseon
+ - Εφαρμοσμένα Μαθηματικά I:Εφαρμοσμένα 1:Efarmosmena_Math_I
+ - Εφαρμοσμένα Μαθηματικά II:Εφαρμοσμένα 2:Efarmosmena_Math_II
+ - Εφαρμοσμένη Θερμοδυναμική:Θερμοδυναμική:Thermodynamiki
+ - Ηλεκτρακουστική I:Ηλεκτρακουστική 1:Ilektrakoustiki_I
+ - Ηλεκτρακουστική II:Ηλεκτρακουστική 2:Ilektrakoustiki_II
+ - Ηλεκτρικά Κυκλώματα I:Κυκλώματα 1:Kyklomata_I
+ - Ηλεκτρικά Κυκλώματα II:Κυκλώματα 2:Kyklomata_II
+ - Ηλεκτρικά Κυκλώματα III:Κυκλώματα 3:Kyklomata_I
+ - Ηλεκτρικές Μετρήσεις I:Μετρήσεις 1:Metriseis_I
+ - Ηλεκτρικές Μετρήσεις II:Μετρήσεις 2:Metriseis_II
+ - Ηλεκτρικές Μηχανές Α\':Μηχανές Α:Mixanes_A
+ - Ηλεκτρικές Μηχανές Β\':Μηχανές Β:Mixanes_B
+ - Ηλεκτρικές Μηχανές Γ\':Μηχανές Γ:Mixanes_C
+ - Ηλεκτρική Οικονομία:Ηλεκτρική Οικονομία:Ilektr_Oikonomia
+ - Ηλεκτρολογικά Υλικά:Ηλεκτρ. Υλικά:Ylika
+ - Ηλεκτρομαγνητική Συμβατότητα:H/M Συμβατότητα:HM_Symvatotita
+ - Ηλεκτρομαγνητικό Πεδίο I:Πεδίο 1:Pedio_I
+ - Ηλεκτρομαγνητικό Πεδίο II:Πεδίο 2:Pedio_II
+ - Ηλεκτρονικά Ισχύος I:Ισχύος 1:Isxyos_I
+ - Ηλεκτρονικά Ισχύος II:Ισχύος 2:Isxyos_II
+ - Ηλεκτρονικές Διατάξεις και Μετρήσεις:Ηλεκτρονικές Διατάξεις και Μετρήσεις:Ilektron_Diatakseis_Metriseis
+ - Ηλεκτρονική I:Ηλεκτρονική 1:Ilektroniki_I
+ - Ηλεκτρονική II:Ηλεκτρονική 2:Ilektroniki_II
+ - Ηλεκτρονική III:Ηλεκτρονική 3:Ilektroniki_III
+ - Ημιαγωγά Υλικά: Θεωρία-Διατάξεις:Ημιαγωγά Υλικά:Imiagoga_Ylika
+ - Θεωρία Πιθανοτήτων και Στατιστική:Πιθανότητες:Pithanotites
+ - Θεωρία Πληροφοριών:Θεωρία Πληρ.:Theoria_Plir
+ - Θεωρία Σημάτων και Γραμμικών Συστημάτων:Σήματα & Συστήματα:Analog_Sima
+ - Θεωρία Σκέδασης:Σκέδαση:Skedasi
+ - Θεωρία Υπολογισμών και Αλγορίθμων:ΘΥΑ:THYA
+ - Θεωρία και Τεχνολογία Πυρηνικών Αντιδραστήρων:Τεχνολογία Αντιδραστήρων:Texn_Antidrasthron
+ - Κβαντική Φυσική:Κβαντική:Kvantiki
+ - Κινητές και Δορυφορικές Επικοινωνίες:Κινητές & Δορυφορικές Επικοινωνίες:Kinites_Doryforikes_Epik
+ - Λειτουργικά Συστήματα:Λειτουργικά:OS
+ - Λογική Σχεδίαση:Λογική Σχεδίαση:Logiki_Sxediasi
+ - Λογισμός I:Λογισμός 1:Logismos_I
+ - Λογισμός II:Λογισμός 2:Logismos_II
+ - Μετάδοση Θερμότητας:Μετάδοση Θερμ.:Metadosi_Therm
+ - Μικροεπεξεργαστές και Περιφερειακά:Μίκρο 2:Mikro_II
+ - Μικροκυματική Τηλεπισκόπηση:Τηλεπισκόπηση:Tilepiskopisi
+ - Μικροκύματα I:Μικροκύματα 1:Mikrokymata_I
+ - Μικροκύματα II:Μικροκύματα 2:Mikrokymata_II
+ - Οπτικές Επικοινωνίες:Οπτικές Τηλεπ.:Optikes_Tilep
+ - Οπτική I:Οπτική 1:Optiki_I
+ - Οπτική II:Οπτική 2:Optiki_II
+ - Οργάνωση Υπολογιστών:Οργάνωση Υπολ.:Org_Ypol
+ - Οργάνωση και Διοίκηση Εργοστασίων:Οργάνωση και Διοίκηση Εργοστασίων:Organ_Dioik_Ergostasion
+ - Παράλληλα και Κατανεμημένα Συστήματα:Παράλληλα:Parallila
+ - Προγραμματιζόμενα Κυκλώματα ASIC:ASIC:ASIC
+ - Προγραμματιστικές Τεχνικές:Προγραμματ. Τεχν.:CPP
+ - Προηγμένες Τεχνικές Επεξεργασίας Σήματος:ΠΤΕΣ:PTES
+ - Προσομοίωση και Μοντελοποίηση Συστημάτων:Μοντελοποίηση:Montelopoiisi
+ - Ρομποτική:Ρομποτική:Robotiki
+ - Σήματα και Συστήματα:Σήματα & Συστήματα:Analog_Sima
+ - Σερβοκινητήρια Συστήματα:Σέρβο:Servo
+ - Σταθμοί Παραγωγής Ηλεκτρικής Ενέργειας:ΣΠΗΕ:SPHE
+ - Στοχαστικό Σήμα:Στοχ. Σήμα:Stox_Sima
+ - Συστήματα Αυτομάτου Ελέγχου I:ΣΑΕ 1:SAE_I
+ - Συστήματα Αυτομάτου Ελέγχου II:ΣΑΕ 2:SAE_II
+ - Συστήματα Αυτομάτου Ελέγχου III:ΣΑΕ 3:SAE_III
+ - Συστήματα Ηλεκτρικής Ενέργειας I:ΣΗΕ 1:SHE_I
+ - Συστήματα Ηλεκτρικής Ενέργειας II:ΣΗΕ 2:SHE_II
+ - Συστήματα Ηλεκτρικής Ενέργειας III:ΣΗΕ 3:SHE_III
+ - Συστήματα Ηλεκτροκίνησης:Ηλεκτροκίνηση:Ilektrokinisi
+ - Συστήματα Μικροϋπολογιστών:Μίκρο 1:Mikro_I
+ - Συστήματα Πολυμέσων και Εικονική Πραγματικότητα:Πολυμέσα:Polymesa
+ - Συστήματα Υπολογιστών (Υπολογιστικά Συστήματα):Συσ. Υπολογιστών:Sys_Ypologiston
+ - Σχεδίαση Συστημάτων VLSI:VLSI:VLSI
+ - Σύνθεση Ενεργών και Παθητικών Κυκλωμάτων:Σύνθεση:Synthesi
+ - Σύνθεση Τηλεπικοινωνιακών Διατάξεων:Σύνθεση Τηλεπ. Διατάξεων:Synth_Tilep_Diatakseon
+ - Τεχνικές Βελτιστοποίησης:Βελτιστοποίηση:Veltistopoiisi
+ - Τεχνικές Κωδικοποίησης:Τεχνικές Κωδικοποίησης:Texn_Kodikopoiisis
+ - Τεχνικές Σχεδίασης με Η/Υ:Σχέδιο:sxedio
+ - Τεχνικές μη Καταστρεπτικών Δοκιμών:Μη Καταστρεπτικές Δοκιμές:Non_Destructive_Tests
+ - Τεχνική Μηχανική:Τεχν. Μηχαν.:Texn_Mixan
+ - Τεχνολογία Ήχου και Εικόνας:Τεχνολογία Ήχου και Εικόνας:Texn_Ixou_Eikonas
+ - Τεχνολογία Ηλεκτροτεχνικών Υλικών:Ηλεκτροτεχνικά Υλικά:Ilektrotexnika_Ylika
+ - Τεχνολογία Λογισμικού:Τεχνολογία Λογισμικού:SE
+ - Τηλεοπτικά Συστήματα:Τηλεοπτικά:Tileoptika
+ - Τηλεπικοινωνιακή Ηλεκτρονική:Τηλεπ. Ηλεκτρ.:Tilep_Ilektr
+ - Υπολογιστικές Μέθοδοι στα Ενεργειακά Συστήματα:ΥΜΕΣ:YMES
+ - Υπολογιστικός Ηλεκτρομαγνητισμός:Υπολογιστικός Η/Μ:Ypologistikos_HM
+ - Υψηλές Τάσεις I:Υψηλές 1:Ypsiles_I
+ - Υψηλές Τάσεις II:Υψηλές 2:Ypsiles_II
+ - Υψηλές Τάσεις III:Υψηλές 3:Ypsiles_III
+ - Υψηλές Τάσεις 4:Υψηλές 4:Ypsiles_IV
+ - Φυσική I:Φυσική 1:Fysiki_I
+ - Φωτονική Τεχνολογία:Φωτονική:Fotoniki
+ - Ψηφιακά Συστήματα I:Ψηφιακά 1:Psifiaka_I
+ - Ψηφιακά Συστήματα II:Ψηφιακά 2:Psifiaka_II
+ - Ψηφιακά Συστήματα III:Ψηφιακά 3:Psifiaka_III
+ - Ψηφιακά Φίλτρα:Φίλτρα:Filtra
+ - Ψηφιακές Τηλεπικοινωνίες I:Ψηφιακές Τηλεπ. 1:Psif_Tilep_I
+ - Ψηφιακές Τηλεπικοινωνίες II:Ψηφιακές Τηλεπ. 2:Psif_Tilep_II
+ - Ψηφιακή Επεξεργασία Εικόνας:ΨΕΕ:PSEE
+ - Ψηφιακή Επεξεργασία Σήματος:ΨΕΣ:PSES
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml-v26/app_preferences_user.xml b/app/src/main/res/xml-v26/app_preferences_user.xml
index fe301eb3..0bda7493 100644
--- a/app/src/main/res/xml-v26/app_preferences_user.xml
+++ b/app/src/main/res/xml-v26/app_preferences_user.xml
@@ -28,7 +28,7 @@
app:iconSpaceReserved="false" />
-
+
-
+