diff --git a/app/build.gradle b/app/build.gradle index 29faec1f..6171590a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,15 +7,15 @@ apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.firebase.crashlytics' android { - compileSdkVersion 30 + compileSdkVersion 31 buildToolsVersion = '30.0.2' defaultConfig { applicationId "gr.thmmy.mthmmy" minSdkVersion 21 targetSdkVersion 30 - versionCode 30 - versionName "2.0.0" + versionCode 31 + versionName "2.0.1" archivesBaseName = "mTHMMY-v$versionName" buildConfigField "String", "CURRENT_BRANCH", "\"" + getCurrentBranch() + "\"" buildConfigField "String", "COMMIT_HASH", "\"" + getCommitHash() + "\"" @@ -76,14 +76,14 @@ tasks.whenTaskAdded { task -> dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation project(":emojis") - implementation 'androidx.appcompat:appcompat:1.4.0-alpha03' + implementation 'androidx.appcompat:appcompat:1.4.1' implementation 'androidx.preference:preference:1.1.1' implementation 'androidx.legacy:legacy-preference-v14:1.0.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.recyclerview:recyclerview:1.2.1' implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' - implementation 'androidx.constraintlayout:constraintlayout:2.1.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.3' implementation 'androidx.exifinterface:exifinterface:1.3.3' implementation 'com.google.android.material:material:1.4.0' implementation platform('com.google.firebase:firebase-bom:28.4.0') diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9003cab6..7c7d5b18 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,8 +7,8 @@ - + @@ -41,6 +41,7 @@ android:configChanges="orientation|screenSize" android:label="@string/app_name" android:launchMode="singleTask" + android:exported="true" android:theme="@style/AppTheme.NoActionBar"> diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadActivity.java index e0b5b986..48ec3b5b 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadActivity.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/upload/UploadActivity.java @@ -10,6 +10,7 @@ import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.AsyncTask; +import android.os.Build; import android.os.Bundle; import android.text.Editable; import android.text.Spannable; @@ -327,30 +328,20 @@ public class UploadActivity extends BaseActivity { //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(BaseApplication.getInstance().getApplicationContext(), "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())); + String newFileName = FileUtils.getFilenameWithoutExtension(editTextFilename); + + if(uploadFile.isCameraPhoto()) + newFileName = newFileName + ".jpg"; + + //Temporarily copies the file to another location (external cache) and renames it + tempFileUri = UploadsHelper.createTempFile(this, storage, + uploadFile.getFileUri(), + newFileName); + + //Something went wrong while creating temporary file, abort + if(tempFileUri == null){ + progressBar.setVisibility(View.GONE); + return; } } else { @@ -561,7 +552,7 @@ public class UploadActivity extends BaseActivity { UploadFile newFile = new UploadFile(true, TakePhoto.processResult(this, photoFileCreated), photoFileCreated); addFileViewToList(FileUtils.getFilenameWithoutExtension(FileUtils. - filenameFromUri(this, newFile.getFileUri()))); + filenameFromUri(this, newFile.getFileUri())) + ".jpg"); filesList.add(newFile); } else if (requestCode == AFR_REQUEST_CODE_FIELDS_BUILDER) { @@ -596,6 +587,10 @@ public class UploadActivity extends BaseActivity { case UPLOAD_REQUEST_CAMERA_CODE: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) takePhoto(); + else { + Timber.w("Take photo failed (permission denied)."); + Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Take photo failed (storage permission denied)", Toast.LENGTH_SHORT).show(); + } break; case UPLOAD_REQUEST_STORAGE_CODE: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED && @@ -609,6 +604,7 @@ public class UploadActivity extends BaseActivity { finish(); } else { + Timber.w("Zip task failed (permission denied)."); Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Please retry uploading.", Toast.LENGTH_SHORT).show(); } break; @@ -720,17 +716,23 @@ public class UploadActivity extends BaseActivity { cancelIntent.setAction(UploadsReceiver.ACTION_CANCEL_UPLOAD); cancelIntent.putExtra(UploadsReceiver.UPLOAD_ID_KEY, uploadID); + int pendingIntentFlags; + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) + pendingIntentFlags = PendingIntent.FLAG_MUTABLE |PendingIntent.FLAG_UPDATE_CURRENT; + else + pendingIntentFlags = PendingIntent.FLAG_UPDATE_CURRENT; + uploadNotificationConfig.getProgress().actions.add(new UploadNotificationAction( R.drawable.ic_cancel_accent_24dp, context.getString(R.string.cancel), PendingIntent.getBroadcast(context, 0, cancelIntent, - PendingIntent.FLAG_UPDATE_CURRENT) + pendingIntentFlags) )); uploadNotificationConfig.getError().actions.add(new UploadNotificationAction( R.drawable.ic_notification, context.getString(R.string.upload_retry), PendingIntent.getBroadcast(context, 0, retryIntent, - PendingIntent.FLAG_UPDATE_CURRENT) + pendingIntentFlags) )); return uploadNotificationConfig; @@ -1069,7 +1071,7 @@ public class UploadActivity extends BaseActivity { @Override protected void onPreExecute() { assert weakActivity != null; - Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Zipping files", Toast.LENGTH_SHORT).show(); + Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Zipping files...", Toast.LENGTH_SHORT).show(); } @Override @@ -1077,7 +1079,7 @@ public class UploadActivity extends BaseActivity { if (weakActivity == null || zipFilename == null) return false; - File zipFile = UploadsHelper.createZipFile(zipFilename); + File zipFile = UploadsHelper.createZipFile(weakActivity.get(), zipFilename); if (zipFile == null) return false; 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 73b7fd21..af91d72c 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 @@ -2,7 +2,6 @@ package gr.thmmy.mthmmy.activities.upload; import android.content.Context; import android.net.Uri; -import android.os.Environment; import android.widget.Toast; import androidx.annotation.NonNull; @@ -27,24 +26,19 @@ import timber.log.Timber; public class UploadsHelper { private static final int BUFFER = 4096; - private static final String TEMP_FILES_DIRECTORY = "~tmp_mTHMMY_uploads"; @SuppressWarnings("ResultOfMethodCallIgnored") @Nullable static Uri createTempFile(Context context, Storage storage, Uri fileUri, String newFilename) { + Timber.i("Creating new temporary file (%s)", newFilename); String oldFilename = FileUtils.filenameFromUri(context, fileUri); String fileExtension = oldFilename.substring(oldFilename.indexOf('.')); - String destinationFilename = Environment.getExternalStorageDirectory().getPath() + - File.separatorChar + TEMP_FILES_DIRECTORY + File.separatorChar + newFilename + fileExtension; - File tempDirectory = new File(android.os.Environment.getExternalStorageDirectory().getPath() + - File.separatorChar + TEMP_FILES_DIRECTORY); - - if (!tempDirectory.exists() && !tempDirectory.mkdirs()) { - Timber.w("Temporary directory build returned false in %s", UploadActivity.class.getSimpleName()); - Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Couldn't create temporary directory", Toast.LENGTH_SHORT).show(); + File tempFilesDirectory = createTempFilesDir(context); + if (tempFilesDirectory==null) return null; - } + + String destinationFilename = tempFilesDirectory.getAbsolutePath() + File.separatorChar + newFilename + fileExtension; InputStream inputStream; BufferedInputStream bufferedInputStream = null; @@ -53,7 +47,7 @@ public class UploadsHelper { try { inputStream = context.getContentResolver().openInputStream(fileUri); if (inputStream == null) { - Timber.w("Input stream was null, %s", UploadActivity.class.getSimpleName()); + Timber.e("Input stream was null, %s", UploadActivity.class.getSimpleName()); return null; } @@ -81,20 +75,19 @@ public class UploadsHelper { } @Nullable - static File createZipFile(@NonNull String zipFilename) { - // Create a zip file name - File zipFolder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) + - File.separator + "mTHMMY"); + static File createZipFile(Context context, @NonNull String zipFilename) { + // Create a zip file in temp directory + Timber.i("Creating temporary zip file %s", zipFilename); - if (!zipFolder.exists() && !zipFolder.mkdirs()) { - Timber.w("Zip folder build returned false in %s", UploadsHelper.class.getSimpleName()); + File tempFilesDirectory = createTempFilesDir(context); + if (tempFilesDirectory==null) return null; - } - return new File(zipFolder, zipFilename); + return new File(tempFilesDirectory, zipFilename); } static void zip(Context context, Uri[] files, Uri zipFile) { + Timber.i("Adding files to %s...", zipFile.getPath()); try { BufferedInputStream origin; OutputStream dest = context.getContentResolver().openOutputStream(zipFile); @@ -117,21 +110,45 @@ public class UploadsHelper { origin.close(); } + Timber.i("Files added successfully to %s.", zipFile.getPath()); + out.close(); } catch (Exception e) { e.printStackTrace(); } } - public static void deleteTempFiles(Storage storage) { - File tempFilesDirectory = new File(Environment.getExternalStorageDirectory().getPath() + - File.separatorChar + TEMP_FILES_DIRECTORY); + private static File createTempFilesDir(Context context){ + File tempFilesDirectory = context.getExternalCacheDir(); + + if (tempFilesDirectory == null){ + Timber.e("Temporary files directory error (%s)!", UploadActivity.class.getSimpleName()); + Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Temporary files directory error (%s)!", Toast.LENGTH_SHORT).show(); + return null; + } - if (storage.isDirectoryExists(tempFilesDirectory.getAbsolutePath())) { - for (File tempFile : storage.getFiles(tempFilesDirectory.getAbsolutePath())) { - storage.deleteFile(tempFile.getAbsolutePath()); + if (!tempFilesDirectory.exists() && !tempFilesDirectory.mkdirs()) { + Timber.e("Temporary directory %s creation returned false (%s)", tempFilesDirectory.getAbsolutePath(), UploadActivity.class.getSimpleName()); + Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Couldn't create temporary directory for file renaming!", Toast.LENGTH_SHORT).show(); + return null; + } + + return tempFilesDirectory; + } + + public static void deleteTempFiles(Context context, Storage storage) { + File tempFilesDirectory = context.getExternalCacheDir(); + + if (tempFilesDirectory != null){ + if (storage.isDirectoryExists(tempFilesDirectory.getAbsolutePath())) { + for (File tempFile : storage.getFiles(tempFilesDirectory.getAbsolutePath())) { + storage.deleteFile(tempFile.getAbsolutePath()); + } + storage.deleteDirectory(tempFilesDirectory.getAbsolutePath()); + Timber.i("Deleted temp files from cache."); } - storage.deleteDirectory(tempFilesDirectory.getAbsolutePath()); } + else + Timber.e("Couldn't delete temp files!"); } } 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 f2af6b7e..21e1af95 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java +++ b/app/src/main/java/gr/thmmy/mthmmy/base/BaseActivity.java @@ -694,6 +694,7 @@ public abstract class BaseActivity extends AppCompatActivity { //True if permissions are OK protected boolean checkPerms() { if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) { + Timber.i("Checking storage permissions."); String[] PERMISSIONS_STORAGE = { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}; @@ -705,12 +706,14 @@ public abstract class BaseActivity extends AppCompatActivity { } //Display popup for user to grant permission - protected void requestPerms(int code) { //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}; + Timber.i("Requesting storage permissions (code %d).", code); requestPermissions(PERMISSIONS_STORAGE, code); } } @@ -722,10 +725,13 @@ public abstract class BaseActivity extends AppCompatActivity { if (permsRequestCode == DOWNLOAD_REQUEST_CODE) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) prepareDownload(tempThmmyFile); + else { + Timber.w("Download failed (permission denied)."); + Toast.makeText(BaseApplication.getInstance().getApplicationContext(), "Download failed (storage permission denied)", Toast.LENGTH_SHORT).show(); + } } } - //----------------------------------DOWNLOAD---------------------- private ThmmyFile tempThmmyFile; 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 b271e39f..482dffdb 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/services/NotificationService.java +++ b/app/src/main/java/gr/thmmy/mthmmy/services/NotificationService.java @@ -178,8 +178,15 @@ public class NotificationService extends FirebaseMessagingService { extras.putString(BUNDLE_TOPIC_TITLE, postNotification.getTopicTitle()); intent.putExtras(extras); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + + int pendingIntentFlags; + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) + pendingIntentFlags = PendingIntent.FLAG_IMMUTABLE |PendingIntent.FLAG_ONE_SHOT; + else + pendingIntentFlags = PendingIntent.FLAG_ONE_SHOT; + PendingIntent pendingIntent = PendingIntent.getActivity(this, requestCode++, intent, - PendingIntent.FLAG_ONE_SHOT); + pendingIntentFlags); int notificationId; String contentText; diff --git a/app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java b/app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java index 9823d3d3..19c52396 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java +++ b/app/src/main/java/gr/thmmy/mthmmy/services/UploadsReceiver.java @@ -105,7 +105,7 @@ public class UploadsReceiver extends UploadServiceBroadcastReceiver { String response = serverResponse.getBodyAsString(); if (response.contains("Η προσθήκη του αρχείου ήταν επιτυχημένη.") || response.contains("The upload was successful.")) { Timber.i("Upload completed successfully (id: %s)", uploadInfo.getUploadId()); - Toast.makeText(context.getApplicationContext(), "Upload completed successfully", Toast.LENGTH_SHORT).show(); + Toast.makeText(context.getApplicationContext(), "Upload completed successfully!", Toast.LENGTH_SHORT).show(); BaseApplication.getInstance().logFirebaseAnalyticsEvent("file_upload", null); } else { @@ -118,7 +118,7 @@ public class UploadsReceiver extends UploadServiceBroadcastReceiver { storage = new Storage(context.getApplicationContext()); } - UploadsHelper.deleteTempFiles(storage); + UploadsHelper.deleteTempFiles(context, storage); } @Override @@ -130,7 +130,7 @@ public class UploadsReceiver extends UploadServiceBroadcastReceiver { storage = new Storage(context.getApplicationContext()); //cancelNotification(context, uploadInfo.getNotificationID()); - UploadsHelper.deleteTempFiles(storage); + UploadsHelper.deleteTempFiles(context, storage); } public static void setDialogDisplay(AlertDialog uploadProgressDialog, String dialogUploadID, 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 index 08b74a4a..8af02800 100644 --- a/app/src/main/res/layout/activity_upload_file_list_row.xml +++ b/app/src/main/res/layout/activity_upload_file_list_row.xml @@ -3,7 +3,9 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="horizontal"> + android:orientation="horizontal" + android:paddingStart="8dp" + android:paddingEnd="0dp">