diff --git a/XixunPlayer/app/build.gradle b/XixunPlayer/app/build.gradle index 6bc8d03..1b5f3cb 100644 --- a/XixunPlayer/app/build.gradle +++ b/XixunPlayer/app/build.gradle @@ -11,7 +11,7 @@ android { minSdk 21 targetSdk 34 versionCode 1 - versionName "2.0.1" + versionName "2.0.4" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleVVV.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleVVV.java deleted file mode 100644 index 232771e..0000000 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleVVV.java +++ /dev/null @@ -1,126 +0,0 @@ -package com.xixun.xixunplayer; - -import android.content.Context; -import android.view.SurfaceHolder; -import android.view.SurfaceView; - -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; - -public class EleVVV extends SurfaceView implements SurfaceHolder.Callback { - - String path; - float vol; - IjkMediaPlayer ijkPlayer; - ExoPlayer exoPlayer; - - public EleVVV(Context context, String path, float vol) { - super(context); - this.path = path; - this.vol = vol; - 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", 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)->{ - if(ijkPlayer.getBitRate() > 12000000) { - getHolder().removeCallback(this); - release(); - initExo(); - } - if(isShown()) start(); - }); - ijkPlayer.prepareAsync(); - } catch (Throwable e) { - Util.makeText(getContext(), Util.toStr(e)).show(); - Util.printStackTrace(e); - ijkPlayer = null; - } - } - @Override - public void surfaceCreated(SurfaceHolder holder) { - if(ijkPlayer!=null) ijkPlayer.setDisplay(holder); - } - @Override - public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - System.out.println("surfaceChanged"); - } - @Override - public void surfaceDestroyed(SurfaceHolder holder) { - System.out.println("surfaceDestroyed"); - } - - 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(); - else pause(); - } -} \ No newline at end of file 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 dc5ede5..5fb55a5 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/MainActivity.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/MainActivity.java @@ -100,7 +100,6 @@ public class MainActivity extends ComponentActivity implements Choreographer.Fra if(requestCode==999 && grantResults.length > 0 && grantResults[0]==PackageManager.PERMISSION_GRANTED && backView==null) init(); } - int receCnt; ConnService connService; @SuppressLint("UnspecifiedRegisterReceiverFlag") @@ -130,13 +129,11 @@ public class MainActivity extends ComponentActivity implements Choreographer.Fra Util.screenWidth = service.getScreenWidth(); Util.screenHeight = service.getScreenHeight(); Util.println(" IsScreenOn: "+Util.isScreenOn+" screen: "+Util.screenWidth+" x "+Util.screenHeight); - if(receCnt++>=1) { - backView = new BackView(MainActivity.this, Util.screenWidth, Util.screenHeight); - state = 5; - if(Util.isScreenOn) initProg(); - else state = 8; - if(progView==null) setContentView(backView); - } + backView = new BackView(MainActivity.this, Util.screenWidth, Util.screenHeight); + state = 5; + if(Util.isScreenOn) initProg(); + else state = 8; + if(progView==null) setContentView(backView); } catch (Exception e) { Util.makeText(MainActivity.this, Util.toStr(e)).show(); Util.printStackTrace(e); @@ -164,13 +161,6 @@ public class MainActivity extends ComponentActivity implements Choreographer.Fra if(! Util.serverURL.startsWith("http")) Util.serverURL = "http://"+Util.serverURL; if(! Util.serverURL.endsWith("/")) Util.serverURL += "/"; } - if(receCnt++>=1) { - backView = new BackView(MainActivity.this, Util.screenWidth, Util.screenHeight); - state = 5; - if(Util.isScreenOn) initProg(); - else state = 8; - if(progView==null) setContentView(backView); - } } catch (Exception e) { Util.makeText(MainActivity.this, Util.toStr(e)).show(); Util.printStackTrace(e); @@ -355,7 +345,7 @@ public class MainActivity extends ComponentActivity implements Choreographer.Fra if(task==null) { if(! root.containsKey("layers")) { state = 3; - Util.println(" Error: task==null\n"); + Util.println(root.isEmpty() ? " Empty program JSON\n" : " Error: task==null\n"); return; } task = new JSMap("items", new JSList<>(new JSMap("repeatTimes", 1, "_program", root))); 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 21fc3b9..1b94895 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Prog.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Prog.java @@ -218,15 +218,13 @@ public class Prog extends AbsLayout { } } } else { - var start = src.startTime; var ele0 = src; for(var map : imgs) { var picDur = map.intg("picDuration")*1000; if(picDur==0) picDur = timeSpan / imgs.size(); src.type = "Image"; - src.startTime = start; - src.endTime = start = src.startTime + picDur; 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; @@ -235,6 +233,7 @@ public class Prog extends AbsLayout { src.isEntryRand = ele0.isEntryRand; src.isExitRand = ele0.isExitRand; } + src.endTime = src.startTime + picDur; if(hasTTS) { src.text = map.str("text"); if(src.text!=null) { @@ -279,7 +278,7 @@ public class Prog extends AbsLayout { 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(context, source); + else if(src.type.startsWith("MultiLineText")) src.view = new SrcSensor(this, source, geo.width, geo.height); else continue; if(src.view==null) continue; src.view.setVisibility(GONE); diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Server.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Server.java index a189451..4abb9a9 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Server.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Server.java @@ -138,10 +138,7 @@ public class Server extends Service { var filename = source.str("id"); if(filename==null) continue; var url = source.stnn("url"); - if(! url.startsWith("http")) { - if(preDownloadURL==null) continue; - url = preDownloadURL + filename; - } + if(! url.startsWith("http")) url = preDownloadURL + filename; var file = new File(Util.programDir+"/"+filename); if(file.exists() && file.length() > 0) { proSize -= file.length(); @@ -154,10 +151,7 @@ public class Server extends Service { var filename = img.str("id"); if(filename==null) continue; var url = img.stnn("url"); - if(! url.startsWith("http")) { - if(preDownloadURL==null) continue; - url = preDownloadURL + filename; - } + if(! url.startsWith("http")) url = preDownloadURL + filename; var file = new File(Util.programDir+"/"+filename); if(file.exists() && file.length() > 0) { proSize -= file.length(); @@ -314,7 +308,12 @@ public class Server extends Service { @Override public String getProgramTask() throws RemoteException { - return null; + try { + return IOs.readStrClose(new FileInputStream(Util.programDir+"/program")); + } catch (Exception e) { + Util.printStackTrace(e); + return Util.toStr(e); + } } @Override 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 52498d7..2f38af5 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcCopy.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcCopy.java @@ -1,12 +1,7 @@ package com.xixun.xixunplayer; import android.content.Context; -import android.graphics.Bitmap; import android.graphics.Canvas; -import android.graphics.Rect; -import android.graphics.RectF; -import android.view.Choreographer; -import android.view.SurfaceView; import android.view.View; import androidx.annotation.NonNull; 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 446ca44..38cba51 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcSensor.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcSensor.java @@ -1,37 +1,41 @@ package com.xixun.xixunplayer; import android.annotation.SuppressLint; -import android.content.Context; 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; - String html, prefix, url, text; - int interval; - boolean isFirst = true; + ArrayList htmls; + String prefix, suffix, url, text; + int interval, pageDur, cur; @SuppressLint("SetJavaScriptEnabled") - public SrcSensor(Context context, JSMap json) { - super(context); - act = (MainActivity) context; + 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); - html = json.stnn("html").replace("%Co2", "%CO2"); + var html = json.stnn("html").replace("%Co2", "%CO2").replace("

", "").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")) { @@ -47,13 +51,18 @@ public class SrcSensor extends WebView implements IntentReceiver, Choreographer. prefix = ""; + prefix += "width:"+width+"; height:"+height+"; position:relative;\">
"; + suffix = "
"; + if(json.bool("center")) suffix += ""; + prog.calls.add(this); } static DecimalFormat fmt0 = NumFmts.newDecFmt("0"); public void onReceive(Intent intent) { try { - var htm = html; + var htm = htmls.get(cur); var temp = intent.getFloatExtra("temperature", -99f); var fa = temp * 1.8f + 32; var noTemp = temp==-99f; @@ -106,39 +115,44 @@ public class SrcSensor extends WebView implements IntentReceiver, Choreographer. .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); + loadDataWithBaseURL(null, prefix+htm+suffix, "text/html", "UTF-8", null); } catch (Exception e) { Util.printStackTrace(e); } } - long nextMs; - - @Override - public void onVisibilityAggregated(boolean isVisible) { - super.onVisibilityAggregated(isVisible); - if(isVisible) { - if(isFirst) { - isFirst = false; - onReceive(act.environIntent); - act.environs.add(this); - } - } else { - act.environs.remove(this); - isFirst = true; - } - } + long nextPageMs, nextMs; + boolean isFirst = true; @Override public void doFrame(long ms) { - if(! isShown()) return; + if(! isShown()) { + 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/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideo.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideo.java index f24bbc0..1188e17 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideo.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideo.java @@ -60,7 +60,7 @@ public class SrcVideo extends TextureView implements TextureView.SurfaceTextureL release(); initExo(); } - if(isShown() || isLive) start(); + if(isShown()) start(); }); ijkPlayer.prepareAsync(); } catch (Throwable e) { @@ -71,18 +71,17 @@ public class SrcVideo extends TextureView implements TextureView.SurfaceTextureL } @Override public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) { + setSurfaceTextureListener(null); if(ijkPlayer!=null) ijkPlayer.setSurface(new Surface(surface)); } @Override - public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) { - } + public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) {} @Override public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) { return false; } @Override - public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) { - } + public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {} void start() { if(ijkPlayer!=null) { diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideo2.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideo2.java new file mode 100644 index 0000000..6b0a181 --- /dev/null +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideo2.java @@ -0,0 +1,106 @@ +package com.xixun.xixunplayer; + +import android.content.Context; +import android.graphics.SurfaceTexture; +import android.media.MediaPlayer; +import android.view.Choreographer; +import android.view.Surface; +import android.view.TextureView; + +import androidx.annotation.NonNull; + +public class SrcVideo2 extends TextureView implements TextureView.SurfaceTextureListener, Choreographer.FrameCallback { + + float vol; + MediaPlayer player; + char state; + boolean isLive; + + public SrcVideo2(Prog prog, String path, float vol, boolean isLive) { + super(prog.getContext()); + this.vol = vol; + 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 { + player.setDataSource(path); + player.prepareAsync(); + } catch (Exception e) { + Util.printStackTrace(e); + Util.makeText(getContext(), Util.toStr(e)).show(); + } + } + @Override + public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) { + setSurfaceTextureListener(null); + if(player!=null) player.setSurface(new Surface(surface)); + } + @Override + public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) {} + @Override + public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) { + return false; + } + @Override + public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {} + + @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); + } + } + + void release() { + if(player!=null) { + player.release(); + player = null; + } + } + @Override + public void doFrame(long ms) { +// if(isShown()) return; +// if(player!=null) { +// player.pause(); +// player.seekTo(0); +// } + } + static String getErrorName(int code) { + if(code==MediaPlayer.MEDIA_ERROR_UNKNOWN) return "UNKNOWN"; + if(code==MediaPlayer.MEDIA_ERROR_SERVER_DIED) return "SERVER_DIED"; + if(code==MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) return "NOT_VALID_FOR_PROGRESSIVE_PLAYBACK"; + if(code==MediaPlayer.MEDIA_ERROR_IO) return "IO"; + if(code==MediaPlayer.MEDIA_ERROR_MALFORMED) return "MALFORMED"; + if(code==MediaPlayer.MEDIA_ERROR_UNSUPPORTED) return "UNSUPPORTED"; + if(code==MediaPlayer.MEDIA_ERROR_TIMED_OUT) return "TIMED_OUT"; + if(code==-2147483648) return "SYSTEM"; + return "Unknown ("+code+")"; + } +} \ 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 5c011a9..7348eec 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVisitor.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVisitor.java @@ -1,21 +1,11 @@ package com.xixun.xixunplayer; import android.annotation.SuppressLint; -import android.content.Context; -import android.content.Intent; import android.graphics.Color; import android.view.Choreographer; import android.webkit.WebView; -import java.text.DecimalFormat; -import java.util.HashMap; -import java.util.regex.Pattern; - -import gnph.util.IOs; -import gnph.util.JSList; import gnph.util.JSMap; -import gnph.util.NumFmts; -import gnph.util.Txts; import gnph.util.URLConn; @SuppressLint("ViewConstructor")