954 lines
41 KiB
Java
954 lines
41 KiB
Java
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");
|
||
}
|
||
} |