From 9b9141ddf75fdbe3de02506455d02b94683ed70e Mon Sep 17 00:00:00 2001 From: Gangphon Date: Fri, 10 Nov 2023 09:47:38 +0800 Subject: [PATCH] xixunplayer --- XixunPlayer/.idea/vcs.xml | 6 ++ .../com/xixun/xixunplayer/EleEnviron.java | 24 +++++--- .../java/com/xixun/xixunplayer/EleScroll.java | 5 +- .../java/com/xixun/xixunplayer/EleVideo.java | 22 ++++++- .../com/xixun/xixunplayer/MainActivity.java | 41 ++++++------- .../main/java/com/xixun/xixunplayer/Page.java | 58 ++++++++---------- .../java/com/xixun/xixunplayer/ProgView.java | 31 ++++++---- .../main/java/com/xixun/xixunplayer/Util.java | 2 + xixuncard | Bin 0 -> 4250 bytes 9 files changed, 110 insertions(+), 79 deletions(-) create mode 100644 XixunPlayer/.idea/vcs.xml create mode 100644 xixuncard diff --git a/XixunPlayer/.idea/vcs.xml b/XixunPlayer/.idea/vcs.xml new file mode 100644 index 0000000..6c0b863 --- /dev/null +++ b/XixunPlayer/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleEnviron.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleEnviron.java index f519025..12dd487 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleEnviron.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleEnviron.java @@ -36,7 +36,7 @@ public class EleEnviron extends View implements Choreographer.FrameCallback { Bitmap title; ArrayList items = new ArrayList<>(); MainActivity act; - int spaceWidth; + int spaceWidth, digitHeight; int interval, cur, end, step; boolean isScroll, isFirst = true; @@ -70,6 +70,7 @@ public class EleEnviron extends View implements Choreographer.FrameCallback { 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(); var scrollSpeed = json.dbl("scrollSpeed"); if(scrollSpeed==0) { var scrollDur = json.dbl("iScrollSpeed"); @@ -128,11 +129,13 @@ public class EleEnviron extends View implements Choreographer.FrameCallback { canvas.drawBitmap(title, x, Math.round((getHeight() - title.getHeight()) / 2), null); x += title.getWidth() + spaceWidth<<1; } + var y = (int) Math.round((getHeight() - digitHeight) / 2); for(var item : items) { - var y = (int) Math.round((getHeight() - item.lable.getHeight()) / 2); - canvas.drawBitmap(item.lable, x, y, null); - x += item.lable.getWidth(); - for(var num : item.nums) { + if(item.lable!=null) { + canvas.drawBitmap(item.lable, x, y, null); + x += item.lable.getWidth(); + } + for(var num : item.nums) if(num!=null) { canvas.drawBitmap(num, x, y, null); x += num.getWidth(); } @@ -150,10 +153,13 @@ public class EleEnviron extends View implements Choreographer.FrameCallback { int i = 0; if(title!=null) canvas.drawBitmap(title, 0, Math.round(itemH * i++ + (itemH - title.getHeight()) / 2), null); for(var item : items) { - var y = (int) Math.round(itemH * i++ + (itemH - item.lable.getHeight()) / 2); - canvas.drawBitmap(item.lable, 0, y, null); - var x = item.lable.getWidth(); - for(var num : item.nums) { + var y = (int) Math.round(itemH * i++ + (itemH - digitHeight) / 2); + var x = 0; + if(item.lable!=null) { + canvas.drawBitmap(item.lable, 0, y, null); + x += item.lable.getWidth(); + } + for(var num : item.nums) if(num!=null) { canvas.drawBitmap(num, x, y, null); x += num.getWidth(); } diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleScroll.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleScroll.java index 2694390..4919478 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleScroll.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleScroll.java @@ -58,8 +58,9 @@ public class EleScroll extends View implements Choreographer.FrameCallback { canvas.drawBitmap(img, 0, cur-img.getHeight(), null); } else canvas.drawBitmap(img, 0, 0, null); if(freshCnt==0 && effect!=0 && interval!=0) choreographer.postFrameCallback(this); - } catch (RuntimeException e) { - ((Page) getParent()).remove(this); + } catch (Throwable e) { + setVisibility(GONE); + img = null; e.printStackTrace(); } } diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleVideo.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleVideo.java index a2797c0..38d0017 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleVideo.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleVideo.java @@ -2,9 +2,10 @@ package com.xixun.xixunplayer; import android.content.Context; import android.media.MediaPlayer; +import android.view.Choreographer; import android.widget.VideoView; -public class EleVideo extends VideoView { +public class EleVideo extends VideoView implements Choreographer.FrameCallback { float vol = 1; @@ -12,9 +13,10 @@ public class EleVideo extends VideoView { super(context); setVideoPath(path); setOnPreparedListener((MediaPlayer player)->{ + player.setLooping(true); if(! isShown()) { - player.pause(); player.seekTo(0); + player.pause(); } if(vol!=1) player.setVolume(vol, vol); setOnPreparedListener(null); @@ -24,6 +26,7 @@ public class EleVideo extends VideoView { return true; }); start(); + choreographer.postFrameCallback(this); } @Override @@ -37,11 +40,24 @@ public class EleVideo extends VideoView { if(isVisible) { if(! isPlaying()) start(); } else { - pause(); seekTo(0); + pause(); + choreographer.postFrameCallback(this); } } + Choreographer choreographer = Choreographer.getInstance(); + @Override + public void doFrame(long frameTimeNanos) { + if(isShown()) return; + if(isPlaying()) { + seekTo(0); + pause(); + System.out.println("pause in doFrame()"); + } + choreographer.postFrameCallback(this); + } + static String getErrorName(int code) { if(code==MediaPlayer.MEDIA_ERROR_UNKNOWN) return "UNKNOWN"; if(code==MediaPlayer.MEDIA_ERROR_SERVER_DIED) return "SERVER_DIED"; 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 681f1a5..e921584 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/MainActivity.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/MainActivity.java @@ -20,6 +20,7 @@ import android.os.StatFs; import android.view.Choreographer; import androidx.activity.ComponentActivity; +import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; @@ -37,8 +38,6 @@ import java.net.ServerSocket; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; -import java.util.Timer; -import java.util.TimerTask; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; @@ -75,9 +74,9 @@ public class MainActivity extends ComponentActivity implements Choreographer.Fra } } @Override - public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); - if(requestCode==999 && grantResults!=null && grantResults.length > 0 && grantResults[0]==PackageManager.PERMISSION_GRANTED && backView==null) init(); + if(requestCode==999 && grantResults.length > 0 && grantResults[0]==PackageManager.PERMISSION_GRANTED && backView==null) init(); } public void init() { @@ -100,9 +99,11 @@ public class MainActivity extends ComponentActivity implements Choreographer.Fra System.out.println("Bind cardsystem aidl service success"); var service = CardService.Stub.asInterface(iBinder); try { + Util.isScreenOn = service.isScreenOpen(); backView = new BackView(MainActivity.this, service.getScreenWidth(), service.getScreenHeight()); state = 5; - initProg(); + if(Util.isScreenOn) initProg(); + else state = 8; if(progView==null) setContentView(backView); } catch (RemoteException e) { Util.makeText(MainActivity.this, e.getMessage()).show(); @@ -118,7 +119,8 @@ public class MainActivity extends ComponentActivity implements Choreographer.Fra @Override public void onReceive(Context context, Intent intent) { System.out.println("Receive PAUSE_PLAYER"); - if(intent.getBooleanExtra("pause", false)) { + Util.isScreenOn = ! intent.getBooleanExtra("pause", false); + if(! Util.isScreenOn) { state = 8; if(progView!=null) { progView.setVisibility(GONE); @@ -334,12 +336,10 @@ public class MainActivity extends ComponentActivity implements Choreographer.Fra var milli = System.currentTimeMillis(); var lastPage = page(curAva); if(milli >= syncMilli) { - lastPage.setVisibility(GONE); - if(lastPage.hasVideo && progView.getChildCount()>1) lastPage.setLeft(lastPage.getRight()+1); + lastPage.hide(); syncProg(milli-syncMilli>=1000 ? milli : syncMilli); } else if(milli >= lastPage.endMilli) { - lastPage.setVisibility(GONE); - if(lastPage.hasVideo && curTimes==lastPage.repeatTimes && progView.getChildCount()>1) lastPage.setLeft(lastPage.getRight()+1); + lastPage.hide(); if(curTimes < lastPage.repeatTimes) curTimes++; else { curTimes = 1; @@ -353,23 +353,18 @@ public class MainActivity extends ComponentActivity implements Choreographer.Fra page(curAva).setMillis(lastPage.endMilli); } else { for(var layer : lastPage.layers) { - for(var ele : layer.eles) if(ele.isShort) { - if(ele.view.getVisibility()!=VISIBLE) { - if(milli < ele.endMilli && milli >= ele.startMilli) ele.view.setVisibility(VISIBLE); - } else { - if(milli >= ele.endMilli) ele.view.setVisibility(GONE); - } + for(var ele : layer.eles) { + if(ele.view.getVisibility()==VISIBLE) { + if(milli >= ele.endMilli) ele.hide(); + } else if(milli < ele.endMilli && milli >= ele.startMilli) ele.show(); } if(milli >= layer.endMilli) { layer.endMilli += layer.dur; for(var ele : layer.eles) { ele.endMilli += layer.dur; ele.startMilli += layer.dur; - if(ele.view.getVisibility()!=VISIBLE) { - if(milli < ele.endMilli && milli >= ele.startMilli) ele.view.setVisibility(VISIBLE); - } else { - if(milli >= ele.endMilli) ele.view.setVisibility(GONE); - } + if(ele.startTime > 0) ele.hide(); + else ele.show(); } } } @@ -400,7 +395,7 @@ public class MainActivity extends ComponentActivity implements Choreographer.Fra avas.clear(); var dur = 0; Page page; - for(int i=0; i eles = new ArrayList<>(); long endMilli = Long.MAX_VALUE; int dur; - boolean repeat; + boolean isLoop; } public static class Sche { @@ -42,36 +55,19 @@ public class Page extends AbsoluteLayout { ArrayList sches; long endMilli = Long.MAX_VALUE; int dur, repeatTimes; - boolean hasVideo = false; - public Page(Context context) { - super(context); + void hide() { + for(var layer : layers) for(var ele : layer.eles) ele.hide(); } public void setMillis(long milli) { endMilli = milli + dur; for(var layer : layers) { - if(layer.repeat) layer.endMilli = milli + layer.dur; - for(var ele : layer.eles) if(ele.isShort) { - if(ele.startTime > 0) { - ele.startMilli = milli + ele.startTime; - ele.view.setVisibility(GONE); - } else ele.view.setVisibility(VISIBLE); + if(layer.isLoop) layer.endMilli = milli + layer.dur; + for(var ele : layer.eles) { ele.endMilli = milli + ele.endTime; - } - } - if(getLeft() != 0) setLeft(0); - setVisibility(VISIBLE); - } - public void remove(View view) { - view.setVisibility(GONE); - removeView(view); - for(int ll=0; ll 1) layer.eles.remove(ee); - else layers.remove(ll); - return; + ele.startMilli = milli + ele.startTime; + if(ele.startTime == 0) ele.show(); } } } diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/ProgView.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/ProgView.java index 6d421c9..199a335 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/ProgView.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/ProgView.java @@ -5,9 +5,11 @@ import android.net.Uri; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.AbsoluteLayout; +import android.widget.FrameLayout; import android.widget.ImageView; import java.io.File; +import java.util.ArrayList; import gnph.util.JSList; import gnph.util.JSMap; @@ -15,20 +17,22 @@ import pl.droidsonroids.gif.GifImageView; public class ProgView extends AbsoluteLayout { + ArrayList pages = new ArrayList<>(); + public ProgView(JSMap task, int width, int height, Context context) { super(context); - JSList pages = task.jslist("items"); - for(var pageMap : pages) { + JSList jpages = task.jslist("items"); + for(var pageMap : jpages) { var _program = pageMap.jsmap("_program"); JSList layers = _program.jslist("layers"); if(layers.isEmpty()) continue; var splitWidths = _program.jslist("splitWidths"); - var page = new Page(context); + var page = new Page(); page.repeatTimes = pageMap.intg("repeatTimes"); page.parse(pageMap.jslist("schedules")); for(int ll=layers.size()-1; ll>=0; ll--) { var layer = new Page.Layer(); - layer.repeat = layers.get(ll).bool("repeat"); + layer.isLoop = layers.get(ll).bool("repeat"); JSList sources = layers.get(ll).jslist("sources"); var border = layers.get(ll).jsmap("border"); EleBorder bdEle = null; @@ -99,9 +103,11 @@ public class ProgView extends AbsoluteLayout { } else if(ele.type.equals("DigitalClockNew")) ele.view = new EleDigiClock(Util.programDir+"/", source, context); else if(ele.type.equals("AnalogClock")) ele.view = new EleAnaClock(w, h, Util.programDir+"/"+ele.id, source, context); else if(ele.type.equals("Video")) { - page.hasVideo = true; + ele.isVideo = true; var videoView = new EleVideo(Util.programDir + "/" + ele.id, context); - ele.view = videoView; + var frame = new FrameLayout(context); + frame.addView(videoView); + ele.view = frame; var vol = source.intg("vol", 100); if (vol < 100) videoView.vol = vol / 100.0f; } else if(ele.type.equals("Audio")) { @@ -124,7 +130,10 @@ public class ProgView extends AbsoluteLayout { else if(ele.type.equals("EnvironmentalMonitoring")) ele.view = new EleEnviron(Util.programDir+"/", source, context); else continue; if(ele.view==null) continue; - if(w>0) page.addView(ele.view, new AbsoluteLayout.LayoutParams(w, h, x, y)); + if(w > 0) { + ele.view.setVisibility(GONE); + addView(ele.view, new AbsoluteLayout.LayoutParams(w, h, x, y)); + } layer.eles.add(ele); ele = new Page.EleBase(); } @@ -137,7 +146,8 @@ public class ProgView extends AbsoluteLayout { ele.startTime = bdStart; ele.endTime = bdEnd; ele.view = bdEle; - page.addView(ele.view, new AbsoluteLayout.LayoutParams(w, h, x, y)); + ele.view.setVisibility(GONE); + addView(ele.view, new AbsoluteLayout.LayoutParams(w, h, x, y)); layer.eles.add(ele); } if(! layer.eles.isEmpty()) page.layers.add(layer); @@ -156,12 +166,11 @@ public class ProgView extends AbsoluteLayout { continue for_layer; } } else if(ele.endTime > page.dur) ele.endTime = page.dur; - ele.isShort = ele.startTime > 0 || ele.endTime < page.dur; } if(layer.dur > page.dur) layer.dur = page.dur; + if(layer.dur == page.dur) layer.isLoop = false; } - page.setVisibility(GONE); - addView(page, new AbsoluteLayout.LayoutParams(width, height, 0, 0)); + pages.add(page); } } } \ No newline at end of file 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 b462e87..f9deb26 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Util.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Util.java @@ -13,6 +13,8 @@ import gnph.util.IOs; public class Util { + public static boolean isScreenOn; + public static final HashMap stateDescs = new HashMap<>(); static { stateDescs.put(1, new String[]{"Initialize", "初始化"}); diff --git a/xixuncard b/xixuncard new file mode 100644 index 0000000000000000000000000000000000000000..a7b3d6597312f4aae1865fb3adcc52bf5057924b GIT binary patch literal 4250 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 XNF}WqY>t)3hpu}C&^))Ou(*E#A1{*$ literal 0 HcmV?d00001