Merge pull request #3 from SimpleMobileTools/master

upd
This commit is contained in:
solokot 2018-07-10 22:05:18 +03:00 committed by GitHub
commit 9a8bf21cc6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 1886 additions and 509 deletions

View File

@ -1,6 +1,33 @@
Changelog Changelog
========== ==========
Version 4.1.3 *(2018-07-10)*
----------------------------
* Fixed some images not being saved properly
* Fixed too dark preview and images on some devices
* Couple other UX and stability improvements
Version 4.1.2 *(2018-06-18)*
----------------------------
* Fixed third party intent handling
* Fixed some glitch causing photos be captured fully zoomed in
* Added a couple extra error message displays
Version 4.1.1 *(2018-06-12)*
----------------------------
* Adding a couple crashfixes
Version 4.1.0 *(2018-06-11)*
----------------------------
* Fully rewrote the Camera functionality under the hood for Android 5+
* Properly save image EXIF data when selected so
* Fix the rotation of images returned to third party intents
* Removed the option to show the captured photo after taking it
Version 4.0.0 *(2018-05-15)* Version 4.0.0 *(2018-05-15)*
---------------------------- ----------------------------

View File

@ -10,8 +10,8 @@ android {
applicationId "com.simplemobiletools.camera" applicationId "com.simplemobiletools.camera"
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 27 targetSdkVersion 27
versionCode 58 versionCode 62
versionName "4.0.0" versionName "4.1.3"
setProperty("archivesBaseName", "camera") setProperty("archivesBaseName", "camera")
} }
@ -45,7 +45,7 @@ ext {
} }
dependencies { dependencies {
implementation 'com.simplemobiletools:commons:4.0.18' implementation 'com.simplemobiletools:commons:4.4.1'
debugImplementation "com.squareup.leakcanary:leakcanary-android:$leakCanaryVersion" debugImplementation "com.squareup.leakcanary:leakcanary-android:$leakCanaryVersion"
releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$leakCanaryVersion" releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$leakCanaryVersion"

View File

@ -2,7 +2,6 @@ package com.simplemobiletools.camera.activities
import android.app.Activity import android.app.Activity
import android.content.Intent import android.content.Intent
import android.hardware.Camera
import android.hardware.SensorManager import android.hardware.SensorManager
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
@ -17,36 +16,37 @@ import com.bumptech.glide.request.RequestOptions
import com.simplemobiletools.camera.BuildConfig import com.simplemobiletools.camera.BuildConfig
import com.simplemobiletools.camera.R import com.simplemobiletools.camera.R
import com.simplemobiletools.camera.extensions.config import com.simplemobiletools.camera.extensions.config
import com.simplemobiletools.camera.extensions.getMyCamera
import com.simplemobiletools.camera.extensions.navBarHeight import com.simplemobiletools.camera.extensions.navBarHeight
import com.simplemobiletools.camera.helpers.* import com.simplemobiletools.camera.helpers.*
import com.simplemobiletools.camera.views.FocusRectView import com.simplemobiletools.camera.interfaces.MyCamera
import com.simplemobiletools.camera.views.Preview import com.simplemobiletools.camera.interfaces.MyPreview
import com.simplemobiletools.camera.views.Preview.PreviewListener import com.simplemobiletools.camera.views.FocusCircleView
import com.simplemobiletools.camera.views.PreviewCameraOne
import com.simplemobiletools.camera.views.PreviewCameraTwo
import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.* import com.simplemobiletools.commons.helpers.*
import com.simplemobiletools.commons.models.Release import com.simplemobiletools.commons.models.Release
import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSavedListener { class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener {
private val FADE_DELAY = 5000 private val FADE_DELAY = 5000L
lateinit var mFocusRectView: FocusRectView
lateinit var mTimerHandler: Handler lateinit var mTimerHandler: Handler
lateinit var mFadeHandler: Handler private lateinit var mOrientationEventListener: OrientationEventListener
private lateinit var mFocusCircleView: FocusCircleView
private lateinit var mFadeHandler: Handler
private lateinit var mCameraImpl: MyCamera
private var mPreview: Preview? = null private var mPreview: MyPreview? = null
private var mPreviewUri: Uri? = null private var mPreviewUri: Uri? = null
private var mFlashlightState = FLASH_OFF
private var mIsInPhotoMode = false private var mIsInPhotoMode = false
private var mIsCameraAvailable = false private var mIsCameraAvailable = false
private var mIsVideoCaptureIntent = false private var mIsVideoCaptureIntent = false
private var mIsHardwareShutterHandled = false private var mIsHardwareShutterHandled = false
private var mCurrVideoRecTimer = 0 private var mCurrVideoRecTimer = 0
private var mCurrCameraId = 0
var mLastHandledOrientation = 0 var mLastHandledOrientation = 0
lateinit var mOrientationEventListener: OrientationEventListener
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
@ -57,8 +57,6 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
appLaunched(BuildConfig.APPLICATION_ID) appLaunched(BuildConfig.APPLICATION_ID)
requestWindowFeature(Window.FEATURE_NO_TITLE) requestWindowFeature(Window.FEATURE_NO_TITLE)
if (config.alwaysOpenBackCamera)
config.lastUsedCamera = Camera.CameraInfo.CAMERA_FACING_BACK
initVariables() initVariables()
tryInitCamera() tryInitCamera()
@ -70,10 +68,11 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
if (hasStorageAndCameraPermissions()) { if (hasStorageAndCameraPermissions()) {
mPreview?.onResumed()
resumeCameraItems() resumeCameraItems()
setupPreviewImage(mIsInPhotoMode) setupPreviewImage(mIsInPhotoMode)
scheduleFadeOut() scheduleFadeOut()
mFocusRectView.setStrokeColor(config.primaryColor) mFocusCircleView.setStrokeColor(getAdjustedPrimaryColor())
if (mIsVideoCaptureIntent && mIsInPhotoMode) { if (mIsVideoCaptureIntent && mIsInPhotoMode) {
handleTogglePhotoVideo() handleTogglePhotoVideo()
@ -97,29 +96,31 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
mFadeHandler.removeCallbacksAndMessages(null) mFadeHandler.removeCallbacksAndMessages(null)
hideTimer() hideTimer()
mPreview?.releaseCamera()
mOrientationEventListener.disable() mOrientationEventListener.disable()
if (mPreview?.isWaitingForTakePictureCallback == true) { if (mPreview?.getCameraState() == STATE_PICTURE_TAKEN) {
toast(R.string.photo_not_saved) toast(R.string.photo_not_saved)
} }
mPreview?.onPaused()
} }
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
mPreview?.releaseCamera()
mPreview?.mActivity = null
mPreview = null mPreview = null
} }
private fun initVariables() { private fun initVariables() {
mIsInPhotoMode = false mIsInPhotoMode = config.initPhotoMode
mIsCameraAvailable = false mIsCameraAvailable = false
mIsVideoCaptureIntent = false mIsVideoCaptureIntent = false
mIsHardwareShutterHandled = false mIsHardwareShutterHandled = false
mCurrVideoRecTimer = 0 mCurrVideoRecTimer = 0
mCurrCameraId = 0
mLastHandledOrientation = 0 mLastHandledOrientation = 0
mCameraImpl = getMyCamera()
if (config.alwaysOpenBackCamera) {
config.lastUsedCamera = mCameraImpl.getBackCameraId().toString()
}
} }
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
@ -127,7 +128,8 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
mIsHardwareShutterHandled = true mIsHardwareShutterHandled = true
shutterPressed() shutterPressed()
true true
} else if (config.volumeButtonsAsShutter && (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP)) { } else if (!mIsHardwareShutterHandled && config.volumeButtonsAsShutter && (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP)) {
mIsHardwareShutterHandled = true
shutterPressed() shutterPressed()
true true
} else { } else {
@ -136,15 +138,16 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
} }
override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean { override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
if (keyCode == KeyEvent.KEYCODE_CAMERA) { if (keyCode == KeyEvent.KEYCODE_CAMERA || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
mIsHardwareShutterHandled = false mIsHardwareShutterHandled = false
} }
return super.onKeyUp(keyCode, event) return super.onKeyUp(keyCode, event)
} }
private fun hideToggleModeAbout() { private fun hideIntentButtons() {
toggle_photo_video.beGone() toggle_photo_video.beGone()
settings.beGone() settings.beGone()
last_photo_video_preview.beGone()
} }
private fun tryInitCamera() { private fun tryInitCamera() {
@ -153,7 +156,6 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
handlePermission(PERMISSION_WRITE_STORAGE) { handlePermission(PERMISSION_WRITE_STORAGE) {
if (it) { if (it) {
initializeCamera() initializeCamera()
handleIntent()
} else { } else {
toast(R.string.no_storage_permissions) toast(R.string.no_storage_permissions)
finish() finish()
@ -166,43 +168,55 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
} }
} }
private fun handleIntent() { private fun isImageCaptureIntent() = intent?.action == MediaStore.ACTION_IMAGE_CAPTURE || intent?.action == MediaStore.ACTION_IMAGE_CAPTURE_SECURE
private fun checkImageCaptureIntent() {
if (isImageCaptureIntent()) { if (isImageCaptureIntent()) {
hideToggleModeAbout() hideIntentButtons()
val output = intent.extras?.get(MediaStore.EXTRA_OUTPUT) val output = intent.extras?.get(MediaStore.EXTRA_OUTPUT)
if (output != null && output is Uri) { if (output != null && output is Uri) {
mPreview?.mTargetUri = output mPreview?.setTargetUri(output)
} }
} else if (intent?.action == MediaStore.ACTION_VIDEO_CAPTURE) {
mIsVideoCaptureIntent = true
hideToggleModeAbout()
shutter.setImageDrawable(resources.getDrawable(R.drawable.ic_video_rec))
} }
mPreview?.isImageCaptureIntent = isImageCaptureIntent()
} }
private fun isImageCaptureIntent() = intent?.action == MediaStore.ACTION_IMAGE_CAPTURE || intent?.action == MediaStore.ACTION_IMAGE_CAPTURE_SECURE private fun checkVideoCaptureIntent() {
if (intent?.action == MediaStore.ACTION_VIDEO_CAPTURE) {
mIsVideoCaptureIntent = true
mIsInPhotoMode = false
hideIntentButtons()
shutter.setImageResource(R.drawable.ic_video_rec)
}
}
private fun initializeCamera() { private fun initializeCamera() {
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
initButtons() initButtons()
camera_surface_view.beVisibleIf(!isLollipopPlus())
camera_texture_view.beVisibleIf(isLollipopPlus())
(btn_holder.layoutParams as RelativeLayout.LayoutParams).setMargins(0, 0, 0, (navBarHeight + resources.getDimension(R.dimen.activity_margin)).toInt()) (btn_holder.layoutParams as RelativeLayout.LayoutParams).setMargins(0, 0, 0, (navBarHeight + resources.getDimension(R.dimen.activity_margin)).toInt())
mCurrCameraId = config.lastUsedCamera checkVideoCaptureIntent()
mPreview = Preview(this, camera_view, this) mPreview = if (isLollipopPlus()) PreviewCameraTwo(this, camera_texture_view, mIsInPhotoMode) else PreviewCameraOne(this, camera_surface_view)
mPreview!!.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) view_holder.addView(mPreview as ViewGroup)
view_holder.addView(mPreview) checkImageCaptureIntent()
toggle_camera.setImageResource(if (mCurrCameraId == Camera.CameraInfo.CAMERA_FACING_BACK) R.drawable.ic_camera_front else R.drawable.ic_camera_rear) mPreview?.setIsImageCaptureIntent(isImageCaptureIntent())
mFocusRectView = FocusRectView(applicationContext) val imageDrawable = if (config.lastUsedCamera == mCameraImpl.getBackCameraId().toString()) R.drawable.ic_camera_front else R.drawable.ic_camera_rear
view_holder.addView(mFocusRectView) toggle_camera.setImageResource(imageDrawable)
mFocusCircleView = FocusCircleView(applicationContext)
view_holder.addView(mFocusCircleView)
mIsInPhotoMode = true
mTimerHandler = Handler() mTimerHandler = Handler()
mFadeHandler = Handler() mFadeHandler = Handler()
mFlashlightState = if (config.turnFlashOffAtStartup) FLASH_OFF else config.flashlightState
setupPreviewImage(true) setupPreviewImage(true)
val initialFlashlightState = if (config.turnFlashOffAtStartup) FLASH_OFF else config.flashlightState
mPreview!!.setFlashlightState(initialFlashlightState)
updateFlashlightState(initialFlashlightState)
} }
private fun initButtons() { private fun initButtons() {
@ -216,28 +230,8 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
} }
private fun toggleCamera() { private fun toggleCamera() {
if (!checkCameraAvailable()) { if (checkCameraAvailable()) {
return mPreview!!.toggleFrontBackCamera()
}
mCurrCameraId = if (mCurrCameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {
Camera.CameraInfo.CAMERA_FACING_FRONT
} else {
Camera.CameraInfo.CAMERA_FACING_BACK
}
config.lastUsedCamera = mCurrCameraId
var newIconId = R.drawable.ic_camera_front
mPreview?.releaseCamera()
if (mPreview?.setCamera(mCurrCameraId) == true) {
if (mCurrCameraId == Camera.CameraInfo.CAMERA_FACING_FRONT) {
newIconId = R.drawable.ic_camera_rear
}
toggle_camera.setImageResource(newIconId)
disableFlash()
hideTimer()
} else {
toast(R.string.camera_switch_error)
} }
} }
@ -249,41 +243,23 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
} }
private fun toggleFlash() { private fun toggleFlash() {
if (!checkCameraAvailable()) { if (checkCameraAvailable()) {
return mPreview?.toggleFlashlight()
}
mFlashlightState = ++mFlashlightState % if (mIsInPhotoMode) 3 else 2
checkFlash()
}
private fun checkFlash() {
when (mFlashlightState) {
FLASH_ON -> enableFlash()
FLASH_AUTO -> autoFlash()
else -> disableFlash()
} }
} }
private fun disableFlash() { fun updateFlashlightState(state: Int) {
mPreview?.disableFlash() config.flashlightState = state
toggle_flash.setImageResource(R.drawable.ic_flash_off) val flashDrawable = when (state) {
mFlashlightState = FLASH_OFF FLASH_OFF -> R.drawable.ic_flash_off
config.flashlightState = FLASH_OFF FLASH_ON -> R.drawable.ic_flash_on
else -> R.drawable.ic_flash_auto
}
toggle_flash.setImageResource(flashDrawable)
} }
private fun enableFlash() { fun updateCameraIcon(isUsingFrontCamera: Boolean) {
mPreview?.enableFlash() toggle_camera.setImageResource(if (isUsingFrontCamera) R.drawable.ic_camera_rear else R.drawable.ic_camera_front)
toggle_flash.setImageResource(R.drawable.ic_flash_on)
mFlashlightState = FLASH_ON
config.flashlightState = FLASH_ON
}
private fun autoFlash() {
mPreview?.autoFlash()
toggle_flash.setImageResource(R.drawable.ic_flash_auto)
mFlashlightState = FLASH_AUTO
config.flashlightState = FLASH_AUTO
} }
private fun shutterPressed() { private fun shutterPressed() {
@ -297,19 +273,12 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
toggleBottomButtons(true) toggleBottomButtons(true)
mPreview?.tryTakePicture() mPreview?.tryTakePicture()
} else { } else {
if (mPreview?.toggleRecording() == true) { mPreview?.toggleRecording()
shutter.setImageDrawable(resources.getDrawable(R.drawable.ic_video_stop))
toggle_camera.beInvisible()
showTimer()
} else {
shutter.setImageDrawable(resources.getDrawable(R.drawable.ic_video_rec))
showToggleCameraIfNeeded()
hideTimer()
}
} }
} }
fun toggleBottomButtons(hide: Boolean) { fun toggleBottomButtons(hide: Boolean) {
runOnUiThread {
val alpha = if (hide) 0f else 1f val alpha = if (hide) 0f else 1f
shutter.animate().alpha(alpha).start() shutter.animate().alpha(alpha).start()
toggle_camera.animate().alpha(alpha).start() toggle_camera.animate().alpha(alpha).start()
@ -319,6 +288,7 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
toggle_camera.isClickable = !hide toggle_camera.isClickable = !hide
toggle_flash.isClickable = !hide toggle_flash.isClickable = !hide
} }
}
private fun launchSettings() { private fun launchSettings() {
if (settings.alpha == 1f) { if (settings.alpha == 1f) {
@ -347,12 +317,14 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
return return
} }
if (mIsVideoCaptureIntent) if (mIsVideoCaptureIntent) {
mPreview?.trySwitchToVideo() mPreview?.tryInitVideoMode()
}
disableFlash() mPreview?.setFlashlightState(FLASH_OFF)
hideTimer() hideTimer()
mIsInPhotoMode = !mIsInPhotoMode mIsInPhotoMode = !mIsInPhotoMode
config.initPhotoMode = mIsInPhotoMode
showToggleCameraIfNeeded() showToggleCameraIfNeeded()
checkButtons() checkButtons()
toggleBottomButtons(false) toggleBottomButtons(false)
@ -367,14 +339,14 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
} }
private fun initPhotoMode() { private fun initPhotoMode() {
toggle_photo_video.setImageDrawable(resources.getDrawable(R.drawable.ic_video)) toggle_photo_video.setImageResource(R.drawable.ic_video)
shutter.setImageDrawable(resources.getDrawable(R.drawable.ic_shutter)) shutter.setImageResource(R.drawable.ic_shutter)
mPreview?.initPhotoMode() mPreview?.initPhotoMode()
setupPreviewImage(true) setupPreviewImage(true)
} }
private fun tryInitVideoMode() { private fun tryInitVideoMode() {
if (mPreview?.initRecorder() == true) { if (mPreview?.initVideoMode() == true) {
initVideoButtons() initVideoButtons()
} else { } else {
if (!mIsVideoCaptureIntent) { if (!mIsVideoCaptureIntent) {
@ -384,11 +356,11 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
} }
private fun initVideoButtons() { private fun initVideoButtons() {
toggle_photo_video.setImageDrawable(resources.getDrawable(R.drawable.ic_camera)) toggle_photo_video.setImageResource(R.drawable.ic_camera)
showToggleCameraIfNeeded() showToggleCameraIfNeeded()
shutter.setImageDrawable(resources.getDrawable(R.drawable.ic_video_rec)) shutter.setImageResource(R.drawable.ic_video_rec)
checkFlash()
setupPreviewImage(false) setupPreviewImage(false)
mPreview?.checkFlashlight()
} }
private fun setupPreviewImage(isPhoto: Boolean) { private fun setupPreviewImage(isPhoto: Boolean) {
@ -416,8 +388,11 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
} }
private fun scheduleFadeOut() { private fun scheduleFadeOut() {
if (!config.keepSettingsVisible) if (!config.keepSettingsVisible) {
mFadeHandler.postDelayed({ fadeOutButtons() }, FADE_DELAY.toLong()) mFadeHandler.postDelayed({
fadeOutButtons()
}, FADE_DELAY)
}
} }
private fun fadeOutButtons() { private fun fadeOutButtons() {
@ -444,11 +419,12 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LOW_PROFILE window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LOW_PROFILE
} }
private fun hideTimer() { fun toggleTimer(show: Boolean) {
video_rec_curr_timer.text = 0.getFormattedDuration() if (show) {
video_rec_curr_timer.beGone() showTimer()
mCurrVideoRecTimer = 0 } else {
mTimerHandler.removeCallbacksAndMessages(null) hideTimer()
}
} }
private fun showTimer() { private fun showTimer() {
@ -456,20 +432,26 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
setupTimer() setupTimer()
} }
private fun hideTimer() {
video_rec_curr_timer.text = 0.getFormattedDuration()
video_rec_curr_timer.beGone()
mCurrVideoRecTimer = 0
mTimerHandler.removeCallbacksAndMessages(null)
}
private fun setupTimer() { private fun setupTimer() {
runOnUiThread(object : Runnable { runOnUiThread(object : Runnable {
override fun run() { override fun run() {
video_rec_curr_timer.text = mCurrVideoRecTimer++.getFormattedDuration() video_rec_curr_timer.text = mCurrVideoRecTimer++.getFormattedDuration()
mTimerHandler.postDelayed(this, 1000) mTimerHandler.postDelayed(this, 1000L)
} }
}) })
} }
private fun resumeCameraItems() { private fun resumeCameraItems() {
showToggleCameraIfNeeded() showToggleCameraIfNeeded()
if (mPreview?.setCamera(mCurrCameraId) == true) { if (mPreview?.resumeCamera() == true) {
hideNavigationBarIcons() hideNavigationBarIcons()
checkFlash()
if (!mIsInPhotoMode) { if (!mIsInPhotoMode) {
initVideoButtons() initVideoButtons()
@ -480,7 +462,7 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
} }
private fun showToggleCameraIfNeeded() { private fun showToggleCameraIfNeeded() {
toggle_camera?.beInvisibleIf(Camera.getNumberOfCameras() <= 1) toggle_camera?.beInvisibleIf(mCameraImpl.getCountOfCameras() <= 1)
} }
private fun hasStorageAndCameraPermissions() = hasPermission(PERMISSION_WRITE_STORAGE) && hasPermission(PERMISSION_CAMERA) private fun hasStorageAndCameraPermissions() = hasPermission(PERMISSION_WRITE_STORAGE) && hasPermission(PERMISSION_CAMERA)
@ -530,26 +512,36 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
return mIsCameraAvailable return mIsCameraAvailable
} }
fun finishActivity() { fun setFlashAvailable(available: Boolean) {
setResult(Activity.RESULT_OK)
finish()
}
override fun setFlashAvailable(available: Boolean) {
if (available) { if (available) {
toggle_flash.beVisible() toggle_flash.beVisible()
} else { } else {
toggle_flash.beInvisible() toggle_flash.beInvisible()
disableFlash() toggle_flash.setImageResource(R.drawable.ic_flash_off)
mPreview?.setFlashlightState(FLASH_OFF)
} }
} }
override fun setIsCameraAvailable(available: Boolean) { fun setIsCameraAvailable(available: Boolean) {
mIsCameraAvailable = available mIsCameraAvailable = available
} }
override fun videoSaved(uri: Uri) { fun setRecordingState(isRecording: Boolean) {
setupPreviewImage(mIsInPhotoMode) runOnUiThread {
if (isRecording) {
shutter.setImageResource(R.drawable.ic_video_stop)
toggle_camera.beInvisible()
showTimer()
} else {
shutter.setImageResource(R.drawable.ic_video_rec)
showToggleCameraIfNeeded()
hideTimer()
}
}
}
fun videoSaved(uri: Uri) {
setupPreviewImage(false)
if (mIsVideoCaptureIntent) { if (mIsVideoCaptureIntent) {
Intent().apply { Intent().apply {
data = uri data = uri
@ -560,11 +552,12 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
} }
} }
override fun drawFocusRect(x: Int, y: Int) = mFocusRectView.drawFocusRect(x, y) fun drawFocusCircle(x: Float, y: Float) = mFocusCircleView.drawFocusCircle(x, y)
override fun mediaSaved(path: String) { override fun mediaSaved(path: String) {
scanPath(path) { mPreview?.imageSaved()
setupPreviewImage(mIsInPhotoMode) rescanPaths(arrayListOf(path)) {
setupPreviewImage(true)
Intent(BROADCAST_REFRESH_MEDIA).apply { Intent(BROADCAST_REFRESH_MEDIA).apply {
putExtra(REFRESH_PATH, path) putExtra(REFRESH_PATH, path)
`package` = "com.simplemobiletools.gallery" `package` = "com.simplemobiletools.gallery"
@ -573,7 +566,8 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
} }
if (isImageCaptureIntent()) { if (isImageCaptureIntent()) {
finishActivity() setResult(Activity.RESULT_OK)
finish()
} }
} }

View File

@ -8,12 +8,10 @@ import com.simplemobiletools.camera.R
import com.simplemobiletools.camera.extensions.config import com.simplemobiletools.camera.extensions.config
import com.simplemobiletools.commons.dialogs.FilePickerDialog import com.simplemobiletools.commons.dialogs.FilePickerDialog
import com.simplemobiletools.commons.dialogs.RadioGroupDialog import com.simplemobiletools.commons.dialogs.RadioGroupDialog
import com.simplemobiletools.commons.extensions.beVisibleIf import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.extensions.getAdjustedPrimaryColor
import com.simplemobiletools.commons.extensions.humanizePath
import com.simplemobiletools.commons.extensions.updateTextColors
import com.simplemobiletools.commons.helpers.LICENSE_GLIDE import com.simplemobiletools.commons.helpers.LICENSE_GLIDE
import com.simplemobiletools.commons.helpers.LICENSE_LEAK_CANARY import com.simplemobiletools.commons.helpers.LICENSE_LEAK_CANARY
import com.simplemobiletools.commons.helpers.isLollipopPlus
import com.simplemobiletools.commons.models.FAQItem import com.simplemobiletools.commons.models.FAQItem
import com.simplemobiletools.commons.models.RadioItem import com.simplemobiletools.commons.models.RadioItem
import kotlinx.android.synthetic.main.activity_settings.* import kotlinx.android.synthetic.main.activity_settings.*
@ -28,6 +26,7 @@ class SettingsActivity : SimpleActivity() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
setupPurchaseThankYou()
setupCustomizeColors() setupCustomizeColors()
setupUseEnglish() setupUseEnglish()
setupAvoidWhatsNew() setupAvoidWhatsNew()
@ -66,6 +65,13 @@ class SettingsActivity : SimpleActivity() {
} }
} }
private fun setupPurchaseThankYou() {
settings_purchase_thank_you_holder.beVisibleIf(!isThankYouInstalled())
settings_purchase_thank_you_holder.setOnClickListener {
launchPurchaseThankYouIntent()
}
}
private fun setupCustomizeColors() { private fun setupCustomizeColors() {
settings_customize_colors_holder.setOnClickListener { settings_customize_colors_holder.setOnClickListener {
startCustomizationActivity() startCustomizationActivity()
@ -105,6 +111,7 @@ class SettingsActivity : SimpleActivity() {
} }
private fun setupShowPreview() { private fun setupShowPreview() {
settings_show_preview_holder.beVisibleIf(!isLollipopPlus())
settings_show_preview.isChecked = config.isShowPreviewEnabled settings_show_preview.isChecked = config.isShowPreviewEnabled
settings_show_preview_holder.setOnClickListener { settings_show_preview_holder.setOnClickListener {
settings_show_preview.toggle() settings_show_preview.toggle()
@ -189,25 +196,29 @@ class SettingsActivity : SimpleActivity() {
} }
private fun setupPhotoQuality() { private fun setupPhotoQuality() {
settings_photo_quality.text = "${config.photoQuality}%" updatePhotoQuality(config.photoQuality)
settings_photo_quality_holder.setOnClickListener { settings_photo_quality_holder.setOnClickListener {
val items = arrayListOf( val items = arrayListOf(
RadioItem(50, "50%"), RadioItem(100, "100%"),
RadioItem(55, "55%"),
RadioItem(60, "60%"),
RadioItem(65, "65%"),
RadioItem(70, "70%"),
RadioItem(75, "75%"),
RadioItem(80, "80%"),
RadioItem(85, "85%"),
RadioItem(90, "90%"),
RadioItem(95, "95%"), RadioItem(95, "95%"),
RadioItem(100, "100%")) RadioItem(90, "90%"),
RadioItem(85, "85%"),
RadioItem(80, "80%"),
RadioItem(75, "75%"),
RadioItem(70, "70%"),
RadioItem(65, "65%"),
RadioItem(60, "60%"),
RadioItem(55, "55%"),
RadioItem(50, "50%"))
RadioGroupDialog(this@SettingsActivity, items, config.photoQuality) { RadioGroupDialog(this@SettingsActivity, items, config.photoQuality) {
config.photoQuality = it as Int config.photoQuality = it as Int
settings_photo_quality.text = "${config.photoQuality}%" updatePhotoQuality(it)
} }
} }
} }
private fun updatePhotoQuality(quality: Int) {
settings_photo_quality.text = "$quality%"
}
} }

View File

@ -1,22 +1,21 @@
package com.simplemobiletools.camera.dialogs package com.simplemobiletools.camera.dialogs
import android.hardware.Camera
import android.support.v7.app.AlertDialog import android.support.v7.app.AlertDialog
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import com.simplemobiletools.camera.R import com.simplemobiletools.camera.R
import com.simplemobiletools.camera.activities.SimpleActivity import com.simplemobiletools.camera.activities.SimpleActivity
import com.simplemobiletools.camera.extensions.config import com.simplemobiletools.camera.extensions.config
import com.simplemobiletools.camera.extensions.getAspectRatio import com.simplemobiletools.camera.models.MySize
import com.simplemobiletools.camera.helpers.Config
import com.simplemobiletools.commons.dialogs.RadioGroupDialog import com.simplemobiletools.commons.dialogs.RadioGroupDialog
import com.simplemobiletools.commons.extensions.setupDialogStuff import com.simplemobiletools.commons.extensions.setupDialogStuff
import com.simplemobiletools.commons.models.RadioItem import com.simplemobiletools.commons.models.RadioItem
import kotlinx.android.synthetic.main.dialog_change_resolution.view.* import kotlinx.android.synthetic.main.dialog_change_resolution.view.*
class ChangeResolutionDialog(val activity: SimpleActivity, val config: Config, val camera: Camera, val callback: () -> Unit) { class ChangeResolutionDialog(val activity: SimpleActivity, val isFrontCamera: Boolean, val photoResolutions: ArrayList<MySize>,
var dialog: AlertDialog val videoResolutions: ArrayList<MySize>, val openVideoResolutions: Boolean, val callback: () -> Unit) {
private val isBackCamera = activity.config.lastUsedCamera == Camera.CameraInfo.CAMERA_FACING_BACK private var dialog: AlertDialog
private val config = activity.config
init { init {
val view = LayoutInflater.from(activity).inflate(R.layout.dialog_change_resolution, null).apply { val view = LayoutInflater.from(activity).inflate(R.layout.dialog_change_resolution, null).apply {
@ -28,50 +27,54 @@ class ChangeResolutionDialog(val activity: SimpleActivity, val config: Config, v
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)
.setOnDismissListener { callback() } .setOnDismissListener { callback() }
.create().apply { .create().apply {
activity.setupDialogStuff(view, this, if (isBackCamera) R.string.back_camera else R.string.front_camera) activity.setupDialogStuff(view, this, if (isFrontCamera) R.string.front_camera else R.string.back_camera) {
if (openVideoResolutions) {
view.change_resolution_video_holder.performClick()
}
}
} }
} }
private fun setupPhotoResolutionPicker(view: View) { private fun setupPhotoResolutionPicker(view: View) {
val items = getFormattedResolutions(camera.parameters.supportedPictureSizes) val items = getFormattedResolutions(photoResolutions)
var selectionIndex = if (isBackCamera) config.backPhotoResIndex else config.frontPhotoResIndex var selectionIndex = if (isFrontCamera) config.frontPhotoResIndex else config.backPhotoResIndex
selectionIndex = Math.max(selectionIndex, 0) selectionIndex = Math.max(selectionIndex, 0)
view.change_resolution_photo_holder.setOnClickListener { view.change_resolution_photo_holder.setOnClickListener {
RadioGroupDialog(activity, items, selectionIndex) { RadioGroupDialog(activity, items, selectionIndex) {
selectionIndex = it as Int selectionIndex = it as Int
view.change_resolution_photo.text = items[selectionIndex].title view.change_resolution_photo.text = items[selectionIndex].title
if (isBackCamera) { if (isFrontCamera) {
config.backPhotoResIndex = it
} else {
config.frontPhotoResIndex = it config.frontPhotoResIndex = it
} else {
config.backPhotoResIndex = it
} }
dialog.dismiss() dialog.dismiss()
} }
} }
view.change_resolution_photo.text = items[selectionIndex].title view.change_resolution_photo.text = items.getOrNull(selectionIndex)?.title
} }
private fun setupVideoResolutionPicker(view: View) { private fun setupVideoResolutionPicker(view: View) {
val items = getFormattedResolutions(camera.parameters.supportedVideoSizes ?: camera.parameters.supportedPreviewSizes) val items = getFormattedResolutions(videoResolutions)
var selectionIndex = if (isBackCamera) config.backVideoResIndex else config.frontVideoResIndex var selectionIndex = if (isFrontCamera) config.frontVideoResIndex else config.backVideoResIndex
view.change_resolution_video_holder.setOnClickListener { view.change_resolution_video_holder.setOnClickListener {
RadioGroupDialog(activity, items, selectionIndex) { RadioGroupDialog(activity, items, selectionIndex) {
selectionIndex = it as Int selectionIndex = it as Int
view.change_resolution_video.text = items[selectionIndex].title view.change_resolution_video.text = items[selectionIndex].title
if (isBackCamera) { if (isFrontCamera) {
config.backVideoResIndex = it
} else {
config.frontVideoResIndex = it config.frontVideoResIndex = it
} else {
config.backVideoResIndex = it
} }
dialog.dismiss() dialog.dismiss()
} }
} }
view.change_resolution_video.text = items[selectionIndex].title view.change_resolution_video.text = items.getOrNull(selectionIndex)?.title
} }
private fun getFormattedResolutions(resolutions: List<Camera.Size>): ArrayList<RadioItem> { private fun getFormattedResolutions(resolutions: List<MySize>): ArrayList<RadioItem> {
val items = ArrayList<RadioItem>(resolutions.size) val items = ArrayList<RadioItem>(resolutions.size)
val sorted = resolutions.sortedByDescending { it.width * it.height } val sorted = resolutions.sortedByDescending { it.width * it.height }
sorted.forEachIndexed { index, size -> sorted.forEachIndexed { index, size ->

View File

@ -1,33 +0,0 @@
package com.simplemobiletools.camera.extensions
import android.app.Activity
import android.hardware.Camera
import android.view.Surface
fun Activity.getPreviewRotation(cameraId: Int): Int {
val info = getCameraInfo(cameraId)
val degrees = getDeviceRotationDegrees()
var result: Int
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360
result = 360 - result
} else {
result = info.orientation - degrees + 360
}
return result % 360
}
fun Activity.getDeviceRotationDegrees() = when (windowManager.defaultDisplay.rotation) {
Surface.ROTATION_90 -> 90
Surface.ROTATION_180 -> 180
Surface.ROTATION_270 -> 270
else -> 0
}
private fun getCameraInfo(cameraId: Int): Camera.CameraInfo {
val info = android.hardware.Camera.CameraInfo()
Camera.getCameraInfo(cameraId, info)
return info
}

View File

@ -4,6 +4,9 @@ import android.content.Context
import android.graphics.Point import android.graphics.Point
import android.view.WindowManager import android.view.WindowManager
import com.simplemobiletools.camera.helpers.Config import com.simplemobiletools.camera.helpers.Config
import com.simplemobiletools.camera.implementations.MyCameraOneImpl
import com.simplemobiletools.camera.implementations.MyCameraTwoImpl
import com.simplemobiletools.commons.helpers.isLollipopPlus
import java.io.File import java.io.File
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
@ -44,3 +47,5 @@ val Context.realScreenSize: Point
} }
val Context.navBarHeight: Int get() = realScreenSize.y - usableScreenSize.y val Context.navBarHeight: Int get() = realScreenSize.y - usableScreenSize.y
fun Context.getMyCamera() = if (isLollipopPlus()) MyCameraTwoImpl(applicationContext) else MyCameraOneImpl(applicationContext)

View File

@ -1,12 +0,0 @@
package com.simplemobiletools.camera.extensions
import android.hardware.Camera
import com.simplemobiletools.camera.helpers.ORIENT_LANDSCAPE_LEFT
import com.simplemobiletools.camera.helpers.ORIENT_LANDSCAPE_RIGHT
fun Int.compensateDeviceRotation(currCameraId: Int) = when {
this == ORIENT_LANDSCAPE_LEFT -> 270
this == ORIENT_LANDSCAPE_RIGHT -> 90
currCameraId == Camera.CameraInfo.CAMERA_FACING_FRONT -> 180
else -> 0
}

View File

@ -4,62 +4,3 @@ import android.content.Context
import android.hardware.Camera import android.hardware.Camera
import com.simplemobiletools.camera.R import com.simplemobiletools.camera.R
val RATIO_TOLERANCE = 0.1f
fun Camera.Size.isSixteenToNine(): Boolean {
val selectedRatio = Math.abs(width / height.toFloat())
val checkedRatio = 16 / 9.toFloat()
val diff = Math.abs(selectedRatio - checkedRatio)
return diff < RATIO_TOLERANCE
}
fun Camera.Size.isFiveToThree(): Boolean {
val selectedRatio = Math.abs(width / height.toFloat())
val checkedRatio = 5 / 3.toFloat()
val diff = Math.abs(selectedRatio - checkedRatio)
return diff < RATIO_TOLERANCE
}
fun Camera.Size.isFourToThree(): Boolean {
val selectedRatio = Math.abs(width / height.toFloat())
val checkedRatio = 4 / 3.toFloat()
val diff = Math.abs(selectedRatio - checkedRatio)
return diff < RATIO_TOLERANCE
}
fun Camera.Size.isThreeToFour(): Boolean {
val selectedRatio = Math.abs(width / height.toFloat())
val checkedRatio = 3 / 4.toFloat()
val diff = Math.abs(selectedRatio - checkedRatio)
return diff < RATIO_TOLERANCE
}
fun Camera.Size.isThreeToTwo(): Boolean {
val selectedRatio = Math.abs(width / height.toFloat())
val checkedRatio = 3 / 2.toFloat()
val diff = Math.abs(selectedRatio - checkedRatio)
return diff < RATIO_TOLERANCE
}
fun Camera.Size.isSixToFive(): Boolean {
val selectedRatio = Math.abs(width / height.toFloat())
val checkedRatio = 6 / 5.toFloat()
val diff = Math.abs(selectedRatio - checkedRatio)
return diff < RATIO_TOLERANCE
}
fun Camera.Size.isOneNineToOne() = Math.abs(1.9 - (width / height.toFloat())) < RATIO_TOLERANCE
fun Camera.Size.isSquare() = width == height
fun Camera.Size.getAspectRatio(context: Context) = when {
isSixteenToNine() -> "16:9"
isFiveToThree() -> "5:3"
isFourToThree() -> "4:3"
isThreeToFour() -> "3:4"
isThreeToTwo() -> "3:2"
isSixToFive() -> "6:5"
isOneNineToOne() -> "1.9:1"
isSquare() -> "1:1"
else -> context.resources.getString(R.string.other)
}

View File

@ -1,7 +1,6 @@
package com.simplemobiletools.camera.helpers package com.simplemobiletools.camera.helpers
import android.content.Context import android.content.Context
import android.hardware.Camera
import android.os.Environment import android.os.Environment
import com.simplemobiletools.commons.helpers.BaseConfig import com.simplemobiletools.commons.helpers.BaseConfig
import java.io.File import java.io.File
@ -31,7 +30,7 @@ class Config(context: Context) : BaseConfig(context) {
set(enabled) = prefs.edit().putBoolean(SOUND, enabled).apply() set(enabled) = prefs.edit().putBoolean(SOUND, enabled).apply()
var focusBeforeCapture: Boolean var focusBeforeCapture: Boolean
get() = prefs.getBoolean(FOCUS_BEFORE_CAPTURE, true) get() = prefs.getBoolean(FOCUS_BEFORE_CAPTURE, false)
set(focus) = prefs.edit().putBoolean(FOCUS_BEFORE_CAPTURE, focus).apply() set(focus) = prefs.edit().putBoolean(FOCUS_BEFORE_CAPTURE, focus).apply()
var volumeButtonsAsShutter: Boolean var volumeButtonsAsShutter: Boolean
@ -43,19 +42,23 @@ class Config(context: Context) : BaseConfig(context) {
set(turnFlashOffAtStartup) = prefs.edit().putBoolean(TURN_FLASH_OFF_AT_STARTUP, turnFlashOffAtStartup).apply() set(turnFlashOffAtStartup) = prefs.edit().putBoolean(TURN_FLASH_OFF_AT_STARTUP, turnFlashOffAtStartup).apply()
var flipPhotos: Boolean var flipPhotos: Boolean
get() = prefs.getBoolean(FLIP_PHOTOS, false) get() = prefs.getBoolean(FLIP_PHOTOS, true)
set(flipPhotos) = prefs.edit().putBoolean(FLIP_PHOTOS, flipPhotos).apply() set(flipPhotos) = prefs.edit().putBoolean(FLIP_PHOTOS, flipPhotos).apply()
var lastUsedCamera: Int var lastUsedCamera: String
get() = prefs.getInt(LAST_USED_CAMERA, Camera.CameraInfo.CAMERA_FACING_BACK) get() = prefs.getString(LAST_USED_CAMERA, "0")
set(cameraId) = prefs.edit().putInt(LAST_USED_CAMERA, cameraId).apply() set(cameraId) = prefs.edit().putString(LAST_USED_CAMERA, cameraId).apply()
var initPhotoMode: Boolean
get() = prefs.getBoolean(INIT_PHOTO_MODE, true)
set(initPhotoMode) = prefs.edit().putBoolean(INIT_PHOTO_MODE, initPhotoMode).apply()
var flashlightState: Int var flashlightState: Int
get() = prefs.getInt(FLASHLIGHT_STATE, FLASH_OFF) get() = prefs.getInt(FLASHLIGHT_STATE, FLASH_OFF)
set(state) = prefs.edit().putInt(FLASHLIGHT_STATE, state).apply() set(state) = prefs.edit().putInt(FLASHLIGHT_STATE, state).apply()
var backPhotoResIndex: Int var backPhotoResIndex: Int
get() = prefs.getInt(BACK_PHOTO_RESOLUTION_INDEX, -1) get() = prefs.getInt(BACK_PHOTO_RESOLUTION_INDEX, 0)
set(backPhotoResIndex) = prefs.edit().putInt(BACK_PHOTO_RESOLUTION_INDEX, backPhotoResIndex).apply() set(backPhotoResIndex) = prefs.edit().putInt(BACK_PHOTO_RESOLUTION_INDEX, backPhotoResIndex).apply()
var backVideoResIndex: Int var backVideoResIndex: Int

View File

@ -1,29 +1,48 @@
package com.simplemobiletools.camera.helpers package com.simplemobiletools.camera.helpers
val ORIENT_PORTRAIT = 0 const val ORIENT_PORTRAIT = 0
val ORIENT_LANDSCAPE_LEFT = 1 const val ORIENT_LANDSCAPE_LEFT = 1
val ORIENT_LANDSCAPE_RIGHT = 2 const val ORIENT_LANDSCAPE_RIGHT = 2
// shared preferences // shared preferences
val SAVE_PHOTOS = "save_photos" const val SAVE_PHOTOS = "save_photos"
val SHOW_PREVIEW = "show_preview" const val SHOW_PREVIEW = "show_preview"
val SOUND = "sound" const val SOUND = "sound"
val FOCUS_BEFORE_CAPTURE = "focus_before_capture" const val FOCUS_BEFORE_CAPTURE = "focus_before_capture_2"
val VOLUME_BUTTONS_AS_SHUTTER = "volume_buttons_as_shutter" const val VOLUME_BUTTONS_AS_SHUTTER = "volume_buttons_as_shutter"
val TURN_FLASH_OFF_AT_STARTUP = "turn_flash_off_at_startup" const val TURN_FLASH_OFF_AT_STARTUP = "turn_flash_off_at_startup"
val FLIP_PHOTOS = "flip_photos" const val FLIP_PHOTOS = "flip_photos"
val LAST_USED_CAMERA = "last_used_camera" const val LAST_USED_CAMERA = "last_used_camera_2"
val FLASHLIGHT_STATE = "flashlight_state" const val FLASHLIGHT_STATE = "flashlight_state"
val BACK_PHOTO_RESOLUTION_INDEX = "back_photo_resolution_index" const val INIT_PHOTO_MODE = "init_photo_mode"
val BACK_VIDEO_RESOLUTION_INDEX = "back_video_resolution_index" const val BACK_PHOTO_RESOLUTION_INDEX = "back_photo_resolution_index_2"
val FRONT_PHOTO_RESOLUTION_INDEX = "front_photo_resolution_index" const val BACK_VIDEO_RESOLUTION_INDEX = "back_video_resolution_index_2"
val FRONT_VIDEO_RESOLUTION_INDEX = "front_video_resolution_index" const val FRONT_PHOTO_RESOLUTION_INDEX = "front_photo_resolution_index_2"
val PHOTO_PREVIEW_HINT_SHOWN = "photo_preview_hint_shown" const val FRONT_VIDEO_RESOLUTION_INDEX = "front_video_resolution_index_2"
val KEEP_SETTINGS_VISIBLE = "keep_settings_visible" const val PHOTO_PREVIEW_HINT_SHOWN = "photo_preview_hint_shown"
val ALWAYS_OPEN_BACK_CAMERA = "always_open_back_camera" const val KEEP_SETTINGS_VISIBLE = "keep_settings_visible"
val SAVE_PHOTO_METADATA = "save_photo_metadata" const val ALWAYS_OPEN_BACK_CAMERA = "always_open_back_camera"
val PHOTO_QUALITY = "photo_quality" const val SAVE_PHOTO_METADATA = "save_photo_metadata"
const val PHOTO_QUALITY = "photo_quality"
val FLASH_OFF = 0 const val FLASH_OFF = 0
val FLASH_ON = 1 const val FLASH_ON = 1
val FLASH_AUTO = 2 const val FLASH_AUTO = 2
// camera states
const val STATE_INIT = 0
const val STATE_PREVIEW = 1
const val STATE_PICTURE_TAKEN = 2
const val STATE_WAITING_LOCK = 3
const val STATE_WAITING_PRECAPTURE = 4
const val STATE_WAITING_NON_PRECAPTURE = 5
const val STATE_STARTING_RECORDING = 6
const val STATE_STOPING_RECORDING = 7
const val STATE_RECORDING = 8
fun compensateDeviceRotation(orientation: Int, isUsingFrontCamera: Boolean) = when {
orientation == ORIENT_LANDSCAPE_LEFT -> 270
orientation == ORIENT_LANDSCAPE_RIGHT -> 90
isUsingFrontCamera -> 180
else -> 0
}

View File

@ -3,17 +3,14 @@ package com.simplemobiletools.camera.helpers
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.graphics.Matrix import android.graphics.Matrix
import android.hardware.Camera
import android.media.ExifInterface import android.media.ExifInterface
import android.net.Uri import android.net.Uri
import android.os.AsyncTask import android.os.AsyncTask
import android.os.Environment import android.os.Environment
import com.simplemobiletools.camera.R import com.simplemobiletools.camera.R
import com.simplemobiletools.camera.activities.MainActivity import com.simplemobiletools.camera.activities.MainActivity
import com.simplemobiletools.camera.extensions.compensateDeviceRotation
import com.simplemobiletools.camera.extensions.config import com.simplemobiletools.camera.extensions.config
import com.simplemobiletools.camera.extensions.getOutputMediaFile import com.simplemobiletools.camera.extensions.getOutputMediaFile
import com.simplemobiletools.camera.extensions.getPreviewRotation
import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.isNougatPlus import com.simplemobiletools.commons.helpers.isNougatPlus
import java.io.File import java.io.File
@ -21,14 +18,16 @@ import java.io.FileNotFoundException
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.OutputStream import java.io.OutputStream
class PhotoProcessor(val activity: MainActivity, val uri: Uri?, val currCameraId: Int, val deviceOrientation: Int) : AsyncTask<ByteArray, Void, String>() { class PhotoProcessor(val activity: MainActivity, val saveUri: Uri?, val deviceOrientation: Int, val previewRotation: Int, val isUsingFrontCamera: Boolean,
val isThirdPartyIntent: Boolean) :
AsyncTask<ByteArray, Void, String>() {
override fun doInBackground(vararg params: ByteArray): String { override fun doInBackground(vararg params: ByteArray): String {
var fos: OutputStream? = null var fos: OutputStream? = null
val path: String val path: String
try { try {
path = if (uri != null) { path = if (saveUri != null) {
uri.path saveUri.path
} else { } else {
activity.getOutputMediaFile(true) activity.getOutputMediaFile(true)
} }
@ -62,27 +61,36 @@ class PhotoProcessor(val activity: MainActivity, val uri: Uri?, val currCameraId
fos = activity.contentResolver.openOutputStream(document.uri) fos = activity.contentResolver.openOutputStream(document.uri)
} else { } else {
fos = if (uri == null) { fos = if (saveUri == null) {
FileOutputStream(photoFile) FileOutputStream(photoFile)
} else { } else {
activity.contentResolver.openOutputStream(uri) activity.contentResolver.openOutputStream(saveUri)
} }
} }
var image = BitmapFactory.decodeByteArray(data, 0, data.size) val exif = try {
val exif = ExifInterface(photoFile.toString()) ExifInterface(path)
} catch (e: Exception) {
null
}
val orient = exif?.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED)
?: ExifInterface.ORIENTATION_UNDEFINED
val deviceRot = deviceOrientation.compensateDeviceRotation(currCameraId)
val previewRot = activity.getPreviewRotation(currCameraId)
val orient = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED)
val imageRot = orient.degreesFromOrientation() val imageRot = orient.degreesFromOrientation()
val totalRotation = (imageRot + deviceRot + previewRot) % 360 val deviceRot = compensateDeviceRotation(deviceOrientation, isUsingFrontCamera)
if (activity.isPathOnSD(path) && !isNougatPlus()) { var image = BitmapFactory.decodeByteArray(data, 0, data.size)
val totalRotation = (imageRot + deviceRot + previewRotation) % 360
if (path.startsWith(activity.internalStoragePath) || isNougatPlus() && !isThirdPartyIntent) {
// do not rotate the image itself in these cases, rotate it by exif only
} else {
// make sure the image itself is rotated at third party intents
image = rotate(image, totalRotation) image = rotate(image, totalRotation)
} }
if (currCameraId == Camera.CameraInfo.CAMERA_FACING_FRONT && !activity.config.flipPhotos) { if (isUsingFrontCamera && activity.config.flipPhotos) {
val matrix = Matrix() val matrix = Matrix()
if (path.startsWith(activity.internalStoragePath)) { if (path.startsWith(activity.internalStoragePath)) {
matrix.preScale(1f, -1f) matrix.preScale(1f, -1f)
@ -99,13 +107,15 @@ class PhotoProcessor(val activity: MainActivity, val uri: Uri?, val currCameraId
try { try {
image.compress(Bitmap.CompressFormat.JPEG, activity.config.photoQuality, fos) image.compress(Bitmap.CompressFormat.JPEG, activity.config.photoQuality, fos)
if (!isThirdPartyIntent) {
activity.saveImageRotation(path, totalRotation) activity.saveImageRotation(path, totalRotation)
}
} catch (e: Exception) { } catch (e: Exception) {
activity.showErrorToast(e) activity.showErrorToast(e)
return "" return ""
} }
if (activity.config.savePhotoMetadata) { if (activity.config.savePhotoMetadata && !isThirdPartyIntent) {
val fileExif = ExifInterface(path) val fileExif = ExifInterface(path)
tempExif.copyTo(fileExif) tempExif.copyTo(fileExif)
} }

View File

@ -0,0 +1,13 @@
package com.simplemobiletools.camera.implementations
import android.content.Context
import android.hardware.Camera
import com.simplemobiletools.camera.interfaces.MyCamera
class MyCameraOneImpl(val context: Context) : MyCamera() {
override fun getFrontCameraId() = Camera.CameraInfo.CAMERA_FACING_FRONT
override fun getBackCameraId() = Camera.CameraInfo.CAMERA_FACING_BACK
override fun getCountOfCameras() = Camera.getNumberOfCameras()
}

View File

@ -0,0 +1,20 @@
package com.simplemobiletools.camera.implementations
import android.annotation.TargetApi
import android.content.Context
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraManager
import android.os.Build
import com.simplemobiletools.camera.interfaces.MyCamera
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
class MyCameraTwoImpl(val context: Context) : MyCamera() {
override fun getFrontCameraId() = CameraCharacteristics.LENS_FACING_FRONT
override fun getBackCameraId() = CameraCharacteristics.LENS_FACING_BACK
override fun getCountOfCameras(): Int {
val manager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
return manager.cameraIdList.size
}
}

View File

@ -0,0 +1,9 @@
package com.simplemobiletools.camera.interfaces
abstract class MyCamera {
abstract fun getFrontCameraId(): Int
abstract fun getBackCameraId(): Int
abstract fun getCountOfCameras(): Int
}

View File

@ -0,0 +1,41 @@
package com.simplemobiletools.camera.interfaces
import android.net.Uri
interface MyPreview {
fun onResumed()
fun onPaused()
fun setTargetUri(uri: Uri)
fun setIsImageCaptureIntent(isImageCaptureIntent: Boolean)
fun setFlashlightState(state: Int)
fun getCameraState(): Int
fun showChangeResolutionDialog()
fun toggleFrontBackCamera()
fun toggleFlashlight()
fun tryTakePicture()
fun toggleRecording()
fun tryInitVideoMode()
fun initPhotoMode()
fun initVideoMode(): Boolean
fun checkFlashlight()
fun deviceOrientationChanged()
fun resumeCamera(): Boolean
fun imageSaved()
}

View File

@ -0,0 +1,5 @@
package com.simplemobiletools.camera.models
import android.graphics.Rect
data class FocusArea(val rect: Rect, val weight: Int)

View File

@ -0,0 +1,50 @@
package com.simplemobiletools.camera.models
import android.annotation.TargetApi
import android.content.Context
import android.os.Build
import android.util.Size
import com.simplemobiletools.camera.R
data class MySize(val width: Int, val height: Int) {
val ratio = width / height.toFloat()
fun isSixteenToNine() = ratio == 16 / 9f
private fun isFiveToThree() = ratio == 5 / 3f
private fun isFourToThree() = ratio == 4 / 3f
private fun isTwoToOne() = ratio == 2f
private fun isThreeToFour() = ratio == 3 / 4f
private fun isThreeToTwo() = ratio == 3 / 2f
private fun isSixToFive() = ratio == 6 / 5f
private fun isNineteenToNine() = ratio == 19 / 9f
private fun isNineteenToEight() = ratio == 19 / 8f
private fun isOneNineToOne() = ratio == 1.9f
private fun isSquare() = width == height
fun getAspectRatio(context: Context) = when {
isSixteenToNine() -> "16:9"
isFiveToThree() -> "5:3"
isFourToThree() -> "4:3"
isThreeToFour() -> "3:4"
isThreeToTwo() -> "3:2"
isSixToFive() -> "6:5"
isOneNineToOne() -> "1.9:1"
isNineteenToNine() -> "19:9"
isNineteenToEight() -> "19:8"
isSquare() -> "1:1"
isTwoToOne() -> "2:1"
else -> context.resources.getString(R.string.other)
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
fun toSize() = Size(width, height)
}

View File

@ -0,0 +1,39 @@
package com.simplemobiletools.camera.views
import android.content.Context
import android.util.AttributeSet
import android.view.TextureView
import android.view.View
// taken from the official Camera2 sample at https://github.com/googlesamples/android-Camera2Basic
class AutoFitTextureView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : TextureView(context, attrs, defStyle) {
private var mRatioWidth = 0
private var mRatioHeight = 0
fun setAspectRatio(width: Int, height: Int) {
if (width < 0 || height < 0) {
throw IllegalArgumentException("Size cannot be negative.")
}
mRatioWidth = width
mRatioHeight = height
requestLayout()
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val width = View.MeasureSpec.getSize(widthMeasureSpec)
val height = View.MeasureSpec.getSize(heightMeasureSpec)
if (mRatioWidth == 0 || mRatioHeight == 0) {
setMeasuredDimension(width, height)
} else {
if (width < height * mRatioWidth / mRatioHeight) {
setMeasuredDimension(width, width * mRatioHeight / mRatioWidth)
} else {
setMeasuredDimension(height * mRatioWidth / mRatioHeight, height)
}
}
}
}

View File

@ -3,28 +3,23 @@ package com.simplemobiletools.camera.views
import android.content.Context import android.content.Context
import android.graphics.Canvas import android.graphics.Canvas
import android.graphics.Paint import android.graphics.Paint
import android.graphics.Rect
import android.os.Handler import android.os.Handler
import android.view.ViewGroup import android.view.ViewGroup
import com.simplemobiletools.camera.extensions.config import com.simplemobiletools.camera.extensions.config
class FocusRectView(context: Context) : ViewGroup(context) { class FocusCircleView(context: Context) : ViewGroup(context) {
private val RECT_SIZE = 50 private val CIRCLE_RADIUS = 50f
private val RECT_DURATION = 500 private val CIRCLE_DURATION = 500L
private var mDrawRect = false private var mDrawCircle = false
private var mHandler: Handler private var mHandler: Handler
private var mPaint: Paint
lateinit var mPaint: Paint private var mLastCenterX = 0f
lateinit var mRect: Rect private var mLastCenterY = 0f
init { init {
setWillNotDraw(false) setWillNotDraw(false)
mHandler = Handler() mHandler = Handler()
setupPaint()
}
private fun setupPaint() {
mPaint = Paint().apply { mPaint = Paint().apply {
style = Paint.Style.STROKE style = Paint.Style.STROKE
color = context.config.primaryColor color = context.config.primaryColor
@ -36,18 +31,19 @@ class FocusRectView(context: Context) : ViewGroup(context) {
mPaint.color = color mPaint.color = color
} }
fun drawFocusRect(x: Int, y: Int) { fun drawFocusCircle(x: Float, y: Float) {
mRect = Rect(x - RECT_SIZE, y - RECT_SIZE, x + RECT_SIZE, y + RECT_SIZE) mLastCenterX = x
toggleRect(true) mLastCenterY = y
toggleCircle(true)
mHandler.removeCallbacksAndMessages(null) mHandler.removeCallbacksAndMessages(null)
mHandler.postDelayed({ mHandler.postDelayed({
toggleRect(false) toggleCircle(false)
}, RECT_DURATION.toLong()) }, CIRCLE_DURATION)
} }
private fun toggleRect(show: Boolean) { private fun toggleCircle(show: Boolean) {
mDrawRect = show mDrawCircle = show
invalidate() invalidate()
} }
@ -55,8 +51,8 @@ class FocusRectView(context: Context) : ViewGroup(context) {
override fun onDraw(canvas: Canvas) { override fun onDraw(canvas: Canvas) {
super.onDraw(canvas) super.onDraw(canvas)
if (mDrawRect) { if (mDrawCircle) {
canvas.drawRect(mRect, mPaint) canvas.drawCircle(mLastCenterX, mLastCenterY, CIRCLE_RADIUS, mPaint)
} }
} }
} }

View File

@ -1,52 +1,65 @@
package com.simplemobiletools.camera.views package com.simplemobiletools.camera.views
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.annotation.TargetApi
import android.app.Activity
import android.content.Context import android.content.Context
import android.graphics.Point import android.graphics.Point
import android.graphics.Rect import android.graphics.Rect
import android.hardware.Camera import android.hardware.Camera
import android.media.* import android.media.AudioManager
import android.media.CamcorderProfile
import android.media.MediaPlayer
import android.media.MediaRecorder
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Environment import android.os.Environment
import android.os.Handler import android.os.Handler
import android.view.ScaleGestureDetector import android.view.*
import android.view.SurfaceHolder
import android.view.SurfaceView
import android.view.ViewGroup
import com.simplemobiletools.camera.R import com.simplemobiletools.camera.R
import com.simplemobiletools.camera.activities.MainActivity import com.simplemobiletools.camera.activities.MainActivity
import com.simplemobiletools.camera.dialogs.ChangeResolutionDialog import com.simplemobiletools.camera.dialogs.ChangeResolutionDialog
import com.simplemobiletools.camera.extensions.* import com.simplemobiletools.camera.extensions.config
import com.simplemobiletools.camera.helpers.Config import com.simplemobiletools.camera.extensions.getMyCamera
import com.simplemobiletools.camera.helpers.PhotoProcessor import com.simplemobiletools.camera.extensions.getOutputMediaFile
import com.simplemobiletools.camera.extensions.realScreenSize
import com.simplemobiletools.camera.helpers.*
import com.simplemobiletools.camera.implementations.MyCameraOneImpl
import com.simplemobiletools.camera.interfaces.MyPreview
import com.simplemobiletools.camera.models.MySize
import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.isJellyBean1Plus
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.util.* import java.util.*
class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScanCompletedListener { class PreviewCameraOne : ViewGroup, SurfaceHolder.Callback, MyPreview {
var mCamera: Camera? = null
private val FOCUS_AREA_SIZE = 100 private val FOCUS_AREA_SIZE = 100
private val PHOTO_PREVIEW_LENGTH = 500L private val PHOTO_PREVIEW_LENGTH = 500L
private val REFOCUS_PERIOD = 3000L private val REFOCUS_PERIOD = 3000L
lateinit var mSurfaceHolder: SurfaceHolder private lateinit var mSurfaceHolder: SurfaceHolder
lateinit var mSurfaceView: SurfaceView private lateinit var mSurfaceView: SurfaceView
lateinit var mCallback: PreviewListener private lateinit var mScreenSize: Point
lateinit var mScreenSize: Point private lateinit var mConfig: Config
lateinit var config: Config
private var mSupportedPreviewSizes: List<Camera.Size>? = null private var mSupportedPreviewSizes: List<Camera.Size>? = null
private var mPreviewSize: Camera.Size? = null private var mPreviewSize: Camera.Size? = null
private var mParameters: Camera.Parameters? = null private var mParameters: Camera.Parameters? = null
private var mRecorder: MediaRecorder? = null private var mRecorder: MediaRecorder? = null
private var mScaleGestureDetector: ScaleGestureDetector? = null private var mScaleGestureDetector: ScaleGestureDetector? = null
private var mZoomRatios = ArrayList<Int>() private var mZoomRatios = ArrayList<Int>()
private var mFlashlightState = FLASH_OFF
private var mCamera: Camera? = null
private var mCameraImpl: MyCameraOneImpl? = null
private var mAutoFocusHandler = Handler()
private var mActivity: MainActivity? = null
private var mTargetUri: Uri? = null
private var mCameraState = STATE_PREVIEW
private var mCurrVideoPath = "" private var mCurrVideoPath = ""
private var mCanTakePicture = false private var mCanTakePicture = false
private var mIsRecording = false private var mIsRecording = false
private var mIsVideoMode = false private var mIsInVideoMode = false
private var mIsSurfaceCreated = false private var mIsSurfaceCreated = false
private var mSwitchToVideoAsap = false private var mSwitchToVideoAsap = false
private var mSetupPreviewAfterMeasure = false private var mSetupPreviewAfterMeasure = false
@ -54,41 +67,31 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
private var mWasZooming = false private var mWasZooming = false
private var mIsPreviewShown = false private var mIsPreviewShown = false
private var mWasCameraPreviewSet = false private var mWasCameraPreviewSet = false
private var mLastClickX = 0 private var mIsImageCaptureIntent = false
private var mLastClickY = 0 private var mIsFocusingBeforeCapture = false
private var mLastClickX = 0f
private var mLastClickY = 0f
private var mCurrCameraId = 0 private var mCurrCameraId = 0
private var mMaxZoom = 0 private var mMaxZoom = 0
private var mRotationAtCapture = 0 private var mRotationAtCapture = 0
private var mIsFocusingBeforeCapture = false
private var autoFocusHandler = Handler()
var mActivity: MainActivity? = null
var isWaitingForTakePictureCallback = false
var mTargetUri: Uri? = null
var isImageCaptureIntent = false
constructor(context: Context) : super(context) constructor(context: Context) : super(context)
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
constructor(activity: MainActivity, surfaceView: SurfaceView, previewListener: PreviewListener) : super(activity) { constructor(activity: MainActivity, surfaceView: SurfaceView) : super(activity) {
mActivity = activity mActivity = activity
mCallback = previewListener
mSurfaceView = surfaceView mSurfaceView = surfaceView
mSurfaceHolder = mSurfaceView.holder mSurfaceHolder = mSurfaceView.holder
mSurfaceHolder.addCallback(this) mSurfaceHolder.addCallback(this)
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS) mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS)
mCanTakePicture = false mCameraImpl = MyCameraOneImpl(activity.applicationContext)
mIsVideoMode = false mConfig = activity.config
mIsSurfaceCreated = false
mSetupPreviewAfterMeasure = false
mCurrVideoPath = ""
config = activity.config
mScreenSize = getScreenSize() mScreenSize = getScreenSize()
initGestureDetector() initGestureDetector()
mSurfaceView.setOnTouchListener { view, event -> mSurfaceView.setOnTouchListener { view, event ->
mLastClickX = event.x.toInt() mLastClickX = event.x
mLastClickY = event.y.toInt() mLastClickY = event.y
if (mMaxZoom > 0 && mParameters?.isZoomSupported == true) { if (mMaxZoom > 0 && mParameters?.isZoomSupported == true) {
mScaleGestureDetector!!.onTouchEvent(event) mScaleGestureDetector!!.onTouchEvent(event)
@ -100,8 +103,9 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
if (mIsPreviewShown) { if (mIsPreviewShown) {
resumePreview() resumePreview()
} else { } else {
if (!mWasZooming && !mIsPreviewShown) if (!mWasZooming && !mIsPreviewShown) {
focusArea(false) focusArea(false)
}
mWasZooming = false mWasZooming = false
mSurfaceView.isSoundEffectsEnabled = true mSurfaceView.isSoundEffectsEnabled = true
@ -109,23 +113,28 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
} }
} }
fun trySwitchToVideo() { override fun onResumed() {}
override fun onPaused() {
releaseCamera()
}
override fun tryInitVideoMode() {
if (mIsSurfaceCreated) { if (mIsSurfaceCreated) {
initRecorder() initVideoMode()
} else { } else {
mSwitchToVideoAsap = true mSwitchToVideoAsap = true
} }
} }
fun setCamera(cameraId: Int): Boolean { override fun resumeCamera(): Boolean {
mCurrCameraId = cameraId
val newCamera: Camera val newCamera: Camera
try { try {
newCamera = Camera.open(cameraId) newCamera = Camera.open(mCurrCameraId)
mCallback.setIsCameraAvailable(true) mActivity!!.setIsCameraAvailable(true)
} catch (e: Exception) { } catch (e: Exception) {
mActivity!!.showErrorToast(e) mActivity!!.showErrorToast(e)
mCallback.setIsCameraAvailable(false) mActivity!!.setIsCameraAvailable(false)
return false return false
} }
@ -135,8 +144,8 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
releaseCamera() releaseCamera()
mCamera = newCamera mCamera = newCamera
if (initCamera() && mIsVideoMode) { if (initCamera() && mIsInVideoMode) {
initRecorder() initVideoMode()
} }
if (!mWasCameraPreviewSet && mIsSurfaceCreated) { if (!mWasCameraPreviewSet && mIsSurfaceCreated) {
@ -146,6 +155,8 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
return true return true
} }
override fun imageSaved() {}
private fun initCamera(): Boolean { private fun initCamera(): Boolean {
if (mCamera == null) if (mCamera == null)
return false return false
@ -169,7 +180,7 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
mParameters!!.focusMode = Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE mParameters!!.focusMode = Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE
} }
mCamera!!.setDisplayOrientation(mActivity!!.getPreviewRotation(mCurrCameraId)) mCamera!!.setDisplayOrientation(getPreviewRotation(mCurrCameraId))
mParameters!!.zoom = 0 mParameters!!.zoom = 0
updateCameraParameters() updateCameraParameters()
@ -182,10 +193,30 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
} }
} }
mCallback.setFlashAvailable(hasFlash(mCamera)) mActivity!!.setFlashAvailable(hasFlash(mCamera))
return true return true
} }
override fun toggleFrontBackCamera() {
mCurrCameraId = if (mCurrCameraId == mCameraImpl!!.getBackCameraId()) {
mCameraImpl!!.getFrontCameraId()
} else {
mCameraImpl!!.getBackCameraId()
}
mConfig.lastUsedCamera = mCurrCameraId.toString()
releaseCamera()
if (resumeCamera()) {
setFlashlightState(FLASH_OFF)
mActivity?.updateCameraIcon(mCurrCameraId == mCameraImpl!!.getFrontCameraId())
mActivity?.toggleTimer(false)
} else {
mActivity?.toast(R.string.camera_switch_error)
}
}
override fun getCameraState() = mCameraState
private fun refreshPreview() { private fun refreshPreview() {
mIsSixteenToNine = getSelectedResolution().isSixteenToNine() mIsSixteenToNine = getSelectedResolution().isSixteenToNine()
mSetupPreviewAfterMeasure = true mSetupPreviewAfterMeasure = true
@ -194,17 +225,17 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
rescheduleAutofocus() rescheduleAutofocus()
} }
private fun getSelectedResolution(): Camera.Size { private fun getSelectedResolution(): MySize {
if (mParameters == null) { if (mParameters == null) {
mParameters = mCamera!!.parameters mParameters = mCamera!!.parameters
} }
var index = getResolutionIndex() var index = getResolutionIndex()
val resolutions = if (mIsVideoMode) { val resolutions = if (mIsInVideoMode) {
mParameters!!.supportedVideoSizes ?: mParameters!!.supportedPreviewSizes mParameters!!.supportedVideoSizes ?: mParameters!!.supportedPreviewSizes
} else { } else {
mParameters!!.supportedPictureSizes mParameters!!.supportedPictureSizes
}.sortedByDescending { it.width * it.height } }.map { MySize(it.width, it.height) }.sortedByDescending { it.width * it.height }
if (index == -1) { if (index == -1) {
index = getDefaultFullscreenResolution(resolutions) ?: 0 index = getDefaultFullscreenResolution(resolutions) ?: 0
@ -214,20 +245,20 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
} }
private fun getResolutionIndex(): Int { private fun getResolutionIndex(): Int {
val isBackCamera = config.lastUsedCamera == Camera.CameraInfo.CAMERA_FACING_BACK val isBackCamera = mConfig.lastUsedCamera == Camera.CameraInfo.CAMERA_FACING_BACK.toString()
return if (mIsVideoMode) { return if (mIsInVideoMode) {
if (isBackCamera) config.backVideoResIndex else config.frontVideoResIndex if (isBackCamera) mConfig.backVideoResIndex else mConfig.frontVideoResIndex
} else { } else {
if (isBackCamera) config.backPhotoResIndex else config.frontPhotoResIndex if (isBackCamera) mConfig.backPhotoResIndex else mConfig.frontPhotoResIndex
} }
} }
private fun getDefaultFullscreenResolution(resolutions: List<Camera.Size>): Int? { private fun getDefaultFullscreenResolution(resolutions: List<MySize>): Int? {
val screenAspectRatio = mActivity!!.realScreenSize.y / mActivity!!.realScreenSize.x.toFloat() val screenAspectRatio = mActivity!!.realScreenSize.y / mActivity!!.realScreenSize.x.toFloat()
resolutions.forEachIndexed { index, size -> resolutions.forEachIndexed { index, size ->
val diff = screenAspectRatio - (size.width / size.height.toFloat()) val diff = screenAspectRatio - (size.width / size.height.toFloat())
if (Math.abs(diff) < RATIO_TOLERANCE) { if (Math.abs(diff) < 0.1f) {
config.backPhotoResIndex = index mConfig.backPhotoResIndex = index
return index return index
} }
} }
@ -281,14 +312,15 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
}) })
} }
fun tryTakePicture() { override fun tryTakePicture() {
if (config.focusBeforeCapture) { if (mConfig.focusBeforeCapture) {
focusArea(true) focusArea(true)
} else { } else {
takePicture() takePicture()
} }
} }
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private fun takePicture() { private fun takePicture() {
if (mCanTakePicture) { if (mCanTakePicture) {
val selectedResolution = getSelectedResolution() val selectedResolution = getSelectedResolution()
@ -298,19 +330,19 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
mActivity!!.toast(R.string.setting_resolution_failed) mActivity!!.toast(R.string.setting_resolution_failed)
} }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { if (isJellyBean1Plus()) {
mCamera!!.enableShutterSound(false) mCamera!!.enableShutterSound(false)
} }
mRotationAtCapture = mActivity!!.mLastHandledOrientation mRotationAtCapture = mActivity!!.mLastHandledOrientation
updateCameraParameters() updateCameraParameters()
isWaitingForTakePictureCallback = true mCameraState = STATE_PICTURE_TAKEN
mIsPreviewShown = true mIsPreviewShown = true
try { try {
Thread { Thread {
mCamera!!.takePicture(null, null, takePictureCallback) mCamera!!.takePicture(null, null, takePictureCallback)
if (config.isSoundEnabled) { if (mConfig.isSoundEnabled) {
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val volume = audioManager.getStreamVolume(AudioManager.STREAM_SYSTEM) val volume = audioManager.getStreamVolume(AudioManager.STREAM_SYSTEM)
if (volume != 0) { if (volume != 0) {
@ -332,16 +364,19 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
return@PictureCallback return@PictureCallback
} }
isWaitingForTakePictureCallback = false mCameraState = STATE_PREVIEW
if (!isImageCaptureIntent) { if (!mIsImageCaptureIntent) {
handlePreview() handlePreview()
} }
if (isImageCaptureIntent) { if (mIsImageCaptureIntent) {
if (mTargetUri != null) { if (mTargetUri != null) {
storePhoto(data) storePhoto(data)
} else { } else {
mActivity!!.finishActivity() mActivity!!.apply {
setResult(Activity.RESULT_OK)
finish()
}
} }
} else { } else {
storePhoto(data) storePhoto(data)
@ -349,14 +384,17 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
} }
private fun storePhoto(data: ByteArray) { private fun storePhoto(data: ByteArray) {
PhotoProcessor(mActivity!!, mTargetUri, mCurrCameraId, mRotationAtCapture).execute(data) val previewRotation = getPreviewRotation(mCurrCameraId)
PhotoProcessor(mActivity!!, mTargetUri, mRotationAtCapture, previewRotation, getIsUsingFrontCamera(), mIsImageCaptureIntent).execute(data)
} }
private fun getIsUsingFrontCamera() = mCurrCameraId == mActivity!!.getMyCamera().getFrontCameraId()
private fun handlePreview() { private fun handlePreview() {
if (config.isShowPreviewEnabled) { if (mConfig.isShowPreviewEnabled) {
if (!config.wasPhotoPreviewHintShown) { if (!mConfig.wasPhotoPreviewHintShown) {
mActivity!!.toast(R.string.click_to_resume_preview) mActivity!!.toast(R.string.click_to_resume_preview)
config.wasPhotoPreviewHintShown = true mConfig.wasPhotoPreviewHintShown = true
} }
} else { } else {
Handler().postDelayed({ Handler().postDelayed({
@ -388,18 +426,18 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
mCamera!!.cancelAutoFocus() mCamera!!.cancelAutoFocus()
if (mParameters!!.maxNumFocusAreas > 0) { if (mParameters!!.maxNumFocusAreas > 0) {
if (mLastClickX == 0 && mLastClickY == 0) { if (mLastClickX == 0f && mLastClickY == 0f) {
mLastClickX = width / 2 mLastClickX = width / 2.toFloat()
mLastClickY = height / 2 mLastClickY = height / 2.toFloat()
} }
val focusRect = calculateFocusArea(mLastClickX.toFloat(), mLastClickY.toFloat()) val focusRect = calculateFocusArea(mLastClickX, mLastClickY)
val focusAreas = ArrayList<Camera.Area>(1) val focusAreas = ArrayList<Camera.Area>(1)
focusAreas.add(Camera.Area(focusRect, 1000)) focusAreas.add(Camera.Area(focusRect, 1000))
mParameters!!.focusAreas = focusAreas mParameters!!.focusAreas = focusAreas
if (showFocusRect) { if (showFocusRect) {
mCallback.drawFocusRect(mLastClickX, mLastClickY) mActivity!!.drawFocusCircle(mLastClickX, mLastClickY)
} }
} }
@ -448,18 +486,21 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
} }
private fun rescheduleAutofocus() { private fun rescheduleAutofocus() {
autoFocusHandler.removeCallbacksAndMessages(null) mAutoFocusHandler.removeCallbacksAndMessages(null)
autoFocusHandler.postDelayed({ mAutoFocusHandler.postDelayed({
if (!mIsVideoMode || !mIsRecording) { if (!mIsInVideoMode || !mIsRecording) {
focusArea(false, false) focusArea(false, false)
} }
}, REFOCUS_PERIOD) }, REFOCUS_PERIOD)
} }
fun showChangeResolutionDialog() { override fun showChangeResolutionDialog() {
if (mCamera != null) { if (mCamera != null) {
val oldResolution = getSelectedResolution() val oldResolution = getSelectedResolution()
ChangeResolutionDialog(mActivity!!, config, mCamera!!) { val photoResolutions = mCamera!!.parameters.supportedPictureSizes.map { MySize(it.width, it.height) } as ArrayList<MySize>
val videoSizes = mCamera!!.parameters.supportedVideoSizes ?: mCamera!!.parameters.supportedPreviewSizes
val videoResolutions = videoSizes.map { MySize(it.width, it.height) } as ArrayList<MySize>
ChangeResolutionDialog(mActivity!!, getIsUsingFrontCamera(), photoResolutions, videoResolutions, false) {
if (oldResolution != getSelectedResolution()) { if (oldResolution != getSelectedResolution()) {
refreshPreview() refreshPreview()
} }
@ -482,7 +523,7 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
mCamera?.setPreviewDisplay(mSurfaceHolder) mCamera?.setPreviewDisplay(mSurfaceHolder)
if (mSwitchToVideoAsap) if (mSwitchToVideoAsap)
initRecorder() initVideoMode()
} catch (e: IOException) { } catch (e: IOException) {
mActivity!!.showErrorToast(e) mActivity!!.showErrorToast(e)
} }
@ -491,8 +532,8 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) { override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
mIsSurfaceCreated = true mIsSurfaceCreated = true
if (mIsVideoMode) { if (mIsInVideoMode) {
initRecorder() initVideoMode()
} }
} }
@ -557,7 +598,7 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
if (mSupportedPreviewSizes != null) { if (mSupportedPreviewSizes != null) {
// for simplicity lets assume that most displays are 16:9 and the remaining ones are 4:3 // for simplicity lets assume that most displays are 16:9 and the remaining ones are 4:3
// always set 16:9 for videos as many devices support 4:3 only in low quality // always set 16:9 for videos as many devices support 4:3 only in low quality
mPreviewSize = if (mIsSixteenToNine || mIsVideoMode) { mPreviewSize = if (mIsSixteenToNine || mIsInVideoMode) {
getOptimalPreviewSize(mSupportedPreviewSizes!!, mScreenSize.y, mScreenSize.x) getOptimalPreviewSize(mSupportedPreviewSizes!!, mScreenSize.y, mScreenSize.x)
} else { } else {
val newRatioHeight = (mScreenSize.x * (4.toDouble() / 3)).toInt() val newRatioHeight = (mScreenSize.x * (4.toDouble() / 3)).toInt()
@ -570,7 +611,7 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
if (mScreenSize.x > mPreviewSize!!.height) { if (mScreenSize.x > mPreviewSize!!.height) {
val ratio = mScreenSize.x.toFloat() / mPreviewSize!!.height val ratio = mScreenSize.x.toFloat() / mPreviewSize!!.height
lp.width = (mPreviewSize!!.height * ratio).toInt() lp.width = (mPreviewSize!!.height * ratio).toInt()
if (mIsSixteenToNine || mIsVideoMode) { if (mIsSixteenToNine || mIsInVideoMode) {
lp.height = mScreenSize.y lp.height = mScreenSize.y
} else { } else {
lp.height = (mPreviewSize!!.width * ratio).toInt() lp.height = (mPreviewSize!!.width * ratio).toInt()
@ -590,18 +631,40 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
} }
} }
fun enableFlash() { override fun setFlashlightState(state: Int) {
mParameters!!.flashMode = Camera.Parameters.FLASH_MODE_TORCH mFlashlightState = state
checkFlashlight()
}
override fun toggleFlashlight() {
val newState = ++mFlashlightState % if (mIsInVideoMode) 2 else 3
setFlashlightState(newState)
}
override fun checkFlashlight() {
when (mFlashlightState) {
FLASH_OFF -> disableFlash()
FLASH_ON -> enableFlash()
FLASH_AUTO -> setAutoFlash()
}
mActivity?.updateFlashlightState(mFlashlightState)
}
private fun disableFlash() {
mFlashlightState = FLASH_OFF
mParameters?.flashMode = Camera.Parameters.FLASH_MODE_OFF
updateCameraParameters() updateCameraParameters()
} }
fun disableFlash() { private fun enableFlash() {
mParameters!!.flashMode = Camera.Parameters.FLASH_MODE_OFF mFlashlightState = FLASH_ON
mParameters?.flashMode = Camera.Parameters.FLASH_MODE_TORCH
updateCameraParameters() updateCameraParameters()
} }
fun autoFlash() { private fun setAutoFlash() {
mParameters!!.flashMode = Camera.Parameters.FLASH_MODE_OFF mFlashlightState = FLASH_AUTO
mParameters?.flashMode = Camera.Parameters.FLASH_MODE_OFF
updateCameraParameters() updateCameraParameters()
Handler().postDelayed({ Handler().postDelayed({
@ -611,16 +674,16 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
}, 1000) }, 1000)
} }
fun initPhotoMode() { override fun initPhotoMode() {
stopRecording() stopRecording()
cleanupRecorder() cleanupRecorder()
mIsRecording = false mIsRecording = false
mIsVideoMode = false mIsInVideoMode = false
refreshPreview() refreshPreview()
} }
// VIDEO RECORDING // VIDEO RECORDING
fun initRecorder(): Boolean { override fun initVideoMode(): Boolean {
if (mCamera == null || mRecorder != null || !mIsSurfaceCreated) { if (mCamera == null || mRecorder != null || !mIsSurfaceCreated) {
return false return false
} }
@ -629,7 +692,7 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
mSwitchToVideoAsap = false mSwitchToVideoAsap = false
mIsRecording = false mIsRecording = false
mIsVideoMode = true mIsInVideoMode = true
mRecorder = MediaRecorder().apply { mRecorder = MediaRecorder().apply {
setCamera(mCamera) setCamera(mCamera)
setVideoSource(MediaRecorder.VideoSource.DEFAULT) setVideoSource(MediaRecorder.VideoSource.DEFAULT)
@ -686,9 +749,9 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
private fun checkPermissions(): Boolean { private fun checkPermissions(): Boolean {
if (mActivity!!.needsStupidWritePermissions(mCurrVideoPath)) { if (mActivity!!.needsStupidWritePermissions(mCurrVideoPath)) {
if (config.treeUri.isEmpty()) { if (mConfig.treeUri.isEmpty()) {
mActivity!!.toast(R.string.save_error_internal_storage) mActivity!!.toast(R.string.save_error_internal_storage)
config.savePhotosFolder = Environment.getExternalStorageDirectory().toString() mConfig.savePhotosFolder = Environment.getExternalStorageDirectory().toString()
releaseCamera() releaseCamera()
return false return false
} }
@ -724,26 +787,33 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
} }
} }
fun toggleRecording(): Boolean { override fun setTargetUri(uri: Uri) {
mTargetUri = uri
}
override fun setIsImageCaptureIntent(isImageCaptureIntent: Boolean) {
mIsImageCaptureIntent = isImageCaptureIntent
}
override fun toggleRecording() {
if (mIsRecording) { if (mIsRecording) {
stopRecording() stopRecording()
initRecorder() initVideoMode()
} else { } else {
startRecording() startRecording()
} }
return mIsRecording
} }
private fun getVideoRotation(): Int { private fun getVideoRotation(): Int {
val deviceRot = mActivity!!.mLastHandledOrientation.compensateDeviceRotation(mCurrCameraId) val deviceRot = compensateDeviceRotation(mActivity!!.mLastHandledOrientation, getIsUsingFrontCamera())
val previewRot = mActivity!!.getPreviewRotation(mCurrCameraId) val previewRot = getPreviewRotation(mCurrCameraId)
return (deviceRot + previewRot) % 360 return (deviceRot + previewRot) % 360
} }
fun deviceOrientationChanged() { override fun deviceOrientationChanged() {
if (mIsVideoMode && !mIsRecording) { if (mIsInVideoMode && !mIsRecording) {
mRecorder = null mRecorder = null
initRecorder() initVideoMode()
} }
} }
@ -754,6 +824,7 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
mRecorder!!.start() mRecorder!!.start()
toggleShutterSound(false) toggleShutterSound(false)
mIsRecording = true mIsRecording = true
mActivity!!.setRecordingState(true)
} catch (e: Exception) { } catch (e: Exception) {
mActivity!!.showErrorToast(e) mActivity!!.showErrorToast(e)
releaseCamera() releaseCamera()
@ -765,7 +836,10 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
try { try {
toggleShutterSound(true) toggleShutterSound(true)
mRecorder!!.stop() mRecorder!!.stop()
mActivity!!.scanPath(mCurrVideoPath) {} mActivity!!.rescanPaths(arrayListOf(mCurrVideoPath)) {
mActivity!!.videoSaved(Uri.fromFile(File(mCurrVideoPath)))
toggleShutterSound(false)
}
} catch (e: RuntimeException) { } catch (e: RuntimeException) {
mActivity!!.showErrorToast(e) mActivity!!.showErrorToast(e)
toggleShutterSound(false) toggleShutterSound(false)
@ -774,10 +848,12 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
mIsRecording = false mIsRecording = false
releaseCamera() releaseCamera()
} }
} }
mRecorder = null mRecorder = null
if (mIsRecording) {
mActivity!!.setRecordingState(false)
}
mIsRecording = false mIsRecording = false
val file = File(mCurrVideoPath) val file = File(mCurrVideoPath)
@ -787,16 +863,11 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
} }
private fun toggleShutterSound(mute: Boolean?) { private fun toggleShutterSound(mute: Boolean?) {
if (!config.isSoundEnabled) { if (!mConfig.isSoundEnabled) {
(mActivity!!.getSystemService(Context.AUDIO_SERVICE) as AudioManager).setStreamMute(AudioManager.STREAM_SYSTEM, mute!!) (mActivity!!.getSystemService(Context.AUDIO_SERVICE) as AudioManager).setStreamMute(AudioManager.STREAM_SYSTEM, mute!!)
} }
} }
override fun onScanCompleted(path: String, uri: Uri) {
mCallback.videoSaved(uri)
toggleShutterSound(false)
}
private fun hasFlash(camera: Camera?): Boolean { private fun hasFlash(camera: Camera?): Boolean {
if (camera == null) { if (camera == null) {
return false return false
@ -823,13 +894,29 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
return size return size
} }
interface PreviewListener { private fun getPreviewRotation(cameraId: Int): Int {
fun setFlashAvailable(available: Boolean) val info = getCameraInfo(cameraId)
val degrees = when (mActivity!!.windowManager.defaultDisplay.rotation) {
Surface.ROTATION_90 -> 90
Surface.ROTATION_180 -> 180
Surface.ROTATION_270 -> 270
else -> 0
}
fun setIsCameraAvailable(available: Boolean) var result: Int
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360
result = 360 - result
} else {
result = info.orientation - degrees + 360
}
fun videoSaved(uri: Uri) return result % 360
}
fun drawFocusRect(x: Int, y: Int) private fun getCameraInfo(cameraId: Int): Camera.CameraInfo {
val info = android.hardware.Camera.CameraInfo()
Camera.getCameraInfo(cameraId, info)
return info
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -7,12 +7,13 @@
android:background="@android:color/black"> android:background="@android:color/black">
<SurfaceView <SurfaceView
android:id="@+id/camera_view" android:id="@+id/camera_surface_view"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"/> android:layout_height="wrap_content"
android:visibility="gone"/>
<SurfaceView <com.simplemobiletools.camera.views.AutoFitTextureView
android:id="@+id/focus_rect_view" android:id="@+id/camera_texture_view"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"/> android:layout_height="wrap_content"/>
@ -91,4 +92,5 @@
android:text="00:00" android:text="00:00"
android:textColor="@android:color/white" android:textColor="@android:color/white"
android:visibility="gone"/> android:visibility="gone"/>
</RelativeLayout> </RelativeLayout>

View File

@ -11,6 +11,28 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<RelativeLayout
android:id="@+id/settings_purchase_thank_you_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
android:paddingRight="@dimen/normal_margin"
android:paddingTop="@dimen/activity_margin">
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/settings_purchase_thank_you"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:paddingLeft="@dimen/medium_margin"
android:paddingStart="@dimen/medium_margin"
android:text="@string/purchase_simple_thank_you"/>
</RelativeLayout>
<RelativeLayout <RelativeLayout
android:id="@+id/settings_customize_colors_holder" android:id="@+id/settings_customize_colors_holder"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -11,6 +11,7 @@
<string name="click_to_resume_preview">انقر على الصورة لاستئناف المعاينة</string> <string name="click_to_resume_preview">انقر على الصورة لاستئناف المعاينة</string>
<string name="photo_not_saved">تعذر حفظ الصورة</string> <string name="photo_not_saved">تعذر حفظ الصورة</string>
<string name="setting_resolution_failed">Setting proper resolution failed</string> <string name="setting_resolution_failed">Setting proper resolution failed</string>
<string name="video_recording_failed">Video recording failed, try using a different resolution</string>
<!-- Other aspect ratio --> <!-- Other aspect ratio -->
<string name="other">آخرى</string> <string name="other">آخرى</string>

View File

@ -11,6 +11,7 @@
<string name="click_to_resume_preview">Önbaxışa davam etmək üçün şəkilə toxun</string> <string name="click_to_resume_preview">Önbaxışa davam etmək üçün şəkilə toxun</string>
<string name="photo_not_saved">Şəkli yaddaşa salmaq olmur</string> <string name="photo_not_saved">Şəkli yaddaşa salmaq olmur</string>
<string name="setting_resolution_failed">Uyğun görüntü imkanını seçmək olmur</string> <string name="setting_resolution_failed">Uyğun görüntü imkanını seçmək olmur</string>
<string name="video_recording_failed">Video recording failed, try using a different resolution</string>
<!-- Other aspect ratio --> <!-- Other aspect ratio -->
<string name="other">digər</string> <string name="other">digər</string>

View File

@ -11,6 +11,7 @@
<string name="click_to_resume_preview">Klicke auf das Bild, um bei der Vorschau zu bleiben</string> <string name="click_to_resume_preview">Klicke auf das Bild, um bei der Vorschau zu bleiben</string>
<string name="photo_not_saved">Das Foto konnte nicht gespeichert werden</string> <string name="photo_not_saved">Das Foto konnte nicht gespeichert werden</string>
<string name="setting_resolution_failed">Korrekte Auflösung konnte nicht eingestellt werden</string> <string name="setting_resolution_failed">Korrekte Auflösung konnte nicht eingestellt werden</string>
<string name="video_recording_failed">Video recording failed, try using a different resolution</string>
<!-- Other aspect ratio --> <!-- Other aspect ratio -->
<string name="other">andere</string> <string name="other">andere</string>
@ -32,7 +33,7 @@
<string name="turn_flash_off_at_startup">Schalte Blitzlicht bei Start aus</string> <string name="turn_flash_off_at_startup">Schalte Blitzlicht bei Start aus</string>
<string name="flip_front_camera_photos_horizontally">Drehe Fotos der Frontkamera horizontal</string> <string name="flip_front_camera_photos_horizontally">Drehe Fotos der Frontkamera horizontal</string>
<string name="keep_settings_visible">Taste für Einstellungen immer sichtbar</string> <string name="keep_settings_visible">Taste für Einstellungen immer sichtbar</string>
<string name="always_open_back_camera">Starte App immer mit der Rückkamera</string> <string name="always_open_back_camera">Starte Anwendung immer mit der Rückkamera</string>
<string name="save_photo_metadata">Exif-Metadaten speichern</string> <string name="save_photo_metadata">Exif-Metadaten speichern</string>
<string name="photo_compression_quality">Fotokomprimierungsqualität</string> <string name="photo_compression_quality">Fotokomprimierungsqualität</string>
<string name="shutter">Shutter</string> <string name="shutter">Shutter</string>

View File

@ -11,6 +11,7 @@
<string name="click_to_resume_preview">Haga clic en la imagen para reanudar la vista previa</string> <string name="click_to_resume_preview">Haga clic en la imagen para reanudar la vista previa</string>
<string name="photo_not_saved">The photo could not be saved</string> <string name="photo_not_saved">The photo could not be saved</string>
<string name="setting_resolution_failed">Setting proper resolution failed</string> <string name="setting_resolution_failed">Setting proper resolution failed</string>
<string name="video_recording_failed">Video recording failed, try using a different resolution</string>
<!-- Other aspect ratio --> <!-- Other aspect ratio -->
<string name="other">otro</string> <string name="other">otro</string>

View File

@ -11,6 +11,7 @@
<string name="click_to_resume_preview">Cliquez sur l\'image pour reprendre l\'aperçu</string> <string name="click_to_resume_preview">Cliquez sur l\'image pour reprendre l\'aperçu</string>
<string name="photo_not_saved">La photo ne peut pas être sauvegardée</string> <string name="photo_not_saved">La photo ne peut pas être sauvegardée</string>
<string name="setting_resolution_failed">La définition de la résolution a échoué</string> <string name="setting_resolution_failed">La définition de la résolution a échoué</string>
<string name="video_recording_failed">Video recording failed, try using a different resolution</string>
<!-- Other aspect ratio --> <!-- Other aspect ratio -->
<string name="other">autre</string> <string name="other">autre</string>

View File

@ -11,13 +11,14 @@
<string name="click_to_resume_preview">Pulse na imaxe para voltar a vista previa</string> <string name="click_to_resume_preview">Pulse na imaxe para voltar a vista previa</string>
<string name="photo_not_saved">Non se puido gardar a fotografía</string> <string name="photo_not_saved">Non se puido gardar a fotografía</string>
<string name="setting_resolution_failed">Fallou o establecemento da resolución</string> <string name="setting_resolution_failed">Fallou o establecemento da resolución</string>
<string name="video_recording_failed">Video recording failed, try using a different resolution</string>
<!-- Other aspect ratio --> <!-- Other aspect ratio -->
<string name="other">outro</string> <string name="other">outro</string>
<!-- FAQ --> <!-- FAQ -->
<string name="faq_1_title">What photo compression quality should I set?</string> <string name="faq_1_title">Qué calidade de compresión debo escoller para a foto?</string>
<string name="faq_1_text">It depends on your goal. For generic purposes most people advise using 75%-80%, when the image is still really good quality, but the file size is reduced drastically compared to 100%.</string> <string name="faq_1_text">Depende do obxetivo. Para uso xeral a maioría de usuarias recomenda 75%-80%, que é un valor bastante bo, pero o tamaño do ficheiro redúcese drásticamente comparado co 100%.</string>
<!-- Settings --> <!-- Settings -->
<string name="save_photos">Gardar fotos e videos en</string> <string name="save_photos">Gardar fotos e videos en</string>
@ -35,7 +36,7 @@
<string name="always_open_back_camera">Abrir sempre o aplicativo coa cámara traseira</string> <string name="always_open_back_camera">Abrir sempre o aplicativo coa cámara traseira</string>
<string name="save_photo_metadata">Gardar metadatos exif da foto</string> <string name="save_photo_metadata">Gardar metadatos exif da foto</string>
<string name="photo_compression_quality">Calidade da compresión da foto</string> <string name="photo_compression_quality">Calidade da compresión da foto</string>
<string name="shutter">Shutter</string> <string name="shutter">Disparador</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->

View File

@ -11,6 +11,7 @@
<string name="click_to_resume_preview">Kliknite sliku kako biste nastavili pregled</string> <string name="click_to_resume_preview">Kliknite sliku kako biste nastavili pregled</string>
<string name="photo_not_saved">Fotografiju nije moguće spremiti</string> <string name="photo_not_saved">Fotografiju nije moguće spremiti</string>
<string name="setting_resolution_failed">Postavljanje ispravne rezolucije nije uspjelo</string> <string name="setting_resolution_failed">Postavljanje ispravne rezolucije nije uspjelo</string>
<string name="video_recording_failed">Video recording failed, try using a different resolution</string>
<!-- Other aspect ratio --> <!-- Other aspect ratio -->
<string name="other">ostalo</string> <string name="other">ostalo</string>

View File

@ -11,6 +11,7 @@
<string name="click_to_resume_preview">Click on the image to resume preview</string> <string name="click_to_resume_preview">Click on the image to resume preview</string>
<string name="photo_not_saved">The photo could not be saved</string> <string name="photo_not_saved">The photo could not be saved</string>
<string name="setting_resolution_failed">Setting proper resolution failed</string> <string name="setting_resolution_failed">Setting proper resolution failed</string>
<string name="video_recording_failed">Video recording failed, try using a different resolution</string>
<!-- Other aspect ratio --> <!-- Other aspect ratio -->
<string name="other">other</string> <string name="other">other</string>

View File

@ -6,14 +6,15 @@
<string name="camera_open_error">カメラへのアクセス時にエラーが発生しました</string> <string name="camera_open_error">カメラへのアクセス時にエラーが発生しました</string>
<string name="video_creating_error">ビデオファイルの作成時にエラーが発生しました</string> <string name="video_creating_error">ビデオファイルの作成時にエラーが発生しました</string>
<string name="video_mode_error">ビデオモードへの切り替えに失敗しました</string> <string name="video_mode_error">ビデオモードへの切り替えに失敗しました</string>
<string name="save_error_internal_storage">エラーが発生しました。保存フォルダは内部ストレージに変更されました</string> <string name="save_error_internal_storage">エラーが発生しました。保存フォルダは内部ストレージに変更されました</string>
<string name="camera_switch_error">カメラの切り替えに失敗しました</string> <string name="camera_switch_error">カメラの切り替えに失敗しました</string>
<string name="click_to_resume_preview">Click on the image to resume preview</string> <string name="click_to_resume_preview">プレビューを再開するには画像をタップ</string>
<string name="photo_not_saved">この画像は保存されていません</string> <string name="photo_not_saved">この画像は保存されていません</string>
<string name="setting_resolution_failed">Setting proper resolution failed</string> <string name="setting_resolution_failed">適切な解像度の設定に失敗しました</string>
<string name="video_recording_failed">Video recording failed, try using a different resolution</string>
<!-- Other aspect ratio --> <!-- Other aspect ratio -->
<string name="other">other</string> <string name="other">その他</string>
<!-- FAQ --> <!-- FAQ -->
<string name="faq_1_title">What photo compression quality should I set?</string> <string name="faq_1_title">What photo compression quality should I set?</string>
@ -23,27 +24,27 @@
<string name="save_photos">写真とビデオの保存先</string> <string name="save_photos">写真とビデオの保存先</string>
<string name="show_preview">キャプチャ後に写真のプレビューを表示</string> <string name="show_preview">キャプチャ後に写真のプレビューを表示</string>
<string name="shutter_sound">シャッター音</string> <string name="shutter_sound">シャッター音</string>
<string name="back_camera">背面カメラ解像度</string> <string name="back_camera">背面カメラ解像度</string>
<string name="front_camera">前面カメラ解像度</string> <string name="front_camera">前面カメラ解像度</string>
<string name="photo">画像</string> <string name="photo">画像</string>
<string name="video">動画</string> <string name="video">動画</string>
<string name="focus_before_capture">キャプチャ前に再度焦点を合わせる</string> <string name="focus_before_capture">キャプチャ前に再度焦点を合わせる</string>
<string name="volume_buttons_as_shutter">音量ボタンで撮影</string> <string name="volume_buttons_as_shutter">音量ボタンで撮影</string>
<string name="turn_flash_off_at_startup">Turn flash off at startup</string> <string name="turn_flash_off_at_startup">起動時にライトをオンにする</string>
<string name="flip_front_camera_photos_horizontally">Flip front camera photos horizontally</string> <string name="flip_front_camera_photos_horizontally">Flip front camera photos horizontally</string>
<string name="keep_settings_visible">Keep the setting buttons visible</string> <string name="keep_settings_visible">設定ボタンを表示したままにする</string>
<string name="always_open_back_camera">Always open the app with the Back camera</string> <string name="always_open_back_camera">Always open the app with the Back camera</string>
<string name="save_photo_metadata">Save photo exif metadata</string> <string name="save_photo_metadata">写真のEXIFデータを保存する</string>
<string name="photo_compression_quality">Photo compression quality</string> <string name="photo_compression_quality">写真の圧縮品質</string>
<string name="shutter">Shutter</string> <string name="shutter">シャッター</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">フラッシュ、ズーム付きのカメラ。広告はありません。</string> <string name="app_short_description">フラッシュ、ズーム付きのカメラ。広告はありません。</string>
<string name="app_long_description"> <string name="app_long_description">
カメラは、写真撮影とビデオ撮影の両方に使用できます。 前面カメラと背面カメラを切り替えたり、保存パスを変更したり、解像度を制限することができます。 フラッシュをオン/オフしたり、ピンチでズームインしたりすることができます。 ビデオの撮影中は、フラッシュを懐中電灯として使用できます。 カメラは、写真撮影とビデオ撮影の両方に使用できます。 前面カメラと背面カメラの切り替え、保存場所を変更、解像度を制限することができます。 フラッシュをオン/オフしたり、ピンチでズームインしたりすることができます。 ビデオの撮影中は、フラッシュを懐中電灯として使用できます。
ハードウェアのカメラボタンを押してこのアプリを起動したい場合は、設定 -> アプリ -> カメラ -> 無効、で付属のカメラアプリを無効にする必要があります。 ハードウェアのカメラボタンを押してこのアプリを起動したい場合は「設定 -> アプリ -> カメラ -> 無効」で端末標準のカメラアプリを無効にする必要があります。
広告や不要なアクセス許可は含まれていません。 完全にオープンソースで、ダークテーマも提供しています。 広告や不要なアクセス許可は含まれていません。 完全にオープンソースで、ダークテーマも提供しています。

View File

@ -11,6 +11,7 @@
<string name="click_to_resume_preview">미리보기를 다시 시작하려면 이미지를 클릭하십시오.</string> <string name="click_to_resume_preview">미리보기를 다시 시작하려면 이미지를 클릭하십시오.</string>
<string name="photo_not_saved">사진을 저장할 수 없습니다.</string> <string name="photo_not_saved">사진을 저장할 수 없습니다.</string>
<string name="setting_resolution_failed">해상도 설정에 실패했습니다.</string> <string name="setting_resolution_failed">해상도 설정에 실패했습니다.</string>
<string name="video_recording_failed">Video recording failed, try using a different resolution</string>
<!-- Other aspect ratio --> <!-- Other aspect ratio -->
<string name="other">other</string> <string name="other">other</string>

View File

@ -11,6 +11,7 @@
<string name="click_to_resume_preview">Paspausti ant paveikslėlio peržiūros pratęsimui</string> <string name="click_to_resume_preview">Paspausti ant paveikslėlio peržiūros pratęsimui</string>
<string name="photo_not_saved">Nuotrauka negali būti išsaugota</string> <string name="photo_not_saved">Nuotrauka negali būti išsaugota</string>
<string name="setting_resolution_failed">Nustatant tinkamą raišką įvyko klaida</string> <string name="setting_resolution_failed">Nustatant tinkamą raišką įvyko klaida</string>
<string name="video_recording_failed">Video recording failed, try using a different resolution</string>
<!-- Other aspect ratio --> <!-- Other aspect ratio -->
<string name="other">Kita</string> <string name="other">Kita</string>

View File

@ -11,6 +11,7 @@
<string name="click_to_resume_preview">Click on the image to resume preview</string> <string name="click_to_resume_preview">Click on the image to resume preview</string>
<string name="photo_not_saved">The photo could not be saved</string> <string name="photo_not_saved">The photo could not be saved</string>
<string name="setting_resolution_failed">Setting proper resolution failed</string> <string name="setting_resolution_failed">Setting proper resolution failed</string>
<string name="video_recording_failed">Video recording failed, try using a different resolution</string>
<!-- Other aspect ratio --> <!-- Other aspect ratio -->
<string name="other">Ander</string> <string name="other">Ander</string>

View File

@ -11,6 +11,7 @@
<string name="click_to_resume_preview">Kliknij obraz, aby wznowić podgląd</string> <string name="click_to_resume_preview">Kliknij obraz, aby wznowić podgląd</string>
<string name="photo_not_saved">Nie mogłem zapisać zdjęcia</string> <string name="photo_not_saved">Nie mogłem zapisać zdjęcia</string>
   <string name="setting_resolution_failed">Nie mogłem ustawić poprawnej rozdzielczości</string>    <string name="setting_resolution_failed">Nie mogłem ustawić poprawnej rozdzielczości</string>
<string name="video_recording_failed">Video recording failed, try using a different resolution</string>
<!-- Other aspect ratio --> <!-- Other aspect ratio -->
<string name="other">inne</string> <string name="other">inne</string>
@ -35,7 +36,7 @@
<string name="always_open_back_camera">Zawsze otwieraj aplikację z włączoną tylną kamerą</string> <string name="always_open_back_camera">Zawsze otwieraj aplikację z włączoną tylną kamerą</string>
   <string name="save_photo_metadata">Zapisuj dane EXIF w zdjęciach</string>    <string name="save_photo_metadata">Zapisuj dane EXIF w zdjęciach</string>
<string name="photo_compression_quality">Poziom kompresji zdjęć</string> <string name="photo_compression_quality">Poziom kompresji zdjęć</string>
<string name="shutter">Shutter</string> <string name="shutter">Migawka</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->

View File

@ -11,6 +11,7 @@
<string name="click_to_resume_preview">Clique na imagem para resumir a pré-visualização</string> <string name="click_to_resume_preview">Clique na imagem para resumir a pré-visualização</string>
<string name="photo_not_saved">A foto não pôde ser salva</string> <string name="photo_not_saved">A foto não pôde ser salva</string>
<string name="setting_resolution_failed">Setting proper resolution failed</string> <string name="setting_resolution_failed">Setting proper resolution failed</string>
<string name="video_recording_failed">Video recording failed, try using a different resolution</string>
<!-- Other aspect ratio --> <!-- Other aspect ratio -->
<string name="other">outra</string> <string name="other">outra</string>

View File

@ -11,6 +11,7 @@
<string name="click_to_resume_preview">Clique na imagem para continuar com a pré-visualização</string> <string name="click_to_resume_preview">Clique na imagem para continuar com a pré-visualização</string>
<string name="photo_not_saved">Não foi possível guardar a foto</string> <string name="photo_not_saved">Não foi possível guardar a foto</string>
<string name="setting_resolution_failed">Falha ao definir a resolução ideal</string> <string name="setting_resolution_failed">Falha ao definir a resolução ideal</string>
<string name="video_recording_failed">Video recording failed, try using a different resolution</string>
<!-- Other aspect ratio --> <!-- Other aspect ratio -->
<string name="other">outra</string> <string name="other">outra</string>
@ -35,7 +36,7 @@
<string name="always_open_back_camera">Abrir a aplicação sempre com a câmara traseira</string> <string name="always_open_back_camera">Abrir a aplicação sempre com a câmara traseira</string>
<string name="save_photo_metadata">Guardar dados exif das fotos</string> <string name="save_photo_metadata">Guardar dados exif das fotos</string>
<string name="photo_compression_quality">Qualidade da compressão</string> <string name="photo_compression_quality">Qualidade da compressão</string>
<string name="shutter">Shutter</string> <string name="shutter">Obturador</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->

View File

@ -11,6 +11,7 @@
<string name="click_to_resume_preview">Чтобы возобновить предпросмотр, нажмите на изображение</string> <string name="click_to_resume_preview">Чтобы возобновить предпросмотр, нажмите на изображение</string>
<string name="photo_not_saved">Не удалось сохранить фото</string> <string name="photo_not_saved">Не удалось сохранить фото</string>
<string name="setting_resolution_failed">Не удалось установить правильное разрешение</string> <string name="setting_resolution_failed">Не удалось установить правильное разрешение</string>
<string name="video_recording_failed">Video recording failed, try using a different resolution</string>
<!-- Other aspect ratio --> <!-- Other aspect ratio -->
<string name="other">другое</string> <string name="other">другое</string>

View File

@ -11,6 +11,7 @@
<string name="click_to_resume_preview">Pre pokračovanie prehliadania kliknite na obrázok</string> <string name="click_to_resume_preview">Pre pokračovanie prehliadania kliknite na obrázok</string>
<string name="photo_not_saved">Fotografiu sa nepodarilo uložiť</string> <string name="photo_not_saved">Fotografiu sa nepodarilo uložiť</string>
<string name="setting_resolution_failed">Nepodarilo sa nastaviť správne rozlíšenie</string> <string name="setting_resolution_failed">Nepodarilo sa nastaviť správne rozlíšenie</string>
<string name="video_recording_failed">Nahrávanie videa zlyhalo, skúste použiť iné rozlíšenie</string>
<!-- Other aspect ratio --> <!-- Other aspect ratio -->
<string name="other">iný</string> <string name="other">iný</string>

View File

@ -2,7 +2,7 @@
<resources> <resources>
<string name="app_name">Simple Camera</string> <string name="app_name">Simple Camera</string>
<string name="app_launcher_name">Kamera</string> <string name="app_launcher_name">Kamera</string>
<string name="camera_unavailable">Kameran inte tillgänglig</string> <string name="camera_unavailable">Kameran är inte tillgänglig</string>
<string name="camera_open_error">Ett fel uppstod vid åtkomst till kameran</string> <string name="camera_open_error">Ett fel uppstod vid åtkomst till kameran</string>
<string name="video_creating_error">Ett fel uppstod när videofilen skulle skapas</string> <string name="video_creating_error">Ett fel uppstod när videofilen skulle skapas</string>
<string name="video_mode_error">Byte till videoläge misslyckades</string> <string name="video_mode_error">Byte till videoläge misslyckades</string>
@ -11,6 +11,7 @@
<string name="click_to_resume_preview">Tryck på bilden för att återuppta förhandsgranskningen</string> <string name="click_to_resume_preview">Tryck på bilden för att återuppta förhandsgranskningen</string>
<string name="photo_not_saved">Fotot kunde inte sparas</string> <string name="photo_not_saved">Fotot kunde inte sparas</string>
<string name="setting_resolution_failed">Det gick inte att ställa in den valda upplösningen</string> <string name="setting_resolution_failed">Det gick inte att ställa in den valda upplösningen</string>
<string name="video_recording_failed">Video recording failed, try using a different resolution</string>
<!-- Other aspect ratio --> <!-- Other aspect ratio -->
<string name="other">annat</string> <string name="other">annat</string>
@ -35,7 +36,7 @@
<string name="always_open_back_camera">Växla till den bakre kameran när appen öppnas</string> <string name="always_open_back_camera">Växla till den bakre kameran när appen öppnas</string>
<string name="save_photo_metadata">Spara fotonas Exif-metadata</string> <string name="save_photo_metadata">Spara fotonas Exif-metadata</string>
<string name="photo_compression_quality">Fotokomprimeringskvalitet</string> <string name="photo_compression_quality">Fotokomprimeringskvalitet</string>
<string name="shutter">Shutter</string> <string name="shutter">Slutare</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Basit Kamera</string>
<string name="app_launcher_name">Kamera</string>
<string name="camera_unavailable">Kamera kullanılamıyor</string>
<string name="camera_open_error">Kameraya erişilirken bir hata oluştu</string>
<string name="video_creating_error">Video dosyası oluşturulurken bir hata oluştu</string>
<string name="video_mode_error">Video moduna geçilemedi</string>
<string name="save_error_internal_storage">Bir hata oluştu, kayıt klasörü dahili depolama olarak değiştirildi</string>
<string name="camera_switch_error">Kameraya geçilemedi</string>
<string name="click_to_resume_preview">Önizlemeye devam etmek için resmin üzerine tıklayın</string>
<string name="photo_not_saved">Fotoğraf kaydedilemedi</string>
<string name="setting_resolution_failed">Doğru çözünürlük ayarı başarısız oldu</string>
<string name="video_recording_failed">Video recording failed, try using a different resolution</string>
<!-- Other aspect ratio -->
<string name="other">diğer</string>
<!-- FAQ -->
<string name="faq_1_title">Hangi fotoğraf sıkıştırma kalitesini ayarlamalıyım?</string>
<string name="faq_1_text">Hedefinize bağlı. Genel amaçlar için çoğu kişi %75-%80\'i kullanmanızı önerir, görüntü hala gerçekten kaliteliyken, dosya boyutu %100\'e göre büyük ölçüde azalır.</string>
<!-- Settings -->
<string name="save_photos">Fotoğraf ve videoları şuraya kaydet:</string>
<string name="show_preview">Çekimden sonra fotoğraf önizlemesini göster</string>
<string name="shutter_sound">Deklanşör sesi</string>
<string name="back_camera">Arka kamera çözünürlükleri</string>
<string name="front_camera">Ön kamera çözünürlükleri</string>
<string name="photo">Fotoğraf</string>
<string name="video">Video</string>
<string name="focus_before_capture">Çekimden önce odaklan</string>
<string name="volume_buttons_as_shutter">Ses tuşlarını deklanşör olarak kullan</string>
<string name="turn_flash_off_at_startup">Başlangıçta flaşı kapat</string>
<string name="flip_front_camera_photos_horizontally">Ön kamera fotoğraflarını yatay olarak çevir</string>
<string name="keep_settings_visible">Ayar düğmelerini görünür durumda tut</string>
<string name="always_open_back_camera">Uygulamayı her zaman Arka kamera ile aç</string>
<string name="save_photo_metadata">Fotoğraf exif meta verilerini kaydet</string>
<string name="photo_compression_quality">Fotoğraf sıkıştırma kalitesi</string>
<string name="shutter">Deklanşör</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Flaşlı, zumlu ve reklamsız bir kamera.</string>
<string name="app_long_description">
Kamera hem fotoğraf çekimi hem de video kaydı için kullanılabilir. Ön ve arka kamera arasında geçiş yapabilir, kayıt yolunu değiştirebilir ve çözünürlüğü sınırlayabilirsiniz. Flaş açılıp kapatılabilir veya el feneri olarak kullanılabilir. Çimdikle yakınlaştırabilir ve uzaklaştırabilirsiniz.
Bu uygulamayı donanımsal kamera tuşuna basarak başlatmak istiyorsanız, Ayarlar -> Uygulamalar -> Kamera -> Devre Dışı Bırak özelliği ile yerleşik Kamera uygulamasını devre dışı bırakmanız gerekebilir.
Reklam veya gereksiz izinler içermez. Tamamen açık kaynaktır, özelleştirilebilir renkler sunar.
Bu uygulama, daha büyük bir uygulama serisinden sadece bir parça. Geri kalanı http://www.simplemobiletools.com adresinde bulabilirsiniz
</string>
<!--
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>

View File

@ -11,6 +11,7 @@
<string name="click_to_resume_preview">點擊圖片回到拍攝預覽</string> <string name="click_to_resume_preview">點擊圖片回到拍攝預覽</string>
<string name="photo_not_saved">相片無法儲存</string> <string name="photo_not_saved">相片無法儲存</string>
<string name="setting_resolution_failed">設定合適解析度失敗</string> <string name="setting_resolution_failed">設定合適解析度失敗</string>
<string name="video_recording_failed">Video recording failed, try using a different resolution</string>
<!-- Other aspect ratio --> <!-- Other aspect ratio -->
<string name="other">其它</string> <string name="other">其它</string>
@ -35,7 +36,7 @@
<string name="always_open_back_camera">開啟程式總是用後鏡頭</string> <string name="always_open_back_camera">開啟程式總是用後鏡頭</string>
<string name="save_photo_metadata">儲存相片EXIF資訊</string> <string name="save_photo_metadata">儲存相片EXIF資訊</string>
<string name="photo_compression_quality">相片壓縮品質</string> <string name="photo_compression_quality">相片壓縮品質</string>
<string name="shutter">Shutter</string> <string name="shutter">快門</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->

View File

@ -11,6 +11,7 @@
<string name="click_to_resume_preview">Click on the image to resume preview</string> <string name="click_to_resume_preview">Click on the image to resume preview</string>
<string name="photo_not_saved">The photo could not be saved</string> <string name="photo_not_saved">The photo could not be saved</string>
<string name="setting_resolution_failed">Setting proper resolution failed</string> <string name="setting_resolution_failed">Setting proper resolution failed</string>
<string name="video_recording_failed">Video recording failed, try using a different resolution</string>
<!-- Other aspect ratio --> <!-- Other aspect ratio -->
<string name="other">other</string> <string name="other">other</string>

View File

@ -1,7 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
ext.kotlin_version = '1.2.41' ext.kotlin_version = '1.2.51'
repositories { repositories {
jcenter() jcenter()
@ -9,7 +9,7 @@ buildscript {
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.1.2' classpath 'com.android.tools.build:gradle:3.1.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong