diff --git a/app/build.gradle b/app/build.gradle index c87a7f9..49feef8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -23,8 +23,8 @@ android { applicationId "git.artdeell.skymodloader" minSdk 26 targetSdk 34 - versionCode 57 - versionName "1.7.1a" + versionCode 58 + versionName "1.7.2" externalNativeBuild { cmake { @@ -72,6 +72,7 @@ dependencies { implementation 'net.fornwall:jelf:0.9.0' implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.1.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation 'com.github.bumptech.glide:glide:4.16.0' implementation 'com.google.zxing:core:3.5.3' implementation 'androidx.recyclerview:recyclerview:1.3.2' implementation "io.noties.markwon:core:4.6.2" diff --git a/app/src/main/cpp/main.cpp b/app/src/main/cpp/main.cpp index 6c00ea0..9ec482d 100644 --- a/app/src/main/cpp/main.cpp +++ b/app/src/main/cpp/main.cpp @@ -374,4 +374,4 @@ Java_git_artdeell_skymodloader_MainActivity_nativeSetSkyBuildKey( env->ReleaseStringUTFChars(key, keyStr); LOGI("SkyBuildKey set: %s", g_skyBuildKey.c_str()); } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/tgc/sky/GameActivity.java b/app/src/main/java/com/tgc/sky/GameActivity.java index 799d75c..231df74 100644 --- a/app/src/main/java/com/tgc/sky/GameActivity.java +++ b/app/src/main/java/com/tgc/sky/GameActivity.java @@ -9,6 +9,7 @@ import android.content.pm.PackageManager; import android.graphics.Insets; import android.graphics.PixelFormat; +import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; import android.hardware.display.DisplayManager; @@ -25,6 +26,7 @@ import android.util.Log; import android.view.Gravity; import android.view.InputDevice; +import android.view.InputEvent; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.SurfaceView; @@ -61,13 +63,12 @@ import git.artdeell.skymodloader.R; import git.artdeell.skymodloader.SMLApplication; import git.artdeell.skymodloader.ImGUITextInput; -import git.artdeell.skymodloader.LoadVideoView; import kotlin.KotlinVersion; -public class GameActivity extends TGCNativeActivity { +public class GameActivity extends TGCNativeActivity implements View.OnCapturedPointerListener { + static final boolean ENABLE_DISPLAY_CUTOUT_MODE = true; private static final String TAG = "GameActivity"; - /* access modifiers changed from: private */ public ArrayList mGameControllerIds; private ArrayList mOnActivityIntentListeners; private ArrayList mOnActivityResultListeners; @@ -82,11 +83,8 @@ public class GameActivity extends TGCNativeActivity { private boolean imguiKeybaordShowing; private ImGUITextInput imguiInput; private boolean m_logoSoundReleased = false; - /* access modifiers changed from: private */ public Rect mSafeAreaInsets = new Rect(); - private Handler m_keyboardHandler = null; private int m_keyboardHeight = 0; - private boolean m_editTextFocused = false; private boolean m_isKeyboardShowing = false; private RelativeLayout m_relativeLayout; @@ -95,6 +93,7 @@ public class GameActivity extends TGCNativeActivity { private boolean m_rTriggerPressed = false; private boolean m_motionEventsDisabled = false; private int m_lastDpadDirection = 23; + private PointF m_lastMouseLocation = new PointF(); SystemIO_android m_systemIO = null; SystemUI_android m_systemUI = null; public boolean portraitOnResume = false; @@ -132,84 +131,47 @@ public static float AppleConvertAndroidScale(float f) { } private native void onCreateNative(); - - /* access modifiers changed from: private */ public native void onSafeAreaInsetsChanged(float[] fArr); - private static native boolean onTouchNative(int i, int i2, float f, float f2, double d); - public native String ResolveTemplateArgsNative(String str); - - public int getAppBuildVersion() { - return com.tgc.sky.BuildConfig.VERSION_CODE; - } - - public String getAppVersion() { - return com.tgc.sky.BuildConfig.SKY_VERSION; - } - - public String getPlatformName() { - return "android"; - } - + public native void onMouseMovedNative(int x, int y); + public native void onMouseDeltaNative(int dx, int dy); + public native void onMouseScrollingDeltaNative(float x, float y); + public native boolean onButtonPressNative(int keyCode, int inputSource, boolean pressed); + public native void setGamepadProductTypeNative(int vendorId, int productId); public native void onAudioDeviceTypeChangedNative(int i); - public native void onBackPressedNative(); - - public native boolean onButtonPressNative(int i, boolean z, boolean z2, double d); - public native void onCommerceUpdateNative(boolean z, boolean z2, boolean z3); - public native void onDpadEventNative(float f, float f2, double d); - public native void onGamepadConnectedNative(); - public native void onGamepadDisconnectedNative(); - public native void onInternetReachabilityNative(boolean z, boolean z2); - public native void onKeyboardCompleteNative(String str, boolean z, boolean z2); - public native void onNFCTagScannedNative(String str, int i, String str2, String str3); - public native void onOpenedWithURLNative(String str, boolean z); - public native void onStickEventNative(float f, float f2, float f3, float f4); - public native void onSystemScreenshotTakenNative(); - public native void onVolumeChangeNative(float f, float f2); + public native void onDisplayChangedNative(); - public float transformHeightToProgram(float f) { - return f; - } - - public float transformHeightToSystem(float f) { - return f; - } - - public float transformWidthToProgram(float f) { - return f; - } - - public float transformWidthToSystem(float f) { - return f; - } + public int getAppBuildVersion() { return com.tgc.sky.BuildConfig.VERSION_CODE; } + public String getAppVersion() { return com.tgc.sky.BuildConfig.SKY_VERSION; } + public String getPlatformName() { return "android"; } + public float transformHeightToProgram(float f) { return f; } + public float transformHeightToSystem(float f) { return f; } + public float transformWidthToProgram(float f) { return f; } + public float transformWidthToSystem(float f) { return f; } + @Override public void attachBaseContext(Context context) { super.attachBaseContext(context); } - public native void onDisplayChangedNative(); - - private boolean isTextRenderingBrokenForDevice() { if (Build.VERSION.SDK_INT == 31 || Build.VERSION.SDK_INT == 32) { String[] strArr = {"OPD2102", "X21N2", "PFUM10", "TB128FU", "RMX3478", "RMX3471", "RMX3472", "2201116SC", "22101317C"}; for (int i = 0; i < 9; i++) { - if (Build.MODEL.compareToIgnoreCase(strArr[i]) == 0) { - return true; - } + if (Build.MODEL.compareToIgnoreCase(strArr[i]) == 0) return true; } return false; } @@ -233,19 +195,19 @@ private void fixTextRenderingOnProblemDevices_HACK() { } } - /* access modifiers changed from: protected */ + @Override public void onCreate(Bundle bundle) { super.onCreate(bundle); DialogJNI.setActivity(this); hideNavigationFullScreen(getWindow().getDecorView()); getWindow().addFlags(2097280); - //getWindow().setSoftInputMode(48); tryEnablingDisplayCutoutMode(); setContentView(R.layout.tgc_logo); this.m_relativeLayout = findViewById(R.id.sml_relLayout); - ((SurfaceView)findViewById(R.id.surfaceView)).getHolder().addCallback(this); + this.m_relativeLayout.setOnCapturedPointerListener(this); + ((SurfaceView) findViewById(R.id.surfaceView)).getHolder().addCallback(this); FileSelector.setActivity(this); - if(imgui == null) imgui = new ImGUI(); + if (imgui == null) imgui = new ImGUI(); SurfaceView imguiView = findViewById(R.id.imguiView); imguiView.getHolder().setFormat(PixelFormat.TRANSLUCENT); imguiView.getHolder().addCallback(imgui); @@ -264,9 +226,7 @@ public void onCreate(Bundle bundle) { initGameController(); logoView = findViewById(R.id.imageView); Intent intent = getIntent(); - if (intent != null) { - HandleNewIntent(intent); - } + if (intent != null) HandleNewIntent(intent); getWindow().getDecorView().setOnApplyWindowInsetsListener((view, windowInsets) -> { try { int max = Integer.max(windowInsets.getStableInsetTop(), windowInsets.getStableInsetBottom()); @@ -274,52 +234,47 @@ public void onCreate(Bundle bundle) { try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { if (windowInsets.getDisplayCutout() != null) { - max = Integer.max(max, Integer.max(windowInsets.getDisplayCutout().getSafeInsetLeft(), windowInsets.getDisplayCutout().getSafeInsetRight())); + max = Integer.max(max, Integer.max( + windowInsets.getDisplayCutout().getSafeInsetLeft(), + windowInsets.getDisplayCutout().getSafeInsetRight())); } } - } catch (NoSuchMethodError ignored) { - } + } catch (NoSuchMethodError ignored) {} } GameActivity.this.mSafeAreaInsets.left = max; GameActivity.this.mSafeAreaInsets.top = 0; GameActivity.this.mSafeAreaInsets.right = max; GameActivity.this.mSafeAreaInsets.bottom = 0; - float transformWidthToProgram = GameActivity.this.transformWidthToProgram(max); - GameActivity.this.onSafeAreaInsetsChanged(new float[]{transformWidthToProgram, 0.0f, transformWidthToProgram, 0.0f}); + float t = GameActivity.this.transformWidthToProgram(max); + GameActivity.this.onSafeAreaInsetsChanged(new float[]{t, 0.0f, t, 0.0f}); return view.onApplyWindowInsets(windowInsets); - } catch (Exception | NoSuchMethodError unused2) { + } catch (Exception | NoSuchMethodError unused) { return windowInsets; } }); - if (Build.VERSION.SDK_INT >= 30) { - setupDisplayListener(); - } + if (Build.VERSION.SDK_INT >= 30) setupDisplayListener(); + } + @Override + public boolean onCapturedPointer(View view, MotionEvent event) { + float dx = event.getX(); + float dy = event.getY(); + onMouseDeltaNative((int) dx, (int) -(dy)); + return true; } private void setupDisplayListener() { final DisplayManager displayManager = (DisplayManager) getSystemService(Context.DISPLAY_SERVICE); - displayManager.registerDisplayListener(new DisplayManager.DisplayListener() { // from class: com.tgc.sky.GameActivity.1 - @Override // android.hardware.display.DisplayManager.DisplayListener - public void onDisplayAdded(int i) { - } - - @Override // android.hardware.display.DisplayManager.DisplayListener - public void onDisplayRemoved(int i) { - } - - @Override // android.hardware.display.DisplayManager.DisplayListener - public void onDisplayChanged(int i) { - if (displayManager.getDisplay(i) != null) { - GameActivity.this.onDisplayChangedNative(); - } + displayManager.registerDisplayListener(new DisplayManager.DisplayListener() { + @Override public void onDisplayAdded(int i) {} + @Override public void onDisplayRemoved(int i) {} + @Override public void onDisplayChanged(int i) { + if (displayManager.getDisplay(i) != null) GameActivity.this.onDisplayChangedNative(); } }, null); } - public String getDeviceBrand() { - return Build.BRAND; - } + public String getDeviceBrand() { return Build.BRAND; } @Override public void onDestroy() { @@ -329,28 +284,23 @@ public void onDestroy() { super.onDestroy(); } - @Override // com.tgc.sky.TGCNativeActivity, android.app.Activity + @Override public void onResume() { - if (this.portraitOnResume) { - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE); - } + if (this.portraitOnResume) setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE); this.m_systemIO.onResume(); this.m_systemAccounts.onResume(); super.onResume(); if (this.portraitOnResume) { new Handler(Looper.getMainLooper()).postDelayed(() -> { - if (GameActivity.this.portraitOnResume) { + if (GameActivity.this.portraitOnResume) GameActivity.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); - } }, 1000L); } } @Override public void onPause() { - if (this.portraitOnResume) { - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE); - } + if (this.portraitOnResume) setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE); this.m_systemIO.onPause(); super.onPause(); } @@ -359,60 +309,40 @@ public void onPause() { public void onWindowFocusChanged(boolean z) { RelativeLayout relativeLayout; super.onWindowFocusChanged(z); - if (z && (relativeLayout = this.m_relativeLayout) != null) { - hideNavigationFullScreen(relativeLayout); - } + if (z && (relativeLayout = this.m_relativeLayout) != null) hideNavigationFullScreen(relativeLayout); } @Override - public void onBackPressed() { - onBackPressedNative(); - } - + public void onBackPressed() { onBackPressedNative(); } - public void AddOnActivityIntentListener(OnActivityIntentListener onActivityIntentListener) { - if (this.mOnActivityIntentListeners == null) { - this.mOnActivityIntentListeners = new ArrayList<>(); - } - if (!this.mOnActivityIntentListeners.contains(onActivityIntentListener)) { - this.mOnActivityIntentListeners.add(onActivityIntentListener); - } + public void AddOnActivityIntentListener(OnActivityIntentListener l) { + if (this.mOnActivityIntentListeners == null) this.mOnActivityIntentListeners = new ArrayList<>(); + if (!this.mOnActivityIntentListeners.contains(l)) this.mOnActivityIntentListeners.add(l); } - public void RemoveOnActivityIntentListeners(OnActivityIntentListener onActivityIntentListener) { - ArrayList arrayList = this.mOnActivityIntentListeners; - if (arrayList != null) { - arrayList.remove(onActivityIntentListener); - } + public void RemoveOnActivityIntentListeners(OnActivityIntentListener l) { + ArrayList a = this.mOnActivityIntentListeners; + if (a != null) a.remove(l); } + @Override public void onNewIntent(Intent intent) { - ArrayList arrayList = this.mOnActivityIntentListeners; - if (arrayList != null) { - Iterator it = arrayList.iterator(); - while (it.hasNext()) { - if (it.next().onNewIntent(intent)) { - return; - } - } + ArrayList a = this.mOnActivityIntentListeners; + if (a != null) { + Iterator it = a.iterator(); + while (it.hasNext()) { if (it.next().onNewIntent(intent)) return; } } HandleNewIntent(intent); } - /* access modifiers changed from: package-private */ public void HandleNewIntent(Intent intent) { Bundle extras = intent.getExtras(); if (extras != null) { - JSONObject jSONObject = new JSONObject(); + JSONObject json = new JSONObject(); for (String str : extras.keySet()) { - try { - //if (!str.startsWith(Constants.REFERRER_API_GOOGLE) && !str.equalsIgnoreCase(Constants.MessagePayloadKeys.FROM) && !str.equalsIgnoreCase(Constants.MessagePayloadKeys.COLLAPSE_KEY)) { - jSONObject.put(str, JSONObject.wrap(extras.get(str))); - //} - } catch (JSONException ignored) { - } + try { json.put(str, JSONObject.wrap(extras.get(str))); } catch (JSONException ignored) {} } - SystemIO_android.getInstance().OnAppLaunchNotificationMessage(jSONObject.toString()); + SystemIO_android.getInstance().OnAppLaunchNotificationMessage(json.toString()); } String action = intent.getAction(); Uri data = intent.getData(); @@ -423,69 +353,45 @@ public void HandleNewIntent(Intent intent) { } } - public void AddOnActivityResultListener(OnActivityResultListener onActivityResultListener) { - if (this.mOnActivityResultListeners == null) { - this.mOnActivityResultListeners = new ArrayList<>(); - } - if (!this.mOnActivityResultListeners.contains(onActivityResultListener)) { - this.mOnActivityResultListeners.add(onActivityResultListener); - } + public void AddOnActivityResultListener(OnActivityResultListener l) { + if (this.mOnActivityResultListeners == null) this.mOnActivityResultListeners = new ArrayList<>(); + if (!this.mOnActivityResultListeners.contains(l)) this.mOnActivityResultListeners.add(l); } - public void RemoveOnActivityResultListeners(OnActivityResultListener onActivityResultListener) { - ArrayList arrayList = this.mOnActivityResultListeners; - if (arrayList != null) { - arrayList.remove(onActivityResultListener); - } + public void RemoveOnActivityResultListeners(OnActivityResultListener l) { + ArrayList a = this.mOnActivityResultListeners; + if (a != null) a.remove(l); } - /* access modifiers changed from: protected */ @Override protected void onActivityResult(int i, int i2, Intent intent) { - Log.i("Interlock","GameActivity onActivityResult"); + Log.i("Interlock", "GameActivity onActivityResult"); super.onActivityResult(i, i2, intent); - ArrayList arrayList = this.mOnActivityResultListeners; - if (arrayList != null) { - for (OnActivityResultListener onActivityResultListener : arrayList) { - onActivityResultListener.onActivityResult(i, i2, intent); - } - } + ArrayList a = this.mOnActivityResultListeners; + if (a != null) { for (OnActivityResultListener l : a) l.onActivityResult(i, i2, intent); } } public boolean checkSelfPermissions(String[] strArr) { boolean z = true; - for (String str : strArr) { - z &= checkSelfPermission(str) == PackageManager.PERMISSION_GRANTED; - } + for (String str : strArr) z &= checkSelfPermission(str) == PackageManager.PERMISSION_GRANTED; return z; } public boolean checkResultPermissions(int[] iArr) { boolean z = iArr.length > 0; - for (int i : iArr) { - z &= i == PackageManager.PERMISSION_GRANTED; - } + for (int i : iArr) z &= i == PackageManager.PERMISSION_GRANTED; return z; } public int[] getSelfPermissions(String[] strArr) { int[] iArr = new int[strArr.length]; - int length = strArr.length; - int i = 0; - int i2 = 0; - while (i < length) { - iArr[i2] = checkSelfPermission(strArr[i]); - i++; - i2++; - } + for (int i = 0; i < strArr.length; i++) iArr[i] = checkSelfPermission(strArr[i]); return iArr; } public boolean shouldShowRequestPermissionsRationale(String[] strArr) { boolean z = false; - for (String str : strArr) { - z |= shouldShowRequestPermissionRationale(str); - } + for (String str : strArr) z |= shouldShowRequestPermissionRationale(str); return z; } @@ -502,294 +408,396 @@ public void requestPermissions(String[] strArr, PermissionCallback permissionCal @Override public void onRequestPermissionsResult(int i, String[] strArr, int[] iArr) { - PermissionCallback permissionCallback; + PermissionCallback cb; super.onRequestPermissionsResult(i, strArr, iArr); - if (i != 100 || (permissionCallback = this.mPermissionCallback) == null) { - return; - } - permissionCallback.onPermissionResult(strArr, iArr); + if (i != 100 || (cb = this.mPermissionCallback) == null) return; + cb.onPermissionResult(strArr, iArr); this.mPermissionCallback = null; } - public void requestPermissionsThroughSettings(final String[] strArr, final PermissionCallback permissionCallback) { - runOnUiThread(new Runnable() { - @Override - public void run() { - GameActivity.this.AddOnActivityResultListener(new OnActivityResultListener() { // from class: com.tgc.sky.GameActivity.3.1 - @Override - public void onActivityResult(int i, int i2, Intent intent) { - if (i == 100) { - permissionCallback.onPermissionResult(strArr, GameActivity.this.getSelfPermissions(strArr)); - GameActivity.this.RemoveOnActivityResultListeners(this); - } + runOnUiThread(() -> { + GameActivity.this.AddOnActivityResultListener(new OnActivityResultListener() { + @Override + public void onActivityResult(int i, int i2, Intent intent) { + if (i == 100) { + permissionCallback.onPermissionResult(strArr, GameActivity.this.getSelfPermissions(strArr)); + GameActivity.this.RemoveOnActivityResultListeners(this); } - }); - GameActivity.this.startActivityForResult(new Intent("android.settings.APPLICATION_DETAILS_SETTINGS", Uri.fromParts("package", BuildConfig.APPLICATION_ID, null)), 100); - } + } + }); + GameActivity.this.startActivityForResult(new Intent( + "android.settings.APPLICATION_DETAILS_SETTINGS", + Uri.fromParts("package", BuildConfig.APPLICATION_ID, null)), 100); }); } + @Override public boolean onTouchEvent(MotionEvent motionEvent) { int actionMasked = motionEvent.getActionMasked(); - if (actionMasked == MotionEvent.ACTION_UP || actionMasked == MotionEvent.ACTION_DOWN || actionMasked == MotionEvent.ACTION_MOVE) { + if (actionMasked == MotionEvent.ACTION_UP || actionMasked == MotionEvent.ACTION_DOWN || actionMasked == MotionEvent.ACTION_MOVE) { ImGUI.submitPositionEvent(motionEvent.getX(), motionEvent.getY()); - if (actionMasked == MotionEvent.ACTION_DOWN) { - ImGUI.submitButtonEvent(0, true); - } - if (actionMasked == MotionEvent.ACTION_UP) { - ImGUI.submitButtonEvent(0, false); - } - - boolean wantsKeyboard = ImGUI.wantsKeyboard(); - if (wantsKeyboard && !imguiKeybaordShowing) { - imguiInput.setKeyboardState(true); - imguiKeybaordShowing = wantsKeyboard; - } - if (!wantsKeyboard && imguiKeybaordShowing) { - imguiInput.setKeyboardState(false); - imguiKeybaordShowing = wantsKeyboard; - } - if (ImGUI.wantsMouse()) { - return true; - } + if (actionMasked == MotionEvent.ACTION_DOWN) ImGUI.submitButtonEvent(0, true); + if (actionMasked == MotionEvent.ACTION_UP) ImGUI.submitButtonEvent(0, false); } + boolean wantsKeyboard = ImGUI.wantsKeyboard(); + if (wantsKeyboard && !imguiKeybaordShowing) { imguiInput.setKeyboardState(true); imguiKeybaordShowing = true; } + if (!wantsKeyboard && imguiKeybaordShowing) { imguiInput.setKeyboardState(false); imguiKeybaordShowing = false; } + if (ImGUI.wantsMouse()) return true; if (actionMasked == MotionEvent.ACTION_MOVE || actionMasked == MotionEvent.ACTION_CANCEL) { for (int i = 0; i < motionEvent.getPointerCount(); i++) { - onTouchNative(motionEvent.getPointerId(i) + 1, actionMasked, motionEvent.getX(i), motionEvent.getY(i), (double) motionEvent.getEventTime()); + onTouchNative(motionEvent.getPointerId(i) + 1, actionMasked, + motionEvent.getX(i), motionEvent.getY(i), (double) motionEvent.getEventTime()); } return true; } int actionIndex = motionEvent.getActionIndex(); - return onTouchNative(motionEvent.getPointerId(actionIndex) + 1, actionMasked, motionEvent.getX(actionIndex), motionEvent.getY(actionIndex), (double) motionEvent.getEventTime()); + return onTouchNative(motionEvent.getPointerId(actionIndex) + 1, actionMasked, + motionEvent.getX(actionIndex), motionEvent.getY(actionIndex), (double) motionEvent.getEventTime()); + } + + private PointF transformPointToProgram(float x, float y) { + PointF p = new PointF(); + p.x = transformWidthToProgram(x); + p.y = transformHeightToProgram(y); + return p; + } + + private void onMouseMoved(int x, int y) { + if (x != 0 || y != 0) setPointerCapture(false); + onMouseMovedNative(x, y); + } + + private void onMouseScrollingDelta(float x, float y) { + if (x != 0.0f || y != 0.0f) setPointerCapture(false); + onMouseScrollingDeltaNative(x, y); + } + + private boolean isEventInTextField(MotionEvent motionEvent) { + if (!m_systemUI.IsTextFieldShowing()) return false; + Rect rect = m_systemUI.GetTextFieldHitRect(); + return rect.contains((int) motionEvent.getX(), (int) motionEvent.getY()); + } + + @Override + public boolean dispatchTouchEvent(MotionEvent motionEvent) { + if (isHardwareMouseEvent(motionEvent)) { + if (isGamepadWithTouchpadEvent(motionEvent)) return true; + PointF point = transformPointToProgram(motionEvent.getX(), motionEvent.getY()); + if (motionEvent.getButtonState() == 1) { + float dx = (point.x - m_lastMouseLocation.x) * 3.0f; + float dy = -(point.y - m_lastMouseLocation.y) * 3.0f; + onMouseDeltaNative((int) dx, (int) dy); + } + onMouseMoved((int) point.x, (int) point.y); + m_lastMouseLocation = point; + if (isEventInTextField(motionEvent)) return super.dispatchTouchEvent(motionEvent); + return true; + } + return super.dispatchTouchEvent(motionEvent); } public void notifyEditTextFocus(boolean z) { this.m_editTextFocused = z; - - int identifier; if (Build.VERSION.SDK_INT < 30) { try { - int iIntValue = ((Integer) InputMethodManager.class.getMethod("getInputMethodWindowVisibleHeight", new Class[0]).invoke((InputMethodManager) getSystemService("input_method"), new Object[0])).intValue(); + int iIntValue = ((Integer) InputMethodManager.class + .getMethod("getInputMethodWindowVisibleHeight", new Class[0]) + .invoke((InputMethodManager) getSystemService("input_method"), new Object[0])).intValue(); if (this.m_editTextFocused && iIntValue == 0) { iIntValue = Utils.dp2px(30.0f); - } else if (this.m_nativeWidth < this.m_nativeHeight && (identifier = getResources().getIdentifier("navigation_bar_height", "dimen", "android")) > 0) { - iIntValue += getResources().getDimensionPixelSize(identifier); + } else if (this.m_nativeWidth < this.m_nativeHeight) { + int id = getResources().getIdentifier("navigation_bar_height", "dimen", "android"); + if (id > 0) iIntValue += getResources().getDimensionPixelSize(id); } toggleKeyboard(this.m_editTextFocused, iIntValue); - if (this.m_editTextFocused) { - getBridgeView().postDelayed((Runnable) () -> notifyEditTextFocus(m_editTextFocused), 100L); - } - } catch (Exception unused) { - } + if (this.m_editTextFocused) + getBridgeView().postDelayed(() -> notifyEditTextFocus(m_editTextFocused), 100L); + } catch (Exception unused) {} } } protected void handleKeyboardInsets(View view, WindowInsets windowInsets) { if (Build.VERSION.SDK_INT >= 30) { - boolean zIsVisible = windowInsets.isVisible(WindowInsets.Type.ime()); + boolean visible = windowInsets.isVisible(WindowInsets.Type.ime()); Insets insets = windowInsets.getInsets(WindowInsets.Type.ime()); - toggleKeyboard(zIsVisible, insets.bottom - insets.top); + toggleKeyboard(visible, insets.bottom - insets.top); } } protected void toggleKeyboard(boolean z, int i) { - LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this); + LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this); if (!z) { if (this.m_isKeyboardShowing) { this.m_isKeyboardShowing = false; this.m_keyboardHeight = 0; onHideKeyboard(); - localBroadcastManager.sendBroadcast(new Intent("KeyboardWillHide")); - return; + lbm.sendBroadcast(new Intent("KeyboardWillHide")); } return; } - if (this.m_isKeyboardShowing && i == this.m_keyboardHeight) { - return; - } + if (this.m_isKeyboardShowing && i == this.m_keyboardHeight) return; this.m_isKeyboardShowing = true; this.m_keyboardHeight = i; onShowKeyboard(i); Intent intent = new Intent("KeyboardWillShow"); intent.putExtra("KeyboardHeight", i); - localBroadcastManager.sendBroadcast(intent); + lbm.sendBroadcast(intent); } public void onGlobalLayout() { Rect rect = new Rect(); this.m_relativeLayout.getWindowVisibleDisplayFrame(rect); int height = this.m_relativeLayout.getHeight() - rect.height(); - LocalBroadcastManager instance = LocalBroadcastManager.getInstance(this); + LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this); if (height <= 0) { if (this.m_isKeyboardShowing) { this.m_isKeyboardShowing = false; onHideKeyboard(); - instance.sendBroadcast(new Intent("KeyboardWillHide")); + lbm.sendBroadcast(new Intent("KeyboardWillHide")); } } else if (!this.m_isKeyboardShowing) { this.m_isKeyboardShowing = true; onShowKeyboard(height); Intent intent = new Intent("KeyboardWillShow"); intent.putExtra("KeyboardHeight", height); - instance.sendBroadcast(intent); + lbm.sendBroadcast(intent); } } protected void onShowKeyboard(int i) { - ArrayList arrayList = this.mOnKeyboardListeners; - if (arrayList == null) { - return; - } - for (OnKeyboardListener onKeyboardListener : arrayList) { - onKeyboardListener.onKeyboardChange(true, i); - } + ArrayList a = this.mOnKeyboardListeners; + if (a == null) return; + for (OnKeyboardListener l : a) l.onKeyboardChange(true, i); getBridgeView().postDelayed(() -> GameActivity.hideNavigationFullScreen(GameActivity.this.getBridgeView()), 100L); } - /* access modifiers changed from: protected */ protected void onHideKeyboard() { - //imguiInput.setVisibility(View.GONE); - ArrayList arrayList = this.mOnKeyboardListeners; - if (arrayList != null) { - for (OnKeyboardListener onKeyboardListener : arrayList) { - onKeyboardListener.onKeyboardChange(false, 0); - } - } + ArrayList a = this.mOnKeyboardListeners; + if (a != null) { for (OnKeyboardListener l : a) l.onKeyboardChange(false, 0); } } - public void addOnKeyboardListener(OnKeyboardListener onKeyboardListener) { - if (this.mOnKeyboardListeners == null) { - this.mOnKeyboardListeners = new ArrayList<>(); - } - if (!this.mOnKeyboardListeners.contains(onKeyboardListener)) { - this.mOnKeyboardListeners.add(onKeyboardListener); + public void addOnKeyboardListener(OnKeyboardListener l) { + if (this.mOnKeyboardListeners == null) this.mOnKeyboardListeners = new ArrayList<>(); + if (!this.mOnKeyboardListeners.contains(l)) this.mOnKeyboardListeners.add(l); + } + + public void RemoveOnKeyboardListener(OnKeyboardListener l) { + ArrayList a = this.mOnKeyboardListeners; + if (a != null) a.remove(l); + } + + private int getDeviceVendorId(InputEvent event) { + InputDevice device = event.getDevice(); + if (device == null) return 0; + return device.getVendorId(); + } + + private int getDeviceProductId(InputEvent event) { + InputDevice device = event.getDevice(); + if (device == null) return 0; + return device.getProductId(); + } + + private boolean isSony(int vendorId) { return vendorId == 0x54c; } + private boolean isDualShock(int productId) { return productId == 0x5c4 || productId == 0x9cc; } + private boolean isDualSense(int productId) { return productId == 0xce6 || productId == 0xdf2; } + + private boolean isHardwareKeyboardEvent(InputEvent event) { + InputDevice device = event.getDevice(); + if (device == null) return false; + int sources = device.getSources(); + return (sources & 0x101) == 0x101 && !device.isVirtual(); + } + + private boolean isHardwareMouseEvent(InputEvent event) { + InputDevice device = event.getDevice(); + if (device == null) return false; + return (event.getSource() & 0x2002) == 0x2002 && !device.isVirtual(); + } + + private boolean isGamepadEvent(InputEvent event) { + if (!mGameControllerIds.contains(Integer.valueOf(event.getDeviceId()))) return false; + return event.isFromSource(0x401) || event.isFromSource(0x1000010); + } + + private boolean isGamepadWithTouchpadEvent(InputEvent event) { + int vendorId = getDeviceVendorId(event); + int productId = getDeviceProductId(event); + return isSony(vendorId) && (isDualShock(productId) || isDualSense(productId)); + } + + private boolean onButtonPress(int keyCode, int inputSource, boolean pressed, int vendorId, int productId) { + if (inputSource == 1) { + setPointerCapture(true); + setGamepadProductTypeNative(vendorId, productId); + } else if (inputSource == 2 || inputSource == 3) { + setPointerCapture(false); } + return onButtonPressNative(keyCode, inputSource, pressed); + } + + private void onGamepadConnected(int vendorId, int productId) { + setPointerCapture(true); + setGamepadProductTypeNative(vendorId, productId); + onGamepadConnectedNative(); + } + + private void setPointerCapture(boolean capture) { + if (capture) getBridgeView().requestPointerCapture(); + else getBridgeView().releasePointerCapture(); } - public void RemoveOnKeyboardListener(OnKeyboardListener onKeyboardListener) { - ArrayList arrayList = this.mOnKeyboardListeners; - if (arrayList != null) { - arrayList.remove(onKeyboardListener); + private int fixKeyCodeCompat(int inputSource, int keyCode, KeyEvent keyEvent) { + if (Build.VERSION.SDK_INT >= 31) return keyCode; + if (inputSource == 1) { + int vendorId = getDeviceVendorId(keyEvent); + int productId = getDeviceProductId(keyEvent); + if (isSony(vendorId)) { + if (keyCode == 4) return 2; + if (keyCode == 125) return 1; + } + } else if (inputSource == 3) { + if (keyCode == 4) return 2; + if (keyCode == 125) return 1; } + return keyCode; } - /* access modifiers changed from: private */ public boolean isValidGameController(int i) { boolean z; InputDevice device = InputDevice.getDevice(i); - if (device == null) { - return false; - } + if (device == null) return false; int sources = device.getSources(); - if ((sources & InputDeviceCompat.SOURCE_GAMEPAD) != 1025 || (sources & InputDeviceCompat.SOURCE_JOYSTICK) != 16777232) { - return false; - } + if ((sources & InputDeviceCompat.SOURCE_GAMEPAD) != 1025 || + (sources & InputDeviceCompat.SOURCE_JOYSTICK) != 16777232) return false; boolean[] hasKeys = device.hasKeys(new int[]{96, 97, 99, 100, 103}); - int length = hasKeys.length; int i2 = 0; while (true) { - if (i2 >= length) { - z = true; - break; - } else if (!hasKeys[i2]) { - z = false; - break; - } else { - i2++; - } + if (i2 >= hasKeys.length) { z = true; break; } + else if (!hasKeys[i2]) { z = false; break; } + else i2++; } int i3 = 0; for (InputDevice.MotionRange next : device.getMotionRanges()) { - if (next.getAxis() == 0 || next.getAxis() == 1 || next.getAxis() == 11 || next.getAxis() == 14) { - i3++; - } + if (next.getAxis() == 0 || next.getAxis() == 1 || next.getAxis() == 11 || next.getAxis() == 14) i3++; } return z && i3 >= 4; } private void initGameController() { - int[] deviceIds; this.mGameControllerIds = new ArrayList<>(); for (int i : InputDevice.getDeviceIds()) { - if (isValidGameController(i)) { - this.mGameControllerIds.add(i); - } + if (isValidGameController(i)) this.mGameControllerIds.add(i); } if (!this.mGameControllerIds.isEmpty()) { - onGamepadConnectedNative(); + InputDevice device = InputDevice.getDevice(mGameControllerIds.get(0)); + if (device != null) onGamepadConnected(device.getVendorId(), device.getProductId()); } InputManager inputManager = (InputManager) getBaseContext().getSystemService(Context.INPUT_SERVICE); if (inputManager != null) { inputManager.registerInputDeviceListener(new InputManager.InputDeviceListener() { - @Override - public void onInputDeviceChanged(int i2) { - } - + @Override public void onInputDeviceChanged(int i2) {} @Override public void onInputDeviceAdded(int i2) { if (GameActivity.this.isValidGameController(i2)) { boolean isEmpty = GameActivity.this.mGameControllerIds.isEmpty(); GameActivity.this.mGameControllerIds.add(i2); if (isEmpty) { - GameActivity.this.onGamepadConnectedNative(); + InputDevice d = InputDevice.getDevice(i2); + if (d != null) GameActivity.this.onGamepadConnected(d.getVendorId(), d.getProductId()); } } } - @Override public void onInputDeviceRemoved(int i2) { if (GameActivity.this.mGameControllerIds.contains(i2)) { GameActivity.this.mGameControllerIds.remove(Integer.valueOf(i2)); - if (GameActivity.this.mGameControllerIds.isEmpty()) { + if (GameActivity.this.mGameControllerIds.isEmpty()) GameActivity.this.onGamepadDisconnectedNative(); - } } } }, null); } } - public boolean onKeyDown(int i, KeyEvent keyEvent) { - if ((keyEvent.getSource() & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD) { - imgui.onKey(i, true); - } - if (keyEvent.getRepeatCount() == 0) { - if (onButtonPressNative(i, this.mGameControllerIds.contains(Integer.valueOf(keyEvent.getDeviceId())) && ((keyEvent.getSource() & 1025) == 1025 || (keyEvent.getSource() & 16777232) == 16777232), true, keyEvent.getEventTime())) { - return true; - } + @Override + public boolean onKeyDown(int keyCode, KeyEvent keyEvent) { + if (keyEvent.getRepeatCount() != 0) return super.onKeyDown(keyCode, keyEvent); + int fixedKeyCode, inputSource; + int vendorId = getDeviceVendorId(keyEvent); + int productId = getDeviceProductId(keyEvent); + if (isGamepadEvent(keyEvent)) { + fixedKeyCode = fixKeyCodeCompat(1, keyCode, keyEvent); + inputSource = 1; + } else if (isHardwareMouseEvent(keyEvent)) { + fixedKeyCode = fixKeyCodeCompat(3, keyCode, keyEvent); + inputSource = 3; + } else if (isHardwareKeyboardEvent(keyEvent)) { + imgui.onKey(keyCode, true); + fixedKeyCode = fixKeyCodeCompat(2, keyCode, keyEvent); + inputSource = 2; + } else { + fixedKeyCode = keyCode; + inputSource = 0; } - return super.onKeyDown(i, keyEvent); + if (onButtonPress(fixedKeyCode, inputSource, true, vendorId, productId)) return true; + return super.onKeyDown(keyCode, keyEvent); } - public boolean onKeyUp(int i, KeyEvent keyEvent) { - if ((keyEvent.getSource() & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD) { - imgui.onKey(i, false); - } - if (keyEvent.getRepeatCount() == 0) { - if (onButtonPressNative(i, this.mGameControllerIds.contains(Integer.valueOf(keyEvent.getDeviceId())) && ((keyEvent.getSource() & 1025) == 1025 || (keyEvent.getSource() & 16777232) == 16777232), false, keyEvent.getEventTime())) { - return true; - } + @Override + public boolean onKeyUp(int keyCode, KeyEvent keyEvent) { + if (keyEvent.getRepeatCount() != 0) return super.onKeyUp(keyCode, keyEvent); + int fixedKeyCode, inputSource; + int vendorId = getDeviceVendorId(keyEvent); + int productId = getDeviceProductId(keyEvent); + if (isGamepadEvent(keyEvent)) { + fixedKeyCode = fixKeyCodeCompat(1, keyCode, keyEvent); + inputSource = 1; + } else if (isHardwareMouseEvent(keyEvent)) { + fixedKeyCode = fixKeyCodeCompat(3, keyCode, keyEvent); + inputSource = 3; + } else if (isHardwareKeyboardEvent(keyEvent)) { + imgui.onKey(keyCode, false); + fixedKeyCode = fixKeyCodeCompat(2, keyCode, keyEvent); + inputSource = 2; + } else { + fixedKeyCode = keyCode; + inputSource = 0; } - return super.onKeyDown(i, keyEvent); + if (onButtonPress(fixedKeyCode, inputSource, false, vendorId, productId)) return true; + return super.onKeyUp(keyCode, keyEvent); } + @Override public boolean onGenericMotionEvent(MotionEvent motionEvent) { - int i; - if (this.m_motionEventsDisabled) { + if (this.m_motionEventsDisabled) return true; + if (isHardwareMouseEvent(motionEvent)) { + int action = motionEvent.getActionMasked(); + if (action == MotionEvent.ACTION_HOVER_MOVE + || action == MotionEvent.ACTION_HOVER_ENTER + || action == MotionEvent.ACTION_HOVER_EXIT) { + PointF point = transformPointToProgram(motionEvent.getX(), motionEvent.getY()); + onMouseMoved((int) point.x, (int) point.y); + m_lastMouseLocation = point; + return true; + } + float scrollX = motionEvent.getAxisValue(MotionEvent.AXIS_HSCROLL); + float scrollY = motionEvent.getAxisValue(MotionEvent.AXIS_VSCROLL); + onMouseScrollingDelta(scrollX, scrollY); return true; } - if (this.mGameControllerIds.contains(Integer.valueOf(motionEvent.getDeviceId()))) { + if (isGamepadEvent(motionEvent)) { + int vendorId = getDeviceVendorId(motionEvent); + int productId = getDeviceProductId(motionEvent); float axisValue = motionEvent.getAxisValue(17); float axisValue2 = motionEvent.getAxisValue(18); + if (Float.compare(axisValue, 0.0f) == 0) axisValue = motionEvent.getAxisValue(23); + if (Float.compare(axisValue2, 0.0f) == 0) axisValue2 = motionEvent.getAxisValue(22); boolean z = Float.compare(axisValue, 1.0f) == 0; boolean z2 = Float.compare(axisValue2, 1.0f) == 0; - if (z != this.m_lTriggerPressed) { - onButtonPressNative(104, true, z, motionEvent.getEventTime()); - } - if (z2 != this.m_rTriggerPressed) { - onButtonPressNative(105, true, z2, motionEvent.getEventTime()); - } + if (z != this.m_lTriggerPressed) onButtonPress(104, 1, z, vendorId, productId); + if (z2 != this.m_rTriggerPressed) onButtonPress(105, 1, z2, vendorId, productId); if (z != this.m_lTriggerPressed || z2 != this.m_rTriggerPressed) { this.m_lTriggerPressed = z; this.m_rTriggerPressed = z2; @@ -797,203 +805,114 @@ public boolean onGenericMotionEvent(MotionEvent motionEvent) { } float axisValue3 = motionEvent.getAxisValue(15); float axisValue4 = motionEvent.getAxisValue(16); - if (Float.compare(axisValue3, -1.0f) == 0) { - i = 21; - } else if (Float.compare(axisValue3, 1.0f) == 0) { - i = 22; - } else if (Float.compare(axisValue4, -1.0f) == 0) { - i = 19; - } else { - i = Float.compare(axisValue4, 1.0f) == 0 ? 20 : 23; - } + int i; + if (Float.compare(axisValue3, -1.0f) == 0) i = 21; + else if (Float.compare(axisValue3, 1.0f) == 0) i = 22; + else if (Float.compare(axisValue4, -1.0f) == 0) i = 19; + else i = Float.compare(axisValue4, 1.0f) == 0 ? 20 : 23; int i2 = this.m_lastDpadDirection; if (i != i2) { - if (i2 != 23) { - onButtonPressNative(i2, true, false, motionEvent.getEventTime()); - } - if (i != 23) { - onButtonPressNative(i, true, true, motionEvent.getEventTime()); - } + if (i2 != 23) onButtonPress(i2, 1, false, vendorId, productId); + if (i != 23) onButtonPress(i, 1, true, vendorId, productId); this.m_lastDpadDirection = i; return true; } - if ((motionEvent.getSource() & 16777232) == 16777232 && (motionEvent.getAction() & KotlinVersion.MAX_COMPONENT_VALUE) == 2) { - onStickEventNative(motionEvent.getAxisValue(0), motionEvent.getAxisValue(1), motionEvent.getAxisValue(11), motionEvent.getAxisValue(14)); + if ((motionEvent.getSource() & 16777232) == 16777232 && + (motionEvent.getAction() & KotlinVersion.MAX_COMPONENT_VALUE) == 2) { + float lx = motionEvent.getAxisValue(0); + float ly = motionEvent.getAxisValue(1); + float rx = motionEvent.getAxisValue(11); + float ry = motionEvent.getAxisValue(14); + if (Math.abs(lx) > 0.02f || Math.abs(ly) > 0.02f || + Math.abs(rx) > 0.02f || Math.abs(ry) > 0.02f) { + setPointerCapture(true); + setGamepadProductTypeNative(vendorId, productId); + } + onStickEventNative(lx, ly, rx, ry); return true; } } return super.onGenericMotionEvent(motionEvent); - } public void addActivePanel(BasePanel basePanel) { - if (this.mActivePanels == null) { - this.mActivePanels = new ArrayList<>(); - } - if (!this.mActivePanels.contains(basePanel)) { - this.mActivePanels.add(basePanel); - } + if (this.mActivePanels == null) this.mActivePanels = new ArrayList<>(); + if (!this.mActivePanels.contains(basePanel)) this.mActivePanels.add(basePanel); } public void removeActivePanel(BasePanel basePanel) { - ArrayList arrayList = this.mActivePanels; - if (arrayList != null) { - arrayList.remove(basePanel); - } + ArrayList a = this.mActivePanels; + if (a != null) a.remove(basePanel); } public void dismissAllPanels() { - ArrayList arrayList = this.mActivePanels; - if (arrayList != null) { - for (BasePanel basePanel : arrayList) { - basePanel.dismiss(); - } - } + ArrayList a = this.mActivePanels; + if (a != null) { for (BasePanel bp : a) bp.dismiss(); } } private void tryEnablingDisplayCutoutMode() { - View decorView; - if (Build.VERSION.SDK_INT >= 28 && (decorView = getWindow().getDecorView()) != null) { - WindowInsets rootWindowInsets = decorView.getRootWindowInsets(); - if (rootWindowInsets == null) { - WindowManager.LayoutParams attributes = getWindow().getAttributes(); - attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; - getWindow().setAttributes(attributes); - } else if (rootWindowInsets.getDisplayCutout() != null) { - WindowManager.LayoutParams attributes2 = getWindow().getAttributes(); - attributes2.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; - getWindow().setAttributes(attributes2); - } + if (Build.VERSION.SDK_INT >= 28) { + View decorView = getWindow().getDecorView(); + if (decorView == null) return; + WindowManager.LayoutParams attrs = getWindow().getAttributes(); + attrs.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; + getWindow().setAttributes(attrs); } } - public RelativeLayout getBridgeView() { - return this.m_relativeLayout; - } - - public Rect GetSafeAreaInsets() { - return this.mSafeAreaInsets; - } - - public static void hideNavigationFullScreen(View view) { - view.setSystemUiVisibility(5894); - } - - public static void showNavigationFullScreen(View view) { - view.setSystemUiVisibility(1792); - } - - public void onAudioDeviceTypeChanged(AudioDeviceType audioDeviceType) { - onAudioDeviceTypeChangedNative(audioDeviceType.ordinal()); - } - - public void onCommerceUpdate(boolean z, boolean z2, boolean z3) { - onCommerceUpdateNative(z, z2, z3); - } - - public String ResolveTemplateArgs(String str) { - return ResolveTemplateArgsNative(str); - } + public RelativeLayout getBridgeView() { return this.m_relativeLayout; } + public Rect GetSafeAreaInsets() { return this.mSafeAreaInsets; } + public static void hideNavigationFullScreen(View view) { view.setSystemUiVisibility(5894); } + public static void showNavigationFullScreen(View view) { view.setSystemUiVisibility(1792); } + public void onAudioDeviceTypeChanged(AudioDeviceType audioDeviceType) { onAudioDeviceTypeChangedNative(audioDeviceType.ordinal()); } + public void onCommerceUpdate(boolean z, boolean z2, boolean z3) { onCommerceUpdateNative(z, z2, z3); } + public String ResolveTemplateArgs(String str) { return ResolveTemplateArgsNative(str); } public void transformPointToSystem(float f, float f2, RectF rectF) { - rectF.left += f; - rectF.right += f; - float height = ((float) getWindow().getDecorView().getHeight()) - f2; - rectF.top += height; - rectF.bottom += height; - } - - public RectF transformRectToSystem(RectF rectF) { - return new RectF(rectF); + rectF.left += f; rectF.right += f; + float h = ((float) getWindow().getDecorView().getHeight()) - f2; + rectF.top += h; rectF.bottom += h; } - public RectF transformRectToProgram(RectF rectF) { - return new RectF(rectF); - } - - public String getAppId() { - return getApplicationInfo().packageName; - } - - public String getAppName() { - return "Sky"; - } - - public String getAppProgramLibDir() { - return getApplicationInfo().nativeLibraryDir; - } + public RectF transformRectToSystem(RectF rectF) { return new RectF(rectF); } + public RectF transformRectToProgram(RectF rectF) { return new RectF(rectF); } + public String getAppId() { return getApplicationInfo().packageName; } + public String getAppName() { return "Sky"; } + public String getAppProgramLibDir() { return getApplicationInfo().nativeLibraryDir; } public String getOpenedWithURL() { Intent intent = getIntent(); - if ("android.intent.action.VIEW".equals(intent.getAction())) { - return intent.getDataString(); - } + if ("android.intent.action.VIEW".equals(intent.getAction())) return intent.getDataString(); return null; } public String getOpenedWithNFC() { Intent intent = getIntent(); - if ("android.nfc.action.NDEF_DISCOVERED".equals(intent.getAction())) { - return intent.getDataString(); - } + if ("android.nfc.action.NDEF_DISCOVERED".equals(intent.getAction())) return intent.getDataString(); return null; } - public void setDisplayWidth(int i) { - this.m_nativeWidth = i; - } - - public int getDisplayWidth() { - return this.m_nativeWidth; - } - - public void setDisplayHeight(int i) { - this.m_nativeHeight = i; - } - - public int getDisplayHeight() { - return this.m_nativeHeight; - } + public void setDisplayWidth(int i) { this.m_nativeWidth = i; } + public int getDisplayWidth() { return this.m_nativeWidth; } + public void setDisplayHeight(int i) { this.m_nativeHeight = i; } + public int getDisplayHeight() { return this.m_nativeHeight; } public float getDisplaySizeInInches() { - DisplayMetrics displayMetrics = new DisplayMetrics(); - getWindowManager().getDefaultDisplay().getRealMetrics(displayMetrics); - float f = ((float) displayMetrics.heightPixels) / displayMetrics.ydpi; - float f2 = ((float) displayMetrics.widthPixels) / displayMetrics.xdpi; - return (float) Math.sqrt((double) ((f2 * f2) + (f * f))); - } - - public float getDisplayXdpi() { - return SMLApplication.skyRes.getDisplayMetrics().xdpi; - } - - public float getDisplayYdpi() { - return SMLApplication.skyRes.getDisplayMetrics().ydpi; - } - - public float getDisplayDensity() { - return SMLApplication.skyRes.getDisplayMetrics().density; - } - - public boolean isScreenHdr() { - return SMLApplication.skyRes.getConfiguration().isScreenHdr(); - } - - public boolean isScreenWideColorGamut() { - return SMLApplication.skyRes.getConfiguration().isScreenWideColorGamut(); - } - - public float getDesiredMinLum() { - return getWindowManager().getDefaultDisplay().getHdrCapabilities().getDesiredMinLuminance(); - } - - public float getDesiredMaxLum() { - return getWindowManager().getDefaultDisplay().getHdrCapabilities().getDesiredMaxLuminance(); - } - - public float getDisplayRefreshRate() { - return getWindowManager().getDefaultDisplay().getRefreshRate(); - } + DisplayMetrics dm = new DisplayMetrics(); + getWindowManager().getDefaultDisplay().getRealMetrics(dm); + float h = ((float) dm.heightPixels) / dm.ydpi; + float w = ((float) dm.widthPixels) / dm.xdpi; + return (float) Math.sqrt((double) (w * w + h * h)); + } + + public float getDisplayXdpi() { return SMLApplication.skyRes.getDisplayMetrics().xdpi; } + public float getDisplayYdpi() { return SMLApplication.skyRes.getDisplayMetrics().ydpi; } + public float getDisplayDensity() { return SMLApplication.skyRes.getDisplayMetrics().density; } + public boolean isScreenHdr() { return SMLApplication.skyRes.getConfiguration().isScreenHdr(); } + public boolean isScreenWideColorGamut() { return SMLApplication.skyRes.getConfiguration().isScreenWideColorGamut(); } + public float getDesiredMinLum() { return getWindowManager().getDefaultDisplay().getHdrCapabilities().getDesiredMinLuminance(); } + public float getDesiredMaxLum() { return getWindowManager().getDefaultDisplay().getHdrCapabilities().getDesiredMaxLuminance(); } + public float getDisplayRefreshRate() { return getWindowManager().getDefaultDisplay().getRefreshRate(); } public int getPhysicalMemorySize() { ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo(); @@ -1002,34 +921,30 @@ public int getPhysicalMemorySize() { } public String getDeviceName() { - String string = Settings.Global.getString(getContentResolver(), "device_name"); - if (string == null || string.isEmpty()) { - string = Settings.Secure.getString(getContentResolver(), "bluetooth_name"); - } - return (string == null || string.isEmpty()) ? "NO_DEVICE_NAME" : string; + String s = Settings.Global.getString(getContentResolver(), "device_name"); + if (s == null || s.isEmpty()) s = Settings.Secure.getString(getContentResolver(), "bluetooth_name"); + return (s == null || s.isEmpty()) ? "NO_DEVICE_NAME" : s; } public String getDeviceModel() { String str = Build.MANUFACTURER; String str2 = Build.MODEL; - if (!str.isEmpty()) { - str = str + " "; - } + if (!str.isEmpty()) str = str + " "; return str + str2; } public String getDeviceDescriptionJson(String str) { try { - JSONObject jSONObject = new JSONObject(); - jSONObject.put("brand", Build.MANUFACTURER); - jSONObject.put("model", Build.MODEL); - JSONObject jSONObject2 = new JSONObject(); - jSONObject2.put("build_brand", Build.BRAND); - jSONObject2.put("build_device", Build.DEVICE); - jSONObject2.put("build_product", Build.PRODUCT); - jSONObject2.put("gpu", str); - jSONObject.put("device_extra", jSONObject2); - return jSONObject.toString(); + JSONObject o = new JSONObject(); + o.put("brand", Build.MANUFACTURER); + o.put("model", Build.MODEL); + JSONObject o2 = new JSONObject(); + o2.put("build_brand", Build.BRAND); + o2.put("build_device", Build.DEVICE); + o2.put("build_product", Build.PRODUCT); + o2.put("gpu", str); + o.put("device_extra", o2); + return o.toString(); } catch (JSONException e) { Log.e(TAG, "Failed to generate deviceDescriptionJson", e); return "{}"; @@ -1037,39 +952,31 @@ public String getDeviceDescriptionJson(String str) { } public byte[] getDeviceUuid() { - @SuppressLint("HardwareIds") String string = Settings.Secure.getString(getContentResolver(), "android_id"); - if (string.length() < 16) { - string = new String(new char[(16 - string.length())]).replace('\0', '0') + string; - } - byte[] bArr = new byte[(string.length() / 2)]; - for (int i = 0; i < string.length(); i += 2) { - bArr[i / 2] = (byte) ((Character.digit(string.charAt(i), 16) << 4) + Character.digit(string.charAt(i + 1), 16)); - } + @SuppressLint("HardwareIds") + String s = Settings.Secure.getString(getContentResolver(), "android_id"); + if (s.length() < 16) s = new String(new char[16 - s.length()]).replace('\0', '0') + s; + byte[] bArr = new byte[s.length() / 2]; + for (int i = 0; i < s.length(); i += 2) + bArr[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16)); return bArr; } public void playLogoSound() { - AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); - if(audioManager.isMusicActive()) return; + AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE); + if (am.isMusicActive()) return; MediaPlayer player = new MediaPlayer(); try { - player.setDataSource(SMLApplication.skyRes.openRawResourceFd(SMLApplication.skyRes.getIdentifier("tgc_logo", "raw", SMLApplication.skyPName))); + player.setDataSource(SMLApplication.skyRes.openRawResourceFd( + SMLApplication.skyRes.getIdentifier("tgc_logo", "raw", SMLApplication.skyPName))); player.prepare(); (m_mediaPlayer = player).start(); - }catch (IOException e) { - e.printStackTrace(); - } + } catch (IOException e) { e.printStackTrace(); } } public boolean tryReleaseLogoSound() { - if(m_mediaPlayer == null) { - this.m_logoSoundReleased = true; - return true; - } + if (m_mediaPlayer == null) { this.m_logoSoundReleased = true; return true; } if (!this.m_logoSoundReleased) { - if (this.m_mediaPlayer.isPlaying()) { - return false; - } + if (this.m_mediaPlayer.isPlaying()) return false; this.m_mediaPlayer.release(); this.m_logoSoundReleased = true; } @@ -1077,43 +984,28 @@ public boolean tryReleaseLogoSound() { } public void fadeoutLogos() { - runOnUiThread(()->{ - AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f, 0.0f); - alphaAnimation.setInterpolator(new AccelerateInterpolator()); - alphaAnimation.setDuration(1000); - alphaAnimation.setAnimationListener(new Animation.AnimationListener() { - @Override - public void onAnimationStart(Animation animation) { - - } - - @Override - public void onAnimationEnd(Animation animation) { + runOnUiThread(() -> { + AlphaAnimation anim = new AlphaAnimation(1.0f, 0.0f); + anim.setInterpolator(new AccelerateInterpolator()); + anim.setDuration(1000); + anim.setAnimationListener(new Animation.AnimationListener() { + @Override public void onAnimationStart(Animation a) {} + @Override public void onAnimationEnd(Animation a) { logoView.setVisibility(View.GONE); - // TODO: Don't remove! git.artdeell.skymodloader.MainActivity.lateInitUserLibs(); } - - @Override - public void onAnimationRepeat(Animation animation) { - - } + @Override public void onAnimationRepeat(Animation a) {} }); - logoView.startAnimation(alphaAnimation); + logoView.startAnimation(anim); }); } - public void pressBackButton() { - moveTaskToBack(true); - } + public void pressBackButton() { moveTaskToBack(true); } public void finishActivity() { finishAndRemoveTask(); new Timer().schedule(new TimerTask() { - public void run() { - System.exit(0); - } + public void run() { System.exit(0); } }, 5000); } - -} +} \ No newline at end of file diff --git a/app/src/main/java/com/tgc/sky/SystemIO_android.java b/app/src/main/java/com/tgc/sky/SystemIO_android.java index cc52658..4fd3dfb 100644 --- a/app/src/main/java/com/tgc/sky/SystemIO_android.java +++ b/app/src/main/java/com/tgc/sky/SystemIO_android.java @@ -596,12 +596,13 @@ public boolean StartRecording() { } } - boolean StartRecording(String str, int i, int i2, int i3, int i4, boolean z, int i5, boolean z2) { - this.m_isGameRecorder = true; - String str2 = C115312.$SwitchMap$com$tgc$sky$SystemIO_android$VideoCodec[VideoCodec.values()[i3].ordinal()] != 2 ? "video/avc" : "video/hevc"; - this.m_isRecording = true; - return this.m_videoRecorder.startRecordingWithFilename(str, i, i2,str2, i4, z, i5, z2); - } + boolean StartRecording(String str, int i, int i2, int i3, boolean z, int i4, boolean z2, int i5, boolean z3) { + this.m_isGameRecorder = true; + String str2 = C115312.$SwitchMap$com$tgc$sky$SystemIO_android$VideoCodec[VideoCodec.values()[i3].ordinal()] != 2 ? "video/avc" : "video/hevc"; + this.m_isRecording = true; + return this.m_videoRecorder.startRecordingWithFilename(str, i, i2, str2, i4, z2, i5, z3); + } + static /* synthetic */ class C115312 { static final /* synthetic */ int[] $SwitchMap$com$tgc$sky$SystemIO_android$VideoCodec; diff --git a/app/src/main/java/com/tgc/sky/SystemSupport_android.java b/app/src/main/java/com/tgc/sky/SystemSupport_android.java index 4869c81..538a37d 100644 --- a/app/src/main/java/com/tgc/sky/SystemSupport_android.java +++ b/app/src/main/java/com/tgc/sky/SystemSupport_android.java @@ -3,23 +3,10 @@ import android.app.Application; import android.content.Context; import android.content.Intent; -import android.net.Uri; -import android.text.Html; -import android.text.method.LinkMovementMethod; -import android.view.View; -import android.widget.TextView; -import android.view.ViewGroup; -import android.view.Window; -import android.widget.ImageButton; -import android.app.Dialog; - -import androidx.appcompat.app.AlertDialog; import java.util.HashMap; import java.util.Map; -import git.artdeell.skymodloader.R; - public class SystemSupport_android implements GameActivity.OnActivityResultListener { private static final String TAG = "SystemSupport_android"; private static volatile SystemSupport_android mInstance; @@ -33,9 +20,7 @@ public class SystemSupport_android implements GameActivity.OnActivityResultListe private boolean m_surveyActive; private boolean m_surveyCompleted; - static void OnApplicationCreate(Application application) { - } public static SystemSupport_android getInstance() { @@ -49,7 +34,6 @@ public static SystemSupport_android getInstance() { return mInstance; } - /* access modifiers changed from: package-private */ public void Initialize(GameActivity gameActivity) { this.m_activity = gameActivity; } @@ -77,7 +61,6 @@ public void ClearMetaData() { } public void EnableCustomerSupport(boolean z) { - } public boolean IsCustomerSupportEnabled() { @@ -85,47 +68,15 @@ public boolean IsCustomerSupportEnabled() { } public void ShowFAQs() { - m_activity.runOnUiThread(()->{ - Dialog dialog = new Dialog(m_activity, R.style.AboutDialog); - dialog.setContentView(R.layout.about_dialog); - - // Support links - setupLink(dialog, R.id.englishTelegram, "https://t.me/+Hc6QeQVTH1JjNjky"); - setupLink(dialog, R.id.russianTelegram, "https://t.me/+mue7BZWmWOxkMDUy"); - - // Original developers - setupLink(dialog, R.id.icarusGithub, "https://github.com/lukas0x1"); - setupLink(dialog, R.id.artDevGithub, "https://github.com/artdeell"); - - // Contributors - setupLink(dialog, R.id.romanGithub, "https://github.com/RomanChamelo"); - setupLink(dialog, R.id.kiojeenGithub, "https://github.com/Kiojeen"); - setupLink(dialog, R.id.gxostGithub, "https://github.com/gxosty"); - - // Design credits - setupLink(dialog, R.id.bannerVk, "https://vk.com/son583"); - setupLink(dialog, R.id.iconTelegram, "https://t.me/Eliatshaha"); - - // Original game - setupLink(dialog, R.id.thatgamecompanyLink, "https://thatgamecompany.com/"); - - dialog.show(); - }); - } - - private void setupLink(Dialog dialog, int viewId, String url) { - TextView textView = dialog.findViewById(viewId); - textView.setOnClickListener(v -> { - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); - m_activity.startActivity(intent); - }); + m_activity.runOnUiThread(() -> + git.artdeell.skymodloader.AboutDialogHelper.show(m_activity) + ); } public void ShowSupportSession() { } public void ShowErrorReportConversation() { - } public boolean IsSupportUIShowing() { @@ -175,6 +126,5 @@ public boolean WasQuestionnaireCompleted() { } public void onActivityResult(int i, int i2, Intent intent) { - } } diff --git a/app/src/main/java/com/tgc/sky/SystemUI_android.java b/app/src/main/java/com/tgc/sky/SystemUI_android.java index 4ef3b23..5ec8959 100644 --- a/app/src/main/java/com/tgc/sky/SystemUI_android.java +++ b/app/src/main/java/com/tgc/sky/SystemUI_android.java @@ -157,13 +157,11 @@ public boolean HasLocalizedString(String str) { return this.m_localizationManager.HasLocalizedString(str); } - public String GetLocalizedTextFromTextId(int textId) { - String textKey = "text_" + textId; - if (HasLocalizedString(textKey)) { - return LocalizeString(textKey); - } - return ""; - } + public String GetLocalizedTextFromTextId(int textId) { + LocalizationManager lm = LocalizationManager.getInstance(); + if (lm == null) return null; + return lm.GetLocalizedTextFromTextId(textId); + } public SpannableStringBuilder GetMarkedUpString(String str, ArrayList arrayList, boolean z) { return this.m_markup.GetMarkedUpString(str, arrayList, z); diff --git a/app/src/main/java/com/tgc/sky/ui/text/LocalizationManager.java b/app/src/main/java/com/tgc/sky/ui/text/LocalizationManager.java index 51d6ca9..87c365e 100644 --- a/app/src/main/java/com/tgc/sky/ui/text/LocalizationManager.java +++ b/app/src/main/java/com/tgc/sky/ui/text/LocalizationManager.java @@ -2,6 +2,7 @@ import android.content.res.Configuration; import android.content.res.Resources; +import android.text.SpannableStringBuilder; import androidx.constraintlayout.core.motion.utils.TypedValues; import androidx.core.app.FrameMetricsAggregator; @@ -114,6 +115,10 @@ public static String GetIETFLanguageTag() { } return GetIETFLanguageTag(language); } + + public static LocalizationManager getInstance() { + return g_instance; + } public static String GetIETFLanguageTag(String str) { String str2 = DEFAULT_COUNTRY_CODES.get(str); @@ -474,6 +479,52 @@ public void run() { }); } + public static SpannableStringBuilder ApplyTextArgs(SpannableStringBuilder sb, ArrayList args) { + if (args == null || args.size() == 0) return sb; + String[] placeholders = {"{{1}}", "{{2}}", "{{3}}"}; + for (int i = 0; i < args.size() && i < placeholders.length; i++) { + String replacement = args.get(i) != null ? args.get(i).toString() : ""; + int pos = 0; + while (true) { + String s = sb.toString(); + int idx = s.indexOf(placeholders[i], pos); + if (idx == -1) break; + sb.replace(idx, idx + 5, replacement); + pos = idx + replacement.length(); + } + } + return sb; +} + +private SpannableStringBuilder GetLocalizedTextFromTextIdRecursive(int i) { + int idx = GetAllocatedTextIdx(i); + if (idx >= kMaxLocalizedStrings) return null; + + LocalizedStringArgs args = m_localizedStrings.get(idx); + + if (args.localizedString != null) { + SpannableStringBuilder sb = new SpannableStringBuilder(args.localizedString); + return ApplyTextArgs(sb, args.args); + } + + if (args.compounded != null) { + SpannableStringBuilder sb = new SpannableStringBuilder(); + for (int id : args.compounded) { + SpannableStringBuilder part = GetLocalizedTextFromTextIdRecursive(id); + if (part != null) sb.append(part); + } + return sb.length() > 0 ? sb : null; + } + + return null; + } + + public String GetLocalizedTextFromTextId(int textId) { + SpannableStringBuilder result = GetLocalizedTextFromTextIdRecursive(textId); + if (result == null) return null; + return result.toString(); + } + private int GetAllocatedTextIdx(int i) { return i != -1 ? i & FrameMetricsAggregator.EVERY_DURATION : kMaxLocalizedStrings; } diff --git a/app/src/main/java/git/artdeell/skymodloader/AboutDialogHelper.java b/app/src/main/java/git/artdeell/skymodloader/AboutDialogHelper.java new file mode 100644 index 0000000..73e4e5b --- /dev/null +++ b/app/src/main/java/git/artdeell/skymodloader/AboutDialogHelper.java @@ -0,0 +1,298 @@ +package git.artdeell.skymodloader; + +import android.app.Activity; +import android.app.Dialog; +import android.content.Intent; +import android.graphics.Typeface; +import android.net.Uri; +import android.view.Gravity; +import android.view.View; +import android.view.Window; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.bumptech.glide.Glide; + +public class AboutDialogHelper { + + public static void show(Activity activity) { + Dialog dialog = new Dialog(activity, R.style.AboutDialog); + dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + dialog.setContentView(R.layout.about_dialog); + dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent); + dialog.getWindow().setLayout( + (int) (activity.getResources().getDisplayMetrics().widthPixels * 0.92), + android.view.ViewGroup.LayoutParams.WRAP_CONTENT + ); + + ((TextView) dialog.findViewById(R.id.about_version)) + .setText("v" + BuildConfig.VERSION_NAME); + + LinearLayout timeline = dialog.findViewById(R.id.about_timeline); + + addChapter(activity, timeline, "The Origin", "Built from scratch by two developers who love Sky.", true); + addPerson(activity, timeline, "artdeell", "Author", "artdeell", "https://github.com/artdeell"); + addPerson(activity, timeline, "lukas0x1", "Author · retired", "lukas0x1", "https://github.com/lukas0x1"); + endChapter(timeline); + + addChapter(activity, timeline, "The Present", "Currently keeping Canvas alive.", true); + addPerson(activity, timeline, "MisterGatto", "Maintainer", "MisterGatto", "https://github.com/MisterGatto"); + endChapter(timeline); + + addChapter(activity, timeline, "The Team", "Everyone who made Canvas better.", true); + addPerson(activity, timeline, "RomanChamelo", null, "RomanChamelo", "https://github.com/RomanChamelo"); + addPerson(activity, timeline, "Kiojeen", null, "Kiojeen", "https://github.com/Kiojeen"); + addPerson(activity, timeline, "Gxosty", null, "Gxosty", "https://github.com/gxosty"); + addPerson(activity, timeline, "achcyano", null, "achcyano", "https://github.com/achcyano"); + addPerson(activity, timeline, "HugeFrog24", null, "HugeFrog24", "https://github.com/HugeFrog24"); + addPerson(activity, timeline, "RadiatedExodus", null, "RadiatedExodus", "https://github.com/RadiatedExodus"); + addPerson(activity, timeline, "HungWah2", null, "HungWah2", "https://github.com/HungWah2"); + addPerson(activity, timeline, "alvindimas05", null, "alvindimas05", "https://github.com/alvindimas05"); + endChapter(timeline); + + addChapter(activity, timeline, "The Art", "The faces behind the visuals.", true); + addPersonCustomLink(activity, timeline, "Сон~", "Banner artwork", "VK ›", "https://vk.com/son583"); + addPersonCustomLink(activity, timeline, "Dysis", "App icon", "TG ›", "https://t.me/Eliatshaha"); + endChapter(timeline); + + addChapter(activity, timeline, "The Community", "Join thousands of players who use Canvas every day.", false); + addCommunityCard(activity, timeline, "Discord", "https://discord.gg/FrHP57VRPs", "https://discord.gg/FrHP57VRPs"); + addCommunityCard(activity, timeline, "English Telegram", "t.me/skyautowax", "https://t.me/skyautowax"); + addCommunityCard(activity, timeline, "Russian Telegram", "t.me/skyruswax", "https://t.me/s/ruautowax"); + endChapter(timeline); + + dialog.findViewById(R.id.thatgamecompanyLink).setOnClickListener(v -> + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://thatgamecompany.com")))); + dialog.findViewById(R.id.dialog_info_close).setOnClickListener(v -> + dialog.dismiss()); + + dialog.show(); + } + + private static void addChapter(Activity a, LinearLayout parent, String title, String subtitle, boolean withLine) { + LinearLayout row = new LinearLayout(a); + row.setOrientation(LinearLayout.HORIZONTAL); + row.setTag("chapter_row"); + + LinearLayout rail = new LinearLayout(a); + rail.setOrientation(LinearLayout.VERTICAL); + rail.setGravity(Gravity.CENTER_HORIZONTAL); + rail.setLayoutParams(new LinearLayout.LayoutParams(dp(a, 32), LinearLayout.LayoutParams.WRAP_CONTENT)); + + TextView dot = new TextView(a); + LinearLayout.LayoutParams dotParams = new LinearLayout.LayoutParams(dp(a, 12), dp(a, 12)); + dotParams.topMargin = dp(a, 4); + dot.setLayoutParams(dotParams); + dot.setBackgroundResource(R.drawable.logo_bg); + rail.addView(dot); + + if (withLine) { + View line = new View(a); + LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(dp(a, 2), 0, 1f); + lp.topMargin = dp(a, 6); + line.setLayoutParams(lp); + line.setBackgroundResource(R.drawable.separator); + line.setAlpha(0.25f); + rail.addView(line); + } + row.addView(rail); + + LinearLayout content = new LinearLayout(a); + content.setOrientation(LinearLayout.VERTICAL); + LinearLayout.LayoutParams cp = new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f); + cp.setMarginStart(dp(a, 12)); + content.setLayoutParams(cp); + + TextView tv = new TextView(a); + tv.setText(title); + tv.setTextSize(16); + tv.setTypeface(null, Typeface.BOLD); + LinearLayout.LayoutParams tvp = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); + tvp.bottomMargin = dp(a, 4); + tv.setLayoutParams(tvp); + content.addView(tv); + + TextView sv = new TextView(a); + sv.setText(subtitle); + sv.setTextSize(12); + sv.setAlpha(0.45f); + sv.setLineSpacing(0, 1.4f); + LinearLayout.LayoutParams svp = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); + svp.bottomMargin = dp(a, 14); + sv.setLayoutParams(svp); + content.addView(sv); + + row.addView(content); + parent.addView(row); + } + + private static void endChapter(LinearLayout parent) { + View last = parent.getChildAt(parent.getChildCount() - 1); + if (last != null) { + LinearLayout.LayoutParams p = (LinearLayout.LayoutParams) last.getLayoutParams(); + p.bottomMargin = dp(last.getContext(), 28); + last.setLayoutParams(p); + } + } + + private static LinearLayout getCurrentChapterContent(LinearLayout parent) { + for (int i = parent.getChildCount() - 1; i >= 0; i--) { + View child = parent.getChildAt(i); + if (child instanceof LinearLayout && "chapter_row".equals(child.getTag())) { + return (LinearLayout) ((LinearLayout) child).getChildAt(1); + } + } + return parent; + } + + private static void addPerson(Activity a, LinearLayout parent, String name, String role, String githubUser, String githubUrl) { + LinearLayout content = getCurrentChapterContent(parent); + + LinearLayout row = new LinearLayout(a); + row.setOrientation(LinearLayout.HORIZONTAL); + row.setGravity(Gravity.CENTER_VERTICAL); + LinearLayout.LayoutParams rp = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); + rp.bottomMargin = dp(a, 10); + row.setLayoutParams(rp); + + ImageView avatar = new ImageView(a); + LinearLayout.LayoutParams ap = new LinearLayout.LayoutParams(dp(a, 38), dp(a, 38)); + ap.setMarginEnd(dp(a, 12)); + avatar.setLayoutParams(ap); + avatar.setBackgroundResource(R.drawable.logo_bg); + avatar.setScaleType(ImageView.ScaleType.CENTER_CROP); + Glide.with(a) + .load("https://github.com/" + githubUser + ".png?size=64") + .circleCrop() + .placeholder(R.drawable.logo_bg) + .error(R.drawable.logo_bg) + .into(avatar); + row.addView(avatar); + + LinearLayout tb = new LinearLayout(a); + tb.setOrientation(LinearLayout.VERTICAL); + tb.setLayoutParams(new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f)); + + TextView nv = new TextView(a); + nv.setText(name); + nv.setTextSize(14); + nv.setTypeface(null, Typeface.BOLD); + tb.addView(nv); + + if (role != null) { + TextView rv = new TextView(a); + rv.setText(role); + rv.setTextSize(11); + rv.setAlpha(0.4f); + tb.addView(rv); + } + row.addView(tb); + + TextView link = new TextView(a); + link.setText("GitHub ›"); + link.setTextSize(12); + link.setAlpha(0.35f); + link.setPadding(dp(a, 4), dp(a, 4), dp(a, 4), dp(a, 4)); + link.setClickable(true); + link.setFocusable(true); + link.setOnClickListener(v -> a.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(githubUrl)))); + row.addView(link); + + content.addView(row); + } + + private static void addPersonCustomLink(Activity a, LinearLayout parent, String name, String role, String linkLabel, String linkUrl) { + LinearLayout content = getCurrentChapterContent(parent); + + LinearLayout row = new LinearLayout(a); + row.setOrientation(LinearLayout.HORIZONTAL); + row.setGravity(Gravity.CENTER_VERTICAL); + LinearLayout.LayoutParams rp = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); + rp.bottomMargin = dp(a, 10); + row.setLayoutParams(rp); + + LinearLayout tb = new LinearLayout(a); + tb.setOrientation(LinearLayout.VERTICAL); + tb.setLayoutParams(new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f)); + + TextView nv = new TextView(a); + nv.setText(name); + nv.setTextSize(14); + nv.setTypeface(null, Typeface.BOLD); + tb.addView(nv); + + if (role != null) { + TextView rv = new TextView(a); + rv.setText(role); + rv.setTextSize(11); + rv.setAlpha(0.4f); + tb.addView(rv); + } + row.addView(tb); + + TextView link = new TextView(a); + link.setText(linkLabel); + link.setTextSize(12); + link.setAlpha(0.35f); + link.setPadding(dp(a, 4), dp(a, 4), dp(a, 4), dp(a, 4)); + link.setClickable(true); + link.setFocusable(true); + link.setOnClickListener(v -> a.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(linkUrl)))); + row.addView(link); + + content.addView(row); + } + + private static void addCommunityCard(Activity a, LinearLayout parent, String title, String subtitle, String url) { + LinearLayout content = getCurrentChapterContent(parent); + + LinearLayout card = new LinearLayout(a); + card.setOrientation(LinearLayout.HORIZONTAL); + card.setGravity(Gravity.CENTER_VERTICAL); + card.setBackgroundResource(R.drawable.buttons); + int p = dp(a, 14); + card.setPadding(p, p, p, p); + LinearLayout.LayoutParams cp = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); + cp.bottomMargin = dp(a, 8); + card.setLayoutParams(cp); + card.setClickable(true); + card.setFocusable(true); + card.setOnClickListener(v -> a.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)))); + + LinearLayout tb = new LinearLayout(a); + tb.setOrientation(LinearLayout.VERTICAL); + tb.setLayoutParams(new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f)); + + TextView tv = new TextView(a); + tv.setText(title); + tv.setTextSize(14); + tv.setTypeface(null, Typeface.BOLD); + tb.addView(tv); + + TextView sv = new TextView(a); + sv.setText(subtitle); + sv.setTextSize(11); + sv.setAlpha(0.4f); + tb.addView(sv); + + card.addView(tb); + + TextView arrow = new TextView(a); + arrow.setText("›"); + arrow.setTextSize(20); + arrow.setAlpha(0.3f); + card.addView(arrow); + + content.addView(card); + } + + private static int dp(android.content.Context ctx, int dp) { + return Math.round(dp * ctx.getResources().getDisplayMetrics().density); + } +} diff --git a/app/src/main/java/git/artdeell/skymodloader/MainActivity.java b/app/src/main/java/git/artdeell/skymodloader/MainActivity.java index d50627a..7e2f91d 100644 --- a/app/src/main/java/git/artdeell/skymodloader/MainActivity.java +++ b/app/src/main/java/git/artdeell/skymodloader/MainActivity.java @@ -114,28 +114,31 @@ private void loadGame() { String versionName = info.versionName; BuildConfig.SKY_VERSION = versionName.substring(0, versionName.indexOf(' ')).trim(); BuildConfig.VERSION_CODE = info.versionCode; - String nativeLibraryDir = info.applicationInfo.nativeLibraryDir; - String libPath = nativeLibraryDir; - File libDir = new File(nativeLibraryDir); - if (!libDir.exists() || libDir.listFiles() == null || libDir.listFiles().length == 0) { - libPath = extractLibrariesFromApk(info.applicationInfo); - } + + String libPath = resolveLibPath(info.applicationInfo); + String elfLibPath = resolveElfLibPath(info.applicationInfo, libPath); File modsDir = new File(getFilesDir(), "mods"); File configDir = new File(getFilesDir(), "config"); if (!configDir.isDirectory() && !configDir.mkdirs()) throw new IOException("Failed to create mod configuration directory"); - android.util.Log.i("MainActivity", "Pre-loading FMOD dependencies from: " + libPath); + + Log.i("MainActivity", "Pre-loading FMOD dependencies from: " + libPath); org.fmod.FMOD.init(this); - File fmodLibDir = new File(libPath); - android.util.Log.i("MainActivity", "Listing all files in: " + libPath); - File[] allFiles = fmodLibDir.listFiles(); - if (allFiles != null) { - for (File f : allFiles) { - android.util.Log.i("MainActivity", "Found file: " + f.getName()); + + if (!libPath.contains("!/lib")) { + File fmodLibDir = new File(libPath); + Log.i("MainActivity", "Listing all files in: " + libPath); + File[] allFiles = fmodLibDir.listFiles(); + if (allFiles != null) { + for (File f : allFiles) { + Log.i("MainActivity", "Found file: " + f.getName()); + } + } else { + Log.e("MainActivity", "Directory is empty or doesn't exist!"); } } else { - android.util.Log.e("MainActivity", "Directory is empty or doesn't exist!"); + Log.i("MainActivity", "Loading libs directly from APK (no extraction)"); } String[] libsToLoad = { @@ -144,22 +147,29 @@ private void loadGame() { "libfmod.so", "libfmodstudio.so" }; + for (String libName : libsToLoad) { - File lib = new File(fmodLibDir, libName); - if (lib.exists()) { - try { - android.util.Log.i("MainActivity", "Loading: " + libName); + try { + if (libPath.contains("!/lib")) { + String fullApkLibPath = libPath + "/" + libName; + Log.i("MainActivity", "Loading from APK: " + fullApkLibPath); + System.load(fullApkLibPath); + } else { + File lib = new File(libPath, libName); + if (!lib.exists()) { + Log.w("MainActivity", "Not found: " + libName + " at " + lib.getAbsolutePath()); + continue; + } + Log.i("MainActivity", "Loading: " + libName); System.load(lib.getAbsolutePath()); - android.util.Log.i("MainActivity", "Successfully loaded: " + libName); - } catch (Throwable e) { - android.util.Log.e("MainActivity", "Failed to load " + libName + ": " + e.getMessage()); - e.printStackTrace(); } - } else { - android.util.Log.w("MainActivity", "Not found: " + libName + " at " + lib.getAbsolutePath()); + Log.i("MainActivity", "Successfully loaded: " + libName); + } catch (Throwable e) { + Log.e("MainActivity", "Failed to load " + libName + ": " + e.getMessage()); } } - ElfLoader loader = new ElfLoader(libPath + ":/system/lib64"); + + ElfLoader loader = new ElfLoader(elfLibPath + ":/system/lib64"); loader.loadLib("libBootloader.so"); System.loadLibrary("ciphered"); @@ -197,9 +207,10 @@ private void loadGame() { MainActivity.customServer(BuildConfig.SKY_SERVER_HOSTNAME); } - new ElfRefcountLoader(libPath + ":/system/lib64", modsDir).load(); + new ElfRefcountLoader(elfLibPath + ":/system/lib64", modsDir).load(); BuildConfig.APPLICATION_ID = SKY_PACKAGE_NAME; startActivity(new Intent(this, GameActivity.class)); + } catch (PackageManager.NameNotFoundException e) { alertDialog(getString(R.string.sky_not_installed)); } catch (Throwable e) { @@ -207,27 +218,137 @@ private void loadGame() { } } + private String resolveLibPath(ApplicationInfo appInfo) throws IOException { + File libDir = new File(appInfo.nativeLibraryDir); + if (libDir.exists() && libDir.listFiles() != null && libDir.listFiles().length > 0) { + Log.i("MainActivity", "Using nativeLibraryDir: " + appInfo.nativeLibraryDir); + return appInfo.nativeLibraryDir; + } + + String apkDirectPath = tryGetApkDirectPath(appInfo); + if (apkDirectPath != null) { + Log.i("MainActivity", "Using direct APK path: " + apkDirectPath); + return apkDirectPath; + } + + Log.w("MainActivity", "Libs are compressed, falling back to extraction"); + return extractLibrariesFromApk(appInfo); + } + + private String tryGetApkDirectPath(ApplicationInfo appInfo) { + String[] splitDirs = appInfo.splitSourceDirs; + if (splitDirs != null) { + for (String splitApk : splitDirs) { + if (splitApk.contains("arm64_v8a") || splitApk.contains("config.arm64") || splitApk.contains("arm64")) { + if (areLibsStoredUncompressed(splitApk)) { + Log.i("MainActivity", "Found stored libs in split APK: " + splitApk); + return splitApk + "!/lib/arm64-v8a"; + } + break; + } + } + } + if (areLibsStoredUncompressed(appInfo.sourceDir)) { + Log.i("MainActivity", "Found stored libs in base APK: " + appInfo.sourceDir); + return appInfo.sourceDir + "!/lib/arm64-v8a"; + } + return null; + } + + private boolean areLibsStoredUncompressed(String apkPath) { + try (java.util.zip.ZipFile zipFile = new java.util.zip.ZipFile(apkPath)) { + java.util.Enumeration entries = zipFile.entries(); + while (entries.hasMoreElements()) { + java.util.zip.ZipEntry entry = entries.nextElement(); + if (entry.getName().startsWith("lib/arm64-v8a/") && entry.getName().endsWith(".so")) { + boolean stored = entry.getMethod() == java.util.zip.ZipEntry.STORED; + Log.d("MainActivity", entry.getName() + " compression=" + (stored ? "STORED" : "DEFLATED")); + return stored; + } + } + } catch (IOException e) { + Log.e("MainActivity", "areLibsStoredUncompressed failed for " + apkPath + ": " + e.getMessage()); + } + return false; + } + + private String resolveElfLibPath(ApplicationInfo appInfo, String mainLibPath) throws IOException { + if (!mainLibPath.contains("!/lib")) { + return mainLibPath; + } + + File extractDir = new File(getFilesDir(), "extracted_libs"); + if (!extractDir.exists() && !extractDir.mkdirs()) { + throw new IOException("Failed to create extraction directory"); + } + + File bootloaderFile = new File(extractDir, "libBootloader.so"); + + boolean useCustomBootloader = sharedPreferences.getBoolean("use_custom_bootloader", false); + if (useCustomBootloader && bootloaderFile.exists()) { + Log.d("MainActivity", "Custom libBootloader.so enabled, skip extraction"); + return extractDir.getAbsolutePath(); + } + + int lastExtractedVersion = sharedPreferences.getInt("bootloader_version", -1); + int currentVersion; + try { + currentVersion = getPackageManager().getPackageInfo(SKY_PACKAGE_NAME, 0).versionCode; + } catch (PackageManager.NameNotFoundException e) { + throw new IOException("Sky package not found: " + e.getMessage()); + } + + if (bootloaderFile.exists() && lastExtractedVersion == currentVersion) { + Log.d("MainActivity", "libBootloader.so already updated (v" + currentVersion + "), skip extraction"); + return extractDir.getAbsolutePath(); + } + + if (bootloaderFile.exists()) { + Log.i("MainActivity", "Sky updated (v" + lastExtractedVersion + " → v" + currentVersion + "), re-extraction of libBootloader.so"); + bootloaderFile.delete(); + } + + String apkPath = mainLibPath.substring(0, mainLibPath.indexOf("!/lib")); + Log.i("MainActivity", "Extracting libBootloader.so from: " + apkPath); + try (java.util.zip.ZipFile zipFile = new java.util.zip.ZipFile(apkPath)) { + java.util.zip.ZipEntry entry = zipFile.getEntry("lib/arm64-v8a/libBootloader.so"); + if (entry == null) throw new IOException("libBootloader.so not found in APK"); + try (java.io.InputStream in = zipFile.getInputStream(entry); + java.io.FileOutputStream out = new java.io.FileOutputStream(bootloaderFile)) { + byte[] buf = new byte[8192]; + int read; + while ((read = in.read(buf)) != -1) out.write(buf, 0, read); + } + bootloaderFile.setExecutable(true); + bootloaderFile.setReadable(true); + Log.i("MainActivity", "libBootloader.so extracted (v" + currentVersion + ")"); + } + + sharedPreferences.edit().putInt("bootloader_version", currentVersion).apply(); + return extractDir.getAbsolutePath(); + } + private String extractLibrariesFromApk(ApplicationInfo appInfo) throws IOException { File extractDir = new File(getFilesDir(), "extracted_libs"); if (!extractDir.exists() && !extractDir.mkdirs()) { throw new IOException("Failed to create extraction directory"); } - android.util.Log.i("MainActivity", "Looking for split APKs..."); - android.util.Log.i("MainActivity", "sourceDir: " + appInfo.sourceDir); + Log.i("MainActivity", "Looking for split APKs..."); + Log.i("MainActivity", "sourceDir: " + appInfo.sourceDir); String[] splitSourceDirs = appInfo.splitSourceDirs; if (splitSourceDirs != null) { - android.util.Log.i("MainActivity", "Found " + splitSourceDirs.length + " split APKs"); + Log.i("MainActivity", "Found " + splitSourceDirs.length + " split APKs"); for (String splitApk : splitSourceDirs) { - android.util.Log.i("MainActivity", "Checking split APK: " + splitApk); + Log.i("MainActivity", "Checking split APK: " + splitApk); if (splitApk.contains("arm64_v8a") || splitApk.contains("config.arm64") || splitApk.contains("arm64")) { - android.util.Log.i("MainActivity", "Found ARM64 split APK: " + splitApk); + Log.i("MainActivity", "Found ARM64 split APK: " + splitApk); extractLibsFromZip(splitApk, extractDir); break; } } } else { - android.util.Log.w("MainActivity", "No split APKs found, trying base APK"); + Log.w("MainActivity", "No split APKs found, trying base APK"); extractLibsFromZip(appInfo.sourceDir, extractDir); } @@ -235,7 +356,7 @@ private String extractLibrariesFromApk(ApplicationInfo appInfo) throws IOExcepti } private void extractLibsFromZip(String apkPath, File destDir) throws IOException { - android.util.Log.i("MainActivity", "Extracting libs from: " + apkPath); + Log.i("MainActivity", "Extracting libs from: " + apkPath); java.util.zip.ZipFile zipFile = new java.util.zip.ZipFile(apkPath); java.util.Enumeration entries = zipFile.entries(); int libCount = 0; @@ -257,14 +378,14 @@ private void extractLibsFromZip(String apkPath, File destDir) throws IOException destFile.setExecutable(true); destFile.setReadable(true); libCount++; - android.util.Log.i("MainActivity", "Extracted: " + libName); + Log.i("MainActivity", "Extracted: " + libName); } else { - android.util.Log.d("MainActivity", "Already exists: " + libName); + Log.d("MainActivity", "Already exists: " + libName); } } } zipFile.close(); - android.util.Log.i("MainActivity", "Extracted " + libCount + " libraries"); + Log.i("MainActivity", "Extracted " + libCount + " libraries"); } @Override @@ -322,9 +443,8 @@ public DeviceInfo getDeviceInfo() { if (deviceInfo.deviceName == null || deviceInfo.deviceName.isEmpty()) { deviceInfo.deviceName = Settings.Secure.getString(getContentResolver(), "bluetooth_name"); } - - deviceInfo.deviceName = (deviceInfo.deviceName == null || deviceInfo.deviceName.isEmpty()) ? "NO_DEVICE_NAME" - : deviceInfo.deviceName; + deviceInfo.deviceName = (deviceInfo.deviceName == null || deviceInfo.deviceName.isEmpty()) + ? "NO_DEVICE_NAME" : deviceInfo.deviceName; deviceInfo.deviceManufacturer = Build.MANUFACTURER; deviceInfo.deviceModel = Build.MODEL; return deviceInfo; @@ -354,4 +474,4 @@ public static native void setDeviceInfoNative( public static native void lateInitUserLibs(); public static native void getSysetemUI(Object systemUI); private static native void nativeSetSkyBuildKey(String key); -} \ No newline at end of file +} diff --git a/app/src/main/java/git/artdeell/skymodloader/SettingsActivity.java b/app/src/main/java/git/artdeell/skymodloader/SettingsActivity.java index ef2ba52..619106b 100644 --- a/app/src/main/java/git/artdeell/skymodloader/SettingsActivity.java +++ b/app/src/main/java/git/artdeell/skymodloader/SettingsActivity.java @@ -1,24 +1,30 @@ package git.artdeell.skymodloader; -import android.app.AlertDialog; +import android.app.Activity; +import android.app.Dialog; import android.content.Intent; +import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; import android.util.Log; -import android.widget.Button; +import android.view.Window; import android.widget.EditText; import android.widget.ImageView; import android.widget.Switch; +import android.widget.TextView; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import java.io.File; +import java.io.IOException; +import java.io.InputStream; public class SettingsActivity extends AppCompatActivity { private static final String TAG = "ClearAppData"; + private static final int REQUEST_PICK_BOOTLOADER = 9001; private Switch hideCanvasMenuSwitch; private Switch ceserverSwitch; private Switch customServerSwitch; @@ -36,7 +42,6 @@ protected void onCreate(Bundle savedInstanceState) { customServerSwitch = findViewById(R.id.mm_enableCustomServer); logcatSwitch = findViewById(R.id.mm_enableLogcat); serverUrlInput = findViewById(R.id.server_url_input); - Button btnClearAppData = findViewById(R.id.btn_clear_app_data); backButton.setOnClickListener(v -> finish()); @@ -64,12 +69,8 @@ protected void onCreate(Bundle savedInstanceState) { serverUrlInput.setText(getSharedPreferences("package_configs", MODE_PRIVATE) .getString("server_host", "")); serverUrlInput.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) {} - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) {} - + @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + @Override public void onTextChanged(CharSequence s, int start, int before, int count) {} @Override public void afterTextChanged(Editable s) { getSharedPreferences("package_configs", MODE_PRIVATE) @@ -82,7 +83,6 @@ public void afterTextChanged(Editable s) { logcatSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { getSharedPreferences("package_configs", MODE_PRIVATE) .edit().putBoolean("logcat_enabled", isChecked).apply(); - Intent logcatIntent = new Intent(this, LogcatMonitorService.class); if (isChecked) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { @@ -97,68 +97,177 @@ public void afterTextChanged(Editable s) { } }); - btnClearAppData.setOnClickListener(v -> clearAppDataComplete()); + findViewById(R.id.btn_clear_app_data).setOnClickListener(v -> showClearDataDialog()); + findViewById(R.id.btn_custom_bootloader).setOnClickListener(v -> showBootloaderDialog()); } - private void clearAppDataComplete() { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle("⚠️ Clear App Data"); - builder.setMessage("Delete all game data?\n\n✅ PRESERVED:\n• mods/\n• Accounts/\n• config/\n• AccountAuthInfo.bin\n\n❌ DELETED:\n• Everything else"); - builder.setPositiveButton("Clear", (dialog, which) -> { - Toast.makeText(this, "Clearing data...", Toast.LENGTH_SHORT).show(); - new Thread(() -> { - int deletedFiles = 0; - int deletedDirs = 0; - try { - Log.i(TAG, "Starting Complete Clear App Data"); - - File externalFilesDir = getExternalFilesDir(null); - if (externalFilesDir != null) { - File externalDataRoot = externalFilesDir.getParentFile(); - if (externalDataRoot != null && externalDataRoot.exists()) { - Log.i(TAG, "Clearing external: " + externalDataRoot.getAbsolutePath()); - int[] counts = deleteRecursiveCount(externalDataRoot); - deletedFiles += counts[0]; - deletedDirs += counts[1]; - Log.i(TAG, "External cleared: " + counts[0] + " files, " + counts[1] + " dirs"); - } - } + private void showBootloaderDialog() { + File current = new File(getFilesDir(), "extracted_libs/libBootloader.so"); + boolean isCustomActive = getSharedPreferences("package_configs", MODE_PRIVATE) + .getBoolean("use_custom_bootloader", false); + String status = (isCustomActive && current.exists()) + ? "Custom enabled · " + current.length() / 1024 + " KB" + : "Original version extracted from Sky's APK"; + + Dialog dialog = new Dialog(this); + dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + dialog.setContentView(R.layout.dialog_custom_bootloader); + dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent); + dialog.getWindow().setLayout( + (int) (getResources().getDisplayMetrics().widthPixels * 0.88), + android.view.ViewGroup.LayoutParams.WRAP_CONTENT + ); + + ((TextView) dialog.findViewById(R.id.dialog_bootloader_status)).setText(status); + + dialog.findViewById(R.id.dialog_bootloader_cancel) + .setOnClickListener(v -> dialog.dismiss()); + + dialog.findViewById(R.id.dialog_bootloader_reset) + .setOnClickListener(v -> { + dialog.dismiss(); + resetBootloaderToOriginal(); + }); + + dialog.findViewById(R.id.dialog_bootloader_pick) + .setOnClickListener(v -> { + dialog.dismiss(); + Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); + intent.setType("application/octet-stream"); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.putExtra("android.content.extra.SHOW_ADVANCED", true); + startActivityForResult(intent, REQUEST_PICK_BOOTLOADER); + }); + + dialog.show(); + } + + private void showClearDataDialog() { + Dialog dialog = new Dialog(this); + dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + dialog.setContentView(R.layout.dialog_clear_data); + dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent); + dialog.getWindow().setLayout( + (int) (getResources().getDisplayMetrics().widthPixels * 0.88), + android.view.ViewGroup.LayoutParams.WRAP_CONTENT + ); + + dialog.findViewById(R.id.dialog_clear_cancel) + .setOnClickListener(v -> dialog.dismiss()); + + dialog.findViewById(R.id.dialog_clear_confirm) + .setOnClickListener(v -> { + dialog.dismiss(); + executeClearAppData(); + }); + + dialog.show(); + } + + private void resetBootloaderToOriginal() { + File extractedLib = new File(getFilesDir(), "extracted_libs/libBootloader.so"); + if (extractedLib.exists()) extractedLib.delete(); + getSharedPreferences("package_configs", MODE_PRIVATE).edit() + .putBoolean("use_custom_bootloader", false) + .putInt("bootloader_version", -1) + .apply(); + Toast.makeText(this, + "Reset applied. Original version will be extracted at the next launch.", + Toast.LENGTH_LONG).show(); + } + + private void copyBootloaderFromUri(Uri uri) { + new Thread(() -> { + try { + File extractDir = new File(getFilesDir(), "extracted_libs"); + if (!extractDir.exists()) extractDir.mkdirs(); + File dest = new File(extractDir, "libBootloader.so"); + + try (InputStream in = getContentResolver().openInputStream(uri); + java.io.FileOutputStream out = new java.io.FileOutputStream(dest)) { + if (in == null) throw new IOException("Impossible to load the selected file"); + byte[] buf = new byte[8192]; + int read; + while ((read = in.read(buf)) != -1) out.write(buf, 0, read); + } + + dest.setExecutable(true, false); + dest.setReadable(true, false); + + getSharedPreferences("package_configs", MODE_PRIVATE) + .edit().putBoolean("use_custom_bootloader", true).apply(); + + Log.i(TAG, "Custom libBootloader.so copied: " + dest.length() / 1024 + " KB"); + runOnUiThread(() -> Toast.makeText(this, + "✓ Custom libBootloader.so applied!", + Toast.LENGTH_LONG).show()); + + } catch (Exception e) { + Log.e(TAG, "Copie failed: " + e.getMessage()); + runOnUiThread(() -> Toast.makeText(this, + "Error: " + e.getMessage(), Toast.LENGTH_LONG).show()); + } + }).start(); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == REQUEST_PICK_BOOTLOADER && resultCode == Activity.RESULT_OK && data != null) { + Uri uri = data.getData(); + if (uri != null) copyBootloaderFromUri(uri); + } + } + + private void executeClearAppData() { + Toast.makeText(this, "Clearing data...", Toast.LENGTH_SHORT).show(); + new Thread(() -> { + int deletedFiles = 0; + int deletedDirs = 0; + try { + Log.i(TAG, "Starting Complete Clear App Data"); - File internalDataRoot = getFilesDir().getParentFile(); - if (internalDataRoot != null && internalDataRoot.exists()) { - Log.i(TAG, "Processing root: " + internalDataRoot.getAbsolutePath()); - int[] rootCounts = clearInternalDataRoot(internalDataRoot); - deletedFiles += rootCounts[0]; - deletedDirs += rootCounts[1]; - Log.i(TAG, "Internal root cleared: " + rootCounts[0] + " files, " + rootCounts[1] + " dirs"); + File externalFilesDir = getExternalFilesDir(null); + if (externalFilesDir != null) { + File externalDataRoot = externalFilesDir.getParentFile(); + if (externalDataRoot != null && externalDataRoot.exists()) { + int[] counts = deleteRecursiveCount(externalDataRoot); + deletedFiles += counts[0]; + deletedDirs += counts[1]; } + } - final int totalFiles = deletedFiles; - final int totalDirs = deletedDirs; - Log.i(TAG, "TOTAL DELETED: " + totalFiles + " files, " + totalDirs + " dirs"); - - runOnUiThread(() -> { - String message = "Cleared " + totalFiles + " files, " + totalDirs + " dirs\nRestarting..."; - Toast.makeText(this, message, Toast.LENGTH_LONG).show(); - new android.os.Handler().postDelayed(() -> { - Intent intent = getPackageManager().getLaunchIntentForPackage(getPackageName()); - if (intent != null) { - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(intent); - } - System.exit(0); - }, 1500); - }); - } catch (Exception e) { - Log.e(TAG, "Error clearing data", e); - runOnUiThread(() -> - Toast.makeText(this, "Error: " + e.getMessage(), Toast.LENGTH_LONG).show() - ); + File internalDataRoot = getFilesDir().getParentFile(); + if (internalDataRoot != null && internalDataRoot.exists()) { + int[] rootCounts = clearInternalDataRoot(internalDataRoot); + deletedFiles += rootCounts[0]; + deletedDirs += rootCounts[1]; } - }).start(); - }); - builder.setNegativeButton("Cancel", null); - builder.show(); + + final int totalFiles = deletedFiles; + final int totalDirs = deletedDirs; + Log.i(TAG, "TOTAL DELETED: " + totalFiles + " files, " + totalDirs + " dirs"); + + runOnUiThread(() -> { + Toast.makeText(this, + "Cleared " + totalFiles + " files, " + totalDirs + " dirs\nRestarting...", + Toast.LENGTH_LONG).show(); + new android.os.Handler().postDelayed(() -> { + Intent intent = getPackageManager().getLaunchIntentForPackage(getPackageName()); + if (intent != null) { + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); + } + System.exit(0); + }, 1500); + }); + } catch (Exception e) { + Log.e(TAG, "Error clearing data", e); + runOnUiThread(() -> + Toast.makeText(this, "Error: " + e.getMessage(), Toast.LENGTH_LONG).show() + ); + } + }).start(); } private int[] clearInternalDataRoot(File dataRoot) { @@ -166,20 +275,50 @@ private int[] clearInternalDataRoot(File dataRoot) { int dirCount = 0; File[] contents = dataRoot.listFiles(); if (contents == null) return new int[]{0, 0}; - for (File item : contents) { String name = item.getName(); if (name.equals("files")) { - Log.i(TAG, "Processing files/ directory selectively..."); - int[] filesCounts = clearFilesDirectory(item); - fileCount += filesCounts[0]; - dirCount += filesCounts[1]; + int[] counts = clearFilesDirectory(item); + fileCount += counts[0]; + dirCount += counts[1]; + } else if (name.equals("shared_prefs")) { + int[] counts = clearSharedPrefsDirectory(item); + fileCount += counts[0]; + dirCount += counts[1]; } else { - Log.i(TAG, "Deleting ROOT item: " + name); int[] counts = deleteRecursiveCount(item); fileCount += counts[0]; dirCount += counts[1]; - Log.i(TAG, "❌ DELETED ROOT: " + name + " (" + counts[0] + " files, " + counts[1] + " dirs)"); + } + } + return new int[]{fileCount, dirCount}; + } + + private int[] clearSharedPrefsDirectory(File sharedPrefsDir) { + int fileCount = 0; + int dirCount = 0; + if (!sharedPrefsDir.exists() || !sharedPrefsDir.isDirectory()) return new int[]{0, 0}; + + File[] contents = sharedPrefsDir.listFiles(); + if (contents == null) return new int[]{0, 0}; + + for (File item : contents) { + if (item.getName().equals("user.xml")) { + Log.i(TAG, "✅ PRESERVED PREF: user.xml"); + continue; + } + String prefName = item.getName().replace(".xml", ""); + getSharedPreferences(prefName, MODE_PRIVATE).edit().clear().commit(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + if (deleteSharedPreferences(prefName)) { + fileCount++; + Log.i(TAG, "❌ DELETED PREF: " + item.getName()); + } + } else { + if (item.delete()) { + fileCount++; + Log.i(TAG, "❌ DELETED PREF (legacy): " + item.getName()); + } } } return new int[]{fileCount, dirCount}; @@ -198,38 +337,22 @@ private int[] clearFilesDirectory(File filesDir) { for (File item : contents) { boolean shouldPreserve = false; String itemName = item.getName(); - if (item.isDirectory()) { - for (String preserved : preservedDirs) { - if (itemName.equals(preserved)) { - shouldPreserve = true; - Log.i(TAG, "✅ PRESERVED DIR: files/" + itemName + "/"); - break; - } + for (String p : preservedDirs) { + if (itemName.equals(p)) { shouldPreserve = true; break; } } } else if (item.isFile()) { - for (String preserved : preservedFiles) { - if (itemName.equals(preserved)) { - shouldPreserve = true; - Log.i(TAG, "✅ PRESERVED FILE: files/" + itemName); - break; - } + for (String p : preservedFiles) { + if (itemName.equals(p)) { shouldPreserve = true; break; } } } - if (!shouldPreserve) { if (item.isFile()) { - if (item.delete()) { - fileCount++; - Log.i(TAG, "❌ DELETED FILE: files/" + itemName); - } else { - Log.w(TAG, "Failed to delete file: files/" + itemName); - } + if (item.delete()) fileCount++; } else if (item.isDirectory()) { int[] counts = deleteRecursiveCount(item); fileCount += counts[0]; dirCount += counts[1]; - Log.i(TAG, "❌ DELETED DIR: files/" + itemName + " (" + counts[0] + " files, " + counts[1] + " dirs)"); } } } @@ -239,27 +362,18 @@ private int[] clearFilesDirectory(File filesDir) { private int[] deleteRecursiveCount(File fileOrDirectory) { int fileCount = 0; int dirCount = 0; - if (fileOrDirectory.isDirectory()) { File[] children = fileOrDirectory.listFiles(); if (children != null) { for (File child : children) { - int[] childCounts = deleteRecursiveCount(child); - fileCount += childCounts[0]; - dirCount += childCounts[1]; + int[] counts = deleteRecursiveCount(child); + fileCount += counts[0]; + dirCount += counts[1]; } } - if (fileOrDirectory.delete()) { - dirCount++; - } else { - Log.w(TAG, "Failed to delete dir: " + fileOrDirectory.getAbsolutePath()); - } + if (fileOrDirectory.delete()) dirCount++; } else { - if (fileOrDirectory.delete()) { - fileCount++; - } else { - Log.w(TAG, "Failed to delete file: " + fileOrDirectory.getAbsolutePath()); - } + if (fileOrDirectory.delete()) fileCount++; } return new int[]{fileCount, dirCount}; } diff --git a/app/src/main/java/git/artdeell/skymodloader/elfmod/ModManagerActivity.java b/app/src/main/java/git/artdeell/skymodloader/elfmod/ModManagerActivity.java index 3910e8e..81dbddd 100644 --- a/app/src/main/java/git/artdeell/skymodloader/elfmod/ModManagerActivity.java +++ b/app/src/main/java/git/artdeell/skymodloader/elfmod/ModManagerActivity.java @@ -7,17 +7,11 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; -import android.net.Uri; import android.os.Build; import android.os.Bundle; -import android.text.SpannableString; -import android.text.Spanned; -import android.text.method.LinkMovementMethod; -import android.text.style.URLSpan; import android.util.Log; import android.view.View; import android.widget.Button; -import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; @@ -25,8 +19,6 @@ import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import com.google.android.material.switchmaterial.SwitchMaterial; - import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -34,6 +26,7 @@ import java.util.ArrayList; import java.util.Objects; +import git.artdeell.skymodloader.AboutDialogHelper; import git.artdeell.skymodloader.BuildConfig; import git.artdeell.skymodloader.DialogY; import git.artdeell.skymodloader.LogcatMonitorService; @@ -96,7 +89,6 @@ public void startModUpdater(ElfModUIMetadata metadata) { Toast.makeText(this, R.string.updater_busy, Toast.LENGTH_SHORT).show(); return; } - Intent serviceStartIntent = new Intent(this, ModUpdaterService.class); serviceStartIntent.putExtra(ModUpdaterService.EXTRA_UPDATE_URL, metadata.getGithubReleasesUrl()); serviceStartIntent.putExtra(ModUpdaterService.EXTRA_LIB_NAME, metadata.name); @@ -121,7 +113,6 @@ private void updateButtonTextColor() { skyPackageName = "com.tgc.sky.android"; sharedPreferences.edit().putString("sky_package_name", skyPackageName).apply(); } - setButtonTextColor(btnLaunchLive, skyPackages.get(0)); setButtonTextColor(btnLaunchChplay, skyPackages.get(1)); setButtonTextColor(btnLaunchHuawei, skyPackages.get(2)); @@ -235,48 +226,7 @@ public void onAddMod(View v) { } public void onModInfo(View v) { - SpannableString message = new SpannableString( - "How to add a mod:\n\n" + - "① Download a compatible .so file\n\n" + - "② Tap on \"Add Mod\" and select the file\n\n" + - "③ You can activate or disable the mod with the toggle\n\n" + - "④ Start the game\n\n" + - "⚠︎ Sometimes mods are broken/need to be updated!\n\n" + - "───────────────────\n\n" + - "Community\n\n" + - "Discord | Telegram" - ); - - String full = message.toString(); - - int discordStart = full.indexOf("Discord"); - int discordEnd = discordStart + "Discord".length(); - int telegramStart = full.indexOf("Telegram"); - int telegramEnd = telegramStart + "Telegram".length(); - - message.setSpan(new URLSpan("https://discord.gg/ekpUFWcCFN") { - @Override - public void onClick(View widget) { - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://discord.gg/ekpUFWcCFN"))); - } - }, discordStart, discordEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - - message.setSpan(new URLSpan("https://t.me/skyautowax") { - @Override - public void onClick(View widget) { - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://t.me/skyautowax"))); - } - }, telegramStart, telegramEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - - AlertDialog dialog = new AlertDialog.Builder(this) - .setTitle("Info & Community") - .setMessage(message) - .setPositiveButton("OK", null) - .create(); - - dialog.show(); - ((TextView) dialog.findViewById(android.R.id.message)) - .setMovementMethod(LinkMovementMethod.getInstance()); + AboutDialogHelper.show(this); } @Override @@ -315,17 +265,10 @@ public void refreshModList(int mode, int which) { ModListAdapter adapter = (ModListAdapter) modListView.getAdapter(); if (adapter != null) { switch (mode) { - case 0: - adapter.notifyItemRemoved(which); - break; - case 1: - adapter.notifyItemInserted(which); - break; - case 2: - adapter.notifyItemChanged(which); - break; - case 3: - adapter.notifyDataSetChanged(); + case 0: adapter.notifyItemRemoved(which); break; + case 1: adapter.notifyItemInserted(which); break; + case 2: adapter.notifyItemChanged(which); break; + case 3: adapter.notifyDataSetChanged(); } } else modListView.setAdapter(new ModListAdapter(loader)); }); @@ -348,13 +291,13 @@ public void signalModAddException() { @Override public void signalModRemovalError() { - runOnUiThread(() -> { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(R.string.mod_remove_unable); - builder.setMessage(R.string.mod_ioe); - builder.setPositiveButton(android.R.string.ok, (d, w) -> {}); - builder.show(); - }); + runOnUiThread(() -> + new AlertDialog.Builder(this) + .setTitle(R.string.mod_remove_unable) + .setMessage(R.string.mod_ioe) + .setPositiveButton(android.R.string.ok, (d, w) -> {}) + .show() + ); } private void handleException() { @@ -363,12 +306,12 @@ private void handleException() { String message; if (e instanceof NoDependenciesException) { NoDependenciesException exc = (NoDependenciesException) e; - StringBuilder stringBuilder = new StringBuilder(); + StringBuilder sb = new StringBuilder(); for (ElfModMetadata meta : exc.failedDependencies) { - stringBuilder.append(getString(R.string.mod_add_missingdep, meta.name, meta.majorVersion, meta.minorVersion)); - stringBuilder.append('\n'); + sb.append(getString(R.string.mod_add_missingdep, meta.name, meta.majorVersion, meta.minorVersion)); + sb.append('\n'); } - message = stringBuilder.toString(); + message = sb.toString(); } else if (e instanceof InvalidModException) { message = getString(R.string.mod_add_wrongformat); } else if (e instanceof IOException) { @@ -384,7 +327,7 @@ private void handleException() { dialogY.positiveButton.setVisibility(View.GONE); dialogY.title.setText(R.string.mod_add_unable); dialogY.content.setText(message); - dialogY.negativeButton.setOnClickListener((view) -> dialogY.dialog.dismiss()); + dialogY.negativeButton.setOnClickListener(view -> dialogY.dialog.dismiss()); dialogY.dialog.setCancelable(true); dialogY.dialog.show(); } @@ -397,13 +340,12 @@ private void handleUnsafeModRemoval() { sb.append(getString(R.string.mod_remove_dep, ModListAdapter.getVisibleModName(meta))); sb.append('\n'); } - - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(R.string.mod_remove_unable); - builder.setMessage(sb.toString()); - builder.setPositiveButton(android.R.string.ok, (d, w) -> loader.resetModRemovalMetadata()); - builder.setOnCancelListener((d) -> loader.resetModRemovalMetadata()); - builder.show(); + new AlertDialog.Builder(this) + .setTitle(R.string.mod_remove_unable) + .setMessage(sb.toString()) + .setPositiveButton(android.R.string.ok, (d, w) -> loader.resetModRemovalMetadata()) + .setOnCancelListener(d -> loader.resetModRemovalMetadata()) + .show(); } private void handleLoading() { @@ -416,9 +358,8 @@ private void handleLoading() { } public boolean findPackage(String packageName) { - PackageManager packageManager = getPackageManager(); try { - packageManager.getPackageInfo(packageName, PackageManager.GET_SHARED_LIBRARY_FILES); + getPackageManager().getPackageInfo(packageName, PackageManager.GET_SHARED_LIBRARY_FILES); return true; } catch (PackageManager.NameNotFoundException e) { return false; @@ -439,13 +380,15 @@ public void launchGame() { } public void onExtraSettingsDialog(View view) { - Intent intent = new Intent(this, SettingsActivity.class); - startActivity(intent); + startActivity(new Intent(this, SettingsActivity.class)); } public void runUpdater() { - Intent updaterService = new Intent(this, CanvasUpdaterService.class); - bindService(updaterService, new CanvasUpdaterConnection(this), BIND_AUTO_CREATE); + bindService( + new Intent(this, CanvasUpdaterService.class), + new CanvasUpdaterConnection(this), + BIND_AUTO_CREATE + ); } public void onClearAppData(View view) { @@ -464,60 +407,27 @@ private void clearAppDataSelective() { .setPositiveButton("Clear", (dialog, which) -> { new Thread(() -> { try { - String packageName = getPackageName(); - File dataDir = new File("/data/data/" + packageName); File filesDir = getFilesDir(); - File externalDataDir = new File("/sdcard/Android/data/" + packageName); - - Log.i("ClearData", "Starting selective clear for package: " + packageName); + File externalDataDir = new File("/sdcard/Android/data/" + getPackageName()); - clearDirectorySelective(filesDir, new String[]{"mods", "Accounts", "config"}, new String[]{"AccountAuthInfo.bin"}); + clearDirectorySelective(filesDir, + new String[]{"mods", "Accounts", "config"}, + new String[]{"AccountAuthInfo.bin"}); File cacheDir = getCacheDir(); - if (cacheDir != null && cacheDir.exists()) { - deleteRecursive(cacheDir); - Log.i("ClearData", "Cache cleared"); - } + if (cacheDir != null && cacheDir.exists()) deleteRecursive(cacheDir); File codeCacheDir = getCodeCacheDir(); - if (codeCacheDir != null && codeCacheDir.exists()) { - deleteRecursive(codeCacheDir); - Log.i("ClearData", "Code cache cleared"); - } - - File extractedLibs = new File(filesDir, "extracted_libs"); - if (extractedLibs.exists()) { - deleteRecursive(extractedLibs); - Log.i("ClearData", "Extracted libs cleared"); - } - - File logsDir = new File(externalDataDir, "files/logs"); - if (logsDir.exists()) { - deleteRecursive(logsDir); - Log.i("ClearData", "Logs cleared"); - } + if (codeCacheDir != null && codeCacheDir.exists()) deleteRecursive(codeCacheDir); if (externalDataDir.exists()) { - clearDirectorySelective(externalDataDir, new String[]{"mods", "Accounts", "config", "configs"}, new String[]{}); - Log.i("ClearData", "External data cleared (selective)"); + clearDirectorySelective(externalDataDir, + new String[]{"mods", "Accounts", "config", "configs"}, + new String[]{}); } - File sharedPrefsDir = new File(dataDir, "shared_prefs"); - if (sharedPrefsDir.exists()) { - File[] prefFiles = sharedPrefsDir.listFiles(); - if (prefFiles != null) { - for (File prefFile : prefFiles) { - if (!prefFile.getName().contains("package_configs")) { - prefFile.delete(); - Log.i("ClearData", "Deleted pref: " + prefFile.getName()); - } - } - } - } - - Log.i("ClearData", "Selective clear completed successfully"); runOnUiThread(() -> { - Toast.makeText(this, "Data cleared successfully. Restarting...", Toast.LENGTH_SHORT).show(); + Toast.makeText(this, "Data cleared. Restarting...", Toast.LENGTH_SHORT).show(); new android.os.Handler().postDelayed(() -> { Intent intent = getPackageManager().getLaunchIntentForPackage(getPackageName()); if (intent != null) { @@ -530,7 +440,7 @@ private void clearAppDataSelective() { } catch (Exception e) { Log.e("ClearData", "Error clearing data", e); runOnUiThread(() -> - Toast.makeText(this, "Error clearing data: " + e.getMessage(), Toast.LENGTH_LONG).show() + Toast.makeText(this, "Error: " + e.getMessage(), Toast.LENGTH_LONG).show() ); } }).start(); @@ -547,29 +457,16 @@ private void clearDirectorySelective(File dir, String[] preserveFolders, String[ boolean shouldPreserve = false; if (file.isDirectory()) { for (String folder : preserveFolders) { - if (file.getName().equals(folder)) { - shouldPreserve = true; - Log.i("ClearData", "Preserving folder: " + file.getName()); - break; - } + if (file.getName().equals(folder)) { shouldPreserve = true; break; } } - } - if (file.isFile()) { + } else if (file.isFile()) { for (String fileName : preserveFiles) { - if (file.getName().equals(fileName)) { - shouldPreserve = true; - Log.i("ClearData", "Preserving file: " + file.getName()); - break; - } + if (file.getName().equals(fileName)) { shouldPreserve = true; break; } } } if (!shouldPreserve) { - if (file.isDirectory()) { - deleteRecursive(file); - } else { - file.delete(); - } - Log.i("ClearData", "Deleted: " + file.getName()); + if (file.isDirectory()) deleteRecursive(file); + else file.delete(); } } } @@ -578,9 +475,7 @@ private void deleteRecursive(File fileOrDirectory) { if (fileOrDirectory.isDirectory()) { File[] children = fileOrDirectory.listFiles(); if (children != null) { - for (File child : children) { - deleteRecursive(child); - } + for (File child : children) deleteRecursive(child); } } fileOrDirectory.delete(); diff --git a/app/src/main/res/layout/about_dialog.xml b/app/src/main/res/layout/about_dialog.xml index 78d3bcf..cf0e39f 100644 --- a/app/src/main/res/layout/about_dialog.xml +++ b/app/src/main/res/layout/about_dialog.xml @@ -1,403 +1,113 @@ + android:layout_height="wrap_content"> + android:background="@drawable/buttons"> - - - - + android:orientation="vertical" + android:gravity="center" + android:paddingTop="36dp" + android:paddingBottom="28dp"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:layout_width="72dp" + android:layout_height="72dp" + android:background="@drawable/logo_bg" + android:layout_marginBottom="16dp"> + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:text="@string/app_name" + android:textSize="24sp" + android:textStyle="bold" + android:letterSpacing="0.02" + android:layout_marginBottom="4dp" /> + + - + - + - + - + - - - - + - - - - - + android:text="Sky is made by thatgamecompany" + android:textSize="11sp" + android:alpha="0.3" + android:padding="8dp" + android:clickable="true" + android:focusable="true" /> + + - - - - - - - + - \ No newline at end of file + diff --git a/app/src/main/res/layout/dialog_clear_data.xml b/app/src/main/res/layout/dialog_clear_data.xml new file mode 100644 index 0000000..9900788 --- /dev/null +++ b/app/src/main/res/layout/dialog_clear_data.xml @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/dialog_custom_bootloader.xml b/app/src/main/res/layout/dialog_custom_bootloader.xml new file mode 100644 index 0000000..16a89b3 --- /dev/null +++ b/app/src/main/res/layout/dialog_custom_bootloader.xml @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/setting_layout.xml b/app/src/main/res/layout/setting_layout.xml index bc630e9..0931400 100644 --- a/app/src/main/res/layout/setting_layout.xml +++ b/app/src/main/res/layout/setting_layout.xml @@ -211,35 +211,97 @@ android:letterSpacing="0.1" android:alpha="0.5" /> -