OSDN Git Service

なんか動くようになった。。
authorMRSa <mrsa@myad.jp>
Tue, 13 Oct 2020 14:24:17 +0000 (23:24 +0900)
committerMRSa <mrsa@myad.jp>
Tue, 13 Oct 2020 14:24:17 +0000 (23:24 +0900)
app/src/main/java/jp/osdn/gokigen/mangle/IScopedStorageAccessPermission.kt [new file with mode: 0644]
app/src/main/java/jp/osdn/gokigen/mangle/MainActivity.kt
app/src/main/java/jp/osdn/gokigen/mangle/StorageOperationWithPermission.kt
app/src/main/java/jp/osdn/gokigen/mangle/operation/CameraControl.kt
app/src/main/java/jp/osdn/gokigen/mangle/operation/imagefile/FileControl.kt
app/src/main/java/jp/osdn/gokigen/mangle/operation/imagefile/IImageStoreGrant.kt [new file with mode: 0644]
app/src/main/java/jp/osdn/gokigen/mangle/operation/imagefile/ImageStoreExternal.kt
app/src/main/java/jp/osdn/gokigen/mangle/scene/MainButtonHandler.kt
app/src/main/java/jp/osdn/gokigen/mangle/scene/SceneChanger.kt

diff --git a/app/src/main/java/jp/osdn/gokigen/mangle/IScopedStorageAccessPermission.kt b/app/src/main/java/jp/osdn/gokigen/mangle/IScopedStorageAccessPermission.kt
new file mode 100644 (file)
index 0000000..db74caa
--- /dev/null
@@ -0,0 +1,14 @@
+package jp.osdn.gokigen.mangle
+
+import android.content.Intent
+import android.net.Uri
+import jp.osdn.gokigen.mangle.operation.imagefile.IImageStoreGrant
+
+interface IScopedStorageAccessPermission
+{
+    fun requestStorageAccessFrameworkLocation()
+    fun responseStorageAccessFrameworkLocation(resultCode: Int, data: Intent?)
+
+    fun requestAccessPermission(requestUri : Uri, grantResponse : IImageStoreGrant)
+    fun responseAccessPermission(resultCode: Int, data: Intent?)
+}
index 14f253d..bb14815 100644 (file)
@@ -22,7 +22,8 @@ class MainActivity : AppCompatActivity()
     private val TAG = toString()
     private val mainButtonHandler : MainButtonHandler = MainButtonHandler(this)
     private val showMessage : ShowMessage = ShowMessage(this)
-    private val sceneChanger : SceneChanger = SceneChanger(this, showMessage)
+    private val accessPermission : IScopedStorageAccessPermission? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { StorageOperationWithPermission(this) } else { null }
+    private val sceneChanger : SceneChanger = SceneChanger(this, showMessage, accessPermission)
 
     override fun onCreate(savedInstanceState: Bundle?)
     {
@@ -74,7 +75,7 @@ class MainActivity : AppCompatActivity()
     {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
         {
-            StorageOperationWithPermission(this).requestAndroidRMediaPermission()
+            StorageOperationWithPermission(this).requestStorageAccessFrameworkLocation()
         }
     }
 
@@ -104,25 +105,13 @@ class MainActivity : AppCompatActivity()
     override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?)
     {
         super.onActivityResult(requestCode, resultCode, data)
-        if ((requestCode == REQUEST_CODE_MEDIA_EDIT)&&(resultCode == RESULT_OK))
+        if (requestCode == REQUEST_CODE_MEDIA_EDIT)
         {
-            Log.v(TAG, " WRITE PERMISSION GRANTED  ${data}")
+            accessPermission?.responseAccessPermission(resultCode, data)
         }
-        if ((requestCode == REQUEST_CODE_OPEN_DOCUMENT_TREE)&&(resultCode == RESULT_OK))
+        if (requestCode == REQUEST_CODE_OPEN_DOCUMENT_TREE)
         {
-            Log.v(TAG, " WRITE PERMISSION GRANTED  ${data}")
-
-            data?.data?.also { uri ->
-                val contentResolver = applicationContext.contentResolver
-                val takeFlags: Int =
-                    Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
-                contentResolver.takePersistableUriPermission(uri, takeFlags)
-                PreferenceValueInitializer().storeStorageLocationPreference(
-                    PreferenceManager.getDefaultSharedPreferences(
-                        this
-                    ), uri
-                )
-            }
+            accessPermission?.responseStorageAccessFrameworkLocation(resultCode, data)
         }
     }
 
