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
==========
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)*
----------------------------

View File

@ -10,8 +10,8 @@ android {
applicationId "com.simplemobiletools.camera"
minSdkVersion 16
targetSdkVersion 27
versionCode 58
versionName "4.0.0"
versionCode 62
versionName "4.1.3"
setProperty("archivesBaseName", "camera")
}
@ -45,7 +45,7 @@ ext {
}
dependencies {
implementation 'com.simplemobiletools:commons:4.0.18'
implementation 'com.simplemobiletools:commons:4.4.1'
debugImplementation "com.squareup.leakcanary:leakcanary-android:$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.content.Intent
import android.hardware.Camera
import android.hardware.SensorManager
import android.net.Uri
import android.os.Bundle
@ -17,36 +16,37 @@ import com.bumptech.glide.request.RequestOptions
import com.simplemobiletools.camera.BuildConfig
import com.simplemobiletools.camera.R
import com.simplemobiletools.camera.extensions.config
import com.simplemobiletools.camera.extensions.getMyCamera
import com.simplemobiletools.camera.extensions.navBarHeight
import com.simplemobiletools.camera.helpers.*
import com.simplemobiletools.camera.views.FocusRectView
import com.simplemobiletools.camera.views.Preview
import com.simplemobiletools.camera.views.Preview.PreviewListener
import com.simplemobiletools.camera.interfaces.MyCamera
import com.simplemobiletools.camera.interfaces.MyPreview
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.helpers.*
import com.simplemobiletools.commons.models.Release
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSavedListener {
private val FADE_DELAY = 5000
class MainActivity : SimpleActivity(), PhotoProcessor.MediaSavedListener {
private val FADE_DELAY = 5000L
lateinit var mFocusRectView: FocusRectView
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 mFlashlightState = FLASH_OFF
private var mIsInPhotoMode = false
private var mIsCameraAvailable = false
private var mIsVideoCaptureIntent = false
private var mIsHardwareShutterHandled = false
private var mCurrVideoRecTimer = 0
private var mCurrCameraId = 0
var mLastHandledOrientation = 0
lateinit var mOrientationEventListener: OrientationEventListener
override fun onCreate(savedInstanceState: Bundle?) {
window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
@ -57,8 +57,6 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
super.onCreate(savedInstanceState)
appLaunched(BuildConfig.APPLICATION_ID)
requestWindowFeature(Window.FEATURE_NO_TITLE)
if (config.alwaysOpenBackCamera)
config.lastUsedCamera = Camera.CameraInfo.CAMERA_FACING_BACK
initVariables()
tryInitCamera()
@ -70,10 +68,11 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
override fun onResume() {
super.onResume()
if (hasStorageAndCameraPermissions()) {
mPreview?.onResumed()
resumeCameraItems()
setupPreviewImage(mIsInPhotoMode)
scheduleFadeOut()
mFocusRectView.setStrokeColor(config.primaryColor)
mFocusCircleView.setStrokeColor(getAdjustedPrimaryColor())
if (mIsVideoCaptureIntent && mIsInPhotoMode) {
handleTogglePhotoVideo()
@ -97,29 +96,31 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
mFadeHandler.removeCallbacksAndMessages(null)
hideTimer()
mPreview?.releaseCamera()
mOrientationEventListener.disable()
if (mPreview?.isWaitingForTakePictureCallback == true) {
if (mPreview?.getCameraState() == STATE_PICTURE_TAKEN) {
toast(R.string.photo_not_saved)
}
mPreview?.onPaused()
}
override fun onDestroy() {
super.onDestroy()
mPreview?.releaseCamera()
mPreview?.mActivity = null
mPreview = null
}
private fun initVariables() {
mIsInPhotoMode = false
mIsInPhotoMode = config.initPhotoMode
mIsCameraAvailable = false
mIsVideoCaptureIntent = false
mIsHardwareShutterHandled = false
mCurrVideoRecTimer = 0
mCurrCameraId = 0
mLastHandledOrientation = 0
mCameraImpl = getMyCamera()
if (config.alwaysOpenBackCamera) {
config.lastUsedCamera = mCameraImpl.getBackCameraId().toString()
}
}
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
@ -127,7 +128,8 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
mIsHardwareShutterHandled = true
shutterPressed()
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()
true
} else {
@ -136,15 +138,16 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
}
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
}
return super.onKeyUp(keyCode, event)
}
private fun hideToggleModeAbout() {
private fun hideIntentButtons() {
toggle_photo_video.beGone()
settings.beGone()
last_photo_video_preview.beGone()
}
private fun tryInitCamera() {
@ -153,7 +156,6 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
handlePermission(PERMISSION_WRITE_STORAGE) {
if (it) {
initializeCamera()
handleIntent()
} else {
toast(R.string.no_storage_permissions)
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()) {
hideToggleModeAbout()
hideIntentButtons()
val output = intent.extras?.get(MediaStore.EXTRA_OUTPUT)
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() {
setContentView(R.layout.activity_main)
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())
mCurrCameraId = config.lastUsedCamera
mPreview = Preview(this, camera_view, this)
mPreview!!.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
view_holder.addView(mPreview)
toggle_camera.setImageResource(if (mCurrCameraId == Camera.CameraInfo.CAMERA_FACING_BACK) R.drawable.ic_camera_front else R.drawable.ic_camera_rear)
checkVideoCaptureIntent()
mPreview = if (isLollipopPlus()) PreviewCameraTwo(this, camera_texture_view, mIsInPhotoMode) else PreviewCameraOne(this, camera_surface_view)
view_holder.addView(mPreview as ViewGroup)
checkImageCaptureIntent()
mPreview?.setIsImageCaptureIntent(isImageCaptureIntent())
mFocusRectView = FocusRectView(applicationContext)
view_holder.addView(mFocusRectView)
val imageDrawable = if (config.lastUsedCamera == mCameraImpl.getBackCameraId().toString()) R.drawable.ic_camera_front else R.drawable.ic_camera_rear
toggle_camera.setImageResource(imageDrawable)
mFocusCircleView = FocusCircleView(applicationContext)
view_holder.addView(mFocusCircleView)
mIsInPhotoMode = true
mTimerHandler = Handler()
mFadeHandler = Handler()
mFlashlightState = if (config.turnFlashOffAtStartup) FLASH_OFF else config.flashlightState
setupPreviewImage(true)
val initialFlashlightState = if (config.turnFlashOffAtStartup) FLASH_OFF else config.flashlightState
mPreview!!.setFlashlightState(initialFlashlightState)
updateFlashlightState(initialFlashlightState)
}
private fun initButtons() {
@ -216,28 +230,8 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
}
private fun toggleCamera() {
if (!checkCameraAvailable()) {
return
}
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)
if (checkCameraAvailable()) {
mPreview!!.toggleFrontBackCamera()
}
}
@ -249,41 +243,23 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
}
private fun toggleFlash() {
if (!checkCameraAvailable()) {
return
}
mFlashlightState = ++mFlashlightState % if (mIsInPhotoMode) 3 else 2
checkFlash()
}
private fun checkFlash() {
when (mFlashlightState) {
FLASH_ON -> enableFlash()
FLASH_AUTO -> autoFlash()
else -> disableFlash()
if (checkCameraAvailable()) {
mPreview?.toggleFlashlight()
}
}
private fun disableFlash() {
mPreview?.disableFlash()
toggle_flash.setImageResource(R.drawable.ic_flash_off)
mFlashlightState = FLASH_OFF
config.flashlightState = FLASH_OFF
fun updateFlashlightState(state: Int) {
config.flashlightState = state
val flashDrawable = when (state) {
FLASH_OFF -> R.drawable.ic_flash_off
FLASH_ON -> R.drawable.ic_flash_on
else -> R.drawable.ic_flash_auto
}
toggle_flash.setImageResource(flashDrawable)
}
private fun enableFlash() {
mPreview?.enableFlash()
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
fun updateCameraIcon(isUsingFrontCamera: Boolean) {
toggle_camera.setImageResource(if (isUsingFrontCamera) R.drawable.ic_camera_rear else R.drawable.ic_camera_front)
}
private fun shutterPressed() {
@ -297,27 +273,21 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
toggleBottomButtons(true)
mPreview?.tryTakePicture()
} else {
if (mPreview?.toggleRecording() == true) {
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()
}
mPreview?.toggleRecording()
}
}
fun toggleBottomButtons(hide: Boolean) {
val alpha = if (hide) 0f else 1f
shutter.animate().alpha(alpha).start()
toggle_camera.animate().alpha(alpha).start()
toggle_flash.animate().alpha(alpha).start()
runOnUiThread {
val alpha = if (hide) 0f else 1f
shutter.animate().alpha(alpha).start()
toggle_camera.animate().alpha(alpha).start()
toggle_flash.animate().alpha(alpha).start()
shutter.isClickable = !hide
toggle_camera.isClickable = !hide
toggle_flash.isClickable = !hide
shutter.isClickable = !hide
toggle_camera.isClickable = !hide
toggle_flash.isClickable = !hide
}
}
private fun launchSettings() {
@ -347,12 +317,14 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
return
}
if (mIsVideoCaptureIntent)
mPreview?.trySwitchToVideo()
if (mIsVideoCaptureIntent) {
mPreview?.tryInitVideoMode()
}
disableFlash()
mPreview?.setFlashlightState(FLASH_OFF)
hideTimer()
mIsInPhotoMode = !mIsInPhotoMode
config.initPhotoMode = mIsInPhotoMode
showToggleCameraIfNeeded()
checkButtons()
toggleBottomButtons(false)
@ -367,14 +339,14 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
}
private fun initPhotoMode() {
toggle_photo_video.setImageDrawable(resources.getDrawable(R.drawable.ic_video))
shutter.setImageDrawable(resources.getDrawable(R.drawable.ic_shutter))
toggle_photo_video.setImageResource(R.drawable.ic_video)
shutter.setImageResource(R.drawable.ic_shutter)
mPreview?.initPhotoMode()
setupPreviewImage(true)
}
private fun tryInitVideoMode() {
if (mPreview?.initRecorder() == true) {
if (mPreview?.initVideoMode() == true) {
initVideoButtons()
} else {
if (!mIsVideoCaptureIntent) {
@ -384,11 +356,11 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
}
private fun initVideoButtons() {
toggle_photo_video.setImageDrawable(resources.getDrawable(R.drawable.ic_camera))
toggle_photo_video.setImageResource(R.drawable.ic_camera)
showToggleCameraIfNeeded()
shutter.setImageDrawable(resources.getDrawable(R.drawable.ic_video_rec))
checkFlash()
shutter.setImageResource(R.drawable.ic_video_rec)
setupPreviewImage(false)
mPreview?.checkFlashlight()
}
private fun setupPreviewImage(isPhoto: Boolean) {
@ -416,8 +388,11 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
}
private fun scheduleFadeOut() {
if (!config.keepSettingsVisible)
mFadeHandler.postDelayed({ fadeOutButtons() }, FADE_DELAY.toLong())
if (!config.keepSettingsVisible) {
mFadeHandler.postDelayed({
fadeOutButtons()
}, FADE_DELAY)
}
}
private fun fadeOutButtons() {
@ -444,11 +419,12 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LOW_PROFILE
}
private fun hideTimer() {
video_rec_curr_timer.text = 0.getFormattedDuration()
video_rec_curr_timer.beGone()
mCurrVideoRecTimer = 0
mTimerHandler.removeCallbacksAndMessages(null)
fun toggleTimer(show: Boolean) {
if (show) {
showTimer()
} else {
hideTimer()
}
}
private fun showTimer() {
@ -456,20 +432,26 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
setupTimer()
}
private fun hideTimer() {
video_rec_curr_timer.text = 0.getFormattedDuration()
video_rec_curr_timer.beGone()
mCurrVideoRecTimer = 0
mTimerHandler.removeCallbacksAndMessages(null)
}
private fun setupTimer() {
runOnUiThread(object : Runnable {
override fun run() {
video_rec_curr_timer.text = mCurrVideoRecTimer++.getFormattedDuration()
mTimerHandler.postDelayed(this, 1000)
mTimerHandler.postDelayed(this, 1000L)
}
})
}
private fun resumeCameraItems() {
showToggleCameraIfNeeded()
if (mPreview?.setCamera(mCurrCameraId) == true) {
if (mPreview?.resumeCamera() == true) {
hideNavigationBarIcons()
checkFlash()
if (!mIsInPhotoMode) {
initVideoButtons()
@ -480,7 +462,7 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
}
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)
@ -530,26 +512,36 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
return mIsCameraAvailable
}
fun finishActivity() {
setResult(Activity.RESULT_OK)
finish()
}
override fun setFlashAvailable(available: Boolean) {
fun setFlashAvailable(available: Boolean) {
if (available) {
toggle_flash.beVisible()
} else {
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
}
override fun videoSaved(uri: Uri) {
setupPreviewImage(mIsInPhotoMode)
fun setRecordingState(isRecording: Boolean) {
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) {
Intent().apply {
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) {
scanPath(path) {
setupPreviewImage(mIsInPhotoMode)
mPreview?.imageSaved()
rescanPaths(arrayListOf(path)) {
setupPreviewImage(true)
Intent(BROADCAST_REFRESH_MEDIA).apply {
putExtra(REFRESH_PATH, path)
`package` = "com.simplemobiletools.gallery"
@ -573,7 +566,8 @@ class MainActivity : SimpleActivity(), PreviewListener, PhotoProcessor.MediaSave
}
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.commons.dialogs.FilePickerDialog
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
import com.simplemobiletools.commons.extensions.beVisibleIf
import com.simplemobiletools.commons.extensions.getAdjustedPrimaryColor
import com.simplemobiletools.commons.extensions.humanizePath
import com.simplemobiletools.commons.extensions.updateTextColors
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.LICENSE_GLIDE
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.RadioItem
import kotlinx.android.synthetic.main.activity_settings.*
@ -28,6 +26,7 @@ class SettingsActivity : SimpleActivity() {
override fun onResume() {
super.onResume()
setupPurchaseThankYou()
setupCustomizeColors()
setupUseEnglish()
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() {
settings_customize_colors_holder.setOnClickListener {
startCustomizationActivity()
@ -105,6 +111,7 @@ class SettingsActivity : SimpleActivity() {
}
private fun setupShowPreview() {
settings_show_preview_holder.beVisibleIf(!isLollipopPlus())
settings_show_preview.isChecked = config.isShowPreviewEnabled
settings_show_preview_holder.setOnClickListener {
settings_show_preview.toggle()
@ -189,25 +196,29 @@ class SettingsActivity : SimpleActivity() {
}
private fun setupPhotoQuality() {
settings_photo_quality.text = "${config.photoQuality}%"
updatePhotoQuality(config.photoQuality)
settings_photo_quality_holder.setOnClickListener {
val items = arrayListOf(
RadioItem(50, "50%"),
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(100, "100%"),
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) {
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
import android.hardware.Camera
import android.support.v7.app.AlertDialog
import android.view.LayoutInflater
import android.view.View
import com.simplemobiletools.camera.R
import com.simplemobiletools.camera.activities.SimpleActivity
import com.simplemobiletools.camera.extensions.config
import com.simplemobiletools.camera.extensions.getAspectRatio
import com.simplemobiletools.camera.helpers.Config
import com.simplemobiletools.camera.models.MySize
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
import com.simplemobiletools.commons.extensions.setupDialogStuff
import com.simplemobiletools.commons.models.RadioItem
import kotlinx.android.synthetic.main.dialog_change_resolution.view.*
class ChangeResolutionDialog(val activity: SimpleActivity, val config: Config, val camera: Camera, val callback: () -> Unit) {
var dialog: AlertDialog
private val isBackCamera = activity.config.lastUsedCamera == Camera.CameraInfo.CAMERA_FACING_BACK
class ChangeResolutionDialog(val activity: SimpleActivity, val isFrontCamera: Boolean, val photoResolutions: ArrayList<MySize>,
val videoResolutions: ArrayList<MySize>, val openVideoResolutions: Boolean, val callback: () -> Unit) {
private var dialog: AlertDialog
private val config = activity.config
init {
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)
.setOnDismissListener { callback() }
.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) {
val items = getFormattedResolutions(camera.parameters.supportedPictureSizes)
var selectionIndex = if (isBackCamera) config.backPhotoResIndex else config.frontPhotoResIndex
val items = getFormattedResolutions(photoResolutions)
var selectionIndex = if (isFrontCamera) config.frontPhotoResIndex else config.backPhotoResIndex
selectionIndex = Math.max(selectionIndex, 0)
view.change_resolution_photo_holder.setOnClickListener {
RadioGroupDialog(activity, items, selectionIndex) {
selectionIndex = it as Int
view.change_resolution_photo.text = items[selectionIndex].title
if (isBackCamera) {
config.backPhotoResIndex = it
} else {
if (isFrontCamera) {
config.frontPhotoResIndex = it
} else {
config.backPhotoResIndex = it
}
dialog.dismiss()
}
}
view.change_resolution_photo.text = items[selectionIndex].title
view.change_resolution_photo.text = items.getOrNull(selectionIndex)?.title
}
private fun setupVideoResolutionPicker(view: View) {
val items = getFormattedResolutions(camera.parameters.supportedVideoSizes ?: camera.parameters.supportedPreviewSizes)
var selectionIndex = if (isBackCamera) config.backVideoResIndex else config.frontVideoResIndex
val items = getFormattedResolutions(videoResolutions)
var selectionIndex = if (isFrontCamera) config.frontVideoResIndex else config.backVideoResIndex
view.change_resolution_video_holder.setOnClickListener {
RadioGroupDialog(activity, items, selectionIndex) {
selectionIndex = it as Int
view.change_resolution_video.text = items[selectionIndex].title
if (isBackCamera) {
config.backVideoResIndex = it
} else {
if (isFrontCamera) {
config.frontVideoResIndex = it
} else {
config.backVideoResIndex = it
}
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 sorted = resolutions.sortedByDescending { it.width * it.height }
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.view.WindowManager
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.text.SimpleDateFormat
import java.util.*
@ -44,3 +47,5 @@ val Context.realScreenSize: Point
}
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 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
import android.content.Context
import android.hardware.Camera
import android.os.Environment
import com.simplemobiletools.commons.helpers.BaseConfig
import java.io.File
@ -31,7 +30,7 @@ class Config(context: Context) : BaseConfig(context) {
set(enabled) = prefs.edit().putBoolean(SOUND, enabled).apply()
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()
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()
var flipPhotos: Boolean
get() = prefs.getBoolean(FLIP_PHOTOS, false)
get() = prefs.getBoolean(FLIP_PHOTOS, true)
set(flipPhotos) = prefs.edit().putBoolean(FLIP_PHOTOS, flipPhotos).apply()
var lastUsedCamera: Int
get() = prefs.getInt(LAST_USED_CAMERA, Camera.CameraInfo.CAMERA_FACING_BACK)
set(cameraId) = prefs.edit().putInt(LAST_USED_CAMERA, cameraId).apply()
var lastUsedCamera: String
get() = prefs.getString(LAST_USED_CAMERA, "0")
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
get() = prefs.getInt(FLASHLIGHT_STATE, FLASH_OFF)
set(state) = prefs.edit().putInt(FLASHLIGHT_STATE, state).apply()
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()
var backVideoResIndex: Int

View File

@ -1,29 +1,48 @@
package com.simplemobiletools.camera.helpers
val ORIENT_PORTRAIT = 0
val ORIENT_LANDSCAPE_LEFT = 1
val ORIENT_LANDSCAPE_RIGHT = 2
const val ORIENT_PORTRAIT = 0
const val ORIENT_LANDSCAPE_LEFT = 1
const val ORIENT_LANDSCAPE_RIGHT = 2
// shared preferences
val SAVE_PHOTOS = "save_photos"
val SHOW_PREVIEW = "show_preview"
val SOUND = "sound"
val FOCUS_BEFORE_CAPTURE = "focus_before_capture"
val VOLUME_BUTTONS_AS_SHUTTER = "volume_buttons_as_shutter"
val TURN_FLASH_OFF_AT_STARTUP = "turn_flash_off_at_startup"
val FLIP_PHOTOS = "flip_photos"
val LAST_USED_CAMERA = "last_used_camera"
val FLASHLIGHT_STATE = "flashlight_state"
val BACK_PHOTO_RESOLUTION_INDEX = "back_photo_resolution_index"
val BACK_VIDEO_RESOLUTION_INDEX = "back_video_resolution_index"
val FRONT_PHOTO_RESOLUTION_INDEX = "front_photo_resolution_index"
val FRONT_VIDEO_RESOLUTION_INDEX = "front_video_resolution_index"
val PHOTO_PREVIEW_HINT_SHOWN = "photo_preview_hint_shown"
val KEEP_SETTINGS_VISIBLE = "keep_settings_visible"
val ALWAYS_OPEN_BACK_CAMERA = "always_open_back_camera"
val SAVE_PHOTO_METADATA = "save_photo_metadata"
val PHOTO_QUALITY = "photo_quality"
const val SAVE_PHOTOS = "save_photos"
const val SHOW_PREVIEW = "show_preview"
const val SOUND = "sound"
const val FOCUS_BEFORE_CAPTURE = "focus_before_capture_2"
const val VOLUME_BUTTONS_AS_SHUTTER = "volume_buttons_as_shutter"
const val TURN_FLASH_OFF_AT_STARTUP = "turn_flash_off_at_startup"
const val FLIP_PHOTOS = "flip_photos"
const val LAST_USED_CAMERA = "last_used_camera_2"
const val FLASHLIGHT_STATE = "flashlight_state"
const val INIT_PHOTO_MODE = "init_photo_mode"
const val BACK_PHOTO_RESOLUTION_INDEX = "back_photo_resolution_index_2"
const val BACK_VIDEO_RESOLUTION_INDEX = "back_video_resolution_index_2"
const val FRONT_PHOTO_RESOLUTION_INDEX = "front_photo_resolution_index_2"
const val FRONT_VIDEO_RESOLUTION_INDEX = "front_video_resolution_index_2"
const val PHOTO_PREVIEW_HINT_SHOWN = "photo_preview_hint_shown"
const val KEEP_SETTINGS_VISIBLE = "keep_settings_visible"
const val ALWAYS_OPEN_BACK_CAMERA = "always_open_back_camera"
const val SAVE_PHOTO_METADATA = "save_photo_metadata"
const val PHOTO_QUALITY = "photo_quality"
val FLASH_OFF = 0
val FLASH_ON = 1
val FLASH_AUTO = 2
const val FLASH_OFF = 0
const val FLASH_ON = 1
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.BitmapFactory
import android.graphics.Matrix
import android.hardware.Camera
import android.media.ExifInterface
import android.net.Uri
import android.os.AsyncTask
import android.os.Environment
import com.simplemobiletools.camera.R
import com.simplemobiletools.camera.activities.MainActivity
import com.simplemobiletools.camera.extensions.compensateDeviceRotation
import com.simplemobiletools.camera.extensions.config
import com.simplemobiletools.camera.extensions.getOutputMediaFile
import com.simplemobiletools.camera.extensions.getPreviewRotation
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.isNougatPlus
import java.io.File
@ -21,14 +18,16 @@ import java.io.FileNotFoundException
import java.io.FileOutputStream
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 {
var fos: OutputStream? = null
val path: String
try {
path = if (uri != null) {
uri.path
path = if (saveUri != null) {
saveUri.path
} else {
activity.getOutputMediaFile(true)
}
@ -62,27 +61,36 @@ class PhotoProcessor(val activity: MainActivity, val uri: Uri?, val currCameraId
fos = activity.contentResolver.openOutputStream(document.uri)
} else {
fos = if (uri == null) {
fos = if (saveUri == null) {
FileOutputStream(photoFile)
} else {
activity.contentResolver.openOutputStream(uri)
activity.contentResolver.openOutputStream(saveUri)
}
}
var image = BitmapFactory.decodeByteArray(data, 0, data.size)
val exif = ExifInterface(photoFile.toString())
val exif = try {
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 totalRotation = (imageRot + deviceRot + previewRot) % 360
if (activity.isPathOnSD(path) && !isNougatPlus()) {
val deviceRot = compensateDeviceRotation(deviceOrientation, isUsingFrontCamera)
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)
}
if (currCameraId == Camera.CameraInfo.CAMERA_FACING_FRONT && !activity.config.flipPhotos) {
if (isUsingFrontCamera && activity.config.flipPhotos) {
val matrix = Matrix()
if (path.startsWith(activity.internalStoragePath)) {
matrix.preScale(1f, -1f)
@ -99,13 +107,15 @@ class PhotoProcessor(val activity: MainActivity, val uri: Uri?, val currCameraId
try {
image.compress(Bitmap.CompressFormat.JPEG, activity.config.photoQuality, fos)
activity.saveImageRotation(path, totalRotation)
if (!isThirdPartyIntent) {
activity.saveImageRotation(path, totalRotation)
}
} catch (e: Exception) {
activity.showErrorToast(e)
return ""
}
if (activity.config.savePhotoMetadata) {
if (activity.config.savePhotoMetadata && !isThirdPartyIntent) {
val fileExif = ExifInterface(path)
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.graphics.Canvas
import android.graphics.Paint
import android.graphics.Rect
import android.os.Handler
import android.view.ViewGroup
import com.simplemobiletools.camera.extensions.config
class FocusRectView(context: Context) : ViewGroup(context) {
private val RECT_SIZE = 50
private val RECT_DURATION = 500
class FocusCircleView(context: Context) : ViewGroup(context) {
private val CIRCLE_RADIUS = 50f
private val CIRCLE_DURATION = 500L
private var mDrawRect = false
private var mDrawCircle = false
private var mHandler: Handler
lateinit var mPaint: Paint
lateinit var mRect: Rect
private var mPaint: Paint
private var mLastCenterX = 0f
private var mLastCenterY = 0f
init {
setWillNotDraw(false)
mHandler = Handler()
setupPaint()
}
private fun setupPaint() {
mPaint = Paint().apply {
style = Paint.Style.STROKE
color = context.config.primaryColor
@ -36,18 +31,19 @@ class FocusRectView(context: Context) : ViewGroup(context) {
mPaint.color = color
}
fun drawFocusRect(x: Int, y: Int) {
mRect = Rect(x - RECT_SIZE, y - RECT_SIZE, x + RECT_SIZE, y + RECT_SIZE)
toggleRect(true)
fun drawFocusCircle(x: Float, y: Float) {
mLastCenterX = x
mLastCenterY = y
toggleCircle(true)
mHandler.removeCallbacksAndMessages(null)
mHandler.postDelayed({
toggleRect(false)
}, RECT_DURATION.toLong())
toggleCircle(false)
}, CIRCLE_DURATION)
}
private fun toggleRect(show: Boolean) {
mDrawRect = show
private fun toggleCircle(show: Boolean) {
mDrawCircle = show
invalidate()
}
@ -55,8 +51,8 @@ class FocusRectView(context: Context) : ViewGroup(context) {
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (mDrawRect) {
canvas.drawRect(mRect, mPaint)
if (mDrawCircle) {
canvas.drawCircle(mLastCenterX, mLastCenterY, CIRCLE_RADIUS, mPaint)
}
}
}

View File

@ -1,52 +1,65 @@
package com.simplemobiletools.camera.views
import android.annotation.SuppressLint
import android.annotation.TargetApi
import android.app.Activity
import android.content.Context
import android.graphics.Point
import android.graphics.Rect
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.os.Build
import android.os.Environment
import android.os.Handler
import android.view.ScaleGestureDetector
import android.view.SurfaceHolder
import android.view.SurfaceView
import android.view.ViewGroup
import android.view.*
import com.simplemobiletools.camera.R
import com.simplemobiletools.camera.activities.MainActivity
import com.simplemobiletools.camera.dialogs.ChangeResolutionDialog
import com.simplemobiletools.camera.extensions.*
import com.simplemobiletools.camera.helpers.Config
import com.simplemobiletools.camera.helpers.PhotoProcessor
import com.simplemobiletools.camera.extensions.config
import com.simplemobiletools.camera.extensions.getMyCamera
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.helpers.isJellyBean1Plus
import java.io.File
import java.io.IOException
import java.util.*
class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScanCompletedListener {
var mCamera: Camera? = null
class PreviewCameraOne : ViewGroup, SurfaceHolder.Callback, MyPreview {
private val FOCUS_AREA_SIZE = 100
private val PHOTO_PREVIEW_LENGTH = 500L
private val REFOCUS_PERIOD = 3000L
lateinit var mSurfaceHolder: SurfaceHolder
lateinit var mSurfaceView: SurfaceView
lateinit var mCallback: PreviewListener
lateinit var mScreenSize: Point
lateinit var config: Config
private lateinit var mSurfaceHolder: SurfaceHolder
private lateinit var mSurfaceView: SurfaceView
private lateinit var mScreenSize: Point
private lateinit var mConfig: Config
private var mSupportedPreviewSizes: List<Camera.Size>? = null
private var mPreviewSize: Camera.Size? = null
private var mParameters: Camera.Parameters? = null
private var mRecorder: MediaRecorder? = null
private var mScaleGestureDetector: ScaleGestureDetector? = null
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 mCanTakePicture = false
private var mIsRecording = false
private var mIsVideoMode = false
private var mIsInVideoMode = false
private var mIsSurfaceCreated = false
private var mSwitchToVideoAsap = false
private var mSetupPreviewAfterMeasure = false
@ -54,41 +67,31 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
private var mWasZooming = false
private var mIsPreviewShown = false
private var mWasCameraPreviewSet = false
private var mLastClickX = 0
private var mLastClickY = 0
private var mIsImageCaptureIntent = false
private var mIsFocusingBeforeCapture = false
private var mLastClickX = 0f
private var mLastClickY = 0f
private var mCurrCameraId = 0
private var mMaxZoom = 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)
@SuppressLint("ClickableViewAccessibility")
constructor(activity: MainActivity, surfaceView: SurfaceView, previewListener: PreviewListener) : super(activity) {
constructor(activity: MainActivity, surfaceView: SurfaceView) : super(activity) {
mActivity = activity
mCallback = previewListener
mSurfaceView = surfaceView
mSurfaceHolder = mSurfaceView.holder
mSurfaceHolder.addCallback(this)
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS)
mCanTakePicture = false
mIsVideoMode = false
mIsSurfaceCreated = false
mSetupPreviewAfterMeasure = false
mCurrVideoPath = ""
config = activity.config
mCameraImpl = MyCameraOneImpl(activity.applicationContext)
mConfig = activity.config
mScreenSize = getScreenSize()
initGestureDetector()
mSurfaceView.setOnTouchListener { view, event ->
mLastClickX = event.x.toInt()
mLastClickY = event.y.toInt()
mLastClickX = event.x
mLastClickY = event.y
if (mMaxZoom > 0 && mParameters?.isZoomSupported == true) {
mScaleGestureDetector!!.onTouchEvent(event)
@ -100,8 +103,9 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
if (mIsPreviewShown) {
resumePreview()
} else {
if (!mWasZooming && !mIsPreviewShown)
if (!mWasZooming && !mIsPreviewShown) {
focusArea(false)
}
mWasZooming = false
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) {
initRecorder()
initVideoMode()
} else {
mSwitchToVideoAsap = true
}
}
fun setCamera(cameraId: Int): Boolean {
mCurrCameraId = cameraId
override fun resumeCamera(): Boolean {
val newCamera: Camera
try {
newCamera = Camera.open(cameraId)
mCallback.setIsCameraAvailable(true)
newCamera = Camera.open(mCurrCameraId)
mActivity!!.setIsCameraAvailable(true)
} catch (e: Exception) {
mActivity!!.showErrorToast(e)
mCallback.setIsCameraAvailable(false)
mActivity!!.setIsCameraAvailable(false)
return false
}
@ -135,8 +144,8 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
releaseCamera()
mCamera = newCamera
if (initCamera() && mIsVideoMode) {
initRecorder()
if (initCamera() && mIsInVideoMode) {
initVideoMode()
}
if (!mWasCameraPreviewSet && mIsSurfaceCreated) {
@ -146,6 +155,8 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
return true
}
override fun imageSaved() {}
private fun initCamera(): Boolean {
if (mCamera == null)
return false
@ -169,7 +180,7 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
mParameters!!.focusMode = Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE
}
mCamera!!.setDisplayOrientation(mActivity!!.getPreviewRotation(mCurrCameraId))
mCamera!!.setDisplayOrientation(getPreviewRotation(mCurrCameraId))
mParameters!!.zoom = 0
updateCameraParameters()
@ -182,10 +193,30 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
}
}
mCallback.setFlashAvailable(hasFlash(mCamera))
mActivity!!.setFlashAvailable(hasFlash(mCamera))
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() {
mIsSixteenToNine = getSelectedResolution().isSixteenToNine()
mSetupPreviewAfterMeasure = true
@ -194,17 +225,17 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
rescheduleAutofocus()
}
private fun getSelectedResolution(): Camera.Size {
private fun getSelectedResolution(): MySize {
if (mParameters == null) {
mParameters = mCamera!!.parameters
}
var index = getResolutionIndex()
val resolutions = if (mIsVideoMode) {
val resolutions = if (mIsInVideoMode) {
mParameters!!.supportedVideoSizes ?: mParameters!!.supportedPreviewSizes
} else {
mParameters!!.supportedPictureSizes
}.sortedByDescending { it.width * it.height }
}.map { MySize(it.width, it.height) }.sortedByDescending { it.width * it.height }
if (index == -1) {
index = getDefaultFullscreenResolution(resolutions) ?: 0
@ -214,20 +245,20 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
}
private fun getResolutionIndex(): Int {
val isBackCamera = config.lastUsedCamera == Camera.CameraInfo.CAMERA_FACING_BACK
return if (mIsVideoMode) {
if (isBackCamera) config.backVideoResIndex else config.frontVideoResIndex
val isBackCamera = mConfig.lastUsedCamera == Camera.CameraInfo.CAMERA_FACING_BACK.toString()
return if (mIsInVideoMode) {
if (isBackCamera) mConfig.backVideoResIndex else mConfig.frontVideoResIndex
} 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()
resolutions.forEachIndexed { index, size ->
val diff = screenAspectRatio - (size.width / size.height.toFloat())
if (Math.abs(diff) < RATIO_TOLERANCE) {
config.backPhotoResIndex = index
if (Math.abs(diff) < 0.1f) {
mConfig.backPhotoResIndex = index
return index
}
}
@ -281,14 +312,15 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
})
}
fun tryTakePicture() {
if (config.focusBeforeCapture) {
override fun tryTakePicture() {
if (mConfig.focusBeforeCapture) {
focusArea(true)
} else {
takePicture()
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private fun takePicture() {
if (mCanTakePicture) {
val selectedResolution = getSelectedResolution()
@ -298,19 +330,19 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
mActivity!!.toast(R.string.setting_resolution_failed)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
if (isJellyBean1Plus()) {
mCamera!!.enableShutterSound(false)
}
mRotationAtCapture = mActivity!!.mLastHandledOrientation
updateCameraParameters()
isWaitingForTakePictureCallback = true
mCameraState = STATE_PICTURE_TAKEN
mIsPreviewShown = true
try {
Thread {
mCamera!!.takePicture(null, null, takePictureCallback)
if (config.isSoundEnabled) {
if (mConfig.isSoundEnabled) {
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val volume = audioManager.getStreamVolume(AudioManager.STREAM_SYSTEM)
if (volume != 0) {
@ -332,16 +364,19 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
return@PictureCallback
}
isWaitingForTakePictureCallback = false
if (!isImageCaptureIntent) {
mCameraState = STATE_PREVIEW
if (!mIsImageCaptureIntent) {
handlePreview()
}
if (isImageCaptureIntent) {
if (mIsImageCaptureIntent) {
if (mTargetUri != null) {
storePhoto(data)
} else {
mActivity!!.finishActivity()
mActivity!!.apply {
setResult(Activity.RESULT_OK)
finish()
}
}
} else {
storePhoto(data)
@ -349,14 +384,17 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
}
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() {
if (config.isShowPreviewEnabled) {
if (!config.wasPhotoPreviewHintShown) {
if (mConfig.isShowPreviewEnabled) {
if (!mConfig.wasPhotoPreviewHintShown) {
mActivity!!.toast(R.string.click_to_resume_preview)
config.wasPhotoPreviewHintShown = true
mConfig.wasPhotoPreviewHintShown = true
}
} else {
Handler().postDelayed({
@ -388,18 +426,18 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
mCamera!!.cancelAutoFocus()
if (mParameters!!.maxNumFocusAreas > 0) {
if (mLastClickX == 0 && mLastClickY == 0) {
mLastClickX = width / 2
mLastClickY = height / 2
if (mLastClickX == 0f && mLastClickY == 0f) {
mLastClickX = width / 2.toFloat()
mLastClickY = height / 2.toFloat()
}
val focusRect = calculateFocusArea(mLastClickX.toFloat(), mLastClickY.toFloat())
val focusRect = calculateFocusArea(mLastClickX, mLastClickY)
val focusAreas = ArrayList<Camera.Area>(1)
focusAreas.add(Camera.Area(focusRect, 1000))
mParameters!!.focusAreas = focusAreas
if (showFocusRect) {
mCallback.drawFocusRect(mLastClickX, mLastClickY)
mActivity!!.drawFocusCircle(mLastClickX, mLastClickY)
}
}
@ -448,18 +486,21 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
}
private fun rescheduleAutofocus() {
autoFocusHandler.removeCallbacksAndMessages(null)
autoFocusHandler.postDelayed({
if (!mIsVideoMode || !mIsRecording) {
mAutoFocusHandler.removeCallbacksAndMessages(null)
mAutoFocusHandler.postDelayed({
if (!mIsInVideoMode || !mIsRecording) {
focusArea(false, false)
}
}, REFOCUS_PERIOD)
}
fun showChangeResolutionDialog() {
override fun showChangeResolutionDialog() {
if (mCamera != null) {
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()) {
refreshPreview()
}
@ -482,7 +523,7 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
mCamera?.setPreviewDisplay(mSurfaceHolder)
if (mSwitchToVideoAsap)
initRecorder()
initVideoMode()
} catch (e: IOException) {
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) {
mIsSurfaceCreated = true
if (mIsVideoMode) {
initRecorder()
if (mIsInVideoMode) {
initVideoMode()
}
}
@ -557,7 +598,7 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
if (mSupportedPreviewSizes != null) {
// 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
mPreviewSize = if (mIsSixteenToNine || mIsVideoMode) {
mPreviewSize = if (mIsSixteenToNine || mIsInVideoMode) {
getOptimalPreviewSize(mSupportedPreviewSizes!!, mScreenSize.y, mScreenSize.x)
} else {
val newRatioHeight = (mScreenSize.x * (4.toDouble() / 3)).toInt()
@ -570,7 +611,7 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
if (mScreenSize.x > mPreviewSize!!.height) {
val ratio = mScreenSize.x.toFloat() / mPreviewSize!!.height
lp.width = (mPreviewSize!!.height * ratio).toInt()
if (mIsSixteenToNine || mIsVideoMode) {
if (mIsSixteenToNine || mIsInVideoMode) {
lp.height = mScreenSize.y
} else {
lp.height = (mPreviewSize!!.width * ratio).toInt()
@ -590,18 +631,40 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
}
}
fun enableFlash() {
mParameters!!.flashMode = Camera.Parameters.FLASH_MODE_TORCH
override fun setFlashlightState(state: Int) {
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()
}
fun disableFlash() {
mParameters!!.flashMode = Camera.Parameters.FLASH_MODE_OFF
private fun enableFlash() {
mFlashlightState = FLASH_ON
mParameters?.flashMode = Camera.Parameters.FLASH_MODE_TORCH
updateCameraParameters()
}
fun autoFlash() {
mParameters!!.flashMode = Camera.Parameters.FLASH_MODE_OFF
private fun setAutoFlash() {
mFlashlightState = FLASH_AUTO
mParameters?.flashMode = Camera.Parameters.FLASH_MODE_OFF
updateCameraParameters()
Handler().postDelayed({
@ -611,16 +674,16 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
}, 1000)
}
fun initPhotoMode() {
override fun initPhotoMode() {
stopRecording()
cleanupRecorder()
mIsRecording = false
mIsVideoMode = false
mIsInVideoMode = false
refreshPreview()
}
// VIDEO RECORDING
fun initRecorder(): Boolean {
override fun initVideoMode(): Boolean {
if (mCamera == null || mRecorder != null || !mIsSurfaceCreated) {
return false
}
@ -629,7 +692,7 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
mSwitchToVideoAsap = false
mIsRecording = false
mIsVideoMode = true
mIsInVideoMode = true
mRecorder = MediaRecorder().apply {
setCamera(mCamera)
setVideoSource(MediaRecorder.VideoSource.DEFAULT)
@ -686,9 +749,9 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
private fun checkPermissions(): Boolean {
if (mActivity!!.needsStupidWritePermissions(mCurrVideoPath)) {
if (config.treeUri.isEmpty()) {
if (mConfig.treeUri.isEmpty()) {
mActivity!!.toast(R.string.save_error_internal_storage)
config.savePhotosFolder = Environment.getExternalStorageDirectory().toString()
mConfig.savePhotosFolder = Environment.getExternalStorageDirectory().toString()
releaseCamera()
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) {
stopRecording()
initRecorder()
initVideoMode()
} else {
startRecording()
}
return mIsRecording
}
private fun getVideoRotation(): Int {
val deviceRot = mActivity!!.mLastHandledOrientation.compensateDeviceRotation(mCurrCameraId)
val previewRot = mActivity!!.getPreviewRotation(mCurrCameraId)
val deviceRot = compensateDeviceRotation(mActivity!!.mLastHandledOrientation, getIsUsingFrontCamera())
val previewRot = getPreviewRotation(mCurrCameraId)
return (deviceRot + previewRot) % 360
}
fun deviceOrientationChanged() {
if (mIsVideoMode && !mIsRecording) {
override fun deviceOrientationChanged() {
if (mIsInVideoMode && !mIsRecording) {
mRecorder = null
initRecorder()
initVideoMode()
}
}
@ -754,6 +824,7 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
mRecorder!!.start()
toggleShutterSound(false)
mIsRecording = true
mActivity!!.setRecordingState(true)
} catch (e: Exception) {
mActivity!!.showErrorToast(e)
releaseCamera()
@ -765,7 +836,10 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
try {
toggleShutterSound(true)
mRecorder!!.stop()
mActivity!!.scanPath(mCurrVideoPath) {}
mActivity!!.rescanPaths(arrayListOf(mCurrVideoPath)) {
mActivity!!.videoSaved(Uri.fromFile(File(mCurrVideoPath)))
toggleShutterSound(false)
}
} catch (e: RuntimeException) {
mActivity!!.showErrorToast(e)
toggleShutterSound(false)
@ -774,10 +848,12 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
mIsRecording = false
releaseCamera()
}
}
mRecorder = null
if (mIsRecording) {
mActivity!!.setRecordingState(false)
}
mIsRecording = false
val file = File(mCurrVideoPath)
@ -787,16 +863,11 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
}
private fun toggleShutterSound(mute: Boolean?) {
if (!config.isSoundEnabled) {
if (!mConfig.isSoundEnabled) {
(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 {
if (camera == null) {
return false
@ -823,13 +894,29 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan
return size
}
interface PreviewListener {
fun setFlashAvailable(available: Boolean)
private fun getPreviewRotation(cameraId: Int): Int {
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">
<SurfaceView
android:id="@+id/camera_view"
android:id="@+id/camera_surface_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
android:layout_height="wrap_content"
android:visibility="gone"/>
<SurfaceView
android:id="@+id/focus_rect_view"
<com.simplemobiletools.camera.views.AutoFitTextureView
android:id="@+id/camera_texture_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
@ -91,4 +92,5 @@
android:text="00:00"
android:textColor="@android:color/white"
android:visibility="gone"/>
</RelativeLayout>

View File

@ -11,6 +11,28 @@
android:layout_height="wrap_content"
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
android:id="@+id/settings_customize_colors_holder"
android:layout_width="match_parent"

View File

@ -11,6 +11,7 @@
<string name="click_to_resume_preview">انقر على الصورة لاستئناف المعاينة</string>
<string name="photo_not_saved">تعذر حفظ الصورة</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 -->
<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="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="video_recording_failed">Video recording failed, try using a different resolution</string>
<!-- Other aspect ratio -->
<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="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="video_recording_failed">Video recording failed, try using a different resolution</string>
<!-- Other aspect ratio -->
<string name="other">andere</string>
@ -32,7 +33,7 @@
<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="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="photo_compression_quality">Fotokomprimierungsqualität</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="photo_not_saved">The photo could not be saved</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 -->
<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="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="video_recording_failed">Video recording failed, try using a different resolution</string>
<!-- Other aspect ratio -->
<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="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="video_recording_failed">Video recording failed, try using a different resolution</string>
<!-- Other aspect ratio -->
<string name="other">outro</string>
<!-- FAQ -->
<string name="faq_1_title">What photo compression quality should I set?</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_title">Qué calidade de compresión debo escoller para a foto?</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 -->
<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="save_photo_metadata">Gardar metadatos exif 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 -->
<!-- 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="photo_not_saved">Fotografiju nije moguće spremiti</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 -->
<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="photo_not_saved">The photo could not be saved</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 -->
<string name="other">other</string>

View File

@ -6,14 +6,15 @@
<string name="camera_open_error">カメラへのアクセス時にエラーが発生しました</string>
<string name="video_creating_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="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="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 -->
<string name="other">other</string>
<string name="other">その他</string>
<!-- FAQ -->
<string name="faq_1_title">What photo compression quality should I set?</string>
@ -23,27 +24,27 @@
<string name="save_photos">写真とビデオの保存先</string>
<string name="show_preview">キャプチャ後に写真のプレビューを表示</string>
<string name="shutter_sound">シャッター音</string>
<string name="back_camera">背面カメラ解像度</string>
<string name="front_camera">前面カメラ解像度</string>
<string name="back_camera">背面カメラ解像度</string>
<string name="front_camera">前面カメラ解像度</string>
<string name="photo">画像</string>
<string name="video">動画</string>
<string name="focus_before_capture">キャプチャ前に再度焦点を合わせる</string>
<string name="volume_buttons_as_shutter">音量ボタンで撮影</string>
<string name="turn_flash_off_at_startup">Turn flash off at startup</string>
<string name="volume_buttons_as_shutter">音量ボタンで撮影</string>
<string name="turn_flash_off_at_startup">起動時にライトをオンにする</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="save_photo_metadata">Save photo exif metadata</string>
<string name="photo_compression_quality">Photo compression quality</string>
<string name="shutter">Shutter</string>
<string name="save_photo_metadata">写真のEXIFデータを保存する</string>
<string name="photo_compression_quality">写真の圧縮品質</string>
<string name="shutter">シャッター</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">フラッシュ、ズーム付きのカメラ。広告はありません。</string>
<string name="app_long_description">
カメラは、写真撮影とビデオ撮影の両方に使用できます。 前面カメラと背面カメラを切り替えたり、保存パスを変更したり、解像度を制限することができます。 フラッシュをオン/オフしたり、ピンチでズームインしたりすることができます。 ビデオの撮影中は、フラッシュを懐中電灯として使用できます。
カメラは、写真撮影とビデオ撮影の両方に使用できます。 前面カメラと背面カメラの切り替え、保存場所を変更、解像度を制限することができます。 フラッシュをオン/オフしたり、ピンチでズームインしたりすることができます。 ビデオの撮影中は、フラッシュを懐中電灯として使用できます。
ハードウェアのカメラボタンを押してこのアプリを起動したい場合は、設定 -> アプリ -> カメラ -> 無効、で付属のカメラアプリを無効にする必要があります。
ハードウェアのカメラボタンを押してこのアプリを起動したい場合は「設定 -> アプリ -> カメラ -> 無効」で端末標準のカメラアプリを無効にする必要があります。
広告や不要なアクセス許可は含まれていません。 完全にオープンソースで、ダークテーマも提供しています。

View File

@ -11,6 +11,7 @@
<string name="click_to_resume_preview">미리보기를 다시 시작하려면 이미지를 클릭하십시오.</string>
<string name="photo_not_saved">사진을 저장할 수 없습니다.</string>
<string name="setting_resolution_failed">해상도 설정에 실패했습니다.</string>
<string name="video_recording_failed">Video recording failed, try using a different resolution</string>
<!-- Other aspect ratio -->
<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="photo_not_saved">Nuotrauka negali būti išsaugota</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 -->
<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="photo_not_saved">The photo could not be saved</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 -->
<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="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="video_recording_failed">Video recording failed, try using a different resolution</string>
<!-- Other aspect ratio -->
<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="save_photo_metadata">Zapisuj dane EXIF w zdjęciach</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 -->
<!-- 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="photo_not_saved">A foto não pôde ser salva</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 -->
<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="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="video_recording_failed">Video recording failed, try using a different resolution</string>
<!-- Other aspect ratio -->
<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="save_photo_metadata">Guardar dados exif das fotos</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 -->
<!-- Short description has to have less than 80 chars -->

View File

@ -11,6 +11,7 @@
<string name="click_to_resume_preview">Чтобы возобновить предпросмотр, нажмите на изображение</string>
<string name="photo_not_saved">Не удалось сохранить фото</string>
<string name="setting_resolution_failed">Не удалось установить правильное разрешение</string>
<string name="video_recording_failed">Video recording failed, try using a different resolution</string>
<!-- Other aspect ratio -->
<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="photo_not_saved">Fotografiu sa nepodarilo uložiť</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 -->
<string name="other">iný</string>

View File

@ -2,7 +2,7 @@
<resources>
<string name="app_name">Simple Camera</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="video_creating_error">Ett fel uppstod när videofilen skulle skapas</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="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="video_recording_failed">Video recording failed, try using a different resolution</string>
<!-- Other aspect ratio -->
<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="save_photo_metadata">Spara fotonas Exif-metadata</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 -->
<!-- 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="photo_not_saved">相片無法儲存</string>
<string name="setting_resolution_failed">設定合適解析度失敗</string>
<string name="video_recording_failed">Video recording failed, try using a different resolution</string>
<!-- Other aspect ratio -->
<string name="other">其它</string>
@ -35,18 +36,18 @@
<string name="always_open_back_camera">開啟程式總是用後鏡頭</string>
<string name="save_photo_metadata">儲存相片EXIF資訊</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 -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">一個有閃光燈和變焦,且沒有廣告的相機。</string>
<string name="app_long_description">
這相機可用來相片拍攝和影片紀錄。你可以切換前後鏡頭、修改儲存路徑和限制解析度。閃光燈能被開關或當作手電筒。你可以捏抓來進行縮放。
如果你想要按下實體相機鍵來啟動這應用程式,你可能必須禁用內建的相機程式(設定 -> 應用程式 -> 相機 -> 停用)。
不包含廣告及非必要的權限,而且完全開放原始碼,並提供自訂顏色。
這程式只是一系列眾多應用程式的其中一項,你可以在這發現更多 https://www.simplemobiletools.com
</string>

View File

@ -11,6 +11,7 @@
<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="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 -->
<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.
buildscript {
ext.kotlin_version = '1.2.41'
ext.kotlin_version = '1.2.51'
repositories {
jcenter()
@ -9,7 +9,7 @@ buildscript {
}
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"
// NOTE: Do not place your application dependencies here; they belong