2023-11-09 08:37:59 +08:00
|
|
|
package com.xixun.xixunplayer;
|
|
|
|
|
|
|
|
import static android.view.View.GONE;
|
|
|
|
import static android.view.View.VISIBLE;
|
|
|
|
|
|
|
|
import android.content.BroadcastReceiver;
|
|
|
|
import android.content.ComponentName;
|
|
|
|
import android.content.Context;
|
|
|
|
import android.content.Intent;
|
|
|
|
import android.content.IntentFilter;
|
|
|
|
import android.content.ServiceConnection;
|
|
|
|
import android.content.pm.PackageManager;
|
|
|
|
import android.graphics.BitmapFactory;
|
|
|
|
import android.os.Build;
|
|
|
|
import android.os.Bundle;
|
|
|
|
import android.os.Environment;
|
|
|
|
import android.os.IBinder;
|
|
|
|
import android.os.RemoteException;
|
|
|
|
import android.os.StatFs;
|
|
|
|
import android.view.Choreographer;
|
|
|
|
|
|
|
|
import androidx.activity.ComponentActivity;
|
2023-11-10 09:47:38 +08:00
|
|
|
import androidx.annotation.NonNull;
|
2023-11-09 08:37:59 +08:00
|
|
|
import androidx.annotation.RequiresApi;
|
|
|
|
import androidx.core.app.ActivityCompat;
|
|
|
|
import androidx.core.content.ContextCompat;
|
|
|
|
|
|
|
|
import com.xixun.joey.aidlset.CardService;
|
|
|
|
|
|
|
|
import java.io.BufferedInputStream;
|
|
|
|
import java.io.File;
|
|
|
|
import java.io.FileInputStream;
|
|
|
|
import java.io.FileNotFoundException;
|
|
|
|
import java.io.FileOutputStream;
|
|
|
|
import java.io.InputStream;
|
|
|
|
import java.io.OutputStream;
|
|
|
|
import java.net.ServerSocket;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Arrays;
|
|
|
|
import java.util.HashSet;
|
|
|
|
import java.util.concurrent.CountDownLatch;
|
|
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
|
|
|
|
|
import gnph.util.IOs;
|
|
|
|
import gnph.util.JSList;
|
|
|
|
import gnph.util.JSMap;
|
|
|
|
import gnph.util.O;
|
|
|
|
|
|
|
|
public class MainActivity extends ComponentActivity implements Choreographer.FrameCallback {
|
|
|
|
|
|
|
|
public static MainActivity ins;
|
|
|
|
public Intent environIntent = new Intent();
|
|
|
|
HashSet<EleEnviron> environs = new HashSet<>();
|
|
|
|
BackView backView;
|
|
|
|
ProgView progView;
|
|
|
|
int state;
|
|
|
|
|
|
|
|
@RequiresApi(api = Build.VERSION_CODES.R)
|
|
|
|
@Override
|
|
|
|
public void onCreate(Bundle savedInstanceState) {
|
|
|
|
super.onCreate(savedInstanceState);
|
2023-11-10 21:45:00 +08:00
|
|
|
startService(new Intent(this, RestartService.class));
|
|
|
|
|
2023-11-09 08:37:59 +08:00
|
|
|
System.out.println("---- MainActivity onCreate ---- UI Thread: "+Thread.currentThread().getId());
|
|
|
|
ins = this;
|
|
|
|
if(ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) init();
|
|
|
|
else {
|
|
|
|
System.out.println("---- No permission, Try again ...");
|
|
|
|
ActivityCompat.requestPermissions(this, new String[]{
|
|
|
|
android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
|
|
|
android.Manifest.permission.READ_EXTERNAL_STORAGE,
|
|
|
|
android.Manifest.permission.MANAGE_EXTERNAL_STORAGE,
|
|
|
|
android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
|
|
|
|
android.Manifest.permission.INTERNET
|
|
|
|
}, 999);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@Override
|
2023-11-10 09:47:38 +08:00
|
|
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
2023-11-09 08:37:59 +08:00
|
|
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
2023-11-10 09:47:38 +08:00
|
|
|
if(requestCode==999 && grantResults.length > 0 && grantResults[0]==PackageManager.PERMISSION_GRANTED && backView==null) init();
|
2023-11-09 08:37:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
public void init() {
|
|
|
|
Util.programDir = Environment.getExternalStorageDirectory() + "/XixunPlayer/program";
|
|
|
|
Util.backImgFile = Environment.getExternalStorageDirectory() + "/XixunPlayer/background";
|
|
|
|
|
|
|
|
var program = new File(Util.programDir);
|
|
|
|
if(program.isFile()) program.delete();
|
|
|
|
System.out.println("mkdir: "+program.mkdirs());
|
|
|
|
|
|
|
|
var aaafiles = new File(Environment.getExternalStorageDirectory()+"/XixunPlayer").listFiles();
|
|
|
|
if(aaafiles != null) for(var file : aaafiles) if(file.isFile() && ! file.getName().startsWith("background")) file.delete();
|
|
|
|
|
|
|
|
var conn = new ServiceConnection() {
|
|
|
|
public void onServiceDisconnected(ComponentName name) {
|
|
|
|
System.out.println("Disconnected cardsystem aidl service");
|
|
|
|
}
|
|
|
|
|
|
|
|
public void onServiceConnected(ComponentName name, IBinder iBinder) {
|
|
|
|
System.out.println("Bind cardsystem aidl service success");
|
|
|
|
var service = CardService.Stub.asInterface(iBinder);
|
|
|
|
try {
|
2023-11-10 09:47:38 +08:00
|
|
|
Util.isScreenOn = service.isScreenOpen();
|
2023-11-09 08:37:59 +08:00
|
|
|
backView = new BackView(MainActivity.this, service.getScreenWidth(), service.getScreenHeight());
|
|
|
|
state = 5;
|
2023-11-10 09:47:38 +08:00
|
|
|
if(Util.isScreenOn) initProg();
|
|
|
|
else state = 8;
|
2023-11-09 08:37:59 +08:00
|
|
|
if(progView==null) setContentView(backView);
|
|
|
|
} catch (RemoteException e) {
|
|
|
|
Util.makeText(MainActivity.this, e.getMessage()).show();
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
var intent = new Intent("com.xixun.joey.aidlset.SettingsService");
|
|
|
|
intent.setPackage("com.xixun.joey.cardsystem");
|
|
|
|
bindService(intent, conn, Context.BIND_AUTO_CREATE);
|
|
|
|
|
|
|
|
registerReceiver(new BroadcastReceiver(){
|
|
|
|
@Override
|
|
|
|
public void onReceive(Context context, Intent intent) {
|
|
|
|
System.out.println("Receive PAUSE_PLAYER");
|
2023-11-10 09:47:38 +08:00
|
|
|
Util.isScreenOn = ! intent.getBooleanExtra("pause", false);
|
|
|
|
if(! Util.isScreenOn) {
|
2023-11-09 08:37:59 +08:00
|
|
|
state = 8;
|
|
|
|
if(progView!=null) {
|
|
|
|
progView.setVisibility(GONE);
|
|
|
|
progView = null;
|
|
|
|
setContentView(backView);
|
|
|
|
}
|
|
|
|
} else if(progView==null) initProg();
|
|
|
|
}
|
|
|
|
}, new IntentFilter("com.xixun.action.PAUSE_PLAYER"), RECEIVER_EXPORTED);
|
|
|
|
|
|
|
|
// registerReceiver(new BroadcastReceiver(){
|
|
|
|
// @Override
|
|
|
|
// public void onReceive(Context context, Intent intent) {
|
|
|
|
// }
|
|
|
|
// }, new IntentFilter("com.xixun.joey.CHANGE_COMPANYID"), RECEIVER_EXPORTED);
|
|
|
|
|
|
|
|
registerReceiver(new BroadcastReceiver(){
|
|
|
|
@Override
|
|
|
|
public void onReceive(Context context, Intent intent) {
|
|
|
|
MainActivity.this.environIntent = intent;
|
|
|
|
for(var environ : environs) {
|
|
|
|
environ.onReceive(intent);
|
|
|
|
environ.invalidate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, new IntentFilter("xixun.intent.action.TEMPERATURE_HUMIDITY"), RECEIVER_EXPORTED);
|
|
|
|
|
|
|
|
new Thread(()->{
|
|
|
|
try {
|
|
|
|
var serverSocket = new ServerSocket(3333);
|
|
|
|
while(true) {
|
|
|
|
InputStream in = null;
|
|
|
|
OutputStream out = null;
|
|
|
|
try {
|
|
|
|
System.out.println("Accept ...");
|
|
|
|
final var socket = serverSocket.accept();
|
|
|
|
System.out.println("Receiving ...");
|
|
|
|
in = socket.getInputStream();
|
|
|
|
out = socket.getOutputStream();
|
|
|
|
JSList<String> hases = null;
|
|
|
|
label:
|
|
|
|
while(true) {
|
|
|
|
var obj = JSMap.from(in);
|
|
|
|
var _type = obj.stnn("_type");
|
|
|
|
System.out.println("_type: "+_type);
|
|
|
|
switch(_type) {
|
|
|
|
case "consult":
|
|
|
|
JSList<String> ids = obj.jslist("idList");
|
|
|
|
hases = new JSList<>();
|
|
|
|
if(ids!=null) for(int i=0; i<ids.size(); i++) {
|
|
|
|
if(new File(Util.programDir + "/" + ids.get(i)).exists()) ids.remove(i--);
|
|
|
|
else hases.add(ids.get(i));
|
|
|
|
}
|
|
|
|
new JSMap("_type", _type, "idList", ids).write(out);
|
|
|
|
break;
|
|
|
|
case "proStart":
|
|
|
|
var proSize = obj.intg("proSize");
|
|
|
|
var statFs = new StatFs(Environment.getExternalStorageDirectory().getPath());
|
|
|
|
var remain = statFs.getAvailableBytes() - proSize - 1048576;
|
|
|
|
if(remain < 0) {
|
|
|
|
var latch = new CountDownLatch(1);
|
|
|
|
runOnUiThread(() -> {
|
|
|
|
System.out.println("removeAllViews ...");
|
|
|
|
if(progView!=null) {
|
|
|
|
progView.setVisibility(GONE);
|
|
|
|
progView = null;
|
|
|
|
setContentView(backView);
|
|
|
|
}
|
|
|
|
latch.countDown();
|
|
|
|
});
|
|
|
|
var files = new File(Util.programDir).listFiles();
|
|
|
|
if(files == null) return;
|
|
|
|
Arrays.sort(files, (f1, f2) -> (int) (f1.lastModified() - f2.lastModified()));
|
|
|
|
try {
|
|
|
|
latch.await();
|
|
|
|
} catch (InterruptedException ignored) {}
|
|
|
|
for(var file : files) {
|
|
|
|
if(hases!=null && hases.contains(file.getName())) continue;
|
|
|
|
var len = file.length();
|
|
|
|
if(file.delete()) {
|
|
|
|
remain += len;
|
|
|
|
if(remain>=0) break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case "fileStart":
|
|
|
|
var size = obj.intg("size");
|
|
|
|
var name = obj.stnn("id");
|
|
|
|
System.out.println(" size: " + size + " name: " + name);
|
|
|
|
var fout = new FileOutputStream(Util.programDir + "/" + name);
|
|
|
|
IOs.write(fout, in, size);
|
|
|
|
fout.flush();
|
|
|
|
fout.getFD().sync();
|
|
|
|
fout.close();
|
|
|
|
break;
|
|
|
|
case "imgFileStart":
|
|
|
|
size = obj.intg("size");
|
|
|
|
fout = new FileOutputStream(Util.backImgFile);
|
|
|
|
IOs.write(fout, in, size);
|
|
|
|
fout.flush();
|
|
|
|
fout.getFD().sync();
|
|
|
|
fout.close();
|
|
|
|
runOnUiThread(() -> {
|
|
|
|
backView.cosImg = BitmapFactory.decodeFile(Util.backImgFile);
|
|
|
|
backView.invalidate();
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
case "imgFileEnd":
|
|
|
|
new JSMap("success", true).writeClose(out);
|
|
|
|
break label;
|
|
|
|
case "proEnd":
|
|
|
|
new JSMap("success", true).writeClose(out);
|
|
|
|
runOnUiThread(this::initProg);
|
|
|
|
break label;
|
|
|
|
case "DelPrograms":
|
|
|
|
var latch = new CountDownLatch(1);
|
|
|
|
AtomicBoolean ok = new AtomicBoolean(false);
|
|
|
|
runOnUiThread(() -> {
|
|
|
|
ok.set(delProgFile());
|
|
|
|
latch.countDown();
|
|
|
|
});
|
|
|
|
try {
|
|
|
|
latch.await();
|
|
|
|
} catch (InterruptedException ignored) {}
|
|
|
|
new JSMap("success", ok.get()).writeClose(out);
|
|
|
|
break label;
|
|
|
|
case "DelBackImg":
|
|
|
|
MainActivity.ins.runOnUiThread(() -> {
|
|
|
|
MainActivity.ins.backView.cosImg = null;
|
|
|
|
MainActivity.ins.backView.invalidate();
|
|
|
|
});
|
|
|
|
new JSMap("success", new File(Util.backImgFile).delete()).writeClose(out);
|
|
|
|
break label;
|
|
|
|
case "getPlayerState":
|
|
|
|
var descs = Util.getState(state);
|
|
|
|
new JSMap("code", state, "des_en", descs[0], "des", descs[1]).writeClose(out);
|
|
|
|
break label;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (Throwable e) {
|
|
|
|
MainActivity.ins.runOnUiThread(() -> {
|
|
|
|
Util.makeText(MainActivity.this, e.getMessage()).show();
|
|
|
|
});
|
|
|
|
e.printStackTrace();
|
|
|
|
} finally {
|
|
|
|
O.close(in, out);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (Throwable e) {
|
|
|
|
MainActivity.ins.runOnUiThread(() -> {
|
|
|
|
Util.makeText(MainActivity.this, e.getMessage()).show();
|
|
|
|
});
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
}).start();
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean delProgFile() {
|
|
|
|
if(progView!=null) {
|
|
|
|
progView.setVisibility(GONE);
|
|
|
|
progView = null;
|
|
|
|
setContentView(backView);
|
|
|
|
}
|
|
|
|
var files = new File(Util.programDir).listFiles();
|
|
|
|
var ok = true;
|
|
|
|
if(files != null) for(var file : files) if(! file.delete()) ok = false;
|
|
|
|
state = 4;
|
|
|
|
try {
|
|
|
|
var out = new FileOutputStream(Util.programDir+"/program");
|
|
|
|
out.write("{}".getBytes());
|
|
|
|
out.flush();
|
|
|
|
out.getFD().sync();
|
|
|
|
out.close();
|
|
|
|
} catch (Throwable ignored) {
|
|
|
|
}
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
public void initProg() {
|
|
|
|
try {
|
|
|
|
var task = JSMap.fromClose(new BufferedInputStream(new FileInputStream(Util.programDir + "/program"))).jsmap("task");
|
|
|
|
if(task==null) {
|
|
|
|
state = 3;
|
|
|
|
return;
|
|
|
|
}
|
2023-11-10 20:44:11 +08:00
|
|
|
var view = new ProgView(task, this);
|
2023-11-09 08:37:59 +08:00
|
|
|
if(view.getChildCount()==0) {
|
|
|
|
state = 3;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if(progView!=null) progView.setVisibility(GONE);
|
|
|
|
progView = view;
|
|
|
|
setContentView(progView);
|
|
|
|
syncProg((System.currentTimeMillis()+999)/1000*1000);
|
|
|
|
choreographer.postFrameCallback(this);
|
|
|
|
} catch (FileNotFoundException e) {
|
|
|
|
state = 3;
|
|
|
|
e.printStackTrace();
|
|
|
|
} catch (Throwable e) {
|
|
|
|
state = 7;
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Choreographer choreographer = Choreographer.getInstance();
|
|
|
|
ArrayList<Integer> avas = new ArrayList<>();
|
|
|
|
int curAva, curTimes = 1;
|
|
|
|
long syncMilli = Long.MAX_VALUE;
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void doFrame(long frameTimeNanos) {
|
|
|
|
if(progView == null) return;
|
|
|
|
var milli = System.currentTimeMillis();
|
|
|
|
var lastPage = page(curAva);
|
|
|
|
if(milli >= syncMilli) {
|
2023-11-10 09:47:38 +08:00
|
|
|
lastPage.hide();
|
2023-11-09 08:37:59 +08:00
|
|
|
syncProg(milli-syncMilli>=1000 ? milli : syncMilli);
|
|
|
|
} else if(milli >= lastPage.endMilli) {
|
2023-11-10 09:47:38 +08:00
|
|
|
lastPage.hide();
|
2023-11-09 08:37:59 +08:00
|
|
|
if(curTimes < lastPage.repeatTimes) curTimes++;
|
|
|
|
else {
|
|
|
|
curTimes = 1;
|
|
|
|
curAva++;
|
|
|
|
if(curAva >= avas.size()) {
|
|
|
|
syncProg(milli-lastPage.endMilli>=1000 ? milli : lastPage.endMilli);
|
|
|
|
choreographer.postFrameCallback(this);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
page(curAva).setMillis(lastPage.endMilli);
|
|
|
|
} else {
|
|
|
|
for(var layer : lastPage.layers) {
|
2023-11-10 09:47:38 +08:00
|
|
|
for(var ele : layer.eles) {
|
|
|
|
if(ele.view.getVisibility()==VISIBLE) {
|
|
|
|
if(milli >= ele.endMilli) ele.hide();
|
|
|
|
} else if(milli < ele.endMilli && milli >= ele.startMilli) ele.show();
|
2023-11-09 08:37:59 +08:00
|
|
|
}
|
|
|
|
if(milli >= layer.endMilli) {
|
|
|
|
layer.endMilli += layer.dur;
|
|
|
|
for(var ele : layer.eles) {
|
|
|
|
ele.endMilli += layer.dur;
|
|
|
|
ele.startMilli += layer.dur;
|
2023-11-10 09:47:38 +08:00
|
|
|
if(ele.startTime > 0) ele.hide();
|
|
|
|
else ele.show();
|
2023-11-09 08:37:59 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
choreographer.postFrameCallback(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void syncProg(long milli) {
|
|
|
|
curTimes = 1;
|
|
|
|
var dur = calAvas(milli);
|
|
|
|
if(dur==0) {
|
|
|
|
syncMilli = milli + 1000;
|
|
|
|
if(state!=2) {
|
|
|
|
setContentView(backView);
|
|
|
|
state = 2;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
var page = page(curAva = 0);
|
|
|
|
syncMilli = milli / dur * dur + dur;
|
2023-11-10 20:44:11 +08:00
|
|
|
if(syncMilli - milli >= 500) page.setMillis(milli);
|
2023-11-09 08:37:59 +08:00
|
|
|
if(state != 6) {
|
|
|
|
setContentView(progView);
|
|
|
|
state = 6;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
int calAvas(long milli) {
|
|
|
|
avas.clear();
|
|
|
|
var dur = 0;
|
|
|
|
Page page;
|
2023-11-10 09:47:38 +08:00
|
|
|
for(int i=0; i<progView.pages.size(); i++) if((page = progView.pages.get(i)).isSchedule(milli+dur)) {
|
2023-11-09 08:37:59 +08:00
|
|
|
avas.add(i);
|
|
|
|
dur += page.dur * page.repeatTimes;
|
|
|
|
}
|
|
|
|
return dur;
|
|
|
|
}
|
|
|
|
|
|
|
|
Page page(int i) {
|
2023-11-10 09:47:38 +08:00
|
|
|
return progView.pages.get(avas.get(i));
|
2023-11-09 08:37:59 +08:00
|
|
|
}
|
|
|
|
}
|