diff --git a/XixunPlayer/.idea/.name b/XixunPlayer/.idea/.name new file mode 100644 index 0000000..d3f190d --- /dev/null +++ b/XixunPlayer/.idea/.name @@ -0,0 +1 @@ +XixunPlayer \ No newline at end of file diff --git a/XixunPlayer/app/build.gradle b/XixunPlayer/app/build.gradle index 03a4fb4..08f9194 100644 --- a/XixunPlayer/app/build.gradle +++ b/XixunPlayer/app/build.gradle @@ -11,7 +11,7 @@ android { minSdk 21 targetSdk 34 versionCode 1 - versionName "2.1.21" + versionName "2.2-beta8-NTimes" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } @@ -34,10 +34,11 @@ android { dependencies { implementation 'androidx.appcompat:appcompat:1.6.1' - implementation 'androidx.media3:media3-exoplayer:1.2.0' -// implementation 'tv.danmaku.ijk.media:ijkplayer-java:0.8.8' -// implementation 'tv.danmaku.ijk.media:ijkplayer-arm64:0.8.8' implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.25' + implementation 'com.aliyun:imageaudit20191230:2.0.6' + implementation 'com.github.wseemann:FFmpegMediaMetadataRetriever-core:1.0.19' + implementation 'com.github.wseemann:FFmpegMediaMetadataRetriever-native-armeabi-v7a:1.0.19' + implementation 'com.github.wseemann:FFmpegMediaMetadataRetriever-native-arm64-v8a:1.0.19' implementation files('libs/gnph.jar') implementation files('libs/zip4j-2.10.0.jar') implementation files('libs/xixun_card_settings_1.2.4.jar') diff --git a/XixunPlayer/app/src/main/AndroidManifest.xml b/XixunPlayer/app/src/main/AndroidManifest.xml index 3459ff0..47da6ae 100644 --- a/XixunPlayer/app/src/main/AndroidManifest.xml +++ b/XixunPlayer/app/src/main/AndroidManifest.xml @@ -16,14 +16,15 @@ android:largeHeap="true" android:allowBackup="true" android:usesCleartextTraffic="true" - android:launchMode="singleTop" + android:launchMode="singleTask" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen" > @@ -32,6 +33,7 @@ @@ -41,7 +43,8 @@ @@ -50,6 +53,7 @@ diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Server.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/AIDLService.java similarity index 76% rename from XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Server.java rename to XixunPlayer/app/src/main/java/com/xixun/xixunplayer/AIDLService.java index 9db2be9..9994042 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Server.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/AIDLService.java @@ -23,7 +23,7 @@ import gnph.util.JSList; import gnph.util.JSMap; import gnph.util.URLConn; -public class Server extends Service { +public class AIDLService extends Service { @Override public IBinder onBind(Intent intent) { @@ -106,7 +106,7 @@ public class Server extends Service { @SuppressLint("ResourceType") @Override public String executeJosnCommand(String jsonstr) throws RemoteException { - Util.println("Server executeJsonCommand ..."+jsonstr);//{"_type":"DeleteTask","id":"652522a0e81d1e000009201a","sendTo":"yzd-player"} + Util.println("AIDL executeJsonCommand ..."+jsonstr);//{"_type":"DeleteTask","id":"652522a0e81d1e000009201a","sendTo":"yzd-player"} String commandId = null; try { var jsonBytes = jsonstr.getBytes(StandardCharsets.UTF_8); @@ -115,7 +115,10 @@ public class Server extends Service { commandId = json.stnn("id"); if(_type.equals("PlayXixunTask") || _type.equals("PlayProgramTask")) { var preDownloadURL = json.str("preDownloadURL"); - if(preDownloadURL==null) preDownloadURL = Util.serverURL+"file/download?id="; + if(preDownloadURL==null) { + Util.println(" serverURL "+Util.serverURL); + preDownloadURL = Util.serverURL+"file/download?id="; + } var task = json.jsmap("task"); JSList jpages = task.jslist("items"); int proSize = 0; @@ -169,35 +172,54 @@ public class Server extends Service { int finalProSize = proSize; String finalCommandId = commandId; var notificationURL = json.str("notificationURL"); + var downMs = System.currentTimeMillis(); + Util.downId = downMs; new Thread(()->{ Util.deleteFiles(finalProSize, hases); for(var needDown : needDowns) { int cnt = 0; for(var src : needDown.srcs) { - try { - var in = new URLConn(src.url).in(); - var fout = new FileOutputStream(Util.programDir+"/"+src.filename); - IOs.writeCloseIn(fout, in); - fout.flush(); - fout.getFD().sync(); - fout.close(); - } catch (Exception e) { - Util.printStackTrace(e); + for(int t=0; ; t++) { + try { + Util.println("download "+src.url); + var conn = new URLConn(src.url); + if(t>0) { + var len = new File(Util.programDir+"/"+src.filename).length(); + conn.setHeader("Range", "bytes="+len+"-"); + Util.println(" Range "+len); + } + var input = conn.in(); + var code = conn.hconn().getResponseCode(); + var fout = new FileOutputStream(Util.programDir+"/"+src.filename, code==206); + IOs.writeCloseIn(fout, input); + fout.flush(); + fout.getFD().sync(); + fout.close(); + if(code==200 || code==206) break; + else Util.println(" error "+code+" "+conn.hconn().getResponseMessage()+" size: "+new File(src.filename).length()+" "+src.filename); + } catch (Throwable e) { + Util.println(Util.toStr(e)); + } + if(t>=7200 || Util.downId!=downMs) return; + try { + Thread.sleep(5000); + } catch (Exception ignored) {} } + cnt++; var progress = cnt*100/needDown.srcs.size(); if(cnt != needDown.srcs.size()) { if(notificationURL==null) { var intent = new Intent("xixun.intent.action.REPLY"); intent.putExtra("reply", new TaskProgressReply(finalCommandId, needDown.prog, progress, 500, 3)); - MainActivity.ins.sendBroadcast(intent); + sendBroadcast(intent); } else { try { new URLConn(notificationURL).timeout(5000).writeJson(new JSMap( "commandId", finalCommandId, "taskItemId", needDown.prog, "progress", progress).toStr()).read(); - } catch (Exception e) { + } catch (Throwable e) { Util.printStackTrace(e); } } @@ -206,19 +228,38 @@ public class Server extends Service { if(notificationURL==null) { var intent = new Intent("xixun.intent.action.REPLY"); intent.putExtra("reply", new TaskProgressReply(finalCommandId, needDown.prog, 100, 400, 0)); - MainActivity.ins.sendBroadcast(intent); + sendBroadcast(intent); } else { try { new URLConn(notificationURL).timeout(5000).writeJson(new JSMap( "commandId", finalCommandId, "taskItemId", needDown.prog, "progress", 100).toStr()).read(); - } catch (Exception e) { + } catch (Throwable e) { Util.printStackTrace(e); } } } - MainActivity.ins.runOnUiThread(() -> MainActivity.ins.initProg(jsonBytes)); + if(Util.downId!=downMs) return; + var acti = MainActivity.ins; + if(acti!=null) acti.runOnUiThread(() -> acti.initProg(jsonBytes)); + else { + try { + var fOut = new FileOutputStream(Util.programDir + "/program"); + fOut.write(jsonBytes); + fOut.flush(); + fOut.getFD().sync(); + fOut.close(); + } catch (Throwable e) { + Util.printStackTrace(e); + } + var intent = new Intent(AIDLService.this, MainActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + AIDLService.this.startActivity(intent); + } + var intent = new Intent("com.xixun.AccessibilityService"); + intent.putExtra("newProgram", "platform"); + sendBroadcast(intent); }).start(); return new JSMap( "_type", "Success", @@ -227,6 +268,7 @@ public class Server extends Service { "commandId", commandId ).toString(); } else if(_type.equals("DeleteTask")) { + Util.downId = 0; MainActivity.ins.runOnUiThread(() -> MainActivity.ins.delProgFile()); return new JSMap( "_type", "DataCallback", @@ -280,6 +322,21 @@ public class Server extends Service { "commandId", commandId, "task", task ).toString(); + } else if(_type.equalsIgnoreCase("UploadPlayLogs")) { + var url = json.str("url"); + Util.logOn = url!=null; + Util.cfg.put("logUploadUrl", url); + Util.cfg.put("logUploadInterval", json.intg("interval", 60)); + try (var fOut = new FileOutputStream(Util.programDir + "/cfg")) { + Util.cfg.write(fOut); + fOut.flush(); + fOut.getFD().sync(); + } + return new JSMap( + "_type", "Success", + "cardId", Util.getCardId(), + "commandId", commandId + ).toString(); } return new JSMap( "_type", "Error", diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/BootCompletedReceiver.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/BootCompletedReceiver.java index a575f42..7433834 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/BootCompletedReceiver.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/BootCompletedReceiver.java @@ -7,10 +7,11 @@ import android.content.Intent; public class BootCompletedReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - Util.println("BootCompletedReceiver onReceive ---- "+intent.getAction()); + Util.println("==== BootCompletedReceiver onReceive >> "+intent.getAction()); + Util.initDir(context); if(MainActivity.ins!=null) return; - intent = new Intent(context, MainActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); + var inten = new Intent(context, MainActivity.class); + inten.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(inten); } } \ No newline at end of file diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/LogThread.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/LogThread.java new file mode 100644 index 0000000..5404dde --- /dev/null +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/LogThread.java @@ -0,0 +1,98 @@ +package com.xixun.xixunplayer; + +import android.content.ContentValues; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.concurrent.ConcurrentLinkedQueue; + +import gnph.util.Digest; +import gnph.util.JSList; +import gnph.util.JSMap; +import gnph.util.URLConn; + +public class LogThread extends Thread { + + public static ConcurrentLinkedQueue rows = new ConcurrentLinkedQueue<>(); + + Digest md5 = Digest.md5(); + + public LogThread() { + + } + + @Override + public void run() { + long next = 0, nextDel = 0; + while(true) { + try { + Thread.sleep(500); + } catch (InterruptedException ignored) { + } + if(! Util.logOn) continue; + var ms = System.currentTimeMillis(); + var row = rows.poll(); + if(row!=null) { + try (var db = Util.openHelper.getWritableDatabase()) { + db.beginTransaction(); + if(ms>=nextDel) { + nextDel = (ms - nextDel < 3000 ? nextDel : ms) + 3600000L; + var num = db.delete("log", "time<"+(ms-720*3600000L), null); + Util.println(" del overdue "+num); + } + var rowid = db.insert("log", null, row); + db.setTransactionSuccessful(); + db.endTransaction(); + Util.println(" rowid "+rowid); + } catch (Exception e) { + Util.printStackTrace(e); + } + } + if(ms>=next) { + next = (ms-next < 3000 ? next : ms) + Util.cfg.intg("logUploadInterval", 60)*1000; + var rows = new JSList(); + try (var db = Util.openHelper.getReadableDatabase()) { + var cursor = db.rawQuery("select * from log order by time", null); + while(cursor.moveToNext()) { + var pid = cursor.getString(2); + if(pid==null || pid.length()!=24) pid = md5.hexStr(cursor.getString(3).getBytes()).substring(0,24); + rows.add(new JSMap( + "beginAt", cursor.getLong(0), + "duration", cursor.getLong(1)/1000, + "pid", pid, + "name", cursor.getString(3), + "type", cursor.getString(4), + "lat", cursor.getDouble(5), + "lng", cursor.getDouble(6) + )); + } + } + Util.println(" Log Cnt "+rows.size()); + if(! rows.isEmpty()) { + Util.println(rows.last().toStr()); + try { + var conn = new URLConn(Util.cfg.stnn("logUploadUrl")).timeout(15000); + conn.addHeader("Card-Id", Util.getCardId()); + conn.forJson(); + rows.write(conn.out()); + var resp = conn.read(); + Util.println(" Upload resp: "+resp); + if("OK".equals(resp) || "".equals(resp)) { + try (var db = Util.openHelper.getWritableDatabase()) { + db.beginTransaction(); + var num = db.delete("log", "time<="+rows.last().lng("beginAt"), null); + db.setTransactionSuccessful(); + db.endTransaction(); + Util.println(" del Upled "+num); + } catch (Exception e) { + Util.printStackTrace(e); + } + } + } catch (Exception e) { + Util.printStackTrace(e); + } + } + } + } + } +} diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/MainActivity.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/MainActivity.java index ca51114..9dbe7aa 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/MainActivity.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/MainActivity.java @@ -9,9 +9,10 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.PackageManager; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; import android.os.Build; import android.os.Bundle; -import android.os.Environment; import android.os.IBinder; import android.os.StrictMode; import android.view.Choreographer; @@ -25,27 +26,24 @@ import androidx.core.content.ContextCompat; import com.xixun.joey.aidlset.CardService; import com.xixun.xy.conn.aidl.ConnService; -import net.lingala.zip4j.ZipFile; - import java.io.BufferedInputStream; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; -import java.net.ServerSocket; import java.util.ArrayList; import java.util.HashSet; +import java.util.LinkedList; import gnph.util.Chsets; -import gnph.util.IOs; import gnph.util.JSList; import gnph.util.JSMap; -public class MainActivity extends Activity implements Choreographer.FrameCallback, Runnable { +public class MainActivity extends Activity implements Choreographer.FrameCallback { public static MainActivity ins; ArrayList reces = new ArrayList<>(); + ArrayList services = new ArrayList<>(); public Intent environIntent = new Intent(); HashSet environs = new HashSet<>(); BackView backView; @@ -59,14 +57,11 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ins = this; - Util.println("==>> MainActivity onCreate >>>> this "+hashCode()+" UI Thread: "+Thread.currentThread().getId()); - try{ - if(RestartService.ins==null) startService(new Intent(this, RestartService.class)); - } catch(Throwable e) { - Util.printStackTrace(e); - } + Util.println("==>> MainActivity onCreate >>>> this "+hashCode()+" Thread: "+Thread.currentThread().getId()); StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitAll().build()); + setContentView(backView = new BackView(MainActivity.this)); + state = 5; if(ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) init(); else { @@ -84,7 +79,7 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); - if(backView!=null) return; + if(Util.programDir!=null) return; if(requestCode==999 && grantResults.length > 0 && grantResults[0]==PackageManager.PERMISSION_GRANTED) init(); else { Util.println("---- Request Permission Failed"); @@ -106,11 +101,13 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac progView = null; } ins = null; - for(var rece : reces) { - try { - unregisterReceiver(rece); - } catch (Throwable ignored){ - } + for(var rece : reces) unregisterReceiver(rece); + for(var service : services) unbindService(service); + try (var fout = new FileOutputStream(Util.programDir+"/cfg")){ + Util.cfg.write(fout); + fout.flush(); + fout.getFD().sync(); + } catch (Exception ignored) { } System.gc(); } @@ -149,17 +146,39 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac ServiceConnection connCard; @SuppressLint("UnspecifiedRegisterReceiverFlag") public void init() { - var dir = Build.VERSION.SDK_INT < Build.VERSION_CODES.R ? Environment.getExternalStorageDirectory().getAbsolutePath() + "/XixunPlayer" : getExternalFilesDir(null).getAbsolutePath(); - Util.println("\n---- Init ----\n dir "+dir); - Util.programDir = dir + "/program"; - Util.backImgFile = dir + "/background"; - setContentView(backView = new BackView(MainActivity.this)); - state = 5; - - var program = new File(Util.programDir); - if(program.isFile()) program.delete(); - Util.println(" mkdir: "+program.mkdirs()); + Util.initDir(this); + if(MainService.ins==null) startService(new Intent(this, MainService.class)); + var cfg = new File(Util.programDir+"/cfg"); + if(! cfg.isFile()) Util.cfg = new JSMap(); + else { + try { + Util.cfg = JSMap.fromClose(new FileInputStream(cfg)); + } catch (Exception e) { + Util.cfg = new JSMap(); + Util.makeText(MainActivity.this, Util.toStr(e)).show(); + Util.printStackTrace(e); + } + } + Util.println(Util.cfg.toStr()); + Util.logOn = Util.cfg.get("logUploadUrl")!=null; + Util.openHelper = new SQLiteOpenHelper(this, "aaa", null, 1) { + @Override + public void onCreate(SQLiteDatabase db) { + db.execSQL("create table cfg(k varchar(32) primary key, v varchar(128))"); + db.execSQL("create table log(time integer primary key, dur integer, id varchar(64), name varchar(64), type varchar(16), lat double, lng double)"); + } + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + } + }; + new LogThread().start(); + if(Util.logOn) { + var db = Util.openHelper.getWritableDatabase(); + var cursor = db.rawQuery("select count(*) aaa from log", null); + while(cursor.moveToNext()) Util.println("Log Cnt "+cursor.getString(0)); + db.close(); + } connCard = new ServiceConnection() { public void onServiceDisconnected(ComponentName name) { serviCard = null; @@ -168,6 +187,7 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac public void onServiceConnected(ComponentName name, IBinder iBinder) { unbindService(this); + services.remove(this); Util.println("->-> AIDL Service cardsystem Connected"); serviCard = CardService.Stub.asInterface(iBinder); try { @@ -187,6 +207,7 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac intenCard = new Intent("com.xixun.joey.aidlset.SettingsService"); intenCard.setPackage("com.xixun.joey.cardsystem"); bindService(intenCard, connCard, Context.BIND_AUTO_CREATE); + services.add(connCard); var connXy = new ServiceConnection() { public void onServiceDisconnected(ComponentName name) { @@ -195,6 +216,7 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac } public void onServiceConnected(ComponentName name, IBinder iBinder) { unbindService(this); + services.remove(this); Util.println("->-> AIDL Service xy.conn Connected"); serviXy = ConnService.Stub.asInterface(iBinder); try { @@ -214,6 +236,7 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac var intenXy = new Intent("xixun.intent.action.CONNECTION_INFO"); intenXy.setPackage("com.xixun.xy.conn"); bindService(intenXy, connXy, Context.BIND_AUTO_CREATE); + services.add(connXy); reces.clear(); BroadcastReceiver rece; @@ -277,34 +300,53 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac }, new IntentFilter("xixun.intent.action.TEMPERATURE_HUMIDITY")); reces.add(rece); + registerReceiver(rece = new BroadcastReceiver(){ + @Override + public void onReceive(Context context, Intent intent) { + Util.lat = intent.getDoubleExtra("latitude", 0); + Util.lng = intent.getDoubleExtra("longitude", 0); + Util.println(" lat "+Util.lat+" lng "+Util.lng); + } + }, new IntentFilter("com.xixun.joey.gpsinfo")); + reces.add(rece); + registerReceiver(rece = new BroadcastReceiver(){ @Override public void onReceive(Context context, Intent intent) { try { var code = intent.getIntExtra("code", 0); Util.println(" remote_control "+code); - if(progView == null || ! progView.isShown() || code > progView.pages.size()) return; + if(progView == null || progView.pages.isEmpty() || ! progView.isShown() || code==-1 || code < -3 || code > progView.pages.size()) return; + if(code<=-2) { + var idx = avas.isEmpty() ? -1 : progView.pages.indexOf(avas.get(curAva)); + if(code==-2) code = idx<=0 ? progView.pages.size() : idx; + else code = idx>=progView.pages.size()-1 ? 1 : idx+2; + } var ms = System.currentTimeMillis(); if(! avas.isEmpty()) { avas.get(curAva).hide(); if(code > 0 && avas.get(curAva)==progView.pages.get(code-1)) for(var call : progView.calls) call.doFrame(ms); } - ms = (ms+999)/1000*1000; - if(code > 0) { + if(code==0) syncProg(ms, 0); + else { avas.clear(); var page = progView.pages.get(code-1); avas.add(page); curAva = 0; curTimes = 1; waitTo = 0; //点播 - page.setMillis(ms, ms); + page.setMillis(ms, ms, showHides); if(state != 6) { setContentView(progView); state = 6; } - } else { - waitTo = Long.MAX_VALUE; - syncProg(ms, 0); + } + var root = JSMap.fromClose(new BufferedInputStream(new FileInputStream(Util.programDir + "/program"))); + root.put("Demand", code); + try (var fOut = new FileOutputStream(Util.programDir + "/program")) { + root.write(fOut); + fOut.flush(); + fOut.getFD().sync(); } } catch (Throwable e) { Util.makeText(MainActivity.this, Util.toStr(e)).show(); @@ -314,92 +356,35 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac }, new IntentFilter("com.xixun.yzd.REMOTE_CONTROL")); reces.add(rece); - var intentFilter = new IntentFilter(Intent.ACTION_MEDIA_MOUNTED); - intentFilter.addDataScheme("file"); - registerReceiver(rece = new BroadcastReceiver(){ - long lastMs; - final char[] pass = {'8','8','8'}; - @Override - public void onReceive(Context context, Intent intent) { - var path = intent.getData().getPath(); - Util.println("\nMEDIA_MOUNTED path: "+path); - var ms = System.currentTimeMillis(); - if(ms-lastMs<1000) return; - lastMs = ms; - Util.makeText(MainActivity.this, "MEDIA_MOUNTED path: "+path+"\nImporting 正在导入 ...").show(); - new Thread(()->{ + if(Util.custom==Util.Custom.Yishi) { + new Thread(()->{ + while(true) { try { - var zip = new ZipFile(path+"/program.zip"); - if(zip.isEncrypted()) zip.setPassword(pass); - long size = 0; - ByteArrayOutputStream jsonOut = null; - var headers = zip.getFileHeaders(); - for(var header : headers) { - size += header.getUncompressedSize(); - if("program".equals(header.getFileName())) IOs.writeClose(jsonOut = new ByteArrayOutputStream(), zip.getInputStream(header)); + Thread.sleep(10000); + if(serviCard!=null && serviCard.asBinder().isBinderAlive()) { + var www = serviCard.getScreenWidth(); + var hhh = serviCard.getScreenHeight(); + if(www!=Util.screenWidth || hhh!=Util.screenHeight) { + var ins = MainActivity.ins; + if(ins!=null) ins.runOnUiThread(() -> { + Util.screenWidth = www; + Util.screenHeight = hhh; + ins.backView.invalidate(); + }); + } + } else { + Util.println(" bindService"); + var ins = MainActivity.ins; + if(ins!=null) ins.runOnUiThread(() -> ins.bindService(ins.intenCard, ins.connCard, Context.BIND_AUTO_CREATE)); } - if(jsonOut==null) { - Util.println("No program File"); - runOnUiThread(() -> Util.makeText(MainActivity.this, "No program File").show()); - return; - } - if(size==0) { - Util.println("zip size is 0"); - runOnUiThread(() -> Util.makeText(MainActivity.this, "zip size is 0").show()); - return; - } - Util.deleteFiles(size, null); - for(var header : headers) if(! "program".equals(header.getFileName())) { - Util.println(" name: " + header.getFileName()); - var fOut = new FileOutputStream(Util.programDir + "/" + header.getFileName()); - IOs.writeCloseIn(fOut, zip.getInputStream(header)); - fOut.flush(); - fOut.getFD().sync(); - fOut.close(); - } - var json = jsonOut.toByteArray(); - runOnUiThread(() -> { - Util.println("Import Succeed"); - Util.makeText(MainActivity.this, "Import Succeed 导入成功\nDon't shut down within 1 minute, otherwise the program may be lost\n不要在 1 分钟内关机,否则节目可能丢失").show(); - initProg(json); - }); - } catch (Exception e) { + } catch (Throwable e) { + var ins = MainActivity.ins; + if(ins!=null) ins.runOnUiThread(() -> Util.makeText(ins, Util.toStr(e)).show()); Util.printStackTrace(e); - runOnUiThread(() -> Util.makeText(MainActivity.this, Util.toStr(e)).show()); } - }).start(); - } - }, intentFilter); - reces.add(rece); - - new Thread(this).start(); -// new Thread(()->{ -// while(true) { -// try { -// Thread.sleep(10000); -// if(serviCard!=null && serviCard.asBinder().isBinderAlive()) { -// var www = serviCard.getScreenWidth(); -// var hhh = serviCard.getScreenHeight(); -// if(www!=Util.screenWidth || hhh!=Util.screenHeight) { -// var ins = MainActivity.ins; -// if(ins!=null) ins.runOnUiThread(() -> { -// Util.screenWidth = www; -// Util.screenHeight = hhh; -// ins.backView.invalidate(); -// }); -// } -// } else { -// Util.println(" bindService"); -// var ins = MainActivity.ins; -// if(ins!=null) ins.runOnUiThread(() -> ins.bindService(ins.intenCard, ins.connCard, Context.BIND_AUTO_CREATE)); -// } -// } catch (Throwable e) { -// var ins = MainActivity.ins; -// if(ins!=null) ins.runOnUiThread(() -> Util.makeText(ins, Util.toStr(e)).show()); -// Util.printStackTrace(e); -// } -// } -// }).start(); + } + }).start(); + } } public void stopProg() { @@ -442,22 +427,24 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac if(task==null && root.containsKey("layers")) task = new JSMap("items", new JSList<>(new JSMap("_program", root))); if(task!=null) { var view = new Prog(task, this); - if(view.getChildCount()!=0) setContentView(insView = view); + if(! view.pages.isEmpty()) setContentView(insView = view); } } catch(FileNotFoundException ignored) { } catch(Throwable e) { state = 7; Util.printStackTrace(e); } + var demand = 0; try { Util.println("\nParse Prog Json"); var root = JSMap.fromClose(new BufferedInputStream(new FileInputStream(Util.programDir + "/program"))); var task = root.jsmap("task"); + demand = root.intg("Demand"); if(task==null && root.containsKey("layers")) task = new JSMap("items", new JSList<>(new JSMap("_program", root))); if(task==null) Util.println(root.isEmpty() ? " Empty program JSON\n" : " Error: task==null\n"); else { var view = new Prog(task, this); - if(view.getChildCount()==0) Util.println(" Error: ChildCount==0\n"); + if(view.pages.isEmpty()) Util.println(" Error: ChildCount==0\n"); else setContentView(progView = view); } } catch(FileNotFoundException e) { @@ -470,7 +457,21 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac if(insView!=null || progView!=null) { state = 5; Util.println("Init Sync"); - syncProg((System.currentTimeMillis()+999)/1000*1000, 0); + var ms = System.currentTimeMillis(); + if(demand==0 || progView==null) syncProg((ms+999)/1000*1000, 0); + else { + avas.clear(); + var page = progView.pages.get(demand-1); + avas.add(page); + curAva = 0; + curTimes = 1; + waitTo = 0; //点播 + page.setMillis(ms, ms, showHides); + if(state != 6) { + setContentView(progView); + state = 6; + } + } if(canAdd) { choreographer.postFrameCallback(this); canAdd = false; @@ -492,15 +493,37 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac task = new JSMap("items", new JSList<>(new JSMap("_program", root))); } var view = new Prog(task, this); - if(view.getChildCount()==0) { + if(view.pages.isEmpty()) { if(! view.isInsert) state = 7; Util.println(" Error: ChildCount==0\n"); Util.println(new String(json, Chsets.UTF8)); return; } + if(Util.cfg.bool("needCheck")) { + new Thread(()->{ + if(Util.check(view)) runOnUiThread(() -> { + afterCheck(view, json); + }); + else runOnUiThread(() -> { + view.release(); + Util.makeText(this, "Contents are not passed").show(); + Util.println("Contents are not passed"); + }); + }).start(); + } else afterCheck(view, json); + } catch (Throwable e) { + state = 7; + Util.makeText(this, Util.toStr(e)).show(); + Util.printStackTrace(e); + Util.println(new String(json, Chsets.UTF8)); + } + } + public void afterCheck(Prog view, byte[] json) { + try { if(view.isInsert) { if(insView!=null) insView.release(); insView = view; + try { avas.get(curAva).hide();} catch (Throwable ignored) {} setContentView(insView); } else { if(progView!=null) progView.release(); @@ -535,16 +558,55 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac Choreographer choreographer = Choreographer.getInstance(); boolean canAdd = true; + static class ShowHide { + long time; + Runnable run; + Prog.Source src; + char act; + + public ShowHide(long time, Prog.Source src, char act) { + this.time = time; + this.src = src; + this.act = act; + } + public ShowHide(long time, Runnable run) { + this.time = time; + this.run = run; + } + } + public LinkedList showHides = new LinkedList<>(); + boolean contains(Prog.Source src, char act) { + for(var showHide : showHides) if(showHide.src==src && showHide.act==act) return true; + return false; + } @Override public void doFrame(long frameTimeNanos) { - if(progView == null && insView==null) { + var milli = System.currentTimeMillis(); + var iter = showHides.iterator(); + while(iter.hasNext()) { + var showHide = iter.next(); + if(showHide.time > milli) { + if(showHide.time >= milli+100) break; + else continue; + } + if(showHide.act=='H') { + showHide.src.hide(); + if(! contains(showHide.src, 'S')) showHide.src.release(); + iter.remove(); + } else if(showHide.act=='S') { + showHide.src.show(); + iter.remove(); + } else if(showHide.run!=null) showHide.run.run(); + } + boolean noProg = progView == null && insView==null; + if(noProg && showHides.isEmpty()) { canAdd = true; return; } choreographer.postFrameCallback(this); canAdd = false; - var milli = System.currentTimeMillis(); + if(noProg) return; if(avas.isEmpty()) { if(milli >= waitTo) { waitTo = Long.MAX_VALUE; @@ -556,9 +618,12 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac return; } var lastPage = avas.get(curAva); - if(milli < lastPage.endMilli) lastPage.showHideSrcs(milli); + if(milli < lastPage.endMilli) lastPage.showHideSrcs(milli, showHides); else { - lastPage.hide(); + for(var layer : lastPage.layers) for(var src : layer.srcs) { + showHides.add(new ShowHide(lastPage.endMilli+1000, src, 'H')); + src.isShow = false; + } if(isInsert) { if(--lastPage.repeatTimes<=0 && ++curAva >= avas.size()) { var isDiff = milli-lastPage.endMilli>=1000; @@ -580,9 +645,12 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac return; } } + } else if(milli-lastPage.endMilli >= 5000) { + Util.println("System Time Changed: "+Util.dateFmt.format(lastPage.endMilli)+" -> "+Util.dateFmt.format(milli)); + lastPage.endMilli = milli; } } - avas.get(curAva).setMillis(lastPage.endMilli, milli); + avas.get(curAva).setMillis(lastPage.endMilli, milli, showHides); Util.println("curAva: "+curAva+" endMs: "+avas.get(curAva).endMilli); } if(isInsert) for(var call : insView.calls) call.doFrame(milli); @@ -592,6 +660,7 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac void syncProg(long milli, long cur) { if(cur==0) cur = milli; Util.println("\nSyncProg"); + waitTo = Long.MAX_VALUE; avas.clear(); curAva = 0; curTimes = 1; @@ -599,13 +668,17 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac if(insView!=null) { for(int i=0; i{ + iii.removeView(src.view); + if(src.view instanceof Choreographer.FrameCallback) iii.calls.remove((Choreographer.FrameCallback) src.view); + })); } insView.pages.remove(i--); } if(insView.pages.isEmpty()) { - insView.release(); + var iii = insView; + showHides.add(new ShowHide(milli + 1000, iii::release)); insView = null; try { var fOut = new FileOutputStream(Util.programDir + "/insert"); @@ -626,7 +699,7 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac if(avas.isEmpty()) for(var page : insView.pages) if(page.sches==null) avas.add(page); if(! avas.isEmpty()) { isInsert = true; - avas.get(curAva).setMillis(milli, cur); + avas.get(curAva).setMillis(milli, cur, showHides); if(! insView.isShown()) setContentView(insView); state = 6; Util.println("avas "+avas.size()+" Insert"); @@ -654,7 +727,7 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac syncMs = milli; Util.println("Sync. dur: "+dur+" milli: "+milli+" start: "+start+" diff: "+(milli - start)); } - avas.get(curAva).setMillis(start, milli); + avas.get(curAva).setMillis(start, milli, showHides); if(! progView.isShown()) setContentView(progView); state = 6; Util.println("Avas "+avas.size()); @@ -668,26 +741,4 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac } Util.println("No Avas, back"); } - - public void run() { - try { - var serverSocket = new ServerSocket(3333); - while(true) { - try { - Util.println("\nAccepting ..."); - var socket = serverSocket.accept(); - new TCPThread(MainActivity.ins, socket).start(); - Util.println("\nAccepted"); - } catch (Throwable e) { - var ins = MainActivity.ins; - if(ins!=null) ins.runOnUiThread(() -> Util.makeText(ins, Util.toStr(e)).show()); - Util.printStackTrace(e); - } - } - } catch (Throwable e) { - var msg = e.getMessage(); - if(msg==null || ! msg.contains("EADDRINUSE")) MainActivity.ins.runOnUiThread(() -> Util.makeText(MainActivity.ins, Util.toStr(e)).show()); - Util.printStackTrace(e); - } - } } \ No newline at end of file diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/MainService.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/MainService.java new file mode 100644 index 0000000..3b97e8a --- /dev/null +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/MainService.java @@ -0,0 +1,128 @@ +package com.xixun.xixunplayer; + +import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.IBinder; + +import net.lingala.zip4j.ZipFile; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.util.ArrayList; + +import gnph.util.IOs; + +public class MainService extends Service { + + static MainService ins; + ArrayList reces = new ArrayList<>(); + @Override + public void onCreate() { + super.onCreate(); + ins = this; + Util.println("==>> MainService onCreate >>>> "+hashCode()+" Thread: "+Thread.currentThread().getId()); + TCPThread.startServer(3333); + if(MainActivity.ins==null) { + var intent = new Intent(this, MainActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); + } + + BroadcastReceiver rece; + var intentFilter = new IntentFilter(Intent.ACTION_MEDIA_MOUNTED); + intentFilter.addDataScheme("file"); + registerReceiver(rece = new BroadcastReceiver(){ + long lastMs; + final char[] pass = {'8','8','8'}; + @Override + public void onReceive(Context context, Intent intent) { // in UI Thread + var path = intent.getData().getPath(); + Util.println("\nMEDIA_MOUNTED path: "+path); + var ms = System.currentTimeMillis(); + if(ms-lastMs<1000) return; + lastMs = ms; + Util.downId = 0; + var acti = MainActivity.ins; + Util.makeText(MainService.this, "MEDIA_MOUNTED path: "+path+"\nImporting 正在导入 ...").show(); + new Thread(()->{ + try { + var zip = new ZipFile(path+"/program.zip"); + if(zip.isEncrypted()) zip.setPassword(pass); + long size = 0; + ByteArrayOutputStream jsonOut = null; + var headers = zip.getFileHeaders(); + for(var header : headers) { + size += header.getUncompressedSize(); + if("program".equals(header.getFileName())) IOs.writeClose(jsonOut = new ByteArrayOutputStream(), zip.getInputStream(header)); + } + if(jsonOut==null) { + Util.println("No program File"); + if(acti!=null) acti.runOnUiThread(() -> Util.makeText(acti, "No program File").show()); + return; + } + if(size==0) { + Util.println("zip size is 0"); + if(acti!=null) acti.runOnUiThread(() -> Util.makeText(acti, "zip size is 0").show()); + return; + } + Util.deleteFiles(size, null); + for(var header : headers) if(! "program".equals(header.getFileName())) { + Util.println(" name: " + header.getFileName()); + var fOut = new FileOutputStream(Util.programDir + "/" + header.getFileName()); + IOs.writeCloseIn(fOut, zip.getInputStream(header)); + fOut.flush(); + fOut.getFD().sync(); + fOut.close(); + } + var json = jsonOut.toByteArray(); + Util.println("Import Succeed"); + if(acti!=null) acti.runOnUiThread(() -> { + Util.makeText(acti, "Import Succeed 导入成功").show(); + acti.initProg(json); + }); + else { + var fOut = new FileOutputStream(Util.programDir + "/program"); + fOut.write(json); + fOut.flush(); + fOut.getFD().sync(); + fOut.close(); + var inten = new Intent(MainService.this, MainActivity.class); + inten.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(inten); + } + var inten = new Intent("com.xixun.AccessibilityService"); + inten.putExtra("newProgram", "USB"); + sendBroadcast(inten); + } catch (Exception e) { + Util.printStackTrace(e); + if(acti!=null) acti.runOnUiThread(() -> Util.makeText(acti, Util.toStr(e)).show()); + } + }).start(); + } + }, intentFilter); + reces.add(rece); + } + + @Override + public void onDestroy() { + super.onDestroy(); + Util.println("==<< MainService onDestroy <<<< this "+hashCode()); + ins = null; + for(var rece : reces) { + try { + unregisterReceiver(rece); + } catch (Throwable ignored){ + } + } + System.gc(); + } + + @Override + public IBinder onBind(Intent intent) { + throw new UnsupportedOperationException("Not yet implemented"); + } +} \ No newline at end of file diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Prog.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Prog.java index 4764d1c..52cca2d 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Prog.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Prog.java @@ -1,6 +1,7 @@ package com.xixun.xixunplayer; import android.annotation.SuppressLint; +import android.content.ContentValues; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -22,6 +23,7 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.SimpleTimeZone; import java.util.TimeZone; @@ -54,6 +56,10 @@ public class Prog extends AbsLayout { } var width = partObj.intg("width"); var height = partObj.intg("height"); + if(Util.custom==Util.Custom.Yishi) { + if(width==0) width = Util.screenWidth; + if(height==0) height = Util.screenHeight; + } AbsLayout box; if(partLengths==null || partLengths.size() <= 1) box = this; else { @@ -65,7 +71,7 @@ public class Prog extends AbsLayout { addView(mask, isVertical ? new AbsLayout.LayoutParams(0, len0, width, height - len0) : new AbsLayout.LayoutParams(len0, 0, width - len0, height)); int x = 0, y = 0; for(int i=1; i videoMap = new HashMap<>(); + var waitAudio = pageMap.bool("waitAudio"); + HashMap videoMap = new HashMap<>(); for(int ll=layers.size()-1; ll>=0; ll--) { var layer = new Layer(); layer.isLoop = layers.get(ll).bool("repeat"); @@ -115,8 +124,13 @@ public class Prog extends AbsLayout { var notAudio = ! src.type.equals("Audio"); if(notAudio) { if(geo.width<=0 || geo.height<=0) { - Util.println("\nError: width or height is 0. _type: "+src.type+" width: "+geo.width+" height: "+geo.height); - continue; + if(Util.custom==Util.Custom.Yishi) { + geo.width = width; + geo.height = height; + } else { + Util.println("\nError: width or height is 0. _type: "+src.type+" width: "+geo.width+" height: "+geo.height); + continue; + } } if(box != this && ((geo.y>=height && height>0) || (geo.x>=width && width>0))) { Util.println("\nError: y>=height or x>=width. _type: "+src.type+" width: "+width+" height: "+height+" x: "+geo.x+" y: "+geo.y); @@ -128,7 +142,7 @@ public class Prog extends AbsLayout { src.endTime = src.startTime + dur; if(bdEnd < src.endTime) bdEnd = src.endTime; if(layer.dur < src.endTime) layer.dur = src.endTime; - if(notAudio) { + if(notAudio || waitAudio) { if(page.sDur < src.endTime) page.sDur = src.endTime; } else if(page.audioDur < src.endTime) page.audioDur = src.endTime; @@ -239,20 +253,35 @@ public class Prog extends AbsLayout { var key = isLive ? url : id + src.startTime + src.endTime; var exist = videoMap.get(key); if(exist!=null) { - var geoOld = (AbsLayout.LayoutParams) exist.getLayoutParams(); - if(geo.width*geo.height > geoOld.width*geoOld.height) { - exist.setLayoutParams(geo); - geo = geoOld; + if(geo.width * geo.height > exist.geo.width * exist.geo.height) { + var aaa = exist.geo; + exist.geo = geo; + geo = aaa; } src.view = new SrcCopy(context, exist); ((SrcCopy) src.view).scaleX = 0; } else { - src.view = new SrcVideo(context, isLive ? url : Util.programDir+"/"+id, source.intg("vol", 100) / 100.0f, dur, source.bool("useHW"), isLive); - videoMap.put(key, src.view); + videoMap.put(key, src); + src.typ = 'V'; + src.path = isLive ? url : Util.programDir+"/"+id; + src.vol = source.intg("vol", 100) / 100.0f; + src.dur = dur; + src.useSW = source.bool("useSW"); + src.geo = geo; + src.box = source.bool("isPreSplit") ? this : box; + layer.srcs.add(src); + src = new Source(); } } else if(src.type.equals("Audio")) { if(id==null) continue; - src.view = new SrcVideo(context, Util.programDir + "/" +id, source.intg("vol", 100) / 100.0f, dur, false, false); + src.typ = 'A'; + src.path = Util.programDir + "/" +id; + src.vol = source.intg("vol", 100) / 100.0f; + src.dur = dur; + src.geo = geo; + src.box = box; + layer.srcs.add(src); + src = new Source(); } else if(src.type.equals("Scroll")) { JSList imgs = source.jslist("imgs"); if(imgs.isEmpty()) continue; @@ -407,7 +436,7 @@ public class Prog extends AbsLayout { if(src.view==null) continue; src.view.setVisibility(GONE); src.view.setLayoutParams(geo); - box.addView(src.view); + (source.bool("isPreSplit") ? this : box).addView(src.view); layer.srcs.add(src); src = new Source(); } @@ -470,6 +499,7 @@ public class Prog extends AbsLayout { } public static class Source { + AbsLayout.LayoutParams geo; View view; float alpha = 1, rotate, scaleX = 1, scaleY = 1; long startMilli, endMilli = Long.MAX_VALUE; @@ -479,7 +509,19 @@ public class Prog extends AbsLayout { Uri uri; TextToSpeech tts; String text; - boolean isEntryRand, isExitRand; + boolean isShow, isEntryRand, isExitRand; + char typ; + AbsLayout box; + String path; + float vol; + int dur; + boolean useSW; + + public Source() { + } + public Source(View view) { + this.view = view; + } void show() { if(view.getVisibility()==VISIBLE) return; @@ -494,9 +536,27 @@ public class Prog extends AbsLayout { void hide() { if(view.getVisibility()!=VISIBLE) return; view.setVisibility(GONE); - if(uri!=null) ((GifImageView) view).setImageURI(uri); + if(view instanceof GifImageView) ((GifImageView) view).setImageURI(uri); if(tts!=null && tts.isSpeaking()) tts.stop(); } + void prepare(long seek) { + var ms = System.currentTimeMillis(); + if(view==null) { + var video = new SrcVideo(box.getContext(), path, vol, dur, seek, useSW); + video.setLayoutParams(geo); + video.setVisibility(GONE); + box.addView(view = video); + } + ms = System.currentTimeMillis() - ms; + Util.println(" prepare ms "+ms); + } + void release() { + if(view instanceof SrcVideo) { + ((SrcVideo)view).release(); + box.removeView(view); + view = null; + } + } void doEff() { var w = view.getLayoutParams().width; var h = view.getLayoutParams().height; @@ -665,7 +725,7 @@ public class Prog extends AbsLayout { } public static class Page { - String name; + String id, name, type; ArrayList layers = new ArrayList<>(); ArrayList sches; long endMilli = Long.MAX_VALUE; @@ -675,7 +735,7 @@ public class Prog extends AbsLayout { for(var layer : layers) for(var src : layer.srcs) src.hide(); } - void setMillis(long start, long cur) { + void setMillis(long start, long cur, List shows) { endMilli = start + sDur; for(var layer : layers) { if(layer.isLoop) layer.endMilli = start + layer.dur; @@ -683,21 +743,37 @@ public class Prog extends AbsLayout { src.endMilli = start + src.endTime; src.startMilli = start + src.startTime; if(src.startTime == 0) { - if(src.view instanceof SrcVideo) ((SrcVideo)src.view).seekTo = cur - src.startMilli; - src.show(); + src.prepare(cur - src.startMilli); + shows.add(new MainActivity.ShowHide(src.startMilli+1000, src, 'S')); + src.isShow = true; } } } + if(Util.logOn) { + var values = new ContentValues(); + values.put("time", cur); + values.put("dur", sDur); + values.put("id", id); + values.put("name", name); + values.put("type", type); + values.put("lat", Util.lat); + values.put("lng", Util.lng); + LogThread.rows.add(values); + } } - void showHideSrcs(long milli) { + void showHideSrcs(long milli, List shows) { for(var layer : layers) { for(var src : layer.srcs) { - if(src.view.getVisibility()==VISIBLE) { - if(milli >= src.endMilli) src.hide(); + if(src.isShow) { + if(milli >= src.endMilli) { + shows.add(new MainActivity.ShowHide(src.endMilli+1000, src, 'H')); + src.isShow = false; + } else src.doEff(); } else if(milli < src.endMilli && milli >= src.startMilli) { - if(src.view instanceof SrcVideo) ((SrcVideo)src.view).seekTo = milli - src.startMilli; - src.show(); + src.prepare(milli - src.startMilli); + shows.add(new MainActivity.ShowHide(src.startMilli+1000, src, 'S')); + src.isShow = true; } } if(milli >= layer.endMilli) { @@ -705,8 +781,14 @@ public class Prog extends AbsLayout { for(var src : layer.srcs) { src.endMilli += layer.dur; src.startMilli += layer.dur; - if(src.startTime > 0) src.hide(); - else src.show(); + if(src.startTime > 0) { + shows.add(new MainActivity.ShowHide(src.startMilli+1000, src, 'H')); + src.isShow = false; + } else { + src.prepare(0); + shows.add(new MainActivity.ShowHide(src.startMilli+1000, src, 'S')); + src.isShow = true; + } } } } diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/RestartService.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/RestartService.java deleted file mode 100644 index 57ec86a..0000000 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/RestartService.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.xixun.xixunplayer; - -import android.app.Service; -import android.content.Intent; -import android.os.IBinder; - -public class RestartService extends Service { - - static RestartService ins; - - @Override - public void onCreate() { - super.onCreate(); - ins = this; - Util.println("==>> RestartService onCreate >>>> "+hashCode()); - if(MainActivity.ins!=null) return; - var intent = new Intent(this, MainActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(intent); - } - - @Override - public void onDestroy() { - super.onDestroy(); - Util.println("==<< RestartService onDestroy <<<< this "+hashCode()); - ins = null; - System.gc(); - } - - @Override - public IBinder onBind(Intent intent) { - throw new UnsupportedOperationException("Not yet implemented"); - } -} \ No newline at end of file diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcCopy.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcCopy.java index 2f38af5..9771186 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcCopy.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcCopy.java @@ -1,36 +1,38 @@ package com.xixun.xixunplayer; +import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Canvas; import android.view.View; import androidx.annotation.NonNull; +@SuppressLint("ViewConstructor") public class SrcCopy extends View { - View view; + Prog.Source source; float scaleX = 1, scaleY = 1; - public SrcCopy(Context context, View view) { + public SrcCopy(Context context, Prog.Source source) { super(context); - this.view = view; + this.source = source; } @Override protected void onDraw(@NonNull Canvas canvas) { super.onDraw(canvas); - if(view==null) return; + if(source==null || source.view==null) return; if(scaleX==0) { - if(view.getWidth()!=0&&getWidth()!=0) { - scaleX = getWidth() / (float) view.getWidth(); - scaleY = getHeight() / (float) view.getHeight(); + if(source.view.getWidth()!=0 && getWidth()!=0) { + scaleX = getWidth() / (float) source.view.getWidth(); + scaleY = getHeight() / (float) source.view.getHeight(); } else { invalidate(); return; } } if(scaleX!=1 || scaleY!=1) canvas.scale(scaleX, scaleY); - view.draw(canvas); + source.view.draw(canvas); invalidate(); } } diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcCountdown.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcCountdown.java index 8b7d4a9..42583e0 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcCountdown.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcCountdown.java @@ -5,6 +5,7 @@ import android.graphics.Color; import android.view.Choreographer; import android.webkit.WebView; +import java.text.DecimalFormat; import java.text.SimpleDateFormat; import gnph.util.JSMap; @@ -13,9 +14,11 @@ import gnph.util.NumFmts; @SuppressLint("ViewConstructor") public class SrcCountdown extends WebView implements Choreographer.FrameCallback { + static DecimalFormat fmt00_0 = NumFmts.newDecFmt("00.0"); + long targetTime; String html, lineHeight, prefix, suffix; - boolean isUp, hasDay, hasHour, hasMin, hasSec; + boolean isUp, hasDay, hasHour, hasMin, hasSec, hasFPS; public SrcCountdown(Prog prog, JSMap json) { super(prog.getContext()); @@ -28,16 +31,20 @@ public class SrcCountdown extends WebView implements Choreographer.FrameCallback setVerticalScrollBarEnabled(false); setHorizontalScrollBarEnabled(false); setInitialScale(100); - isUp = json.bool("isUp"); html = json.stnn("html"); + hasFPS = html.contains("%FPS"); + isUp = json.bool("isUp"); lineHeight = json.str("lineHeight"); prefix = ""; suffix = ""; try { - var aaa = html.substring(0, 16).trim(); - if(aaa.startsWith("")) { + suffix = ""; + html = html.substring(6); + } else if(start.startsWith("", "text/html", "UTF-8", null); } - int caseThen(int val, int cas, int then) { - return val==cas ? then : val; - } long lastSec; diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcSensor.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcSensor.java index 15b0127..50fef31 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcSensor.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcSensor.java @@ -51,6 +51,8 @@ public class SrcSensor extends WebView implements IntentReceiver, Choreographer. prefix = ""; prog.calls.add(this); diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideo.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideo.java index 8814ee0..01ed85c 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideo.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideo.java @@ -7,11 +7,6 @@ import android.view.Surface; import android.view.TextureView; import androidx.annotation.NonNull; -import androidx.annotation.OptIn; -import androidx.media3.common.MediaItem; -import androidx.media3.common.util.UnstableApi; -import androidx.media3.exoplayer.ExoPlayer; -import androidx.media3.exoplayer.SeekParameters; import tv.danmaku.ijk.media.player.IMediaPlayer; import tv.danmaku.ijk.media.player.IjkMediaPlayer; @@ -19,58 +14,36 @@ import tv.danmaku.ijk.media.player.IjkMediaPlayer; @SuppressLint("ViewConstructor") public class SrcVideo extends TextureView implements TextureView.SurfaceTextureListener { - String path; - float vol; - int dur; IjkMediaPlayer ijkPlayer; - ExoPlayer exoPlayer; + Surface surface; long bitRate; - long seekTo; - boolean isLive; - public SrcVideo(Context context, String path, float vol, int dur, boolean useHW, boolean isLive) { + public SrcVideo(Context context, String path, float vol, int dur, long seek, boolean useSW) { super(context); - this.path = path; - this.vol = vol; - this.dur = dur; - this.isLive = isLive; - initIjk(useHW); - } - - @OptIn(markerClass = UnstableApi.class) - void initExo() { - exoPlayer = new ExoPlayer.Builder(getContext()).build(); - exoPlayer.setMediaItem(MediaItem.fromUri(path)); - exoPlayer.setRepeatMode(ExoPlayer.REPEAT_MODE_ONE); - exoPlayer.setSeekParameters(SeekParameters.CLOSEST_SYNC); - exoPlayer.setVolume(vol); - exoPlayer.setVideoTextureView(this); - exoPlayer.prepare(); - } - void initIjk(boolean useHW) { + setSurfaceTextureListener(this); + Util.println(" video new"); ijkPlayer = new IjkMediaPlayer(); - ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-avc", 1); - ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-hevc", 1); + if(! useSW) { + ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-avc", 1); + ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-hevc", 1); + } ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 0); ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzeduration", 1); - ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 0); + ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 12); try { - setSurfaceTextureListener(this); ijkPlayer.setDataSource(path); ijkPlayer.setLooping(true); ijkPlayer.setVolume(vol, vol); ijkPlayer.setOnPreparedListener((IMediaPlayer var1)->{ ijkPlayer.setOnPreparedListener(null); bitRate = ijkPlayer.getBitRate(); - if(bitRate > 12000000) { - setSurfaceTextureListener(null); - release(); - initExo(); - } else { - var diff = dur - ijkPlayer.getDuration(); - if(diff>0 && diff<=1000) ijkPlayer.setLooping(false); + var diff = dur - ijkPlayer.getDuration(); + if(diff>0 && diff<=1000) ijkPlayer.setLooping(false); + if(seek>=1000) { + ijkPlayer.seekTo(seek); + Util.println(" Seek "+seek); } - if(isShown()) start(); + if(isShown()) ijkPlayer.start(); }); ijkPlayer.setOnErrorListener((IMediaPlayer var1, int var2, int var3)->{ Util.println(" Video Error: "+var1+" "+var2+" "+var3); @@ -83,72 +56,42 @@ public class SrcVideo extends TextureView implements TextureView.SurfaceTextureL ijkPlayer = null; } } + @Override public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) { - Util.println(" onSurfaceTextureAvailable "+(ijkPlayer==null?"ijkPlayer==null":"")); - if(ijkPlayer!=null) ijkPlayer.setSurface(new Surface(surface)); + Util.println(" SurfaceTexture Available "+(ijkPlayer==null?"ijkPlayer==null":"")); + this.surface = new Surface(surface); + ijkPlayer.setSurface(this.surface); } @Override public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) {} @Override public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) { + Util.println(" SurfaceTexture Destroyed"); return false; } @Override public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {} - void start() { - if(ijkPlayer!=null) { - ijkPlayer.seekTo(seekTo); - ijkPlayer.start(); - } else if(exoPlayer!=null) { - if(exoPlayer.getPlaybackState()==ExoPlayer.STATE_IDLE) exoPlayer.prepare(); - exoPlayer.seekTo(seekTo); - exoPlayer.play(); - } - } - void pause() { - if(ijkPlayer!=null) { - ijkPlayer.pause(); - ijkPlayer.seekTo(0); - } - if(exoPlayer!=null && exoPlayer.isPlaying()) { - exoPlayer.pause(); - exoPlayer.seekToDefaultPosition(); - } - } void release() { + Util.println(" video release"); if(ijkPlayer!=null) { ijkPlayer.release(); ijkPlayer = null; } - if(exoPlayer!=null) { - exoPlayer.release(); - exoPlayer = null; + if(surface!=null) { + surface.release(); + surface = null; } } - String getState() { - if(exoPlayer!=null) { - var state = exoPlayer.getPlaybackState(); - if(state==ExoPlayer.STATE_IDLE) return "STATE_IDLE"; - if(state==ExoPlayer.STATE_BUFFERING) return "STATE_BUFFERING"; - if(state==ExoPlayer.STATE_READY) return "STATE_READY"; - if(state==ExoPlayer.STATE_ENDED) return "STATE_ENDED"; - } - return "null"; - } - @Override public void onVisibilityAggregated(boolean isVisible) { super.onVisibilityAggregated(isVisible); - if(isVisible) { - start(); - if(isLive) ijkPlayer.setVolume(vol, vol); - } else if(isLive) ijkPlayer.setVolume(0, 0); + if(isVisible) ijkPlayer.start(); else { - seekTo = 0; - pause(); + ijkPlayer.pause(); + ijkPlayer.seekTo(0); } } } \ No newline at end of file diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideoSurface.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideoSurface.java deleted file mode 100644 index e95a8d1..0000000 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideoSurface.java +++ /dev/null @@ -1,138 +0,0 @@ -package com.xixun.xixunplayer; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.view.SurfaceHolder; -import android.view.SurfaceView; - -import androidx.annotation.NonNull; -import androidx.annotation.OptIn; -import androidx.media3.common.MediaItem; -import androidx.media3.common.util.UnstableApi; -import androidx.media3.exoplayer.ExoPlayer; -import androidx.media3.exoplayer.SeekParameters; - -import tv.danmaku.ijk.media.player.IMediaPlayer; -import tv.danmaku.ijk.media.player.IjkMediaPlayer; - -@SuppressLint("ViewConstructor") -public class SrcVideoSurface extends SurfaceView implements SurfaceHolder.Callback { - - String path; - float vol; - IjkMediaPlayer ijkPlayer; - ExoPlayer exoPlayer; - long bitRate; - boolean isLive; - - public SrcVideoSurface(Context context, String path, float vol, boolean isLive) { - super(context); - this.path = path; - this.vol = vol; - this.isLive = isLive; - initIjk(); - } - - @OptIn(markerClass = UnstableApi.class) - void initExo() { - exoPlayer = new ExoPlayer.Builder(getContext()).build(); - exoPlayer.setMediaItem(MediaItem.fromUri(path)); - exoPlayer.setRepeatMode(ExoPlayer.REPEAT_MODE_ONE); - exoPlayer.setSeekParameters(SeekParameters.CLOSEST_SYNC); - exoPlayer.setVolume(vol); - exoPlayer.setVideoSurfaceView(this); - exoPlayer.prepare(); - } - void initIjk() { - ijkPlayer = new IjkMediaPlayer(); - ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-avc", 1); - ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 0); - ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzeduration", 1); - ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48); - try { - getHolder().addCallback(this); - ijkPlayer.setDataSource(path); - ijkPlayer.setLooping(true); - ijkPlayer.setVolume(vol, vol); - ijkPlayer.setOnPreparedListener((IMediaPlayer var1)->{ - ijkPlayer.setOnPreparedListener(null); - bitRate = ijkPlayer.getBitRate(); - if(bitRate > 12000000) { - getHolder().removeCallback(this); - release(); - initExo(); - } - if(isShown()) start(); - }); - ijkPlayer.setOnErrorListener((IMediaPlayer var1, int var2, int var3)->{ - Util.println(" Video Error: "+var1+" "+var2+" "+var3); - return true; - }); - ijkPlayer.prepareAsync(); - } catch (Throwable e) { - Util.makeText(getContext(), Util.toStr(e)).show(); - Util.printStackTrace(e); - ijkPlayer = null; - } - } - @Override - public void surfaceCreated(@NonNull SurfaceHolder holder) { - Util.println(" surfaceCreated "+(ijkPlayer==null?"ijkPlayer==null":"")); - if(ijkPlayer!=null) ijkPlayer.setDisplay(holder); - } - @Override - public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {} - @Override - public void surfaceDestroyed(@NonNull SurfaceHolder holder) {} - - void start() { - if(ijkPlayer!=null) { - ijkPlayer.seekTo(0); - ijkPlayer.start(); - } else if(exoPlayer!=null) { - if(exoPlayer.getPlaybackState()==ExoPlayer.STATE_IDLE) exoPlayer.prepare(); - exoPlayer.play(); - } - } - void pause() { - if(ijkPlayer!=null) { - ijkPlayer.pause(); - ijkPlayer.seekTo(0); - } - if(exoPlayer!=null && exoPlayer.isPlaying()) { - exoPlayer.pause(); - exoPlayer.seekToDefaultPosition(); - } - } - void release() { - if(ijkPlayer!=null) { - ijkPlayer.release(); - ijkPlayer = null; - } - if(exoPlayer!=null) { - exoPlayer.release(); - exoPlayer = null; - } - } - - String getState() { - if(exoPlayer!=null) { - var state = exoPlayer.getPlaybackState(); - if(state==ExoPlayer.STATE_IDLE) return "STATE_IDLE"; - if(state==ExoPlayer.STATE_BUFFERING) return "STATE_BUFFERING"; - if(state==ExoPlayer.STATE_READY) return "STATE_READY"; - if(state==ExoPlayer.STATE_ENDED) return "STATE_ENDED"; - } - return "null"; - } - - @Override - public void onVisibilityAggregated(boolean isVisible) { - super.onVisibilityAggregated(isVisible); - if(isVisible) { - start(); - if(isLive) ijkPlayer.setVolume(vol, vol); - } else if(isLive) ijkPlayer.setVolume(0, 0); - else pause(); - } -} \ No newline at end of file diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVisitor.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVisitor.java index 7348eec..52356a7 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVisitor.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVisitor.java @@ -5,6 +5,8 @@ import android.graphics.Color; import android.view.Choreographer; import android.webkit.WebView; +import java.nio.charset.StandardCharsets; + import gnph.util.JSMap; import gnph.util.URLConn; @@ -22,6 +24,10 @@ public class SrcVisitor extends WebView implements Choreographer.FrameCallback { html = json.stnn("html").replace("%{yesterday.", "%{arr.-1."); lineHeight = json.str("lineHeight"); url = json.str("url"); + if(url!=null) { + var token = json.str("token"); + if(token!=null) url += "?dataKey="+token; + } prefix = ""; @@ -31,8 +37,13 @@ public class SrcVisitor extends WebView implements Choreographer.FrameCallback { public void refresh() { try { var htm = html; - try { - var res = JSMap.fromClose(new URLConn(url).in()).jsmap("data"); + try { + var resp = URLConn.send(url); + var data = JSMap.from(resp.getBytes(StandardCharsets.UTF_8)).jsmap("data"); + if(data==null) { + Util.println("SrcVisitor No data: "+resp); + return; + } int lastEnd = 0, appendIdx = 0; var buf = new StringBuilder(htm.length()*3/2); int start; @@ -47,7 +58,7 @@ public class SrcVisitor extends WebView implements Choreographer.FrameCallback { lastEnd = fde+1; if(fde == fds) continue; var fd = htm.substring(fds, fde); - var replace = res.str(fd); + var replace = data.str(fd); if(replace!=null) { buf.append(htm, appendIdx, start).append(replace); appendIdx = lastEnd; diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcWeb.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcWeb.java index 3b1350a..18e92e8 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcWeb.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcWeb.java @@ -43,18 +43,36 @@ public class SrcWeb extends WebView implements Choreographer.FrameCallback { }); loadUrl(url = json.str("url")); refresh = json.intg("refreshSec")*1000; - if(refresh>0 && url!=null) prog.calls.add(this); + if(Util.custom==Util.Custom.Yishi) { + prog.calls.add(this); + } else { + if(refresh>0 && url!=null) prog.calls.add(this); + } nextMs = System.currentTimeMillis() + refresh; } long nextMs; - + int w, h; @Override public void doFrame(long ms) { if(! isShown()) return; - if(ms>=nextMs) { - nextMs = ms + refresh; - loadUrl(url); + + if(Util.custom==Util.Custom.Yishi) { + if(refresh>0 && url!=null && ms>=nextMs) { + nextMs = ms + refresh; + loadUrl(url); + } + if(Util.screenWidth!=w || Util.screenHeight!=h) { + w = Util.screenWidth; + h = Util.screenHeight; + Util.println(" w "+w+" h "+h); + setLayoutParams(new AbsLayout.LayoutParams(0, 0, w, h)); + } + } else { + if(ms>=nextMs) { + nextMs = ms + refresh; + loadUrl(url); + } } } } diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/TCPThread.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/TCPThread.java index fc1da68..4b90d19 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/TCPThread.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/TCPThread.java @@ -3,14 +3,14 @@ package com.xixun.xixunplayer; import static android.view.View.VISIBLE; import android.app.ActivityManager; +import android.content.Context; +import android.content.Intent; import android.graphics.BitmapFactory; import android.os.Environment; import android.os.StatFs; +import android.view.WindowManager; import android.webkit.WebView; -import androidx.annotation.OptIn; -import androidx.media3.common.util.UnstableApi; - import net.lingala.zip4j.ZipFile; import java.io.ByteArrayOutputStream; @@ -20,6 +20,7 @@ import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; +import java.net.ServerSocket; import java.net.Socket; import java.net.SocketTimeoutException; import java.text.SimpleDateFormat; @@ -37,17 +38,41 @@ import gnph.util.NumFmts; import gnph.util.O; public class TCPThread extends Thread { + + public static void startServer(int port) { + new Thread(()->{ + try { + var serverSocket = new ServerSocket(port); + while(true) { + try { + Util.println("\nAccepting ..."); + var socket = serverSocket.accept(); + new TCPThread(MainActivity.ins, socket).start(); + Util.println("\nAccepted"); + } catch (Throwable e) { + var ins = MainActivity.ins; + if(ins!=null) ins.runOnUiThread(() -> Util.makeText(ins, Util.toStr(e)).show()); + Util.printStackTrace(e); + } + } + } catch (Throwable e) { + var msg = e.getMessage(); + if(msg==null || ! msg.contains("in use")) MainActivity.ins.runOnUiThread(() -> Util.makeText(MainActivity.ins, Util.toStr(e)).show()); + Util.printStackTrace(e); + } + }).start(); + } + MainActivity main; Socket socket; InputStream in; OutputStream out; - SimpleDateFormat fmt = new SimpleDateFormat("MM-dd HH:mm:ss"); + SimpleDateFormat fmt = new SimpleDateFormat("yy-MM-dd HH:mm:ss"); public TCPThread(MainActivity main, Socket socket) { this.main = main; this.socket = socket; } - @OptIn(markerClass = UnstableApi.class) @Override public void run() { try { socket.setSoTimeout(600000); @@ -73,6 +98,7 @@ public class TCPThread extends Thread { new JSMap("_type", _type, "existed", existed).write(out); } } else if("proStart".equals(_type)) { + Util.downId = 0; Util.deleteFiles(obj.intg("proSize"), existed); } else if("fileStart".equals(_type)) { var size = obj.intg("size"); @@ -106,17 +132,28 @@ public class TCPThread extends Thread { }); new JSMap("success", true).write(out); } else if("proEnd".equals(_type)) { - new JSMap("success", progJson!=null).write(out); + new JSMap("_type", "AckSuccess", "success", progJson!=null).write(out); if(progJson!=null) { var json = progJson.toByteArray(); progJson = null; - if(main!=null) main.runOnUiThread(() -> main.initProg(json)); + var acti = MainActivity.ins; + if(acti!=null) acti.runOnUiThread(() -> acti.initProg(json)); else { var fOut = new FileOutputStream(Util.programDir + "/program"); fOut.write(json); fOut.flush(); fOut.getFD().sync(); fOut.close(); + if(MainService.ins!=null) { + var intent = new Intent(MainService.ins, MainActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + MainService.ins.startActivity(intent); + } + } + if(MainService.ins!=null) { + var intent = new Intent("com.xixun.AccessibilityService"); + intent.putExtra("newProgram", "TCP"); + MainService.ins.sendBroadcast(intent); } } } else if("playZipTask".equals(_type)) { @@ -149,9 +186,26 @@ public class TCPThread extends Thread { fOut.close(); } var json = jsonOut.toByteArray(); - if(main!=null) main.runOnUiThread(()->main.initProg(json)); - Util.println(" Succeed"); + var acti = MainActivity.ins; + if(acti!=null) acti.runOnUiThread(()->acti.initProg(json)); + else { + var fOut = new FileOutputStream(Util.programDir + "/program"); + fOut.write(json); + fOut.flush(); + fOut.getFD().sync(); + fOut.close(); + if(MainService.ins!=null) { + var intent = new Intent(MainService.ins, MainActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + MainService.ins.startActivity(intent); + } + } new JSMap("success", true).write(out); + if(MainService.ins!=null) { + var intent = new Intent("com.xixun.AccessibilityService"); + intent.putExtra("newProgram", "TCP"); + MainService.ins.sendBroadcast(intent); + } } } catch (Exception e) { Util.printStackTrace(e); @@ -159,15 +213,18 @@ public class TCPThread extends Thread { if(main!=null) main.runOnUiThread(() -> Util.makeText(main, Util.toStr(e)).show()); } } else if("DelPrograms".equals(_type)) { - var latch = new CountDownLatch(1); + Util.downId = 0; var ok = new AtomicBoolean(false); - if(main!=null) main.runOnUiThread(() -> { - ok.set(main.delProgFile()); - latch.countDown(); - }); - try { - latch.await(); - } catch (InterruptedException ignored) {} + if(main!=null) { + var latch = new CountDownLatch(1); + main.runOnUiThread(() -> { + ok.set(main.delProgFile()); + latch.countDown(); + }); + try { + latch.await(); + } catch (InterruptedException ignored) {} + } new JSMap("success", ok.get()).write(out); } else if("getProgramName".equals(_type)) { try { @@ -186,6 +243,15 @@ public class TCPThread extends Thread { MainActivity.ins.backView.invalidate(); }); new JSMap("success", new File(Util.backImgFile).delete()).write(out); + } else if("Config".equals(_type)) { + var entrySet = obj.entrySet(); + for(var entry : entrySet) if(! entry.getKey().equals("_type")) Util.cfg.put(entry.getKey(), entry.getValue()); + try (var fOut = new FileOutputStream(Util.programDir + "/cfg")) { + Util.cfg.write(fOut); + fOut.flush(); + fOut.getFD().sync(); + } + new JSMap("success", new File(Util.backImgFile).delete()).write(out); } else if("getPlayerState".equals(_type)) { var state = main==null ? 8 : main.state; var descs = Util.getState(state); @@ -200,6 +266,10 @@ public class TCPThread extends Thread { var dur = 0; if(main==null) writer.append("\nAPP is Closed\n"); else { + try { + var packageInfo = main.getPackageManager().getPackageInfo(main.getPackageName(), 0); + writer.append("ver: ").append(packageInfo.versionName).append("\n"); + } catch (Exception ignored) {} if(main.progView!=null) for(var page : main.progView.pages) dur += page.tDur; writer.append("ProgSend: ").append(fmt.format(new File(Util.programDir + "/program").lastModified())).append(" ProgDur: ").append(String.valueOf(dur)).append("ms"); writer.append(" Size Avas: ").append(String.valueOf(main.avas.size())).append(" Pages: ").append(main.progView==null ? "null" : String.valueOf(main.progView.pages.size())); @@ -212,6 +282,13 @@ public class TCPThread extends Thread { else writer.append("Page End: ").append(fmt.format(main.avas.get(main.curAva).endMilli)).append(" CurPage: ").append(String.valueOf(main.curAva)).append(" ").append(String.valueOf(main.avas.get(main.curAva).name)).append("\n"); } writer.append(" Current: ").append(fmt.format(System.currentTimeMillis())).append(" ").append(String.valueOf(TimeZone.getDefault().getRawOffset()/3600000f)).append(" ").append(TimeZone.getDefault().getDisplayName()).append("\n"); + + if(main!=null) { + var mng = (WindowManager) main.getSystemService(Context.WINDOW_SERVICE); + var display = mng.getDefaultDisplay(); + writer.append("RefreshRate: ").append(String.valueOf(display.getRefreshRate())).append("\n"); + } + var statFs = new StatFs(Environment.getExternalStorageDirectory().getPath()); writer.append(" Disk: ").append(String.valueOf(statFs.getTotalBytes()/1000000)).append(" ").append(String.valueOf(statFs.getAvailableBytes()/1000000)).append(" ").append(String.valueOf(statFs.getFreeBytes()/1000000)).append(" (total avail free)\n"); if(main!=null) { @@ -228,6 +305,11 @@ public class TCPThread extends Thread { for(var socket : Util.socketThreads) writer.append(" ").append(String.valueOf(socket.socket.getInetAddress())).append(":").append(String.valueOf(socket.socket.getPort())).append(" Buf:").append(String.valueOf(socket.socket.getReceiveBufferSize()/1000)).append("k SoLinger:").append(String.valueOf(socket.socket.getSoLinger())).append("\n"); writer.append("\n"); + writer.append("Server URL "+Util.serverURL); + writer.append("\n\n"); + writer.append(Util.cfg.toStr()); + writer.append("\n\n"); + var latch = new CountDownLatch(1); if(main!=null) main.runOnUiThread(() -> { if(! main.avas.isEmpty()) { @@ -247,15 +329,6 @@ public class TCPThread extends Thread { writer.append("VideoDecoder: ").append(mediaInfo.mVideoDecoder).append(" ").append(mediaInfo.mVideoDecoderImpl).append(" (").append(String.valueOf(view.ijkPlayer.getVideoDecoder())).append(")\n"); writer.append("AudioDecoder: ").append(mediaInfo.mAudioDecoder).append(" ").append(mediaInfo.mAudioDecoderImpl).append("\n"); //writer.append("PROFILE: ").append(mediaInfo.mMeta.getString(IjkMediaMeta.IJKM_KEY_CODEC_PROFILE)).append("\n"); - } else if(view.exoPlayer!=null) { - writer.append("VideoPlaying: ").append(String.valueOf(view.exoPlayer.isPlaying())).append(" ").append(String.valueOf(view.exoPlayer.getCurrentPosition())).append("/").append(String.valueOf(view.exoPlayer.getDuration())).append("\n"); - writer.append(" BitRate: ").append(String.valueOf(view.bitRate/1000)).append("k\n"); - writer.append("PlaybackStat: ").append(view.getState()).append("\n"); - writer.append(" PlayerError: ").append(String.valueOf(view.exoPlayer.getPlayerError())).append("\n"); - writer.append(" VideoFormat: ").append(String.valueOf(view.exoPlayer.getVideoFormat())).append("\n"); - var cnt = view.exoPlayer.getRendererCount(); - for(int rr=0; rr (int) (f2.lastModified() - f1.lastModified())); + Arrays.sort(files, (f1, f2) -> Long.signum(f2.lastModified() - f1.lastModified())); var writer = new OutputStreamWriter(out); for(var file : files) writer.append(fmt.format(new Date(file.lastModified()))).append(' ').append(file.getName()).append(' ').append(String.valueOf(file.length())).append('\n'); writer.flush(); diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Util.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Util.java index 652722c..9801bef 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Util.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Util.java @@ -1,17 +1,26 @@ package com.xixun.xixunplayer; import android.content.Context; +import android.database.sqlite.SQLiteOpenHelper; +import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.os.Build; import android.os.Environment; import android.os.StatFs; import android.view.Gravity; import android.widget.Toast; +import com.aliyun.imageaudit20191230.models.ScanImageAdvanceRequest; +import com.aliyun.tea.TeaModel; + import java.io.CharArrayWriter; import java.io.File; import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Random; @@ -20,11 +29,32 @@ import java.util.Vector; import java.util.concurrent.CountDownLatch; import gnph.util.IOs; +import gnph.util.JSMap; +import gnph.util.O; +import wseemann.media.FFmpegMediaMetadataRetriever; + public class Util { + enum Custom{Normal, Yishi}; + public static final Custom custom = Custom.Normal; + public static JSMap cfg; + public static SQLiteOpenHelper openHelper; public static String serverURL; + public static volatile long downId; public static int screenWidth = 1920, screenHeight = 1080; - public static boolean isScreenOn; + public static double lat, lng; + public static boolean isScreenOn, logOn; + + public static void initDir(Context ctx) { + var dir = Build.VERSION.SDK_INT < Build.VERSION_CODES.R ? Environment.getExternalStorageDirectory().getAbsolutePath() + "/XixunPlayer" : ctx.getExternalFilesDir(null).getAbsolutePath(); + Util.println("\n---- Init dir "+dir); + Util.programDir = dir + "/program"; + Util.backImgFile = dir + "/background"; + + var program = new File(Util.programDir); + if(program.isFile()) program.delete(); + Util.println(" mkdir: "+program.mkdirs()); + } public static final BitmapFactory.Options noScaled = new BitmapFactory.Options(); static { @@ -47,6 +77,7 @@ public class Util { public static StringBuffer buf = new StringBuffer(); public static Random rand = new Random(); + public static SimpleDateFormat dateFmt = new SimpleDateFormat("yy-MM-dd HH:mm:ss.SSS"); public static void println(String msg) { System.out.println(msg); @@ -87,7 +118,7 @@ public class Util { public static String programDir, backImgFile; public static String getCardId() { try { - var bytes = IOs.readBytesClose(new FileInputStream(new File("/data/joey/signed/card.id"))); + var bytes = IOs.readBytesClose(new FileInputStream("/data/joey/signed/card.id")); if(bytes.length < 40) return ""; byte[] cMyKey = new byte[]{97, 119, 38, 3, 46, 112, 36, 93, 58, 100, 103, 62, 115, 112, 114, 51, 43, 61, 2, 101, 119}; for(int i=0; i<20; ++i) bytes[i] = (byte) (bytes[i * 2] - cMyKey[i] - i - (bytes[i * 2 + 1] - 3)); @@ -102,7 +133,7 @@ public class Util { public static void deleteFiles(long proSize, Set keeps) { var statFs = new StatFs(Environment.getExternalStorageDirectory().getPath()); - var remain = statFs.getAvailableBytes() - proSize - 1048576; + var remain = statFs.getAvailableBytes() - proSize - 400000000; if(remain >= 0) return; var latch = new CountDownLatch(1); MainActivity.ins.runOnUiThread(() -> { @@ -125,4 +156,72 @@ public class Util { } } } + public static int caseThen(int val, int cas, int then) { + return val==cas ? then : val; + } + + public static com.aliyun.imageaudit20191230.Client createClient() throws Exception { + com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config().setAccessKeyId("LTAI5tJh71Sz9fRM3itMm2jH").setAccessKeySecret("yujMXB95cNTpG4IUJ6eFDhZwJuHl6u"); + config.endpoint = "imageaudit.cn-shanghai.aliyuncs.com"; + return new com.aliyun.imageaudit20191230.Client(config); + } + public static boolean check(Prog view) { + try { + var client = createClient(); + var taskList = new ArrayList(); + var sceneList = new ArrayList(); + int idx = 0; + for(var page : view.pages) for(var layer : page.layers) for(var src : layer.srcs) { + if(src.typ == 'V') { + var retriever = new FFmpegMediaMetadataRetriever(); + try { + retriever.setDataSource(src.path); + var dur = Long.parseLong(retriever.extractMetadata(FFmpegMediaMetadataRetriever.METADATA_KEY_DURATION)); + Util.println(" dur "+dur); + for(long i=0; i + + + + + \ No newline at end of file diff --git a/XixunPlayer1/.idea/gradle.xml b/XixunPlayer1/.idea/gradle.xml new file mode 100644 index 0000000..ae388c2 --- /dev/null +++ b/XixunPlayer1/.idea/gradle.xml @@ -0,0 +1,20 @@ + + + + + + + \ No newline at end of file diff --git a/XixunPlayer1/.idea/misc.xml b/XixunPlayer1/.idea/misc.xml new file mode 100644 index 0000000..a25e2a9 --- /dev/null +++ b/XixunPlayer1/.idea/misc.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/XixunPlayer1/.idea/vcs.xml b/XixunPlayer1/.idea/vcs.xml new file mode 100644 index 0000000..6c0b863 --- /dev/null +++ b/XixunPlayer1/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/XixunPlayer1/app/.gitignore b/XixunPlayer1/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/XixunPlayer1/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/XixunPlayer1/app/build.gradle b/XixunPlayer1/app/build.gradle new file mode 100644 index 0000000..07cb109 --- /dev/null +++ b/XixunPlayer1/app/build.gradle @@ -0,0 +1,75 @@ +plugins { + id 'com.android.application' +} + +android { + namespace 'com.xixun.xixunplayer' + compileSdk 34 + + defaultConfig { + applicationId "com.xixun.xixunplayer" + minSdk 21 + targetSdk 34 + versionCode 1 + versionName "2.1.46-3568" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + + buildFeatures { + aidl true + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } + + signingConfigs { + a133 {//全志 a133 + keyAlias 'platform' + keyPassword 'youngfeel' + storeFile file('D:/_docs/comp/android/keystore/a133_android11_system.jks') + storePassword 'youngfeel' + } + } +} + +dependencies { + implementation 'androidx.appcompat:appcompat:1.6.1' +// implementation 'androidx.media3:media3-exoplayer:1.2.0' +// implementation 'tv.danmaku.ijk.media:ijkplayer-java:0.8.8' +// implementation 'tv.danmaku.ijk.media:ijkplayer-arm64:0.8.8' + implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.25' + implementation 'com.aliyun:imageaudit20191230:2.0.6' + implementation 'com.github.wseemann:FFmpegMediaMetadataRetriever-core:1.0.19' + implementation 'com.github.wseemann:FFmpegMediaMetadataRetriever-native-armeabi-v7a:1.0.19' + implementation 'com.github.wseemann:FFmpegMediaMetadataRetriever-native-arm64-v8a:1.0.19' + implementation files('libs/gnph.jar') + implementation files('libs/zip4j-2.10.0.jar') + implementation files('libs/xixun_card_settings_1.2.4.jar') + implementation files('libs/ijkplayer-java-0.8.8.aar') + implementation files('libs/ijkplayer-armv7a-0.8.8.aar') + implementation files('libs/ijkplayer-arm64-0.8.8.aar') + implementation files('libs/connService2.jar') +} + +def getAppName() { + def stringsFile = android.sourceSets.main.res.sourceFiles.find { it.name.equals 'strings.xml' } + String s = new XmlParser().parse(stringsFile).string.find { it.@name.equals 'app_name' }.text(); + return s.replaceAll("\"", ""); +} + +// 修改 Apk 名 +android.applicationVariants.configureEach { variant -> + variant.outputs.configureEach { + def fileName = "${getAppName()}-${versionName}.apk" + outputFileName = fileName + } +} diff --git a/XixunPlayer1/app/libs/connService2.jar b/XixunPlayer1/app/libs/connService2.jar new file mode 100644 index 0000000..9bb1491 Binary files /dev/null and b/XixunPlayer1/app/libs/connService2.jar differ diff --git a/XixunPlayer1/app/libs/gnph.jar b/XixunPlayer1/app/libs/gnph.jar new file mode 100644 index 0000000..49484bf Binary files /dev/null and b/XixunPlayer1/app/libs/gnph.jar differ diff --git a/XixunPlayer1/app/libs/ijkplayer-arm64-0.8.8.aar b/XixunPlayer1/app/libs/ijkplayer-arm64-0.8.8.aar new file mode 100644 index 0000000..105f873 Binary files /dev/null and b/XixunPlayer1/app/libs/ijkplayer-arm64-0.8.8.aar differ diff --git a/XixunPlayer1/app/libs/ijkplayer-armv7a-0.8.8.aar b/XixunPlayer1/app/libs/ijkplayer-armv7a-0.8.8.aar new file mode 100644 index 0000000..d84b81a Binary files /dev/null and b/XixunPlayer1/app/libs/ijkplayer-armv7a-0.8.8.aar differ diff --git a/XixunPlayer1/app/libs/ijkplayer-java-0.8.8.aar b/XixunPlayer1/app/libs/ijkplayer-java-0.8.8.aar new file mode 100644 index 0000000..8224130 Binary files /dev/null and b/XixunPlayer1/app/libs/ijkplayer-java-0.8.8.aar differ diff --git a/XixunPlayer1/app/libs/xixun_card_settings_1.2.4.jar b/XixunPlayer1/app/libs/xixun_card_settings_1.2.4.jar new file mode 100644 index 0000000..0d17c22 Binary files /dev/null and b/XixunPlayer1/app/libs/xixun_card_settings_1.2.4.jar differ diff --git a/XixunPlayer1/app/libs/zip4j-2.10.0.jar b/XixunPlayer1/app/libs/zip4j-2.10.0.jar new file mode 100644 index 0000000..d80d844 Binary files /dev/null and b/XixunPlayer1/app/libs/zip4j-2.10.0.jar differ diff --git a/XixunPlayer1/app/proguard-rules.pro b/XixunPlayer1/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/XixunPlayer1/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/XixunPlayer1/app/src/main/AndroidManifest.xml b/XixunPlayer1/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..3b6eb67 --- /dev/null +++ b/XixunPlayer1/app/src/main/AndroidManifest.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/XixunPlayer1/app/src/main/aidl/com/xixun/util/PlayerInfo.aidl b/XixunPlayer1/app/src/main/aidl/com/xixun/util/PlayerInfo.aidl new file mode 100644 index 0000000..7726d8e --- /dev/null +++ b/XixunPlayer1/app/src/main/aidl/com/xixun/util/PlayerInfo.aidl @@ -0,0 +1,47 @@ +// PlayerInfo.aidl +package com.xixun.util; + +// Declare any non-default types here with import statements + +interface PlayerInfo { + //***需要实现,用于外部接口获取当前播放的节目名 + String getProgramName(); + //不需要实现 + String getVersion(); + //外部接口通知播放器屏幕宽高发生变化 + void setScreenWidth(int w); + void setScreenHeight(int h); + //不需要 + void taskScreenshot(String cmdId); + //不需要 + void setExternalTemperature(float t); + void setInternalTemperature(float t); + void setHumidity(float h); + //暂时不需要 + boolean forcePlayProgram(String pid); + boolean finishForcePlay(); + //需要实现,让其他进程获取到当前播放的节目id + String getCurProgramId(); + //需要,外部进程设置播放器的USB节目解压密码 + void setUSBProgramPwd(String pwd); + //***需要,接收平台节目接口 + String executeJosnCommand(String josn); + //暂停播放,以前的版本没有实现 + void pausePlayer(boolean b); + //查询节目是否暂停播放 + boolean isPause(); + //***需要,清空节目和下载的素材 + boolean clearTasks(); + //需要,返回当前已有的节目数量 + int countOfPrograms(int type); + //需要,指定id播放插播节目 + void playInsertTask(String pid); + //需要,指定id停止播放插播节目 + void stopInsertTask(String pid); + //***需要,回读当前节目json + String getProgramTask(); + //不需要 + void setUploadLogUrl(String playLog); + //不需要 + String getUploadLogUrl(); +} \ No newline at end of file diff --git a/XixunPlayer1/app/src/main/assets/imgs/W0.png b/XixunPlayer1/app/src/main/assets/imgs/W0.png new file mode 100644 index 0000000..3e37d21 Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W0.png differ diff --git a/XixunPlayer1/app/src/main/assets/imgs/W1.png b/XixunPlayer1/app/src/main/assets/imgs/W1.png new file mode 100644 index 0000000..5dee862 Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W1.png differ diff --git a/XixunPlayer1/app/src/main/assets/imgs/W10.png b/XixunPlayer1/app/src/main/assets/imgs/W10.png new file mode 100644 index 0000000..7d55efe Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W10.png differ diff --git a/XixunPlayer1/app/src/main/assets/imgs/W13.png b/XixunPlayer1/app/src/main/assets/imgs/W13.png new file mode 100644 index 0000000..8577bdc Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W13.png differ diff --git a/XixunPlayer1/app/src/main/assets/imgs/W14.png b/XixunPlayer1/app/src/main/assets/imgs/W14.png new file mode 100644 index 0000000..2537239 Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W14.png differ diff --git a/XixunPlayer1/app/src/main/assets/imgs/W15.png b/XixunPlayer1/app/src/main/assets/imgs/W15.png new file mode 100644 index 0000000..4be21b3 Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W15.png differ diff --git a/XixunPlayer1/app/src/main/assets/imgs/W16.png b/XixunPlayer1/app/src/main/assets/imgs/W16.png new file mode 100644 index 0000000..0f2e015 Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W16.png differ diff --git a/XixunPlayer1/app/src/main/assets/imgs/W17.png b/XixunPlayer1/app/src/main/assets/imgs/W17.png new file mode 100644 index 0000000..04f83db Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W17.png differ diff --git a/XixunPlayer1/app/src/main/assets/imgs/W18.png b/XixunPlayer1/app/src/main/assets/imgs/W18.png new file mode 100644 index 0000000..a3aec72 Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W18.png differ diff --git a/XixunPlayer1/app/src/main/assets/imgs/W19.png b/XixunPlayer1/app/src/main/assets/imgs/W19.png new file mode 100644 index 0000000..32736e2 Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W19.png differ diff --git a/XixunPlayer1/app/src/main/assets/imgs/W2.png b/XixunPlayer1/app/src/main/assets/imgs/W2.png new file mode 100644 index 0000000..c27cea2 Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W2.png differ diff --git a/XixunPlayer1/app/src/main/assets/imgs/W20.png b/XixunPlayer1/app/src/main/assets/imgs/W20.png new file mode 100644 index 0000000..2b326a8 Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W20.png differ diff --git a/XixunPlayer1/app/src/main/assets/imgs/W29.png b/XixunPlayer1/app/src/main/assets/imgs/W29.png new file mode 100644 index 0000000..1178e2e Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W29.png differ diff --git a/XixunPlayer1/app/src/main/assets/imgs/W3.png b/XixunPlayer1/app/src/main/assets/imgs/W3.png new file mode 100644 index 0000000..10e825b Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W3.png differ diff --git a/XixunPlayer1/app/src/main/assets/imgs/W30.png b/XixunPlayer1/app/src/main/assets/imgs/W30.png new file mode 100644 index 0000000..de3615c Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W30.png differ diff --git a/XixunPlayer1/app/src/main/assets/imgs/W31.png b/XixunPlayer1/app/src/main/assets/imgs/W31.png new file mode 100644 index 0000000..b4dd147 Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W31.png differ diff --git a/XixunPlayer1/app/src/main/assets/imgs/W32.png b/XixunPlayer1/app/src/main/assets/imgs/W32.png new file mode 100644 index 0000000..581663d Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W32.png differ diff --git a/XixunPlayer1/app/src/main/assets/imgs/W33.png b/XixunPlayer1/app/src/main/assets/imgs/W33.png new file mode 100644 index 0000000..3b51e5a Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W33.png differ diff --git a/XixunPlayer1/app/src/main/assets/imgs/W34.png b/XixunPlayer1/app/src/main/assets/imgs/W34.png new file mode 100644 index 0000000..0bc4e72 Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W34.png differ diff --git a/XixunPlayer1/app/src/main/assets/imgs/W35.png b/XixunPlayer1/app/src/main/assets/imgs/W35.png new file mode 100644 index 0000000..cfa67ec Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W35.png differ diff --git a/XixunPlayer1/app/src/main/assets/imgs/W36.png b/XixunPlayer1/app/src/main/assets/imgs/W36.png new file mode 100644 index 0000000..2b326a8 Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W36.png differ diff --git a/XixunPlayer1/app/src/main/assets/imgs/W4.png b/XixunPlayer1/app/src/main/assets/imgs/W4.png new file mode 100644 index 0000000..17a05d8 Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W4.png differ diff --git a/XixunPlayer1/app/src/main/assets/imgs/W44.png b/XixunPlayer1/app/src/main/assets/imgs/W44.png new file mode 100644 index 0000000..d829389 Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W44.png differ diff --git a/XixunPlayer1/app/src/main/assets/imgs/W45.png b/XixunPlayer1/app/src/main/assets/imgs/W45.png new file mode 100644 index 0000000..9129ef8 Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W45.png differ diff --git a/XixunPlayer1/app/src/main/assets/imgs/W46.png b/XixunPlayer1/app/src/main/assets/imgs/W46.png new file mode 100644 index 0000000..9129ef8 Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W46.png differ diff --git a/XixunPlayer1/app/src/main/assets/imgs/W5.png b/XixunPlayer1/app/src/main/assets/imgs/W5.png new file mode 100644 index 0000000..5f7cbd7 Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W5.png differ diff --git a/XixunPlayer1/app/src/main/assets/imgs/W6.png b/XixunPlayer1/app/src/main/assets/imgs/W6.png new file mode 100644 index 0000000..2e1a63c Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W6.png differ diff --git a/XixunPlayer1/app/src/main/assets/imgs/W7.png b/XixunPlayer1/app/src/main/assets/imgs/W7.png new file mode 100644 index 0000000..d37cc41 Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W7.png differ diff --git a/XixunPlayer1/app/src/main/assets/imgs/W8.png b/XixunPlayer1/app/src/main/assets/imgs/W8.png new file mode 100644 index 0000000..a3394db Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W8.png differ diff --git a/XixunPlayer1/app/src/main/assets/imgs/W9.png b/XixunPlayer1/app/src/main/assets/imgs/W9.png new file mode 100644 index 0000000..8e98a1c Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/W9.png differ diff --git a/XixunPlayer1/app/src/main/assets/imgs/logo3.png b/XixunPlayer1/app/src/main/assets/imgs/logo3.png new file mode 100644 index 0000000..3b4ea20 Binary files /dev/null and b/XixunPlayer1/app/src/main/assets/imgs/logo3.png differ diff --git a/XixunPlayer1/app/src/main/java/com/xixun/command/reply/ReplyBase.java b/XixunPlayer1/app/src/main/java/com/xixun/command/reply/ReplyBase.java new file mode 100644 index 0000000..d2f446a --- /dev/null +++ b/XixunPlayer1/app/src/main/java/com/xixun/command/reply/ReplyBase.java @@ -0,0 +1,10 @@ +package com.xixun.command.reply; + +import java.io.Serializable; + + +public abstract class ReplyBase implements Serializable{ + + private static final long serialVersionUID = -3630726876519388513L; + public String commandId; +} diff --git a/XixunPlayer1/app/src/main/java/com/xixun/command/reply/TaskProgressReply.java b/XixunPlayer1/app/src/main/java/com/xixun/command/reply/TaskProgressReply.java new file mode 100644 index 0000000..54b2a64 --- /dev/null +++ b/XixunPlayer1/app/src/main/java/com/xixun/command/reply/TaskProgressReply.java @@ -0,0 +1,18 @@ +package com.xixun.command.reply; + +public class TaskProgressReply extends ReplyBase { + + private static final long serialVersionUID = 6264049742389542806L; + public int percent; + public String taskItemId; + public int speed; + public int remainingSeconds; + + public TaskProgressReply(String commandId, String taskItemId, int percent, int sp, int rs) { + this.commandId = commandId; + this.taskItemId = taskItemId; + this.percent = percent; + this.speed = sp; + this.remainingSeconds = rs; + } +} \ No newline at end of file diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/AIDLService.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/AIDLService.java new file mode 100644 index 0000000..268e434 --- /dev/null +++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/AIDLService.java @@ -0,0 +1,426 @@ +package com.xixun.xixunplayer; + +import android.annotation.SuppressLint; +import android.app.Service; +import android.content.Intent; +import android.graphics.BitmapFactory; +import android.os.IBinder; +import android.os.RemoteException; + +import com.xixun.command.reply.TaskProgressReply; +import com.xixun.util.PlayerInfo; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Base64; +import java.util.HashSet; + +import gnph.util.IOs; +import gnph.util.JSList; +import gnph.util.JSMap; +import gnph.util.URLConn; + +public class AIDLService extends Service { + + @Override + public IBinder onBind(Intent intent) { + return binder; + } + + PlayerInfo.Stub binder = new PlayerInfo.Stub() { + @Override + public String getProgramName() throws RemoteException { + try { + String name = null; + var ins = MainActivity.ins; + if(ins!=null && ! ins.avas.isEmpty()) name = ins.avas.get(ins.curAva).name; + Util.println("Server getProgramName. <-"+name); + return name; + } catch (Exception e) { + Util.printStackTrace(e); + return null; + } + } + + @Override + public String getVersion() throws RemoteException { + Util.println("Server getVersion. <-"+null); + return null; + } + + @Override + public void setScreenWidth(int width) throws RemoteException { + Util.println("Server setScreenWidth. ->"+width); + } + + @Override + public void setScreenHeight(int height) throws RemoteException { + Util.println("Server setScreenHeight. ->"+height); + + } + + @Override + public void taskScreenshot(String cmdId) throws RemoteException { + Util.println("Server taskScreenshot. ->"+cmdId); + } + + @Override + public void setExternalTemperature(float t) throws RemoteException { + + } + + @Override + public void setInternalTemperature(float t) throws RemoteException { + + } + + @Override + public void setHumidity(float h) throws RemoteException { + + } + + @Override + public boolean forcePlayProgram(String pid) throws RemoteException { + return false; + } + + @Override + public boolean finishForcePlay() throws RemoteException { + return false; + } + + @Override + public String getCurProgramId() throws RemoteException { + Util.println("Server getCurProgramId ..."); + return null; + } + + @Override + public void setUSBProgramPwd(String pwd) throws RemoteException { + + } + + @SuppressLint("ResourceType") + @Override + public String executeJosnCommand(String jsonstr) throws RemoteException { + Util.println("AIDL executeJsonCommand ..."+jsonstr);//{"_type":"DeleteTask","id":"652522a0e81d1e000009201a","sendTo":"yzd-player"} + String commandId = null; + try { + var jsonBytes = jsonstr.getBytes(StandardCharsets.UTF_8); + var json = JSMap.from(jsonBytes); + var _type = json.stnn("_type"); + commandId = json.stnn("id"); + if(_type.equals("PlayXixunTask") || _type.equals("PlayProgramTask")) { + var preDownloadURL = json.str("preDownloadURL"); + if(preDownloadURL==null) { + Util.println(" serverURL "+Util.serverURL); + preDownloadURL = Util.serverURL+"file/download?id="; + } + var task = json.jsmap("task"); + JSList jpages = task.jslist("items"); + int proSize = 0; + var needDowns = new ArrayList(); + var hases = new HashSet(); + for(var pageMap : jpages) { + var _program = pageMap.jsmap("_program"); + if(_program==null) continue; + proSize += _program.intg("totalSize"); + var needDown = new NeedDowns(); + needDown.prog = pageMap.stnn("_id"); + needDowns.add(needDown); + JSList layers = _program.jslist("layers"); + if(layers==null || layers.isEmpty()) continue; + for(int ll=layers.size()-1; ll>=0; ll--) { + var layer = new Prog.Layer(); + JSList sources = layers.get(ll).jslist("sources"); +// var border = layers.get(ll).jsmap("border"); +// if(border!=null) { +// } + for(var source : sources) { + var type = source.stnn("_type"); + if(type.equals("Video") || type.equals("Audio") || type.equals("Image") || type.startsWith("DigitalClock") || type.equals("Timer") || type.startsWith("Environ")) { + var filename = source.str("id"); + if(filename==null) continue; + var url = source.stnn("url"); + if(! url.startsWith("http")) url = preDownloadURL + filename; + var file = new File(Util.programDir+"/"+filename); + if(file.exists() && file.length() > 0) { + proSize -= file.length(); + hases.add(filename); + } else needDown.srcs.add(new Src(filename, url)); + } else if(type.startsWith("MultiPng") || type.equals("SplitText")) { + JSList imgs = source.jslist("arrayPics"); + if(imgs.isEmpty()) continue; + for(var img : imgs) { + var filename = img.str("id"); + if(filename==null) continue; + var url = img.stnn("url"); + if(! url.startsWith("http")) url = preDownloadURL + filename; + var file = new File(Util.programDir+"/"+filename); + if(file.exists() && file.length() > 0) { + proSize -= file.length(); + hases.add(filename); + } else needDown.srcs.add(new Src(filename, url)); + } + } + } + } + } + int finalProSize = proSize; + String finalCommandId = commandId; + var notificationURL = json.str("notificationURL"); + var downMs = System.currentTimeMillis(); + Util.downId = downMs; + new Thread(()->{ + Util.deleteFiles(finalProSize, hases); + for(var needDown : needDowns) { + int cnt = 0; + for(var src : needDown.srcs) { + for(int t=0; ; t++) { + try { + Util.println("download "+src.url); + var conn = new URLConn(src.url); + if(t>0) { + var len = new File(Util.programDir+"/"+src.filename).length(); + conn.setHeader("Range", "bytes="+len+"-"); + Util.println(" Range "+len); + } + var input = conn.in(); + var code = conn.hconn().getResponseCode(); + var fout = new FileOutputStream(Util.programDir+"/"+src.filename, code==206); + IOs.writeCloseIn(fout, input); + fout.flush(); + fout.getFD().sync(); + fout.close(); + if(code==200 || code==206) break; + else Util.println(" error "+code+" "+conn.hconn().getResponseMessage()+" size: "+new File(src.filename).length()+" "+src.filename); + } catch (Throwable e) { + Util.println(Util.toStr(e)); + } + if(t>=720 || Util.downId!=downMs) return; + try { + Thread.sleep(5000); + } catch (Exception ignored) {} + } + + cnt++; + var progress = cnt*100/needDown.srcs.size(); + if(cnt != needDown.srcs.size()) { + if(notificationURL==null) { + var intent = new Intent("xixun.intent.action.REPLY"); + intent.putExtra("reply", new TaskProgressReply(finalCommandId, needDown.prog, progress, 500, 3)); + sendBroadcast(intent); + } else { + try { + new URLConn(notificationURL).timeout(5000).writeJson(new JSMap( + "commandId", finalCommandId, + "taskItemId", needDown.prog, + "progress", progress).toStr()).read(); + } catch (Throwable e) { + Util.printStackTrace(e); + } + } + } + } + if(notificationURL==null) { + var intent = new Intent("xixun.intent.action.REPLY"); + intent.putExtra("reply", new TaskProgressReply(finalCommandId, needDown.prog, 100, 400, 0)); + sendBroadcast(intent); + } else { + try { + var res = new URLConn(notificationURL).timeout(5000).writeJson(new JSMap( + "commandId", finalCommandId, + "taskItemId", needDown.prog, + "progress", 100).toStr()).read(); + Util.println("progress "+100+" "+res); + } catch (Throwable e) { + Util.printStackTrace(e); + } + } + } + if(Util.downId!=downMs) return; + var acti = MainActivity.ins; + if(acti!=null) acti.runOnUiThread(() -> acti.initProg(jsonBytes)); + else { + try { + var fOut = new FileOutputStream(Util.programDir + "/program"); + fOut.write(jsonBytes); + fOut.flush(); + fOut.getFD().sync(); + fOut.close(); + } catch (Throwable e) { + Util.printStackTrace(e); + } + var intent = new Intent(AIDLService.this, MainActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + AIDLService.this.startActivity(intent); + } + var intent = new Intent("com.xixun.AccessibilityService"); + intent.putExtra("newProgram", "platform"); + sendBroadcast(intent); + }).start(); + return new JSMap( + "_type", "Success", + "result", "received command", + "cardId", Util.getCardId(), + "commandId", commandId + ).toString(); + } else if(_type.equals("DeleteTask")) { + Util.downId = 0; + MainActivity.ins.runOnUiThread(() -> MainActivity.ins.delProgFile()); + return new JSMap( + "_type", "DataCallback", + "result", "received command", + "cardId", Util.getCardId(), + "commandId", commandId + ).toString(); + } else if(_type.equals("PlayerStateCommand")) { + var descs = Util.getState(MainActivity.ins.state); + return new JSMap( + "_type", "Success", + "cardId", Util.getCardId(), + "commandId", commandId, + "code", MainActivity.ins.state, + "des_en", descs[0], + "des", descs[1] + ).toString(); + } else if(_type.equals("SetPlayerBackground")) { + var url = json.str("url"); + if(url==null) new File(Util.backImgFile).delete(); + else { + var fout = new FileOutputStream(Util.backImgFile); + IOs.write(fout, new URLConn(url).in()); + fout.flush(); + fout.getFD().sync(); + fout.close(); + } + MainActivity.ins.runOnUiThread(() -> { + MainActivity.ins.backView.cosImg = url==null ? null : BitmapFactory.decodeFile(Util.backImgFile, Util.noScaled); + MainActivity.ins.backView.invalidate(); + }); + return new JSMap( + "_type", "Success", + "cardId", Util.getCardId(), + "commandId", commandId + ).toString(); + } else if(_type.equals("GetPlayerBackground")) { + var backImg = new File(Util.backImgFile); + var img = Base64.getEncoder().encodeToString(IOs.readBytesClose(backImg.exists() ? new FileInputStream(backImg) : MainActivity.ins.getResources().openRawResource(R.drawable.back))); + return new JSMap( + "_type", "DataCallback", + "cardId", Util.getCardId(), + "commandId", commandId, + "img", img + ).toString(); + } else if(_type.equalsIgnoreCase("getProgramTask")) { + var task = JSMap.fromClose(new FileInputStream(Util.programDir+"/program")).jsmap("task"); + return new JSMap( + "_type", "ProgramTaskCallback", + "cardId", Util.getCardId(), + "commandId", commandId, + "task", task + ).toString(); + } else if(_type.equalsIgnoreCase("UploadPlayLogs")) { + var url = json.str("url"); + Util.logOn = url!=null; + Util.cfg.put("logUploadUrl", url); + Util.cfg.put("logUploadInterval", json.intg("interval", 60)); + try (var fOut = new FileOutputStream(Util.programDir + "/cfg")) { + Util.cfg.write(fOut); + fOut.flush(); + fOut.getFD().sync(); + } + return new JSMap( + "_type", "Success", + "cardId", Util.getCardId(), + "commandId", commandId + ).toString(); + } + return new JSMap( + "_type", "Error", + "errorMessage", "Unknown Type: "+_type, + "cardId", Util.getCardId(), + "commandId", commandId + ).toString(); + } catch (Exception e) { + Util.printStackTrace(e); + return new JSMap( + "_type", "Error", + "errorMessage", Util.toStr(e), + "cardId", Util.getCardId(), + "commandId", commandId + ).toString(); + } + } + + @Override + public void pausePlayer(boolean b) throws RemoteException { + + } + + @Override + public boolean isPause() throws RemoteException { + return false; + } + + @Override + public boolean clearTasks() throws RemoteException { + MainActivity.ins.runOnUiThread(() -> MainActivity.ins.delProgFile()); + return true; + } + + @Override + public int countOfPrograms(int type) throws RemoteException { + var cnt = MainActivity.ins!=null && MainActivity.ins.progView!=null ? MainActivity.ins.progView.pages.size() : 0; + Util.println("Server countOfPrograms. <-"+cnt); + return cnt; + } + + @Override + public void playInsertTask(String pid) throws RemoteException { + + } + + @Override + public void stopInsertTask(String pid) throws RemoteException { + + } + + @Override + public String getProgramTask() throws RemoteException { + try { + return IOs.readStrClose(new FileInputStream(Util.programDir+"/program")); + } catch (Exception e) { + Util.printStackTrace(e); + return Util.toStr(e); + } + } + + @Override + public void setUploadLogUrl(String playLog) throws RemoteException { + + } + + @Override + public String getUploadLogUrl() throws RemoteException { + return null; + } + }; + + static class Src { + String filename; + String url; + + public Src(String filename, String url) { + this.filename = filename; + this.url = url; + } + } + static class NeedDowns { + String prog; + ArrayList srcs = new ArrayList<>(); + } +} \ No newline at end of file diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/AbsLayout.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/AbsLayout.java new file mode 100644 index 0000000..b00610b --- /dev/null +++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/AbsLayout.java @@ -0,0 +1,107 @@ +package com.xixun.xixunplayer; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; + +public class AbsLayout extends ViewGroup { + + public AbsLayout(Context context) { + this(context, null); + } + + public AbsLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public AbsLayout(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public AbsLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int count = getChildCount(); + int maxHeight = 0; + int maxWidth = 0; + measureChildren(widthMeasureSpec, heightMeasureSpec); + for(int i = 0; i < count; i++) { + View child = getChildAt(i); + if(child.getVisibility() != GONE) { + int childRight; + int childBottom; + var lp = (LayoutParams) child.getLayoutParams(); + childRight = lp.x + child.getMeasuredWidth(); + childBottom = lp.y + child.getMeasuredHeight(); + maxWidth = Math.max(maxWidth, childRight); + maxHeight = Math.max(maxHeight, childBottom); + } + } + maxWidth += getPaddingLeft() + getPaddingRight(); + maxHeight += getPaddingTop() + getPaddingBottom(); + maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); + maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); + setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, 0), resolveSizeAndState(maxHeight, heightMeasureSpec, 0)); + } + + @Override + protected ViewGroup.LayoutParams generateDefaultLayoutParams() { + return new LayoutParams(0, 0, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + int count = getChildCount(); + for (int i = 0; i < count; i++) { + View child = getChildAt(i); + if(child.getVisibility() != GONE) { + var lp = (LayoutParams) child.getLayoutParams(); + int childLeft = getPaddingLeft() + lp.x; + int childTop = getPaddingTop() + lp.y; + child.layout(childLeft, childTop, childLeft + child.getMeasuredWidth(), childTop + child.getMeasuredHeight()); + } + } + } + + @Override + public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { + return new LayoutParams(getContext(), attrs); + } + + @Override + protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { + return p instanceof LayoutParams; + } + + @Override + protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { + return new LayoutParams(p); + } + + @Override + public boolean shouldDelayChildPressedState() { + return false; + } + + public static class LayoutParams extends ViewGroup.LayoutParams { + + public int x; + public int y; + + public LayoutParams(int x, int y, int width, int height) { + super(width, height); + this.x = x; + this.y = y; + } + public LayoutParams(Context c, AttributeSet attrs) { + super(c, attrs); + } + public LayoutParams(ViewGroup.LayoutParams source) { + super(source); + } + } +} diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/BackView.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/BackView.java new file mode 100644 index 0000000..e7b42a2 --- /dev/null +++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/BackView.java @@ -0,0 +1,31 @@ +package com.xixun.xixunplayer; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.view.View; + +public class BackView extends View { + + private Bitmap img; + Bitmap cosImg; + Rect rect = new Rect(); + + public BackView(Context context) { + super(context); + img = BitmapFactory.decodeResource(context.getResources(), R.drawable.back, Util.noScaled); + cosImg = BitmapFactory.decodeFile(Util.backImgFile, Util.noScaled); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + rect.right = Util.screenWidth; + rect.bottom = Util.screenHeight; + canvas.clipRect(rect); + if(cosImg!=null) canvas.drawBitmap(cosImg, null, rect, null); + else if(img!=null) for(int y=0; y> "+intent.getAction()); + Util.initDir(context); + if(MainActivity.ins!=null) return; + var inten = new Intent(context, MainActivity.class); + inten.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(inten); + } +} \ No newline at end of file diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/IntentReceiver.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/IntentReceiver.java new file mode 100644 index 0000000..d8401df --- /dev/null +++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/IntentReceiver.java @@ -0,0 +1,7 @@ +package com.xixun.xixunplayer; + +import android.content.Intent; + +public interface IntentReceiver { + void onReceive(Intent intent); +} diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/LogThread.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/LogThread.java new file mode 100644 index 0000000..5404dde --- /dev/null +++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/LogThread.java @@ -0,0 +1,98 @@ +package com.xixun.xixunplayer; + +import android.content.ContentValues; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.concurrent.ConcurrentLinkedQueue; + +import gnph.util.Digest; +import gnph.util.JSList; +import gnph.util.JSMap; +import gnph.util.URLConn; + +public class LogThread extends Thread { + + public static ConcurrentLinkedQueue rows = new ConcurrentLinkedQueue<>(); + + Digest md5 = Digest.md5(); + + public LogThread() { + + } + + @Override + public void run() { + long next = 0, nextDel = 0; + while(true) { + try { + Thread.sleep(500); + } catch (InterruptedException ignored) { + } + if(! Util.logOn) continue; + var ms = System.currentTimeMillis(); + var row = rows.poll(); + if(row!=null) { + try (var db = Util.openHelper.getWritableDatabase()) { + db.beginTransaction(); + if(ms>=nextDel) { + nextDel = (ms - nextDel < 3000 ? nextDel : ms) + 3600000L; + var num = db.delete("log", "time<"+(ms-720*3600000L), null); + Util.println(" del overdue "+num); + } + var rowid = db.insert("log", null, row); + db.setTransactionSuccessful(); + db.endTransaction(); + Util.println(" rowid "+rowid); + } catch (Exception e) { + Util.printStackTrace(e); + } + } + if(ms>=next) { + next = (ms-next < 3000 ? next : ms) + Util.cfg.intg("logUploadInterval", 60)*1000; + var rows = new JSList(); + try (var db = Util.openHelper.getReadableDatabase()) { + var cursor = db.rawQuery("select * from log order by time", null); + while(cursor.moveToNext()) { + var pid = cursor.getString(2); + if(pid==null || pid.length()!=24) pid = md5.hexStr(cursor.getString(3).getBytes()).substring(0,24); + rows.add(new JSMap( + "beginAt", cursor.getLong(0), + "duration", cursor.getLong(1)/1000, + "pid", pid, + "name", cursor.getString(3), + "type", cursor.getString(4), + "lat", cursor.getDouble(5), + "lng", cursor.getDouble(6) + )); + } + } + Util.println(" Log Cnt "+rows.size()); + if(! rows.isEmpty()) { + Util.println(rows.last().toStr()); + try { + var conn = new URLConn(Util.cfg.stnn("logUploadUrl")).timeout(15000); + conn.addHeader("Card-Id", Util.getCardId()); + conn.forJson(); + rows.write(conn.out()); + var resp = conn.read(); + Util.println(" Upload resp: "+resp); + if("OK".equals(resp) || "".equals(resp)) { + try (var db = Util.openHelper.getWritableDatabase()) { + db.beginTransaction(); + var num = db.delete("log", "time<="+rows.last().lng("beginAt"), null); + db.setTransactionSuccessful(); + db.endTransaction(); + Util.println(" del Upled "+num); + } catch (Exception e) { + Util.printStackTrace(e); + } + } + } catch (Exception e) { + Util.printStackTrace(e); + } + } + } + } + } +} diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/MainActivity.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/MainActivity.java new file mode 100644 index 0000000..3bcd70c --- /dev/null +++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/MainActivity.java @@ -0,0 +1,731 @@ +package com.xixun.xixunplayer; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.ServiceConnection; +import android.content.pm.PackageManager; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.os.Build; +import android.os.Bundle; +import android.os.IBinder; +import android.os.StrictMode; +import android.view.Choreographer; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; + +import com.xixun.joey.aidlset.CardService; +import com.xixun.xy.conn.aidl.ConnService; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashSet; + +import gnph.util.Chsets; +import gnph.util.JSList; +import gnph.util.JSMap; + +public class MainActivity extends Activity implements Choreographer.FrameCallback { + + public static MainActivity ins; + ArrayList reces = new ArrayList<>(); + ArrayList services = new ArrayList<>(); + public Intent environIntent = new Intent(); + HashSet environs = new HashSet<>(); + BackView backView; + Prog progView, insView; + long launchMilli = System.currentTimeMillis(); + long syncMs; + int state; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + ins = this; + Util.println("==>> MainActivity onCreate >>>> this "+hashCode()+" Thread: "+Thread.currentThread().getId()); + + StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitAll().build()); + setContentView(backView = new BackView(MainActivity.this)); + state = 5; + + hookWebView(); + + if(ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) init(); + else { + Util.println("---- No permission, Try again ..."); + Util.makeText(this, "No permission, Try again ...").show(); + ActivityCompat.requestPermissions(this, new String[]{ + android.Manifest.permission.WRITE_EXTERNAL_STORAGE, + android.Manifest.permission.READ_EXTERNAL_STORAGE, + android.Manifest.permission.MANAGE_EXTERNAL_STORAGE, + android.Manifest.permission.RECEIVE_BOOT_COMPLETED, + android.Manifest.permission.INTERNET + }, 999); + } + } + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if(Util.programDir!=null) return; + if(requestCode==999 && grantResults.length > 0 && grantResults[0]==PackageManager.PERMISSION_GRANTED) init(); + else { + Util.println("---- Request Permission Failed"); + Util.makeText(this, "Request Permission Failed").show(); + } + } + @SuppressLint("SoonBlockedPrivateApi") + public static void hookWebView() { + if(android.os.Process.myUid() != android.os.Process.SYSTEM_UID) return; + try { + var factoryClass = Class.forName("android.webkit.WebViewFactory"); + var field = factoryClass.getDeclaredField("sProviderInstance"); + field.setAccessible(true); + var sProviderInstance = field.get(null); + if (sProviderInstance != null) { + Util.println("sProviderInstance isn't null"); + return; + } + Method getProviderClassMethod; + if (Build.VERSION.SDK_INT > 22) { // above 22 + getProviderClassMethod = factoryClass.getDeclaredMethod("getProviderClass"); + } else if (Build.VERSION.SDK_INT == 22) { // method name is a little different + getProviderClassMethod = factoryClass.getDeclaredMethod("getFactoryClass"); + } else { // no security check below 22 + Util.println("Don't need to Hook WebView"); + return; + } + getProviderClassMethod.setAccessible(true); + var providerClass = (Class) getProviderClassMethod.invoke(factoryClass); + var delegateClass = Class.forName("android.webkit.WebViewDelegate"); + var declaredConstructor = delegateClass.getDeclaredConstructor(); + declaredConstructor.setAccessible(true); + sProviderInstance = providerClass.getDeclaredMethod("create", delegateClass).invoke(providerClass, declaredConstructor.newInstance()); + field.set("sProviderInstance", sProviderInstance); + Util.println("sProviderInstance"+sProviderInstance.toString()); + Util.println("Hook Done!"); + } catch (Throwable e) { + Util.printStackTrace(e); + } + } + @Override + protected void onDestroy() { + super.onDestroy(); + Util.println("==<< MainActivity onDestroy <<<< this "+hashCode()); + state = 8; + if(insView!=null) { + insView.release(); + insView = null; + } + if(progView!=null) { + progView.release(); + progView = null; + } + ins = null; + for(var rece : reces) unregisterReceiver(rece); + for(var service : services) unbindService(service); + try (var fout = new FileOutputStream(Util.programDir+"/cfg")){ + Util.cfg.write(fout); + fout.flush(); + fout.getFD().sync(); + } catch (Exception ignored) { + } + System.gc(); + } + + @Override + protected void onStart() { + super.onStart(); + Util.println(" ==>> MainActivity onStart >> "+hashCode()); + } + @Override + protected void onStop() { + super.onStop(); + Util.println(" ==<< MainActivity onStop << "+hashCode()); + } + @Override + protected void onRestart() { + super.onRestart(); + Util.println(" ==>> MainActivity onRestart >> "+hashCode()); + } + + @Override + protected void onResume() { + super.onResume(); + Util.println(" ==>> MainActivity onResume >> "+hashCode()); + } + + @Override + protected void onPause() { + super.onPause(); + Util.println(" ==<< MainActivity onPause << "+hashCode()); + } + + CardService serviCard; + ConnService serviXy; + Intent intenCard; + ServiceConnection connCard; + @SuppressLint("UnspecifiedRegisterReceiverFlag") + public void init() { + Util.initDir(this); + if(MainService.ins==null) startService(new Intent(this, MainService.class)); + + var cfg = new File(Util.programDir+"/cfg"); + if(! cfg.isFile()) Util.cfg = new JSMap(); + else { + try { + Util.cfg = JSMap.fromClose(new FileInputStream(cfg)); + } catch (Exception e) { + Util.cfg = new JSMap(); + Util.makeText(MainActivity.this, Util.toStr(e)).show(); + Util.printStackTrace(e); + } + } + Util.println(Util.cfg.toStr()); + Util.logOn = Util.cfg.get("logUploadUrl")!=null; + Util.openHelper = new SQLiteOpenHelper(this, "aaa", null, 1) { + @Override + public void onCreate(SQLiteDatabase db) { + db.execSQL("create table cfg(k varchar(32) primary key, v varchar(128))"); + db.execSQL("create table log(time integer primary key, dur integer, id varchar(64), name varchar(64), type varchar(16), lat double, lng double)"); + } + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + } + }; + new LogThread().start(); + if(Util.logOn) { + var db = Util.openHelper.getWritableDatabase(); + var cursor = db.rawQuery("select count(*) aaa from log", null); + while(cursor.moveToNext()) Util.println("Log Cnt "+cursor.getString(0)); + db.close(); + } + connCard = new ServiceConnection() { + public void onServiceDisconnected(ComponentName name) { + serviCard = null; + Util.println("<-<- AIDL Service cardsystem Disconnected"); + } + + public void onServiceConnected(ComponentName name, IBinder iBinder) { + unbindService(this); + services.remove(this); + Util.println("->-> AIDL Service cardsystem Connected"); + serviCard = CardService.Stub.asInterface(iBinder); + try { + Util.isScreenOn = serviCard.isScreenOpen(); + Util.screenWidth = serviCard.getScreenWidth(); + Util.screenHeight = serviCard.getScreenHeight(); + Util.println(" IsScreenOn: "+Util.isScreenOn+" screen: "+Util.screenWidth+" x "+Util.screenHeight); + backView.invalidate(); + if(Util.isScreenOn) initProg(); + else state = 8; + } catch (Exception e) { + Util.makeText(MainActivity.this, Util.toStr(e)).show(); + Util.printStackTrace(e); + } + } + }; + intenCard = new Intent("com.xixun.joey.aidlset.SettingsService"); + intenCard.setPackage("com.xixun.joey.cardsystem"); + bindService(intenCard, connCard, Context.BIND_AUTO_CREATE); + services.add(connCard); + + var connXy = new ServiceConnection() { + public void onServiceDisconnected(ComponentName name) { + serviXy = null; + Util.println("<-<- AIDL Service xy.conn Disconnected"); + } + public void onServiceConnected(ComponentName name, IBinder iBinder) { + unbindService(this); + services.remove(this); + Util.println("->-> AIDL Service xy.conn Connected"); + serviXy = ConnService.Stub.asInterface(iBinder); + try { + Util.serverURL = serviXy.getServerURL(); + Util.println(" ServerURL: "+Util.serverURL); + if(Util.serverURL==null || Util.serverURL.isEmpty()) Util.serverURL = "https://m2mled.net/"; + else { + if(! Util.serverURL.startsWith("http")) Util.serverURL = "http://"+Util.serverURL; + if(! Util.serverURL.endsWith("/")) Util.serverURL += "/"; + } + } catch (Exception e) { + Util.makeText(MainActivity.this, Util.toStr(e)).show(); + Util.printStackTrace(e); + } + } + }; + var intenXy = new Intent("xixun.intent.action.CONNECTION_INFO"); + intenXy.setPackage("com.xixun.xy.conn"); + bindService(intenXy, connXy, Context.BIND_AUTO_CREATE); + services.add(connXy); + + reces.clear(); + BroadcastReceiver rece; + registerReceiver(rece = new BroadcastReceiver(){ + @Override + public void onReceive(Context context, Intent intent) { + Util.println("Receive PAUSE_PLAYER"); + Util.isScreenOn = ! intent.getBooleanExtra("pause", false); + Util.println(" IsScreenOn: "+Util.isScreenOn); + if(! Util.isScreenOn) { + state = 8; + if(insView!=null) { + insView.release(); + insView = null; + } + if(progView!=null) { + progView.release(); + progView = null; + } + setContentView(backView); + } else if(progView==null && insView==null) MainActivity.ins.runOnUiThread(() -> initProg()); + System.gc(); + } + }, new IntentFilter("com.xixun.action.PAUSE_PLAYER")); + reces.add(rece); + + registerReceiver(rece = new BroadcastReceiver(){ + @Override + public void onReceive(Context context, Intent intent) { + Util.println("Receive CHANGE_COMPANYID"); + try { + if(serviXy!=null && serviXy.asBinder().isBinderAlive()) { + Util.serverURL = serviXy.getServerURL(); + Util.println(" ServerURL: "+Util.serverURL); + if(Util.serverURL==null || Util.serverURL.isEmpty()) Util.serverURL = "https://m2mled.net/"; + else { + if(! Util.serverURL.startsWith("http")) Util.serverURL = "http://"+Util.serverURL; + if(! Util.serverURL.endsWith("/")) Util.serverURL += "/"; + } + } else { + Util.println(" bindService"); + bindService(intenXy, connXy, Context.BIND_AUTO_CREATE); + } + } catch (Exception e) { + Util.makeText(MainActivity.this, Util.toStr(e)).show(); + Util.printStackTrace(e); + } + } + }, new IntentFilter("com.xixun.joey.CHANGE_COMPANYID")); + reces.add(rece); + + registerReceiver(rece = new BroadcastReceiver(){ + @Override + public void onReceive(Context context, Intent intent) { + MainActivity.this.environIntent = intent; + for(var environ : environs) { + environ.onReceive(intent); + ((View)environ).invalidate(); + } + } + }, new IntentFilter("xixun.intent.action.TEMPERATURE_HUMIDITY")); + reces.add(rece); + + registerReceiver(rece = new BroadcastReceiver(){ + @Override + public void onReceive(Context context, Intent intent) { + Util.lat = intent.getDoubleExtra("latitude", 0); + Util.lng = intent.getDoubleExtra("longitude", 0); + Util.println(" lat "+Util.lat+" lng "+Util.lng); + } + }, new IntentFilter("com.xixun.joey.gpsinfo")); + reces.add(rece); + + registerReceiver(rece = new BroadcastReceiver(){ + @Override + public void onReceive(Context context, Intent intent) { + try { + var code = intent.getIntExtra("code", 0); + Util.println(" remote_control "+code); + if(progView == null || progView.pages.isEmpty() || ! progView.isShown() || code==-1 || code < -3 || code > progView.pages.size()) return; + if(code<=-2) { + var idx = avas.isEmpty() ? -1 : progView.pages.indexOf(avas.get(curAva)); + if(code==-2) code = idx<=0 ? progView.pages.size() : idx; + else code = idx>=progView.pages.size()-1 ? 1 : idx+2; + } + var ms = System.currentTimeMillis(); + if(! avas.isEmpty()) { + avas.get(curAva).hide(); + if(code > 0 && avas.get(curAva)==progView.pages.get(code-1)) for(var call : progView.calls) call.doFrame(ms); + } + if(code==0) syncProg(ms, 0); + else { + avas.clear(); + var page = progView.pages.get(code-1); + avas.add(page); + curAva = 0; + curTimes = 1; + waitTo = 0; //点播 + page.setMillis(ms, ms); + if(state != 6) { + setContentView(progView); + state = 6; + } + } + var root = JSMap.fromClose(new BufferedInputStream(new FileInputStream(Util.programDir + "/program"))); + root.put("Demand", code); + try (var fOut = new FileOutputStream(Util.programDir + "/program")) { + root.write(fOut); + fOut.flush(); + fOut.getFD().sync(); + } + } catch (Throwable e) { + Util.makeText(MainActivity.this, Util.toStr(e)).show(); + Util.printStackTrace(e); + } + } + }, new IntentFilter("com.xixun.yzd.REMOTE_CONTROL")); + reces.add(rece); + + if(Util.custom==Util.Custom.Yishi) { + new Thread(()->{ + while(true) { + try { + Thread.sleep(10000); + if(serviCard!=null && serviCard.asBinder().isBinderAlive()) { + var www = serviCard.getScreenWidth(); + var hhh = serviCard.getScreenHeight(); + if(www!=Util.screenWidth || hhh!=Util.screenHeight) { + var ins = MainActivity.ins; + if(ins!=null) ins.runOnUiThread(() -> { + Util.screenWidth = www; + Util.screenHeight = hhh; + ins.backView.invalidate(); + }); + } + } else { + Util.println(" bindService"); + var ins = MainActivity.ins; + if(ins!=null) ins.runOnUiThread(() -> ins.bindService(ins.intenCard, ins.connCard, Context.BIND_AUTO_CREATE)); + } + } catch (Throwable e) { + var ins = MainActivity.ins; + if(ins!=null) ins.runOnUiThread(() -> Util.makeText(ins, Util.toStr(e)).show()); + Util.printStackTrace(e); + } + } + }).start(); + } + } + + public void stopProg() { + avas.clear(); + curAva = 0; + curTimes = 1; + if(insView!=null) { + insView.release(); + insView = null; + } + if(progView!=null) { + progView.release(); + progView = null; + } + setContentView(backView); + System.gc(); + } + public boolean delProgFile() { + stopProg(); + var files = new File(Util.programDir).listFiles(); + var ok = true; + if(files != null) for(var file : files) if(! file.delete()) ok = false; + state = 4; + try { + var out = new FileOutputStream(Util.programDir+"/program"); + out.write("{}".getBytes()); + out.flush(); + out.getFD().sync(); + out.close(); + } catch (Throwable ignored) { + } + return ok; + } + public void initProg() { + state = 1; + try { + Util.println("\nParse Insert Prog Json"); + var root = JSMap.fromClose(new BufferedInputStream(new FileInputStream(Util.programDir + "/insert"))); + var task = root.jsmap("task"); + if(task==null && root.containsKey("layers")) task = new JSMap("items", new JSList<>(new JSMap("_program", root))); + if(task!=null) { + var view = new Prog(task, this); + if(view.getChildCount()!=0) setContentView(insView = view); + } + } catch(FileNotFoundException ignored) { + } catch(Throwable e) { + state = 7; + Util.printStackTrace(e); + } + var demand = 0; + try { + Util.println("\nParse Prog Json"); + var root = JSMap.fromClose(new BufferedInputStream(new FileInputStream(Util.programDir + "/program"))); + var task = root.jsmap("task"); + demand = root.intg("Demand"); + if(task==null && root.containsKey("layers")) task = new JSMap("items", new JSList<>(new JSMap("_program", root))); + if(task==null) Util.println(root.isEmpty() ? " Empty program JSON\n" : " Error: task==null\n"); + else { + var view = new Prog(task, this); + if(view.getChildCount()==0) Util.println(" Error: ChildCount==0\n"); + else setContentView(progView = view); + } + } catch(FileNotFoundException e) { + Util.println(""+e); + } catch(Throwable e) { + state = 7; + Util.makeText(this, Util.toStr(e)).show(); + Util.printStackTrace(e); + } + if(insView!=null || progView!=null) { + state = 5; + Util.println("Init Sync"); + var ms = System.currentTimeMillis(); + if(demand==0 || progView==null) syncProg((ms+999)/1000*1000, 0); + else { + avas.clear(); + var page = progView.pages.get(demand-1); + avas.add(page); + curAva = 0; + curTimes = 1; + waitTo = 0; //点播 + page.setMillis(ms, ms); + if(state != 6) { + setContentView(progView); + state = 6; + } + } + if(canAdd) { + choreographer.postFrameCallback(this); + canAdd = false; + } + } else if(state != 7) state = 3; + } + public void initProg(byte[] json) { + try { + Util.println("\nParse Prog Json"); + var root = JSMap.from(json); + var task = root.jsmap("task"); + if(task==null) { + if(! root.containsKey("layers")) { + state = 7; + Util.println(" Error: task==null\n"); + Util.println(new String(json, Chsets.UTF8)); + return; + } + task = new JSMap("items", new JSList<>(new JSMap("_program", root))); + } + var view = new Prog(task, this); + if(view.getChildCount()==0) { + if(! view.isInsert) state = 7; + Util.println(" Error: ChildCount==0\n"); + Util.println(new String(json, Chsets.UTF8)); + return; + } + if(Util.cfg.bool("needCheck")) { + new Thread(()->{ + if(Util.check(view)) runOnUiThread(() -> { + afterCheck(view, json); + }); + else runOnUiThread(() -> { + view.release(); + Util.makeText(this, "Contents are not passed").show(); + Util.println("Contents are not passed"); + }); + }).start(); + } else afterCheck(view, json); + } catch (Throwable e) { + state = 7; + Util.makeText(this, Util.toStr(e)).show(); + Util.printStackTrace(e); + Util.println(new String(json, Chsets.UTF8)); + } + } + public void afterCheck(Prog view, byte[] json) { + try { + if(view.isInsert) { + if(insView!=null) insView.release(); + insView = view; + try { avas.get(curAva).hide();} catch (Throwable ignored) {} + setContentView(insView); + } else { + if(progView!=null) progView.release(); + progView = view; + setContentView(progView); + } + var fOut = new FileOutputStream(Util.programDir + (view.isInsert?"/insert":"/program")); + fOut.write(json); + fOut.flush(); + fOut.getFD().sync(); + fOut.close(); + state = 5; + System.gc(); + Util.println("Init Sync"); + syncProg((System.currentTimeMillis()+999)/1000*1000, 0); + if(canAdd) { + choreographer.postFrameCallback(this); + canAdd = false; + } + } catch (Throwable e) { + state = 7; + Util.makeText(this, Util.toStr(e)).show(); + Util.printStackTrace(e); + Util.println(new String(json, Chsets.UTF8)); + } + } + + ArrayList avas = new ArrayList<>(); + int curAva, curTimes = 1; + long waitTo = Long.MAX_VALUE; + boolean isInsert; + + Choreographer choreographer = Choreographer.getInstance(); + boolean canAdd = true; + + @Override + public void doFrame(long frameTimeNanos) { + if(progView == null && insView==null) { + canAdd = true; + return; + } + choreographer.postFrameCallback(this); + canAdd = false; + var milli = System.currentTimeMillis(); + if(avas.isEmpty()) { + if(milli >= waitTo) { + waitTo = Long.MAX_VALUE; + Util.println("wait sync"); + syncProg(milli, 0); + if(insView!=null) for(var call : insView.calls) call.doFrame(milli); + if(progView!=null) for(var call : progView.calls) call.doFrame(milli); + } + return; + } + var lastPage = avas.get(curAva); + if(milli < lastPage.endMilli) lastPage.showHideSrcs(milli); + else { + lastPage.hide(); + if(isInsert) { + if(--lastPage.repeatTimes<=0 && ++curAva >= avas.size()) { + var isDiff = milli-lastPage.endMilli>=1000; + syncProg(isDiff ? milli : lastPage.endMilli, milli); + if(insView!=null) for(var call : insView.calls) call.doFrame(milli); + if(progView!=null) for(var call : progView.calls) call.doFrame(milli); + return; + } + } else { + if(waitTo > 0) { //waitTo==0 为点播,不换页 + if(curTimes < lastPage.repeatTimes) curTimes++; + else { + curTimes = 1; + if(++curAva >= avas.size()) { + var isDiff = milli-lastPage.endMilli>=1000; + syncProg(isDiff ? milli : lastPage.endMilli, milli); + if(insView!=null) for(var call : insView.calls) call.doFrame(milli); + if(progView!=null) for(var call : progView.calls) call.doFrame(milli); + return; + } + } + } else if(milli-lastPage.endMilli >= 5000) { + Util.println("System Time Changed: "+Util.dateFmt.format(lastPage.endMilli)+" -> "+Util.dateFmt.format(milli)); + lastPage.endMilli = milli; + } + } + avas.get(curAva).setMillis(lastPage.endMilli, milli); + Util.println("curAva: "+curAva+" endMs: "+avas.get(curAva).endMilli); + } + if(isInsert) for(var call : insView.calls) call.doFrame(milli); + else for(var call : progView.calls) call.doFrame(milli); + } + + void syncProg(long milli, long cur) { + if(cur==0) cur = milli; + Util.println("\nSyncProg"); + waitTo = Long.MAX_VALUE; + avas.clear(); + curAva = 0; + curTimes = 1; + isInsert = false; + if(insView!=null) { + for(int i=0; i reces = new ArrayList<>(); + @Override + public void onCreate() { + super.onCreate(); + ins = this; + Util.println("==>> MainService onCreate >>>> "+hashCode()+" Thread: "+Thread.currentThread().getId()); + TCPThread.startServer(3333); + if(MainActivity.ins==null) { + var intent = new Intent(this, MainActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); + } + + BroadcastReceiver rece; + var intentFilter = new IntentFilter(Intent.ACTION_MEDIA_MOUNTED); + intentFilter.addDataScheme("file"); + registerReceiver(rece = new BroadcastReceiver(){ + long lastMs; + final char[] pass = {'8','8','8'}; + @Override + public void onReceive(Context context, Intent intent) { // in UI Thread + var path = intent.getData().getPath(); + Util.println("\nMEDIA_MOUNTED path: "+path); + var ms = System.currentTimeMillis(); + if(ms-lastMs<1000) return; + lastMs = ms; + Util.downId = 0; + var acti = MainActivity.ins; + Util.makeText(MainService.this, "MEDIA_MOUNTED path: "+path+"\nImporting 正在导入 ...").show(); + new Thread(()->{ + try { + var zip = new ZipFile(path+"/program.zip"); + if(zip.isEncrypted()) zip.setPassword(pass); + long size = 0; + ByteArrayOutputStream jsonOut = null; + var headers = zip.getFileHeaders(); + for(var header : headers) { + size += header.getUncompressedSize(); + if("program".equals(header.getFileName())) IOs.writeClose(jsonOut = new ByteArrayOutputStream(), zip.getInputStream(header)); + } + if(jsonOut==null) { + Util.println("No program File"); + if(acti!=null) acti.runOnUiThread(() -> Util.makeText(acti, "No program File").show()); + return; + } + if(size==0) { + Util.println("zip size is 0"); + if(acti!=null) acti.runOnUiThread(() -> Util.makeText(acti, "zip size is 0").show()); + return; + } + Util.deleteFiles(size, null); + for(var header : headers) if(! "program".equals(header.getFileName())) { + Util.println(" name: " + header.getFileName()); + var fOut = new FileOutputStream(Util.programDir + "/" + header.getFileName()); + IOs.writeCloseIn(fOut, zip.getInputStream(header)); + fOut.flush(); + fOut.getFD().sync(); + fOut.close(); + } + var json = jsonOut.toByteArray(); + Util.println("Import Succeed"); + if(acti!=null) acti.runOnUiThread(() -> { + Util.makeText(acti, "Import Succeed 导入成功").show(); + acti.initProg(json); + }); + else { + var fOut = new FileOutputStream(Util.programDir + "/program"); + fOut.write(json); + fOut.flush(); + fOut.getFD().sync(); + fOut.close(); + var inten = new Intent(MainService.this, MainActivity.class); + inten.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(inten); + } + var inten = new Intent("com.xixun.AccessibilityService"); + inten.putExtra("newProgram", "USB"); + sendBroadcast(inten); + } catch (Exception e) { + Util.printStackTrace(e); + if(acti!=null) acti.runOnUiThread(() -> Util.makeText(acti, Util.toStr(e)).show()); + } + }).start(); + } + }, intentFilter); + reces.add(rece); + } + + @Override + public void onDestroy() { + super.onDestroy(); + Util.println("==<< MainService onDestroy <<<< this "+hashCode()); + ins = null; + for(var rece : reces) { + try { + unregisterReceiver(rece); + } catch (Throwable ignored){ + } + } + System.gc(); + } + + @Override + public IBinder onBind(Intent intent) { + throw new UnsupportedOperationException("Not yet implemented"); + } +} \ No newline at end of file diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/Prog.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/Prog.java new file mode 100644 index 0000000..6267084 --- /dev/null +++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/Prog.java @@ -0,0 +1,814 @@ +package com.xixun.xixunplayer; + +import android.annotation.SuppressLint; +import android.content.ContentValues; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.net.Uri; +import android.os.CountDownTimer; +import android.speech.tts.TextToSpeech; +import android.view.Choreographer; +import android.view.View; +import android.webkit.JavascriptInterface; +import android.webkit.WebView; +import android.widget.ImageView; + +import java.io.File; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.SimpleTimeZone; +import java.util.TimeZone; + +import gnph.util.JSList; +import gnph.util.JSMap; +import pl.droidsonroids.gif.GifImageView; + +@SuppressLint("ViewConstructor") +public class Prog extends AbsLayout { + + ArrayList pages = new ArrayList<>(); + ArrayList calls = new ArrayList<>(); + boolean isInsert; + + @SuppressLint("SetJavaScriptEnabled") + public Prog(JSMap task, Context context) { + super(context); + isInsert = task.bool("insert"); + JSList jpages = task.jslist("items"); + JSList partLengths = task.jslist("partLengths"); + var isVertical = task.bool("isVertical"); + var partObj = task; + if(partLengths==null) { + try { + var _program = jpages.get(0).jsmap("_program"); + partLengths = _program.jslist("splitWidths"); + isVertical = _program.bool("isVer"); + partObj = _program; + } catch (Throwable ignored){} + } + var width = partObj.intg("width"); + var height = partObj.intg("height"); + if(Util.custom==Util.Custom.Yishi) { + if(width==0) width = Util.screenWidth; + if(height==0) height = Util.screenHeight; + } + AbsLayout box; + if(partLengths==null || partLengths.size() <= 1) box = this; + else { + box = new AbsLayout(context); + addView(box, new AbsLayout.LayoutParams(0, 0, width, height)); + var mask = new View(context); + mask.setBackgroundColor(0xff000000); + var len0 = partLengths.get(0).intValue(); + addView(mask, isVertical ? new AbsLayout.LayoutParams(0, len0, width, height - len0) : new AbsLayout.LayoutParams(len0, 0, width - len0, height)); + int x = 0, y = 0; + for(int i=1; i layers = _program.jslist("layers"); + var isSimple = _program.intg("version")==2; + if(layers==null || layers.isEmpty()) continue; + if(isSimple) { + width = _program.intg("width", Util.screenWidth); + height = _program.intg("height", Util.screenHeight); + } + var page = new Page(); + page.id = _program.str("_id"); + page.name = _program.str("name"); + page.type = "advanced"; + page.repeatTimes = pageMap.intg("repeatTimes", 1); + page.parse(pageMap.jslist("schedules")); + var waitAudio = pageMap.bool("waitAudio"); + HashMap videoMap = new HashMap<>(); + for(int ll=layers.size()-1; ll>=0; ll--) { + var layer = new Layer(); + layer.isLoop = layers.get(ll).bool("repeat"); + JSList sources = layers.get(ll).jslist("sources"); + var border = layers.get(ll).jsmap("border"); + SrcBorder bdEle = null; + int bdWidth = 0, bdStart = Integer.MAX_VALUE, bdEnd = 0; + if(border!=null) { + bdEle = new SrcBorder(this, Util.programDir+"/"+border.stnn("img"), border.stnn("eff"), border.intg("speed")); + bdWidth = bdEle.img.getHeight(); + } + var src = new Source(); + for(var source : sources) { + src.type = source.stnn("_type"); + if(src.type.isEmpty()) continue; + var dur = source.intg("timeSpan")*1000; + if(dur==0) { + Util.println("\nError: timeSpan is 0. _type: "+src.type); + continue; + } + var geo = isSimple ? new AbsLayout.LayoutParams(0, 0, width, height) : new AbsLayout.LayoutParams(source.intg("left")+bdWidth, source.intg("top")+bdWidth, source.intg("width")-bdWidth-bdWidth, source.intg("height")-bdWidth-bdWidth); + var notAudio = ! src.type.equals("Audio"); + if(notAudio) { + if(geo.width<=0 || geo.height<=0) { + if(Util.custom==Util.Custom.Yishi) { + geo.width = width; + geo.height = height; + } else { + Util.println("\nError: width or height is 0. _type: "+src.type+" width: "+geo.width+" height: "+geo.height); + continue; + } + } + if(box != this && ((geo.y>=height && height>0) || (geo.x>=width && width>0))) { + Util.println("\nError: y>=height or x>=width. _type: "+src.type+" width: "+width+" height: "+height+" x: "+geo.x+" y: "+geo.y); + continue; + } + } + src.startTime = isSimple ? (layer.srcs.isEmpty() ? 0 : layer.srcs.get(layer.srcs.size()-1).endTime) : source.intg("playTime")*1000; + if(bdStart > src.startTime) bdStart = src.startTime; + src.endTime = src.startTime + dur; + if(bdEnd < src.endTime) bdEnd = src.endTime; + if(layer.dur < src.endTime) layer.dur = src.endTime; + if(notAudio || waitAudio) { + if(page.sDur < src.endTime) page.sDur = src.endTime; + } else if(page.audioDur < src.endTime) page.audioDur = src.endTime; + + src.entryDur = source.intg("entryEffectTimeSpan")*60; + if(src.entryDur > 0) { + var effect = source.str("entryEffect"); + if(effect == null || effect.equalsIgnoreCase("None")) src.entryDur = 0; + else if(effect.equalsIgnoreCase("Random")) src.isEntryRand = true; + else { + effect = effect.replace("MOVING", "MOVE"); + effect = effect.replace("LEFT_TOP", "TL"); + effect = effect.replace("RIGHT_TOP", "TR"); + effect = effect.replace("RIGHT_BOTTOM", "BR"); + effect = effect.replace("LEFT_BOTTOM", "BL"); + effect = effect.replace("ALPHA", "FADE"); + effect = effect.replace("_IN", ""); + effect = effect.replace("_OUT", ""); + try { + src.entryEff = Effect.valueOf(effect); + } catch (Throwable e) { + if(effect.equalsIgnoreCase("ROTATE_RIGHT")) src.entryEff = Effect.ROTATE; + else if(effect.equalsIgnoreCase("ROTATE_LEFT")) src.entryEff = Effect.ROTATE_R; + else src.entryDur = 0; + } + } + } + src.exitDur = source.intg("exitEffectTimeSpan")*60; + if(src.exitDur > 0) { + var effect = source.str("exitEffect"); + if(effect == null || effect.equalsIgnoreCase("None")) src.exitDur = 0; + else if(effect.equalsIgnoreCase("Random")) src.isExitRand = true; + else { + effect = effect.replace("MOVING", "MOVE"); + effect = effect.replace("LEFT_TOP", "TL"); + effect = effect.replace("RIGHT_TOP", "TR"); + effect = effect.replace("RIGHT_BOTTOM", "BR"); + effect = effect.replace("LEFT_BOTTOM", "BL"); + effect = effect.replace("ALPHA", "FADE"); + effect = effect.replace("_IN", ""); + effect = effect.replace("_OUT", ""); + try { + src.exitEff = Effect.valueOf(effect); + } catch (Throwable e) { + if(effect.equalsIgnoreCase("ROTATE_RIGHT")) src.exitEff = Effect.ROTATE; + else if(effect.equalsIgnoreCase("ROTATE_LEFT")) src.exitEff = Effect.ROTATE_R; + else src.exitDur = 0; + } + } + } + if(src.exitDur!=0) src.exitStart = dur*60/1000 - src.exitDur; + + src.alpha = (float) source.dbl("opacity", 1); + var breathe = source.dbl("breathe"); + if(breathe > 0) src.breathe = (int) Math.round(60 / breathe); + var blink = source.dbl("blink"); + if(blink > 0) src.blinkHalf = (int) Math.round(30 / blink); + src.rotate = (float) source.dbl("rotate"); + src.scaleX = (float) source.dbl("scaleX", 1); + src.scaleY = (float) source.dbl("scaleY", 1); + + var id = source.str("id"); + var fileExt = source.stnn("fileExt"); + if(id!=null && fileExt.startsWith(".") && new File(Util.programDir + "/" + id + fileExt).exists()) id += fileExt; + src.view = null; + if(src.type.equals("Image")) { + if(id==null) continue; + var isGif = fileExt.toLowerCase().endsWith("gif"); + if(isGif) { + var imgView = new GifImageView(context); + imgView.setImageURI(src.uri = Uri.fromFile(new File(Util.programDir + "/" + id))); + imgView.setScaleType(ImageView.ScaleType.FIT_XY); + src.view = imgView; + } else { + var imgView = new ImageView(context); + var file = Util.programDir+"/"+id; + new Thread(()->{ + try { + var ttt = System.currentTimeMillis(); + var img = BitmapFactory.decodeFile(file); +// if(img.getByteCount()>1500*1500*4) { +// var matrix = new Matrix(); +// matrix.setScale(0.5f, 0.5f); +// img = Bitmap.createBitmap(img, 0, 0, img.getWidth(), img.getHeight(), matrix, true); +// } + ttt = System.currentTimeMillis() - ttt; + Util.println(" img "+img.getWidth()+"x"+img.getHeight()+" "+ttt); + Bitmap finalImg = img; + MainActivity.ins.runOnUiThread(() -> imgView.setImageBitmap(finalImg)); + } catch (Throwable e) { + var stackTrace = Util.toStackTrace(e); + Util.println(stackTrace); + var img = Bitmap.createBitmap(600, 400, Bitmap.Config.ARGB_8888); + var paint = new Paint(); + paint.setColor(Color.RED); + new Canvas(img).drawText(stackTrace, 0, 0, paint); + MainActivity.ins.runOnUiThread(() -> imgView.setImageBitmap(img)); + } + }).start(); + imgView.setScaleType(ImageView.ScaleType.FIT_XY); + src.view = imgView; + } + } else if(src.type.endsWith("Video")) { + var isLive = src.type.startsWith("Live"); + var url = source.str("url"); + if(isLive) { + if(url==null) continue; + } else if(id==null) continue; + var key = isLive ? url : id + src.startTime + src.endTime; + var exist = videoMap.get(key); + if(exist!=null) { + var geoOld = (AbsLayout.LayoutParams) exist.getLayoutParams(); + if(geo.width*geo.height > geoOld.width*geoOld.height) { + exist.setLayoutParams(geo); + geo = geoOld; + } + src.view = new SrcCopy(context, exist); + ((SrcCopy) src.view).scaleX = 0; + } else { + src.view = new SrcVideo(context, isLive ? url : Util.programDir+"/"+id, source.intg("vol", 100) / 100.0f, dur, source.bool("useSW"), isLive); + videoMap.put(key, src.view); + } + } else if(src.type.equals("Audio")) { + if(id==null) continue; + src.view = new SrcVideo(context, Util.programDir + "/" +id, source.intg("vol", 100) / 100.0f, dur, false, false); + } else if(src.type.equals("Scroll")) { + JSList imgs = source.jslist("imgs"); + if(imgs.isEmpty()) continue; + var images = new ArrayList(); + for(var img : imgs) images.add(BitmapFactory.decodeFile(Util.programDir+"/"+img)); + var directStr = source.str("direct"); + src.view = new SrcScroll(this, images, directStr==null ? 0 : directStr.charAt(0), source.dbl("speed")); + src.text = source.str("tts"); + if(src.text!=null) { + src.tts = new TextToSpeech(context, (int status)->{ + Util.println("status: "+status+" "+(status==TextToSpeech.SUCCESS)); + }, "com.iflytek.speechcloud"); + src.tts.setSpeechRate((float) source.dbl("speechRate")); + } + } else if(src.type.startsWith("MultiPng") || src.type.equals("SplitText")) { + JSList imgs = source.jslist("arrayPics"); + if(imgs.isEmpty()) continue; + var mode = source.str("curchange"); + var hasTTS = src.type.endsWith("Audio"); + var speechRate = (float) source.dbl("voiceRate"); + if(mode!=null ? mode.endsWith("roll") : (imgs.size()==1 && imgs.get(0).intg("picDuration")==0)) { + var img = imgs.get(0); + var images = new ArrayList(); + images.add(BitmapFactory.decodeFile(Util.programDir+"/"+img.stnn("id"))); + var speed = img.dbl("scrollSpeed"); + if(speed==0) { + var scrollDur = img.dbl("effectSpeed"); + if(scrollDur!=0) speed = 1000 / scrollDur; + } + char direct = 0; + var directStr = img.str("effect"); + if(directStr!=null && ! directStr.equals("no")) { + int idx = directStr.lastIndexOf(' '); + if(idx > -1) direct = directStr.charAt(idx+1); + } + src.view = new SrcScroll(this, images, direct, speed); + if(hasTTS) { + src.text = img.str("text"); + if(src.text!=null) { + src.tts = new TextToSpeech(context, (int status)->{ + Util.println("status: "+status+" "+(status==TextToSpeech.SUCCESS)); + }, "com.iflytek.speechcloud"); + src.tts.setSpeechRate(speechRate); + } + } + } else { + var ele0 = src; + for(var map : imgs) { + var picDur = map.intg("picDuration")*1000; + if(picDur==0) picDur = dur / imgs.size(); + src.type = "Image"; + if(src!=ele0) { + src.startTime = layer.srcs.get(layer.srcs.size()-1).endTime; + src.entryEff = ele0.entryEff; + src.exitEff = ele0.exitEff; + src.entryDur = ele0.entryDur; + src.exitDur = ele0.exitDur; + src.exitStart = ele0.exitStart; + src.isEntryRand = ele0.isEntryRand; + src.isExitRand = ele0.isExitRand; + src.alpha = ele0.alpha; + src.breathe = ele0.breathe; + src.blinkHalf = ele0.blinkHalf; + src.rotate = ele0.rotate; + src.scaleX = ele0.scaleX; + src.scaleY = ele0.scaleY; + } + src.endTime = src.startTime + picDur; + if(hasTTS) { + src.text = map.str("text"); + if(src.text!=null) { + src.tts = new TextToSpeech(context, (int status)->{ + Util.println("status: "+status+" "+(status==TextToSpeech.SUCCESS)); + }, "com.iflytek.speechcloud"); + src.tts.setSpeechRate(speechRate); + } + } + var imgView = new ImageView(context); + imgView.setImageURI(Uri.fromFile(new File(Util.programDir + "/" + map.stnn("id")))); + imgView.setScaleType(ImageView.ScaleType.FIT_XY); + src.view = imgView; + src.view.setVisibility(GONE); + src.view.setLayoutParams(geo); + box.addView(src.view); + layer.srcs.add(src); + src = new Source(); + } + } + } else if(src.type.equals("DigitalClock")) src.view = new SrcDigitalClock(this, source); + else if(src.type.startsWith("DigitalClock")) src.view = new SrcDigiClock(this, source); + else if(src.type.equals("AnalogClock")) src.view = new SrcAnaClock(this, geo.width, geo.height, Util.programDir + "/" + id, source); + else if(src.type.equals("WebURL")) src.view = new SrcWeb(this, source); + else if(src.type.equals("Timer")) src.view = new SrcTimer(this, source); + else if(src.type.equals("Countdown")) src.view = new SrcCountdown(this, source); + else if(src.type.startsWith("Environ")) src.view = new SrcEnviron(this, source); + else if(src.type.startsWith("Weather")) src.view = new SrcWeather(context, source); + else if(src.type.startsWith("VistorSource")) src.view = new SrcVisitor(this, source); + else if(src.type.startsWith("MultiLineText")) src.view = new SrcSensor(this, source, geo.width, geo.height); + else if(src.type.startsWith("SingleLineText")) { + var webView = new WebView(context); + var settings = webView.getSettings(); + settings.setJavaScriptEnabled(true); + webView.setVerticalScrollBarEnabled(false); + webView.setHorizontalScrollBarEnabled(false); + webView.setBackgroundColor(Color.TRANSPARENT); + webView.setInitialScale(100); + webView.setLayoutParams(new AbsLayout.LayoutParams(0, -geo.height, geo.width, geo.height)); + var html = source.stnn("html"); + var prefix = ""; + var suffix = ""; + var speed = source.dbl("speed"); + if(speed!=0) speed = 1000.0 / speed; + var imgs = new ArrayList(); + imgs.add(Bitmap.createBitmap(geo.width, geo.height, Bitmap.Config.ARGB_8888)); + var view = new SrcScroll(this, imgs, 'l', speed); + src.view = view; + webView.addJavascriptInterface(new Object() { + @JavascriptInterface + public void setWidth(int width) { + MainActivity.ins.runOnUiThread(() -> { + var hhh = view.imgs.get(0).getHeight(); + var www = width + hhh; + webView.getLayoutParams().width = www; + view.imgs.set(0, Bitmap.createBitmap(www, hhh, Bitmap.Config.ARGB_8888)); + addView(webView); + var atimer = new CountDownTimer(500, 500) { + @Override + public void onTick(long millisUntilFinished) {} + @Override + public void onFinish() { + var canvas = new Canvas(view.imgs.get(0)); + webView.draw(canvas); + webView.setVisibility(GONE); + view.freshCnt = view.cur = 0; + if(view.direct=='l') view.end = -(view.imgs.get(0).getWidth()-view.step); + else if(view.direct=='r') view.end = view.imgs.get(0).getWidth()-view.step; + view.invalidate(); + } + }; + if(isShown()) atimer.start(); + else timer = atimer; + }); + } + }, "java"); + webView.loadDataWithBaseURL(null, prefix+html+suffix, "text/html", "UTF-8", null); + } + else continue; + if(src.view==null) continue; + src.view.setVisibility(GONE); + src.view.setLayoutParams(geo); + (source.bool("isPreSplit") ? this : box).addView(src.view); + layer.srcs.add(src); + src = new Source(); + } + if(bdEle!=null && bdStart < bdEnd) { + JSList geometry = border.jslist("geometry"); + src.startTime = bdStart; + src.endTime = bdEnd; + src.rotate = (float) border.dbl("rotate"); + src.view = bdEle; + src.view.setVisibility(GONE); + box.addView(src.view, new AbsLayout.LayoutParams(geometry.get(0).intValue(), geometry.get(1).intValue(), geometry.get(2).intValue(), geometry.get(3).intValue())); + layer.srcs.add(src); + } + if(! layer.srcs.isEmpty()) page.layers.add(layer); + } + if(page.sDur==0) { + if(page.audioDur > 0) page.sDur = page.audioDur; + else continue; + } + page.tDur = page.sDur * page.repeatTimes; + for_layer: for(int ll=0; ll= page.sDur) { + if(layer.srcs.size() > 1) layer.srcs.remove(ss--); + else { + page.layers.remove(ll--); + continue for_layer; + } + } else if(src.endTime > page.sDur) src.endTime = page.sDur; + } + if(layer.dur > page.sDur) layer.dur = page.sDur; + if(layer.dur == page.sDur) layer.isLoop = false; + } + pages.add(page); + } + } + + void release() { + try { + setVisibility(GONE); + View view; + for(int cc=0; cc=exitStart) { + if(ff-1 <= exitStart && (blinkHalf!=0 || breathe!=0)) view.setAlpha(alpha); + var fff = ff - exitStart; + if(fff > exitDur) fff = exitDur; + if(exitEff == Effect.EXPAND_HOR) setScaleX(1 - fff / (float) exitDur); + else if(exitEff == Effect.EXPAND_VER) setScaleY(1 - fff / (float) exitDur); + else if(exitEff == Effect.EXPAND_LEFT) { + var rate = fff / (float) exitDur; + setScaleX(1 - rate); + view.setTranslationX(rate * w / 2); + } else if(exitEff == Effect.EXPAND_TOP) { + var rate = fff / (float) exitDur; + view.setScaleY(1 - rate); + view.setTranslationY(rate * h / 2); + } else if(exitEff == Effect.EXPAND_RIGHT) { + var rate = fff / (float) exitDur; + setScaleX(1 - rate); + view.setTranslationX(- rate * w / 2); + } else if(exitEff == Effect.EXPAND_BOTTOM) { + var rate = fff / (float) exitDur; + setScaleY(1 - rate); + view.setTranslationY(- rate * h / 2); + } + else if(exitEff == Effect.MOVE_LEFT) view.setTranslationX(- fff*w / (float) exitDur); + else if(exitEff == Effect.MOVE_RIGHT) view.setTranslationX(fff*w / (float) exitDur); + else if(exitEff == Effect.MOVE_TOP) view.setTranslationY(- fff*h / (float) exitDur); + else if(exitEff == Effect.MOVE_BOTTOM) view.setTranslationY(fff*h / (float) exitDur); + else if(exitEff == Effect.FADE) view.setAlpha(alpha - alpha * fff / exitDur); + else if(exitEff == Effect.ZOOM) { + setScaleX(1 - fff / (float) exitDur); + setScaleY(view.getScaleX()); + } else if(exitEff == Effect.ZOOM_TL) { + var rate = fff / (float) exitDur; + view.setTranslationX(- rate * w / 2); + view.setTranslationY(- rate * h / 2); + setScaleX(1 - rate); + setScaleY(1 - rate); + } else if(exitEff == Effect.ZOOM_BR) { + var rate = fff / (float) exitDur; + view.setTranslationX(rate * w / 2); + view.setTranslationY(rate * h / 2); + setScaleX(1 - rate); + setScaleY(1 - rate); + } else if(exitEff == Effect.ZOOM_TR) { + var rate = fff / (float) exitDur; + view.setTranslationX(rate * w / 2); + view.setTranslationY(- rate * h / 2); + setScaleX(1 - rate); + setScaleY(1 - rate); + } else if(exitEff == Effect.ZOOM_BL) { + var rate = fff / (float) exitDur; + view.setTranslationX(- rate * w / 2); + view.setTranslationY(rate * h / 2); + setScaleX(1 - rate); + setScaleY(1 - rate); + } else if(exitEff == Effect.ROTATE) { + var rate = fff / (float) exitDur; + setScaleX(1 - rate); + setScaleY(view.getScaleX()); + view.setRotation(rotate + rate * 360); + } else if(exitEff == Effect.ROTATE_R) { + var rate = fff / (float) exitDur; + setScaleX(1 - rate); + setScaleY(view.getScaleX()); + view.setRotation(rotate - rate * 360); + } + } else { + resetEff(); + if(blinkHalf!=0) view.setAlpha((ff / blinkHalf & 1) == 0 ? alpha : 0); + else if(breathe!=0) view.setAlpha((float) (Math.cos(ff%breathe*2*Math.PI/breathe)/2+0.5)); + } + ff++; + } + void resetEff() { + view.setTranslationX(0); + view.setTranslationY(0); + view.setAlpha(alpha); + view.setScaleX(scaleX); + view.setScaleY(scaleY); + view.setRotation(rotate); + } + void setScaleX(float val) { + view.setScaleX(scaleX*val); + } + void setScaleY(float val) { + view.setScaleY(scaleY*val); + } + } + public static class Layer { + ArrayList srcs = new ArrayList<>(); + long endMilli = Long.MAX_VALUE; + int dur; + boolean isLoop; + } + public enum Effect { + EXPAND_HOR, EXPAND_VER, EXPAND_LEFT, EXPAND_TOP, EXPAND_RIGHT, EXPAND_BOTTOM, + ZOOM, ZOOM_TL, ZOOM_TR, ZOOM_BR, ZOOM_BL, + ROTATE, ROTATE_R, + FADE, + MOVE_LEFT, MOVE_TOP, MOVE_RIGHT, MOVE_BOTTOM, + } + public static class Sche { + long startDate = -1, endDate = -1; + int startTime = -1, endTime = -1; + HashSet weeks; + } + + public static class Page { + String id, name, type; + ArrayList layers = new ArrayList<>(); + ArrayList sches; + long endMilli = Long.MAX_VALUE; + int sDur, tDur, repeatTimes, audioDur; + + void hide() { + for(var layer : layers) for(var src : layer.srcs) src.hide(); + } + + void setMillis(long start, long cur) { + endMilli = start + sDur; + for(var layer : layers) { + if(layer.isLoop) layer.endMilli = start + layer.dur; + for(var src : layer.srcs) { + src.endMilli = start + src.endTime; + src.startMilli = start + src.startTime; + if(src.startTime == 0) { + if(src.view instanceof SrcVideo) ((SrcVideo)src.view).seekTo = cur - src.startMilli; + src.show(); + } + } + } + if(Util.logOn) { + var values = new ContentValues(); + values.put("time", cur); + values.put("dur", sDur); + values.put("id", id); + values.put("name", name); + values.put("type", type); + values.put("lat", Util.lat); + values.put("lng", Util.lng); + LogThread.rows.add(values); + } + } + void showHideSrcs(long milli) { + for(var layer : layers) { + for(var src : layer.srcs) { + if(src.view.getVisibility()==VISIBLE) { + if(milli >= src.endMilli) src.hide(); + else src.doEff(); + } else if(milli < src.endMilli && milli >= src.startMilli) { + if(src.view instanceof SrcVideo) ((SrcVideo)src.view).seekTo = milli - src.startMilli; + src.show(); + } + } + if(milli >= layer.endMilli) { + layer.endMilli += layer.dur; + for(var src : layer.srcs) { + src.endMilli += layer.dur; + src.startMilli += layer.dur; + if(src.startTime > 0) src.hide(); + else src.show(); + } + } + } + } + boolean isScheOn(long milli) { + if(sches==null) return false; + var local = milli + TimeZone.getDefault().getOffset(milli); + int week = -1; + for(var sche : sches) { + if(notInRange(sche.startDate, local, sche.endDate)) continue; + if(notInRange(sche.startTime, local % 86400000L, sche.endTime)) continue; + if(sche.weeks==null) return true; + if(week==-1) week = ((int)(local / 86400000L) + 4) % 7; + if(sche.weeks.contains(week)) return true; + } + return false; + } + public boolean notInRange(long start, long val, long end) { + if(end==-1) return false; + return start <= end ? !(val >= start && val < end) : val >= end && val < start; + } + SimpleDateFormat dateFmt = new SimpleDateFormat("yyyy-MM-dd"); + SimpleDateFormat timeFmt = new SimpleDateFormat("HH:mm:ss"); + { + var zone = new SimpleTimeZone(0, "0"); + dateFmt.getCalendar().setTimeZone(zone); + timeFmt.getCalendar().setTimeZone(zone); + } + public void parse(JSList schedules) { + if(schedules!=null) for(var schedule : schedules) { + var sche = new Sche(); + var startTime = schedule.str("startTime"); + if(startTime!=null) { + if(startTime.length()<=5) startTime += ":00"; + try { + sche.startTime = (int)timeFmt.parse(startTime).getTime(); + Util.println("startTime "+timeFmt.format(sche.startTime)+" "+sche.startTime); + } catch (ParseException e) { + Util.printStackTrace(e); + } + } + var endTime = schedule.str("endTime"); + if(endTime!=null) { + if(endTime.length()<=5) endTime += ":00"; + try { + sche.endTime = (int)timeFmt.parse(endTime).getTime(); + Util.println("endTime "+timeFmt.format(sche.endTime)+" "+sche.endTime); + } catch (ParseException e) { + Util.printStackTrace(e); + } + } + if(sche.startTime==sche.endTime) sche.startTime = sche.endTime = -1; + var startDate = schedule.str("startDate"); + if(startDate!=null) { + try { + sche.startDate = dateFmt.parse(startDate).getTime(); + } catch (ParseException e) { + Util.printStackTrace(e); + } + } + var endDate = schedule.str("endDate"); + if(endDate!=null) { + try { + sche.endDate = dateFmt.parse(endDate).getTime() + 86400000L; + } catch (ParseException e) { + Util.printStackTrace(e); + } + } + if(sche.startDate==sche.endDate) sche.startDate = sche.endDate = -1; + JSList weekFilter = schedule.jslist("weekFilter"); + if(weekFilter!=null && ! weekFilter.isEmpty() && weekFilter.size() < 7) { + sche.weeks = new HashSet<>(); + for(var week : weekFilter) sche.weeks.add(week.intValue() % 7); + } + if(sches==null) sches = new ArrayList<>(); + sches.add(sche); + } + } + } +} \ No newline at end of file diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcAnaClock.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcAnaClock.java new file mode 100644 index 0000000..a48e0f3 --- /dev/null +++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcAnaClock.java @@ -0,0 +1,166 @@ +package com.xixun.xixunplayer; + +import android.annotation.SuppressLint; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.RectF; +import android.view.Choreographer; +import android.view.View; + +import androidx.annotation.NonNull; + +import java.util.Calendar; +import java.util.TimeZone; + +import gnph.util.JSMap; + +@SuppressLint("ViewConstructor") +public class SrcAnaClock extends View implements Choreographer.FrameCallback { + + Calendar calendar; + Bitmap img; + int pinHourColor, pinMinColor, pinSecColor; + Path hPath, mPath, sPath; + float hAngle, mAngle, sAngle; + Paint paintPin = new Paint(), paint = new Paint(); + boolean showSecHand; + + public SrcAnaClock(Prog prog, float w, float h, String path, JSMap source) { + super(prog.getContext()); + var timeZoneStr = source.str("timeZone"); + //if(timeZoneStr!=null) timeZone = ZoneId.of(timeZoneStr); + var timeZone = timeZoneStr==null ? null : TimeZone.getTimeZone(timeZoneStr); + calendar = timeZone==null ? Calendar.getInstance() : Calendar.getInstance(timeZone); + + var sideLen = Math.min(w, h); + var halfSide = sideLen / 2; + var lineWidth = sideLen / 128; + if(lineWidth < 1) lineWidth = 1; + + paintPin.setAntiAlias(true); + paintPin.setStyle(Paint.Style.FILL_AND_STROKE); + paintPin.setStrokeWidth(lineWidth); + paintPin.setStrokeJoin(Paint.Join.ROUND); + paint.setAntiAlias(true); + paint.setStyle(Paint.Style.FILL); + var fontSize = sideLen / 11; + paint.setTextSize(fontSize); + + img = BitmapFactory.decodeFile(path); + if(img==null) { + img = Bitmap.createBitmap((int)w, (int)h, Bitmap.Config.ARGB_8888); + var canvas = new Canvas(img); + canvas.translate(w / 2, h / 2); + if(source.bool("showBg")) { + var bgColor = Color.parseColor(source.stnn("bgColor")); + var opacity = source.dbl("opacity", -1); + if(opacity!=-1) bgColor = bgColor & 0x00ffffff | ((int)opacity*255)<<24; + paint.setColor(bgColor); + canvas.drawOval(-halfSide, -halfSide, halfSide, halfSide, paint); + } + var minMarkSize = sideLen/48; + var hourMarkSize = sideLen/32; + var scaleHourColor = Color.parseColor(source.stnn("scaleHourColor")); + var scaleMinColor = Color.parseColor(source.stnn("scaleMinColor")); + var showScaleNum = source.bool("showScaleNum"); + var rScale = (sideLen - Math.max(minMarkSize, hourMarkSize)) / 2; + var rNum = halfSide * 0.83; + for(int i=0; i<60; i++) { + var k = i * Math.PI / 30; + var x = (float) (Math.sin(k) * rScale); + var y = (float) (-Math.cos(k) * rScale); + if(i % 5 > 0) { + var rr = minMarkSize / 2f; + paint.setColor(scaleMinColor); + canvas.drawOval(x-rr, y-rr, x+rr, y+rr, paint); + } else { + var rr = hourMarkSize / 2f; + paint.setColor(scaleHourColor); + canvas.drawOval(x-rr, y-rr, x+rr, y+rr, paint); + if(showScaleNum) { + var hour = i/5; + if(hour==0) hour = 12; + x = (float) (Math.sin(k) * rNum); + y = (float) (-Math.cos(k) * rNum); + canvas.drawText(String.valueOf(hour), x - fontSize*(hour<10 ? 0.28f : 0.6f), y + fontSize*0.355f, paint); + } + } + } + } + pinHourColor = Color.parseColor(source.stnn("pinHourColor")); + pinMinColor = Color.parseColor(source.stnn("pinMinColor")); + pinSecColor = Color.parseColor(source.stnn("pinSecColor")); + var hhLen = (float) source.dbl("pinHourLen", 50); + var mhLen = (float) source.dbl("pinMinLen", 75); + var shLen = (float) source.dbl("pinSecLen", 100); + var hhWidth = (float) source.dbl("pinHourWidth", 15); + var mhWidth = (float) source.dbl("pinMinWidth", 10); + var shWidth = (float) source.dbl("pinSecWidth", 5); + showSecHand = source.bool("showSecond"); + + var rx = hhWidth*sideLen/400; + hPath = new Path(); + hPath.moveTo(rx, 0); + hPath.lineTo(0, hhLen*sideLen/-200); + hPath.lineTo(-rx, 0); + hPath.close(); + + rx = mhWidth*sideLen/400; + mPath = new Path(); + mPath.moveTo(rx, 0); + mPath.lineTo(0, mhLen*sideLen/-200); + mPath.lineTo(-rx, 0); + mPath.close(); + rx = shWidth*sideLen/400; + sPath = new Path(); + sPath.moveTo(rx, 0); + sPath.lineTo(0, shLen*sideLen/-200); + sPath.lineTo(-rx, 0); + sPath.close(); + prog.calls.add(this); + } + + void cal() { + //var time = timeZone==null ? LocalTime.now() : LocalTime.now(timeZone); + calendar.setTimeInMillis(lastSec*1000); + sAngle = calendar.get(Calendar.SECOND) * 6; + mAngle = calendar.get(Calendar.MINUTE) * 6 + sAngle/60; + hAngle = calendar.get(Calendar.HOUR_OF_DAY) * 30 + mAngle/12; + } + @Override + protected void onDraw(@NonNull Canvas canvas) { + super.onDraw(canvas); + if(img != null) canvas.drawBitmap(img, null, new RectF(0,0, getWidth(), getHeight()), null); + canvas.translate(getWidth()/2f, getHeight()/2f); + + paintPin.setColor(pinHourColor); + canvas.rotate(hAngle); + canvas.drawPath(hPath, paintPin); + + paintPin.setColor(pinMinColor); + canvas.rotate(mAngle-hAngle); + canvas.drawPath(mPath, paintPin); + if(showSecHand) { + paintPin.setColor(pinSecColor); + canvas.rotate(sAngle-mAngle); + canvas.drawPath(sPath, paintPin); + } + } + + long lastSec; + + @Override + public void doFrame(long ms) { + if(! isShown()) return; + var sec = ms / 1000; + if(sec != lastSec) { + lastSec = sec; + cal(); + invalidate(); + } + } +} \ No newline at end of file diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcBorder.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcBorder.java new file mode 100644 index 0000000..10f6c60 --- /dev/null +++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcBorder.java @@ -0,0 +1,89 @@ +package com.xixun.xixunplayer; + +import android.annotation.SuppressLint; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Path; +import android.view.Choreographer; +import android.view.View; + +import androidx.annotation.NonNull; + +@SuppressLint("ViewConstructor") +public class SrcBorder extends View implements Choreographer.FrameCallback { + Bitmap img; + int[] lens = new int[4]; + Path[] paths = new Path[4]; + int[] offs = new int[]{0,0,0,0}; + byte eff; + int interval; + + public SrcBorder(Prog prog, String path, String effStr, int speed) { + super(prog.getContext()); + img = BitmapFactory.decodeFile(path); + if(effStr.startsWith("ro")) eff = 'r'; + else if(effStr.startsWith("bl")) eff = 'b'; + if(eff=='r') interval = speed==1 ? 4 : (speed==2 ? 2 : 1); + else interval = speed==1 ? 30 : (speed==2 ? 15 : 4); + if(eff!=0) prog.calls.add(this); + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + lens[0] = lens[2] = w; + lens[1] = lens[3] = h; + int bdWidth = img.getHeight(); + for(int i=0; i<2; i++) { + Path path = new Path(); + path.moveTo(0,0); + path.lineTo(lens[i], 0); + path.lineTo(lens[i] - bdWidth, bdWidth); + path.lineTo(bdWidth, bdWidth); + path.close(); + paths[i] = paths[i+2] = path; + } + } + + @Override + protected void onDraw(@NonNull Canvas canvas) { + super.onDraw(canvas); + if(eff!='b' || offs[0] <= 0) { + int bdWidth = img.getHeight(); + int bdLen = img.getWidth(); + offs[1] = (offs[0] + lens[0]-bdWidth)%bdLen; + offs[2] = (offs[0] + lens[0]+lens[1]-bdWidth*2)%bdLen; + offs[3] = (offs[0] + lens[0]*2+lens[1]-bdWidth*3)%bdLen; + for(int ll=0; ll<4; ll++) { + canvas.save(); + canvas.clipPath(paths[ll]); + for(int x=-offs[ll]; x"; + suffix = ""; + try { + var start = html.substring(0, 16).trim(); + if(start.startsWith("")) { + suffix = ""; + html = html.substring(6); + } else if(start.startsWith(" imgs = new HashMap<>(); + ImageView[] yearComps = {new ImageView(getContext()), new ImageView(getContext()), new ImageView(getContext()), new ImageView(getContext())}; + ImageView[] monthComps = {new ImageView(getContext()), new ImageView(getContext())}; + ImageView[] dayComps = {new ImageView(getContext()), new ImageView(getContext())}; + ImageView weekComp = new ImageView(getContext()), ampmComp = new ImageView(getContext()); + ImageView[] hourComps = {new ImageView(getContext()), new ImageView(getContext())}; + ImageView[] minComps = {new ImageView(getContext()), new ImageView(getContext())}; + ImageView[] secComps = {new ImageView(getContext()), new ImageView(getContext())}; + + Calendar calendar; + boolean multiline, weekly, isSingleMD, isHour12, isSingleHour; + + public SrcDigiClock(Prog prog, JSMap json) { + super(prog.getContext()); + setGravity(Gravity.CENTER); + var timeZoneStr = json.str("timeZone"); + //if(timeZoneStr!=null) timeZone = ZoneId.of(timeZoneStr); + var timeZone = timeZoneStr==null ? null : TimeZone.getTimeZone(timeZoneStr); + calendar = timeZone==null ? Calendar.getInstance() : Calendar.getInstance(timeZone); + var spaceWidth = json.dbl("spaceWidth"); + JSList pics = json.jslist("arrayPics"); + if(pics!=null && ! pics.isEmpty() && pics.get(0).containsKey("startX")) { + var big = BitmapFactory.decodeFile(Util.programDir+"/"+json.stnn("id")); + for(var pic : pics) { + var startX = pic.intg("startX"); + var startY = pic.intg("startY"); + imgs.put(pic.str("name"), Bitmap.createBitmap(big, startX, startY, pic.intg("endX")-startX, pic.intg("endY")-startY)); + } + } else for(var pic : pics) imgs.put(pic.str("name"), BitmapFactory.decodeFile(Util.programDir+"/"+pic.stnn("id"))); + int dateStyle = json.intg("dateStyle"); + isSingleMD = dateStyle==1||dateStyle==2||dateStyle==4||dateStyle==6||dateStyle==8||dateStyle==10||dateStyle==12; + isSingleHour = json.intg("timeStyle") != 0; + var timeSep = imgs.get("maohao"); + weekly = json.bool("weekly"); + isHour12 = json.bool("hour12"); + var AmPm = isHour12 && json.bool("AmPm"); + var hour = json.bool("hour"); + var min = json.bool("min"); + var sec = json.bool("sec"); + multiline = json.bool("multiline"); + addStretch(); + if(multiline) { + vertical(); + var hBox = new LinearBox(this).horizontal(); + hBox.addStretch(); + addDate(dateStyle, json, hBox); + hBox.addStretch(); + if(weekly) { + hBox = new LinearBox(this).horizontal(); + hBox.addStretch(); + hBox.addView(weekComp); + hBox.addStretch(); + } + hBox = new LinearBox(this).horizontal(); + hBox.addStretch(); + if(AmPm) { + hBox.addView(ampmComp); + hBox.addSpacing((int)spaceWidth); + } + if(hour) { + hBox.addView(hourComps[0]); + hBox.addView(hourComps[1]); + } + if(hour&&min) hBox.addView(newImgView(timeSep)); + if(min) { + hBox.addView(minComps[0]); + hBox.addView(minComps[1]); + } + if(min&&sec) hBox.addView(newImgView(timeSep)); + if(sec) { + hBox.addView(secComps[0]); + hBox.addView(secComps[1]); + } + hBox.addStretch(); + } else { + setOrientation(HORIZONTAL); + addDate(dateStyle, json, this); + if(getChildCount()>1) addSpacing((int)spaceWidth*2); + if(weekly) { + addView(weekComp); + addSpacing((int)spaceWidth*2); + } + if(AmPm) { + addView(ampmComp); + addSpacing((int)spaceWidth); + } + if(hour) { + addView(hourComps[0]); + addView(hourComps[1]); + } + if(hour&&min) addView(newImgView(timeSep)); + if(min) { + addView(minComps[0]); + addView(minComps[1]); + } + if(min&&sec) addView(newImgView(timeSep)); + if(sec) { + addView(secComps[0]); + addView(secComps[1]); + } + } + addStretch(); + prog.calls.add(this); + } + + ImageView newImgView(Bitmap img) { + var imgv = new ImageView(getContext()); + imgv.setImageBitmap(img); + return imgv; + } + void addDate(int dateStyle, JSMap layer, LinearLayout tar) { + if(dateStyle==0 || dateStyle==1) { + addYear(layer, tar, imgs.get("YEAR")); + if(layer.bool("month")) { + tar.addView(monthComps[0]); + tar.addView(monthComps[1]); + tar.addView(newImgView(imgs.get("MONTH"))); + } + if(layer.bool("day")) { + tar.addView(dayComps[0]); + tar.addView(dayComps[1]); + tar.addView(newImgView(imgs.get("DAY"))); + } + } else if(dateStyle==2 || dateStyle==3) { + var sep = imgs.get("xiegang"); + if(layer.bool("month")) { + tar.addView(monthComps[0]); + tar.addView(monthComps[1]); + tar.addView(newImgView(sep)); + } + if(layer.bool("day")) { + tar.addView(dayComps[0]); + tar.addView(dayComps[1]); + tar.addView(newImgView(sep)); + } + addYear(layer, tar, null); + } else if(dateStyle==4 || dateStyle==5) { + var sep = imgs.get("xiegang"); + if(layer.bool("day")) { + tar.addView(dayComps[0]); + tar.addView(dayComps[1]); + tar.addView(newImgView(sep)); + } + if(layer.bool("month")) { + tar.addView(monthComps[0]); + tar.addView(monthComps[1]); + tar.addView(newImgView(sep)); + } + addYear(layer, tar, null); + } else if(dateStyle==6 || dateStyle==7) { + var sep = imgs.get("xiegang"); + addYear(layer, tar, sep); + if(layer.bool("month")) { + tar.addView(monthComps[0]); + tar.addView(monthComps[1]); + tar.addView(newImgView(sep)); + } + if(layer.bool("day")) { + tar.addView(dayComps[0]); + tar.addView(dayComps[1]); + } + } else if(dateStyle==8 || dateStyle==9) { + var sep = imgs.get("hengxian"); + if(layer.bool("month")) { + tar.addView(monthComps[0]); + tar.addView(monthComps[1]); + tar.addView(newImgView(sep)); + } + if(layer.bool("day")) { + tar.addView(dayComps[0]); + tar.addView(dayComps[1]); + tar.addView(newImgView(sep)); + } + addYear(layer, tar, null); + } else if(dateStyle==10 || dateStyle==11) { + var sep = imgs.get("hengxian"); + if(layer.bool("day")) { + tar.addView(dayComps[0]); + tar.addView(dayComps[1]); + tar.addView(newImgView(sep)); + } + if(layer.bool("month")) { + tar.addView(monthComps[0]); + tar.addView(monthComps[1]); + tar.addView(newImgView(sep)); + } + addYear(layer, tar, null); + } else if(dateStyle==12 || dateStyle==13) { + var sep = imgs.get("hengxian"); + addYear(layer, tar, sep); + if(layer.bool("month")) { + tar.addView(monthComps[0]); + tar.addView(monthComps[1]); + tar.addView(newImgView(sep)); + } + if(layer.bool("day")) { + tar.addView(dayComps[0]); + tar.addView(dayComps[1]); + } + } + } + + void addYear(JSMap layer, LinearLayout tar, Bitmap sep) { + if(layer.bool("year")) { + if(layer.bool("fullYear")) { + tar.addView(yearComps[0]); + tar.addView(yearComps[1]); + } + tar.addView(yearComps[2]); + tar.addView(yearComps[3]); + if(sep != null) tar.addView(newImgView(sep)); + } + } + + void cal() { + //var dt = timeZone==null ? LocalDateTime.now() : LocalDateTime.now(timeZone); + calendar.setTimeInMillis(lastSec*1000); + var hour = calendar.get(Calendar.HOUR_OF_DAY); + ampmComp.setImageBitmap(imgs.get(hour < 12 ? "AM":"PM")); + var hh = NumFmts.zz().format(isHour12 ? Util.caseThen(calendar.get(Calendar.HOUR), 0, 12) : hour); + hourComps[0].setImageBitmap(isSingleHour && hh.charAt(0)=='0' ? null : imgs.get(hh.substring(0,1))); + hourComps[1].setImageBitmap(imgs.get(hh.substring(1,2))); + var mm = NumFmts.zz().format(calendar.get(Calendar.MINUTE)); + minComps[0].setImageBitmap(imgs.get(mm.substring(0,1))); + minComps[1].setImageBitmap(imgs.get(mm.substring(1,2))); + var ss = NumFmts.zz().format(calendar.get(Calendar.SECOND)); + secComps[0].setImageBitmap(imgs.get(ss.substring(0,1))); + secComps[1].setImageBitmap(imgs.get(ss.substring(1,2))); + var day = calendar.get(Calendar.DAY_OF_MONTH); + if(yearComps[0].getDrawable()==null || day!=lastDay) { + lastDay = day; + if(weekly) weekComp.setImageBitmap(imgs.get(weeks[calendar.get(Calendar.DAY_OF_WEEK) - 1])); + var yyyy = NumFmts.zzzz().format(calendar.get(Calendar.YEAR)); + yearComps[0].setImageBitmap(imgs.get(yyyy.substring(0,1))); + yearComps[1].setImageBitmap(imgs.get(yyyy.substring(1,2))); + yearComps[2].setImageBitmap(imgs.get(yyyy.substring(2,3))); + yearComps[3].setImageBitmap(imgs.get(yyyy.substring(3,4))); + mm = NumFmts.zz().format(calendar.get(Calendar.MONTH)+1); + monthComps[0].setImageBitmap(isSingleMD && mm.charAt(0)=='0' ? null : imgs.get(mm.substring(0,1))); + monthComps[1].setImageBitmap(imgs.get(mm.substring(1,2))); + var dd = NumFmts.zz().format(day); + dayComps[0].setImageBitmap(isSingleMD && dd.charAt(0)=='0' ? null : imgs.get(dd.substring(0,1))); + dayComps[1].setImageBitmap(imgs.get(dd.substring(1,2))); + } + } + int lastDay; + + long lastSec; + + @Override + public void doFrame(long ms) { + if(! isShown()) return; + var sec = ms / 1000; + if(sec != lastSec) { + lastSec = sec; + cal(); + } + } +} \ No newline at end of file diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcDigitalClock.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcDigitalClock.java new file mode 100644 index 0000000..9716a8d --- /dev/null +++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcDigitalClock.java @@ -0,0 +1,89 @@ +package com.xixun.xixunplayer; + +import android.annotation.SuppressLint; +import android.graphics.Color; +import android.view.Choreographer; +import android.webkit.WebView; + +import java.util.Calendar; +import java.util.Locale; +import java.util.SimpleTimeZone; + +import gnph.util.JSMap; +import gnph.util.NumFmts; +import gnph.util.Txts; + +@SuppressLint("ViewConstructor") +public class SrcDigitalClock extends WebView implements Choreographer.FrameCallback { + + String html, lineHeight, prefix; + Calendar calendar; + Locale locale; + boolean hasYear, hasMonthStr, hasMonth, hasDay, hasWeek, hasAP, hasHour, hasHour12, hasMin, hasSec; + + public SrcDigitalClock(Prog prog, JSMap json) { + super(prog.getContext()); + setBackgroundColor(Color.TRANSPARENT); + setVerticalScrollBarEnabled(false); + setHorizontalScrollBarEnabled(false); + setInitialScale(100); + html = json.stnn("html"); + lineHeight = json.str("lineHeight"); + var timezone = json.dbl("timezone", 9999); + calendar = timezone==9999 ? Calendar.getInstance() : Calendar.getInstance(new SimpleTimeZone((int) (timezone*3600000), "")); + prefix = ""; + hasYear = html.contains("%y"); + var hhh = html.replace("%Mw", ""); + hasMonthStr = html.length()!=hhh.length(); + hasMonth = hhh.contains("%M"); + hasDay = html.contains("%d"); + hasWeek = html.contains("%w"); + hasAP = html.contains("%am"); + hasHour = html.contains("%H"); + hasHour12 = html.contains("%h"); + hasMin = html.contains("%m"); + hasSec = html.contains("%s"); + try { + var lan = Txts.split(json.stnn("language"), "-", 1); + if(lan.isEmpty()) locale = Locale.getDefault(); + else if(lan.size()==1) locale = lan.get(0).equals("cn") ? new Locale("zh", "CN") : new Locale(lan.get(0)); + else locale = new Locale(lan.get(0), lan.get(1)); + } catch (Exception e) { + Util.printStackTrace(e); + Util.makeText(prog.getContext(), Util.toStr(e)).show(); + } + prog.calls.add(this); + } + + void cal() { + calendar.setTimeInMillis(lastSec*1000); + var htm = html; + if(hasYear) htm = htm.replace("%y", Long.toString(calendar.get(Calendar.YEAR))); + try { + if(hasMonthStr) htm = htm.replace("%Mw", calendar.getDisplayName(Calendar.MONTH, Calendar.SHORT, locale)); + if(hasWeek) htm = htm.replace("%w", calendar.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.LONG, locale)); + if(hasAP) htm = htm.replace("%am", calendar.getDisplayName(Calendar.AM_PM, Calendar.SHORT, locale)); + } catch (Exception ignored) {} + if(hasMonth) htm = htm.replace("%M", NumFmts.zz().format(calendar.get(Calendar.MONTH)+1)); + if(hasDay) htm = htm.replace("%d", NumFmts.zz().format(calendar.get(Calendar.DAY_OF_MONTH))); + if(hasHour) htm = htm.replace("%H", NumFmts.zz().format(calendar.get(Calendar.HOUR_OF_DAY))); + if(hasHour12) htm = htm.replace("%h", NumFmts.zz().format(Util.caseThen(calendar.get(Calendar.HOUR), 0, 12))); + if(hasMin) htm = htm.replace("%m", NumFmts.zz().format(calendar.get(Calendar.MINUTE))); + if(hasSec) htm = htm.replace("%s", NumFmts.zz().format(calendar.get(Calendar.SECOND))); + loadDataWithBaseURL(null, prefix+htm+"", "text/html", "UTF-8", null); + } + + long lastSec; + + @Override + public void doFrame(long ms) { + if(! isShown()) return; + var sec = ms / 1000; + if(sec != lastSec) { + lastSec = sec; + cal(); + } + } +} \ No newline at end of file diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcEnviron.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcEnviron.java new file mode 100644 index 0000000..cfc5153 --- /dev/null +++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcEnviron.java @@ -0,0 +1,241 @@ +package com.xixun.xixunplayer; + +import android.annotation.SuppressLint; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Color; +import android.view.Choreographer; +import android.view.View; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; + +import gnph.util.JSList; +import gnph.util.JSMap; +import gnph.util.NumFmts; + +@SuppressLint("ViewConstructor") +public class SrcEnviron extends View implements Choreographer.FrameCallback, IntentReceiver { + static class Item { + String key; + Bitmap label; + ArrayList nums = new ArrayList<>(); + Bitmap unit; + + public Item(String key, Bitmap label, Bitmap unit) { + this.key = key; + this.label = label; + this.unit = unit; + } + } + static String[] directs = {"NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW", "N"}; + HashMap imgMap = new HashMap<>(); + Bitmap title; + ArrayList items = new ArrayList<>(); + MainActivity act; + int alignType, spaceWidth, digitHeight; + int interval, cur, end, step; + boolean isScroll, isFirst = true; + + public SrcEnviron(Prog prog, JSMap json) { + super(prog.getContext()); + act = (MainActivity) prog.getContext(); + alignType = json.intg("alignType"); + spaceWidth = json.intg("spaceWidth"); + isScroll = json.bool("bSingleScroll"); + try { + setBackgroundColor(Color.parseColor(json.stnn("backColor"))); + } catch (Exception ignored) {} + var values = json.jsmap("values"); + if(values!=null) { + if(values.get("0") instanceof String) { + var titleStr = json.str("title"); + if(titleStr!=null) title = BitmapFactory.decodeFile(Util.programDir+"/"+titleStr); + var entrys = values.entrySet(); + for(var entry : entrys) imgMap.put(entry.getKey(), BitmapFactory.decodeFile(Util.programDir+"/"+entry.getValue())); + JSList jitems = json.jslist("items"); + for(var jitem : jitems) { + var unit = jitem.str("unit"); + items.add(new Item(jitem.str("name"), BitmapFactory.decodeFile(Util.programDir + "/" + jitem.stnn("label")), unit == null ? null : BitmapFactory.decodeFile(Util.programDir + "/" + unit))); + } + } else { + var big = BitmapFactory.decodeFile(Util.programDir+"/"+json.stnn("id")); + var titleGeo = json.jsmap("title"); + if(titleGeo!=null) title = Bitmap.createBitmap(big, titleGeo.intg("x"), titleGeo.intg("y"), titleGeo.intg("w"), titleGeo.intg("h")); + var entrys = values.entrySet(); + for(var entry : entrys) { + var val = (JSMap) entry.getValue(); + imgMap.put(entry.getKey(), Bitmap.createBitmap(big, val.intg("x"), val.intg("y"), val.intg("w"), val.intg("h"))); + } + JSList jitems = json.jslist("items"); + for(var jitem : jitems) { + var label = jitem.jsmap("label"); + var unit = jitem.jsmap("unit"); + items.add(new Item(jitem.str("name"), Bitmap.createBitmap(big, label.intg("x"), label.intg("y"), label.intg("w"), label.intg("h")), unit == null ? null : Bitmap.createBitmap(big, unit.intg("x"), unit.intg("y"), unit.intg("w"), unit.intg("h")))); + } + } + } else { + JSList pics = json.jslist("arrayPics"); + if(pics==null) return; + if(! pics.isEmpty() && pics.get(0).containsKey("startX")) { + var big = BitmapFactory.decodeFile(Util.programDir+"/"+json.stnn("id")); + for(var pic : pics) { + var startX = pic.intg("startX"); + var startY = pic.intg("startY"); + imgMap.put(pic.str("name"), Bitmap.createBitmap(big, startX, startY, pic.intg("endX")-startX, pic.intg("endY")-startY)); + } + } else for(var img : pics) imgMap.put(img.str("name"), BitmapFactory.decodeFile(Util.programDir+"/"+img.stnn("id"))); + imgMap.put("-", imgMap.remove("minus_sign")); + title = imgMap.get("labeltitle"); + if(json.bool("bTemperature")) items.add(new Item("temperature", imgMap.remove("labeltemperature"), imgMap.remove("unit_celsius"))); + if(json.bool("bHumidity")) items.add(new Item("humidity", imgMap.remove("labelhumidity"), imgMap.remove("unit_humidity"))); + if(json.bool("bNoise")) items.add(new Item("noise", imgMap.remove("labelnoise"), imgMap.remove("unit_noise"))); + if(json.bool("bWindSpeed")) items.add(new Item("windSpeed", imgMap.remove("labelwindSpeed"), imgMap.remove("unit_windspeed"))); + if(json.bool("bWindDirection")) items.add(new Item("windDirection", imgMap.remove("labelwindDirection"), null)); + if(json.bool("bPM25")) items.add(new Item("pm2.5", imgMap.remove("labelpm25"), imgMap.get("unit_pm10"))); + if(json.bool("bPM10")) items.add(new Item("pm10", imgMap.remove("labelpm10"), imgMap.get("unit_pm10"))); + } + digitHeight = imgMap.get("0").getHeight(); + if(spaceWidth==0) spaceWidth = imgMap.get("0").getWidth(); + var scrollSpeed = json.dbl("scrollSpeed"); + if(scrollSpeed==0) { + var scrollDur = json.dbl("iScrollSpeed"); + if(scrollDur==0) return; + scrollSpeed = 1000 / scrollDur; + } + interval = step = 1; + if(scrollSpeed > 60) step = (int) Math.round(scrollSpeed/60); + else if(scrollSpeed < 60) interval = (int) Math.round(60/scrollSpeed); + if(isScroll) prog.calls.add(this); + } + + public static Method method; + static { + try { + method = Intent.class.getMethod("getExtra", String.class, Object.class); + } catch (Exception e) { + e.printStackTrace(); + } + } + public void onReceive(Intent intent) { + try { + for(var item : items) { + item.nums.clear(); + if(item.unit==null) { + var num = intent.getIntExtra(item.key, -1); + if(num>=0 && num<=15) { + if(directs[num].length() < 3) item.nums.add(imgMap.get(directs[num])); + else { + item.nums.add(imgMap.get(directs[num].substring(0, 1))); + item.nums.add(imgMap.get(directs[num].substring(1))); + } + } else { + var img = imgMap.get("-"); + item.nums.add(img); + item.nums.add(img); + } + } else { + var num = ((Number) method.invoke(intent, item.key, -999)).floatValue(); + var str = num==-999 || (num==-1 && ! item.key.endsWith("rature")) ? "--" : NumFmts.zz().format(num); + for(int cc=0; cc imgs; + Rect rect = new Rect(); + int interval, cur, end, step; + char direct; + + public SrcScroll(Prog prog, ArrayList imgs, char direct, double speed) { + super(prog.getContext()); + this.imgs = imgs; + this.direct = direct; + var img0 = imgs.get(0); + Util.println(" SrcScroll img cnt "+imgs.size()+", "+img0.getWidth()+"x"+img0.getHeight()+" direct "+direct+" speed "+speed); + if(img0.getWidth() > 4096) { + imgs.clear(); + var rem = img0.getWidth(); + do { + imgs.add(Bitmap.createBitmap(img0, img0.getWidth()-rem, 0, Math.min(4096, rem), img0.getHeight())); + rem -= 4096; + } while (rem>0); + } else if(img0.getHeight() > 4096) { + imgs.clear(); + var rem = img0.getHeight(); + do { + imgs.add(Bitmap.createBitmap(img0, 0, img0.getHeight()-rem, img0.getWidth(), Math.min(4096, rem))); + rem -= 4096; + } while (rem>0); + } + + if(speed==0) return; + int width = 0, height = 0; + for(var img : imgs) { + width += img.getWidth(); + height += img.getHeight(); + } + if(direct=='l') end = -(width-step); + else if(direct=='r') end = width-step; + else if(direct=='t') end = -(height-step); + else if(direct=='b') end = height-step; + else direct = 0; + if(direct==0) return; + + interval = step = 1; + if(speed > 60) step = (int) Math.round(speed/60); + else if(speed < 60) interval = (int) Math.round(60/speed); + prog.calls.add(this); + } + @Override + protected void onDraw(@NonNull Canvas canvas) { + super.onDraw(canvas); + if(imgs.isEmpty()) return; + try { + drawOther(canvas); + } catch (Throwable e) { + setVisibility(GONE); + imgs.clear(); + Util.printStackTrace(e); + } + } + public void drawOther(Canvas canvas) { + if(! canvas.getClipBounds(rect)) { + rect.left = rect.top = 0; + rect.right = getWidth(); + rect.bottom = getHeight(); + } + if(direct=='l') { + int ii = 0, x = cur; + do { + if(x > rect.left-imgs.get(ii).getWidth() && x < rect.right) canvas.drawBitmap(imgs.get(ii), x, 0, null); + x += imgs.get(ii).getWidth(); + if(++ii >= imgs.size()) ii = 0; + } while(x < rect.right); + } else if(direct=='r') { + int ii = imgs.size()-1, x = cur + getWidth(); + boolean con1; + do { + x -= imgs.get(ii).getWidth(); + con1 = x > rect.left-imgs.get(ii).getWidth(); + if(con1 && x < rect.right) canvas.drawBitmap(imgs.get(ii), x, 0, null); + if(--ii < 0) ii = imgs.size()-1; + } while(con1); + } else if(direct=='t') { + int ii = 0, y = cur; + do { + if(y > rect.top-imgs.get(ii).getHeight() && y < rect.bottom) canvas.drawBitmap(imgs.get(ii), 0, y, null); + y += imgs.get(ii).getHeight(); + if(++ii >= imgs.size()) ii = 0; + } while(y < rect.bottom); + } else if(direct=='b') { + int ii = imgs.size()-1, y = cur + getHeight(); + boolean con1; + do { + y -= imgs.get(ii).getHeight(); + con1 = y > rect.top-imgs.get(ii).getHeight(); + if(con1 && y < rect.bottom) canvas.drawBitmap(imgs.get(ii), 0, y, null); + if(--ii < 0) ii = imgs.size()-1; + } while(con1); + } else canvas.drawBitmap(imgs.get(0), 0, 0, null); + } + + int freshCnt; + + @Override + public void doFrame(long ms) { + if(! isShown()) { + freshCnt = cur = 0; + return; + } + if(freshCnt < interval) freshCnt++; + else { + freshCnt = 1; + if(direct=='t' || direct=='l') { + if(cur <= end) cur -= end; + else cur -= step; + } else if(direct=='b' || direct=='r') { + if(cur >= end) cur -= end; + else cur += step; + } + invalidate(); + } + } +} \ No newline at end of file diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcSensor.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcSensor.java new file mode 100644 index 0000000..50fef31 --- /dev/null +++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcSensor.java @@ -0,0 +1,159 @@ +package com.xixun.xixunplayer; + +import android.annotation.SuppressLint; +import android.content.Intent; +import android.graphics.Color; +import android.view.Choreographer; +import android.webkit.WebView; + +import java.text.DecimalFormat; +import java.util.ArrayList; + +import gnph.util.JSMap; +import gnph.util.NumFmts; +import gnph.util.Txts; +import gnph.util.URLConn; + +public class SrcSensor extends WebView implements IntentReceiver, Choreographer.FrameCallback { + + static String directs[] = {"NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW", "N"}; + MainActivity act; + ArrayList htmls; + String prefix, url, text; + int interval, pageDur, cur; + + @SuppressLint("SetJavaScriptEnabled") + public SrcSensor(Prog prog, JSMap json, int width, int height) { + super(prog.getContext()); + act = (MainActivity) prog.getContext(); + setBackgroundColor(Color.TRANSPARENT); + setVerticalScrollBarEnabled(false); + setHorizontalScrollBarEnabled(false); + setInitialScale(100); + getSettings().setJavaScriptEnabled(true); + + var html = json.stnn("html").replace("%Co2", "%CO2").trim(); + htmls = Txts.split(html, "

", 1); + pageDur = json.intg("speed")*1000; + if(pageDur==0) pageDur = json.intg("timeSpan")*1000 / htmls.size(); + url = json.str("sUrl"); + interval = json.intg("sInterval") * 1000; + if(url!=null && interval>0 && html.contains("%s")) { + nextMs = System.currentTimeMillis() + interval; + Util.println("url: "+url); + try { + text = URLConn.send(url); + } catch (Exception e) { + text = ""; + Util.printStackTrace(e); + } + } + prefix = ""; + prog.calls.add(this); + } + + static DecimalFormat fmt0 = NumFmts.newDecFmt("0"); + public void onReceive(Intent intent) { + try { + var htm = htmls.get(cur); + var temp = intent.getFloatExtra("temperature", -99f); + var fa = temp * 1.8f + 32; + var noTemp = temp==-99f; + var t2 = noTemp ? "--" : NumFmts.fix2().format(temp); + var t1 = noTemp ? "--" : NumFmts.fix1().format(temp); + var tn = noTemp ? "--" : String.valueOf(temp); + var t0 = noTemp ? "--" : fmt0.format(temp); + var f2 = noTemp ? "--" : NumFmts.fix2().format(fa); + var f1 = noTemp ? "--" : NumFmts.fix1().format(fa); + var fn = noTemp ? "--" : String.valueOf(fa); + var f0 = noTemp ? "--" : fmt0.format(fa); + + var humi = intent.getFloatExtra("humidity", -1); + var h = humi == -1 ? "--" : String.valueOf(humi); + var noise = intent.getFloatExtra("noise", -1); + var windSpeed = intent.getFloatExtra("windSpeed", -1); + int pm2 = intent.getIntExtra("pm2.5", -1); + int pm10 = intent.getIntExtra("pm10", -1); + int windDir = intent.getIntExtra("windDirection", -1); + int SO2 = intent.getIntExtra("SO2", -1); + int NO2 = intent.getIntExtra("NO2", -1); + int CO2 = intent.getIntExtra("CO2", -1); + int CO = intent.getIntExtra("CO", -1); + int O3 = intent.getIntExtra("O3", -1); + var pressure = intent.getFloatExtra("pressure", -1); + var rainfall = intent.getFloatExtra("rainfall", -1); + int radiation = intent.getIntExtra("radiation", -1); + int beam = intent.getIntExtra("beam", -1); + //int brightness = intent.getIntExtra("brightness", -1); + var dp = intent.getFloatExtra("dpTemperature", -99f); + + if(text!=null) htm = htm.replace("%s", text); + htm = htm.replace("%T2", t2 + '℃').replace("%T1", t1 + '℃').replace("%T", tn + '℃').replace("%c2", t2).replace("%c1", t1).replace("%c", t0) + .replace("%t2", f2 + '℉').replace("%t1", f1 + '℉').replace("%t", fn + '℉').replace("%f2", f2).replace("%f1", f1).replace("%f", f0) + .replace("%RH", h+'%').replace("%h", h) + .replace("%ns", (noise == -1 ? "--" : String.valueOf(noise)) + "dB") + .replace("%ws", (windSpeed == -1 ? "--" : String.valueOf(windSpeed)) + "m/s") + .replace("%wd", windDir<0||windDir>=directs.length ? "--" : directs[windDir]) + .replace("%pm2", (pm2 == -1 ? "--" : String.valueOf(pm2)) + "μg/m³") + .replace("%pm10", (pm10 == -1 ? "--" : String.valueOf(pm10)) + "μg/m³") + + .replace("%SO2", (SO2 == -1 ? "--" : String.valueOf(SO2)) +"ppb") + .replace("%NO2", (NO2 == -1 ? "--" : String.valueOf(NO2)) +"ppb") + .replace("%CO2", (CO2 == -1 ? "--" : String.valueOf(CO2)) +"ppm") + .replace("%CO", (CO == -1 ? "--" : String.valueOf(CO)) +"ppb") + .replace("%O3", (O3 == -1 ? "--" : String.valueOf(O3)) +"ppb") + .replace("%dp", (dp == -1 ? "--" : String.valueOf(dp)) +'℃') + .replace("%pressure", (pressure == -1 ? "--" : String.valueOf(pressure))+"hPa") + .replace("%rainfall", (rainfall == -1 ? "--" : String.valueOf(rainfall))+"mm") + .replace("%radiation", (radiation == -1 ? "--" : String.valueOf(radiation))+"W/m²") + .replace("%bm", (beam == -1 ? "--" : String.valueOf(beam))+"lux"); + + loadDataWithBaseURL(null, prefix+htm+"

", "text/html", "UTF-8", null); + } catch (Exception e) { + Util.printStackTrace(e); + } + } + + long nextPageMs, nextMs; + boolean isFirst = true; + + @Override + public void doFrame(long ms) { + if(! isShown()) { + if(! isFirst) { + act.environs.remove(this); + isFirst = true; + } + return; + } + boolean needRefresh = false; + if(isFirst) { + isFirst = false; + cur = 0; + nextPageMs = ms + pageDur; + act.environs.add(this); + needRefresh = true; + } else if(ms>=nextPageMs) { + nextPageMs += pageDur; + if(cur>=htmls.size()-1) cur = 0; + else cur++; + needRefresh = true; + } + if(text!=null && ms>=nextMs) { + nextMs = ms + interval; + try { + text = URLConn.send(url); + needRefresh = true; + } catch (Exception e) { + Util.printStackTrace(e); + } + } + if(needRefresh) onReceive(act.environIntent); + } +} diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcTimer.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcTimer.java new file mode 100644 index 0000000..02e632f --- /dev/null +++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcTimer.java @@ -0,0 +1,158 @@ +package com.xixun.xixunplayer; + +import android.annotation.SuppressLint; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.view.Choreographer; +import android.view.View; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.HashMap; + +import gnph.util.JSMap; +import gnph.util.NumFmts; + +@SuppressLint("ViewConstructor") +public class SrcTimer extends View implements Choreographer.FrameCallback { + + HashMap imgMap = new HashMap<>(); + ArrayList imgs = new ArrayList<>(); + Bitmap text, day, hour, min, sec; + int spaceWidth, len; + Paint paint = new Paint(); + long targetTime; + boolean isDown; + boolean isMultiline; + boolean hasDay, hasHour, hasMin, hasSec; + + public SrcTimer(Prog prog, JSMap json) { + super(prog.getContext()); + var imgEntrys = json.jsmap("imgs").entrySet(); + for(var imgEntry : imgEntrys) imgMap.put(imgEntry.getKey(), BitmapFactory.decodeFile(Util.programDir+"/"+imgEntry.getValue())); + text = imgMap.get("text"); + day = imgMap.get("day"); + hour = imgMap.get("hour"); + min = imgMap.get("min"); + sec = imgMap.get("sec"); + spaceWidth = (int) Math.round(json.dbl("spaceWidth")); + isDown = json.bool("isDown"); + var dateFmt = new SimpleDateFormat("y-M-d H:m:s"); + try { + targetTime = dateFmt.parse(json.stnn("targetTime")).getTime() / 1000; + } catch (Exception e) { + Util.makeText(prog.getContext(), Util.toStr(e)).show(); + Util.printStackTrace(e); + } + hasDay = json.bool("hasDay"); + hasHour = json.bool("hasHour"); + hasMin = json.bool("hasMin"); + hasSec = json.bool("hasSec"); + isMultiline = json.bool("isMultiline"); + paint.setTextAlign(Paint.Align.CENTER); + try { + setBackgroundColor(Color.parseColor(json.stnn("backColor"))); + } catch (Exception ignored) {} + prog.calls.add(this); + } + + void cal() { + var secs = isDown ? targetTime - lastSec : lastSec - targetTime; + if(secs < 0) secs = 0; + len = 0; + imgs.clear(); + if(text!=null && ! isMultiline) { + imgs.add(text); + imgs.add(null); + len += text.getWidth(); + len += spaceWidth; + } + if(hasDay) { + var str = Long.toString(secs/86400); + for(int cc=0; cc{ + ijkPlayer.setOnPreparedListener(null); + bitRate = ijkPlayer.getBitRate(); + var diff = dur - ijkPlayer.getDuration(); + if(diff>0 && diff<=1000) ijkPlayer.setLooping(false); + if(isShown()) start(); + }); + ijkPlayer.setOnErrorListener((IMediaPlayer var1, int var2, int var3)->{ + Util.println(" Video Error: "+var1+" "+var2+" "+var3); + return true; + }); + ijkPlayer.prepareAsync(); + } catch (Throwable e) { + Util.makeText(getContext(), Util.toStr(e)).show(); + Util.printStackTrace(e); + ijkPlayer = null; + } + } + @Override + public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) { + Util.println(" SurfaceTexture Available "+(ijkPlayer==null?"ijkPlayer==null":"")); + if(ijkPlayer!=null) ijkPlayer.setSurface(new Surface(surface)); + } + @Override + public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) {} + @Override + public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) { + Util.println(" SurfaceTexture Destroyed"); + return false; + } + @Override + public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {} + + void start() { + if(ijkPlayer!=null) { + ijkPlayer.seekTo(seekTo); + ijkPlayer.start(); + } + } + void pause() { + if(ijkPlayer!=null) { + ijkPlayer.pause(); + ijkPlayer.seekTo(0); + } + } + void release() { + if(ijkPlayer!=null) { + ijkPlayer.release(); + ijkPlayer = null; + } + } + + String getState() { + return "null"; + } + + @Override + public void onVisibilityAggregated(boolean isVisible) { + super.onVisibilityAggregated(isVisible); + if(isVisible) { + start(); + if(isLive) ijkPlayer.setVolume(vol, vol); + } else if(isLive) ijkPlayer.setVolume(0, 0); + else { + seekTo = 0; + pause(); + } + } +} \ No newline at end of file diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideo2.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcVideo2.java similarity index 72% rename from XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideo2.java rename to XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcVideo2.java index 5ff8678..8db5dbc 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideo2.java +++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcVideo2.java @@ -1,5 +1,6 @@ package com.xixun.xixunplayer; +import android.annotation.SuppressLint; import android.graphics.SurfaceTexture; import android.media.MediaPlayer; import android.view.Choreographer; @@ -8,38 +9,36 @@ import android.view.TextureView; import androidx.annotation.NonNull; +@SuppressLint("ViewConstructor") public class SrcVideo2 extends TextureView implements TextureView.SurfaceTextureListener, Choreographer.FrameCallback { float vol; + int dur; MediaPlayer player; - char state; boolean isLive; - public SrcVideo2(Prog prog, String path, float vol, boolean isLive) { + public SrcVideo2(Prog prog, String path, float vol, int dur, boolean isLive) { super(prog.getContext()); this.vol = vol; + this.dur = dur; this.isLive = isLive; player = new MediaPlayer(); - setSurfaceTextureListener(this); - player.setLooping(true); - player.setVolume(vol, vol); - player.setOnPreparedListener((MediaPlayer player)->{ - player.setOnPreparedListener(null); - Util.println(" ------Prepared isShown"+isShown()+" "+this); - if(isShown()) { - player.start(); - state = 'S'; - } else state = 'P'; - prog.calls.add(this); - }); - player.setOnErrorListener((MediaPlayer mp, int what, int extra)->{ - var err = "Media Error: "+getErrorName(what)+". "+getErrorName(extra); - Util.makeText(getContext(), err).show(); - Util.println(err); - return true; - }); try { + setSurfaceTextureListener(this); player.setDataSource(path); + player.setLooping(true); + player.setVolume(vol, vol); + player.setOnPreparedListener((MediaPlayer player)->{ + player.setOnPreparedListener(null); + var diff = dur - player.getDuration(); + if(diff>0 && diff<=1000) player.setLooping(false); + if(isShown()) player.start(); + //prog.calls.add(this); + }); + player.setOnErrorListener((MediaPlayer mp, int what, int extra)->{ + Util.println("Media Error: "+getErrorName(what)+". "+getErrorName(extra)); + return true; + }); player.prepareAsync(); } catch (Exception e) { Util.printStackTrace(e); @@ -48,7 +47,6 @@ public class SrcVideo2 extends TextureView implements TextureView.SurfaceTexture } @Override public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) { - setSurfaceTextureListener(null); if(player!=null) player.setSurface(new Surface(surface)); } @Override @@ -63,15 +61,12 @@ public class SrcVideo2 extends TextureView implements TextureView.SurfaceTexture @Override public void onVisibilityAggregated(boolean isVisible) { super.onVisibilityAggregated(isVisible); - if(state==0) return; if(isVisible) { - Util.println(" ------start "+this); player.seekTo(0); player.start(); if(isLive) player.setVolume(vol, vol); } else if(isLive) player.setVolume(0, 0); else { - Util.println(" ------pause "+this); player.pause(); player.seekTo(0); } diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcVideoSurface.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcVideoSurface.java new file mode 100644 index 0000000..0da577e --- /dev/null +++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcVideoSurface.java @@ -0,0 +1,146 @@ +//package com.xixun.xixunplayer; +// +//import android.annotation.SuppressLint; +//import android.content.Context; +//import android.view.SurfaceHolder; +//import android.view.SurfaceView; +// +//import androidx.annotation.NonNull; +//import androidx.annotation.OptIn; +//import androidx.media3.common.MediaItem; +//import androidx.media3.common.util.UnstableApi; +//import androidx.media3.exoplayer.ExoPlayer; +//import androidx.media3.exoplayer.SeekParameters; +// +//import tv.danmaku.ijk.media.player.IMediaPlayer; +//import tv.danmaku.ijk.media.player.IjkMediaPlayer; +// +//@SuppressLint("ViewConstructor") +//public class SrcVideoSurface extends SurfaceView implements SurfaceHolder.Callback { +// +// String path; +// float vol; +// int dur; +// IjkMediaPlayer ijkPlayer; +// ExoPlayer exoPlayer; +// long bitRate; +// boolean isLive; +// +// public SrcVideoSurface(Context context, String path, float vol, int dur, boolean useHW, boolean isLive) { +// super(context); +// this.path = path; +// this.vol = vol; +// this.dur = dur; +// this.isLive = isLive; +// initIjk(useHW); +// } +// +// @OptIn(markerClass = UnstableApi.class) +// void initExo() { +// exoPlayer = new ExoPlayer.Builder(getContext()).build(); +// exoPlayer.setMediaItem(MediaItem.fromUri(path)); +// exoPlayer.setRepeatMode(ExoPlayer.REPEAT_MODE_ONE); +// exoPlayer.setSeekParameters(SeekParameters.CLOSEST_SYNC); +// exoPlayer.setVolume(vol); +// exoPlayer.setVideoSurfaceView(this); +// exoPlayer.prepare(); +// } +// void initIjk(boolean useHW) { +// ijkPlayer = new IjkMediaPlayer(); +// if(useHW) { +// ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-avc", 1); +// ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-hevc", 1); +// } +// ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 0); +// ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzeduration", 1); +// ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 8); +// try { +// getHolder().addCallback(this); +// ijkPlayer.setDataSource(path); +// ijkPlayer.setLooping(true); +// ijkPlayer.setVolume(vol, vol); +// ijkPlayer.setOnPreparedListener((IMediaPlayer var1)->{ +// ijkPlayer.setOnPreparedListener(null); +// bitRate = ijkPlayer.getBitRate(); +// if(bitRate > 12000000) { +// getHolder().removeCallback(this); +// release(); +// initExo(); +// } else { +// var diff = dur - ijkPlayer.getDuration(); +// if(diff>0 && diff<=1000) ijkPlayer.setLooping(false); +// } +// if(isShown()) start(); +// }); +// ijkPlayer.setOnErrorListener((IMediaPlayer var1, int var2, int var3)->{ +// Util.println(" Video Error: "+var1+" "+var2+" "+var3); +// return true; +// }); +// ijkPlayer.prepareAsync(); +// } catch (Throwable e) { +// Util.makeText(getContext(), Util.toStr(e)).show(); +// Util.printStackTrace(e); +// ijkPlayer = null; +// } +// } +// @Override +// public void surfaceCreated(@NonNull SurfaceHolder holder) { +// Util.println(" surfaceCreated "+(ijkPlayer==null?"ijkPlayer==null":"")); +// if(ijkPlayer!=null) ijkPlayer.setDisplay(holder); +// } +// @Override +// public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {} +// @Override +// public void surfaceDestroyed(@NonNull SurfaceHolder holder) {} +// +// void start() { +// if(ijkPlayer!=null) { +// ijkPlayer.seekTo(0); +// ijkPlayer.start(); +// } else if(exoPlayer!=null) { +// if(exoPlayer.getPlaybackState()==ExoPlayer.STATE_IDLE) exoPlayer.prepare(); +// exoPlayer.play(); +// } +// } +// void pause() { +// if(ijkPlayer!=null) { +// ijkPlayer.pause(); +// ijkPlayer.seekTo(0); +// } +// if(exoPlayer!=null && exoPlayer.isPlaying()) { +// exoPlayer.pause(); +// exoPlayer.seekToDefaultPosition(); +// } +// } +// void release() { +// if(ijkPlayer!=null) { +// ijkPlayer.release(); +// ijkPlayer = null; +// } +// if(exoPlayer!=null) { +// exoPlayer.release(); +// exoPlayer = null; +// } +// } +// +// String getState() { +// if(exoPlayer!=null) { +// var state = exoPlayer.getPlaybackState(); +// if(state==ExoPlayer.STATE_IDLE) return "STATE_IDLE"; +// if(state==ExoPlayer.STATE_BUFFERING) return "STATE_BUFFERING"; +// if(state==ExoPlayer.STATE_READY) return "STATE_READY"; +// if(state==ExoPlayer.STATE_ENDED) return "STATE_ENDED"; +// } +// return "null"; +// } +// +// @Override +// public void onVisibilityAggregated(boolean isVisible) { +// super.onVisibilityAggregated(isVisible); +// if(isVisible) { +// start(); +// if(isLive) ijkPlayer.setVolume(vol, vol); +// } else if(isLive) ijkPlayer.setVolume(0, 0); +// else pause(); +// } +//} \ No newline at end of file diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcVisitor.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcVisitor.java new file mode 100644 index 0000000..52356a7 --- /dev/null +++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcVisitor.java @@ -0,0 +1,95 @@ +package com.xixun.xixunplayer; + +import android.annotation.SuppressLint; +import android.graphics.Color; +import android.view.Choreographer; +import android.webkit.WebView; + +import java.nio.charset.StandardCharsets; + +import gnph.util.JSMap; +import gnph.util.URLConn; + +@SuppressLint("ViewConstructor") +public class SrcVisitor extends WebView implements Choreographer.FrameCallback { + + String html, lineHeight, prefix, url; + + public SrcVisitor(Prog prog, JSMap json) { + super(prog.getContext()); + setBackgroundColor(Color.TRANSPARENT); + setVerticalScrollBarEnabled(false); + setHorizontalScrollBarEnabled(false); + setInitialScale(100); + html = json.stnn("html").replace("%{yesterday.", "%{arr.-1."); + lineHeight = json.str("lineHeight"); + url = json.str("url"); + if(url!=null) { + var token = json.str("token"); + if(token!=null) url += "?dataKey="+token; + } + prefix = ""; + if(url!=null) prog.calls.add(this); + } + + public void refresh() { + try { + var htm = html; + try { + var resp = URLConn.send(url); + var data = JSMap.from(resp.getBytes(StandardCharsets.UTF_8)).jsmap("data"); + if(data==null) { + Util.println("SrcVisitor No data: "+resp); + return; + } + int lastEnd = 0, appendIdx = 0; + var buf = new StringBuilder(htm.length()*3/2); + int start; + while((start = htm.indexOf("%{", lastEnd)) > -1) { + int fds = start+2, fde = start+2; + try { + for(; fde<=fds+16; fde++) if(htm.charAt(fde)=='}') break; + if(fde > fds+16) { + lastEnd = fds; + continue; + } + lastEnd = fde+1; + if(fde == fds) continue; + var fd = htm.substring(fds, fde); + var replace = data.str(fd); + if(replace!=null) { + buf.append(htm, appendIdx, start).append(replace); + appendIdx = lastEnd; + } + Util.println("Found: " + htm.substring(start, lastEnd)+" fd "+fd); + } catch (Exception e) { + lastEnd = fds; + Util.printStackTrace(e); + } + } + if(buf.length()>0) { + if(appendIdx < htm.length()) buf.append(htm, appendIdx, htm.length()); + htm = buf.toString(); + } + } catch (Exception e) { + Util.printStackTrace(e); + } + loadDataWithBaseURL(null, prefix+htm+"", "text/html", "UTF-8", null); + } catch (Exception e) { + Util.printStackTrace(e); + } + } + + long nextMs; + + @Override + public void doFrame(long ms) { + if(! isShown()) return; + if(ms>=nextMs) { + nextMs = ms + 15000; + refresh(); + } + } +} diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcWeather.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcWeather.java new file mode 100644 index 0000000..227bdb0 --- /dev/null +++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcWeather.java @@ -0,0 +1,244 @@ +package com.xixun.xixunplayer; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Color; +import android.webkit.WebView; + +import java.util.HashMap; + +import gnph.util.JSList; +import gnph.util.JSMap; +import gnph.util.Txts; +import gnph.util.URLConn; + +@SuppressLint("ViewConstructor") +public class SrcWeather extends WebView { + + String html, lineHeight, prefix, city; + int code; + boolean hasAQI, hasCur, hasDays; + + public SrcWeather(Context context, JSMap json) { + super(context); + setBackgroundColor(Color.TRANSPARENT); + setVerticalScrollBarEnabled(false); + setHorizontalScrollBarEnabled(false); + setInitialScale(100); + html = json.stnn("html").replace("%{yesterday.", "%{arr.-1."); + lineHeight = json.str("lineHeight"); + city = json.str("city"); + code = json.intg("code"); + prefix = ""; + hasAQI = html.contains("%{aqi}"); + hasCur = html.contains("%{current}") || html.contains("%{arr.0."); + hasDays = html.contains("%{arr."); + + nextMs = System.currentTimeMillis() + 1800000; + refresh(); + } + + public void refresh() { + try { + var htm = html; + if(hasAQI) { + try { + JSList aqiForecast = JSMap.fromClose(new URLConn("https://www.ledokcloud.com/weather/whapi/json/alicityweather/aqiforecast5days").writeJson("{\"cityId\":"+code+"}").in()).jsmap("data").jslist("aqiForecast"); + htm = htm.replace("%{aqi}", aqiForecast.get(1).stnn("value")); + } catch (Exception e) { + Util.printStackTrace(e); + } + } + String type = null; + if(hasCur) { + try { + var condition = JSMap.fromClose(new URLConn("https://www.ledokcloud.com/weather/whapi/json/alicityweather/condition").writeJson("{\"cityId\":"+code+"}").in()).jsmap("data").jsmap("condition"); + htm = htm.replace("%{current}", condition.stnn("temp")); + htm = htm.replace("%{arr.0.type}", type = condition.stnn("condition")); + htm = htm.replace("%{arr.0.fx}", condition.stnn("windDir")); + htm = htm.replace("%{arr.0.fl}", condition.stnn("windLevel")); + } catch (Exception e) { + Util.printStackTrace(e); + } + } + if(hasDays) { + try { + JSList days = JSMap.fromClose(new URLConn("https://www.ledokcloud.com/weather/whapi/json/alicityweather/forecast15days").writeJson("{\"cityId\":"+code+"}").in()).jsmap("data").jslist("forecast"); + int lastEnd = 0, appendIdx = 0; + var buf = new StringBuilder(htm.length()*3/2); + int start; + while((start = htm.indexOf("%{arr.", lastEnd)) > -1) { + int idxS = start+6, idxE; + try { + if(htm.charAt(idxS+1)=='.') idxE = idxS+1; + else if(htm.charAt(idxS+2)=='.') idxE = idxS+2; + else { + lastEnd = idxS; + continue; + } + var dd = Integer.parseInt(htm.substring(idxS, idxE))+1; + if(dd>=days.size()) { + lastEnd = idxE+1; + continue; + } + int fds = idxE+1, fde = idxE+2; + for(; fde<=fds+16; fde++) if(htm.charAt(fde)=='}') break; + if(fde > fds+16) { + lastEnd = fds; + continue; + } + var fd = htm.substring(fds, fde); + String replace = null; + if(fd.equals("date")) replace = days.get(dd).stnn("predictDate"); + else if(fd.equals("type")) replace = days.get(dd).stnn("conditionDay"); + else if(fd.equals("high")) replace = days.get(dd).stnn("tempDay"); + else if(fd.equals("low")) replace = days.get(dd).stnn("tempNight"); + else if(fd.equals("fx")) replace = days.get(dd).stnn("windDirDay"); + else if(fd.equals("fl")) replace = days.get(dd).stnn("windLevelDay"); + else if(fd.startsWith("img-")) { + var parts = Txts.split(fd, "-", 1); + String w = "32", h = "32"; + if(parts.size()>=3) { + w = parts.get(1); + h = parts.get(2); + } + replace = ""; + } + lastEnd = fde+1; + if(replace!=null) { + buf.append(htm, appendIdx, start).append(replace); + appendIdx = lastEnd; + } + Util.println("Found: " + htm.substring(start, lastEnd)+" dd "+dd+" fd "+fd); + } catch (Exception e) { + lastEnd = idxS; + Util.printStackTrace(e); + } + } + if(buf.length()>0) { + if(appendIdx < htm.length()) buf.append(htm, appendIdx, htm.length()); + htm = buf.toString(); + } + } catch (Exception e) { + Util.printStackTrace(e); + } + } + loadDataWithBaseURL(null, prefix+htm+"", "text/html", "UTF-8", null); + } catch (Exception e) { + Util.printStackTrace(e); + } + } + + static HashMap dayImgMap = new HashMap<>(); + static HashMap nitImgMap = new HashMap<>(); + + static{ + dayImgMap.put("晴", "0"); + dayImgMap.put("大部晴朗", "0"); + dayImgMap.put("多云", "1"); + dayImgMap.put("少云", "1"); + dayImgMap.put("阴", "2"); + dayImgMap.put("阵雨", "3"); + dayImgMap.put("局部阵雨", "3"); + dayImgMap.put("小阵雨", "3"); + dayImgMap.put("强阵雨", "3"); + dayImgMap.put("阵雪", "13"); + dayImgMap.put("小阵雪", "13"); + dayImgMap.put("雾", "18"); + dayImgMap.put("冻雾", "18"); + dayImgMap.put("沙尘暴", "20"); + dayImgMap.put("浮尘", "29"); + dayImgMap.put("尘卷风", "29"); + dayImgMap.put("扬沙", "29"); + dayImgMap.put("强沙尘暴", "20"); + dayImgMap.put("霾", "45"); + dayImgMap.put("雷阵雨", "4"); + dayImgMap.put("雷电", "4"); + dayImgMap.put("雷暴", "4"); + dayImgMap.put("雷阵雨伴有冰雹", "5"); + dayImgMap.put("冰雹", "5"); + dayImgMap.put("冰针", "5"); + dayImgMap.put("冰粒", "5"); + dayImgMap.put("雨夹雪", "6"); + dayImgMap.put("小雨", "7"); + dayImgMap.put("中雨", "8"); + dayImgMap.put("大雨", "9"); + dayImgMap.put("暴雨", "10"); + dayImgMap.put("大暴雨", "10"); + dayImgMap.put("特大暴雨", "10"); + dayImgMap.put("小雪", "14"); + dayImgMap.put("中雪", "15"); + dayImgMap.put("大雪", "16"); + dayImgMap.put("暴雪", "17"); + dayImgMap.put("冻雨", "19"); + dayImgMap.put("雪", "15"); + dayImgMap.put("雨", "8"); + dayImgMap.put("小到中雨", "7"); + dayImgMap.put("中到大雨", "9"); + dayImgMap.put("大到暴雨", "10"); + dayImgMap.put("小到中雪", "15"); + dayImgMap.put("无天气类型", "unknown"); + + nitImgMap.put("晴", "30"); + nitImgMap.put("大部晴朗", "30"); + nitImgMap.put("多云", "31"); + nitImgMap.put("少云", "31"); + nitImgMap.put("阴", "2"); + nitImgMap.put("阵雨", "33"); + nitImgMap.put("局部阵雨", "33"); + nitImgMap.put("小阵雨", "33"); + nitImgMap.put("强阵雨", "33"); + nitImgMap.put("阵雪", "34"); + nitImgMap.put("小阵雪", "4"); + nitImgMap.put("雾", "32"); + nitImgMap.put("冻雾", "32"); + nitImgMap.put("沙尘暴", "36"); + nitImgMap.put("浮尘", "35"); + nitImgMap.put("尘卷风", "5"); + nitImgMap.put("扬沙", "35"); + nitImgMap.put("强沙尘暴", "36"); + nitImgMap.put("霾", "46"); + nitImgMap.put("雷阵雨", "4"); + nitImgMap.put("雷电", "4"); + nitImgMap.put("雷暴", "4"); + nitImgMap.put("雷阵雨伴有冰雹", "5"); + nitImgMap.put("冰雹", "5"); + nitImgMap.put("冰针", "5"); + nitImgMap.put("冰粒", "5"); + nitImgMap.put("雨夹雪", "6"); + nitImgMap.put("小雨", "7"); + nitImgMap.put("中雨", "8"); + nitImgMap.put("大雨", "9"); + nitImgMap.put("暴雨", "10"); + nitImgMap.put("大暴雨", "10"); + nitImgMap.put("特大暴雨", "10"); + nitImgMap.put("小雪", "14"); + nitImgMap.put("中雪", "15"); + nitImgMap.put("大雪", "16"); + nitImgMap.put("暴雪", "17"); + nitImgMap.put("冻雨", "19"); + nitImgMap.put("雪", "15"); + nitImgMap.put("雨", "8"); + nitImgMap.put("小到中雨", "7"); + nitImgMap.put("中到大雨", "9"); + nitImgMap.put("大到暴雨", "10"); + nitImgMap.put("小到中雪", "15"); + nitImgMap.put("无天气类型", "unknown"); + } + + long nextMs; + + @Override + public void onVisibilityAggregated(boolean isVisible) { + super.onVisibilityAggregated(isVisible); + if(isVisible) { + var ms = System.currentTimeMillis(); + if(ms>=nextMs) { + nextMs = ms + 1800000; + refresh(); + } + } + } +} diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcWeb.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcWeb.java new file mode 100644 index 0000000..92bc7c6 --- /dev/null +++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/SrcWeb.java @@ -0,0 +1,79 @@ +package com.xixun.xixunplayer; + +import android.annotation.SuppressLint; +import android.graphics.Color; +import android.net.http.SslError; +import android.view.Choreographer; +import android.webkit.SslErrorHandler; +import android.webkit.WebView; +import android.webkit.WebViewClient; + +import gnph.util.JSMap; + +@SuppressLint("ViewConstructor") +public class SrcWeb extends WebView implements Choreographer.FrameCallback { + + String url; + int refresh; + + @SuppressLint("SetJavaScriptEnabled") + public SrcWeb(Prog prog, JSMap json) { + super(prog.getContext()); + var settings = getSettings(); + settings.setJavaScriptEnabled(true); + settings.setDomStorageEnabled(true); + settings.setLoadsImagesAutomatically(true); + setVerticalScrollBarEnabled(false); + setHorizontalScrollBarEnabled(false); + setBackgroundColor(Color.TRANSPARENT); + setInitialScale(json.intg("zoom", 100)); + clearCache(true); + setWebViewClient(new WebViewClient() { + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + return false; + } + @Override + public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { + handler.proceed(); //忽略SSL证书错误,加载网页 + } + @Override + public void onPageFinished(WebView view, String url) { + view.loadUrl("javascript:window.scrollTo("+json.str("offX", "0")+", "+json.str("offY", "0")+")"); + } + }); + loadUrl(url = json.str("url")); + refresh = json.intg("refreshSec")*1000; + if(Util.custom==Util.Custom.Yishi) { + prog.calls.add(this); + } else { + if(refresh>0 && url!=null) prog.calls.add(this); + } + nextMs = System.currentTimeMillis() + refresh; + } + + long nextMs; + int w, h; + @Override + public void doFrame(long ms) { + if(! isShown()) return; + + if(Util.custom==Util.Custom.Yishi) { + if(refresh>0 && url!=null && ms>=nextMs) { + nextMs = ms + refresh; + loadUrl(url); + } + if(Util.screenWidth!=w || Util.screenHeight!=h) { + w = Util.screenWidth; + h = Util.screenHeight; + Util.println(" w "+w+" h "+h); + setLayoutParams(new AbsLayout.LayoutParams(0, 0, w, h)); + } + } else { + if(ms>=nextMs) { + nextMs = ms + refresh; + loadUrl(url); + } + } + } +} diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/TCPThread.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/TCPThread.java new file mode 100644 index 0000000..0606659 --- /dev/null +++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/TCPThread.java @@ -0,0 +1,417 @@ +package com.xixun.xixunplayer; + +import static android.view.View.VISIBLE; + +import android.app.ActivityManager; +import android.content.Context; +import android.content.Intent; +import android.graphics.BitmapFactory; +import android.os.Environment; +import android.os.StatFs; +import android.view.WindowManager; +import android.webkit.WebView; + +import androidx.annotation.OptIn; + +import net.lingala.zip4j.ZipFile; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketTimeoutException; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.HashSet; +import java.util.TimeZone; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; + +import gnph.util.IOs; +import gnph.util.JSList; +import gnph.util.JSMap; +import gnph.util.NumFmts; +import gnph.util.O; + +public class TCPThread extends Thread { + + public static void startServer(int port) { + new Thread(()->{ + try { + var serverSocket = new ServerSocket(port); + while(true) { + try { + Util.println("\nAccepting ..."); + var socket = serverSocket.accept(); + new TCPThread(MainActivity.ins, socket).start(); + Util.println("\nAccepted"); + } catch (Throwable e) { + var ins = MainActivity.ins; + if(ins!=null) ins.runOnUiThread(() -> Util.makeText(ins, Util.toStr(e)).show()); + Util.printStackTrace(e); + } + } + } catch (Throwable e) { + var msg = e.getMessage(); + if(msg==null || ! msg.contains("in use")) MainActivity.ins.runOnUiThread(() -> Util.makeText(MainActivity.ins, Util.toStr(e)).show()); + Util.printStackTrace(e); + } + }).start(); + } + + MainActivity main; + Socket socket; + InputStream in; + OutputStream out; + SimpleDateFormat fmt = new SimpleDateFormat("yy-MM-dd HH:mm:ss"); + + public TCPThread(MainActivity main, Socket socket) { + this.main = main; + this.socket = socket; + } + public void run() { + try { + socket.setSoTimeout(600000); + Util.socketThreads.add(this); + in = socket.getInputStream(); + out = socket.getOutputStream(); + HashSet existed = null; + ByteArrayOutputStream progJson = null; + while(true) { + var obj = JSMap.from(in); + var _type = obj.stnn("_type"); + Util.println("_type: "+_type); + if("consult".equals(_type)) { + JSList files = obj.jslist("files"); + if(files==null) new JSMap("_type", _type, "idList", obj.jslist("idList")).write(out); + else { + existed = new HashSet<>(); + for(var fil : files) { + var name = fil.stnn("name"); + var file = new File(Util.programDir + "/" + name); + if(file.isFile() && file.length()==fil.intg("size")) existed.add(name); + } + new JSMap("_type", _type, "existed", existed).write(out); + } + } else if("proStart".equals(_type)) { + Util.downId = 0; + Util.deleteFiles(obj.intg("proSize"), existed); + } else if("fileStart".equals(_type)) { + var size = obj.intg("size"); + var name = obj.stnn("id"); + Util.println(" size: " + size + " name: " + name); + if(name.equals("program")) { + progJson = new ByteArrayOutputStream(); + IOs.writeCloseOut(progJson, in, size); + } else { + var fOut = new FileOutputStream(Util.programDir + "/" + name); + try { + IOs.write(fOut, in, size); + fOut.flush(); + fOut.getFD().sync(); + fOut.close(); + } catch(Throwable e) { + fOut.close(); + new File(Util.programDir + "/" + name).delete(); + } + } + } else if("setBackImg".equals(_type) || "imgFileStart".equals(_type)) { + var fout = new FileOutputStream(Util.backImgFile); + IOs.write(fout, in, obj.intg("size")); + fout.flush(); + fout.getFD().sync(); + fout.close(); + var img = BitmapFactory.decodeFile(Util.backImgFile, Util.noScaled); + if(main!=null) main.runOnUiThread(() -> { + main.backView.cosImg = img; + main.backView.invalidate(); + }); + new JSMap("success", true).write(out); + } else if("proEnd".equals(_type)) { + new JSMap("_type", "AckSuccess", "success", progJson!=null).write(out); + if(progJson!=null) { + var json = progJson.toByteArray(); + progJson = null; + var acti = MainActivity.ins; + if(acti!=null) acti.runOnUiThread(() -> acti.initProg(json)); + else { + var fOut = new FileOutputStream(Util.programDir + "/program"); + fOut.write(json); + fOut.flush(); + fOut.getFD().sync(); + fOut.close(); + if(MainService.ins!=null) { + var intent = new Intent(MainService.ins, MainActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + MainService.ins.startActivity(intent); + } + } + if(MainService.ins!=null) { + var intent = new Intent("com.xixun.AccessibilityService"); + intent.putExtra("newProgram", "TCP"); + MainService.ins.sendBroadcast(intent); + } + } + } else if("playZipTask".equals(_type)) { + var zip = new ZipFile(Util.programDir+"/"+obj.stnn("proName")); + if(zip.isEncrypted()) zip.setPassword(new char[]{'8','8','8'}); + long size = 0; + ByteArrayOutputStream jsonOut = null; + try { + var headers = zip.getFileHeaders(); + for(var header : headers) { + size += header.getUncompressedSize(); + if("program".equals(header.getFileName())) IOs.writeClose(jsonOut = new ByteArrayOutputStream(), zip.getInputStream(header)); + } + if(jsonOut==null) { + Util.println("No 'program' File"); + new JSMap("success", false, "msg", "No 'program' File").write(out); + if(main!=null) main.runOnUiThread(() -> Util.makeText(main, "No program File").show()); + } else if(size==0) { + Util.println("Zip Size is 0"); + new JSMap("success", false, "msg", "Zip Size is 0").write(out); + if(main!=null) main.runOnUiThread(() -> Util.makeText(main, "zip size is 0").show()); + } else { + Util.deleteFiles(size, null); + for(var header : headers) if(! "program".equals(header.getFileName())) { + Util.println(" name: " + header.getFileName()); + var fOut = new FileOutputStream(Util.programDir + "/" + header.getFileName()); + IOs.writeCloseIn(fOut, zip.getInputStream(header)); + fOut.flush(); + fOut.getFD().sync(); + fOut.close(); + } + var json = jsonOut.toByteArray(); + var acti = MainActivity.ins; + if(acti!=null) acti.runOnUiThread(()->acti.initProg(json)); + else { + var fOut = new FileOutputStream(Util.programDir + "/program"); + fOut.write(json); + fOut.flush(); + fOut.getFD().sync(); + fOut.close(); + if(MainService.ins!=null) { + var intent = new Intent(MainService.ins, MainActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + MainService.ins.startActivity(intent); + } + } + new JSMap("success", true).write(out); + if(MainService.ins!=null) { + var intent = new Intent("com.xixun.AccessibilityService"); + intent.putExtra("newProgram", "TCP"); + MainService.ins.sendBroadcast(intent); + } + } + } catch (Exception e) { + Util.printStackTrace(e); + new JSMap("success", false).write(out); + if(main!=null) main.runOnUiThread(() -> Util.makeText(main, Util.toStr(e)).show()); + } + } else if("DelPrograms".equals(_type)) { + Util.downId = 0; + var ok = new AtomicBoolean(false); + if(main!=null) { + var latch = new CountDownLatch(1); + main.runOnUiThread(() -> { + ok.set(main.delProgFile()); + latch.countDown(); + }); + try { + latch.await(); + } catch (InterruptedException ignored) {} + } + new JSMap("success", ok.get()).write(out); + } else if("getProgramName".equals(_type)) { + try { + String name = null; + var ins = MainActivity.ins; + if(ins!=null && ! ins.avas.isEmpty()) name = ins.avas.get(ins.curAva).name; + new JSMap("success", true, "name", name).write(out); + Util.println(" name "+name); + } catch (Exception e) { + Util.printStackTrace(e); + new JSMap("success", false, "msg", Util.toStr(e)).write(out); + } + } else if("DelBackImg".equals(_type)) { + MainActivity.ins.runOnUiThread(() -> { + MainActivity.ins.backView.cosImg = null; + MainActivity.ins.backView.invalidate(); + }); + new JSMap("success", new File(Util.backImgFile).delete()).write(out); + } else if("Config".equals(_type)) { + var entrySet = obj.entrySet(); + for(var entry : entrySet) if(! entry.getKey().equals("_type")) Util.cfg.put(entry.getKey(), entry.getValue()); + try (var fOut = new FileOutputStream(Util.programDir + "/cfg")) { + Util.cfg.write(fOut); + fOut.flush(); + fOut.getFD().sync(); + } + new JSMap("success", new File(Util.backImgFile).delete()).write(out); + } else if("getPlayerState".equals(_type)) { + var state = main==null ? 8 : main.state; + var descs = Util.getState(state); + new JSMap( + "code", state, + "des_en", descs[0], + "des", descs[1] + ).write(out); + } else if("GetInfo".equals(_type)) { + var writer = new OutputStreamWriter(out); + var fmt = new SimpleDateFormat("yy-MM-dd HH:mm:ss.SSS"); + var dur = 0; + if(main==null) writer.append("\nAPP is Closed\n"); + else { + try { + var packageInfo = main.getPackageManager().getPackageInfo(main.getPackageName(), 0); + writer.append("ver: ").append(packageInfo.versionName).append("\n"); + } catch (Exception ignored) {} + if(main.progView!=null) for(var page : main.progView.pages) dur += page.tDur; + writer.append("ProgSend: ").append(fmt.format(new File(Util.programDir + "/program").lastModified())).append(" ProgDur: ").append(String.valueOf(dur)).append("ms"); + writer.append(" Size Avas: ").append(String.valueOf(main.avas.size())).append(" Pages: ").append(main.progView==null ? "null" : String.valueOf(main.progView.pages.size())); + writer.append("\n"); + writer.append(" Launch: ").append(fmt.format(main.launchMilli)).append("\n"); + writer.append(" Sync: ").append(fmt.format(main.syncMs)).append("\n"); + if(main.insView==null) writer.append(" InseView is Null\n"); + if(main.progView==null) writer.append(" ProgView is Null\n"); + if(main.avas.isEmpty()) writer.append(" No Avas\n"); + else writer.append("Page End: ").append(fmt.format(main.avas.get(main.curAva).endMilli)).append(" CurPage: ").append(String.valueOf(main.curAva)).append(" ").append(String.valueOf(main.avas.get(main.curAva).name)).append("\n"); + } + writer.append(" Current: ").append(fmt.format(System.currentTimeMillis())).append(" ").append(String.valueOf(TimeZone.getDefault().getRawOffset()/3600000f)).append(" ").append(TimeZone.getDefault().getDisplayName()).append("\n"); + + if(main!=null) { + var mng = (WindowManager) main.getSystemService(Context.WINDOW_SERVICE); + var display = mng.getDefaultDisplay(); + writer.append("RefreshRate: ").append(String.valueOf(display.getRefreshRate())).append("\n"); + } + + var statFs = new StatFs(Environment.getExternalStorageDirectory().getPath()); + writer.append(" Disk: ").append(String.valueOf(statFs.getTotalBytes()/1000000)).append(" ").append(String.valueOf(statFs.getAvailableBytes()/1000000)).append(" ").append(String.valueOf(statFs.getFreeBytes()/1000000)).append(" (total avail free)\n"); + if(main!=null) { + var actManager = (ActivityManager) main.getSystemService(MainActivity.ACTIVITY_SERVICE); + var memoryInfo = new ActivityManager.MemoryInfo(); + actManager.getMemoryInfo(memoryInfo); + writer.append(" Memory: ").append(String.valueOf(memoryInfo.totalMem/1000000)).append(" ").append(String.valueOf(memoryInfo.availMem/1000000)).append(" ").append(String.valueOf(memoryInfo.threshold/1000000)).append(" ").append(String.valueOf(memoryInfo.lowMemory)).append(" (total avail threshold low)\n"); + } + var runtime = Runtime.getRuntime(); + writer.append("Runtime: ").append(String.valueOf(runtime.maxMemory()/1000000)).append(" ").append(String.valueOf(runtime.totalMemory()/1000000)).append(" ").append(NumFmts.cfix2().format(runtime.freeMemory()*0.000001)).append(" (max total free)\n"); +// writer.append("/proc/stat\n"); +// IOs.writeCloseIn(writer, new FileReader("/proc/stat")); + writer.append("\nSockets ").append(String.valueOf(Util.socketThreads.size())).append("\n"); + for(var socket : Util.socketThreads) writer.append(" ").append(String.valueOf(socket.socket.getInetAddress())).append(":").append(String.valueOf(socket.socket.getPort())).append(" Buf:").append(String.valueOf(socket.socket.getReceiveBufferSize()/1000)).append("k SoLinger:").append(String.valueOf(socket.socket.getSoLinger())).append("\n"); + + writer.append("\n"); + writer.append("Server URL "+Util.serverURL); + writer.append("\n\n"); + writer.append(Util.cfg.toStr()); + writer.append("\n\n"); + + var latch = new CountDownLatch(1); + if(main!=null) main.runOnUiThread(() -> { + if(! main.avas.isEmpty()) { + var page = main.avas.get(main.curAva); + for(var layer : page.layers) for(var src : layer.srcs) if(src.view.getVisibility()==VISIBLE) { + try { + if(src.view instanceof SrcVideo) { + var view = (SrcVideo) src.view; + if(view.ijkPlayer!=null) { + writer.append("VideoPlaying: ").append(String.valueOf(view.ijkPlayer.isPlaying())).append("\tCur/Dur: ").append(String.valueOf(view.ijkPlayer.getCurrentPosition())).append("/").append(String.valueOf(view.ijkPlayer.getDuration())).append("\n"); + var mediaInfo = view.ijkPlayer.getMediaInfo(); + var vStream = mediaInfo.mMeta.mVideoStream; + writer.append(" BitRate: ").append(String.valueOf(view.ijkPlayer.getBitRate()/1000)).append("k\tFPS: ").append(String.valueOf(vStream.mFpsNum/(float)vStream.mFpsDen)).append(" (").append(String.valueOf(vStream.mFpsNum)).append("/").append(String.valueOf(vStream.mFpsDen)).append(")\n"); + var tracks = view.ijkPlayer.getTrackInfo(); + for(var track : tracks) writer.append(" ").append(track.getInfoInline()).append("\n"); + writer.append(" Format: ").append(mediaInfo.mMeta.mFormat).append("\n"); + writer.append("VideoDecoder: ").append(mediaInfo.mVideoDecoder).append(" ").append(mediaInfo.mVideoDecoderImpl).append(" (").append(String.valueOf(view.ijkPlayer.getVideoDecoder())).append(")\n"); + writer.append("AudioDecoder: ").append(mediaInfo.mAudioDecoder).append(" ").append(mediaInfo.mAudioDecoderImpl).append("\n"); + //writer.append("PROFILE: ").append(mediaInfo.mMeta.getString(IjkMediaMeta.IJKM_KEY_CODEC_PROFILE)).append("\n"); + } + writer.append("\n"); + } else if(src.view instanceof SrcWeather) { + var view = (SrcWeather) src.view; + writer.append("SrcWeather html: ").append(view.html).append("\n"); + } else if(src.view instanceof WebView) { + var view = (WebView) src.view; + writer.append("Title: ").append(view.getTitle()).append("\n"); + writer.append(" Url: ").append(view.getUrl()).append("\n"); + writer.append("OriginUrl: ").append(view.getOriginalUrl()).append("\n"); + writer.append("Progress: ").append(String.valueOf(view.getProgress())).append("\n"); + } + } catch (Exception ignored) { + } + } + } + latch.countDown(); + }); + try { + latch.await(); + } catch (InterruptedException ignored) {} + writer.flush(); + } else if("GetLog".equals(_type)) { + out.write(Util.buf.toString().getBytes()); + } else if("ListProgFiles".equals(_type)) { + var files = new File(Util.programDir).listFiles(); + if(files!=null) { + Arrays.sort(files, (f1, f2) -> Long.signum(f2.lastModified() - f1.lastModified())); + var writer = new OutputStreamWriter(out); + for(var file : files) writer.append(fmt.format(new Date(file.lastModified()))).append(' ').append(file.getName()).append(' ').append(String.valueOf(file.length())).append('\n'); + writer.flush(); + } + } else if("GetFile".equals(_type)) { + var name = obj.str("name"); + if(name==null) new JSMap("msg", "name is null").write(out); + else { + var file = new File(Util.programDir+"/"+name); + if(! file.isFile()) new JSMap("msg", "file not exist").write(out); + else { + new JSMap("len", file.length()).write(out); + IOs.writeCloseIn(out, new FileInputStream(file)); + } + } + } else if("GetJsonWithFileInfo".equals(_type)) { + var inse = new File(Util.programDir+"/insert"); + var files = new File(Util.programDir).listFiles(); + if(inse.isFile()) { + var json = IOs.readStrClose(new FileInputStream(inse)); + if(files!=null) for(var file : files) if(! "program".equals(file.getName())) json = json.replace("\""+file.getName()+"\"", "\""+file.getName()+"\"/*"+file.length()+" "+fmt.format(new Date(file.lastModified()))+"*/"); + var writer = new OutputStreamWriter(out); + writer.append("insert:\n"); + writer.append(json); + if(! json.endsWith("\n")) writer.append("\n"); + writer.flush(); + } + var prog = new File(Util.programDir+"/program"); + if(! prog.isFile()) new JSMap("msg", "'program' file not exist").write(out); + else { + var json = IOs.readStrClose(new FileInputStream(prog)); + if(files!=null) for(var file : files) if(! "program".equals(file.getName())) json = json.replace("\""+file.getName()+"\"", "\""+file.getName()+"\"/*"+file.length()+" "+fmt.format(new Date(file.lastModified()))+"*/"); + var writer = new OutputStreamWriter(out); + writer.append("\nprogram:\n"); + writer.append(json); + writer.flush(); + } + } + out.flush(); + Util.println("cmd end"); + } + } catch (Throwable e) { + var emsg = e.getMessage(); + if(e instanceof SocketTimeoutException || "Socket closed".equals(emsg) || (emsg!=null && emsg.endsWith("end-of-input"))) { + Util.println(emsg); + return; + } + if(main!=null) main.runOnUiThread(() -> Util.makeText(main, Util.toStr(e)).show()); + Util.printStackTrace(e); + } finally { + Util.socketThreads.remove(this); + O.close(in, out, socket); + Util.println("conn end\n"); + } + } +} diff --git a/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/Util.java b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/Util.java new file mode 100644 index 0000000..0a84f6b --- /dev/null +++ b/XixunPlayer1/app/src/main/java/com/xixun/xixunplayer/Util.java @@ -0,0 +1,227 @@ +package com.xixun.xixunplayer; + +import android.content.Context; +import android.database.sqlite.SQLiteOpenHelper; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.os.Build; +import android.os.Environment; +import android.os.StatFs; +import android.view.Gravity; +import android.widget.Toast; + +import com.aliyun.imageaudit20191230.models.ScanImageAdvanceRequest; +import com.aliyun.tea.TeaModel; + +import java.io.CharArrayWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Random; +import java.util.Set; +import java.util.Vector; +import java.util.concurrent.CountDownLatch; + +import gnph.util.IOs; +import gnph.util.JSMap; +import gnph.util.O; +import wseemann.media.FFmpegMediaMetadataRetriever; + + +public class Util { + public enum Custom{Normal, Yishi}; + public static final Custom custom = Custom.Normal; + public static JSMap cfg; + public static SQLiteOpenHelper openHelper; + public static String serverURL; + public static volatile long downId; + public static int screenWidth = 1920, screenHeight = 1080; + public static double lat, lng; + public static boolean isScreenOn, logOn; + + public static void initDir(Context ctx) { + var dir = Build.VERSION.SDK_INT < Build.VERSION_CODES.R ? Environment.getExternalStorageDirectory().getAbsolutePath() + "/XixunPlayer" : ctx.getExternalFilesDir(null).getAbsolutePath(); + Util.println("\n---- Init dir "+dir); + Util.programDir = dir + "/program"; + Util.backImgFile = dir + "/background"; + + var program = new File(Util.programDir); + if(program.isFile()) program.delete(); + Util.println(" mkdir: "+program.mkdirs()); + } + + public static final BitmapFactory.Options noScaled = new BitmapFactory.Options(); + static { + noScaled.inScaled = false; + } + public static final Vector socketThreads = new Vector<>(); + public static final HashMap stateDescs = new HashMap<>(); + static { + stateDescs.put(1, new String[]{"Initialize", "初始化"}); + stateDescs.put(2, new String[]{"Schedules is over", "定时节目结束"}); + stateDescs.put(3, new String[]{"No programs waiting to be played", "无待播放的节目"}); + stateDescs.put(4, new String[]{"Delete program", "删除节目"}); + stateDescs.put(5, new String[]{"Program processing", "处理节目中"}); + stateDescs.put(6, new String[]{"Program Processed", "处理节目完成"}); + stateDescs.put(7, new String[]{"Program maybe error", "节目可能有误"}); + stateDescs.put(8, new String[]{"Screen-off", "关屏"}); + stateDescs.put(9, new String[]{"Program's area hasn't arrived yet", "定点节目不在范围"}); + } + public static final String[] stateDescsUnknow = {"Unknown", "未知"}; + + public static StringBuffer buf = new StringBuffer(); + public static Random rand = new Random(); + public static SimpleDateFormat dateFmt = new SimpleDateFormat("yy-MM-dd HH:mm:ss.SSS"); + + public static void println(String msg) { + System.out.println(msg); + if(buf.length()>1000000) buf.replace(0, 100000, ""); + buf.append(msg).append("\n"); + } + public static void printStackTrace(Throwable e) { + println(toStackTrace(e)); + } + + public static String toStr(Throwable e) { + var traces = e.getStackTrace(); + var msg = e.toString(); + for(var trace : traces) if(trace.getClassName().startsWith("com.xixun.xixunplayer.")) { + msg += "\n at "+trace.getFileName()+'.'+trace.getMethodName()+':'+trace.getLineNumber(); + } + return msg; + } + public static String toStackTrace(Throwable e) { + var out = new CharArrayWriter(); + var writer = new PrintWriter(out); + e.printStackTrace(writer); + writer.flush(); + return out.toString(); + } + + public static Toast makeText(Context context, CharSequence text) { + var toast = Toast.makeText(context, text, Toast.LENGTH_LONG); + toast.setGravity(Gravity.TOP | Gravity.LEFT, 0, 0); + return toast; + } + + public static String[] getState(int state) { + var descs = stateDescs.get(state); + return descs!=null ? descs : stateDescsUnknow; + } + + public static String programDir, backImgFile; + public static String getCardId() { + try { + var bytes = IOs.readBytesClose(new FileInputStream("/data/joey/signed/card.id")); + if(bytes.length < 40) return ""; + byte[] cMyKey = new byte[]{97, 119, 38, 3, 46, 112, 36, 93, 58, 100, 103, 62, 115, 112, 114, 51, 43, 61, 2, 101, 119}; + for(int i=0; i<20; ++i) bytes[i] = (byte) (bytes[i * 2] - cMyKey[i] - i - (bytes[i * 2 + 1] - 3)); + var cardId = new String(bytes); + if(cardId.length() > 13) cardId = cardId.substring(0, 13); + return cardId; + } catch (IOException e) { + printStackTrace(e); + return ""; + } + } + + public static void deleteFiles(long proSize, Set keeps) { + var statFs = new StatFs(Environment.getExternalStorageDirectory().getPath()); + var remain = statFs.getAvailableBytes() - proSize - 400000000; + if(remain >= 0) return; + var latch = new CountDownLatch(1); + MainActivity.ins.runOnUiThread(() -> { + System.out.println("stopProg ..."); + MainActivity.ins.stopProg(); + latch.countDown(); + }); + var files = new File(Util.programDir).listFiles(); + if(files == null) return; + Arrays.sort(files, (f1, f2) -> (int) (f1.lastModified() - f2.lastModified())); + try { + latch.await(); + } catch (InterruptedException ignored) {} + for(var file : files) { + if(keeps!=null && keeps.contains(file.getName())) continue; + var len = file.length(); + if(file.delete()) { + remain += len; + if(remain>=0) break; + } + } + } + public static int caseThen(int val, int cas, int then) { + return val==cas ? then : val; + } + + public static com.aliyun.imageaudit20191230.Client createClient() throws Exception { + com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config().setAccessKeyId("LTAI5tJh71Sz9fRM3itMm2jH").setAccessKeySecret("yujMXB95cNTpG4IUJ6eFDhZwJuHl6u"); + config.endpoint = "imageaudit.cn-shanghai.aliyuncs.com"; + return new com.aliyun.imageaudit20191230.Client(config); + } + public static boolean check(Prog view) { + try { + var client = createClient(); + var taskList = new ArrayList(); + var sceneList = new ArrayList(); + int idx = 0; + for(var page : view.pages) for(var layer : page.layers) for(var src : layer.srcs) { + if(src.view instanceof SrcVideo) { + var retriever = new FFmpegMediaMetadataRetriever(); + try { + retriever.setDataSource(((SrcVideo) src.view).path); + var dur = Long.parseLong(retriever.extractMetadata(FFmpegMediaMetadataRetriever.METADATA_KEY_DURATION)); + Util.println(" dur "+dur); + for(long i=0; i + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/XixunPlayer1/app/src/main/res/drawable/ic_launcher_foreground.xml b/XixunPlayer1/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/XixunPlayer1/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/XixunPlayer1/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/XixunPlayer1/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000..c209e78 Binary files /dev/null and b/XixunPlayer1/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/XixunPlayer1/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/XixunPlayer1/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000..b2dfe3d Binary files /dev/null and b/XixunPlayer1/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/XixunPlayer1/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/XixunPlayer1/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000..4f0f1d6 Binary files /dev/null and b/XixunPlayer1/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/XixunPlayer1/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/XixunPlayer1/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/XixunPlayer1/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/XixunPlayer1/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/XixunPlayer1/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000..948a307 Binary files /dev/null and b/XixunPlayer1/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/XixunPlayer1/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/XixunPlayer1/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..1b9a695 Binary files /dev/null and b/XixunPlayer1/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/XixunPlayer1/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/XixunPlayer1/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/XixunPlayer1/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/XixunPlayer1/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/XixunPlayer1/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/XixunPlayer1/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/XixunPlayer1/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/XixunPlayer1/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/XixunPlayer1/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/XixunPlayer1/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/XixunPlayer1/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/XixunPlayer1/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/XixunPlayer1/app/src/main/res/values/colors.xml b/XixunPlayer1/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..c8524cd --- /dev/null +++ b/XixunPlayer1/app/src/main/res/values/colors.xml @@ -0,0 +1,5 @@ + + + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/XixunPlayer1/app/src/main/res/values/strings.xml b/XixunPlayer1/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..a619236 --- /dev/null +++ b/XixunPlayer1/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + XixunPlayer + \ No newline at end of file diff --git a/XixunPlayer1/build.gradle b/XixunPlayer1/build.gradle new file mode 100644 index 0000000..3daed1d --- /dev/null +++ b/XixunPlayer1/build.gradle @@ -0,0 +1,4 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +plugins { +id 'com.android.application' version '8.1.1' apply false +} \ No newline at end of file diff --git a/XixunPlayer1/gradle.properties b/XixunPlayer1/gradle.properties new file mode 100644 index 0000000..3e927b1 --- /dev/null +++ b/XixunPlayer1/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/XixunPlayer1/gradle/wrapper/gradle-wrapper.jar b/XixunPlayer1/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e708b1c Binary files /dev/null and b/XixunPlayer1/gradle/wrapper/gradle-wrapper.jar differ diff --git a/XixunPlayer1/gradle/wrapper/gradle-wrapper.properties b/XixunPlayer1/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..d587198 --- /dev/null +++ b/XixunPlayer1/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Oct 11 11:55:49 CST 2023 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/XixunPlayer1/gradlew b/XixunPlayer1/gradlew new file mode 100644 index 0000000..4f906e0 --- /dev/null +++ b/XixunPlayer1/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/XixunPlayer1/gradlew.bat b/XixunPlayer1/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/XixunPlayer1/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/XixunPlayer1/settings.gradle b/XixunPlayer1/settings.gradle new file mode 100644 index 0000000..366ef33 --- /dev/null +++ b/XixunPlayer1/settings.gradle @@ -0,0 +1,18 @@ +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + jcenter() + } +} + +rootProject.name = "XixunPlayer" +include ':app'