mirror of
https://github.com/chararomandroid/android_device_samsung_a20
synced 2026-08-22 20:15:32 -04:00
universal7885: CameraLightSensor: Convert to Kotlin
Change-Id: I8162d1f88583143a333b282ad59e45085ec66023 Signed-off-by: roynatech2544 <whiteshell2544@naver.com>
This commit is contained in:
parent
e3848cdb11
commit
7f2d2c5b3c
5 changed files with 447 additions and 446 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
android_app {
|
android_app {
|
||||||
name: "CameraLightSensor",
|
name: "CameraLightSensor",
|
||||||
srcs: [
|
srcs: [
|
||||||
"src/**/*.java",
|
"src/**/*.kt",
|
||||||
],
|
],
|
||||||
platform_apis: true,
|
platform_apis: true,
|
||||||
privileged: true,
|
privileged: true,
|
||||||
|
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
package com.eurekateam.cameralightsensor;
|
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import com.eurekateam.cameralightsensor.CameraLightSensorService;
|
|
||||||
|
|
||||||
public class BootReceiver extends BroadcastReceiver {
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
if (intent.getAction().equalsIgnoreCase(Intent.ACTION_BOOT_COMPLETED)) {
|
|
||||||
if (context != null) {
|
|
||||||
final Intent i = new Intent(context, CameraLightSensorService.class);
|
|
||||||
context.startForegroundService(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
package com.eurekateam.cameralightsensor
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
|
||||||
|
class BootReceiver : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
if (intent.action.equals(Intent.ACTION_BOOT_COMPLETED, ignoreCase = true)) {
|
||||||
|
val i = Intent(context, CameraLightSensorService::class.java)
|
||||||
|
context.startForegroundService(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,427 +0,0 @@
|
||||||
package com.eurekateam.cameralightsensor;
|
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.app.Notification;
|
|
||||||
import android.app.NotificationChannel;
|
|
||||||
import android.app.NotificationManager;
|
|
||||||
import android.app.PendingIntent;
|
|
||||||
import android.app.Service;
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.IntentFilter;
|
|
||||||
import android.content.pm.ServiceInfo;
|
|
||||||
import android.database.ContentObserver;
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.graphics.BitmapFactory;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.ImageFormat;
|
|
||||||
import android.hardware.camera2.CameraAccessException;
|
|
||||||
import android.hardware.camera2.CameraCaptureSession;
|
|
||||||
import android.hardware.camera2.CameraCharacteristics;
|
|
||||||
import android.hardware.camera2.CameraDevice;
|
|
||||||
import android.hardware.camera2.CameraManager;
|
|
||||||
import android.hardware.camera2.CaptureRequest;
|
|
||||||
import android.media.Image;
|
|
||||||
import android.media.ImageReader;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.IBinder;
|
|
||||||
import android.os.Looper;
|
|
||||||
import android.os.PowerManager;
|
|
||||||
import android.os.SystemClock;
|
|
||||||
import android.provider.Settings;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import androidx.core.app.NotificationCompat;
|
|
||||||
import androidx.core.content.ContextCompat;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
public class CameraLightSensorService extends Service {
|
|
||||||
protected static final String TAG = CameraLightSensorService.class.getSimpleName();
|
|
||||||
static final boolean DEBUG = false;
|
|
||||||
IntentFilter screenStateFilter;
|
|
||||||
private Context mContext;
|
|
||||||
public ContentResolver contentResolver;
|
|
||||||
private boolean mRegistered;
|
|
||||||
protected static final int CAMERA_CHOICE = CameraCharacteristics.LENS_FACING_FRONT;
|
|
||||||
boolean destroy;
|
|
||||||
protected CameraDevice cameraDevice;
|
|
||||||
protected CameraCaptureSession session;
|
|
||||||
volatile boolean avail = true;
|
|
||||||
protected ImageReader imageReader;
|
|
||||||
private CameraManager manager;
|
|
||||||
private final static int DELAY = 5 * 1000; // 5 Seconds
|
|
||||||
private boolean mServiceStarted;
|
|
||||||
volatile private boolean mLock;
|
|
||||||
private boolean mThreadRunning;
|
|
||||||
|
|
||||||
public Notification PushNotification()
|
|
||||||
{
|
|
||||||
NotificationManager nm = (NotificationManager) mContext.getSystemService(NOTIFICATION_SERVICE);
|
|
||||||
NotificationChannel channel = new NotificationChannel(
|
|
||||||
mContext.getBasePackageName(), "CameraLightSensor",
|
|
||||||
NotificationManager.IMPORTANCE_LOW
|
|
||||||
);
|
|
||||||
nm.createNotificationChannel(channel);
|
|
||||||
NotificationCompat.Builder builder =
|
|
||||||
new NotificationCompat.Builder(mContext, mContext.getBasePackageName());
|
|
||||||
Intent notificationIntent = new Intent(mContext, CameraLightSensorService.class);
|
|
||||||
PendingIntent contentIntent = PendingIntent.getActivity(mContext,50,
|
|
||||||
notificationIntent,PendingIntent.FLAG_IMMUTABLE);
|
|
||||||
|
|
||||||
//set
|
|
||||||
builder.setContentIntent(contentIntent);
|
|
||||||
builder.setSmallIcon(R.drawable.ic_brightness);
|
|
||||||
builder.setContentTitle("Camera Light Sensor Service");
|
|
||||||
builder.setChannelId(mContext.getBasePackageName());
|
|
||||||
|
|
||||||
return builder.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
if (DEBUG) Log.d(TAG, "Destroying service");
|
|
||||||
if(mRegistered) contentResolver.unregisterContentObserver(mSettingsObserver);
|
|
||||||
mRegistered = false;
|
|
||||||
cameraDevice.close();
|
|
||||||
if (session != null && avail) {
|
|
||||||
try {
|
|
||||||
session.abortCaptures();
|
|
||||||
session.close();
|
|
||||||
} catch (CameraAccessException e) {
|
|
||||||
Log.e(TAG, e.getMessage());
|
|
||||||
} catch (IllegalStateException e2){
|
|
||||||
Log.e(TAG, "Session Already Closed");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
manager.unregisterAvailabilityCallback(availabilityCallback);
|
|
||||||
avail = false;
|
|
||||||
destroy = true;
|
|
||||||
stopForeground(true);
|
|
||||||
super.onDestroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IBinder onBind(Intent intent) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final BroadcastReceiver mScreenStateReceiver = new BroadcastReceiver() {
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
|
|
||||||
onDisplayOn();
|
|
||||||
mServiceStarted = true;
|
|
||||||
} else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
|
|
||||||
if (mServiceStarted) onDisplayOff();
|
|
||||||
mServiceStarted = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// Make a listener for settings
|
|
||||||
ContentObserver mSettingsObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
|
|
||||||
@Override
|
|
||||||
public void onChange(boolean selfChange) {
|
|
||||||
super.onChange(selfChange);
|
|
||||||
Log.i(TAG, "observer: Brightness Settings Changed");
|
|
||||||
try {
|
|
||||||
if(Settings.System.getInt(contentResolver, Settings.System.SCREEN_BRIGHTNESS_MODE)
|
|
||||||
== Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC){
|
|
||||||
registerReceiver(mScreenStateReceiver, screenStateFilter);
|
|
||||||
mRegistered = true;
|
|
||||||
onDisplayOn();
|
|
||||||
}else{
|
|
||||||
if(mRegistered) unregisterReceiver(mScreenStateReceiver);
|
|
||||||
mRegistered = false;
|
|
||||||
onDisplayOff();
|
|
||||||
}
|
|
||||||
} catch (Settings.SettingNotFoundException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public boolean deliverSelfNotifications() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private void onDisplayOn() {
|
|
||||||
if(DEBUG) Log.d(TAG, "Screen is on. Starting Service...");
|
|
||||||
avail = true;
|
|
||||||
readyCamera();
|
|
||||||
}
|
|
||||||
private void onDisplayOff(){
|
|
||||||
if(DEBUG) Log.d(TAG, "Screen is off. Stopping Service...");
|
|
||||||
thread.interrupt();
|
|
||||||
avail = false;
|
|
||||||
mThreadRunning = false;
|
|
||||||
}
|
|
||||||
public static void BatteryOptimization(Context context){
|
|
||||||
Intent intent = new Intent();
|
|
||||||
String packageName = context.getPackageName();
|
|
||||||
PowerManager pm = (PowerManager) context.getSystemService(POWER_SERVICE);
|
|
||||||
if (!pm.isIgnoringBatteryOptimizations(packageName)) {
|
|
||||||
intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
|
|
||||||
intent.setData(Uri.parse("package:" + packageName));
|
|
||||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
context.startActivity(intent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
protected CameraDevice.StateCallback cameraStateCallback = new CameraDevice.StateCallback() {
|
|
||||||
@Override
|
|
||||||
public void onOpened(@NonNull CameraDevice camera) {
|
|
||||||
Log.d(TAG, "CameraDevice.StateCallback onOpened");
|
|
||||||
cameraDevice = camera;
|
|
||||||
actOnReadyCameraDevice();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDisconnected(@NonNull CameraDevice camera) {
|
|
||||||
Log.w(TAG, "CameraDevice.StateCallback onDisconnected");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(@NonNull CameraDevice camera, int error) {
|
|
||||||
Log.e(TAG, "CameraDevice.StateCallback onError " + error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
protected CameraCaptureSession.StateCallback sessionStateCallback = new CameraCaptureSession.StateCallback() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReady(CameraCaptureSession session) {
|
|
||||||
if (!destroy) {
|
|
||||||
CameraLightSensorService.this.session = session;
|
|
||||||
try {
|
|
||||||
if (createCaptureRequest() == null) return;
|
|
||||||
try {
|
|
||||||
session.capture(createCaptureRequest(), captureCallback, null);
|
|
||||||
} catch (CameraAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (IllegalStateException e) {
|
|
||||||
Log.w(TAG, "onReady: Session is NULL");
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(TAG, "Camera is in use");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onConfigured(CameraCaptureSession session) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
protected ImageReader.OnImageAvailableListener onImageAvailableListener = reader -> {
|
|
||||||
if (DEBUG) Log.d(TAG, "onImageAvailable: Capturing");
|
|
||||||
Image img = reader.acquireLatestImage();
|
|
||||||
if (img != null) {
|
|
||||||
try {
|
|
||||||
processImage(img);
|
|
||||||
} catch (Settings.SettingNotFoundException | InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
img.close();
|
|
||||||
if (DEBUG)
|
|
||||||
Log.d(TAG, "ImageReader.OnImageAvailableListener: Closing Camera and Sessions..");
|
|
||||||
cameraDevice.close();
|
|
||||||
session.close();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
@SuppressLint("MissingPermission")
|
|
||||||
public void readyCamera() {
|
|
||||||
manager = (CameraManager) getSystemService(CAMERA_SERVICE);
|
|
||||||
try {
|
|
||||||
String pickedCamera = getCamera(manager);
|
|
||||||
manager.registerAvailabilityCallback(availabilityCallback, null);
|
|
||||||
manager.openCamera(pickedCamera, cameraStateCallback, null);
|
|
||||||
imageReader = ImageReader.newInstance(250, 250, ImageFormat.JPEG, 2 /* images buffered */);
|
|
||||||
imageReader.setOnImageAvailableListener(onImageAvailableListener, null);
|
|
||||||
if(!mThreadRunning){
|
|
||||||
Thread mMyThread = new Thread(thread);
|
|
||||||
mMyThread.start();
|
|
||||||
mThreadRunning = true;
|
|
||||||
}
|
|
||||||
if (DEBUG) Log.d(TAG, "imageReader created");
|
|
||||||
} catch (CameraAccessException e) {
|
|
||||||
Log.e(TAG, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCamera(CameraManager manager) {
|
|
||||||
try {
|
|
||||||
for (String cameraId : manager.getCameraIdList()) {
|
|
||||||
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
|
|
||||||
int cOrientation = characteristics.get(CameraCharacteristics.LENS_FACING);
|
|
||||||
if (cOrientation == CAMERA_CHOICE) {
|
|
||||||
return cameraId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (CameraAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
|
||||||
mContext = getApplicationContext();
|
|
||||||
contentResolver = getContentResolver();
|
|
||||||
startForeground(50, PushNotification(), ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA);
|
|
||||||
BatteryOptimization(mContext);
|
|
||||||
mRegistered = false;
|
|
||||||
screenStateFilter = new IntentFilter(Intent.ACTION_SCREEN_ON);
|
|
||||||
screenStateFilter.addAction(Intent.ACTION_SCREEN_OFF);
|
|
||||||
try {
|
|
||||||
if(Settings.System.getInt(contentResolver, Settings.System.SCREEN_BRIGHTNESS_MODE)
|
|
||||||
== Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC){
|
|
||||||
registerReceiver(mScreenStateReceiver, screenStateFilter);
|
|
||||||
mRegistered = true;
|
|
||||||
}
|
|
||||||
} catch (Settings.SettingNotFoundException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
Uri setting = Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE);
|
|
||||||
contentResolver.registerContentObserver(setting, false, mSettingsObserver);
|
|
||||||
if(DEBUG) Log.d(TAG, "onStartCommand flags " + flags + " startId " + startId);
|
|
||||||
destroy = false;
|
|
||||||
avail = true;
|
|
||||||
startForeground(50, PushNotification());
|
|
||||||
return START_STICKY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate() {
|
|
||||||
if(DEBUG) Log.d(TAG, "onCreate service");
|
|
||||||
mContext = getApplicationContext();
|
|
||||||
startForeground(50, PushNotification());
|
|
||||||
super.onCreate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void actOnReadyCameraDevice() {
|
|
||||||
try {
|
|
||||||
cameraDevice.createCaptureSession(Collections.singletonList(imageReader.getSurface()), sessionStateCallback, null);
|
|
||||||
} catch (CameraAccessException e) {
|
|
||||||
Log.e(TAG, e.getMessage());
|
|
||||||
mLock = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private void processImage(Image image) throws Settings.SettingNotFoundException, InterruptedException {
|
|
||||||
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
|
|
||||||
byte[] bytes = new byte[buffer.capacity()];
|
|
||||||
buffer.get(bytes);
|
|
||||||
Bitmap bitmapImage = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, null);
|
|
||||||
int brightness = calculateBrightnessEstimate(bitmapImage,5);
|
|
||||||
AdjustBrightness(brightness);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected CaptureRequest createCaptureRequest() {
|
|
||||||
try {
|
|
||||||
CaptureRequest.Builder builder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
|
|
||||||
builder.addTarget(imageReader.getSurface());
|
|
||||||
return builder.build();
|
|
||||||
} catch (CameraAccessException e) {
|
|
||||||
Log.e(TAG, e.getMessage());
|
|
||||||
mLock = false;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CameraManager.AvailabilityCallback availabilityCallback = new CameraManager.AvailabilityCallback() {
|
|
||||||
@Override
|
|
||||||
public void onCameraOpened(String cameraId, String packageId) {
|
|
||||||
super.onCameraOpened(cameraId, packageId);
|
|
||||||
Log.i(TAG, "CameraManager.AvailabilityCallback: Camera " + cameraId
|
|
||||||
+ " Opened by Package " + packageId);
|
|
||||||
mLock = true;
|
|
||||||
if (packageId.equals(mContext.getBasePackageName())) return;
|
|
||||||
avail = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCameraUnavailable(String cameraId) {
|
|
||||||
if (DEBUG) Log.i(TAG, "CameraManager.AvailabilityCallback : Camera NOT Available. ");
|
|
||||||
avail = false;
|
|
||||||
super.onCameraUnavailable(cameraId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCameraAvailable(String cameraId) {
|
|
||||||
if (DEBUG) Log.i(TAG, "CameraManager.AvailabilityCallback : Camera IS Available. ");
|
|
||||||
avail = true;
|
|
||||||
mLock = false;
|
|
||||||
super.onCameraAvailable(cameraId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCameraClosed(String cameraId) {
|
|
||||||
super.onCameraClosed(cameraId);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
CameraCaptureSession.CaptureCallback captureCallback = new CameraCaptureSession.CaptureCallback() {
|
|
||||||
@Override
|
|
||||||
public void onCaptureSequenceCompleted(CameraCaptureSession session, int sequenceId, long frameNumber) {
|
|
||||||
if (DEBUG) Log.d(TAG, "captureCallback: Closing Session");
|
|
||||||
super.onCaptureSequenceCompleted(session, sequenceId, frameNumber);
|
|
||||||
cameraDevice.close();
|
|
||||||
session.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
public int calculateBrightnessEstimate(Bitmap bitmap, int pixelSpacing) {
|
|
||||||
int R = 0; int G = 0; int B = 0;
|
|
||||||
int height = bitmap.getHeight();
|
|
||||||
int width = bitmap.getWidth();
|
|
||||||
int n = 0;
|
|
||||||
int[] pixels = new int[width * height];
|
|
||||||
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
|
|
||||||
for (int i = 0; i < pixels.length; i += pixelSpacing) {
|
|
||||||
int color = pixels[i];
|
|
||||||
R += Color.red(color);
|
|
||||||
G += Color.green(color);
|
|
||||||
B += Color.blue(color);
|
|
||||||
n++;
|
|
||||||
}
|
|
||||||
return (R + B + G) / (n * 3);
|
|
||||||
}
|
|
||||||
private void AdjustBrightness(int brightness) throws Settings.SettingNotFoundException {
|
|
||||||
if (DEBUG) Log.i(TAG, "AdjustBrightness: Received Brightness Value " + brightness);
|
|
||||||
int oldbrightness = Settings.System.getInt(getContentResolver(), Settings.System.SCREEN_BRIGHTNESS);
|
|
||||||
if (DEBUG) Log.i(TAG, "AdjustBrightness: OldVal = " + oldbrightness + " NewVal = " +
|
|
||||||
brightness + " Adjusting..");
|
|
||||||
int newbrightness = brightness;
|
|
||||||
if (newbrightness > 255){
|
|
||||||
newbrightness = 255;
|
|
||||||
}else if (newbrightness < 0){
|
|
||||||
newbrightness = 0;
|
|
||||||
}
|
|
||||||
Settings.System.putInt(getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, newbrightness);
|
|
||||||
}
|
|
||||||
Thread thread = new Thread(() -> {
|
|
||||||
while (!Thread.currentThread().isInterrupted()) {
|
|
||||||
if (avail && !mLock) {
|
|
||||||
SystemClock.sleep(DELAY);
|
|
||||||
ContextCompat.getMainExecutor(mContext).execute(() -> {
|
|
||||||
if (avail) readyCamera();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,432 @@
|
||||||
|
package com.eurekateam.cameralightsensor
|
||||||
|
|
||||||
|
import android.hardware.camera2.CameraDevice
|
||||||
|
import android.hardware.camera2.CameraCaptureSession
|
||||||
|
import kotlin.jvm.Volatile
|
||||||
|
import android.hardware.camera2.CameraManager
|
||||||
|
import androidx.core.app.NotificationCompat
|
||||||
|
import android.hardware.camera2.CameraAccessException
|
||||||
|
import android.database.ContentObserver
|
||||||
|
import android.provider.Settings.SettingNotFoundException
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.*
|
||||||
|
import android.content.*
|
||||||
|
import android.content.pm.ServiceInfo
|
||||||
|
import android.hardware.camera2.CameraCharacteristics
|
||||||
|
import kotlin.Throws
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.ImageFormat
|
||||||
|
import android.hardware.camera2.CaptureRequest
|
||||||
|
import android.hardware.camera2.CameraManager.AvailabilityCallback
|
||||||
|
import android.media.Image
|
||||||
|
import android.media.ImageReader
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.*
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import android.provider.Settings
|
||||||
|
import android.util.Log
|
||||||
|
import java.lang.Exception
|
||||||
|
import java.lang.IllegalStateException
|
||||||
|
|
||||||
|
open class CameraLightSensorService : Service() {
|
||||||
|
var screenStateFilter: IntentFilter? = null
|
||||||
|
private var mContext: Context? = null
|
||||||
|
private var mRegistered = false
|
||||||
|
var destroy = false
|
||||||
|
private var cameraDevice: CameraDevice? = null
|
||||||
|
private var session: CameraCaptureSession? = null
|
||||||
|
|
||||||
|
@Volatile
|
||||||
|
var avail = true
|
||||||
|
private var imageReader: ImageReader? = null
|
||||||
|
private var manager: CameraManager? = null
|
||||||
|
private var mServiceStarted = false
|
||||||
|
|
||||||
|
@Volatile
|
||||||
|
private var mLock = false
|
||||||
|
private var mThreadRunning = false
|
||||||
|
private fun PushNotification(): Notification {
|
||||||
|
val nm = mContext!!.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
val channel = NotificationChannel(
|
||||||
|
mContext!!.basePackageName, "CameraLightSensor",
|
||||||
|
NotificationManager.IMPORTANCE_LOW
|
||||||
|
)
|
||||||
|
nm.createNotificationChannel(channel)
|
||||||
|
val builder = NotificationCompat.Builder(mContext!!, mContext!!.basePackageName)
|
||||||
|
val notificationIntent = Intent(mContext, CameraLightSensorService::class.java)
|
||||||
|
val contentIntent = PendingIntent.getActivity(
|
||||||
|
mContext, 50,
|
||||||
|
notificationIntent, PendingIntent.FLAG_IMMUTABLE
|
||||||
|
)
|
||||||
|
|
||||||
|
//set
|
||||||
|
builder.setContentIntent(contentIntent)
|
||||||
|
builder.setSmallIcon(R.drawable.ic_brightness)
|
||||||
|
builder.setContentTitle("Camera Light Sensor Service")
|
||||||
|
builder.setChannelId(mContext!!.basePackageName)
|
||||||
|
return builder.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
if (DEBUG) Log.d(TAG, "Destroying service")
|
||||||
|
if (mRegistered) contentResolver!!.unregisterContentObserver(mSettingsObserver)
|
||||||
|
mRegistered = false
|
||||||
|
cameraDevice!!.close()
|
||||||
|
if (session != null && avail) {
|
||||||
|
try {
|
||||||
|
session!!.abortCaptures()
|
||||||
|
session!!.close()
|
||||||
|
} catch (e: CameraAccessException) {
|
||||||
|
Log.e(TAG, e.message)
|
||||||
|
} catch (e2: IllegalStateException) {
|
||||||
|
Log.e(TAG, "Session Already Closed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
manager!!.unregisterAvailabilityCallback(availabilityCallback)
|
||||||
|
avail = false
|
||||||
|
destroy = true
|
||||||
|
stopForeground(true)
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBind(intent: Intent): IBinder? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private val mScreenStateReceiver: BroadcastReceiver = object : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
if (intent.action == Intent.ACTION_SCREEN_ON) {
|
||||||
|
onDisplayOn()
|
||||||
|
mServiceStarted = true
|
||||||
|
} else if (intent.action == Intent.ACTION_SCREEN_OFF) {
|
||||||
|
if (mServiceStarted) onDisplayOff()
|
||||||
|
mServiceStarted = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make a listener for settings
|
||||||
|
private var mSettingsObserver: ContentObserver =
|
||||||
|
object : ContentObserver(Handler(Looper.getMainLooper())) {
|
||||||
|
override fun onChange(selfChange: Boolean) {
|
||||||
|
super.onChange(selfChange)
|
||||||
|
Log.i(TAG, "observer: Brightness Settings Changed")
|
||||||
|
try {
|
||||||
|
if (Settings.System.getInt(
|
||||||
|
contentResolver,
|
||||||
|
Settings.System.SCREEN_BRIGHTNESS_MODE
|
||||||
|
)
|
||||||
|
== Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC
|
||||||
|
) {
|
||||||
|
registerReceiver(mScreenStateReceiver, screenStateFilter)
|
||||||
|
mRegistered = true
|
||||||
|
onDisplayOn()
|
||||||
|
} else {
|
||||||
|
if (mRegistered) unregisterReceiver(mScreenStateReceiver)
|
||||||
|
mRegistered = false
|
||||||
|
onDisplayOff()
|
||||||
|
}
|
||||||
|
} catch (e: SettingNotFoundException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deliverSelfNotifications(): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onDisplayOn() {
|
||||||
|
if (DEBUG) Log.d(TAG, "Screen is on. Starting Service...")
|
||||||
|
avail = true
|
||||||
|
readyCamera()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onDisplayOff() {
|
||||||
|
if (DEBUG) Log.d(TAG, "Screen is off. Stopping Service...")
|
||||||
|
thread.interrupt()
|
||||||
|
avail = false
|
||||||
|
mThreadRunning = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private var cameraStateCallback: CameraDevice.StateCallback =
|
||||||
|
object : CameraDevice.StateCallback() {
|
||||||
|
override fun onOpened(camera: CameraDevice) {
|
||||||
|
Log.d(TAG, "CameraDevice.StateCallback onOpened")
|
||||||
|
cameraDevice = camera
|
||||||
|
actOnReadyCameraDevice()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDisconnected(camera: CameraDevice) {
|
||||||
|
Log.w(TAG, "CameraDevice.StateCallback onDisconnected")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(camera: CameraDevice, error: Int) {
|
||||||
|
Log.e(TAG, "CameraDevice.StateCallback onError $error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private var sessionStateCallback: CameraCaptureSession.StateCallback =
|
||||||
|
object : CameraCaptureSession.StateCallback() {
|
||||||
|
override fun onReady(session: CameraCaptureSession) {
|
||||||
|
if (!destroy) {
|
||||||
|
this@CameraLightSensorService.session = session
|
||||||
|
try {
|
||||||
|
if (createCaptureRequest() == null) return
|
||||||
|
try {
|
||||||
|
session.capture(createCaptureRequest(), captureCallback, null)
|
||||||
|
} catch (e: CameraAccessException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
} catch (e: IllegalStateException) {
|
||||||
|
Log.w(TAG, "onReady: Session is NULL")
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Camera is in use")
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onConfigured(session: CameraCaptureSession) {}
|
||||||
|
override fun onConfigureFailed(session: CameraCaptureSession) {}
|
||||||
|
}
|
||||||
|
private var onImageAvailableListener =
|
||||||
|
ImageReader.OnImageAvailableListener { reader: ImageReader ->
|
||||||
|
if (DEBUG) Log.d(TAG, "onImageAvailable: Capturing")
|
||||||
|
val img = reader.acquireLatestImage()
|
||||||
|
if (img != null) {
|
||||||
|
try {
|
||||||
|
processImage(img)
|
||||||
|
} catch (e: SettingNotFoundException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
} catch (e: InterruptedException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
img.close()
|
||||||
|
if (DEBUG) Log.d(
|
||||||
|
TAG,
|
||||||
|
"ImageReader.OnImageAvailableListener: Closing Camera and Sessions.."
|
||||||
|
)
|
||||||
|
cameraDevice!!.close()
|
||||||
|
session!!.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("MissingPermission")
|
||||||
|
fun readyCamera() {
|
||||||
|
manager = getSystemService(CAMERA_SERVICE) as CameraManager
|
||||||
|
try {
|
||||||
|
val pickedCamera = getCamera(manager)
|
||||||
|
manager!!.registerAvailabilityCallback(availabilityCallback, null)
|
||||||
|
manager!!.openCamera(pickedCamera, cameraStateCallback, null)
|
||||||
|
imageReader =
|
||||||
|
ImageReader.newInstance(250, 250, ImageFormat.JPEG, 2 /* images buffered */)
|
||||||
|
imageReader?.setOnImageAvailableListener(onImageAvailableListener, null)
|
||||||
|
if (!mThreadRunning) {
|
||||||
|
val mMyThread = Thread(thread)
|
||||||
|
mMyThread.start()
|
||||||
|
mThreadRunning = true
|
||||||
|
}
|
||||||
|
if (DEBUG) Log.d(TAG, "imageReader created")
|
||||||
|
} catch (e: CameraAccessException) {
|
||||||
|
Log.e(TAG, e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getCamera(manager: CameraManager?): String? {
|
||||||
|
try {
|
||||||
|
for (cameraId in manager!!.cameraIdList) {
|
||||||
|
val characteristics = manager.getCameraCharacteristics(cameraId)
|
||||||
|
val cOrientation = characteristics.get(CameraCharacteristics.LENS_FACING)
|
||||||
|
if (cOrientation == CAMERA_CHOICE) {
|
||||||
|
return cameraId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: CameraAccessException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
||||||
|
mContext = applicationContext
|
||||||
|
startForeground(50, PushNotification(), ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA)
|
||||||
|
BatteryOptimization(mContext)
|
||||||
|
mRegistered = false
|
||||||
|
screenStateFilter = IntentFilter(Intent.ACTION_SCREEN_ON)
|
||||||
|
screenStateFilter!!.addAction(Intent.ACTION_SCREEN_OFF)
|
||||||
|
try {
|
||||||
|
if (Settings.System.getInt(contentResolver, Settings.System.SCREEN_BRIGHTNESS_MODE)
|
||||||
|
== Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC
|
||||||
|
) {
|
||||||
|
registerReceiver(mScreenStateReceiver, screenStateFilter)
|
||||||
|
mRegistered = true
|
||||||
|
}
|
||||||
|
} catch (e: SettingNotFoundException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
val setting = Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE)
|
||||||
|
contentResolver?.registerContentObserver(setting, false, mSettingsObserver)
|
||||||
|
if (DEBUG) Log.d(TAG, "onStartCommand flags $flags startId $startId")
|
||||||
|
destroy = false
|
||||||
|
avail = true
|
||||||
|
startForeground(50, PushNotification())
|
||||||
|
return START_STICKY
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate() {
|
||||||
|
if (DEBUG) Log.d(TAG, "onCreate service")
|
||||||
|
mContext = applicationContext
|
||||||
|
startForeground(50, PushNotification())
|
||||||
|
super.onCreate()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun actOnReadyCameraDevice() {
|
||||||
|
try {
|
||||||
|
cameraDevice!!.createCaptureSession(
|
||||||
|
listOf(imageReader!!.surface),
|
||||||
|
sessionStateCallback,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
} catch (e: CameraAccessException) {
|
||||||
|
Log.e(TAG, e.message)
|
||||||
|
mLock = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(SettingNotFoundException::class, InterruptedException::class)
|
||||||
|
private fun processImage(image: Image) {
|
||||||
|
val buffer = image.planes[0].buffer
|
||||||
|
val bytes = ByteArray(buffer.capacity())
|
||||||
|
buffer[bytes]
|
||||||
|
val bitmapImage = BitmapFactory.decodeByteArray(bytes, 0, bytes.size, null)
|
||||||
|
val brightness = calculateBrightnessEstimate(bitmapImage, 5)
|
||||||
|
AdjustBrightness(brightness)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun createCaptureRequest(): CaptureRequest? {
|
||||||
|
return try {
|
||||||
|
val builder = cameraDevice!!.createCaptureRequest(CameraDevice.TEMPLATE_RECORD)
|
||||||
|
builder.addTarget(imageReader!!.surface)
|
||||||
|
builder.build()
|
||||||
|
} catch (e: CameraAccessException) {
|
||||||
|
Log.e(TAG, e.message)
|
||||||
|
mLock = false
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var availabilityCallback: AvailabilityCallback = object : AvailabilityCallback() {
|
||||||
|
override fun onCameraOpened(cameraId: String, packageId: String) {
|
||||||
|
super.onCameraOpened(cameraId, packageId)
|
||||||
|
Log.i(
|
||||||
|
TAG, "CameraManager.AvailabilityCallback: Camera " + cameraId
|
||||||
|
+ " Opened by Package " + packageId
|
||||||
|
)
|
||||||
|
mLock = true
|
||||||
|
if (packageId == mContext!!.basePackageName) return
|
||||||
|
avail = false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCameraUnavailable(cameraId: String) {
|
||||||
|
if (DEBUG) Log.i(TAG, "CameraManager.AvailabilityCallback : Camera NOT Available. ")
|
||||||
|
avail = false
|
||||||
|
super.onCameraUnavailable(cameraId)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCameraAvailable(cameraId: String) {
|
||||||
|
if (DEBUG) Log.i(TAG, "CameraManager.AvailabilityCallback : Camera IS Available. ")
|
||||||
|
avail = true
|
||||||
|
mLock = false
|
||||||
|
super.onCameraAvailable(cameraId)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCameraClosed(cameraId: String) {
|
||||||
|
super.onCameraClosed(cameraId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var captureCallback: CameraCaptureSession.CaptureCallback =
|
||||||
|
object : CameraCaptureSession.CaptureCallback() {
|
||||||
|
override fun onCaptureSequenceCompleted(
|
||||||
|
session: CameraCaptureSession,
|
||||||
|
sequenceId: Int,
|
||||||
|
frameNumber: Long
|
||||||
|
) {
|
||||||
|
if (DEBUG) Log.d(TAG, "captureCallback: Closing Session")
|
||||||
|
super.onCaptureSequenceCompleted(session, sequenceId, frameNumber)
|
||||||
|
cameraDevice!!.close()
|
||||||
|
session.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun calculateBrightnessEstimate(bitmap: Bitmap, pixelSpacing: Int): Int {
|
||||||
|
var R = 0
|
||||||
|
var G = 0
|
||||||
|
var B = 0
|
||||||
|
val height = bitmap.height
|
||||||
|
val width = bitmap.width
|
||||||
|
var n = 0
|
||||||
|
val pixels = IntArray(width * height)
|
||||||
|
bitmap.getPixels(pixels, 0, width, 0, 0, width, height)
|
||||||
|
var i = 0
|
||||||
|
while (i < pixels.size) {
|
||||||
|
val color = pixels[i]
|
||||||
|
R += Color.red(color)
|
||||||
|
G += Color.green(color)
|
||||||
|
B += Color.blue(color)
|
||||||
|
n++
|
||||||
|
i += pixelSpacing
|
||||||
|
}
|
||||||
|
return (R + B + G) / (n * 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(SettingNotFoundException::class)
|
||||||
|
private fun AdjustBrightness(brightness: Int) {
|
||||||
|
if (DEBUG) Log.i(TAG, "AdjustBrightness: Received Brightness Value $brightness")
|
||||||
|
val oldbrightness =
|
||||||
|
Settings.System.getInt(contentResolver, Settings.System.SCREEN_BRIGHTNESS)
|
||||||
|
if (DEBUG) Log.i(
|
||||||
|
TAG, "AdjustBrightness: OldVal = " + oldbrightness + " NewVal = " +
|
||||||
|
brightness + " Adjusting.."
|
||||||
|
)
|
||||||
|
var newbrightness = brightness
|
||||||
|
if (newbrightness > 255) {
|
||||||
|
newbrightness = 255
|
||||||
|
} else if (newbrightness < 0) {
|
||||||
|
newbrightness = 0
|
||||||
|
}
|
||||||
|
Settings.System.putInt(
|
||||||
|
contentResolver,
|
||||||
|
Settings.System.SCREEN_BRIGHTNESS,
|
||||||
|
newbrightness
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var thread = Thread {
|
||||||
|
while (!Thread.currentThread().isInterrupted) {
|
||||||
|
if (avail && !mLock) {
|
||||||
|
SystemClock.sleep(DELAY.toLong())
|
||||||
|
ContextCompat.getMainExecutor(mContext!!).execute { if (avail) readyCamera() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
protected val TAG: String = CameraLightSensorService::class.java.simpleName
|
||||||
|
const val DEBUG = false
|
||||||
|
protected const val CAMERA_CHOICE = CameraCharacteristics.LENS_FACING_FRONT
|
||||||
|
private const val DELAY = 5 * 1000 // 5 Seconds
|
||||||
|
fun BatteryOptimization(context: Context?) {
|
||||||
|
val intent = Intent()
|
||||||
|
val packageName = context!!.packageName
|
||||||
|
val pm = context.getSystemService(POWER_SERVICE) as PowerManager
|
||||||
|
if (!pm.isIgnoringBatteryOptimizations(packageName)) {
|
||||||
|
intent.action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
|
||||||
|
intent.data = Uri.parse("package:$packageName")
|
||||||
|
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
|
context.startActivity(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue