--- /dev/null
+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?)
+}
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?)
{
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
{
- StorageOperationWithPermission(this).requestAndroidRMediaPermission()
+ StorageOperationWithPermission(this).requestStorageAccessFrameworkLocation()
}
}
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)
}
}
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
/**
*
*/
@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
{
*/
}
- 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)
}
}
+ 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()
+ }
+
}
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
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
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)
{
return (fileControl)
}
+
+ companion object
+ {
+ private val TAG = this.toString()
+ }
}
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?
{
private val TAG = this.toString()
}
-
}
--- /dev/null
+package jp.osdn.gokigen.mangle.operation.imagefile
+
+interface IImageStoreGrant
+{
+ fun grantStoreImage()
+}
\ No newline at end of file
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)
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>(
}
}
}
- return (true)
+ catch (e : Exception)
+ {
+ e.printStackTrace()
+ }
+ return (true)
+ }
+
+ override fun grantStoreImage()
+ {
+
}
private fun isExternalStorageWritable(): Boolean
class MainButtonHandler(private val activity : AppCompatActivity) : View.OnClickListener
{
- private val TAG = toString()
private lateinit var sceneChanger : SceneChanger
override fun onClick(v: View?)
{
Log.v(TAG, " - - - - - - - - - MESSAGE - - - - - - - - -")
}
+
+ companion object
+ {
+ private val TAG = this.toString()
+ }
}
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
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
init
{
Log.v(TAG, " SceneChanger is created. ")
- cameraControl = CameraControl(activity)
+ cameraControl = CameraControl(activity, accessRequest)
cameraControl.initialize()
}
{
cameraControl.finishCamera()
}
+
+ companion object
+ {
+ private val TAG = this.toString()
+ }
}