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 0035293..9aefca3 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/MainActivity.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/MainActivity.java @@ -11,6 +11,8 @@ import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; +import android.media.AudioFocusRequest; +import android.media.AudioManager; import android.os.Build; import android.os.Bundle; import android.os.IBinder; @@ -19,7 +21,6 @@ import android.view.Choreographer; import android.view.View; import androidx.annotation.NonNull; -import androidx.annotation.RequiresApi; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; @@ -31,6 +32,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedList; @@ -52,7 +54,6 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac long syncMs; int state; - @RequiresApi(api = Build.VERSION_CODES.R) @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -63,6 +64,8 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac setContentView(backView = new BackView(MainActivity.this)); state = 5; + hookWebView(); + if(ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) init(); else { Util.println("---- No permission, Try again ..."); @@ -86,7 +89,40 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac Util.makeText(this, "Request Permission Failed").show(); } } - + @SuppressLint("SoonBlockedPrivateApi") + public static void hookWebView() { + if(android.os.Process.myUid() != android.os.Process.SYSTEM_UID) return; + try { + var factoryClass = Class.forName("android.webkit.WebViewFactory"); + var field = factoryClass.getDeclaredField("sProviderInstance"); + field.setAccessible(true); + var sProviderInstance = field.get(null); + if (sProviderInstance != null) { + Util.println("sProviderInstance isn't null"); + return; + } + Method getProviderClassMethod; + if (Build.VERSION.SDK_INT > 22) { // above 22 + getProviderClassMethod = factoryClass.getDeclaredMethod("getProviderClass"); + } else if (Build.VERSION.SDK_INT == 22) { // method name is a little different + getProviderClassMethod = factoryClass.getDeclaredMethod("getFactoryClass"); + } else { // no security check below 22 + Util.println("Don't need to Hook WebView"); + return; + } + getProviderClassMethod.setAccessible(true); + var providerClass = (Class) getProviderClassMethod.invoke(factoryClass); + var delegateClass = Class.forName("android.webkit.WebViewDelegate"); + var declaredConstructor = delegateClass.getDeclaredConstructor(); + declaredConstructor.setAccessible(true); + sProviderInstance = providerClass.getDeclaredMethod("create", delegateClass).invoke(providerClass, declaredConstructor.newInstance()); + field.set("sProviderInstance", sProviderInstance); + Util.println("sProviderInstance"+sProviderInstance.toString()); + Util.println("Hook Done!"); + } catch (Throwable e) { + Util.printStackTrace(e); + } + } @Override protected void onDestroy() { super.onDestroy(); @@ -103,6 +139,7 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac ins = null; for(var rece : reces) unregisterReceiver(rece); for(var service : services) unbindService(service); + if(audioManager!=null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) audioManager.abandonAudioFocusRequest(audioFocusRequest); try (var fout = new FileOutputStream(Util.programDir+"/cfg")){ Util.cfg.write(fout); fout.flush(); @@ -324,8 +361,16 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac } var ms = System.currentTimeMillis(); if(! avas.isEmpty()) { - avas.get(curAva).hide(); - if(code > 0 && avas.get(curAva)==progView.pages.get(code-1)) for(var call : progView.calls) call.doFrame(ms); + var page = avas.get(curAva); + for(var layer : page.layers) for(var src : layer.srcs) if(src.isShow) { + showHides.add(new MainActivity.ShowHide(ms+1000, src, 'H')); + src.isShow = false; + } + if(code > 0 && avas.get(curAva)==progView.pages.get(code-1)) { + showHides.add(new ShowHide(ms + 1000, ()->{ + for(var call : progView.calls) call.doFrame(ms); + })); + } } if(code==0) syncProg(ms, 0); else { @@ -385,8 +430,52 @@ public class MainActivity extends Activity implements Choreographer.FrameCallbac } }).start(); } + if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); + audioFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK).setOnAudioFocusChangeListener((int focusChange)-> { + switch (focusChange) { + case AudioManager.AUDIOFOCUS_GAIN: + Util.println("AUDIOFOCUS_GAIN"); + Util.isAudioGain = true; + break; + case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK: + Util.println("AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK"); + Util.isAudioGain = true; + break; + case AudioManager.AUDIOFOCUS_LOSS: + Util.println("AUDIOFOCUS_LOSS"); + Util.isAudioGain = false; + break; + case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: + Util.println("AUDIOFOCUS_LOSS_TRANSIENT"); //短暂失去音频焦点,暂停播放等待又一次获得音频焦点 + Util.isAudioGain = false; + break; + case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: + Util.println("AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK"); //减少声音就可以 + Util.isAudioGain = false; + break; + } + View view; + if(Util.isAudioGain) { + if(insView!=null) { + for(int cc=0; cc shows) { endMilli = start + sDur; for(var layer : layers) { diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideo.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideo.java index 0390d82..265c355 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideo.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcVideo.java @@ -16,10 +16,12 @@ public class SrcVideo extends TextureView implements TextureView.SurfaceTextureL IjkMediaPlayer ijkPlayer; Surface surface; + float vol; long bitRate; public SrcVideo(Context context, String path, float vol, int dur, long seek, boolean useSW) { super(context); + this.vol = vol; setSurfaceTextureListener(this); Util.println(" video new"); ijkPlayer = new IjkMediaPlayer(); @@ -33,7 +35,8 @@ public class SrcVideo extends TextureView implements TextureView.SurfaceTextureL try { ijkPlayer.setDataSource(path); ijkPlayer.setLooping(true); - ijkPlayer.setVolume(vol, vol); + if(Util.isAudioGain) ijkPlayer.setVolume(vol, vol); + else ijkPlayer.setVolume(0, 0); ijkPlayer.setOnPreparedListener((IMediaPlayer var1)->{ ijkPlayer.setOnPreparedListener(null); if(getAlpha() < 0.25) { @@ -74,15 +77,17 @@ public class SrcVideo extends TextureView implements TextureView.SurfaceTextureL public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {} void release() { - Util.println(" video release"); + Util.println(" video releasing"); if(ijkPlayer!=null) { ijkPlayer.release(); ijkPlayer = null; } + Util.println(" surface releasing"); if(surface!=null) { surface.release(); surface = null; } + Util.println(" surface released"); } // @Override diff --git a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcWeb.java b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcWeb.java index 277bf1a..f9b9b59 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcWeb.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/SrcWeb.java @@ -52,7 +52,10 @@ public class SrcWeb extends WebView implements Choreographer.FrameCallback { @Override public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if(request.isForMainFrame() && error.getErrorCode()==-2 && request.getUrl().toString().equals(url)) needReload = true; + if(request.isForMainFrame() && error.getErrorCode()==-2 && request.getUrl().toString().equals(url)) { + needReload = true; + Util.println(" WebView Need Reload"); + } Util.println(" WebView ReceivedError "+error.getErrorCode()+" "+error.getDescription()+"; isForMainFrame "+request.isForMainFrame()+" URL "+request.getUrl()); } } 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 9801bef..42a7d06 100644 --- a/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Util.java +++ b/XixunPlayer/app/src/main/java/com/xixun/xixunplayer/Util.java @@ -43,7 +43,7 @@ public class Util { public static volatile long downId; public static int screenWidth = 1920, screenHeight = 1080; public static double lat, lng; - public static boolean isScreenOn, logOn; + public static boolean isScreenOn, isAudioGain, logOn; public static void initDir(Context ctx) { var dir = Build.VERSION.SDK_INT < Build.VERSION_CODES.R ? Environment.getExternalStorageDirectory().getAbsolutePath() + "/XixunPlayer" : ctx.getExternalFilesDir(null).getAbsolutePath();