You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
TermApp/app/src/main/java/com/xinyingpower/microphoto/MicroPhotoService.java

579 lines
21 KiB
Java

package com.xinyingpower.microphoto;
import android.app.AlarmManager;
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.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Environment;
import android.os.IBinder;
import android.os.SystemClock;
import androidx.core.app.NotificationCompat;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.util.Log;
import android.widget.RemoteViews;
import android.widget.Toast;
import com.dowse.camera.client.DSCameraManager;
import java.io.File;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MicroPhotoService extends Service {
public static final String TAG = "MPService";
// Used to load the 'microphoto' library on application startup.
static {
System.loadLibrary("microphoto");
}
private static String ALARM_EVENT = "com.xinyingpower.mp.MicroPhotoService.AlarmReceiver";
public static final int NOTIFICATION_ID_FOREGROUND_SERVICE = 8466503;
public static final String ACTION_START = "ACT_START";
public static final String ACTION_STOP = "ACT_STOP";
public static final String ACTION_MAIN = "ACT_MAIN";
private static String ACTION_HEARTBEAT = "ACT_HB";
private static String ACTION_TAKE_PHOTO = "ACT_TP";
private static String ACTION_TAKE_PHOTO_MANUALLY = "ACT_TP_M";
private static String ACTION_TIMEOUT = "ACT_TIMEOUT";
private static String EXTRA_PARAM_CHANNEL = "Channel";
private static String EXTRA_PARAM_PRESET = "Preset";
private static String EXTRA_PARAM_PHOTO_OR_VIDEO = "PhotoOrVideo";
private static String EXTRA_PARAM_SCHEDULES = "Schedules";
private static String EXTRA_PARAM_SCHEDULE = "Schedule_";
private static String EXTRA_PARAM_TIME = "Time";
// private static String EXTRA_PARAM_FILENAME = "FileName";
private static String EXTRA_PARAM_TIMER_UID = "TimerUid";
// private static String EXTRA_PARAM_TIMER_TYPE = "TimerType";
private static String EXTRA_PARAM_TIMEOUT = "Timeout";
private final static String FOREGROUND_CHANNEL_ID = "foreground_channel_id";
public static class STATE_SERVICE {
public static final int CONNECTED = 10;
public static final int NOT_CONNECTED = 0;
}
private AlarmManager mAlarmManager;
private NotificationManager mNotificationManager;
private int mHeartbeatDuration = 0; // 5m: 5 * 60 * 1000
private long mNextHeartbeatTime = 0;
private Map<Long, PendingIntent> mTimers = new HashMap<>();
private static int stateService = STATE_SERVICE.NOT_CONNECTED;
public MicroPhotoService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onCreate() {
super.onCreate();
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
stateService = STATE_SERVICE.NOT_CONNECTED;
alarmReceiver = new AlarmReceiver(this);
// 注册广播接受者
IntentFilter intentFilter = new IntentFilter(ACTION_HEARTBEAT);
intentFilter.addAction(ACTION_TAKE_PHOTO);
intentFilter.addAction(ACTION_TIMEOUT);
intentFilter.addAction(ACTION_TAKE_PHOTO_MANUALLY);
registerReceiver( alarmReceiver, intentFilter);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
while (true) {
AlarmManager.AlarmClockInfo aci = alarmManager.getNextAlarmClock();
if (aci == null) {
break;
}
if (aci.getShowIntent().isBroadcast()) {
// alarmManager.cancel(aci.getShowIntent());
}
}
// alarmManager.cancel();
boolean res = false;
/*
res = DSCameraManager.getInstace().init();
ChannelPicParam picParam = new ChannelPicParam();
picParam.setColor(1);
picParam.setWidth(1920);
picParam.setHeight(1080);
picParam.setCompress_radio(40);
DSCameraManager.getInstace().setPicParam(1, picParam);
*/
// Environment.getExternalStoragePublicDirectory(String)
File path = new File(Environment.getExternalStorageDirectory(), "com.xyp.mp/");
if (!path.exists()) {
path.mkdirs();
}
// File path = getApplicationContext().getFilesDir();
String appPath = path.getAbsolutePath();
Log.i(TAG, "AppPath=" + appPath);
String ip = "180.166.218.222";
int port = 40032;
String cmdid = "XYDEV100230100012";
init(appPath, ip, port, cmdid);
// registerHeartbeatTimer(getHeartbeatDuration());
Date date = new Date();
long nowTs = date.getTime() / 1000;
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
long startTime = date.getTime() / 1000;
long baseTime = nowTs - startTime;
registerCaptureSchedule(startTime, baseTime);
// registerPhotoTimer();
}
public static class AlarmReceiver extends BroadcastReceiver {
private MicroPhotoService mService;
public AlarmReceiver(MicroPhotoService service) {
mService = service;
}
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(TextUtils.equals(ACTION_HEARTBEAT, action)){
Log.i(TAG, "receiver ACTION=" + action);
mService.sendHeartbeat();
mService.registerHeartbeatTimer();
}
else if(TextUtils.equals(ACTION_TAKE_PHOTO, action)) {
long ts = intent.getLongExtra(EXTRA_PARAM_TIME, 0);
int cnt = intent.getIntExtra(EXTRA_PARAM_SCHEDULES, 0);
if (cnt > 0) {
for (int idx = 0; idx < cnt; idx++) {
long val = intent.getLongExtra(EXTRA_PARAM_SCHEDULE + idx, 0);
int channel = (int)((val & 0xFF0000L) >> 16);
int preset = (int)((val & 0xFF00L) >> 8);
mService.notifyToTakePhoto(channel, preset, ts, mService.buildPhotoDir(channel), mService.buildPhotoFileName(channel, preset, ts), true);
}
}
// Register Next Photo Timer
Date date = new Date();
long nowTs = date.getTime() / 1000;
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
long startTime = date.getTime() / 1000;
long baseTime = nowTs - startTime;
mService.registerCaptureSchedule(startTime, baseTime);
}
else if(TextUtils.equals(ACTION_TAKE_PHOTO_MANUALLY, action)) {
int channel = intent.getIntExtra(EXTRA_PARAM_CHANNEL, 0);
int preset = intent.getIntExtra(EXTRA_PARAM_PRESET, 0);
// long ts = intent.getLongExtra(EXTRA_PARAM_TIME, 0);
boolean photoOrVideo = intent.getBooleanExtra(EXTRA_PARAM_PHOTO_OR_VIDEO, true);
long ts = System.currentTimeMillis() / 1000;
mService.notifyToTakePhoto(channel, preset, ts, mService.buildPhotoDir(channel), mService.buildPhotoFileName(channel, preset, ts), photoOrVideo);
}
else if(TextUtils.equals(ACTION_TIMEOUT, action)) {
long uid = intent.getLongExtra(EXTRA_PARAM_TIMER_UID, 0);
Log.i(TAG, "Timeout:" + uid);
mService.fireTimeout(uid);
int timeout = intent.getIntExtra(EXTRA_PARAM_TIMEOUT, 0);
Long uidObj = Long.valueOf(uid);
PendingIntent pendingIntent = mService.mTimers.get(uidObj);
if (pendingIntent != null) {
mService.registerTimer(pendingIntent, uid, timeout);
}
}
}
}
private void registerHeartbeatTimer(int duration) {
int orgHeartbeatDuration = mHeartbeatDuration;
mHeartbeatDuration = duration;
if (orgHeartbeatDuration == 0) {
registerHeartbeatTimer();
}
}
private void registerHeartbeatTimer() {
// 创建延迟意图
Intent alarmIntent = new Intent();
alarmIntent.setAction(ACTION_HEARTBEAT);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, alarmIntent, 0);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + mHeartbeatDuration, pendingIntent);
mNextHeartbeatTime = System.currentTimeMillis() + mHeartbeatDuration;
// alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + timeout, pendingIntent);
}
private void registerPhotoTimer(int channel, int preset, long ts, long timeout, List<Long> schedules) {
// 创建延迟意图
Intent alarmIntent = new Intent();
alarmIntent.setAction(ACTION_TAKE_PHOTO);
int cnt = schedules.size();
alarmIntent.putExtra(EXTRA_PARAM_SCHEDULES, cnt);
String channelStr = "";
for (int idx = 0; idx < cnt; idx++) {
alarmIntent.putExtra(EXTRA_PARAM_SCHEDULE + idx, schedules.get(idx).longValue());
channelStr += schedules.get(idx).toString() + " ";
}
alarmIntent.putExtra(EXTRA_PARAM_TIME, ts);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
long currentTimeMillis = System.currentTimeMillis();
Date date = new Date(currentTimeMillis + timeout);
String dateStr = (String) DateFormat.format("MM-dd kk:mm:ss", date);
Log.d(TAG, "Register Photo Timer: " + dateStr + " currentTimeMillis=" + currentTimeMillis + " timeout=" + timeout + " Channels=" + channelStr);
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + timeout, pendingIntent);
}
// private HashMap<Long, Integer> mTimers = new HashMap<Long, Integer>();
public boolean registerTimer(long uid, int timeout) {
// 创建延迟意图
Intent alarmIntent = new Intent();
alarmIntent.setAction(ACTION_TIMEOUT);
alarmIntent.putExtra(EXTRA_PARAM_TIMER_UID, uid);
alarmIntent.putExtra(EXTRA_PARAM_TIMEOUT, timeout);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mTimers.put(Long.valueOf(uid), pendingIntent);
return registerTimer(pendingIntent, uid, timeout);
}
public boolean registerTimer(PendingIntent pendingIntent, long uid, int timeout) {
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + timeout, pendingIntent);
Log.i(TAG, "RegTimer:" + uid + " timeout=" + timeout);
return true;
}
public boolean unregisterTimer(long uid) {
Long uidObj = Long.valueOf(uid);
PendingIntent pendingIntent = mTimers.get(uidObj);
if (pendingIntent != null) {
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.cancel(pendingIntent);
mTimers.remove(uidObj);
Log.i(TAG, "UnregTimer:" + uid);
}
return true;
}
private boolean registerCaptureSchedule(long startTime, long baseTime) {
long[] photoTimeData = getPhotoTimeData();
if (photoTimeData == null) {
return false;
}
int cnt = photoTimeData.length;
short channel = 0;
short preset = 0;
int ts = 0;
long val = 0L;
// int maxDuration = mHeartbeatDuration * 2 / 1000;
int maxDuration = 35 * 60 * 1000 + 1000;
int currentTs = 0;
List<Long> schedules = new ArrayList<>();
for (int idx = 0; idx < cnt; idx++) {
val = photoTimeData[idx];
ts = (int)((val & 0x00FFFFFF00000000L) >> 32);
if (ts < baseTime) {
continue;
}
if ((ts - baseTime) > maxDuration) {
break;
}
if (currentTs == 0) {
currentTs = ts;
channel = (short)((val & 0xFF0000L) >> 16);
preset = (short)((val & 0xFF00L) >> 8);
schedules.add(Long.valueOf(val));
} else if (ts > currentTs) {
break;
} else {
schedules.add(Long.valueOf(val));
}
}
if (!schedules.isEmpty()) {
long expectedTs = startTime + ts;
Date date = new Date(expectedTs * 1000);
// Log.d(TAG, "Register Photo Time: " + date.toString() + " BaseTime=" + baseTime + " ts=" + ts + " startTime=" + startTime + " expectedTs=" + expectedTs);
registerPhotoTimer(channel, preset, currentTs, (currentTs - baseTime) * 1000, schedules);
}
return true;
}
public static void takePhoto(Context context, int channel, int preset, boolean photoOrVideo) {
Intent intent = new Intent(ACTION_TAKE_PHOTO_MANUALLY);
intent.putExtra(EXTRA_PARAM_CHANNEL, channel);
intent.putExtra(EXTRA_PARAM_PRESET, preset);
intent.putExtra(EXTRA_PARAM_PHOTO_OR_VIDEO, photoOrVideo);
context.sendBroadcast(intent);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent == null) {
stopForeground(true);
stopSelf();
return START_NOT_STICKY;
}
// if user starts the service
switch (intent.getAction()) {
case ACTION_START:
Log.d(TAG, "Received user starts foreground intent");
startForeground(NOTIFICATION_ID_FOREGROUND_SERVICE, prepareNotification());
// Start the locker receiver
final ScreenActionReceiver screenactionreceiver = new ScreenActionReceiver();
registerReceiver(screenactionreceiver, screenactionreceiver.getFilter());
connect();
break;
case ACTION_STOP:
stopForeground(true);
stopSelf();
break;
default:
stopForeground(true);
stopSelf();
}
return START_NOT_STICKY;
}
private void connect() {
// after 10 seconds its connected
new android.os.Handler().postDelayed(
new Runnable() {
public void run() {
Log.d(TAG, "Bluetooth Low Energy device is connected!!");
Toast.makeText(getApplicationContext(),"Connected!",Toast.LENGTH_SHORT).show();
stateService = STATE_SERVICE.CONNECTED;
startForeground(NOTIFICATION_ID_FOREGROUND_SERVICE, prepareNotification());
}
}, 10000);
}
private Notification prepareNotification() {
// handle build version above android oreo
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O &&
mNotificationManager.getNotificationChannel(FOREGROUND_CHANNEL_ID) == null) {
CharSequence name = getString(R.string.text_name_notification);
int importance = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel channel = new NotificationChannel(FOREGROUND_CHANNEL_ID, name, importance);
channel.enableVibration(false);
mNotificationManager.createNotificationChannel(channel);
}
Intent notificationIntent = new Intent(this, MainActivity.class);
notificationIntent.setAction(ACTION_MAIN);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
// if min sdk goes below honeycomb
/*if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
} else {
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}*/
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
// make a stop intent
Intent stopIntent = new Intent(this, MicroPhotoService.class);
stopIntent.setAction(ACTION_STOP);
PendingIntent pendingStopIntent = PendingIntent.getService(this, 0, stopIntent, PendingIntent.FLAG_UPDATE_CURRENT);
RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.notification);
remoteViews.setOnClickPendingIntent(R.id.btn_stop, pendingStopIntent);
// if it is connected
switch(stateService) {
case STATE_SERVICE.NOT_CONNECTED:
remoteViews.setTextViewText(R.id.tv_state, "DISCONNECTED");
break;
case STATE_SERVICE.CONNECTED:
remoteViews.setTextViewText(R.id.tv_state, "CONNECTED");
break;
}
// notification builder
NotificationCompat.Builder notificationBuilder;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
notificationBuilder = new NotificationCompat.Builder(this, FOREGROUND_CHANNEL_ID);
} else {
notificationBuilder = new NotificationCompat.Builder(this);
}
notificationBuilder
.setContent(remoteViews)
.setSmallIcon(R.mipmap.ic_launcher)
.setCategory(NotificationCompat.CATEGORY_SERVICE)
.setOnlyAlertOnce(true)
.setOngoing(true)
.setAutoCancel(true)
.setContentIntent(pendingIntent);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
notificationBuilder.setVisibility(Notification.VISIBILITY_PUBLIC);
}
return notificationBuilder.build();
}
/*
public boolean takePhoto(short channel, short preset, String path) {
boolean res = DSCameraManager.getInstace().takePhoto(path, channel, 0, 0);
if (!res) {
int idx = 0;
do {
res = DSCameraManager.getInstace().isCameraServiceOK();
if (!res)
{
try {
Thread.sleep(1000L);
} catch (InterruptedException ex) {
// ex.printStackTrace();
}
}
} while(idx < 6);
if (DSCameraManager.getInstace().isCameraServiceOK()) {
res = DSCameraManager.getInstace().takePhoto(path, channel, 0, 0);
}
}
return res;
}
*/
@Override
public void onDestroy() {
stateService = STATE_SERVICE.NOT_CONNECTED;
uninit();
DSCameraManager.getInstace().unInit();
super.onDestroy();
}
protected boolean updateTime(long timeInMillis) {
boolean res = false;
try {
// Calendar c = Calendar.getInstance();
// c.set(2010, 1, 1, 12, 00, 00);
AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
am.setTime(timeInMillis);
res = true;
} catch (Exception ex) {
int aa = 0;
}
return true;
}
public String buildPhotoDir(int channel) {
File path = new File(Environment.getExternalStorageDirectory(), "com.xyp.mp/photos/");
if (!path.exists() && !path.mkdirs()) {
return null;
}
String p = path.getAbsolutePath();
if (!p.endsWith(File.separator)) {
p += File.separator;
}
return p;
}
private String buildPhotoFileName(int channel, int preset, long ts) {
String date = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
String photoFile = "img_" + Integer.toString(channel) + "_" + Integer.toHexString(preset).toUpperCase() + "_" + date + ".jpg";
return photoFile;
}
protected native boolean init(String appPath, String ip, int port, String cmdid);
protected native long getHeartbeatDuration();
protected native long[] getPhotoTimeData();
protected native boolean notifyToTakePhoto(int channel, int preset, long scheduleTime, String path, String fileName, boolean sendToCma);
protected native boolean sendHeartbeat();
protected native boolean fireTimeout(long uid);
protected native boolean uninit();
protected long mHandler = 0;
private AlarmReceiver alarmReceiver = null;
}