diff --git a/app/src/main/java/com/simplemobiletools/camera/PhotoProcessor.kt b/app/src/main/java/com/simplemobiletools/camera/PhotoProcessor.kt index e8abda60..68c680f7 100644 --- a/app/src/main/java/com/simplemobiletools/camera/PhotoProcessor.kt +++ b/app/src/main/java/com/simplemobiletools/camera/PhotoProcessor.kt @@ -7,6 +7,7 @@ import android.util.Log import com.simplemobiletools.camera.activities.MainActivity import com.simplemobiletools.filepicker.extensions.getFileDocument import com.simplemobiletools.filepicker.extensions.needsStupidWritePermissions +import com.simplemobiletools.filepicker.extensions.toast import java.io.* import java.lang.ref.WeakReference @@ -39,7 +40,7 @@ class PhotoProcessor(val activity: MainActivity, val uri: Uri?) : AsyncTask permissions = new ArrayList<>(2); - if (!Utils.Companion.hasCameraPermission(getApplicationContext())) { - permissions.add(Manifest.permission.CAMERA); - } - if (!Utils.Companion.hasStoragePermission(getApplicationContext())) { - permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE); - } - ActivityCompat.requestPermissions(this, permissions.toArray(new String[permissions.size()]), CAMERA_STORAGE_PERMISSION); - } - } - - private void handleIntent() { - final Intent intent = getIntent(); - if (intent != null && intent.getAction() != null) { - if (intent.getExtras() != null && intent.getAction().equals(MediaStore.ACTION_IMAGE_CAPTURE) || - intent.getAction().equals(MediaStore.ACTION_IMAGE_CAPTURE_SECURE)) { - mIsImageCaptureIntent = true; - hideToggleModeAbout(); - final Object output = intent.getExtras().get(MediaStore.EXTRA_OUTPUT); - if (output != null && output instanceof Uri) { - mPreview.setTargetUri((Uri) output); - } - } else if (intent.getAction().equals(MediaStore.ACTION_VIDEO_CAPTURE)) { - mIsVideoCaptureIntent = true; - hideToggleModeAbout(); - mShutterBtn.setImageDrawable(getResources().getDrawable(R.mipmap.video_rec)); - } - } - } - - private void initializeCamera() { - setContentView(R.layout.activity_main); - ButterKnife.bind(this); - - if (Utils.Companion.hasNavBar(getResources()) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - final View btnLayout = findViewById(R.id.btn_holder); - final RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) btnLayout.getLayoutParams(); - lp.setMargins(0, 0, 0, lp.bottomMargin + Utils.Companion.getNavBarHeight(getResources())); - } - - mCurrCamera = getConfig().getLastUsedCamera(); - mPreview = new Preview(this, (SurfaceView) findViewById(R.id.camera_view), this); - mPreview.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); - mViewHolder.addView(mPreview); - mToggleCameraBtn.setImageResource(mCurrCamera == Camera.CameraInfo.CAMERA_FACING_BACK ? R.mipmap.camera_front : R.mipmap.camera_back); - - mFocusRectView = new FocusRectView(getApplicationContext()); - mViewHolder.addView(mFocusRectView); - - mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE); - mIsInPhotoMode = true; - mTimerHandler = new Handler(); - mFadeHandler = new Handler(); - mIsFlashEnabled = getConfig().getLastFlashlightState(); - setupPreviewImage(true); - } - - private boolean hasCameraAndStoragePermission() { - return Utils.Companion.hasCameraPermission(getApplicationContext()) && Utils.Companion.hasStoragePermission(getApplicationContext()); - } - - @Override - public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - mIsAskingPermissions = false; - - if (requestCode == CAMERA_STORAGE_PERMISSION) { - if (hasCameraAndStoragePermission()) { - initializeCamera(); - handleIntent(); - } else { - Utils.Companion.showToast(getApplicationContext(), R.string.no_permissions); - finish(); - } - } else if (requestCode == AUDIO_PERMISSION) { - if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - togglePhotoVideo(); - } else { - Utils.Companion.showToast(getApplicationContext(), R.string.no_audio_permissions); - if (mIsVideoCaptureIntent) - finish(); - } - } - } - - @OnClick(R.id.toggle_camera) - public void toggleCamera() { - if (!checkCameraAvailable()) { - return; - } - - if (mCurrCamera == Camera.CameraInfo.CAMERA_FACING_BACK) { - mCurrCamera = Camera.CameraInfo.CAMERA_FACING_FRONT; - } else { - mCurrCamera = Camera.CameraInfo.CAMERA_FACING_BACK; - } - - getConfig().setLastUsedCamera(mCurrCamera); - int newIconId = R.mipmap.camera_front; - mPreview.releaseCamera(); - if (mPreview.setCamera(mCurrCamera)) { - if (mCurrCamera == Camera.CameraInfo.CAMERA_FACING_FRONT) { - newIconId = R.mipmap.camera_back; - } - mToggleCameraBtn.setImageResource(newIconId); - disableFlash(); - hideTimer(); - } else { - Utils.Companion.showToast(getApplicationContext(), R.string.camera_switch_error); - } - } - - @OnClick(R.id.last_photo_video_preview) - public void showLastMediaPreview() { - if (mPreviewUri == null) - return; - - try { - final String REVIEW_ACTION = "com.android.camera.action.REVIEW"; - Intent intent = new Intent(REVIEW_ACTION, mPreviewUri); - this.startActivity(intent); - } catch (ActivityNotFoundException e) { - Intent intent = new Intent(Intent.ACTION_VIEW, mPreviewUri); - if (intent.resolveActivity(getPackageManager()) != null) { - startActivity(intent); - } else { - Utils.Companion.showToast(getApplicationContext(), R.string.no_gallery_app_available); - } - } - } - - @OnClick(R.id.toggle_flash) - public void toggleFlash() { - if (!checkCameraAvailable()) { - return; - } - - mIsFlashEnabled = !mIsFlashEnabled; - checkFlash(); - } - - private void checkFlash() { - if (mIsFlashEnabled) { - enableFlash(); - } else { - disableFlash(); - } - } - - private void disableFlash() { - mPreview.disableFlash(); - mToggleFlashBtn.setImageResource(R.mipmap.flash_off); - mIsFlashEnabled = false; - getConfig().setLastFlashlightState(mIsFlashEnabled); - } - - private void enableFlash() { - mPreview.enableFlash(); - mToggleFlashBtn.setImageResource(R.mipmap.flash_on); - mIsFlashEnabled = true; - getConfig().setLastFlashlightState(mIsFlashEnabled); - } - - @OnClick(R.id.shutter) - public void handleShutterPressed() { - shutterPressed(); - } - - private void shutterPressed() { - if (!checkCameraAvailable()) { - return; - } - - handleShutter(); - } - - private void handleShutter() { - if (mIsInPhotoMode) { - toggleBottomButtons(true); - mPreview.takePicture(); - new Handler().postDelayed(new Runnable() { - @Override - public void run() { - toggleBottomButtons(false); - } - }, Preview.PHOTO_PREVIEW_LENGTH); - } else { - final Resources res = getResources(); - final boolean isRecording = mPreview.toggleRecording(); - if (isRecording) { - mShutterBtn.setImageDrawable(res.getDrawable(R.mipmap.video_stop)); - mToggleCameraBtn.setVisibility(View.INVISIBLE); - showTimer(); - } else { - mShutterBtn.setImageDrawable(res.getDrawable(R.mipmap.video_rec)); - mToggleCameraBtn.setVisibility(View.VISIBLE); - hideTimer(); - } - } - } - - private void toggleBottomButtons(Boolean hide) { - mShutterBtn.animate().alpha(hide ? 0 : 1).start(); - mToggleCameraBtn.animate().alpha(hide ? 0 : 1).start(); - mToggleFlashBtn.animate().alpha(hide ? 0 : 1).start(); - } - - @OnClick(R.id.settings) - public void launchSettings() { - if (mSettingsBtn.getAlpha() == 1.f) { - final Intent intent = new Intent(getApplicationContext(), SettingsActivity.class); - startActivity(intent); - } else { - fadeInButtons(); - } - } - - @OnClick(R.id.toggle_photo_video) - public void handleTogglePhotoVideo() { - togglePhotoVideo(); - checkButtons(); - } - - private void togglePhotoVideo() { - if (!checkCameraAvailable()) { - return; - } - - if (!Utils.Companion.hasAudioPermission(getApplicationContext())) { - ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, AUDIO_PERMISSION); - mIsAskingPermissions = true; - return; - } - - if (mIsVideoCaptureIntent) - mPreview.trySwitchToVideo(); - - disableFlash(); - hideTimer(); - mIsInPhotoMode = !mIsInPhotoMode; - mToggleCameraBtn.setVisibility(View.VISIBLE); - } - - private void checkButtons() { - if (mIsInPhotoMode) { - initPhotoButtons(); - } else { - tryInitVideoButtons(); - } - } - - private void initPhotoButtons() { - final Resources res = getResources(); - mTogglePhotoVideoBtn.setImageDrawable(res.getDrawable(R.mipmap.videocam)); - mShutterBtn.setImageDrawable(res.getDrawable(R.mipmap.camera)); - mPreview.initPhotoMode(); - setupPreviewImage(true); - } - - private void tryInitVideoButtons() { - if (mPreview.initRecorder()) { - initVideoButtons(); - } else { - if (!mIsVideoCaptureIntent) { - Utils.Companion.showToast(getApplicationContext(), R.string.video_mode_error); - } - } - } - - private void initVideoButtons() { - final Resources res = getResources(); - mTogglePhotoVideoBtn.setImageDrawable(res.getDrawable(R.mipmap.photo)); - mToggleCameraBtn.setVisibility(View.VISIBLE); - mShutterBtn.setImageDrawable(res.getDrawable(R.mipmap.video_rec)); - checkFlash(); - setupPreviewImage(false); - } - - private void setupPreviewImage(boolean isPhoto) { - final Uri uri = (isPhoto) ? MediaStore.Images.Media.EXTERNAL_CONTENT_URI : MediaStore.Video.Media.EXTERNAL_CONTENT_URI; - final long lastMediaId = getLastMediaId(uri); - if (lastMediaId == 0) { - return; - } - final ContentResolver cr = getContentResolver(); - mPreviewUri = Uri.withAppendedPath(uri, String.valueOf(lastMediaId)); - Bitmap tmb; - - if (isPhoto) { - tmb = MediaStore.Images.Thumbnails.getThumbnail(cr, lastMediaId, MediaStore.Images.Thumbnails.MICRO_KIND, null); - final int rotationDegrees = getImageRotation(); - tmb = rotateThumbnail(tmb, rotationDegrees); - } else { - tmb = MediaStore.Video.Thumbnails.getThumbnail(cr, lastMediaId, MediaStore.Video.Thumbnails.MICRO_KIND, null); - } - - setPreviewImage(tmb); - } - - private int getImageRotation() { - final String[] projection = {MediaStore.Images.ImageColumns.ORIENTATION}; - Cursor cursor = null; - try { - cursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, null, null, null); - if (cursor != null && cursor.moveToFirst()) { - final int orientationIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.ORIENTATION); - return cursor.getInt(orientationIndex); - } - } finally { - if (cursor != null) { - cursor.close(); - } - } - return 0; - } - - private Bitmap rotateThumbnail(Bitmap tmb, int degrees) { - if (degrees == 0) - return tmb; - - final Matrix matrix = new Matrix(); - matrix.setRotate(degrees, tmb.getWidth() / 2, tmb.getHeight() / 2); - return Bitmap.createBitmap(tmb, 0, 0, tmb.getWidth(), tmb.getHeight(), matrix, true); - } - - private void setPreviewImage(final Bitmap bmp) { - if (bmp != null) { - mLastPhotoVideoPreview.post(new Runnable() { - @Override - public void run() { - mLastPhotoVideoPreview.setImageBitmap(bmp); - } - }); - } - } - - private long getLastMediaId(Uri uri) { - final String[] projection = {MediaStore.Images.ImageColumns._ID}; - Cursor cursor = null; - try { - cursor = getContentResolver().query(uri, projection, null, null, MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC"); - if (cursor != null && cursor.moveToFirst()) { - final int idIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns._ID); - return cursor.getLong(idIndex); - } - } finally { - if (cursor != null) { - cursor.close(); - } - } - return 0; - } - - private void scheduleFadeOut() { - mFadeHandler.postDelayed(new Runnable() { - @Override - public void run() { - fadeOutButtons(); - } - }, FADE_DELAY); - } - - private void fadeOutButtons() { - fadeAnim(mSettingsBtn, .5f); - fadeAnim(mTogglePhotoVideoBtn, .0f); - fadeAnim(mLastPhotoVideoPreview, .0f); - } - - private void fadeInButtons() { - fadeAnim(mSettingsBtn, 1.f); - fadeAnim(mTogglePhotoVideoBtn, 1.f); - fadeAnim(mLastPhotoVideoPreview, 1.f); - scheduleFadeOut(); - } - - private void fadeAnim(View view, float value) { - view.animate().alpha(value).start(); - if (value == .0f) { - view.setClickable(false); - } else { - view.setClickable(true); - } - } - - private void hideNavigationBarIcons() { - getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE); - } - - private void hideTimer() { - mRecCurrTimer.setText(Utils.Companion.formatSeconds(0)); - mRecCurrTimer.setVisibility(View.GONE); - mCurrVideoRecTimer = 0; - mTimerHandler.removeCallbacksAndMessages(null); - } - - private void showTimer() { - mRecCurrTimer.setVisibility(View.VISIBLE); - setupTimer(); - } - - private void setupTimer() { - runOnUiThread(new Runnable() { - @Override - public void run() { - mRecCurrTimer.setText(Utils.Companion.formatSeconds(mCurrVideoRecTimer++)); - mTimerHandler.postDelayed(this, 1000); - } - }); - } - - @Override - protected void onResume() { - super.onResume(); - if (hasCameraAndStoragePermission()) { - resumeCameraItems(); - setupPreviewImage(mIsInPhotoMode); - scheduleFadeOut(); - - if (mIsVideoCaptureIntent && mIsInPhotoMode) { - togglePhotoVideo(); - checkButtons(); - } - } - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - } - - private void resumeCameraItems() { - final int cnt = Camera.getNumberOfCameras(); - if (cnt == 1) { - mToggleCameraBtn.setVisibility(View.INVISIBLE); - } - - if (mPreview.setCamera(mCurrCamera)) { - hideNavigationBarIcons(); - checkFlash(); - - if (mSensorManager != null) { - final Sensor accelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); - mSensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_GAME); - } - - if (!mIsInPhotoMode) { - initVideoButtons(); - } - } else { - Utils.Companion.showToast(getApplicationContext(), R.string.camera_switch_error); - } - } - - @Override - protected void onPause() { - super.onPause(); - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - if (!hasCameraAndStoragePermission() || mIsAskingPermissions) - return; - - if (mFadeHandler != null) - mFadeHandler.removeCallbacksAndMessages(null); - - hideTimer(); - if (mPreview != null) { - mPreview.releaseCamera(); - } - - if (mSensorManager != null) - mSensorManager.unregisterListener(this); - } - - @Override - public void onSensorChanged(SensorEvent event) { - if (event.values[0] < 6.5 && event.values[0] > -6.5) { - mOrientation = Constants.ORIENT_PORTRAIT; - } else { - if (event.values[0] > 0) { - mOrientation = Constants.ORIENT_LANDSCAPE_LEFT; - } else { - mOrientation = Constants.ORIENT_LANDSCAPE_RIGHT; - } - } - if (mOrientation != mLastHandledOrientation) { - int degrees = 0; - switch (mOrientation) { - case Constants.ORIENT_LANDSCAPE_LEFT: - degrees = 90; - break; - case Constants.ORIENT_LANDSCAPE_RIGHT: - degrees = -90; - break; - default: - break; - } - - animateViews(degrees); - mLastHandledOrientation = mOrientation; - } - } - - private void animateViews(int degrees) { - View[] views = {mToggleCameraBtn, mToggleFlashBtn, mTogglePhotoVideoBtn, mShutterBtn, mSettingsBtn, mLastPhotoVideoPreview}; - for (View view : views) { - rotate(view, degrees); - } - } - - private void rotate(View view, int degrees) { - view.animate().rotation(degrees).start(); - } - - @Override - public void onAccuracyChanged(Sensor sensor, int accuracy) { - - } - - private boolean checkCameraAvailable() { - if (!mIsCameraAvailable) { - Utils.Companion.showToast(getApplicationContext(), R.string.camera_unavailable); - } - return mIsCameraAvailable; - } - - @Override - public void setFlashAvailable(boolean available) { - if (available) { - mToggleFlashBtn.setVisibility(View.VISIBLE); - } else { - mToggleFlashBtn.setVisibility(View.INVISIBLE); - disableFlash(); - } - } - - @Override - public void setIsCameraAvailable(boolean available) { - mIsCameraAvailable = available; - } - - @Override - public int getCurrentOrientation() { - return mOrientation; - } - - @Override - public void videoSaved(Uri uri) { - setupPreviewImage(mIsInPhotoMode); - if (mIsVideoCaptureIntent) { - final Intent intent = new Intent(); - intent.setData(uri); - intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - setResult(RESULT_OK, intent); - finish(); - } - } - - @Override - public void drawFocusRect(int x, int y) { - if (mFocusRectView != null) { - mFocusRectView.drawFocusRect(x, y); - } - } - - @Override - public void mediaSaved(String path) { - final String[] paths = {path}; - MediaScannerConnection.scanFile(getApplicationContext(), paths, null, this); - - if (mIsImageCaptureIntent) { - setResult(RESULT_OK); - finish(); - } - } - - @Override - protected void onDestroy() { - super.onDestroy(); - Config.Companion.newInstance(getApplicationContext()).setFirstRun(false); - if (mPreview != null) - mPreview.releaseCamera(); - } - - @Override - public void onScanCompleted(String path, Uri uri) { - setupPreviewImage(mIsInPhotoMode); - } -} diff --git a/app/src/main/kotlin/com/simplemobiletools/camera/activities/MainActivity.kt b/app/src/main/kotlin/com/simplemobiletools/camera/activities/MainActivity.kt new file mode 100644 index 00000000..4fbd8c8f --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/camera/activities/MainActivity.kt @@ -0,0 +1,603 @@ +package com.simplemobiletools.camera.activities + +import android.Manifest +import android.app.Activity +import android.content.ActivityNotFoundException +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.content.res.Resources +import android.database.Cursor +import android.graphics.Bitmap +import android.graphics.Matrix +import android.hardware.* +import android.media.MediaScannerConnection +import android.net.Uri +import android.os.Build +import android.os.Bundle +import android.os.Handler +import android.provider.MediaStore +import android.support.v4.app.ActivityCompat +import android.view.* +import android.widget.RelativeLayout +import com.simplemobiletools.camera.* +import com.simplemobiletools.camera.Preview.PreviewListener +import com.simplemobiletools.camera.views.FocusRectView +import com.simplemobiletools.filepicker.extensions.hasStoragePermission +import com.simplemobiletools.filepicker.extensions.toast +import kotlinx.android.synthetic.main.activity_main.* +import java.util.* + +class MainActivity : SimpleActivity(), SensorEventListener, PreviewListener, PhotoProcessor.MediaSavedListener, MediaScannerConnection.OnScanCompletedListener { + companion object { + + private val CAMERA_STORAGE_PERMISSION = 1 + private val AUDIO_PERMISSION = 2 + private val FADE_DELAY = 5000 + + lateinit var mSensorManager: SensorManager + lateinit var mFocusRectView: FocusRectView + lateinit var mTimerHandler: Handler + lateinit var mFadeHandler: Handler + lateinit var mPreview: Preview + lateinit var mRes: Resources + + private var mPreviewUri: Uri? = null + private var mIsFlashEnabled = false + private var mIsInPhotoMode = false + private var mIsAskingPermissions = false + private var mIsCameraAvailable = false + private var mIsImageCaptureIntent = false + private var mIsVideoCaptureIntent = false + private var mIsHardwareShutterHandled = false + private var mCurrVideoRecTimer = 0 + private var mOrientation = 0 + private var mCurrCamera = 0 + private var mLastHandledOrientation = 0 + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + requestWindowFeature(Window.FEATURE_NO_TITLE) + window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN) + mRes = resources + tryInitCamera() + + supportActionBar?.hide() + + toggle_camera.setOnClickListener { toggleCamera() } + last_photo_video_preview.setOnClickListener { showLastMediaPreview() } + toggle_flash.setOnClickListener { toggleFlash() } + shutter.setOnClickListener { shutterPressed() } + settings.setOnClickListener { launchSettings() } + toggle_photo_video.setOnClickListener { handleTogglePhotoVideo() } + } + + override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { + return if (keyCode == KeyEvent.KEYCODE_CAMERA && !mIsHardwareShutterHandled) { + mIsHardwareShutterHandled = true + shutterPressed() + true + } else { + super.onKeyDown(keyCode, event) + } + } + + override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean { + if (keyCode == KeyEvent.KEYCODE_CAMERA) { + mIsHardwareShutterHandled = false + } + return super.onKeyUp(keyCode, event) + } + + private fun hideToggleModeAbout() { + toggle_photo_video.visibility = View.GONE + settings.visibility = View.GONE + } + + private fun tryInitCamera() { + if (hasCameraAndStoragePermission()) { + initializeCamera() + handleIntent() + } else { + val permissions = ArrayList(2) + if (!Utils.hasCameraPermission(applicationContext)) { + permissions.add(Manifest.permission.CAMERA) + } + if (!hasStoragePermission()) { + permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE) + } + ActivityCompat.requestPermissions(this, permissions.toTypedArray(), CAMERA_STORAGE_PERMISSION) + } + } + + 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) { + mIsImageCaptureIntent = true + hideToggleModeAbout() + val output = intent.extras.get(MediaStore.EXTRA_OUTPUT) + if (output != null && output is Uri) { + mPreview.setTargetUri(output) + } + } else if (intent.action == MediaStore.ACTION_VIDEO_CAPTURE) { + mIsVideoCaptureIntent = true + hideToggleModeAbout() + shutter.setImageDrawable(mRes.getDrawable(R.mipmap.video_rec)) + } + } + } + + private fun initializeCamera() { + setContentView(R.layout.activity_main) + + if (Utils.hasNavBar(mRes) && 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)) + } + + mCurrCamera = 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 (mCurrCamera == Camera.CameraInfo.CAMERA_FACING_BACK) R.mipmap.camera_front else R.mipmap.camera_back) + + mFocusRectView = FocusRectView(applicationContext) + view_holder.addView(mFocusRectView) + + mSensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager + mIsInPhotoMode = true + mTimerHandler = Handler() + mFadeHandler = Handler() + mIsFlashEnabled = config.lastFlashlightState + setupPreviewImage(true) + } + + private fun hasCameraAndStoragePermission() = Utils.hasCameraPermission(applicationContext) && hasStoragePermission() + + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + mIsAskingPermissions = false + + if (requestCode == CAMERA_STORAGE_PERMISSION) { + if (hasCameraAndStoragePermission()) { + initializeCamera() + handleIntent() + } else { + toast(R.string.no_permissions) + finish() + } + } else if (requestCode == AUDIO_PERMISSION) { + if (grantResults.size > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + togglePhotoVideo() + } else { + toast(R.string.no_audio_permissions) + if (mIsVideoCaptureIntent) + finish() + } + } + } + + fun toggleCamera() { + if (!checkCameraAvailable()) { + return + } + + if (mCurrCamera == Camera.CameraInfo.CAMERA_FACING_BACK) { + mCurrCamera = Camera.CameraInfo.CAMERA_FACING_FRONT + } else { + mCurrCamera = Camera.CameraInfo.CAMERA_FACING_BACK + } + + config.lastUsedCamera = mCurrCamera + var newIconId = R.mipmap.camera_front + mPreview.releaseCamera() + if (mPreview.setCamera(mCurrCamera)) { + if (mCurrCamera == Camera.CameraInfo.CAMERA_FACING_FRONT) { + newIconId = R.mipmap.camera_back + } + toggle_camera.setImageResource(newIconId) + disableFlash() + hideTimer() + } else { + toast(R.string.camera_switch_error) + } + } + + fun showLastMediaPreview() { + if (mPreviewUri == null) + return + + try { + val REVIEW_ACTION = "com.android.camera.action.REVIEW" + val intent = Intent(REVIEW_ACTION, mPreviewUri) + startActivity(intent) + } catch (e: ActivityNotFoundException) { + val intent = Intent(Intent.ACTION_VIEW, mPreviewUri) + if (intent.resolveActivity(packageManager) != null) { + startActivity(intent) + } else { + toast(R.string.no_gallery_app_available) + } + } + } + + fun toggleFlash() { + if (!checkCameraAvailable()) { + return + } + + mIsFlashEnabled = !mIsFlashEnabled + checkFlash() + } + + private fun checkFlash() { + if (mIsFlashEnabled) { + enableFlash() + } else { + disableFlash() + } + } + + private fun disableFlash() { + mPreview.disableFlash() + toggle_flash.setImageResource(R.mipmap.flash_off) + mIsFlashEnabled = false + config.lastFlashlightState = mIsFlashEnabled + } + + private fun enableFlash() { + mPreview.enableFlash() + toggle_flash.setImageResource(R.mipmap.flash_on) + mIsFlashEnabled = true + config.lastFlashlightState = mIsFlashEnabled + } + + private fun shutterPressed() { + if (checkCameraAvailable()) { + handleShutter() + } + } + + private fun handleShutter() { + if (mIsInPhotoMode) { + toggleBottomButtons(true) + mPreview.takePicture() + Handler().postDelayed({ toggleBottomButtons(false) }, Preview.PHOTO_PREVIEW_LENGTH.toLong()) + } else { + val isRecording = mPreview.toggleRecording() + if (isRecording) { + shutter.setImageDrawable(mRes.getDrawable(R.mipmap.video_stop)) + toggle_camera.visibility = View.INVISIBLE + showTimer() + } else { + shutter.setImageDrawable(mRes.getDrawable(R.mipmap.video_rec)) + toggle_camera.visibility = View.VISIBLE + hideTimer() + } + } + } + + private 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() + } + + fun launchSettings() { + if (settings.alpha == 1f) { + val intent = Intent(applicationContext, SettingsActivity::class.java) + startActivity(intent) + } else { + fadeInButtons() + } + } + + fun handleTogglePhotoVideo() { + togglePhotoVideo() + checkButtons() + } + + private fun togglePhotoVideo() { + if (!checkCameraAvailable()) { + return + } + + if (!Utils.hasAudioPermission(applicationContext)) { + ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.RECORD_AUDIO), AUDIO_PERMISSION) + mIsAskingPermissions = true + return + } + + if (mIsVideoCaptureIntent) + mPreview.trySwitchToVideo() + + disableFlash() + hideTimer() + mIsInPhotoMode = !mIsInPhotoMode + toggle_camera.visibility = View.VISIBLE + } + + private fun checkButtons() { + if (mIsInPhotoMode) { + initPhotoButtons() + } else { + tryInitVideoButtons() + } + } + + private fun initPhotoButtons() { + toggle_photo_video.setImageDrawable(mRes.getDrawable(R.mipmap.videocam)) + shutter.setImageDrawable(mRes.getDrawable(R.mipmap.camera)) + mPreview.initPhotoMode() + setupPreviewImage(true) + } + + private fun tryInitVideoButtons() { + if (mPreview.initRecorder()) { + initVideoButtons() + } else { + if (!mIsVideoCaptureIntent) { + toast(R.string.video_mode_error) + } + } + } + + private fun initVideoButtons() { + toggle_photo_video.setImageDrawable(mRes.getDrawable(R.mipmap.photo)) + toggle_camera.visibility = View.VISIBLE + shutter.setImageDrawable(mRes.getDrawable(R.mipmap.video_rec)) + checkFlash() + setupPreviewImage(false) + } + + private fun setupPreviewImage(isPhoto: Boolean) { + val uri = if (isPhoto) MediaStore.Images.Media.EXTERNAL_CONTENT_URI else MediaStore.Video.Media.EXTERNAL_CONTENT_URI + val lastMediaId = getLastMediaId(uri) + if (lastMediaId == 0L) { + return + } + val cr = contentResolver + mPreviewUri = Uri.withAppendedPath(uri, lastMediaId.toString()) + var tmb: Bitmap + + if (isPhoto) { + tmb = MediaStore.Images.Thumbnails.getThumbnail(cr, lastMediaId, MediaStore.Images.Thumbnails.MICRO_KIND, null) + val rotationDegrees = getImageRotation() + tmb = rotateThumbnail(tmb, rotationDegrees) + } else { + tmb = MediaStore.Video.Thumbnails.getThumbnail(cr, lastMediaId, MediaStore.Video.Thumbnails.MICRO_KIND, null) + } + + setPreviewImage(tmb) + } + + private fun getImageRotation(): Int { + val projection = arrayOf(MediaStore.Images.ImageColumns.ORIENTATION) + var cursor: Cursor? = null + try { + cursor = contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, null, null, null) + if (cursor != null && cursor.moveToFirst()) { + val orientationIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.ORIENTATION) + return cursor.getInt(orientationIndex) + } + } finally { + cursor?.close() + } + return 0 + } + + private fun rotateThumbnail(tmb: Bitmap, degrees: Int): Bitmap { + if (degrees == 0) + return tmb + + val matrix = Matrix() + matrix.setRotate(degrees.toFloat(), (tmb.width / 2).toFloat(), (tmb.height / 2).toFloat()) + return Bitmap.createBitmap(tmb, 0, 0, tmb.width, tmb.height, matrix, true) + } + + private fun setPreviewImage(bmp: Bitmap?) { + if (bmp != null) { + last_photo_video_preview.post { last_photo_video_preview.setImageBitmap(bmp) } + } + } + + private fun getLastMediaId(uri: Uri): Long { + val projection = arrayOf(MediaStore.Images.ImageColumns._ID) + var cursor: Cursor? = null + try { + cursor = contentResolver.query(uri, projection, null, null, "${MediaStore.Images.ImageColumns.DATE_TAKEN} DESC") + if (cursor != null && cursor.moveToFirst()) { + val idIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns._ID) + return cursor.getLong(idIndex) + } + } finally { + cursor?.close() + } + return 0 + } + + private fun scheduleFadeOut() = mFadeHandler.postDelayed({ fadeOutButtons() }, FADE_DELAY.toLong()) + + private fun fadeOutButtons() { + fadeAnim(settings, .5f) + fadeAnim(toggle_photo_video, .0f) + fadeAnim(last_photo_video_preview, .0f) + } + + private fun fadeInButtons() { + fadeAnim(settings, 1f) + fadeAnim(toggle_photo_video, 1f) + fadeAnim(last_photo_video_preview, 1f) + scheduleFadeOut() + } + + private fun fadeAnim(view: View, value: Float) { + view.animate().alpha(value).start() + view.isClickable = value != .0f + } + + private fun hideNavigationBarIcons() { + window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LOW_PROFILE + } + + private fun hideTimer() { + video_rec_curr_timer.text = Utils.formatSeconds(0) + video_rec_curr_timer.visibility = View.GONE + mCurrVideoRecTimer = 0 + mTimerHandler.removeCallbacksAndMessages(null) + } + + private fun showTimer() { + video_rec_curr_timer.visibility = View.VISIBLE + setupTimer() + } + + private fun setupTimer() { + runOnUiThread(object : Runnable { + override fun run() { + video_rec_curr_timer.text = Utils.formatSeconds(mCurrVideoRecTimer++) + mTimerHandler.postDelayed(this, 1000) + } + }) + } + + override fun onResume() { + super.onResume() + if (hasCameraAndStoragePermission()) { + resumeCameraItems() + setupPreviewImage(mIsInPhotoMode) + scheduleFadeOut() + + if (mIsVideoCaptureIntent && mIsInPhotoMode) { + togglePhotoVideo() + checkButtons() + } + } + window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + } + + private fun resumeCameraItems() { + val cnt = Camera.getNumberOfCameras() + if (cnt == 1) { + toggle_camera.visibility = View.INVISIBLE + } + + if (mPreview.setCamera(mCurrCamera)) { + hideNavigationBarIcons() + checkFlash() + + val accelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) + mSensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_GAME) + + if (!mIsInPhotoMode) { + initVideoButtons() + } + } else { + toast(R.string.camera_switch_error) + } + } + + override fun onPause() { + super.onPause() + window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + if (!hasCameraAndStoragePermission() || mIsAskingPermissions) + return + + mFadeHandler.removeCallbacksAndMessages(null) + + hideTimer() + mPreview.releaseCamera() + mSensorManager.unregisterListener(this) + } + + override fun onSensorChanged(event: SensorEvent) { + if (event.values[0] < 6.5 && event.values[0] > -6.5) { + mOrientation = Constants.ORIENT_PORTRAIT + } else { + if (event.values[0] > 0) { + mOrientation = Constants.ORIENT_LANDSCAPE_LEFT + } else { + mOrientation = Constants.ORIENT_LANDSCAPE_RIGHT + } + } + + if (mOrientation != mLastHandledOrientation) { + val degrees = when (mOrientation) { + Constants.ORIENT_LANDSCAPE_LEFT -> 90 + Constants.ORIENT_LANDSCAPE_RIGHT -> -90 + else -> 0 + } + + animateViews(degrees) + mLastHandledOrientation = mOrientation + } + } + + private fun animateViews(degrees: Int) { + val views = arrayOf(toggle_camera, toggle_flash, toggle_photo_video, shutter, settings, last_photo_video_preview) + for (view in views) { + rotate(view, degrees) + } + } + + private fun rotate(view: View, degrees: Int) = view.animate().rotation(degrees.toFloat()).start() + + override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) { + } + + private fun checkCameraAvailable(): Boolean { + if (!mIsCameraAvailable) { + toast(R.string.camera_unavailable) + } + return mIsCameraAvailable + } + + override fun setFlashAvailable(available: Boolean) { + if (available) { + toggle_flash.visibility = View.VISIBLE + } else { + toggle_flash.visibility = View.INVISIBLE + disableFlash() + } + } + + override fun setIsCameraAvailable(available: Boolean) { + mIsCameraAvailable = available + } + + override fun getCurrentOrientation() = mOrientation + + override fun videoSaved(uri: Uri) { + setupPreviewImage(mIsInPhotoMode) + if (mIsVideoCaptureIntent) { + Intent().apply { + data = uri + flags = Intent.FLAG_GRANT_READ_URI_PERMISSION + setResult(Activity.RESULT_OK, this) + } + finish() + } + } + + override fun drawFocusRect(x: Int, y: Int) = mFocusRectView.drawFocusRect(x, y) + + override fun mediaSaved(path: String) { + val paths = arrayOf(path) + MediaScannerConnection.scanFile(applicationContext, paths, null, this) + + if (mIsImageCaptureIntent) { + setResult(Activity.RESULT_OK) + finish() + } + } + + override fun onDestroy() { + super.onDestroy() + config.isFirstRun = false + mPreview.releaseCamera() + } + + override fun onScanCompleted(path: String, uri: Uri) = setupPreviewImage(mIsInPhotoMode) +}