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>
Binary file app/src/main/res/drawable-hdpi/ic_action_share.png has changed
Binary file app/src/main/res/drawable-mdpi/ic_action_share.png has changed
Binary file app/src/main/res/drawable-xhdpi/ic_action_share.png has changed
Binary file app/src/main/res/drawable-xxhdpi/ic_action_share.png has changed
--- 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 -->