index 255ce82..99d88c9 100644 (file)
@@ -7,10 +7,14 @@ import android.os.Build
 import android.os.Environment
 import android.provider.DocumentsContract
 import android.provider.MediaStore
+import android.util.Log
 import androidx.annotation.RequiresApi
+import androidx.appcompat.app.AppCompatActivity
 import androidx.fragment.app.FragmentActivity
 import androidx.preference.PreferenceManager
+import jp.osdn.gokigen.mangle.operation.imagefile.IImageStoreGrant
 import jp.osdn.gokigen.mangle.preference.IPreferencePropertyAccessor
+import jp.osdn.gokigen.mangle.preference.PreferenceValueInitializer
 import java.io.File
 
 /**
@@ -18,9 +22,12 @@ import java.io.File
  *
  */
 @RequiresApi(api = Build.VERSION_CODES.R)
-class StorageOperationWithPermission(private val activity: FragmentActivity)
+class StorageOperationWithPermission(private val activity: FragmentActivity) : IScopedStorageAccessPermission
 {
-    fun requestAndroidRMediaPermission()
+    private var callbackOwner : IImageStoreGrant? = null
+
+
+    override fun requestStorageAccessFrameworkLocation()
     {
         try
         {
@@ -73,10 +80,34 @@ class StorageOperationWithPermission(private val activity: FragmentActivity)
 */
     }
 
-    fun requestAndroidRMediaPermission(requestUri : Uri)
+    override fun responseStorageAccessFrameworkLocation(resultCode: Int, data: Intent?)
+    {
+        if (resultCode == AppCompatActivity.RESULT_OK)
+        {
+            Log.v(TAG, " DOCUMENT TREE GRANTED  ${data}")
+            data?.data?.also { uri ->
+                val contentResolver = activity.applicationContext.contentResolver
+                val takeFlags: Int =
+                    Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+                contentResolver.takePersistableUriPermission(uri, takeFlags)
+                PreferenceValueInitializer().storeStorageLocationPreference(
+                    PreferenceManager.getDefaultSharedPreferences(
+                        activity
+                    ), uri
+                )
+            }
+        }
+        else
+        {
+            Log.v(TAG, " DOCUMENT TREE DENIED  ${resultCode}")
+        }
+    }
+
+    override fun requestAccessPermission(requestUri : Uri, grantResponse : IImageStoreGrant)
     {
         try
         {
+            callbackOwner = grantResponse
             val urisToModify: List<Uri> = listOf( requestUri )
             val contentResolver: ContentResolver = activity.contentResolver
             val editPendingIntent = MediaStore.createWriteRequest(contentResolver, urisToModify)
@@ -88,4 +119,21 @@ class StorageOperationWithPermission(private val activity: FragmentActivity)
         }
     }
 
+    override fun responseAccessPermission(resultCode: Int, data: Intent?)
+    {
+        if (resultCode == AppCompatActivity.RESULT_OK)
+        {
+            Log.v(TAG, " WRITE PERMISSION GRANTED  ${data}")
+        }
+        else
+        {
+            Log.v(TAG, " WRITE PERMISSION DENIED  ${resultCode}")
+        }
+    }
+
+    companion object
+    {
+        private val  TAG = this.toString()
+    }
+
 }
index 78a0f60..25b75fc 100644 (file)
@@ -9,6 +9,7 @@ import androidx.camera.core.Preview
 import androidx.camera.lifecycle.ProcessCameraProvider
 import androidx.core.content.ContextCompat
 import androidx.fragment.app.FragmentActivity
+import jp.osdn.gokigen.mangle.IScopedStorageAccessPermission
 import jp.osdn.gokigen.mangle.R
 import jp.osdn.gokigen.mangle.liveview.ILiveView
 import jp.osdn.gokigen.mangle.liveview.ILiveViewRefresher
@@ -18,9 +19,8 @@ import jp.osdn.gokigen.mangle.operation.imagefile.FileControl
 import java.util.concurrent.ExecutorService
 import java.util.concurrent.Executors
 
-class CameraControl(private val activity : FragmentActivity) : ICameraControl
+class CameraControl(private val activity : FragmentActivity, private val accessPermission : IScopedStorageAccessPermission?) : ICameraControl
 {
-    private val TAG = toString()
     private lateinit var cameraExecutor: ExecutorService
     private lateinit var liveViewListener : CameraLiveViewListenerImpl
     private lateinit var fileControl : FileControl
@@ -38,7 +38,7 @@ class CameraControl(private val activity : FragmentActivity) : ICameraControl
         liveViewListener = CameraLiveViewListenerImpl(activity)
         cameraExecutor = Executors.newSingleThreadExecutor()
         storeImage = StoreImage(activity, liveViewListener)
-        fileControl = FileControl(activity, storeImage)
+        fileControl = FileControl(activity, storeImage, accessPermission)
     }
 
     override fun setRefresher(refresher: ILiveViewRefresher, imageView : ILiveView)
@@ -176,4 +176,9 @@ class CameraControl(private val activity : FragmentActivity) : ICameraControl
     {
         return (fileControl)
     }
+
+    companion object
+    {
+        private val  TAG = this.toString()
+    }
 }
