diff --git a/.idea/dictionaries/Jerry.xml b/.idea/dictionaries/Jerry.xml index d8446e9..19d38da 100644 --- a/.idea/dictionaries/Jerry.xml +++ b/.idea/dictionaries/Jerry.xml @@ -1,6 +1,7 @@ + autofocus jerryc unlockme diff --git a/app/build.gradle b/app/build.gradle index 60b2f50..23036bc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,7 +8,7 @@ android { minSdkVersion 21 targetSdkVersion 29 versionCode 1 - versionName "1.0" + versionName "0.0.1-alpha" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" resConfigs "xxhdpi" } diff --git a/app/src/androidTest/java/jerryc05/unlockme/ExampleInstrumentedTest.java b/app/src/androidTest/java/jerryc05/unlockme/ExampleInstrumentedTest.java index 45d334e..4e8315c 100644 --- a/app/src/androidTest/java/jerryc05/unlockme/ExampleInstrumentedTest.java +++ b/app/src/androidTest/java/jerryc05/unlockme/ExampleInstrumentedTest.java @@ -1,15 +1,9 @@ package jerryc05.unlockme; -import android.content.Context; - -import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.ext.junit.runners.AndroidJUnit4; -import org.junit.Test; import org.junit.runner.RunWith; -import static org.junit.Assert.*; - /** * Instrumented test, which will execute on an Android device. * diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c72744c..bdb2c45 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -6,13 +6,16 @@ + diff --git a/app/src/main/java/jerryc05/unlockme/MainActivity.java b/app/src/main/java/jerryc05/unlockme/MainActivity.java index 0dbd469..b19bf7b 100644 --- a/app/src/main/java/jerryc05/unlockme/MainActivity.java +++ b/app/src/main/java/jerryc05/unlockme/MainActivity.java @@ -9,50 +9,96 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCaptureSession; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CameraDevice.StateCallback; +import android.hardware.camera2.CameraManager; import android.os.Build; import android.os.Bundle; -import android.provider.MediaStore; import android.util.Log; import android.view.View; import android.widget.Toast; +import java.util.Objects; +import java.util.concurrent.locks.ReentrantLock; + import jerryc05.unlockme.receivers.MyDAReceiver; @SuppressWarnings("NullableProblems") public class MainActivity extends Activity { - private final static String TAG = - MainActivity.class.getName(); - private DevicePolicyManager devicePolicyManager; - private ComponentName componentName; - private final static int REQUEST_CODE_CAMERA = 0; + private final static String + TAG = MainActivity.class.getName(); + private final static int + REQUEST_CODE_CAMERA = 0, + REQUEST_CODE_DEVICE_ADMIN = 1; + + protected ReentrantLock requestDeviceAdminLock; + protected DevicePolicyManager mDevicePolicyManager; + private ComponentName mComponentName; + private String cameraID; + private CameraCharacteristics mCameraCharacteristics; + private CameraManager mCameraManager; + private CameraCaptureSession mCameraCaptureSession; + private StateCallback mStateCallback; + protected CameraDevice mCameraDevice; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - new Thread(new Runnable() { - @Override - public void run() { - handleDeviceAdmin(); - findViewById(R.id.activity_main_button_takePhoto) - .setOnClickListener(new View.OnClickListener() { + findViewById(R.id.activity_main_button_takePhoto) + .setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + new Thread(new Runnable() { @Override - public void onClick(View view) { + public void run() { takePhoto(); } - }); - } - }).start(); + }).start(); + } + }); } @Override protected void onResume() { super.onResume(); - if (isDeviceAdminDisabled()) - requireDeviceAdmin(); + new Thread(new Runnable() { + @Override + public void run() { + if (requestDeviceAdminLock == null) + requestDeviceAdminLock = new ReentrantLock(); + requestDeviceAdminLock.lock(); + requestDeviceAdmin(); + } + }).start(); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, + Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + if (requestCode == REQUEST_CODE_DEVICE_ADMIN && + !mDevicePolicyManager.isAdminActive(mComponentName)) { + if (requestDeviceAdminLock == null) + requestDeviceAdminLock = new ReentrantLock(); + requestDeviceAdminLock.lock(); + AlertExceptionToUI( + new UnsupportedOperationException( + "Device Admin permission not acquired!"), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + System.exit(1); + } + }); + } } @Override @@ -60,94 +106,209 @@ public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (requestCode == REQUEST_CODE_CAMERA) { - final String granted = grantResults.length > 0 && - grantResults[0] == PackageManager.PERMISSION_GRANTED + final boolean granted = grantResults.length > 0 && + grantResults[0] == PackageManager.PERMISSION_GRANTED; + final String granted_str = granted ? "Camera Permission Granted √" : "Camera Permission Denied ×"; + if (BuildConfig.DEBUG) Log.d(TAG, "onRequestPermissionsResult: " + granted); + Toast.makeText(this, - granted, Toast.LENGTH_SHORT) + granted_str, Toast.LENGTH_SHORT) .show(); - takePhoto(); + if (granted) takePhoto(); } } + void requestDeviceAdmin() { + if (requestDeviceAdminLock != null) { + requestDeviceAdminLock.unlock(); + requestDeviceAdminLock = null; + } + if (mDevicePolicyManager == null) + mDevicePolicyManager = (DevicePolicyManager) Objects.requireNonNull( + getSystemService(Context.DEVICE_POLICY_SERVICE)); + if (mComponentName == null) + mComponentName = + new ComponentName(getApplicationContext(), MyDAReceiver.class); + + if (!mDevicePolicyManager.isAdminActive(mComponentName)) { + final Intent intentDeviceAdmin = new Intent( + DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN); + intentDeviceAdmin.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, + mComponentName); + intentDeviceAdmin.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, + getResources().getString(R.string.device_admin_explanation)); + + runOnUiThread(new Runnable() { + @Override + public void run() { + startActivityForResult(intentDeviceAdmin, REQUEST_CODE_DEVICE_ADMIN); + } + }); + } else if (BuildConfig.DEBUG) + Log.d(TAG, "requestDeviceAdmin: DA is activated!"); + } + void takePhoto() { if (BuildConfig.DEBUG) Log.d(TAG, "takePhoto: "); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && - checkSelfPermission(Manifest.permission.CAMERA) - != PackageManager.PERMISSION_GRANTED) { - if (shouldShowRequestPermissionRationale( - Manifest.permission.CAMERA)) - new AlertDialog.Builder(this) - .setTitle("Permission Required") - .setMessage(getResources().getString(R.string.permission_camera_explanation)) - .setCancelable(false) - .setPositiveButton("OK", new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - requestPermissions( - new String[]{Manifest.permission.CAMERA}, - REQUEST_CODE_CAMERA); - } - }) - .show(); - else - requestPermissions( - new String[]{Manifest.permission.CAMERA}, - REQUEST_CODE_CAMERA); - - } else { - final int REQUEST_IMAGE_CAPTURE = 1; + if (requestRuntimePermissions()) { + if (BuildConfig.DEBUG) + Log.d(TAG, "takePhoto: Permission acquired!"); - Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); - if (takePictureIntent.resolveActivity(getPackageManager()) != null) { - startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE); - } + if (cameraID == null || mStateCallback == null) + setupCamera2(); + openCamera2(); } } - void handleDeviceAdmin() { - devicePolicyManager = (DevicePolicyManager) - getSystemService(Context.DEVICE_POLICY_SERVICE); - componentName = new ComponentName( - getApplicationContext(), MyDAReceiver.class); + private boolean requestRuntimePermissions() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || + checkSelfPermission(Manifest.permission.CAMERA) == + PackageManager.PERMISSION_GRANTED) + return true; + + if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) + runOnUiThread(new Runnable() { + @Override + public void run() { + new AlertDialog.Builder(MainActivity.this) + .setTitle("Permission Required") + .setMessage(getResources().getString( + R.string.permission_camera_explanation)) + .setCancelable(false) + .setPositiveButton("OK", + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, + int i) { + requestPermissions( + new String[]{Manifest.permission.CAMERA}, + REQUEST_CODE_CAMERA); + } + }) + .show(); + } + }); + else + requestPermissions(new String[]{Manifest.permission.CAMERA}, + REQUEST_CODE_CAMERA); - if (isDeviceAdminDisabled()) - requireDeviceAdmin(); + return false; } - private boolean isDeviceAdminDisabled() { - final boolean disabled = devicePolicyManager == null || - !devicePolicyManager.isAdminActive(componentName); + private void setupCamera2() { + if (mCameraManager == null) + mCameraManager = + (CameraManager) getSystemService(Context.CAMERA_SERVICE); + try { + assert mCameraManager != null; + for (String eachCameraID : mCameraManager.getCameraIdList()) { - runOnUiThread(new Runnable() { + CameraCharacteristics characteristics = + mCameraManager.getCameraCharacteristics(eachCameraID); + + final Integer FACING = + characteristics.get(CameraCharacteristics.LENS_FACING); + if (FACING == null || + FACING != CameraCharacteristics.LENS_FACING_FRONT) + continue; + + cameraID = eachCameraID; + mCameraCharacteristics = characteristics; + break; + } + if (cameraID == null) + throw new UnsupportedOperationException("Cannot find Front Camera!"); + + if (mStateCallback == null) + mStateCallback = new CameraDevice.StateCallback() { + + @Override + public void onOpened(CameraDevice cameraDevice) { + mCameraDevice = cameraDevice; + //创建CameraPreviewSession +// createCameraPreviewSession(); +// try { +// mCaptureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); +// mCaptureRequestBuilder.addTarget(previewSurface); +// cameraDevice.createCaptureSession(Arrays.asList(previewSurface, mImageReader.getSurface()), new CameraCaptureSession.StateCallback() { +// @Override +// public void onConfigured(CameraCaptureSession session) { +// try { +// mCaptureRequest = mCaptureRequestBuilder.build(); +// mCameraCaptureSession = session; +// mCameraCaptureSession.setRepeatingRequest(mCaptureRequest, null, mCameraHandler); +// } catch (CameraAccessException e) { +// e.printStackTrace(); +// } +// } +// +// @Override +// public void onConfigureFailed(CameraCaptureSession session) { +// +// } +// }, mCameraHandler); +// } catch (CameraAccessException e) { +// e.printStackTrace(); +// } + } + + @Override + public void onDisconnected(CameraDevice cameraDevice) { + cameraDevice.close(); + mCameraDevice = null; + } + + @Override + public void onError(CameraDevice cameraDevice, int error) { + cameraDevice.close(); + mCameraDevice = null; + } + }; + + } catch (Exception e) { + AlertExceptionToUI(e); + } + } + + private void openCamera2() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || + checkSelfPermission(Manifest.permission.CAMERA) == + PackageManager.PERMISSION_GRANTED) { + try { + mCameraManager.openCamera(cameraID, mStateCallback, null); + } catch (CameraAccessException e) { + AlertExceptionToUI(e); + } + } else + requestRuntimePermissions(); + } + + private void AlertExceptionToUI(Exception e) { + AlertExceptionToUI(e, new DialogInterface.OnClickListener() { @Override - public void run() { - Toast.makeText(MainActivity.this, - disabled ? "Device Admin Disabled ×" - : "Device Admin Enabled √", Toast.LENGTH_SHORT) - .show(); + public void onClick(DialogInterface dialogInterface, int i) { + throw new UnsupportedOperationException(e); } }); - return disabled; } - private void requireDeviceAdmin() { - Intent intent = new Intent( - DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN); - intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, - componentName); - intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, - getResources().getString(R.string.device_admin_explanation)); - + private void AlertExceptionToUI(Exception e, + DialogInterface.OnClickListener onClickListener) { runOnUiThread(new Runnable() { @Override public void run() { - startActivity(intent); + new AlertDialog.Builder(MainActivity.this) + .setTitle("Fatal Error") + .setMessage(e.toString()) + .setCancelable(false) + .setPositiveButton("OK", onClickListener) + .show(); } }); } diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml deleted file mode 100644 index 1f6bb29..0000000 --- a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 0d025f9..0000000 --- a/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_launcher_shield_foreground.xml b/app/src/main/res/drawable/ic_launcher_shield_foreground.xml new file mode 100644 index 0000000..bf58de2 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_shield_foreground.xml @@ -0,0 +1,45 @@ + + + + + + + + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index eca70cf..0000000 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_shield.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_shield.xml new file mode 100644 index 0000000..0778fd1 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_shield.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index b0907ca..0000000 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_shield.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_shield.png new file mode 100644 index 0000000..7452dba Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_shield.png differ diff --git a/app/src/main/res/values/ic_launcher_shield_background.xml b/app/src/main/res/values/ic_launcher_shield_background.xml new file mode 100644 index 0000000..652e08f --- /dev/null +++ b/app/src/main/res/values/ic_launcher_shield_background.xml @@ -0,0 +1,4 @@ + + + #FFFFFF + \ No newline at end of file diff --git a/app/src/test/java/jerryc05/unlockme/ExampleUnitTest.java b/app/src/test/java/jerryc05/unlockme/ExampleUnitTest.java index cdcbdc1..eb1ac34 100644 --- a/app/src/test/java/jerryc05/unlockme/ExampleUnitTest.java +++ b/app/src/test/java/jerryc05/unlockme/ExampleUnitTest.java @@ -1,9 +1,5 @@ package jerryc05.unlockme; -import org.junit.Test; - -import static org.junit.Assert.*; - /** * Example local unit test, which will execute on the development machine (host). *