From 7642ed4ec329bbd2e6c5a38bd6763190e0e6c054 Mon Sep 17 00:00:00 2001 From: Jerry Chen Date: Tue, 9 Jul 2019 16:15:55 -0400 Subject: [PATCH] v0.0.1-alpha; change icon; optimize device admin logic; Signed-off-by: Jerry Chen --- .idea/dictionaries/Jerry.xml | 1 + app/build.gradle | 2 +- .../unlockme/ExampleInstrumentedTest.java | 6 - app/src/main/AndroidManifest.xml | 7 +- .../java/jerryc05/unlockme/MainActivity.java | 315 +++++++++++++----- .../drawable-v24/ic_launcher_foreground.xml | 34 -- .../res/drawable/ic_launcher_background.xml | 170 ---------- .../ic_launcher_shield_foreground.xml | 45 +++ .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 - .../mipmap-anydpi-v26/ic_launcher_shield.xml | 5 + .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 6387 -> 0 bytes .../res/mipmap-xxhdpi/ic_launcher_shield.png | Bin 0 -> 8440 bytes .../values/ic_launcher_shield_background.xml | 4 + .../jerryc05/unlockme/ExampleUnitTest.java | 4 - 14 files changed, 299 insertions(+), 299 deletions(-) delete mode 100644 app/src/main/res/drawable-v24/ic_launcher_foreground.xml delete mode 100644 app/src/main/res/drawable/ic_launcher_background.xml create mode 100644 app/src/main/res/drawable/ic_launcher_shield_foreground.xml delete mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher_shield.xml delete mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_shield.png create mode 100644 app/src/main/res/values/ic_launcher_shield_background.xml 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 b0907cac3bfd8fbfdc46e1108247f0a1055387ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6387 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 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 0000000000000000000000000000000000000000..7452dbaa807685110d543ac9d9939c9b57adec7d GIT binary patch literal 8440 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 a?)ZP|uKrtu_uMA{0000 + + #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). *