xixunplayer
|
@ -0,0 +1 @@
|
|||
XixunPlayer
|
|
@ -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')
|
||||
|
|
|
@ -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" >
|
||||
<service
|
||||
android:name=".RestartService"
|
||||
android:name=".MainService"
|
||||
android:launchMode="singleTask"
|
||||
android:exported="false" >
|
||||
<intent-filter>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
@ -32,6 +33,7 @@
|
|||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:launchMode="singleTask"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name" >
|
||||
<intent-filter>
|
||||
|
@ -41,7 +43,8 @@
|
|||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".Server"
|
||||
android:name=".AIDLService"
|
||||
android:launchMode="singleTask"
|
||||
android:exported="true" >
|
||||
<intent-filter>
|
||||
<action android:name="com.xixun.action.PlayerInfo" />
|
||||
|
@ -50,6 +53,7 @@
|
|||
|
||||
<receiver
|
||||
android:name=".BootCompletedReceiver"
|
||||
android:launchMode="singleTask"
|
||||
android:exported="true" >
|
||||
<intent-filter android:priority="999" >
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
|
|
|
@ -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<JSMap> 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",
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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<ContentValues> 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<JSMap>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<BroadcastReceiver> reces = new ArrayList<>();
|
||||
ArrayList<ServiceConnection> services = new ArrayList<>();
|
||||
public Intent environIntent = new Intent();
|
||||
HashSet<IntentReceiver> 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<ShowHide> 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<insView.pages.size(); i++) if(insView.pages.get(i).repeatTimes <= 0) {
|
||||
for(var layer : insView.pages.get(i).layers) for(var src : layer.srcs) {
|
||||
insView.removeView(src.view);
|
||||
if(src.view instanceof Choreographer.FrameCallback) insView.calls.remove((Choreographer.FrameCallback) src.view);
|
||||
var iii = insView;
|
||||
showHides.add(new ShowHide(milli + 1000, ()->{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<BroadcastReceiver> 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");
|
||||
}
|
||||
}
|
|
@ -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<partLengths.size(); i++) {
|
||||
var part = new SrcCopy(context, box);
|
||||
var part = new SrcCopy(context, new Source(box));
|
||||
if(isVertical) {
|
||||
y -= partLengths.get(i-1);
|
||||
x += width;
|
||||
|
@ -87,10 +93,13 @@ public class Prog extends AbsLayout {
|
|||
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"));
|
||||
HashMap<String, View> videoMap = new HashMap<>();
|
||||
var waitAudio = pageMap.bool("waitAudio");
|
||||
HashMap<String, Prog.Source> 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<String> 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<Layer> layers = new ArrayList<>();
|
||||
ArrayList<Sche> 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<MainActivity.ShowHide> 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<MainActivity.ShowHide> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 = "<body style=\"color:#fff;margin:0;padding:0;";
|
||||
if(lineHeight!=null) prefix += "line-height:"+lineHeight+";";
|
||||
if(! hasFPS && lineHeight!=null) prefix += "line-height:"+lineHeight+";";
|
||||
prefix += "\">";
|
||||
suffix = "</body>";
|
||||
try {
|
||||
var aaa = html.substring(0, 16).trim();
|
||||
if(aaa.startsWith("<html") || aaa.startsWith("<body") || aaa.startsWith("<!DOCTYPE")) prefix = suffix = "";
|
||||
var start = html.substring(0, 16).trim();
|
||||
if(start.startsWith("<body>")) {
|
||||
suffix = "";
|
||||
html = html.substring(6);
|
||||
} else if(start.startsWith("<html") || start.startsWith("<body") || start.startsWith("<!DOCTYPE")) prefix = suffix = "";
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
hasDay = html.contains("%d");
|
||||
|
@ -78,11 +85,19 @@ public class SrcCountdown extends WebView implements Choreographer.FrameCallback
|
|||
loadDataWithBaseURL(null, prefix+htm+suffix, "text/html", "UTF-8", null);
|
||||
}
|
||||
|
||||
long lastSec;
|
||||
long lastSec, lastMs;
|
||||
|
||||
@Override
|
||||
public void doFrame(long ms) {
|
||||
if(! isShown()) return;
|
||||
if(hasFPS) {
|
||||
var fps = 1000.0 / (ms - lastMs);
|
||||
lastMs = ms;
|
||||
var htm = html;
|
||||
htm = htm.replace("%FPS", fmt00_0.format(fps));
|
||||
loadDataWithBaseURL(null, prefix+htm+suffix, "text/html", "UTF-8", null);
|
||||
return;
|
||||
}
|
||||
var sec = ms / 1000;
|
||||
if(sec != lastSec) {
|
||||
lastSec = sec;
|
||||
|
|
|
@ -16,6 +16,7 @@ import java.util.TimeZone;
|
|||
import gnph.android.LinearBox;
|
||||
import gnph.util.JSList;
|
||||
import gnph.util.JSMap;
|
||||
import gnph.util.NumFmts;
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
public class SrcDigiClock extends LinearBox implements Choreographer.FrameCallback {
|
||||
|
@ -31,8 +32,7 @@ public class SrcDigiClock extends LinearBox implements Choreographer.FrameCallba
|
|||
ImageView[] secComps = {new ImageView(getContext()), new ImageView(getContext())};
|
||||
|
||||
Calendar calendar;
|
||||
SimpleDateFormat timeptn, dataFmt = new SimpleDateFormat("yyyyMMdd");
|
||||
boolean multiline, weekly, isSingleMonth;
|
||||
boolean multiline, weekly, isSingleMD, isHour12, isSingleHour;
|
||||
|
||||
public SrcDigiClock(Prog prog, JSMap json) {
|
||||
super(prog.getContext());
|
||||
|
@ -52,12 +52,12 @@ public class SrcDigiClock extends LinearBox implements Choreographer.FrameCallba
|
|||
}
|
||||
} else for(var pic : pics) imgs.put(pic.str("name"), BitmapFactory.decodeFile(Util.programDir+"/"+pic.stnn("id")));
|
||||
int dateStyle = json.intg("dateStyle");
|
||||
isSingleMonth = dateStyle==1||dateStyle==2||dateStyle==4||dateStyle==6||dateStyle==8||dateStyle==10||dateStyle==12;
|
||||
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");
|
||||
var hour12 = json.bool("hour12");
|
||||
var AmPm = hour12 && json.bool("AmPm");
|
||||
timeptn = new SimpleDateFormat(hour12 ? "hhmmssa" : "HHmmss");
|
||||
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");
|
||||
|
@ -239,30 +239,35 @@ public class SrcDigiClock extends LinearBox implements Choreographer.FrameCallba
|
|||
void cal() {
|
||||
//var dt = timeZone==null ? LocalDateTime.now() : LocalDateTime.now(timeZone);
|
||||
calendar.setTimeInMillis(lastSec*1000);
|
||||
var date = calendar.getTime();
|
||||
var hms = timeptn.format(date);
|
||||
ampmComp.setImageBitmap(imgs.get(calendar.get(Calendar.HOUR_OF_DAY) < 12?"AM":"PM"));
|
||||
hourComps[0].setImageBitmap(imgs.get(hms.substring(0,1)));
|
||||
hourComps[1].setImageBitmap(imgs.get(hms.substring(1,2)));
|
||||
minComps[0].setImageBitmap(imgs.get(hms.substring(2,3)));
|
||||
minComps[1].setImageBitmap(imgs.get(hms.substring(3,4)));
|
||||
secComps[0].setImageBitmap(imgs.get(hms.substring(4,5)));
|
||||
secComps[1].setImageBitmap(imgs.get(hms.substring(5,6)));
|
||||
var ymd = dataFmt.format(date);
|
||||
if(yearComps[0].getDrawable()==null || ! ymd.equals(lastYmd)) {
|
||||
lastYmd = ymd;
|
||||
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]));
|
||||
yearComps[0].setImageBitmap(imgs.get(ymd.substring(0,1)));
|
||||
yearComps[1].setImageBitmap(imgs.get(ymd.substring(1,2)));
|
||||
yearComps[2].setImageBitmap(imgs.get(ymd.substring(2,3)));
|
||||
yearComps[3].setImageBitmap(imgs.get(ymd.substring(3,4)));
|
||||
monthComps[0].setImageBitmap(isSingleMonth && ymd.charAt(4)=='0' ? null : imgs.get(ymd.substring(4,5)));
|
||||
monthComps[1].setImageBitmap(imgs.get(ymd.substring(5,6)));
|
||||
dayComps[0].setImageBitmap(isSingleMonth && ymd.charAt(6)=='0' ? null : imgs.get(ymd.substring(6,7)));
|
||||
dayComps[1].setImageBitmap(imgs.get(ymd.substring(7,8)));
|
||||
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)));
|
||||
}
|
||||
}
|
||||
String lastYmd;
|
||||
int lastDay;
|
||||
|
||||
long lastSec;
|
||||
|
||||
|
|
|
@ -63,20 +63,17 @@ public class SrcDigitalClock extends WebView implements Choreographer.FrameCallb
|
|||
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.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(caseThen(calendar.get(Calendar.HOUR), 0, 12)));
|
||||
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+"</body>", "text/html", "UTF-8", null);
|
||||
}
|
||||
int caseThen(int val, int cas, int then) {
|
||||
return val==cas ? then : val;
|
||||
}
|
||||
|
||||
long lastSec;
|
||||
|
||||
|
|
|
@ -51,6 +51,8 @@ public class SrcSensor extends WebView implements IntentReceiver, Choreographer.
|
|||
prefix = "<body style=\"color:#fff; margin:0;padding:0;";
|
||||
var lineHeight = json.str("lineHeight");
|
||||
if(lineHeight!=null) prefix += "line-height:"+lineHeight+";";
|
||||
var backgroundColor = json.str("backgroundColor");
|
||||
if(backgroundColor!=null) prefix += "background-color:"+backgroundColor+";";
|
||||
if(json.bool("center")) prefix += "display:flex; align-items:center;";
|
||||
prefix += "\">";
|
||||
prog.calls.add(this);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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 = "<body style=\"color:#fff;margin:0;padding:0;";
|
||||
if(lineHeight!=null) prefix += "line-height:"+lineHeight+";";
|
||||
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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<cnt; rr++) writer.append(" Renderer: ").append(String.valueOf(view.exoPlayer.getRendererType(rr))).append(" ").append(String.valueOf(view.exoPlayer.getRenderer(rr))).append("\n");
|
||||
writer.append("getPlaybackParameters: ").append(String.valueOf(view.exoPlayer.getPlaybackParameters())).append("\n");
|
||||
}
|
||||
writer.append("\n");
|
||||
} else if(src.view instanceof SrcWeather) {
|
||||
|
@ -283,7 +356,7 @@ public class TCPThread extends Thread {
|
|||
} else if("ListProgFiles".equals(_type)) {
|
||||
var files = new File(Util.programDir).listFiles();
|
||||
if(files!=null) {
|
||||
Arrays.sort(files, (f1, f2) -> (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();
|
||||
|
|
|
@ -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<String> 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<ScanImageAdvanceRequest.ScanImageAdvanceRequestTask>();
|
||||
var sceneList = new ArrayList<String>();
|
||||
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<dur; i+=10000) {
|
||||
var frame = retriever.getFrameAtTime(i*1000, FFmpegMediaMetadataRetriever.OPTION_CLOSEST);
|
||||
var file = Util.programDir+"/capture"+(++idx);
|
||||
var out = new FileOutputStream(file);
|
||||
frame.compress(Bitmap.CompressFormat.JPEG, 75, out);
|
||||
O.close(out);
|
||||
var in = new FileInputStream(file);
|
||||
var task = new ScanImageAdvanceRequest.ScanImageAdvanceRequestTask();
|
||||
task.setDataId("capture"+idx);
|
||||
task.setImageTimeMillisecond(1L);
|
||||
task.setInterval(1);
|
||||
task.setMaxFrames(1);
|
||||
task.setImageURLObject(in);
|
||||
taskList.add(task);
|
||||
sceneList.add("porn");
|
||||
}
|
||||
} finally {
|
||||
retriever.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
var scanImageAdvanceRequest = new com.aliyun.imageaudit20191230.models.ScanImageAdvanceRequest().setTask(taskList).setScene(sceneList);
|
||||
var runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
try {
|
||||
var scanImageResponse = client.scanImageAdvance(scanImageAdvanceRequest, runtime);
|
||||
Util.println(com.aliyun.teautil.Common.toJSONString(TeaModel.buildMap(scanImageResponse))); // 获取整体结果
|
||||
var results = scanImageResponse.getBody().getData().results;
|
||||
for(var result : results) {
|
||||
Util.println(" dataId "+result.dataId);
|
||||
Util.println(" imageURL "+result.imageURL);
|
||||
for(var subResult : result.subResults) {
|
||||
Util.println(" suggestion "+subResult.suggestion);
|
||||
if(! "pass".equals(subResult.suggestion)) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (com.aliyun.tea.TeaException teaException) {
|
||||
Util.println(com.aliyun.teautil.Common.toJSONString(teaException)); // 获取整体报错信息
|
||||
//Util.println(teaException.getCode());// 获取单个字段
|
||||
return false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Util.printStackTrace(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
|
@ -0,0 +1,3 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
|
@ -0,0 +1 @@
|
|||
XixunPlayer
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="17" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="testRunner" value="GRADLE" />
|
||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleJvm" value="jbr-17" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,14 @@
|
|||
<project version="4">
|
||||
<component name="EntryPointsManager">
|
||||
<list size="1">
|
||||
<item index="0" class="java.lang.String" itemvalue="android.webkit.JavascriptInterface" />
|
||||
</list>
|
||||
</component>
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1 @@
|
|||
/build
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,64 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- android:sharedUserId="android.uid.system" android:sharedUserMaxSdkVersion="32">-->
|
||||
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission
|
||||
android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
||||
tools:ignore="ScopedStorage" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:largeHeap="true"
|
||||
android:allowBackup="true"
|
||||
android:usesCleartextTraffic="true"
|
||||
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" >
|
||||
<service
|
||||
android:name=".MainService"
|
||||
android:launchMode="singleTask"
|
||||
android:exported="false" >
|
||||
<intent-filter>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:launchMode="singleTask"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".AIDLService"
|
||||
android:launchMode="singleTask"
|
||||
android:exported="true" >
|
||||
<intent-filter>
|
||||
<action android:name="com.xixun.action.PlayerInfo" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<receiver
|
||||
android:name=".BootCompletedReceiver"
|
||||
android:launchMode="singleTask"
|
||||
android:exported="true" >
|
||||
<intent-filter android:priority="999" >
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<meta-data android:name="android.max_aspect" android:value="2.1" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -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();
|
||||
}
|
After Width: | Height: | Size: 680 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 929 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 748 B |
After Width: | Height: | Size: 877 B |
After Width: | Height: | Size: 923 B |
After Width: | Height: | Size: 866 B |
After Width: | Height: | Size: 698 B |
After Width: | Height: | Size: 878 B |
After Width: | Height: | Size: 647 B |
After Width: | Height: | Size: 937 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 669 B |
After Width: | Height: | Size: 966 B |
After Width: | Height: | Size: 569 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 964 B |
After Width: | Height: | Size: 971 B |
After Width: | Height: | Size: 937 B |
After Width: | Height: | Size: 833 B |
After Width: | Height: | Size: 673 B |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 1003 B |
After Width: | Height: | Size: 964 B |
After Width: | Height: | Size: 787 B |
After Width: | Height: | Size: 829 B |
After Width: | Height: | Size: 904 B |
After Width: | Height: | Size: 2.2 KiB |
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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<JSMap> jpages = task.jslist("items");
|
||||
int proSize = 0;
|
||||
var needDowns = new ArrayList<NeedDowns>();
|
||||
var hases = new HashSet<String>();
|
||||
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<JSMap> 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<JSMap> 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<JSMap> 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<Src> srcs = new ArrayList<>();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<rect.bottom; y+=img.getHeight()) for(int x=0; x<rect.right; x+=img.getWidth()) canvas.drawBitmap(img, x, y, null);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
package com.xixun.xixunplayer;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
public class BootCompletedReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Util.println("==== BootCompletedReceiver onReceive >> "+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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package com.xixun.xixunplayer;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
public interface IntentReceiver {
|
||||
void onReceive(Intent intent);
|
||||
}
|
|
@ -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<ContentValues> 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<JSMap>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<BroadcastReceiver> reces = new ArrayList<>();
|
||||
ArrayList<ServiceConnection> services = new ArrayList<>();
|
||||
public Intent environIntent = new Intent();
|
||||
HashSet<IntentReceiver> environs = new HashSet<>();
|
||||
BackView backView;
|
||||
Prog progView, insView;
|
||||
long launchMilli = System.currentTimeMillis();
|
||||
long syncMs;
|
||||
int state;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
ins = this;
|
||||
Util.println("==>> MainActivity onCreate >>>> this "+hashCode()+" Thread: "+Thread.currentThread().getId());
|
||||
|
||||
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitAll().build());
|
||||
setContentView(backView = new BackView(MainActivity.this));
|
||||
state = 5;
|
||||
|
||||
hookWebView();
|
||||
|
||||
if(ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) init();
|
||||
else {
|
||||
Util.println("---- No permission, Try again ...");
|
||||
Util.makeText(this, "No permission, Try again ...").show();
|
||||
ActivityCompat.requestPermissions(this, new String[]{
|
||||
android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
||||
android.Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
android.Manifest.permission.MANAGE_EXTERNAL_STORAGE,
|
||||
android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
|
||||
android.Manifest.permission.INTERNET
|
||||
}, 999);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
if(Util.programDir!=null) return;
|
||||
if(requestCode==999 && grantResults.length > 0 && grantResults[0]==PackageManager.PERMISSION_GRANTED) init();
|
||||
else {
|
||||
Util.println("---- Request Permission Failed");
|
||||
Util.makeText(this, "Request Permission Failed").show();
|
||||
}
|
||||
}
|
||||
@SuppressLint("SoonBlockedPrivateApi")
|
||||
public static void hookWebView() {
|
||||
if(android.os.Process.myUid() != android.os.Process.SYSTEM_UID) return;
|
||||
try {
|
||||
var factoryClass = Class.forName("android.webkit.WebViewFactory");
|
||||
var field = factoryClass.getDeclaredField("sProviderInstance");
|
||||
field.setAccessible(true);
|
||||
var sProviderInstance = field.get(null);
|
||||
if (sProviderInstance != null) {
|
||||
Util.println("sProviderInstance isn't null");
|
||||
return;
|
||||
}
|
||||
Method getProviderClassMethod;
|
||||
if (Build.VERSION.SDK_INT > 22) { // above 22
|
||||
getProviderClassMethod = factoryClass.getDeclaredMethod("getProviderClass");
|
||||
} else if (Build.VERSION.SDK_INT == 22) { // method name is a little different
|
||||
getProviderClassMethod = factoryClass.getDeclaredMethod("getFactoryClass");
|
||||
} else { // no security check below 22
|
||||
Util.println("Don't need to Hook WebView");
|
||||
return;
|
||||
}
|
||||
getProviderClassMethod.setAccessible(true);
|
||||
var providerClass = (Class<?>) getProviderClassMethod.invoke(factoryClass);
|
||||
var delegateClass = Class.forName("android.webkit.WebViewDelegate");
|
||||
var declaredConstructor = delegateClass.getDeclaredConstructor();
|
||||
declaredConstructor.setAccessible(true);
|
||||
sProviderInstance = providerClass.getDeclaredMethod("create", delegateClass).invoke(providerClass, declaredConstructor.newInstance());
|
||||
field.set("sProviderInstance", sProviderInstance);
|
||||
Util.println("sProviderInstance"+sProviderInstance.toString());
|
||||
Util.println("Hook Done!");
|
||||
} catch (Throwable e) {
|
||||
Util.printStackTrace(e);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
Util.println("==<< MainActivity onDestroy <<<< this "+hashCode());
|
||||
state = 8;
|
||||
if(insView!=null) {
|
||||
insView.release();
|
||||
insView = null;
|
||||
}
|
||||
if(progView!=null) {
|
||||
progView.release();
|
||||
progView = null;
|
||||
}
|
||||
ins = null;
|
||||
for(var rece : reces) unregisterReceiver(rece);
|
||||
for(var service : services) unbindService(service);
|
||||
try (var fout = new FileOutputStream(Util.programDir+"/cfg")){
|
||||
Util.cfg.write(fout);
|
||||
fout.flush();
|
||||
fout.getFD().sync();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
System.gc();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
Util.println(" ==>> MainActivity onStart >> "+hashCode());
|
||||
}
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
Util.println(" ==<< MainActivity onStop << "+hashCode());
|
||||
}
|
||||
@Override
|
||||
protected void onRestart() {
|
||||
super.onRestart();
|
||||
Util.println(" ==>> MainActivity onRestart >> "+hashCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
Util.println(" ==>> MainActivity onResume >> "+hashCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
Util.println(" ==<< MainActivity onPause << "+hashCode());
|
||||
}
|
||||
|
||||
CardService serviCard;
|
||||
ConnService serviXy;
|
||||
Intent intenCard;
|
||||
ServiceConnection connCard;
|
||||
@SuppressLint("UnspecifiedRegisterReceiverFlag")
|
||||
public void init() {
|
||||
Util.initDir(this);
|
||||
if(MainService.ins==null) startService(new Intent(this, MainService.class));
|
||||
|
||||
var cfg = new File(Util.programDir+"/cfg");
|
||||
if(! cfg.isFile()) Util.cfg = new JSMap();
|
||||
else {
|
||||
try {
|
||||
Util.cfg = JSMap.fromClose(new FileInputStream(cfg));
|
||||
} catch (Exception e) {
|
||||
Util.cfg = new JSMap();
|
||||
Util.makeText(MainActivity.this, Util.toStr(e)).show();
|
||||
Util.printStackTrace(e);
|
||||
}
|
||||
}
|
||||
Util.println(Util.cfg.toStr());
|
||||
Util.logOn = Util.cfg.get("logUploadUrl")!=null;
|
||||
Util.openHelper = new SQLiteOpenHelper(this, "aaa", null, 1) {
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
db.execSQL("create table cfg(k varchar(32) primary key, v varchar(128))");
|
||||
db.execSQL("create table log(time integer primary key, dur integer, id varchar(64), name varchar(64), type varchar(16), lat double, lng double)");
|
||||
}
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
}
|
||||
};
|
||||
new LogThread().start();
|
||||
if(Util.logOn) {
|
||||
var db = Util.openHelper.getWritableDatabase();
|
||||
var cursor = db.rawQuery("select count(*) aaa from log", null);
|
||||
while(cursor.moveToNext()) Util.println("Log Cnt "+cursor.getString(0));
|
||||
db.close();
|
||||
}
|
||||
connCard = new ServiceConnection() {
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
serviCard = null;
|
||||
Util.println("<-<- AIDL Service cardsystem Disconnected");
|
||||
}
|
||||
|
||||
public void onServiceConnected(ComponentName name, IBinder iBinder) {
|
||||
unbindService(this);
|
||||
services.remove(this);
|
||||
Util.println("->-> AIDL Service cardsystem Connected");
|
||||
serviCard = CardService.Stub.asInterface(iBinder);
|
||||
try {
|
||||
Util.isScreenOn = serviCard.isScreenOpen();
|
||||
Util.screenWidth = serviCard.getScreenWidth();
|
||||
Util.screenHeight = serviCard.getScreenHeight();
|
||||
Util.println(" IsScreenOn: "+Util.isScreenOn+" screen: "+Util.screenWidth+" x "+Util.screenHeight);
|
||||
backView.invalidate();
|
||||
if(Util.isScreenOn) initProg();
|
||||
else state = 8;
|
||||
} catch (Exception e) {
|
||||
Util.makeText(MainActivity.this, Util.toStr(e)).show();
|
||||
Util.printStackTrace(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
intenCard = new Intent("com.xixun.joey.aidlset.SettingsService");
|
||||
intenCard.setPackage("com.xixun.joey.cardsystem");
|
||||
bindService(intenCard, connCard, Context.BIND_AUTO_CREATE);
|
||||
services.add(connCard);
|
||||
|
||||
var connXy = new ServiceConnection() {
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
serviXy = null;
|
||||
Util.println("<-<- AIDL Service xy.conn Disconnected");
|
||||
}
|
||||
public void onServiceConnected(ComponentName name, IBinder iBinder) {
|
||||
unbindService(this);
|
||||
services.remove(this);
|
||||
Util.println("->-> AIDL Service xy.conn Connected");
|
||||
serviXy = ConnService.Stub.asInterface(iBinder);
|
||||
try {
|
||||
Util.serverURL = serviXy.getServerURL();
|
||||
Util.println(" ServerURL: "+Util.serverURL);
|
||||
if(Util.serverURL==null || Util.serverURL.isEmpty()) Util.serverURL = "https://m2mled.net/";
|
||||
else {
|
||||
if(! Util.serverURL.startsWith("http")) Util.serverURL = "http://"+Util.serverURL;
|
||||
if(! Util.serverURL.endsWith("/")) Util.serverURL += "/";
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Util.makeText(MainActivity.this, Util.toStr(e)).show();
|
||||
Util.printStackTrace(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
var intenXy = new Intent("xixun.intent.action.CONNECTION_INFO");
|
||||
intenXy.setPackage("com.xixun.xy.conn");
|
||||
bindService(intenXy, connXy, Context.BIND_AUTO_CREATE);
|
||||
services.add(connXy);
|
||||
|
||||
reces.clear();
|
||||
BroadcastReceiver rece;
|
||||
registerReceiver(rece = new BroadcastReceiver(){
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Util.println("Receive PAUSE_PLAYER");
|
||||
Util.isScreenOn = ! intent.getBooleanExtra("pause", false);
|
||||
Util.println(" IsScreenOn: "+Util.isScreenOn);
|
||||
if(! Util.isScreenOn) {
|
||||
state = 8;
|
||||
if(insView!=null) {
|
||||
insView.release();
|
||||
insView = null;
|
||||
}
|
||||
if(progView!=null) {
|
||||
progView.release();
|
||||
progView = null;
|
||||
}
|
||||
setContentView(backView);
|
||||
} else if(progView==null && insView==null) MainActivity.ins.runOnUiThread(() -> initProg());
|
||||
System.gc();
|
||||
}
|
||||
}, new IntentFilter("com.xixun.action.PAUSE_PLAYER"));
|
||||
reces.add(rece);
|
||||
|
||||
registerReceiver(rece = new BroadcastReceiver(){
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Util.println("Receive CHANGE_COMPANYID");
|
||||
try {
|
||||
if(serviXy!=null && serviXy.asBinder().isBinderAlive()) {
|
||||
Util.serverURL = serviXy.getServerURL();
|
||||
Util.println(" ServerURL: "+Util.serverURL);
|
||||
if(Util.serverURL==null || Util.serverURL.isEmpty()) Util.serverURL = "https://m2mled.net/";
|
||||
else {
|
||||
if(! Util.serverURL.startsWith("http")) Util.serverURL = "http://"+Util.serverURL;
|
||||
if(! Util.serverURL.endsWith("/")) Util.serverURL += "/";
|
||||
}
|
||||
} else {
|
||||
Util.println(" bindService");
|
||||
bindService(intenXy, connXy, Context.BIND_AUTO_CREATE);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Util.makeText(MainActivity.this, Util.toStr(e)).show();
|
||||
Util.printStackTrace(e);
|
||||
}
|
||||
}
|
||||
}, new IntentFilter("com.xixun.joey.CHANGE_COMPANYID"));
|
||||
reces.add(rece);
|
||||
|
||||
registerReceiver(rece = new BroadcastReceiver(){
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
MainActivity.this.environIntent = intent;
|
||||
for(var environ : environs) {
|
||||
environ.onReceive(intent);
|
||||
((View)environ).invalidate();
|
||||
}
|
||||
}
|
||||
}, new IntentFilter("xixun.intent.action.TEMPERATURE_HUMIDITY"));
|
||||
reces.add(rece);
|
||||
|
||||
registerReceiver(rece = new BroadcastReceiver(){
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Util.lat = intent.getDoubleExtra("latitude", 0);
|
||||
Util.lng = intent.getDoubleExtra("longitude", 0);
|
||||
Util.println(" lat "+Util.lat+" lng "+Util.lng);
|
||||
}
|
||||
}, new IntentFilter("com.xixun.joey.gpsinfo"));
|
||||
reces.add(rece);
|
||||
|
||||
registerReceiver(rece = new BroadcastReceiver(){
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
try {
|
||||
var code = intent.getIntExtra("code", 0);
|
||||
Util.println(" remote_control "+code);
|
||||
if(progView == null || progView.pages.isEmpty() || ! progView.isShown() || code==-1 || code < -3 || code > progView.pages.size()) return;
|
||||
if(code<=-2) {
|
||||
var idx = avas.isEmpty() ? -1 : progView.pages.indexOf(avas.get(curAva));
|
||||
if(code==-2) code = idx<=0 ? progView.pages.size() : idx;
|
||||
else code = idx>=progView.pages.size()-1 ? 1 : idx+2;
|
||||
}
|
||||
var ms = System.currentTimeMillis();
|
||||
if(! avas.isEmpty()) {
|
||||
avas.get(curAva).hide();
|
||||
if(code > 0 && avas.get(curAva)==progView.pages.get(code-1)) for(var call : progView.calls) call.doFrame(ms);
|
||||
}
|
||||
if(code==0) syncProg(ms, 0);
|
||||
else {
|
||||
avas.clear();
|
||||
var page = progView.pages.get(code-1);
|
||||
avas.add(page);
|
||||
curAva = 0;
|
||||
curTimes = 1;
|
||||
waitTo = 0; //点播
|
||||
page.setMillis(ms, ms);
|
||||
if(state != 6) {
|
||||
setContentView(progView);
|
||||
state = 6;
|
||||
}
|
||||
}
|
||||
var root = JSMap.fromClose(new BufferedInputStream(new FileInputStream(Util.programDir + "/program")));
|
||||
root.put("Demand", code);
|
||||
try (var fOut = new FileOutputStream(Util.programDir + "/program")) {
|
||||
root.write(fOut);
|
||||
fOut.flush();
|
||||
fOut.getFD().sync();
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Util.makeText(MainActivity.this, Util.toStr(e)).show();
|
||||
Util.printStackTrace(e);
|
||||
}
|
||||
}
|
||||
}, new IntentFilter("com.xixun.yzd.REMOTE_CONTROL"));
|
||||
reces.add(rece);
|
||||
|
||||
if(Util.custom==Util.Custom.Yishi) {
|
||||
new Thread(()->{
|
||||
while(true) {
|
||||
try {
|
||||
Thread.sleep(10000);
|
||||
if(serviCard!=null && serviCard.asBinder().isBinderAlive()) {
|
||||
var www = serviCard.getScreenWidth();
|
||||
var hhh = serviCard.getScreenHeight();
|
||||
if(www!=Util.screenWidth || hhh!=Util.screenHeight) {
|
||||
var ins = MainActivity.ins;
|
||||
if(ins!=null) ins.runOnUiThread(() -> {
|
||||
Util.screenWidth = www;
|
||||
Util.screenHeight = hhh;
|
||||
ins.backView.invalidate();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
Util.println(" bindService");
|
||||
var ins = MainActivity.ins;
|
||||
if(ins!=null) ins.runOnUiThread(() -> ins.bindService(ins.intenCard, ins.connCard, Context.BIND_AUTO_CREATE));
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
var ins = MainActivity.ins;
|
||||
if(ins!=null) ins.runOnUiThread(() -> Util.makeText(ins, Util.toStr(e)).show());
|
||||
Util.printStackTrace(e);
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
|
||||
public void stopProg() {
|
||||
avas.clear();
|
||||
curAva = 0;
|
||||
curTimes = 1;
|
||||
if(insView!=null) {
|
||||
insView.release();
|
||||
insView = null;
|
||||
}
|
||||
if(progView!=null) {
|
||||
progView.release();
|
||||
progView = null;
|
||||
}
|
||||
setContentView(backView);
|
||||
System.gc();
|
||||
}
|
||||
public boolean delProgFile() {
|
||||
stopProg();
|
||||
var files = new File(Util.programDir).listFiles();
|
||||
var ok = true;
|
||||
if(files != null) for(var file : files) if(! file.delete()) ok = false;
|
||||
state = 4;
|
||||
try {
|
||||
var out = new FileOutputStream(Util.programDir+"/program");
|
||||
out.write("{}".getBytes());
|
||||
out.flush();
|
||||
out.getFD().sync();
|
||||
out.close();
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
public void initProg() {
|
||||
state = 1;
|
||||
try {
|
||||
Util.println("\nParse Insert Prog Json");
|
||||
var root = JSMap.fromClose(new BufferedInputStream(new FileInputStream(Util.programDir + "/insert")));
|
||||
var task = root.jsmap("task");
|
||||
if(task==null && root.containsKey("layers")) task = new JSMap("items", new JSList<>(new JSMap("_program", root)));
|
||||
if(task!=null) {
|
||||
var view = new Prog(task, this);
|
||||
if(view.getChildCount()!=0) setContentView(insView = view);
|
||||
}
|
||||
} catch(FileNotFoundException ignored) {
|
||||
} catch(Throwable e) {
|
||||
state = 7;
|
||||
Util.printStackTrace(e);
|
||||
}
|
||||
var demand = 0;
|
||||
try {
|
||||
Util.println("\nParse Prog Json");
|
||||
var root = JSMap.fromClose(new BufferedInputStream(new FileInputStream(Util.programDir + "/program")));
|
||||
var task = root.jsmap("task");
|
||||
demand = root.intg("Demand");
|
||||
if(task==null && root.containsKey("layers")) task = new JSMap("items", new JSList<>(new JSMap("_program", root)));
|
||||
if(task==null) Util.println(root.isEmpty() ? " Empty program JSON\n" : " Error: task==null\n");
|
||||
else {
|
||||
var view = new Prog(task, this);
|
||||
if(view.getChildCount()==0) Util.println(" Error: ChildCount==0\n");
|
||||
else setContentView(progView = view);
|
||||
}
|
||||
} catch(FileNotFoundException e) {
|
||||
Util.println(""+e);
|
||||
} catch(Throwable e) {
|
||||
state = 7;
|
||||
Util.makeText(this, Util.toStr(e)).show();
|
||||
Util.printStackTrace(e);
|
||||
}
|
||||
if(insView!=null || progView!=null) {
|
||||
state = 5;
|
||||
Util.println("Init Sync");
|
||||
var ms = System.currentTimeMillis();
|
||||
if(demand==0 || progView==null) syncProg((ms+999)/1000*1000, 0);
|
||||
else {
|
||||
avas.clear();
|
||||
var page = progView.pages.get(demand-1);
|
||||
avas.add(page);
|
||||
curAva = 0;
|
||||
curTimes = 1;
|
||||
waitTo = 0; //点播
|
||||
page.setMillis(ms, ms);
|
||||
if(state != 6) {
|
||||
setContentView(progView);
|
||||
state = 6;
|
||||
}
|
||||
}
|
||||
if(canAdd) {
|
||||
choreographer.postFrameCallback(this);
|
||||
canAdd = false;
|
||||
}
|
||||
} else if(state != 7) state = 3;
|
||||
}
|
||||
public void initProg(byte[] json) {
|
||||
try {
|
||||
Util.println("\nParse Prog Json");
|
||||
var root = JSMap.from(json);
|
||||
var task = root.jsmap("task");
|
||||
if(task==null) {
|
||||
if(! root.containsKey("layers")) {
|
||||
state = 7;
|
||||
Util.println(" Error: task==null\n");
|
||||
Util.println(new String(json, Chsets.UTF8));
|
||||
return;
|
||||
}
|
||||
task = new JSMap("items", new JSList<>(new JSMap("_program", root)));
|
||||
}
|
||||
var view = new Prog(task, this);
|
||||
if(view.getChildCount()==0) {
|
||||
if(! view.isInsert) state = 7;
|
||||
Util.println(" Error: ChildCount==0\n");
|
||||
Util.println(new String(json, Chsets.UTF8));
|
||||
return;
|
||||
}
|
||||
if(Util.cfg.bool("needCheck")) {
|
||||
new Thread(()->{
|
||||
if(Util.check(view)) runOnUiThread(() -> {
|
||||
afterCheck(view, json);
|
||||
});
|
||||
else runOnUiThread(() -> {
|
||||
view.release();
|
||||
Util.makeText(this, "Contents are not passed").show();
|
||||
Util.println("Contents are not passed");
|
||||
});
|
||||
}).start();
|
||||
} else afterCheck(view, json);
|
||||
} catch (Throwable e) {
|
||||
state = 7;
|
||||
Util.makeText(this, Util.toStr(e)).show();
|
||||
Util.printStackTrace(e);
|
||||
Util.println(new String(json, Chsets.UTF8));
|
||||
}
|
||||
}
|
||||
public void afterCheck(Prog view, byte[] json) {
|
||||
try {
|
||||
if(view.isInsert) {
|
||||
if(insView!=null) insView.release();
|
||||
insView = view;
|
||||
try { avas.get(curAva).hide();} catch (Throwable ignored) {}
|
||||
setContentView(insView);
|
||||
} else {
|
||||
if(progView!=null) progView.release();
|
||||
progView = view;
|
||||
setContentView(progView);
|
||||
}
|
||||
var fOut = new FileOutputStream(Util.programDir + (view.isInsert?"/insert":"/program"));
|
||||
fOut.write(json);
|
||||
fOut.flush();
|
||||
fOut.getFD().sync();
|
||||
fOut.close();
|
||||
state = 5;
|
||||
System.gc();
|
||||
Util.println("Init Sync");
|
||||
syncProg((System.currentTimeMillis()+999)/1000*1000, 0);
|
||||
if(canAdd) {
|
||||
choreographer.postFrameCallback(this);
|
||||
canAdd = false;
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
state = 7;
|
||||
Util.makeText(this, Util.toStr(e)).show();
|
||||
Util.printStackTrace(e);
|
||||
Util.println(new String(json, Chsets.UTF8));
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<Prog.Page> avas = new ArrayList<>();
|
||||
int curAva, curTimes = 1;
|
||||
long waitTo = Long.MAX_VALUE;
|
||||
boolean isInsert;
|
||||
|
||||
Choreographer choreographer = Choreographer.getInstance();
|
||||
boolean canAdd = true;
|
||||
|
||||
@Override
|
||||
public void doFrame(long frameTimeNanos) {
|
||||
if(progView == null && insView==null) {
|
||||
canAdd = true;
|
||||
return;
|
||||
}
|
||||
choreographer.postFrameCallback(this);
|
||||
canAdd = false;
|
||||
var milli = System.currentTimeMillis();
|
||||
if(avas.isEmpty()) {
|
||||
if(milli >= waitTo) {
|
||||
waitTo = Long.MAX_VALUE;
|
||||
Util.println("wait sync");
|
||||
syncProg(milli, 0);
|
||||
if(insView!=null) for(var call : insView.calls) call.doFrame(milli);
|
||||
if(progView!=null) for(var call : progView.calls) call.doFrame(milli);
|
||||
}
|
||||
return;
|
||||
}
|
||||
var lastPage = avas.get(curAva);
|
||||
if(milli < lastPage.endMilli) lastPage.showHideSrcs(milli);
|
||||
else {
|
||||
lastPage.hide();
|
||||
if(isInsert) {
|
||||
if(--lastPage.repeatTimes<=0 && ++curAva >= avas.size()) {
|
||||
var isDiff = milli-lastPage.endMilli>=1000;
|
||||
syncProg(isDiff ? milli : lastPage.endMilli, milli);
|
||||
if(insView!=null) for(var call : insView.calls) call.doFrame(milli);
|
||||
if(progView!=null) for(var call : progView.calls) call.doFrame(milli);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if(waitTo > 0) { //waitTo==0 为点播,不换页
|
||||
if(curTimes < lastPage.repeatTimes) curTimes++;
|
||||
else {
|
||||
curTimes = 1;
|
||||
if(++curAva >= avas.size()) {
|
||||
var isDiff = milli-lastPage.endMilli>=1000;
|
||||
syncProg(isDiff ? milli : lastPage.endMilli, milli);
|
||||
if(insView!=null) for(var call : insView.calls) call.doFrame(milli);
|
||||
if(progView!=null) for(var call : progView.calls) call.doFrame(milli);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if(milli-lastPage.endMilli >= 5000) {
|
||||
Util.println("System Time Changed: "+Util.dateFmt.format(lastPage.endMilli)+" -> "+Util.dateFmt.format(milli));
|
||||
lastPage.endMilli = milli;
|
||||
}
|
||||
}
|
||||
avas.get(curAva).setMillis(lastPage.endMilli, milli);
|
||||
Util.println("curAva: "+curAva+" endMs: "+avas.get(curAva).endMilli);
|
||||
}
|
||||
if(isInsert) for(var call : insView.calls) call.doFrame(milli);
|
||||
else for(var call : progView.calls) call.doFrame(milli);
|
||||
}
|
||||
|
||||
void syncProg(long milli, long cur) {
|
||||
if(cur==0) cur = milli;
|
||||
Util.println("\nSyncProg");
|
||||
waitTo = Long.MAX_VALUE;
|
||||
avas.clear();
|
||||
curAva = 0;
|
||||
curTimes = 1;
|
||||
isInsert = false;
|
||||
if(insView!=null) {
|
||||
for(int i=0; i<insView.pages.size(); i++) if(insView.pages.get(i).repeatTimes <= 0) {
|
||||
for(var layer : insView.pages.get(i).layers) for(var src : layer.srcs) {
|
||||
insView.removeView(src.view);
|
||||
if(src.view instanceof Choreographer.FrameCallback) insView.calls.remove((Choreographer.FrameCallback) src.view);
|
||||
}
|
||||
insView.pages.remove(i--);
|
||||
}
|
||||
if(insView.pages.isEmpty()) {
|
||||
insView.release();
|
||||
insView = null;
|
||||
try {
|
||||
var fOut = new FileOutputStream(Util.programDir + "/insert");
|
||||
fOut.write("{}".getBytes());
|
||||
fOut.flush();
|
||||
fOut.getFD().sync();
|
||||
fOut.close();
|
||||
} catch(Exception e) {
|
||||
Util.printStackTrace(e);
|
||||
}
|
||||
System.gc();
|
||||
} else {
|
||||
var dur = 0;
|
||||
for(var page : insView.pages) if(page.isScheOn(milli+dur)) {
|
||||
avas.add(page);
|
||||
dur += page.tDur;
|
||||
}
|
||||
if(avas.isEmpty()) for(var page : insView.pages) if(page.sches==null) avas.add(page);
|
||||
if(! avas.isEmpty()) {
|
||||
isInsert = true;
|
||||
avas.get(curAva).setMillis(milli, cur);
|
||||
if(! insView.isShown()) setContentView(insView);
|
||||
state = 6;
|
||||
Util.println("avas "+avas.size()+" Insert");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(progView!=null) {
|
||||
var dur = 0;
|
||||
for(var page : progView.pages) if(page.isScheOn(milli+dur)) {
|
||||
avas.add(page);
|
||||
dur += page.tDur;
|
||||
}
|
||||
if(dur==0) for(var page : progView.pages) if(page.sches==null) {
|
||||
avas.add(page);
|
||||
dur += page.tDur;
|
||||
}
|
||||
if(dur!=0) {
|
||||
var start = milli / dur * dur;
|
||||
if(start < milli) {
|
||||
do {
|
||||
start += avas.get(curAva++).tDur;
|
||||
} while(curAva < avas.size() && start<=milli);
|
||||
start -= avas.get(--curAva).tDur;
|
||||
syncMs = milli;
|
||||
Util.println("Sync. dur: "+dur+" milli: "+milli+" start: "+start+" diff: "+(milli - start));
|
||||
}
|
||||
avas.get(curAva).setMillis(start, milli);
|
||||
if(! progView.isShown()) setContentView(progView);
|
||||
state = 6;
|
||||
Util.println("Avas "+avas.size());
|
||||
return;
|
||||
}
|
||||
}
|
||||
waitTo = milli + 1000;
|
||||
if(state!=2) {
|
||||
setContentView(backView);
|
||||
state = 2;
|
||||
}
|
||||
Util.println("No Avas, back");
|
||||
}
|
||||
}
|
|
@ -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<BroadcastReceiver> 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");
|
||||
}
|
||||
}
|
|
@ -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<Page> pages = new ArrayList<>();
|
||||
ArrayList<Choreographer.FrameCallback> calls = new ArrayList<>();
|
||||
boolean isInsert;
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
public Prog(JSMap task, Context context) {
|
||||
super(context);
|
||||
isInsert = task.bool("insert");
|
||||
JSList<JSMap> jpages = task.jslist("items");
|
||||
JSList<Long> 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<partLengths.size(); i++) {
|
||||
var part = new SrcCopy(context, box);
|
||||
if(isVertical) {
|
||||
y -= partLengths.get(i-1);
|
||||
x += width;
|
||||
addView(part, new AbsLayout.LayoutParams(x, y, width, partLengths.get(i).intValue() - y));
|
||||
} else {
|
||||
x -= partLengths.get(i-1);
|
||||
y += height;
|
||||
addView(part, new AbsLayout.LayoutParams(x, y, partLengths.get(i).intValue() - x, height));
|
||||
}
|
||||
}
|
||||
}
|
||||
for(var pageMap : jpages) {
|
||||
var _program = pageMap.jsmap("_program");
|
||||
JSList<JSMap> 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<String, View> videoMap = new HashMap<>();
|
||||
for(int ll=layers.size()-1; ll>=0; ll--) {
|
||||
var layer = new Layer();
|
||||
layer.isLoop = layers.get(ll).bool("repeat");
|
||||
JSList<JSMap> 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<String> imgs = source.jslist("imgs");
|
||||
if(imgs.isEmpty()) continue;
|
||||
var images = new ArrayList<Bitmap>();
|
||||
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<JSMap> 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<Bitmap>();
|
||||
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 = "<body style=\"color:#fff; margin:0; padding:0; white-space:nowrap; ";
|
||||
var lineHeight = source.get("lineHeight");
|
||||
if(lineHeight!=null) prefix += "line-height:"+lineHeight+";";
|
||||
var fontSize = lineHeight instanceof Number ? geo.height / ((Number)lineHeight).doubleValue() : geo.height;
|
||||
prefix += "height:"+geo.height+"; font-size:"+fontSize+"px;\">";
|
||||
var suffix = "</body><script>window.onload = function() {\n" +
|
||||
"window.java.setWidth(document.body.scrollWidth);};</script>";
|
||||
var speed = source.dbl("speed");
|
||||
if(speed!=0) speed = 1000.0 / speed;
|
||||
var imgs = new ArrayList<Bitmap>();
|
||||
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<Long> 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.layers.size(); ll++) {
|
||||
var layer = page.layers.get(ll);
|
||||
for(int ss=0; ss<layer.srcs.size(); ss++) {
|
||||
var src = layer.srcs.get(ss);
|
||||
if(src.startTime >= 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<getChildCount(); cc++) if((view = getChildAt(cc)) instanceof SrcVideo) ((SrcVideo) view).release();
|
||||
for(var page : pages) for(var layer : page.layers) for(var src : layer.srcs) if(src.tts!=null) src.tts.shutdown();
|
||||
} catch (Throwable e) {
|
||||
Util.printStackTrace(e);
|
||||
}
|
||||
}
|
||||
CountDownTimer timer;
|
||||
@Override
|
||||
public void onVisibilityAggregated(boolean isVisible) {
|
||||
super.onVisibilityAggregated(isVisible);
|
||||
if(isVisible) {
|
||||
if(timer!=null) {
|
||||
timer.start();
|
||||
timer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Source {
|
||||
View view;
|
||||
float alpha = 1, rotate, scaleX = 1, scaleY = 1;
|
||||
long startMilli, endMilli = Long.MAX_VALUE;
|
||||
int startTime, entryDur, exitStart = Integer.MAX_VALUE, exitDur, endTime, ff, breathe, blinkHalf;
|
||||
Effect entryEff, exitEff;
|
||||
String type;
|
||||
Uri uri;
|
||||
TextToSpeech tts;
|
||||
String text;
|
||||
boolean isEntryRand, isExitRand;
|
||||
|
||||
void show() {
|
||||
if(view.getVisibility()==VISIBLE) return;
|
||||
view.setVisibility(VISIBLE);
|
||||
ff = 0;
|
||||
if(isEntryRand) entryEff = Effect.values()[Util.rand.nextInt(Effect.values().length)];
|
||||
if(isExitRand) exitEff = Effect.values()[Util.rand.nextInt(Effect.values().length)];
|
||||
resetEff();
|
||||
doEff();
|
||||
if(tts!=null) tts.speak(text, TextToSpeech.QUEUE_FLUSH, null, null);
|
||||
}
|
||||
void hide() {
|
||||
if(view.getVisibility()!=VISIBLE) return;
|
||||
view.setVisibility(GONE);
|
||||
if(uri!=null) ((GifImageView) view).setImageURI(uri);
|
||||
if(tts!=null && tts.isSpeaking()) tts.stop();
|
||||
}
|
||||
void doEff() {
|
||||
var w = view.getLayoutParams().width;
|
||||
var h = view.getLayoutParams().height;
|
||||
if(ff < entryDur) {
|
||||
if(entryEff == Effect.EXPAND_HOR) setScaleX(ff / (float) entryDur);
|
||||
else if(entryEff == Effect.EXPAND_VER) setScaleY(ff / (float) entryDur);
|
||||
else if(entryEff == Effect.EXPAND_LEFT) {
|
||||
setScaleX(ff / (float) entryDur);
|
||||
view.setTranslationX((1 - view.getScaleX()) * w / 2);
|
||||
} else if(entryEff == Effect.EXPAND_TOP) {
|
||||
setScaleY(ff / (float) entryDur);
|
||||
view.setTranslationY((1 - view.getScaleY()) * h / 2);
|
||||
} else if(entryEff == Effect.EXPAND_RIGHT) {
|
||||
setScaleX(ff / (float) entryDur);
|
||||
view.setTranslationX((view.getScaleX() - 1) * w / 2);
|
||||
} else if(entryEff == Effect.EXPAND_BOTTOM) {
|
||||
setScaleY(ff / (float) entryDur);
|
||||
view.setTranslationY((view.getScaleY() - 1) * h / 2);
|
||||
}
|
||||
else if(entryEff == Effect.MOVE_LEFT) view.setTranslationX(w - ff*w/(float) entryDur);
|
||||
else if(entryEff == Effect.MOVE_TOP) view.setTranslationY(h - ff*h/(float) entryDur);
|
||||
else if(entryEff == Effect.MOVE_RIGHT) view.setTranslationX(ff*w/(float) entryDur - w);
|
||||
else if(entryEff == Effect.MOVE_BOTTOM) view.setTranslationY(ff*h/(float) entryDur - h);
|
||||
else if(entryEff == Effect.FADE) view.setAlpha(alpha * ff / entryDur);
|
||||
else if(entryEff == Effect.ZOOM) {
|
||||
setScaleX(ff / (float) entryDur);
|
||||
setScaleY(view.getScaleX());
|
||||
} else if(entryEff == Effect.ZOOM_TL) {
|
||||
var rate = ff / (float) entryDur;
|
||||
view.setTranslationX((rate - 1) * w / 2);
|
||||
view.setTranslationY((rate - 1) * h / 2);
|
||||
setScaleX(rate);
|
||||
setScaleY(rate);
|
||||
} else if(entryEff == Effect.ZOOM_BR) {
|
||||
var rate = ff / (float) entryDur;
|
||||
view.setTranslationX((1 - rate) * w / 2);
|
||||
view.setTranslationY((1 - rate) * h / 2);
|
||||
setScaleX(rate);
|
||||
setScaleY(rate);
|
||||
} else if(entryEff == Effect.ZOOM_TR) {
|
||||
var rate = ff / (float) entryDur;
|
||||
view.setTranslationX((1 - rate) * w / 2);
|
||||
view.setTranslationY((rate - 1) * h / 2);
|
||||
setScaleX(rate);
|
||||
setScaleY(rate);
|
||||
} else if(entryEff == Effect.ZOOM_BL) {
|
||||
var rate = ff / (float) entryDur;
|
||||
view.setTranslationX((rate - 1) * w / 2);
|
||||
view.setTranslationY((1 - rate) * h / 2);
|
||||
setScaleX(rate);
|
||||
setScaleY(rate);
|
||||
} else if(entryEff == Effect.ROTATE) {
|
||||
setScaleX(ff / (float) entryDur);
|
||||
setScaleY(view.getScaleX());
|
||||
view.setRotation(rotate + view.getScaleX() * 360);
|
||||
} else if(entryEff == Effect.ROTATE_R) {
|
||||
setScaleX(ff / (float) entryDur);
|
||||
setScaleY(view.getScaleX());
|
||||
view.setRotation(rotate - view.getScaleX() * 360);
|
||||
}
|
||||
} else if(ff>=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<Source> 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<Integer> weeks;
|
||||
}
|
||||
|
||||
public static class Page {
|
||||
String id, name, type;
|
||||
ArrayList<Layer> layers = new ArrayList<>();
|
||||
ArrayList<Sche> 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<JSMap> 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<Long> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<lens[ll]; x+=bdLen) canvas.drawBitmap(img, x, 0, null);
|
||||
canvas.restore();
|
||||
if(ll<3) {
|
||||
canvas.translate(lens[ll], 0);
|
||||
canvas.rotate(90);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int freshCnt;
|
||||
|
||||
@Override
|
||||
public void doFrame(long ms) {
|
||||
if(! isShown()) {
|
||||
freshCnt = offs[0] = 0;
|
||||
return;
|
||||
}
|
||||
if(freshCnt < interval) freshCnt++;
|
||||
else {
|
||||
freshCnt = 1;
|
||||
if(eff=='r') {
|
||||
if(offs[0] <= 0) offs[0] = img.getWidth()-1;
|
||||
else offs[0]--;
|
||||
} else offs[0] = offs[0]==0 ? 1 : 0;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package com.xixun.xixunplayer;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public class SrcCopy extends View {
|
||||
|
||||
View view;
|
||||
float scaleX = 1, scaleY = 1;
|
||||
|
||||
public SrcCopy(Context context, View view) {
|
||||
super(context);
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(@NonNull Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
if(view==null) return;
|
||||
if(scaleX==0) {
|
||||
if(view.getWidth()!=0&&getWidth()!=0) {
|
||||
scaleX = getWidth() / (float) view.getWidth();
|
||||
scaleY = getHeight() / (float) view.getHeight();
|
||||
} else {
|
||||
invalidate();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(scaleX!=1 || scaleY!=1) canvas.scale(scaleX, scaleY);
|
||||
view.draw(canvas);
|
||||
invalidate();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
package com.xixun.xixunplayer;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.Color;
|
||||
import android.view.Choreographer;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
import gnph.util.JSMap;
|
||||
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, hasFPS;
|
||||
|
||||
public SrcCountdown(Prog prog, JSMap json) {
|
||||
super(prog.getContext());
|
||||
try {
|
||||
var backColor = json.str("backColor");
|
||||
setBackgroundColor(backColor==null ? Color.TRANSPARENT : Color.parseColor(backColor));
|
||||
} catch (Exception ignored) {
|
||||
setBackgroundColor(Color.TRANSPARENT);
|
||||
}
|
||||
setVerticalScrollBarEnabled(false);
|
||||
setHorizontalScrollBarEnabled(false);
|
||||
setInitialScale(100);
|
||||
html = json.stnn("html");
|
||||
hasFPS = html.contains("%FPS");
|
||||
isUp = json.bool("isUp");
|
||||
lineHeight = json.str("lineHeight");
|
||||
prefix = "<body style=\"color:#fff;margin:0;padding:0;";
|
||||
if(! hasFPS && lineHeight!=null) prefix += "line-height:"+lineHeight+";";
|
||||
prefix += "\">";
|
||||
suffix = "</body>";
|
||||
try {
|
||||
var start = html.substring(0, 16).trim();
|
||||
if(start.startsWith("<body>")) {
|
||||
suffix = "";
|
||||
html = html.substring(6);
|
||||
} else if(start.startsWith("<html") || start.startsWith("<body") || start.startsWith("<!DOCTYPE")) prefix = suffix = "";
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
hasDay = html.contains("%d");
|
||||
hasHour = html.contains("%h");
|
||||
hasMin = html.contains("%m");
|
||||
hasSec = html.contains("%s");
|
||||
var time = json.stnn("time");
|
||||
try {
|
||||
try {
|
||||
targetTime = new SimpleDateFormat("y-M-d H:m:s").parse(time).getTime() / 1000;
|
||||
} catch (Exception e) {
|
||||
targetTime = new SimpleDateFormat("y-M-d H:m").parse(time).getTime() / 1000;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Util.printStackTrace(e);
|
||||
Util.makeText(prog.getContext(), Util.toStr(e)).show();
|
||||
}
|
||||
prog.calls.add(this);
|
||||
}
|
||||
|
||||
void cal() {
|
||||
var secs = isUp ? lastSec - targetTime : targetTime - lastSec;
|
||||
if(secs < 0) secs = 0;
|
||||
var htm = html;
|
||||
if(hasDay) {
|
||||
htm = htm.replace("%d", Long.toString(secs/86400));
|
||||
secs %= 86400;
|
||||
}
|
||||
if(hasHour) {
|
||||
htm = htm.replace("%h", NumFmts.zz().format(secs/3600));
|
||||
secs %= 3600;
|
||||
}
|
||||
if(hasMin) {
|
||||
htm = htm.replace("%m", NumFmts.zz().format(secs/60));
|
||||
secs %= 60;
|
||||
}
|
||||
if(hasSec) htm = htm.replace("%s", NumFmts.zz().format(secs));
|
||||
loadDataWithBaseURL(null, prefix+htm+suffix, "text/html", "UTF-8", null);
|
||||
}
|
||||
|
||||
long lastSec, lastMs;
|
||||
|
||||
@Override
|
||||
public void doFrame(long ms) {
|
||||
if(! isShown()) return;
|
||||
if(hasFPS) {
|
||||
var fps = 1000.0 / (ms - lastMs);
|
||||
lastMs = ms;
|
||||
var htm = html;
|
||||
htm = htm.replace("%FPS", fmt00_0.format(fps));
|
||||
loadDataWithBaseURL(null, prefix+htm+suffix, "text/html", "UTF-8", null);
|
||||
return;
|
||||
}
|
||||
var sec = ms / 1000;
|
||||
if(sec != lastSec) {
|
||||
lastSec = sec;
|
||||
cal();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,283 @@
|
|||
package com.xixun.xixunplayer;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.view.Choreographer;
|
||||
import android.view.Gravity;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import gnph.android.LinearBox;
|
||||
import gnph.util.JSList;
|
||||
import gnph.util.JSMap;
|
||||
import gnph.util.NumFmts;
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
public class SrcDigiClock extends LinearBox implements Choreographer.FrameCallback {
|
||||
|
||||
static final String[] weeks = {"SUN", "MON","TUE","WED","THU","FRI","SAT"};
|
||||
HashMap<String, Bitmap> 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<JSMap> 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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 = "<body style=\"color:#fff;margin:0;padding:0;";
|
||||
if(lineHeight!=null) prefix += "line-height:"+lineHeight+";";
|
||||
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+"</body>", "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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<Bitmap> 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<String, Bitmap> imgMap = new HashMap<>();
|
||||
Bitmap title;
|
||||
ArrayList<Item> 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<JSMap> 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<JSMap> 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<JSMap> 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<str.length(); cc++) item.nums.add(imgMap.get(str.substring(cc, cc+1)));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Util.printStackTrace(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
if(isFirst) {
|
||||
isFirst = false;
|
||||
onReceive(act.environIntent);
|
||||
act.environs.add(this);
|
||||
}
|
||||
if(isScroll) {
|
||||
var x = cur;
|
||||
var cycCnt = cur==0 ? 1 : 2;
|
||||
for(int ccc=0; ccc<cycCnt; ccc++) {
|
||||
if(title!=null) {
|
||||
canvas.drawBitmap(title, x, Math.round((getHeight() - title.getHeight()) / 2), null);
|
||||
x += title.getWidth() + spaceWidth<<1;
|
||||
}
|
||||
var y = (int) Math.round((getHeight() - digitHeight) / 2);
|
||||
for(var item : items) {
|
||||
if(item.label !=null) {
|
||||
canvas.drawBitmap(item.label, x, y, null);
|
||||
x += item.label.getWidth();
|
||||
}
|
||||
for(var num : item.nums) if(num!=null) {
|
||||
canvas.drawBitmap(num, x, y, null);
|
||||
x += num.getWidth();
|
||||
}
|
||||
if(item.unit!=null) {
|
||||
canvas.drawBitmap(item.unit, x, y, null);
|
||||
x += item.unit.getWidth();
|
||||
}
|
||||
x += spaceWidth<<1;
|
||||
}
|
||||
if(ccc==0) end = -(x-cur-step);
|
||||
}
|
||||
} else {
|
||||
var itemH = (double) getHeight() / (title!=null ? items.size()+1 : items.size());
|
||||
int i = 0, x = 0;
|
||||
if(title!=null) {
|
||||
if(alignType==1) x = (getWidth() - title.getWidth()) / 2;
|
||||
else if(alignType==2) x = getWidth() - title.getWidth();
|
||||
canvas.drawBitmap(title, x, Math.round(itemH * i++ + (itemH - title.getHeight()) / 2), null);
|
||||
}
|
||||
for(var item : items) {
|
||||
var y = (int) Math.round(itemH * i++ + (itemH - digitHeight) / 2);
|
||||
x = 0;
|
||||
if(alignType!=0) {
|
||||
if(item.label !=null) x += item.label.getWidth();
|
||||
for(var num : item.nums) if(num!=null) x += num.getWidth();
|
||||
if(item.unit!=null) x += item.unit.getWidth();
|
||||
x = getWidth() - x;
|
||||
if(alignType==1) x /= 2;
|
||||
}
|
||||
if(item.label !=null) {
|
||||
canvas.drawBitmap(item.label, x, y, null);
|
||||
x += item.label.getWidth();
|
||||
}
|
||||
for(var num : item.nums) if(num!=null) {
|
||||
canvas.drawBitmap(num, x, y, null);
|
||||
x += num.getWidth();
|
||||
}
|
||||
if(item.unit!=null) canvas.drawBitmap(item.unit, x, y, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVisibilityAggregated(boolean isVisible) {
|
||||
super.onVisibilityAggregated(isVisible);
|
||||
if(! isVisible) {
|
||||
act.environs.remove(this);
|
||||
isFirst = true;
|
||||
}
|
||||
}
|
||||
|
||||
int freshCnt;
|
||||
|
||||
@Override
|
||||
public void doFrame(long ms) {
|
||||
if(! isShown()) {
|
||||
freshCnt = cur = 0;
|
||||
return;
|
||||
}
|
||||
if(freshCnt < interval) freshCnt++;
|
||||
else {
|
||||
freshCnt = 1;
|
||||
if(cur <= end) cur -= end;
|
||||
else cur -= step;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
package com.xixun.xixunplayer;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
import android.view.Choreographer;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
public class SrcScroll extends View implements Choreographer.FrameCallback {
|
||||
|
||||
ArrayList<Bitmap> imgs;
|
||||
Rect rect = new Rect();
|
||||
int interval, cur, end, step;
|
||||
char direct;
|
||||
|
||||
public SrcScroll(Prog prog, ArrayList<Bitmap> 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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<String> 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, "</p>", 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 = "<body style=\"color:#fff; margin:0;padding:0;";
|
||||
var lineHeight = json.str("lineHeight");
|
||||
if(lineHeight!=null) prefix += "line-height:"+lineHeight+";";
|
||||
var backgroundColor = json.str("backgroundColor");
|
||||
if(backgroundColor!=null) prefix += "background-color:"+backgroundColor+";";
|
||||
if(json.bool("center")) prefix += "display:flex; align-items:center;";
|
||||
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+"</p></body>", "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);
|
||||
}
|
||||
}
|
|
@ -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<String, Bitmap> imgMap = new HashMap<>();
|
||||
ArrayList<Bitmap> 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<str.length(); cc++) {
|
||||
var img = imgMap.get(str.substring(cc, cc+1));
|
||||
imgs.add(img);
|
||||
len += img.getWidth();
|
||||
}
|
||||
imgs.add(null);
|
||||
imgs.add(day);
|
||||
imgs.add(null);
|
||||
len += day.getWidth();
|
||||
len += spaceWidth * 2;
|
||||
secs %= 86400;
|
||||
}
|
||||
if(hasHour) {
|
||||
var str = NumFmts.zz().format(secs/3600);
|
||||
for(int cc=0; cc<str.length(); cc++) {
|
||||
var img = imgMap.get(str.substring(cc, cc+1));
|
||||
imgs.add(img);
|
||||
len += img.getWidth();
|
||||
}
|
||||
imgs.add(hour);
|
||||
imgs.add(null);
|
||||
len += hour.getWidth();
|
||||
len += spaceWidth;
|
||||
secs %= 3600;
|
||||
}
|
||||
if(hasMin) {
|
||||
var str = NumFmts.zz().format(secs/60);
|
||||
for(int cc=0; cc<str.length(); cc++) {
|
||||
var img = imgMap.get(str.substring(cc, cc+1));
|
||||
imgs.add(img);
|
||||
len += img.getWidth();
|
||||
}
|
||||
imgs.add(min);
|
||||
imgs.add(null);
|
||||
len += min.getWidth();
|
||||
len += spaceWidth;
|
||||
secs %= 60;
|
||||
}
|
||||
if(hasSec) {
|
||||
var str = NumFmts.zz().format(secs);
|
||||
for(int cc=0; cc<str.length(); cc++) {
|
||||
var img = imgMap.get(str.substring(cc, cc+1));
|
||||
imgs.add(img);
|
||||
len += img.getWidth();
|
||||
}
|
||||
imgs.add(sec);
|
||||
imgs.add(null);
|
||||
len += sec.getWidth();
|
||||
len += spaceWidth;
|
||||
}
|
||||
imgs.remove(imgs.size()-1);
|
||||
len -= spaceWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
int x = (getWidth()-len) / 2, y;
|
||||
if(text!=null && isMultiline) {
|
||||
y = getHeight() / 2;
|
||||
canvas.drawBitmap(text, (getWidth()-text.getWidth()) / 2, y-text.getHeight(), null);
|
||||
} else y = (getHeight() - imgs.get(0).getHeight()) / 2;
|
||||
for(var img : imgs) {
|
||||
if(img==null) x += spaceWidth;
|
||||
else {
|
||||
canvas.drawBitmap(img, x, y, null);
|
||||
x += img.getWidth();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
long lastSec;
|
||||
|
||||
@Override
|
||||
public void doFrame(long ms) {
|
||||
if(! isShown()) return;
|
||||
var sec = ms / 1000;
|
||||
if(sec != lastSec) {
|
||||
lastSec = sec;
|
||||
cal();
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
package com.xixun.xixunplayer;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.graphics.SurfaceTexture;
|
||||
import android.view.Surface;
|
||||
import android.view.TextureView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import tv.danmaku.ijk.media.player.IMediaPlayer;
|
||||
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;
|
||||
long bitRate;
|
||||
long seekTo;
|
||||
boolean isLive;
|
||||
|
||||
public SrcVideo(Context context, String path, float vol, int dur, boolean useSW, boolean isLive) {
|
||||
super(context);
|
||||
this.path = path;
|
||||
this.vol = vol;
|
||||
this.dur = dur;
|
||||
this.isLive = isLive;
|
||||
init(useSW);
|
||||
}
|
||||
|
||||
void init(boolean useSW) {
|
||||
ijkPlayer = new IjkMediaPlayer();
|
||||
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", 12);
|
||||
try {
|
||||
setSurfaceTextureListener(this);
|
||||
ijkPlayer.setDataSource(path);
|
||||
ijkPlayer.setLooping(true);
|
||||
ijkPlayer.setVolume(vol, vol);
|
||||
ijkPlayer.setOnPreparedListener((IMediaPlayer var1)->{
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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();
|
||||
// }
|
||||
//}
|
|
@ -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 = "<body style=\"color:#fff;margin:0;padding:0;";
|
||||
if(lineHeight!=null) prefix += "line-height:"+lineHeight+";";
|
||||
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+"</body>", "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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 = "<body style=\"color:#fff;margin:0;padding:0;";
|
||||
if(lineHeight!=null) prefix += "line-height:"+lineHeight+";";
|
||||
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<JSMap> 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<JSMap> 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 = "<img src=\"file:///android_asset/imgs/W" + dayImgMap.get(dd==1 ? type : days.get(dd).stnn("conditionDay")) + ".png\" style=\"width:"+w+"px; height:"+h+"px;\">";
|
||||
}
|
||||
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+"</body>", "text/html", "UTF-8", null);
|
||||
} catch (Exception e) {
|
||||
Util.printStackTrace(e);
|
||||
}
|
||||
}
|
||||
|
||||
static HashMap<String, String> dayImgMap = new HashMap<>();
|
||||
static HashMap<String, String> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<String> 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<JSMap> 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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<TCPThread> socketThreads = new Vector<>();
|
||||
public static final HashMap<Integer, String[]> 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<String> 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<ScanImageAdvanceRequest.ScanImageAdvanceRequestTask>();
|
||||
var sceneList = new ArrayList<String>();
|
||||
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<dur; i+=10000) {
|
||||
var frame = retriever.getFrameAtTime(i*1000, FFmpegMediaMetadataRetriever.OPTION_CLOSEST);
|
||||
var file = Util.programDir+"/capture"+(++idx);
|
||||
var out = new FileOutputStream(file);
|
||||
frame.compress(Bitmap.CompressFormat.JPEG, 75, out);
|
||||
O.close(out);
|
||||
var in = new FileInputStream(file);
|
||||
var task = new ScanImageAdvanceRequest.ScanImageAdvanceRequestTask();
|
||||
task.setDataId("capture"+idx);
|
||||
task.setImageTimeMillisecond(1L);
|
||||
task.setInterval(1);
|
||||
task.setMaxFrames(1);
|
||||
task.setImageURLObject(in);
|
||||
taskList.add(task);
|
||||
sceneList.add("porn");
|
||||
}
|
||||
} finally {
|
||||
retriever.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
var scanImageAdvanceRequest = new com.aliyun.imageaudit20191230.models.ScanImageAdvanceRequest().setTask(taskList).setScene(sceneList);
|
||||
var runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
try {
|
||||
var scanImageResponse = client.scanImageAdvance(scanImageAdvanceRequest, runtime);
|
||||
Util.println(com.aliyun.teautil.Common.toJSONString(TeaModel.buildMap(scanImageResponse))); // 获取整体结果
|
||||
var results = scanImageResponse.getBody().getData().results;
|
||||
for(var result : results) {
|
||||
Util.println(" dataId "+result.dataId);
|
||||
Util.println(" imageURL "+result.imageURL);
|
||||
for(var subResult : result.subResults) {
|
||||
Util.println(" suggestion "+subResult.suggestion);
|
||||
if(! "pass".equals(subResult.suggestion)) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (com.aliyun.tea.TeaException teaException) {
|
||||
Util.println(com.aliyun.teautil.Common.toJSONString(teaException)); // 获取整体报错信息
|
||||
//Util.println(teaException.getCode());// 获取单个字段
|
||||
return false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Util.printStackTrace(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|