Android/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/MainActivity.java
2025-07-28 19:14:00 +08:00

731 lines
30 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.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.StrictMode;
import android.view.Choreographer;
import android.view.View;
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 gnph.util.Chsets;
import gnph.util.JSList;
import gnph.util.JSMap;
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;
Prog progView, insView;
long launchMilli = System.currentTimeMillis();
long syncMs;
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
}, 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;
}
ins = null;
for(var rece : reces) unregisterReceiver(rece);
for(var service : services) unbindService(service);
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());
}
@Override
protected void onRestart() {
super.onRestart();
Util.println(" ==>> MainActivity onRestart >> "+hashCode());
}
@Override
protected void onResume() {
super.onResume();
Util.println(" ==>> MainActivity onResume >> "+hashCode());
}
@Override
protected void onPause() {
super.onPause();
Util.println(" ==<< MainActivity onPause << "+hashCode());
}
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 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;
} 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;
if(insView!=null) {
insView.release();
insView = null;
}
if(progView!=null) {
progView.release();
progView = null;
}
setContentView(backView);
} else if(progView==null && insView==null) MainActivity.ins.runOnUiThread(() -> initProg());
System.gc();
}
}, 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()) {
avas.get(curAva).hide();
if(code > 0 && avas.get(curAva)==progView.pages.get(code-1)) 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);
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);
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();
}
}
public void stopProg() {
avas.clear();
curAva = 0;
curTimes = 1;
if(insView!=null) {
insView.release();
insView = null;
}
if(progView!=null) {
progView.release();
progView = null;
}
setContentView(backView);
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 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);
}
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.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");
var ms = System.currentTimeMillis();
if(demand==0 || progView==null) syncProg((ms+999)/1000*1000, 0);
else {
avas.clear();
var page = progView.pages.get(demand-1);
avas.add(page);
curAva = 0;
curTimes = 1;
waitTo = 0; //点播
page.setMillis(ms, ms);
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.getChildCount()==0) {
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 {
if(view.isInsert) {
if(insView!=null) insView.release();
insView = view;
try { avas.get(curAva).hide();} 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);
fOut.flush();
fOut.getFD().sync();
fOut.close();
state = 5;
System.gc();
Util.println("Init Sync");
syncProg((System.currentTimeMillis()+999)/1000*1000, 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;
@Override
public void doFrame(long frameTimeNanos) {
if(progView == null && insView==null) {
canAdd = true;
return;
}
choreographer.postFrameCallback(this);
canAdd = false;
var milli = System.currentTimeMillis();
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);
else {
lastPage.hide();
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);
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) {
insView.removeView(src.view);
if(src.view instanceof Choreographer.FrameCallback) insView.calls.remove((Choreographer.FrameCallback) src.view);
}
insView.pages.remove(i--);
}
if(insView.pages.isEmpty()) {
insView.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);
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;
syncMs = milli;
Util.println("Sync. dur: "+dur+" milli: "+milli+" start: "+start+" diff: "+(milli - start));
}
avas.get(curAva).setMillis(start, milli);
if(! progView.isShown()) setContentView(progView);
state = 6;
Util.println("Avas "+avas.size());
return;
}
}
waitTo = milli + 1000;
if(state!=2) {
setContentView(backView);
state = 2;
}
Util.println("No Avas, back");
}
}