index 8d5f60a..1ea4293 100644 (file)
@@ -6,14 +6,15 @@ import android.view.View
 import androidx.camera.core.ImageCapture
 import androidx.fragment.app.FragmentActivity
 import androidx.preference.PreferenceManager
+import jp.osdn.gokigen.mangle.IScopedStorageAccessPermission
 import jp.osdn.gokigen.mangle.R
 import jp.osdn.gokigen.mangle.liveview.storeimage.IStoreImage
 import jp.osdn.gokigen.mangle.preference.IPreferencePropertyAccessor
 
-class FileControl(private val context: FragmentActivity, private val storeImage : IStoreImage) : View.OnClickListener
+class FileControl(private val context: FragmentActivity, private val storeImage : IStoreImage, accessRequest : IScopedStorageAccessPermission?) : View.OnClickListener
 {
     private val storeLocal = ImageStoreLocal(context)
-    private val storeExternal = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { ImageStoreExternal(context) } else { ImageStoreExternalLegacy(context) }
+    private val storeExternal = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { ImageStoreExternal(context, accessRequest) } else { ImageStoreExternalLegacy(context) }
     private var imageCapture: ImageCapture? = null
 
     fun prepare() : ImageCapture?
@@ -103,5 +104,4 @@ class FileControl(private val context: FragmentActivity, private val storeImage
     {
         private val  TAG = this.toString()
     }
-
 }
diff --git a/app/src/main/java/jp/osdn/gokigen/mangle/operation/imagefile/IImageStoreGrant.kt b/app/src/main/java/jp/osdn/gokigen/mangle/operation/imagefile/IImageStoreGrant.kt
new file mode 100644 (file)
index 0000000..efeb267
--- /dev/null
@@ -0,0 +1,6 @@
+package jp.osdn.gokigen.mangle.operation.imagefile
+
+interface IImageStoreGrant
+{
+    fun grantStoreImage()
+}
\ No newline at end of file
index 35c26ee..dbc3af0 100644 (file)
@@ -12,98 +12,142 @@ import androidx.camera.core.ImageCapture
 import androidx.camera.core.ImageCaptureException
 import androidx.core.content.ContextCompat
 import androidx.fragment.app.FragmentActivity
+import androidx.preference.PreferenceManager
 import com.google.android.material.snackbar.Snackbar
+import jp.osdn.gokigen.mangle.IScopedStorageAccessPermission
 import jp.osdn.gokigen.mangle.R
+import jp.osdn.gokigen.mangle.preference.IPreferencePropertyAccessor
 import java.io.File
+import java.io.OutputStream
 import java.text.SimpleDateFormat
 import java.util.*
 
 @RequiresApi(api = Build.VERSION_CODES.Q)
-class ImageStoreExternal(private val context: FragmentActivity) : IImageStore
+class ImageStoreExternal(private val context: FragmentActivity, private val accessRequest : IScopedStorageAccessPermission?) : IImageStore, IImageStoreGrant
 {
-    private fun getExternalOutputDirectory(): File
+    private fun getExternalOutputDirectory(): String
     {
-        val directoryPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).path + "/" + context.getString(
-            R.string.app_location) + "/"
-        val target = File(directoryPath)
+        var uriString = ""
+        var prefString : String? = ""
         try
         {
-            target.mkdirs()
+            prefString = PreferenceManager.getDefaultSharedPreferences(context).getString(
+                IPreferencePropertyAccessor.EXTERNAL_STORAGE_LOCATION, IPreferencePropertyAccessor.EXTERNAL_STORAGE_LOCATION_DEFAULT_VALUE)
+            if (prefString != null)
+            {
+                uriString = Uri.decode(prefString)
+            }
+            if (uriString.isEmpty())
+            {
+                // 設定がない場合はデフォルトの場所に...
+                uriString = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).path + "/" + context.getString(R.string.app_location) + "/"
+            }
+
+            // DCIM 以下に変更... しかし、deprecated...
+            //uriString = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).path + uriString.substring(uriString.lastIndexOf("DCIM") + 4)
+        }
+        catch (e : Exception)
+        {
+            e.printStackTrace()
+        }
+
+        try
+        {
+            File(uriString).mkdirs()
         }
         catch (e: Exception)
         {
             e.printStackTrace()
         }
-        Log.v(TAG, "  ----- RECORD Directory PATH : $directoryPath -----")
-        return (target)
+        Log.v(TAG, "  ----- RECORD Directory PATH : ${uriString} : ${prefString} -----")
+/*
+        try
+        {
+            //context.grantUriPermission(context.getCallingPackage(), Uri.parse(Uri.decode(prefString)), Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION)
+            context.grantUriPermission(context.getCallingPackage(), Uri.parse(Uri.decode(uriString)), Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION)
+        }
+        catch (e : Exception)
+        {
+            e.printStackTrace()
+        }
+*/
+        return (uriString)
     }
 
