diff --git a/XixunPlayer/app/build.gradle b/XixunPlayer/app/build.gradle index 0fe7371..3f435b1 100644 --- a/XixunPlayer/app/build.gradle +++ b/XixunPlayer/app/build.gradle @@ -8,7 +8,7 @@ android { defaultConfig { applicationId "com.xixun.xixunplayer" - minSdk 26 + minSdk 24 targetSdk 34 versionCode 1 versionName "1.0" @@ -40,7 +40,23 @@ dependencies { // implementation 'tv.danmaku.ijk.media:ijkplayer-arm64:0.8.8' implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.25' 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') -} \ No newline at end of file +} + +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.all { variant -> + variant.outputs.all { + def fileName = "${getAppName()}-${versionName}.apk" + outputFileName = fileName + } +} diff --git a/XixunPlayer/app/libs/ijkplayer-armv7a-0.8.8.aar b/XixunPlayer/app/libs/ijkplayer-armv7a-0.8.8.aar new file mode 100644 index 0000000..d84b81a Binary files /dev/null and b/XixunPlayer/app/libs/ijkplayer-armv7a-0.8.8.aar differ diff --git a/XixunPlayer/app/libs/zip4j-2.10.0.jar b/XixunPlayer/app/libs/zip4j-2.10.0.jar new file mode 100644 index 0000000..d80d844 Binary files /dev/null and b/XixunPlayer/app/libs/zip4j-2.10.0.jar differ diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/BackView.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/BackView.java index aecdd17..cd40933 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/BackView.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/BackView.java @@ -11,20 +11,20 @@ import android.view.View; public class BackView extends View { public Bitmap img, cosImg; - public int width, height; + Rect rect = new Rect(); public BackView(Context context, int width, int height) { super(context); img = BitmapFactory.decodeResource(context.getResources(), R.drawable.back); cosImg = BitmapFactory.decodeFile(Environment.getExternalStorageDirectory() + "/XixunPlayer/background"); - this.width = width; - this.height = height; + rect.right = width; + rect.bottom = height; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); - if(cosImg!=null) canvas.drawBitmap(cosImg, new Rect(0, 0, cosImg.getWidth(), cosImg.getHeight()), new Rect(0, 0, width, height), null); - else if(img!=null) for(int y=0; y 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(m_attr.hourMark==2) drawMarkNumber(painter, {x, y}, m_attr.hourMarkColor, m_attr.hourMarkSize, i/5); + } + } + } + imgRect = new Rect(0, 0, img.getWidth(), img.getHeight()); + 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); @@ -76,20 +117,22 @@ public class EleAnaClock extends View implements Choreographer.FrameCallback { sPath.close(); } void cal() { - var time = timeZone==null ? LocalTime.now() : LocalTime.now(timeZone); - sAngle = time.getSecond() * 6; - mAngle = time.getMinute() * 6 + sAngle/60; - hAngle = time.getHour() * 30 + mAngle/12; + //var time = timeZone==null ? LocalTime.now() : LocalTime.now(timeZone); + var calendar = timeZone==null ? Calendar.getInstance() : Calendar.getInstance(timeZone); + 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(Canvas canvas) { + protected void onDraw(@NonNull Canvas canvas) { super.onDraw(canvas); if(lastSec==0) { cal(); choreographer.postFrameCallback(this); + lastSec = 1; } - if(img != null) canvas.drawBitmap(img, new Rect(0,0,img.getWidth(), img.getHeight()), new RectF(0,0, getWidth(), getHeight()), paint); - canvas.translate(getWidth()/2, getHeight()/2); + if(img != null) canvas.drawBitmap(img, imgRect, new RectF(0,0, getWidth(), getHeight()), paint); + canvas.translate(getWidth()/2f, getHeight()/2f); paint.setColor(pinHourColor); canvas.rotate(hAngle); @@ -106,7 +149,7 @@ public class EleAnaClock extends View implements Choreographer.FrameCallback { } Choreographer choreographer = Choreographer.getInstance(); - long lastSec = 0; + long lastSec; @Override public void doFrame(long frameTimeNanos) { diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleBorder.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleBorder.java index d0d59ea..94c5ba9 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleBorder.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleBorder.java @@ -63,7 +63,10 @@ public class EleBorder extends View implements Choreographer.FrameCallback { } } } - if(freshCnt==0 && eff!=0) choreographer.postFrameCallback(this); + if(freshCnt==0 && eff!=0) { + choreographer.postFrameCallback(this); + freshCnt = 1; + } } Choreographer choreographer = Choreographer.getInstance(); diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleDigiClock.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleDigiClock.java index 6ffd273..5adf528 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleDigiClock.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleDigiClock.java @@ -1,5 +1,6 @@ package com.xixun.xixunplayer; +import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -9,47 +10,58 @@ import android.view.View; import android.widget.ImageView; import android.widget.LinearLayout; -import java.time.LocalDateTime; -import java.time.ZoneId; +import androidx.annotation.NonNull; + +import java.text.SimpleDateFormat; +import java.util.Calendar; import java.util.HashMap; +import java.util.TimeZone; import gnph.android.LinearBox; import gnph.util.Dates; import gnph.util.JSList; import gnph.util.JSMap; -import gnph.util.Sys; +@SuppressLint("ViewConstructor") public class EleDigiClock extends LinearBox implements Choreographer.FrameCallback { - static final String weeks[] = {"MON","TUE","WED","THU","FRI","SAT","SUN"}; + static final String weeks[] = {"SUN", "MON","TUE","WED","THU","FRI","SAT"}; HashMap 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[] 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())}; + ImageView[] hourComps = {new ImageView(getContext()), new ImageView(getContext())}; + ImageView[] minComps = {new ImageView(getContext()), new ImageView(getContext())}; + ImageView[] secComps = {new ImageView(getContext()), new ImageView(getContext())}; - ZoneId timeZone; - String timeptn; + TimeZone timeZone; + SimpleDateFormat timeptn, dataFmt = new SimpleDateFormat("yyyyMMdd"); boolean multiline, weekly, isSingleMonth; - public EleDigiClock(String prefix, JSMap json, Context context) { + public EleDigiClock(Context context, JSMap json) { super(context); setGravity(Gravity.CENTER); - var aZone = json.str("timeZone"); - if(aZone!=null) timeZone = ZoneId.of(aZone); + var timeZoneStr = json.str("timeZone"); + //if(timeZoneStr!=null) timeZone = ZoneId.of(timeZoneStr); + if(timeZoneStr!=null) TimeZone.getTimeZone(timeZoneStr); var spaceWidth = json.dbl("spaceWidth"); JSList pics = json.jslist("arrayPics"); - for(var pic : pics) imgs.put(pic.stnn("name"), BitmapFactory.decodeFile(prefix+pic.stnn("id"))); + 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"); isSingleMonth = dateStyle==1||dateStyle==2||dateStyle==4||dateStyle==6||dateStyle==8||dateStyle==10||dateStyle==12; var timeSep = imgs.get("maohao"); weekly = json.bool("weekly"); var hour12 = json.bool("hour12"); - var AmPm = hour12 ? json.bool("AmPm") : false; - timeptn = hour12 ? "hhmmssa" : "HHmmss"; + var AmPm = hour12 && json.bool("AmPm"); + timeptn = new SimpleDateFormat(hour12 ? "hhmmssa" : "HHmmss"); var hour = json.bool("hour"); var min = json.bool("min"); var sec = json.bool("sec"); @@ -228,20 +240,19 @@ public class EleDigiClock extends LinearBox implements Choreographer.FrameCallba } void cal() { - var dt = timeZone==null ? LocalDateTime.now() : LocalDateTime.now(timeZone); - var time = dt.toLocalTime(); - var hms = Dates.fmt(time, timeptn); - ampmComp.setImageBitmap(imgs.get(time.getHour()<12?"AM":"PM")); + //var dt = timeZone==null ? LocalDateTime.now() : LocalDateTime.now(timeZone); + var calendar = timeZone==null ? Calendar.getInstance() : Calendar.getInstance(timeZone); + var hms = timeptn.format(calendar.getTime()); + ampmComp.setImageBitmap(imgs.get(calendar.get(Calendar.HOUR_OF_DAY) < 12?"AM":"PM")); hourComps[0].setImageBitmap(imgs.get(hms.substring(0,1))); hourComps[1].setImageBitmap(imgs.get(hms.substring(1,2))); minComps[0].setImageBitmap(imgs.get(hms.substring(2,3))); minComps[1].setImageBitmap(imgs.get(hms.substring(3,4))); secComps[0].setImageBitmap(imgs.get(hms.substring(4,5))); secComps[1].setImageBitmap(imgs.get(hms.substring(5,6))); - if(yearComps[0].getDrawable()==null || (time.getHour()==0 && time.getSecond()==0)) { - var date = dt.toLocalDate(); - if(weekly) weekComp.setImageBitmap(imgs.get(weeks[date.getDayOfWeek().ordinal()])); - var ymd = Dates.fmt(date, "yyyyMMdd"); + if(yearComps[0].getDrawable()==null || (calendar.get(Calendar.HOUR_OF_DAY)==0 && calendar.get(Calendar.SECOND)==0)) { + if(weekly) weekComp.setImageBitmap(imgs.get(weeks[calendar.get(Calendar.DAY_OF_WEEK) - 1])); + var ymd = dataFmt.format(calendar.getTime()); yearComps[0].setImageBitmap(imgs.get(ymd.substring(0,1))); yearComps[1].setImageBitmap(imgs.get(ymd.substring(1,2))); yearComps[2].setImageBitmap(imgs.get(ymd.substring(2,3))); @@ -254,18 +265,19 @@ public class EleDigiClock extends LinearBox implements Choreographer.FrameCallba } @Override - protected void onVisibilityChanged(View changedView, int visibility) { + protected void onVisibilityChanged(@NonNull View changedView, int visibility) { super.onVisibilityChanged(changedView, visibility); if(visibility==View.VISIBLE) { if(lastSec==0) { cal(); choreographer.postFrameCallback(this); + lastSec = 1; } } } Choreographer choreographer = Choreographer.getInstance(); - long lastSec = 0; + long lastSec; @Override public void doFrame(long frameTimeNanos) { @@ -277,7 +289,6 @@ public class EleDigiClock extends LinearBox implements Choreographer.FrameCallba if(sec != lastSec) { lastSec = sec; cal(); - invalidate(); } choreographer.postFrameCallback(this); } 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 12dd487..b904c7b 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleEnviron.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleEnviron.java @@ -36,30 +36,39 @@ public class EleEnviron extends View implements Choreographer.FrameCallback { Bitmap title; ArrayList items = new ArrayList<>(); MainActivity act; - int spaceWidth, digitHeight; + int alignType, spaceWidth, digitHeight; int interval, cur, end, step; boolean isScroll, isFirst = true; - public EleEnviron(String prefix, JSMap json, Context context) { + public EleEnviron(Context context, JSMap json) { super(context); act = (MainActivity) context; + 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"); - JSList arrayPics = json.jslist("arrayPics"); if(values!=null) { + title = BitmapFactory.decodeFile(Util.programDir+"/"+json.stnn("title")); var entrys = values.entrySet(); - for(var entry : entrys) imgMap.put(entry.getKey(), BitmapFactory.decodeFile(prefix+entry.getValue())); + 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(prefix+jitem.stnn("label")), unit==null ? null : BitmapFactory.decodeFile(prefix+unit))); + items.add(new Item(jitem.str("name"), BitmapFactory.decodeFile(Util.programDir+"/"+jitem.stnn("label")), unit==null ? null : BitmapFactory.decodeFile(Util.programDir+"/"+unit))); } } else { - for(var img : arrayPics) imgMap.put(img.str("name"), BitmapFactory.decodeFile(prefix+img.stnn("id"))); + 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"); + 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"))); @@ -71,6 +80,7 @@ public class EleEnviron extends View implements Choreographer.FrameCallback { 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"); @@ -109,7 +119,7 @@ public class EleEnviron extends View implements Choreographer.FrameCallback { } } } catch (Exception e) { - e.printStackTrace(); + Util.printStackTrace(e); } } @@ -147,16 +157,30 @@ public class EleEnviron extends View implements Choreographer.FrameCallback { } if(ccc==0) end = -(x-cur-step); } - if(freshCnt==0 && interval!=0) choreographer.postFrameCallback(this); + if(freshCnt==0 && interval!=0) { + choreographer.postFrameCallback(this); + freshCnt = 1; + } } else { var itemH = (double) getHeight() / (title!=null ? items.size()+1 : items.size()); - int i = 0; - if(title!=null) canvas.drawBitmap(title, 0, Math.round(itemH * i++ + (itemH - title.getHeight()) / 2), null); + int i = 0, x = 0; + if(title!=null) { + if(alignType==1) x = (getWidth() - title.getWidth()) / 2; + else if(alignType==2) x = getWidth() - title.getWidth(); + canvas.drawBitmap(title, x, Math.round(itemH * i++ + (itemH - title.getHeight()) / 2), null); + } for(var item : items) { var y = (int) Math.round(itemH * i++ + (itemH - digitHeight) / 2); - var x = 0; + x = 0; + if(alignType!=0) { + if(item.lable!=null) x += item.lable.getWidth(); + for(var num : item.nums) if(num!=null) x += num.getWidth(); + if(item.unit!=null) x += item.unit.getWidth(); + x = getWidth() - x; + if(alignType==1) x /= 2; + } if(item.lable!=null) { - canvas.drawBitmap(item.lable, 0, y, null); + canvas.drawBitmap(item.lable, x, y, null); x += item.lable.getWidth(); } for(var num : item.nums) if(num!=null) { diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleFlip.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleFlip.java deleted file mode 100644 index 931d103..0000000 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleFlip.java +++ /dev/null @@ -1,128 +0,0 @@ -package com.xixun.xixunplayer; - - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Canvas; -import android.view.Choreographer; -import android.view.View; - -import java.util.ArrayList; -import java.util.Random; - -import gnph.util.JSList; -import gnph.util.JSMap; - -public class EleFlip extends View implements DrawOther, Choreographer.FrameCallback { - - static final char effTypes[] = {'l', 't', 'r', 'b'}; - ArrayList others; - Random rand = new Random(); - ArrayList imgs = new ArrayList<>(); - int picDur, EffDur, AniInterval, step, imgc, imgx, imgy; - char effType; - boolean needRand = false; - - public EleFlip(String dirPre, JSList maps, Context context) { - super(context); - var map = maps.get(0); - picDur = map.intg("picDuration")*60; - if(picDur==0) return; - EffDur = map.intg("effectSpeed")*60; - for(var amap : maps) imgs.add(BitmapFactory.decodeFile(dirPre+amap.stnn("id"))); - var effStr = map.str("effect"); - if(effStr == null || effStr.equals("no")) EffDur = 0; - else if(effStr.endsWith("left")) effType = 'l'; - else if(effStr.endsWith("top")) effType = 't'; - else if(effStr.endsWith("right")) effType = 'r'; - else if(effStr.endsWith("bottom")) effType = 'b'; - else if(effStr.equals("random")) needRand = true; - else EffDur = 0; - } - - void startMove() { - if(EffDur==0) return; - if(needRand) effType = effTypes[rand.nextInt(4)]; - double effDurD = EffDur; - if(effType=='l') { - imgx = getWidth(); - imgy = 0; - effDurD /= getWidth(); - } else if(effType=='r') { - imgx = -getWidth(); - imgy = 0; - effDurD /= getWidth(); - } else if(effType=='t') { - imgx = 0; - imgy = getHeight(); - effDurD /= getHeight(); - } else if(effType=='b') { - imgx = 0; - imgy = -getHeight(); - effDurD /= getHeight(); - } else return; - AniInterval = (int) Math.round(effDurD); - if(AniInterval < 1) AniInterval = 1; - step = (int) Math.round(AniInterval / effDurD); - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - if(freshCnt==0 && (EffDur!=0 || imgs.size()>1)) { - startMove(); - choreographer.postFrameCallback(this); - } - drawOther(canvas); - } - @Override - public void drawOther(Canvas canvas) { - canvas.drawBitmap(imgs.get(imgc), imgx, imgy, null); - } - - Choreographer choreographer = Choreographer.getInstance(); - int freshCnt, curDur; - - @Override - public void doFrame(long frameTimeNanos) { - if(! isShown()) { - freshCnt = curDur = imgc = 0; - return; - } - if(curDur < picDur) { - curDur++; - if(AniInterval > 0) { - if(freshCnt < AniInterval) freshCnt++; - else { - freshCnt = 1; - if(effType=='l') { - imgx -= step; - if(imgx < 0) imgx = 0; - } else if(effType=='t') { - imgy -= step; - if(imgy < 0) imgy = 0; - } else if(effType=='r') { - imgx += step; - if(imgx > 0) imgx = 0; - } else if(effType=='b') { - imgy += step; - if(imgy > 0) imgy = 0; - } - if(imgx==0 && imgy==0) AniInterval = 0; - invalidate(); - if(others!=null) for(var other : others) other.invalidate(); - } - } - } else { - curDur = 0; - freshCnt = 1; - if(imgc >= imgs.size()-1) imgc = 0; - else imgc++; - startMove(); - invalidate(); - if(others!=null) for(var other : others) other.invalidate(); - } - choreographer.postFrameCallback(this); - } -} \ No newline at end of file diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleOtherPart.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleOtherPart.java deleted file mode 100644 index 8faf332..0000000 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleOtherPart.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.xixun.xixunplayer; - -import android.content.Context; -import android.graphics.Canvas; -import android.view.View; - -public class EleOtherPart extends View { - - DrawOther master; - - public EleOtherPart(Context context, DrawOther master) { - super(context); - this.master = master; - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - try { - master.drawOther(canvas); - } catch (Throwable e) { - setVisibility(GONE); - e.printStackTrace(); - } - } -} 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 8c6c363..502cb25 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleScroll.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleScroll.java @@ -1,29 +1,42 @@ package com.xixun.xixunplayer; +import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; +import android.graphics.Rect; import android.view.Choreographer; import android.view.View; +import androidx.annotation.NonNull; + import java.util.ArrayList; +import java.util.List; import gnph.util.JSMap; -public class EleScroll extends View implements DrawOther, Choreographer.FrameCallback { +@SuppressLint("ViewConstructor") +public class EleScroll extends View implements Choreographer.FrameCallback { - ArrayList others; - int w, h; - Bitmap img; + ArrayList imgs = new ArrayList<>(); + Rect rect = new Rect(); + int imgsWidth; int interval, cur, end, step; char effect; - public EleScroll(Context context, String dirPre, JSMap json, int w, int h) { + public EleScroll(Context context, JSMap json) { super(context); - this.w = w; - this.h = h; - img = BitmapFactory.decodeFile(dirPre + json.stnn("id")); + var img = BitmapFactory.decodeFile(Util.programDir+"/"+json.stnn("id")); + imgsWidth = img.getWidth(); + if(imgsWidth <= 8192) imgs.add(img); + else { + var rem = img.getWidth(); + do { + imgs.add(Bitmap.createBitmap(img, img.getWidth()-rem, 0, Math.min(8192, rem), img.getHeight())); + rem -= 8192; + } while (rem>0); + } var effStr = json.str("effect"); if(effStr==null || effStr.equals("no")) return; var scrollSpeed = json.dbl("scrollSpeed"); @@ -38,47 +51,67 @@ public class EleScroll extends View implements DrawOther, Choreographer.FrameCal int idx = effStr.lastIndexOf(' '); if(idx > -1) { effect = effStr.charAt(idx+1); - if(effect=='l') end = -(img.getWidth()-step); - else if(effect=='r') end = img.getWidth()-step; - else if(effect=='t') end = -(img.getHeight()-step); - else if(effect=='b') end = img.getHeight()-step; + if(effect=='l') end = -(imgsWidth-step); + else if(effect=='r') end = imgsWidth-step; + else if(effect=='t') end = -(imgs.get(0).getHeight()-step); + else if(effect=='b') end = imgs.get(0).getHeight()-step; } } @Override - protected void onDraw(Canvas canvas) { + protected void onDraw(@NonNull Canvas canvas) { super.onDraw(canvas); - if(img==null) return; + if(imgs.isEmpty()) return; try { drawOther(canvas); - if(freshCnt==0 && effect!=0 && interval!=0) choreographer.postFrameCallback(this); + if(freshCnt==0 && effect!=0 && interval!=0) { + choreographer.postFrameCallback(this); + freshCnt = 1; + } } catch (Throwable e) { setVisibility(GONE); - img = null; - e.printStackTrace(); + imgs.clear(); + Util.printStackTrace(e); } } - @Override public void drawOther(Canvas canvas) { - if(img==null) return; + if(! canvas.getClipBounds(rect)) { + rect.left = rect.top = 0; + rect.right = getWidth(); + rect.bottom = getHeight(); + } if(effect=='l') { - var x = cur; + int ii = 0, x = cur; do { - canvas.drawBitmap(img, x, 0, null); - x += img.getWidth(); - } while(x < w); + 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(effect=='r') { - var x = cur + w - img.getWidth(); + int ii = imgs.size()-1, x = cur + getWidth(); + boolean con1; do { - canvas.drawBitmap(img, x, 0, null); - x -= img.getWidth(); - } while(x > -img.getWidth()); + 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(effect=='t') { - canvas.drawBitmap(img, 0, cur, null); - canvas.drawBitmap(img, 0, cur+img.getHeight(), null); + 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(effect=='b') { - canvas.drawBitmap(img, 0, cur, null); - canvas.drawBitmap(img, 0, cur-img.getHeight(), null); - } else canvas.drawBitmap(img, 0, 0, null); + 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); } Choreographer choreographer = Choreographer.getInstance(); @@ -101,7 +134,6 @@ public class EleScroll extends View implements DrawOther, Choreographer.FrameCal else cur += step; } invalidate(); - if(others!=null) for(var other : others) other.invalidate(); } choreographer.postFrameCallback(this); } diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleScrollAny.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleScrollAny.java deleted file mode 100644 index dd16679..0000000 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleScrollAny.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.xixun.xixunplayer; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Canvas; -import android.view.Choreographer; -import android.view.View; - -import java.util.ArrayList; - -import gnph.util.JSMap; - -public class EleScrollAny extends View implements DrawOther, Choreographer.FrameCallback { - - ArrayList others; - Bitmap img; - float cur, end, step; - char effect; - - public EleScrollAny(String dirPre, JSMap json, Context context) { - super(context); - img = BitmapFactory.decodeFile(dirPre + json.stnn("id")); - var effStr = json.str("effect"); - if(effStr==null || effStr.equals("no")) return; - var scrollSpeed = json.dbl("scrollSpeed"); - if(scrollSpeed==0) { - var scrollDur = json.dbl("effectSpeed"); - if(scrollDur==0) return; - scrollSpeed = 1000 / scrollDur; - } - step = (float) (scrollSpeed / 60); - int idx = effStr.lastIndexOf(' '); - if(idx > -1) { - effect = effStr.charAt(idx+1); - if(effect=='l') end = -(img.getWidth()-step); - else if(effect=='r') end = img.getWidth()-step; - else if(effect=='t') end = -(img.getHeight()-step); - else if(effect=='b') end = img.getHeight()-step; - } - } - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - if(img==null) return; - try { - drawOther(canvas); - if(freshCnt==0 && effect!=0 && step!=0) choreographer.postFrameCallback(this); - } catch (Throwable e) { - setVisibility(GONE); - img = null; - e.printStackTrace(); - } - } - @Override - public void drawOther(Canvas canvas) { - if(img==null) return; - if(effect=='l') { - canvas.drawBitmap(img, cur, 0, null); - canvas.drawBitmap(img, cur+img.getWidth(), 0, null); - } else if(effect=='r') { - var x = cur-img.getWidth()+getWidth(); - canvas.drawBitmap(img, x, 0, null); - canvas.drawBitmap(img, x-img.getWidth(), 0, null); - } else if(effect=='t') { - canvas.drawBitmap(img, 0, cur, null); - canvas.drawBitmap(img, 0, cur+img.getHeight(), null); - } else if(effect=='b') { - canvas.drawBitmap(img, 0, cur, null); - canvas.drawBitmap(img, 0, cur-img.getHeight(), null); - } else canvas.drawBitmap(img, 0, 0, null); - } - - Choreographer choreographer = Choreographer.getInstance(); - int freshCnt; - - @Override - public void doFrame(long frameTimeNanos) { - if(! isShown()) { - cur = freshCnt = 0; - return; - } - freshCnt = 1; - if(effect=='t' || effect=='l') { - if(cur <= end) cur -= end; - else cur -= step; - } else if(effect=='b' || effect=='r') { - if(cur >= end) cur -= end; - else cur += step; - } - invalidate(); - if(others!=null) for(var other : others) other.invalidate(); - choreographer.postFrameCallback(this); - } -} \ No newline at end of file diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleTimer2.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleTimer2.java deleted file mode 100644 index b7bc898..0000000 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleTimer2.java +++ /dev/null @@ -1,110 +0,0 @@ -package com.xixun.xixunplayer; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Typeface; -import android.view.Choreographer; -import android.view.View; - -import gnph.util.Dates; -import gnph.util.JSMap; -import gnph.util.NumFmts; - -public class EleTimer2 extends View implements Choreographer.FrameCallback { - - Paint paint = new Paint(); - long targetTime; - String title, text; - int x, y; - boolean isDown; - boolean isMultiline; - boolean hasDay; - boolean hasHour; - boolean hasMin; - boolean hasSec; - - public EleTimer2(JSMap json, Context context) { - super(context); - isDown = json.bool("isDown"); - targetTime = Dates.milli(json.stnn("targetTime")) / 1000; - hasDay = json.bool("hasDay"); - hasHour = json.bool("hasHour"); - hasMin = json.bool("hasMin"); - hasSec = json.bool("hasSec"); - title = json.stnn("text"); - isMultiline = json.bool("isMultiline"); - int style = 0; - if(json.bool("fontBold")) style |= 1; - if(json.bool("fontItalic")) style |= 2; - @SuppressLint("WrongConstant") var font = Typeface.create(json.stnn("font"), style); - paint.setTypeface(font); - paint.setTextSize(json.intg("fontSize")); - paint.setUnderlineText(json.bool("fontUnderline")); - paint.setColor(Color.parseColor(json.stnn("textColor"))); - paint.setTextAlign(Paint.Align.CENTER); - try { - setBackgroundColor(Color.parseColor(json.stnn("backColor"))); - } catch (Exception ignored) {} - } - - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - super.onSizeChanged(w, h, oldw, oldh); - x = getWidth() / 2; - var fontMetrics = paint.getFontMetrics(); - y = (int) (getHeight() - fontMetrics.top - fontMetrics.bottom) / 2; - } - - void cal() { - var cur = System.currentTimeMillis() / 1000; - var secs = isDown ? targetTime - cur : cur - targetTime; - if(secs < 0) secs = 0; - var buf = new StringBuilder(title); - if(buf.length() != 0) buf.append(isMultiline ? '\n' : ' '); - if(hasDay) { - buf.append(secs/86400).append(" ").append("day").append(" "); - secs %= 86400; - } - if(hasHour) { - buf.append(NumFmts.zz().format(secs/3600)).append("hour").append(" "); - secs %= 3600; - } - if(hasMin) { - buf.append(NumFmts.zz().format(secs/60)).append("min").append(" "); - secs %= 60; - } - if(hasSec) buf.append(NumFmts.zz().format(secs)).append("sec").append(" "); - text = buf.toString().trim(); - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - if(lastSec==0) { - cal(); - choreographer.postFrameCallback(this); - } - canvas.drawText(text, x, y, paint); - } - - Choreographer choreographer = Choreographer.getInstance(); - long lastSec = 0; - - @Override - public void doFrame(long frameTimeNanos) { - if(! isShown()) { - lastSec = 0; - return; - } - var sec = frameTimeNanos / 1000000000; - if(sec != lastSec) { - lastSec = sec; - cal(); - invalidate(); - } - choreographer.postFrameCallback(this); - } -} \ No newline at end of file diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleVVV.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleVVV.java new file mode 100644 index 0000000..232771e --- /dev/null +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleVVV.java @@ -0,0 +1,126 @@ +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/EleVideo.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleVideo.java index fbdfe77..ed044fa 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleVideo.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleVideo.java @@ -1,9 +1,12 @@ package com.xixun.xixunplayer; import android.content.Context; -import android.view.SurfaceHolder; -import android.view.SurfaceView; +import android.graphics.Bitmap; +import android.graphics.SurfaceTexture; +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; @@ -13,12 +16,13 @@ import androidx.media3.exoplayer.SeekParameters; import tv.danmaku.ijk.media.player.IMediaPlayer; import tv.danmaku.ijk.media.player.IjkMediaPlayer; -public class EleVideo extends SurfaceView implements SurfaceHolder.Callback { +public class EleVideo extends TextureView implements TextureView.SurfaceTextureListener { String path; float vol; IjkMediaPlayer ijkPlayer; ExoPlayer exoPlayer; + long bitRate; public EleVideo(Context context, String path, float vol) { super(context); @@ -34,7 +38,7 @@ public class EleVideo extends SurfaceView implements SurfaceHolder.Callback { exoPlayer.setRepeatMode(ExoPlayer.REPEAT_MODE_ONE); exoPlayer.setSeekParameters(SeekParameters.CLOSEST_SYNC); exoPlayer.setVolume(vol); - exoPlayer.setVideoSurfaceView(this); + exoPlayer.setVideoTextureView(this); exoPlayer.prepare(); } void initIjk() { @@ -44,13 +48,14 @@ public class EleVideo extends SurfaceView implements SurfaceHolder.Callback { ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzeduration", 1); ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48); try { - getHolder().addCallback(this); + setSurfaceTextureListener(this); ijkPlayer.setDataSource(path); ijkPlayer.setLooping(true); ijkPlayer.setVolume(vol, vol); ijkPlayer.setOnPreparedListener((IMediaPlayer var1)->{ - if(ijkPlayer.getBitRate() > 12000000) { - getHolder().removeCallback(this); + bitRate = ijkPlayer.getBitRate(); + if(bitRate > 12000000) { + setSurfaceTextureListener(null); release(); initExo(); } @@ -58,27 +63,31 @@ public class EleVideo extends SurfaceView implements SurfaceHolder.Callback { }); ijkPlayer.prepareAsync(); } catch (Throwable e) { - Util.makeText(getContext(), e.getMessage()).show(); - e.printStackTrace(); + Util.makeText(getContext(), Util.toStr(e)).show(); + Util.printStackTrace(e); ijkPlayer = null; } } @Override - public void surfaceCreated(SurfaceHolder holder) { - if(ijkPlayer!=null) ijkPlayer.setDisplay(holder); + public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) { + if(ijkPlayer!=null) ijkPlayer.setSurface(new Surface(surface)); } @Override - public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - System.out.println("surfaceChanged"); + public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) { } @Override - public void surfaceDestroyed(SurfaceHolder holder) { - System.out.println("surfaceDestroyed"); + public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) { + return false; + } + @Override + public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) { } void start() { - if(ijkPlayer!=null) ijkPlayer.start(); - else if(exoPlayer!=null) { + if(ijkPlayer!=null) { + ijkPlayer.seekTo(0); + ijkPlayer.start(); + } else if(exoPlayer!=null) { if(exoPlayer.getPlaybackState()==ExoPlayer.STATE_IDLE) exoPlayer.prepare(); exoPlayer.play(); } diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleVideo2.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleVideo2.java index bb4a2ee..f36ea9b 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleVideo2.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleVideo2.java @@ -24,7 +24,7 @@ public class EleVideo2 extends VideoView implements Choreographer.FrameCallback setOnErrorListener((MediaPlayer mp, int what, int extra)->{ var err = "Media Error: "+getErrorName(what)+". "+getErrorName(extra); Util.makeText(getContext(), err).show(); - MainActivity.ins.buf.append(err+'\n'); + Util.println(err); return true; }); start(); @@ -39,25 +39,29 @@ public class EleVideo2 extends VideoView implements Choreographer.FrameCallback public void onVisibilityAggregated(boolean isVisible) { super.onVisibilityAggregated(isVisible); if(isVisible) { - MainActivity.ins.buf.append("Video Show\n"); + Util.println("Video Show"); if(! isPlaying()) { seekTo(0); start(); seekTo(0); - if(! isPlaying()) MainActivity.ins.buf.append("Video isn't Playing on Show\n"); + if(! isPlaying()) Util.println("Video isn't Playing on Show"); } } else { - MainActivity.ins.buf.append("Video Hide\n"); + Util.println("Video Hide"); seekTo(0); pause(); seekTo(0); showCnt = 3; - choreographer.postFrameCallback(this); + if(canAdd) { + choreographer.postFrameCallback(this); + canAdd = false; + } } } Choreographer choreographer = Choreographer.getInstance(); int showCnt; + boolean canAdd = true; @Override public void doFrame(long frameTimeNanos) { if(isShown()) { @@ -66,7 +70,7 @@ public class EleVideo2 extends VideoView implements Choreographer.FrameCallback start(); seekTo(0); if(! isPlaying()) { - MainActivity.ins.buf.append("Video isn't Playing on doFrame ").append(showCnt).append('\n'); + Util.println("Video isn't Playing on doFrame "+showCnt); if(showCnt==0) { suspend(); resume(); @@ -74,14 +78,18 @@ public class EleVideo2 extends VideoView implements Choreographer.FrameCallback } } if(showCnt>0) showCnt--; - else return; + else { + canAdd = true; + return; + } } else if(isPlaying()) { seekTo(0); pause(); seekTo(0); - System.out.println("pause in doFrame()"); + Util.println("pause in doFrame()"); } choreographer.postFrameCallback(this); + canAdd = false; } static String getErrorName(int code) { 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 dddd581..7147212 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/MainActivity.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/MainActivity.java @@ -2,6 +2,7 @@ package com.xixun.xixunplayer; import static android.view.View.VISIBLE; +import android.annotation.SuppressLint; import android.app.ActivityManager; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -30,27 +31,29 @@ import androidx.media3.common.util.UnstableApi; import com.xixun.joey.aidlset.CardService; +import net.lingala.zip4j.ZipFile; + import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.FileReader; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.net.ServerSocket; +import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.HashSet; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; -import gnph.util.Dates; import gnph.util.IOs; import gnph.util.JSList; import gnph.util.JSMap; import gnph.util.NumFmts; import gnph.util.O; -import tv.danmaku.ijk.media.player.IjkMediaMeta; public class MainActivity extends ComponentActivity implements Choreographer.FrameCallback, Runnable { @@ -67,15 +70,14 @@ public class MainActivity extends ComponentActivity implements Choreographer.Fra @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + var msg = "==== MainActivity onCreate ==== UI Thread: "+Thread.currentThread().getId(); + Util.println(msg); + ins = this; startService(new Intent(this, RestartService.class)); - var msg = "---- MainActivity onCreate ---- UI Thread: "+Thread.currentThread().getId(); - System.out.println(msg); - buf.append(msg).append('\n'); - ins = this; if(ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) init(); else { - System.out.println("---- No permission, Try again ..."); + Util.println("---- No permission, Try again ..."); ActivityCompat.requestPermissions(this, new String[]{ android.Manifest.permission.WRITE_EXTERNAL_STORAGE, android.Manifest.permission.READ_EXTERNAL_STORAGE, @@ -85,42 +87,45 @@ public class MainActivity extends ComponentActivity implements Choreographer.Fra }, 999); } } + @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if(requestCode==999 && grantResults.length > 0 && grantResults[0]==PackageManager.PERMISSION_GRANTED && backView==null) init(); } + @SuppressLint("UnspecifiedRegisterReceiverFlag") public void init() { - Util.programDir = Environment.getExternalStorageDirectory() + "/XixunPlayer/program"; - Util.backImgFile = Environment.getExternalStorageDirectory() + "/XixunPlayer/background"; + var dir = Build.VERSION.SDK_INT < Build.VERSION_CODES.R ? Environment.getExternalStorageDirectory().getAbsolutePath() + "/XixunPlayer" : getExternalFilesDir(null).getAbsolutePath(); + var msg = "---- dir "+dir; + Util.println(msg); + Util.programDir = dir + "/program"; + Util.backImgFile = dir + "/background"; var program = new File(Util.programDir); if(program.isFile()) program.delete(); - var msg = "mkdir: "+program.mkdirs(); - System.out.println(msg); - buf.append(msg).append('\n'); + msg = "---- mkdir: "+program.mkdirs(); + Util.println(msg); var conn = new ServiceConnection() { public void onServiceDisconnected(ComponentName name) { - System.out.println("Disconnected cardsystem aidl service"); + Util.println("Disconnected cardsystem aidl service"); } public void onServiceConnected(ComponentName name, IBinder iBinder) { - System.out.println("Bind cardsystem aidl service success"); - buf.append("Bind cardsystem aidl service success\n"); + Util.println("Bind cardsystem aidl service success"); var service = CardService.Stub.asInterface(iBinder); try { Util.isScreenOn = service.isScreenOpen(); - buf.append("isScreenOn:").append(Util.isScreenOn).append("\n"); + Util.println("isScreenOn: "+Util.isScreenOn); backView = new BackView(MainActivity.this, service.getScreenWidth(), service.getScreenHeight()); state = 5; if(Util.isScreenOn) initProg(); else state = 8; if(progView==null) setContentView(backView); } catch (RemoteException e) { - Util.makeText(MainActivity.this, e.toString()).show(); - throw new RuntimeException(e); + Util.makeText(MainActivity.this, Util.toStr(e)).show(); + Util.printStackTrace(e); } finally { unbindService(this); } @@ -130,13 +135,58 @@ public class MainActivity extends ComponentActivity implements Choreographer.Fra intent.setPackage("com.xixun.joey.cardsystem"); bindService(intent, conn, Context.BIND_AUTO_CREATE); + var intentFilter = new IntentFilter(Intent.ACTION_MEDIA_MOUNTED); + intentFilter.addDataScheme("file"); + registerReceiver(new BroadcastReceiver(){ + long lastMs; + 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).show(); + new Thread(()->{ + try { + var zip = new ZipFile(path+"/program.zip"); + if(zip.isEncrypted()) zip.setPassword(pass); + var json = JSMap.fromClose(zip.getInputStream(zip.getFileHeader("program"))); + var isIns = json.bool("isIns"); + long size = 0; + var headers = zip.getFileHeaders(); + for(var header : headers) size += header.getUncompressedSize(); + 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) zip.extractFile(header, Util.programDir); + runOnUiThread(() -> { + Util.println("Import Succeed"); + Util.makeText(MainActivity.this, "Import Succeed").show(); + initProg(); + }); + } catch (Exception e) { + Util.printStackTrace(e); + runOnUiThread(() -> { + Util.makeText(MainActivity.this, Util.toStr(e)).show(); + }); + } + }).start(); + } + }, intentFilter); + registerReceiver(new BroadcastReceiver(){ @Override public void onReceive(Context context, Intent intent) { - System.out.println("Receive PAUSE_PLAYER"); - buf.append("Receive PAUSE_PLAYER\n"); + Util.println("Receive PAUSE_PLAYER"); Util.isScreenOn = ! intent.getBooleanExtra("pause", false); - buf.append("isScreenOn:").append(Util.isScreenOn).append("\n"); + Util.println(" isScreenOn: "+Util.isScreenOn); if(! Util.isScreenOn) { state = 8; if(progView!=null) { @@ -146,7 +196,7 @@ public class MainActivity extends ComponentActivity implements Choreographer.Fra } } else if(progView==null) initProg(); } - }, new IntentFilter("com.xixun.action.PAUSE_PLAYER"), RECEIVER_EXPORTED); + }, new IntentFilter("com.xixun.action.PAUSE_PLAYER")); // registerReceiver(new BroadcastReceiver(){ // @Override @@ -163,14 +213,14 @@ public class MainActivity extends ComponentActivity implements Choreographer.Fra environ.invalidate(); } } - }, new IntentFilter("xixun.intent.action.TEMPERATURE_HUMIDITY"), RECEIVER_EXPORTED); + }, new IntentFilter("xixun.intent.action.TEMPERATURE_HUMIDITY")); registerReceiver(new BroadcastReceiver(){ @Override public void onReceive(Context context, Intent intent) { try { var code = intent.getIntExtra("code", 0); - buf.append("remote_control ").append(code).append("\n"); + Util.println(" remote_control "+code); if(progView == null || code > progView.pages.size()) return; if(! progView.avas.isEmpty()) page(curAva).hide(); var millis = (System.currentTimeMillis()+999)/1000*1000; @@ -181,7 +231,6 @@ public class MainActivity extends ComponentActivity implements Choreographer.Fra curTimes = 1; waitTo = 0; var page = page(0); - dur = page.tDur; page.setMillis(millis); if(state != 6) { setContentView(progView); @@ -192,11 +241,11 @@ public class MainActivity extends ComponentActivity implements Choreographer.Fra syncProg(millis); } } catch (Throwable e) { - Util.makeText(MainActivity.this, e.toString()).show(); - e.printStackTrace(); + Util.makeText(MainActivity.this, Util.toStr(e)).show(); + Util.printStackTrace(e); } } - }, new IntentFilter("com.xixun.yzd.REMOTE_CONTROL"), RECEIVER_EXPORTED); + }, new IntentFilter("com.xixun.yzd.REMOTE_CONTROL")); new Thread(this).start(); } @@ -238,99 +287,95 @@ public class MainActivity extends ComponentActivity implements Choreographer.Fra if(progView!=null) progView.release(); progView = view; setContentView(progView); - buf.append("init sync\n"); + state = 5; + Util.println("Init Sync"); syncProg((System.currentTimeMillis()+999)/1000*1000); - choreographer.postFrameCallback(this); + if(canAdd) { + choreographer.postFrameCallback(this); + canAdd = false; + } } catch (FileNotFoundException e) { state = 3; - e.printStackTrace(); + Util.printStackTrace(e); } catch (Throwable e) { state = 7; - Util.makeText(this, e.toString()).show(); - e.printStackTrace(); + Util.makeText(this, Util.toStr(e)).show(); + Util.printStackTrace(e); } } Choreographer choreographer = Choreographer.getInstance(); - int curAva, curTimes = 1, dur; + int curAva, curTimes = 1; long waitTo = Long.MAX_VALUE; - StringBuffer buf = new StringBuffer(); + boolean canAdd = true; @Override public void doFrame(long frameTimeNanos) { - if(progView == null) return; + if(progView == null) { + canAdd = true; + return; + } var milli = System.currentTimeMillis(); if(progView.avas.isEmpty()) { if(milli >= waitTo) { waitTo = Long.MAX_VALUE; - buf.append("wait sync\n"); + Util.println("wait sync"); syncProg(milli); } } else { var lastPage = page(curAva); if(milli >= lastPage.endMilli) { lastPage.hide(); - if(waitTo>0) { + if(waitTo>0) { //waitTo==0 为点播,不换页 if(curTimes < lastPage.repeatTimes) curTimes++; else { curTimes = 1; curAva++; if(curAva >= progView.avas.size()) { var isDiff = milli-lastPage.endMilli>=1000; - if(buf.length()>1000000) buf.replace(0, 100000, ""); - buf.append("isDiff:").append(isDiff).append(" endMs:").append(lastPage.endMilli).append(" millis:").append(milli).append("\n"); + if(Util.buf.length()>1000000) Util.buf.replace(0, 100000, ""); + Util.println("isDiff: "+isDiff+" endMs: "+lastPage.endMilli+" millis:"+milli); syncProg(isDiff ? milli : lastPage.endMilli); - buf.append("after. curAva:").append(curAva).append(" endMs:").append(lastPage.endMilli).append("\n"); + Util.println("after. curAva: "+curAva+" endMs: "+lastPage.endMilli); choreographer.postFrameCallback(this); + canAdd = false; return; } } } page(curAva).setMillis(lastPage.endMilli); - buf.append("curAva:").append(curAva).append(" endMs:").append(lastPage.endMilli).append("\n"); + Util.println("curAva: "+curAva+" endMs: "+lastPage.endMilli); } else { for(var layer : lastPage.layers) { - for(var ele : layer.eles) { - if(ele.view.getVisibility()==VISIBLE) { - if(milli >= ele.endMilli) ele.hide(); -// else if(ele.needLoop) { -// try { -// var vv = (EleVideo) ele.view; -// var vdur = vv.getDuration(); -// if(vdur>=1000) { -// ele.needLoop = false; -// if(ele.endTime-ele.startTime-vdur>=1000) vv.exoPlayer.setRepeatMode(ExoPlayer.REPEAT_MODE_ONE); -// } -// } catch (Throwable e) { -// Util.makeText(this, e.toString()).show(); -// e.printStackTrace(); -// } -// } - } else if(milli < ele.endMilli && milli >= ele.startMilli) ele.show(); + 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) src.show(); } if(milli >= layer.endMilli) { layer.endMilli += layer.dur; - for(var ele : layer.eles) { - ele.endMilli += layer.dur; - ele.startMilli += layer.dur; - if(ele.startTime > 0) ele.hide(); - else ele.show(); + for(var src : layer.srcs) { + src.endMilli += layer.dur; + src.startMilli += layer.dur; + if(src.startTime > 0) src.hide(); + else src.show(); } } } } } choreographer.postFrameCallback(this); + canAdd = false; } void syncProg(long milli) { curTimes = 1; progView.avas.clear(); - dur = 0; - Prog.Page page; - for(int i=0; i hases = null; + HashSet hases = null; while(true) { var obj = JSMap.from(in); var _type = obj.stnn("_type"); - System.out.println("_type: "+_type); + Util.println("_type: "+_type); if("consult".equals(_type)) { JSList ids = obj.jslist("idList"); - hases = new JSList<>(); + hases = new HashSet<>(); if(ids!=null) for(int i=0; i { - if(! progView.avas.isEmpty()) { + if(progView!=null && ! progView.avas.isEmpty()) { var page = page(curAva); - for(var layer : page.layers) for(var ele : layer.eles) if(ele.view.getVisibility()==VISIBLE) { + for(var layer : page.layers) for(var src : layer.srcs) if(src.view.getVisibility()==VISIBLE) { try { - if(ele.view instanceof EleVideo) { - var view = (EleVideo) ele.view; + if(src.view instanceof EleVideo) { + var view = (EleVideo) src.view; if(view.ijkPlayer!=null) { - writer.append("VideoPlaying: ").append(String.valueOf(view.ijkPlayer.isPlaying())).append(" ").append(String.valueOf(view.ijkPlayer.getCurrentPosition())).append("/").append(String.valueOf(view.ijkPlayer.getDuration())).append("\n"); - writer.append(" BitRate: ").append(String.valueOf(view.ijkPlayer.getBitRate()/1000)).append("\n"); - writer.append("VideoDecoder: ").append(String.valueOf(view.ijkPlayer.getVideoDecoder())).append("\n"); + 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(); - writer.append("Format: ").append(mediaInfo.mMeta.mFormat).append("\n"); - writer.append("VideoDecoder: ").append(mediaInfo.mVideoDecoder).append(" ").append(mediaInfo.mVideoDecoderImpl).append("\n"); + 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"); } else if(view.exoPlayer!=null) { - writer.append("isVideoPlaying: ").append(String.valueOf(view.exoPlayer.isPlaying())).append(" ").append(String.valueOf(view.exoPlayer.getCurrentPosition())).append("/").append(String.valueOf(view.exoPlayer.getDuration())).append("\n"); - writer.append("getPlaybackState: ").append(view.getState()).append("\n"); - writer.append("getPlayerError: ").append(String.valueOf(view.exoPlayer.getPlayerError())).append("\n"); - writer.append("getVideoFormat: ").append(String.valueOf(view.exoPlayer.getVideoFormat())).append("\n"); + 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 { - Util.makeText(MainActivity.this, e.getMessage()).show(); + Util.makeText(MainActivity.this, Util.toStr(e)).show(); }); - e.printStackTrace(); + Util.printStackTrace(e); } finally { O.close(in, out); + Util.println("conn end"); } } } catch (Throwable e) { MainActivity.ins.runOnUiThread(() -> { - Util.makeText(MainActivity.this, e.toString()).show(); + Util.makeText(MainActivity.this, Util.toStr(e)).show(); }); - e.printStackTrace(); + Util.printStackTrace(e); } } } \ 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 0d1f7af..a11c1d8 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Prog.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Prog.java @@ -9,10 +9,10 @@ import android.webkit.WebViewClient; import android.widget.ImageView; import java.io.File; -import java.time.LocalDate; -import java.time.LocalTime; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.HashSet; +import java.util.HashMap; import gnph.util.Dates; import gnph.util.JSList; @@ -24,19 +24,53 @@ public class Prog extends AbsLayout { ArrayList pages = new ArrayList<>(); ArrayList avas = new ArrayList<>(); - HashSet toBeHided = new HashSet<>(); public Prog(JSMap task, Context context) { super(context); 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){} + } + AbsLayout box = this; + if(partLengths!=null && partLengths.size() > 1) { + box = new AbsLayout(context); + var width = partObj.intg("width"); + var height = partObj.intg("height"); + 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"); - if(layers.isEmpty()) continue; - JSList splitWidths = _program.jslist("splitWidths"); + if(layers==null || layers.isEmpty()) continue; var page = new Page(); page.repeatTimes = pageMap.intg("repeatTimes"); page.parse(pageMap.jslist("schedules")); + HashMap videoMap = new HashMap<>(); for(int ll=layers.size()-1; ll>=0; ll--) { var layer = new Layer(); layer.isLoop = layers.get(ll).bool("repeat"); @@ -48,73 +82,139 @@ public class Prog extends AbsLayout { bdEle = new EleBorder(Util.programDir+"/"+border.stnn("img"), border.stnn("eff"), border.intg("speed"), context); bdWidth = bdEle.img.getHeight(); } - var ele = new Source(); - int x, y, w, h; - String id; + var src = new Source(); for(var source : sources) { - ele.type = source.stnn("_type"); - if(ele.type.isEmpty()) continue; + src.type = source.stnn("_type"); + if(src.type.isEmpty()) continue; var timeSpan = source.intg("timeSpan")*1000; if(timeSpan==0) continue; - x = source.intg("left")+bdWidth; - y = source.intg("top")+bdWidth; - w = source.intg("width")-bdWidth-bdWidth; - h = source.intg("height")-bdWidth-bdWidth; - boolean notAudio = ! ele.type.equals("Audio"); - if((w<=0 || h<=0) && notAudio) continue; - ele.startTime = source.intg("playTime")*1000; - if(bdStart > ele.startTime) bdStart = ele.startTime; - ele.endTime = ele.startTime + timeSpan; - if(bdEnd < ele.endTime) bdEnd = ele.endTime; - if(layer.dur < ele.endTime) layer.dur = ele.endTime; - if(page.sDur < ele.endTime && notAudio) page.sDur = ele.endTime; - id = source.stnn("id"); - ele.view = null; - if(ele.type.equals("Image")) { - var isGif = source.stnn("fileExt").equalsIgnoreCase("gif"); - ImageView imgView = isGif ? new GifImageView(context) : new ImageView(context); - imgView.setImageURI(ele.uri = Uri.fromFile(new File(Util.programDir + "/" + id))); - imgView.setScaleType(ImageView.ScaleType.FIT_XY); - if(! isGif) ele.uri = null; - ele.view = imgView; - } else if(ele.type.equals("MultiPng")) { - JSList imgs = source.jslist("arrayPics"); - if(imgs.isEmpty()) continue; - if(imgs.size()==1 && imgs.get(0).intg("picDuration")==0) ele.view = new EleScroll(context, Util.programDir+"/", imgs.get(0), w, h); - else ele.view = new EleFlip(Util.programDir+"/", imgs, context); - } else if(ele.type.equals("SplitText")) { - JSList imgs = source.jslist("arrayPics"); - if(imgs.isEmpty()) continue; - ele.view = new AbsLayout(context); - var partHeight = _program.intg("height"); - if(imgs.size()==1 && imgs.get(0).intg("picDuration")==0) { - var view = new EleScroll(context, Util.programDir+"/", imgs.get(0), w, h); - ((AbsLayout) ele.view).addView(view, new AbsLayout.LayoutParams(x, y, splitWidths.get(0).intValue(), h)); - view.others = new ArrayList<>(); - for(int i=1; i(); - for(int i=1; i src.startTime) bdStart = src.startTime; + src.endTime = src.startTime + timeSpan; + if(bdEnd < src.endTime) bdEnd = src.endTime; + if(layer.dur < src.endTime) layer.dur = src.endTime; + if(notAudio) { + 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; } } - w = 0; - } 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 + "/" + id, source, context); - else if(ele.type.equals("Video") || ele.type.equals("Audio")) ele.view = new EleVideo(context, Util.programDir + "/" +id, source.intg("vol", 100) / 100.0f); - else if(ele.type.equals("WebURL")) { + } + 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 = timeSpan*60/1000 - src.exitDur; + + var id = source.stnn("id"); + src.view = null; + if(src.type.equals("Image")) { + var isGif = source.stnn("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); + imgView.setImageURI(Uri.fromFile(new File(Util.programDir + "/" + id))); + imgView.setScaleType(ImageView.ScaleType.FIT_XY); + src.view = imgView; + } + } else if(src.type.equals("MultiPng") || src.type.equals("SplitText")) { + JSList imgs = source.jslist("arrayPics"); + if(imgs.isEmpty()) continue; + if(imgs.size()==1 && imgs.get(0).intg("picDuration")==0) src.view = new EleScroll(context, imgs.get(0)); + else { + var start = src.startTime; + var ele0 = src; + for(var map : imgs) { + var picDur = map.intg("picDuration")*1000; + if(picDur==0) continue; + src.type = "Image"; + src.startTime = start; + src.endTime = start = src.startTime + picDur; + if(src!=ele0) { + 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; + } + 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.startsWith("DigitalClock")) src.view = new EleDigiClock(context, source); + else if(src.type.equals("AnalogClock")) src.view = new EleAnaClock(geo.width, geo.height, Util.programDir + "/" + id, source, context); + else if(src.type.equals("Audio")) src.view = new EleVideo(context, Util.programDir + "/" +id, source.intg("vol", 100) / 100.0f); + else if(src.type.equals("Video")) { + var key = id + src.startTime + src.endTime; + var videoView = videoMap.get(key); + if(videoView!=null) { + var geoOld = (AbsLayout.LayoutParams) videoView.getLayoutParams(); + if(geo.width*geo.height > geoOld.width*geoOld.height) { + videoView.setLayoutParams(geo); + geo = geoOld; + } + src.view = new SrcCopy(context, videoView); + ((SrcCopy) src.view).scaleX = 0; + } else { + src.view = new EleVideo(context, Util.programDir+"/"+id, source.intg("vol", 100) / 100.0f); + videoMap.put(key, (EleVideo) src.view); + } + } else if(src.type.equals("WebURL")) { var webView = new WebView(context); webView.setWebViewClient(new WebViewClient() { @Override @@ -123,46 +223,46 @@ public class Prog extends AbsLayout { } }); webView.loadUrl(source.stnn("url")); - ele.view = webView; + src.view = webView; } - else if(ele.type.equals("Timer")) ele.view = new EleTimer(Util.programDir+"/", source, context); - else if(ele.type.equals("EnvironmentalMonitoring")) ele.view = new EleEnviron(Util.programDir+"/", source, context); + else if(src.type.equals("Timer")) src.view = new SrcTimer(context, source); + else if(src.type.equals("Countdown")) src.view = new SrcCountdown(context, source); + else if(src.type.startsWith("Environ")) src.view = new EleEnviron(context, source); else continue; - if(ele.view==null) continue; - if(w > 0) ele.view.setLayoutParams(new AbsLayout.LayoutParams(x, y, w, h)); - ele.view.setVisibility(GONE); - addView(ele.view); - layer.eles.add(ele); - ele = new Source(); + if(src.view==null) continue; + src.view.setVisibility(GONE); + src.view.setLayoutParams(geo); + box.addView(src.view); + layer.srcs.add(src); + src = new Source(); } if(bdEle!=null && ! sources.isEmpty()) { JSList geometry = border.jslist("geometry"); - x = geometry.get(0).intValue(); - y = geometry.get(1).intValue(); - w = geometry.get(2).intValue(); - h = geometry.get(3).intValue(); - ele.startTime = bdStart; - ele.endTime = bdEnd; - ele.view = bdEle; - ele.view.setVisibility(GONE); - addView(ele.view, new AbsLayout.LayoutParams(x, y, w, h)); - layer.eles.add(ele); + src.startTime = bdStart; + src.endTime = bdEnd; + 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.eles.isEmpty()) page.layers.add(layer); + if(! layer.srcs.isEmpty()) page.layers.add(layer); + } + if(page.sDur==0) { + if(page.audioDur > 0) page.sDur = page.audioDur; + else continue; } - if(page.sDur==0) continue; page.tDur = page.sDur * page.repeatTimes; for_layer: for(int ll=0; ll= page.sDur) { - if(layer.eles.size() > 1) layer.eles.remove(ee--); + for(int ss=0; ss= page.sDur) { + if(layer.srcs.size() > 1) layer.srcs.remove(ss--); else { page.layers.remove(ll--); continue for_layer; } - } else if(ele.endTime > page.sDur) ele.endTime = page.sDur; + } 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; @@ -177,61 +277,208 @@ public class Prog extends AbsLayout { View view; for(int cc=0; cc=exitStart) { + var fff = ff - exitStart; + if(fff > exitDur) fff = exitDur; + if(exitEff == Effect.EXPAND_HOR) view.setScaleX(1 - fff / (float) exitDur); + else if(exitEff == Effect.EXPAND_VER) view.setScaleY(1 - fff / (float) exitDur); + else if(exitEff == Effect.EXPAND_LEFT) { + var rate = fff / (float) exitDur; + view.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; + view.setScaleX(1 - rate); + view.setTranslationX(- rate * w / 2); + } else if(exitEff == Effect.EXPAND_BOTTOM) { + var rate = fff / (float) exitDur; + view.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(1 - fff / (float) exitDur); + else if(exitEff == Effect.ZOOM) { + view.setScaleX(1 - fff / (float) exitDur); + view.setScaleY(view.getScaleX()); + } else if(exitEff == Effect.ZOOM_TL) { + var rate = fff / (float) exitDur; + view.setTranslationX(- rate * w / 2); + view.setTranslationY(- rate * h / 2); + view.setScaleX(1 - rate); + view.setScaleY(1 - rate); + } else if(exitEff == Effect.ZOOM_BR) { + var rate = fff / (float) exitDur; + view.setTranslationX(rate * w / 2); + view.setTranslationY(rate * h / 2); + view.setScaleX(1 - rate); + view.setScaleY(1 - rate); + } else if(exitEff == Effect.ZOOM_TR) { + var rate = fff / (float) exitDur; + view.setTranslationX(rate * w / 2); + view.setTranslationY(- rate * h / 2); + view.setScaleX(1 - rate); + view.setScaleY(1 - rate); + } else if(exitEff == Effect.ZOOM_BL) { + var rate = fff / (float) exitDur; + view.setTranslationX(- rate * w / 2); + view.setTranslationY(rate * h / 2); + view.setScaleX(1 - rate); + view.setScaleY(1 - rate); + } else if(exitEff == Effect.ROTATE) { + var rate = fff / (float) exitDur; + view.setScaleX(1 - rate); + view.setScaleY(view.getScaleX()); + view.setRotation(rate * 360); + } else if(exitEff == Effect.ROTATE_R) { + var rate = fff / (float) exitDur; + view.setScaleX(1 - rate); + view.setScaleY(view.getScaleX()); + view.setRotation(- rate * 360); + } + } else resetEff(); + ff++; + } + void resetEff() { + view.setTranslationX(0); + view.setTranslationY(0); + view.setAlpha(1); + view.setScaleX(1); + view.setScaleY(1); + view.setRotation(0); + } } public static class Layer { - ArrayList eles = new ArrayList<>(); + 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; - JSList weeks; + JSList weeks; } public static class Page { ArrayList layers = new ArrayList<>(); ArrayList sches; long endMilli = Long.MAX_VALUE; - int sDur, tDur, repeatTimes; + int sDur, tDur, repeatTimes, audioDur; void hide() { - for(var layer : layers) for(var ele : layer.eles) ele.hide(); + for(var layer : layers) for(var src : layer.srcs) src.hide(); } public void setMillis(long milli) { endMilli = milli + sDur; for(var layer : layers) { if(layer.isLoop) layer.endMilli = milli + layer.dur; - for(var ele : layer.eles) { - ele.endMilli = milli + ele.endTime; - ele.startMilli = milli + ele.startTime; - if(ele.startTime == 0) ele.show(); + for(var src : layer.srcs) { + src.endMilli = milli + src.endTime; + src.startMilli = milli + src.startTime; + if(src.startTime == 0) src.show(); } } } @@ -239,12 +486,12 @@ public class Prog extends AbsLayout { if(sches==null) return true; var local = milli + Dates.zoneOff; var time = local % 86400000L; - var week = -1; + long week = -1; for(var sche : sches) { if(notInRange(sche.startDate, local, sche.endDate)) continue; if(notInRange(sche.startTime, time, sche.endTime)) continue; if(sche.weeks==null) return true; - if(week==-1) week = LocalDate.ofEpochDay(local / 86400000L).getDayOfWeek().getValue(); + if(week==-1) week = (local / 86400000L + 3) % 7 + 1; if(sche.weeks.contains(week)) return true; } return false; @@ -253,21 +500,48 @@ public class Prog extends AbsLayout { 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"); public void parse(JSList schedules) { if(schedules!=null) for(var schedule : schedules) { var sche = new Sche(); var startTime = schedule.str("startTime"); - if(startTime!=null) sche.startTime = LocalTime.parse(startTime).toSecondOfDay()*1000; + if(startTime!=null) { + if(startTime.length()==5) startTime += ":00"; + try { + sche.startTime = (int)timeFmt.parse(startTime).getTime()+Dates.zoneOff; + } catch (ParseException e) { + Util.printStackTrace(e); + } + } var endTime = schedule.str("endTime"); - if(endTime!=null) sche.endTime = LocalTime.parse(endTime).toSecondOfDay()*1000; + if(endTime!=null) { + if(endTime.length()==5) endTime += ":00"; + try { + sche.endTime = (int)timeFmt.parse(endTime).getTime()+Dates.zoneOff; + } catch (ParseException e) { + Util.printStackTrace(e); + } + } if(sche.startTime==sche.endTime) sche.startTime = sche.endTime = -1; var startDate = schedule.str("startDate"); - if(startDate!=null) sche.startDate = LocalDate.parse(startDate).toEpochDay() * 86400000L; + if(startDate!=null) { + try { + sche.startDate = dateFmt.parse(startDate).getTime() + Dates.zoneOff; + } catch (ParseException e) { + Util.printStackTrace(e); + } + } var endDate = schedule.str("endDate"); - if(endDate!=null) sche.endDate = (LocalDate.parse(endDate).toEpochDay() + 1) * 86400000L; + if(endDate!=null) { + try { + sche.endDate = dateFmt.parse(endDate).getTime() + Dates.zoneOff + 86400000L; + } catch (ParseException e) { + Util.printStackTrace(e); + } + } if(sche.startDate==sche.endDate) sche.startDate = sche.endDate = -1; - JSList weekFilter = schedule.jslist("weekFilter"); + JSList weekFilter = schedule.jslist("weekFilter"); if(weekFilter!=null && ! weekFilter.isEmpty() && weekFilter.size() < 7) sche.weeks = weekFilter; if(sches==null) sches = new ArrayList<>(); sches.add(sche); diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/RestartService.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/RestartService.java index 59098fa..33b5f21 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/RestartService.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/RestartService.java @@ -9,7 +9,7 @@ public class RestartService extends Service { @Override public void onCreate() { super.onCreate(); - System.out.println("---- RestartService onCreate"); + Util.println("---- RestartService onCreate"); if(MainActivity.ins!=null) return; var intent = new Intent(this, MainActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 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 34c61d1..bc485e3 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Server.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Server.java @@ -4,7 +4,6 @@ import android.annotation.SuppressLint; import android.app.Service; import android.content.Intent; import android.graphics.BitmapFactory; -import android.os.Environment; import android.os.IBinder; import android.os.RemoteException; @@ -13,10 +12,14 @@ import com.xixun.util.PlayerInfo; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; +import java.io.IOException; 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; @@ -30,6 +33,7 @@ public class Server extends Service { PlayerInfo.Stub binder = new PlayerInfo.Stub() { @Override public String getProgramName() throws RemoteException { + Util.println("Server getProgramName ..."); return null; } @@ -80,6 +84,7 @@ public class Server extends Service { @Override public String getCurProgramId() throws RemoteException { + Util.println("Server getCurProgramId ..."); return null; } @@ -91,19 +96,126 @@ public class Server extends Service { @SuppressLint("ResourceType") @Override public String executeJosnCommand(String jsonstr) throws RemoteException { - System.out.println("Server executeJsonCommand ..."+jsonstr);//{"_type":"DeleteTask","id":"652522a0e81d1e000009201a","sendTo":"yzd-player"} - String id = null; + Util.println("Server executeJsonCommand ..."+jsonstr);//{"_type":"DeleteTask","id":"652522a0e81d1e000009201a","sendTo":"yzd-player"} + String commandId = null; try { - var json = JSMap.from(jsonstr.getBytes(StandardCharsets.UTF_8)); + var jsonBytes = jsonstr.getBytes(StandardCharsets.UTF_8); + var json = JSMap.from(jsonBytes); var _type = json.stnn("_type"); - id = json.stnn("id"); - if(_type.equals("PlayXixunTask")) { - MainActivity.ins.runOnUiThread(() -> MainActivity.ins.delProgFile()); + commandId = json.stnn("id"); + boolean is2 = false; + if(_type.equals("PlayXixunTask") || (is2 = _type.equals("PlayProgramTask"))) { + var preDownloadURL = json.str("preDownloadURL"); + if(preDownloadURL==null && is2) preDownloadURL = "https://m2mled.net/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")) { + if(preDownloadURL==null) continue; + 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.equals("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")) { + if(preDownloadURL==null) continue; + 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"); + 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); + } + cnt++; + if(notificationURL!=null && cnt!=needDown.srcs.size()) { + try { + new URLConn(notificationURL).timeout(5000).writeJson(new JSMap( + "commandId", finalCommandId, + "taskItemId", needDown.prog, + "progress", cnt*100/needDown.srcs.size()).toStr()).read(); + } catch (IOException e) { + Util.printStackTrace(e); + } + } + } + if(notificationURL!=null) { + try { + new URLConn(notificationURL).timeout(5000).writeJson(new JSMap( + "commandId", finalCommandId, + "taskItemId", needDown.prog, + "progress", 100).toStr()).read(); + } catch (IOException e) { + Util.printStackTrace(e); + } + } + } + try { + IOs.writeClose(new FileOutputStream(Util.programDir+"/program"), jsonBytes); + MainActivity.ins.runOnUiThread(MainActivity.ins::initProg); + } catch (Exception e) { + Util.printStackTrace(e); + } + }).start(); return new JSMap( - "_type", "DataCallback", + "_type", "Success", "result", "received command", "cardId", Util.getCardId(), - "commandId", id + "commandId", commandId ).toString(); } else if(_type.equals("DeleteTask")) { MainActivity.ins.runOnUiThread(() -> MainActivity.ins.delProgFile()); @@ -111,14 +223,14 @@ public class Server extends Service { "_type", "DataCallback", "result", "received command", "cardId", Util.getCardId(), - "commandId", id + "commandId", commandId ).toString(); } else if(_type.equals("PlayerStateCommand")) { var descs = Util.getState(MainActivity.ins.state); return new JSMap( "_type", "Success", "cardId", Util.getCardId(), - "commandId", id, + "commandId", commandId, "code", MainActivity.ins.state, "des_en", descs[0], "des", descs[1] @@ -134,7 +246,7 @@ public class Server extends Service { return new JSMap( "_type", "Success", "cardId", Util.getCardId(), - "commandId", id + "commandId", commandId ).toString(); } else if(_type.equals("GetPlayerBackground")) { var backImg = new File(Util.backImgFile); @@ -142,7 +254,7 @@ public class Server extends Service { return new JSMap( "_type", "DataCallback", "cardId", Util.getCardId(), - "commandId", id, + "commandId", commandId, "img", img ).toString(); } @@ -150,15 +262,15 @@ public class Server extends Service { "_type", "Error", "errorMessage", "Unknow Type", "cardId", Util.getCardId(), - "commandId", id + "commandId", commandId ).toString(); } catch (Exception e) { - e.printStackTrace(); + Util.printStackTrace(e); return new JSMap( "_type", "Error", "errorMessage", e.toString(), "cardId", Util.getCardId(), - "commandId", id + "commandId", commandId ).toString(); } } @@ -209,4 +321,18 @@ public class Server extends Service { 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/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcCopy.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcCopy.java new file mode 100644 index 0000000..52498d7 --- /dev/null +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcCopy.java @@ -0,0 +1,41 @@ +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; + +public class SrcCopy extends View { + + View view; + float scaleX = 1, scaleY = 1; + + public SrcCopy(Context context, View view) { + super(context); + this.view = view; + } + + @Override + protected void onDraw(@NonNull Canvas canvas) { + super.onDraw(canvas); + if(view==null) return; + if(scaleX==0) { + if(view.getWidth()!=0&&getWidth()!=0) { + scaleX = getWidth() / (float) view.getWidth(); + scaleY = getHeight() / (float) view.getHeight(); + } else { + invalidate(); + return; + } + } + if(scaleX!=1 || scaleY!=1) canvas.scale(scaleX, scaleY); + view.draw(canvas); + invalidate(); + } +} diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcCountdown.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcCountdown.java new file mode 100644 index 0000000..26b7612 --- /dev/null +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcCountdown.java @@ -0,0 +1,98 @@ +package com.xixun.xixunplayer; + +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.view.Choreographer; +import android.view.View; +import android.webkit.WebView; + +import androidx.annotation.NonNull; + +import java.util.ArrayList; +import java.util.HashMap; + +import gnph.util.Dates; +import gnph.util.JSMap; +import gnph.util.NumFmts; + +public class SrcCountdown extends WebView implements Choreographer.FrameCallback { + + long targetTime; + String html; + boolean hasDay, hasHour, hasMin, hasSec; + + public SrcCountdown(Context context, JSMap json) { + super(context); + try { + html = json.stnn("html"); + targetTime = Dates.milli(json.stnn("time")) / 1000; + hasDay = html.contains("%d"); + hasHour = html.contains("%h"); + hasMin = html.contains("%m"); + hasSec = html.contains("%s"); + } catch (Exception e) { + Util.printStackTrace(e); + Util.makeText(context, Util.toStr(e)).show(); + } + } + + void cal() { + var cur = System.currentTimeMillis() / 1000; + var secs = targetTime - cur; + if(secs < 0) secs = 0; + var htm = html; + if(hasDay) { + var str = Long.toString(secs/86400); + secs %= 86400; + htm = htm.replace("%d", str); + } + if(hasHour) { + var str = NumFmts.zz().format(secs/3600); + secs %= 3600; + htm = htm.replace("%h", str); + } + if(hasMin) { + var str = NumFmts.zz().format(secs/60); + secs %= 60; + htm = htm.replace("%m", str); + } + if(hasSec) { + var str = NumFmts.zz().format(secs); + htm = htm.replace("%s", str); + } + loadData(htm, "text/html", null); + } + + @Override + protected void onVisibilityChanged(@NonNull View changedView, int visibility) { + super.onVisibilityChanged(changedView, visibility); + if(visibility==View.VISIBLE) { + if(lastSec==0) { + cal(); + choreographer.postFrameCallback(this); + lastSec = 1; + } + } + } + + Choreographer choreographer = Choreographer.getInstance(); + long lastSec; + + @Override + public void doFrame(long frameTimeNanos) { + if(! isShown()) { + lastSec = 0; + return; + } + var sec = System.currentTimeMillis() / 1000; + if(sec != lastSec) { + lastSec = sec; + cal(); + } + choreographer.postFrameCallback(this); + } +} \ No newline at end of file diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcImage.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcImage.java new file mode 100644 index 0000000..82a842a --- /dev/null +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcImage.java @@ -0,0 +1,174 @@ +package com.xixun.xixunplayer; + + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.RectF; +import android.view.Choreographer; +import android.view.View; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import gnph.util.JSList; +import gnph.util.JSMap; + +public class SrcImage extends View implements Choreographer.FrameCallback { + + static final char effTypes[] = {'l', 't', 'r', 'b'}; + Bitmap img; + Rect RECT = new Rect(); + RectF rect; + Paint paint = new Paint(); + int entryDur, exitStart; + char entryType, exitType; + boolean isEntryRand, isExitRand; + + public SrcImage(Context context, JSMap map, int w, int h) { + super(context); + RECT.right = w; + RECT.bottom = h; + img = BitmapFactory.decodeFile(Util.programDir+"/"+map.stnn("id")); + if(img==null) return; + rect = new RectF(0, 0, w, h); +// entryDur = map.intg("entryEffectTimeSpan")*60; +// if(entryDur > 0) { +// var effStr = map.str("entryEffect"); +// if(effStr == null || effStr.equalsIgnoreCase("None")) entryDur = 0; +// else if(effStr.equalsIgnoreCase("moving_left")) entryType = 'l'; +// else if(effStr.equalsIgnoreCase("moving_top")) entryType = 't'; +// else if(effStr.equalsIgnoreCase("moving_right")) entryType = 'r'; +// else if(effStr.equalsIgnoreCase("moving_bottom")) entryType = 'b'; +// else if(effStr.equalsIgnoreCase("alpha_in")) entryType = 'a'; +// else if(effStr.equalsIgnoreCase("zoom_in")) entryType = 'z'; +// else if(effStr.equalsIgnoreCase("Random")) isEntryRand = true; +// else entryDur = 0; +// } +// var exitDur = map.intg("exitEffectTimeSpan")*60; +// if(exitDur > 0) { +// var effStrExit = map.str("exitEffect"); +// if(effStrExit == null || effStrExit.equalsIgnoreCase("None")) exitDur = 0; +// else if(effStrExit.equalsIgnoreCase("moving_left")) exitType = 'l'; +// else if(effStrExit.equalsIgnoreCase("moving_top")) exitType = 't'; +// else if(effStrExit.equalsIgnoreCase("moving_right")) exitType = 'r'; +// else if(effStrExit.equalsIgnoreCase("moving_bottom")) exitType = 'b'; +// else if(effStrExit.equalsIgnoreCase("alpha_out")) exitType = 'a'; +// else if(effStrExit.equalsIgnoreCase("zoom_out")) exitType = 'z'; +// else if(effStrExit.equalsIgnoreCase("Random")) isExitRand = true; +// else exitDur = 0; +// } +// exitStart = exitDur==0 ? Integer.MAX_VALUE : map.intg("timeSpan")*60 - exitDur; + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + drawOther(canvas, this); + } + public void drawOther(Canvas canvas, View view) { +// var fff = ff==0 ? 0 : ff-1; +// if(fff < entryDur) { +// if(entryType=='l') { +// rect.left = RECT.right - fff*RECT.right/(float) entryDur; +// rect.right = rect.left + RECT.right; +// canvas.drawBitmap(img, null, rect, null); +// } else if(entryType=='r') { +// rect.left = -RECT.right + fff*RECT.right/(float) entryDur; +// rect.right = rect.left + RECT.right; +// canvas.drawBitmap(img, null, rect, null); +// } else if(entryType=='t') { +// rect.top = RECT.bottom - fff*RECT.bottom/(float) entryDur; +// rect.bottom = rect.top + RECT.bottom; +// canvas.drawBitmap(img, null, rect, null); +// } else if(entryType=='b') { +// rect.top = -RECT.bottom + fff*RECT.bottom/(float) entryDur; +// rect.bottom = rect.top + RECT.bottom; +// canvas.drawBitmap(img, null, rect, null); +// } else if(entryType=='a') { +// paint.setAlpha(fff*255/entryDur); +// canvas.drawBitmap(img, null, rect, paint); +// } else if(entryType=='z') { +// var haw = RECT.right/2f; +// var hah = RECT.bottom/2f; +// var ww = fff*haw/(float) entryDur; +// var hh = fff*hah/(float) entryDur; +// rect.left = haw - ww; +// rect.right = haw + ww; +// rect.top = hah - hh; +// rect.bottom = hah + hh; +// canvas.drawBitmap(img, null, rect, null); +// } +// } else if(fff>=exitStart) { +// fff -= exitStart; +// if(exitType=='l') { +// rect.left = - fff*RECT.right/(float) entryDur; +// rect.right = rect.left + RECT.right; +// canvas.drawBitmap(img, null, rect, null); +// } else if(exitType=='r') { +// rect.left = fff*RECT.right/(float) entryDur; +// rect.right = rect.left + RECT.right; +// canvas.drawBitmap(img, null, rect, null); +// } else if(exitType=='t') { +// rect.top = - fff*RECT.bottom/(float) entryDur; +// rect.bottom = rect.top + RECT.bottom; +// canvas.drawBitmap(img, null, rect, null); +// } else if(exitType=='b') { +// rect.top = fff*RECT.bottom/(float) entryDur; +// rect.bottom = rect.top + RECT.bottom; +// canvas.drawBitmap(img, null, rect, null); +// } else if(exitType=='a') { +// paint.setAlpha(255 - fff*255/entryDur); +// canvas.drawBitmap(img, null, rect, paint); +// } else if(exitType=='z') { +// var haw = RECT.right/2f; +// var hah = RECT.bottom/2f; +// var ww = haw - fff*haw/(float) entryDur; +// var hh = hah - fff*hah/(float) entryDur; +// rect.left = haw - ww; +// rect.right = haw + ww; +// rect.top = hah - hh; +// rect.bottom = hah + hh; +// canvas.drawBitmap(img, null, rect, null); +// } +// } else + canvas.drawBitmap(img, null, RECT, null); + } + + Choreographer choreographer = Choreographer.getInstance(); + int ff; + boolean canAdd = true; + +// @Override +// public void onVisibilityAggregated(boolean isVisible) { +// super.onVisibilityAggregated(isVisible); +// ff = 0; +// if(isVisible) { +// if(canAdd && (entryDur > 0 || exitStart < Integer.MAX_VALUE)) { +// choreographer.postFrameCallback(this); +// canAdd = false; +// } +// } +// } + @Override + public void doFrame(long frameTimeNanos) { + if(! isShown()) { + canAdd = true; + return; + } + if(ff <= entryDur || ff>=exitStart) { + if(ff==0) { + if(isEntryRand) entryType = effTypes[Util.rand.nextInt(4)]; + if(isExitRand) exitType = effTypes[Util.rand.nextInt(4)]; + } + invalidate(); + } + ff++; + choreographer.postFrameCallback(this); + canAdd = false; + } +} \ No newline at end of file diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleTimer.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcTimer.java similarity index 91% rename from XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleTimer.java rename to XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcTimer.java index 6f971a9..5f1c25b 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/EleTimer.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcTimer.java @@ -1,26 +1,22 @@ package com.xixun.xixunplayer; -import android.annotation.SuppressLint; 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.graphics.Typeface; import android.view.Choreographer; import android.view.View; -import android.widget.ImageView; import java.util.ArrayList; import java.util.HashMap; import gnph.util.Dates; -import gnph.util.JSList; import gnph.util.JSMap; import gnph.util.NumFmts; -public class EleTimer extends View implements Choreographer.FrameCallback { +public class SrcTimer extends View implements Choreographer.FrameCallback { HashMap imgMap = new HashMap<>(); ArrayList imgs = new ArrayList<>(); @@ -30,15 +26,12 @@ public class EleTimer extends View implements Choreographer.FrameCallback { long targetTime; boolean isDown; boolean isMultiline; - boolean hasDay; - boolean hasHour; - boolean hasMin; - boolean hasSec; + boolean hasDay, hasHour, hasMin, hasSec; - public EleTimer(String prefix, JSMap json, Context context) { + public SrcTimer(Context context, JSMap json) { super(context); var imgEntrys = json.jsmap("imgs").entrySet(); - for(var imgEntry : imgEntrys) imgMap.put(imgEntry.getKey(), BitmapFactory.decodeFile(prefix+imgEntry.getValue())); + 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"); @@ -132,6 +125,7 @@ public class EleTimer extends View implements Choreographer.FrameCallback { if(lastSec==0) { cal(); choreographer.postFrameCallback(this); + lastSec = 1; } int x = (getWidth()-len) / 2, y; if(text!=null && isMultiline) { @@ -148,7 +142,7 @@ public class EleTimer extends View implements Choreographer.FrameCallback { } Choreographer choreographer = Choreographer.getInstance(); - long lastSec = 0; + long lastSec; @Override public void doFrame(long frameTimeNanos) { @@ -156,7 +150,7 @@ public class EleTimer extends View implements Choreographer.FrameCallback { lastSec = 0; return; } - var sec = frameTimeNanos / 1000000000; + var sec = System.currentTimeMillis() / 1000; if(sec != lastSec) { lastSec = sec; cal(); 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 7cc414e..06ad091 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Util.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Util.java @@ -6,12 +6,15 @@ import android.os.StatFs; import android.view.Gravity; import android.widget.Toast; +import java.io.CharArrayWriter; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.io.PrintWriter; import java.util.Arrays; import java.util.HashMap; -import java.util.List; +import java.util.Random; +import java.util.Set; import java.util.concurrent.CountDownLatch; import gnph.util.IOs; @@ -34,6 +37,33 @@ public class Util { } public static final String[] stateDescsUnknow = {"Unknown", "未知"}; + public static StringBuffer buf = new StringBuffer(); + public static Random rand = new Random(); + + public static void println(String msg) { + System.out.println(msg); + 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); @@ -56,12 +86,12 @@ public class Util { if(cardId.length() > 13) cardId = cardId.substring(0, 13); return cardId; } catch (IOException e) { - e.printStackTrace(); + printStackTrace(e); return ""; } } - public static void deleteFiles(int proSize, List keeps) { + public static void deleteFiles(long proSize, Set keeps) { var statFs = new StatFs(Environment.getExternalStorageDirectory().getPath()); var remain = statFs.getAvailableBytes() - proSize - 1048576; if(remain >= 0) return; diff --git a/XixunPlayer/app/src/main/res/mipmap-anydpi/ic_launcher.xml b/XixunPlayer/app/src/main/res/mipmap-anydpi/ic_launcher.xml deleted file mode 100644 index 6f3b755..0000000 --- a/XixunPlayer/app/src/main/res/mipmap-anydpi/ic_launcher.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/XixunPlayer/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/XixunPlayer/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml deleted file mode 100644 index 6f3b755..0000000 --- a/XixunPlayer/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/keystore 3568/3568A(视美泰)/keystore.jks b/keystore 3568/3568A(视美泰)/keystore.jks new file mode 100644 index 0000000..cb5c296 Binary files /dev/null and b/keystore 3568/3568A(视美泰)/keystore.jks differ diff --git a/keystore 3568/3568D(卓策)/xixun-d.keystore b/keystore 3568/3568D(卓策)/xixun-d.keystore new file mode 100644 index 0000000..53f5e84 Binary files /dev/null and b/keystore 3568/3568D(卓策)/xixun-d.keystore differ