Mercurial > cgi-bin > hgweb.cgi > SimpleResizer
changeset 42:45e4df5226c0
Shares, but creates multiple versions of cruft file.
author | David Barts <n5jrn@me.com> |
---|---|
date | Sat, 10 Apr 2021 09:02:44 -0700 |
parents | 9231f1a41a59 |
children | 9cb9bb5da247 |
files | app/build.gradle app/src/main/java/com/bartsent/simpleresizer/EditImage.kt app/src/main/res/drawable-anydpi/ic_action_share.xml app/src/main/res/drawable-hdpi/ic_action_share.png app/src/main/res/drawable-mdpi/ic_action_share.png app/src/main/res/drawable-xhdpi/ic_action_share.png app/src/main/res/drawable-xxhdpi/ic_action_share.png app/src/main/res/layout-land/activity_edit_image.xml app/src/main/res/layout/activity_edit_image.xml app/src/main/res/values/strings.xml |
diffstat | 10 files changed, 158 insertions(+), 29 deletions(-) [+] |
line wrap: on
line diff
--- 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" }
--- 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<Unit>? = 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<Unit> { 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<String>, 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<String>(MediaStore.MediaColumns.DISPLAY_NAME, MediaStore.MediaColumns.MIME_TYPE, MediaStore.MediaColumns.RELATIVE_PATH) + else + arrayOf<String>(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) }
--- /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 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="#FFFFFF" + android:alpha="0.8"> + <path + android:fillColor="@android:color/white" + android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/> +</vector>
--- 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" /> + <com.google.android.material.floatingactionbutton.FloatingActionButton + android:id="@+id/fabulous" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@string/share_text" + android:onClick="shareClicked" + android:src="@drawable/ic_action_share" + android:visibility="gone" + app:layout_constraintBottom_toBottomOf="@id/image" + app:layout_constraintEnd_toEndOf="@id/image" + app:layout_constraintStart_toEndOf="@id/image" + app:layout_constraintTop_toBottomOf="@id/image" /> + <!-- XXX: Android is prone to gratuitous cropping, hence padding. --> <TextView android:id="@+id/image_size"
--- a/app/src/main/res/layout/activity_edit_image.xml Thu Apr 08 21:00:57 2021 -0700 +++ b/app/src/main/res/layout/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" /> + <com.google.android.material.floatingactionbutton.FloatingActionButton + android:id="@+id/fabulous" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@string/share_text" + android:onClick="shareClicked" + android:src="@drawable/ic_action_share" + android:visibility="gone" + app:layout_constraintBottom_toBottomOf="@id/image" + app:layout_constraintEnd_toEndOf="@id/image" + app:layout_constraintStart_toEndOf="@id/image" + app:layout_constraintTop_toBottomOf="@id/image" /> + <TextView android:id="@+id/image_size" android:layout_width="wrap_content"
--- a/app/src/main/res/values/strings.xml Thu Apr 08 21:00:57 2021 -0700 +++ b/app/src/main/res/values/strings.xml Sat Apr 10 09:02:44 2021 -0700 @@ -26,6 +26,7 @@ <string name="rotate_text">Rotate</string> <string name="scale_text">Resize</string> <string name="selecting_message">Selecting imageā¦</string> + <string name="share_text">Share</string> <string name="title_activity_settings">Settings</string> <!-- Preference Titles -->