Save files to app external dirs
This commit is contained in:
parent
97c3f59741
commit
1a925bd070
|
@ -2,6 +2,8 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="dev.lowrespalmtree.zmingz">
|
||||
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
|
|
|
@ -1,28 +1,68 @@
|
|||
package dev.lowrespalmtree.zmingz
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.BitmapFactory
|
||||
import android.os.AsyncTask
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.provider.MediaStore
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.PopupMenu
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.arthenica.mobileffmpeg.Config.RETURN_CODE_CANCEL
|
||||
import com.arthenica.mobileffmpeg.Config.RETURN_CODE_SUCCESS
|
||||
import com.arthenica.mobileffmpeg.FFmpeg
|
||||
import kotlinx.android.synthetic.main.activity_main.*
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
class MainActivity: AppCompatActivity() {
|
||||
|
||||
/** Path for first image/video. */
|
||||
private var path1 = ""
|
||||
/** Path for second image/video. */
|
||||
private var path2 = ""
|
||||
/** Temporary path for the media to save, to use after perms being granted. */
|
||||
private var savingPath = ""
|
||||
/** Temporary type for the media to save, to use after perms being granted. */
|
||||
private var savingType = MediaType.IMAGE
|
||||
|
||||
private enum class MediaType { IMAGE, VIDEO }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
permissions: Array<out String>,
|
||||
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() {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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" />
|
||||
|
||||
<ImageView
|
||||
|
@ -30,6 +31,7 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.5"
|
||||
android:onClick="openViewMenu"
|
||||
android:contentDescription="@android:string/untitled" />
|
||||
|
||||
</LinearLayout>
|
||||
|
@ -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"
|
||||
|
|
|
@ -3,4 +3,8 @@
|
|||
|
||||
<string name="open_image">Image</string>
|
||||
<string name="open_video">Video</string>
|
||||
<string name="please_wait">Please wait...</string>
|
||||
<string name="save">Save</string>
|
||||
<string name="share">Share</string>
|
||||
<string name="write_error">Failed to create file.</string>
|
||||
</resources>
|
||||
|
|
Reference in a new issue