convert MainActivity to kotlin

This commit is contained in:
tibbi 2016-11-15 23:47:06 +01:00
parent c50608aa27
commit 900f12634e
4 changed files with 605 additions and 715 deletions

View File

@ -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<Byte
val config = Config.newInstance(activity)
if (config.treeUri.isEmpty()) {
activity.runOnUiThread {
Utils.showToast(activity, R.string.save_error_internal_storage)
activity.toast(R.string.save_error_internal_storage)
}
config.savePhotosFolder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).toString()
return ""

View File

@ -9,7 +9,6 @@ import android.graphics.Point
import android.hardware.Camera
import android.support.v4.content.ContextCompat
import com.simplemobiletools.filepicker.extensions.getFileDocument
import com.simplemobiletools.filepicker.extensions.hasStoragePermission
import com.simplemobiletools.filepicker.extensions.needsStupidWritePermissions
import com.simplemobiletools.filepicker.extensions.toast
import java.io.File
@ -102,8 +101,6 @@ class Utils {
fun hasCameraPermission(context: Context) = ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED
fun hasStoragePermission(context: Context) = context.hasStoragePermission()
fun hasAudioPermission(cxt: Context) = ContextCompat.checkSelfPermission(cxt, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED
fun needsStupidWritePermissions(context: Context, path: String) = context.needsStupidWritePermissions(path)

View File

@ -1,711 +0,0 @@
package com.simplemobiletools.camera.activities;
import android.Manifest;
import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
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.Camera;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
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.support.v7.app.ActionBar;
import android.view.KeyEvent;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.simplemobiletools.camera.Config;
import com.simplemobiletools.camera.Constants;
import com.simplemobiletools.camera.views.FocusRectView;
import com.simplemobiletools.camera.PhotoProcessor;
import com.simplemobiletools.camera.Preview;
import com.simplemobiletools.camera.Preview.PreviewListener;
import com.simplemobiletools.camera.R;
import com.simplemobiletools.camera.Utils;
import java.util.ArrayList;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class MainActivity extends SimpleActivity
implements SensorEventListener, PreviewListener, PhotoProcessor.MediaSavedListener, MediaScannerConnection.OnScanCompletedListener {
@BindView(R.id.view_holder) RelativeLayout mViewHolder;
@BindView(R.id.toggle_camera) ImageView mToggleCameraBtn;
@BindView(R.id.toggle_flash) ImageView mToggleFlashBtn;
@BindView(R.id.toggle_photo_video) ImageView mTogglePhotoVideoBtn;
@BindView(R.id.shutter) ImageView mShutterBtn;
@BindView(R.id.video_rec_curr_timer) TextView mRecCurrTimer;
@BindView(R.id.settings) View mSettingsBtn;
@BindView(R.id.last_photo_video_preview) ImageView mLastPhotoVideoPreview;
private static final int CAMERA_STORAGE_PERMISSION = 1;
private static final int AUDIO_PERMISSION = 2;
private static final int FADE_DELAY = 5000;
private static SensorManager mSensorManager;
private static Preview mPreview;
private static FocusRectView mFocusRectView;
private static Handler mTimerHandler;
private static Uri mPreviewUri;
private static Handler mFadeHandler;
private static boolean mIsFlashEnabled;
private static boolean mIsInPhotoMode;
private static boolean mIsAskingPermissions;
private static boolean mIsCameraAvailable;
private static boolean mIsImageCaptureIntent;
private static boolean mIsVideoCaptureIntent;
private static boolean mIsHardwareShutterHandled;
private static int mCurrVideoRecTimer;
private static int mOrientation;
private static int mCurrCamera;
private static int mLastHandledOrientation;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
tryInitCamera();
final ActionBar actionbar = getSupportActionBar();
if (actionbar != null)
actionbar.hide();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_CAMERA && !mIsHardwareShutterHandled) {
mIsHardwareShutterHandled = true;
shutterPressed();
return true;
} else {
return super.onKeyDown(keyCode, event);
}
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_CAMERA) {
mIsHardwareShutterHandled = false;
}
return super.onKeyUp(keyCode, event);
}
private void hideToggleModeAbout() {
if (mTogglePhotoVideoBtn != null)
mTogglePhotoVideoBtn.setVisibility(View.GONE);
if (mSettingsBtn != null)
mSettingsBtn.setVisibility(View.GONE);
}
private void tryInitCamera() {
if (hasCameraAndStoragePermission()) {
initializeCamera();
handleIntent();
} else {
final List<String> 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);
}
}

View File

@ -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<String>(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<String>, 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<View>(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)
}