mirror of
https://github.com/SimpleMobileTools/Simple-Camera.git
synced 2025-03-03 18:47:39 +01:00
commit
9a8bf21cc6
27
CHANGELOG.md
27
CHANGELOG.md
@ -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)*
|
||||
----------------------------
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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%"
|
||||
}
|
||||
}
|
||||
|
@ -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 ->
|
||||
|
@ -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
|
||||
}
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package com.simplemobiletools.camera.interfaces
|
||||
|
||||
abstract class MyCamera {
|
||||
abstract fun getFrontCameraId(): Int
|
||||
|
||||
abstract fun getBackCameraId(): Int
|
||||
|
||||
abstract fun getCountOfCameras(): Int
|
||||
}
|
@ -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()
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package com.simplemobiletools.camera.models
|
||||
|
||||
import android.graphics.Rect
|
||||
|
||||
data class FocusArea(val rect: Rect, val weight: Int)
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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
@ -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>
|
||||
|
@ -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"
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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 -->
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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">
|
||||
カメラは、写真撮影とビデオ撮影の両方に使用できます。 前面カメラと背面カメラを切り替えたり、保存パスを変更したり、解像度を制限することができます。 フラッシュをオン/オフしたり、ピンチでズームインしたりすることができます。 ビデオの撮影中は、フラッシュを懐中電灯として使用できます。
|
||||
カメラは、写真撮影とビデオ撮影の両方に使用できます。 前面カメラと背面カメラの切り替え、保存場所を変更、解像度を制限することができます。 フラッシュをオン/オフしたり、ピンチでズームインしたりすることができます。 ビデオの撮影中は、フラッシュを懐中電灯として使用できます。
|
||||
|
||||
ハードウェアのカメラボタンを押してこのアプリを起動したい場合は、設定 -> アプリ -> カメラ -> 無効、で付属のカメラアプリを無効にする必要があります。
|
||||
ハードウェアのカメラボタンを押してこのアプリを起動したい場合は「設定 -> アプリ -> カメラ -> 無効」で端末標準のカメラアプリを無効にする必要があります。
|
||||
|
||||
広告や不要なアクセス許可は含まれていません。 完全にオープンソースで、ダークテーマも提供しています。
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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 -->
|
||||
|
@ -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>
|
||||
|
@ -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 -->
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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 -->
|
||||
|
58
app/src/main/res/values-tr/strings.xml
Normal file
58
app/src/main/res/values-tr/strings.xml
Normal 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>
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user