diff --git a/app/build.gradle b/app/build.gradle index a16e4b9e..6ac102ec 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -32,7 +32,7 @@ android { } dependencies { - compile 'com.simplemobiletools:commons:2.13.7' + compile 'com.simplemobiletools:commons:2.13.9' compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" } diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/PhotoProcessor.kt b/app/src/main/kotlin/com/simplemobiletools/camera/PhotoProcessor.kt index a50cefa2..d998483a 100644 --- a/app/src/main/kotlin/com/simplemobiletools/camera/PhotoProcessor.kt +++ b/app/src/main/kotlin/com/simplemobiletools/camera/PhotoProcessor.kt @@ -5,6 +5,7 @@ import android.os.AsyncTask import android.os.Environment import android.util.Log import com.simplemobiletools.camera.activities.MainActivity +import com.simplemobiletools.camera.extensions.getOutputMediaFile import com.simplemobiletools.commons.extensions.getFileDocument import com.simplemobiletools.commons.extensions.needsStupidWritePermissions import com.simplemobiletools.commons.extensions.toast @@ -28,7 +29,7 @@ class PhotoProcessor(val activity: MainActivity, val uri: Uri?) : AsyncTask @@ -142,8 +143,7 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan if (focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) mParameters!!.focusMode = Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE - val rotation = Utils.getPreviewRotation(mActivity, cameraId) - mCamera!!.setDisplayOrientation(rotation) + mCamera!!.setDisplayOrientation(mActivity.getPreviewRotation(cameraId)) mCamera!!.parameters = mParameters if (mCanTakePicture) { @@ -155,7 +155,7 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan } } - mCallback.setFlashAvailable(Utils.hasFlash(mCamera)) + mCallback.setFlashAvailable(hasFlash(mCamera)) } if (mIsVideoMode) { @@ -220,8 +220,8 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan fun takePicture() { if (mCanTakePicture) { - var rotation = Utils.getMediaRotation(mActivity, mCurrCameraId) - rotation += Utils.compensateDeviceRotation(mCurrCameraId, mCallback.getCurrentOrientation()) + var rotation = mActivity.getMediaRotation(mCurrCameraId) + rotation += mCallback.getCurrentOrientation().compensateDeviceRotation(mCurrCameraId) /*final Camera.Size maxSize = getOptimalPictureSize(); mParameters.setPictureSize(maxSize.width, maxSize.height);*/ @@ -476,7 +476,7 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan mRecorder!!.setVideoSource(MediaRecorder.VideoSource.DEFAULT) mRecorder!!.setAudioSource(MediaRecorder.AudioSource.DEFAULT) - mCurrVideoPath = Utils.getOutputMediaFile(mActivity, false) + mCurrVideoPath = mActivity.getOutputMediaFile(false) if (mCurrVideoPath.isEmpty()) { mActivity.toast(R.string.video_creating_error) return false @@ -510,7 +510,7 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan } mRecorder!!.setPreviewDisplay(mSurfaceHolder.surface) - val rotation = Utils.getFinalRotation(mActivity, mCurrCameraId, mCallback.getCurrentOrientation()) + val rotation = mActivity.getFinalRotation(mCurrCameraId, mCallback.getCurrentOrientation()) mInitVideoRotation = rotation mRecorder!!.setOrientationHint(rotation) @@ -541,7 +541,7 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan } private fun startRecording() { - if (mInitVideoRotation != Utils.getFinalRotation(mActivity, mCurrCameraId, mCallback.getCurrentOrientation())) { + if (mInitVideoRotation != mActivity.getFinalRotation(mCurrCameraId, mCallback.getCurrentOrientation())) { cleanupRecorder() initRecorder() } @@ -598,6 +598,32 @@ class Preview : ViewGroup, SurfaceHolder.Callback, MediaScannerConnection.OnScan toggleShutterSound(false) } + private fun hasFlash(camera: Camera?): Boolean { + if (camera == null) { + return false + } + + if (camera.parameters.flashMode == null) { + return false + } + + val supportedFlashModes = camera.parameters.supportedFlashModes + if (supportedFlashModes == null || supportedFlashModes.isEmpty() || + supportedFlashModes.size == 1 && supportedFlashModes[0] == Camera.Parameters.FLASH_MODE_OFF) { + return false + } + + return true + } + + private fun getScreenSize(): Point { + val display = mActivity.windowManager.defaultDisplay + val size = Point() + display.getSize(size) + size.y += mActivity.resources.getNavBarHeight() + return size + } + private class SizesComparator : Comparator, Serializable { override fun compare(a: Camera.Size, b: Camera.Size): Int { diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/Utils.kt b/app/src/main/kotlin/com/simplemobiletools/camera/Utils.kt deleted file mode 100644 index 5aaf5584..00000000 --- a/app/src/main/kotlin/com/simplemobiletools/camera/Utils.kt +++ /dev/null @@ -1,154 +0,0 @@ -package com.simplemobiletools.camera - -import android.Manifest -import android.app.Activity -import android.content.Context -import android.content.pm.PackageManager -import android.content.res.Resources -import android.graphics.Point -import android.hardware.Camera -import android.support.v4.content.ContextCompat -import android.view.Surface -import com.simplemobiletools.commons.extensions.toast -import java.io.File -import java.text.SimpleDateFormat -import java.util.* - -class Utils { - companion object { - fun getCameraInfo(cameraId: Int): Camera.CameraInfo { - val info = android.hardware.Camera.CameraInfo() - Camera.getCameraInfo(cameraId, info) - return info - } - - fun showToast(context: Context, resId: Int) = context.toast(resId) - - fun hasFlash(camera: Camera?): Boolean { - if (camera == null) { - return false - } - - if (camera.parameters.flashMode == null) { - return false - } - - val supportedFlashModes = camera.parameters.supportedFlashModes - if (supportedFlashModes == null || supportedFlashModes.isEmpty() || - supportedFlashModes.size == 1 && supportedFlashModes[0] == Camera.Parameters.FLASH_MODE_OFF) { - return false - } - - return true - } - - fun getOutputMediaFile(context: Context, isPhoto: Boolean): String { - val mediaStorageDir = File(Config.newInstance(context).savePhotosFolder) - - if (!mediaStorageDir.exists()) { - if (!mediaStorageDir.mkdirs()) { - return "" - } - } - - val timestamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date()) - return if (isPhoto) { - "${mediaStorageDir.path}${File.separator}IMG_$timestamp.jpg" - } else { - "${mediaStorageDir.path}${File.separator}VID_$timestamp.mp4" - } - } - - fun formatSeconds(duration: Int): String { - val sb = StringBuilder(8) - val hours = duration / (60 * 60) - val minutes = duration % (60 * 60) / 60 - val seconds = duration % (60 * 60) % 60 - - if (duration > 3600000) { - sb.append(String.format(Locale.getDefault(), "%02d", hours)).append(":") - } - - sb.append(String.format(Locale.getDefault(), "%02d", minutes)) - sb.append(":").append(String.format(Locale.getDefault(), "%02d", seconds)) - - return sb.toString() - } - - fun getScreenSize(activity: Activity): Point { - val display = activity.windowManager.defaultDisplay - val size = Point() - display.getSize(size) - size.y += getNavBarHeight(activity.resources) - return size - } - - fun getNavBarHeight(res: Resources): Int { - val id = res.getIdentifier("navigation_bar_height", "dimen", "android") - return if (id > 0 && hasNavBar(res)) { - res.getDimensionPixelSize(id) - } else - 0 - } - - fun hasNavBar(res: Resources): Boolean { - val id = res.getIdentifier("config_showNavigationBar", "bool", "android") - return id > 0 && res.getBoolean(id) - } - - fun hasCameraPermission(context: Context) = ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED - - fun hasAudioPermission(cxt: Context) = ContextCompat.checkSelfPermission(cxt, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED - - fun getRotationDegrees(activity: Activity): Int { - return when (activity.windowManager.defaultDisplay.rotation) { - Surface.ROTATION_0 -> 0 - Surface.ROTATION_90 -> 90 - Surface.ROTATION_180 -> 180 - Surface.ROTATION_270 -> 270 - else -> 0 - } - } - - fun getPreviewRotation(activity: Activity, cameraId: Int): Int { - val info = Utils.getCameraInfo(cameraId) - val degrees = Utils.getRotationDegrees(activity) - - 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 getMediaRotation(activity: Activity, cameraId: Int): Int { - val degrees = Utils.getRotationDegrees(activity) - val info = Utils.getCameraInfo(cameraId) - return if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { - (360 + info.orientation + degrees) % 360 - } else - (360 + info.orientation - degrees) % 360 - } - - fun compensateDeviceRotation(currCameraId: Int, deviceOrientation: Int): Int { - var degrees = 0 - val isFrontCamera = currCameraId == Camera.CameraInfo.CAMERA_FACING_FRONT - if (deviceOrientation == Constants.ORIENT_LANDSCAPE_LEFT) { - degrees += if (isFrontCamera) 90 else 270 - } else if (deviceOrientation == Constants.ORIENT_LANDSCAPE_RIGHT) { - degrees += if (isFrontCamera) 270 else 90 - } - return degrees - } - - fun getFinalRotation(activity: Activity, currCameraId: Int, deviceOrientation: Int): Int { - var rotation = Utils.getMediaRotation(activity, currCameraId) - rotation += Utils.compensateDeviceRotation(currCameraId, deviceOrientation) - return rotation % 360 - } - } -} diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/activities/MainActivity.kt b/app/src/main/kotlin/com/simplemobiletools/camera/activities/MainActivity.kt index 3bdbc8eb..635b6f8f 100644 --- a/app/src/main/kotlin/com/simplemobiletools/camera/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/camera/activities/MainActivity.kt @@ -24,10 +24,7 @@ import com.simplemobiletools.camera.* import com.simplemobiletools.camera.Preview.PreviewListener import com.simplemobiletools.camera.extensions.config import com.simplemobiletools.camera.views.FocusRectView -import com.simplemobiletools.commons.extensions.checkWhatsNew -import com.simplemobiletools.commons.extensions.hasWriteStoragePermission -import com.simplemobiletools.commons.extensions.storeStoragePaths -import com.simplemobiletools.commons.extensions.toast +import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.models.Release import kotlinx.android.synthetic.main.activity_main.* import java.util.* @@ -99,7 +96,7 @@ class MainActivity : SimpleActivity(), SensorEventListener, PreviewListener, Pho handleIntent() } else { val permissions = ArrayList(2) - if (!Utils.hasCameraPermission(applicationContext)) { + if (!hasCameraPermission()) { permissions.add(Manifest.permission.CAMERA) } if (!hasWriteStoragePermission()) { @@ -110,8 +107,8 @@ class MainActivity : SimpleActivity(), SensorEventListener, PreviewListener, Pho } private fun handleIntent() { - if (intent != null && intent.action != null) { - if (intent.extras != null && intent.action == MediaStore.ACTION_IMAGE_CAPTURE || intent.action == MediaStore.ACTION_IMAGE_CAPTURE_SECURE) { + if (intent?.action != null) { + if (intent.action == MediaStore.ACTION_IMAGE_CAPTURE || intent.action == MediaStore.ACTION_IMAGE_CAPTURE_SECURE) { mIsImageCaptureIntent = true hideToggleModeAbout() val output = intent.extras.get(MediaStore.EXTRA_OUTPUT) @@ -130,9 +127,9 @@ class MainActivity : SimpleActivity(), SensorEventListener, PreviewListener, Pho setContentView(R.layout.activity_main) initButtons() - if (Utils.hasNavBar(mRes) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + if (mRes.hasNavBar() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { val lp = btn_holder.layoutParams as RelativeLayout.LayoutParams - lp.setMargins(0, 0, 0, lp.bottomMargin + Utils.getNavBarHeight(mRes)) + lp.setMargins(0, 0, 0, lp.bottomMargin + mRes.getNavBarHeight()) } mCurrCamera = config.lastUsedCamera @@ -161,7 +158,7 @@ class MainActivity : SimpleActivity(), SensorEventListener, PreviewListener, Pho toggle_photo_video.setOnClickListener { handleTogglePhotoVideo() } } - private fun hasCameraAndStoragePermission() = Utils.hasCameraPermission(applicationContext) && hasWriteStoragePermission() + private fun hasCameraAndStoragePermission() = hasCameraPermission() && hasWriteStoragePermission() override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) @@ -176,7 +173,7 @@ class MainActivity : SimpleActivity(), SensorEventListener, PreviewListener, Pho finish() } } else if (requestCode == AUDIO_PERMISSION) { - if (grantResults.size > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { togglePhotoVideo() } else { toast(R.string.no_audio_permissions) @@ -311,7 +308,7 @@ class MainActivity : SimpleActivity(), SensorEventListener, PreviewListener, Pho return } - if (!Utils.hasAudioPermission(applicationContext)) { + if (!hasRecordAudioPermission()) { ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.RECORD_AUDIO), AUDIO_PERMISSION) mIsAskingPermissions = true return @@ -414,7 +411,7 @@ class MainActivity : SimpleActivity(), SensorEventListener, PreviewListener, Pho } private fun hideTimer() { - video_rec_curr_timer.text = Utils.formatSeconds(0) + video_rec_curr_timer.text = 0.getFormattedDuration() video_rec_curr_timer.visibility = View.GONE mCurrVideoRecTimer = 0 mTimerHandler.removeCallbacksAndMessages(null) @@ -428,7 +425,7 @@ class MainActivity : SimpleActivity(), SensorEventListener, PreviewListener, Pho private fun setupTimer() { runOnUiThread(object : Runnable { override fun run() { - video_rec_curr_timer.text = Utils.formatSeconds(mCurrVideoRecTimer++) + video_rec_curr_timer.text = mCurrVideoRecTimer++.getFormattedDuration() mTimerHandler.postDelayed(this, 1000) } }) diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/extensions/activity.kt b/app/src/main/kotlin/com/simplemobiletools/camera/extensions/activity.kt new file mode 100644 index 00000000..4451b997 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/camera/extensions/activity.kt @@ -0,0 +1,48 @@ +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 = getRotationDegrees(this) + + 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.getMediaRotation(cameraId: Int): Int { + val degrees = getRotationDegrees(this) + val info = getCameraInfo(cameraId) + return if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { + (360 + info.orientation + degrees) % 360 + } else + (360 + info.orientation - degrees) % 360 +} + +fun Activity.getFinalRotation(currCameraId: Int, deviceOrientation: Int): Int { + var rotation = getMediaRotation(currCameraId) + rotation += deviceOrientation.compensateDeviceRotation(currCameraId) + return rotation % 360 +} + +private fun getRotationDegrees(activity: Activity) = when (activity.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 +} diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/extensions/context.kt b/app/src/main/kotlin/com/simplemobiletools/camera/extensions/context.kt index cac74718..7c976391 100644 --- a/app/src/main/kotlin/com/simplemobiletools/camera/extensions/context.kt +++ b/app/src/main/kotlin/com/simplemobiletools/camera/extensions/context.kt @@ -2,5 +2,25 @@ package com.simplemobiletools.camera.extensions import android.content.Context import com.simplemobiletools.camera.Config +import java.io.File +import java.text.SimpleDateFormat +import java.util.* val Context.config: Config get() = Config.newInstance(this) + +fun Context.getOutputMediaFile(isPhoto: Boolean): String { + val mediaStorageDir = File(config.savePhotosFolder) + + if (!mediaStorageDir.exists()) { + if (!mediaStorageDir.mkdirs()) { + return "" + } + } + + val timestamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date()) + return if (isPhoto) { + "${mediaStorageDir.path}${File.separator}IMG_$timestamp.jpg" + } else { + "${mediaStorageDir.path}${File.separator}VID_$timestamp.mp4" + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/extensions/int.kt b/app/src/main/kotlin/com/simplemobiletools/camera/extensions/int.kt new file mode 100644 index 00000000..cdb4dd18 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/camera/extensions/int.kt @@ -0,0 +1,15 @@ +package com.simplemobiletools.camera.extensions + +import android.hardware.Camera +import com.simplemobiletools.camera.Constants + +fun Int.compensateDeviceRotation(currCameraId: Int): Int { + var degrees = 0 + val isFrontCamera = currCameraId == Camera.CameraInfo.CAMERA_FACING_FRONT + if (this == Constants.ORIENT_LANDSCAPE_LEFT) { + degrees += if (isFrontCamera) 90 else 270 + } else if (this == Constants.ORIENT_LANDSCAPE_RIGHT) { + degrees += if (isFrontCamera) 270 else 90 + } + return degrees +}