Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pre-release fixes: fixes for most common issues from play console. #6484

Merged
merged 7 commits into from
Jan 17, 2025
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,7 @@ public void startActivity(Intent i) {
@Override
protected void onResume() {
super.onResume();
if(!Tools.checkStorageRoot(this)) {
startActivity(new Intent(this, MissingStorageActivity.class));
finish();
}
Tools.checkStorageInteractive(this);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,12 @@ public class ImportControlActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Tools.initContextConstants(getApplicationContext());
if(Tools.checkStorageInteractive(this)) {
Tools.initStorageConstants(getApplicationContext());
}else {
// Return early, no initialization needed.
return;
}

setContentView(R.layout.activity_import_control);
mEditText = findViewById(R.id.editText_import_control_file_name);
Expand All @@ -61,6 +66,12 @@ protected void onNewIntent(Intent intent) {
@Override
protected void onPostResume() {
super.onPostResume();
if(!Tools.checkStorageInteractive(this)) {
// Don't try to read the file as when this check fails, external storage paths
// are no longer valid (likely unmounted).
// checkStorageInteractive() will finish this activity for us.
return;
}
if(!mHasIntentChanged) return;
mIsFileVerified = false;
getUriData();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,9 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) {

if (requestCode == 1 && resultCode == Activity.RESULT_OK) {
// Reload PREF_DEFAULTCTRL_PATH
// If the storage root got unmounted/unreadable we won't be able to load the file anyway,
// and MissingStorageActivity will be started.
if(!Tools.checkStorageRoot(this)) return;
LauncherPreferences.loadPreferences(getApplicationContext());
try {
mControlLayout.loadLayout(LauncherPreferences.PREF_DEFAULTCTRL_PATH);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.util.concurrent.TimeUnit;

import net.kdt.pojavlaunch.lifecycle.ContextExecutor;
import net.kdt.pojavlaunch.prefs.LauncherPreferences;
import net.kdt.pojavlaunch.tasks.AsyncAssetManager;
import net.kdt.pojavlaunch.utils.*;
import net.kdt.pojavlaunch.utils.FileUtils;
Expand Down Expand Up @@ -57,10 +58,15 @@ public void onCreate() {

try {
super.onCreate();

Tools.DIR_DATA = getDir("files", MODE_PRIVATE).getParent();
Tools.DIR_CACHE = getCacheDir();
Tools.DIR_ACCOUNT_NEW = Tools.DIR_DATA + "/accounts";
if(Tools.checkStorageRoot(this)){
// Implicitly initializes early constants and storage constants.
// Required to run the main activity properly.
LauncherPreferences.loadPreferences(this);
} else {
// In other cases, only initialize enough for the basicmost basics to work
// and not explode.
Tools.initEarlyConstants(this);
}
Tools.DEVICE_ARCHITECTURE = Architecture.getDeviceArchitecture();
//Force x86 lib directory for Asus x86 based zenfones
if(Architecture.isx86Device() && Architecture.is32BitsDevice()){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import net.kdt.pojavlaunch.prefs.LauncherPreferences;
import net.kdt.pojavlaunch.tasks.AsyncAssetManager;

public class TestStorageActivity extends Activity {
Expand Down Expand Up @@ -60,7 +61,8 @@ private void exit() {
startActivity(new Intent(this, MissingStorageActivity.class));
return;
}
//Only run them once we get a definitive green light to use storage
//Initialize constants (implicitly) and preferences after we confirm that we have storage.
LauncherPreferences.loadPreferences(this);
AsyncAssetManager.unpackComponents(this);
AsyncAssetManager.unpackSingleFiles(this);

Expand Down
40 changes: 33 additions & 7 deletions app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Process;
import android.provider.DocumentsContract;
import android.provider.OpenableColumns;
import android.util.ArrayMap;
Expand Down Expand Up @@ -164,14 +163,42 @@ public static boolean checkStorageRoot(Context context) {
}

/**
* Since some constant requires the use of the Context object
* You can call this function to initialize them.
* Any value (in)directly dependant on DIR_DATA should be set only here.
* Checks if the Pojav's storage root is accessible and read-writable. If it's not, starts
* the MissingStorageActivity and finishes the supplied activity.
* @param context the Activity that checks for storage availability
* @return whether the storage is available or not.
*/
public static void initContextConstants(Context ctx){
public static boolean checkStorageInteractive(Activity context) {
if(!Tools.checkStorageRoot(context)) {
context.startActivity(new Intent(context, MissingStorageActivity.class));
context.finish();
return false;
}
return true;
}

/**
* Initialize context constants most necessary for launcher's early startup phase
* that are not dependent on user storage.
* All values that depend on DIR_DATA and are not dependent on DIR_GAME_HOME must
* be initialized here.
* @param ctx the context for initialization.
*/
public static void initEarlyConstants(Context ctx) {
DIR_CACHE = ctx.getCacheDir();
DIR_DATA = ctx.getFilesDir().getParent();
MULTIRT_HOME = DIR_DATA+"/runtimes";
MULTIRT_HOME = DIR_DATA + "/runtimes";
DIR_ACCOUNT_NEW = DIR_DATA + "/accounts";
NATIVE_LIB_DIR = ctx.getApplicationInfo().nativeLibraryDir;
}

/**
* Initialize context constants that depend on user storage.
* Any value (in)directly dependent on DIR_GAME_HOME should be set only here.
* You ABSOLUTELY MUST check for storage presence using checkStorageRoot() before calling this.
*/
public static void initStorageConstants(Context ctx){
initEarlyConstants(ctx);
DIR_GAME_HOME = getPojavStorageRoot(ctx).getAbsolutePath();
DIR_GAME_NEW = DIR_GAME_HOME + "/.minecraft";
DIR_HOME_VERSION = DIR_GAME_NEW + "/versions";
Expand All @@ -181,7 +208,6 @@ public static void initContextConstants(Context ctx){
OBSOLETE_RESOURCES_PATH = DIR_GAME_NEW + "/resources";
CTRLMAP_PATH = DIR_GAME_HOME + "/controlmap";
CTRLDEF_FILE = DIR_GAME_HOME + "/controlmap/default.json";
NATIVE_LIB_DIR = ctx.getApplicationInfo().nativeLibraryDir;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@

import static android.content.Context.INPUT_METHOD_SERVICE;

import android.annotation.SuppressLint;
import android.content.Context;
import android.text.Editable;
import android.text.Selection;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.inputmethod.InputMethodManager;
Expand Down Expand Up @@ -34,31 +35,6 @@ public TouchCharInput(@NonNull Context context, @Nullable AttributeSet attrs, in
private boolean mIsDoingInternalChanges = false;
private CharacterSenderStrategy mCharacterSender;

/**
* We take the new chars, and send them to the game.
* If less chars are present, remove some.
* The text is always cleaned up.
*/
@Override
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
super.onTextChanged(text, start, lengthBefore, lengthAfter);
if(mIsDoingInternalChanges)return;
if(mCharacterSender != null){
for(int i=0; i < lengthBefore; ++i){
mCharacterSender.sendBackspace();
}

for(int i=start, count = 0; count < lengthAfter; ++i){
mCharacterSender.sendChar(text.charAt(i));
++count;
}
}

//Reset the keyboard state
if(text.length() < 1) clear();
}


/**
* When we change from app to app, the keyboard gets disabled.
* So, we disable the object
Expand Down Expand Up @@ -102,13 +78,15 @@ public void switchKeyboardState(){
* Clear the EditText from any leftover inputs
* It does not affect the in-game input
*/
@SuppressLint("SetTextI18n")
public void clear(){
mIsDoingInternalChanges = true;
// Edit the Editable directly as it doesn't affect the state
// of the TextView.
Editable editable = getEditableText();
editable.clear();
//Braille space, doesn't trigger keyboard auto-complete
//replacing directly the text without though setText avoids notifying changes
setText(TEXT_FILLER);
setSelection(TEXT_FILLER.length());
editable.append(TEXT_FILLER);
Selection.setSelection(editable, TEXT_FILLER.length());
mIsDoingInternalChanges = false;
}

Expand Down Expand Up @@ -142,6 +120,9 @@ public void setCharacterSender(CharacterSenderStrategy characterSender){

/** This function deals with anything that has to be executed when the constructor is called */
private void setup(){
// Using TextWatcher instead of overriding onTextChanged because some Huawei firmware
// calls setText in constructor, causing havoc for our listener
addTextChangedListener(new InputTextWatcher());
setOnEditorActionListener((textView, i, keyEvent) -> {
sendEnter();
clear();
Expand All @@ -151,5 +132,38 @@ private void setup(){
clear();
disable();
}
private class InputTextWatcher implements android.text.TextWatcher {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

}

/**
* We take the new chars, and send them to the game.
* If less chars are present, remove some.
* The text is always cleaned up.
*/
@Override
public void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
if(mIsDoingInternalChanges) return;
if(mCharacterSender != null){
for(int i=0; i < lengthBefore; ++i){
mCharacterSender.sendBackspace();
}

for(int i=start, count = 0; count < lengthAfter; ++i){
mCharacterSender.sendChar(text.charAt(i));
++count;
}
}
}

@Override
public void afterTextChanged(Editable editable) {
if(mIsDoingInternalChanges) return;
// Moved from onTextChanged because "It is an error to attempt to make changes to s from this callback."
// reference: https://developer.android.com/reference/android/text/TextWatcher#onTextChanged(java.lang.CharSequence,%20int,%20int,%20int)
if(editable.length() < 1) clear();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ private void updateLoaderVersions() {
}

private void updateLoaderSpinner() {
if(mLoaderVersionArray == null) return;
mLoaderVersionSpinner.setAdapter(createAdapter(mLoaderVersionArray, mOnlyStableCheckbox.isChecked()));
}

Expand Down Expand Up @@ -285,6 +286,7 @@ private void updateGameVersions() {
}

private void updateGameSpinner() {
if(mGameVersionArray == null) return;
mGameVersionSpinner.setAdapter(createAdapter(mGameVersionArray, mOnlyStableCheckbox.isChecked()));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ public class LauncherPreferences {


public static void loadPreferences(Context ctx) {
//Required for the data folder.
Tools.initContextConstants(ctx);
//Required for CTRLDEF_FILE and MultiRT
Tools.initStorageConstants(ctx);
boolean isDevicePowerful = isDevicePowerful(ctx);

PREF_RENDERER = DEFAULT_PREF.getString("renderer", "opengles2");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public class FolderProvider extends DocumentsProvider {

private static final String ALL_MIME_TYPES = "*/*";

private static final File BASE_DIR = new File(Tools.DIR_GAME_HOME);
private File BASE_DIR;

private ContentResolver mContentResolver;

Expand Down Expand Up @@ -138,6 +138,12 @@ public AssetFileDescriptor openDocumentThumbnail(String documentId, Point sizeHi

@Override
public boolean onCreate() {
if(Tools.checkStorageRoot(getContext())) {
Tools.initStorageConstants(getContext());
}else {
return false;
}
BASE_DIR = new File(Tools.DIR_GAME_HOME);
mContentResolver = getContext().getContentResolver();
mStorageProviderAuthortiy = getContext().getString(R.string.storageProviderAuthorities);
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,8 @@ public static void launchJavaVM(final AppCompatActivity activity, final Runtime
System.out.println(JVMArgs);

initJavaRuntime(runtimeHome);
setupExitTrap(activity.getApplication());
JREUtils.setupExitMethod(activity.getApplication());
JREUtils.initializeGameExitHook();
chdir(gameDirectory == null ? Tools.DIR_GAME_NEW : gameDirectory.getAbsolutePath());
userArgs.add(0,"java"); //argv[0] is the program name according to C standard.

Expand Down Expand Up @@ -572,10 +573,12 @@ public static int getDetectedVersion() {
public static native void setLdLibraryPath(String ldLibraryPath);
public static native void setupBridgeWindow(Object surface);
public static native void releaseBridgeWindow();
public static native void setupExitTrap(Context context);
public static native void initializeGameExitHook();
public static native void setupExitMethod(Context context);
// Obtain AWT screen pixels to render on Android SurfaceView
public static native int[] renderAWTScreenFrame(/* Object canvas, int width, int height */);
static {
System.loadLibrary("exithook");
System.loadLibrary("pojavexec");
System.loadLibrary("pojavexec_awt");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

import androidx.preference.*;
import java.util.*;
import net.kdt.pojavlaunch.prefs.*;

public class LocaleUtils extends ContextWrapper {

Expand All @@ -22,7 +21,10 @@ public LocaleUtils(Context base) {
public static ContextWrapper setLocale(Context context) {
if (DEFAULT_PREF == null) {
DEFAULT_PREF = PreferenceManager.getDefaultSharedPreferences(context);
LauncherPreferences.loadPreferences(context);
// Too early to initialize all prefs here, as this is called by PojavApplication
// before storage checks are done and before the storage paths are initialized.
// So only initialize PREF_FORCE_ENGLISH for the check below.
PREF_FORCE_ENGLISH = DEFAULT_PREF.getBoolean("force_english", false);
}

if(PREF_FORCE_ENGLISH){
Expand Down
8 changes: 7 additions & 1 deletion app_pojavlauncher/src/main/jni/Android.mk
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ include $(CLEAR_VARS)
LOCAL_LDLIBS := -ldl -llog -landroid
# -lGLESv2
LOCAL_MODULE := pojavexec
LOCAL_SHARED_LIBRARIES := bytehook
# LOCAL_CFLAGS += -DDEBUG
# -DGLES_TEST
LOCAL_SRC_FILES := \
Expand All @@ -43,6 +42,13 @@ LOCAL_LDLIBS += -lEGL -lGLESv2
endif
include $(BUILD_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := exithook
LOCAL_LDLIBS := -ldl -llog
LOCAL_SHARED_LIBRARIES := bytehook pojavexec
LOCAL_SRC_FILES := exit_hook.c
include $(BUILD_SHARED_LIBRARY)

#ifeq ($(TARGET_ARCH_ABI),arm64-v8a)
include $(CLEAR_VARS)
LOCAL_MODULE := linkerhook
Expand Down
Loading
Loading