diff --git a/XixunPlayer/.idea/.name b/XixunPlayer/.idea/.name
new file mode 100644
index 0000000..d3f190d
--- /dev/null
+++ b/XixunPlayer/.idea/.name
@@ -0,0 +1 @@
+XixunPlayer
\ No newline at end of file
diff --git a/XixunPlayer/app/build.gradle b/XixunPlayer/app/build.gradle
index 03a4fb4..08f9194 100644
--- a/XixunPlayer/app/build.gradle
+++ b/XixunPlayer/app/build.gradle
@@ -11,7 +11,7 @@ android {
minSdk 21
targetSdk 34
versionCode 1
- versionName "2.1.21"
+ versionName "2.2-beta8-NTimes"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
@@ -34,10 +34,11 @@ android {
dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1'
- implementation 'androidx.media3:media3-exoplayer:1.2.0'
-// implementation 'tv.danmaku.ijk.media:ijkplayer-java:0.8.8'
-// implementation 'tv.danmaku.ijk.media:ijkplayer-arm64:0.8.8'
implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.25'
+ implementation 'com.aliyun:imageaudit20191230:2.0.6'
+ implementation 'com.github.wseemann:FFmpegMediaMetadataRetriever-core:1.0.19'
+ implementation 'com.github.wseemann:FFmpegMediaMetadataRetriever-native-armeabi-v7a:1.0.19'
+ implementation 'com.github.wseemann:FFmpegMediaMetadataRetriever-native-arm64-v8a:1.0.19'
implementation files('libs/gnph.jar')
implementation files('libs/zip4j-2.10.0.jar')
implementation files('libs/xixun_card_settings_1.2.4.jar')
diff --git a/XixunPlayer/app/src/main/AndroidManifest.xml b/XixunPlayer/app/src/main/AndroidManifest.xml
index 3459ff0..47da6ae 100644
--- a/XixunPlayer/app/src/main/AndroidManifest.xml
+++ b/XixunPlayer/app/src/main/AndroidManifest.xml
@@ -16,14 +16,15 @@
android:largeHeap="true"
android:allowBackup="true"
android:usesCleartextTraffic="true"
- android:launchMode="singleTop"
+ android:launchMode="singleTask"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen" >
@@ -32,6 +33,7 @@
@@ -41,7 +43,8 @@
@@ -50,6 +53,7 @@
diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Server.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/AIDLService.java
similarity index 76%
rename from XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Server.java
rename to XixunPlayer/app/src/main/java/com/xixun/xixunplayer/AIDLService.java
index 9db2be9..9994042 100644
--- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Server.java
+++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/AIDLService.java
@@ -23,7 +23,7 @@ import gnph.util.JSList;
import gnph.util.JSMap;
import gnph.util.URLConn;
-public class Server extends Service {
+public class AIDLService extends Service {
@Override
public IBinder onBind(Intent intent) {
@@ -106,7 +106,7 @@ public class Server extends Service {
@SuppressLint("ResourceType")
@Override
public String executeJosnCommand(String jsonstr) throws RemoteException {
- Util.println("Server executeJsonCommand ..."+jsonstr);//{"_type":"DeleteTask","id":"652522a0e81d1e000009201a","sendTo":"yzd-player"}
+ Util.println("AIDL executeJsonCommand ..."+jsonstr);//{"_type":"DeleteTask","id":"652522a0e81d1e000009201a","sendTo":"yzd-player"}
String commandId = null;
try {
var jsonBytes = jsonstr.getBytes(StandardCharsets.UTF_8);
@@ -115,7 +115,10 @@ public class Server extends Service {
commandId = json.stnn("id");
if(_type.equals("PlayXixunTask") || _type.equals("PlayProgramTask")) {
var preDownloadURL = json.str("preDownloadURL");
- if(preDownloadURL==null) preDownloadURL = Util.serverURL+"file/download?id=";
+ if(preDownloadURL==null) {
+ Util.println(" serverURL "+Util.serverURL);
+ preDownloadURL = Util.serverURL+"file/download?id=";
+ }
var task = json.jsmap("task");
JSList jpages = task.jslist("items");
int proSize = 0;
@@ -169,35 +172,54 @@ public class Server extends Service {
int finalProSize = proSize;
String finalCommandId = commandId;
var notificationURL = json.str("notificationURL");
+ var downMs = System.currentTimeMillis();
+ Util.downId = downMs;
new Thread(()->{
Util.deleteFiles(finalProSize, hases);
for(var needDown : needDowns) {
int cnt = 0;
for(var src : needDown.srcs) {
- try {
- var in = new URLConn(src.url).in();
- var fout = new FileOutputStream(Util.programDir+"/"+src.filename);
- IOs.writeCloseIn(fout, in);
- fout.flush();
- fout.getFD().sync();
- fout.close();
- } catch (Exception e) {
- Util.printStackTrace(e);
+ for(int t=0; ; t++) {
+ try {
+ Util.println("download "+src.url);
+ var conn = new URLConn(src.url);
+ if(t>0) {
+ var len = new File(Util.programDir+"/"+src.filename).length();
+ conn.setHeader("Range", "bytes="+len+"-");
+ Util.println(" Range "+len);
+ }
+ var input = conn.in();
+ var code = conn.hconn().getResponseCode();
+ var fout = new FileOutputStream(Util.programDir+"/"+src.filename, code==206);
+ IOs.writeCloseIn(fout, input);
+ fout.flush();
+ fout.getFD().sync();
+ fout.close();
+ if(code==200 || code==206) break;
+ else Util.println(" error "+code+" "+conn.hconn().getResponseMessage()+" size: "+new File(src.filename).length()+" "+src.filename);
+ } catch (Throwable e) {
+ Util.println(Util.toStr(e));
+ }
+ if(t>=7200 || Util.downId!=downMs) return;
+ try {
+ Thread.sleep(5000);
+ } catch (Exception ignored) {}
}
+
cnt++;
var progress = cnt*100/needDown.srcs.size();
if(cnt != needDown.srcs.size()) {
if(notificationURL==null) {
var intent = new Intent("xixun.intent.action.REPLY");
intent.putExtra("reply", new TaskProgressReply(finalCommandId, needDown.prog, progress, 500, 3));
- MainActivity.ins.sendBroadcast(intent);
+ sendBroadcast(intent);
} else {
try {
new URLConn(notificationURL).timeout(5000).writeJson(new JSMap(
"commandId", finalCommandId,
"taskItemId", needDown.prog,
"progress", progress).toStr()).read();
- } catch (Exception e) {
+ } catch (Throwable e) {
Util.printStackTrace(e);
}
}
@@ -206,19 +228,38 @@ public class Server extends Service {
if(notificationURL==null) {
var intent = new Intent("xixun.intent.action.REPLY");
intent.putExtra("reply", new TaskProgressReply(finalCommandId, needDown.prog, 100, 400, 0));
- MainActivity.ins.sendBroadcast(intent);
+ sendBroadcast(intent);
} else {
try {
new URLConn(notificationURL).timeout(5000).writeJson(new JSMap(
"commandId", finalCommandId,
"taskItemId", needDown.prog,
"progress", 100).toStr()).read();
- } catch (Exception e) {
+ } catch (Throwable e) {
Util.printStackTrace(e);
}
}
}
- MainActivity.ins.runOnUiThread(() -> MainActivity.ins.initProg(jsonBytes));
+ if(Util.downId!=downMs) return;
+ var acti = MainActivity.ins;
+ if(acti!=null) acti.runOnUiThread(() -> acti.initProg(jsonBytes));
+ else {
+ try {
+ var fOut = new FileOutputStream(Util.programDir + "/program");
+ fOut.write(jsonBytes);
+ fOut.flush();
+ fOut.getFD().sync();
+ fOut.close();
+ } catch (Throwable e) {
+ Util.printStackTrace(e);
+ }
+ var intent = new Intent(AIDLService.this, MainActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ AIDLService.this.startActivity(intent);
+ }
+ var intent = new Intent("com.xixun.AccessibilityService");
+ intent.putExtra("newProgram", "platform");
+ sendBroadcast(intent);
}).start();
return new JSMap(
"_type", "Success",
@@ -227,6 +268,7 @@ public class Server extends Service {
"commandId", commandId
).toString();
} else if(_type.equals("DeleteTask")) {
+ Util.downId = 0;
MainActivity.ins.runOnUiThread(() -> MainActivity.ins.delProgFile());
return new JSMap(
"_type", "DataCallback",
@@ -280,6 +322,21 @@ public class Server extends Service {
"commandId", commandId,
"task", task
).toString();
+ } else if(_type.equalsIgnoreCase("UploadPlayLogs")) {
+ var url = json.str("url");
+ Util.logOn = url!=null;
+ Util.cfg.put("logUploadUrl", url);
+ Util.cfg.put("logUploadInterval", json.intg("interval", 60));
+ try (var fOut = new FileOutputStream(Util.programDir + "/cfg")) {
+ Util.cfg.write(fOut);
+ fOut.flush();
+ fOut.getFD().sync();
+ }
+ return new JSMap(
+ "_type", "Success",
+ "cardId", Util.getCardId(),
+ "commandId", commandId
+ ).toString();
}
return new JSMap(
"_type", "Error",
diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/BootCompletedReceiver.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/BootCompletedReceiver.java
index a575f42..7433834 100644
--- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/BootCompletedReceiver.java
+++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/BootCompletedReceiver.java
@@ -7,10 +7,11 @@ import android.content.Intent;
public class BootCompletedReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- Util.println("BootCompletedReceiver onReceive ---- "+intent.getAction());
+ Util.println("==== BootCompletedReceiver onReceive >> "+intent.getAction());
+ Util.initDir(context);
if(MainActivity.ins!=null) return;
- intent = new Intent(context, MainActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- context.startActivity(intent);
+ var inten = new Intent(context, MainActivity.class);
+ inten.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(inten);
}
}
\ No newline at end of file
diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/LogThread.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/LogThread.java
new file mode 100644
index 0000000..5404dde
--- /dev/null
+++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/LogThread.java
@@ -0,0 +1,98 @@
+package com.xixun.xixunplayer;
+
+import android.content.ContentValues;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import gnph.util.Digest;
+import gnph.util.JSList;
+import gnph.util.JSMap;
+import gnph.util.URLConn;
+
+public class LogThread extends Thread {
+
+ public static ConcurrentLinkedQueue rows = new ConcurrentLinkedQueue<>();
+
+ Digest md5 = Digest.md5();
+
+ public LogThread() {
+
+ }
+
+ @Override
+ public void run() {
+ long next = 0, nextDel = 0;
+ while(true) {
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException ignored) {
+ }
+ if(! Util.logOn) continue;
+ var ms = System.currentTimeMillis();
+ var row = rows.poll();
+ if(row!=null) {
+ try (var db = Util.openHelper.getWritableDatabase()) {
+ db.beginTransaction();
+ if(ms>=nextDel) {
+ nextDel = (ms - nextDel < 3000 ? nextDel : ms) + 3600000L;
+ var num = db.delete("log", "time<"+(ms-720*3600000L), null);
+ Util.println(" del overdue "+num);
+ }
+ var rowid = db.insert("log", null, row);
+ db.setTransactionSuccessful();
+ db.endTransaction();
+ Util.println(" rowid "+rowid);
+ } catch (Exception e) {
+ Util.printStackTrace(e);
+ }
+ }
+ if(ms>=next) {
+ next = (ms-next < 3000 ? next : ms) + Util.cfg.intg("logUploadInterval", 60)*1000;
+ var rows = new JSList();
+ try (var db = Util.openHelper.getReadableDatabase()) {
+ var cursor = db.rawQuery("select * from log order by time", null);
+ while(cursor.moveToNext()) {
+ var pid = cursor.getString(2);
+ if(pid==null || pid.length()!=24) pid = md5.hexStr(cursor.getString(3).getBytes()).substring(0,24);
+ rows.add(new JSMap(
+ "beginAt", cursor.getLong(0),
+ "duration", cursor.getLong(1)/1000,
+ "pid", pid,
+ "name", cursor.getString(3),
+ "type", cursor.getString(4),
+ "lat", cursor.getDouble(5),
+ "lng", cursor.getDouble(6)
+ ));
+ }
+ }
+ Util.println(" Log Cnt "+rows.size());
+ if(! rows.isEmpty()) {
+ Util.println(rows.last().toStr());
+ try {
+ var conn = new URLConn(Util.cfg.stnn("logUploadUrl")).timeout(15000);
+ conn.addHeader("Card-Id", Util.getCardId());
+ conn.forJson();
+ rows.write(conn.out());
+ var resp = conn.read();
+ Util.println(" Upload resp: "+resp);
+ if("OK".equals(resp) || "".equals(resp)) {
+ try (var db = Util.openHelper.getWritableDatabase()) {
+ db.beginTransaction();
+ var num = db.delete("log", "time<="+rows.last().lng("beginAt"), null);
+ db.setTransactionSuccessful();
+ db.endTransaction();
+ Util.println(" del Upled "+num);
+ } catch (Exception e) {
+ Util.printStackTrace(e);
+ }
+ }
+ } catch (Exception e) {
+ Util.printStackTrace(e);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/MainActivity.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/MainActivity.java
index ca51114..9dbe7aa 100644
--- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/MainActivity.java
+++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/MainActivity.java
@@ -9,9 +9,10 @@ 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.Environment;
import android.os.IBinder;
import android.os.StrictMode;
import android.view.Choreographer;
@@ -25,27 +26,24 @@ 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.ArrayList;
import java.util.HashSet;
+import java.util.LinkedList;
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 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;
@@ -59,14 +57,11 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ins = this;
- Util.println("==>> MainActivity onCreate >>>> this "+hashCode()+" UI Thread: "+Thread.currentThread().getId());
- try{
- if(RestartService.ins==null) startService(new Intent(this, RestartService.class));
- } catch(Throwable e) {
- Util.printStackTrace(e);
- }
+ 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;
if(ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) init();
else {
@@ -84,7 +79,7 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
- if(backView!=null) return;
+ if(Util.programDir!=null) return;
if(requestCode==999 && grantResults.length > 0 && grantResults[0]==PackageManager.PERMISSION_GRANTED) init();
else {
Util.println("---- Request Permission Failed");
@@ -106,11 +101,13 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
progView = null;
}
ins = null;
- for(var rece : reces) {
- try {
- unregisterReceiver(rece);
- } catch (Throwable ignored){
- }
+ 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();
}
@@ -149,17 +146,39 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
ServiceConnection connCard;
@SuppressLint("UnspecifiedRegisterReceiverFlag")
public void init() {
- var dir = Build.VERSION.SDK_INT < Build.VERSION_CODES.R ? Environment.getExternalStorageDirectory().getAbsolutePath() + "/XixunPlayer" : getExternalFilesDir(null).getAbsolutePath();
- Util.println("\n---- Init ----\n dir "+dir);
- Util.programDir = dir + "/program";
- Util.backImgFile = dir + "/background";
- setContentView(backView = new BackView(MainActivity.this));
- state = 5;
-
- var program = new File(Util.programDir);
- if(program.isFile()) program.delete();
- Util.println(" mkdir: "+program.mkdirs());
+ 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;
@@ -168,6 +187,7 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
public void onServiceConnected(ComponentName name, IBinder iBinder) {
unbindService(this);
+ services.remove(this);
Util.println("->-> AIDL Service cardsystem Connected");
serviCard = CardService.Stub.asInterface(iBinder);
try {
@@ -187,6 +207,7 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
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) {
@@ -195,6 +216,7 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
}
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 {
@@ -214,6 +236,7 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
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;
@@ -277,34 +300,53 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
}, 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.isShown() || code > progView.pages.size()) return;
+ 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);
}
- ms = (ms+999)/1000*1000;
- if(code > 0) {
+ 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);
+ page.setMillis(ms, ms, showHides);
if(state != 6) {
setContentView(progView);
state = 6;
}
- } else {
- waitTo = Long.MAX_VALUE;
- syncProg(ms, 0);
+ }
+ 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();
@@ -314,92 +356,35 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
}, new IntentFilter("com.xixun.yzd.REMOTE_CONTROL"));
reces.add(rece);
- var intentFilter = new IntentFilter(Intent.ACTION_MEDIA_MOUNTED);
- intentFilter.addDataScheme("file");
- registerReceiver(rece = 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(()->{
+ if(Util.custom==Util.Custom.Yishi) {
+ new Thread(()->{
+ while(true) {
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));
+ 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));
}
- 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) {
+ } catch (Throwable e) {
+ var ins = MainActivity.ins;
+ if(ins!=null) ins.runOnUiThread(() -> Util.makeText(ins, Util.toStr(e)).show());
Util.printStackTrace(e);
- runOnUiThread(() -> Util.makeText(MainActivity.this, Util.toStr(e)).show());
}
- }).start();
- }
- }, intentFilter);
- reces.add(rece);
-
- new Thread(this).start();
-// 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();
+ }
+ }).start();
+ }
}
public void stopProg() {
@@ -442,22 +427,24 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
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);
+ 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.getChildCount()==0) Util.println(" Error: ChildCount==0\n");
+ if(view.pages.isEmpty()) Util.println(" Error: ChildCount==0\n");
else setContentView(progView = view);
}
} catch(FileNotFoundException e) {
@@ -470,7 +457,21 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
if(insView!=null || progView!=null) {
state = 5;
Util.println("Init Sync");
- syncProg((System.currentTimeMillis()+999)/1000*1000, 0);
+ 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, showHides);
+ if(state != 6) {
+ setContentView(progView);
+ state = 6;
+ }
+ }
if(canAdd) {
choreographer.postFrameCallback(this);
canAdd = false;
@@ -492,15 +493,37 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
task = new JSMap("items", new JSList<>(new JSMap("_program", root)));
}
var view = new Prog(task, this);
- if(view.getChildCount()==0) {
+ 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 {
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();
@@ -535,16 +558,55 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
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<>();
+ 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) {
- if(progView == null && insView==null) {
+ var milli = System.currentTimeMillis();
+ var iter = showHides.iterator();
+ while(iter.hasNext()) {
+ var showHide = iter.next();
+ if(showHide.time > milli) {
+ if(showHide.time >= milli+100) break;
+ else continue;
+ }
+ if(showHide.act=='H') {
+ showHide.src.hide();
+ if(! contains(showHide.src, 'S')) showHide.src.release();
+ iter.remove();
+ } else if(showHide.act=='S') {
+ showHide.src.show();
+ iter.remove();
+ } else if(showHide.run!=null) showHide.run.run();
+ }
+ boolean noProg = progView == null && insView==null;
+ if(noProg && showHides.isEmpty()) {
canAdd = true;
return;
}
choreographer.postFrameCallback(this);
canAdd = false;
- var milli = System.currentTimeMillis();
+ if(noProg) return;
if(avas.isEmpty()) {
if(milli >= waitTo) {
waitTo = Long.MAX_VALUE;
@@ -556,9 +618,12 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
return;
}
var lastPage = avas.get(curAva);
- if(milli < lastPage.endMilli) lastPage.showHideSrcs(milli);
+ if(milli < lastPage.endMilli) lastPage.showHideSrcs(milli, showHides);
else {
- lastPage.hide();
+ for(var layer : lastPage.layers) for(var src : layer.srcs) {
+ showHides.add(new ShowHide(lastPage.endMilli+1000, src, 'H'));
+ src.isShow = false;
+ }
if(isInsert) {
if(--lastPage.repeatTimes<=0 && ++curAva >= avas.size()) {
var isDiff = milli-lastPage.endMilli>=1000;
@@ -580,9 +645,12 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
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);
+ 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);
@@ -592,6 +660,7 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
void syncProg(long milli, long cur) {
if(cur==0) cur = milli;
Util.println("\nSyncProg");
+ waitTo = Long.MAX_VALUE;
avas.clear();
curAva = 0;
curTimes = 1;
@@ -599,13 +668,17 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
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()) {
- insView.release();
+ var iii = insView;
+ showHides.add(new ShowHide(milli + 1000, iii::release));
insView = null;
try {
var fOut = new FileOutputStream(Util.programDir + "/insert");
@@ -626,7 +699,7 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
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);
+ avas.get(curAva).setMillis(milli, cur, showHides);
if(! insView.isShown()) setContentView(insView);
state = 6;
Util.println("avas "+avas.size()+" Insert");
@@ -654,7 +727,7 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
syncMs = milli;
Util.println("Sync. dur: "+dur+" milli: "+milli+" start: "+start+" diff: "+(milli - start));
}
- avas.get(curAva).setMillis(start, milli);
+ avas.get(curAva).setMillis(start, milli, showHides);
if(! progView.isShown()) setContentView(progView);
state = 6;
Util.println("Avas "+avas.size());
@@ -668,26 +741,4 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac
}
Util.println("No Avas, back");
}
-
- public void run() {
- try {
- var serverSocket = new ServerSocket(3333);
- while(true) {
- try {
- Util.println("\nAccepting ...");
- var socket = serverSocket.accept();
- new TCPThread(MainActivity.ins, socket).start();
- Util.println("\nAccepted");
- } catch (Throwable e) {
- var ins = MainActivity.ins;
- if(ins!=null) ins.runOnUiThread(() -> Util.makeText(ins, Util.toStr(e)).show());
- Util.printStackTrace(e);
- }
- }
- } catch (Throwable e) {
- var msg = e.getMessage();
- if(msg==null || ! msg.contains("EADDRINUSE")) MainActivity.ins.runOnUiThread(() -> Util.makeText(MainActivity.ins, Util.toStr(e)).show());
- Util.printStackTrace(e);
- }
- }
}
\ No newline at end of file
diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/MainService.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/MainService.java
new file mode 100644
index 0000000..3b97e8a
--- /dev/null
+++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/MainService.java
@@ -0,0 +1,128 @@
+package com.xixun.xixunplayer;
+
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.IBinder;
+
+import net.lingala.zip4j.ZipFile;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.ArrayList;
+
+import gnph.util.IOs;
+
+public class MainService extends Service {
+
+ static MainService ins;
+ ArrayList reces = new ArrayList<>();
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ ins = this;
+ Util.println("==>> MainService onCreate >>>> "+hashCode()+" Thread: "+Thread.currentThread().getId());
+ TCPThread.startServer(3333);
+ if(MainActivity.ins==null) {
+ var intent = new Intent(this, MainActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ }
+
+ BroadcastReceiver rece;
+ var intentFilter = new IntentFilter(Intent.ACTION_MEDIA_MOUNTED);
+ intentFilter.addDataScheme("file");
+ registerReceiver(rece = new BroadcastReceiver(){
+ long lastMs;
+ final char[] pass = {'8','8','8'};
+ @Override
+ public void onReceive(Context context, Intent intent) { // in UI Thread
+ var path = intent.getData().getPath();
+ Util.println("\nMEDIA_MOUNTED path: "+path);
+ var ms = System.currentTimeMillis();
+ if(ms-lastMs<1000) return;
+ lastMs = ms;
+ Util.downId = 0;
+ var acti = MainActivity.ins;
+ Util.makeText(MainService.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");
+ if(acti!=null) acti.runOnUiThread(() -> Util.makeText(acti, "No program File").show());
+ return;
+ }
+ if(size==0) {
+ Util.println("zip size is 0");
+ if(acti!=null) acti.runOnUiThread(() -> Util.makeText(acti, "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();
+ Util.println("Import Succeed");
+ if(acti!=null) acti.runOnUiThread(() -> {
+ Util.makeText(acti, "Import Succeed 导入成功").show();
+ acti.initProg(json);
+ });
+ else {
+ var fOut = new FileOutputStream(Util.programDir + "/program");
+ fOut.write(json);
+ fOut.flush();
+ fOut.getFD().sync();
+ fOut.close();
+ var inten = new Intent(MainService.this, MainActivity.class);
+ inten.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(inten);
+ }
+ var inten = new Intent("com.xixun.AccessibilityService");
+ inten.putExtra("newProgram", "USB");
+ sendBroadcast(inten);
+ } catch (Exception e) {
+ Util.printStackTrace(e);
+ if(acti!=null) acti.runOnUiThread(() -> Util.makeText(acti, Util.toStr(e)).show());
+ }
+ }).start();
+ }
+ }, intentFilter);
+ reces.add(rece);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ Util.println("==<< MainService onDestroy <<<< this "+hashCode());
+ ins = null;
+ for(var rece : reces) {
+ try {
+ unregisterReceiver(rece);
+ } catch (Throwable ignored){
+ }
+ }
+ System.gc();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ throw new UnsupportedOperationException("Not yet implemented");
+ }
+}
\ No newline at end of file
diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Prog.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Prog.java
index 4764d1c..52cca2d 100644
--- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Prog.java
+++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Prog.java
@@ -1,6 +1,7 @@
package com.xixun.xixunplayer;
import android.annotation.SuppressLint;
+import android.content.ContentValues;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -22,6 +23,7 @@ import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.SimpleTimeZone;
import java.util.TimeZone;
@@ -54,6 +56,10 @@ public class Prog extends AbsLayout {
}
var width = partObj.intg("width");
var height = partObj.intg("height");
+ if(Util.custom==Util.Custom.Yishi) {
+ if(width==0) width = Util.screenWidth;
+ if(height==0) height = Util.screenHeight;
+ }
AbsLayout box;
if(partLengths==null || partLengths.size() <= 1) box = this;
else {
@@ -65,7 +71,7 @@ public class Prog extends AbsLayout {
addView(mask, isVertical ? new AbsLayout.LayoutParams(0, len0, width, height - len0) : new AbsLayout.LayoutParams(len0, 0, width - len0, height));
int x = 0, y = 0;
for(int i=1; i videoMap = new HashMap<>();
+ var waitAudio = pageMap.bool("waitAudio");
+ HashMap videoMap = new HashMap<>();
for(int ll=layers.size()-1; ll>=0; ll--) {
var layer = new Layer();
layer.isLoop = layers.get(ll).bool("repeat");
@@ -115,8 +124,13 @@ public class Prog extends AbsLayout {
var notAudio = ! src.type.equals("Audio");
if(notAudio) {
if(geo.width<=0 || geo.height<=0) {
- Util.println("\nError: width or height is 0. _type: "+src.type+" width: "+geo.width+" height: "+geo.height);
- continue;
+ if(Util.custom==Util.Custom.Yishi) {
+ geo.width = width;
+ geo.height = height;
+ } else {
+ Util.println("\nError: width or height is 0. _type: "+src.type+" width: "+geo.width+" height: "+geo.height);
+ continue;
+ }
}
if(box != this && ((geo.y>=height && height>0) || (geo.x>=width && width>0))) {
Util.println("\nError: y>=height or x>=width. _type: "+src.type+" width: "+width+" height: "+height+" x: "+geo.x+" y: "+geo.y);
@@ -128,7 +142,7 @@ public class Prog extends AbsLayout {
src.endTime = src.startTime + dur;
if(bdEnd < src.endTime) bdEnd = src.endTime;
if(layer.dur < src.endTime) layer.dur = src.endTime;
- if(notAudio) {
+ if(notAudio || waitAudio) {
if(page.sDur < src.endTime) page.sDur = src.endTime;
} else if(page.audioDur < src.endTime) page.audioDur = src.endTime;
@@ -239,20 +253,35 @@ public class Prog extends AbsLayout {
var key = isLive ? url : id + src.startTime + src.endTime;
var exist = videoMap.get(key);
if(exist!=null) {
- var geoOld = (AbsLayout.LayoutParams) exist.getLayoutParams();
- if(geo.width*geo.height > geoOld.width*geoOld.height) {
- exist.setLayoutParams(geo);
- geo = geoOld;
+ if(geo.width * geo.height > exist.geo.width * exist.geo.height) {
+ var aaa = exist.geo;
+ exist.geo = geo;
+ geo = aaa;
}
src.view = new SrcCopy(context, exist);
((SrcCopy) src.view).scaleX = 0;
} else {
- src.view = new SrcVideo(context, isLive ? url : Util.programDir+"/"+id, source.intg("vol", 100) / 100.0f, dur, source.bool("useHW"), isLive);
- videoMap.put(key, src.view);
+ videoMap.put(key, src);
+ src.typ = 'V';
+ src.path = isLive ? url : Util.programDir+"/"+id;
+ src.vol = source.intg("vol", 100) / 100.0f;
+ src.dur = dur;
+ src.useSW = source.bool("useSW");
+ src.geo = geo;
+ src.box = source.bool("isPreSplit") ? this : box;
+ layer.srcs.add(src);
+ src = new Source();
}
} else if(src.type.equals("Audio")) {
if(id==null) continue;
- src.view = new SrcVideo(context, Util.programDir + "/" +id, source.intg("vol", 100) / 100.0f, dur, false, false);
+ src.typ = 'A';
+ src.path = Util.programDir + "/" +id;
+ src.vol = source.intg("vol", 100) / 100.0f;
+ src.dur = dur;
+ src.geo = geo;
+ src.box = box;
+ layer.srcs.add(src);
+ src = new Source();
} else if(src.type.equals("Scroll")) {
JSList imgs = source.jslist("imgs");
if(imgs.isEmpty()) continue;
@@ -407,7 +436,7 @@ public class Prog extends AbsLayout {
if(src.view==null) continue;
src.view.setVisibility(GONE);
src.view.setLayoutParams(geo);
- box.addView(src.view);
+ (source.bool("isPreSplit") ? this : box).addView(src.view);
layer.srcs.add(src);
src = new Source();
}
@@ -470,6 +499,7 @@ public class Prog extends AbsLayout {
}
public static class Source {
+ AbsLayout.LayoutParams geo;
View view;
float alpha = 1, rotate, scaleX = 1, scaleY = 1;
long startMilli, endMilli = Long.MAX_VALUE;
@@ -479,7 +509,19 @@ public class Prog extends AbsLayout {
Uri uri;
TextToSpeech tts;
String text;
- boolean isEntryRand, isExitRand;
+ boolean isShow, isEntryRand, isExitRand;
+ char typ;
+ AbsLayout box;
+ String path;
+ float vol;
+ int dur;
+ boolean useSW;
+
+ public Source() {
+ }
+ public Source(View view) {
+ this.view = view;
+ }
void show() {
if(view.getVisibility()==VISIBLE) return;
@@ -494,9 +536,27 @@ public class Prog extends AbsLayout {
void hide() {
if(view.getVisibility()!=VISIBLE) return;
view.setVisibility(GONE);
- if(uri!=null) ((GifImageView) view).setImageURI(uri);
+ if(view instanceof GifImageView) ((GifImageView) view).setImageURI(uri);
if(tts!=null && tts.isSpeaking()) tts.stop();
}
+ void prepare(long seek) {
+ var ms = System.currentTimeMillis();
+ if(view==null) {
+ var video = new SrcVideo(box.getContext(), path, vol, dur, seek, useSW);
+ video.setLayoutParams(geo);
+ video.setVisibility(GONE);
+ box.addView(view = video);
+ }
+ ms = System.currentTimeMillis() - ms;
+ Util.println(" prepare ms "+ms);
+ }
+ void release() {
+ if(view instanceof SrcVideo) {
+ ((SrcVideo)view).release();
+ box.removeView(view);
+ view = null;
+ }
+ }
void doEff() {
var w = view.getLayoutParams().width;
var h = view.getLayoutParams().height;
@@ -665,7 +725,7 @@ public class Prog extends AbsLayout {
}
public static class Page {
- String name;
+ String id, name, type;
ArrayList layers = new ArrayList<>();
ArrayList sches;
long endMilli = Long.MAX_VALUE;
@@ -675,7 +735,7 @@ public class Prog extends AbsLayout {
for(var layer : layers) for(var src : layer.srcs) src.hide();
}
- void setMillis(long start, long cur) {
+ void setMillis(long start, long cur, List shows) {
endMilli = start + sDur;
for(var layer : layers) {
if(layer.isLoop) layer.endMilli = start + layer.dur;
@@ -683,21 +743,37 @@ public class Prog extends AbsLayout {
src.endMilli = start + src.endTime;
src.startMilli = start + src.startTime;
if(src.startTime == 0) {
- if(src.view instanceof SrcVideo) ((SrcVideo)src.view).seekTo = cur - src.startMilli;
- src.show();
+ src.prepare(cur - src.startMilli);
+ shows.add(new MainActivity.ShowHide(src.startMilli+1000, src, 'S'));
+ src.isShow = true;
}
}
}
+ if(Util.logOn) {
+ var values = new ContentValues();
+ values.put("time", cur);
+ values.put("dur", sDur);
+ values.put("id", id);
+ values.put("name", name);
+ values.put("type", type);
+ values.put("lat", Util.lat);
+ values.put("lng", Util.lng);
+ LogThread.rows.add(values);
+ }
}
- void showHideSrcs(long milli) {
+ void showHideSrcs(long milli, List shows) {
for(var layer : layers) {
for(var src : layer.srcs) {
- if(src.view.getVisibility()==VISIBLE) {
- if(milli >= src.endMilli) src.hide();
+ if(src.isShow) {
+ if(milli >= src.endMilli) {
+ shows.add(new MainActivity.ShowHide(src.endMilli+1000, src, 'H'));
+ src.isShow = false;
+ }
else src.doEff();
} else if(milli < src.endMilli && milli >= src.startMilli) {
- if(src.view instanceof SrcVideo) ((SrcVideo)src.view).seekTo = milli - src.startMilli;
- src.show();
+ src.prepare(milli - src.startMilli);
+ shows.add(new MainActivity.ShowHide(src.startMilli+1000, src, 'S'));
+ src.isShow = true;
}
}
if(milli >= layer.endMilli) {
@@ -705,8 +781,14 @@ public class Prog extends AbsLayout {
for(var src : layer.srcs) {
src.endMilli += layer.dur;
src.startMilli += layer.dur;
- if(src.startTime > 0) src.hide();
- else src.show();
+ if(src.startTime > 0) {
+ shows.add(new MainActivity.ShowHide(src.startMilli+1000, src, 'H'));
+ src.isShow = false;
+ } else {
+ src.prepare(0);
+ shows.add(new MainActivity.ShowHide(src.startMilli+1000, src, 'S'));
+ src.isShow = true;
+ }
}
}
}
diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/RestartService.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/RestartService.java
deleted file mode 100644
index 57ec86a..0000000
--- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/RestartService.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.xixun.xixunplayer;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-
-public class RestartService extends Service {
-
- static RestartService ins;
-
- @Override
- public void onCreate() {
- super.onCreate();
- ins = this;
- Util.println("==>> RestartService onCreate >>>> "+hashCode());
- if(MainActivity.ins!=null) return;
- var intent = new Intent(this, MainActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(intent);
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- Util.println("==<< RestartService onDestroy <<<< this "+hashCode());
- ins = null;
- System.gc();
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- throw new UnsupportedOperationException("Not yet implemented");
- }
-}
\ No newline at end of file
diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcCopy.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcCopy.java
index 2f38af5..9771186 100644
--- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcCopy.java
+++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcCopy.java
@@ -1,36 +1,38 @@
package com.xixun.xixunplayer;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.view.View;
import androidx.annotation.NonNull;
+@SuppressLint("ViewConstructor")
public class SrcCopy extends View {
- View view;
+ Prog.Source source;
float scaleX = 1, scaleY = 1;
- public SrcCopy(Context context, View view) {
+ public SrcCopy(Context context, Prog.Source source) {
super(context);
- this.view = view;
+ this.source = source;
}
@Override
protected void onDraw(@NonNull Canvas canvas) {
super.onDraw(canvas);
- if(view==null) return;
+ if(source==null || source.view==null) return;
if(scaleX==0) {
- if(view.getWidth()!=0&&getWidth()!=0) {
- scaleX = getWidth() / (float) view.getWidth();
- scaleY = getHeight() / (float) view.getHeight();
+ if(source.view.getWidth()!=0 && getWidth()!=0) {
+ scaleX = getWidth() / (float) source.view.getWidth();
+ scaleY = getHeight() / (float) source.view.getHeight();
} else {
invalidate();
return;
}
}
if(scaleX!=1 || scaleY!=1) canvas.scale(scaleX, scaleY);
- view.draw(canvas);
+ source.view.draw(canvas);
invalidate();
}
}
diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcCountdown.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcCountdown.java
index 8b7d4a9..42583e0 100644
--- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcCountdown.java
+++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcCountdown.java
@@ -5,6 +5,7 @@ import android.graphics.Color;
import android.view.Choreographer;
import android.webkit.WebView;
+import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import gnph.util.JSMap;
@@ -13,9 +14,11 @@ import gnph.util.NumFmts;
@SuppressLint("ViewConstructor")
public class SrcCountdown extends WebView implements Choreographer.FrameCallback {
+ static DecimalFormat fmt00_0 = NumFmts.newDecFmt("00.0");
+
long targetTime;
String html, lineHeight, prefix, suffix;
- boolean isUp, hasDay, hasHour, hasMin, hasSec;
+ boolean isUp, hasDay, hasHour, hasMin, hasSec, hasFPS;
public SrcCountdown(Prog prog, JSMap json) {
super(prog.getContext());
@@ -28,16 +31,20 @@ public class SrcCountdown extends WebView implements Choreographer.FrameCallback
setVerticalScrollBarEnabled(false);
setHorizontalScrollBarEnabled(false);
setInitialScale(100);
- isUp = json.bool("isUp");
html = json.stnn("html");
+ hasFPS = html.contains("%FPS");
+ isUp = json.bool("isUp");
lineHeight = json.str("lineHeight");
prefix = "";
suffix = "";
try {
- var aaa = html.substring(0, 16).trim();
- if(aaa.startsWith("")) {
+ suffix = "";
+ html = html.substring(6);
+ } else if(start.startsWith("", "text/html", "UTF-8", null);
}
- int caseThen(int val, int cas, int then) {
- return val==cas ? then : val;
- }
long lastSec;
diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcSensor.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcSensor.java
index 15b0127..50fef31 100644
--- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcSensor.java
+++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcSensor.java
@@ -51,6 +51,8 @@ public class SrcSensor extends WebView implements IntentReceiver, Choreographer.
prefix = "";
prog.calls.add(this);
diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideo.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideo.java
index 8814ee0..01ed85c 100644
--- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideo.java
+++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideo.java
@@ -7,11 +7,6 @@ import android.view.Surface;
import android.view.TextureView;
import androidx.annotation.NonNull;
-import androidx.annotation.OptIn;
-import androidx.media3.common.MediaItem;
-import androidx.media3.common.util.UnstableApi;
-import androidx.media3.exoplayer.ExoPlayer;
-import androidx.media3.exoplayer.SeekParameters;
import tv.danmaku.ijk.media.player.IMediaPlayer;
import tv.danmaku.ijk.media.player.IjkMediaPlayer;
@@ -19,58 +14,36 @@ import tv.danmaku.ijk.media.player.IjkMediaPlayer;
@SuppressLint("ViewConstructor")
public class SrcVideo extends TextureView implements TextureView.SurfaceTextureListener {
- String path;
- float vol;
- int dur;
IjkMediaPlayer ijkPlayer;
- ExoPlayer exoPlayer;
+ Surface surface;
long bitRate;
- long seekTo;
- boolean isLive;
- public SrcVideo(Context context, String path, float vol, int dur, boolean useHW, boolean isLive) {
+ public SrcVideo(Context context, String path, float vol, int dur, long seek, boolean useSW) {
super(context);
- this.path = path;
- this.vol = vol;
- this.dur = dur;
- this.isLive = isLive;
- initIjk(useHW);
- }
-
- @OptIn(markerClass = UnstableApi.class)
- void initExo() {
- exoPlayer = new ExoPlayer.Builder(getContext()).build();
- exoPlayer.setMediaItem(MediaItem.fromUri(path));
- exoPlayer.setRepeatMode(ExoPlayer.REPEAT_MODE_ONE);
- exoPlayer.setSeekParameters(SeekParameters.CLOSEST_SYNC);
- exoPlayer.setVolume(vol);
- exoPlayer.setVideoTextureView(this);
- exoPlayer.prepare();
- }
- void initIjk(boolean useHW) {
+ setSurfaceTextureListener(this);
+ Util.println(" video new");
ijkPlayer = new IjkMediaPlayer();
- ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-avc", 1);
- ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-hevc", 1);
+ if(! useSW) {
+ ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-avc", 1);
+ ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-hevc", 1);
+ }
ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 0);
ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzeduration", 1);
- ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 0);
+ ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 12);
try {
- setSurfaceTextureListener(this);
ijkPlayer.setDataSource(path);
ijkPlayer.setLooping(true);
ijkPlayer.setVolume(vol, vol);
ijkPlayer.setOnPreparedListener((IMediaPlayer var1)->{
ijkPlayer.setOnPreparedListener(null);
bitRate = ijkPlayer.getBitRate();
- if(bitRate > 12000000) {
- setSurfaceTextureListener(null);
- release();
- initExo();
- } else {
- var diff = dur - ijkPlayer.getDuration();
- if(diff>0 && diff<=1000) ijkPlayer.setLooping(false);
+ var diff = dur - ijkPlayer.getDuration();
+ if(diff>0 && diff<=1000) ijkPlayer.setLooping(false);
+ if(seek>=1000) {
+ ijkPlayer.seekTo(seek);
+ Util.println(" Seek "+seek);
}
- if(isShown()) start();
+ if(isShown()) ijkPlayer.start();
});
ijkPlayer.setOnErrorListener((IMediaPlayer var1, int var2, int var3)->{
Util.println(" Video Error: "+var1+" "+var2+" "+var3);
@@ -83,72 +56,42 @@ public class SrcVideo extends TextureView implements TextureView.SurfaceTextureL
ijkPlayer = null;
}
}
+
@Override
public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) {
- Util.println(" onSurfaceTextureAvailable "+(ijkPlayer==null?"ijkPlayer==null":""));
- if(ijkPlayer!=null) ijkPlayer.setSurface(new Surface(surface));
+ Util.println(" SurfaceTexture Available "+(ijkPlayer==null?"ijkPlayer==null":""));
+ this.surface = new Surface(surface);
+ ijkPlayer.setSurface(this.surface);
}
@Override
public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) {}
@Override
public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) {
+ Util.println(" SurfaceTexture Destroyed");
return false;
}
@Override
public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {}
- void start() {
- if(ijkPlayer!=null) {
- ijkPlayer.seekTo(seekTo);
- ijkPlayer.start();
- } else if(exoPlayer!=null) {
- if(exoPlayer.getPlaybackState()==ExoPlayer.STATE_IDLE) exoPlayer.prepare();
- exoPlayer.seekTo(seekTo);
- exoPlayer.play();
- }
- }
- void pause() {
- if(ijkPlayer!=null) {
- ijkPlayer.pause();
- ijkPlayer.seekTo(0);
- }
- if(exoPlayer!=null && exoPlayer.isPlaying()) {
- exoPlayer.pause();
- exoPlayer.seekToDefaultPosition();
- }
- }
void release() {
+ Util.println(" video release");
if(ijkPlayer!=null) {
ijkPlayer.release();
ijkPlayer = null;
}
- if(exoPlayer!=null) {
- exoPlayer.release();
- exoPlayer = null;
+ if(surface!=null) {
+ surface.release();
+ surface = null;
}
}
- String getState() {
- if(exoPlayer!=null) {
- var state = exoPlayer.getPlaybackState();
- if(state==ExoPlayer.STATE_IDLE) return "STATE_IDLE";
- if(state==ExoPlayer.STATE_BUFFERING) return "STATE_BUFFERING";
- if(state==ExoPlayer.STATE_READY) return "STATE_READY";
- if(state==ExoPlayer.STATE_ENDED) return "STATE_ENDED";
- }
- return "null";
- }
-
@Override
public void onVisibilityAggregated(boolean isVisible) {
super.onVisibilityAggregated(isVisible);
- if(isVisible) {
- start();
- if(isLive) ijkPlayer.setVolume(vol, vol);
- } else if(isLive) ijkPlayer.setVolume(0, 0);
+ if(isVisible) ijkPlayer.start();
else {
- seekTo = 0;
- pause();
+ ijkPlayer.pause();
+ ijkPlayer.seekTo(0);
}
}
}
\ No newline at end of file
diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideoSurface.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideoSurface.java
deleted file mode 100644
index e95a8d1..0000000
--- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideoSurface.java
+++ /dev/null
@@ -1,138 +0,0 @@
-package com.xixun.xixunplayer;
-
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.OptIn;
-import androidx.media3.common.MediaItem;
-import androidx.media3.common.util.UnstableApi;
-import androidx.media3.exoplayer.ExoPlayer;
-import androidx.media3.exoplayer.SeekParameters;
-
-import tv.danmaku.ijk.media.player.IMediaPlayer;
-import tv.danmaku.ijk.media.player.IjkMediaPlayer;
-
-@SuppressLint("ViewConstructor")
-public class SrcVideoSurface extends SurfaceView implements SurfaceHolder.Callback {
-
- String path;
- float vol;
- IjkMediaPlayer ijkPlayer;
- ExoPlayer exoPlayer;
- long bitRate;
- boolean isLive;
-
- public SrcVideoSurface(Context context, String path, float vol, boolean isLive) {
- super(context);
- this.path = path;
- this.vol = vol;
- this.isLive = isLive;
- initIjk();
- }
-
- @OptIn(markerClass = UnstableApi.class)
- void initExo() {
- exoPlayer = new ExoPlayer.Builder(getContext()).build();
- exoPlayer.setMediaItem(MediaItem.fromUri(path));
- exoPlayer.setRepeatMode(ExoPlayer.REPEAT_MODE_ONE);
- exoPlayer.setSeekParameters(SeekParameters.CLOSEST_SYNC);
- exoPlayer.setVolume(vol);
- exoPlayer.setVideoSurfaceView(this);
- exoPlayer.prepare();
- }
- void initIjk() {
- ijkPlayer = new IjkMediaPlayer();
- ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-avc", 1);
- ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 0);
- ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzeduration", 1);
- ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48);
- try {
- getHolder().addCallback(this);
- ijkPlayer.setDataSource(path);
- ijkPlayer.setLooping(true);
- ijkPlayer.setVolume(vol, vol);
- ijkPlayer.setOnPreparedListener((IMediaPlayer var1)->{
- ijkPlayer.setOnPreparedListener(null);
- bitRate = ijkPlayer.getBitRate();
- if(bitRate > 12000000) {
- getHolder().removeCallback(this);
- release();
- initExo();
- }
- if(isShown()) start();
- });
- ijkPlayer.setOnErrorListener((IMediaPlayer var1, int var2, int var3)->{
- Util.println(" Video Error: "+var1+" "+var2+" "+var3);
- return true;
- });
- ijkPlayer.prepareAsync();
- } catch (Throwable e) {
- Util.makeText(getContext(), Util.toStr(e)).show();
- Util.printStackTrace(e);
- ijkPlayer = null;
- }
- }
- @Override
- public void surfaceCreated(@NonNull SurfaceHolder holder) {
- Util.println(" surfaceCreated "+(ijkPlayer==null?"ijkPlayer==null":""));
- if(ijkPlayer!=null) ijkPlayer.setDisplay(holder);
- }
- @Override
- public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {}
- @Override
- public void surfaceDestroyed(@NonNull SurfaceHolder holder) {}
-
- void start() {
- if(ijkPlayer!=null) {
- ijkPlayer.seekTo(0);
- ijkPlayer.start();
- } else if(exoPlayer!=null) {
- if(exoPlayer.getPlaybackState()==ExoPlayer.STATE_IDLE) exoPlayer.prepare();
- exoPlayer.play();
- }
- }
- void pause() {
- if(ijkPlayer!=null) {
- ijkPlayer.pause();
- ijkPlayer.seekTo(0);
- }
- if(exoPlayer!=null && exoPlayer.isPlaying()) {
- exoPlayer.pause();
- exoPlayer.seekToDefaultPosition();
- }
- }
- void release() {
- if(ijkPlayer!=null) {
- ijkPlayer.release();
- ijkPlayer = null;
- }
- if(exoPlayer!=null) {
- exoPlayer.release();
- exoPlayer = null;
- }
- }
-
- String getState() {
- if(exoPlayer!=null) {
- var state = exoPlayer.getPlaybackState();
- if(state==ExoPlayer.STATE_IDLE) return "STATE_IDLE";
- if(state==ExoPlayer.STATE_BUFFERING) return "STATE_BUFFERING";
- if(state==ExoPlayer.STATE_READY) return "STATE_READY";
- if(state==ExoPlayer.STATE_ENDED) return "STATE_ENDED";
- }
- return "null";
- }
-
- @Override
- public void onVisibilityAggregated(boolean isVisible) {
- super.onVisibilityAggregated(isVisible);
- if(isVisible) {
- start();
- if(isLive) ijkPlayer.setVolume(vol, vol);
- } else if(isLive) ijkPlayer.setVolume(0, 0);
- else pause();
- }
-}
\ No newline at end of file
diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVisitor.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVisitor.java
index 7348eec..52356a7 100644
--- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVisitor.java
+++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVisitor.java
@@ -5,6 +5,8 @@ import android.graphics.Color;
import android.view.Choreographer;
import android.webkit.WebView;
+import java.nio.charset.StandardCharsets;
+
import gnph.util.JSMap;
import gnph.util.URLConn;
@@ -22,6 +24,10 @@ public class SrcVisitor extends WebView implements Choreographer.FrameCallback {
html = json.stnn("html").replace("%{yesterday.", "%{arr.-1.");
lineHeight = json.str("lineHeight");
url = json.str("url");
+ if(url!=null) {
+ var token = json.str("token");
+ if(token!=null) url += "?dataKey="+token;
+ }
prefix = "";
@@ -31,8 +37,13 @@ public class SrcVisitor extends WebView implements Choreographer.FrameCallback {
public void refresh() {
try {
var htm = html;
- try {
- var res = JSMap.fromClose(new URLConn(url).in()).jsmap("data");
+ try {
+ var resp = URLConn.send(url);
+ var data = JSMap.from(resp.getBytes(StandardCharsets.UTF_8)).jsmap("data");
+ if(data==null) {
+ Util.println("SrcVisitor No data: "+resp);
+ return;
+ }
int lastEnd = 0, appendIdx = 0;
var buf = new StringBuilder(htm.length()*3/2);
int start;
@@ -47,7 +58,7 @@ public class SrcVisitor extends WebView implements Choreographer.FrameCallback {
lastEnd = fde+1;
if(fde == fds) continue;
var fd = htm.substring(fds, fde);
- var replace = res.str(fd);
+ var replace = data.str(fd);
if(replace!=null) {
buf.append(htm, appendIdx, start).append(replace);
appendIdx = lastEnd;
diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcWeb.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcWeb.java
index 3b1350a..18e92e8 100644
--- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcWeb.java
+++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcWeb.java
@@ -43,18 +43,36 @@ public class SrcWeb extends WebView implements Choreographer.FrameCallback {
});
loadUrl(url = json.str("url"));
refresh = json.intg("refreshSec")*1000;
- if(refresh>0 && url!=null) prog.calls.add(this);
+ if(Util.custom==Util.Custom.Yishi) {
+ prog.calls.add(this);
+ } else {
+ if(refresh>0 && url!=null) prog.calls.add(this);
+ }
nextMs = System.currentTimeMillis() + refresh;
}
long nextMs;
-
+ int w, h;
@Override
public void doFrame(long ms) {
if(! isShown()) return;
- if(ms>=nextMs) {
- nextMs = ms + refresh;
- loadUrl(url);
+
+ if(Util.custom==Util.Custom.Yishi) {
+ if(refresh>0 && url!=null && ms>=nextMs) {
+ nextMs = ms + refresh;
+ loadUrl(url);
+ }
+ if(Util.screenWidth!=w || Util.screenHeight!=h) {
+ w = Util.screenWidth;
+ h = Util.screenHeight;
+ Util.println(" w "+w+" h "+h);
+ setLayoutParams(new AbsLayout.LayoutParams(0, 0, w, h));
+ }
+ } else {
+ if(ms>=nextMs) {
+ nextMs = ms + refresh;
+ loadUrl(url);
+ }
}
}
}
diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/TCPThread.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/TCPThread.java
index fc1da68..4b90d19 100644
--- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/TCPThread.java
+++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/TCPThread.java
@@ -3,14 +3,14 @@ package com.xixun.xixunplayer;
import static android.view.View.VISIBLE;
import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.os.StatFs;
+import android.view.WindowManager;
import android.webkit.WebView;
-import androidx.annotation.OptIn;
-import androidx.media3.common.util.UnstableApi;
-
import net.lingala.zip4j.ZipFile;
import java.io.ByteArrayOutputStream;
@@ -20,6 +20,7 @@ import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
+import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.text.SimpleDateFormat;
@@ -37,17 +38,41 @@ import gnph.util.NumFmts;
import gnph.util.O;
public class TCPThread extends Thread {
+
+ public static void startServer(int port) {
+ new Thread(()->{
+ try {
+ var serverSocket = new ServerSocket(port);
+ while(true) {
+ try {
+ Util.println("\nAccepting ...");
+ var socket = serverSocket.accept();
+ new TCPThread(MainActivity.ins, socket).start();
+ Util.println("\nAccepted");
+ } catch (Throwable e) {
+ var ins = MainActivity.ins;
+ if(ins!=null) ins.runOnUiThread(() -> Util.makeText(ins, Util.toStr(e)).show());
+ Util.printStackTrace(e);
+ }
+ }
+ } catch (Throwable e) {
+ var msg = e.getMessage();
+ if(msg==null || ! msg.contains("in use")) MainActivity.ins.runOnUiThread(() -> Util.makeText(MainActivity.ins, Util.toStr(e)).show());
+ Util.printStackTrace(e);
+ }
+ }).start();
+ }
+
MainActivity main;
Socket socket;
InputStream in;
OutputStream out;
- SimpleDateFormat fmt = new SimpleDateFormat("MM-dd HH:mm:ss");
+ SimpleDateFormat fmt = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
public TCPThread(MainActivity main, Socket socket) {
this.main = main;
this.socket = socket;
}
- @OptIn(markerClass = UnstableApi.class) @Override
public void run() {
try {
socket.setSoTimeout(600000);
@@ -73,6 +98,7 @@ public class TCPThread extends Thread {
new JSMap("_type", _type, "existed", existed).write(out);
}
} else if("proStart".equals(_type)) {
+ Util.downId = 0;
Util.deleteFiles(obj.intg("proSize"), existed);
} else if("fileStart".equals(_type)) {
var size = obj.intg("size");
@@ -106,17 +132,28 @@ public class TCPThread extends Thread {
});
new JSMap("success", true).write(out);
} else if("proEnd".equals(_type)) {
- new JSMap("success", progJson!=null).write(out);
+ new JSMap("_type", "AckSuccess", "success", progJson!=null).write(out);
if(progJson!=null) {
var json = progJson.toByteArray();
progJson = null;
- if(main!=null) main.runOnUiThread(() -> main.initProg(json));
+ var acti = MainActivity.ins;
+ if(acti!=null) acti.runOnUiThread(() -> acti.initProg(json));
else {
var fOut = new FileOutputStream(Util.programDir + "/program");
fOut.write(json);
fOut.flush();
fOut.getFD().sync();
fOut.close();
+ if(MainService.ins!=null) {
+ var intent = new Intent(MainService.ins, MainActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ MainService.ins.startActivity(intent);
+ }
+ }
+ if(MainService.ins!=null) {
+ var intent = new Intent("com.xixun.AccessibilityService");
+ intent.putExtra("newProgram", "TCP");
+ MainService.ins.sendBroadcast(intent);
}
}
} else if("playZipTask".equals(_type)) {
@@ -149,9 +186,26 @@ public class TCPThread extends Thread {
fOut.close();
}
var json = jsonOut.toByteArray();
- if(main!=null) main.runOnUiThread(()->main.initProg(json));
- Util.println(" Succeed");
+ var acti = MainActivity.ins;
+ if(acti!=null) acti.runOnUiThread(()->acti.initProg(json));
+ else {
+ var fOut = new FileOutputStream(Util.programDir + "/program");
+ fOut.write(json);
+ fOut.flush();
+ fOut.getFD().sync();
+ fOut.close();
+ if(MainService.ins!=null) {
+ var intent = new Intent(MainService.ins, MainActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ MainService.ins.startActivity(intent);
+ }
+ }
new JSMap("success", true).write(out);
+ if(MainService.ins!=null) {
+ var intent = new Intent("com.xixun.AccessibilityService");
+ intent.putExtra("newProgram", "TCP");
+ MainService.ins.sendBroadcast(intent);
+ }
}
} catch (Exception e) {
Util.printStackTrace(e);
@@ -159,15 +213,18 @@ public class TCPThread extends Thread {
if(main!=null) main.runOnUiThread(() -> Util.makeText(main, Util.toStr(e)).show());
}
} else if("DelPrograms".equals(_type)) {
- var latch = new CountDownLatch(1);
+ Util.downId = 0;
var ok = new AtomicBoolean(false);
- if(main!=null) main.runOnUiThread(() -> {
- ok.set(main.delProgFile());
- latch.countDown();
- });
- try {
- latch.await();
- } catch (InterruptedException ignored) {}
+ if(main!=null) {
+ var latch = new CountDownLatch(1);
+ main.runOnUiThread(() -> {
+ ok.set(main.delProgFile());
+ latch.countDown();
+ });
+ try {
+ latch.await();
+ } catch (InterruptedException ignored) {}
+ }
new JSMap("success", ok.get()).write(out);
} else if("getProgramName".equals(_type)) {
try {
@@ -186,6 +243,15 @@ public class TCPThread extends Thread {
MainActivity.ins.backView.invalidate();
});
new JSMap("success", new File(Util.backImgFile).delete()).write(out);
+ } else if("Config".equals(_type)) {
+ var entrySet = obj.entrySet();
+ for(var entry : entrySet) if(! entry.getKey().equals("_type")) Util.cfg.put(entry.getKey(), entry.getValue());
+ try (var fOut = new FileOutputStream(Util.programDir + "/cfg")) {
+ Util.cfg.write(fOut);
+ fOut.flush();
+ fOut.getFD().sync();
+ }
+ new JSMap("success", new File(Util.backImgFile).delete()).write(out);
} else if("getPlayerState".equals(_type)) {
var state = main==null ? 8 : main.state;
var descs = Util.getState(state);
@@ -200,6 +266,10 @@ public class TCPThread extends Thread {
var dur = 0;
if(main==null) writer.append("\nAPP is Closed\n");
else {
+ try {
+ var packageInfo = main.getPackageManager().getPackageInfo(main.getPackageName(), 0);
+ writer.append("ver: ").append(packageInfo.versionName).append("\n");
+ } catch (Exception ignored) {}
if(main.progView!=null) for(var page : main.progView.pages) dur += page.tDur;
writer.append("ProgSend: ").append(fmt.format(new File(Util.programDir + "/program").lastModified())).append(" ProgDur: ").append(String.valueOf(dur)).append("ms");
writer.append(" Size Avas: ").append(String.valueOf(main.avas.size())).append(" Pages: ").append(main.progView==null ? "null" : String.valueOf(main.progView.pages.size()));
@@ -212,6 +282,13 @@ public class TCPThread extends Thread {
else writer.append("Page End: ").append(fmt.format(main.avas.get(main.curAva).endMilli)).append(" CurPage: ").append(String.valueOf(main.curAva)).append(" ").append(String.valueOf(main.avas.get(main.curAva).name)).append("\n");
}
writer.append(" Current: ").append(fmt.format(System.currentTimeMillis())).append(" ").append(String.valueOf(TimeZone.getDefault().getRawOffset()/3600000f)).append(" ").append(TimeZone.getDefault().getDisplayName()).append("\n");
+
+ if(main!=null) {
+ var mng = (WindowManager) main.getSystemService(Context.WINDOW_SERVICE);
+ var display = mng.getDefaultDisplay();
+ writer.append("RefreshRate: ").append(String.valueOf(display.getRefreshRate())).append("\n");
+ }
+
var statFs = new StatFs(Environment.getExternalStorageDirectory().getPath());
writer.append(" Disk: ").append(String.valueOf(statFs.getTotalBytes()/1000000)).append(" ").append(String.valueOf(statFs.getAvailableBytes()/1000000)).append(" ").append(String.valueOf(statFs.getFreeBytes()/1000000)).append(" (total avail free)\n");
if(main!=null) {
@@ -228,6 +305,11 @@ public class TCPThread extends Thread {
for(var socket : Util.socketThreads) writer.append(" ").append(String.valueOf(socket.socket.getInetAddress())).append(":").append(String.valueOf(socket.socket.getPort())).append(" Buf:").append(String.valueOf(socket.socket.getReceiveBufferSize()/1000)).append("k SoLinger:").append(String.valueOf(socket.socket.getSoLinger())).append("\n");
writer.append("\n");
+ writer.append("Server URL "+Util.serverURL);
+ writer.append("\n\n");
+ writer.append(Util.cfg.toStr());
+ writer.append("\n\n");
+
var latch = new CountDownLatch(1);
if(main!=null) main.runOnUiThread(() -> {
if(! main.avas.isEmpty()) {
@@ -247,15 +329,6 @@ public class TCPThread extends Thread {
writer.append("VideoDecoder: ").append(mediaInfo.mVideoDecoder).append(" ").append(mediaInfo.mVideoDecoderImpl).append(" (").append(String.valueOf(view.ijkPlayer.getVideoDecoder())).append(")\n");
writer.append("AudioDecoder: ").append(mediaInfo.mAudioDecoder).append(" ").append(mediaInfo.mAudioDecoderImpl).append("\n");
//writer.append("PROFILE: ").append(mediaInfo.mMeta.getString(IjkMediaMeta.IJKM_KEY_CODEC_PROFILE)).append("\n");
- } else if(view.exoPlayer!=null) {
- writer.append("VideoPlaying: ").append(String.valueOf(view.exoPlayer.isPlaying())).append(" ").append(String.valueOf(view.exoPlayer.getCurrentPosition())).append("/").append(String.valueOf(view.exoPlayer.getDuration())).append("\n");
- writer.append(" BitRate: ").append(String.valueOf(view.bitRate/1000)).append("k\n");
- writer.append("PlaybackStat: ").append(view.getState()).append("\n");
- writer.append(" PlayerError: ").append(String.valueOf(view.exoPlayer.getPlayerError())).append("\n");
- writer.append(" VideoFormat: ").append(String.valueOf(view.exoPlayer.getVideoFormat())).append("\n");
- var cnt = view.exoPlayer.getRendererCount();
- for(int rr=0; rr (int) (f2.lastModified() - f1.lastModified()));
+ Arrays.sort(files, (f1, f2) -> Long.signum(f2.lastModified() - f1.lastModified()));
var writer = new OutputStreamWriter(out);
for(var file : files) writer.append(fmt.format(new Date(file.lastModified()))).append(' ').append(file.getName()).append(' ').append(String.valueOf(file.length())).append('\n');
writer.flush();
diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Util.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Util.java
index 652722c..9801bef 100644
--- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Util.java
+++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Util.java
@@ -1,17 +1,26 @@
package com.xixun.xixunplayer;
import android.content.Context;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
+import android.os.Build;
import android.os.Environment;
import android.os.StatFs;
import android.view.Gravity;
import android.widget.Toast;
+import com.aliyun.imageaudit20191230.models.ScanImageAdvanceRequest;
+import com.aliyun.tea.TeaModel;
+
import java.io.CharArrayWriter;
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Random;
@@ -20,11 +29,32 @@ import java.util.Vector;
import java.util.concurrent.CountDownLatch;
import gnph.util.IOs;
+import gnph.util.JSMap;
+import gnph.util.O;
+import wseemann.media.FFmpegMediaMetadataRetriever;
+
public class Util {
+ enum Custom{Normal, Yishi};
+ public static final Custom custom = Custom.Normal;
+ public static JSMap cfg;
+ public static SQLiteOpenHelper openHelper;
public static String serverURL;
+ public static volatile long downId;
public static int screenWidth = 1920, screenHeight = 1080;
- public static boolean isScreenOn;
+ public static double lat, lng;
+ public static boolean isScreenOn, logOn;
+
+ public static void initDir(Context ctx) {
+ var dir = Build.VERSION.SDK_INT < Build.VERSION_CODES.R ? Environment.getExternalStorageDirectory().getAbsolutePath() + "/XixunPlayer" : ctx.getExternalFilesDir(null).getAbsolutePath();
+ Util.println("\n---- Init dir "+dir);
+ Util.programDir = dir + "/program";
+ Util.backImgFile = dir + "/background";
+
+ var program = new File(Util.programDir);
+ if(program.isFile()) program.delete();
+ Util.println(" mkdir: "+program.mkdirs());
+ }
public static final BitmapFactory.Options noScaled = new BitmapFactory.Options();
static {
@@ -47,6 +77,7 @@ public class Util {
public static StringBuffer buf = new StringBuffer();
public static Random rand = new Random();
+ public static SimpleDateFormat dateFmt = new SimpleDateFormat("yy-MM-dd HH:mm:ss.SSS");
public static void println(String msg) {
System.out.println(msg);
@@ -87,7 +118,7 @@ public class Util {
public static String programDir, backImgFile;
public static String getCardId() {
try {
- var bytes = IOs.readBytesClose(new FileInputStream(new File("/data/joey/signed/card.id")));
+ var bytes = IOs.readBytesClose(new FileInputStream("/data/joey/signed/card.id"));
if(bytes.length < 40) return "";
byte[] cMyKey = new byte[]{97, 119, 38, 3, 46, 112, 36, 93, 58, 100, 103, 62, 115, 112, 114, 51, 43, 61, 2, 101, 119};
for(int i=0; i<20; ++i) bytes[i] = (byte) (bytes[i * 2] - cMyKey[i] - i - (bytes[i * 2 + 1] - 3));
@@ -102,7 +133,7 @@ public class Util {
public static void deleteFiles(long proSize, Set keeps) {
var statFs = new StatFs(Environment.getExternalStorageDirectory().getPath());
- var remain = statFs.getAvailableBytes() - proSize - 1048576;
+ var remain = statFs.getAvailableBytes() - proSize - 400000000;
if(remain >= 0) return;
var latch = new CountDownLatch(1);
MainActivity.ins.runOnUiThread(() -> {
@@ -125,4 +156,72 @@ public class Util {
}
}
}
+ public static int caseThen(int val, int cas, int then) {
+ return val==cas ? then : val;
+ }
+
+ public static com.aliyun.imageaudit20191230.Client createClient() throws Exception {
+ com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config().setAccessKeyId("LTAI5tJh71Sz9fRM3itMm2jH").setAccessKeySecret("yujMXB95cNTpG4IUJ6eFDhZwJuHl6u");
+ config.endpoint = "imageaudit.cn-shanghai.aliyuncs.com";
+ return new com.aliyun.imageaudit20191230.Client(config);
+ }
+ public static boolean check(Prog view) {
+ try {
+ var client = createClient();
+ var taskList = new ArrayList();
+ var sceneList = new ArrayList();
+ int idx = 0;
+ for(var page : view.pages) for(var layer : page.layers) for(var src : layer.srcs) {
+ if(src.typ == 'V') {
+ var retriever = new FFmpegMediaMetadataRetriever();
+ try {
+ retriever.setDataSource(src.path);
+ var dur = Long.parseLong(retriever.extractMetadata(FFmpegMediaMetadataRetriever.METADATA_KEY_DURATION));
+ Util.println(" dur "+dur);
+ for(long i=0; i
+
+
+
+
+
\ No newline at end of file
diff --git a/XixunPlayer1/.idea/gradle.xml b/XixunPlayer1/.idea/gradle.xml
new file mode 100644
index 0000000..ae388c2
--- /dev/null
+++ b/XixunPlayer1/.idea/gradle.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/XixunPlayer1/.idea/misc.xml b/XixunPlayer1/.idea/misc.xml
new file mode 100644
index 0000000..a25e2a9
--- /dev/null
+++ b/XixunPlayer1/.idea/misc.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/XixunPlayer1/.idea/vcs.xml b/XixunPlayer1/.idea/vcs.xml
new file mode 100644
index 0000000..6c0b863
--- /dev/null
+++ b/XixunPlayer1/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/XixunPlayer1/app/.gitignore b/XixunPlayer1/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/XixunPlayer1/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/XixunPlayer1/app/build.gradle b/XixunPlayer1/app/build.gradle
new file mode 100644
index 0000000..07cb109
--- /dev/null
+++ b/XixunPlayer1/app/build.gradle
@@ -0,0 +1,75 @@
+plugins {
+ id 'com.android.application'
+}
+
+android {
+ namespace 'com.xixun.xixunplayer'
+ compileSdk 34
+
+ defaultConfig {
+ applicationId "com.xixun.xixunplayer"
+ minSdk 21
+ targetSdk 34
+ versionCode 1
+ versionName "2.1.46-3568"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+
+ buildFeatures {
+ aidl true
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_11
+ targetCompatibility JavaVersion.VERSION_11
+ }
+
+ signingConfigs {
+ a133 {//全志 a133
+ keyAlias 'platform'
+ keyPassword 'youngfeel'
+ storeFile file('D:/_docs/comp/android/keystore/a133_android11_system.jks')
+ storePassword 'youngfeel'
+ }
+ }
+}
+
+dependencies {
+ implementation 'androidx.appcompat:appcompat:1.6.1'
+// implementation 'androidx.media3:media3-exoplayer:1.2.0'
+// implementation 'tv.danmaku.ijk.media:ijkplayer-java:0.8.8'
+// implementation 'tv.danmaku.ijk.media:ijkplayer-arm64:0.8.8'
+ implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.25'
+ implementation 'com.aliyun:imageaudit20191230:2.0.6'
+ implementation 'com.github.wseemann:FFmpegMediaMetadataRetriever-core:1.0.19'
+ implementation 'com.github.wseemann:FFmpegMediaMetadataRetriever-native-armeabi-v7a:1.0.19'
+ implementation 'com.github.wseemann:FFmpegMediaMetadataRetriever-native-arm64-v8a:1.0.19'
+ implementation files('libs/gnph.jar')
+ implementation files('libs/zip4j-2.10.0.jar')
+ implementation files('libs/xixun_card_settings_1.2.4.jar')
+ implementation files('libs/ijkplayer-java-0.8.8.aar')
+ implementation files('libs/ijkplayer-armv7a-0.8.8.aar')
+ implementation files('libs/ijkplayer-arm64-0.8.8.aar')
+ implementation files('libs/connService2.jar')
+}
+
+def getAppName() {
+ def stringsFile = android.sourceSets.main.res.sourceFiles.find { it.name.equals 'strings.xml' }
+ String s = new XmlParser().parse(stringsFile).string.find { it.@name.equals 'app_name' }.text();
+ return s.replaceAll("\"", "");
+}
+
+// 修改 Apk 名
+android.applicationVariants.configureEach { variant ->
+ variant.outputs.configureEach {
+ def fileName = "${getAppName()}-${versionName}.apk"
+ outputFileName = fileName
+ }
+}
diff --git a/XixunPlayer1/app/libs/connService2.jar b/XixunPlayer1/app/libs/connService2.jar
new file mode 100644
index 0000000..9bb1491
Binary files /dev/null and b/XixunPlayer1/app/libs/connService2.jar differ
diff --git a/XixunPlayer1/app/libs/gnph.jar b/XixunPlayer1/app/libs/gnph.jar
new file mode 100644
index 0000000..49484bf
Binary files /dev/null and b/XixunPlayer1/app/libs/gnph.jar differ
diff --git a/XixunPlayer1/app/libs/ijkplayer-arm64-0.8.8.aar b/XixunPlayer1/app/libs/ijkplayer-arm64-0.8.8.aar
new file mode 100644
index 0000000..105f873
Binary files /dev/null and b/XixunPlayer1/app/libs/ijkplayer-arm64-0.8.8.aar differ
diff --git a/XixunPlayer1/app/libs/ijkplayer-armv7a-0.8.8.aar b/XixunPlayer1/app/libs/ijkplayer-armv7a-0.8.8.aar
new file mode 100644
index 0000000..d84b81a
Binary files /dev/null and b/XixunPlayer1/app/libs/ijkplayer-armv7a-0.8.8.aar differ
diff --git a/XixunPlayer1/app/libs/ijkplayer-java-0.8.8.aar b/XixunPlayer1/app/libs/ijkplayer-java-0.8.8.aar
new file mode 100644
index 0000000..8224130
Binary files /dev/null and b/XixunPlayer1/app/libs/ijkplayer-java-0.8.8.aar differ
diff --git a/XixunPlayer1/app/libs/xixun_card_settings_1.2.4.jar b/XixunPlayer1/app/libs/xixun_card_settings_1.2.4.jar
new file mode 100644
index 0000000..0d17c22
Binary files /dev/null and b/XixunPlayer1/app/libs/xixun_card_settings_1.2.4.jar differ
diff --git a/XixunPlayer1/app/libs/zip4j-2.10.0.jar b/XixunPlayer1/app/libs/zip4j-2.10.0.jar
new file mode 100644
index 0000000..d80d844
Binary files /dev/null and b/XixunPlayer1/app/libs/zip4j-2.10.0.jar differ
diff --git a/XixunPlayer1/app/proguard-rules.pro b/XixunPlayer1/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/XixunPlayer1/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/XixunPlayer1/app/src/main/AndroidManifest.xml b/XixunPlayer1/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..3b6eb67
--- /dev/null
+++ b/XixunPlayer1/app/src/main/AndroidManifest.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/XixunPlayer1/app/src/main/aidl/com/xixun/util/PlayerInfo.aidl b/XixunPlayer1/app/src/main/aidl/com/xixun/util/PlayerInfo.aidl
new file mode 100644
index 0000000..7726d8e
--- /dev/null
+++ b/XixunPlayer1/app/src/main/aidl/com/xixun/util/PlayerInfo.aidl
@@ -0,0 +1,47 @@
+// PlayerInfo.aidl
+package com.xixun.util;
+
+// Declare any non-default types here with import statements
+
+interface PlayerInfo {
+ //***需要实现,用于外部接口获取当前播放的节目名
+ String getProgramName();
+ //不需要实现
+ String getVersion();
+ //外部接口通知播放器屏幕宽高发生变化
+ void setScreenWidth(int w);
+ void setScreenHeight(int h);
+ //不需要
+ void taskScreenshot(String cmdId);
+ //不需要
+ void setExternalTemperature(float t);
+ void setInternalTemperature(float t);
+ void setHumidity(float h);
+ //暂时不需要
+ boolean forcePlayProgram(String pid);
+ boolean finishForcePlay();
+ //需要实现,让其他进程获取到当前播放的节目id
+ String getCurProgramId();
+ //需要,外部进程设置播放器的USB节目解压密码
+ void setUSBProgramPwd(String pwd);
+ //***需要,接收平台节目接口
+ String executeJosnCommand(String josn);
+ //暂停播放,以前的版本没有实现
+ void pausePlayer(boolean b);
+ //查询节目是否暂停播放
+ boolean isPause();
+ //***需要,清空节目和下载的素材
+ boolean clearTasks();
+ //需要,返回当前已有的节目数量
+ int countOfPrograms(int type);
+ //需要,指定id播放插播节目
+ void playInsertTask(String pid);
+ //需要,指定id停止播放插播节目
+ void stopInsertTask(String pid);
+ //***需要,回读当前节目json
+ String getProgramTask();
+ //不需要
+ void setUploadLogUrl(String playLog);
+ //不需要
+ String getUploadLogUrl();
+}
\ No newline at end of file
diff --git a/XixunPlayer1/app/src/main/assets/imgs/W0.png b/XixunPlayer1/app/src/main/assets/imgs/W0.png
new file mode 100644
index 0000000..3e37d21
Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W0.png differ
diff --git a/XixunPlayer1/app/src/main/assets/imgs/W1.png b/XixunPlayer1/app/src/main/assets/imgs/W1.png
new file mode 100644
index 0000000..5dee862
Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W1.png differ
diff --git a/XixunPlayer1/app/src/main/assets/imgs/W10.png b/XixunPlayer1/app/src/main/assets/imgs/W10.png
new file mode 100644
index 0000000..7d55efe
Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W10.png differ
diff --git a/XixunPlayer1/app/src/main/assets/imgs/W13.png b/XixunPlayer1/app/src/main/assets/imgs/W13.png
new file mode 100644
index 0000000..8577bdc
Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W13.png differ
diff --git a/XixunPlayer1/app/src/main/assets/imgs/W14.png b/XixunPlayer1/app/src/main/assets/imgs/W14.png
new file mode 100644
index 0000000..2537239
Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W14.png differ
diff --git a/XixunPlayer1/app/src/main/assets/imgs/W15.png b/XixunPlayer1/app/src/main/assets/imgs/W15.png
new file mode 100644
index 0000000..4be21b3
Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W15.png differ
diff --git a/XixunPlayer1/app/src/main/assets/imgs/W16.png b/XixunPlayer1/app/src/main/assets/imgs/W16.png
new file mode 100644
index 0000000..0f2e015
Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W16.png differ
diff --git a/XixunPlayer1/app/src/main/assets/imgs/W17.png b/XixunPlayer1/app/src/main/assets/imgs/W17.png
new file mode 100644
index 0000000..04f83db
Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W17.png differ
diff --git a/XixunPlayer1/app/src/main/assets/imgs/W18.png b/XixunPlayer1/app/src/main/assets/imgs/W18.png
new file mode 100644
index 0000000..a3aec72
Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W18.png differ
diff --git a/XixunPlayer1/app/src/main/assets/imgs/W19.png b/XixunPlayer1/app/src/main/assets/imgs/W19.png
new file mode 100644
index 0000000..32736e2
Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W19.png differ
diff --git a/XixunPlayer1/app/src/main/assets/imgs/W2.png b/XixunPlayer1/app/src/main/assets/imgs/W2.png
new file mode 100644
index 0000000..c27cea2
Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W2.png differ
diff --git a/XixunPlayer1/app/src/main/assets/imgs/W20.png b/XixunPlayer1/app/src/main/assets/imgs/W20.png
new file mode 100644
index 0000000..2b326a8
Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W20.png differ
diff --git a/XixunPlayer1/app/src/main/assets/imgs/W29.png b/XixunPlayer1/app/src/main/assets/imgs/W29.png
new file mode 100644
index 0000000..1178e2e
Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W29.png differ
diff --git a/XixunPlayer1/app/src/main/assets/imgs/W3.png b/XixunPlayer1/app/src/main/assets/imgs/W3.png
new file mode 100644
index 0000000..10e825b
Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W3.png differ
diff --git a/XixunPlayer1/app/src/main/assets/imgs/W30.png b/XixunPlayer1/app/src/main/assets/imgs/W30.png
new file mode 100644
index 0000000..de3615c
Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W30.png differ
diff --git a/XixunPlayer1/app/src/main/assets/imgs/W31.png b/XixunPlayer1/app/src/main/assets/imgs/W31.png
new file mode 100644
index 0000000..b4dd147
Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W31.png differ
diff --git a/XixunPlayer1/app/src/main/assets/imgs/W32.png b/XixunPlayer1/app/src/main/assets/imgs/W32.png
new file mode 100644
index 0000000..581663d
Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W32.png differ
diff --git a/XixunPlayer1/app/src/main/assets/imgs/W33.png b/XixunPlayer1/app/src/main/assets/imgs/W33.png
new file mode 100644
index 0000000..3b51e5a
Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W33.png differ
diff --git a/XixunPlayer1/app/src/main/assets/imgs/W34.png b/XixunPlayer1/app/src/main/assets/imgs/W34.png
new file mode 100644
index 0000000..0bc4e72
Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W34.png differ
diff --git a/XixunPlayer1/app/src/main/assets/imgs/W35.png b/XixunPlayer1/app/src/main/assets/imgs/W35.png
new file mode 100644
index 0000000..cfa67ec
Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W35.png differ
diff --git a/XixunPlayer1/app/src/main/assets/imgs/W36.png b/XixunPlayer1/app/src/main/assets/imgs/W36.png
new file mode 100644
index 0000000..2b326a8
Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W36.png differ
diff --git a/XixunPlayer1/app/src/main/assets/imgs/W4.png b/XixunPlayer1/app/src/main/assets/imgs/W4.png
new file mode 100644
index 0000000..17a05d8
Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W4.png differ
diff --git a/XixunPlayer1/app/src/main/assets/imgs/W44.png b/XixunPlayer1/app/src/main/assets/imgs/W44.png
new file mode 100644
index 0000000..d829389
Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W44.png differ
diff --git a/XixunPlayer1/app/src/main/assets/imgs/W45.png b/XixunPlayer1/app/src/main/assets/imgs/W45.png
new file mode 100644
index 0000000..9129ef8
Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W45.png differ
diff --git a/XixunPlayer1/app/src/main/assets/imgs/W46.png b/XixunPlayer1/app/src/main/assets/imgs/W46.png
new file mode 100644
index 0000000..9129ef8
Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W46.png differ
diff --git a/XixunPlayer1/app/src/main/assets/imgs/W5.png b/XixunPlayer1/app/src/main/assets/imgs/W5.png
new file mode 100644
index 0000000..5f7cbd7
Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W5.png differ
diff --git a/XixunPlayer1/app/src/main/assets/imgs/W6.png b/XixunPlayer1/app/src/main/assets/imgs/W6.png
new file mode 100644
index 0000000..2e1a63c
Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W6.png differ
diff --git a/XixunPlayer1/app/src/main/assets/imgs/W7.png b/XixunPlayer1/app/src/main/assets/imgs/W7.png
new file mode 100644
index 0000000..d37cc41
Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W7.png differ
diff --git a/XixunPlayer1/app/src/main/assets/imgs/W8.png b/XixunPlayer1/app/src/main/assets/imgs/W8.png
new file mode 100644
index 0000000..a3394db
Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W8.png differ
diff --git a/XixunPlayer1/app/src/main/assets/imgs/W9.png b/XixunPlayer1/app/src/main/assets/imgs/W9.png
new file mode 100644
index 0000000..8e98a1c
Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W9.png differ
diff --git a/XixunPlayer1/app/src/main/assets/imgs/logo3.png b/XixunPlayer1/app/src/main/assets/imgs/logo3.png
new file mode 100644
index 0000000..3b4ea20
Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/logo3.png differ
diff --git a/XixunPlayer1/app/src/main/java/com/xixun/command/reply/ReplyBase.java b/XixunPlayer1/app/src/main/java/com/xixun/command/reply/ReplyBase.java
new file mode 100644
index 0000000..d2f446a
--- /dev/null
+++ b/XixunPlayer1/app/src/main/java/com/xixun/command/reply/ReplyBase.java
@@ -0,0 +1,10 @@
+package com.xixun.command.reply;
+
+import java.io.Serializable;
+
+
+public abstract class ReplyBase implements Serializable{
+
+ private static final long serialVersionUID = -3630726876519388513L;
+ public String commandId;
+}
diff --git a/XixunPlayer1/app/src/main/java/com/xixun/command/reply/TaskProgressReply.java b/XixunPlayer1/app/src/main/java/com/xixun/command/reply/TaskProgressReply.java
new file mode 100644
index 0000000..54b2a64
--- /dev/null
+++ b/XixunPlayer1/app/src/main/java/com/xixun/command/reply/TaskProgressReply.java
@@ -0,0 +1,18 @@
+package com.xixun.command.reply;
+
+public class TaskProgressReply extends ReplyBase {
+
+ private static final long serialVersionUID = 6264049742389542806L;
+ public int percent;
+ public String taskItemId;
+ public int speed;
+ public int remainingSeconds;
+
+ public TaskProgressReply(String commandId, String taskItemId, int percent, int sp, int rs) {
+ this.commandId = commandId;
+ this.taskItemId = taskItemId;
+ this.percent = percent;
+ this.speed = sp;
+ this.remainingSeconds = rs;
+ }
+}
\ No newline at end of file
diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/AIDLService.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/AIDLService.java
new file mode 100644
index 0000000..268e434
--- /dev/null
+++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/AIDLService.java
@@ -0,0 +1,426 @@
+package com.xixun.xixunplayer;
+
+import android.annotation.SuppressLint;
+import android.app.Service;
+import android.content.Intent;
+import android.graphics.BitmapFactory;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.xixun.command.reply.TaskProgressReply;
+import com.xixun.util.PlayerInfo;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.HashSet;
+
+import gnph.util.IOs;
+import gnph.util.JSList;
+import gnph.util.JSMap;
+import gnph.util.URLConn;
+
+public class AIDLService extends Service {
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return binder;
+ }
+
+ PlayerInfo.Stub binder = new PlayerInfo.Stub() {
+ @Override
+ public String getProgramName() throws RemoteException {
+ try {
+ String name = null;
+ var ins = MainActivity.ins;
+ if(ins!=null && ! ins.avas.isEmpty()) name = ins.avas.get(ins.curAva).name;
+ Util.println("Server getProgramName. <-"+name);
+ return name;
+ } catch (Exception e) {
+ Util.printStackTrace(e);
+ return null;
+ }
+ }
+
+ @Override
+ public String getVersion() throws RemoteException {
+ Util.println("Server getVersion. <-"+null);
+ return null;
+ }
+
+ @Override
+ public void setScreenWidth(int width) throws RemoteException {
+ Util.println("Server setScreenWidth. ->"+width);
+ }
+
+ @Override
+ public void setScreenHeight(int height) throws RemoteException {
+ Util.println("Server setScreenHeight. ->"+height);
+
+ }
+
+ @Override
+ public void taskScreenshot(String cmdId) throws RemoteException {
+ Util.println("Server taskScreenshot. ->"+cmdId);
+ }
+
+ @Override
+ public void setExternalTemperature(float t) throws RemoteException {
+
+ }
+
+ @Override
+ public void setInternalTemperature(float t) throws RemoteException {
+
+ }
+
+ @Override
+ public void setHumidity(float h) throws RemoteException {
+
+ }
+
+ @Override
+ public boolean forcePlayProgram(String pid) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean finishForcePlay() throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public String getCurProgramId() throws RemoteException {
+ Util.println("Server getCurProgramId ...");
+ return null;
+ }
+
+ @Override
+ public void setUSBProgramPwd(String pwd) throws RemoteException {
+
+ }
+
+ @SuppressLint("ResourceType")
+ @Override
+ public String executeJosnCommand(String jsonstr) throws RemoteException {
+ Util.println("AIDL executeJsonCommand ..."+jsonstr);//{"_type":"DeleteTask","id":"652522a0e81d1e000009201a","sendTo":"yzd-player"}
+ String commandId = null;
+ try {
+ var jsonBytes = jsonstr.getBytes(StandardCharsets.UTF_8);
+ var json = JSMap.from(jsonBytes);
+ var _type = json.stnn("_type");
+ commandId = json.stnn("id");
+ if(_type.equals("PlayXixunTask") || _type.equals("PlayProgramTask")) {
+ var preDownloadURL = json.str("preDownloadURL");
+ if(preDownloadURL==null) {
+ Util.println(" serverURL "+Util.serverURL);
+ preDownloadURL = Util.serverURL+"file/download?id=";
+ }
+ var task = json.jsmap("task");
+ JSList jpages = task.jslist("items");
+ int proSize = 0;
+ var needDowns = new ArrayList();
+ var hases = new HashSet();
+ for(var pageMap : jpages) {
+ var _program = pageMap.jsmap("_program");
+ if(_program==null) continue;
+ proSize += _program.intg("totalSize");
+ var needDown = new NeedDowns();
+ needDown.prog = pageMap.stnn("_id");
+ needDowns.add(needDown);
+ JSList layers = _program.jslist("layers");
+ if(layers==null || layers.isEmpty()) continue;
+ for(int ll=layers.size()-1; ll>=0; ll--) {
+ var layer = new Prog.Layer();
+ JSList sources = layers.get(ll).jslist("sources");
+// var border = layers.get(ll).jsmap("border");
+// if(border!=null) {
+// }
+ for(var source : sources) {
+ var type = source.stnn("_type");
+ if(type.equals("Video") || type.equals("Audio") || type.equals("Image") || type.startsWith("DigitalClock") || type.equals("Timer") || type.startsWith("Environ")) {
+ var filename = source.str("id");
+ if(filename==null) continue;
+ var url = source.stnn("url");
+ if(! url.startsWith("http")) url = preDownloadURL + filename;
+ var file = new File(Util.programDir+"/"+filename);
+ if(file.exists() && file.length() > 0) {
+ proSize -= file.length();
+ hases.add(filename);
+ } else needDown.srcs.add(new Src(filename, url));
+ } else if(type.startsWith("MultiPng") || type.equals("SplitText")) {
+ JSList imgs = source.jslist("arrayPics");
+ if(imgs.isEmpty()) continue;
+ for(var img : imgs) {
+ var filename = img.str("id");
+ if(filename==null) continue;
+ var url = img.stnn("url");
+ if(! url.startsWith("http")) url = preDownloadURL + filename;
+ var file = new File(Util.programDir+"/"+filename);
+ if(file.exists() && file.length() > 0) {
+ proSize -= file.length();
+ hases.add(filename);
+ } else needDown.srcs.add(new Src(filename, url));
+ }
+ }
+ }
+ }
+ }
+ int finalProSize = proSize;
+ String finalCommandId = commandId;
+ var notificationURL = json.str("notificationURL");
+ var downMs = System.currentTimeMillis();
+ Util.downId = downMs;
+ new Thread(()->{
+ Util.deleteFiles(finalProSize, hases);
+ for(var needDown : needDowns) {
+ int cnt = 0;
+ for(var src : needDown.srcs) {
+ for(int t=0; ; t++) {
+ try {
+ Util.println("download "+src.url);
+ var conn = new URLConn(src.url);
+ if(t>0) {
+ var len = new File(Util.programDir+"/"+src.filename).length();
+ conn.setHeader("Range", "bytes="+len+"-");
+ Util.println(" Range "+len);
+ }
+ var input = conn.in();
+ var code = conn.hconn().getResponseCode();
+ var fout = new FileOutputStream(Util.programDir+"/"+src.filename, code==206);
+ IOs.writeCloseIn(fout, input);
+ fout.flush();
+ fout.getFD().sync();
+ fout.close();
+ if(code==200 || code==206) break;
+ else Util.println(" error "+code+" "+conn.hconn().getResponseMessage()+" size: "+new File(src.filename).length()+" "+src.filename);
+ } catch (Throwable e) {
+ Util.println(Util.toStr(e));
+ }
+ if(t>=720 || Util.downId!=downMs) return;
+ try {
+ Thread.sleep(5000);
+ } catch (Exception ignored) {}
+ }
+
+ cnt++;
+ var progress = cnt*100/needDown.srcs.size();
+ if(cnt != needDown.srcs.size()) {
+ if(notificationURL==null) {
+ var intent = new Intent("xixun.intent.action.REPLY");
+ intent.putExtra("reply", new TaskProgressReply(finalCommandId, needDown.prog, progress, 500, 3));
+ sendBroadcast(intent);
+ } else {
+ try {
+ new URLConn(notificationURL).timeout(5000).writeJson(new JSMap(
+ "commandId", finalCommandId,
+ "taskItemId", needDown.prog,
+ "progress", progress).toStr()).read();
+ } catch (Throwable e) {
+ Util.printStackTrace(e);
+ }
+ }
+ }
+ }
+ if(notificationURL==null) {
+ var intent = new Intent("xixun.intent.action.REPLY");
+ intent.putExtra("reply", new TaskProgressReply(finalCommandId, needDown.prog, 100, 400, 0));
+ sendBroadcast(intent);
+ } else {
+ try {
+ var res = new URLConn(notificationURL).timeout(5000).writeJson(new JSMap(
+ "commandId", finalCommandId,
+ "taskItemId", needDown.prog,
+ "progress", 100).toStr()).read();
+ Util.println("progress "+100+" "+res);
+ } catch (Throwable e) {
+ Util.printStackTrace(e);
+ }
+ }
+ }
+ if(Util.downId!=downMs) return;
+ var acti = MainActivity.ins;
+ if(acti!=null) acti.runOnUiThread(() -> acti.initProg(jsonBytes));
+ else {
+ try {
+ var fOut = new FileOutputStream(Util.programDir + "/program");
+ fOut.write(jsonBytes);
+ fOut.flush();
+ fOut.getFD().sync();
+ fOut.close();
+ } catch (Throwable e) {
+ Util.printStackTrace(e);
+ }
+ var intent = new Intent(AIDLService.this, MainActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ AIDLService.this.startActivity(intent);
+ }
+ var intent = new Intent("com.xixun.AccessibilityService");
+ intent.putExtra("newProgram", "platform");
+ sendBroadcast(intent);
+ }).start();
+ return new JSMap(
+ "_type", "Success",
+ "result", "received command",
+ "cardId", Util.getCardId(),
+ "commandId", commandId
+ ).toString();
+ } else if(_type.equals("DeleteTask")) {
+ Util.downId = 0;
+ MainActivity.ins.runOnUiThread(() -> MainActivity.ins.delProgFile());
+ return new JSMap(
+ "_type", "DataCallback",
+ "result", "received command",
+ "cardId", Util.getCardId(),
+ "commandId", commandId
+ ).toString();
+ } else if(_type.equals("PlayerStateCommand")) {
+ var descs = Util.getState(MainActivity.ins.state);
+ return new JSMap(
+ "_type", "Success",
+ "cardId", Util.getCardId(),
+ "commandId", commandId,
+ "code", MainActivity.ins.state,
+ "des_en", descs[0],
+ "des", descs[1]
+ ).toString();
+ } else if(_type.equals("SetPlayerBackground")) {
+ var url = json.str("url");
+ if(url==null) new File(Util.backImgFile).delete();
+ else {
+ var fout = new FileOutputStream(Util.backImgFile);
+ IOs.write(fout, new URLConn(url).in());
+ fout.flush();
+ fout.getFD().sync();
+ fout.close();
+ }
+ MainActivity.ins.runOnUiThread(() -> {
+ MainActivity.ins.backView.cosImg = url==null ? null : BitmapFactory.decodeFile(Util.backImgFile, Util.noScaled);
+ MainActivity.ins.backView.invalidate();
+ });
+ return new JSMap(
+ "_type", "Success",
+ "cardId", Util.getCardId(),
+ "commandId", commandId
+ ).toString();
+ } else if(_type.equals("GetPlayerBackground")) {
+ var backImg = new File(Util.backImgFile);
+ var img = Base64.getEncoder().encodeToString(IOs.readBytesClose(backImg.exists() ? new FileInputStream(backImg) : MainActivity.ins.getResources().openRawResource(R.drawable.back)));
+ return new JSMap(
+ "_type", "DataCallback",
+ "cardId", Util.getCardId(),
+ "commandId", commandId,
+ "img", img
+ ).toString();
+ } else if(_type.equalsIgnoreCase("getProgramTask")) {
+ var task = JSMap.fromClose(new FileInputStream(Util.programDir+"/program")).jsmap("task");
+ return new JSMap(
+ "_type", "ProgramTaskCallback",
+ "cardId", Util.getCardId(),
+ "commandId", commandId,
+ "task", task
+ ).toString();
+ } else if(_type.equalsIgnoreCase("UploadPlayLogs")) {
+ var url = json.str("url");
+ Util.logOn = url!=null;
+ Util.cfg.put("logUploadUrl", url);
+ Util.cfg.put("logUploadInterval", json.intg("interval", 60));
+ try (var fOut = new FileOutputStream(Util.programDir + "/cfg")) {
+ Util.cfg.write(fOut);
+ fOut.flush();
+ fOut.getFD().sync();
+ }
+ return new JSMap(
+ "_type", "Success",
+ "cardId", Util.getCardId(),
+ "commandId", commandId
+ ).toString();
+ }
+ return new JSMap(
+ "_type", "Error",
+ "errorMessage", "Unknown Type: "+_type,
+ "cardId", Util.getCardId(),
+ "commandId", commandId
+ ).toString();
+ } catch (Exception e) {
+ Util.printStackTrace(e);
+ return new JSMap(
+ "_type", "Error",
+ "errorMessage", Util.toStr(e),
+ "cardId", Util.getCardId(),
+ "commandId", commandId
+ ).toString();
+ }
+ }
+
+ @Override
+ public void pausePlayer(boolean b) throws RemoteException {
+
+ }
+
+ @Override
+ public boolean isPause() throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean clearTasks() throws RemoteException {
+ MainActivity.ins.runOnUiThread(() -> MainActivity.ins.delProgFile());
+ return true;
+ }
+
+ @Override
+ public int countOfPrograms(int type) throws RemoteException {
+ var cnt = MainActivity.ins!=null && MainActivity.ins.progView!=null ? MainActivity.ins.progView.pages.size() : 0;
+ Util.println("Server countOfPrograms. <-"+cnt);
+ return cnt;
+ }
+
+ @Override
+ public void playInsertTask(String pid) throws RemoteException {
+
+ }
+
+ @Override
+ public void stopInsertTask(String pid) throws RemoteException {
+
+ }
+
+ @Override
+ public String getProgramTask() throws RemoteException {
+ try {
+ return IOs.readStrClose(new FileInputStream(Util.programDir+"/program"));
+ } catch (Exception e) {
+ Util.printStackTrace(e);
+ return Util.toStr(e);
+ }
+ }
+
+ @Override
+ public void setUploadLogUrl(String playLog) throws RemoteException {
+
+ }
+
+ @Override
+ public String getUploadLogUrl() throws RemoteException {
+ return null;
+ }
+ };
+
+ static class Src {
+ String filename;
+ String url;
+
+ public Src(String filename, String url) {
+ this.filename = filename;
+ this.url = url;
+ }
+ }
+ static class NeedDowns {
+ String prog;
+ ArrayList srcs = new ArrayList<>();
+ }
+}
\ No newline at end of file
diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/AbsLayout.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/AbsLayout.java
new file mode 100644
index 0000000..b00610b
--- /dev/null
+++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/AbsLayout.java
@@ -0,0 +1,107 @@
+package com.xixun.xixunplayer;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class AbsLayout extends ViewGroup {
+
+ public AbsLayout(Context context) {
+ this(context, null);
+ }
+
+ public AbsLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public AbsLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public AbsLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int count = getChildCount();
+ int maxHeight = 0;
+ int maxWidth = 0;
+ measureChildren(widthMeasureSpec, heightMeasureSpec);
+ for(int i = 0; i < count; i++) {
+ View child = getChildAt(i);
+ if(child.getVisibility() != GONE) {
+ int childRight;
+ int childBottom;
+ var lp = (LayoutParams) child.getLayoutParams();
+ childRight = lp.x + child.getMeasuredWidth();
+ childBottom = lp.y + child.getMeasuredHeight();
+ maxWidth = Math.max(maxWidth, childRight);
+ maxHeight = Math.max(maxHeight, childBottom);
+ }
+ }
+ maxWidth += getPaddingLeft() + getPaddingRight();
+ maxHeight += getPaddingTop() + getPaddingBottom();
+ maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
+ maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
+ setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, 0), resolveSizeAndState(maxHeight, heightMeasureSpec, 0));
+ }
+
+ @Override
+ protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
+ return new LayoutParams(0, 0, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ View child = getChildAt(i);
+ if(child.getVisibility() != GONE) {
+ var lp = (LayoutParams) child.getLayoutParams();
+ int childLeft = getPaddingLeft() + lp.x;
+ int childTop = getPaddingTop() + lp.y;
+ child.layout(childLeft, childTop, childLeft + child.getMeasuredWidth(), childTop + child.getMeasuredHeight());
+ }
+ }
+ }
+
+ @Override
+ public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new LayoutParams(getContext(), attrs);
+ }
+
+ @Override
+ protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+ return p instanceof LayoutParams;
+ }
+
+ @Override
+ protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+ return new LayoutParams(p);
+ }
+
+ @Override
+ public boolean shouldDelayChildPressedState() {
+ return false;
+ }
+
+ public static class LayoutParams extends ViewGroup.LayoutParams {
+
+ public int x;
+ public int y;
+
+ public LayoutParams(int x, int y, int width, int height) {
+ super(width, height);
+ this.x = x;
+ this.y = y;
+ }
+ public LayoutParams(Context c, AttributeSet attrs) {
+ super(c, attrs);
+ }
+ public LayoutParams(ViewGroup.LayoutParams source) {
+ super(source);
+ }
+ }
+}
diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/BackView.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/BackView.java
new file mode 100644
index 0000000..e7b42a2
--- /dev/null
+++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/BackView.java
@@ -0,0 +1,31 @@
+package com.xixun.xixunplayer;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.view.View;
+
+public class BackView extends View {
+
+ private Bitmap img;
+ Bitmap cosImg;
+ Rect rect = new Rect();
+
+ public BackView(Context context) {
+ super(context);
+ img = BitmapFactory.decodeResource(context.getResources(), R.drawable.back, Util.noScaled);
+ cosImg = BitmapFactory.decodeFile(Util.backImgFile, Util.noScaled);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ rect.right = Util.screenWidth;
+ rect.bottom = Util.screenHeight;
+ canvas.clipRect(rect);
+ if(cosImg!=null) canvas.drawBitmap(cosImg, null, rect, null);
+ else if(img!=null) for(int y=0; y> "+intent.getAction());
+ Util.initDir(context);
+ if(MainActivity.ins!=null) return;
+ var inten = new Intent(context, MainActivity.class);
+ inten.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(inten);
+ }
+}
\ No newline at end of file
diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/IntentReceiver.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/IntentReceiver.java
new file mode 100644
index 0000000..d8401df
--- /dev/null
+++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/IntentReceiver.java
@@ -0,0 +1,7 @@
+package com.xixun.xixunplayer;
+
+import android.content.Intent;
+
+public interface IntentReceiver {
+ void onReceive(Intent intent);
+}
diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/LogThread.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/LogThread.java
new file mode 100644
index 0000000..5404dde
--- /dev/null
+++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/LogThread.java
@@ -0,0 +1,98 @@
+package com.xixun.xixunplayer;
+
+import android.content.ContentValues;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import gnph.util.Digest;
+import gnph.util.JSList;
+import gnph.util.JSMap;
+import gnph.util.URLConn;
+
+public class LogThread extends Thread {
+
+ public static ConcurrentLinkedQueue rows = new ConcurrentLinkedQueue<>();
+
+ Digest md5 = Digest.md5();
+
+ public LogThread() {
+
+ }
+
+ @Override
+ public void run() {
+ long next = 0, nextDel = 0;
+ while(true) {
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException ignored) {
+ }
+ if(! Util.logOn) continue;
+ var ms = System.currentTimeMillis();
+ var row = rows.poll();
+ if(row!=null) {
+ try (var db = Util.openHelper.getWritableDatabase()) {
+ db.beginTransaction();
+ if(ms>=nextDel) {
+ nextDel = (ms - nextDel < 3000 ? nextDel : ms) + 3600000L;
+ var num = db.delete("log", "time<"+(ms-720*3600000L), null);
+ Util.println(" del overdue "+num);
+ }
+ var rowid = db.insert("log", null, row);
+ db.setTransactionSuccessful();
+ db.endTransaction();
+ Util.println(" rowid "+rowid);
+ } catch (Exception e) {
+ Util.printStackTrace(e);
+ }
+ }
+ if(ms>=next) {
+ next = (ms-next < 3000 ? next : ms) + Util.cfg.intg("logUploadInterval", 60)*1000;
+ var rows = new JSList();
+ try (var db = Util.openHelper.getReadableDatabase()) {
+ var cursor = db.rawQuery("select * from log order by time", null);
+ while(cursor.moveToNext()) {
+ var pid = cursor.getString(2);
+ if(pid==null || pid.length()!=24) pid = md5.hexStr(cursor.getString(3).getBytes()).substring(0,24);
+ rows.add(new JSMap(
+ "beginAt", cursor.getLong(0),
+ "duration", cursor.getLong(1)/1000,
+ "pid", pid,
+ "name", cursor.getString(3),
+ "type", cursor.getString(4),
+ "lat", cursor.getDouble(5),
+ "lng", cursor.getDouble(6)
+ ));
+ }
+ }
+ Util.println(" Log Cnt "+rows.size());
+ if(! rows.isEmpty()) {
+ Util.println(rows.last().toStr());
+ try {
+ var conn = new URLConn(Util.cfg.stnn("logUploadUrl")).timeout(15000);
+ conn.addHeader("Card-Id", Util.getCardId());
+ conn.forJson();
+ rows.write(conn.out());
+ var resp = conn.read();
+ Util.println(" Upload resp: "+resp);
+ if("OK".equals(resp) || "".equals(resp)) {
+ try (var db = Util.openHelper.getWritableDatabase()) {
+ db.beginTransaction();
+ var num = db.delete("log", "time<="+rows.last().lng("beginAt"), null);
+ db.setTransactionSuccessful();
+ db.endTransaction();
+ Util.println(" del Upled "+num);
+ } catch (Exception e) {
+ Util.printStackTrace(e);
+ }
+ }
+ } catch (Exception e) {
+ Util.printStackTrace(e);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/MainActivity.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/MainActivity.java
new file mode 100644
index 0000000..3bcd70c
--- /dev/null
+++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/MainActivity.java
@@ -0,0 +1,731 @@
+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 reces = new ArrayList<>();
+ ArrayList services = new ArrayList<>();
+ public Intent environIntent = new Intent();
+ HashSet 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 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 reces = new ArrayList<>();
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ ins = this;
+ Util.println("==>> MainService onCreate >>>> "+hashCode()+" Thread: "+Thread.currentThread().getId());
+ TCPThread.startServer(3333);
+ if(MainActivity.ins==null) {
+ var intent = new Intent(this, MainActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ }
+
+ BroadcastReceiver rece;
+ var intentFilter = new IntentFilter(Intent.ACTION_MEDIA_MOUNTED);
+ intentFilter.addDataScheme("file");
+ registerReceiver(rece = new BroadcastReceiver(){
+ long lastMs;
+ final char[] pass = {'8','8','8'};
+ @Override
+ public void onReceive(Context context, Intent intent) { // in UI Thread
+ var path = intent.getData().getPath();
+ Util.println("\nMEDIA_MOUNTED path: "+path);
+ var ms = System.currentTimeMillis();
+ if(ms-lastMs<1000) return;
+ lastMs = ms;
+ Util.downId = 0;
+ var acti = MainActivity.ins;
+ Util.makeText(MainService.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");
+ if(acti!=null) acti.runOnUiThread(() -> Util.makeText(acti, "No program File").show());
+ return;
+ }
+ if(size==0) {
+ Util.println("zip size is 0");
+ if(acti!=null) acti.runOnUiThread(() -> Util.makeText(acti, "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();
+ Util.println("Import Succeed");
+ if(acti!=null) acti.runOnUiThread(() -> {
+ Util.makeText(acti, "Import Succeed 导入成功").show();
+ acti.initProg(json);
+ });
+ else {
+ var fOut = new FileOutputStream(Util.programDir + "/program");
+ fOut.write(json);
+ fOut.flush();
+ fOut.getFD().sync();
+ fOut.close();
+ var inten = new Intent(MainService.this, MainActivity.class);
+ inten.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(inten);
+ }
+ var inten = new Intent("com.xixun.AccessibilityService");
+ inten.putExtra("newProgram", "USB");
+ sendBroadcast(inten);
+ } catch (Exception e) {
+ Util.printStackTrace(e);
+ if(acti!=null) acti.runOnUiThread(() -> Util.makeText(acti, Util.toStr(e)).show());
+ }
+ }).start();
+ }
+ }, intentFilter);
+ reces.add(rece);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ Util.println("==<< MainService onDestroy <<<< this "+hashCode());
+ ins = null;
+ for(var rece : reces) {
+ try {
+ unregisterReceiver(rece);
+ } catch (Throwable ignored){
+ }
+ }
+ System.gc();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ throw new UnsupportedOperationException("Not yet implemented");
+ }
+}
\ No newline at end of file
diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/Prog.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/Prog.java
new file mode 100644
index 0000000..6267084
--- /dev/null
+++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/Prog.java
@@ -0,0 +1,814 @@
+package com.xixun.xixunplayer;
+
+import android.annotation.SuppressLint;
+import android.content.ContentValues;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.net.Uri;
+import android.os.CountDownTimer;
+import android.speech.tts.TextToSpeech;
+import android.view.Choreographer;
+import android.view.View;
+import android.webkit.JavascriptInterface;
+import android.webkit.WebView;
+import android.widget.ImageView;
+
+import java.io.File;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.SimpleTimeZone;
+import java.util.TimeZone;
+
+import gnph.util.JSList;
+import gnph.util.JSMap;
+import pl.droidsonroids.gif.GifImageView;
+
+@SuppressLint("ViewConstructor")
+public class Prog extends AbsLayout {
+
+ ArrayList pages = new ArrayList<>();
+ ArrayList calls = new ArrayList<>();
+ boolean isInsert;
+
+ @SuppressLint("SetJavaScriptEnabled")
+ public Prog(JSMap task, Context context) {
+ super(context);
+ isInsert = task.bool("insert");
+ JSList jpages = task.jslist("items");
+ JSList partLengths = task.jslist("partLengths");
+ var isVertical = task.bool("isVertical");
+ var partObj = task;
+ if(partLengths==null) {
+ try {
+ var _program = jpages.get(0).jsmap("_program");
+ partLengths = _program.jslist("splitWidths");
+ isVertical = _program.bool("isVer");
+ partObj = _program;
+ } catch (Throwable ignored){}
+ }
+ var width = partObj.intg("width");
+ var height = partObj.intg("height");
+ if(Util.custom==Util.Custom.Yishi) {
+ if(width==0) width = Util.screenWidth;
+ if(height==0) height = Util.screenHeight;
+ }
+ AbsLayout box;
+ if(partLengths==null || partLengths.size() <= 1) box = this;
+ else {
+ box = new AbsLayout(context);
+ addView(box, new AbsLayout.LayoutParams(0, 0, width, height));
+ var mask = new View(context);
+ mask.setBackgroundColor(0xff000000);
+ var len0 = partLengths.get(0).intValue();
+ addView(mask, isVertical ? new AbsLayout.LayoutParams(0, len0, width, height - len0) : new AbsLayout.LayoutParams(len0, 0, width - len0, height));
+ int x = 0, y = 0;
+ for(int i=1; i layers = _program.jslist("layers");
+ var isSimple = _program.intg("version")==2;
+ if(layers==null || layers.isEmpty()) continue;
+ if(isSimple) {
+ width = _program.intg("width", Util.screenWidth);
+ height = _program.intg("height", Util.screenHeight);
+ }
+ var page = new Page();
+ page.id = _program.str("_id");
+ page.name = _program.str("name");
+ page.type = "advanced";
+ page.repeatTimes = pageMap.intg("repeatTimes", 1);
+ page.parse(pageMap.jslist("schedules"));
+ var waitAudio = pageMap.bool("waitAudio");
+ HashMap videoMap = new HashMap<>();
+ for(int ll=layers.size()-1; ll>=0; ll--) {
+ var layer = new Layer();
+ layer.isLoop = layers.get(ll).bool("repeat");
+ JSList sources = layers.get(ll).jslist("sources");
+ var border = layers.get(ll).jsmap("border");
+ SrcBorder bdEle = null;
+ int bdWidth = 0, bdStart = Integer.MAX_VALUE, bdEnd = 0;
+ if(border!=null) {
+ bdEle = new SrcBorder(this, Util.programDir+"/"+border.stnn("img"), border.stnn("eff"), border.intg("speed"));
+ bdWidth = bdEle.img.getHeight();
+ }
+ var src = new Source();
+ for(var source : sources) {
+ src.type = source.stnn("_type");
+ if(src.type.isEmpty()) continue;
+ var dur = source.intg("timeSpan")*1000;
+ if(dur==0) {
+ Util.println("\nError: timeSpan is 0. _type: "+src.type);
+ continue;
+ }
+ var geo = isSimple ? new AbsLayout.LayoutParams(0, 0, width, height) : new AbsLayout.LayoutParams(source.intg("left")+bdWidth, source.intg("top")+bdWidth, source.intg("width")-bdWidth-bdWidth, source.intg("height")-bdWidth-bdWidth);
+ var notAudio = ! src.type.equals("Audio");
+ if(notAudio) {
+ if(geo.width<=0 || geo.height<=0) {
+ if(Util.custom==Util.Custom.Yishi) {
+ geo.width = width;
+ geo.height = height;
+ } else {
+ Util.println("\nError: width or height is 0. _type: "+src.type+" width: "+geo.width+" height: "+geo.height);
+ continue;
+ }
+ }
+ if(box != this && ((geo.y>=height && height>0) || (geo.x>=width && width>0))) {
+ Util.println("\nError: y>=height or x>=width. _type: "+src.type+" width: "+width+" height: "+height+" x: "+geo.x+" y: "+geo.y);
+ continue;
+ }
+ }
+ src.startTime = isSimple ? (layer.srcs.isEmpty() ? 0 : layer.srcs.get(layer.srcs.size()-1).endTime) : source.intg("playTime")*1000;
+ if(bdStart > src.startTime) bdStart = src.startTime;
+ src.endTime = src.startTime + dur;
+ if(bdEnd < src.endTime) bdEnd = src.endTime;
+ if(layer.dur < src.endTime) layer.dur = src.endTime;
+ if(notAudio || waitAudio) {
+ if(page.sDur < src.endTime) page.sDur = src.endTime;
+ } else if(page.audioDur < src.endTime) page.audioDur = src.endTime;
+
+ src.entryDur = source.intg("entryEffectTimeSpan")*60;
+ if(src.entryDur > 0) {
+ var effect = source.str("entryEffect");
+ if(effect == null || effect.equalsIgnoreCase("None")) src.entryDur = 0;
+ else if(effect.equalsIgnoreCase("Random")) src.isEntryRand = true;
+ else {
+ effect = effect.replace("MOVING", "MOVE");
+ effect = effect.replace("LEFT_TOP", "TL");
+ effect = effect.replace("RIGHT_TOP", "TR");
+ effect = effect.replace("RIGHT_BOTTOM", "BR");
+ effect = effect.replace("LEFT_BOTTOM", "BL");
+ effect = effect.replace("ALPHA", "FADE");
+ effect = effect.replace("_IN", "");
+ effect = effect.replace("_OUT", "");
+ try {
+ src.entryEff = Effect.valueOf(effect);
+ } catch (Throwable e) {
+ if(effect.equalsIgnoreCase("ROTATE_RIGHT")) src.entryEff = Effect.ROTATE;
+ else if(effect.equalsIgnoreCase("ROTATE_LEFT")) src.entryEff = Effect.ROTATE_R;
+ else src.entryDur = 0;
+ }
+ }
+ }
+ src.exitDur = source.intg("exitEffectTimeSpan")*60;
+ if(src.exitDur > 0) {
+ var effect = source.str("exitEffect");
+ if(effect == null || effect.equalsIgnoreCase("None")) src.exitDur = 0;
+ else if(effect.equalsIgnoreCase("Random")) src.isExitRand = true;
+ else {
+ effect = effect.replace("MOVING", "MOVE");
+ effect = effect.replace("LEFT_TOP", "TL");
+ effect = effect.replace("RIGHT_TOP", "TR");
+ effect = effect.replace("RIGHT_BOTTOM", "BR");
+ effect = effect.replace("LEFT_BOTTOM", "BL");
+ effect = effect.replace("ALPHA", "FADE");
+ effect = effect.replace("_IN", "");
+ effect = effect.replace("_OUT", "");
+ try {
+ src.exitEff = Effect.valueOf(effect);
+ } catch (Throwable e) {
+ if(effect.equalsIgnoreCase("ROTATE_RIGHT")) src.exitEff = Effect.ROTATE;
+ else if(effect.equalsIgnoreCase("ROTATE_LEFT")) src.exitEff = Effect.ROTATE_R;
+ else src.exitDur = 0;
+ }
+ }
+ }
+ if(src.exitDur!=0) src.exitStart = dur*60/1000 - src.exitDur;
+
+ src.alpha = (float) source.dbl("opacity", 1);
+ var breathe = source.dbl("breathe");
+ if(breathe > 0) src.breathe = (int) Math.round(60 / breathe);
+ var blink = source.dbl("blink");
+ if(blink > 0) src.blinkHalf = (int) Math.round(30 / blink);
+ src.rotate = (float) source.dbl("rotate");
+ src.scaleX = (float) source.dbl("scaleX", 1);
+ src.scaleY = (float) source.dbl("scaleY", 1);
+
+ var id = source.str("id");
+ var fileExt = source.stnn("fileExt");
+ if(id!=null && fileExt.startsWith(".") && new File(Util.programDir + "/" + id + fileExt).exists()) id += fileExt;
+ src.view = null;
+ if(src.type.equals("Image")) {
+ if(id==null) continue;
+ var isGif = fileExt.toLowerCase().endsWith("gif");
+ if(isGif) {
+ var imgView = new GifImageView(context);
+ imgView.setImageURI(src.uri = Uri.fromFile(new File(Util.programDir + "/" + id)));
+ imgView.setScaleType(ImageView.ScaleType.FIT_XY);
+ src.view = imgView;
+ } else {
+ var imgView = new ImageView(context);
+ var file = Util.programDir+"/"+id;
+ new Thread(()->{
+ try {
+ var ttt = System.currentTimeMillis();
+ var img = BitmapFactory.decodeFile(file);
+// if(img.getByteCount()>1500*1500*4) {
+// var matrix = new Matrix();
+// matrix.setScale(0.5f, 0.5f);
+// img = Bitmap.createBitmap(img, 0, 0, img.getWidth(), img.getHeight(), matrix, true);
+// }
+ ttt = System.currentTimeMillis() - ttt;
+ Util.println(" img "+img.getWidth()+"x"+img.getHeight()+" "+ttt);
+ Bitmap finalImg = img;
+ MainActivity.ins.runOnUiThread(() -> imgView.setImageBitmap(finalImg));
+ } catch (Throwable e) {
+ var stackTrace = Util.toStackTrace(e);
+ Util.println(stackTrace);
+ var img = Bitmap.createBitmap(600, 400, Bitmap.Config.ARGB_8888);
+ var paint = new Paint();
+ paint.setColor(Color.RED);
+ new Canvas(img).drawText(stackTrace, 0, 0, paint);
+ MainActivity.ins.runOnUiThread(() -> imgView.setImageBitmap(img));
+ }
+ }).start();
+ imgView.setScaleType(ImageView.ScaleType.FIT_XY);
+ src.view = imgView;
+ }
+ } else if(src.type.endsWith("Video")) {
+ var isLive = src.type.startsWith("Live");
+ var url = source.str("url");
+ if(isLive) {
+ if(url==null) continue;
+ } else if(id==null) continue;
+ var key = isLive ? url : id + src.startTime + src.endTime;
+ var exist = videoMap.get(key);
+ if(exist!=null) {
+ var geoOld = (AbsLayout.LayoutParams) exist.getLayoutParams();
+ if(geo.width*geo.height > geoOld.width*geoOld.height) {
+ exist.setLayoutParams(geo);
+ geo = geoOld;
+ }
+ src.view = new SrcCopy(context, exist);
+ ((SrcCopy) src.view).scaleX = 0;
+ } else {
+ src.view = new SrcVideo(context, isLive ? url : Util.programDir+"/"+id, source.intg("vol", 100) / 100.0f, dur, source.bool("useSW"), isLive);
+ videoMap.put(key, src.view);
+ }
+ } else if(src.type.equals("Audio")) {
+ if(id==null) continue;
+ src.view = new SrcVideo(context, Util.programDir + "/" +id, source.intg("vol", 100) / 100.0f, dur, false, false);
+ } else if(src.type.equals("Scroll")) {
+ JSList imgs = source.jslist("imgs");
+ if(imgs.isEmpty()) continue;
+ var images = new ArrayList();
+ for(var img : imgs) images.add(BitmapFactory.decodeFile(Util.programDir+"/"+img));
+ var directStr = source.str("direct");
+ src.view = new SrcScroll(this, images, directStr==null ? 0 : directStr.charAt(0), source.dbl("speed"));
+ src.text = source.str("tts");
+ if(src.text!=null) {
+ src.tts = new TextToSpeech(context, (int status)->{
+ Util.println("status: "+status+" "+(status==TextToSpeech.SUCCESS));
+ }, "com.iflytek.speechcloud");
+ src.tts.setSpeechRate((float) source.dbl("speechRate"));
+ }
+ } else if(src.type.startsWith("MultiPng") || src.type.equals("SplitText")) {
+ JSList imgs = source.jslist("arrayPics");
+ if(imgs.isEmpty()) continue;
+ var mode = source.str("curchange");
+ var hasTTS = src.type.endsWith("Audio");
+ var speechRate = (float) source.dbl("voiceRate");
+ if(mode!=null ? mode.endsWith("roll") : (imgs.size()==1 && imgs.get(0).intg("picDuration")==0)) {
+ var img = imgs.get(0);
+ var images = new ArrayList();
+ images.add(BitmapFactory.decodeFile(Util.programDir+"/"+img.stnn("id")));
+ var speed = img.dbl("scrollSpeed");
+ if(speed==0) {
+ var scrollDur = img.dbl("effectSpeed");
+ if(scrollDur!=0) speed = 1000 / scrollDur;
+ }
+ char direct = 0;
+ var directStr = img.str("effect");
+ if(directStr!=null && ! directStr.equals("no")) {
+ int idx = directStr.lastIndexOf(' ');
+ if(idx > -1) direct = directStr.charAt(idx+1);
+ }
+ src.view = new SrcScroll(this, images, direct, speed);
+ if(hasTTS) {
+ src.text = img.str("text");
+ if(src.text!=null) {
+ src.tts = new TextToSpeech(context, (int status)->{
+ Util.println("status: "+status+" "+(status==TextToSpeech.SUCCESS));
+ }, "com.iflytek.speechcloud");
+ src.tts.setSpeechRate(speechRate);
+ }
+ }
+ } else {
+ var ele0 = src;
+ for(var map : imgs) {
+ var picDur = map.intg("picDuration")*1000;
+ if(picDur==0) picDur = dur / imgs.size();
+ src.type = "Image";
+ if(src!=ele0) {
+ src.startTime = layer.srcs.get(layer.srcs.size()-1).endTime;
+ src.entryEff = ele0.entryEff;
+ src.exitEff = ele0.exitEff;
+ src.entryDur = ele0.entryDur;
+ src.exitDur = ele0.exitDur;
+ src.exitStart = ele0.exitStart;
+ src.isEntryRand = ele0.isEntryRand;
+ src.isExitRand = ele0.isExitRand;
+ src.alpha = ele0.alpha;
+ src.breathe = ele0.breathe;
+ src.blinkHalf = ele0.blinkHalf;
+ src.rotate = ele0.rotate;
+ src.scaleX = ele0.scaleX;
+ src.scaleY = ele0.scaleY;
+ }
+ src.endTime = src.startTime + picDur;
+ if(hasTTS) {
+ src.text = map.str("text");
+ if(src.text!=null) {
+ src.tts = new TextToSpeech(context, (int status)->{
+ Util.println("status: "+status+" "+(status==TextToSpeech.SUCCESS));
+ }, "com.iflytek.speechcloud");
+ src.tts.setSpeechRate(speechRate);
+ }
+ }
+ var imgView = new ImageView(context);
+ imgView.setImageURI(Uri.fromFile(new File(Util.programDir + "/" + map.stnn("id"))));
+ imgView.setScaleType(ImageView.ScaleType.FIT_XY);
+ src.view = imgView;
+ src.view.setVisibility(GONE);
+ src.view.setLayoutParams(geo);
+ box.addView(src.view);
+ layer.srcs.add(src);
+ src = new Source();
+ }
+ }
+ } else if(src.type.equals("DigitalClock")) src.view = new SrcDigitalClock(this, source);
+ else if(src.type.startsWith("DigitalClock")) src.view = new SrcDigiClock(this, source);
+ else if(src.type.equals("AnalogClock")) src.view = new SrcAnaClock(this, geo.width, geo.height, Util.programDir + "/" + id, source);
+ else if(src.type.equals("WebURL")) src.view = new SrcWeb(this, source);
+ else if(src.type.equals("Timer")) src.view = new SrcTimer(this, source);
+ else if(src.type.equals("Countdown")) src.view = new SrcCountdown(this, source);
+ else if(src.type.startsWith("Environ")) src.view = new SrcEnviron(this, source);
+ else if(src.type.startsWith("Weather")) src.view = new SrcWeather(context, source);
+ else if(src.type.startsWith("VistorSource")) src.view = new SrcVisitor(this, source);
+ else if(src.type.startsWith("MultiLineText")) src.view = new SrcSensor(this, source, geo.width, geo.height);
+ else if(src.type.startsWith("SingleLineText")) {
+ var webView = new WebView(context);
+ var settings = webView.getSettings();
+ settings.setJavaScriptEnabled(true);
+ webView.setVerticalScrollBarEnabled(false);
+ webView.setHorizontalScrollBarEnabled(false);
+ webView.setBackgroundColor(Color.TRANSPARENT);
+ webView.setInitialScale(100);
+ webView.setLayoutParams(new AbsLayout.LayoutParams(0, -geo.height, geo.width, geo.height));
+ var html = source.stnn("html");
+ var prefix = "";
+ var suffix = "";
+ var speed = source.dbl("speed");
+ if(speed!=0) speed = 1000.0 / speed;
+ var imgs = new ArrayList();
+ imgs.add(Bitmap.createBitmap(geo.width, geo.height, Bitmap.Config.ARGB_8888));
+ var view = new SrcScroll(this, imgs, 'l', speed);
+ src.view = view;
+ webView.addJavascriptInterface(new Object() {
+ @JavascriptInterface
+ public void setWidth(int width) {
+ MainActivity.ins.runOnUiThread(() -> {
+ var hhh = view.imgs.get(0).getHeight();
+ var www = width + hhh;
+ webView.getLayoutParams().width = www;
+ view.imgs.set(0, Bitmap.createBitmap(www, hhh, Bitmap.Config.ARGB_8888));
+ addView(webView);
+ var atimer = new CountDownTimer(500, 500) {
+ @Override
+ public void onTick(long millisUntilFinished) {}
+ @Override
+ public void onFinish() {
+ var canvas = new Canvas(view.imgs.get(0));
+ webView.draw(canvas);
+ webView.setVisibility(GONE);
+ view.freshCnt = view.cur = 0;
+ if(view.direct=='l') view.end = -(view.imgs.get(0).getWidth()-view.step);
+ else if(view.direct=='r') view.end = view.imgs.get(0).getWidth()-view.step;
+ view.invalidate();
+ }
+ };
+ if(isShown()) atimer.start();
+ else timer = atimer;
+ });
+ }
+ }, "java");
+ webView.loadDataWithBaseURL(null, prefix+html+suffix, "text/html", "UTF-8", null);
+ }
+ else continue;
+ if(src.view==null) continue;
+ src.view.setVisibility(GONE);
+ src.view.setLayoutParams(geo);
+ (source.bool("isPreSplit") ? this : box).addView(src.view);
+ layer.srcs.add(src);
+ src = new Source();
+ }
+ if(bdEle!=null && bdStart < bdEnd) {
+ JSList geometry = border.jslist("geometry");
+ src.startTime = bdStart;
+ src.endTime = bdEnd;
+ src.rotate = (float) border.dbl("rotate");
+ src.view = bdEle;
+ src.view.setVisibility(GONE);
+ box.addView(src.view, new AbsLayout.LayoutParams(geometry.get(0).intValue(), geometry.get(1).intValue(), geometry.get(2).intValue(), geometry.get(3).intValue()));
+ layer.srcs.add(src);
+ }
+ if(! layer.srcs.isEmpty()) page.layers.add(layer);
+ }
+ if(page.sDur==0) {
+ if(page.audioDur > 0) page.sDur = page.audioDur;
+ else continue;
+ }
+ page.tDur = page.sDur * page.repeatTimes;
+ for_layer: for(int ll=0; ll= page.sDur) {
+ if(layer.srcs.size() > 1) layer.srcs.remove(ss--);
+ else {
+ page.layers.remove(ll--);
+ continue for_layer;
+ }
+ } else if(src.endTime > page.sDur) src.endTime = page.sDur;
+ }
+ if(layer.dur > page.sDur) layer.dur = page.sDur;
+ if(layer.dur == page.sDur) layer.isLoop = false;
+ }
+ pages.add(page);
+ }
+ }
+
+ void release() {
+ try {
+ setVisibility(GONE);
+ View view;
+ for(int cc=0; cc=exitStart) {
+ if(ff-1 <= exitStart && (blinkHalf!=0 || breathe!=0)) view.setAlpha(alpha);
+ var fff = ff - exitStart;
+ if(fff > exitDur) fff = exitDur;
+ if(exitEff == Effect.EXPAND_HOR) setScaleX(1 - fff / (float) exitDur);
+ else if(exitEff == Effect.EXPAND_VER) setScaleY(1 - fff / (float) exitDur);
+ else if(exitEff == Effect.EXPAND_LEFT) {
+ var rate = fff / (float) exitDur;
+ setScaleX(1 - rate);
+ view.setTranslationX(rate * w / 2);
+ } else if(exitEff == Effect.EXPAND_TOP) {
+ var rate = fff / (float) exitDur;
+ view.setScaleY(1 - rate);
+ view.setTranslationY(rate * h / 2);
+ } else if(exitEff == Effect.EXPAND_RIGHT) {
+ var rate = fff / (float) exitDur;
+ setScaleX(1 - rate);
+ view.setTranslationX(- rate * w / 2);
+ } else if(exitEff == Effect.EXPAND_BOTTOM) {
+ var rate = fff / (float) exitDur;
+ setScaleY(1 - rate);
+ view.setTranslationY(- rate * h / 2);
+ }
+ else if(exitEff == Effect.MOVE_LEFT) view.setTranslationX(- fff*w / (float) exitDur);
+ else if(exitEff == Effect.MOVE_RIGHT) view.setTranslationX(fff*w / (float) exitDur);
+ else if(exitEff == Effect.MOVE_TOP) view.setTranslationY(- fff*h / (float) exitDur);
+ else if(exitEff == Effect.MOVE_BOTTOM) view.setTranslationY(fff*h / (float) exitDur);
+ else if(exitEff == Effect.FADE) view.setAlpha(alpha - alpha * fff / exitDur);
+ else if(exitEff == Effect.ZOOM) {
+ setScaleX(1 - fff / (float) exitDur);
+ setScaleY(view.getScaleX());
+ } else if(exitEff == Effect.ZOOM_TL) {
+ var rate = fff / (float) exitDur;
+ view.setTranslationX(- rate * w / 2);
+ view.setTranslationY(- rate * h / 2);
+ setScaleX(1 - rate);
+ setScaleY(1 - rate);
+ } else if(exitEff == Effect.ZOOM_BR) {
+ var rate = fff / (float) exitDur;
+ view.setTranslationX(rate * w / 2);
+ view.setTranslationY(rate * h / 2);
+ setScaleX(1 - rate);
+ setScaleY(1 - rate);
+ } else if(exitEff == Effect.ZOOM_TR) {
+ var rate = fff / (float) exitDur;
+ view.setTranslationX(rate * w / 2);
+ view.setTranslationY(- rate * h / 2);
+ setScaleX(1 - rate);
+ setScaleY(1 - rate);
+ } else if(exitEff == Effect.ZOOM_BL) {
+ var rate = fff / (float) exitDur;
+ view.setTranslationX(- rate * w / 2);
+ view.setTranslationY(rate * h / 2);
+ setScaleX(1 - rate);
+ setScaleY(1 - rate);
+ } else if(exitEff == Effect.ROTATE) {
+ var rate = fff / (float) exitDur;
+ setScaleX(1 - rate);
+ setScaleY(view.getScaleX());
+ view.setRotation(rotate + rate * 360);
+ } else if(exitEff == Effect.ROTATE_R) {
+ var rate = fff / (float) exitDur;
+ setScaleX(1 - rate);
+ setScaleY(view.getScaleX());
+ view.setRotation(rotate - rate * 360);
+ }
+ } else {
+ resetEff();
+ if(blinkHalf!=0) view.setAlpha((ff / blinkHalf & 1) == 0 ? alpha : 0);
+ else if(breathe!=0) view.setAlpha((float) (Math.cos(ff%breathe*2*Math.PI/breathe)/2+0.5));
+ }
+ ff++;
+ }
+ void resetEff() {
+ view.setTranslationX(0);
+ view.setTranslationY(0);
+ view.setAlpha(alpha);
+ view.setScaleX(scaleX);
+ view.setScaleY(scaleY);
+ view.setRotation(rotate);
+ }
+ void setScaleX(float val) {
+ view.setScaleX(scaleX*val);
+ }
+ void setScaleY(float val) {
+ view.setScaleY(scaleY*val);
+ }
+ }
+ public static class Layer {
+ ArrayList srcs = new ArrayList<>();
+ long endMilli = Long.MAX_VALUE;
+ int dur;
+ boolean isLoop;
+ }
+ public enum Effect {
+ EXPAND_HOR, EXPAND_VER, EXPAND_LEFT, EXPAND_TOP, EXPAND_RIGHT, EXPAND_BOTTOM,
+ ZOOM, ZOOM_TL, ZOOM_TR, ZOOM_BR, ZOOM_BL,
+ ROTATE, ROTATE_R,
+ FADE,
+ MOVE_LEFT, MOVE_TOP, MOVE_RIGHT, MOVE_BOTTOM,
+ }
+ public static class Sche {
+ long startDate = -1, endDate = -1;
+ int startTime = -1, endTime = -1;
+ HashSet weeks;
+ }
+
+ public static class Page {
+ String id, name, type;
+ ArrayList layers = new ArrayList<>();
+ ArrayList sches;
+ long endMilli = Long.MAX_VALUE;
+ int sDur, tDur, repeatTimes, audioDur;
+
+ void hide() {
+ for(var layer : layers) for(var src : layer.srcs) src.hide();
+ }
+
+ void setMillis(long start, long cur) {
+ endMilli = start + sDur;
+ for(var layer : layers) {
+ if(layer.isLoop) layer.endMilli = start + layer.dur;
+ for(var src : layer.srcs) {
+ src.endMilli = start + src.endTime;
+ src.startMilli = start + src.startTime;
+ if(src.startTime == 0) {
+ if(src.view instanceof SrcVideo) ((SrcVideo)src.view).seekTo = cur - src.startMilli;
+ src.show();
+ }
+ }
+ }
+ if(Util.logOn) {
+ var values = new ContentValues();
+ values.put("time", cur);
+ values.put("dur", sDur);
+ values.put("id", id);
+ values.put("name", name);
+ values.put("type", type);
+ values.put("lat", Util.lat);
+ values.put("lng", Util.lng);
+ LogThread.rows.add(values);
+ }
+ }
+ void showHideSrcs(long milli) {
+ for(var layer : layers) {
+ for(var src : layer.srcs) {
+ if(src.view.getVisibility()==VISIBLE) {
+ if(milli >= src.endMilli) src.hide();
+ else src.doEff();
+ } else if(milli < src.endMilli && milli >= src.startMilli) {
+ if(src.view instanceof SrcVideo) ((SrcVideo)src.view).seekTo = milli - src.startMilli;
+ src.show();
+ }
+ }
+ if(milli >= layer.endMilli) {
+ layer.endMilli += layer.dur;
+ for(var src : layer.srcs) {
+ src.endMilli += layer.dur;
+ src.startMilli += layer.dur;
+ if(src.startTime > 0) src.hide();
+ else src.show();
+ }
+ }
+ }
+ }
+ boolean isScheOn(long milli) {
+ if(sches==null) return false;
+ var local = milli + TimeZone.getDefault().getOffset(milli);
+ int week = -1;
+ for(var sche : sches) {
+ if(notInRange(sche.startDate, local, sche.endDate)) continue;
+ if(notInRange(sche.startTime, local % 86400000L, sche.endTime)) continue;
+ if(sche.weeks==null) return true;
+ if(week==-1) week = ((int)(local / 86400000L) + 4) % 7;
+ if(sche.weeks.contains(week)) return true;
+ }
+ return false;
+ }
+ public boolean notInRange(long start, long val, long end) {
+ if(end==-1) return false;
+ return start <= end ? !(val >= start && val < end) : val >= end && val < start;
+ }
+ SimpleDateFormat dateFmt = new SimpleDateFormat("yyyy-MM-dd");
+ SimpleDateFormat timeFmt = new SimpleDateFormat("HH:mm:ss");
+ {
+ var zone = new SimpleTimeZone(0, "0");
+ dateFmt.getCalendar().setTimeZone(zone);
+ timeFmt.getCalendar().setTimeZone(zone);
+ }
+ public void parse(JSList schedules) {
+ if(schedules!=null) for(var schedule : schedules) {
+ var sche = new Sche();
+ var startTime = schedule.str("startTime");
+ if(startTime!=null) {
+ if(startTime.length()<=5) startTime += ":00";
+ try {
+ sche.startTime = (int)timeFmt.parse(startTime).getTime();
+ Util.println("startTime "+timeFmt.format(sche.startTime)+" "+sche.startTime);
+ } catch (ParseException e) {
+ Util.printStackTrace(e);
+ }
+ }
+ var endTime = schedule.str("endTime");
+ if(endTime!=null) {
+ if(endTime.length()<=5) endTime += ":00";
+ try {
+ sche.endTime = (int)timeFmt.parse(endTime).getTime();
+ Util.println("endTime "+timeFmt.format(sche.endTime)+" "+sche.endTime);
+ } catch (ParseException e) {
+ Util.printStackTrace(e);
+ }
+ }
+ if(sche.startTime==sche.endTime) sche.startTime = sche.endTime = -1;
+ var startDate = schedule.str("startDate");
+ if(startDate!=null) {
+ try {
+ sche.startDate = dateFmt.parse(startDate).getTime();
+ } catch (ParseException e) {
+ Util.printStackTrace(e);
+ }
+ }
+ var endDate = schedule.str("endDate");
+ if(endDate!=null) {
+ try {
+ sche.endDate = dateFmt.parse(endDate).getTime() + 86400000L;
+ } catch (ParseException e) {
+ Util.printStackTrace(e);
+ }
+ }
+ if(sche.startDate==sche.endDate) sche.startDate = sche.endDate = -1;
+ JSList weekFilter = schedule.jslist("weekFilter");
+ if(weekFilter!=null && ! weekFilter.isEmpty() && weekFilter.size() < 7) {
+ sche.weeks = new HashSet<>();
+ for(var week : weekFilter) sche.weeks.add(week.intValue() % 7);
+ }
+ if(sches==null) sches = new ArrayList<>();
+ sches.add(sche);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcAnaClock.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcAnaClock.java
new file mode 100644
index 0000000..a48e0f3
--- /dev/null
+++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcAnaClock.java
@@ -0,0 +1,166 @@
+package com.xixun.xixunplayer;
+
+import android.annotation.SuppressLint;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.RectF;
+import android.view.Choreographer;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+
+import java.util.Calendar;
+import java.util.TimeZone;
+
+import gnph.util.JSMap;
+
+@SuppressLint("ViewConstructor")
+public class SrcAnaClock extends View implements Choreographer.FrameCallback {
+
+ Calendar calendar;
+ Bitmap img;
+ int pinHourColor, pinMinColor, pinSecColor;
+ Path hPath, mPath, sPath;
+ float hAngle, mAngle, sAngle;
+ Paint paintPin = new Paint(), paint = new Paint();
+ boolean showSecHand;
+
+ public SrcAnaClock(Prog prog, float w, float h, String path, JSMap source) {
+ super(prog.getContext());
+ var timeZoneStr = source.str("timeZone");
+ //if(timeZoneStr!=null) timeZone = ZoneId.of(timeZoneStr);
+ var timeZone = timeZoneStr==null ? null : TimeZone.getTimeZone(timeZoneStr);
+ calendar = timeZone==null ? Calendar.getInstance() : Calendar.getInstance(timeZone);
+
+ var sideLen = Math.min(w, h);
+ var halfSide = sideLen / 2;
+ var lineWidth = sideLen / 128;
+ if(lineWidth < 1) lineWidth = 1;
+
+ paintPin.setAntiAlias(true);
+ paintPin.setStyle(Paint.Style.FILL_AND_STROKE);
+ paintPin.setStrokeWidth(lineWidth);
+ paintPin.setStrokeJoin(Paint.Join.ROUND);
+ paint.setAntiAlias(true);
+ paint.setStyle(Paint.Style.FILL);
+ var fontSize = sideLen / 11;
+ paint.setTextSize(fontSize);
+
+ img = BitmapFactory.decodeFile(path);
+ if(img==null) {
+ img = Bitmap.createBitmap((int)w, (int)h, Bitmap.Config.ARGB_8888);
+ var canvas = new Canvas(img);
+ canvas.translate(w / 2, h / 2);
+ if(source.bool("showBg")) {
+ var bgColor = Color.parseColor(source.stnn("bgColor"));
+ var opacity = source.dbl("opacity", -1);
+ if(opacity!=-1) bgColor = bgColor & 0x00ffffff | ((int)opacity*255)<<24;
+ paint.setColor(bgColor);
+ canvas.drawOval(-halfSide, -halfSide, halfSide, halfSide, paint);
+ }
+ var minMarkSize = sideLen/48;
+ var hourMarkSize = sideLen/32;
+ var scaleHourColor = Color.parseColor(source.stnn("scaleHourColor"));
+ var scaleMinColor = Color.parseColor(source.stnn("scaleMinColor"));
+ var showScaleNum = source.bool("showScaleNum");
+ var rScale = (sideLen - Math.max(minMarkSize, hourMarkSize)) / 2;
+ var rNum = halfSide * 0.83;
+ for(int i=0; i<60; i++) {
+ var k = i * Math.PI / 30;
+ var x = (float) (Math.sin(k) * rScale);
+ var y = (float) (-Math.cos(k) * rScale);
+ if(i % 5 > 0) {
+ var rr = minMarkSize / 2f;
+ paint.setColor(scaleMinColor);
+ canvas.drawOval(x-rr, y-rr, x+rr, y+rr, paint);
+ } else {
+ var rr = hourMarkSize / 2f;
+ paint.setColor(scaleHourColor);
+ canvas.drawOval(x-rr, y-rr, x+rr, y+rr, paint);
+ if(showScaleNum) {
+ var hour = i/5;
+ if(hour==0) hour = 12;
+ x = (float) (Math.sin(k) * rNum);
+ y = (float) (-Math.cos(k) * rNum);
+ canvas.drawText(String.valueOf(hour), x - fontSize*(hour<10 ? 0.28f : 0.6f), y + fontSize*0.355f, paint);
+ }
+ }
+ }
+ }
+ pinHourColor = Color.parseColor(source.stnn("pinHourColor"));
+ pinMinColor = Color.parseColor(source.stnn("pinMinColor"));
+ pinSecColor = Color.parseColor(source.stnn("pinSecColor"));
+ var hhLen = (float) source.dbl("pinHourLen", 50);
+ var mhLen = (float) source.dbl("pinMinLen", 75);
+ var shLen = (float) source.dbl("pinSecLen", 100);
+ var hhWidth = (float) source.dbl("pinHourWidth", 15);
+ var mhWidth = (float) source.dbl("pinMinWidth", 10);
+ var shWidth = (float) source.dbl("pinSecWidth", 5);
+ showSecHand = source.bool("showSecond");
+
+ var rx = hhWidth*sideLen/400;
+ hPath = new Path();
+ hPath.moveTo(rx, 0);
+ hPath.lineTo(0, hhLen*sideLen/-200);
+ hPath.lineTo(-rx, 0);
+ hPath.close();
+
+ rx = mhWidth*sideLen/400;
+ mPath = new Path();
+ mPath.moveTo(rx, 0);
+ mPath.lineTo(0, mhLen*sideLen/-200);
+ mPath.lineTo(-rx, 0);
+ mPath.close();
+ rx = shWidth*sideLen/400;
+ sPath = new Path();
+ sPath.moveTo(rx, 0);
+ sPath.lineTo(0, shLen*sideLen/-200);
+ sPath.lineTo(-rx, 0);
+ sPath.close();
+ prog.calls.add(this);
+ }
+
+ void cal() {
+ //var time = timeZone==null ? LocalTime.now() : LocalTime.now(timeZone);
+ calendar.setTimeInMillis(lastSec*1000);
+ sAngle = calendar.get(Calendar.SECOND) * 6;
+ mAngle = calendar.get(Calendar.MINUTE) * 6 + sAngle/60;
+ hAngle = calendar.get(Calendar.HOUR_OF_DAY) * 30 + mAngle/12;
+ }
+ @Override
+ protected void onDraw(@NonNull Canvas canvas) {
+ super.onDraw(canvas);
+ if(img != null) canvas.drawBitmap(img, null, new RectF(0,0, getWidth(), getHeight()), null);
+ canvas.translate(getWidth()/2f, getHeight()/2f);
+
+ paintPin.setColor(pinHourColor);
+ canvas.rotate(hAngle);
+ canvas.drawPath(hPath, paintPin);
+
+ paintPin.setColor(pinMinColor);
+ canvas.rotate(mAngle-hAngle);
+ canvas.drawPath(mPath, paintPin);
+ if(showSecHand) {
+ paintPin.setColor(pinSecColor);
+ canvas.rotate(sAngle-mAngle);
+ canvas.drawPath(sPath, paintPin);
+ }
+ }
+
+ long lastSec;
+
+ @Override
+ public void doFrame(long ms) {
+ if(! isShown()) return;
+ var sec = ms / 1000;
+ if(sec != lastSec) {
+ lastSec = sec;
+ cal();
+ invalidate();
+ }
+ }
+}
\ No newline at end of file
diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcBorder.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcBorder.java
new file mode 100644
index 0000000..10f6c60
--- /dev/null
+++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcBorder.java
@@ -0,0 +1,89 @@
+package com.xixun.xixunplayer;
+
+import android.annotation.SuppressLint;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Path;
+import android.view.Choreographer;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+
+@SuppressLint("ViewConstructor")
+public class SrcBorder extends View implements Choreographer.FrameCallback {
+ Bitmap img;
+ int[] lens = new int[4];
+ Path[] paths = new Path[4];
+ int[] offs = new int[]{0,0,0,0};
+ byte eff;
+ int interval;
+
+ public SrcBorder(Prog prog, String path, String effStr, int speed) {
+ super(prog.getContext());
+ img = BitmapFactory.decodeFile(path);
+ if(effStr.startsWith("ro")) eff = 'r';
+ else if(effStr.startsWith("bl")) eff = 'b';
+ if(eff=='r') interval = speed==1 ? 4 : (speed==2 ? 2 : 1);
+ else interval = speed==1 ? 30 : (speed==2 ? 15 : 4);
+ if(eff!=0) prog.calls.add(this);
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ lens[0] = lens[2] = w;
+ lens[1] = lens[3] = h;
+ int bdWidth = img.getHeight();
+ for(int i=0; i<2; i++) {
+ Path path = new Path();
+ path.moveTo(0,0);
+ path.lineTo(lens[i], 0);
+ path.lineTo(lens[i] - bdWidth, bdWidth);
+ path.lineTo(bdWidth, bdWidth);
+ path.close();
+ paths[i] = paths[i+2] = path;
+ }
+ }
+
+ @Override
+ protected void onDraw(@NonNull Canvas canvas) {
+ super.onDraw(canvas);
+ if(eff!='b' || offs[0] <= 0) {
+ int bdWidth = img.getHeight();
+ int bdLen = img.getWidth();
+ offs[1] = (offs[0] + lens[0]-bdWidth)%bdLen;
+ offs[2] = (offs[0] + lens[0]+lens[1]-bdWidth*2)%bdLen;
+ offs[3] = (offs[0] + lens[0]*2+lens[1]-bdWidth*3)%bdLen;
+ for(int ll=0; ll<4; ll++) {
+ canvas.save();
+ canvas.clipPath(paths[ll]);
+ for(int x=-offs[ll]; x";
+ suffix = "";
+ try {
+ var start = html.substring(0, 16).trim();
+ if(start.startsWith("")) {
+ suffix = "";
+ html = html.substring(6);
+ } else if(start.startsWith(" imgs = new HashMap<>();
+ ImageView[] yearComps = {new ImageView(getContext()), new ImageView(getContext()), new ImageView(getContext()), new ImageView(getContext())};
+ ImageView[] monthComps = {new ImageView(getContext()), new ImageView(getContext())};
+ ImageView[] dayComps = {new ImageView(getContext()), new ImageView(getContext())};
+ ImageView weekComp = new ImageView(getContext()), ampmComp = new ImageView(getContext());
+ ImageView[] hourComps = {new ImageView(getContext()), new ImageView(getContext())};
+ ImageView[] minComps = {new ImageView(getContext()), new ImageView(getContext())};
+ ImageView[] secComps = {new ImageView(getContext()), new ImageView(getContext())};
+
+ Calendar calendar;
+ boolean multiline, weekly, isSingleMD, isHour12, isSingleHour;
+
+ public SrcDigiClock(Prog prog, JSMap json) {
+ super(prog.getContext());
+ setGravity(Gravity.CENTER);
+ var timeZoneStr = json.str("timeZone");
+ //if(timeZoneStr!=null) timeZone = ZoneId.of(timeZoneStr);
+ var timeZone = timeZoneStr==null ? null : TimeZone.getTimeZone(timeZoneStr);
+ calendar = timeZone==null ? Calendar.getInstance() : Calendar.getInstance(timeZone);
+ var spaceWidth = json.dbl("spaceWidth");
+ JSList pics = json.jslist("arrayPics");
+ if(pics!=null && ! pics.isEmpty() && pics.get(0).containsKey("startX")) {
+ var big = BitmapFactory.decodeFile(Util.programDir+"/"+json.stnn("id"));
+ for(var pic : pics) {
+ var startX = pic.intg("startX");
+ var startY = pic.intg("startY");
+ imgs.put(pic.str("name"), Bitmap.createBitmap(big, startX, startY, pic.intg("endX")-startX, pic.intg("endY")-startY));
+ }
+ } else for(var pic : pics) imgs.put(pic.str("name"), BitmapFactory.decodeFile(Util.programDir+"/"+pic.stnn("id")));
+ int dateStyle = json.intg("dateStyle");
+ isSingleMD = dateStyle==1||dateStyle==2||dateStyle==4||dateStyle==6||dateStyle==8||dateStyle==10||dateStyle==12;
+ isSingleHour = json.intg("timeStyle") != 0;
+ var timeSep = imgs.get("maohao");
+ weekly = json.bool("weekly");
+ isHour12 = json.bool("hour12");
+ var AmPm = isHour12 && json.bool("AmPm");
+ var hour = json.bool("hour");
+ var min = json.bool("min");
+ var sec = json.bool("sec");
+ multiline = json.bool("multiline");
+ addStretch();
+ if(multiline) {
+ vertical();
+ var hBox = new LinearBox(this).horizontal();
+ hBox.addStretch();
+ addDate(dateStyle, json, hBox);
+ hBox.addStretch();
+ if(weekly) {
+ hBox = new LinearBox(this).horizontal();
+ hBox.addStretch();
+ hBox.addView(weekComp);
+ hBox.addStretch();
+ }
+ hBox = new LinearBox(this).horizontal();
+ hBox.addStretch();
+ if(AmPm) {
+ hBox.addView(ampmComp);
+ hBox.addSpacing((int)spaceWidth);
+ }
+ if(hour) {
+ hBox.addView(hourComps[0]);
+ hBox.addView(hourComps[1]);
+ }
+ if(hour&&min) hBox.addView(newImgView(timeSep));
+ if(min) {
+ hBox.addView(minComps[0]);
+ hBox.addView(minComps[1]);
+ }
+ if(min&&sec) hBox.addView(newImgView(timeSep));
+ if(sec) {
+ hBox.addView(secComps[0]);
+ hBox.addView(secComps[1]);
+ }
+ hBox.addStretch();
+ } else {
+ setOrientation(HORIZONTAL);
+ addDate(dateStyle, json, this);
+ if(getChildCount()>1) addSpacing((int)spaceWidth*2);
+ if(weekly) {
+ addView(weekComp);
+ addSpacing((int)spaceWidth*2);
+ }
+ if(AmPm) {
+ addView(ampmComp);
+ addSpacing((int)spaceWidth);
+ }
+ if(hour) {
+ addView(hourComps[0]);
+ addView(hourComps[1]);
+ }
+ if(hour&&min) addView(newImgView(timeSep));
+ if(min) {
+ addView(minComps[0]);
+ addView(minComps[1]);
+ }
+ if(min&&sec) addView(newImgView(timeSep));
+ if(sec) {
+ addView(secComps[0]);
+ addView(secComps[1]);
+ }
+ }
+ addStretch();
+ prog.calls.add(this);
+ }
+
+ ImageView newImgView(Bitmap img) {
+ var imgv = new ImageView(getContext());
+ imgv.setImageBitmap(img);
+ return imgv;
+ }
+ void addDate(int dateStyle, JSMap layer, LinearLayout tar) {
+ if(dateStyle==0 || dateStyle==1) {
+ addYear(layer, tar, imgs.get("YEAR"));
+ if(layer.bool("month")) {
+ tar.addView(monthComps[0]);
+ tar.addView(monthComps[1]);
+ tar.addView(newImgView(imgs.get("MONTH")));
+ }
+ if(layer.bool("day")) {
+ tar.addView(dayComps[0]);
+ tar.addView(dayComps[1]);
+ tar.addView(newImgView(imgs.get("DAY")));
+ }
+ } else if(dateStyle==2 || dateStyle==3) {
+ var sep = imgs.get("xiegang");
+ if(layer.bool("month")) {
+ tar.addView(monthComps[0]);
+ tar.addView(monthComps[1]);
+ tar.addView(newImgView(sep));
+ }
+ if(layer.bool("day")) {
+ tar.addView(dayComps[0]);
+ tar.addView(dayComps[1]);
+ tar.addView(newImgView(sep));
+ }
+ addYear(layer, tar, null);
+ } else if(dateStyle==4 || dateStyle==5) {
+ var sep = imgs.get("xiegang");
+ if(layer.bool("day")) {
+ tar.addView(dayComps[0]);
+ tar.addView(dayComps[1]);
+ tar.addView(newImgView(sep));
+ }
+ if(layer.bool("month")) {
+ tar.addView(monthComps[0]);
+ tar.addView(monthComps[1]);
+ tar.addView(newImgView(sep));
+ }
+ addYear(layer, tar, null);
+ } else if(dateStyle==6 || dateStyle==7) {
+ var sep = imgs.get("xiegang");
+ addYear(layer, tar, sep);
+ if(layer.bool("month")) {
+ tar.addView(monthComps[0]);
+ tar.addView(monthComps[1]);
+ tar.addView(newImgView(sep));
+ }
+ if(layer.bool("day")) {
+ tar.addView(dayComps[0]);
+ tar.addView(dayComps[1]);
+ }
+ } else if(dateStyle==8 || dateStyle==9) {
+ var sep = imgs.get("hengxian");
+ if(layer.bool("month")) {
+ tar.addView(monthComps[0]);
+ tar.addView(monthComps[1]);
+ tar.addView(newImgView(sep));
+ }
+ if(layer.bool("day")) {
+ tar.addView(dayComps[0]);
+ tar.addView(dayComps[1]);
+ tar.addView(newImgView(sep));
+ }
+ addYear(layer, tar, null);
+ } else if(dateStyle==10 || dateStyle==11) {
+ var sep = imgs.get("hengxian");
+ if(layer.bool("day")) {
+ tar.addView(dayComps[0]);
+ tar.addView(dayComps[1]);
+ tar.addView(newImgView(sep));
+ }
+ if(layer.bool("month")) {
+ tar.addView(monthComps[0]);
+ tar.addView(monthComps[1]);
+ tar.addView(newImgView(sep));
+ }
+ addYear(layer, tar, null);
+ } else if(dateStyle==12 || dateStyle==13) {
+ var sep = imgs.get("hengxian");
+ addYear(layer, tar, sep);
+ if(layer.bool("month")) {
+ tar.addView(monthComps[0]);
+ tar.addView(monthComps[1]);
+ tar.addView(newImgView(sep));
+ }
+ if(layer.bool("day")) {
+ tar.addView(dayComps[0]);
+ tar.addView(dayComps[1]);
+ }
+ }
+ }
+
+ void addYear(JSMap layer, LinearLayout tar, Bitmap sep) {
+ if(layer.bool("year")) {
+ if(layer.bool("fullYear")) {
+ tar.addView(yearComps[0]);
+ tar.addView(yearComps[1]);
+ }
+ tar.addView(yearComps[2]);
+ tar.addView(yearComps[3]);
+ if(sep != null) tar.addView(newImgView(sep));
+ }
+ }
+
+ void cal() {
+ //var dt = timeZone==null ? LocalDateTime.now() : LocalDateTime.now(timeZone);
+ calendar.setTimeInMillis(lastSec*1000);
+ var hour = calendar.get(Calendar.HOUR_OF_DAY);
+ ampmComp.setImageBitmap(imgs.get(hour < 12 ? "AM":"PM"));
+ var hh = NumFmts.zz().format(isHour12 ? Util.caseThen(calendar.get(Calendar.HOUR), 0, 12) : hour);
+ hourComps[0].setImageBitmap(isSingleHour && hh.charAt(0)=='0' ? null : imgs.get(hh.substring(0,1)));
+ hourComps[1].setImageBitmap(imgs.get(hh.substring(1,2)));
+ var mm = NumFmts.zz().format(calendar.get(Calendar.MINUTE));
+ minComps[0].setImageBitmap(imgs.get(mm.substring(0,1)));
+ minComps[1].setImageBitmap(imgs.get(mm.substring(1,2)));
+ var ss = NumFmts.zz().format(calendar.get(Calendar.SECOND));
+ secComps[0].setImageBitmap(imgs.get(ss.substring(0,1)));
+ secComps[1].setImageBitmap(imgs.get(ss.substring(1,2)));
+ var day = calendar.get(Calendar.DAY_OF_MONTH);
+ if(yearComps[0].getDrawable()==null || day!=lastDay) {
+ lastDay = day;
+ if(weekly) weekComp.setImageBitmap(imgs.get(weeks[calendar.get(Calendar.DAY_OF_WEEK) - 1]));
+ var yyyy = NumFmts.zzzz().format(calendar.get(Calendar.YEAR));
+ yearComps[0].setImageBitmap(imgs.get(yyyy.substring(0,1)));
+ yearComps[1].setImageBitmap(imgs.get(yyyy.substring(1,2)));
+ yearComps[2].setImageBitmap(imgs.get(yyyy.substring(2,3)));
+ yearComps[3].setImageBitmap(imgs.get(yyyy.substring(3,4)));
+ mm = NumFmts.zz().format(calendar.get(Calendar.MONTH)+1);
+ monthComps[0].setImageBitmap(isSingleMD && mm.charAt(0)=='0' ? null : imgs.get(mm.substring(0,1)));
+ monthComps[1].setImageBitmap(imgs.get(mm.substring(1,2)));
+ var dd = NumFmts.zz().format(day);
+ dayComps[0].setImageBitmap(isSingleMD && dd.charAt(0)=='0' ? null : imgs.get(dd.substring(0,1)));
+ dayComps[1].setImageBitmap(imgs.get(dd.substring(1,2)));
+ }
+ }
+ int lastDay;
+
+ long lastSec;
+
+ @Override
+ public void doFrame(long ms) {
+ if(! isShown()) return;
+ var sec = ms / 1000;
+ if(sec != lastSec) {
+ lastSec = sec;
+ cal();
+ }
+ }
+}
\ No newline at end of file
diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcDigitalClock.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcDigitalClock.java
new file mode 100644
index 0000000..9716a8d
--- /dev/null
+++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcDigitalClock.java
@@ -0,0 +1,89 @@
+package com.xixun.xixunplayer;
+
+import android.annotation.SuppressLint;
+import android.graphics.Color;
+import android.view.Choreographer;
+import android.webkit.WebView;
+
+import java.util.Calendar;
+import java.util.Locale;
+import java.util.SimpleTimeZone;
+
+import gnph.util.JSMap;
+import gnph.util.NumFmts;
+import gnph.util.Txts;
+
+@SuppressLint("ViewConstructor")
+public class SrcDigitalClock extends WebView implements Choreographer.FrameCallback {
+
+ String html, lineHeight, prefix;
+ Calendar calendar;
+ Locale locale;
+ boolean hasYear, hasMonthStr, hasMonth, hasDay, hasWeek, hasAP, hasHour, hasHour12, hasMin, hasSec;
+
+ public SrcDigitalClock(Prog prog, JSMap json) {
+ super(prog.getContext());
+ setBackgroundColor(Color.TRANSPARENT);
+ setVerticalScrollBarEnabled(false);
+ setHorizontalScrollBarEnabled(false);
+ setInitialScale(100);
+ html = json.stnn("html");
+ lineHeight = json.str("lineHeight");
+ var timezone = json.dbl("timezone", 9999);
+ calendar = timezone==9999 ? Calendar.getInstance() : Calendar.getInstance(new SimpleTimeZone((int) (timezone*3600000), ""));
+ prefix = "";
+ hasYear = html.contains("%y");
+ var hhh = html.replace("%Mw", "");
+ hasMonthStr = html.length()!=hhh.length();
+ hasMonth = hhh.contains("%M");
+ hasDay = html.contains("%d");
+ hasWeek = html.contains("%w");
+ hasAP = html.contains("%am");
+ hasHour = html.contains("%H");
+ hasHour12 = html.contains("%h");
+ hasMin = html.contains("%m");
+ hasSec = html.contains("%s");
+ try {
+ var lan = Txts.split(json.stnn("language"), "-", 1);
+ if(lan.isEmpty()) locale = Locale.getDefault();
+ else if(lan.size()==1) locale = lan.get(0).equals("cn") ? new Locale("zh", "CN") : new Locale(lan.get(0));
+ else locale = new Locale(lan.get(0), lan.get(1));
+ } catch (Exception e) {
+ Util.printStackTrace(e);
+ Util.makeText(prog.getContext(), Util.toStr(e)).show();
+ }
+ prog.calls.add(this);
+ }
+
+ void cal() {
+ calendar.setTimeInMillis(lastSec*1000);
+ var htm = html;
+ if(hasYear) htm = htm.replace("%y", Long.toString(calendar.get(Calendar.YEAR)));
+ try {
+ if(hasMonthStr) htm = htm.replace("%Mw", calendar.getDisplayName(Calendar.MONTH, Calendar.SHORT, locale));
+ if(hasWeek) htm = htm.replace("%w", calendar.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.LONG, locale));
+ if(hasAP) htm = htm.replace("%am", calendar.getDisplayName(Calendar.AM_PM, Calendar.SHORT, locale));
+ } catch (Exception ignored) {}
+ if(hasMonth) htm = htm.replace("%M", NumFmts.zz().format(calendar.get(Calendar.MONTH)+1));
+ if(hasDay) htm = htm.replace("%d", NumFmts.zz().format(calendar.get(Calendar.DAY_OF_MONTH)));
+ if(hasHour) htm = htm.replace("%H", NumFmts.zz().format(calendar.get(Calendar.HOUR_OF_DAY)));
+ if(hasHour12) htm = htm.replace("%h", NumFmts.zz().format(Util.caseThen(calendar.get(Calendar.HOUR), 0, 12)));
+ if(hasMin) htm = htm.replace("%m", NumFmts.zz().format(calendar.get(Calendar.MINUTE)));
+ if(hasSec) htm = htm.replace("%s", NumFmts.zz().format(calendar.get(Calendar.SECOND)));
+ loadDataWithBaseURL(null, prefix+htm+"", "text/html", "UTF-8", null);
+ }
+
+ long lastSec;
+
+ @Override
+ public void doFrame(long ms) {
+ if(! isShown()) return;
+ var sec = ms / 1000;
+ if(sec != lastSec) {
+ lastSec = sec;
+ cal();
+ }
+ }
+}
\ No newline at end of file
diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcEnviron.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcEnviron.java
new file mode 100644
index 0000000..cfc5153
--- /dev/null
+++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcEnviron.java
@@ -0,0 +1,241 @@
+package com.xixun.xixunplayer;
+
+import android.annotation.SuppressLint;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.view.Choreographer;
+import android.view.View;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import gnph.util.JSList;
+import gnph.util.JSMap;
+import gnph.util.NumFmts;
+
+@SuppressLint("ViewConstructor")
+public class SrcEnviron extends View implements Choreographer.FrameCallback, IntentReceiver {
+ static class Item {
+ String key;
+ Bitmap label;
+ ArrayList nums = new ArrayList<>();
+ Bitmap unit;
+
+ public Item(String key, Bitmap label, Bitmap unit) {
+ this.key = key;
+ this.label = label;
+ this.unit = unit;
+ }
+ }
+ static String[] directs = {"NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW", "N"};
+ HashMap imgMap = new HashMap<>();
+ Bitmap title;
+ ArrayList- items = new ArrayList<>();
+ MainActivity act;
+ int alignType, spaceWidth, digitHeight;
+ int interval, cur, end, step;
+ boolean isScroll, isFirst = true;
+
+ public SrcEnviron(Prog prog, JSMap json) {
+ super(prog.getContext());
+ act = (MainActivity) prog.getContext();
+ alignType = json.intg("alignType");
+ spaceWidth = json.intg("spaceWidth");
+ isScroll = json.bool("bSingleScroll");
+ try {
+ setBackgroundColor(Color.parseColor(json.stnn("backColor")));
+ } catch (Exception ignored) {}
+ var values = json.jsmap("values");
+ if(values!=null) {
+ if(values.get("0") instanceof String) {
+ var titleStr = json.str("title");
+ if(titleStr!=null) title = BitmapFactory.decodeFile(Util.programDir+"/"+titleStr);
+ var entrys = values.entrySet();
+ for(var entry : entrys) imgMap.put(entry.getKey(), BitmapFactory.decodeFile(Util.programDir+"/"+entry.getValue()));
+ JSList jitems = json.jslist("items");
+ for(var jitem : jitems) {
+ var unit = jitem.str("unit");
+ items.add(new Item(jitem.str("name"), BitmapFactory.decodeFile(Util.programDir + "/" + jitem.stnn("label")), unit == null ? null : BitmapFactory.decodeFile(Util.programDir + "/" + unit)));
+ }
+ } else {
+ var big = BitmapFactory.decodeFile(Util.programDir+"/"+json.stnn("id"));
+ var titleGeo = json.jsmap("title");
+ if(titleGeo!=null) title = Bitmap.createBitmap(big, titleGeo.intg("x"), titleGeo.intg("y"), titleGeo.intg("w"), titleGeo.intg("h"));
+ var entrys = values.entrySet();
+ for(var entry : entrys) {
+ var val = (JSMap) entry.getValue();
+ imgMap.put(entry.getKey(), Bitmap.createBitmap(big, val.intg("x"), val.intg("y"), val.intg("w"), val.intg("h")));
+ }
+ JSList jitems = json.jslist("items");
+ for(var jitem : jitems) {
+ var label = jitem.jsmap("label");
+ var unit = jitem.jsmap("unit");
+ items.add(new Item(jitem.str("name"), Bitmap.createBitmap(big, label.intg("x"), label.intg("y"), label.intg("w"), label.intg("h")), unit == null ? null : Bitmap.createBitmap(big, unit.intg("x"), unit.intg("y"), unit.intg("w"), unit.intg("h"))));
+ }
+ }
+ } else {
+ JSList pics = json.jslist("arrayPics");
+ if(pics==null) return;
+ if(! pics.isEmpty() && pics.get(0).containsKey("startX")) {
+ var big = BitmapFactory.decodeFile(Util.programDir+"/"+json.stnn("id"));
+ for(var pic : pics) {
+ var startX = pic.intg("startX");
+ var startY = pic.intg("startY");
+ imgMap.put(pic.str("name"), Bitmap.createBitmap(big, startX, startY, pic.intg("endX")-startX, pic.intg("endY")-startY));
+ }
+ } else for(var img : pics) imgMap.put(img.str("name"), BitmapFactory.decodeFile(Util.programDir+"/"+img.stnn("id")));
+ imgMap.put("-", imgMap.remove("minus_sign"));
+ title = imgMap.get("labeltitle");
+ if(json.bool("bTemperature")) items.add(new Item("temperature", imgMap.remove("labeltemperature"), imgMap.remove("unit_celsius")));
+ if(json.bool("bHumidity")) items.add(new Item("humidity", imgMap.remove("labelhumidity"), imgMap.remove("unit_humidity")));
+ if(json.bool("bNoise")) items.add(new Item("noise", imgMap.remove("labelnoise"), imgMap.remove("unit_noise")));
+ if(json.bool("bWindSpeed")) items.add(new Item("windSpeed", imgMap.remove("labelwindSpeed"), imgMap.remove("unit_windspeed")));
+ if(json.bool("bWindDirection")) items.add(new Item("windDirection", imgMap.remove("labelwindDirection"), null));
+ if(json.bool("bPM25")) items.add(new Item("pm2.5", imgMap.remove("labelpm25"), imgMap.get("unit_pm10")));
+ if(json.bool("bPM10")) items.add(new Item("pm10", imgMap.remove("labelpm10"), imgMap.get("unit_pm10")));
+ }
+ digitHeight = imgMap.get("0").getHeight();
+ if(spaceWidth==0) spaceWidth = imgMap.get("0").getWidth();
+ var scrollSpeed = json.dbl("scrollSpeed");
+ if(scrollSpeed==0) {
+ var scrollDur = json.dbl("iScrollSpeed");
+ if(scrollDur==0) return;
+ scrollSpeed = 1000 / scrollDur;
+ }
+ interval = step = 1;
+ if(scrollSpeed > 60) step = (int) Math.round(scrollSpeed/60);
+ else if(scrollSpeed < 60) interval = (int) Math.round(60/scrollSpeed);
+ if(isScroll) prog.calls.add(this);
+ }
+
+ public static Method method;
+ static {
+ try {
+ method = Intent.class.getMethod("getExtra", String.class, Object.class);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ public void onReceive(Intent intent) {
+ try {
+ for(var item : items) {
+ item.nums.clear();
+ if(item.unit==null) {
+ var num = intent.getIntExtra(item.key, -1);
+ if(num>=0 && num<=15) {
+ if(directs[num].length() < 3) item.nums.add(imgMap.get(directs[num]));
+ else {
+ item.nums.add(imgMap.get(directs[num].substring(0, 1)));
+ item.nums.add(imgMap.get(directs[num].substring(1)));
+ }
+ } else {
+ var img = imgMap.get("-");
+ item.nums.add(img);
+ item.nums.add(img);
+ }
+ } else {
+ var num = ((Number) method.invoke(intent, item.key, -999)).floatValue();
+ var str = num==-999 || (num==-1 && ! item.key.endsWith("rature")) ? "--" : NumFmts.zz().format(num);
+ for(int cc=0; cc imgs;
+ Rect rect = new Rect();
+ int interval, cur, end, step;
+ char direct;
+
+ public SrcScroll(Prog prog, ArrayList imgs, char direct, double speed) {
+ super(prog.getContext());
+ this.imgs = imgs;
+ this.direct = direct;
+ var img0 = imgs.get(0);
+ Util.println(" SrcScroll img cnt "+imgs.size()+", "+img0.getWidth()+"x"+img0.getHeight()+" direct "+direct+" speed "+speed);
+ if(img0.getWidth() > 4096) {
+ imgs.clear();
+ var rem = img0.getWidth();
+ do {
+ imgs.add(Bitmap.createBitmap(img0, img0.getWidth()-rem, 0, Math.min(4096, rem), img0.getHeight()));
+ rem -= 4096;
+ } while (rem>0);
+ } else if(img0.getHeight() > 4096) {
+ imgs.clear();
+ var rem = img0.getHeight();
+ do {
+ imgs.add(Bitmap.createBitmap(img0, 0, img0.getHeight()-rem, img0.getWidth(), Math.min(4096, rem)));
+ rem -= 4096;
+ } while (rem>0);
+ }
+
+ if(speed==0) return;
+ int width = 0, height = 0;
+ for(var img : imgs) {
+ width += img.getWidth();
+ height += img.getHeight();
+ }
+ if(direct=='l') end = -(width-step);
+ else if(direct=='r') end = width-step;
+ else if(direct=='t') end = -(height-step);
+ else if(direct=='b') end = height-step;
+ else direct = 0;
+ if(direct==0) return;
+
+ interval = step = 1;
+ if(speed > 60) step = (int) Math.round(speed/60);
+ else if(speed < 60) interval = (int) Math.round(60/speed);
+ prog.calls.add(this);
+ }
+ @Override
+ protected void onDraw(@NonNull Canvas canvas) {
+ super.onDraw(canvas);
+ if(imgs.isEmpty()) return;
+ try {
+ drawOther(canvas);
+ } catch (Throwable e) {
+ setVisibility(GONE);
+ imgs.clear();
+ Util.printStackTrace(e);
+ }
+ }
+ public void drawOther(Canvas canvas) {
+ if(! canvas.getClipBounds(rect)) {
+ rect.left = rect.top = 0;
+ rect.right = getWidth();
+ rect.bottom = getHeight();
+ }
+ if(direct=='l') {
+ int ii = 0, x = cur;
+ do {
+ if(x > rect.left-imgs.get(ii).getWidth() && x < rect.right) canvas.drawBitmap(imgs.get(ii), x, 0, null);
+ x += imgs.get(ii).getWidth();
+ if(++ii >= imgs.size()) ii = 0;
+ } while(x < rect.right);
+ } else if(direct=='r') {
+ int ii = imgs.size()-1, x = cur + getWidth();
+ boolean con1;
+ do {
+ x -= imgs.get(ii).getWidth();
+ con1 = x > rect.left-imgs.get(ii).getWidth();
+ if(con1 && x < rect.right) canvas.drawBitmap(imgs.get(ii), x, 0, null);
+ if(--ii < 0) ii = imgs.size()-1;
+ } while(con1);
+ } else if(direct=='t') {
+ int ii = 0, y = cur;
+ do {
+ if(y > rect.top-imgs.get(ii).getHeight() && y < rect.bottom) canvas.drawBitmap(imgs.get(ii), 0, y, null);
+ y += imgs.get(ii).getHeight();
+ if(++ii >= imgs.size()) ii = 0;
+ } while(y < rect.bottom);
+ } else if(direct=='b') {
+ int ii = imgs.size()-1, y = cur + getHeight();
+ boolean con1;
+ do {
+ y -= imgs.get(ii).getHeight();
+ con1 = y > rect.top-imgs.get(ii).getHeight();
+ if(con1 && y < rect.bottom) canvas.drawBitmap(imgs.get(ii), 0, y, null);
+ if(--ii < 0) ii = imgs.size()-1;
+ } while(con1);
+ } else canvas.drawBitmap(imgs.get(0), 0, 0, null);
+ }
+
+ int freshCnt;
+
+ @Override
+ public void doFrame(long ms) {
+ if(! isShown()) {
+ freshCnt = cur = 0;
+ return;
+ }
+ if(freshCnt < interval) freshCnt++;
+ else {
+ freshCnt = 1;
+ if(direct=='t' || direct=='l') {
+ if(cur <= end) cur -= end;
+ else cur -= step;
+ } else if(direct=='b' || direct=='r') {
+ if(cur >= end) cur -= end;
+ else cur += step;
+ }
+ invalidate();
+ }
+ }
+}
\ No newline at end of file
diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcSensor.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcSensor.java
new file mode 100644
index 0000000..50fef31
--- /dev/null
+++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcSensor.java
@@ -0,0 +1,159 @@
+package com.xixun.xixunplayer;
+
+import android.annotation.SuppressLint;
+import android.content.Intent;
+import android.graphics.Color;
+import android.view.Choreographer;
+import android.webkit.WebView;
+
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+
+import gnph.util.JSMap;
+import gnph.util.NumFmts;
+import gnph.util.Txts;
+import gnph.util.URLConn;
+
+public class SrcSensor extends WebView implements IntentReceiver, Choreographer.FrameCallback {
+
+ static String directs[] = {"NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW", "N"};
+ MainActivity act;
+ ArrayList htmls;
+ String prefix, url, text;
+ int interval, pageDur, cur;
+
+ @SuppressLint("SetJavaScriptEnabled")
+ public SrcSensor(Prog prog, JSMap json, int width, int height) {
+ super(prog.getContext());
+ act = (MainActivity) prog.getContext();
+ setBackgroundColor(Color.TRANSPARENT);
+ setVerticalScrollBarEnabled(false);
+ setHorizontalScrollBarEnabled(false);
+ setInitialScale(100);
+ getSettings().setJavaScriptEnabled(true);
+
+ var html = json.stnn("html").replace("%Co2", "%CO2").trim();
+ htmls = Txts.split(html, "
", 1);
+ pageDur = json.intg("speed")*1000;
+ if(pageDur==0) pageDur = json.intg("timeSpan")*1000 / htmls.size();
+ url = json.str("sUrl");
+ interval = json.intg("sInterval") * 1000;
+ if(url!=null && interval>0 && html.contains("%s")) {
+ nextMs = System.currentTimeMillis() + interval;
+ Util.println("url: "+url);
+ try {
+ text = URLConn.send(url);
+ } catch (Exception e) {
+ text = "";
+ Util.printStackTrace(e);
+ }
+ }
+ prefix = "";
+ prog.calls.add(this);
+ }
+
+ static DecimalFormat fmt0 = NumFmts.newDecFmt("0");
+ public void onReceive(Intent intent) {
+ try {
+ var htm = htmls.get(cur);
+ var temp = intent.getFloatExtra("temperature", -99f);
+ var fa = temp * 1.8f + 32;
+ var noTemp = temp==-99f;
+ var t2 = noTemp ? "--" : NumFmts.fix2().format(temp);
+ var t1 = noTemp ? "--" : NumFmts.fix1().format(temp);
+ var tn = noTemp ? "--" : String.valueOf(temp);
+ var t0 = noTemp ? "--" : fmt0.format(temp);
+ var f2 = noTemp ? "--" : NumFmts.fix2().format(fa);
+ var f1 = noTemp ? "--" : NumFmts.fix1().format(fa);
+ var fn = noTemp ? "--" : String.valueOf(fa);
+ var f0 = noTemp ? "--" : fmt0.format(fa);
+
+ var humi = intent.getFloatExtra("humidity", -1);
+ var h = humi == -1 ? "--" : String.valueOf(humi);
+ var noise = intent.getFloatExtra("noise", -1);
+ var windSpeed = intent.getFloatExtra("windSpeed", -1);
+ int pm2 = intent.getIntExtra("pm2.5", -1);
+ int pm10 = intent.getIntExtra("pm10", -1);
+ int windDir = intent.getIntExtra("windDirection", -1);
+ int SO2 = intent.getIntExtra("SO2", -1);
+ int NO2 = intent.getIntExtra("NO2", -1);
+ int CO2 = intent.getIntExtra("CO2", -1);
+ int CO = intent.getIntExtra("CO", -1);
+ int O3 = intent.getIntExtra("O3", -1);
+ var pressure = intent.getFloatExtra("pressure", -1);
+ var rainfall = intent.getFloatExtra("rainfall", -1);
+ int radiation = intent.getIntExtra("radiation", -1);
+ int beam = intent.getIntExtra("beam", -1);
+ //int brightness = intent.getIntExtra("brightness", -1);
+ var dp = intent.getFloatExtra("dpTemperature", -99f);
+
+ if(text!=null) htm = htm.replace("%s", text);
+ htm = htm.replace("%T2", t2 + '℃').replace("%T1", t1 + '℃').replace("%T", tn + '℃').replace("%c2", t2).replace("%c1", t1).replace("%c", t0)
+ .replace("%t2", f2 + '℉').replace("%t1", f1 + '℉').replace("%t", fn + '℉').replace("%f2", f2).replace("%f1", f1).replace("%f", f0)
+ .replace("%RH", h+'%').replace("%h", h)
+ .replace("%ns", (noise == -1 ? "--" : String.valueOf(noise)) + "dB")
+ .replace("%ws", (windSpeed == -1 ? "--" : String.valueOf(windSpeed)) + "m/s")
+ .replace("%wd", windDir<0||windDir>=directs.length ? "--" : directs[windDir])
+ .replace("%pm2", (pm2 == -1 ? "--" : String.valueOf(pm2)) + "μg/m³")
+ .replace("%pm10", (pm10 == -1 ? "--" : String.valueOf(pm10)) + "μg/m³")
+
+ .replace("%SO2", (SO2 == -1 ? "--" : String.valueOf(SO2)) +"ppb")
+ .replace("%NO2", (NO2 == -1 ? "--" : String.valueOf(NO2)) +"ppb")
+ .replace("%CO2", (CO2 == -1 ? "--" : String.valueOf(CO2)) +"ppm")
+ .replace("%CO", (CO == -1 ? "--" : String.valueOf(CO)) +"ppb")
+ .replace("%O3", (O3 == -1 ? "--" : String.valueOf(O3)) +"ppb")
+ .replace("%dp", (dp == -1 ? "--" : String.valueOf(dp)) +'℃')
+ .replace("%pressure", (pressure == -1 ? "--" : String.valueOf(pressure))+"hPa")
+ .replace("%rainfall", (rainfall == -1 ? "--" : String.valueOf(rainfall))+"mm")
+ .replace("%radiation", (radiation == -1 ? "--" : String.valueOf(radiation))+"W/m²")
+ .replace("%bm", (beam == -1 ? "--" : String.valueOf(beam))+"lux");
+
+ loadDataWithBaseURL(null, prefix+htm+"", "text/html", "UTF-8", null);
+ } catch (Exception e) {
+ Util.printStackTrace(e);
+ }
+ }
+
+ long nextPageMs, nextMs;
+ boolean isFirst = true;
+
+ @Override
+ public void doFrame(long ms) {
+ if(! isShown()) {
+ if(! isFirst) {
+ act.environs.remove(this);
+ isFirst = true;
+ }
+ return;
+ }
+ boolean needRefresh = false;
+ if(isFirst) {
+ isFirst = false;
+ cur = 0;
+ nextPageMs = ms + pageDur;
+ act.environs.add(this);
+ needRefresh = true;
+ } else if(ms>=nextPageMs) {
+ nextPageMs += pageDur;
+ if(cur>=htmls.size()-1) cur = 0;
+ else cur++;
+ needRefresh = true;
+ }
+ if(text!=null && ms>=nextMs) {
+ nextMs = ms + interval;
+ try {
+ text = URLConn.send(url);
+ needRefresh = true;
+ } catch (Exception e) {
+ Util.printStackTrace(e);
+ }
+ }
+ if(needRefresh) onReceive(act.environIntent);
+ }
+}
diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcTimer.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcTimer.java
new file mode 100644
index 0000000..02e632f
--- /dev/null
+++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcTimer.java
@@ -0,0 +1,158 @@
+package com.xixun.xixunplayer;
+
+import android.annotation.SuppressLint;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.view.Choreographer;
+import android.view.View;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import gnph.util.JSMap;
+import gnph.util.NumFmts;
+
+@SuppressLint("ViewConstructor")
+public class SrcTimer extends View implements Choreographer.FrameCallback {
+
+ HashMap imgMap = new HashMap<>();
+ ArrayList imgs = new ArrayList<>();
+ Bitmap text, day, hour, min, sec;
+ int spaceWidth, len;
+ Paint paint = new Paint();
+ long targetTime;
+ boolean isDown;
+ boolean isMultiline;
+ boolean hasDay, hasHour, hasMin, hasSec;
+
+ public SrcTimer(Prog prog, JSMap json) {
+ super(prog.getContext());
+ var imgEntrys = json.jsmap("imgs").entrySet();
+ for(var imgEntry : imgEntrys) imgMap.put(imgEntry.getKey(), BitmapFactory.decodeFile(Util.programDir+"/"+imgEntry.getValue()));
+ text = imgMap.get("text");
+ day = imgMap.get("day");
+ hour = imgMap.get("hour");
+ min = imgMap.get("min");
+ sec = imgMap.get("sec");
+ spaceWidth = (int) Math.round(json.dbl("spaceWidth"));
+ isDown = json.bool("isDown");
+ var dateFmt = new SimpleDateFormat("y-M-d H:m:s");
+ try {
+ targetTime = dateFmt.parse(json.stnn("targetTime")).getTime() / 1000;
+ } catch (Exception e) {
+ Util.makeText(prog.getContext(), Util.toStr(e)).show();
+ Util.printStackTrace(e);
+ }
+ hasDay = json.bool("hasDay");
+ hasHour = json.bool("hasHour");
+ hasMin = json.bool("hasMin");
+ hasSec = json.bool("hasSec");
+ isMultiline = json.bool("isMultiline");
+ paint.setTextAlign(Paint.Align.CENTER);
+ try {
+ setBackgroundColor(Color.parseColor(json.stnn("backColor")));
+ } catch (Exception ignored) {}
+ prog.calls.add(this);
+ }
+
+ void cal() {
+ var secs = isDown ? targetTime - lastSec : lastSec - targetTime;
+ if(secs < 0) secs = 0;
+ len = 0;
+ imgs.clear();
+ if(text!=null && ! isMultiline) {
+ imgs.add(text);
+ imgs.add(null);
+ len += text.getWidth();
+ len += spaceWidth;
+ }
+ if(hasDay) {
+ var str = Long.toString(secs/86400);
+ for(int cc=0; cc{
+ ijkPlayer.setOnPreparedListener(null);
+ bitRate = ijkPlayer.getBitRate();
+ var diff = dur - ijkPlayer.getDuration();
+ if(diff>0 && diff<=1000) ijkPlayer.setLooping(false);
+ if(isShown()) start();
+ });
+ ijkPlayer.setOnErrorListener((IMediaPlayer var1, int var2, int var3)->{
+ Util.println(" Video Error: "+var1+" "+var2+" "+var3);
+ return true;
+ });
+ ijkPlayer.prepareAsync();
+ } catch (Throwable e) {
+ Util.makeText(getContext(), Util.toStr(e)).show();
+ Util.printStackTrace(e);
+ ijkPlayer = null;
+ }
+ }
+ @Override
+ public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) {
+ Util.println(" SurfaceTexture Available "+(ijkPlayer==null?"ijkPlayer==null":""));
+ if(ijkPlayer!=null) ijkPlayer.setSurface(new Surface(surface));
+ }
+ @Override
+ public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) {}
+ @Override
+ public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) {
+ Util.println(" SurfaceTexture Destroyed");
+ return false;
+ }
+ @Override
+ public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {}
+
+ void start() {
+ if(ijkPlayer!=null) {
+ ijkPlayer.seekTo(seekTo);
+ ijkPlayer.start();
+ }
+ }
+ void pause() {
+ if(ijkPlayer!=null) {
+ ijkPlayer.pause();
+ ijkPlayer.seekTo(0);
+ }
+ }
+ void release() {
+ if(ijkPlayer!=null) {
+ ijkPlayer.release();
+ ijkPlayer = null;
+ }
+ }
+
+ String getState() {
+ return "null";
+ }
+
+ @Override
+ public void onVisibilityAggregated(boolean isVisible) {
+ super.onVisibilityAggregated(isVisible);
+ if(isVisible) {
+ start();
+ if(isLive) ijkPlayer.setVolume(vol, vol);
+ } else if(isLive) ijkPlayer.setVolume(0, 0);
+ else {
+ seekTo = 0;
+ pause();
+ }
+ }
+}
\ No newline at end of file
diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideo2.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcVideo2.java
similarity index 72%
rename from XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideo2.java
rename to XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcVideo2.java
index 5ff8678..8db5dbc 100644
--- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideo2.java
+++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcVideo2.java
@@ -1,5 +1,6 @@
package com.xixun.xixunplayer;
+import android.annotation.SuppressLint;
import android.graphics.SurfaceTexture;
import android.media.MediaPlayer;
import android.view.Choreographer;
@@ -8,38 +9,36 @@ import android.view.TextureView;
import androidx.annotation.NonNull;
+@SuppressLint("ViewConstructor")
public class SrcVideo2 extends TextureView implements TextureView.SurfaceTextureListener, Choreographer.FrameCallback {
float vol;
+ int dur;
MediaPlayer player;
- char state;
boolean isLive;
- public SrcVideo2(Prog prog, String path, float vol, boolean isLive) {
+ public SrcVideo2(Prog prog, String path, float vol, int dur, boolean isLive) {
super(prog.getContext());
this.vol = vol;
+ this.dur = dur;
this.isLive = isLive;
player = new MediaPlayer();
- setSurfaceTextureListener(this);
- player.setLooping(true);
- player.setVolume(vol, vol);
- player.setOnPreparedListener((MediaPlayer player)->{
- player.setOnPreparedListener(null);
- Util.println(" ------Prepared isShown"+isShown()+" "+this);
- if(isShown()) {
- player.start();
- state = 'S';
- } else state = 'P';
- prog.calls.add(this);
- });
- player.setOnErrorListener((MediaPlayer mp, int what, int extra)->{
- var err = "Media Error: "+getErrorName(what)+". "+getErrorName(extra);
- Util.makeText(getContext(), err).show();
- Util.println(err);
- return true;
- });
try {
+ setSurfaceTextureListener(this);
player.setDataSource(path);
+ player.setLooping(true);
+ player.setVolume(vol, vol);
+ player.setOnPreparedListener((MediaPlayer player)->{
+ player.setOnPreparedListener(null);
+ var diff = dur - player.getDuration();
+ if(diff>0 && diff<=1000) player.setLooping(false);
+ if(isShown()) player.start();
+ //prog.calls.add(this);
+ });
+ player.setOnErrorListener((MediaPlayer mp, int what, int extra)->{
+ Util.println("Media Error: "+getErrorName(what)+". "+getErrorName(extra));
+ return true;
+ });
player.prepareAsync();
} catch (Exception e) {
Util.printStackTrace(e);
@@ -48,7 +47,6 @@ public class SrcVideo2 extends TextureView implements TextureView.SurfaceTexture
}
@Override
public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) {
- setSurfaceTextureListener(null);
if(player!=null) player.setSurface(new Surface(surface));
}
@Override
@@ -63,15 +61,12 @@ public class SrcVideo2 extends TextureView implements TextureView.SurfaceTexture
@Override
public void onVisibilityAggregated(boolean isVisible) {
super.onVisibilityAggregated(isVisible);
- if(state==0) return;
if(isVisible) {
- Util.println(" ------start "+this);
player.seekTo(0);
player.start();
if(isLive) player.setVolume(vol, vol);
} else if(isLive) player.setVolume(0, 0);
else {
- Util.println(" ------pause "+this);
player.pause();
player.seekTo(0);
}
diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcVideoSurface.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcVideoSurface.java
new file mode 100644
index 0000000..0da577e
--- /dev/null
+++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcVideoSurface.java
@@ -0,0 +1,146 @@
+//package com.xixun.xixunplayer;
+//
+//import android.annotation.SuppressLint;
+//import android.content.Context;
+//import android.view.SurfaceHolder;
+//import android.view.SurfaceView;
+//
+//import androidx.annotation.NonNull;
+//import androidx.annotation.OptIn;
+//import androidx.media3.common.MediaItem;
+//import androidx.media3.common.util.UnstableApi;
+//import androidx.media3.exoplayer.ExoPlayer;
+//import androidx.media3.exoplayer.SeekParameters;
+//
+//import tv.danmaku.ijk.media.player.IMediaPlayer;
+//import tv.danmaku.ijk.media.player.IjkMediaPlayer;
+//
+//@SuppressLint("ViewConstructor")
+//public class SrcVideoSurface extends SurfaceView implements SurfaceHolder.Callback {
+//
+// String path;
+// float vol;
+// int dur;
+// IjkMediaPlayer ijkPlayer;
+// ExoPlayer exoPlayer;
+// long bitRate;
+// boolean isLive;
+//
+// public SrcVideoSurface(Context context, String path, float vol, int dur, boolean useHW, boolean isLive) {
+// super(context);
+// this.path = path;
+// this.vol = vol;
+// this.dur = dur;
+// this.isLive = isLive;
+// initIjk(useHW);
+// }
+//
+// @OptIn(markerClass = UnstableApi.class)
+// void initExo() {
+// exoPlayer = new ExoPlayer.Builder(getContext()).build();
+// exoPlayer.setMediaItem(MediaItem.fromUri(path));
+// exoPlayer.setRepeatMode(ExoPlayer.REPEAT_MODE_ONE);
+// exoPlayer.setSeekParameters(SeekParameters.CLOSEST_SYNC);
+// exoPlayer.setVolume(vol);
+// exoPlayer.setVideoSurfaceView(this);
+// exoPlayer.prepare();
+// }
+// void initIjk(boolean useHW) {
+// ijkPlayer = new IjkMediaPlayer();
+// if(useHW) {
+// ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-avc", 1);
+// ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-hevc", 1);
+// }
+// ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 0);
+// ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzeduration", 1);
+// ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 8);
+// try {
+// getHolder().addCallback(this);
+// ijkPlayer.setDataSource(path);
+// ijkPlayer.setLooping(true);
+// ijkPlayer.setVolume(vol, vol);
+// ijkPlayer.setOnPreparedListener((IMediaPlayer var1)->{
+// ijkPlayer.setOnPreparedListener(null);
+// bitRate = ijkPlayer.getBitRate();
+// if(bitRate > 12000000) {
+// getHolder().removeCallback(this);
+// release();
+// initExo();
+// } else {
+// var diff = dur - ijkPlayer.getDuration();
+// if(diff>0 && diff<=1000) ijkPlayer.setLooping(false);
+// }
+// if(isShown()) start();
+// });
+// ijkPlayer.setOnErrorListener((IMediaPlayer var1, int var2, int var3)->{
+// Util.println(" Video Error: "+var1+" "+var2+" "+var3);
+// return true;
+// });
+// ijkPlayer.prepareAsync();
+// } catch (Throwable e) {
+// Util.makeText(getContext(), Util.toStr(e)).show();
+// Util.printStackTrace(e);
+// ijkPlayer = null;
+// }
+// }
+// @Override
+// public void surfaceCreated(@NonNull SurfaceHolder holder) {
+// Util.println(" surfaceCreated "+(ijkPlayer==null?"ijkPlayer==null":""));
+// if(ijkPlayer!=null) ijkPlayer.setDisplay(holder);
+// }
+// @Override
+// public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {}
+// @Override
+// public void surfaceDestroyed(@NonNull SurfaceHolder holder) {}
+//
+// void start() {
+// if(ijkPlayer!=null) {
+// ijkPlayer.seekTo(0);
+// ijkPlayer.start();
+// } else if(exoPlayer!=null) {
+// if(exoPlayer.getPlaybackState()==ExoPlayer.STATE_IDLE) exoPlayer.prepare();
+// exoPlayer.play();
+// }
+// }
+// void pause() {
+// if(ijkPlayer!=null) {
+// ijkPlayer.pause();
+// ijkPlayer.seekTo(0);
+// }
+// if(exoPlayer!=null && exoPlayer.isPlaying()) {
+// exoPlayer.pause();
+// exoPlayer.seekToDefaultPosition();
+// }
+// }
+// void release() {
+// if(ijkPlayer!=null) {
+// ijkPlayer.release();
+// ijkPlayer = null;
+// }
+// if(exoPlayer!=null) {
+// exoPlayer.release();
+// exoPlayer = null;
+// }
+// }
+//
+// String getState() {
+// if(exoPlayer!=null) {
+// var state = exoPlayer.getPlaybackState();
+// if(state==ExoPlayer.STATE_IDLE) return "STATE_IDLE";
+// if(state==ExoPlayer.STATE_BUFFERING) return "STATE_BUFFERING";
+// if(state==ExoPlayer.STATE_READY) return "STATE_READY";
+// if(state==ExoPlayer.STATE_ENDED) return "STATE_ENDED";
+// }
+// return "null";
+// }
+//
+// @Override
+// public void onVisibilityAggregated(boolean isVisible) {
+// super.onVisibilityAggregated(isVisible);
+// if(isVisible) {
+// start();
+// if(isLive) ijkPlayer.setVolume(vol, vol);
+// } else if(isLive) ijkPlayer.setVolume(0, 0);
+// else pause();
+// }
+//}
\ No newline at end of file
diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcVisitor.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcVisitor.java
new file mode 100644
index 0000000..52356a7
--- /dev/null
+++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcVisitor.java
@@ -0,0 +1,95 @@
+package com.xixun.xixunplayer;
+
+import android.annotation.SuppressLint;
+import android.graphics.Color;
+import android.view.Choreographer;
+import android.webkit.WebView;
+
+import java.nio.charset.StandardCharsets;
+
+import gnph.util.JSMap;
+import gnph.util.URLConn;
+
+@SuppressLint("ViewConstructor")
+public class SrcVisitor extends WebView implements Choreographer.FrameCallback {
+
+ String html, lineHeight, prefix, url;
+
+ public SrcVisitor(Prog prog, JSMap json) {
+ super(prog.getContext());
+ setBackgroundColor(Color.TRANSPARENT);
+ setVerticalScrollBarEnabled(false);
+ setHorizontalScrollBarEnabled(false);
+ setInitialScale(100);
+ html = json.stnn("html").replace("%{yesterday.", "%{arr.-1.");
+ lineHeight = json.str("lineHeight");
+ url = json.str("url");
+ if(url!=null) {
+ var token = json.str("token");
+ if(token!=null) url += "?dataKey="+token;
+ }
+ prefix = "";
+ if(url!=null) prog.calls.add(this);
+ }
+
+ public void refresh() {
+ try {
+ var htm = html;
+ try {
+ var resp = URLConn.send(url);
+ var data = JSMap.from(resp.getBytes(StandardCharsets.UTF_8)).jsmap("data");
+ if(data==null) {
+ Util.println("SrcVisitor No data: "+resp);
+ return;
+ }
+ int lastEnd = 0, appendIdx = 0;
+ var buf = new StringBuilder(htm.length()*3/2);
+ int start;
+ while((start = htm.indexOf("%{", lastEnd)) > -1) {
+ int fds = start+2, fde = start+2;
+ try {
+ for(; fde<=fds+16; fde++) if(htm.charAt(fde)=='}') break;
+ if(fde > fds+16) {
+ lastEnd = fds;
+ continue;
+ }
+ lastEnd = fde+1;
+ if(fde == fds) continue;
+ var fd = htm.substring(fds, fde);
+ var replace = data.str(fd);
+ if(replace!=null) {
+ buf.append(htm, appendIdx, start).append(replace);
+ appendIdx = lastEnd;
+ }
+ Util.println("Found: " + htm.substring(start, lastEnd)+" fd "+fd);
+ } catch (Exception e) {
+ lastEnd = fds;
+ Util.printStackTrace(e);
+ }
+ }
+ if(buf.length()>0) {
+ if(appendIdx < htm.length()) buf.append(htm, appendIdx, htm.length());
+ htm = buf.toString();
+ }
+ } catch (Exception e) {
+ Util.printStackTrace(e);
+ }
+ loadDataWithBaseURL(null, prefix+htm+"", "text/html", "UTF-8", null);
+ } catch (Exception e) {
+ Util.printStackTrace(e);
+ }
+ }
+
+ long nextMs;
+
+ @Override
+ public void doFrame(long ms) {
+ if(! isShown()) return;
+ if(ms>=nextMs) {
+ nextMs = ms + 15000;
+ refresh();
+ }
+ }
+}
diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcWeather.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcWeather.java
new file mode 100644
index 0000000..227bdb0
--- /dev/null
+++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcWeather.java
@@ -0,0 +1,244 @@
+package com.xixun.xixunplayer;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.Color;
+import android.webkit.WebView;
+
+import java.util.HashMap;
+
+import gnph.util.JSList;
+import gnph.util.JSMap;
+import gnph.util.Txts;
+import gnph.util.URLConn;
+
+@SuppressLint("ViewConstructor")
+public class SrcWeather extends WebView {
+
+ String html, lineHeight, prefix, city;
+ int code;
+ boolean hasAQI, hasCur, hasDays;
+
+ public SrcWeather(Context context, JSMap json) {
+ super(context);
+ setBackgroundColor(Color.TRANSPARENT);
+ setVerticalScrollBarEnabled(false);
+ setHorizontalScrollBarEnabled(false);
+ setInitialScale(100);
+ html = json.stnn("html").replace("%{yesterday.", "%{arr.-1.");
+ lineHeight = json.str("lineHeight");
+ city = json.str("city");
+ code = json.intg("code");
+ prefix = "";
+ hasAQI = html.contains("%{aqi}");
+ hasCur = html.contains("%{current}") || html.contains("%{arr.0.");
+ hasDays = html.contains("%{arr.");
+
+ nextMs = System.currentTimeMillis() + 1800000;
+ refresh();
+ }
+
+ public void refresh() {
+ try {
+ var htm = html;
+ if(hasAQI) {
+ try {
+ JSList aqiForecast = JSMap.fromClose(new URLConn("https://www.ledokcloud.com/weather/whapi/json/alicityweather/aqiforecast5days").writeJson("{\"cityId\":"+code+"}").in()).jsmap("data").jslist("aqiForecast");
+ htm = htm.replace("%{aqi}", aqiForecast.get(1).stnn("value"));
+ } catch (Exception e) {
+ Util.printStackTrace(e);
+ }
+ }
+ String type = null;
+ if(hasCur) {
+ try {
+ var condition = JSMap.fromClose(new URLConn("https://www.ledokcloud.com/weather/whapi/json/alicityweather/condition").writeJson("{\"cityId\":"+code+"}").in()).jsmap("data").jsmap("condition");
+ htm = htm.replace("%{current}", condition.stnn("temp"));
+ htm = htm.replace("%{arr.0.type}", type = condition.stnn("condition"));
+ htm = htm.replace("%{arr.0.fx}", condition.stnn("windDir"));
+ htm = htm.replace("%{arr.0.fl}", condition.stnn("windLevel"));
+ } catch (Exception e) {
+ Util.printStackTrace(e);
+ }
+ }
+ if(hasDays) {
+ try {
+ JSList days = JSMap.fromClose(new URLConn("https://www.ledokcloud.com/weather/whapi/json/alicityweather/forecast15days").writeJson("{\"cityId\":"+code+"}").in()).jsmap("data").jslist("forecast");
+ int lastEnd = 0, appendIdx = 0;
+ var buf = new StringBuilder(htm.length()*3/2);
+ int start;
+ while((start = htm.indexOf("%{arr.", lastEnd)) > -1) {
+ int idxS = start+6, idxE;
+ try {
+ if(htm.charAt(idxS+1)=='.') idxE = idxS+1;
+ else if(htm.charAt(idxS+2)=='.') idxE = idxS+2;
+ else {
+ lastEnd = idxS;
+ continue;
+ }
+ var dd = Integer.parseInt(htm.substring(idxS, idxE))+1;
+ if(dd>=days.size()) {
+ lastEnd = idxE+1;
+ continue;
+ }
+ int fds = idxE+1, fde = idxE+2;
+ for(; fde<=fds+16; fde++) if(htm.charAt(fde)=='}') break;
+ if(fde > fds+16) {
+ lastEnd = fds;
+ continue;
+ }
+ var fd = htm.substring(fds, fde);
+ String replace = null;
+ if(fd.equals("date")) replace = days.get(dd).stnn("predictDate");
+ else if(fd.equals("type")) replace = days.get(dd).stnn("conditionDay");
+ else if(fd.equals("high")) replace = days.get(dd).stnn("tempDay");
+ else if(fd.equals("low")) replace = days.get(dd).stnn("tempNight");
+ else if(fd.equals("fx")) replace = days.get(dd).stnn("windDirDay");
+ else if(fd.equals("fl")) replace = days.get(dd).stnn("windLevelDay");
+ else if(fd.startsWith("img-")) {
+ var parts = Txts.split(fd, "-", 1);
+ String w = "32", h = "32";
+ if(parts.size()>=3) {
+ w = parts.get(1);
+ h = parts.get(2);
+ }
+ replace = "
";
+ }
+ lastEnd = fde+1;
+ if(replace!=null) {
+ buf.append(htm, appendIdx, start).append(replace);
+ appendIdx = lastEnd;
+ }
+ Util.println("Found: " + htm.substring(start, lastEnd)+" dd "+dd+" fd "+fd);
+ } catch (Exception e) {
+ lastEnd = idxS;
+ Util.printStackTrace(e);
+ }
+ }
+ if(buf.length()>0) {
+ if(appendIdx < htm.length()) buf.append(htm, appendIdx, htm.length());
+ htm = buf.toString();
+ }
+ } catch (Exception e) {
+ Util.printStackTrace(e);
+ }
+ }
+ loadDataWithBaseURL(null, prefix+htm+"", "text/html", "UTF-8", null);
+ } catch (Exception e) {
+ Util.printStackTrace(e);
+ }
+ }
+
+ static HashMap dayImgMap = new HashMap<>();
+ static HashMap nitImgMap = new HashMap<>();
+
+ static{
+ dayImgMap.put("晴", "0");
+ dayImgMap.put("大部晴朗", "0");
+ dayImgMap.put("多云", "1");
+ dayImgMap.put("少云", "1");
+ dayImgMap.put("阴", "2");
+ dayImgMap.put("阵雨", "3");
+ dayImgMap.put("局部阵雨", "3");
+ dayImgMap.put("小阵雨", "3");
+ dayImgMap.put("强阵雨", "3");
+ dayImgMap.put("阵雪", "13");
+ dayImgMap.put("小阵雪", "13");
+ dayImgMap.put("雾", "18");
+ dayImgMap.put("冻雾", "18");
+ dayImgMap.put("沙尘暴", "20");
+ dayImgMap.put("浮尘", "29");
+ dayImgMap.put("尘卷风", "29");
+ dayImgMap.put("扬沙", "29");
+ dayImgMap.put("强沙尘暴", "20");
+ dayImgMap.put("霾", "45");
+ dayImgMap.put("雷阵雨", "4");
+ dayImgMap.put("雷电", "4");
+ dayImgMap.put("雷暴", "4");
+ dayImgMap.put("雷阵雨伴有冰雹", "5");
+ dayImgMap.put("冰雹", "5");
+ dayImgMap.put("冰针", "5");
+ dayImgMap.put("冰粒", "5");
+ dayImgMap.put("雨夹雪", "6");
+ dayImgMap.put("小雨", "7");
+ dayImgMap.put("中雨", "8");
+ dayImgMap.put("大雨", "9");
+ dayImgMap.put("暴雨", "10");
+ dayImgMap.put("大暴雨", "10");
+ dayImgMap.put("特大暴雨", "10");
+ dayImgMap.put("小雪", "14");
+ dayImgMap.put("中雪", "15");
+ dayImgMap.put("大雪", "16");
+ dayImgMap.put("暴雪", "17");
+ dayImgMap.put("冻雨", "19");
+ dayImgMap.put("雪", "15");
+ dayImgMap.put("雨", "8");
+ dayImgMap.put("小到中雨", "7");
+ dayImgMap.put("中到大雨", "9");
+ dayImgMap.put("大到暴雨", "10");
+ dayImgMap.put("小到中雪", "15");
+ dayImgMap.put("无天气类型", "unknown");
+
+ nitImgMap.put("晴", "30");
+ nitImgMap.put("大部晴朗", "30");
+ nitImgMap.put("多云", "31");
+ nitImgMap.put("少云", "31");
+ nitImgMap.put("阴", "2");
+ nitImgMap.put("阵雨", "33");
+ nitImgMap.put("局部阵雨", "33");
+ nitImgMap.put("小阵雨", "33");
+ nitImgMap.put("强阵雨", "33");
+ nitImgMap.put("阵雪", "34");
+ nitImgMap.put("小阵雪", "4");
+ nitImgMap.put("雾", "32");
+ nitImgMap.put("冻雾", "32");
+ nitImgMap.put("沙尘暴", "36");
+ nitImgMap.put("浮尘", "35");
+ nitImgMap.put("尘卷风", "5");
+ nitImgMap.put("扬沙", "35");
+ nitImgMap.put("强沙尘暴", "36");
+ nitImgMap.put("霾", "46");
+ nitImgMap.put("雷阵雨", "4");
+ nitImgMap.put("雷电", "4");
+ nitImgMap.put("雷暴", "4");
+ nitImgMap.put("雷阵雨伴有冰雹", "5");
+ nitImgMap.put("冰雹", "5");
+ nitImgMap.put("冰针", "5");
+ nitImgMap.put("冰粒", "5");
+ nitImgMap.put("雨夹雪", "6");
+ nitImgMap.put("小雨", "7");
+ nitImgMap.put("中雨", "8");
+ nitImgMap.put("大雨", "9");
+ nitImgMap.put("暴雨", "10");
+ nitImgMap.put("大暴雨", "10");
+ nitImgMap.put("特大暴雨", "10");
+ nitImgMap.put("小雪", "14");
+ nitImgMap.put("中雪", "15");
+ nitImgMap.put("大雪", "16");
+ nitImgMap.put("暴雪", "17");
+ nitImgMap.put("冻雨", "19");
+ nitImgMap.put("雪", "15");
+ nitImgMap.put("雨", "8");
+ nitImgMap.put("小到中雨", "7");
+ nitImgMap.put("中到大雨", "9");
+ nitImgMap.put("大到暴雨", "10");
+ nitImgMap.put("小到中雪", "15");
+ nitImgMap.put("无天气类型", "unknown");
+ }
+
+ long nextMs;
+
+ @Override
+ public void onVisibilityAggregated(boolean isVisible) {
+ super.onVisibilityAggregated(isVisible);
+ if(isVisible) {
+ var ms = System.currentTimeMillis();
+ if(ms>=nextMs) {
+ nextMs = ms + 1800000;
+ refresh();
+ }
+ }
+ }
+}
diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcWeb.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcWeb.java
new file mode 100644
index 0000000..92bc7c6
--- /dev/null
+++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcWeb.java
@@ -0,0 +1,79 @@
+package com.xixun.xixunplayer;
+
+import android.annotation.SuppressLint;
+import android.graphics.Color;
+import android.net.http.SslError;
+import android.view.Choreographer;
+import android.webkit.SslErrorHandler;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+
+import gnph.util.JSMap;
+
+@SuppressLint("ViewConstructor")
+public class SrcWeb extends WebView implements Choreographer.FrameCallback {
+
+ String url;
+ int refresh;
+
+ @SuppressLint("SetJavaScriptEnabled")
+ public SrcWeb(Prog prog, JSMap json) {
+ super(prog.getContext());
+ var settings = getSettings();
+ settings.setJavaScriptEnabled(true);
+ settings.setDomStorageEnabled(true);
+ settings.setLoadsImagesAutomatically(true);
+ setVerticalScrollBarEnabled(false);
+ setHorizontalScrollBarEnabled(false);
+ setBackgroundColor(Color.TRANSPARENT);
+ setInitialScale(json.intg("zoom", 100));
+ clearCache(true);
+ setWebViewClient(new WebViewClient() {
+ @Override
+ public boolean shouldOverrideUrlLoading(WebView view, String url) {
+ return false;
+ }
+ @Override
+ public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
+ handler.proceed(); //忽略SSL证书错误,加载网页
+ }
+ @Override
+ public void onPageFinished(WebView view, String url) {
+ view.loadUrl("javascript:window.scrollTo("+json.str("offX", "0")+", "+json.str("offY", "0")+")");
+ }
+ });
+ loadUrl(url = json.str("url"));
+ refresh = json.intg("refreshSec")*1000;
+ if(Util.custom==Util.Custom.Yishi) {
+ prog.calls.add(this);
+ } else {
+ if(refresh>0 && url!=null) prog.calls.add(this);
+ }
+ nextMs = System.currentTimeMillis() + refresh;
+ }
+
+ long nextMs;
+ int w, h;
+ @Override
+ public void doFrame(long ms) {
+ if(! isShown()) return;
+
+ if(Util.custom==Util.Custom.Yishi) {
+ if(refresh>0 && url!=null && ms>=nextMs) {
+ nextMs = ms + refresh;
+ loadUrl(url);
+ }
+ if(Util.screenWidth!=w || Util.screenHeight!=h) {
+ w = Util.screenWidth;
+ h = Util.screenHeight;
+ Util.println(" w "+w+" h "+h);
+ setLayoutParams(new AbsLayout.LayoutParams(0, 0, w, h));
+ }
+ } else {
+ if(ms>=nextMs) {
+ nextMs = ms + refresh;
+ loadUrl(url);
+ }
+ }
+ }
+}
diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/TCPThread.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/TCPThread.java
new file mode 100644
index 0000000..0606659
--- /dev/null
+++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/TCPThread.java
@@ -0,0 +1,417 @@
+package com.xixun.xixunplayer;
+
+import static android.view.View.VISIBLE;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.BitmapFactory;
+import android.os.Environment;
+import android.os.StatFs;
+import android.view.WindowManager;
+import android.webkit.WebView;
+
+import androidx.annotation.OptIn;
+
+import net.lingala.zip4j.ZipFile;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.TimeZone;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import gnph.util.IOs;
+import gnph.util.JSList;
+import gnph.util.JSMap;
+import gnph.util.NumFmts;
+import gnph.util.O;
+
+public class TCPThread extends Thread {
+
+ public static void startServer(int port) {
+ new Thread(()->{
+ try {
+ var serverSocket = new ServerSocket(port);
+ while(true) {
+ try {
+ Util.println("\nAccepting ...");
+ var socket = serverSocket.accept();
+ new TCPThread(MainActivity.ins, socket).start();
+ Util.println("\nAccepted");
+ } catch (Throwable e) {
+ var ins = MainActivity.ins;
+ if(ins!=null) ins.runOnUiThread(() -> Util.makeText(ins, Util.toStr(e)).show());
+ Util.printStackTrace(e);
+ }
+ }
+ } catch (Throwable e) {
+ var msg = e.getMessage();
+ if(msg==null || ! msg.contains("in use")) MainActivity.ins.runOnUiThread(() -> Util.makeText(MainActivity.ins, Util.toStr(e)).show());
+ Util.printStackTrace(e);
+ }
+ }).start();
+ }
+
+ MainActivity main;
+ Socket socket;
+ InputStream in;
+ OutputStream out;
+ SimpleDateFormat fmt = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
+
+ public TCPThread(MainActivity main, Socket socket) {
+ this.main = main;
+ this.socket = socket;
+ }
+ public void run() {
+ try {
+ socket.setSoTimeout(600000);
+ Util.socketThreads.add(this);
+ in = socket.getInputStream();
+ out = socket.getOutputStream();
+ HashSet existed = null;
+ ByteArrayOutputStream progJson = null;
+ while(true) {
+ var obj = JSMap.from(in);
+ var _type = obj.stnn("_type");
+ Util.println("_type: "+_type);
+ if("consult".equals(_type)) {
+ JSList files = obj.jslist("files");
+ if(files==null) new JSMap("_type", _type, "idList", obj.jslist("idList")).write(out);
+ else {
+ existed = new HashSet<>();
+ for(var fil : files) {
+ var name = fil.stnn("name");
+ var file = new File(Util.programDir + "/" + name);
+ if(file.isFile() && file.length()==fil.intg("size")) existed.add(name);
+ }
+ new JSMap("_type", _type, "existed", existed).write(out);
+ }
+ } else if("proStart".equals(_type)) {
+ Util.downId = 0;
+ Util.deleteFiles(obj.intg("proSize"), existed);
+ } else if("fileStart".equals(_type)) {
+ var size = obj.intg("size");
+ var name = obj.stnn("id");
+ Util.println(" size: " + size + " name: " + name);
+ if(name.equals("program")) {
+ progJson = new ByteArrayOutputStream();
+ IOs.writeCloseOut(progJson, in, size);
+ } else {
+ var fOut = new FileOutputStream(Util.programDir + "/" + name);
+ try {
+ IOs.write(fOut, in, size);
+ fOut.flush();
+ fOut.getFD().sync();
+ fOut.close();
+ } catch(Throwable e) {
+ fOut.close();
+ new File(Util.programDir + "/" + name).delete();
+ }
+ }
+ } else if("setBackImg".equals(_type) || "imgFileStart".equals(_type)) {
+ var fout = new FileOutputStream(Util.backImgFile);
+ IOs.write(fout, in, obj.intg("size"));
+ fout.flush();
+ fout.getFD().sync();
+ fout.close();
+ var img = BitmapFactory.decodeFile(Util.backImgFile, Util.noScaled);
+ if(main!=null) main.runOnUiThread(() -> {
+ main.backView.cosImg = img;
+ main.backView.invalidate();
+ });
+ new JSMap("success", true).write(out);
+ } else if("proEnd".equals(_type)) {
+ new JSMap("_type", "AckSuccess", "success", progJson!=null).write(out);
+ if(progJson!=null) {
+ var json = progJson.toByteArray();
+ progJson = null;
+ var acti = MainActivity.ins;
+ if(acti!=null) acti.runOnUiThread(() -> acti.initProg(json));
+ else {
+ var fOut = new FileOutputStream(Util.programDir + "/program");
+ fOut.write(json);
+ fOut.flush();
+ fOut.getFD().sync();
+ fOut.close();
+ if(MainService.ins!=null) {
+ var intent = new Intent(MainService.ins, MainActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ MainService.ins.startActivity(intent);
+ }
+ }
+ if(MainService.ins!=null) {
+ var intent = new Intent("com.xixun.AccessibilityService");
+ intent.putExtra("newProgram", "TCP");
+ MainService.ins.sendBroadcast(intent);
+ }
+ }
+ } else if("playZipTask".equals(_type)) {
+ var zip = new ZipFile(Util.programDir+"/"+obj.stnn("proName"));
+ if(zip.isEncrypted()) zip.setPassword(new char[]{'8','8','8'});
+ long size = 0;
+ ByteArrayOutputStream jsonOut = null;
+ try {
+ 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");
+ new JSMap("success", false, "msg", "No 'program' File").write(out);
+ if(main!=null) main.runOnUiThread(() -> Util.makeText(main, "No program File").show());
+ } else if(size==0) {
+ Util.println("Zip Size is 0");
+ new JSMap("success", false, "msg", "Zip Size is 0").write(out);
+ if(main!=null) main.runOnUiThread(() -> Util.makeText(main, "zip size is 0").show());
+ } else {
+ 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();
+ var acti = MainActivity.ins;
+ if(acti!=null) acti.runOnUiThread(()->acti.initProg(json));
+ else {
+ var fOut = new FileOutputStream(Util.programDir + "/program");
+ fOut.write(json);
+ fOut.flush();
+ fOut.getFD().sync();
+ fOut.close();
+ if(MainService.ins!=null) {
+ var intent = new Intent(MainService.ins, MainActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ MainService.ins.startActivity(intent);
+ }
+ }
+ new JSMap("success", true).write(out);
+ if(MainService.ins!=null) {
+ var intent = new Intent("com.xixun.AccessibilityService");
+ intent.putExtra("newProgram", "TCP");
+ MainService.ins.sendBroadcast(intent);
+ }
+ }
+ } catch (Exception e) {
+ Util.printStackTrace(e);
+ new JSMap("success", false).write(out);
+ if(main!=null) main.runOnUiThread(() -> Util.makeText(main, Util.toStr(e)).show());
+ }
+ } else if("DelPrograms".equals(_type)) {
+ Util.downId = 0;
+ var ok = new AtomicBoolean(false);
+ if(main!=null) {
+ var latch = new CountDownLatch(1);
+ main.runOnUiThread(() -> {
+ ok.set(main.delProgFile());
+ latch.countDown();
+ });
+ try {
+ latch.await();
+ } catch (InterruptedException ignored) {}
+ }
+ new JSMap("success", ok.get()).write(out);
+ } else if("getProgramName".equals(_type)) {
+ try {
+ String name = null;
+ var ins = MainActivity.ins;
+ if(ins!=null && ! ins.avas.isEmpty()) name = ins.avas.get(ins.curAva).name;
+ new JSMap("success", true, "name", name).write(out);
+ Util.println(" name "+name);
+ } catch (Exception e) {
+ Util.printStackTrace(e);
+ new JSMap("success", false, "msg", Util.toStr(e)).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("Config".equals(_type)) {
+ var entrySet = obj.entrySet();
+ for(var entry : entrySet) if(! entry.getKey().equals("_type")) Util.cfg.put(entry.getKey(), entry.getValue());
+ try (var fOut = new FileOutputStream(Util.programDir + "/cfg")) {
+ Util.cfg.write(fOut);
+ fOut.flush();
+ fOut.getFD().sync();
+ }
+ new JSMap("success", new File(Util.backImgFile).delete()).write(out);
+ } else if("getPlayerState".equals(_type)) {
+ var state = main==null ? 8 : main.state;
+ 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 = new SimpleDateFormat("yy-MM-dd HH:mm:ss.SSS");
+ var dur = 0;
+ if(main==null) writer.append("\nAPP is Closed\n");
+ else {
+ try {
+ var packageInfo = main.getPackageManager().getPackageInfo(main.getPackageName(), 0);
+ writer.append("ver: ").append(packageInfo.versionName).append("\n");
+ } catch (Exception ignored) {}
+ if(main.progView!=null) for(var page : main.progView.pages) dur += page.tDur;
+ writer.append("ProgSend: ").append(fmt.format(new File(Util.programDir + "/program").lastModified())).append(" ProgDur: ").append(String.valueOf(dur)).append("ms");
+ writer.append(" Size Avas: ").append(String.valueOf(main.avas.size())).append(" Pages: ").append(main.progView==null ? "null" : String.valueOf(main.progView.pages.size()));
+ writer.append("\n");
+ writer.append(" Launch: ").append(fmt.format(main.launchMilli)).append("\n");
+ writer.append(" Sync: ").append(fmt.format(main.syncMs)).append("\n");
+ if(main.insView==null) writer.append(" InseView is Null\n");
+ if(main.progView==null) writer.append(" ProgView is Null\n");
+ if(main.avas.isEmpty()) writer.append(" No Avas\n");
+ else writer.append("Page End: ").append(fmt.format(main.avas.get(main.curAva).endMilli)).append(" CurPage: ").append(String.valueOf(main.curAva)).append(" ").append(String.valueOf(main.avas.get(main.curAva).name)).append("\n");
+ }
+ writer.append(" Current: ").append(fmt.format(System.currentTimeMillis())).append(" ").append(String.valueOf(TimeZone.getDefault().getRawOffset()/3600000f)).append(" ").append(TimeZone.getDefault().getDisplayName()).append("\n");
+
+ if(main!=null) {
+ var mng = (WindowManager) main.getSystemService(Context.WINDOW_SERVICE);
+ var display = mng.getDefaultDisplay();
+ writer.append("RefreshRate: ").append(String.valueOf(display.getRefreshRate())).append("\n");
+ }
+
+ var statFs = new StatFs(Environment.getExternalStorageDirectory().getPath());
+ writer.append(" Disk: ").append(String.valueOf(statFs.getTotalBytes()/1000000)).append(" ").append(String.valueOf(statFs.getAvailableBytes()/1000000)).append(" ").append(String.valueOf(statFs.getFreeBytes()/1000000)).append(" (total avail free)\n");
+ if(main!=null) {
+ var actManager = (ActivityManager) main.getSystemService(MainActivity.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: ").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");
+// writer.append("/proc/stat\n");
+// IOs.writeCloseIn(writer, new FileReader("/proc/stat"));
+ writer.append("\nSockets ").append(String.valueOf(Util.socketThreads.size())).append("\n");
+ for(var socket : Util.socketThreads) writer.append(" ").append(String.valueOf(socket.socket.getInetAddress())).append(":").append(String.valueOf(socket.socket.getPort())).append(" Buf:").append(String.valueOf(socket.socket.getReceiveBufferSize()/1000)).append("k SoLinger:").append(String.valueOf(socket.socket.getSoLinger())).append("\n");
+
+ writer.append("\n");
+ writer.append("Server URL "+Util.serverURL);
+ writer.append("\n\n");
+ writer.append(Util.cfg.toStr());
+ writer.append("\n\n");
+
+ var latch = new CountDownLatch(1);
+ if(main!=null) main.runOnUiThread(() -> {
+ if(! main.avas.isEmpty()) {
+ var page = main.avas.get(main.curAva);
+ for(var layer : page.layers) for(var src : layer.srcs) if(src.view.getVisibility()==VISIBLE) {
+ try {
+ if(src.view instanceof SrcVideo) {
+ var view = (SrcVideo) src.view;
+ if(view.ijkPlayer!=null) {
+ writer.append("VideoPlaying: ").append(String.valueOf(view.ijkPlayer.isPlaying())).append("\tCur/Dur: ").append(String.valueOf(view.ijkPlayer.getCurrentPosition())).append("/").append(String.valueOf(view.ijkPlayer.getDuration())).append("\n");
+ var mediaInfo = view.ijkPlayer.getMediaInfo();
+ var vStream = mediaInfo.mMeta.mVideoStream;
+ writer.append(" BitRate: ").append(String.valueOf(view.ijkPlayer.getBitRate()/1000)).append("k\tFPS: ").append(String.valueOf(vStream.mFpsNum/(float)vStream.mFpsDen)).append(" (").append(String.valueOf(vStream.mFpsNum)).append("/").append(String.valueOf(vStream.mFpsDen)).append(")\n");
+ var tracks = view.ijkPlayer.getTrackInfo();
+ for(var track : tracks) writer.append(" ").append(track.getInfoInline()).append("\n");
+ writer.append(" Format: ").append(mediaInfo.mMeta.mFormat).append("\n");
+ writer.append("VideoDecoder: ").append(mediaInfo.mVideoDecoder).append(" ").append(mediaInfo.mVideoDecoderImpl).append(" (").append(String.valueOf(view.ijkPlayer.getVideoDecoder())).append(")\n");
+ writer.append("AudioDecoder: ").append(mediaInfo.mAudioDecoder).append(" ").append(mediaInfo.mAudioDecoderImpl).append("\n");
+ //writer.append("PROFILE: ").append(mediaInfo.mMeta.getString(IjkMediaMeta.IJKM_KEY_CODEC_PROFILE)).append("\n");
+ }
+ writer.append("\n");
+ } else if(src.view instanceof SrcWeather) {
+ var view = (SrcWeather) src.view;
+ writer.append("SrcWeather html: ").append(view.html).append("\n");
+ } else if(src.view instanceof WebView) {
+ var view = (WebView) src.view;
+ writer.append("Title: ").append(view.getTitle()).append("\n");
+ writer.append(" Url: ").append(view.getUrl()).append("\n");
+ writer.append("OriginUrl: ").append(view.getOriginalUrl()).append("\n");
+ writer.append("Progress: ").append(String.valueOf(view.getProgress())).append("\n");
+ }
+ } catch (Exception ignored) {
+ }
+ }
+ }
+ latch.countDown();
+ });
+ try {
+ latch.await();
+ } catch (InterruptedException ignored) {}
+ writer.flush();
+ } else if("GetLog".equals(_type)) {
+ out.write(Util.buf.toString().getBytes());
+ } else if("ListProgFiles".equals(_type)) {
+ var files = new File(Util.programDir).listFiles();
+ if(files!=null) {
+ Arrays.sort(files, (f1, f2) -> Long.signum(f2.lastModified() - f1.lastModified()));
+ var writer = new OutputStreamWriter(out);
+ for(var file : files) writer.append(fmt.format(new Date(file.lastModified()))).append(' ').append(file.getName()).append(' ').append(String.valueOf(file.length())).append('\n');
+ writer.flush();
+ }
+ } else if("GetFile".equals(_type)) {
+ var name = obj.str("name");
+ if(name==null) new JSMap("msg", "name is null").write(out);
+ else {
+ var file = new File(Util.programDir+"/"+name);
+ if(! file.isFile()) new JSMap("msg", "file not exist").write(out);
+ else {
+ new JSMap("len", file.length()).write(out);
+ IOs.writeCloseIn(out, new FileInputStream(file));
+ }
+ }
+ } else if("GetJsonWithFileInfo".equals(_type)) {
+ var inse = new File(Util.programDir+"/insert");
+ var files = new File(Util.programDir).listFiles();
+ if(inse.isFile()) {
+ var json = IOs.readStrClose(new FileInputStream(inse));
+ if(files!=null) for(var file : files) if(! "program".equals(file.getName())) json = json.replace("\""+file.getName()+"\"", "\""+file.getName()+"\"/*"+file.length()+" "+fmt.format(new Date(file.lastModified()))+"*/");
+ var writer = new OutputStreamWriter(out);
+ writer.append("insert:\n");
+ writer.append(json);
+ if(! json.endsWith("\n")) writer.append("\n");
+ writer.flush();
+ }
+ var prog = new File(Util.programDir+"/program");
+ if(! prog.isFile()) new JSMap("msg", "'program' file not exist").write(out);
+ else {
+ var json = IOs.readStrClose(new FileInputStream(prog));
+ if(files!=null) for(var file : files) if(! "program".equals(file.getName())) json = json.replace("\""+file.getName()+"\"", "\""+file.getName()+"\"/*"+file.length()+" "+fmt.format(new Date(file.lastModified()))+"*/");
+ var writer = new OutputStreamWriter(out);
+ writer.append("\nprogram:\n");
+ writer.append(json);
+ writer.flush();
+ }
+ }
+ out.flush();
+ Util.println("cmd end");
+ }
+ } catch (Throwable e) {
+ var emsg = e.getMessage();
+ if(e instanceof SocketTimeoutException || "Socket closed".equals(emsg) || (emsg!=null && emsg.endsWith("end-of-input"))) {
+ Util.println(emsg);
+ return;
+ }
+ if(main!=null) main.runOnUiThread(() -> Util.makeText(main, Util.toStr(e)).show());
+ Util.printStackTrace(e);
+ } finally {
+ Util.socketThreads.remove(this);
+ O.close(in, out, socket);
+ Util.println("conn end\n");
+ }
+ }
+}
diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/Util.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/Util.java
new file mode 100644
index 0000000..0a84f6b
--- /dev/null
+++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/Util.java
@@ -0,0 +1,227 @@
+package com.xixun.xixunplayer;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Build;
+import android.os.Environment;
+import android.os.StatFs;
+import android.view.Gravity;
+import android.widget.Toast;
+
+import com.aliyun.imageaudit20191230.models.ScanImageAdvanceRequest;
+import com.aliyun.tea.TeaModel;
+
+import java.io.CharArrayWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Random;
+import java.util.Set;
+import java.util.Vector;
+import java.util.concurrent.CountDownLatch;
+
+import gnph.util.IOs;
+import gnph.util.JSMap;
+import gnph.util.O;
+import wseemann.media.FFmpegMediaMetadataRetriever;
+
+
+public class Util {
+ public enum Custom{Normal, Yishi};
+ public static final Custom custom = Custom.Normal;
+ public static JSMap cfg;
+ public static SQLiteOpenHelper openHelper;
+ public static String serverURL;
+ public static volatile long downId;
+ public static int screenWidth = 1920, screenHeight = 1080;
+ public static double lat, lng;
+ public static boolean isScreenOn, logOn;
+
+ public static void initDir(Context ctx) {
+ var dir = Build.VERSION.SDK_INT < Build.VERSION_CODES.R ? Environment.getExternalStorageDirectory().getAbsolutePath() + "/XixunPlayer" : ctx.getExternalFilesDir(null).getAbsolutePath();
+ Util.println("\n---- Init dir "+dir);
+ Util.programDir = dir + "/program";
+ Util.backImgFile = dir + "/background";
+
+ var program = new File(Util.programDir);
+ if(program.isFile()) program.delete();
+ Util.println(" mkdir: "+program.mkdirs());
+ }
+
+ public static final BitmapFactory.Options noScaled = new BitmapFactory.Options();
+ static {
+ noScaled.inScaled = false;
+ }
+ public static final Vector socketThreads = new Vector<>();
+ public static final HashMap stateDescs = new HashMap<>();
+ static {
+ stateDescs.put(1, new String[]{"Initialize", "初始化"});
+ stateDescs.put(2, new String[]{"Schedules is over", "定时节目结束"});
+ stateDescs.put(3, new String[]{"No programs waiting to be played", "无待播放的节目"});
+ stateDescs.put(4, new String[]{"Delete program", "删除节目"});
+ stateDescs.put(5, new String[]{"Program processing", "处理节目中"});
+ stateDescs.put(6, new String[]{"Program Processed", "处理节目完成"});
+ stateDescs.put(7, new String[]{"Program maybe error", "节目可能有误"});
+ stateDescs.put(8, new String[]{"Screen-off", "关屏"});
+ stateDescs.put(9, new String[]{"Program's area hasn't arrived yet", "定点节目不在范围"});
+ }
+ public static final String[] stateDescsUnknow = {"Unknown", "未知"};
+
+ public static StringBuffer buf = new StringBuffer();
+ public static Random rand = new Random();
+ public static SimpleDateFormat dateFmt = new SimpleDateFormat("yy-MM-dd HH:mm:ss.SSS");
+
+ public static void println(String msg) {
+ System.out.println(msg);
+ if(buf.length()>1000000) buf.replace(0, 100000, "");
+ buf.append(msg).append("\n");
+ }
+ public static void printStackTrace(Throwable e) {
+ println(toStackTrace(e));
+ }
+
+ public static String toStr(Throwable e) {
+ var traces = e.getStackTrace();
+ var msg = e.toString();
+ for(var trace : traces) if(trace.getClassName().startsWith("com.xixun.xixunplayer.")) {
+ msg += "\n at "+trace.getFileName()+'.'+trace.getMethodName()+':'+trace.getLineNumber();
+ }
+ return msg;
+ }
+ public static String toStackTrace(Throwable e) {
+ var out = new CharArrayWriter();
+ var writer = new PrintWriter(out);
+ e.printStackTrace(writer);
+ writer.flush();
+ return out.toString();
+ }
+
+ public static Toast makeText(Context context, CharSequence text) {
+ var toast = Toast.makeText(context, text, Toast.LENGTH_LONG);
+ toast.setGravity(Gravity.TOP | Gravity.LEFT, 0, 0);
+ return toast;
+ }
+
+ public static String[] getState(int state) {
+ var descs = stateDescs.get(state);
+ return descs!=null ? descs : stateDescsUnknow;
+ }
+
+ public static String programDir, backImgFile;
+ public static String getCardId() {
+ try {
+ var bytes = IOs.readBytesClose(new FileInputStream("/data/joey/signed/card.id"));
+ if(bytes.length < 40) return "";
+ byte[] cMyKey = new byte[]{97, 119, 38, 3, 46, 112, 36, 93, 58, 100, 103, 62, 115, 112, 114, 51, 43, 61, 2, 101, 119};
+ for(int i=0; i<20; ++i) bytes[i] = (byte) (bytes[i * 2] - cMyKey[i] - i - (bytes[i * 2 + 1] - 3));
+ var cardId = new String(bytes);
+ if(cardId.length() > 13) cardId = cardId.substring(0, 13);
+ return cardId;
+ } catch (IOException e) {
+ printStackTrace(e);
+ return "";
+ }
+ }
+
+ public static void deleteFiles(long proSize, Set keeps) {
+ var statFs = new StatFs(Environment.getExternalStorageDirectory().getPath());
+ var remain = statFs.getAvailableBytes() - proSize - 400000000;
+ if(remain >= 0) return;
+ var latch = new CountDownLatch(1);
+ MainActivity.ins.runOnUiThread(() -> {
+ System.out.println("stopProg ...");
+ MainActivity.ins.stopProg();
+ 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(keeps!=null && keeps.contains(file.getName())) continue;
+ var len = file.length();
+ if(file.delete()) {
+ remain += len;
+ if(remain>=0) break;
+ }
+ }
+ }
+ public static int caseThen(int val, int cas, int then) {
+ return val==cas ? then : val;
+ }
+
+ public static com.aliyun.imageaudit20191230.Client createClient() throws Exception {
+ com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config().setAccessKeyId("LTAI5tJh71Sz9fRM3itMm2jH").setAccessKeySecret("yujMXB95cNTpG4IUJ6eFDhZwJuHl6u");
+ config.endpoint = "imageaudit.cn-shanghai.aliyuncs.com";
+ return new com.aliyun.imageaudit20191230.Client(config);
+ }
+ public static boolean check(Prog view) {
+ try {
+ var client = createClient();
+ var taskList = new ArrayList();
+ var sceneList = new ArrayList();
+ int idx = 0;
+ for(var page : view.pages) for(var layer : page.layers) for(var src : layer.srcs) {
+ if(src.view instanceof SrcVideo) {
+ var retriever = new FFmpegMediaMetadataRetriever();
+ try {
+ retriever.setDataSource(((SrcVideo) src.view).path);
+ var dur = Long.parseLong(retriever.extractMetadata(FFmpegMediaMetadataRetriever.METADATA_KEY_DURATION));
+ Util.println(" dur "+dur);
+ for(long i=0; i
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/XixunPlayer1/app/src/main/res/drawable/ic_launcher_foreground.xml b/XixunPlayer1/app/src/main/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 0000000..2b068d1
--- /dev/null
+++ b/XixunPlayer1/app/src/main/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/XixunPlayer1/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/XixunPlayer1/app/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 0000000..c209e78
Binary files /dev/null and b/XixunPlayer1/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/XixunPlayer1/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/XixunPlayer1/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..b2dfe3d
Binary files /dev/null and b/XixunPlayer1/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/XixunPlayer1/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/XixunPlayer1/app/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 0000000..4f0f1d6
Binary files /dev/null and b/XixunPlayer1/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/XixunPlayer1/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/XixunPlayer1/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..62b611d
Binary files /dev/null and b/XixunPlayer1/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/XixunPlayer1/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/XixunPlayer1/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 0000000..948a307
Binary files /dev/null and b/XixunPlayer1/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/XixunPlayer1/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/XixunPlayer1/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..1b9a695
Binary files /dev/null and b/XixunPlayer1/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/XixunPlayer1/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/XixunPlayer1/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..28d4b77
Binary files /dev/null and b/XixunPlayer1/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/XixunPlayer1/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/XixunPlayer1/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9287f50
Binary files /dev/null and b/XixunPlayer1/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/XixunPlayer1/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/XixunPlayer1/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..aa7d642
Binary files /dev/null and b/XixunPlayer1/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/XixunPlayer1/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/XixunPlayer1/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9126ae3
Binary files /dev/null and b/XixunPlayer1/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/XixunPlayer1/app/src/main/res/values/colors.xml b/XixunPlayer1/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..c8524cd
--- /dev/null
+++ b/XixunPlayer1/app/src/main/res/values/colors.xml
@@ -0,0 +1,5 @@
+
+
+ #FF000000
+ #FFFFFFFF
+
\ No newline at end of file
diff --git a/XixunPlayer1/app/src/main/res/values/strings.xml b/XixunPlayer1/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..a619236
--- /dev/null
+++ b/XixunPlayer1/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ XixunPlayer
+
\ No newline at end of file
diff --git a/XixunPlayer1/build.gradle b/XixunPlayer1/build.gradle
new file mode 100644
index 0000000..3daed1d
--- /dev/null
+++ b/XixunPlayer1/build.gradle
@@ -0,0 +1,4 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+plugins {
+id 'com.android.application' version '8.1.1' apply false
+}
\ No newline at end of file
diff --git a/XixunPlayer1/gradle.properties b/XixunPlayer1/gradle.properties
new file mode 100644
index 0000000..3e927b1
--- /dev/null
+++ b/XixunPlayer1/gradle.properties
@@ -0,0 +1,21 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=true
\ No newline at end of file
diff --git a/XixunPlayer1/gradle/wrapper/gradle-wrapper.jar b/XixunPlayer1/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..e708b1c
Binary files /dev/null and b/XixunPlayer1/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/XixunPlayer1/gradle/wrapper/gradle-wrapper.properties b/XixunPlayer1/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..d587198
--- /dev/null
+++ b/XixunPlayer1/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Oct 11 11:55:49 CST 2023
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/XixunPlayer1/gradlew b/XixunPlayer1/gradlew
new file mode 100644
index 0000000..4f906e0
--- /dev/null
+++ b/XixunPlayer1/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/XixunPlayer1/gradlew.bat b/XixunPlayer1/gradlew.bat
new file mode 100644
index 0000000..107acd3
--- /dev/null
+++ b/XixunPlayer1/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/XixunPlayer1/settings.gradle b/XixunPlayer1/settings.gradle
new file mode 100644
index 0000000..366ef33
--- /dev/null
+++ b/XixunPlayer1/settings.gradle
@@ -0,0 +1,18 @@
+pluginManagement {
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ jcenter()
+ }
+}
+
+rootProject.name = "XixunPlayer"
+include ':app'