package com.xixun.xixunplayer; import static android.view.View.VISIBLE; import android.app.ActivityManager; 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 android.webkit.WebView; import androidx.activity.ComponentActivity; import androidx.annotation.NonNull; import androidx.annotation.OptIn; 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.io.OutputStreamWriter; import java.net.ServerSocket; import java.util.Arrays; import java.util.HashSet; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; import gnph.util.Dates; import gnph.util.IOs; import gnph.util.JSList; import gnph.util.JSMap; import gnph.util.NumFmts; import gnph.util.O; public class MainActivity extends ComponentActivity implements Choreographer.FrameCallback, Runnable { public static MainActivity ins; public Intent environIntent = new Intent(); HashSet environs = new HashSet<>(); BackView backView; Prog progView; long launchMilli = System.currentTimeMillis(); long syncMs; int state; @RequiresApi(api = Build.VERSION_CODES.R) @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); startService(new Intent(this, RestartService.class)); var msg = "---- MainActivity onCreate ---- UI Thread: "+Thread.currentThread().getId(); System.out.println(msg); buf.append(msg).append('\n'); 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 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(); } 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(); var msg = "mkdir: "+program.mkdirs(); System.out.println(msg); buf.append(msg).append('\n'); 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"); buf.append("Bind cardsystem aidl service success\n"); var service = CardService.Stub.asInterface(iBinder); try { Util.isScreenOn = service.isScreenOpen(); buf.append("isScreenOn:").append(Util.isScreenOn).append("\n"); backView = new BackView(MainActivity.this, service.getScreenWidth(), service.getScreenHeight()); state = 5; if(Util.isScreenOn) initProg(); else state = 8; if(progView==null) setContentView(backView); } catch (RemoteException e) { Util.makeText(MainActivity.this, e.toString()).show(); throw new RuntimeException(e); } finally { unbindService(this); } } }; 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"); buf.append("Receive PAUSE_PLAYER\n"); Util.isScreenOn = ! intent.getBooleanExtra("pause", false); buf.append("isScreenOn:").append(Util.isScreenOn).append("\n"); if(! Util.isScreenOn) { state = 8; if(progView!=null) { progView.release(); 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); 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; } } else { waitTo = Long.MAX_VALUE; syncProg(millis); } } catch (Throwable e) { Util.makeText(MainActivity.this, e.toString()).show(); e.printStackTrace(); } } }, new IntentFilter("com.xixun.yzd.REMOTE_CONTROL"), RECEIVER_EXPORTED); new Thread(this).start(); } public boolean delProgFile() { if(progView!=null) { progView.release(); 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; } var view = new Prog(task, this); if(view.getChildCount()==0) { state = 3; return; } if(progView!=null) progView.release(); progView = view; setContentView(progView); buf.append("init sync\n"); syncProg((System.currentTimeMillis()+999)/1000*1000); choreographer.postFrameCallback(this); } catch (FileNotFoundException e) { state = 3; e.printStackTrace(); } catch (Throwable e) { state = 7; Util.makeText(this, e.toString()).show(); e.printStackTrace(); } } Choreographer choreographer = Choreographer.getInstance(); int curAva, curTimes = 1, dur; long waitTo = Long.MAX_VALUE; StringBuffer buf = new StringBuffer(); @Override public void doFrame(long frameTimeNanos) { if(progView == null) return; var milli = System.currentTimeMillis(); if(progView.avas.isEmpty()) { if(milli >= waitTo) { waitTo = Long.MAX_VALUE; buf.append("wait sync\n"); syncProg(milli); } } else { 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); } } page(curAva).setMillis(lastPage.endMilli); buf.append("curAva:").append(curAva).append(" endMs:").append(lastPage.endMilli).append("\n"); } else { for(var layer : lastPage.layers) { for(var ele : layer.eles) { 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(); } } } } } choreographer.postFrameCallback(this); } void syncProg(long milli) { curTimes = 1; progView.avas.clear(); dur = 0; Prog.Page page; for(int i=0; i hases = null; while(true) { var obj = JSMap.from(in); var _type = obj.stnn("_type"); System.out.println("_type: "+_type); if("consult".equals(_type)) { JSList ids = obj.jslist("idList"); hases = new JSList<>(); if(ids!=null) for(int i=0; i { 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 { 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(); } } }