Android/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/MainActivity.java
2026-01-23 15:14:23 +08:00

954 lines
41 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.xixun.xixunplayer;
import android.annotation.SuppressLint;
import android.app.Activity;
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.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.graphics.Color;
import android.media.AudioFocusRequest;
import android.media.AudioManager;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.StrictMode;
import android.view.Choreographer;
import android.view.View;
import android.webkit.JavascriptInterface;
import android.webkit.WebView;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.xixun.joey.aidlset.CardService;
import com.xixun.xy.conn.aidl.ConnService;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import gnph.util.Chsets;
import gnph.util.JSList;
import gnph.util.JSMap;
import gnph.util.O;
public class MainActivity extends Activity implements Choreographer.FrameCallback {
public static MainActivity ins;
ArrayList<BroadcastReceiver> reces = new ArrayList<>();
ArrayList<ServiceConnection> services = new ArrayList<>();
public Intent environIntent = new Intent();
HashSet<IntentReceiver> environs = new HashSet<>();
BackView backView;
WebView backViewL;
Prog progView, insView;
long launchMilli = System.currentTimeMillis();
int state;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ins = this;
Util.println("==>> MainActivity onCreate >>>> this "+hashCode()+" Thread: "+Thread.currentThread().getId());
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitAll().build());
setContentView(backView = new BackView(MainActivity.this));
state = 5;
hookWebView();
if(ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) init();
else {
Util.println("---- No permission, Try again ...");
Util.makeText(this, "No permission, Try again ...").show();
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,
android.Manifest.permission.ACCESS_COARSE_LOCATION
}, 999);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(Util.programDir!=null) return;
if(requestCode==999 && grantResults.length > 0 && grantResults[0]==PackageManager.PERMISSION_GRANTED) init();
else {
Util.println("---- Request Permission Failed");
Util.makeText(this, "Request Permission Failed").show();
}
}
@SuppressLint("SoonBlockedPrivateApi")
public static void hookWebView() {
if(android.os.Process.myUid() != android.os.Process.SYSTEM_UID) return;
try {
var factoryClass = Class.forName("android.webkit.WebViewFactory");
var field = factoryClass.getDeclaredField("sProviderInstance");
field.setAccessible(true);
var sProviderInstance = field.get(null);
if (sProviderInstance != null) {
Util.println("sProviderInstance isn't null");
return;
}
Method getProviderClassMethod;
if (Build.VERSION.SDK_INT > 22) { // above 22
getProviderClassMethod = factoryClass.getDeclaredMethod("getProviderClass");
} else if (Build.VERSION.SDK_INT == 22) { // method name is a little different
getProviderClassMethod = factoryClass.getDeclaredMethod("getFactoryClass");
} else { // no security check below 22
Util.println("Don't need to Hook WebView");
return;
}
getProviderClassMethod.setAccessible(true);
var providerClass = (Class<?>) getProviderClassMethod.invoke(factoryClass);
var delegateClass = Class.forName("android.webkit.WebViewDelegate");
var declaredConstructor = delegateClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
sProviderInstance = providerClass.getDeclaredMethod("create", delegateClass).invoke(providerClass, declaredConstructor.newInstance());
field.set("sProviderInstance", sProviderInstance);
Util.println("sProviderInstance"+sProviderInstance.toString());
Util.println("Hook Done!");
} catch (Throwable e) {
Util.printStackTrace(e);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
Util.println("==<< MainActivity onDestroy <<<< this "+hashCode());
state = 8;
setContentView(backView);
if(insView!=null) {
insView.release();
insView = null;
}
if(progView!=null) {
progView.release();
progView = null;
}
if(backViewL!=null) {
backViewL.removeAllViews();
backViewL.destroy();
backViewL = null;
}
ins = null;
for(var rece : reces) unregisterReceiver(rece);
for(var service : services) unbindService(service);
if(audioManager!=null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) audioManager.abandonAudioFocusRequest(audioFocusRequest);
try (var fout = new FileOutputStream(Util.programDir+"/cfg")){
Util.cfg.write(fout);
fout.flush();
fout.getFD().sync();
} catch (Exception ignored) {
}
System.gc();
}
@Override
protected void onStart() {
super.onStart();
Util.println(" ==>> MainActivity onStart >> "+hashCode());
}
@Override
protected void onStop() {
super.onStop();
Util.println(" ==<< MainActivity onStop << "+hashCode());
state = 8;
stopProg();
}
@Override
protected void onRestart() {
super.onRestart();
Util.println(" ==>> MainActivity onRestart >> "+hashCode());
if(progView==null && insView==null) runOnUiThread(this::initProg);
}
@Override
protected void onResume() {
super.onResume();
Util.println(" ==>> MainActivity onResume >> "+hashCode());
}
@Override
protected void onPause() {
super.onPause();
Util.println(" ==<< MainActivity onPause << "+hashCode());
state = 8;
}
CardService serviCard;
ConnService serviXy;
Intent intenCard;
ServiceConnection connCard;
@SuppressLint("UnspecifiedRegisterReceiverFlag")
public void init() {
Util.initDir(this);
if(MainService.ins==null) startService(new Intent(this, MainService.class));
var cardId = Util.getCardId();
if(! cardId.isEmpty()) Util.cardType = cardId.toLowerCase().charAt(0);
if(Util.cardType=='l') {
backViewL = new WebView(this);
backViewL.setBackgroundColor(Color.TRANSPARENT);
backViewL.setVerticalScrollBarEnabled(false);
backViewL.setHorizontalScrollBarEnabled(false);
backViewL.setInitialScale(100);
backViewL.setPadding(0, 0, 0, 0);
backViewL.clearCache(true);
var settings = backViewL.getSettings();
settings.setJavaScriptEnabled(true);
settings.setAllowFileAccess(true);
settings.setDomStorageEnabled(true);
settings.setBuiltInZoomControls(false);
settings.setDisplayZoomControls(false);
settings.setSupportZoom(false);
settings.setUseWideViewPort(true);
settings.setLoadsImagesAutomatically(true);
settings.setLoadWithOverviewMode(true);
// 添加JS交互接口核心传递宽高
backViewL.addJavascriptInterface(new Object() {
@JavascriptInterface
public String getSizeFromAndroid() {
if(serviCard!=null && serviCard.asBinder().isBinderAlive()) {
try {
var www = serviCard.getScreenWidth();
var hhh = serviCard.getScreenHeight();
if(www!=Util.screenWidth || hhh!=Util.screenHeight) {
Util.screenWidth = www;
Util.screenHeight = hhh;
ins.backView.invalidate();
}
} catch(Throwable e) {
Util.printStackTrace(e);
}
} else {
Util.println(" bindService");
ins.bindService(ins.intenCard, ins.connCard, Context.BIND_AUTO_CREATE);
}
return "{\"width\":\"" + Util.screenWidth + "px\",\"height\":\"" + Util.screenHeight + "px\"}";
}
@JavascriptInterface
public String getNetwork() {// 获取当前网络类型
NetworkTypeUtil.NetworkType networkType = NetworkTypeUtil.getCurrentNetworkType(MainActivity.this);
String value = "";
switch (networkType) {
case WIFI:
value = NetworkTypeUtil.getWifiSsid(MainActivity.this);// 处理未获取到SSID的情况
if(O.isEmpty(value)) value = "未知WiFi名称";
break;
case ETHERNET:
break;
case CELLULAR:// 细分蜂窝网络类型2G/3G/4G
value = NetworkTypeUtil.getCellularSubType(MainActivity.this);;
break;
case NONE:
break;
case UNKNOWN:
break;
}
var IP = NetworkTypeUtil.getActiveNetworkIp(MainActivity.this);
return "{\"type\":\"" + networkType + "\",\"value\":\""+value+"\",\"ip\":\""+IP+"\",\"id\":\""+cardId+"\"}";
}
@JavascriptInterface
public void onJsCallback(String message) {
}
}, "AndroidBridge");
}
var cfg = new File(Util.programDir+"/cfg");
if(! cfg.isFile()) Util.cfg = new JSMap();
else {
try {
Util.cfg = JSMap.fromClose(new FileInputStream(cfg));
} catch (Exception e) {
Util.cfg = new JSMap();
Util.makeText(MainActivity.this, Util.toStr(e)).show();
Util.printStackTrace(e);
}
}
Util.println(Util.cfg.toStr());
Util.logOn = Util.cfg.get("logUploadUrl")!=null;
Util.openHelper = new SQLiteOpenHelper(this, "aaa", null, 1) {
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table cfg(k varchar(32) primary key, v varchar(128))");
db.execSQL("create table log(time integer primary key, dur integer, id varchar(64), name varchar(64), type varchar(16), lat double, lng double)");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
};
new LogThread().start();
if(Util.logOn) {
var db = Util.openHelper.getWritableDatabase();
var cursor = db.rawQuery("select count(*) aaa from log", null);
while(cursor.moveToNext()) Util.println("Log Cnt "+cursor.getString(0));
db.close();
}
connCard = new ServiceConnection() {
public void onServiceDisconnected(ComponentName name) {
serviCard = null;
Util.println("<-<- AIDL Service cardsystem Disconnected");
}
public void onServiceConnected(ComponentName name, IBinder iBinder) {
unbindService(this);
services.remove(this);
Util.println("->-> AIDL Service cardsystem Connected");
serviCard = CardService.Stub.asInterface(iBinder);
try {
Util.isScreenOn = serviCard.isScreenOpen();
Util.screenWidth = serviCard.getScreenWidth();
Util.screenHeight = serviCard.getScreenHeight();
Util.println(" IsScreenOn: "+Util.isScreenOn+" screen: "+Util.screenWidth+" x "+Util.screenHeight);
backView.invalidate();
if(Util.isScreenOn) initProg();
else state = 8;
if(backView.isShown() && backViewL!=null) {
setContentView(backViewL);
backViewL.loadUrl("file:///android_asset/local_page.html");
}
} catch (Exception e) {
Util.makeText(MainActivity.this, Util.toStr(e)).show();
Util.printStackTrace(e);
}
}
};
intenCard = new Intent("com.xixun.joey.aidlset.SettingsService");
intenCard.setPackage("com.xixun.joey.cardsystem");
bindService(intenCard, connCard, Context.BIND_AUTO_CREATE);
services.add(connCard);
var connXy = new ServiceConnection() {
public void onServiceDisconnected(ComponentName name) {
serviXy = null;
Util.println("<-<- AIDL Service xy.conn Disconnected");
}
public void onServiceConnected(ComponentName name, IBinder iBinder) {
unbindService(this);
services.remove(this);
Util.println("->-> AIDL Service xy.conn Connected");
serviXy = ConnService.Stub.asInterface(iBinder);
try {
Util.serverURL = serviXy.getServerURL();
Util.println(" ServerURL: "+Util.serverURL);
if(Util.serverURL==null || Util.serverURL.isEmpty()) Util.serverURL = "https://m2mled.net/";
else {
if(! Util.serverURL.startsWith("http")) Util.serverURL = "http://"+Util.serverURL;
if(! Util.serverURL.endsWith("/")) Util.serverURL += "/";
}
} catch (Exception e) {
Util.makeText(MainActivity.this, Util.toStr(e)).show();
Util.printStackTrace(e);
}
}
};
var intenXy = new Intent("xixun.intent.action.CONNECTION_INFO");
intenXy.setPackage("com.xixun.xy.conn");
bindService(intenXy, connXy, Context.BIND_AUTO_CREATE);
services.add(connXy);
reces.clear();
BroadcastReceiver rece;
registerReceiver(rece = new BroadcastReceiver(){
@Override
public void onReceive(Context context, Intent intent) {
Util.println("Receive PAUSE_PLAYER");
Util.isScreenOn = ! intent.getBooleanExtra("pause", false);
Util.println(" IsScreenOn: "+Util.isScreenOn);
if(! Util.isScreenOn) {
state = 8;
stopProg();
} else if(progView==null && insView==null) runOnUiThread(() -> initProg());
}
}, new IntentFilter("com.xixun.action.PAUSE_PLAYER"));
reces.add(rece);
registerReceiver(rece = new BroadcastReceiver(){
@Override
public void onReceive(Context context, Intent intent) {
Util.println("Receive CHANGE_COMPANYID");
try {
if(serviXy!=null && serviXy.asBinder().isBinderAlive()) {
Util.serverURL = serviXy.getServerURL();
Util.println(" ServerURL: "+Util.serverURL);
if(Util.serverURL==null || Util.serverURL.isEmpty()) Util.serverURL = "https://m2mled.net/";
else {
if(! Util.serverURL.startsWith("http")) Util.serverURL = "http://"+Util.serverURL;
if(! Util.serverURL.endsWith("/")) Util.serverURL += "/";
}
} else {
Util.println(" bindService");
bindService(intenXy, connXy, Context.BIND_AUTO_CREATE);
}
} catch (Exception e) {
Util.makeText(MainActivity.this, Util.toStr(e)).show();
Util.printStackTrace(e);
}
}
}, new IntentFilter("com.xixun.joey.CHANGE_COMPANYID"));
reces.add(rece);
registerReceiver(rece = new BroadcastReceiver(){
@Override
public void onReceive(Context context, Intent intent) {
MainActivity.this.environIntent = intent;
for(var environ : environs) {
environ.onReceive(intent);
((View)environ).invalidate();
}
}
}, new IntentFilter("xixun.intent.action.TEMPERATURE_HUMIDITY"));
reces.add(rece);
registerReceiver(rece = new BroadcastReceiver(){
@Override
public void onReceive(Context context, Intent intent) {
Util.lat = intent.getDoubleExtra("latitude", 0);
Util.lng = intent.getDoubleExtra("longitude", 0);
Util.println(" lat "+Util.lat+" lng "+Util.lng);
}
}, new IntentFilter("com.xixun.joey.gpsinfo"));
reces.add(rece);
registerReceiver(rece = new BroadcastReceiver(){
@Override
public void onReceive(Context context, Intent intent) {
try {
var code = intent.getIntExtra("code", 0);
Util.println(" remote_control "+code);
if(progView == null || progView.pages.isEmpty() || ! progView.isShown() || code==-1 || code < -3 || code > progView.pages.size()) return;
if(code<=-2) {
var idx = avas.isEmpty() ? -1 : progView.pages.indexOf(avas.get(curAva));
if(code==-2) code = idx<=0 ? progView.pages.size() : idx;
else code = idx>=progView.pages.size()-1 ? 1 : idx+2;
}
var ms = System.currentTimeMillis();
if(! avas.isEmpty()) {
var page = avas.get(curAva);
for(var layer : page.layers) for(var src : layer.srcs) if(src.isShow) {
showHides.add(new MainActivity.ShowHide(ms+500, src, 'H'));
src.isShow = false;
}
if(code > 0 && avas.get(curAva)==progView.pages.get(code-1)) {
showHides.add(new ShowHide(ms + 500, ()->{
for(var call : progView.calls) call.doFrame(ms);
}));
}
}
if(code==0) syncProg(ms, 0);
else {
avas.clear();
var page = progView.pages.get(code-1);
avas.add(page);
curAva = 0;
curTimes = 1;
waitTo = 0; //点播
page.setMillis(ms, ms, showHides);
if(state != 6) {
setContentView(progView);
state = 6;
}
}
var root = JSMap.fromClose(new BufferedInputStream(new FileInputStream(Util.programDir + "/program")));
root.put("Demand", code);
try (var fOut = new FileOutputStream(Util.programDir + "/program")) {
root.write(fOut);
var spaces = " ".getBytes();
for(int i=0;i<1000; i++) fOut.write(spaces);
fOut.flush();
fOut.getFD().sync();
}
} catch (Throwable e) {
Util.makeText(MainActivity.this, Util.toStr(e)).show();
Util.printStackTrace(e);
}
}
}, new IntentFilter("com.xixun.yzd.REMOTE_CONTROL"));
reces.add(rece);
if(Util.custom==Util.Custom.Yishi) {
new Thread(()->{
while(true) {
try {
Thread.sleep(10000);
if(serviCard!=null && serviCard.asBinder().isBinderAlive()) {
var www = serviCard.getScreenWidth();
var hhh = serviCard.getScreenHeight();
if(www!=Util.screenWidth || hhh!=Util.screenHeight) {
var ins = MainActivity.ins;
if(ins!=null) ins.runOnUiThread(() -> {
Util.screenWidth = www;
Util.screenHeight = hhh;
ins.backView.invalidate();
});
}
} else {
Util.println(" bindService");
var ins = MainActivity.ins;
if(ins!=null) ins.runOnUiThread(() -> ins.bindService(ins.intenCard, ins.connCard, Context.BIND_AUTO_CREATE));
}
} catch (Throwable e) {
var ins = MainActivity.ins;
if(ins!=null) ins.runOnUiThread(() -> Util.makeText(ins, Util.toStr(e)).show());
Util.printStackTrace(e);
}
}
}).start();
}
Util.isAudioLoss = false;
if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
audioFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK).setOnAudioFocusChangeListener((int focusChange)-> {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN:
Util.println("AUDIOFOCUS_GAIN");
Util.isAudioLoss = false;
break;
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
Util.println("AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK");
Util.isAudioLoss = false;
break;
case AudioManager.AUDIOFOCUS_LOSS:
Util.println("AUDIOFOCUS_LOSS");
Util.isAudioLoss = true;
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
Util.println("AUDIOFOCUS_LOSS_TRANSIENT"); //短暂失去音频焦点,暂停播放等待又一次获得音频焦点
Util.isAudioLoss = true;
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
Util.println("AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK"); //减少声音就可以
Util.isAudioLoss = true;
break;
}
View view;
if(Util.isAudioLoss) {
if(insView!=null) {
for(int cc=0; cc<insView.getChildCount(); cc++) if((view = insView.getChildAt(cc)) instanceof SrcVideo) ((SrcVideo) view).ijkPlayer.setVolume(0,0);
} else if(progView!=null) {
for(int cc=0; cc<progView.getChildCount(); cc++) if((view = progView.getChildAt(cc)) instanceof SrcVideo) ((SrcVideo) view).ijkPlayer.setVolume(0,0);
}
} else {
if(insView!=null) {
for(int cc=0; cc<insView.getChildCount(); cc++) if((view = insView.getChildAt(cc)) instanceof SrcVideo) ((SrcVideo) view).ijkPlayer.setVolume(((SrcVideo) view).vol, ((SrcVideo) view).vol);
} else if(progView!=null) {
for(int cc=0; cc<progView.getChildCount(); cc++) if((view = progView.getChildAt(cc)) instanceof SrcVideo) ((SrcVideo) view).ijkPlayer.setVolume(((SrcVideo) view).vol, ((SrcVideo) view).vol);
}
}
}).build();
Util.println("requestAudioFocus: "+audioManager.requestAudioFocus(audioFocusRequest));
}
}
AudioManager audioManager;
AudioFocusRequest audioFocusRequest;
public void stopProg() {
Util.println("---- Stop Prog\n");
avas.clear();
showHides.clear();
showeds.clear();
curAva = 0;
curTimes = 1;
if(backViewL==null) setContentView(backView);
else {
setContentView(backViewL);
backViewL.loadUrl("file:///android_asset/local_page.html");
}
if(insView!=null) {
insView.release();
insView = null;
}
if(progView!=null) {
progView.release();
progView = null;
}
System.gc();
}
public boolean delProgFile() {
stopProg();
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 fOut = new FileOutputStream(Util.programDir+"/program");
fOut.write("{}".getBytes());
var spaces = " ".getBytes();
for(int i=0;i<1000; i++) fOut.write(spaces);
fOut.flush();
fOut.getFD().sync();
fOut.close();
} catch (Throwable ignored) {
}
return ok;
}
public void initProg() {
Util.println("\n---- Init Prog");
if(progView!=null || insView!=null) return;
state = 1;
try {
Util.println(" Parse Insert Json");
var root = JSMap.fromClose(new BufferedInputStream(new FileInputStream(Util.programDir + "/insert")));
var task = root.jsmap("task");
if(task==null && root.containsKey("layers")) task = new JSMap("items", new JSList<>(new JSMap("_program", root)));
if(task!=null) {
var view = new Prog(task, this);
if(! view.pages.isEmpty()) setContentView(insView = view);
}
} catch(FileNotFoundException ignored) {
} catch(Throwable e) {
state = 7;
Util.printStackTrace(e);
}
var demand = 0;
try {
Util.println(" Parse Json");
var root = JSMap.fromClose(new BufferedInputStream(new FileInputStream(Util.programDir + "/program")));
var task = root.jsmap("task");
demand = root.intg("Demand");
if(task==null && root.containsKey("layers")) task = new JSMap("items", new JSList<>(new JSMap("_program", root)));
if(task==null) Util.println(root.isEmpty() ? " Empty program JSON\n" : " Error: task==null\n");
else {
var view = new Prog(task, this);
if(view.pages.isEmpty()) Util.println(" Error: ChildCount==0\n");
else setContentView(progView = view);
}
} catch(FileNotFoundException e) {
Util.println(""+e);
} catch(Throwable e) {
state = 7;
Util.makeText(this, Util.toStr(e)).show();
Util.printStackTrace(e);
}
if(insView!=null || progView!=null) {
state = 5;
var ms = System.currentTimeMillis();
if(demand==0 || progView==null) {
Util.println(" Init Sync");
syncProg(ms, 0);
} else {
Util.println(" demand "+demand);
avas.clear();
var page = progView.pages.get(demand-1);
avas.add(page);
curAva = 0;
curTimes = 1;
waitTo = 0; //点播
page.setMillis(ms, ms, showHides);
if(state != 6) {
setContentView(progView);
state = 6;
}
}
if(canAdd) {
choreographer.postFrameCallback(this);
canAdd = false;
}
} else if(state != 7) state = 3;
}
public void initProg(byte[] json) {
Util.println("\n---- Init Prog with json");
try {
var root = JSMap.from(json);
var task = root.jsmap("task");
if(task==null) {
if(! root.containsKey("layers")) {
state = 7;
Util.println(" Error: task==null\n");
Util.println(new String(json, Chsets.UTF8));
return;
}
task = new JSMap("items", new JSList<>(new JSMap("_program", root)));
}
var view = new Prog(task, this);
if(view.pages.isEmpty()) {
if(! view.isInsert) state = 7;
Util.println(" Error: ChildCount==0\n");
Util.println(new String(json, Chsets.UTF8));
return;
}
if(Util.cfg.bool("needCheck")) {
new Thread(()->{
if(Util.check(view)) runOnUiThread(() -> {
afterCheck(view, json);
});
else runOnUiThread(() -> {
view.release();
Util.makeText(this, "Contents are not passed").show();
Util.println("Contents are not passed");
});
}).start();
} else afterCheck(view, json);
} catch (Throwable e) {
state = 7;
Util.makeText(this, Util.toStr(e)).show();
Util.printStackTrace(e);
Util.println(new String(json, Chsets.UTF8));
}
}
public void afterCheck(Prog view, byte[] json) {
try {
var ms = System.currentTimeMillis();
if(view.isInsert) {
if(insView!=null) insView.release();
insView = view;
try {
var page = avas.get(curAva);
for(var layer : page.layers) for(var src : layer.srcs) if(src.isShow) {
showHides.add(new ShowHide(ms+500, src, 'H'));
src.isShow = false;
}
} catch (Throwable ignored) {}
setContentView(insView);
} else {
if(progView!=null) progView.release();
progView = view;
setContentView(progView);
}
var fOut = new FileOutputStream(Util.programDir + (view.isInsert?"/insert":"/program"));
fOut.write(json);
var spaces = " ".getBytes();
for(int i=0;i<1000; i++) fOut.write(spaces);
fOut.flush();
fOut.getFD().sync();
fOut.close();
state = 5;
System.gc();
Util.println("Init Sync");
syncProg(ms, 0);
if(canAdd) {
choreographer.postFrameCallback(this);
canAdd = false;
}
} catch (Throwable e) {
state = 7;
Util.makeText(this, Util.toStr(e)).show();
Util.printStackTrace(e);
Util.println(new String(json, Chsets.UTF8));
}
}
ArrayList<Prog.Page> avas = new ArrayList<>();
int curAva, curTimes = 1;
long waitTo = Long.MAX_VALUE;
boolean isInsert;
Choreographer choreographer = Choreographer.getInstance();
boolean canAdd = true;
static class ShowHide {
long time;
Runnable run;
Prog.Source src;
char act;
public ShowHide(long time, Prog.Source src, char act) {
this.time = time;
this.src = src;
this.act = act;
}
public ShowHide(long time, Runnable run) {
this.time = time;
this.run = run;
}
}
public LinkedList<ShowHide> showHides = new LinkedList<>();
public HashSet<Prog.Source> showeds = new HashSet<>();
boolean contains(Prog.Source src, char act) {
for(var showHide : showHides) if(showHide.src==src && showHide.act==act) return true;
return false;
}
@Override
public void doFrame(long frameTimeNanos) {
var milli = System.currentTimeMillis();
var iter = showHides.iterator();
while(iter.hasNext()) {
var showHide = iter.next();
if(showHide.time > milli) {
if(showHide.time > milli+400) break;
else continue;
}
if(showHide.act=='H') {
showHide.src.hide();
showeds.remove(showHide.src);
if(! contains(showHide.src, 'S')) showHide.src.release();
iter.remove();
} else if(showHide.act=='S') {
showHide.src.show();
showeds.add(showHide.src);
iter.remove();
} else if(showHide.run!=null) {
showHide.run.run();
iter.remove();
}
}
var iterator = showeds.iterator();
while(iterator.hasNext()) {
var showed = iterator.next();
if(showed.view==null || ! showed.view.isShown()) iterator.remove();
else showed.doEff();
}
boolean noProg = progView == null && insView==null;
if(noProg && showHides.isEmpty()) {
canAdd = true;
return;
}
choreographer.postFrameCallback(this);
canAdd = false;
if(noProg) return;
if(avas.isEmpty()) {
if(milli >= waitTo) {
waitTo = Long.MAX_VALUE;
Util.println("wait sync");
syncProg(milli, 0);
if(insView!=null) for(var call : insView.calls) call.doFrame(milli);
if(progView!=null) for(var call : progView.calls) call.doFrame(milli);
}
return;
}
var lastPage = avas.get(curAva);
if(milli < lastPage.endMilli) lastPage.showHideSrcs(milli, showHides);
else {
for(var layer : lastPage.layers) for(var src : layer.srcs) if(src.isShow) {
showHides.add(new ShowHide(lastPage.endMilli+500, src, 'H'));
src.isShow = false;
}
if(isInsert) {
if(--lastPage.repeatTimes<=0 && ++curAva >= avas.size()) {
var isDiff = milli-lastPage.endMilli>=1000;
syncProg(isDiff ? milli : lastPage.endMilli, milli);
if(insView!=null) for(var call : insView.calls) call.doFrame(milli);
if(progView!=null) for(var call : progView.calls) call.doFrame(milli);
return;
}
} else {
if(waitTo > 0) { //waitTo==0 为点播,不换页
if(curTimes < lastPage.repeatTimes) curTimes++;
else {
curTimes = 1;
if(++curAva >= avas.size()) {
var isDiff = milli-lastPage.endMilli>=1000;
syncProg(isDiff ? milli : lastPage.endMilli, milli);
if(insView!=null) for(var call : insView.calls) call.doFrame(milli);
if(progView!=null) for(var call : progView.calls) call.doFrame(milli);
return;
}
}
} else if(milli-lastPage.endMilli >= 5000) {
Util.println("System Time Changed: "+Util.dateFmt.format(lastPage.endMilli)+" -> "+Util.dateFmt.format(milli));
lastPage.endMilli = milli;
}
}
avas.get(curAva).setMillis(lastPage.endMilli, milli, showHides);
Util.println("curAva: "+curAva+" endMs: "+avas.get(curAva).endMilli);
}
if(isInsert) for(var call : insView.calls) call.doFrame(milli);
else for(var call : progView.calls) call.doFrame(milli);
}
void syncProg(long milli, long cur) {
if(cur==0) cur = milli;
Util.println("\nSyncProg");
waitTo = Long.MAX_VALUE;
avas.clear();
curAva = 0;
curTimes = 1;
isInsert = false;
if(insView!=null) {
for(int i=0; i<insView.pages.size(); i++) if(insView.pages.get(i).repeatTimes <= 0) {
for(var layer : insView.pages.get(i).layers) for(var src : layer.srcs) {
var iii = insView;
showHides.add(new ShowHide(milli + 500, ()->{
iii.removeView(src.view);
if(src.view instanceof Choreographer.FrameCallback) iii.calls.remove((Choreographer.FrameCallback) src.view);
}));
}
insView.pages.remove(i--);
}
if(insView.pages.isEmpty()) {
var iii = insView;
showHides.add(new ShowHide(milli + 500, iii::release));
insView = null;
try {
var fOut = new FileOutputStream(Util.programDir + "/insert");
fOut.write("{}".getBytes());
fOut.flush();
fOut.getFD().sync();
fOut.close();
} catch(Exception e) {
Util.printStackTrace(e);
}
System.gc();
} else {
var dur = 0;
for(var page : insView.pages) if(page.isScheOn(milli+dur)) {
avas.add(page);
dur += page.tDur;
}
if(avas.isEmpty()) for(var page : insView.pages) if(page.sches==null) avas.add(page);
if(! avas.isEmpty()) {
isInsert = true;
avas.get(curAva).setMillis(milli, cur, showHides);
if(! insView.isShown()) setContentView(insView);
state = 6;
Util.println("avas "+avas.size()+" Insert");
return;
}
}
}
if(progView!=null) {
var dur = 0;
for(var page : progView.pages) if(page.isScheOn(milli+dur)) {
avas.add(page);
dur += page.tDur;
}
if(dur==0) for(var page : progView.pages) if(page.sches==null) {
avas.add(page);
dur += page.tDur;
}
if(dur!=0) {
var start = milli / dur * dur;
if(start < milli) {
do {
start += avas.get(curAva++).tDur;
} while(curAva < avas.size() && start<=milli);
start -= avas.get(--curAva).tDur;
Util.println("Sync. dur: "+dur+" milli: "+milli+" start: "+start+" diff: "+(milli - start));
}
avas.get(curAva).setMillis(start, milli, showHides);
if(! progView.isShown()) setContentView(progView);
state = 6;
Util.println("Avas "+avas.size());
return;
}
}
waitTo = milli + 1000;
if(state!=2) {
if(backViewL==null) setContentView(backView);
else {
setContentView(backViewL);
backViewL.loadUrl("file:///android_asset/local_page.html");
}
state = 2;
}
Util.println("No Avas, back");
}
}