# HG changeset patch # User David Barts # Date 1618070564 25200 # Node ID 45e4df5226c03bcb64618392a6ef9674d97e0b6e # Parent 9231f1a41a59ef511d5aa885a5660542dddd9577 Shares, but creates multiple versions of cruft file. diff -r 9231f1a41a59 -r 45e4df5226c0 app/build.gradle --- a/app/build.gradle Thu Apr 08 21:00:57 2021 -0700 +++ b/app/build.gradle Sat Apr 10 09:02:44 2021 -0700 @@ -11,8 +11,8 @@ applicationId "com.bartsent.simpleresizer" minSdkVersion 23 targetSdkVersion 30 - versionCode 4 - versionName "1.03" + versionCode 5 + versionName "1.04" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff -r 9231f1a41a59 -r 45e4df5226c0 app/src/main/java/com/bartsent/simpleresizer/EditImage.kt --- a/app/src/main/java/com/bartsent/simpleresizer/EditImage.kt Thu Apr 08 21:00:57 2021 -0700 +++ b/app/src/main/java/com/bartsent/simpleresizer/EditImage.kt Sat Apr 10 09:02:44 2021 -0700 @@ -29,6 +29,7 @@ import com.bartsent.simpleresizer.databinding.ActivityEditImageBinding import com.bartsent.simpleresizer.lib.ThreadPools import com.bartsent.simpleresizer.lib.getScaledInstance +import com.google.android.material.floatingactionbutton.FloatingActionButton import java.io.File import java.io.IOException import java.util.concurrent.Callable @@ -39,6 +40,8 @@ var uri: Uri? = null var bitmap: Bitmap? = null var reader: Future? = null + var permissionsCallback: (() -> Unit)? = null + var sharable: Boolean = false } private lateinit var viewModel: State @@ -46,6 +49,8 @@ sort() } + private val IMAGE_TO_SEND = "sent_image.jpg" + private lateinit var binding: ActivityEditImageBinding override fun onCreate(savedInstanceState: Bundle?) { @@ -132,6 +137,7 @@ // User has opened a new image. if (imageUri != viewModel.uri) { viewModel.uri = imageUri + makeMundane() binding.progressBar.visibility = ProgressBar.VISIBLE viewModel.reader = ThreadPools.WORKERS.submit(Callable { val newBitmap = contentResolver.openInputStream(imageUri).use { @@ -153,6 +159,7 @@ val oldBitmap = viewModel.bitmap if (oldBitmap != null) setImage(oldBitmap) + binding.fabulous.visibility = if (viewModel.sharable) FloatingActionButton.VISIBLE else FloatingActionButton.GONE } override fun onDestroy() { @@ -237,6 +244,7 @@ runOnUiThread { binding.progressBar.visibility = ProgressBar.INVISIBLE setImage(newBitmap) + makeFabulous() } } } @@ -314,9 +322,20 @@ fun cancelClicked(view: View): Unit { unsetImage() + makeMundane() finish() } + private fun makeFabulous(): Unit { + binding.fabulous.visibility = FloatingActionButton.VISIBLE + viewModel.sharable = true + } + + private fun makeMundane(): Unit { + binding.fabulous.visibility = FloatingActionButton.GONE + viewModel.sharable = false + } + private val REQUEST_WRITE_EXTERNAL = 42 override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray): Unit { @@ -325,24 +344,32 @@ return } if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) { - doneClicked(null) + val cb = viewModel.permissionsCallback + if (cb != null) { + viewModel.permissionsCallback = null + cb() + } } else { showError(getString(R.string.error_unable_no_permissions)) } } - fun requestWritePermission(): Unit { + private fun requestWritePermission(): Unit { requestPermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), REQUEST_WRITE_EXTERNAL) } - fun doneClicked(view: View?): Unit { - // If we need WRITE_EXTERNAL_STORAGE, request it and bail. We will be called again - // (with the permission) if it is granted. + private fun needsWritePermission(callback: () -> Unit): Boolean { if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.Q) { if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) { + viewModel.permissionsCallback = callback if (shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)) AlertDialog.Builder(this).also { - it.setMessage(getString(R.string.permission_needed, getString(R.string.app_name))) + it.setMessage( + getString( + R.string.permission_needed, + getString(R.string.app_name) + ) + ) it.setNeutralButton(R.string.ok_text) { dialog, _ -> dialog.dismiss() requestWritePermission() @@ -351,34 +378,99 @@ }.show() else requestWritePermission() - return + return true } } + return false + } + + fun shareClicked(view: View): Unit { + // If we need WRITE_EXTERNAL_STORAGE, request it and bail. We will be called again + // (with the permission) if it is granted. + if (needsWritePermission({ shareClicked(view) })) + return // If we get here, we have permission to save (if we need it). + val contentValues = makeContentValues(IMAGE_TO_SEND) + + // Delete any old file(s) + val cols = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) + arrayOf(MediaStore.MediaColumns.DISPLAY_NAME, MediaStore.MediaColumns.MIME_TYPE, MediaStore.MediaColumns.RELATIVE_PATH) + else + arrayOf(MediaStore.MediaColumns.DISPLAY_NAME, MediaStore.MediaColumns.MIME_TYPE) + val query = StringBuilder() + for (col in cols) { + if (query.isNotEmpty()) + query.append(" and ") + query.append(col) + query.append(" = ?") + } + try { + contentResolver.delete( + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + query.toString(), + cols.map { contentValues.getAsString(it) }.toTypedArray() + ) + } catch (e: Exception) { + Log.e("EditImage", "unexpected exception when sharing!", e) + } + + // Save new file, use it to share data. + saveAs(contentValues) { + val shareIntent: Intent = Intent().apply { + action = Intent.ACTION_SEND + putExtra(Intent.EXTRA_STREAM, it) + type = "image/jpeg" + } + startActivity(Intent.createChooser(shareIntent, resources.getText(R.string.share_text))) + } + } + + fun doneClicked(view: View): Unit { + // If we need WRITE_EXTERNAL_STORAGE, request it and bail. We will be called again + // (with the permission) if it is granted. + if (needsWritePermission({ doneClicked(view) })) + return + + // If we get here, we have permission to save (if we need it). + val image = viewModel.bitmap!! + var fileName = getFileName(viewModel.uri!!) + if (fileName == null) { + val d = java.util.Date() + fileName = "IMG_%tY%tm%td_%tH%tM%tS".format(d, d, d, d, d, d) + } + val dot = fileName.lastIndexOf('.') + if (dot != -1) + fileName = fileName.substring(0, dot) + fileName = "${fileName}_${image.width}x${image.height}.jpg" + saveAs(makeContentValues(fileName)) { + unsetImage() + makeMundane() + finish() + } + } + + private fun makeContentValues(fileName: String): ContentValues = ContentValues().apply { + put(MediaStore.MediaColumns.DISPLAY_NAME, fileName) + put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg") + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { + put(MediaStore.MediaColumns.RELATIVE_PATH, + File(Environment.DIRECTORY_PICTURES, getString(R.string.app_name)).path) + } + } + + private fun saveAs(contentValues: ContentValues, callback: (Uri) -> Unit) { val image = viewModel.bitmap!! binding.progressBar.visibility = ProgressBar.VISIBLE ThreadPools.WORKERS.execute { - val contentValues = ContentValues().apply { - var fileName = getFileName(viewModel.uri!!) - if (fileName == null) { - val d = java.util.Date() - fileName = "IMG_%tY%tm%td_%tH%tM%tS".format(d, d, d, d, d, d) - } - val dot = fileName.lastIndexOf('.') - if (dot != -1) - fileName = fileName.substring(0, dot) - fileName = "${fileName}_${image.width}x${image.height}.jpg" - put(MediaStore.MediaColumns.DISPLAY_NAME, fileName) - put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg") - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { - put(MediaStore.MediaColumns.RELATIVE_PATH, - File(Environment.DIRECTORY_PICTURES, getString(R.string.app_name)).path) - } + var errorMessage: String? = null + val myUri = try { + contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues) + } catch(e: Exception) { + Log.e("EditImage", "unexpected exception when saving!", e) + null } - var errorMessage: String? = null try { - val myUri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues) if (myUri == null) { throw IOException(getString(R.string.error_create_mediastore)) } @@ -406,8 +498,7 @@ runOnUiThread { binding.progressBar.visibility = ProgressBar.INVISIBLE if (errorMessage == null) { - unsetImage() - finish() + callback(myUri!!) } else { showError(errorMessage) } diff -r 9231f1a41a59 -r 45e4df5226c0 app/src/main/res/drawable-anydpi/ic_action_share.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/src/main/res/drawable-anydpi/ic_action_share.xml Sat Apr 10 09:02:44 2021 -0700 @@ -0,0 +1,11 @@ + + + diff -r 9231f1a41a59 -r 45e4df5226c0 app/src/main/res/drawable-hdpi/ic_action_share.png Binary file app/src/main/res/drawable-hdpi/ic_action_share.png has changed diff -r 9231f1a41a59 -r 45e4df5226c0 app/src/main/res/drawable-mdpi/ic_action_share.png Binary file app/src/main/res/drawable-mdpi/ic_action_share.png has changed diff -r 9231f1a41a59 -r 45e4df5226c0 app/src/main/res/drawable-xhdpi/ic_action_share.png Binary file app/src/main/res/drawable-xhdpi/ic_action_share.png has changed diff -r 9231f1a41a59 -r 45e4df5226c0 app/src/main/res/drawable-xxhdpi/ic_action_share.png Binary file app/src/main/res/drawable-xxhdpi/ic_action_share.png has changed diff -r 9231f1a41a59 -r 45e4df5226c0 app/src/main/res/layout-land/activity_edit_image.xml --- a/app/src/main/res/layout-land/activity_edit_image.xml Thu Apr 08 21:00:57 2021 -0700 +++ b/app/src/main/res/layout-land/activity_edit_image.xml Sat Apr 10 09:02:44 2021 -0700 @@ -19,6 +19,19 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> + + + + Rotate Resize Selecting imageā€¦ + Share Settings