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 reces = new ArrayList<>(); ArrayList services = new ArrayList<>(); public Intent environIntent = new Intent(); HashSet 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; 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()); if(progView==null && insView==null) runOnUiThread(this::initProg); } @Override protected void onPause() { super.onPause(); Util.println(" ==<< MainActivity onPause << "+hashCode()); state = 8; stopProg(); } 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名称"; System.out.println("当前是 WiFi 网络:" + value); break; case ETHERNET: System.out.println("当前是有线网络"); break; case CELLULAR: // 细分蜂窝网络类型(2G/3G/4G) String cellularSubType = NetworkTypeUtil.getCellularSubType(MainActivity.this); value = cellularSubType; System.out.println("当前是蜂窝网络:" + cellularSubType); break; case NONE: System.out.println("无网络连接"); break; case UNKNOWN: System.out.println("未知网络类型(如蓝牙、VPN)"); 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(); } 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.isAudioGain = true; break; case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK: Util.println("AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK"); Util.isAudioGain = true; break; case AudioManager.AUDIOFOCUS_LOSS: Util.println("AUDIOFOCUS_LOSS"); Util.isAudioGain = false; break; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: Util.println("AUDIOFOCUS_LOSS_TRANSIENT"); //短暂失去音频焦点,暂停播放等待又一次获得音频焦点 Util.isAudioGain = false; break; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: Util.println("AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK"); //减少声音就可以 Util.isAudioGain = false; break; } View view; if(Util.isAudioGain) { if(insView!=null) { for(int cc=0; cc(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("\nParse Prog 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; Util.println("Init Sync"); var ms = System.currentTimeMillis(); if(demand==0 || progView==null) syncProg(ms, 0); else { 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) { try { Util.println("\nParse Prog Json"); 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 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 showHides = new LinkedList<>(); public HashSet 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{ 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"); } }