-    override fun takePhoto(imageCapture : ImageCapture?) : Boolean
-    {
-        if ((!isExternalStorageWritable())||(imageCapture == null))
-        {
+    override fun takePhoto(imageCapture : ImageCapture?) : Boolean {
+        if ((!isExternalStorageWritable()) || (imageCapture == null)) {
             Log.v(TAG, " takePhotoExternal() : cannot write image to external.")
             return (false)
         }
         Log.v(TAG, " takePhotoExternal()")
 
-        val outputDir = getExternalOutputDirectory()
+        val outputDirString = getExternalOutputDirectory()
+        if (outputDirString.isEmpty()) {
+            return (false)
+        }
+        val outputDir = File(outputDirString)
         val resolver = context.contentResolver
 
         val mimeType = "image/jpeg"
         val now = System.currentTimeMillis()
-        val path = Environment.DIRECTORY_DCIM + File.separator + context.getString(R.string.app_location) // Environment.DIRECTORY_PICTURES  + File.separator + "gokigen" //"DCIM/aira01a/"
+        val path =
+            Environment.DIRECTORY_DCIM + File.separator + context.getString(R.string.app_location) // Environment.DIRECTORY_PICTURES  + File.separator + "gokigen" //"DCIM/aira01a/"
         val photoFile = "P" + SimpleDateFormat(FILENAME_FORMAT, Locale.US).format(now) + ".jpg"
 
-        val extStorageUri : Uri
+        val extStorageUri = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
         val values = ContentValues()
         values.put(MediaStore.Images.Media.TITLE, photoFile)
         values.put(MediaStore.Images.Media.DISPLAY_NAME, photoFile)
         values.put(MediaStore.Images.Media.MIME_TYPE, mimeType)
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
+        values.put(MediaStore.Images.Media.RELATIVE_PATH, path)
+        values.put(MediaStore.Images.Media.IS_PENDING, true)
+        values.put(MediaStore.Images.Media.DATA, outputDir.absolutePath + File.separator + photoFile)
+
+        var imageUri: Uri? = null
+        try
         {
-            values.put(MediaStore.Images.Media.RELATIVE_PATH, path)
-            values.put(MediaStore.Images.Media.IS_PENDING, true)
-            extStorageUri = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
-            values.put(MediaStore.Images.Media.DATA, outputDir.absolutePath + File.separator + photoFile)
+            imageUri = resolver.insert(extStorageUri, values)
         }
-        else
+        catch (e: Exception)
         {
-            values.put(MediaStore.Images.Media.DATA, outputDir.absolutePath + File.separator + photoFile)
-            extStorageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
+            e.printStackTrace()
         }
-
-        val imageUri = resolver.insert(extStorageUri, values)
-        if (imageUri != null)
+        if (imageUri == null)
         {
-            resolver.update(imageUri, values, null, null)
+            return (false)
+        }
 
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
-            {
-                Log.v(TAG, "  ===== StorageOperationWithPermission() : $imageUri =====")
-                //StorageOperationWithPermission(context).requestAndroidRMediaPermission(imageUri)
-            }
+        /////////////////////////////
+        Log.v(TAG, "  ===== imageUri : $imageUri =====")
+        val cursor = resolver.query(imageUri, null, null, null, null)
+        DatabaseUtils.dumpCursor(cursor)
+        cursor!!.close()
+        /////////////////////////////
 
-            /////////////////////////////
-            val cursor = resolver.query(imageUri, null, null, null, null)
-            DatabaseUtils.dumpCursor(cursor)
-            cursor!!.close()
-            /////////////////////////////
+        var openStream : OutputStream? = null
+        try
+        {
+            openStream = resolver.openOutputStream(imageUri)
+        }
+        catch (e : Exception)
+        {
+            values.put(MediaStore.Images.Media.IS_PENDING, false)
+            resolver.update(imageUri, values, null, null)
+            e.printStackTrace()
+        }
 
-            val openStream = resolver.openOutputStream(imageUri)
+        try
+        {
             if (openStream != null)
             {
                 val outputOptions = ImageCapture.OutputFileOptions.Builder(openStream).build()
                 imageCapture.takePicture(
                     outputOptions,
                     ContextCompat.getMainExecutor(context),
-                    object : ImageCapture.OnImageSavedCallback
-                    {
-                        override fun onError(e: ImageCaptureException)
-                        {
+                    object : ImageCapture.OnImageSavedCallback {
+                        override fun onError(e: ImageCaptureException) {
                             Log.e(TAG, "Photo capture failed: ${e.message} ", e)
-                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
-                            {
+                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                                 //values.clear()
                                 values.put(MediaStore.Images.Media.IS_PENDING, false)
                                 resolver.update(imageUri, values, null, null)
@@ -111,15 +155,14 @@ class ImageStoreExternal(private val context: FragmentActivity) : IImageStore
                             e.printStackTrace()
                         }
 
-                        override fun onImageSaved(output: ImageCapture.OutputFileResults)
-                        {
-                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
-                            {
+                        override fun onImageSaved(output: ImageCapture.OutputFileResults) {
+                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                                 //values.clear()
                                 values.put(MediaStore.Images.Media.IS_PENDING, false)
                                 resolver.update(imageUri, values, null, null)
                             }
-                            val msg = context.getString(R.string.capture_success) + " $path/$photoFile"
+                            val msg =
+                                context.getString(R.string.capture_success) + " $path/$photoFile"
                             //Toast.makeText(context.baseContext, msg, Toast.LENGTH_SHORT).show()
                             Snackbar.make(
                                 context.findViewById<androidx.constraintlayout.widget.ConstraintLayout>(
@@ -140,7 +183,16 @@ class ImageStoreExternal(private val context: FragmentActivity) : IImageStore
                 }
             }
         }
-        return (true)
+        catch (e : Exception)
+        {
+            e.printStackTrace()
+        }
+         return (true)
+    }
+
+    override fun grantStoreImage()
+    {
+
     }
 
     private fun isExternalStorageWritable(): Boolean
index 921f8e0..99655ab 100644 (file)
@@ -9,7 +9,6 @@ import jp.osdn.gokigen.mangle.R
 
 class MainButtonHandler(private val activity : AppCompatActivity) : View.OnClickListener
 {
-    private val TAG = toString()
     private lateinit var sceneChanger : SceneChanger
 
     override fun onClick(v: View?)
@@ -59,4 +58,9 @@ class MainButtonHandler(private val activity : AppCompatActivity) : View.OnClick
     {
         Log.v(TAG, " - - - - - - - - - MESSAGE - - - - - - - - -")
     }
+
+    companion object
+    {
+        private val  TAG = this.toString()
+    }
 }
index 8ce6e93..3e42741 100644 (file)
@@ -6,6 +6,7 @@ import androidx.fragment.app.Fragment
 import androidx.fragment.app.FragmentActivity
 import androidx.fragment.app.FragmentTransaction
 import androidx.preference.PreferenceManager
+import jp.osdn.gokigen.mangle.IScopedStorageAccessPermission
 import jp.osdn.gokigen.mangle.R
 import jp.osdn.gokigen.mangle.liveview.LiveImageViewFragment
 import jp.osdn.gokigen.mangle.logcat.LogCatFragment
@@ -16,9 +17,8 @@ import jp.osdn.gokigen.mangle.preview.PreviewFragment
 import jp.osdn.gokigen.mangle.utils.ConfirmationDialog
 import jp.osdn.gokigen.mangle.utils.ConfirmationDialog.Callback
 
-class SceneChanger(private val activity: FragmentActivity, private val informationNotify: IInformationReceiver) : IChangeScene
+class SceneChanger(private val activity: FragmentActivity, private val informationNotify: IInformationReceiver, accessRequest : IScopedStorageAccessPermission?) : IChangeScene
 {
-    private val TAG = toString()
     private val cameraControl: CameraControl
     private lateinit var liveviewFragment : LiveImageViewFragment
     private lateinit var previewFragment : PreviewFragment
@@ -28,7 +28,7 @@ class SceneChanger(private val activity: FragmentActivity, private val informati
     init
     {
         Log.v(TAG, " SceneChanger is created. ")
-        cameraControl = CameraControl(activity)
+        cameraControl = CameraControl(activity, accessRequest)
         cameraControl.initialize()
     }
 
@@ -159,4 +159,9 @@ class SceneChanger(private val activity: FragmentActivity, private val informati
     {
         cameraControl.finishCamera()
     }
+
+    companion object
+    {
+        private val  TAG = this.toString()
+    }
 }