mirror of
https://github.com/2009scape/2009Scape-mobile.git
synced 2025-12-10 10:20:32 -07:00
Merge
This commit is contained in:
commit
21cfe0515a
110 changed files with 1490 additions and 780 deletions
26
README.md
26
README.md
|
|
@ -10,13 +10,13 @@
|
|||
---------
|
||||
* From [Boardwalk](https://github.com/zhuowei/Boardwalk)'s ashes here comes PojavLauncher!
|
||||
|
||||
* PojavLauncher is a launcher that allows you to play Minecraft:Java Edition on your Android device!
|
||||
* PojavLauncher is a launcher that allows you to play Minecraft: Java Edition on your Android device!
|
||||
|
||||
* It can run almost every version of the Minecraft, allowing you to use .jar only installers to install modloaders like [forge](https://files.minecraftforge.net/), [fabric](http://fabricmc.net/), [optifine](https://optifine.net); Utility and hack clients like [LabyMod](https://www.labymod.net/en), [Wurst](https://www.wurstclient.net/) and much more!
|
||||
|
||||
* For more details [check out our wiki](https://github.com/PojavLauncherTeam/PojavLauncher/wiki)
|
||||
## Some notes to start with
|
||||
- We do not exist on TikTok. No one from the dev team makes TikTok videos.
|
||||
- We do not have an official TikTok account. No one from the dev team makes TikTok videos.
|
||||
- The official Twitter for PojavLauncher is [@PLaunchTeam](https://twitter.com/PLaunchTeam). Any others (most notably @PojavLauncher) are fake, please report them to Twitter's moderation team.
|
||||
|
||||
## Navigation
|
||||
|
|
@ -37,21 +37,21 @@
|
|||
|
||||
## Getting PojavLauncher
|
||||
|
||||
Getting PojavLauncher is divided into 3 parts.
|
||||
You can get PojavLauncher via three methods:
|
||||
|
||||
1. You can get prebuilt app from [stable releases](https://github.com/PojavLauncherTeam/PojavLauncher/releases) or [automatic builds](https://github.com/PojavLauncherTeam/PojavLauncher/actions).
|
||||
1. You can get the prebuilt app from [stable releases](https://github.com/PojavLauncherTeam/PojavLauncher/releases) or [automatic builds](https://github.com/PojavLauncherTeam/PojavLauncher/actions).
|
||||
|
||||
2. You can get it from Google Play by clicking:
|
||||
2. You can get it from Google Play by clicking on this badge:
|
||||
[](https://play.google.com/store/apps/details?id=net.kdt.pojavlaunch)
|
||||
|
||||
3. You can [build](#building) from source.
|
||||
## Building
|
||||
If you want to build after launcher code changes, follow the steps below.
|
||||
If you want to build from source code, follow the steps below.
|
||||
### Java Runtime Environment (JRE)
|
||||
- JRE for Android is [here](https://github.com/PojavLauncherTeam/openjdk-multiarch-jdk8u), and the build script is [here](https://github.com/PojavLauncherTeam/android-openjdk-build-multiarch).
|
||||
- Follow build instruction on build script [README.md](https://github.com/PojavLauncherTeam/android-openjdk-build-multiarch/blob/buildjre8/README.md).
|
||||
- You can also get [CI auto builds](https://github.com/PojavLauncherTeam/android-openjdk-build-multiarch/actions) if you are lazy or failing it for some reason.
|
||||
* Either get `jre8-pojav` artifact from auto builds, or do splitting by yourself:</br>
|
||||
- You can also get [CI auto builds](https://github.com/PojavLauncherTeam/android-openjdk-build-multiarch/actions) if you are lazy or are failing to build it for some reason.
|
||||
* Either get the `jre8-pojav` artifact from auto builds, or split all artifacts by yourself:</br>
|
||||
- Get JREs for all of 4 supported architectures (arm, arm64, x86, x86_64) </br>
|
||||
- Split JRE into parts:</br>
|
||||
Platform-independent: .jar files, libraries, configs, etc...</br>
|
||||
|
|
@ -59,14 +59,14 @@ If you want to build after launcher code changes, follow the steps below.
|
|||
- Create:</br>
|
||||
A file named `universal.tar.xz` with all platform-independent files</br>
|
||||
4 files named `bin-<arch>.tar.xz` with all platform-dependent files per-architecture</br>
|
||||
- Put these in `assets/components/jre/` folder</br>
|
||||
- Put these in the `assets/components/jre/` folder</br>
|
||||
- (If needed) update the Version file with the current date</br>
|
||||
|
||||
### LWJGL
|
||||
- **Coming soon**
|
||||
|
||||
### The Launcher
|
||||
- Because languages are auto added by Crowdin, you need to run language list generator before building. In the project directory, run:
|
||||
- Because languages are auto-added by Crowdin, you need to run the language list generator before building. In the project directory, run:
|
||||
|
||||
* On Linux, Mac OS:
|
||||
```
|
||||
|
|
@ -87,13 +87,13 @@ Then, run these commands ~~or build using Android Studio~~.
|
|||
```
|
||||
./gradlew :app_pojavlauncher:assembleDebug
|
||||
```
|
||||
(Replace `gradlew` to `gradlew.bat` if you are building on Windows).
|
||||
(Replace `gradlew` with `gradlew.bat` if you are building on Windows).
|
||||
|
||||
## Current status
|
||||
- [x] ~~OpenJDK 9 Mobile port: ARM32, ARM64, x86, x86_64.~~ Replaced by JRE8.
|
||||
- [x] OpenJDK 8 Mobile port: ARM32, ARM64, x86, x86_64
|
||||
- [x] OpenJDK 17 Mobile port: ARM32, ARM64, x86, x86_64
|
||||
- [x] Mod installer headless
|
||||
- [x] Headless mod installer
|
||||
- [x] Mod installer with GUI. Used `Caciocavallo` project for AWT without X11.
|
||||
- [x] OpenGL in OpenJDK environment
|
||||
- [x] OpenAL (works on most devices)
|
||||
|
|
@ -107,7 +107,7 @@ Then, run these commands ~~or build using Android Studio~~.
|
|||
|
||||
## Known Issues
|
||||
- Controller mods aren't working.
|
||||
- Random crashes could happen very often on Android 5.x during game load or join world.
|
||||
- Random crashes could happen very often on Android 5.x when loading the game or joining a world.
|
||||
- With big modpacks textures could be messed up
|
||||
- Probably more, that's why we have a bug tracker ;)
|
||||
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ android {
|
|||
shrinkResources false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
signingConfig signingConfigs.customDebug
|
||||
resValue 'string', 'application_package', 'net.kdt.pojavlaunch.debug'
|
||||
resValue 'string', 'storageProviderAuthorities', 'net.kdt.pojavlaunch.scoped.gamefolder.debug'
|
||||
resValue 'string', 'shareProviderAuthority', 'net.kdt.pojavlaunch.scoped.controlfolder.debug'
|
||||
}
|
||||
|
|
@ -94,6 +95,7 @@ android {
|
|||
// multiDexEnabled = true
|
||||
// debuggable = true
|
||||
resValue 'string', 'storageProviderAuthorities', 'net.kdt.pojavlaunch.scoped.gamefolder'
|
||||
resValue 'string', 'application_package', 'net.kdt.pojavlaunch'
|
||||
}
|
||||
gplay {
|
||||
initWith release
|
||||
|
|
@ -133,8 +135,8 @@ dependencies {
|
|||
implementation "androidx.constraintlayout:constraintlayout:2.1.2"
|
||||
|
||||
implementation 'com.github.duanhong169:checkerboarddrawable:1.0.2'
|
||||
implementation 'com.intuit.sdp:sdp-android:1.1.0'
|
||||
implementation 'com.intuit.ssp:ssp-android:1.1.0'
|
||||
implementation 'com.github.PojavLauncherTeam:portrait-sdp:ed33e89cbc'
|
||||
implementation 'com.github.PojavLauncherTeam:portrait-ssp:6c02fd739b'
|
||||
implementation 'com.github.Mathias-Boulay:ExtendedView:1.0.0'
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,87 +3,106 @@
|
|||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="net.kdt.pojavlaunch">
|
||||
|
||||
<uses-feature android:glEsVersion="0x00020000"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="28"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-feature android:glEsVersion="0x00020000" />
|
||||
<uses-feature
|
||||
android:name="android.hardware.type.pc"
|
||||
android:required="false" />
|
||||
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="28" />
|
||||
<uses-permission
|
||||
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="28" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:name=".PojavApplication"
|
||||
android:theme="@style/AppTheme"
|
||||
android:label="@string/app_name"
|
||||
android:allowBackup="true"
|
||||
android:allowNativeHeapPointerTagging="false"
|
||||
android:appCategory="game"
|
||||
android:hasFragileUserData="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:isGame="true"
|
||||
android:label="@string/app_name"
|
||||
android:process=":launcher"
|
||||
android:resizeableActivity="true"
|
||||
android:hasFragileUserData="true"
|
||||
android:allowNativeHeapPointerTagging="false">
|
||||
|
||||
<meta-data
|
||||
android:name="android.max_aspect"
|
||||
android:value="ratio_float"/>
|
||||
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity
|
||||
android:name=".MissingStorageActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<meta-data
|
||||
android:name="android.max_aspect"
|
||||
android:value="ratio_float" />
|
||||
|
||||
<activity
|
||||
android:name=".TestStorageActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTop"
|
||||
android:label="@string/app_short_name"
|
||||
android:name=".LauncherActivity">
|
||||
android:launchMode="singleTop">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name=".ImportControlActivity"
|
||||
android:exported="true"
|
||||
android:windowSoftInputMode="stateVisible"
|
||||
android:launchMode="singleInstance"
|
||||
<activity
|
||||
android:name=".LauncherActivity"
|
||||
android:label="@string/app_short_name" />
|
||||
<activity
|
||||
android:name=".ImportControlActivity"
|
||||
android:configChanges="keyboard|keyboardHidden"
|
||||
>
|
||||
|
||||
<intent-filter android:scheme="content"
|
||||
android:exported="true"
|
||||
android:launchMode="singleInstance"
|
||||
android:windowSoftInputMode="stateVisible">
|
||||
<intent-filter
|
||||
android:label="@string/import_control_label"
|
||||
android:scheme="content"
|
||||
tools:ignore="AppLinkUrlError">
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<action android:name="android.intent.action.SEND"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<data android:mimeType="application/json"/>
|
||||
<data android:mimeType="text/json"/>
|
||||
<data android:mimeType="text/plain"/>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
<data android:mimeType="application/json" />
|
||||
<data android:mimeType="text/json" />
|
||||
<data android:mimeType="text/plain" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:theme="@style/Theme.AppCompat.DayNight.Dialog"
|
||||
android:screenOrientation="sensorLandscape"
|
||||
android:name=".FatalErrorActivity"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize|keyboard|navigation"/>
|
||||
android:configChanges="keyboardHidden|orientation|screenSize|keyboard|navigation"
|
||||
android:theme="@style/Theme.AppCompat.DayNight.Dialog" />
|
||||
<activity
|
||||
android:theme="@style/Theme.AppCompat.DayNight.Dialog"
|
||||
android:screenOrientation="sensorLandscape"
|
||||
android:name=".ExitActivity"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize|keyboard|navigation"/>
|
||||
|
||||
android:configChanges="keyboardHidden|orientation|screenSize|keyboard|navigation"
|
||||
android:theme="@style/Theme.AppCompat.DayNight.Dialog" />
|
||||
<activity
|
||||
android:screenOrientation="sensorLandscape"
|
||||
android:name=".JavaGUILauncherActivity"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize|keyboard|navigation"/>
|
||||
|
||||
android:configChanges="keyboardHidden|orientation|screenSize|keyboard|navigation"
|
||||
android:screenOrientation="sensorLandscape" />
|
||||
<activity
|
||||
android:screenOrientation="sensorLandscape"
|
||||
android:name=".CustomControlsActivity"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize|keyboard|navigation"/>
|
||||
android:configChanges="keyboardHidden|orientation|screenSize|keyboard|navigation"
|
||||
android:exported="false"
|
||||
android:screenOrientation="sensorLandscape">
|
||||
<intent-filter>
|
||||
<action android:name=".CustomControlsActivity" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:process=":game"
|
||||
android:launchMode="standard"
|
||||
android:screenOrientation="sensorLandscape"
|
||||
android:name=".MainActivity"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize|smallestScreenSize|screenLayout|keyboard|navigation"/>
|
||||
android:configChanges="keyboardHidden|orientation|screenSize|smallestScreenSize|screenLayout|keyboard|navigation|uiMode"
|
||||
android:launchMode="standard"
|
||||
android:process=":game"
|
||||
android:screenOrientation="sensorLandscape" />
|
||||
|
||||
<provider
|
||||
android:name=".scoped.FolderProvider"
|
||||
|
|
@ -96,9 +115,10 @@
|
|||
</intent-filter>
|
||||
</provider>
|
||||
|
||||
<service android:name=".services.ProgressService"/>
|
||||
<service android:name=".services.GameService" android:process=":game"/>
|
||||
|
||||
<service android:name=".services.ProgressService" />
|
||||
<service
|
||||
android:name=".services.GameService"
|
||||
android:process=":game" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
</manifest>
|
||||
Binary file not shown.
|
|
@ -1 +1 @@
|
|||
HEllo People
|
||||
HEllo PeopLE
|
||||
Binary file not shown.
|
|
@ -1 +1 @@
|
|||
20220831
|
||||
20230118
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
1668936448354
|
||||
1677612008557
|
||||
|
|
@ -39,6 +39,8 @@ public class LauncherMenuButton extends ExtendedButton {
|
|||
setPaddingRelative(padding, 0, 0, 0);
|
||||
setGravity(Gravity.CENTER_VERTICAL);
|
||||
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimensionPixelSize(R.dimen._12ssp));
|
||||
|
||||
// Set drawable size
|
||||
int[] sizes = getExtendedViewData().getSizeCompounds();
|
||||
sizes[0] = resources.getDimensionPixelSize(R.dimen._30sdp);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ public class MineButton extends androidx.appcompat.widget.AppCompatButton {
|
|||
public void init() {
|
||||
setTypeface(ResourcesCompat.getFont(getContext(), R.font.noto_sans_bold));
|
||||
setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.mine_button_background, null));
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimensionPixelSize(R.dimen._13ssp));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ public class ProgressLayout extends ConstraintLayout implements View.OnClickList
|
|||
public static final String DOWNLOAD_VERSION_LIST = "download_verlist";
|
||||
public static final String AUTHENTICATE_MICROSOFT = "authenticate_microsoft";
|
||||
public static final String INSTALL_MODPACK = "install_modpack";
|
||||
public static final String EXTRACT_COMPONENTS = "extract_components";
|
||||
public static final String EXTRACT_SINGLE_FILES = "extract_single_files";
|
||||
|
||||
public ProgressLayout(@NonNull Context context) {
|
||||
super(context);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package com.kdt.mcgui;
|
|||
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
|
|
@ -30,11 +29,11 @@ import net.kdt.pojavlaunch.Tools;
|
|||
import net.kdt.pojavlaunch.authenticator.listener.DoneListener;
|
||||
import net.kdt.pojavlaunch.authenticator.listener.ErrorListener;
|
||||
import net.kdt.pojavlaunch.authenticator.listener.ProgressListener;
|
||||
import net.kdt.pojavlaunch.authenticator.microsoft.PresentedException;
|
||||
import net.kdt.pojavlaunch.authenticator.microsoft.MicrosoftBackgroundLogin;
|
||||
import net.kdt.pojavlaunch.extra.ExtraConstants;
|
||||
import net.kdt.pojavlaunch.extra.ExtraCore;
|
||||
import net.kdt.pojavlaunch.extra.ExtraListener;
|
||||
import net.kdt.pojavlaunch.services.ProgressService;
|
||||
import net.kdt.pojavlaunch.value.MinecraftAccount;
|
||||
|
||||
import java.io.File;
|
||||
|
|
@ -93,7 +92,12 @@ public class mcAccountSpinner extends AppCompatSpinner implements AdapterView.On
|
|||
|
||||
private final ErrorListener mErrorListener = errorMessage -> {
|
||||
mLoginBarPaint.setColor(Color.RED);
|
||||
Tools.showError(getContext(), errorMessage);
|
||||
if(errorMessage instanceof PresentedException) {
|
||||
PresentedException exception = (PresentedException) errorMessage;
|
||||
Tools.showError(getContext(), exception.toString(getContext()), exception.getCause());
|
||||
}else {
|
||||
Tools.showError(getContext(), errorMessage);
|
||||
}
|
||||
invalidate();
|
||||
};
|
||||
|
||||
|
|
@ -284,11 +288,16 @@ public class mcAccountSpinner extends AppCompatSpinner implements AdapterView.On
|
|||
ExtendedTextView view = ((ExtendedTextView) getSelectedView());
|
||||
if(view != null){
|
||||
Bitmap bitmap = mSelectecAccount.getSkinFace();
|
||||
mHeadDrawable = new BitmapDrawable(bitmap);
|
||||
mHeadDrawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
|
||||
if(bitmap != null) {
|
||||
mHeadDrawable = new BitmapDrawable(bitmap);
|
||||
mHeadDrawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
|
||||
|
||||
view.setCompoundDrawables(mHeadDrawable, null, null, null);
|
||||
view.postProcessDrawables();
|
||||
view.setCompoundDrawables(mHeadDrawable, null, null, null);
|
||||
view.postProcessDrawables();
|
||||
}else{
|
||||
view.setCompoundDrawables(null, null, null, null);
|
||||
view.postProcessDrawables();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import android.os.Build;
|
|||
import android.os.Bundle;
|
||||
import android.transition.Slide;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
|
@ -74,6 +75,7 @@ public class mcVersionSpinner extends ExtendedTextView {
|
|||
/** Initialize various behaviors */
|
||||
private void init(){
|
||||
// Setup various attributes
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimensionPixelSize(R.dimen._12ssp));
|
||||
setGravity(Gravity.CENTER_VERTICAL);
|
||||
int startPadding = getContext().getResources().getDimensionPixelOffset(R.dimen._17sdp);
|
||||
int endPadding = getContext().getResources().getDimensionPixelOffset(R.dimen._5sdp);
|
||||
|
|
|
|||
|
|
@ -8,6 +8,12 @@ import net.kdt.pojavlaunch.utils.*;
|
|||
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_IGNORE_NOTCH;
|
||||
|
||||
public abstract class BaseActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context newBase) {
|
||||
super.attachBaseContext(LocaleUtils.setLocale(newBase));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
|
@ -28,9 +34,19 @@ public abstract class BaseActivity extends AppCompatActivity {
|
|||
//new Throwable("StartActivity").printStackTrace();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
if(!Tools.checkStorageRoot(this)) {
|
||||
startActivity(new Intent(this, MissingStorageActivity.class));
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostResume() {
|
||||
super.onPostResume();
|
||||
Tools.setFullscreen(this, setFullscreen());
|
||||
Tools.ignoreNotch(PREF_IGNORE_NOTCH,this);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -220,7 +220,7 @@ public class CustomControlsActivity extends BaseActivity {
|
|||
private static void setDefaultControlJson(String path,ControlLayout ctrlLayout) {
|
||||
// Load before save to make sure control is not error
|
||||
try {
|
||||
ctrlLayout.loadLayout(Tools.GLOBAL_GSON.fromJson(Tools.read(path), CustomControls.class));
|
||||
ctrlLayout.loadLayout(path);
|
||||
LauncherPreferences.DEFAULT_PREF.edit().putString("defaultCtrl", path).apply();
|
||||
LauncherPreferences.PREF_DEFAULTCTRL_PATH = path;
|
||||
} catch (IOException| JsonSyntaxException exception) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,124 @@
|
|||
package net.kdt.pojavlaunch;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.view.OrientationEventListener;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import net.kdt.pojavlaunch.prefs.LauncherPreferences;
|
||||
|
||||
import org.lwjgl.glfw.CallbackBridge;
|
||||
|
||||
public class GyroControl implements SensorEventListener, GrabListener{
|
||||
private final WindowManager mWindowManager;
|
||||
private int mSurfaceRotation;
|
||||
private final SensorManager mSensorManager;
|
||||
private final Sensor mSensor;
|
||||
private final OrientationCorrectionListener mCorrectionListener;
|
||||
private boolean mShouldHandleEvents;
|
||||
private boolean mFirstPass;
|
||||
private float xFactor; // -1 or 1 depending on device orientation
|
||||
private float yFactor;
|
||||
private boolean mSwapXY;
|
||||
|
||||
private final float[] mPreviousRotation = new float[16];
|
||||
private final float[] mCurrentRotation = new float[16];
|
||||
private final float[] mAngleDifference = new float[3];
|
||||
|
||||
public GyroControl(Activity activity) {
|
||||
mWindowManager = activity.getWindowManager();
|
||||
mSurfaceRotation = -10;
|
||||
mSensorManager = (SensorManager) activity.getSystemService(Context.SENSOR_SERVICE);
|
||||
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GAME_ROTATION_VECTOR);
|
||||
mCorrectionListener = new OrientationCorrectionListener(activity);
|
||||
}
|
||||
|
||||
public void enable() {
|
||||
if(mSensor == null) return;
|
||||
mFirstPass = true;
|
||||
mSensorManager.registerListener(this, mSensor, 1000 * LauncherPreferences.PREF_GYRO_SAMPLE_RATE);
|
||||
mCorrectionListener.enable();
|
||||
mShouldHandleEvents = CallbackBridge.isGrabbing();
|
||||
CallbackBridge.addGrabListener(this);
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
if(mSensor == null) return;
|
||||
mSensorManager.unregisterListener(this);
|
||||
mCorrectionListener.disable();
|
||||
CallbackBridge.removeGrabListener(this);
|
||||
}
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent sensorEvent) {
|
||||
if (!mShouldHandleEvents) return;
|
||||
// Copy the old array content
|
||||
System.arraycopy(mCurrentRotation, 0, mPreviousRotation, 0, 16);
|
||||
SensorManager.getRotationMatrixFromVector(mCurrentRotation, sensorEvent.values);
|
||||
|
||||
if(mFirstPass){ // Setup initial position
|
||||
mFirstPass = false;
|
||||
return;
|
||||
}
|
||||
SensorManager.getAngleChange(mAngleDifference, mCurrentRotation, mPreviousRotation);
|
||||
|
||||
CallbackBridge.mouseX -= (mAngleDifference[mSwapXY ? 2 : 1] * 1000 * LauncherPreferences.PREF_GYRO_SENSITIVITY * xFactor);
|
||||
CallbackBridge.mouseY += (mAngleDifference[mSwapXY ? 1 : 2] * 1000 * LauncherPreferences.PREF_GYRO_SENSITIVITY * yFactor);
|
||||
CallbackBridge.sendCursorPos(CallbackBridge.mouseX, CallbackBridge.mouseY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor sensor, int i) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGrabState(boolean isGrabbing) {
|
||||
mFirstPass = true;
|
||||
mShouldHandleEvents = isGrabbing;
|
||||
}
|
||||
|
||||
class OrientationCorrectionListener extends OrientationEventListener {
|
||||
|
||||
public OrientationCorrectionListener(Context context) {
|
||||
super(context, SensorManager.SENSOR_DELAY_NORMAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOrientationChanged(int i) {
|
||||
// Force to wait to be in game before setting factors
|
||||
// Theoretically, one could use the whole interface in portrait...
|
||||
if(!mShouldHandleEvents) return;
|
||||
int surfaceRotation = mWindowManager.getDefaultDisplay().getRotation();
|
||||
if(surfaceRotation == mSurfaceRotation) return;
|
||||
|
||||
if(i == OrientationEventListener.ORIENTATION_UNKNOWN) {
|
||||
return; //change nothing
|
||||
}
|
||||
mSurfaceRotation = surfaceRotation;
|
||||
|
||||
if((315 < i && i <= 360) || (i < 45) ) {
|
||||
mSwapXY = true;
|
||||
xFactor = 1;
|
||||
yFactor = 1;
|
||||
}else if(45 < i && i < 135) {
|
||||
mSwapXY = false;
|
||||
xFactor = 1;
|
||||
yFactor = -1;
|
||||
}else if(135 < i && i < 225) {
|
||||
mSwapXY = true;
|
||||
xFactor = -1;
|
||||
yFactor = -1;
|
||||
}else if(225 < i && i < 315) {
|
||||
mSwapXY = false;
|
||||
xFactor = -1;
|
||||
yFactor = 1;
|
||||
}
|
||||
if(LauncherPreferences.PREF_GYRO_INVERT_X) xFactor *= -1;
|
||||
if(LauncherPreferences.PREF_GYRO_INVERT_Y) yFactor *= -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -67,7 +67,11 @@ public class ImportControlActivity extends Activity {
|
|||
if(!mHasIntentChanged) return;
|
||||
mIsFileVerified = false;
|
||||
getUriData();
|
||||
mEditText.setText(getNameFromURI(mUriData));
|
||||
if(mUriData == null) {
|
||||
finishAndRemoveTask();
|
||||
return;
|
||||
}
|
||||
mEditText.setText(trimFileName(Tools.getFileName(this, mUriData)));
|
||||
mHasIntentChanged = false;
|
||||
|
||||
//Import and verify thread
|
||||
|
|
@ -189,12 +193,4 @@ public class ImportControlActivity extends Activity {
|
|||
}
|
||||
}
|
||||
|
||||
public String getNameFromURI(Uri uri) {
|
||||
Cursor c = getContentResolver().query(uri, null, null, null, null);
|
||||
c.moveToFirst();
|
||||
String fileName = c.getString(c.getColumnIndex(OpenableColumns.DISPLAY_NAME));
|
||||
c.close();
|
||||
return trimFileName(fileName);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ public class JMinecraftVersionList {
|
|||
public String mainClass;
|
||||
public String minecraftArguments;
|
||||
public int minimumLauncherVersion;
|
||||
public DependentLibrary optifineLib;
|
||||
public String releaseTime;
|
||||
public String time;
|
||||
public String type;
|
||||
|
|
|
|||
|
|
@ -262,7 +262,7 @@ public class JavaGUILauncherActivity extends BaseActivity implements View.OnTouc
|
|||
|
||||
// Enable Caciocavallo
|
||||
JREUtils.jreReleaseList = JREUtils.readJREReleaseProperties(LauncherPreferences.PREF_DEFAULT_RUNTIME);
|
||||
Tools.getCacioJavaArgs(javaArgList,JREUtils.jreReleaseList.get("JAVA_VERSION").equals("1.8.0"));
|
||||
Tools.getCacioJavaArgs(javaArgList,JREUtils.jreReleaseList.get("JAVA_VERSION").startsWith("1.8.0"));
|
||||
|
||||
if (javaArgs != null) {
|
||||
javaArgList.addAll(Arrays.asList(javaArgs.split(" ")));
|
||||
|
|
@ -271,16 +271,18 @@ public class JavaGUILauncherActivity extends BaseActivity implements View.OnTouc
|
|||
javaArgList.add(modFile.getAbsolutePath());
|
||||
}
|
||||
|
||||
Logger.getInstance().appendToLog("Info: Java arguments: " + Arrays.toString(javaArgList.toArray(new String[0])));
|
||||
|
||||
// Run java on sandbox, non-overrideable.
|
||||
Collections.reverse(javaArgList);
|
||||
javaArgList.add("-Xbootclasspath/a:" + Tools.DIR_DATA + "/security/pro-grade.jar");
|
||||
javaArgList.add("-Djava.security.manager=net.sourceforge.prograde.sm.ProGradeJSM");
|
||||
javaArgList.add("-Djava.security.policy=" + Tools.DIR_DATA + "/security/java_sandbox.policy");
|
||||
Collections.reverse(javaArgList);
|
||||
if (LauncherPreferences.PREF_JAVA_SANDBOX) {
|
||||
Collections.reverse(javaArgList);
|
||||
javaArgList.add("-Xbootclasspath/a:" + Tools.DIR_DATA + "/security/pro-grade.jar");
|
||||
javaArgList.add("-Djava.security.manager=net.sourceforge.prograde.sm.ProGradeJSM");
|
||||
javaArgList.add("-Djava.security.policy=" + Tools.DIR_DATA + "/security/java_sandbox.policy");
|
||||
Collections.reverse(javaArgList);
|
||||
}
|
||||
|
||||
return JREUtils.launchJavaVM(this, javaArgList);
|
||||
Logger.getInstance().appendToLog("Info: Java arguments: " + Arrays.toString(javaArgList.toArray(new String[0])));
|
||||
|
||||
return JREUtils.launchJavaVM(this, null,javaArgList);
|
||||
} catch (Throwable th) {
|
||||
Tools.showError(this, th, true);
|
||||
return -1;
|
||||
|
|
|
|||
|
|
@ -1,13 +1,10 @@
|
|||
package net.kdt.pojavlaunch;
|
||||
|
||||
import static android.os.Build.VERSION_CODES.P;
|
||||
|
||||
import static net.kdt.pojavlaunch.MainActivity.INTENT_MINECRAFT_VERSION;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
|
@ -23,14 +20,10 @@ import androidx.core.content.ContextCompat;
|
|||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentContainerView;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.lifecycle.Lifecycle;
|
||||
import androidx.lifecycle.LifecycleEventObserver;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
|
||||
import com.kdt.mcgui.ProgressLayout;
|
||||
import com.kdt.mcgui.mcAccountSpinner;
|
||||
|
||||
import net.kdt.pojavlaunch.fragments.LocalLoginFragment;
|
||||
import net.kdt.pojavlaunch.fragments.MainMenuFragment;
|
||||
import net.kdt.pojavlaunch.fragments.MicrosoftLoginFragment;
|
||||
import net.kdt.pojavlaunch.extra.ExtraConstants;
|
||||
|
|
@ -43,14 +36,11 @@ import net.kdt.pojavlaunch.prefs.LauncherPreferences;
|
|||
import net.kdt.pojavlaunch.prefs.screens.LauncherPreferenceFragment;
|
||||
import net.kdt.pojavlaunch.progresskeeper.ProgressKeeper;
|
||||
import net.kdt.pojavlaunch.services.ProgressServiceKeeper;
|
||||
import net.kdt.pojavlaunch.tasks.AsyncAssetManager;
|
||||
import net.kdt.pojavlaunch.tasks.AsyncMinecraftDownloader;
|
||||
import net.kdt.pojavlaunch.tasks.AsyncVersionList;
|
||||
import net.kdt.pojavlaunch.value.launcherprofiles.LauncherProfiles;
|
||||
import net.kdt.pojavlaunch.value.launcherprofiles.MinecraftProfile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class LauncherActivity extends BaseActivity {
|
||||
public static final String SETTING_FRAGMENT_TAG = "SETTINGS_FRAGMENT";
|
||||
|
||||
|
|
@ -81,10 +71,10 @@ public class LauncherActivity extends BaseActivity {
|
|||
|
||||
/* Listener for the auth method selection screen */
|
||||
private final ExtraListener<Boolean> mSelectAuthMethod = (key, value) -> {
|
||||
if(isFragmentVisible(SelectAuthFragment.TAG)
|
||||
|| isFragmentVisible(LocalLoginFragment.TAG)
|
||||
|| isFragmentVisible(MicrosoftLoginFragment.TAG)
|
||||
) return false;
|
||||
Fragment fragment = getSupportFragmentManager().findFragmentById(mFragmentView.getId());
|
||||
// Allow starting the add account only from the main menu, should it be moved to fragment itself ?
|
||||
if(!(fragment instanceof MainMenuFragment)) return false;
|
||||
|
||||
Tools.swapFragment(this, SelectAuthFragment.class, SelectAuthFragment.TAG, true, null);
|
||||
return false;
|
||||
};
|
||||
|
|
@ -116,13 +106,12 @@ public class LauncherActivity extends BaseActivity {
|
|||
}
|
||||
|
||||
String selectedProfile = LauncherPreferences.DEFAULT_PREF.getString(LauncherPreferences.PREF_KEY_CURRENT_PROFILE,"");
|
||||
if (LauncherProfiles.mainProfileJson == null || LauncherProfiles.mainProfileJson.profiles == null
|
||||
|| !LauncherProfiles.mainProfileJson.profiles.containsKey(selectedProfile)){
|
||||
if (LauncherProfiles.mainProfileJson == null || !LauncherProfiles.mainProfileJson.profiles.containsKey(selectedProfile)){
|
||||
Toast.makeText(this, R.string.error_no_version, Toast.LENGTH_LONG).show();
|
||||
return false;
|
||||
}
|
||||
MinecraftProfile prof = LauncherProfiles.mainProfileJson.profiles.get(selectedProfile);
|
||||
if (prof == null || prof.lastVersionId == null){
|
||||
if (prof == null || prof.lastVersionId == null || "Unknown".equals(prof.lastVersionId)){
|
||||
Toast.makeText(this, R.string.error_no_version, Toast.LENGTH_LONG).show();
|
||||
return false;
|
||||
}
|
||||
|
|
@ -134,18 +123,28 @@ public class LauncherActivity extends BaseActivity {
|
|||
}
|
||||
String normalizedVersionId = AsyncMinecraftDownloader.normalizeVersionId(prof.lastVersionId);
|
||||
JMinecraftVersionList.Version mcVersion = AsyncMinecraftDownloader.getListedVersion(normalizedVersionId);
|
||||
new AsyncMinecraftDownloader(this, mcVersion, normalizedVersionId, () -> runOnUiThread(() -> {
|
||||
try {
|
||||
Intent mainIntent = new Intent(getBaseContext(), MainActivity.class);
|
||||
mainIntent.putExtra(INTENT_MINECRAFT_VERSION, normalizedVersionId);
|
||||
mainIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
startActivity(mainIntent);
|
||||
finish();
|
||||
android.os.Process.killProcess(android.os.Process.myPid()); //You should kill yourself, NOW!
|
||||
} catch (Throwable e) {
|
||||
Tools.showError(getBaseContext(), e);
|
||||
new AsyncMinecraftDownloader(this, mcVersion, normalizedVersionId, new AsyncMinecraftDownloader.DoneListener() {
|
||||
@Override
|
||||
public void onDownloadDone() {
|
||||
ProgressKeeper.waitUntilDone(()-> runOnUiThread(() -> {
|
||||
try {
|
||||
Intent mainIntent = new Intent(getBaseContext(), MainActivity.class);
|
||||
mainIntent.putExtra(INTENT_MINECRAFT_VERSION, normalizedVersionId);
|
||||
mainIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
startActivity(mainIntent);
|
||||
finish();
|
||||
android.os.Process.killProcess(android.os.Process.myPid()); //You should kill yourself, NOW!
|
||||
} catch (Throwable e) {
|
||||
Tools.showError(getBaseContext(), e);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}));
|
||||
|
||||
@Override
|
||||
public void onDownloadFailed(Throwable th) {
|
||||
if(th != null) Tools.showError(LauncherActivity.this, R.string.mc_download_failed, th);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -17,8 +17,10 @@ import android.content.res.Configuration;
|
|||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.os.*;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.util.*;
|
||||
import android.view.*;
|
||||
import android.webkit.MimeTypeMap;
|
||||
import android.widget.*;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
|
@ -44,7 +46,7 @@ import net.kdt.pojavlaunch.value.launcherprofiles.MinecraftProfile;
|
|||
import org.lwjgl.glfw.*;
|
||||
import android.net.*;
|
||||
|
||||
public class MainActivity extends BaseActivity {
|
||||
public class MainActivity extends BaseActivity implements ControlButtonMenuListener{
|
||||
public static volatile ClipboardManager GLOBAL_CLIPBOARD;
|
||||
public static final String INTENT_MINECRAFT_VERSION = "intent_version";
|
||||
|
||||
|
|
@ -60,6 +62,7 @@ public class MainActivity extends BaseActivity {
|
|||
private DrawerLayout drawerLayout;
|
||||
private ListView navDrawer;
|
||||
private View mDrawerPullButton;
|
||||
private GyroControl mGyroControl = null;
|
||||
public static ControlLayout mControlLayout;
|
||||
|
||||
|
||||
|
|
@ -71,7 +74,7 @@ public class MainActivity extends BaseActivity {
|
|||
public ArrayAdapter<String> ingameControlsEditorArrayAdapter;
|
||||
public AdapterView.OnItemClickListener ingameControlsEditorListener;
|
||||
|
||||
protected volatile JMinecraftVersionList.Version mVersionInfo;
|
||||
protected volatile String mVersionId;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
|
|
@ -85,6 +88,7 @@ public class MainActivity extends BaseActivity {
|
|||
initLayout(R.layout.activity_basemain);
|
||||
CallbackBridge.addGrabListener(touchpad);
|
||||
CallbackBridge.addGrabListener(minecraftGLView);
|
||||
if(LauncherPreferences.PREF_ENALBE_GYRO) mGyroControl = new GyroControl(this);
|
||||
|
||||
// Enabling this on TextureView results in a broken white result
|
||||
if(PREF_USE_ALTERNATE_SURFACE) getWindow().setBackgroundDrawable(null);
|
||||
|
|
@ -116,8 +120,9 @@ public class MainActivity extends BaseActivity {
|
|||
protected void initLayout(int resId) {
|
||||
setContentView(resId);
|
||||
bindValues();
|
||||
mControlLayout.setMenuListener(this);
|
||||
|
||||
mDrawerPullButton.setOnClickListener(v -> drawerLayout.openDrawer(navDrawer));
|
||||
mDrawerPullButton.setOnClickListener(v -> onClickedMenu());
|
||||
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
|
||||
|
||||
try {
|
||||
|
|
@ -147,8 +152,8 @@ public class MainActivity extends BaseActivity {
|
|||
String version = getIntent().getStringExtra(INTENT_MINECRAFT_VERSION);
|
||||
version = version == null ? minecraftProfile.lastVersionId : version;
|
||||
|
||||
mVersionInfo = Tools.getVersionInfo(version);
|
||||
isInputStackCall = mVersionInfo.arguments != null;
|
||||
mVersionId = version;
|
||||
isInputStackCall = Tools.getVersionInfo(mVersionId).arguments != null;
|
||||
|
||||
Tools.getDisplayMetrics(this);
|
||||
windowWidth = Tools.getDisplayFriendlyRes(currentDisplayMetrics.widthPixels, scaleFactor);
|
||||
|
|
@ -165,7 +170,8 @@ public class MainActivity extends BaseActivity {
|
|||
case 2: minecraftGLView.togglepointerDebugging(); break;
|
||||
case 3: dialogSendCustomKey(); break;
|
||||
case 4: adjustMouseSpeedLive(); break;
|
||||
case 5: openCustomControls(); break;
|
||||
case 5: adjustGyroSensitivityLive(); break;
|
||||
case 6: openCustomControls(); break;
|
||||
}
|
||||
drawerLayout.closeDrawers();
|
||||
};
|
||||
|
|
@ -178,8 +184,9 @@ public class MainActivity extends BaseActivity {
|
|||
minecraftGLView.setSurfaceReadyListener(() -> {
|
||||
try {
|
||||
// Setup virtual mouse right before launching
|
||||
if (PREF_VIRTUAL_MOUSE_START)
|
||||
touchpad.switchState();
|
||||
if (PREF_VIRTUAL_MOUSE_START) {
|
||||
touchpad.post(() -> touchpad.switchState());
|
||||
}
|
||||
|
||||
runCraft();
|
||||
}catch (Throwable e){
|
||||
|
|
@ -210,6 +217,7 @@ public class MainActivity extends BaseActivity {
|
|||
} catch (Throwable th) {
|
||||
Tools.showError(this, th);
|
||||
}
|
||||
mDrawerPullButton.setVisibility(mControlLayout.hasMenuButton() ? View.GONE : View.VISIBLE);
|
||||
mControlLayout.toggleControlVisible();
|
||||
}
|
||||
|
||||
|
|
@ -236,14 +244,13 @@ public class MainActivity extends BaseActivity {
|
|||
public void onResume() {
|
||||
super.onResume();
|
||||
mIsResuming = true;
|
||||
final int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
|
||||
final View decorView = getWindow().getDecorView();
|
||||
decorView.setSystemUiVisibility(uiOptions);
|
||||
if(mGyroControl != null) mGyroControl.enable();
|
||||
CallbackBridge.nativeSetWindowAttrib(LwjglGlfwKeycode.GLFW_HOVERED, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
if(mGyroControl != null) mGyroControl.disable();
|
||||
if (CallbackBridge.isGrabbing()){
|
||||
sendKeyPress(LwjglGlfwKeycode.GLFW_KEY_ESCAPE);
|
||||
}
|
||||
|
|
@ -326,15 +333,13 @@ public class MainActivity extends BaseActivity {
|
|||
checkJavaArgsIsLaunchable(JREUtils.jreReleaseList.get("JAVA_VERSION"));
|
||||
// appendlnToLog("Info: Custom Java arguments: \"" + LauncherPreferences.PREF_CUSTOM_JAVA_ARGS + "\"");
|
||||
|
||||
Logger.getInstance().appendToLog("Info: Selected Minecraft version: " + mVersionInfo.id +
|
||||
((mVersionInfo.inheritsFrom == null || mVersionInfo.inheritsFrom.equals(mVersionInfo.id)) ?
|
||||
"" : " (" + mVersionInfo.inheritsFrom + ")"));
|
||||
Logger.getInstance().appendToLog("Info: Selected Minecraft version: " + mVersionId);
|
||||
|
||||
|
||||
JREUtils.redirectAndPrintJRELog();
|
||||
|
||||
LauncherProfiles.update();
|
||||
Tools.launchMinecraft(this, mProfile, minecraftProfile);
|
||||
Tools.launchMinecraft(this, mProfile, minecraftProfile, mVersionId);
|
||||
}
|
||||
|
||||
private void checkJavaArgsIsLaunchable(String jreVersion) throws Throwable {
|
||||
|
|
@ -395,6 +400,7 @@ public class MainActivity extends BaseActivity {
|
|||
mControlLayout.setModifiable(true);
|
||||
navDrawer.setAdapter(ingameControlsEditorArrayAdapter);
|
||||
navDrawer.setOnItemClickListener(ingameControlsEditorListener);
|
||||
mDrawerPullButton.setVisibility(View.VISIBLE);
|
||||
isInEditor = true;
|
||||
}
|
||||
|
||||
|
|
@ -407,6 +413,7 @@ public class MainActivity extends BaseActivity {
|
|||
minecraftProfile.controlFile == null
|
||||
? LauncherPreferences.PREF_DEFAULTCTRL_PATH
|
||||
: Tools.CTRLMAP_PATH + "/" + minecraftProfile.controlFile);
|
||||
mDrawerPullButton.setVisibility(mControlLayout.hasMenuButton() ? View.GONE : View.VISIBLE);
|
||||
} catch (IOException e) {
|
||||
Tools.showError(this,e);
|
||||
}
|
||||
|
|
@ -505,16 +512,99 @@ public class MainActivity extends BaseActivity {
|
|||
b.show();
|
||||
}
|
||||
|
||||
int tmpGyroSensitivity;
|
||||
public void adjustGyroSensitivityLive() {
|
||||
if(!LauncherPreferences.PREF_ENALBE_GYRO) {
|
||||
Toast.makeText(this, R.string.toast_turn_on_gyro, Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
AlertDialog.Builder b = new AlertDialog.Builder(this);
|
||||
b.setTitle(R.string.preference_gyro_sensitivity_title);
|
||||
View v = LayoutInflater.from(this).inflate(R.layout.dialog_live_mouse_speed_editor,null);
|
||||
final SeekBar sb = v.findViewById(R.id.mouseSpeed);
|
||||
final TextView tv = v.findViewById(R.id.mouseSpeedTV);
|
||||
sb.setMax(275);
|
||||
tmpGyroSensitivity = (int) ((LauncherPreferences.PREF_GYRO_SENSITIVITY*100));
|
||||
sb.setProgress(tmpGyroSensitivity -25);
|
||||
tv.setText(tmpGyroSensitivity +" %");
|
||||
sb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
|
||||
tmpGyroSensitivity = i+25;
|
||||
tv.setText(tmpGyroSensitivity +" %");
|
||||
}
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {}
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {}
|
||||
});
|
||||
b.setView(v);
|
||||
b.setPositiveButton(android.R.string.ok, (dialogInterface, i) -> {
|
||||
LauncherPreferences.PREF_GYRO_SENSITIVITY = ((float) tmpGyroSensitivity)/100f;
|
||||
LauncherPreferences.DEFAULT_PREF.edit().putInt("gyroSensitivity", tmpGyroSensitivity).commit();
|
||||
dialogInterface.dismiss();
|
||||
System.gc();
|
||||
});
|
||||
b.setNegativeButton(android.R.string.cancel, (dialogInterface, i) -> {
|
||||
dialogInterface.dismiss();
|
||||
System.gc();
|
||||
});
|
||||
b.show();
|
||||
}
|
||||
|
||||
private static void setUri(Context context, String input, Intent intent) {
|
||||
if(input.startsWith("file:")) {
|
||||
int truncLength = 5;
|
||||
if(input.startsWith("file://")) truncLength = 7;
|
||||
input = input.substring(truncLength);
|
||||
Log.i("MainActivity", input);
|
||||
boolean isDirectory = new File(input).isDirectory();
|
||||
if(isDirectory) {
|
||||
intent.setType(DocumentsContract.Document.MIME_TYPE_DIR);
|
||||
}else{
|
||||
String type = null;
|
||||
String extension = MimeTypeMap.getFileExtensionFromUrl(input);
|
||||
if(extension != null) type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
|
||||
if(type == null) type = "*/*";
|
||||
intent.setType(type);
|
||||
}
|
||||
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
intent.setData(DocumentsContract.buildDocumentUri(
|
||||
context.getString(R.string.storageProviderAuthorities), input
|
||||
));
|
||||
return;
|
||||
}
|
||||
intent.setDataAndType(Uri.parse(input), "*/*");
|
||||
}
|
||||
|
||||
public static void openLink(String link) {
|
||||
Context ctx = touchpad.getContext(); // no more better way to obtain a context statically
|
||||
((Activity)ctx).runOnUiThread(() -> {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setDataAndType(Uri.parse(link.replace("file://", "content://")), "*/*");
|
||||
setUri(ctx, link, intent);
|
||||
ctx.startActivity(intent);
|
||||
} catch (Throwable th) {
|
||||
Tools.showError(ctx, th);
|
||||
}
|
||||
});
|
||||
}
|
||||
public static void openPath(String path) {
|
||||
Context ctx = touchpad.getContext(); // no more better way to obtain a context statically
|
||||
((Activity)ctx).runOnUiThread(() -> {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setDataAndType(DocumentsContract.buildDocumentUri(ctx.getString(R.string.storageProviderAuthorities), path), "*/*");
|
||||
ctx.startActivity(intent);
|
||||
} catch (Throwable th) {
|
||||
Tools.showError(ctx, th);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClickedMenu() {
|
||||
drawerLayout.openDrawer(navDrawer);
|
||||
navDrawer.requestLayout();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@ import org.lwjgl.glfw.CallbackBridge;
|
|||
* Class dealing with showing minecraft surface and taking inputs to dispatch them to minecraft
|
||||
*/
|
||||
public class MinecraftGLSurface extends View implements GrabListener{
|
||||
Handler uiThreadHandler = new Handler();
|
||||
/* Gamepad object for gamepad inputs, instantiated on need */
|
||||
private Gamepad mGamepad = null;
|
||||
/* Pointer Debug textview, used to show info about the pointer state */
|
||||
|
|
@ -168,6 +167,7 @@ public class MinecraftGLSurface extends View implements GrabListener{
|
|||
}else{
|
||||
TextureView textureView = new TextureView(getContext());
|
||||
textureView.setOpaque(true);
|
||||
textureView.setAlpha(1.0f);
|
||||
mSurface = textureView;
|
||||
|
||||
textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
|
||||
|
|
@ -388,7 +388,6 @@ public class MinecraftGLSurface extends View implements GrabListener{
|
|||
|
||||
/**
|
||||
* The event for mouse/joystick movements
|
||||
* We don't do the gamepad right now.
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
@Override
|
||||
|
|
@ -411,6 +410,10 @@ public class MinecraftGLSurface extends View implements GrabListener{
|
|||
break;
|
||||
}
|
||||
if(mouseCursorIndex == -1) return false; // we cant consoom that, theres no mice!
|
||||
|
||||
// Make sure we grabbed the mouse if necessary
|
||||
updateGrabState(CallbackBridge.isGrabbing());
|
||||
|
||||
switch(event.getActionMasked()) {
|
||||
case MotionEvent.ACTION_HOVER_MOVE:
|
||||
CallbackBridge.mouseX = (event.getX(mouseCursorIndex) * mScaleFactor);
|
||||
|
|
@ -420,7 +423,7 @@ public class MinecraftGLSurface extends View implements GrabListener{
|
|||
CallbackBridge.DEBUG_STRING.setLength(0);
|
||||
return true;
|
||||
case MotionEvent.ACTION_SCROLL:
|
||||
CallbackBridge.sendScroll((double) event.getAxisValue(MotionEvent.AXIS_VSCROLL), (double) event.getAxisValue(MotionEvent.AXIS_HSCROLL));
|
||||
CallbackBridge.sendScroll((double) event.getAxisValue(MotionEvent.AXIS_HSCROLL), (double) event.getAxisValue(MotionEvent.AXIS_VSCROLL));
|
||||
return true;
|
||||
case MotionEvent.ACTION_BUTTON_PRESS:
|
||||
return sendMouseButtonUnconverted(event.getActionButton(),true);
|
||||
|
|
@ -634,10 +637,12 @@ public class MinecraftGLSurface extends View implements GrabListener{
|
|||
|
||||
new Thread(() -> {
|
||||
try {
|
||||
Thread.sleep(200);
|
||||
if(mSurfaceReadyListener != null){
|
||||
mSurfaceReadyListener.isReady();
|
||||
// Wait until the listener is attached
|
||||
while (mSurfaceReadyListener == null){
|
||||
Thread.sleep(100);
|
||||
}
|
||||
|
||||
mSurfaceReadyListener.isReady();
|
||||
} catch (Throwable e) {
|
||||
Tools.showError(getContext(), e, true);
|
||||
}
|
||||
|
|
@ -646,20 +651,24 @@ public class MinecraftGLSurface extends View implements GrabListener{
|
|||
|
||||
@Override
|
||||
public void onGrabState(boolean isGrabbing) {
|
||||
uiThreadHandler.post(()->updateGrabState(isGrabbing));
|
||||
post(()->updateGrabState(isGrabbing));
|
||||
}
|
||||
|
||||
private void updateGrabState(boolean isGrabbing) {
|
||||
if(MainActivity.isAndroid8OrHigher()) {
|
||||
if (isGrabbing && !hasPointerCapture()) {
|
||||
if(!MainActivity.isAndroid8OrHigher()) return;
|
||||
|
||||
boolean hasPointerCapture = hasPointerCapture();
|
||||
if(isGrabbing){
|
||||
if(!hasPointerCapture) {
|
||||
requestFocus();
|
||||
requestPointerCapture();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isGrabbing && hasPointerCapture()) {
|
||||
releasePointerCapture();
|
||||
clearFocus();
|
||||
}
|
||||
if(hasPointerCapture) {
|
||||
releasePointerCapture();
|
||||
clearFocus();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -670,5 +679,6 @@ public class MinecraftGLSurface extends View implements GrabListener{
|
|||
|
||||
public void setSurfaceReadyListener(SurfaceReadyListener listener){
|
||||
mSurfaceReadyListener = listener;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
package net.kdt.pojavlaunch;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
public class MissingStorageActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.storage_test_no_sdcard);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
package net.kdt.pojavlaunch;
|
||||
|
||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
|
||||
import android.app.*;
|
||||
import android.content.*;
|
||||
import android.content.pm.*;
|
||||
|
|
@ -21,13 +23,13 @@ import net.kdt.pojavlaunch.utils.*;
|
|||
|
||||
public class PojavApplication extends Application {
|
||||
public static String CRASH_REPORT_TAG = "PojavCrashReport";
|
||||
public static ExecutorService sExecutorService = new ThreadPoolExecutor(0, 4, 500, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
|
||||
public static ExecutorService sExecutorService = new ThreadPoolExecutor(4, 4, 500, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
Thread.setDefaultUncaughtExceptionHandler((thread, th) -> {
|
||||
boolean storagePermAllowed = Build.VERSION.SDK_INT < 23 || Build.VERSION.SDK_INT >= 29 ||
|
||||
ActivityCompat.checkSelfPermission(PojavApplication.this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
|
||||
boolean storagePermAllowed = (Build.VERSION.SDK_INT < 23 || Build.VERSION.SDK_INT >= 29 ||
|
||||
ActivityCompat.checkSelfPermission(PojavApplication.this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) && Tools.checkStorageRoot(PojavApplication.this);
|
||||
File crashFile = new File(storagePermAllowed ? Tools.DIR_GAME_HOME : Tools.DIR_DATA, "latestcrash.txt");
|
||||
try {
|
||||
// Write to file, since some devices may not able to show error
|
||||
|
|
@ -73,11 +75,10 @@ public class PojavApplication extends Application {
|
|||
.concat("/x86");
|
||||
}
|
||||
AsyncAssetManager.unpackRuntime(getAssets(), false);
|
||||
AsyncAssetManager.unpackComponents(this);
|
||||
AsyncAssetManager.unpackSingleFiles(this);
|
||||
} catch (Throwable throwable) {
|
||||
Intent ferrorIntent = new Intent(this, FatalErrorActivity.class);
|
||||
ferrorIntent.putExtra("throwable", throwable);
|
||||
ferrorIntent.setFlags(FLAG_ACTIVITY_NEW_TASK);
|
||||
startActivity(ferrorIntent);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
package net.kdt.pojavlaunch;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import net.kdt.pojavlaunch.tasks.AsyncAssetManager;
|
||||
|
||||
public class TestStorageActivity extends Activity {
|
||||
private final int REQUEST_STORAGE_REQUEST_CODE = 1;
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if(Build.VERSION.SDK_INT >= 23 && Build.VERSION.SDK_INT < 29 && !isStorageAllowed(this)) requestStoragePermission();
|
||||
else exit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
if(requestCode == REQUEST_STORAGE_REQUEST_CODE) {
|
||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
exit();
|
||||
} else {
|
||||
Toast.makeText(this, R.string.toast_permission_denied, Toast.LENGTH_LONG).show();
|
||||
requestStoragePermission();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isStorageAllowed(Context context) {
|
||||
//Getting the permission status
|
||||
int result1 = ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
||||
int result2 = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE);
|
||||
|
||||
|
||||
//If permission is granted returning true
|
||||
return result1 == PackageManager.PERMISSION_GRANTED &&
|
||||
result2 == PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
private void requestStoragePermission() {
|
||||
|
||||
ActivityCompat.requestPermissions(this, new String[]{
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_STORAGE_REQUEST_CODE);
|
||||
}
|
||||
|
||||
private void exit() {
|
||||
if(!Tools.checkStorageRoot(this)) {
|
||||
startActivity(new Intent(this, MissingStorageActivity.class));
|
||||
return;
|
||||
}
|
||||
//Only run them once we get a definitive green light to use storage
|
||||
AsyncAssetManager.unpackComponents(this);
|
||||
AsyncAssetManager.unpackSingleFiles(this);
|
||||
|
||||
Intent intent = new Intent(this, LauncherActivity.class);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package net.kdt.pojavlaunch;
|
|||
|
||||
import android.app.*;
|
||||
import android.content.*;
|
||||
import android.content.res.Configuration;
|
||||
import android.database.Cursor;
|
||||
import android.net.*;
|
||||
import android.os.*;
|
||||
|
|
@ -85,6 +86,26 @@ public final class Tools {
|
|||
public static final String LIBNAME_OPTIFINE = "optifine:OptiFine";
|
||||
public static final int RUN_MOD_INSTALLER = 2050;
|
||||
|
||||
|
||||
private static File getPojavStorageRoot(Context ctx) {
|
||||
if(SDK_INT >= 29) {
|
||||
return ctx.getExternalFilesDir(null);
|
||||
}else{
|
||||
return new File(Environment.getExternalStorageDirectory(),"games/PojavLauncher");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the Pojav's storage root is accessible and read-writable
|
||||
* @param context context to get the storage root if it's not set yet
|
||||
* @return true if storage is fine, false if storage is not accessible
|
||||
*/
|
||||
public static boolean checkStorageRoot(Context context) {
|
||||
File externalFilesDir = DIR_GAME_HOME == null ? Tools.getPojavStorageRoot(context) : new File(DIR_GAME_HOME);
|
||||
//externalFilesDir == null when the storage is not mounted if it was obtained with the context call
|
||||
return externalFilesDir != null && Environment.getExternalStorageState(externalFilesDir).equals(Environment.MEDIA_MOUNTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Since some constant requires the use of the Context object
|
||||
* You can call this function to initialize them.
|
||||
|
|
@ -93,11 +114,7 @@ public final class Tools {
|
|||
public static void initContextConstants(Context ctx){
|
||||
DIR_DATA = ctx.getFilesDir().getParent();
|
||||
MULTIRT_HOME = DIR_DATA+"/runtimes";
|
||||
if(SDK_INT >= 29) {
|
||||
DIR_GAME_HOME = ctx.getExternalFilesDir(null).getAbsolutePath();
|
||||
}else{
|
||||
DIR_GAME_HOME = new File(Environment.getExternalStorageDirectory(),"games/PojavLauncher").getAbsolutePath();
|
||||
}
|
||||
DIR_GAME_HOME = getPojavStorageRoot(ctx).getAbsolutePath();
|
||||
DIR_GAME_NEW = DIR_GAME_HOME + "/.minecraft";
|
||||
DIR_HOME_VERSION = DIR_GAME_NEW + "/versions";
|
||||
DIR_HOME_LIBRARY = DIR_GAME_NEW + "/libraries";
|
||||
|
|
@ -110,7 +127,8 @@ public final class Tools {
|
|||
}
|
||||
|
||||
|
||||
public static void launchMinecraft(final Activity activity, MinecraftAccount minecraftAccount, MinecraftProfile minecraftProfile) throws Throwable {
|
||||
public static void launchMinecraft(final Activity activity, MinecraftAccount minecraftAccount,
|
||||
MinecraftProfile minecraftProfile, String versionId) throws Throwable {
|
||||
ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();
|
||||
((ActivityManager)activity.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryInfo(mi);
|
||||
if(LauncherPreferences.PREF_RAM_ALLOCATION > (mi.availMem/1048576L)) {
|
||||
|
|
@ -126,9 +144,7 @@ public final class Tools {
|
|||
memoryErrorLock.wait();
|
||||
}
|
||||
}
|
||||
|
||||
JMinecraftVersionList.Version versionInfo = Tools.getVersionInfo(minecraftProfile.lastVersionId);
|
||||
|
||||
JMinecraftVersionList.Version versionInfo = Tools.getVersionInfo(versionId);
|
||||
LauncherProfiles.update();
|
||||
String gamedirPath = Tools.getGameDirPath(minecraftProfile);
|
||||
|
||||
|
|
@ -143,24 +159,11 @@ public final class Tools {
|
|||
OldVersionsUtils.selectOpenGlVersion(versionInfo);
|
||||
|
||||
|
||||
String launchClassPath = generateLaunchClassPath(versionInfo, minecraftProfile.lastVersionId);
|
||||
String launchClassPath = generateLaunchClassPath(versionInfo, versionId);
|
||||
|
||||
List<String> javaArgList = new ArrayList<String>();
|
||||
|
||||
getCacioJavaArgs(javaArgList, JREUtils.jreReleaseList.get("JAVA_VERSION").equals("1.8.0"));
|
||||
|
||||
/*
|
||||
int mcReleaseDate = Integer.parseInt(versionInfo.releaseTime.substring(0, 10).replace("-", ""));
|
||||
// 13w17a: 20130425
|
||||
// 13w18a: 20130502
|
||||
if (mcReleaseDate < 20130502 && versionInfo.minimumLauncherVersion < 9){
|
||||
ctx.appendlnToLog("AWT-enabled version detected! ("+mcReleaseDate+")");
|
||||
getCacioJavaArgs(javaArgList,false);
|
||||
}else{
|
||||
getCacioJavaArgs(javaArgList,false); // true
|
||||
ctx.appendlnToLog("Headless version detected! ("+mcReleaseDate+")");
|
||||
}
|
||||
*/
|
||||
getCacioJavaArgs(javaArgList, JREUtils.jreReleaseList.get("JAVA_VERSION").startsWith("1.8.0"));
|
||||
|
||||
if (versionInfo.logging != null) {
|
||||
String configFile = Tools.DIR_DATA + "/security/" + versionInfo.logging.client.file.id.replace("client", "log4j-rce-patch");
|
||||
|
|
@ -169,14 +172,14 @@ public final class Tools {
|
|||
}
|
||||
javaArgList.add("-Dlog4j.configurationFile=" + configFile);
|
||||
}
|
||||
javaArgList.addAll(Arrays.asList(getMinecraftJVMArgs(minecraftProfile.lastVersionId, gamedirPath)));
|
||||
javaArgList.addAll(Arrays.asList(getMinecraftJVMArgs(versionId, gamedirPath)));
|
||||
javaArgList.add("-cp");
|
||||
javaArgList.add(getLWJGL3ClassPath() + ":" + launchClassPath);
|
||||
|
||||
javaArgList.add(versionInfo.mainClass);
|
||||
javaArgList.addAll(Arrays.asList(launchArgs));
|
||||
// ctx.appendlnToLog("full args: "+javaArgList.toString());
|
||||
JREUtils.launchJavaVM(activity, javaArgList);
|
||||
JREUtils.launchJavaVM(activity, gamedirPath, javaArgList);
|
||||
}
|
||||
|
||||
public static String getGameDirPath(@NonNull MinecraftProfile minecraftProfile){
|
||||
|
|
@ -279,6 +282,7 @@ public final class Tools {
|
|||
varArgMap.put("classpath_separator", ":");
|
||||
varArgMap.put("library_directory", strGameDir + "/libraries");
|
||||
varArgMap.put("version_name", versionInfo.id);
|
||||
varArgMap.put("natives_directory", Tools.NATIVE_LIB_DIR);
|
||||
|
||||
List<String> minecraftArgs = new ArrayList<String>();
|
||||
if (versionInfo.arguments != null) {
|
||||
|
|
@ -324,7 +328,7 @@ public final class Tools {
|
|||
varArgMap.put("auth_session", profile.accessToken); // For legacy versions of MC
|
||||
varArgMap.put("auth_access_token", profile.accessToken);
|
||||
varArgMap.put("auth_player_name", username);
|
||||
varArgMap.put("auth_uuid", profile.profileId);
|
||||
varArgMap.put("auth_uuid", profile.profileId.replace("-", ""));
|
||||
varArgMap.put("auth_xuid", profile.xuid);
|
||||
varArgMap.put("assets_root", Tools.ASSETS_PATH);
|
||||
varArgMap.put("assets_index_name", versionInfo.assets);
|
||||
|
|
@ -358,17 +362,6 @@ public final class Tools {
|
|||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
minecraftArgs.add("--width");
|
||||
minecraftArgs.add(Integer.toString(CallbackBridge.windowWidth));
|
||||
minecraftArgs.add("--height");
|
||||
minecraftArgs.add(Integer.toString(CallbackBridge.windowHeight));
|
||||
|
||||
minecraftArgs.add("--fullscreenWidth");
|
||||
minecraftArgs.add(Integer.toString(CallbackBridge.windowWidth));
|
||||
minecraftArgs.add("--fullscreenHeight");
|
||||
minecraftArgs.add(Integer.toString(CallbackBridge.windowHeight));
|
||||
*/
|
||||
|
||||
String[] argsFromJson = JSONUtils.insertJSONValueList(
|
||||
splitAndFilterEmpty(
|
||||
|
|
@ -438,26 +431,6 @@ public final class Tools {
|
|||
|
||||
String[] classpath = generateLibClasspath(info);
|
||||
|
||||
// Debug: LWJGL 3 override
|
||||
// File lwjgl2Folder = new File(Tools.MAIN_PATH, "lwjgl2");
|
||||
|
||||
/*
|
||||
File lwjgl3Folder = new File(Tools.MAIN_PATH, "lwjgl3");
|
||||
if (lwjgl3Folder.exists()) {
|
||||
for (File file: lwjgl3Folder.listFiles()) {
|
||||
if (file.getName().endsWith(".jar")) {
|
||||
libStr.append(file.getAbsolutePath() + ":");
|
||||
}
|
||||
}
|
||||
} else if (lwjgl2Folder.exists()) {
|
||||
for (File file: lwjgl2Folder.listFiles()) {
|
||||
if (file.getName().endsWith(".jar")) {
|
||||
libStr.append(file.getAbsolutePath() + ":");
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if (isClientFirst) {
|
||||
libStr.append(getPatchedFile(actualname));
|
||||
}
|
||||
|
|
@ -484,18 +457,18 @@ public final class Tools {
|
|||
}else{
|
||||
if (SDK_INT >= Build.VERSION_CODES.R) {
|
||||
activity.getDisplay().getRealMetrics(displayMetrics);
|
||||
} else if(SDK_INT >= P) {
|
||||
} else { // Removed the clause for devices with unofficial notch support, since it also ruins all devices with virtual nav bars before P
|
||||
activity.getWindowManager().getDefaultDisplay().getRealMetrics(displayMetrics);
|
||||
}else{ // Some old devices can have a notch despite it not being officially supported
|
||||
activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
|
||||
}
|
||||
if(!PREF_IGNORE_NOTCH){
|
||||
//Remove notch width when it isn't ignored.
|
||||
displayMetrics.widthPixels -= PREF_NOTCH_SIZE;
|
||||
if(activity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
|
||||
displayMetrics.heightPixels -= PREF_NOTCH_SIZE;
|
||||
else
|
||||
displayMetrics.widthPixels -= PREF_NOTCH_SIZE;
|
||||
}
|
||||
}
|
||||
currentDisplayMetrics = displayMetrics;
|
||||
|
||||
return displayMetrics;
|
||||
}
|
||||
|
||||
|
|
@ -550,27 +523,45 @@ public final class Tools {
|
|||
}
|
||||
File destinationFile = new File(output, outputName);
|
||||
if(!destinationFile.exists() || overwrite){
|
||||
IOUtils.copy(ctx.getAssets().open(fileName), new FileOutputStream(destinationFile));
|
||||
try(InputStream inputStream = ctx.getAssets().open(fileName)) {
|
||||
try (OutputStream outputStream = new FileOutputStream(destinationFile)){
|
||||
IOUtils.copy(inputStream, outputStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String printToString(Throwable throwable) {
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
PrintWriter printWriter = new PrintWriter(stringWriter);
|
||||
throwable.printStackTrace(printWriter);
|
||||
printWriter.close();
|
||||
return stringWriter.toString();
|
||||
}
|
||||
|
||||
public static void showError(Context ctx, Throwable e) {
|
||||
showError(ctx, e, false);
|
||||
}
|
||||
|
||||
public static void showError(final Context ctx, final Throwable e, final boolean exitIfOk) {
|
||||
showError(ctx, R.string.global_error, e, exitIfOk, false);
|
||||
showError(ctx, R.string.global_error, null ,e, exitIfOk, false);
|
||||
}
|
||||
public static void showError(final Context ctx, final int rolledMessage, final Throwable e) {
|
||||
showError(ctx, R.string.global_error, ctx.getString(rolledMessage), e, false, false);
|
||||
}
|
||||
public static void showError(final Context ctx, final String rolledMessage, final Throwable e) {
|
||||
showError(ctx, R.string.global_error, rolledMessage, e, false, false);
|
||||
}
|
||||
|
||||
public static void showError(final Context ctx, final int titleId, final Throwable e, final boolean exitIfOk) {
|
||||
showError(ctx, titleId, e, exitIfOk, false);
|
||||
showError(ctx, titleId, null, e, exitIfOk, false);
|
||||
}
|
||||
|
||||
private static void showError(final Context ctx, final int titleId, final Throwable e, final boolean exitIfOk, final boolean showMore) {
|
||||
private static void showError(final Context ctx, final int titleId, final String rolledMessage, final Throwable e, final boolean exitIfOk, final boolean showMore) {
|
||||
e.printStackTrace();
|
||||
|
||||
Runnable runnable = () -> {
|
||||
final String errMsg = showMore ? Log.getStackTraceString(e): e.getMessage();
|
||||
final String errMsg = showMore ? printToString(e) : rolledMessage != null ? rolledMessage : e.getMessage();
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder((Context) ctx)
|
||||
.setTitle(titleId)
|
||||
.setMessage(errMsg)
|
||||
|
|
@ -583,7 +574,7 @@ public final class Tools {
|
|||
}
|
||||
}
|
||||
})
|
||||
.setNegativeButton(showMore ? R.string.error_show_less : R.string.error_show_more, (DialogInterface.OnClickListener) (p1, p2) -> showError(ctx, titleId, e, exitIfOk, !showMore))
|
||||
.setNegativeButton(showMore ? R.string.error_show_less : R.string.error_show_more, (DialogInterface.OnClickListener) (p1, p2) -> showError(ctx, titleId, rolledMessage, e, exitIfOk, !showMore))
|
||||
.setNeutralButton(android.R.string.copy, (DialogInterface.OnClickListener) (p1, p2) -> {
|
||||
ClipboardManager mgr = (ClipboardManager) ctx.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
mgr.setPrimaryClip(ClipData.newPlainText("error", Log.getStackTraceString(e)));
|
||||
|
|
@ -674,11 +665,6 @@ public final class Tools {
|
|||
public static JMinecraftVersionList.Version getVersionInfo(String versionName, boolean skipInheriting) {
|
||||
try {
|
||||
JMinecraftVersionList.Version customVer = Tools.GLOBAL_GSON.fromJson(read(DIR_HOME_VERSION + "/" + versionName + "/" + versionName + ".json"), JMinecraftVersionList.Version.class);
|
||||
for (DependentLibrary lib : customVer.libraries) {
|
||||
if (lib.name.startsWith(LIBNAME_OPTIFINE)) {
|
||||
customVer.optifineLib = lib;
|
||||
}
|
||||
}
|
||||
if (skipInheriting || customVer.inheritsFrom == null || customVer.inheritsFrom.equals(customVer.id)) {
|
||||
return customVer;
|
||||
} else {
|
||||
|
|
@ -698,7 +684,7 @@ public final class Tools {
|
|||
insertSafety(inheritsVer, customVer,
|
||||
"assetIndex", "assets", "id",
|
||||
"mainClass", "minecraftArguments",
|
||||
"optifineLib", "releaseTime", "time", "type"
|
||||
"releaseTime", "time", "type"
|
||||
);
|
||||
|
||||
List<DependentLibrary> libList = new ArrayList<DependentLibrary>(Arrays.asList(inheritsVer.libraries));
|
||||
|
|
@ -719,7 +705,7 @@ public final class Tools {
|
|||
}
|
||||
}
|
||||
|
||||
libList.add(lib);
|
||||
libList.add(0, lib);
|
||||
}
|
||||
} finally {
|
||||
inheritsVer.libraries = libList.toArray(new DependentLibrary[0]);
|
||||
|
|
@ -785,87 +771,20 @@ public final class Tools {
|
|||
}
|
||||
}
|
||||
|
||||
public static String convertStream(InputStream inputStream) throws IOException {
|
||||
return convertStream(inputStream, Charset.forName("UTF-8"));
|
||||
}
|
||||
|
||||
public static String convertStream(InputStream inputStream, Charset charset) throws IOException {
|
||||
StringBuilder out = new StringBuilder();
|
||||
int len;
|
||||
byte[] buf = new byte[512];
|
||||
while((len = inputStream.read(buf))!=-1) {
|
||||
out.append(new String(buf, 0, len, charset));
|
||||
}
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
public static File lastFileModified(String dir) {
|
||||
File fl = new File(dir);
|
||||
|
||||
File[] files = fl.listFiles(File::isFile);
|
||||
if(files == null) {
|
||||
return null;
|
||||
// The patch was a bit wrong...
|
||||
// So, this may be null, why? Because this folder may not exist yet
|
||||
// Or it may not have any files...
|
||||
// Doesn't matter. We must check for that in the crash fragment.
|
||||
}
|
||||
long lastMod = Long.MIN_VALUE;
|
||||
File choice = null;
|
||||
for (File file : files) {
|
||||
if (file.lastModified() > lastMod) {
|
||||
choice = file;
|
||||
lastMod = file.lastModified();
|
||||
}
|
||||
}
|
||||
return choice;
|
||||
}
|
||||
|
||||
|
||||
public static String read(InputStream is) throws IOException {
|
||||
StringBuilder out = new StringBuilder();
|
||||
int len;
|
||||
byte[] buf = new byte[512];
|
||||
while((len = is.read(buf))!=-1) {
|
||||
out.append(new String(buf, 0, len));
|
||||
}
|
||||
return out.toString();
|
||||
String readResult = IOUtils.toString(is, StandardCharsets.UTF_8);
|
||||
is.close();
|
||||
return readResult;
|
||||
}
|
||||
|
||||
public static String read(String path) throws IOException {
|
||||
return read(new FileInputStream(path));
|
||||
}
|
||||
|
||||
public static void write(String path, byte[] content) throws IOException
|
||||
{
|
||||
File outPath = new File(path);
|
||||
outPath.getParentFile().mkdirs();
|
||||
outPath.createNewFile();
|
||||
|
||||
BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream(path));
|
||||
fos.write(content, 0, content.length);
|
||||
fos.close();
|
||||
}
|
||||
|
||||
public static void write(String path, String content) throws IOException {
|
||||
write(path, content.getBytes());
|
||||
}
|
||||
|
||||
public static byte[] loadFromAssetToByte(Context ctx, String inFile) {
|
||||
byte[] buffer = null;
|
||||
|
||||
try {
|
||||
InputStream stream = ctx.getAssets().open(inFile);
|
||||
|
||||
int size = stream.available();
|
||||
buffer = new byte[size];
|
||||
stream.read(buffer);
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
// Handle exceptions here
|
||||
e.printStackTrace();
|
||||
try(FileOutputStream outStream = new FileOutputStream(path)) {
|
||||
IOUtils.write(content, outStream);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public static void downloadFile(String urlInput, String nameOutput) throws IOException {
|
||||
|
|
@ -927,22 +846,14 @@ public final class Tools {
|
|||
}
|
||||
|
||||
public static String getFileName(Context ctx, Uri uri) {
|
||||
String result = null;
|
||||
if (uri.getScheme().equals("content")) {
|
||||
try (Cursor cursor = ctx.getContentResolver().query(uri, null, null, null, null)) {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (result == null) {
|
||||
result = uri.getPath();
|
||||
int cut = result.lastIndexOf('/');
|
||||
if (cut != -1) {
|
||||
result = result.substring(cut + 1);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
Cursor c = ctx.getContentResolver().query(uri, null, null, null, null);
|
||||
if(c == null) return uri.getLastPathSegment(); // idk myself but it happens on asus file manager
|
||||
c.moveToFirst();
|
||||
int columnIndex = c.getColumnIndex(OpenableColumns.DISPLAY_NAME);
|
||||
if(columnIndex == -1) return uri.getLastPathSegment();
|
||||
String fileName = c.getString(columnIndex);
|
||||
c.close();
|
||||
return fileName;
|
||||
}
|
||||
|
||||
/** Swap the main fragment with another */
|
||||
|
|
@ -1019,7 +930,9 @@ public final class Tools {
|
|||
final String name = getFileName(activity, uri);
|
||||
final File modInstallerFile = new File(activity.getCacheDir(), name);
|
||||
FileOutputStream fos = new FileOutputStream(modInstallerFile);
|
||||
IOUtils.copy(activity.getContentResolver().openInputStream(uri), fos);
|
||||
InputStream input = activity.getContentResolver().openInputStream(uri);
|
||||
IOUtils.copy(input, fos);
|
||||
input.close();
|
||||
fos.close();
|
||||
activity.runOnUiThread(() -> {
|
||||
alertDialog.dismiss();
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ public class Touchpad extends FrameLayout implements GrabListener{
|
|||
private final ImageView mMousePointerImageView = new ImageView(getContext());
|
||||
/* Detect a classic android Tap */
|
||||
private final GestureDetector mSingleTapDetector = new GestureDetector(getContext(), new SingleTapConfirm());
|
||||
private final Handler uiThreadHandler = new Handler();
|
||||
/* Resolution scaler option, allow downsizing a window */
|
||||
private final float mScaleFactor = DEFAULT_PREF.getInt("resolutionRatio",100)/100f;
|
||||
/* Current pointer ID to move the mouse */
|
||||
|
|
@ -181,7 +180,7 @@ public class Touchpad extends FrameLayout implements GrabListener{
|
|||
|
||||
@Override
|
||||
public void onGrabState(boolean isGrabbing) {
|
||||
uiThreadHandler.post(()->updateGrabState(isGrabbing));
|
||||
post(()->updateGrabState(isGrabbing));
|
||||
}
|
||||
private void updateGrabState(boolean isGrabbing) {
|
||||
if(!isGrabbing) {
|
||||
|
|
|
|||
|
|
@ -3,12 +3,14 @@ package net.kdt.pojavlaunch.authenticator.microsoft;
|
|||
import static net.kdt.pojavlaunch.PojavApplication.sExecutorService;
|
||||
|
||||
import android.os.Looper;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.kdt.mcgui.ProgressLayout;
|
||||
|
||||
import net.kdt.pojavlaunch.R;
|
||||
import net.kdt.pojavlaunch.Tools;
|
||||
import net.kdt.pojavlaunch.value.MinecraftAccount;
|
||||
import net.kdt.pojavlaunch.authenticator.listener.*;
|
||||
|
|
@ -39,6 +41,15 @@ public class MicrosoftBackgroundLogin {
|
|||
private final boolean mIsRefresh;
|
||||
private final String mAuthCode;
|
||||
private final android.os.Handler mHandler = new android.os.Handler(Looper.getMainLooper());
|
||||
private static final Map<Long, Integer> XSTS_ERRORS;
|
||||
static {
|
||||
XSTS_ERRORS = new ArrayMap<>();
|
||||
XSTS_ERRORS.put(2148916233L, R.string.xerr_no_account);
|
||||
XSTS_ERRORS.put(2148916235L, R.string.xerr_not_available);
|
||||
XSTS_ERRORS.put(2148916236L ,R.string.xerr_adult_verification);
|
||||
XSTS_ERRORS.put(2148916237L ,R.string.xerr_adult_verification);
|
||||
XSTS_ERRORS.put(2148916238L ,R.string.xerr_child);
|
||||
}
|
||||
|
||||
/* Fields used to fill the account */
|
||||
public boolean isRefresh;
|
||||
|
|
@ -211,6 +222,15 @@ public class MicrosoftBackgroundLogin {
|
|||
Log.i("MicrosoftLogin","Xbl Xsts = " + token + "; Uhs = " + uhs);
|
||||
return new String[]{uhs, token};
|
||||
//acquireMinecraftToken(uhs,jo.getString("Token"));
|
||||
}else if(conn.getResponseCode() == 401) {
|
||||
String responseContents = Tools.read(conn.getErrorStream());
|
||||
JSONObject jo = new JSONObject(responseContents);
|
||||
long xerr = jo.optLong("XErr", -1);
|
||||
Integer locale_id = XSTS_ERRORS.get(xerr);
|
||||
if(locale_id != null) {
|
||||
throw new PresentedException(new RuntimeException(responseContents), locale_id);
|
||||
}
|
||||
throw new PresentedException(new RuntimeException(responseContents), R.string.xerr_unknown, xerr);
|
||||
}else{
|
||||
throwResponseError(conn);
|
||||
}
|
||||
|
|
@ -274,8 +294,8 @@ public class MicrosoftBackgroundLogin {
|
|||
}else{
|
||||
Log.i("MicrosoftLogin","It seems that this Microsoft Account does not own the game.");
|
||||
doesOwnGame = false;
|
||||
|
||||
throwResponseError(conn);
|
||||
throw new PresentedException(new RuntimeException(conn.getResponseMessage()), R.string.minecraft_not_owned);
|
||||
//throwResponseError(conn);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -289,7 +309,7 @@ public class MicrosoftBackgroundLogin {
|
|||
|
||||
|
||||
/** Set common properties, and enable interactivity if desired */
|
||||
private static void setCommonProperties(HttpURLConnection conn, String formData, boolean interactive){
|
||||
private static void setCommonProperties(HttpURLConnection conn, String formData, boolean interactive) {
|
||||
conn.setRequestProperty("Content-Type", "application/json");
|
||||
conn.setRequestProperty("Accept", "application/json");
|
||||
conn.setRequestProperty("charset", "utf-8");
|
||||
|
|
@ -321,6 +341,9 @@ public class MicrosoftBackgroundLogin {
|
|||
|
||||
private void throwResponseError(HttpURLConnection conn) throws IOException {
|
||||
Log.i("MicrosoftLogin", "Error code: " + conn.getResponseCode() + ": " + conn.getResponseMessage());
|
||||
if(conn.getResponseCode() == 429) {
|
||||
throw new PresentedException(R.string.microsoft_login_retry_later);
|
||||
}
|
||||
throw new RuntimeException(conn.getResponseMessage());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
package net.kdt.pojavlaunch.authenticator.microsoft;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
public class PresentedException extends RuntimeException {
|
||||
final int localizationStringId;
|
||||
final Object[] extraArgs;
|
||||
|
||||
public PresentedException(int localizationStringId, Object... extraArgs) {
|
||||
this.localizationStringId = localizationStringId;
|
||||
this.extraArgs = extraArgs;
|
||||
}
|
||||
|
||||
public PresentedException(Throwable throwable, int localizationStringId, Object... extraArgs) {
|
||||
super(throwable);
|
||||
this.localizationStringId = localizationStringId;
|
||||
this.extraArgs = extraArgs;
|
||||
}
|
||||
|
||||
public String toString(Context context) {
|
||||
return context.getString(localizationStringId, extraArgs);
|
||||
}
|
||||
}
|
||||
|
|
@ -160,6 +160,7 @@ public class ColorSelector implements HueSelectionListener, RectangleSelectionLi
|
|||
public void setAlphaEnabled(boolean alphaEnabled){
|
||||
mAlphaEnabled = alphaEnabled;
|
||||
mAlphaView.setVisibility(alphaEnabled ? View.VISIBLE : View.GONE);
|
||||
mAlphaView.setAlpha(255);
|
||||
}
|
||||
|
||||
private void notifyColorSelector(int color){
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
package net.kdt.pojavlaunch.customcontrols;
|
||||
|
||||
public interface ControlButtonMenuListener {
|
||||
void onClickedMenu();
|
||||
}
|
||||
|
|
@ -29,6 +29,7 @@ public class ControlData {
|
|||
public static final int SPECIALBTN_MOUSEMID = -6;
|
||||
public static final int SPECIALBTN_SCROLLUP = -7;
|
||||
public static final int SPECIALBTN_SCROLLDOWN = -8;
|
||||
public static final int SPECIALBTN_MENU = -9;
|
||||
|
||||
private static ControlData[] SPECIAL_BUTTONS;
|
||||
private static String[] SPECIAL_BUTTON_NAME_ARRAY;
|
||||
|
|
@ -65,7 +66,8 @@ public class ControlData {
|
|||
|
||||
new ControlData("MID", new int[]{SPECIALBTN_MOUSEMID}, "${margin}", "${margin}"),
|
||||
new ControlData("SCROLLUP", new int[]{SPECIALBTN_SCROLLUP}, "${margin}", "${margin}"),
|
||||
new ControlData("SCROLLDOWN", new int[]{SPECIALBTN_SCROLLDOWN}, "${margin}", "${margin}")
|
||||
new ControlData("SCROLLDOWN", new int[]{SPECIALBTN_SCROLLDOWN}, "${margin}", "${margin}"),
|
||||
new ControlData("MENU", new int[]{SPECIALBTN_MENU}, "${margin}", "${margin}")
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import com.google.gson.*;
|
|||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
import net.kdt.pojavlaunch.*;
|
||||
|
|
@ -26,12 +27,18 @@ import net.kdt.pojavlaunch.prefs.*;
|
|||
|
||||
public class ControlLayout extends FrameLayout {
|
||||
protected CustomControls mLayout;
|
||||
/* Accessible when inside the game by ControlInterface implementations, cached for perf. */
|
||||
private MinecraftGLSurface mGameSurface = null;
|
||||
|
||||
/* Cache to buttons for performance purposes */
|
||||
private List<ControlInterface> mButtons;
|
||||
private boolean mModifiable = false;
|
||||
private CustomControlsActivity mActivity;
|
||||
private boolean mControlVisible = false;
|
||||
|
||||
private EditControlPopup mControlPopup = null;
|
||||
private ControlHandleView mHandleView;
|
||||
private ControlButtonMenuListener mMenuListener;
|
||||
public ActionRow actionRow = null;
|
||||
|
||||
public ControlLayout(Context ctx) {
|
||||
|
|
@ -87,6 +94,8 @@ public class ControlLayout extends FrameLayout {
|
|||
mLayout.scaledAt = LauncherPreferences.PREF_BUTTONSIZE;
|
||||
|
||||
setModified(false);
|
||||
mButtons = null;
|
||||
getButtonChildren(); // Force refresh
|
||||
} // loadLayout
|
||||
|
||||
//CONTROL BUTTON
|
||||
|
|
@ -222,14 +231,17 @@ public class ControlLayout extends FrameLayout {
|
|||
|
||||
}
|
||||
|
||||
public ArrayList<ControlInterface> getButtonChildren(){
|
||||
ArrayList<ControlInterface> children = new ArrayList<>();
|
||||
for(int i=0; i<getChildCount(); ++i){
|
||||
View v = getChildAt(i);
|
||||
if(v instanceof ControlInterface)
|
||||
children.add(((ControlInterface) v));
|
||||
public List<ControlInterface> getButtonChildren(){
|
||||
if(mModifiable || mButtons == null){
|
||||
mButtons = new ArrayList<>();
|
||||
for(int i=0; i<getChildCount(); ++i){
|
||||
View v = getChildAt(i);
|
||||
if(v instanceof ControlInterface)
|
||||
mButtons.add(((ControlInterface) v));
|
||||
}
|
||||
}
|
||||
return children;
|
||||
|
||||
return mButtons;
|
||||
}
|
||||
|
||||
public void refreshControlButtonPositions(){
|
||||
|
|
@ -287,48 +299,42 @@ public class ControlLayout extends FrameLayout {
|
|||
|
||||
|
||||
HashMap<View, ControlInterface> mapTable = new HashMap<>();
|
||||
int[] location = new int[2];
|
||||
//While this is called onTouch, this should only be called from a ControlButton.
|
||||
public boolean onTouch(View v, MotionEvent ev) {
|
||||
ControlInterface lastControlButton = mapTable.get(v);
|
||||
|
||||
//Check if the action is cancelling, reset the lastControl button associated to the view
|
||||
if(ev.getActionMasked() == MotionEvent.ACTION_UP || ev.getActionMasked() == MotionEvent.ACTION_CANCEL){
|
||||
if(lastControlButton != null) lastControlButton.sendKeyPresses(false);
|
||||
mapTable.put(v, null);
|
||||
for(ControlInterface control : mapTable.values()){
|
||||
control.sendKeyPresses(false);
|
||||
}
|
||||
mapTable.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
if(ev.getActionMasked() != MotionEvent.ACTION_MOVE) return false;
|
||||
|
||||
//Optimization pass to avoid looking at all children again
|
||||
if(lastControlButton != null){
|
||||
if( ev.getRawX() > lastControlButton.getControlView().getX() && ev.getRawX() < lastControlButton.getControlView().getX() + lastControlButton.getControlView().getWidth() &&
|
||||
ev.getRawY() > lastControlButton.getControlView().getY() && ev.getRawY() < lastControlButton.getControlView().getY() + lastControlButton.getControlView().getHeight()){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//Release last keys
|
||||
if (lastControlButton != null) lastControlButton.sendKeyPresses(false);
|
||||
mapTable.put(v, null);
|
||||
|
||||
//Look for another SWIPEABLE button
|
||||
getLocationOnScreen(location);
|
||||
// Update the state of all swipeable buttons
|
||||
for(ControlInterface button : getButtonChildren()){
|
||||
if(!button.getProperties().isSwipeable) continue;
|
||||
|
||||
if( ev.getRawX() > button.getControlView().getX() && ev.getRawX() < button.getControlView().getX() + button.getControlView().getWidth() &&
|
||||
ev.getRawY() > button.getControlView().getY() && ev.getRawY() < button.getControlView().getY() + button.getControlView().getHeight()){
|
||||
if( ev.getRawX() > button.getControlView().getX() + location[0]
|
||||
&& ev.getRawX() - getGameSurface().getX() < button.getControlView().getX() + button.getControlView().getWidth() + location[0]
|
||||
&& ev.getRawY() > button.getControlView().getY()
|
||||
&& ev.getRawY() < button.getControlView().getY() + button.getControlView().getHeight()){
|
||||
|
||||
//Press the new key
|
||||
if(!button.equals(lastControlButton)){
|
||||
if(mapTable.get(button.getControlView()) == null){
|
||||
button.sendKeyPresses(true);
|
||||
|
||||
mapTable.put(v, button);
|
||||
mapTable.put(button.getControlView(), button);
|
||||
}
|
||||
}else{
|
||||
if(mapTable.get(button.getControlView()) != null){
|
||||
button.sendKeyPresses(false);
|
||||
mapTable.remove(button.getControlView());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -367,4 +373,30 @@ public class ControlLayout extends FrameLayout {
|
|||
mLayout.save(path);
|
||||
} catch (IOException e) {Log.e("ControlLayout", "Failed to save the layout at:" + path);}
|
||||
}
|
||||
|
||||
|
||||
public boolean hasMenuButton() {
|
||||
for(ControlInterface controlInterface : getButtonChildren()){
|
||||
for (int keycode : controlInterface.getProperties().keycodes) {
|
||||
if (keycode == ControlData.SPECIALBTN_MENU) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setMenuListener(ControlButtonMenuListener menuListener) {
|
||||
this.mMenuListener = menuListener;
|
||||
}
|
||||
|
||||
public void notifyAppMenu() {
|
||||
if(mMenuListener != null) mMenuListener.onClickedMenu();
|
||||
}
|
||||
|
||||
/** Cached getter for perf purposes */
|
||||
public MinecraftGLSurface getGameSurface(){
|
||||
if(mGameSurface == null){
|
||||
mGameSurface = findViewById(R.id.main_game_render_view);
|
||||
}
|
||||
return mGameSurface;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,17 +5,22 @@ import com.google.gson.JsonSyntaxException;
|
|||
import net.kdt.pojavlaunch.LwjglGlfwKeycode;
|
||||
import net.kdt.pojavlaunch.Tools;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.lwjgl.glfw.CallbackBridge;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class LayoutConverter {
|
||||
public static boolean convertLookType = false; //false = flat; true = classic
|
||||
public static CustomControls loadAndConvertIfNecessary(String jsonPath) throws IOException, JsonSyntaxException {
|
||||
|
||||
String jsonLayoutData = Tools.read(jsonPath);
|
||||
try {
|
||||
JSONObject layoutJobj = new JSONObject(jsonLayoutData);
|
||||
|
|
|
|||
|
|
@ -7,10 +7,12 @@ import android.view.*;
|
|||
import android.widget.*;
|
||||
|
||||
|
||||
import net.kdt.pojavlaunch.customcontrols.ControlButtonMenuListener;
|
||||
import net.kdt.pojavlaunch.customcontrols.ControlData;
|
||||
import net.kdt.pojavlaunch.customcontrols.ControlLayout;
|
||||
import net.kdt.pojavlaunch.customcontrols.handleview.*;
|
||||
import net.kdt.pojavlaunch.*;
|
||||
import net.kdt.pojavlaunch.prefs.LauncherPreferences;
|
||||
|
||||
import org.lwjgl.glfw.*;
|
||||
|
||||
|
|
@ -22,16 +24,19 @@ import static org.lwjgl.glfw.CallbackBridge.sendMouseButton;
|
|||
public class ControlButton extends TextView implements ControlInterface {
|
||||
private final Paint mRectPaint = new Paint();
|
||||
protected ControlData mProperties;
|
||||
private final ControlLayout mControlLayout;
|
||||
|
||||
protected boolean mIsToggled = false;
|
||||
protected boolean mIsPointerOutOfBounds = false;
|
||||
|
||||
public ControlButton(ControlLayout layout, ControlData properties) {
|
||||
super(layout.getContext());
|
||||
mControlLayout = layout;
|
||||
setGravity(Gravity.CENTER);
|
||||
setAllCaps(true);
|
||||
setAllCaps(LauncherPreferences.PREF_BUTTON_ALL_CAPS);
|
||||
setTextColor(Color.WHITE);
|
||||
setPadding(4, 4, 4, 4);
|
||||
setTextSize(14); // Nullify the default size setting
|
||||
|
||||
//setOnLongClickListener(this);
|
||||
|
||||
|
|
@ -104,8 +109,8 @@ public class ControlButton extends TextView implements ControlInterface {
|
|||
case MotionEvent.ACTION_MOVE:
|
||||
//Send the event to be taken as a mouse action
|
||||
if(getProperties().passThruEnabled && CallbackBridge.isGrabbing()){
|
||||
MinecraftGLSurface v = getControlLayoutParent().findViewById(R.id.main_game_render_view);
|
||||
if (v != null) v.dispatchTouchEvent(event);
|
||||
View gameSurface = getControlLayoutParent().getGameSurface();
|
||||
if(gameSurface != null) gameSurface.dispatchTouchEvent(event);
|
||||
}
|
||||
|
||||
//If out of bounds
|
||||
|
|
@ -144,8 +149,8 @@ public class ControlButton extends TextView implements ControlInterface {
|
|||
case MotionEvent.ACTION_CANCEL: // 3
|
||||
case MotionEvent.ACTION_POINTER_UP: // 6
|
||||
if(getProperties().passThruEnabled){
|
||||
MinecraftGLSurface v = getControlLayoutParent().findViewById(R.id.main_game_render_view);
|
||||
if (v != null) v.dispatchTouchEvent(event);
|
||||
View gameSurface = getControlLayoutParent().getGameSurface();
|
||||
if(gameSurface != null) gameSurface.dispatchTouchEvent(event);
|
||||
}
|
||||
if(mIsPointerOutOfBounds) getControlLayoutParent().onTouch(this, event);
|
||||
mIsPointerOutOfBounds = false;
|
||||
|
|
@ -182,6 +187,7 @@ public class ControlButton extends TextView implements ControlInterface {
|
|||
sendKeyPress(keycode, CallbackBridge.getCurrentMods(), isDown);
|
||||
CallbackBridge.setModifiers(keycode, isDown);
|
||||
}else{
|
||||
Log.i("punjabilauncher", "sendSpecialKey("+keycode+","+isDown+")");
|
||||
sendSpecialKey(keycode, isDown);
|
||||
}
|
||||
}
|
||||
|
|
@ -220,6 +226,9 @@ public class ControlButton extends TextView implements ControlInterface {
|
|||
case ControlData.SPECIALBTN_SCROLLUP:
|
||||
if (!isDown) CallbackBridge.sendScroll(0, -1d);
|
||||
break;
|
||||
case ControlData.SPECIALBTN_MENU:
|
||||
mControlLayout.notifyAppMenu();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -276,7 +276,7 @@ public interface ControlInterface extends View.OnLongClickListener {
|
|||
}
|
||||
|
||||
default void injectProperties(){
|
||||
getControlView().setTranslationZ(10);
|
||||
getControlView().post(() -> getControlView().setTranslationZ(10));
|
||||
}
|
||||
|
||||
/** Inject a touch listener on the view to make editing controls straight forward */
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import android.widget.Toast;
|
|||
import androidx.core.content.res.ResourcesCompat;
|
||||
import androidx.core.math.MathUtils;
|
||||
|
||||
import net.kdt.pojavlaunch.GrabListener;
|
||||
import net.kdt.pojavlaunch.LwjglGlfwKeycode;
|
||||
import net.kdt.pojavlaunch.R;
|
||||
import net.kdt.pojavlaunch.prefs.LauncherPreferences;
|
||||
|
|
@ -37,7 +38,7 @@ import static net.kdt.pojavlaunch.utils.MCOptionUtils.getMcScale;
|
|||
import static org.lwjgl.glfw.CallbackBridge.sendKeyPress;
|
||||
import static org.lwjgl.glfw.CallbackBridge.sendMouseButton;
|
||||
|
||||
public class Gamepad {
|
||||
public class Gamepad implements GrabListener {
|
||||
|
||||
/* Resolution scaler option, allow downsizing a window */
|
||||
private final float mScaleFactor = LauncherPreferences.DEFAULT_PREF.getInt("resolutionRatio",100)/100f;
|
||||
|
|
@ -67,7 +68,8 @@ public class Gamepad {
|
|||
private final GamepadMap mMenuMap = GamepadMap.getDefaultMenuMap();
|
||||
private GamepadMap mCurrentMap = mGameMap;
|
||||
|
||||
private boolean mLastGrabbingState = true;
|
||||
// The negation is to force trigger the onGrabState
|
||||
private boolean isGrabbing = !CallbackBridge.isGrabbing();
|
||||
//private final boolean mModifierDigitalTriggers;
|
||||
private final boolean mModifierAnalogTriggers;
|
||||
private boolean mModifierSwappedAxis = true; //Triggers and right stick axis are swapped.
|
||||
|
|
@ -84,7 +86,6 @@ public class Gamepad {
|
|||
Choreographer.FrameCallback frameCallback = new Choreographer.FrameCallback() {
|
||||
@Override
|
||||
public void doFrame(long frameTimeNanos) {
|
||||
updateGrabbingState();
|
||||
tick(frameTimeNanos);
|
||||
mScreenChoreographer.postFrameCallback(this);
|
||||
}
|
||||
|
|
@ -129,6 +130,8 @@ public class Gamepad {
|
|||
placePointerView(CallbackBridge.physicalWidth/2, CallbackBridge.physicalHeight/2);
|
||||
|
||||
((ViewGroup)contextView.getParent()).addView(mPointerImageView);
|
||||
|
||||
CallbackBridge.addGrabListener(this);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -278,7 +281,7 @@ public class Gamepad {
|
|||
private void tick(long frameTimeNanos){
|
||||
//update mouse position
|
||||
if(mLastHorizontalValue != 0 || mLastVerticalValue != 0){
|
||||
GamepadJoystick currentJoystick = mLastGrabbingState ? mLeftJoystick : mRightJoystick;
|
||||
GamepadJoystick currentJoystick = isGrabbing ? mLeftJoystick : mRightJoystick;
|
||||
|
||||
double acceleration = (mMouseMagnitude - currentJoystick.getDeadzone()) / (1 - currentJoystick.getDeadzone());
|
||||
acceleration = Math.pow(acceleration, MOUSE_MAX_ACCELERATION);
|
||||
|
|
@ -294,7 +297,7 @@ public class Gamepad {
|
|||
CallbackBridge.mouseX += deltaX;
|
||||
CallbackBridge.mouseY -= deltaY;
|
||||
|
||||
if(!mLastGrabbingState){
|
||||
if(!isGrabbing){
|
||||
CallbackBridge.mouseX = MathUtils.clamp(CallbackBridge.mouseX, 0, CallbackBridge.windowWidth);
|
||||
CallbackBridge.mouseY = MathUtils.clamp(CallbackBridge.mouseY, 0, CallbackBridge.windowHeight);
|
||||
placePointerView((int) (CallbackBridge.mouseX / mScaleFactor), (int) (CallbackBridge.mouseY/ mScaleFactor));
|
||||
|
|
@ -311,36 +314,8 @@ public class Gamepad {
|
|||
mLastFrameTime = frameTimeNanos;
|
||||
}
|
||||
|
||||
/** Update the grabbing state, and change the currentMap, mouse position and sensibility */
|
||||
private void updateGrabbingState() {
|
||||
boolean lastGrabbingValue = mLastGrabbingState;
|
||||
mLastGrabbingState = CallbackBridge.isGrabbing();
|
||||
if(lastGrabbingValue == mLastGrabbingState) return;
|
||||
|
||||
// Switch grabbing state then
|
||||
mCurrentMap.resetPressedState();
|
||||
if(mLastGrabbingState){
|
||||
mCurrentMap = mGameMap;
|
||||
mPointerImageView.setVisibility(View.INVISIBLE);
|
||||
mMouseSensitivity = 18;
|
||||
return;
|
||||
}
|
||||
|
||||
mCurrentMap = mMenuMap;
|
||||
sendDirectionalKeycode(mCurrentJoystickDirection, false, mGameMap); // removing what we were doing
|
||||
|
||||
mMouse_x = CallbackBridge.windowWidth/2;
|
||||
mMouse_y = CallbackBridge.windowHeight/2;
|
||||
CallbackBridge.sendCursorPos(mMouse_x, mMouse_y);
|
||||
placePointerView(CallbackBridge.physicalWidth/2, CallbackBridge.physicalHeight/2);
|
||||
mPointerImageView.setVisibility(View.VISIBLE);
|
||||
// Sensitivity in menu is MC and HARDWARE resolution dependent
|
||||
mMouseSensitivity = 19 * mScaleFactor / mSensitivityFactor;
|
||||
|
||||
}
|
||||
|
||||
private void updateMouseJoystick(MotionEvent event){
|
||||
GamepadJoystick currentJoystick = mLastGrabbingState ? mRightJoystick : mLeftJoystick;
|
||||
GamepadJoystick currentJoystick = isGrabbing ? mRightJoystick : mLeftJoystick;
|
||||
float horizontalValue = currentJoystick.getHorizontalAxis(event);
|
||||
float verticalValue = currentJoystick.getVerticalAxis(event);
|
||||
if(horizontalValue != mLastHorizontalValue || verticalValue != mLastVerticalValue){
|
||||
|
|
@ -362,7 +337,7 @@ public class Gamepad {
|
|||
}
|
||||
|
||||
private void updateDirectionalJoystick(MotionEvent event){
|
||||
GamepadJoystick currentJoystick = mLastGrabbingState ? mLeftJoystick : mRightJoystick;
|
||||
GamepadJoystick currentJoystick = isGrabbing ? mLeftJoystick : mRightJoystick;
|
||||
|
||||
int lastJoystickDirection = mCurrentJoystickDirection;
|
||||
mCurrentJoystickDirection = currentJoystick.getHeightDirection(event);
|
||||
|
|
@ -446,4 +421,32 @@ public class Gamepad {
|
|||
mPointerImageView.setX(x - mPointerImageView.getWidth()/2);
|
||||
mPointerImageView.setY(y - mPointerImageView.getHeight()/2);
|
||||
}
|
||||
|
||||
/** Update the grabbing state, and change the currentMap, mouse position and sensibility */
|
||||
@Override
|
||||
public void onGrabState(boolean isGrabbing) {
|
||||
boolean lastGrabbingValue = this.isGrabbing;
|
||||
this.isGrabbing = isGrabbing;
|
||||
if(lastGrabbingValue == isGrabbing) return;
|
||||
|
||||
// Switch grabbing state then
|
||||
mCurrentMap.resetPressedState();
|
||||
if(isGrabbing){
|
||||
mCurrentMap = mGameMap;
|
||||
mPointerImageView.setVisibility(View.INVISIBLE);
|
||||
mMouseSensitivity = 18;
|
||||
return;
|
||||
}
|
||||
|
||||
mCurrentMap = mMenuMap;
|
||||
sendDirectionalKeycode(mCurrentJoystickDirection, false, mGameMap); // removing what we were doing
|
||||
|
||||
mMouse_x = CallbackBridge.windowWidth/2;
|
||||
mMouse_y = CallbackBridge.windowHeight/2;
|
||||
CallbackBridge.sendCursorPos(mMouse_x, mMouse_y);
|
||||
placePointerView(CallbackBridge.physicalWidth/2, CallbackBridge.physicalHeight/2);
|
||||
mPointerImageView.setVisibility(View.VISIBLE);
|
||||
// Sensitivity in menu is MC and HARDWARE resolution dependent
|
||||
mMouseSensitivity = 19 * mScaleFactor / mSensitivityFactor;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ public class ControlHandleView extends View {
|
|||
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(size, size);
|
||||
setLayoutParams(params);
|
||||
setBackground(mDrawable);
|
||||
setElevation(3);
|
||||
setTranslationZ(10.5F);
|
||||
}
|
||||
|
||||
public void setControlButton(ControlInterface controlInterface){
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import static net.kdt.pojavlaunch.Tools.currentDisplayMetrics;
|
|||
import android.animation.ObjectAnimator;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
|
|
@ -63,10 +64,13 @@ public class EditControlPopup {
|
|||
if(internalChanges) return;
|
||||
|
||||
internalChanges = true;
|
||||
if(right != (int)safeParseFloat(mWidthEditText.getText().toString())){
|
||||
int width = (int)(safeParseFloat(mWidthEditText.getText().toString()));
|
||||
|
||||
if(width >= 0 && Math.abs(right - width) > 1){
|
||||
mWidthEditText.setText(String.valueOf(right - left));
|
||||
}
|
||||
if(bottom != (int) safeParseFloat(mHeightEditText.getText().toString())){
|
||||
int height = (int)(safeParseFloat(mHeightEditText.getText().toString()));
|
||||
if(height >= 0 && Math.abs(bottom - height) > 1){
|
||||
mHeightEditText.setText(String.valueOf(bottom - top));
|
||||
}
|
||||
|
||||
|
|
@ -98,7 +102,8 @@ public class EditControlPopup {
|
|||
|
||||
mColorSelector = new ColorSelector(context, parent, null);
|
||||
mColorSelector.getRootView().setElevation(11);
|
||||
mColorSelector.getRootView().setX(-context.getResources().getDimensionPixelOffset(R.dimen._230sdp));
|
||||
mColorSelector.getRootView().setTranslationZ(11);
|
||||
mColorSelector.getRootView().setX(-context.getResources().getDimensionPixelOffset(R.dimen._280sdp));
|
||||
|
||||
mEditPopupAnimator = ObjectAnimator.ofFloat(mScrollView, "x", 0).setDuration(1000);
|
||||
mColorEditorAnimator = ObjectAnimator.ofFloat(mColorSelector.getRootView(), "x", 0).setDuration(1000);
|
||||
|
|
@ -107,7 +112,8 @@ public class EditControlPopup {
|
|||
mColorEditorAnimator.setInterpolator(decelerate);
|
||||
|
||||
mScrollView.setElevation(10);
|
||||
mScrollView.setX(-context.getResources().getDimensionPixelOffset(R.dimen._230sdp));
|
||||
mScrollView.setTranslationZ(10);
|
||||
mScrollView.setX(-context.getResources().getDimensionPixelOffset(R.dimen._280sdp));
|
||||
|
||||
bindLayout();
|
||||
loadAdapter();
|
||||
|
|
@ -163,8 +169,7 @@ public class EditControlPopup {
|
|||
}
|
||||
|
||||
mDisplayingColor = true;
|
||||
if(color != -1)
|
||||
mColorSelector.show(color);
|
||||
mColorSelector.show(color == -1 ? Color.WHITE : color);
|
||||
}
|
||||
|
||||
/** Slide out the layout */
|
||||
|
|
@ -211,7 +216,6 @@ public class EditControlPopup {
|
|||
mAdapter = new ArrayAdapter<>(mRootView.getContext(), R.layout.item_centered_textview);
|
||||
mSpecialArray = ControlData.buildSpecialButtonArray();
|
||||
for (int i = 0; i < mSpecialArray.length; i++) {
|
||||
//TODO this will break for sure
|
||||
mSpecialArray[i] = "SPECIAL_" + mSpecialArray[i];
|
||||
}
|
||||
Collections.reverse(Arrays.asList(mSpecialArray));
|
||||
|
|
@ -386,8 +390,11 @@ public class EditControlPopup {
|
|||
public void afterTextChanged(Editable s) {
|
||||
if(internalChanges) return;
|
||||
|
||||
mCurrentlyEditedButton.getProperties().setWidth(safeParseFloat(s.toString()));
|
||||
mCurrentlyEditedButton.updateProperties();
|
||||
float width = safeParseFloat(s.toString());
|
||||
if(width >= 0){
|
||||
mCurrentlyEditedButton.getProperties().setWidth(width);
|
||||
mCurrentlyEditedButton.updateProperties();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -401,8 +408,11 @@ public class EditControlPopup {
|
|||
public void afterTextChanged(Editable s) {
|
||||
if(internalChanges) return;
|
||||
|
||||
mCurrentlyEditedButton.getProperties().setHeight(safeParseFloat(s.toString()));
|
||||
mCurrentlyEditedButton.updateProperties();
|
||||
float height = safeParseFloat(s.toString());
|
||||
if(height >= 0){
|
||||
mCurrentlyEditedButton.getProperties().setHeight(height);
|
||||
mCurrentlyEditedButton.updateProperties();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -522,7 +532,7 @@ public class EditControlPopup {
|
|||
}
|
||||
|
||||
private float safeParseFloat(String string){
|
||||
float out = 10; // 10 px as a fallback
|
||||
float out = -1; // -1
|
||||
try {
|
||||
out = Float.parseFloat(string);
|
||||
}catch (NumberFormatException e){
|
||||
|
|
|
|||
|
|
@ -94,11 +94,8 @@ public class TouchCharInput extends androidx.appcompat.widget.AppCompatEditText
|
|||
*/
|
||||
public boolean switchKeyboardState(){
|
||||
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(INPUT_METHOD_SERVICE);
|
||||
//If an hard keyboard is present, never trigger the soft one
|
||||
if(hasFocus()
|
||||
|| (getResources().getConfiguration().keyboard == Configuration.KEYBOARD_QWERTY
|
||||
&& getResources().getConfiguration().hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES)){
|
||||
imm.hideSoftInputFromWindow(getWindowToken(), 0);
|
||||
// Allow, regardless of whether or not a hardware keyboard is declared
|
||||
if(hasFocus()){
|
||||
clear();
|
||||
disable();
|
||||
return false;
|
||||
|
|
@ -130,7 +127,6 @@ public class TouchCharInput extends androidx.appcompat.widget.AppCompatEditText
|
|||
setFocusable(true);
|
||||
setVisibility(VISIBLE);
|
||||
requestFocus();
|
||||
|
||||
}
|
||||
|
||||
/** Lose ability to exist, take focus and have some text being input */
|
||||
|
|
@ -139,6 +135,7 @@ public class TouchCharInput extends androidx.appcompat.widget.AppCompatEditText
|
|||
setVisibility(GONE);
|
||||
clearFocus();
|
||||
setEnabled(false);
|
||||
//setFocusable(false);
|
||||
}
|
||||
|
||||
/** Send the enter key. */
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
package net.kdt.pojavlaunch.fragments;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageButton;
|
||||
|
|
@ -19,6 +21,8 @@ import net.kdt.pojavlaunch.extra.ExtraConstants;
|
|||
import net.kdt.pojavlaunch.extra.ExtraCore;
|
||||
import net.kdt.pojavlaunch.progresskeeper.ProgressKeeper;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class MainMenuFragment extends Fragment {
|
||||
public static final String TAG = "MainMenuFragment";
|
||||
|
||||
|
|
@ -31,6 +35,7 @@ public class MainMenuFragment extends Fragment {
|
|||
Button mNewsButton = view.findViewById(R.id.news_button);
|
||||
Button mCustomControlButton = view.findViewById(R.id.custom_control_button);
|
||||
Button mInstallJarButton = view.findViewById(R.id.install_jar_button);
|
||||
Button mShareLogsButton = view.findViewById(R.id.share_logs_button);
|
||||
|
||||
ImageButton mEditProfileButton = view.findViewById(R.id.edit_profile_button);
|
||||
Button mPlayButton = view.findViewById(R.id.play_button);
|
||||
|
|
@ -46,6 +51,19 @@ public class MainMenuFragment extends Fragment {
|
|||
|
||||
mPlayButton.setOnClickListener(v -> ExtraCore.setValue(ExtraConstants.LAUNCH_GAME, true));
|
||||
|
||||
mShareLogsButton.setOnClickListener((v) -> {
|
||||
Uri contentUri = DocumentsContract.buildDocumentUri(getString(R.string.storageProviderAuthorities), Tools.DIR_GAME_HOME + "/latestlog.txt");
|
||||
|
||||
Intent shareIntent = new Intent();
|
||||
shareIntent.setAction(Intent.ACTION_SEND);
|
||||
shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
|
||||
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
shareIntent.setType("text/plain");
|
||||
|
||||
Intent sendIntent = Intent.createChooser(shareIntent, "latestlog.txt");
|
||||
startActivity(sendIntent);
|
||||
});
|
||||
|
||||
}
|
||||
private void runInstallerWithConfirmation(boolean isCustomArgs) {
|
||||
if (ProgressKeeper.getTaskCount() == 0)
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ public class MultiRTUtils {
|
|||
public static Runtime read(String name) {
|
||||
if(sCache.containsKey(name)) return sCache.get(name);
|
||||
Runtime returnRuntime;
|
||||
File release = new File(RUNTIME_FOLDER,"/"+name+"/release");
|
||||
File release = new File(RUNTIME_FOLDER,name+"/release");
|
||||
if(!release.exists()) {
|
||||
return new Runtime(name);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,127 +0,0 @@
|
|||
package net.kdt.pojavlaunch.prefs;
|
||||
|
||||
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.DEFAULT_PREF;
|
||||
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_CONTROL_BOTTOM_OFFSET;
|
||||
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_CONTROL_LEFT_OFFSET;
|
||||
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_CONTROL_RIGHT_OFFSET;
|
||||
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_CONTROL_TOP_OFFSET;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import net.kdt.pojavlaunch.R;
|
||||
|
||||
/** Custom preference class displaying a dialog */
|
||||
public class ControlOffsetPreference extends Preference {
|
||||
|
||||
private AlertDialog mPreferenceDialog;
|
||||
|
||||
public ControlOffsetPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
public ControlOffsetPreference(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onClick() {
|
||||
mPreferenceDialog.show();
|
||||
|
||||
SeekBar topOffsetSeekbar = mPreferenceDialog.findViewById(R.id.control_offset_top_seekbar);
|
||||
SeekBar rightOffsetSeekbar = mPreferenceDialog.findViewById(R.id.control_offset_right_seekbar);
|
||||
SeekBar bottomOffsetSeekbar = mPreferenceDialog.findViewById(R.id.control_offset_bottom_seekbar);
|
||||
SeekBar leftOffsetSeekbar = mPreferenceDialog.findViewById(R.id.control_offset_left_seekbar);
|
||||
|
||||
TextView topOffsetTextView = mPreferenceDialog.findViewById(R.id.control_offset_top_textview);
|
||||
TextView rightOffsetTextView = mPreferenceDialog.findViewById(R.id.control_offset_right_textview);
|
||||
TextView bottomOffsetTextView = mPreferenceDialog.findViewById(R.id.control_offset_bottom_textview);
|
||||
TextView leftOffsetTextView = mPreferenceDialog.findViewById(R.id.control_offset_left_textview);
|
||||
|
||||
SeekBar.OnSeekBarChangeListener seekBarChangeListener = new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
|
||||
if(seekBar == topOffsetSeekbar){
|
||||
String text = String.format("%s %d%s", getContext().getString(R.string.control_top_offset), i, " px");
|
||||
topOffsetTextView.setText(text);
|
||||
return;
|
||||
}
|
||||
if(seekBar == rightOffsetSeekbar){
|
||||
String text = String.format("%s %d%s", getContext().getString(R.string.control_right_offset), i, " px");
|
||||
rightOffsetTextView.setText(text);
|
||||
return;
|
||||
}
|
||||
if(seekBar == bottomOffsetSeekbar){
|
||||
String text = String.format("%s %d%s", getContext().getString(R.string.control_bottom_offset), i, " px");
|
||||
bottomOffsetTextView.setText(text);
|
||||
return;
|
||||
}
|
||||
if(seekBar == leftOffsetSeekbar){
|
||||
String text = String.format("%s %d%s", getContext().getString(R.string.control_left_offset), i, " px");
|
||||
leftOffsetTextView.setText(text);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {}
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {}
|
||||
};
|
||||
|
||||
topOffsetSeekbar.setOnSeekBarChangeListener(seekBarChangeListener);
|
||||
rightOffsetSeekbar.setOnSeekBarChangeListener(seekBarChangeListener);
|
||||
bottomOffsetSeekbar.setOnSeekBarChangeListener(seekBarChangeListener);
|
||||
leftOffsetSeekbar.setOnSeekBarChangeListener(seekBarChangeListener);
|
||||
|
||||
topOffsetSeekbar.setProgress(PREF_CONTROL_TOP_OFFSET);
|
||||
rightOffsetSeekbar.setProgress(PREF_CONTROL_RIGHT_OFFSET);
|
||||
bottomOffsetSeekbar.setProgress(PREF_CONTROL_BOTTOM_OFFSET);
|
||||
leftOffsetSeekbar.setProgress(PREF_CONTROL_LEFT_OFFSET);
|
||||
|
||||
seekBarChangeListener.onProgressChanged(topOffsetSeekbar, PREF_CONTROL_TOP_OFFSET, false);
|
||||
seekBarChangeListener.onProgressChanged(rightOffsetSeekbar, PREF_CONTROL_RIGHT_OFFSET, false);
|
||||
seekBarChangeListener.onProgressChanged(bottomOffsetSeekbar, PREF_CONTROL_BOTTOM_OFFSET, false);
|
||||
seekBarChangeListener.onProgressChanged(leftOffsetSeekbar, PREF_CONTROL_LEFT_OFFSET, false);
|
||||
|
||||
// Custom writing to preferences
|
||||
mPreferenceDialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(view -> {
|
||||
DEFAULT_PREF.edit().putInt("controlTopOffset", topOffsetSeekbar.getProgress()).apply();
|
||||
DEFAULT_PREF.edit().putInt("controlRightOffset", rightOffsetSeekbar.getProgress()).apply();
|
||||
DEFAULT_PREF.edit().putInt("controlBottomOffset", bottomOffsetSeekbar.getProgress()).apply();
|
||||
DEFAULT_PREF.edit().putInt("controlLeftOffset", leftOffsetSeekbar.getProgress()).apply();
|
||||
|
||||
|
||||
mPreferenceDialog.dismiss();
|
||||
});
|
||||
}
|
||||
|
||||
private void init(){
|
||||
// Setup visual values
|
||||
if(getTitle() == null){
|
||||
setTitle(R.string.preference_control_offset_title);
|
||||
setSummary(R.string.preference_control_offset_description);
|
||||
}
|
||||
if(getIcon() == null){
|
||||
setIcon(android.R.drawable.radiobutton_off_background);
|
||||
}
|
||||
|
||||
// Prepare Alert dialog
|
||||
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getContext());
|
||||
dialogBuilder.setView(R.layout.dialog_control_offset_preference);
|
||||
dialogBuilder.setTitle(getContext().getString(R.string.control_offset_title));
|
||||
|
||||
dialogBuilder.setPositiveButton(android.R.string.ok, null);
|
||||
dialogBuilder.setNegativeButton(android.R.string.cancel, null);
|
||||
|
||||
mPreferenceDialog = dialogBuilder.create();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -3,6 +3,8 @@ package net.kdt.pojavlaunch.prefs;
|
|||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.os.Build.VERSION_CODES.P;
|
||||
|
||||
import static net.kdt.pojavlaunch.Architecture.is32BitsDevice;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.*;
|
||||
import android.graphics.Rect;
|
||||
|
|
@ -41,15 +43,22 @@ public class LauncherPreferences {
|
|||
public static float PREF_MOUSESPEED = 1f;
|
||||
public static int PREF_RAM_ALLOCATION;
|
||||
public static String PREF_DEFAULT_RUNTIME;
|
||||
public static int PREF_CONTROL_TOP_OFFSET = 0;
|
||||
public static int PREF_CONTROL_RIGHT_OFFSET = 0;
|
||||
public static int PREF_CONTROL_BOTTOM_OFFSET = 0;
|
||||
public static int PREF_CONTROL_LEFT_OFFSET = 0;
|
||||
public static boolean PREF_SUSTAINED_PERFORMANCE = false;
|
||||
public static boolean PREF_VIRTUAL_MOUSE_START = false;
|
||||
public static boolean PREF_ARC_CAPES = false;
|
||||
public static boolean PREF_USE_ALTERNATE_SURFACE = true;
|
||||
public static boolean PREF_JAVA_SANDBOX = true;
|
||||
public static int PREF_SCALE_FACTOR = 100;
|
||||
public static boolean PREF_ENALBE_GYRO = false;
|
||||
public static float PREF_GYRO_SENSITIVITY = 1f;
|
||||
public static int PREF_GYRO_SAMPLE_RATE = 16;
|
||||
|
||||
public static boolean PREF_GYRO_INVERT_X = false;
|
||||
|
||||
public static boolean PREF_GYRO_INVERT_Y = false;
|
||||
public static boolean PREF_FORCE_VSYNC = false;
|
||||
|
||||
public static boolean PREF_BUTTON_ALL_CAPS = true;
|
||||
|
||||
|
||||
public static void loadPreferences(Context ctx) {
|
||||
|
|
@ -74,15 +83,19 @@ public class LauncherPreferences {
|
|||
PREF_DISABLE_GESTURES = DEFAULT_PREF.getBoolean("disableGestures",false);
|
||||
PREF_RAM_ALLOCATION = DEFAULT_PREF.getInt("allocation", findBestRAMAllocation(ctx));
|
||||
PREF_CUSTOM_JAVA_ARGS = DEFAULT_PREF.getString("javaArgs", "");
|
||||
PREF_CONTROL_TOP_OFFSET = DEFAULT_PREF.getInt("controlTopOffset", 0);
|
||||
PREF_CONTROL_RIGHT_OFFSET = DEFAULT_PREF.getInt("controlRightOffset", 0);
|
||||
PREF_CONTROL_BOTTOM_OFFSET = DEFAULT_PREF.getInt("controlBottomOffset", 0);
|
||||
PREF_CONTROL_LEFT_OFFSET = DEFAULT_PREF.getInt("controlLeftOffset", 0);
|
||||
PREF_SUSTAINED_PERFORMANCE = DEFAULT_PREF.getBoolean("sustainedPerformance", false);
|
||||
PREF_VIRTUAL_MOUSE_START = DEFAULT_PREF.getBoolean("mouse_start", false);
|
||||
PREF_ARC_CAPES = DEFAULT_PREF.getBoolean("arc_capes",false);
|
||||
PREF_USE_ALTERNATE_SURFACE = DEFAULT_PREF.getBoolean("alternate_surface", false);
|
||||
PREF_JAVA_SANDBOX = DEFAULT_PREF.getBoolean("java_sandbox", true);
|
||||
PREF_SCALE_FACTOR = DEFAULT_PREF.getInt("resolutionRatio", 100);
|
||||
PREF_ENALBE_GYRO = DEFAULT_PREF.getBoolean("enableGyro", false);
|
||||
PREF_GYRO_SENSITIVITY = ((float)DEFAULT_PREF.getInt("gyroSensitivity", 100))/100f;
|
||||
PREF_GYRO_SAMPLE_RATE = DEFAULT_PREF.getInt("gyroSampleRate", 16);
|
||||
PREF_GYRO_INVERT_X = DEFAULT_PREF.getBoolean("gyroInvertX", false);
|
||||
PREF_GYRO_INVERT_Y = DEFAULT_PREF.getBoolean("gyroInvertY", false);
|
||||
PREF_FORCE_VSYNC = DEFAULT_PREF.getBoolean("force_vsync", false);
|
||||
PREF_BUTTON_ALL_CAPS = DEFAULT_PREF.getBoolean("buttonAllCaps", true);
|
||||
|
||||
/*
|
||||
if (PREF_CUSTOM_JAVA_ARGS.isEmpty()) {
|
||||
|
|
@ -146,6 +159,9 @@ public class LauncherPreferences {
|
|||
if (deviceRam < 1024) return 300;
|
||||
if (deviceRam < 1536) return 450;
|
||||
if (deviceRam < 2048) return 600;
|
||||
// Limit the max for 32 bits devices more harshly
|
||||
if (is32BitsDevice()) return 700;
|
||||
|
||||
if (deviceRam < 3064) return 936;
|
||||
if (deviceRam < 4096) return 1148;
|
||||
if (deviceRam < 6144) return 1536;
|
||||
|
|
|
|||
|
|
@ -1,21 +1,29 @@
|
|||
package net.kdt.pojavlaunch.prefs.screens;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorManager;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
import net.kdt.pojavlaunch.R;
|
||||
import net.kdt.pojavlaunch.prefs.CustomSeekBarPreference;
|
||||
import net.kdt.pojavlaunch.prefs.LauncherPreferences;
|
||||
|
||||
public class LauncherPreferenceControlFragment extends LauncherPreferenceFragment {
|
||||
|
||||
private boolean mGyroAvailable = false;
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle b, String str) {
|
||||
// Get values
|
||||
int longPressTrigger = LauncherPreferences.PREF_LONGPRESS_TRIGGER;
|
||||
int prefButtonSize = (int) LauncherPreferences.PREF_BUTTONSIZE;
|
||||
int mouseScale = (int) LauncherPreferences.PREF_MOUSESCALE;
|
||||
int gyroSampleRate = LauncherPreferences.PREF_GYRO_SAMPLE_RATE;
|
||||
float mouseSpeed = LauncherPreferences.PREF_MOUSESPEED;
|
||||
float gyroSpeed = LauncherPreferences.PREF_GYRO_SENSITIVITY;
|
||||
|
||||
//Triggers a write for some reason which resets the value
|
||||
addPreferencesFromResource(R.xml.pref_control);
|
||||
|
|
@ -40,7 +48,21 @@ public class LauncherPreferenceControlFragment extends LauncherPreferenceFragmen
|
|||
seek6.setValue((int)(mouseSpeed *100f));
|
||||
seek6.setSuffix(" %");
|
||||
|
||||
Context context = getContext();
|
||||
if(context != null) {
|
||||
mGyroAvailable = ((SensorManager)context.getSystemService(Context.SENSOR_SERVICE)).getDefaultSensor(Sensor.TYPE_GYROSCOPE) != null;
|
||||
}
|
||||
PreferenceCategory gyroCategory = (PreferenceCategory) findPreference("gyroCategory");
|
||||
gyroCategory.setVisible(mGyroAvailable);
|
||||
|
||||
CustomSeekBarPreference gyroSensitivitySeek = findPreference("gyroSensitivity");
|
||||
gyroSensitivitySeek.setRange(25, 300);
|
||||
gyroSensitivitySeek.setValue((int) (gyroSpeed*100f));
|
||||
gyroSensitivitySeek.setSuffix(" %");
|
||||
CustomSeekBarPreference gyroSampleRateSeek = findPreference("gyroSampleRate");
|
||||
gyroSampleRateSeek.setRange(5, 50);
|
||||
gyroSampleRateSeek.setValue(gyroSampleRate);
|
||||
gyroSampleRateSeek.setSuffix(" ms");
|
||||
computeVisibility();
|
||||
}
|
||||
|
||||
|
|
@ -51,8 +73,11 @@ public class LauncherPreferenceControlFragment extends LauncherPreferenceFragmen
|
|||
}
|
||||
|
||||
private void computeVisibility(){
|
||||
CustomSeekBarPreference seek2 = findPreference("timeLongPressTrigger");
|
||||
seek2.setVisible(!LauncherPreferences.PREF_DISABLE_GESTURES);
|
||||
findPreference("timeLongPressTrigger").setVisible(!LauncherPreferences.PREF_DISABLE_GESTURES);
|
||||
findPreference("gyroSensitivity").setVisible(LauncherPreferences.PREF_ENALBE_GYRO);
|
||||
findPreference("gyroSampleRate").setVisible(LauncherPreferences.PREF_ENALBE_GYRO);
|
||||
findPreference("gyroInvertX").setVisible(LauncherPreferences.PREF_ENALBE_GYRO);
|
||||
findPreference("gyroInvertY").setVisible(LauncherPreferences.PREF_ENALBE_GYRO);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ public class LauncherPreferenceJavaFragment extends LauncherPreferenceFragment {
|
|||
CustomSeekBarPreference seek7 = findPreference("allocation");
|
||||
seek7.setMin(256);
|
||||
|
||||
if(is32BitsDevice()) maxRAM = Math.min(1100, deviceRam);
|
||||
if(is32BitsDevice() || deviceRam < 2048) maxRAM = Math.min(1000, deviceRam);
|
||||
else maxRAM = deviceRam - (deviceRam < 3064 ? 800 : 1024); //To have a minimum for the device to breathe
|
||||
|
||||
seek7.setMax(maxRAM);
|
||||
|
|
|
|||
|
|
@ -2,14 +2,17 @@ package net.kdt.pojavlaunch.prefs.screens;
|
|||
|
||||
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_NOTCH_SIZE;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.SwitchPreference;
|
||||
import androidx.preference.SwitchPreferenceCompat;
|
||||
|
||||
import net.kdt.pojavlaunch.R;
|
||||
import net.kdt.pojavlaunch.prefs.CustomSeekBarPreference;
|
||||
import net.kdt.pojavlaunch.prefs.LauncherPreferences;
|
||||
|
||||
/**
|
||||
* Fragment for any settings video related
|
||||
|
|
@ -34,5 +37,17 @@ public class LauncherPreferenceVideoFragment extends LauncherPreferenceFragment
|
|||
// Sustained performance is only available since Nougat
|
||||
SwitchPreference sustainedPerfSwitch = findPreference("sustainedPerformance");
|
||||
sustainedPerfSwitch.setVisible(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N);
|
||||
computeVisibility();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences p, String s) {
|
||||
super.onSharedPreferenceChanged(p, s);
|
||||
computeVisibility();
|
||||
}
|
||||
|
||||
private void computeVisibility(){
|
||||
SwitchPreferenceCompat preference = findPreference("force_vsync");
|
||||
preference.setVisible(LauncherPreferences.PREF_USE_ALTERNATE_SURFACE);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ public class ProfileAdapter extends BaseAdapter {
|
|||
mProfiles = new HashMap<>(LauncherProfiles.mainProfileJson.profiles);
|
||||
if(enableCreateButton) {
|
||||
mCreateProfile = new MinecraftProfile();
|
||||
mCreateProfile.name = "Create new profile";
|
||||
mCreateProfile.name = context.getString(R.string.create_profile);
|
||||
mCreateProfile.lastVersionId = null;
|
||||
}
|
||||
mProfileList = new ArrayList<>(Arrays.asList(mProfiles.keySet().toArray(new String[0])));
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
package net.kdt.pojavlaunch.progresskeeper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class ProgressKeeper {
|
||||
private static final ConcurrentHashMap<String, ConcurrentLinkedQueue<ProgressListener>> sProgressListeners = new ConcurrentHashMap<>();
|
||||
private static final ConcurrentHashMap<String, ProgressState> sProgressStates = new ConcurrentHashMap<>();
|
||||
private static final ArrayList<TaskCountListener> sTaskCountListeners = new ArrayList<>();
|
||||
private static final HashMap<String, List<ProgressListener>> sProgressListeners = new HashMap<>();
|
||||
private static final HashMap<String, ProgressState> sProgressStates = new HashMap<>();
|
||||
private static final List<TaskCountListener> sTaskCountListeners = new ArrayList<>();
|
||||
|
||||
public static void submitProgress(String progressRecord, int progress, int resid, Object... va) {
|
||||
public static synchronized void submitProgress(String progressRecord, int progress, int resid, Object... va) {
|
||||
ProgressState progressState = sProgressStates.get(progressRecord);
|
||||
boolean shouldCallStarted = progressState == null;
|
||||
boolean shouldCallEnded = resid == -1 && progress == -1;
|
||||
|
|
@ -27,7 +27,7 @@ public class ProgressKeeper {
|
|||
progressState.varArg = va;
|
||||
}
|
||||
|
||||
ConcurrentLinkedQueue<ProgressListener> progressListeners = sProgressListeners.get(progressRecord);
|
||||
List<ProgressListener> progressListeners = sProgressListeners.get(progressRecord);
|
||||
if(progressListeners != null)
|
||||
for(ProgressListener listener : progressListeners) {
|
||||
if(shouldCallStarted) listener.onProgressStarted();
|
||||
|
|
@ -36,14 +36,14 @@ public class ProgressKeeper {
|
|||
}
|
||||
}
|
||||
|
||||
private static void updateTaskCount() {
|
||||
private static synchronized void updateTaskCount() {
|
||||
int count = sProgressStates.size();
|
||||
for(TaskCountListener listener : sTaskCountListeners) {
|
||||
listener.onUpdateTaskCount(count);
|
||||
}
|
||||
}
|
||||
|
||||
public static void addListener(String progressRecord, ProgressListener listener) {
|
||||
public static synchronized void addListener(String progressRecord, ProgressListener listener) {
|
||||
ProgressState state = sProgressStates.get(progressRecord);
|
||||
if(state != null && (state.resid != -1 || state.progress != -1)) {
|
||||
listener.onProgressStarted();
|
||||
|
|
@ -51,28 +51,53 @@ public class ProgressKeeper {
|
|||
}else{
|
||||
listener.onProgressEnded();
|
||||
}
|
||||
ConcurrentLinkedQueue<ProgressListener> listenerWeakReferenceList = sProgressListeners.get(progressRecord);
|
||||
if(listenerWeakReferenceList == null) sProgressListeners.put(progressRecord, (listenerWeakReferenceList = new ConcurrentLinkedQueue<>()));
|
||||
List<ProgressListener> listenerWeakReferenceList = sProgressListeners.get(progressRecord);
|
||||
if(listenerWeakReferenceList == null) sProgressListeners.put(progressRecord, (listenerWeakReferenceList = new ArrayList<>()));
|
||||
listenerWeakReferenceList.add(listener);
|
||||
}
|
||||
|
||||
public static void removeListener(String progressRecord, ProgressListener listener) {
|
||||
ConcurrentLinkedQueue<ProgressListener> listenerWeakReferenceList = sProgressListeners.get(progressRecord);
|
||||
public static synchronized void removeListener(String progressRecord, ProgressListener listener) {
|
||||
List<ProgressListener> listenerWeakReferenceList = sProgressListeners.get(progressRecord);
|
||||
if(listenerWeakReferenceList != null) listenerWeakReferenceList.remove(listener);
|
||||
}
|
||||
|
||||
public static void addTaskCountListener(TaskCountListener listener) {
|
||||
public static synchronized void addTaskCountListener(TaskCountListener listener) {
|
||||
listener.onUpdateTaskCount(sProgressStates.size());
|
||||
if(!sTaskCountListeners.contains(listener)) sTaskCountListeners.add(listener);
|
||||
}
|
||||
public static void addTaskCountListener(TaskCountListener listener, boolean runUpdate) {
|
||||
public static synchronized void addTaskCountListener(TaskCountListener listener, boolean runUpdate) {
|
||||
if(runUpdate) listener.onUpdateTaskCount(sProgressStates.size());
|
||||
if(!sTaskCountListeners.contains(listener)) sTaskCountListeners.add(listener);
|
||||
}
|
||||
public static void removeTaskCountListener(TaskCountListener listener) {
|
||||
public static synchronized void removeTaskCountListener(TaskCountListener listener) {
|
||||
sTaskCountListeners.remove(listener);
|
||||
}
|
||||
public static int getTaskCount() {
|
||||
|
||||
/**
|
||||
* Waits until all tasks are done and runs the runnable, or if there were no pending process remaining
|
||||
* The runnable runs from the thread that updated the task count last, and it might be the UI thread,
|
||||
* so don't put long running processes in it
|
||||
* @param runnable the runnable to run when no tasks are remaining
|
||||
*/
|
||||
public static void waitUntilDone(final Runnable runnable) {
|
||||
// If we do it the other way the listener would be removed before it was added, which will cause a listener object leak
|
||||
if(getTaskCount() == 0) {
|
||||
runnable.run();
|
||||
return;
|
||||
}
|
||||
TaskCountListener listener = new TaskCountListener() {
|
||||
@Override
|
||||
public void onUpdateTaskCount(int taskCount) {
|
||||
if(taskCount == 0) {
|
||||
runnable.run();
|
||||
}
|
||||
removeTaskCountListener(this);
|
||||
}
|
||||
};
|
||||
addTaskCountListener(listener);
|
||||
}
|
||||
|
||||
public static synchronized int getTaskCount() {
|
||||
return sProgressStates.size();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,21 @@
|
|||
package net.kdt.pojavlaunch.scoped;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.graphics.Point;
|
||||
import android.os.CancellationSignal;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.provider.DocumentsContract.Document;
|
||||
import android.provider.DocumentsContract.Root;
|
||||
import android.provider.DocumentsProvider;
|
||||
import android.util.Log;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import net.kdt.pojavlaunch.R;
|
||||
import net.kdt.pojavlaunch.Tools;
|
||||
|
||||
|
|
@ -19,8 +24,10 @@ import org.apache.commons.io.FileUtils;
|
|||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A document provider for the Storage Access Framework which exposes the files in the
|
||||
|
|
@ -183,6 +190,7 @@ public class FolderProvider extends DocumentsProvider {
|
|||
|
||||
@Override
|
||||
public String getDocumentType(String documentId) throws FileNotFoundException {
|
||||
Log.i("FolderPRovider", "getDocumentType("+documentId+")");
|
||||
File file = getFileForDocId(documentId);
|
||||
return getMimeType(file);
|
||||
}
|
||||
|
|
@ -300,4 +308,20 @@ public class FolderProvider extends DocumentsProvider {
|
|||
row.add(Document.COLUMN_ICON, R.mipmap.ic_launcher);
|
||||
}
|
||||
|
||||
@Override
|
||||
@TargetApi(26)
|
||||
public DocumentsContract.Path findDocumentPath(@Nullable String parentDocumentId, String childDocumentId) throws FileNotFoundException {
|
||||
File source = BASE_DIR;
|
||||
if(parentDocumentId != null) source = getFileForDocId(parentDocumentId);
|
||||
File destination = getFileForDocId(childDocumentId);
|
||||
List<String> pathIds = new ArrayList<>();
|
||||
while(!source.equals(destination) && destination != null) {
|
||||
pathIds.add(getDocIdForFile(destination));
|
||||
destination = destination.getParentFile();
|
||||
}
|
||||
pathIds.add(getDocIdForFile(source));
|
||||
Collections.reverse(pathIds);
|
||||
Log.i("FolderProvider", pathIds.toString());
|
||||
return new DocumentsContract.Path(getDocIdForFile(source), pathIds);
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import android.content.Intent;
|
|||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Process;
|
||||
import android.util.Log;
|
||||
|
||||
|
|
@ -28,7 +29,7 @@ import net.kdt.pojavlaunch.progresskeeper.TaskCountListener;
|
|||
*/
|
||||
public class ProgressService extends Service implements TaskCountListener {
|
||||
|
||||
private final Handler mainThreadHandler = new Handler();
|
||||
private final Handler mainThreadHandler = new Handler(Looper.getMainLooper());
|
||||
private NotificationManagerCompat notificationManagerCompat;
|
||||
|
||||
/** Simple wrapper to start the service */
|
||||
|
|
|
|||
|
|
@ -4,22 +4,12 @@ package net.kdt.pojavlaunch.tasks;
|
|||
import static net.kdt.pojavlaunch.Architecture.archAsString;
|
||||
import static net.kdt.pojavlaunch.PojavApplication.sExecutorService;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.AssetManager;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
|
||||
import com.kdt.mcgui.ProgressLayout;
|
||||
|
||||
import net.kdt.pojavlaunch.R;
|
||||
import net.kdt.pojavlaunch.Tools;
|
||||
import net.kdt.pojavlaunch.multirt.MultiRTUtils;
|
||||
|
||||
|
|
@ -49,7 +39,8 @@ public class AsyncAssetManager {
|
|||
} catch (IOException e) {
|
||||
Log.e("JREAuto", "JRE was not included on this APK.", e);
|
||||
}
|
||||
if(current_rt_version == null && MultiRTUtils.getExactJreName(8) != null) return true; //Assume user maintains his own runtime
|
||||
String exactJREName = MultiRTUtils.getExactJreName(8);
|
||||
if(current_rt_version == null && exactJREName != null && !exactJREName.equals("Internal")/*this clause is for when the internal runtime is goofed*/) return true; //Assume user maintains his own runtime
|
||||
if(rt_version == null) return otherRuntimesAvailable; // On noruntime builds, skip if there is at least 1 runtime installed (no matter if it is 8 or not)
|
||||
if(rt_version.equals(current_rt_version)) return true; //If we already have an integrated one installed, check if it's up-to-date
|
||||
|
||||
|
|
@ -73,8 +64,8 @@ public class AsyncAssetManager {
|
|||
|
||||
/** Unpack single files, with no regard to version tracking */
|
||||
public static void unpackSingleFiles(Context ctx){
|
||||
ProgressLayout.setProgress(ProgressLayout.EXTRACT_SINGLE_FILES, 0);
|
||||
sExecutorService.execute(() -> {
|
||||
|
||||
try {
|
||||
Tools.copyAssetFile(ctx, "options.txt", Tools.DIR_GAME_NEW, false);
|
||||
Tools.copyAssetFile(ctx, "default.json", Tools.CTRLMAP_PATH, false);
|
||||
|
|
@ -85,11 +76,12 @@ public class AsyncAssetManager {
|
|||
} catch (IOException e) {
|
||||
Log.e("AsyncAssetManager", "Failed to unpack critical components !");
|
||||
}
|
||||
|
||||
ProgressLayout.clearProgress(ProgressLayout.EXTRACT_SINGLE_FILES);
|
||||
});
|
||||
}
|
||||
|
||||
public static void unpackComponents(Context ctx){
|
||||
ProgressLayout.setProgress(ProgressLayout.EXTRACT_COMPONENTS, 0);
|
||||
sExecutorService.execute(() -> {
|
||||
try {
|
||||
unpackComponent(ctx, "caciocavallo", false);
|
||||
|
|
@ -101,6 +93,7 @@ public class AsyncAssetManager {
|
|||
} catch (IOException e) {
|
||||
Log.e("AsyncAssetManager", "Failed o unpack components !",e );
|
||||
}
|
||||
ProgressLayout.clearProgress(ProgressLayout.EXTRACT_COMPONENTS);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import net.kdt.pojavlaunch.Tools;
|
|||
import net.kdt.pojavlaunch.extra.ExtraConstants;
|
||||
import net.kdt.pojavlaunch.extra.ExtraCore;
|
||||
import net.kdt.pojavlaunch.prefs.LauncherPreferences;
|
||||
import net.kdt.pojavlaunch.services.ProgressService;
|
||||
import net.kdt.pojavlaunch.progresskeeper.ProgressKeeper;
|
||||
import net.kdt.pojavlaunch.utils.DownloadUtils;
|
||||
import net.kdt.pojavlaunch.value.DependentLibrary;
|
||||
import net.kdt.pojavlaunch.value.MinecraftClientInfo;
|
||||
|
|
@ -40,6 +40,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class AsyncMinecraftDownloader {
|
||||
private static final float BYTE_TO_MB = 1024 * 1024;
|
||||
public static final String MINECRAFT_RES = "https://resources.download.minecraft.net/";
|
||||
|
||||
/* Allows each downloading thread to have its own RECYCLED buffer */
|
||||
|
|
@ -48,12 +49,16 @@ public class AsyncMinecraftDownloader {
|
|||
public AsyncMinecraftDownloader(@NonNull Activity activity, JMinecraftVersionList.Version version, String realVersion,
|
||||
@NonNull DoneListener listener){ // this was there for a reason
|
||||
sExecutorService.execute(() -> {
|
||||
if(downloadGame(activity, version, realVersion))
|
||||
try {
|
||||
downloadGame(activity, version, realVersion);
|
||||
listener.onDownloadDone();
|
||||
}catch (DownloaderException e) {
|
||||
listener.onDownloadFailed(e.getCause());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean downloadGame(@NonNull Activity activity, JMinecraftVersionList.Version verInfo, String versionName){
|
||||
/* we do the throws DownloaderException thing to avoid blanket-catching Exception as a form of anti-lazy-developer protection */
|
||||
private void downloadGame(@NonNull Activity activity, JMinecraftVersionList.Version verInfo, String versionName) throws DownloaderException {
|
||||
final String downVName = "/" + versionName + "/" + versionName;
|
||||
|
||||
//Downloading libraries
|
||||
|
|
@ -61,22 +66,22 @@ public class AsyncMinecraftDownloader {
|
|||
JAssets assets = null;
|
||||
try {
|
||||
File verJsonDir = new File(Tools.DIR_HOME_VERSION + downVName + ".json");
|
||||
|
||||
if (verInfo != null && verInfo.url != null) {
|
||||
if(!LauncherPreferences.PREF_CHECK_LIBRARY_SHA) Log.w("Chk","Checker is off");
|
||||
|
||||
boolean isManifestGood = verJsonDir.exists()
|
||||
&& (!LauncherPreferences.PREF_CHECK_LIBRARY_SHA
|
||||
|| verInfo.sha1 == null
|
||||
|| Tools.compareSHA1(verJsonDir, verInfo.sha1));
|
||||
|
||||
if(!isManifestGood) {
|
||||
ProgressLayout.setProgress(ProgressLayout.DOWNLOAD_MINECRAFT, 0, R.string.mcl_launch_downloading, versionName + ".json");
|
||||
verJsonDir.delete();
|
||||
downloadFileMonitored(verInfo.url, verJsonDir, getByteBuffer(),
|
||||
(curr, max) -> ProgressLayout.setProgress(ProgressLayout.DOWNLOAD_MINECRAFT,
|
||||
(int) Math.max((float)curr/max*100,0), R.string.mcl_launch_downloading, versionName + ".json")
|
||||
);
|
||||
downloadVersionJson(versionName, verJsonDir, verInfo);
|
||||
}
|
||||
JMinecraftVersionList.Version originalVersion = Tools.getVersionInfo(versionName, true);
|
||||
if(originalVersion.inheritsFrom != null && !originalVersion.inheritsFrom.isEmpty()) {
|
||||
Log.i("Downloader", "probe: inheritsFrom="+originalVersion.inheritsFrom);
|
||||
String version = originalVersion.inheritsFrom;
|
||||
String downName = Tools.DIR_HOME_VERSION+"/"+version+"/"+version+".json";
|
||||
JMinecraftVersionList.Version listedVersion = getListedVersion(originalVersion.inheritsFrom);
|
||||
if(listedVersion != null) {
|
||||
Log.i("Downloader", "probe: verifying "+version);
|
||||
downloadVersionJson(version, new File(downName), listedVersion);
|
||||
}else{
|
||||
Log.i("Downloader", "failed to test source version before downloading.");
|
||||
Log.i("Downloader", "Inheriting from versions not in the Mojang list?");
|
||||
Log.i("Downloader", "If so, feel free to open a PR at our GitHub repository to add this feature!");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -84,13 +89,16 @@ public class AsyncMinecraftDownloader {
|
|||
|
||||
// THIS one function need the activity in the case of an error
|
||||
if(!JRE17Util.installNewJreIfNeeded(activity, verInfo)){
|
||||
return false;
|
||||
ProgressKeeper.submitProgress(ProgressLayout.DOWNLOAD_MINECRAFT, -1, -1);
|
||||
throw new DownloaderException();
|
||||
}
|
||||
|
||||
try {
|
||||
assets = downloadIndex(verInfo, new File(Tools.ASSETS_PATH, "indexes/" + verInfo.assets + ".json"));
|
||||
if(verInfo.assets != null)
|
||||
assets = downloadIndex(verInfo, new File(Tools.ASSETS_PATH, "indexes/" + verInfo.assets + ".json"));
|
||||
} catch (IOException e) {
|
||||
Log.e("AsyncMcDownloader", e.toString(), e);
|
||||
throw new DownloaderException(e);
|
||||
}
|
||||
|
||||
File outLib;
|
||||
|
|
@ -121,7 +129,7 @@ public class AsyncMinecraftDownloader {
|
|||
downloadFileMonitored(
|
||||
verInfo.logging.client.file.url, outLib, getByteBuffer(),
|
||||
(curr, max) -> ProgressLayout.setProgress(ProgressLayout.DOWNLOAD_MINECRAFT,
|
||||
(int) Math.max((float)curr/max*100,0), R.string.mcl_launch_downloading, finalVerInfo.logging.client.file.id)
|
||||
(int) Math.max((float)curr/max*100,0), R.string.mcl_launch_downloading_progress, finalVerInfo.logging.client.file.id, curr/BYTE_TO_MB, max/BYTE_TO_MB)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -154,7 +162,6 @@ public class AsyncMinecraftDownloader {
|
|||
}
|
||||
}
|
||||
File minecraftMainFile = new File(minecraftMainJar);
|
||||
JMinecraftVersionList.Version originalVersion = Tools.getVersionInfo(versionName, true);
|
||||
Log.i("Downloader", "originalVersion.inheritsFrom="+originalVersion.inheritsFrom);
|
||||
Log.i("Downloader", "originalVersion.downloads="+originalVersion.downloads);
|
||||
MinecraftClientInfo originalClientInfo;
|
||||
|
|
@ -178,6 +185,8 @@ public class AsyncMinecraftDownloader {
|
|||
}
|
||||
} catch (Throwable e) {
|
||||
Log.e("AsyncMcDownloader", e.toString(),e );
|
||||
ProgressKeeper.submitProgress(ProgressLayout.DOWNLOAD_MINECRAFT, -1, -1);
|
||||
throw new DownloaderException(e);
|
||||
}
|
||||
|
||||
ProgressLayout.setProgress(ProgressLayout.DOWNLOAD_MINECRAFT, 0, R.string.mcl_launch_cleancache);
|
||||
|
|
@ -191,12 +200,14 @@ public class AsyncMinecraftDownloader {
|
|||
|
||||
|
||||
try {
|
||||
downloadAssets(assets, verInfo.assets, assets.mapToResources ? new File(Tools.OBSOLETE_RESOURCES_PATH) : new File(Tools.ASSETS_PATH));
|
||||
if(assets != null)
|
||||
downloadAssets(assets, verInfo.assets, assets.mapToResources ? new File(Tools.OBSOLETE_RESOURCES_PATH) : new File(Tools.ASSETS_PATH));
|
||||
} catch (Exception e) {
|
||||
Log.e("AsyncMcDownloader", e.toString(), e);
|
||||
ProgressKeeper.submitProgress(ProgressLayout.DOWNLOAD_MINECRAFT, -1, -1);
|
||||
throw new DownloaderException(e);
|
||||
}
|
||||
|
||||
return true;
|
||||
ProgressKeeper.submitProgress(ProgressLayout.DOWNLOAD_MINECRAFT, -1, -1);
|
||||
}
|
||||
|
||||
public void verifyAndDownloadMainJar(String url, String sha1, File destination) throws Exception{
|
||||
|
|
@ -204,7 +215,7 @@ public class AsyncMinecraftDownloader {
|
|||
url,
|
||||
destination, getByteBuffer(),
|
||||
(curr, max) -> ProgressLayout.setProgress(ProgressLayout.DOWNLOAD_MINECRAFT,
|
||||
(int) Math.max((float)curr/max*100,0), R.string.mcl_launch_downloading, destination.getName()));
|
||||
(int) Math.max((float)curr/max*100,0), R.string.mcl_launch_downloading_progress, destination.getName(), curr/BYTE_TO_MB, max/BYTE_TO_MB));
|
||||
}
|
||||
|
||||
public void downloadAssets(final JAssets assets, String assetsVersion, final File outputDir) throws IOException {
|
||||
|
|
@ -258,7 +269,7 @@ public class AsyncMinecraftDownloader {
|
|||
while ((!executor.awaitTermination(1000, TimeUnit.MILLISECONDS))&&(!localInterrupt.get()) /*&&mActivity.mIsAssetsProcessing*/) {
|
||||
int DLSize = downloadedSize.get();
|
||||
ProgressLayout.setProgress(ProgressLayout.DOWNLOAD_MINECRAFT,(int) Math.max((float) DLSize/assetsSizeBytes*100, 0),
|
||||
R.string.mcl_launch_downloading, "assets");
|
||||
R.string.mcl_launch_downloading_progress, "assets", (float)DLSize/BYTE_TO_MB, (float)assetsSizeBytes/BYTE_TO_MB);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -324,7 +335,7 @@ public class AsyncMinecraftDownloader {
|
|||
|
||||
downloadFileMonitored(libPathURL, outLib, getByteBuffer(),
|
||||
(curr, max) -> ProgressLayout.setProgress(ProgressLayout.DOWNLOAD_MINECRAFT,
|
||||
(int) Math.max((float)curr/max*100,0), R.string.mcl_launch_downloading, outLib.getName())
|
||||
(int) Math.max((float)curr/max*100,0), R.string.mcl_launch_downloading_progress, outLib.getName(), curr/BYTE_TO_MB, max/BYTE_TO_MB)
|
||||
);
|
||||
|
||||
isFileGood = (libItem.downloads.artifact.sha1 == null
|
||||
|
|
@ -356,6 +367,24 @@ public class AsyncMinecraftDownloader {
|
|||
return Tools.GLOBAL_GSON.fromJson(Tools.read(output.getAbsolutePath()), JAssets.class);
|
||||
}
|
||||
|
||||
public void downloadVersionJson(String versionName, File verJsonDir, JMinecraftVersionList.Version verInfo) throws IOException {
|
||||
if(!LauncherPreferences.PREF_CHECK_LIBRARY_SHA) Log.w("Chk","Checker is off");
|
||||
|
||||
boolean isManifestGood = verJsonDir.exists()
|
||||
&& (!LauncherPreferences.PREF_CHECK_LIBRARY_SHA
|
||||
|| verInfo.sha1 == null
|
||||
|| Tools.compareSHA1(verJsonDir, verInfo.sha1));
|
||||
|
||||
if(!isManifestGood) {
|
||||
ProgressLayout.setProgress(ProgressLayout.DOWNLOAD_MINECRAFT, 0, R.string.mcl_launch_downloading, versionName + ".json");
|
||||
verJsonDir.delete();
|
||||
downloadFileMonitored(verInfo.url, verJsonDir, getByteBuffer(),
|
||||
(curr, max) -> ProgressLayout.setProgress(ProgressLayout.DOWNLOAD_MINECRAFT,
|
||||
(int) Math.max((float)curr/max*100,0), R.string.mcl_launch_downloading_progress, versionName + ".json", curr/BYTE_TO_MB, max/BYTE_TO_MB)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static String normalizeVersionId(String versionString) {
|
||||
JMinecraftVersionList versionList = (JMinecraftVersionList) ExtraCore.getValue(ExtraConstants.RELEASE_TABLE);
|
||||
if(versionList == null || versionList.versions == null) return versionString;
|
||||
|
|
@ -391,6 +420,14 @@ public class AsyncMinecraftDownloader {
|
|||
|
||||
public interface DoneListener{
|
||||
void onDownloadDone();
|
||||
void onDownloadFailed(Throwable throwable);
|
||||
}
|
||||
|
||||
private static class DownloaderException extends Exception {
|
||||
public DownloaderException() {}
|
||||
public DownloaderException(Throwable e) {
|
||||
super(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ public class JREUtils {
|
|||
Map<String, String> envMap = new ArrayMap<>();
|
||||
envMap.put("POJAV_NATIVEDIR", NATIVE_LIB_DIR);
|
||||
envMap.put("JAVA_HOME", Tools.DIR_HOME_JRE);
|
||||
envMap.put("HOME", Tools.DIR_GAME_NEW);
|
||||
envMap.put("HOME", Tools.DIR_GAME_HOME);
|
||||
envMap.put("TMPDIR", activity.getCacheDir().getAbsolutePath());
|
||||
envMap.put("LIBGL_MIPMAP", "3");
|
||||
|
||||
|
|
@ -203,6 +203,8 @@ public class JREUtils {
|
|||
// The OPEN GL version is changed according
|
||||
envMap.put("LIBGL_ES", (String) ExtraCore.getValue(ExtraConstants.OPEN_GL_VERSION));
|
||||
|
||||
envMap.put("FORCE_VSYNC", String.valueOf(LauncherPreferences.PREF_FORCE_VSYNC));
|
||||
|
||||
envMap.put("MESA_GLSL_CACHE_DIR", activity.getCacheDir().getAbsolutePath());
|
||||
if (LOCAL_RENDERER != null) {
|
||||
envMap.put("MESA_GL_VERSION_OVERRIDE", LOCAL_RENDERER.equals("opengles3_virgl")?"4.3":"4.6");
|
||||
|
|
@ -274,7 +276,7 @@ public class JREUtils {
|
|||
// return ldLibraryPath;
|
||||
}
|
||||
|
||||
public static int launchJavaVM(final Activity activity,final List<String> JVMArgs) throws Throwable {
|
||||
public static int launchJavaVM(final Activity activity, String gameDirectory, final List<String> JVMArgs) throws Throwable {
|
||||
JREUtils.relocateLibPath();
|
||||
// For debugging only!
|
||||
/*
|
||||
|
|
@ -309,7 +311,7 @@ public class JREUtils {
|
|||
|
||||
initJavaRuntime();
|
||||
setupExitTrap(activity.getApplication());
|
||||
chdir(Tools.DIR_GAME_NEW);
|
||||
chdir(gameDirectory == null ? Tools.DIR_GAME_NEW : gameDirectory);
|
||||
userArgs.add(0,"java"); //argv[0] is the program name according to C standard.
|
||||
|
||||
final int exitCode = VMLauncher.launchJVM(userArgs.toArray(new String[0]));
|
||||
|
|
@ -340,12 +342,13 @@ public class JREUtils {
|
|||
ArrayList<String> overridableArguments = new ArrayList<>(Arrays.asList(
|
||||
"-Djava.home=" + Tools.DIR_HOME_JRE,
|
||||
"-Djava.io.tmpdir=" + ctx.getCacheDir().getAbsolutePath(),
|
||||
"-Duser.home=" + new File(Tools.DIR_GAME_NEW).getParent(),
|
||||
"-Duser.home=" + Tools.DIR_GAME_HOME,
|
||||
"-Duser.language=" + System.getProperty("user.language"),
|
||||
"-Dos.name=Linux",
|
||||
"-Dos.version=Android-" + Build.VERSION.RELEASE,
|
||||
"-Dpojav.path.minecraft=" + Tools.DIR_GAME_NEW,
|
||||
"-Dpojav.path.private.account=" + Tools.DIR_ACCOUNT_NEW,
|
||||
"-Duser.timezone=" + TimeZone.getDefault().getID(),
|
||||
|
||||
//LWJGL 3 DEBUG FLAGS
|
||||
//"-Dorg.lwjgl.util.Debug=true",
|
||||
|
|
@ -385,6 +388,45 @@ public class JREUtils {
|
|||
return userArguments;
|
||||
}
|
||||
|
||||
/*
|
||||
* more better arguments parser that allows escapes and doesnt change up args
|
||||
* @param _args all args as a string
|
||||
* @return a list of split args
|
||||
*/
|
||||
/*public static ArrayList<String> parseJavaArguments(String _args) {
|
||||
char bracketOpen = '\0';
|
||||
char[] str = _args.toCharArray();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
ArrayList<String> ret = new ArrayList<String>();
|
||||
for(int i = 0; i < str.length; i++) {
|
||||
if(str[i] == '\\') {
|
||||
sb.append(str[i+1]);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if(str[i] == ' ' && bracketOpen == '\0') {
|
||||
if(sb.length() > 0) {
|
||||
ret.add(sb.toString());
|
||||
sb = new StringBuilder();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if(str[i] == '"' || str[i] == '\'') {
|
||||
if(bracketOpen == str[i]) {
|
||||
bracketOpen = '\0';
|
||||
continue;
|
||||
}else if(bracketOpen == '\0') {
|
||||
bracketOpen = str[i];
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
sb.append(str[i]);
|
||||
}
|
||||
ret.add(sb.toString());
|
||||
return ret;
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Parse and separate java arguments in a user friendly fashion
|
||||
* It supports multi line and absence of spaces between arguments
|
||||
|
|
@ -397,12 +439,23 @@ public class JREUtils {
|
|||
ArrayList<String> parsedArguments = new ArrayList<>(0);
|
||||
args = args.trim().replace(" ", "");
|
||||
//For each prefixes, we separate args.
|
||||
for(String prefix : new String[]{"-XX:-","-XX:+", "-XX:","--","-"}){
|
||||
String[] separators = new String[]{"-XX:-","-XX:+", "-XX:","--", "-D", "-X", "-javaagent:", "-verbose"};
|
||||
for(String prefix : separators){
|
||||
while (true){
|
||||
int start = args.indexOf(prefix);
|
||||
if(start == -1) break;
|
||||
//Get the end of the current argument
|
||||
int end = args.indexOf("-", start + prefix.length());
|
||||
//Get the end of the current argument by checking the nearest separator
|
||||
int end = -1;
|
||||
for(String separator: separators){
|
||||
int tempEnd = args.indexOf(separator, start + prefix.length());
|
||||
if(tempEnd == -1) continue;
|
||||
if(end == -1){
|
||||
end = tempEnd;
|
||||
continue;
|
||||
}
|
||||
end = Math.min(end, tempEnd);
|
||||
}
|
||||
//Fallback
|
||||
if(end == -1) end = args.length();
|
||||
|
||||
//Extract it
|
||||
|
|
|
|||
|
|
@ -6,31 +6,47 @@ import static net.kdt.pojavlaunch.prefs.LauncherPreferences.DEFAULT_PREF;
|
|||
import android.content.*;
|
||||
import android.content.res.*;
|
||||
import android.os.Build;
|
||||
import android.os.LocaleList;
|
||||
|
||||
import androidx.core.os.ConfigurationCompat;
|
||||
import androidx.preference.*;
|
||||
import java.util.*;
|
||||
import net.kdt.pojavlaunch.prefs.*;
|
||||
|
||||
public class LocaleUtils {
|
||||
public class LocaleUtils extends ContextWrapper {
|
||||
|
||||
public LocaleUtils(Context base) {
|
||||
super(base);
|
||||
}
|
||||
|
||||
public static Locale getLocale(){
|
||||
return Locale.getDefault();
|
||||
}
|
||||
|
||||
public static Context setLocale(Context context) {
|
||||
public static ContextWrapper setLocale(Context context) {
|
||||
if (DEFAULT_PREF == null) {
|
||||
DEFAULT_PREF = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
LauncherPreferences.loadPreferences(context);
|
||||
}
|
||||
|
||||
if(DEFAULT_PREF.getBoolean("force_english", false)){
|
||||
Locale locale = Locale.ENGLISH;
|
||||
Locale.setDefault(locale);
|
||||
Configuration config = context.getResources().getConfiguration();
|
||||
config.locale = locale;
|
||||
context.getResources().updateConfiguration(config, context.getResources().getDisplayMetrics());
|
||||
Resources resources = context.getResources();
|
||||
Configuration configuration = resources.getConfiguration();
|
||||
|
||||
configuration.setLocale(Locale.ENGLISH);
|
||||
Locale.setDefault(Locale.ENGLISH);
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
|
||||
LocaleList localeList = new LocaleList(Locale.ENGLISH);
|
||||
LocaleList.setDefault(localeList);
|
||||
configuration.setLocales(localeList);
|
||||
}
|
||||
|
||||
resources.updateConfiguration(configuration, resources.getDisplayMetrics());
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1){
|
||||
context = context.createConfigurationContext(configuration);
|
||||
}
|
||||
}
|
||||
|
||||
return context;
|
||||
return new LocaleUtils(context);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ import net.kdt.pojavlaunch.extra.ExtraConstants;
|
|||
import net.kdt.pojavlaunch.extra.ExtraCore;
|
||||
import net.kdt.pojavlaunch.value.launcherprofiles.MinecraftProfile;
|
||||
|
||||
import java.sql.Date;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
/** Class here to help with various stuff to help run lower versions smoothly */
|
||||
|
|
@ -26,10 +26,18 @@ public class OldVersionsUtils {
|
|||
}
|
||||
|
||||
try {
|
||||
String openGlVersion = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH).parse(creationDate.substring(0, creationDate.indexOf("T"))).before(new Date(2011-1900, 6, 8)) ? "1" : "2";
|
||||
int tIndexOf = creationDate.indexOf('T');
|
||||
if(tIndexOf != -1) creationDate = creationDate.substring(0, tIndexOf);
|
||||
Date creationDateObj = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH).parse(creationDate);
|
||||
if(creationDateObj == null) {
|
||||
Log.e("GL_SELECT", "Failed to parse version date");
|
||||
ExtraCore.setValue(ExtraConstants.OPEN_GL_VERSION, "2");
|
||||
return;
|
||||
}
|
||||
String openGlVersion = creationDateObj.before(new Date(2011-1900, 6, 8)) ? "1" : "2";
|
||||
ExtraCore.setValue(ExtraConstants.OPEN_GL_VERSION, openGlVersion);
|
||||
}catch (ParseException exception){
|
||||
Log.e("OPENGL SELECTION", exception.toString());
|
||||
Log.e("GL_SELECT", exception.toString());
|
||||
ExtraCore.setValue(ExtraConstants.OPEN_GL_VERSION, "2");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ public class MinecraftAccount {
|
|||
|
||||
public Bitmap getSkinFace(){
|
||||
if(skinFaceBase64 == null){
|
||||
return Bitmap.createBitmap(1,1, Bitmap.Config.ARGB_8888);
|
||||
return null;
|
||||
}
|
||||
byte[] faceIconBytes = Base64.decode(skinFaceBase64, Base64.DEFAULT);
|
||||
return BitmapFactory.decodeByteArray(faceIconBytes, 0, faceIconBytes.length);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package net.kdt.pojavlaunch.value.launcherprofiles;
|
|||
import com.google.gson.*;
|
||||
import net.kdt.pojavlaunch.*;
|
||||
import java.io.*;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class LauncherProfiles {
|
||||
public static MinecraftLauncherProfiles mainProfileJson;
|
||||
|
|
@ -11,8 +12,10 @@ public class LauncherProfiles {
|
|||
if (mainProfileJson == null) {
|
||||
if (launcherProfilesFile.exists()) {
|
||||
mainProfileJson = Tools.GLOBAL_GSON.fromJson(Tools.read(launcherProfilesFile.getAbsolutePath()), MinecraftLauncherProfiles.class);
|
||||
if(mainProfileJson.profiles == null) mainProfileJson.profiles = new HashMap<>();
|
||||
} else {
|
||||
mainProfileJson = new MinecraftLauncherProfiles();
|
||||
mainProfileJson.profiles = new HashMap<>();
|
||||
}
|
||||
} else {
|
||||
Tools.write(launcherProfilesFile.getAbsolutePath(), mainProfileJson.toJson());
|
||||
|
|
|
|||
|
|
@ -206,11 +206,19 @@ public class CallbackBridge {
|
|||
}
|
||||
}
|
||||
|
||||
private static void onGrabStateChanged(boolean grabbing) {
|
||||
private static void onGrabStateChanged(final boolean grabbing) {
|
||||
isGrabbing = grabbing;
|
||||
synchronized (grabListeners) {
|
||||
for (GrabListener g : grabListeners) g.onGrabState(grabbing);
|
||||
}
|
||||
sChoreographer.postFrameCallbackDelayed((time) -> {
|
||||
// If the grab re-changed, skip notify process
|
||||
if(isGrabbing != grabbing) return;
|
||||
|
||||
System.out.println("Grab changed : " + grabbing);
|
||||
synchronized (grabListeners) {
|
||||
for (GrabListener g : grabListeners) g.onGrabState(grabbing);
|
||||
}
|
||||
|
||||
}, 16);
|
||||
|
||||
}
|
||||
public static void addGrabListener(GrabListener listener) {
|
||||
synchronized (grabListeners) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
#include <jni.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static JavaVM* dalvikJavaVMPtr;
|
||||
|
||||
|
|
@ -13,6 +15,10 @@ jmethodID method_GetRGB;
|
|||
jclass class_CTCAndroidInput;
|
||||
jmethodID method_ReceiveInput;
|
||||
|
||||
jclass class_MainActivity;
|
||||
jmethodID method_OpenLink;
|
||||
jmethodID method_OpenPath;
|
||||
|
||||
jclass class_Frame;
|
||||
jclass class_Rectangle;
|
||||
jmethodID constructor_Rectangle;
|
||||
|
|
@ -26,6 +32,11 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
|||
if (dalvikJavaVMPtr == NULL) {
|
||||
//Save dalvik global JavaVM pointer
|
||||
dalvikJavaVMPtr = vm;
|
||||
JNIEnv *env = NULL;
|
||||
(*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_4);
|
||||
class_MainActivity = (*env)->NewGlobalRef(env,(*env)->FindClass(env, "net/kdt/pojavlaunch/MainActivity"));
|
||||
method_OpenLink= (*env)->GetStaticMethodID(env, class_MainActivity, "openLink", "(Ljava/lang/String;)V");
|
||||
method_OpenPath= (*env)->GetStaticMethodID(env, class_MainActivity, "openLink", "(Ljava/lang/String;)V");
|
||||
} else if (dalvikJavaVMPtr != vm) {
|
||||
runtimeJavaVMPtr = vm;
|
||||
}
|
||||
|
|
@ -136,6 +147,30 @@ Java_net_kdt_pojavlaunch_AWTInputBridge_nativePutClipboard(JNIEnv *env, jclass c
|
|||
(*runtimeJNIEnvPtr_CLIPBOARD)->DeleteLocalRef(runtimeJNIEnvPtr_CLIPBOARD,o_stringSelection);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_net_java_openjdk_cacio_ctc_CTCDesktopPeer_openFile(JNIEnv *env, jclass clazz, jstring filePath) {
|
||||
JNIEnv *dalvikEnv;char detachable = 0;
|
||||
if((*dalvikJavaVMPtr)->GetEnv(dalvikJavaVMPtr, (void **) &dalvikEnv, JNI_VERSION_1_6) == JNI_EDETACHED) {
|
||||
(*dalvikJavaVMPtr)->AttachCurrentThread(dalvikJavaVMPtr, &dalvikEnv, NULL);
|
||||
detachable = 1;
|
||||
}
|
||||
const char* stringChars = (*env)->GetStringUTFChars(env, filePath, NULL);
|
||||
(*dalvikEnv)->CallStaticVoidMethod(dalvikEnv, class_MainActivity, method_OpenPath, (*dalvikEnv)->NewStringUTF(dalvikEnv, stringChars));
|
||||
(*env)->ReleaseStringUTFChars(env, filePath, stringChars);
|
||||
if(detachable) (*dalvikJavaVMPtr)->DetachCurrentThread(dalvikJavaVMPtr);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_net_java_openjdk_cacio_ctc_CTCDesktopPeer_openUri(JNIEnv *env, jclass clazz, jstring uri) {
|
||||
JNIEnv *dalvikEnv;char detachable = 0;
|
||||
if((*dalvikJavaVMPtr)->GetEnv(dalvikJavaVMPtr, (void **) &dalvikEnv, JNI_VERSION_1_6) == JNI_EDETACHED) {
|
||||
(*dalvikJavaVMPtr)->AttachCurrentThread(dalvikJavaVMPtr, &dalvikEnv, NULL);
|
||||
detachable = 1;
|
||||
}
|
||||
const char* stringChars = (*env)->GetStringUTFChars(env, uri, NULL);
|
||||
(*dalvikEnv)->CallStaticVoidMethod(dalvikEnv, class_MainActivity, method_OpenLink, (*dalvikEnv)->NewStringUTF(dalvikEnv, stringChars));
|
||||
(*env)->ReleaseStringUTFChars(env, uri, stringChars);
|
||||
if(detachable) (*dalvikJavaVMPtr)->DetachCurrentThread(dalvikJavaVMPtr);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_net_kdt_pojavlaunch_AWTInputBridge_nativeMoveWindow(JNIEnv *env, jclass clazz, jint xoff, jint yoff) {
|
||||
if (runtimeJNIEnvPtr_INPUT == NULL) {
|
||||
|
|
|
|||
|
|
@ -165,5 +165,7 @@ void gl_setup_window() {
|
|||
}
|
||||
|
||||
void gl_swap_interval(int swapInterval) {
|
||||
if(pojav_environ->force_vsync) swapInterval = 1;
|
||||
|
||||
eglSwapInterval_p(g_EglDisplay, swapInterval);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -732,6 +732,11 @@ int pojavInit() {
|
|||
savedHeight = ANativeWindow_getHeight(pojav_environ->pojavWindow);
|
||||
ANativeWindow_setBuffersGeometry(pojav_environ->pojavWindow,savedWidth,savedHeight,AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM);
|
||||
|
||||
// Only affects GL4ES as of now
|
||||
const char *forceVsync = getenv("FORCE_VSYNC");
|
||||
if (strcmp(forceVsync, "true") == 0)
|
||||
pojav_environ->force_vsync = true;
|
||||
|
||||
// NOTE: Override for now.
|
||||
const char *renderer = getenv("POJAV_RENDERER");
|
||||
if (strncmp("opengles3_virgl", renderer, 15) == 0) {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ struct pojav_environ_s {
|
|||
struct ANativeWindow* pojavWindow;
|
||||
render_window_t* mainWindowBundle;
|
||||
int config_renderer;
|
||||
bool force_vsync;
|
||||
};
|
||||
extern struct pojav_environ_s *pojav_environ;
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
app_pojavlauncher/src/main/jniLibs/arm64-v8a/libunpack200.so
Normal file → Executable file
BIN
app_pojavlauncher/src/main/jniLibs/arm64-v8a/libunpack200.so
Normal file → Executable file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
app_pojavlauncher/src/main/jniLibs/armeabi-v7a/libunpack200.so
Normal file → Executable file
BIN
app_pojavlauncher/src/main/jniLibs/armeabi-v7a/libunpack200.so
Normal file → Executable file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
app_pojavlauncher/src/main/jniLibs/x86/libunpack200.so
Normal file → Executable file
BIN
app_pojavlauncher/src/main/jniLibs/x86/libunpack200.so
Normal file → Executable file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
app_pojavlauncher/src/main/jniLibs/x86_64/libunpack200.so
Normal file → Executable file
BIN
app_pojavlauncher/src/main/jniLibs/x86_64/libunpack200.so
Normal file → Executable file
Binary file not shown.
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="@dimen/_128sdp" android:tint="@color/primary_text"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="@dimen/_128sdp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M18,2h-8L4.02,8L4,20c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4C20,2.9 19.1,2 18,2zM13,17h-2v-2h2V17zM13,13h-2V8h2V13z"/>
|
||||
</vector>
|
||||
|
|
@ -44,6 +44,17 @@
|
|||
app:layout_constraintTop_toBottomOf="@id/custom_control_button"
|
||||
/>
|
||||
|
||||
<com.kdt.mcgui.LauncherMenuButton
|
||||
android:id="@+id/share_logs_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/_66sdp"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:drawableStart="@android:drawable/ic_menu_share"
|
||||
android:text="@string/main_share_logs"
|
||||
|
||||
app:layout_constraintTop_toBottomOf="@id/install_jar_button"
|
||||
/>
|
||||
|
||||
|
||||
<com.kdt.mcgui.mcVersionSpinner
|
||||
android:id="@+id/mc_version_spinner"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
|
||||
|
||||
android:layout_marginHorizontal="15dp"
|
||||
android:layout_marginHorizontal="@dimen/_15sdp"
|
||||
android:background="@drawable/menu_background"
|
||||
android:paddingVertical="20dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@
|
|||
android:layout_height="@dimen/_52sdp"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:src="@drawable/ic_menu_settings"
|
||||
android:scaleType="fitCenter"
|
||||
android:paddingVertical="@dimen/_8sdp"
|
||||
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="@dimen/_230sdp"
|
||||
android:layout_width="@dimen/_280sdp"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/background_app"
|
||||
android:paddingHorizontal="@dimen/_5sdp"
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
<net.kdt.pojavlaunch.colorselector.SVRectangleView
|
||||
android:id="@+id/color_selector_rectangle_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="150dp"
|
||||
android:layout_height="@dimen/_150sdp"
|
||||
android:focusable="true"
|
||||
android:layout_marginEnd="8dp"
|
||||
|
||||
|
|
@ -25,7 +25,7 @@
|
|||
<net.kdt.pojavlaunch.colorselector.ColorSideBySideView
|
||||
android:id="@+id/color_selector_color_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="94dp"
|
||||
android:layout_height="@dimen/_94sdp"
|
||||
android:layout_marginTop="8dp"
|
||||
app:layout_constraintEnd_toEndOf="@+id/color_selector_rectangle_view"
|
||||
app:layout_constraintStart_toStartOf="@+id/color_selector_rectangle_view"
|
||||
|
|
@ -39,7 +39,7 @@
|
|||
android:gravity="center"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="text"
|
||||
android:minHeight="48dp"
|
||||
android:minHeight="@dimen/_48sdp"
|
||||
android:typeface="monospace"
|
||||
app:layout_constraintEnd_toEndOf="@+id/color_selector_color_view"
|
||||
app:layout_constraintStart_toStartOf="@+id/color_selector_color_view"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<com.kdt.DefocusableScrollView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="@dimen/_230sdp"
|
||||
android:layout_width="@dimen/_280sdp"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:background="@color/background_app"
|
||||
|
|
@ -58,7 +58,7 @@
|
|||
|
||||
<EditText
|
||||
android:id="@+id/editSize_editTextX"
|
||||
android:layout_width="140dp"
|
||||
android:layout_width="@dimen/_138sdp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:imeOptions="flagNoExtractUi"
|
||||
|
|
|
|||
|
|
@ -1,64 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginHorizontal="15dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/control_offset_top_textview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/control_top_offset"
|
||||
/>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/control_offset_top_seekbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/control_offset_right_textview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/control_right_offset"
|
||||
/>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/control_offset_right_seekbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/control_offset_bottom_textview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/control_bottom_offset"
|
||||
/>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/control_offset_bottom_seekbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/control_offset_left_textview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/control_left_offset"
|
||||
/>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/control_offset_left_seekbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
|
@ -44,6 +44,17 @@
|
|||
app:layout_constraintTop_toBottomOf="@id/custom_control_button"
|
||||
/>
|
||||
|
||||
<com.kdt.mcgui.LauncherMenuButton
|
||||
android:id="@+id/share_logs_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/_66sdp"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:drawableStart="@android:drawable/ic_menu_share"
|
||||
android:text="@string/main_share_logs"
|
||||
|
||||
app:layout_constraintTop_toBottomOf="@id/install_jar_button"
|
||||
/>
|
||||
|
||||
|
||||
<com.kdt.mcgui.mcVersionSpinner
|
||||
android:id="@+id/mc_version_spinner"
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/_24sdp"
|
||||
android:background="@drawable/background_line"
|
||||
android:textSize="@dimen/_13ssp"
|
||||
|
||||
android:ems="10"
|
||||
android:hint="@string/unnamed"
|
||||
|
|
@ -54,7 +55,7 @@
|
|||
android:background="@drawable/background_line"
|
||||
android:hint="Select a version"
|
||||
android:paddingHorizontal="@dimen/_12sdp"
|
||||
android:textSize="@dimen/_14sdp"
|
||||
android:textSize="@dimen/_13ssp"
|
||||
|
||||
app:layout_constraintEnd_toStartOf="@+id/vprof_editor_version_button"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
|
@ -65,7 +66,8 @@
|
|||
android:layout_width="@dimen/_72sdp"
|
||||
android:layout_height="0dp"
|
||||
|
||||
android:text="Select"
|
||||
android:text="@string/global_select"
|
||||
|
||||
app:layout_constraintBottom_toBottomOf="@+id/vprof_editor_version_spinner"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/vprof_editor_version_spinner" />
|
||||
|
|
@ -89,7 +91,7 @@
|
|||
android:background="@drawable/background_line"
|
||||
android:hint="@string/use_global_default"
|
||||
android:paddingHorizontal="@dimen/_12sdp"
|
||||
android:textSize="@dimen/_14sdp"
|
||||
android:textSize="@dimen/_13ssp"
|
||||
|
||||
app:layout_constraintEnd_toStartOf="@id/vprof_editor_ctrl_button"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
|
@ -100,7 +102,8 @@
|
|||
android:layout_width="@dimen/_72sdp"
|
||||
android:layout_height="0dp"
|
||||
|
||||
android:text="SELECT"
|
||||
android:text="@string/global_select"
|
||||
|
||||
|
||||
app:layout_constraintBottom_toBottomOf="@id/vprof_editor_ctrl_spinner"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
|
@ -125,6 +128,7 @@
|
|||
android:hint="@string/use_global_default"
|
||||
android:inputType="text"
|
||||
android:paddingHorizontal="@dimen/_12sdp"
|
||||
android:textSize="@dimen/_13ssp"
|
||||
|
||||
app:layout_constraintTop_toBottomOf="@+id/vprof_editor_ctrl_spinner" />
|
||||
|
||||
|
|
@ -149,7 +153,7 @@
|
|||
|
||||
android:hint=".minecraft"
|
||||
android:paddingHorizontal="@dimen/_12sdp"
|
||||
android:textSize="@dimen/_14ssp"
|
||||
android:textSize="@dimen/_13ssp"
|
||||
|
||||
app:layout_constraintEnd_toStartOf="@id/vprof_editor_path_button"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
|
@ -159,7 +163,7 @@
|
|||
android:id="@+id/vprof_editor_path_button"
|
||||
android:layout_width="@dimen/_72sdp"
|
||||
android:layout_height="0dp"
|
||||
android:text="SELECT"
|
||||
android:text="@string/global_select"
|
||||
|
||||
app:layout_constraintBottom_toBottomOf="@id/vprof_editor_path"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
|
@ -180,6 +184,7 @@
|
|||
android:layout_marginTop="32dp"
|
||||
android:background="@drawable/background_line"
|
||||
android:paddingVertical="0px"
|
||||
android:textSize="@dimen/_13ssp"
|
||||
|
||||
app:layout_constraintTop_toBottomOf="@+id/vprof_editor_path"
|
||||
|
||||
|
|
@ -234,7 +239,7 @@
|
|||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="26dp"
|
||||
android:text="Save profile"
|
||||
android:text="@string/global_save"
|
||||
app:layout_constraintEnd_toStartOf="@+id/vprof_editor_delete_button"
|
||||
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
|
|
@ -247,7 +252,7 @@
|
|||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
android:text="Delete profile"
|
||||
android:text="@string/global_delete"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
|
|
|
|||
|
|
@ -1,32 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/app_short_name"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:fontFamily="@font/noto_sans_bold"
|
||||
android:textSize="40sp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:text="@string/app_motd"
|
||||
android:fontFamily="@font/noto_sans_bold"
|
||||
android:id="@+id/startscreen_text"/>
|
||||
|
||||
<ProgressBar
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:id="@+id/startscreenProgress"
|
||||
android:visibility="invisible"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="@dimen/_128sdp"
|
||||
android:layout_height="@dimen/_128sdp"
|
||||
android:adjustViewBounds="true"
|
||||
android:src="@drawable/storage_alert" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/_8sdp"
|
||||
android:paddingTop="@dimen/_10sdp"
|
||||
android:paddingEnd="@dimen/_8sdp"
|
||||
android:text="@string/storage_required" />
|
||||
</LinearLayout>
|
||||
15
app_pojavlauncher/src/main/res/values-land/styles.xml
Normal file
15
app_pojavlauncher/src/main/res/values-land/styles.xml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Light or not? -->
|
||||
<style name="AppTheme" parent="@style/Theme.AppCompat.NoActionBar">
|
||||
<item name="windowNoTitle">true</item>
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="android:windowContentOverlay">@null</item>
|
||||
<item name="android:statusBarColor">@color/background_status_bar</item>
|
||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
|
||||
|
||||
<item name="android:textSize">@dimen/_14ssp</item>
|
||||
|
||||
</style>
|
||||
</resources>
|
||||
|
|
@ -30,6 +30,7 @@
|
|||
<item>@string/control_adebug</item>
|
||||
<item>@string/control_customkey</item>
|
||||
<item>@string/mcl_setting_title_mousespeed</item>
|
||||
<item>@string/preference_gyro_sensitivity_title</item>
|
||||
<item>@string/mcl_option_customcontrol</item>
|
||||
</string-array>
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
<string name="login_dialog_no_saved_account">You don\'t have any saved account.\nUse the option <b>\"Keep me logged in\"</b> to save an account upon first login.</string>
|
||||
|
||||
<!-- Hint -->
|
||||
<string name="hint_control_mapping">"Swipe from right to left to open menu ◀\nHold a button to customize: edit, resize or delete."</string>
|
||||
<string name="hint_control_mapping">"Tap the top center cog icon to open the context menu ⚙\nTap a button to customize: edit, resize, delete, or customize it's keybind."</string>
|
||||
|
||||
<!-- Warnings -->
|
||||
<string name="warning_remove_account">This account will be removed!</string>
|
||||
|
|
@ -52,7 +52,7 @@
|
|||
<string name="mcl_tab_news">News</string>
|
||||
<string name="mcl_tab_console">Development console</string>
|
||||
<string name="mcl_tab_crash">Crash log</string>
|
||||
|
||||
|
||||
<!-- MCLauncherActivity: Account status -->
|
||||
<string name="mcl_account_connected">Connected</string>
|
||||
<string name="mcl_account_local">Local</string>
|
||||
|
|
@ -110,6 +110,8 @@
|
|||
<string name="mcl_setting_veroption_snapshot">Snapshot</string>
|
||||
<string name="mcl_setting_veroption_oldalpha">Old-alpha</string>
|
||||
<string name="mcl_setting_veroption_oldbeta">Old-beta</string>
|
||||
<string name="mcl_setting_java_sandbox">Sandbox .jar installer</string>
|
||||
<string name="mcl_setting_java_sandbox_subtitle">Control the availability of sandbox security manager when launching .jar installer.</string>
|
||||
<string name="mcl_version_clone">Clone</string>
|
||||
|
||||
<!-- Global strings -->
|
||||
|
|
@ -125,6 +127,7 @@
|
|||
<string name="global_unpacking">Unpacking %s</string>
|
||||
<string name="global_error_field_empty">This field can\'t be empty</string>
|
||||
<string name="global_waiting">Wait</string>
|
||||
<string name="global_select">Select</string>
|
||||
|
||||
<!-- MainActivity: strings -->
|
||||
<string name="mcn_exit_title">Application/Game exited with code %d, check latestlog.txt for more details.</string>
|
||||
|
|
@ -168,13 +171,13 @@
|
|||
<string name="import_control_verifying_file">File is being verified, please wait and try again…</string>
|
||||
<string name="import_control_invalid_name">Invalid name or file already exists</string>
|
||||
<string name="import_control_done">Importation done</string>
|
||||
<!--
|
||||
<!--
|
||||
<string name="control_more3"></string>
|
||||
<string name="control_more4"></string>
|
||||
-->
|
||||
<string name="customctrl_edit">Editing %s</string>
|
||||
<string name="customctrl_remove">Remove %s?</string>
|
||||
|
||||
|
||||
<string name="customctrl_keyname">Keycode</string>
|
||||
<string name="customctrl_toggle">Toggleable</string>
|
||||
<string name="customctrl_size">Size</string>
|
||||
|
|
@ -187,7 +190,7 @@
|
|||
<string name="customctrl_corner_radius">Corner radius</string>
|
||||
<string name="customctrl_stroke_width">Stroke width</string>
|
||||
<string name="customctrl_stroke_color">Stroke color</string>
|
||||
|
||||
|
||||
<string name="customctrl_dynamicpos">Manual Dynamic position</string>
|
||||
<string name="customctrl_dynamicpos_x">Dynamic X</string>
|
||||
<string name="customctrl_dynamicpos_y">Dynamic Y</string>
|
||||
|
|
@ -207,7 +210,7 @@
|
|||
<string name="customctrl_selectdefault">Select default Control json</string>
|
||||
<string name="customctrl_export">Export control</string>
|
||||
|
||||
|
||||
<string name="main_share_logs">Share log file</string>
|
||||
<string name="main_install_jar_file">Install .jar</string>
|
||||
<string name="main_options">Options</string>
|
||||
<string name="main_play">Play</string>
|
||||
|
|
@ -268,21 +271,33 @@
|
|||
<string name="preference_control_offset_title">Control side offsets</string>
|
||||
<string name="preference_control_offset_description">Set a custom offset to each side of the screen</string>
|
||||
<string name="preference_mouse_start_title">Start with virtual mouse on</string>
|
||||
<string name="preference_mouse_start_description">Using this option will get you judged by Mathias-Boulay</string>
|
||||
<string name="preference_mouse_start_description">Virtual Mouse appears at startup when this option is on</string>
|
||||
<string name="preference_video_title">Video and renderer</string>
|
||||
<string name="preference_video_description">Resolution, scaling type, and renderer</string>
|
||||
<string name="preference_control_title">Control customization</string>
|
||||
<string name="preference_control_description">Gestures types, triggers, and scaling</string>
|
||||
<string name="preference_java_title">Java Tweaks</string>
|
||||
<string name="preference_java_description">Java versions, JVM Arguments, and RAM amount</string>
|
||||
<string name="preference_java_description">Java versions, JVM Arguments, RAM amount and sandbox</string>
|
||||
<string name="preference_misc_title">Miscellaneous settings</string>
|
||||
<string name="preference_misc_description">Version list, and libs checks</string>
|
||||
<string name="preference_experimental_title">Experimental Stuff</string>
|
||||
<string name="preference_experimental_description">Use things there with consideration, no support.</string>
|
||||
<string name="preference_sustained_performance_title">Enable sustained performance mode</string>
|
||||
<string name="preference_sustained_performance_description">Limit thermal throttling by limiting peak performance</string>
|
||||
<string name="preference_force_vsync_title">Force enable vsync</string>
|
||||
<string name="preference_force_vsync_description">Limit thermal throttling by limiting peak performance</string>
|
||||
<string name="preference_force_english_title">Force language to english</string>
|
||||
<string name="preference_force_english_description">Allows you to see original strings, as intended by developers. Requires a restart</string>
|
||||
<string name="preference_edit_controls_title">Edit custom controls</string>
|
||||
<string name="preference_edit_controls_summary">Tweak the control scheme to fit your needs</string>
|
||||
|
||||
<string name="preference_category_gyro_controls">Gyro controls</string>
|
||||
<string name="preference_category_virtual_mouse">Virtual mouse</string>
|
||||
|
||||
<string name="preference_gyro_invert_x_axis">Invert X axis</string>
|
||||
<string name="preference_gyro_invert_y_axis">Invert Y axis</string>
|
||||
<string name="preference_gyro_invert_x_axis_description">Invert the horizontal axis</string>
|
||||
<string name="preference_gyro_invert_y_axis_description">Invert the vertical axis</string>
|
||||
|
||||
<string name="preference_back_title">Back to the last screen</string>
|
||||
<string name="gles_hack_title">GL4ES Shrink hack</string>
|
||||
|
|
@ -319,4 +334,23 @@
|
|||
<string name="tasks_ongoing">Tasks are in progress, please wait</string>
|
||||
<string name="no_saved_accounts">No saved accounts</string>
|
||||
<string name="downloading_versions">Downloading version list…</string>
|
||||
<string name="mc_download_failed">Failed to download Minecraft! This could be due to bad network connection, incorrectly placed files, etc..</string>
|
||||
<string name="xerr_unknown">Unknown Xbox Live API error %d</string>
|
||||
<string name="xerr_no_account">You don\'t seem to have an Xbox Live account. Please log in once on https://minecraft.net/ and try again.</string>
|
||||
<string name="xerr_adult_verification">An adult needs to verify your account.</string>
|
||||
<string name="xerr_child">Your account is a child account, and needs to be added into a Family in order to log in.</string>
|
||||
<string name="minecraft_not_owned">It seems like this account does not have a Minecraft profile. If you have Xbox Game Pass, please log in on https://minecraft.net/ and set it up.</string>
|
||||
<string name="xerr_not_available">Xbox Live is not available in your country</string>
|
||||
<string name="preference_enable_gyro_title">Enable gyroscope controls</string>
|
||||
<string name="preference_enable_gyro_description">Enabling this will allow you to turn in game by turning your phone</string>
|
||||
<string name="preference_gyro_sensitivity_title">Gyroscope controls sensitivity</string>
|
||||
<string name="preference_gyro_sensitivity_description">Adjust the sensitivity of gyroscope controls</string>
|
||||
<string name="toast_turn_on_gyro">Enable gyro controls first to use this!</string>
|
||||
<string name="preference_gyro_sample_rate_title">Gyroscope sampling rate</string>
|
||||
<string name="preference_gyro_sample_rate_description">If you have performance issues with the gyroscope controls, increase this</string>
|
||||
<string name="microsoft_login_retry_later">Too many requests, please try again later.</string>
|
||||
<string name="storage_required">PojavLauncher requires an attached external storage. Please reconnect your storage and restart the app.</string>
|
||||
<string name="mcl_setting_title_buttonallcaps">Use only capital letters in button labels</string>
|
||||
<string name="mcl_setting_subtitle_buttonallcaps">Disable this if you want to use lowercase letters in your controls</string>
|
||||
<string name="create_profile">Create new profile</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -9,5 +9,7 @@
|
|||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
|
||||
|
||||
<item name="android:textSize">@dimen/_12ssp</item>
|
||||
|
||||
</style>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,15 @@
|
|||
|
||||
<net.kdt.pojavlaunch.prefs.BackButtonPreference/>
|
||||
|
||||
|
||||
<Preference
|
||||
android:title="@string/preference_edit_controls_title"
|
||||
android:summary="@string/preference_edit_controls_summary"
|
||||
app2:icon="@drawable/ic_menu_custom_controls"
|
||||
>
|
||||
<intent android:targetPackage="@string/application_package" android:targetClass="net.kdt.pojavlaunch.CustomControlsActivity" android:action=".CustomControlsActivity"/>
|
||||
</Preference>
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="Gestures ">
|
||||
|
||||
|
|
@ -38,11 +47,17 @@
|
|||
app2:seekBarIncrement="5"
|
||||
android:icon="@drawable/ic_setting_control_scale"
|
||||
/>
|
||||
<SwitchPreference
|
||||
android:key="buttonAllCaps"
|
||||
android:defaultValue="true"
|
||||
android:title="@string/mcl_setting_title_buttonallcaps"
|
||||
android:summary="@string/mcl_setting_subtitle_buttonallcaps"
|
||||
/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="Virtual mouse">
|
||||
android:title="@string/preference_category_virtual_mouse">
|
||||
|
||||
<net.kdt.pojavlaunch.prefs.CustomSeekBarPreference
|
||||
android:key="mousescale"
|
||||
|
|
@ -68,6 +83,37 @@
|
|||
android:title="@string/preference_mouse_start_title"
|
||||
android:summary="@string/preference_mouse_start_description"
|
||||
/>
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="gyroCategory"
|
||||
android:title="@string/preference_category_gyro_controls"
|
||||
>
|
||||
<SwitchPreference
|
||||
android:key="enableGyro"
|
||||
android:title="@string/preference_enable_gyro_title"
|
||||
android:summary="@string/preference_enable_gyro_description"/>
|
||||
<net.kdt.pojavlaunch.prefs.CustomSeekBarPreference
|
||||
android:key="gyroSensitivity"
|
||||
android:title="@string/preference_gyro_sensitivity_title"
|
||||
android:summary="@string/preference_gyro_sensitivity_description"
|
||||
app2:selectable="false"
|
||||
app2:seekBarIncrement="5"
|
||||
app2:showSeekBarValue="true"/>
|
||||
<net.kdt.pojavlaunch.prefs.CustomSeekBarPreference
|
||||
android:key="gyroSampleRate"
|
||||
android:title="@string/preference_gyro_sample_rate_title"
|
||||
android:summary="@string/preference_gyro_sample_rate_description"
|
||||
app2:selectable="false"
|
||||
app2:showSeekBarValue="true"/>
|
||||
<SwitchPreference
|
||||
android:key="gyroInvertX"
|
||||
android:title="@string/preference_gyro_invert_x_axis"
|
||||
android:summary="@string/preference_gyro_invert_x_axis_description"/>
|
||||
<SwitchPreference
|
||||
android:key="gyroInvertY"
|
||||
android:title="@string/preference_gyro_invert_y_axis"
|
||||
android:summary="@string/preference_gyro_invert_y_axis_description"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,12 @@
|
|||
app2:seekBarIncrement="10"
|
||||
app2:selectable="false"/>
|
||||
|
||||
<SwitchPreference
|
||||
android:defaultValue="true"
|
||||
android:key="java_sandbox"
|
||||
android:summary="@string/mcl_setting_java_sandbox_subtitle"
|
||||
android:title="@string/mcl_setting_java_sandbox" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue