Skip to content

Commit

Permalink
Selfie: Add support for saving to custom directory
Browse files Browse the repository at this point in the history
Change-Id: Ie9b1634888a2efd9cc6cdc9d01a65fa785776101
  • Loading branch information
luk1337 committed Aug 13, 2022
1 parent e66761a commit f8166c1
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 14 deletions.
24 changes: 16 additions & 8 deletions app/src/main/java/org/lineageos/selfie/MainActivity.kt
Expand Up @@ -331,7 +331,10 @@ class MainActivity : AppCompatActivity() {
shutterButton.isEnabled = false

// Create output options object which contains file + metadata
val outputOptions = StorageUtils.getPhotoMediaStoreOutputOptions(contentResolver)
val outputOptions = StorageUtils.getPhotoMediaStoreOutputOptions(
this,
sharedPreferences.customStorageLocation
)

// Set up image capture listener, which is triggered after photo has
// been taken
Expand All @@ -353,9 +356,10 @@ class MainActivity : AppCompatActivity() {
viewFinder.foreground.alpha = anim.animatedValue as Int
}
}.start()
val msg = "Photo capture succeeded: ${output.savedUri}"
sharedPreferences.lastSavedUri = output.savedUri
updateGalleryButton(output.savedUri)
val savedUri = output.savedUri ?: sharedPreferences.lastSavedUri
val msg = "Photo capture succeeded: $savedUri"
sharedPreferences.lastSavedUri = savedUri
updateGalleryButton(savedUri)
Log.d(LOG_TAG, msg)
isTakingPhoto = false
shutterButton.isEnabled = true
Expand All @@ -374,7 +378,10 @@ class MainActivity : AppCompatActivity() {
}

// Create output options object which contains file + metadata
val outputOptions = StorageUtils.getVideoMediaStoreOutputOptions(contentResolver)
val outputOptions = StorageUtils.getVideoMediaStoreOutputOptions(
this,
sharedPreferences.customStorageLocation
)

// Play shutter sound
if (cameraSoundsUtils.playStartVideoRecording()) {
Expand All @@ -390,9 +397,10 @@ class MainActivity : AppCompatActivity() {
override fun onVideoSaved(output: OutputFileResults) {
cameraSoundsUtils.playStopVideoRecording()
stopRecordingTimer()
val msg = "Video capture succeeded: ${output.savedUri}"
sharedPreferences.lastSavedUri = output.savedUri
updateGalleryButton(output.savedUri)
val savedUri = output.savedUri ?: sharedPreferences.lastSavedUri
val msg = "Video capture succeeded: ${savedUri}"
sharedPreferences.lastSavedUri = savedUri
updateGalleryButton(savedUri)
Log.d(LOG_TAG, msg)
tookSomething = true
}
Expand Down
28 changes: 28 additions & 0 deletions app/src/main/java/org/lineageos/selfie/SettingsActivity.kt
Expand Up @@ -6,9 +6,12 @@

package org.lineageos.selfie

import android.content.Intent
import android.os.Bundle
import android.view.MenuItem
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference
import org.lineageos.selfie.utils.CameraSoundsUtils
Expand Down Expand Up @@ -39,10 +42,35 @@ class SettingsActivity : AppCompatActivity() {

class SettingsFragment : PreferenceFragmentCompat() {
private val shutterSound by lazy { findPreference<SwitchPreference>("shutter_sound") }
private val fileOutputLocation by lazy { findPreference<SwitchPreference>("custom_storage_location") }

private val openDocumentTree = registerForActivityResult(
ActivityResultContracts.OpenDocumentTree()
) {
if (it != null) {
context?.contentResolver?.takePersistableUriPermission(
it, Intent.FLAG_GRANT_WRITE_URI_PERMISSION
)
}
fileOutputLocation?.isChecked = it != null
fileOutputLocation?.sharedPreferences?.customStorageLocation = it
}

override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.root_preferences, rootKey)
shutterSound?.isEnabled = !CameraSoundsUtils.mustPlaySounds
fileOutputLocation?.apply {
isChecked = sharedPreferences?.customStorageLocation != null
onPreferenceChangeListener =
Preference.OnPreferenceChangeListener { _, newValue ->
if (newValue as Boolean) {
openDocumentTree.launch(null)
} else {
sharedPreferences?.customStorageLocation = null
}
true
}
}
}
}
}
13 changes: 13 additions & 0 deletions app/src/main/java/org/lineageos/selfie/SharedPreferencesExt.kt
Expand Up @@ -271,6 +271,19 @@ internal var SharedPreferences.aspectRatio: Int
}
}

