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.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.IBinder; import android.os.StrictMode; import android.view.Choreographer; import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import com.xixun.joey.aidlset.CardService; import com.xixun.xy.conn.aidl.ConnService; import net.lingala.zip4j.ZipFile; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.net.ServerSocket; import java.util.HashSet; import gnph.util.Chsets; import gnph.util.IOs; import gnph.util.JSList; import gnph.util.JSMap; public class MainActivity extends Activity implements Choreographer.FrameCallback, Runnable { public static MainActivity ins; public Intent environIntent = new Intent(); HashSet environs = new HashSet<>(); BackView backView; Prog progView, insView; long launchMilli = System.currentTimeMillis(); long syncMs; int state; @RequiresApi(api = Build.VERSION_CODES.R) @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); var msg = "==== MainActivity onCreate ==== UI Thread: "+Thread.currentThread().getId(); Util.println(msg); ins = this; startService(new Intent(this, RestartService.class)); StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitAll().build()); if(ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) init(); else { Util.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 public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if(requestCode==999 && grantResults.length > 0 && grantResults[0]==PackageManager.PERMISSION_GRANTED && backView==null) init(); } ConnService connService; @SuppressLint("UnspecifiedRegisterReceiverFlag") public void init() { var dir = Build.VERSION.SDK_INT < Build.VERSION_CODES.R ? Environment.getExternalStorageDirectory().getAbsolutePath() + "/XixunPlayer" : getExternalFilesDir(null).getAbsolutePath(); var msg = "---- dir "+dir; Util.println(msg); Util.programDir = dir + "/program"; Util.backImgFile = dir + "/background"; var program = new File(Util.programDir); if(program.isFile()) program.delete(); msg = "---- mkdir: "+program.mkdirs(); Util.println(msg); var cardConn = new ServiceConnection() { public void onServiceDisconnected(ComponentName name) { Util.println("Disconnected cardsystem aidl service"); } public void onServiceConnected(ComponentName name, IBinder iBinder) { unbindService(this); Util.println("Bind cardsystem aidl service success"); var service = CardService.Stub.asInterface(iBinder); try { Util.isScreenOn = service.isScreenOpen(); Util.screenWidth = service.getScreenWidth(); Util.screenHeight = service.getScreenHeight(); Util.println(" IsScreenOn: "+Util.isScreenOn+" screen: "+Util.screenWidth+" x "+Util.screenHeight); setContentView(backView = new BackView(MainActivity.this, Util.screenWidth, Util.screenHeight)); state = 5; if(Util.isScreenOn) initProg(); else state = 8; } catch (Exception e) { Util.makeText(MainActivity.this, Util.toStr(e)).show(); Util.printStackTrace(e); } } }; var intent = new Intent("com.xixun.joey.aidlset.SettingsService"); intent.setPackage("com.xixun.joey.cardsystem"); bindService(intent, cardConn, Context.BIND_AUTO_CREATE); var connConn = new ServiceConnection() { public void onServiceDisconnected(ComponentName name) { Util.println("Disconnected xy.conn aidl service"); connService = null; } public void onServiceConnected(ComponentName name, IBinder iBinder) { unbindService(this); Util.println("Bind xy.conn aidl service success"); connService = ConnService.Stub.asInterface(iBinder); try { Util.serverURL = connService.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); } } }; intent = new Intent("xixun.intent.action.CONNECTION_INFO"); intent.setPackage("com.xixun.xy.conn"); bindService(intent, connConn, Context.BIND_AUTO_CREATE); registerReceiver(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; if(insView!=null) { insView.release(); insView = null; } if(progView!=null) { progView.release(); progView = null; } setContentView(backView); } else if(progView==null && insView==null) initProg(); } }, new IntentFilter("com.xixun.action.PAUSE_PLAYER")); registerReceiver(new BroadcastReceiver(){ @Override public void onReceive(Context context, Intent intent) { Util.println("Receive CHANGE_COMPANYID"); if(connService!=null) { try { Util.serverURL = connService.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); } } } }, new IntentFilter("com.xixun.joey.CHANGE_COMPANYID")); registerReceiver(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")); registerReceiver(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.isShown() || code > progView.pages.size()) return; if(! progView.avas.isEmpty()) { progView.curAva().hide(); if(progView.avas.size()==1 && progView.avas.get(0)==code-1) for(var call : progView.calls) call.doFrame(System.currentTimeMillis()); } var millis = (System.currentTimeMillis()+999)/1000*1000; if(code > 0) { progView.avas.clear(); progView.avas.add(code-1); progView.curAva = 0; progView.curTimes = 1; progView.waitTo = 0; //点播 var page = progView.curAva(); page.setMillis(millis); if(state != 6) { setContentView(progView); state = 6; } } else { progView.waitTo = Long.MAX_VALUE; syncProg(millis); } } catch (Throwable e) { Util.makeText(MainActivity.this, Util.toStr(e)).show(); Util.printStackTrace(e); } } }, new IntentFilter("com.xixun.yzd.REMOTE_CONTROL")); var intentFilter = new IntentFilter(Intent.ACTION_MEDIA_MOUNTED); intentFilter.addDataScheme("file"); registerReceiver(new BroadcastReceiver(){ long lastMs; final char[] pass = {'8','8','8'}; @Override public void onReceive(Context context, Intent intent) { var path = intent.getData().getPath(); Util.println("\nMEDIA_MOUNTED path: "+path); var ms = System.currentTimeMillis(); if(ms-lastMs<1000) return; lastMs = ms; Util.makeText(MainActivity.this, "MEDIA_MOUNTED path: "+path+"\nImporting 正在导入 ...").show(); new Thread(()->{ try { var zip = new ZipFile(path+"/program.zip"); if(zip.isEncrypted()) zip.setPassword(pass); long size = 0; ByteArrayOutputStream jsonOut = null; var headers = zip.getFileHeaders(); for(var header : headers) { size += header.getUncompressedSize(); if("program".equals(header.getFileName())) IOs.writeClose(jsonOut = new ByteArrayOutputStream(), zip.getInputStream(header)); } if(jsonOut==null) { Util.println("No program File"); runOnUiThread(() -> Util.makeText(MainActivity.this, "No program File").show()); return; } if(size==0) { Util.println("zip size is 0"); runOnUiThread(() -> Util.makeText(MainActivity.this, "zip size is 0").show()); return; } Util.deleteFiles(size, null); for(var header : headers) if(! "program".equals(header.getFileName())) { Util.println(" name: " + header.getFileName()); var fOut = new FileOutputStream(Util.programDir + "/" + header.getFileName()); IOs.writeCloseIn(fOut, zip.getInputStream(header)); fOut.flush(); fOut.getFD().sync(); fOut.close(); } var json = jsonOut.toByteArray(); runOnUiThread(() -> { Util.println("Import Succeed"); Util.makeText(MainActivity.this, "Import Succeed 导入成功\nDon't shut down within 1 minute, otherwise the program may be lost\n不要在 1 分钟内关机,否则节目可能丢失").show(); initProg(json); }); } catch (Exception e) { Util.printStackTrace(e); runOnUiThread(() -> Util.makeText(MainActivity.this, Util.toStr(e)).show()); } }).start(); } }, intentFilter); new Thread(this).start(); } public void stopProg() { if(insView!=null) { insView.release(); insView = null; } if(progView!=null) { progView.release(); progView = null; } setContentView(backView); } 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 out = new FileOutputStream(Util.programDir+"/program"); out.write("{}".getBytes()); out.flush(); out.getFD().sync(); out.close(); } catch (Throwable ignored) { } return ok; } public void initProg() { state = 1; try { Util.println("\nParse Insert Prog 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.getChildCount()!=0) setContentView(insView = view); } } catch(FileNotFoundException ignored) { } catch(Throwable e) { state = 7; Util.printStackTrace(e); } try { Util.println("\nParse Prog Json"); var root = JSMap.fromClose(new BufferedInputStream(new FileInputStream(Util.programDir + "/program"))); var task = root.jsmap("task"); 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.getChildCount()==0) 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"); syncProg((System.currentTimeMillis()+999)/1000*1000); 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.getChildCount()==0) { if(! view.isInsert) state = 7; Util.println(" Error: ChildCount==0\n"); Util.println(new String(json, Chsets.UTF8)); return; } if(view.isInsert) { if(insView!=null) insView.release(); insView = view; 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); fOut.flush(); fOut.getFD().sync(); fOut.close(); state = 5; Util.println("Init Sync"); syncProg((System.currentTimeMillis()+999)/1000*1000); 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)); } } Choreographer choreographer = Choreographer.getInstance(); boolean canAdd = true; @Override public void doFrame(long frameTimeNanos) { if(progView == null && insView==null) { canAdd = true; return; } var milli = System.currentTimeMillis(); if(insView!=null && ! insView.avas.isEmpty()) { var lastPage = insView.curAva(); if(milli >= lastPage.endMilli) { lastPage.hide(); if(--lastPage.repeatTimes<=0 && ++insView.curAva >= insView.avas.size()) { var isDiff = milli-lastPage.endMilli>=1000; Util.println("isDiff: "+isDiff+" endMs: "+lastPage.endMilli+" millis:"+milli); syncProg(isDiff ? milli : lastPage.endMilli); choreographer.postFrameCallback(this); canAdd = false; if(insView!=null) for(var call : insView.calls) call.doFrame(milli); return; } insView.curAva().setMillis(lastPage.endMilli); Util.println("curAva: "+insView.curAva+" endMs: "+insView.curAva().endMilli); } else lastPage.showHideSrcs(milli); choreographer.postFrameCallback(this); canAdd = false; for(var call : insView.calls) call.doFrame(milli); return; } if(progView.avas.isEmpty()) { if(milli >= progView.waitTo) { progView.waitTo = Long.MAX_VALUE; Util.println("wait sync"); syncProg(milli); } } else { var lastPage = progView.curAva(); if(milli >= lastPage.endMilli) { lastPage.hide(); if(progView.waitTo > 0) { //waitTo==0 为点播,不换页 if(progView.curTimes < lastPage.repeatTimes) progView.curTimes++; else { progView.curTimes = 1; if(++progView.curAva >= progView.avas.size()) { var isDiff = milli-lastPage.endMilli>=1000; if(Util.buf.length()>1000000) Util.buf.replace(0, 100000, ""); Util.println("isDiff: "+isDiff+" endMs: "+lastPage.endMilli+" millis:"+milli); syncProg(isDiff ? milli : lastPage.endMilli); if(! progView.isShown() || progView.avas.isEmpty()) Util.println("after. No Avas"); else Util.println("after. curAva: "+progView.curAva+" endMs: "+progView.curAva().endMilli); choreographer.postFrameCallback(this); canAdd = false; for(var call : progView.calls) call.doFrame(milli); return; } } } progView.curAva().setMillis(lastPage.endMilli); Util.println("curAva: "+progView.curAva+" endMs: "+progView.curAva().endMilli); } else lastPage.showHideSrcs(milli); } choreographer.postFrameCallback(this); canAdd = false; for(var call : progView.calls) call.doFrame(milli); } void syncProg(long milli) { if(insView!=null) { insView.avas.clear(); for(int i=0; i Util.makeText(MainActivity.this, Util.toStr(e)).show()); Util.printStackTrace(e); } } } catch (Throwable e) { MainActivity.ins.runOnUiThread(() -> Util.makeText(MainActivity.this, Util.toStr(e)).show()); Util.printStackTrace(e); } } }