Refactor XashActivity, fix crash in XashService, add compat wrappers

This commit is contained in:
mittorn 2020-07-07 07:28:43 +07:00
parent ac0e9b633d
commit 8602bd4e9e
5 changed files with 747 additions and 689 deletions

View file

@ -59,6 +59,12 @@ public class LauncherActivity extends Activity
}
setContentView(R.layout.activity_launcher);
if( sdk > 17 )
{
ImageView icon = (ImageView) findViewById(R.id.launcherIcon);
icon.setImageDrawable(getResources().getDrawable(R.mipmap.ic_launcher));
}
TabHost tabHost = (TabHost) findViewById(R.id.tabhost);
@ -264,12 +270,11 @@ public class LauncherActivity extends Activity
hideRodirSettings( !useRoDir.isChecked() );
updateResolutionResult();
toggleResolutionFields();
FWGSLib.applyPermissions( this, new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, REQUEST_PERMISSIONS );
FWGSLib.cmp.applyPermissions( this, new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, REQUEST_PERMISSIONS );
if( !mPref.getBoolean("successfulRun",false) )
showFirstRun();
}
@Override
public void onRequestPermissionsResult( int requestCode, String[] permissions, int[] grantResults )
{
if( requestCode == REQUEST_PERMISSIONS )
@ -434,6 +439,12 @@ public class LauncherActivity extends Activity
dialog.setContentView(R.layout.about);
dialog.setCancelable(true);
dialog.show();
if( sdk > 17 )
{
ImageView icon = (ImageView) dialog.findViewById(R.id.aboutIcon);
icon.setImageDrawable(getResources().getDrawable(R.mipmap.ic_launcher));
}
TextView tView6 = (TextView) dialog.findViewById(R.id.textView6);
tView6.setMovementMethod(LinkMovementMethod.getInstance());
((Button)dialog.findViewById( R.id.button_about_ok )).setOnClickListener(new View.OnClickListener(){

View file

@ -32,7 +32,7 @@ import java.security.MessageDigest;
import su.xash.engine.R;
import su.xash.engine.XashConfig;
import su.xash.engine.JoystickHandler;
import su.xash.engine.XashInput;
import android.provider.Settings.Secure;
import android.Manifest;
@ -57,8 +57,7 @@ public class XashActivity extends Activity {
public static final int sdk = Integer.valueOf( Build.VERSION.SDK );
public static final String TAG = "XASH3D:XashActivity";
public static int mPixelFormat;
public static JoystickHandler handler;
public static ImmersiveMode mImmersiveMode;
public static XashInput.JoystickHandler handler;
public static boolean keyboardVisible = false;
public static boolean mEngineReady = false;
public static boolean mEnginePaused = false;
@ -110,6 +109,7 @@ public class XashActivity extends Activity {
{
Log.v( TAG, "onCreate()" );
super.onCreate( savedInstanceState );
mEngineReady = false;
if( sdk >= 8 && CertCheck.dumbAntiPDALifeCheck( this ) )
@ -137,7 +137,7 @@ public class XashActivity extends Activity {
mUseRoDir = mPref.getBoolean("use_rodir", false);
mWriteDir = mPref.getString("writedir", FWGSLib.getExternalFilesDir( this ));
FWGSLib.applyPermissions( this, new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, REQUEST_PERMISSIONS );
FWGSLib.cmp.applyPermissions( this, new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, REQUEST_PERMISSIONS );
// just in case
if( mWriteDir.length() == 0 )
@ -161,8 +161,7 @@ public class XashActivity extends Activity {
checkWritePermission( basedir );
}
}
@Override
public void onRequestPermissionsResult( int requestCode, String[] permissions, int[] grantResults )
{
if( requestCode == REQUEST_PERMISSIONS )
@ -294,10 +293,7 @@ public class XashActivity extends Activity {
super.onWindowFocusChanged( hasFocus );
if( mImmersiveMode != null )
{
mImmersiveMode.apply();
}
FWGSLib.cmp.applyImmersiveMode(keyboardVisible, mDecorView);
}
public static void setFolderAsk( Context ctx, Boolean b )
@ -430,14 +426,7 @@ public class XashActivity extends Activity {
SurfaceHolder holder = mSurface.getHolder();
holder.setType( SurfaceHolder.SURFACE_TYPE_GPU );
if( sdk >= 14 )
handler = new JoystickHandler_v14();
else if( sdk >= 12 )
handler = new JoystickHandler_v12();
else
handler = new JoystickHandler();
handler.init();
handler = XashInput.getJoystickHandler();
mVibrator = ( Vibrator )getSystemService( Context.VIBRATOR_SERVICE );
mHasVibrator = handler.hasVibrator() && (mVibrator != null);
@ -446,14 +435,8 @@ public class XashActivity extends Activity {
mUseVolume = mPref.getBoolean( "usevolume", false );
if( mPref.getBoolean( "enableResizeWorkaround", true ) )
AndroidBug5497Workaround.assistActivity( this );
// Immersive Mode is available only at >KitKat
Boolean enableImmersive = ( sdk >= 19 ) && ( mPref.getBoolean( "immersive_mode", true ) );
if( enableImmersive )
mImmersiveMode = new ImmersiveMode_v19();
else mImmersiveMode = new ImmersiveMode();
mDecorView = getWindow().getDecorView();
if( XashActivity.mPref.getBoolean( "immersive_mode", false ) )
mDecorView = getWindow().getDecorView();
if( mPref.getBoolean( "resolution_fixed", false ) )
{
@ -480,11 +463,7 @@ public class XashActivity extends Activity {
}
}
}
if( sdk >= Build.VERSION_CODES.O )
startForegroundService( new Intent( getBaseContext(), XashService.class ) );
else if( sdk >= 5 )
startService( new Intent( getBaseContext(), XashService.class ) );
FWGSLib.cmp.startForegroundService( this, new Intent( getBaseContext(), XashService.class ) );
mEngineReady = true;
}
@ -494,18 +473,7 @@ public class XashActivity extends Activity {
{
Intent intent = getIntent();
String enginedir;
try
{
ApplicationInfo ai = FWGSLib.getApplicationInfo(this, null, 0);
enginedir = ai.nativeLibraryDir;
}
catch(Exception e)
{
e.printStackTrace();
enginedir = getFilesDir().getParentFile().getPath() + "/lib";
}
String enginedir = FWGSLib.cmp.getNativeLibDir(this);
String argv = FWGSLib.getStringExtraFromIntent( intent, "argv", mPref.getString( "argv", "-dev 3 -log" ) );
String gamelibdir = FWGSLib.getStringExtraFromIntent( intent, "gamelibdir", enginedir );
@ -920,16 +888,14 @@ public class XashActivity extends Activity {
imm.showSoftInput( mTextEdit, 0 );
keyboardVisible = true;
if( XashActivity.mImmersiveMode != null )
XashActivity.mImmersiveMode.apply();
FWGSLib.cmp.applyImmersiveMode( keyboardVisible, mDecorView );
}
else
{
mTextEdit.setVisibility( View.GONE );
imm.hideSoftInputFromWindow( mTextEdit.getWindowToken(), 0 );
keyboardVisible = false;
if( XashActivity.mImmersiveMode != null )
XashActivity.mImmersiveMode.apply();
FWGSLib.cmp.applyImmersiveMode( keyboardVisible, mDecorView );
}
}
}
@ -1050,18 +1016,6 @@ public class XashActivity extends Activity {
}
}
/**
Simple nativeInit() runnable
*/
class XashMain implements Runnable
{
public void run()
{
XashActivity.nativeInit( XashActivity.mArgv );
}
}
/**
EngineSurface. This is what we draw on, so we need to know when it's created
in order to do anything useful.
@ -1096,10 +1050,7 @@ class EngineSurface extends SurfaceView implements SurfaceHolder.Callback, View.
setFocusableInTouchMode( true );
requestFocus();
setOnKeyListener( this );
if( XashActivity.sdk >= 5 )
setOnTouchListener( new EngineTouchListener_v5() );
else
setOnTouchListener( new EngineTouchListener_v1() );
setOnTouchListener( XashInput.getTouchListener() );
}
// Called when we have a valid drawing surface
@ -1161,7 +1112,13 @@ class EngineSurface extends SurfaceView implements SurfaceHolder.Callback, View.
// Now start up the C app thread
if( mEngThread == null )
{
mEngThread = new Thread( new XashMain(), "EngineThread" );
mEngThread = new Thread( new Runnable(){
@Override
public void run()
{
XashActivity.nativeInit( XashActivity.mArgv );
}
}, "EngineThread" );
mEngThread.start();
}
resizing = false;
@ -1381,214 +1338,6 @@ class EngineSurface extends SurfaceView implements SurfaceHolder.Callback, View.
}
/* This is a fake invisible editor view that receives the input and defines the
* pan&scan region
*/
class DummyEdit extends View implements View.OnKeyListener
{
InputConnection ic;
public DummyEdit( Context context )
{
super( context );
setFocusableInTouchMode( true );
setFocusable( true );
setOnKeyListener( this );
}
@Override
public boolean onCheckIsTextEditor()
{
return true;
}
@Override
public boolean onKey( View v, int keyCode, KeyEvent event )
{
return XashActivity.handleKey( keyCode, event );
}
@Override
public InputConnection onCreateInputConnection( EditorInfo outAttrs )
{
ic = new XashInputConnection( this, true );
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
| 33554432 /* API 11: EditorInfo.IME_FLAG_NO_FULLSCREEN */;
return ic;
}
}
class XashInputConnection extends BaseInputConnection
{
public XashInputConnection( View targetView, boolean fullEditor )
{
super( targetView, fullEditor );
}
@Override
public boolean sendKeyEvent( KeyEvent event )
{
if( XashActivity.handleKey( event.getKeyCode(), event ) )
return true;
return super.sendKeyEvent( event );
}
@Override
public boolean commitText( CharSequence text, int newCursorPosition )
{
// nativeCommitText(text.toString(), newCursorPosition);
if( text.toString().equals( "\n" ) )
{
XashActivity.nativeKey( 1, KeyEvent.KEYCODE_ENTER );
XashActivity.nativeKey( 0, KeyEvent.KEYCODE_ENTER );
}
XashActivity.nativeString( text.toString() );
return super.commitText( text, newCursorPosition );
}
@Override
public boolean setComposingText( CharSequence text, int newCursorPosition )
{
// a1batross:
// This method is intended to show composed text immediately
// that after will be replaced by text from "commitText" method
// Just leaving this unimplemented fixes "twice" input on T9/Swype-like keyboards
//ativeSetComposingText(text.toString(), newCursorPosition);
// XashActivity.nativeString( text.toString() );
return super.setComposingText( text, newCursorPosition );
}
public native void nativeSetComposingText( String text, int newCursorPosition );
@Override
public boolean deleteSurroundingText( int beforeLength, int afterLength )
{
// Workaround to capture backspace key. Ref: http://stackoverflow.com/questions/14560344/android-backspace-in-webview-baseinputconnection
// and https://bugzilla.libsdl.org/show_bug.cgi?id=2265
if( beforeLength > 0 && afterLength == 0 )
{
boolean ret = true;
// backspace(s)
while( beforeLength-- > 0 )
{
boolean ret_key = sendKeyEvent( new KeyEvent( KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL ) )
&& sendKeyEvent( new KeyEvent( KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL ) );
ret = ret && ret_key;
}
return ret;
}
return super.deleteSurroundingText(beforeLength, afterLength);
}
}
class EngineTouchListener_v1 implements View.OnTouchListener
{
// Touch events
public boolean onTouch( View v, MotionEvent event )
{
XashActivity.nativeTouch( 0, event.getAction(), event.getX(), event.getY() );
return true;
}
}
class EngineTouchListener_v5 implements View.OnTouchListener
{
float lx = 0, ly = 0;
boolean secondarypressed = false;
// Touch events
public boolean onTouch( View v, MotionEvent event )
{
//final int touchDevId = event.getDeviceId();
final int pointerCount = event.getPointerCount();
int action = event.getActionMasked();
int pointerFingerId, mouseButton, i = -1;
float x, y;
switch( action )
{
case MotionEvent.ACTION_MOVE:
if( ( !XashActivity.fMouseShown ) && ( ( XashActivity.handler.getSource( event ) & InputDevice.SOURCE_MOUSE ) == InputDevice.SOURCE_MOUSE ) )
{
x = event.getX();
y = event.getY();
XashActivity.nativeMouseMove( x - lx, y - ly );
lx = x;
ly = y;
return true;
}
for( i = 0; i < pointerCount; i++ )
{
pointerFingerId = event.getPointerId( i );
x = event.getX( i ) * XashActivity.mTouchScaleX;
y = event.getY( i ) * XashActivity.mTouchScaleY;
XashActivity.nativeTouch( pointerFingerId, 2, x, y );
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_DOWN:
if( !XashActivity.fMouseShown && ( ( XashActivity.handler.getSource( event ) & InputDevice.SOURCE_MOUSE ) == InputDevice.SOURCE_MOUSE ) )
{
lx = event.getX();
ly = event.getY();
boolean down = ( action == MotionEvent.ACTION_DOWN ) || ( action == MotionEvent.ACTION_POINTER_DOWN );
int buttonState = XashActivity.handler.getButtonState( event );
if( down && ( buttonState & MotionEvent.BUTTON_SECONDARY ) != 0 )
{
XashActivity.nativeKey( 1, -243 );
secondarypressed = true;
return true;
}
else if( !down && secondarypressed && ( buttonState & MotionEvent.BUTTON_SECONDARY ) == 0 )
{
secondarypressed = false;
XashActivity.nativeKey( 0, -243 );
return true;
}
XashActivity.nativeKey( down ? 1 : 0, -241 );
return true;
}
i = 0;
// fallthrough
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_POINTER_DOWN:
// Non primary pointer up/down
if( i == -1 )
{
i = event.getActionIndex();
}
pointerFingerId = event.getPointerId( i );
x = event.getX( i ) * XashActivity.mTouchScaleX;
y = event.getY( i ) * XashActivity.mTouchScaleY;
if( action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP )
XashActivity.nativeTouch( pointerFingerId,1, x, y );
if( action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN )
XashActivity.nativeTouch( pointerFingerId,0, x, y );
break;
case MotionEvent.ACTION_CANCEL:
for( i = 0; i < pointerCount; i++ )
{
pointerFingerId = event.getPointerId( i );
x = event.getX( i ) * XashActivity.mTouchScaleX;
y = event.getY( i ) * XashActivity.mTouchScaleY;
XashActivity.nativeTouch( pointerFingerId, 1, x, y );
}
break;
default: break;
}
return true;
}
}
class AndroidBug5497Workaround
{
// For more information, see https://code.google.com/p/android/issues/detail?id=5497
@ -1637,8 +1386,7 @@ class AndroidBug5497Workaround
XashActivity.keyboardVisible = false;
}
if( XashActivity.mImmersiveMode != null )
XashActivity.mImmersiveMode.apply();
FWGSLib.cmp.applyImmersiveMode( XashActivity.keyboardVisible, XashActivity.mDecorView );
mChildOfContent.requestLayout();
usableHeightPrevious = usableHeightNow;
@ -1651,377 +1399,4 @@ class AndroidBug5497Workaround
mChildOfContent.getWindowVisibleDisplayFrame( r );
return r.bottom - r.top;
}
}
class ImmersiveMode
{
void apply()
{
//stub
}
}
class ImmersiveMode_v19 extends ImmersiveMode
{
@Override
void apply()
{
if( !XashActivity.keyboardVisible )
XashActivity.mDecorView.setSystemUiVisibility(
0x00000100 // View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| 0x00000200 // View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| 0x00000400 // View.SYSTEM_UI_FLAG_LAYOUT_FULSCREEN
| 0x00000002 // View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar
| 0x00000004 // View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar
| 0x00001000 // View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
);
else
XashActivity.mDecorView.setSystemUiVisibility( 0 );
}
}
class JoystickHandler
{
public int getSource( KeyEvent event )
{
return InputDevice.SOURCE_UNKNOWN;
}
public int getSource( MotionEvent event )
{
return InputDevice.SOURCE_UNKNOWN;
}
public boolean handleAxis( MotionEvent event )
{
return false;
}
public boolean isGamepadButton( int keyCode )
{
return false;
}
public String keyCodeToString( int keyCode )
{
return String.valueOf( keyCode );
}
public void init()
{
}
public boolean hasVibrator()
{
return true;
}
public void showMouse( boolean show )
{
}
public int getButtonState( MotionEvent event )
{
return 0;
}
}
class Wrap_NVMouseExtensions
{
private static Object inputManager;
private static Method mInputManager_setCursorVisibility;
private static Method mView_setPointerIcon;
private static Class mPointerIcon;
private static Object mEmptyIcon;
public static int nMotionEvent_AXIS_RELATIVE_X = 0;
public static int nMotionEvent_AXIS_RELATIVE_Y = 0;
//**************************************************************************
static
{
try
{
mInputManager_setCursorVisibility =
Class.forName( "android.hardware.input.InputManager" ).getMethod( "setCursorVisibility", boolean.class );
inputManager = XashActivity.mSingleton.getSystemService( "input" );
}
catch( Exception ex )
{
try
{
mPointerIcon=Class.forName("android.view.PointerIcon");
mEmptyIcon = mPointerIcon.getDeclaredMethod("getSystemIcon",android.content.Context.class, int.class).invoke(null,XashActivity.mSingleton.getContext(),0);
mView_setPointerIcon = View.class.getMethod("setPointerIcon",mPointerIcon);
}
catch( Exception ex1 )
{
ex1.printStackTrace();
}
}
/* DO THE SAME FOR RELATIVEY */
}
//*************************************************************************
public static void checkAvailable() throws Exception
{
Field fieldMotionEvent_AXIS_RELATIVE_X = MotionEvent.class.getField( "AXIS_RELATIVE_X" );
nMotionEvent_AXIS_RELATIVE_X = ( Integer )fieldMotionEvent_AXIS_RELATIVE_X.get( null );
Field fieldMotionEvent_AXIS_RELATIVE_Y = MotionEvent.class.getField( "AXIS_RELATIVE_Y" );
nMotionEvent_AXIS_RELATIVE_Y = ( Integer )fieldMotionEvent_AXIS_RELATIVE_Y.get( null );
}
static void setPointerIcon( View view, boolean fVisibility )
{
Log.v("XashInput", "SET CURSOR VISIBILITY " + fVisibility + " obj " + mEmptyIcon.toString() );
try
{
mView_setPointerIcon.invoke(view,fVisibility?null:mEmptyIcon);
}
catch( Exception e)
{
e.printStackTrace();
}
}
static void setGroupPointerIcon( ViewGroup parent, boolean fVisibility )
{
for( int i = parent.getChildCount() - 1; i >= 0; i-- )
{
try
{
final View child = parent.getChildAt(i);
if( child == null )
continue;
if( child instanceof ViewGroup )
{
setGroupPointerIcon((ViewGroup) child, fVisibility);
}
setPointerIcon( child, fVisibility);
}
catch( Exception ex )
{
ex.printStackTrace();
}
}
}
//**************************************************************************
public static void setCursorVisibility( boolean fVisibility )
{
try
{
mInputManager_setCursorVisibility.invoke( inputManager, fVisibility );
}
catch( Exception e )
{
try
{
ViewGroup rootViewGroup = (ViewGroup) XashActivity.mSingleton.getWindow().getDecorView();
setGroupPointerIcon(rootViewGroup, fVisibility);
setGroupPointerIcon((ViewGroup)XashActivity.mDecorView, fVisibility);
for (int i = 0; i < rootViewGroup.getChildCount(); i++) {
View view = rootViewGroup.getChildAt(i);
setPointerIcon(view, fVisibility);
}
}
catch( Exception ex)
{
ex.printStackTrace();
}
}
}
//**************************************************************************
public static int getAxisRelativeX()
{
return nMotionEvent_AXIS_RELATIVE_X;
}
public static int getAxisRelativeY()
{
return nMotionEvent_AXIS_RELATIVE_Y;
}
}
class JoystickHandler_v12 extends JoystickHandler
{
private static float prevSide, prevFwd, prevYaw, prevPtch, prevLT, prevRT, prevHX, prevHY;
public static boolean mNVMouseExtensions = false;
static int mouseId;
static
{
try
{
Wrap_NVMouseExtensions.checkAvailable();
mNVMouseExtensions = true;
}
catch( Throwable t )
{
mNVMouseExtensions = false;
}
}
@Override
public void init()
{
XashActivity.mSurface.setOnGenericMotionListener( new MotionListener() );
Log.d( XashActivity.TAG, "mNVMouseExtensions = " + mNVMouseExtensions );
}
@Override
public int getSource( KeyEvent event )
{
return event.getSource();
}
@Override
public int getSource( MotionEvent event )
{
if( event.getDeviceId() == mouseId )
return InputDevice.SOURCE_MOUSE;
return event.getSource();
}
@Override
public boolean handleAxis( MotionEvent event )
{
// how event can be from null device, Android?
final InputDevice device = event.getDevice();
if( device == null )
return false;
// maybe I need to cache this...
for( InputDevice.MotionRange range: device.getMotionRanges() )
{
// normalize in -1.0..1.0 (copied from SDL2)
final float cur = ( event.getAxisValue( range.getAxis(), event.getActionIndex() ) - range.getMin() ) / range.getRange() * 2.0f - 1.0f;
final float dead = range.getFlat(); // get axis dead zone
switch( range.getAxis() )
{
// typical axes
// move
case MotionEvent.AXIS_X:
prevSide = XashActivity.performEngineAxisEvent( cur, XashActivity.JOY_AXIS_SIDE, prevSide, dead );
break;
case MotionEvent.AXIS_Y:
prevFwd = XashActivity.performEngineAxisEvent( cur, XashActivity.JOY_AXIS_FWD, prevFwd, dead );
break;
// rotate. Invert, so by default this works as it's should
case MotionEvent.AXIS_Z:
prevPtch = XashActivity.performEngineAxisEvent( -cur, XashActivity.JOY_AXIS_PITCH, prevPtch, dead );
break;
case MotionEvent.AXIS_RZ:
prevYaw = XashActivity.performEngineAxisEvent( -cur, XashActivity.JOY_AXIS_YAW, prevYaw, dead );
break;
// trigger
case MotionEvent.AXIS_RTRIGGER:
prevLT = XashActivity.performEngineAxisEvent( cur, XashActivity.JOY_AXIS_RT, prevLT, dead );
break;
case MotionEvent.AXIS_LTRIGGER:
prevRT = XashActivity.performEngineAxisEvent( cur, XashActivity.JOY_AXIS_LT, prevRT, dead );
break;
// hats
case MotionEvent.AXIS_HAT_X:
prevHX = XashActivity.performEngineHatEvent( cur, true, prevHX );
break;
case MotionEvent.AXIS_HAT_Y:
prevHY = XashActivity.performEngineHatEvent( cur, false, prevHY );
break;
}
}
return true;
}
@Override
public boolean isGamepadButton( int keyCode )
{
return KeyEvent.isGamepadButton( keyCode );
}
@Override
public String keyCodeToString( int keyCode )
{
return KeyEvent.keyCodeToString( keyCode );
}
class MotionListener implements View.OnGenericMotionListener
{
@Override
public boolean onGenericMotion( View view, MotionEvent event )
{
final int source = XashActivity.handler.getSource( event );
if( FWGSLib.FExactBitSet( source, InputDevice.SOURCE_GAMEPAD ) ||
FWGSLib.FExactBitSet( source, InputDevice.SOURCE_CLASS_JOYSTICK ) )
return XashActivity.handler.handleAxis( event );
if( mNVMouseExtensions )
{
float x = event.getAxisValue( Wrap_NVMouseExtensions.getAxisRelativeX(), 0 );
float y = event.getAxisValue( Wrap_NVMouseExtensions.getAxisRelativeY(), 0 );
if( !FWGSLib.FExactBitSet( source, InputDevice.SOURCE_MOUSE) && (x != 0 || y != 0 ))
mouseId = event.getDeviceId();
switch( event.getAction() )
{
case MotionEvent.ACTION_SCROLL:
if( event.getAxisValue( MotionEvent.AXIS_VSCROLL ) < 0.0f )
{
XashActivity.nativeKey( 1, -239 );
XashActivity.nativeKey( 0, -239 );
return true;
}
else
{
XashActivity.nativeKey( 1, -240 );
XashActivity.nativeKey( 0, -240 );
}
return true;
}
XashActivity.nativeMouseMove( x, y );
// Log.v("XashInput", "MouseMove: " +x + " " + y );
return true;
}
// TODO: Add it someday
// else if( (event.getSource() & InputDevice.SOURCE_CLASS_TRACKBALL) == InputDevice.SOURCE_CLASS_TRACKBALL )
// return XashActivity.handleBall( event );
//return super.onGenericMotion( view, event );
return false;
}
}
@Override
public boolean hasVibrator()
{
if( XashActivity.mVibrator != null )
return XashActivity.mVibrator.hasVibrator();
return false;
}
@Override
public void showMouse( boolean show )
{
if( mNVMouseExtensions )
Wrap_NVMouseExtensions.setCursorVisibility( show );
}
}
class JoystickHandler_v14 extends JoystickHandler_v12
{
@Override
public int getButtonState( MotionEvent event )
{
return event.getButtonState();
}
}
}

View file

@ -0,0 +1,614 @@
package su.xash.engine;
import java.lang.*;
import java.lang.reflect.*;
import java.util.*;
import android.app.*;
import android.content.*;
import android.view.*;
import android.os.*;
import android.util.*;
import android.graphics.*;
import android.text.method.*;
import android.text.*;
import android.media.*;
import android.hardware.*;
import android.content.*;
import android.widget.*;
import android.content.pm.*;
import android.net.Uri;
import android.provider.*;
import android.view.inputmethod.*;
import su.xash.fwgslib.*;
public class XashInput
{
public static JoystickHandler getJoystickHandler()
{
JoystickHandler handler;
if( FWGSLib.sdk >= 14 )
handler = new JoystickHandler_v14();
else if( FWGSLib.sdk >= 12 )
handler = new JoystickHandler_v12();
else
handler = new JoystickHandler();
handler.init();
return handler;
}
public static class JoystickHandler
{
public int getSource( KeyEvent event )
{
return InputDevice.SOURCE_UNKNOWN;
}
public int getSource( MotionEvent event )
{
return InputDevice.SOURCE_UNKNOWN;
}
public boolean handleAxis( MotionEvent event )
{
return false;
}
public boolean isGamepadButton( int keyCode )
{
return false;
}
public String keyCodeToString( int keyCode )
{
return String.valueOf( keyCode );
}
public void init()
{
}
public boolean hasVibrator()
{
return true;
}
public void showMouse( boolean show )
{
}
public int getButtonState( MotionEvent event )
{
return 0;
}
}
static class JoystickHandler_v12 extends JoystickHandler
{
private static float prevSide, prevFwd, prevYaw, prevPtch, prevLT, prevRT, prevHX, prevHY;
public static boolean mNVMouseExtensions = false;
static int mouseId;
static
{
try
{
Wrap_NVMouseExtensions.checkAvailable();
mNVMouseExtensions = true;
}
catch( Throwable t )
{
mNVMouseExtensions = false;
}
}
@Override
public void init()
{
XashActivity.mSurface.setOnGenericMotionListener( new MotionListener() );
Log.d( XashActivity.TAG, "mNVMouseExtensions = " + mNVMouseExtensions );
}
@Override
public int getSource( KeyEvent event )
{
return event.getSource();
}
@Override
public int getSource( MotionEvent event )
{
if( event.getDeviceId() == mouseId )
return InputDevice.SOURCE_MOUSE;
return event.getSource();
}
@Override
public boolean handleAxis( MotionEvent event )
{
// how event can be from null device, Android?
final InputDevice device = event.getDevice();
if( device == null )
return false;
// maybe I need to cache this...
for( InputDevice.MotionRange range: device.getMotionRanges() )
{
// normalize in -1.0..1.0 (copied from SDL2)
final float cur = ( event.getAxisValue( range.getAxis(), event.getActionIndex() ) - range.getMin() ) / range.getRange() * 2.0f - 1.0f;
final float dead = range.getFlat(); // get axis dead zone
switch( range.getAxis() )
{
// typical axes
// move
case MotionEvent.AXIS_X:
prevSide = XashActivity.performEngineAxisEvent( cur, XashActivity.JOY_AXIS_SIDE, prevSide, dead );
break;
case MotionEvent.AXIS_Y:
prevFwd = XashActivity.performEngineAxisEvent( cur, XashActivity.JOY_AXIS_FWD, prevFwd, dead );
break;
// rotate. Invert, so by default this works as it's should
case MotionEvent.AXIS_Z:
prevPtch = XashActivity.performEngineAxisEvent( -cur, XashActivity.JOY_AXIS_PITCH, prevPtch, dead );
break;
case MotionEvent.AXIS_RZ:
prevYaw = XashActivity.performEngineAxisEvent( -cur, XashActivity.JOY_AXIS_YAW, prevYaw, dead );
break;
// trigger
case MotionEvent.AXIS_RTRIGGER:
prevLT = XashActivity.performEngineAxisEvent( cur, XashActivity.JOY_AXIS_RT, prevLT, dead );
break;
case MotionEvent.AXIS_LTRIGGER:
prevRT = XashActivity.performEngineAxisEvent( cur, XashActivity.JOY_AXIS_LT, prevRT, dead );
break;
// hats
case MotionEvent.AXIS_HAT_X:
prevHX = XashActivity.performEngineHatEvent( cur, true, prevHX );
break;
case MotionEvent.AXIS_HAT_Y:
prevHY = XashActivity.performEngineHatEvent( cur, false, prevHY );
break;
}
}
return true;
}
@Override
public boolean isGamepadButton( int keyCode )
{
return KeyEvent.isGamepadButton( keyCode );
}
@Override
public String keyCodeToString( int keyCode )
{
return KeyEvent.keyCodeToString( keyCode );
}
class MotionListener implements View.OnGenericMotionListener
{
@Override
public boolean onGenericMotion( View view, MotionEvent event )
{
final int source = XashActivity.handler.getSource( event );
if( FWGSLib.FExactBitSet( source, InputDevice.SOURCE_GAMEPAD ) ||
FWGSLib.FExactBitSet( source, InputDevice.SOURCE_CLASS_JOYSTICK ) )
return XashActivity.handler.handleAxis( event );
if( mNVMouseExtensions )
{
float x = event.getAxisValue( Wrap_NVMouseExtensions.getAxisRelativeX(), 0 );
float y = event.getAxisValue( Wrap_NVMouseExtensions.getAxisRelativeY(), 0 );
if( !FWGSLib.FExactBitSet( source, InputDevice.SOURCE_MOUSE) && (x != 0 || y != 0 ))
mouseId = event.getDeviceId();
switch( event.getAction() )
{
case MotionEvent.ACTION_SCROLL:
if( event.getAxisValue( MotionEvent.AXIS_VSCROLL ) < 0.0f )
{
XashActivity.nativeKey( 1, -239 );
XashActivity.nativeKey( 0, -239 );
return true;
}
else
{
XashActivity.nativeKey( 1, -240 );
XashActivity.nativeKey( 0, -240 );
}
return true;
}
XashActivity.nativeMouseMove( x, y );
// Log.v("XashInput", "MouseMove: " +x + " " + y );
return true;
}
// TODO: Add it someday
// else if( (event.getSource() & InputDevice.SOURCE_CLASS_TRACKBALL) == InputDevice.SOURCE_CLASS_TRACKBALL )
// return XashActivity.handleBall( event );
//return super.onGenericMotion( view, event );
return false;
}
}
@Override
public boolean hasVibrator()
{
if( XashActivity.mVibrator != null )
return XashActivity.mVibrator.hasVibrator();
return false;
}
@Override
public void showMouse( boolean show )
{
if( mNVMouseExtensions )
Wrap_NVMouseExtensions.setCursorVisibility( show );
}
}
static class JoystickHandler_v14 extends JoystickHandler_v12
{
@Override
public int getButtonState( MotionEvent event )
{
return event.getButtonState();
}
}
public static View.OnTouchListener getTouchListener()
{
if( FWGSLib.sdk >= 5 )
return new EngineTouchListener_v5();
else
return new EngineTouchListener_v1();
}
}
class Wrap_NVMouseExtensions
{
private static Object inputManager;
private static Method mInputManager_setCursorVisibility;
private static Method mView_setPointerIcon;
private static Class mPointerIcon;
private static Object mEmptyIcon;
public static int nMotionEvent_AXIS_RELATIVE_X = 0;
public static int nMotionEvent_AXIS_RELATIVE_Y = 0;
//**************************************************************************
static
{
try
{
mInputManager_setCursorVisibility =
Class.forName( "android.hardware.input.InputManager" ).getMethod( "setCursorVisibility", boolean.class );
inputManager = XashActivity.mSingleton.getSystemService( "input" );
}
catch( Exception ex )
{
try
{
mPointerIcon=Class.forName("android.view.PointerIcon");
mEmptyIcon = mPointerIcon.getDeclaredMethod("getSystemIcon",android.content.Context.class, int.class).invoke(null,XashActivity.mSingleton.getContext(),0);
mView_setPointerIcon = View.class.getMethod("setPointerIcon",mPointerIcon);
}
catch( Exception ex1 )
{
ex1.printStackTrace();
}
}
/* DO THE SAME FOR RELATIVEY */
}
//*************************************************************************
public static void checkAvailable() throws Exception
{
Field fieldMotionEvent_AXIS_RELATIVE_X = MotionEvent.class.getField( "AXIS_RELATIVE_X" );
nMotionEvent_AXIS_RELATIVE_X = ( Integer )fieldMotionEvent_AXIS_RELATIVE_X.get( null );
Field fieldMotionEvent_AXIS_RELATIVE_Y = MotionEvent.class.getField( "AXIS_RELATIVE_Y" );
nMotionEvent_AXIS_RELATIVE_Y = ( Integer )fieldMotionEvent_AXIS_RELATIVE_Y.get( null );
}
static void setPointerIcon( View view, boolean fVisibility )
{
Log.v("XashInput", "SET CURSOR VISIBILITY " + fVisibility + " obj " + mEmptyIcon.toString() );
try
{
mView_setPointerIcon.invoke(view,fVisibility?null:mEmptyIcon);
}
catch( Exception e)
{
e.printStackTrace();
}
}
static void setGroupPointerIcon( ViewGroup parent, boolean fVisibility )
{
for( int i = parent.getChildCount() - 1; i >= 0; i-- )
{
try
{
final View child = parent.getChildAt(i);
if( child == null )
continue;
if( child instanceof ViewGroup )
{
setGroupPointerIcon((ViewGroup) child, fVisibility);
}
setPointerIcon( child, fVisibility);
}
catch( Exception ex )
{
ex.printStackTrace();
}
}
}
//**************************************************************************
public static void setCursorVisibility( boolean fVisibility )
{
try
{
mInputManager_setCursorVisibility.invoke( inputManager, fVisibility );
}
catch( Exception e )
{
try
{
ViewGroup rootViewGroup = (ViewGroup) XashActivity.mSingleton.getWindow().getDecorView();
setGroupPointerIcon(rootViewGroup, fVisibility);
setGroupPointerIcon((ViewGroup)XashActivity.mDecorView, fVisibility);
for (int i = 0; i < rootViewGroup.getChildCount(); i++) {
View view = rootViewGroup.getChildAt(i);
setPointerIcon(view, fVisibility);
}
}
catch( Exception ex)
{
ex.printStackTrace();
}
}
}
//**************************************************************************
public static int getAxisRelativeX()
{
return nMotionEvent_AXIS_RELATIVE_X;
}
public static int getAxisRelativeY()
{
return nMotionEvent_AXIS_RELATIVE_Y;
}
}
class EngineTouchListener_v1 implements View.OnTouchListener
{
// Touch events
public boolean onTouch( View v, MotionEvent event )
{
XashActivity.nativeTouch( 0, event.getAction(), event.getX(), event.getY() );
return true;
}
}
class EngineTouchListener_v5 implements View.OnTouchListener
{
float lx = 0, ly = 0;
boolean secondarypressed = false;
// Touch events
public boolean onTouch( View v, MotionEvent event )
{
//final int touchDevId = event.getDeviceId();
final int pointerCount = event.getPointerCount();
int action = event.getActionMasked();
int pointerFingerId, mouseButton, i = -1;
float x, y;
switch( action )
{
case MotionEvent.ACTION_MOVE:
if( ( !XashActivity.fMouseShown ) && ( ( XashActivity.handler.getSource( event ) & InputDevice.SOURCE_MOUSE ) == InputDevice.SOURCE_MOUSE ) )
{
x = event.getX();
y = event.getY();
XashActivity.nativeMouseMove( x - lx, y - ly );
lx = x;
ly = y;
return true;
}
for( i = 0; i < pointerCount; i++ )
{
pointerFingerId = event.getPointerId( i );
x = event.getX( i ) * XashActivity.mTouchScaleX;
y = event.getY( i ) * XashActivity.mTouchScaleY;
XashActivity.nativeTouch( pointerFingerId, 2, x, y );
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_DOWN:
if( !XashActivity.fMouseShown && ( ( XashActivity.handler.getSource( event ) & InputDevice.SOURCE_MOUSE ) == InputDevice.SOURCE_MOUSE ) )
{
lx = event.getX();
ly = event.getY();
boolean down = ( action == MotionEvent.ACTION_DOWN ) || ( action == MotionEvent.ACTION_POINTER_DOWN );
int buttonState = XashActivity.handler.getButtonState( event );
if( down && ( buttonState & MotionEvent.BUTTON_SECONDARY ) != 0 )
{
XashActivity.nativeKey( 1, -243 );
secondarypressed = true;
return true;
}
else if( !down && secondarypressed && ( buttonState & MotionEvent.BUTTON_SECONDARY ) == 0 )
{
secondarypressed = false;
XashActivity.nativeKey( 0, -243 );
return true;
}
XashActivity.nativeKey( down ? 1 : 0, -241 );
return true;
}
i = 0;
// fallthrough
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_POINTER_DOWN:
// Non primary pointer up/down
if( i == -1 )
{
i = event.getActionIndex();
}
pointerFingerId = event.getPointerId( i );
x = event.getX( i ) * XashActivity.mTouchScaleX;
y = event.getY( i ) * XashActivity.mTouchScaleY;
if( action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP )
XashActivity.nativeTouch( pointerFingerId,1, x, y );
if( action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN )
XashActivity.nativeTouch( pointerFingerId,0, x, y );
break;
case MotionEvent.ACTION_CANCEL:
for( i = 0; i < pointerCount; i++ )
{
pointerFingerId = event.getPointerId( i );
x = event.getX( i ) * XashActivity.mTouchScaleX;
y = event.getY( i ) * XashActivity.mTouchScaleY;
XashActivity.nativeTouch( pointerFingerId, 1, x, y );
}
break;
default: break;
}
return true;
}
}
/* This is a fake invisible editor view that receives the input and defines the
* pan&scan region
*/
class DummyEdit extends View implements View.OnKeyListener
{
InputConnection ic;
public DummyEdit( Context context )
{
super( context );
setFocusableInTouchMode( true );
setFocusable( true );
setOnKeyListener( this );
}
@Override
public boolean onCheckIsTextEditor()
{
return true;
}
@Override
public boolean onKey( View v, int keyCode, KeyEvent event )
{
return XashActivity.handleKey( keyCode, event );
}
@Override
public InputConnection onCreateInputConnection( EditorInfo outAttrs )
{
ic = new XashInputConnection( this, true );
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
| 33554432 /* API 11: EditorInfo.IME_FLAG_NO_FULLSCREEN */;
return ic;
}
}
class XashInputConnection extends BaseInputConnection
{
public XashInputConnection( View targetView, boolean fullEditor )
{
super( targetView, fullEditor );
}
@Override
public boolean sendKeyEvent( KeyEvent event )
{
if( XashActivity.handleKey( event.getKeyCode(), event ) )
return true;
return super.sendKeyEvent( event );
}
@Override
public boolean commitText( CharSequence text, int newCursorPosition )
{
// nativeCommitText(text.toString(), newCursorPosition);
if( text.toString().equals( "\n" ) )
{
XashActivity.nativeKey( 1, KeyEvent.KEYCODE_ENTER );
XashActivity.nativeKey( 0, KeyEvent.KEYCODE_ENTER );
}
XashActivity.nativeString( text.toString() );
return super.commitText( text, newCursorPosition );
}
@Override
public boolean setComposingText( CharSequence text, int newCursorPosition )
{
// a1batross:
// This method is intended to show composed text immediately
// that after will be replaced by text from "commitText" method
// Just leaving this unimplemented fixes "twice" input on T9/Swype-like keyboards
//ativeSetComposingText(text.toString(), newCursorPosition);
// XashActivity.nativeString( text.toString() );
return super.setComposingText( text, newCursorPosition );
}
public native void nativeSetComposingText( String text, int newCursorPosition );
@Override
public boolean deleteSurroundingText( int beforeLength, int afterLength )
{
// Workaround to capture backspace key. Ref: http://stackoverflow.com/questions/14560344/android-backspace-in-webview-baseinputconnection
// and https://bugzilla.libsdl.org/show_bug.cgi?id=2265
if( beforeLength > 0 && afterLength == 0 )
{
boolean ret = true;
// backspace(s)
while( beforeLength-- > 0 )
{
boolean ret_key = sendKeyEvent( new KeyEvent( KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL ) )
&& sendKeyEvent( new KeyEvent( KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL ) );
ret = ret && ret_key;
}
return ret;
}
return super.deleteSurroundingText(beforeLength, afterLength);
}
}

View file

@ -32,7 +32,6 @@ import java.security.MessageDigest;
import su.xash.engine.R;
import su.xash.engine.XashConfig;
import su.xash.engine.JoystickHandler;
public class XashService extends Service
@ -64,9 +63,9 @@ public class XashService extends Service
{
Log.d("XashService", "Service Started");
not = XashNotification.getXashNotification(this);
not = XashNotification.getXashNotification();
startForeground(not.getId(), not.createNotification());
startForeground(not.getId(), not.createNotification(this));
return START_NOT_STICKY;
}
@ -98,20 +97,16 @@ public class XashService extends Service
}
stopSelf();
}
public static class XashNotification
{
public Notification notification;
public final int notificationId = 100;
public Context ctx;
public XashNotification(Context ctx)
{
this.ctx = ctx;
}
protected Context ctx;
public Notification createNotification()
public Notification createNotification(Context context)
{
ctx = context;
Intent engineIntent = new Intent(ctx, XashActivity.class);
engineIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
@ -133,14 +128,14 @@ public class XashService extends Service
public void setIcon(Bitmap bmp)
{
notification.contentView.setImageViewBitmap( R.id.status_image, bmp );
NotificationManager nm = ctx.getSystemService(NotificationManager.class);
NotificationManager nm = (NotificationManager)ctx.getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify( notificationId, notification );
}
public void setText(String title)
{
notification.contentView.setTextViewText( R.id.status_text, title );
NotificationManager nm = ctx.getSystemService(NotificationManager.class);
NotificationManager nm = (NotificationManager)ctx.getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify( notificationId, notification );
}
@ -149,28 +144,26 @@ public class XashService extends Service
return notificationId;
}
public static XashNotification getXashNotification(Context ctx)
public static XashNotification getXashNotification()
{
if( XashActivity.sdk >= Build.VERSION_CODES.O )
return new XashNotification_O(ctx);
else if( XashActivity.sdk >= 21 )
return new XashNotification_v21(ctx);
return new XashNotification(ctx);
if( XashActivity.sdk >= 26 )
return new XashNotification_v26();
// else if( XashActivity.sdk >= 21 )
// return new XashNotification_v21();
else
return new XashNotification();
}
}
private static class XashNotification_v21 extends XashNotification
{
protected Notification.Builder builder;
public XashNotification_v21(Context ctx)
{
super(ctx);
}
@Override
public Notification createNotification()
public Notification createNotification(Context context)
{
ctx = context;
Intent engineIntent = new Intent(ctx, XashActivity.class);
engineIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
@ -196,7 +189,7 @@ public class XashService extends Service
public void setIcon(Bitmap bmp)
{
notification = builder.setLargeIcon(bmp).build();
NotificationManager nm = ctx.getSystemService(NotificationManager.class);
NotificationManager nm = (NotificationManager)ctx.getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify( notificationId, notification );
}
@ -204,25 +197,21 @@ public class XashService extends Service
public void setText(String str)
{
notification = builder.setContentText(str).build();
NotificationManager nm = ctx.getSystemService(NotificationManager.class);
NotificationManager nm = (NotificationManager)ctx.getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify( notificationId, notification );
}
}
private static class XashNotification_O extends XashNotification_v21
private static class XashNotification_v26 extends XashNotification_v21
{
private static final String CHANNEL_ID = "XashServiceChannel";
public XashNotification_O(Context ctx)
{
super(ctx);
}
private void createNotificationChannel()
{
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
if (XashActivity.sdk >= Build.VERSION_CODES.O)
if (XashActivity.sdk >= 26)
{
final NotificationManager nm = ctx.getSystemService(NotificationManager.class);
@ -244,14 +233,14 @@ public class XashService extends Service
}
@Override
public Notification createNotification()
public Notification createNotification(Context ctx)
{
createNotificationChannel();
builder = new Notification.Builder(ctx);
builder.setChannelId(CHANNEL_ID);
return super.createNotification();
return super.createNotification(ctx);
}
}
};

View file

@ -229,9 +229,58 @@ public class FWGSLib
}
}
public static void applyPermissions( final Activity act, final String permissions[], final int code )
public static class Compat
{
if( sdk >= 23 )
public void applyPermissions( final Activity act, final String permissions[], final int code ) {}
public void applyImmersiveMode( boolean keyboardVisible, View decorView ) {}
public void startForegroundService( Context ctx, Intent intent ) {}
public String getNativeLibDir(Context ctx)
{
return ctx.getFilesDir().getParentFile().getPath() + "/lib";
}
}
static class Compat_9 extends Compat
{
public String getNativeLibDir(Context ctx)
{
try {
ApplicationInfo ai = getApplicationInfo(ctx, null, 0);
return ai.nativeLibraryDir;
}
catch(Exception e) {
return super.getNativeLibDir(ctx);
}
}
public void startForegroundService( Context ctx, Intent intent ) {
ctx.startService( intent );
}
}
static class Compat_19 extends Compat_9
{
public void applyImmersiveMode(boolean keyboardVisible, View decorView)
{
//if( !XashActivity.mPref.getBoolean( "immersive_mode", false ) )
// return;
if( keyboardVisible )
decorView.setSystemUiVisibility(
0x00000100 // View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| 0x00000200 // View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| 0x00000400 // View.SYSTEM_UI_FLAG_LAYOUT_FULSCREEN
| 0x00000002 // View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar
| 0x00000004 // View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar
| 0x00001000 // View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
);
else
decorView.setSystemUiVisibility( 0 );
}
}
static class Compat_23 extends Compat_19
{
public void applyPermissions( final Activity act, final String permissions[], final int code )
{
List<String> requestPermissions = new ArrayList<String>();
@ -256,6 +305,25 @@ public class FWGSLib
}
}
static class Compat_26 extends Compat_23
{
public void startForegroundService(Context ctx, Intent intent){
ctx.startForegroundService(intent);
}
}
public static Compat cmp;
static {
int sdk1 = Integer.valueOf(Build.VERSION.SDK);
if( sdk1 >= 26 )
cmp = new Compat_26();
if( sdk1 >= 23 )
cmp = new Compat_23();
else if( sdk1 >= 9 )
cmp = new Compat_9();
else cmp = new Compat();
}
public static ApplicationInfo getApplicationInfo(Context ctx, String pkgName, int flags) throws PackageManager.NameNotFoundException
{
PackageManager pm = ctx.getPackageManager();
@ -268,3 +336,4 @@ public class FWGSLib
public static final int sdk = Integer.valueOf(Build.VERSION.SDK);
}