// Custom storage location
private const val CUSTOM_STORAGE_LOCATION = "custom_storage_location"

internal var SharedPreferences.customStorageLocation: Uri?
get() {
return runCatching { Uri.parse(getString(CUSTOM_STORAGE_LOCATION, null)) }.getOrNull()
}
set(value) {
edit {
putString(CUSTOM_STORAGE_LOCATION, value?.toString())
}
}

// Shutter sound
private const val SHUTTER_SOUND_KEY = "shutter_sound"
private const val SHUTTER_SOUND_DEFAULT = true
Expand Down
45 changes: 39 additions & 6 deletions app/src/main/java/org/lineageos/selfie/utils/StorageUtils.kt
Expand Up @@ -6,12 +6,15 @@

package org.lineageos.selfie.utils

import android.content.ContentResolver
import android.content.ContentValues
import android.content.Context
import android.net.Uri
import android.provider.MediaStore
import androidx.camera.core.ImageCapture
import androidx.camera.video.MediaStoreOutputOptions
import androidx.camera.view.video.OutputFileOptions
import androidx.documentfile.provider.DocumentFile
import androidx.preference.PreferenceManager
import org.lineageos.selfie.lastSavedUri
import java.text.SimpleDateFormat
import java.util.Locale

Expand All @@ -23,8 +26,21 @@ object StorageUtils {
* Returns a new ImageCapture.OutputFileOptions to use to store a JPEG photo
*/
fun getPhotoMediaStoreOutputOptions(
contentResolver: ContentResolver
context: Context,
customStorageLocation: Uri?
): ImageCapture.OutputFileOptions {
if (customStorageLocation != null) {
val documentFile = DocumentFile.fromTreeUri(context, customStorageLocation)
val file = documentFile?.createFile("image/jpeg", getCurrentTimeString())

// Store URI in shared preferences
PreferenceManager.getDefaultSharedPreferences(context).lastSavedUri = file!!.uri

return ImageCapture.OutputFileOptions
.Builder(context.contentResolver.openOutputStream(file.uri)!!)
.build()
}

val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, getCurrentTimeString())
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
Expand All @@ -33,7 +49,7 @@ object StorageUtils {

return ImageCapture.OutputFileOptions
.Builder(
contentResolver, MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
context.contentResolver, MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
contentValues
)
.build()
Expand All @@ -43,15 +59,32 @@ object StorageUtils {
* Returns a new MediaStoreOutputOptions to use to store a MP4 video
*/
@androidx.camera.view.video.ExperimentalVideo
fun getVideoMediaStoreOutputOptions(contentResolver: ContentResolver): OutputFileOptions {
fun getVideoMediaStoreOutputOptions(
context: Context,
customStorageLocation: Uri?
): OutputFileOptions {
if (customStorageLocation != null) {
val documentFile = DocumentFile.fromTreeUri(context, customStorageLocation)
val file = documentFile?.createFile("video/mp4", getCurrentTimeString())

// Store URI in shared preferences
PreferenceManager.getDefaultSharedPreferences(context).lastSavedUri = file!!.uri

return OutputFileOptions
.builder(context.contentResolver.openFileDescriptor(file.uri, "w")!!)
.build()
}

val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, getCurrentTimeString())
put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4")
put(MediaStore.Video.Media.RELATIVE_PATH, STORAGE_DESTINATION)
}

return OutputFileOptions
.builder(contentResolver, MediaStore.Video.Media.EXTERNAL_CONTENT_URI, contentValues)
.builder(
context.contentResolver, MediaStore.Video.Media.EXTERNAL_CONTENT_URI, contentValues
)
.build()
}

Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Expand Up @@ -32,6 +32,7 @@
<string name="videos_header">Videos</string>

<!-- General Preferences -->
<string name="custom_storage_location_title">Custom storage location</string>
<string name="shutter_sound_title">Shutter sound</string>
<string name="shutter_sound_summary">Note: In some countries this setting will be ignored</string>

Expand Down
5 changes: 5 additions & 0 deletions app/src/main/res/xml/root_preferences.xml
Expand Up @@ -2,6 +2,11 @@

<PreferenceCategory app:title="@string/general_header">

<SwitchPreference
app:key="custom_storage_location"
app:persistent="false"
app:title="@string/custom_storage_location_title" />

<SwitchPreference
app:defaultValue="true"
app:key="shutter_sound"
Expand Down

0 comments on commit f8166c1

Please sign in to comment.