Android/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/MainActivity.java

556 lines
28 KiB
Java
Raw Normal View History

2023-11-09 08:37:59 +08:00
package com.xixun.xixunplayer;
import static android.view.View.VISIBLE;
2023-12-01 16:17:06 +08:00
import android.app.ActivityManager;
2023-11-09 08:37:59 +08:00
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;
2023-12-01 16:17:06 +08:00
import android.webkit.WebView;
2023-11-09 08:37:59 +08:00
import androidx.activity.ComponentActivity;
2023-11-10 09:47:38 +08:00
import androidx.annotation.NonNull;
2023-12-01 16:17:06 +08:00
import androidx.annotation.OptIn;
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;
2023-12-01 16:17:06 +08:00
import java.io.OutputStreamWriter;
2023-11-09 08:37:59 +08:00
import java.net.ServerSocket;
import java.util.Arrays;
import java.util.HashSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
2023-12-01 16:17:06 +08:00
import gnph.util.Dates;
2023-11-09 08:37:59 +08:00
import gnph.util.IOs;
import gnph.util.JSList;
import gnph.util.JSMap;
2023-12-01 16:17:06 +08:00
import gnph.util.NumFmts;
2023-11-09 08:37:59 +08:00
import gnph.util.O;
2023-12-01 16:17:06 +08:00
public class MainActivity extends ComponentActivity implements Choreographer.FrameCallback, Runnable {
2023-11-09 08:37:59 +08:00
public static MainActivity ins;
public Intent environIntent = new Intent();
HashSet<EleEnviron> environs = new HashSet<>();
BackView backView;
2023-12-01 16:17:06 +08:00
Prog progView;
long launchMilli = System.currentTimeMillis();
long syncMs;
2023-11-09 08:37:59 +08:00
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-12-01 16:17:06 +08:00
var msg = "---- MainActivity onCreate ---- UI Thread: "+Thread.currentThread().getId();
System.out.println(msg);
buf.append(msg).append('\n');
2023-11-09 08:37:59 +08:00
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();
2023-12-01 16:17:06 +08:00
var msg = "mkdir: "+program.mkdirs();
System.out.println(msg);
buf.append(msg).append('\n');
2023-11-09 08:37:59 +08:00
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");
2023-12-01 16:17:06 +08:00
buf.append("Bind cardsystem aidl service success\n");
2023-11-09 08:37:59 +08:00
var service = CardService.Stub.asInterface(iBinder);
try {
2023-11-10 09:47:38 +08:00
Util.isScreenOn = service.isScreenOpen();
2023-12-01 16:17:06 +08:00
buf.append("isScreenOn:").append(Util.isScreenOn).append("\n");
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) {
2023-12-01 16:17:06 +08:00
Util.makeText(MainActivity.this, e.toString()).show();
2023-11-09 08:37:59 +08:00
throw new RuntimeException(e);
2023-12-01 16:17:06 +08:00
} finally {
unbindService(this);
2023-11-09 08:37:59 +08:00
}
}
};
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-12-01 16:17:06 +08:00
buf.append("Receive PAUSE_PLAYER\n");
2023-11-10 09:47:38 +08:00
Util.isScreenOn = ! intent.getBooleanExtra("pause", false);
2023-12-01 16:17:06 +08:00
buf.append("isScreenOn:").append(Util.isScreenOn).append("\n");
2023-11-10 09:47:38 +08:00
if(! Util.isScreenOn) {
2023-11-09 08:37:59 +08:00
state = 8;
if(progView!=null) {
2023-12-01 16:17:06 +08:00
progView.release();
2023-11-09 08:37:59 +08:00
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);
2023-12-01 16:17:06 +08:00
registerReceiver(new BroadcastReceiver(){
@Override
public void onReceive(Context context, Intent intent) {
try {
var code = intent.getIntExtra("code", 0);
buf.append("remote_control ").append(code).append("\n");
if(progView == null || code > progView.pages.size()) return;
if(! progView.avas.isEmpty()) page(curAva).hide();
var millis = (System.currentTimeMillis()+999)/1000*1000;
if(code > 0) {
progView.avas.clear();
progView.avas.add(code-1);
curAva = 0;
curTimes = 1;
waitTo = 0;
var page = page(0);
dur = page.tDur;
progView.stopAndPrepare(curAva);
page.setMillis(millis);
if(state != 6) {
setContentView(progView);
state = 6;
2023-11-09 08:37:59 +08:00
}
2023-12-01 16:17:06 +08:00
} else {
waitTo = Long.MAX_VALUE;
syncProg(millis);
2023-11-09 08:37:59 +08:00
}
2023-12-01 16:17:06 +08:00
} catch (Throwable e) {
Util.makeText(MainActivity.this, e.toString()).show();
e.printStackTrace();
2023-11-09 08:37:59 +08:00
}
}
2023-12-01 16:17:06 +08:00
}, new IntentFilter("com.xixun.yzd.REMOTE_CONTROL"), RECEIVER_EXPORTED);
new Thread(this).start();
2023-11-09 08:37:59 +08:00
}
public boolean delProgFile() {
if(progView!=null) {
2023-12-01 16:17:06 +08:00
progView.release();
2023-11-09 08:37:59 +08:00
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-12-01 16:17:06 +08:00
var view = new Prog(task, this);
2023-11-09 08:37:59 +08:00
if(view.getChildCount()==0) {
state = 3;
return;
}
2023-12-01 16:17:06 +08:00
if(progView!=null) progView.release();
2023-11-09 08:37:59 +08:00
progView = view;
setContentView(progView);
2023-12-01 16:17:06 +08:00
buf.append("init sync\n");
2023-11-09 08:37:59 +08:00
syncProg((System.currentTimeMillis()+999)/1000*1000);
choreographer.postFrameCallback(this);
} catch (FileNotFoundException e) {
state = 3;
e.printStackTrace();
} catch (Throwable e) {
state = 7;
2023-12-01 16:17:06 +08:00
Util.makeText(this, e.toString()).show();
2023-11-09 08:37:59 +08:00
e.printStackTrace();
}
}
Choreographer choreographer = Choreographer.getInstance();
2023-12-01 16:17:06 +08:00
int curAva, curTimes = 1, dur;
long waitTo = Long.MAX_VALUE;
StringBuffer buf = new StringBuffer();
2023-11-09 08:37:59 +08:00
@Override
public void doFrame(long frameTimeNanos) {
if(progView == null) return;
var milli = System.currentTimeMillis();
2023-12-01 16:17:06 +08:00
if(progView.avas.isEmpty()) {
if(milli >= waitTo) {
waitTo = Long.MAX_VALUE;
buf.append("wait sync\n");
syncProg(milli);
2023-11-09 08:37:59 +08:00
}
} else {
2023-12-01 16:17:06 +08:00
var lastPage = page(curAva);
if(milli >= lastPage.endMilli) {
lastPage.hide();
if(waitTo>0) {
if(curTimes < lastPage.repeatTimes) curTimes++;
else {
curTimes = 1;
curAva++;
if(curAva >= progView.avas.size()) {
var isDiff = milli-lastPage.endMilli>=1000;
if(buf.length()>1000000) buf.replace(0, 100000, "");
buf.append("isDiff:").append(isDiff).append(" endMs:").append(lastPage.endMilli).append(" millis:").append(milli).append("\n");
syncProg(isDiff ? milli : lastPage.endMilli);
buf.append("after. curAva:").append(curAva).append(" endMs:").append(lastPage.endMilli).append("\n");
choreographer.postFrameCallback(this);
return;
}
progView.stopAndPrepare(curAva);
}
2023-11-09 08:37:59 +08:00
}
2023-12-01 16:17:06 +08:00
page(curAva).setMillis(lastPage.endMilli);
buf.append("curAva:").append(curAva).append(" endMs:").append(lastPage.endMilli).append("\n");
} else {
for(var layer : lastPage.layers) {
2023-11-09 08:37:59 +08:00
for(var ele : layer.eles) {
2023-12-01 16:17:06 +08:00
if(ele.view.getVisibility()==VISIBLE) {
if(milli >= ele.endMilli) ele.hide();
// else if(ele.needLoop) {
// try {
// var vv = (EleVideo) ele.view;
// var vdur = vv.getDuration();
// if(vdur>=1000) {
// ele.needLoop = false;
// if(ele.endTime-ele.startTime-vdur>=1000) vv.exoPlayer.setRepeatMode(ExoPlayer.REPEAT_MODE_ONE);
// }
// } catch (Throwable e) {
// Util.makeText(this, e.toString()).show();
// e.printStackTrace();
// }
// }
} else if(milli < ele.endMilli && milli >= ele.startMilli) ele.show();
}
if(milli >= layer.endMilli) {
layer.endMilli += layer.dur;
for(var ele : layer.eles) {
ele.endMilli += layer.dur;
ele.startMilli += layer.dur;
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;
2023-12-01 16:17:06 +08:00
progView.avas.clear();
dur = 0;
Prog.Page page;
for(int i=0; i<progView.pages.size(); i++) if((page = progView.pages.get(i)).isSchedule(milli+dur)) {
progView.avas.add(i);
dur += page.tDur;
}
2023-11-09 08:37:59 +08:00
if(dur==0) {
2023-12-01 16:17:06 +08:00
waitTo = milli + 1000;
2023-11-09 08:37:59 +08:00
if(state!=2) {
setContentView(backView);
state = 2;
}
} else {
2023-12-01 16:17:06 +08:00
var start = milli / dur * dur;
curAva = 0;
if(start < milli) {
do {
start += page(curAva++).tDur;
} while(curAva < progView.avas.size() && start<=milli);
start -= page(--curAva).tDur;
syncMs = milli;
buf.append("Sync. milli:").append(milli).append(" start:").append(start).append(" diff:").append(milli - start).append("\n");
}
progView.stopAndPrepare(curAva);
page(curAva).setMillis(start);
2023-11-09 08:37:59 +08:00
if(state != 6) {
setContentView(progView);
state = 6;
}
}
}
2023-12-01 16:17:06 +08:00
Prog.Page page(int i) {
return progView.pages.get(progView.avas.get(i));
2023-11-09 08:37:59 +08:00
}
2023-12-01 16:17:06 +08:00
public void run() {
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;
while(true) {
var obj = JSMap.from(in);
var _type = obj.stnn("_type");
System.out.println("_type: "+_type);
if("consult".equals(_type)) {
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);
} else if("proStart".equals(_type)) {
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.release();
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;
}
}
}
} else if("fileStart".equals(_type)) {
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();
} else if("imgFileStart".equals(_type)) {
var size = obj.intg("size");
var 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();
});
} else if("imgFileEnd".equals(_type)) {
new JSMap("success", true).write(out);
} else if("proEnd".equals(_type)) {
new JSMap("success", true).write(out);
buf.append("proEnd\n");
runOnUiThread(this::initProg);
} else if("DelPrograms".equals(_type)) {
var latch = new CountDownLatch(1);
var ok = new AtomicBoolean(false);
runOnUiThread(() -> {
ok.set(delProgFile());
latch.countDown();
});
try {
latch.await();
} catch (InterruptedException ignored) {}
new JSMap("success", ok.get()).write(out);
} else if("DelBackImg".equals(_type)) {
MainActivity.ins.runOnUiThread(() -> {
MainActivity.ins.backView.cosImg = null;
MainActivity.ins.backView.invalidate();
});
new JSMap("success", new File(Util.backImgFile).delete()).write(out);
} else if("getPlayerState".equals(_type)) {
var descs = Util.getState(state);
new JSMap(
"code", state,
"des_en", descs[0],
"des", descs[1]
).write(out);
} else if("GetInfo".equals(_type)) {
var writer = new OutputStreamWriter(out);
var fmt = "yy-MM-dd HH:mm:ss.SSS";
writer.append("ProgSend: ").append(Dates.fmt(new File(Util.programDir + "/program").lastModified(), fmt)).append(" ProgDur: ").append(String.valueOf(dur));
if(progView!=null) writer.append(" Pages: ").append(String.valueOf(progView.avas.size())).append(" / ").append(String.valueOf(progView.pages.size()));
writer.append("\n");
writer.append(" Launch: ").append(Dates.fmt(launchMilli, fmt)).append("\n");
writer.append(" Sync: ").append(Dates.fmt(syncMs, fmt)).append("\n");
writer.append("Page End: ").append(progView==null || progView.avas.isEmpty() ? "0" : Dates.fmt(page(curAva).endMilli, fmt)).append(" CurPage: ").append(String.valueOf(curAva)).append(" / ").append(progView==null || progView.avas.isEmpty() ? "0" : String.valueOf(progView.avas.get(curAva))).append("\n");
writer.append(" Current: ").append(Dates.fmt(System.currentTimeMillis(), fmt)).append("\n");
var actManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
var memoryInfo = new ActivityManager.MemoryInfo();
actManager.getMemoryInfo(memoryInfo);
writer.append("Memory: ").append(String.valueOf(memoryInfo.totalMem/1000000)).append(" ").append(String.valueOf(memoryInfo.availMem/1000000)).append(" ").append(String.valueOf(memoryInfo.threshold/1000000)).append(" ").append(String.valueOf(memoryInfo.lowMemory)).append(" (total avail threshold low)\n");
var runtime = Runtime.getRuntime();
writer.append("Runtime Memory: ").append(String.valueOf(runtime.maxMemory()/1000000)).append(" ").append(String.valueOf(runtime.totalMemory()/1000000)).append(" ").append(NumFmts.cfix2().format(runtime.freeMemory()*0.000001)).append(" (max total free)\n");
var latch = new CountDownLatch(1);
runOnUiThread(() -> {
if(! progView.avas.isEmpty()) {
var page = page(curAva);
for(var layer : page.layers) for(var ele : layer.eles) if(ele.view.getVisibility()==VISIBLE) {
try {
if(ele.view instanceof EleVideo) {
var view = (EleVideo) ele.view;
if(view.ijkPlayer!=null) {
writer.append("isVideoPlaying: ").append(String.valueOf(view.ijkPlayer.isPlaying())).append("\n");
writer.append("getVideoDecoder: ").append(String.valueOf(view.ijkPlayer.getVideoDecoder())).append("\n");
var MediaInfo = view.ijkPlayer.getMediaInfo();
writer.append("mMediaPlayerName: ").append(MediaInfo.mMediaPlayerName).append("\n");
writer.append("mVideoDecoder: ").append(MediaInfo.mVideoDecoder).append("\n");
writer.append("mVideoDecoderImpl: ").append(MediaInfo.mVideoDecoderImpl).append("\n");
writer.append("mAudioDecoder: ").append(MediaInfo.mAudioDecoder).append("\n");
writer.append("mAudioDecoderImpl: ").append(MediaInfo.mAudioDecoderImpl).append("\n");
writer.append("mFormat: ").append(MediaInfo.mMeta.mFormat).append("\n");
}
// else if(view.exoPlayer!=null) {
// writer.append("isVideoPlaying: ").append(String.valueOf(view.exoPlayer.isPlaying())).append(String.valueOf(view.exoPlayer.getCurrentPosition())).append("/").append(String.valueOf(view.exoPlayer.getDuration())).append("\n");
// writer.append("getPlaybackState: ").append(view.getState()).append("\n");
// writer.append("getPlayerError: ").append(String.valueOf(view.exoPlayer.getPlayerError())).append("\n");
// writer.append("getPlaybackSuppressionReason: ").append(String.valueOf(view.exoPlayer.getPlaybackSuppressionReason())).append("\n");
// writer.append("getVideoFormat: ").append(String.valueOf(view.exoPlayer.getVideoFormat())).append("\n");
// var cnt = view.exoPlayer.getRendererCount();
// for(int rr=0; rr<cnt; rr++) writer.append(" Renderer: ").append(String.valueOf(view.exoPlayer.getRendererType(rr))).append(" ").append(String.valueOf(view.exoPlayer.getRenderer(rr))).append("\n");
// writer.append("getPlaybackParameters: ").append(String.valueOf(view.exoPlayer.getPlaybackParameters())).append("\n");
// }
} else if(ele.view instanceof WebView) {
var view = (WebView) ele.view;
writer.append("Title: ").append(view.getTitle()).append("\n");
writer.append(" Url: ").append(view.getUrl()).append("\n");
writer.append("OrUrl: ").append(view.getOriginalUrl()).append("\n");
writer.append("Progress: ").append(String.valueOf(view.getProgress())).append("\n");
}
} catch (Exception ignored) {
}
}
}
latch.countDown();
});
try {
latch.await();
} catch (InterruptedException ignored) {}
writer.flush();
} else if("GetLog".equals(_type)) {
out.write(buf.toString().getBytes());
} else if("GetFile".equals(_type)) {
var name = obj.str("name");
if(name!=null) IOs.writeCloseIn(out, new FileInputStream(Util.programDir+"/"+name));
else new JSMap("code", 1, "msg", "name is null").write(out);
}
}
} catch (Throwable e) {
var emsg = e.getMessage();
if(emsg!=null && ("Socket closed".equals(emsg) || emsg.endsWith("end-of-input"))) continue;
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.toString()).show();
});
e.printStackTrace();
}
2023-11-09 08:37:59 +08:00
}
}