From 1a925bd070cf85597d3ebe0078e119e7d95ddf62 Mon Sep 17 00:00:00 2001 From: dece Date: Fri, 1 May 2020 21:06:02 +0200 Subject: [PATCH] Save files to app external dirs --- app/src/main/AndroidManifest.xml | 2 + .../dev/lowrespalmtree/zmingz/MainActivity.kt | 141 +++++++++++++++--- app/src/main/res/layout/activity_main.xml | 4 + app/src/main/res/values/strings.xml | 4 + 4 files changed, 132 insertions(+), 19 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7c5663c..3c23c00 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,8 @@ + + handlePickResult(requestCode, resultCode, data) + else -> super.onActivityResult(requestCode, resultCode, data) + } + } + + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, + grantResults: IntArray + ) { + if (requestCode == REQ_WRITE_PERM) { + if (permissions[0] == Manifest.permission.WRITE_EXTERNAL_STORAGE) { + if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { + saveWithPerm(savingPath, savingType) + return + } + } + } + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + } + fun openFile(v: View) { val req: Int val pickIntent = when (v.id) { @@ -39,17 +79,36 @@ class MainActivity: AppCompatActivity() { startActivityForResult(pickIntent, req) } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - when (requestCode) { - REQ_PICK_IMG, REQ_PICK_VID -> handlePickResult(requestCode, resultCode, data) - else -> super.onActivityResult(requestCode, resultCode, data) + fun openViewMenu(v: View) { + val menu = PopupMenu(this, v) + val saveItem = menu.menu.add(R.string.save) + val shareItem = menu.menu.add(R.string.share) + menu.setOnMenuItemClickListener { item -> + val path = when (v.id) { + imageView1.id, videoView1.id -> path1 + imageView2.id, videoView2.id -> path2 + else -> return@setOnMenuItemClickListener false + } + when (item.itemId) { + saveItem.itemId -> { + val type = when (v.id) { + imageView1.id, imageView2.id -> MediaType.IMAGE + videoView1.id, videoView2.id -> MediaType.VIDEO + else -> return@setOnMenuItemClickListener false + } + save(path, type) + } + else -> {} + } + true } + menu.show() } @Suppress("UNUSED_PARAMETER") private fun handlePickResult(requestCode: Int, resultCode: Int, data: Intent?) { val uri = data?.data - ?: return Unit .also { Log.e(TAG, "No intent or data.") } + ?: return Unit .also { Log.e(TAG, "No intent or data") } val uriPath = uri.path ?: return Unit .also { Log.e(TAG, "No path in URI") } val extension = getFileExtension(uriPath) @@ -64,6 +123,10 @@ class MainActivity: AppCompatActivity() { } } + imageLayout.visibility = View.GONE + videoLayout.visibility = View.GONE + Toast.makeText(this, R.string.please_wait, Toast.LENGTH_SHORT).show() + val outputFile1 = File.createTempFile("output1", ".$extension", cacheDir) MirrorTask(WeakReference(this), requestCode, 1) .execute(inputFile.canonicalPath, outputFile1.canonicalPath, VF1) @@ -85,9 +148,9 @@ class MainActivity: AppCompatActivity() { val vf = params[2]!! val command = "-i $inputPath -vf \"$vf\" -y $outputPath" return when (val rc = FFmpeg.execute(command)) { - RETURN_CODE_SUCCESS -> true. also { Log.d(TAG, "ffmpeg success") } - RETURN_CODE_CANCEL -> false .also { Log.d(TAG, "ffmpeg canceled") } - else -> false .also { Log.d(TAG, "ffmpeg failed with rc $rc") } + RETURN_CODE_SUCCESS -> true. also { Log.d(TAG, "FFmpeg success") } + RETURN_CODE_CANCEL -> false .also { Log.d(TAG, "FFmpeg canceled") } + else -> false .also { Log.d(TAG, "FFmpeg failed with rc $rc") } } } @@ -101,25 +164,21 @@ class MainActivity: AppCompatActivity() { internal fun updateViews(requestCode: Int, viewIndex: Int, outputPath: String) { if (requestCode == REQ_PICK_IMG) { - if (imageLayout.visibility != View.VISIBLE) { - videoLayout.visibility = View.GONE + if (imageLayout.visibility != View.VISIBLE) imageLayout.visibility = View.VISIBLE - } val mirrored = BitmapFactory.decodeFile(outputPath) val view = when (viewIndex) { - 1 -> imageView1 - 2 -> imageView2 + 1 -> { path1 = outputPath; imageView1 } + 2 -> { path2 = outputPath; imageView2 } else -> return } view.setImageBitmap(mirrored) } else if (requestCode == REQ_PICK_VID) { - if (videoLayout.visibility != View.VISIBLE) { - imageLayout.visibility = View.GONE + if (videoLayout.visibility != View.VISIBLE) videoLayout.visibility = View.VISIBLE - } val view = when (viewIndex) { - 1 -> videoView1 - 2 -> videoView2 + 1 -> { path1 = outputPath; videoView1 } + 2 -> { path2 = outputPath; videoView2 } else -> return } view.setVideoPath(outputPath) @@ -128,10 +187,54 @@ class MainActivity: AppCompatActivity() { } } + private fun save(path: String, type: MediaType) { + val writePermission = Manifest.permission.WRITE_EXTERNAL_STORAGE + val permissionStatus = checkSelfPermission(writePermission) + if (permissionStatus == PackageManager.PERMISSION_GRANTED) { + saveWithPerm(path, type) + } else { + savingPath = path + savingType = type + requestPermissions(arrayOf(writePermission), REQ_WRITE_PERM) + } + } + + private fun saveWithPerm(path: String, type: MediaType) { + // Ensure media directories for the app exist. + val envDirId = when (type) { + MediaType.IMAGE -> Environment.DIRECTORY_PICTURES + MediaType.VIDEO -> Environment.DIRECTORY_MOVIES + } + val externalMediaDir = getExternalFilesDir(envDirId) + ?: return Unit .also { Log.e(TAG, "Can't find an external dir") } + val mediaDir = File(externalMediaDir, getString(R.string.app_name)) + if (!mediaDir.exists() && !mediaDir.mkdir()) { + Log.e(TAG, "Failed to create app media dir: $mediaDir") + Toast.makeText(this, R.string.write_error, Toast.LENGTH_SHORT).show() + return + } + + val extension = getFileExtension(path).let { if (it.isEmpty()) "xxx" else it } + val outputFile = File(mediaDir.canonicalPath, "${System.currentTimeMillis()}.$extension") + if (!outputFile.createNewFile()) { + Log.e(TAG, "Failed to create new file: $outputFile") + Toast.makeText(this, R.string.write_error, Toast.LENGTH_SHORT).show() + return + } + + FileInputStream(path).use { fis -> + FileOutputStream(outputFile).use { fos -> + fos.buffered().write(fis.buffered().readBytes()) + } + } + Log.i(TAG, "File saved at $outputFile") + } + companion object { private const val TAG = "ZMINGZ" private const val REQ_PICK_IMG = 1 private const val REQ_PICK_VID = 2 + private const val REQ_WRITE_PERM = 3 private const val VF1 = "crop=iw/2:ih:0:0,split[left][tmp];[tmp]hflip[right];[left][right] hstack" private const val VF2 = "crop=iw/2:ih:iw/2:0,split[left][tmp];[tmp]hflip[right];[right][left] hstack" @@ -142,4 +245,4 @@ class MainActivity: AppCompatActivity() { } } -} +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 26342be..59383ee 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -23,6 +23,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="0.5" + android:onClick="openViewMenu" android:contentDescription="@android:string/untitled" /> @@ -50,6 +52,7 @@ android:id="@+id/videoView1" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:onClick="openViewMenu" app:layout_constraintBottom_toTopOf="@id/videoView2" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" @@ -60,6 +63,7 @@ android:id="@+id/videoView2" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:onClick="openViewMenu" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0d7bd86..1b3fc3f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -3,4 +3,8 @@ Image Video + Please wait... + Save + Share + Failed to create file.