Update Mobile Build (#5813)

* LibGDX Update

* unused import

* disable minimize on screen lock
This commit is contained in:
kevlahnota
2024-08-12 01:12:29 +08:00
committed by GitHub
parent d2c42071aa
commit af060121cf
51 changed files with 661 additions and 293 deletions

View File

@@ -5,6 +5,7 @@ import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import forge.gui.GuiBase;
import java.io.BufferedReader;
@@ -20,9 +21,13 @@ public class Launcher extends Activity {
startActivity(main);
Intent intent = getIntent();
String action = intent.getAction();
String type = intent.getType();
sendIntent(intent, intent.getAction(), intent.getType());
finish();
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
}
private void sendIntent(Intent intent, String action, String type) {
if (Intent.ACTION_SEND.equals(action) && type != null) {
final Handler handler = new Handler();
handler.postDelayed(() -> {
@@ -50,9 +55,7 @@ public class Launcher extends Activity {
}
}, 1500);
}
finish();
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
@@ -61,8 +64,7 @@ public class Launcher extends Activity {
String type = intent.getType();
if (Intent.ACTION_SEND.equals(action) && type != null) {
final Handler handler = new Handler();
handler.postDelayed(() -> {
new Handler(Looper.getMainLooper()).postDelayed(() -> {
if ("text/plain".equals(type)) {
Uri textUri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
if (textUri != null) {

View File

@@ -1,32 +1,24 @@
package forge.app;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.Normalizer;
import java.util.ArrayList;
import android.graphics.Point;
import android.view.InputDevice;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Version;
import com.badlogic.gdx.backends.android.AndroidApplication;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Typeface;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.StateListDrawable;
@@ -36,35 +28,57 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.PowerManager;
import android.provider.Settings;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.style.StyleSpan;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.Gravity;
import android.view.InputDevice;
import android.view.View;
import android.view.WindowManager;
import android.webkit.MimeTypeMap;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Version;
import com.badlogic.gdx.backends.android.AndroidApplication;
import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration;
import com.badlogic.gdx.backends.android.AndroidAudio;
import com.getkeepsafe.relinker.ReLinker;
import de.cketti.fileprovider.PublicFileProvider;
import forge.Forge;
import forge.interfaces.IDeviceAdapter;
import forge.localinstance.properties.ForgePreferences;
import forge.model.FModel;
import forge.util.FileUtil;
import forge.util.ThreadUtil;
import io.sentry.Breadcrumb;
import io.sentry.Sentry;
import org.apache.commons.lang3.tuple.Pair;
//import io.sentry.android.core.SentryAndroid;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.text.Normalizer;
import java.util.ArrayList;
public class Main extends AndroidApplication {
AndroidAdapter Gadapter;
ArrayList<String> gamepads;
AndroidClipboard androidClipboard;
boolean hasLaunched;
private AndroidAdapter Gadapter;
private ArrayList<String> gamepads;
private AndroidClipboard androidClipboard;
private boolean isMIUI;
private String ASSETS_DIR = "";
private SharedPreferences sharedPreferences;
private int mShortAnimationDuration;
private View forgeLogo = null, forgeView = null, activeView = null;
private ProgressBar progressBar;
private TextView progressText;
private AndroidClipboard getAndroidClipboard() {
if (androidClipboard == null)
@@ -72,16 +86,62 @@ public class Main extends AndroidApplication {
return androidClipboard;
}
public static boolean isMiUi() {
return !TextUtils.isEmpty(getSystemProperty("ro.miui.ui.version.name"));
}
public static String getSystemProperty(String propName) {
String line;
BufferedReader input = null;
try {
java.lang.Process p = Runtime.getRuntime().exec("getprop " + propName);
input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024);
line = input.readLine();
input.close();
} catch (IOException ex) {
return null;
} finally {
if (input != null) {
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return line;
}
@Override
protected void onResume() {
try {
super.onResume();
} catch (Exception e) {
}
}
@Override
public AndroidAudio createAudio(Context context, AndroidApplicationConfiguration config) {
return super.createAudio(context, config);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
gamepads = getGameControllers();
setContentView(getResources().getIdentifier("main", "layout", getPackageName()));
mShortAnimationDuration = getResources().getInteger(android.R.integer.config_shortAnimTime);
sharedPreferences = getPreferences(Context.MODE_PRIVATE);
progressBar = findViewById(getResources().getIdentifier("pBar", "id", getPackageName()));
progressBar.setIndeterminate(true);
progressBar.setVisibility(View.GONE);
progressText = findViewById(getResources().getIdentifier("pText", "id", getPackageName()));
progressText.setVisibility(View.GONE);
if (hasLaunched)
return;
hasLaunched = true;
//init Sentry
//SentryAndroid.init(this);
isMIUI = isMiUi();
if (isMIUI)
preventSleep(true);
gamepads = getGameControllers();
//get total device RAM in mb
ActivityManager actManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
@@ -89,51 +149,84 @@ public class Main extends AndroidApplication {
actManager.getMemoryInfo(memInfo);
int totalMemory = Math.round(memInfo.totalMem / 1024f / 1024f);
boolean permissiongranted = checkPermission();
Gadapter = new AndroidAdapter(this.getContext());
initForge(Gadapter, permissiongranted, totalMemory, isTabletDevice(this.getContext()), Build.VERSION.SDK_INT, Build.VERSION.RELEASE);
boolean permissiongranted = checkPermission();
Gadapter = new AndroidAdapter(getContext());
initForge(Gadapter, permissiongranted, totalMemory, isTabletDevice(getContext()));
}
private void crossfade(View contentView, View previousView) {
activeView = contentView;
// Set the content view to 0% opacity but visible, so that it is visible
// (but fully transparent) during the animation.
contentView.setAlpha(0f);
contentView.setVisibility(View.VISIBLE);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT);
addContentView(contentView, params);
// Animate the content view to 100% opacity, and clear any animation
// listener set on the view.
contentView.animate()
.alpha(1f)
.setDuration(mShortAnimationDuration)
.setListener(null);
// Animate the loading view to 0% opacity. After the animation ends,
// set its visibility to GONE as an optimization step (it won't
// participate in layout passes, etc.)
previousView.animate()
.alpha(0f)
.setDuration(mShortAnimationDuration)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
previousView.setVisibility(View.GONE);
}
});
}
private static boolean isTabletDevice(Context activityContext) {
Display display = ((Activity) activityContext).getWindowManager().getDefaultDisplay();
Display display = ((Activity) activityContext).getWindowManager().getDefaultDisplay();
DisplayMetrics metrics = new DisplayMetrics();
display.getMetrics(metrics);
float widthInches = metrics.widthPixels / metrics.xdpi;
float heightInches = metrics.heightPixels / metrics.ydpi;
double diagonalInches = Math.sqrt(Math.pow(widthInches, 2) + Math.pow(heightInches, 2));
if (diagonalInches >= 7.0) {
return true;
}
return false;
return diagonalInches >= 7.0;
}
private void displayMessage(AndroidAdapter adapter, boolean ex, String msg){
private void displayMessage(View previousView, AndroidAdapter adapter, boolean ex, String msg, boolean manageApp) {
TableLayout TL = new TableLayout(this);
TL.setBackgroundResource(android.R.color.black);
TableRow row = new TableRow(this);
TableRow row2 = new TableRow(this);
TextView text = new TextView(this);
text.setGravity(Gravity.LEFT);
text.setTypeface(Typeface.SERIF);
String SP = Build.VERSION.SDK_INT > 29 ? "Files & Media" : "Storage Permission";
String title="Forge needs Storage Permission to run properly...\n" +
String title = "Forge needs " + SP + " to run properly...\n" +
"Follow these simple steps:\n\n";
String steps = " 1) Tap \"Open App Details\" Button.\n" +
" 2) Tap Permissions\n"+
" 3) Turn on the Storage Permission.\n\n"+
String steps = " 1) Tap \"App Settings\" Button.\n" +
" 2) Tap Permissions\n" +
" 3) Enable the " + SP + ".\n\n" +
"(You can tap anywhere to exit and restart the app)\n\n";
if (ex) {
title = "Forge didn't initialize!\n";
steps = msg + "\n\n";
title = manageApp ? "Forge AutoUpdater Permission...\n" : "Forge didn't initialize!\n";
steps = manageApp ? " 1) Tap \"App Settings\" Button.\n" +
" 2) Enable \"Allow apps from this source\"\n" +
"(You can tap anywhere to exit and restart the app)\n\n" : msg + "\n\n";
}
SpannableString ss1= new SpannableString(title);
SpannableString ss1 = new SpannableString(title);
ss1.setSpan(new StyleSpan(Typeface.BOLD), 0, ss1.length(), 0);
text.append(ss1);
text.append(steps);
row.addView(text);
row.setGravity(Gravity.CENTER);
int[] colors = {Color.TRANSPARENT,Color.TRANSPARENT};
int[] pressed = {Color.GREEN,Color.GREEN};
int[] colors = {Color.TRANSPARENT, Color.TRANSPARENT};
int[] pressed = {Color.GREEN, Color.GREEN};
GradientDrawable gd = new GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM, colors);
gd.setStroke(3, Color.DKGRAY);
@@ -145,21 +238,29 @@ public class Main extends AndroidApplication {
gd2.setCornerRadius(100);
Button button = new Button(this);
button.setText("Open App Details");
button.setText("App Settings");
button.setTypeface(Typeface.DEFAULT_BOLD);
StateListDrawable states = new StateListDrawable();
states.addState(new int[] {android.R.attr.state_pressed}, gd2);
states.addState(new int[] { }, gd);
states.addState(new int[]{android.R.attr.state_pressed}, gd2);
states.addState(new int[]{}, gd);
button.setBackground(states);
button.setTextColor(Color.RED);
button.setOnClickListener(v -> {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivity(intent);
if (manageApp) {
Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES)
.setData(Uri.parse(String.format("package:%s", getPackageName())))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
} else {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
.setData(Uri.parse(String.format("package:%s", getPackageName())))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
});
row2.addView(button);
@@ -168,121 +269,189 @@ public class Main extends AndroidApplication {
TL.addView(row, new TableLayout.LayoutParams(TableLayout.LayoutParams.WRAP_CONTENT, TableLayout.LayoutParams.WRAP_CONTENT));
TL.addView(row2, new TableLayout.LayoutParams(TableLayout.LayoutParams.WRAP_CONTENT, TableLayout.LayoutParams.WRAP_CONTENT));
TL.setGravity(Gravity.CENTER);
TL.setOnClickListener(v -> adapter.exit());
setContentView(TL);
TL.setOnClickListener(v -> adapter.restart());
crossfade(TL, previousView);
}
private void loadGame(final String title, final String steps, boolean isLandscape, AndroidAdapter adapter, boolean permissiongranted, int totalRAM, boolean isTabletDevice, AndroidApplicationConfiguration config, boolean exception, String msg) {
try {
forgeLogo = findViewById(getResources().getIdentifier("logo_id", "id", getPackageName()));
forgeView = initializeForView(Forge.getApp(getAndroidClipboard(), adapter, ASSETS_DIR, false, !isLandscape, totalRAM, isTabletDevice, Build.VERSION.SDK_INT, Build.VERSION.RELEASE, getDeviceName()), config);
getAnimator(ObjectAnimator.ofFloat(forgeLogo, "alpha", 0f, 1f).setDuration(1800), null, new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
if (!permissiongranted || exception) {
displayMessage(forgeLogo, adapter, exception, msg, false);
} else if (title.isEmpty() && steps.isEmpty()) {
if (isLandscape) {
Main.this.setRequestedOrientation(Build.VERSION.SDK_INT >= 26 ?
ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE : //Oreo and above has virtual back/menu buttons
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
} else {
Main.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
}
crossfade(forgeView, forgeLogo);
} else {
if (sharedPreferences.getBoolean("run_anyway", false)) {
crossfade(forgeView, forgeLogo);
return;
}
TableLayout TL = new TableLayout(getContext());
TL.setBackgroundResource(android.R.color.black);
TableRow messageRow = new TableRow(getContext());
TableRow checkboxRow = new TableRow(getContext());
TableRow buttonRow = new TableRow(getContext());
TextView text = new TextView(getContext());
text.setGravity(Gravity.LEFT);
text.setTypeface(Typeface.SERIF);
SpannableString ss1 = new SpannableString(title);
ss1.setSpan(new StyleSpan(Typeface.BOLD), 0, ss1.length(), 0);
text.append(ss1);
text.append(steps + "\n");
messageRow.addView(text);
messageRow.setGravity(Gravity.CENTER);
CheckBox checkBox = new CheckBox(getContext());
checkBox.setTypeface(Typeface.SERIF);
checkBox.setGravity(Gravity.TOP);
checkBox.setChecked(false);
checkBox.setPadding(30, 30, 30, 30);
checkBox.setTypeface(Typeface.SERIF);
checkBox.setText(" Don't remind me next time. ");
checkBox.setScaleX(0.9f);
checkBox.setScaleY(0.9f);
checkBox.setOnCheckedChangeListener((buttonView, isChecked) ->
sharedPreferences.edit().putBoolean("run_anyway", isChecked).apply());
checkboxRow.addView(checkBox);
checkboxRow.setGravity(Gravity.CENTER);
int[] colors = {Color.TRANSPARENT, Color.TRANSPARENT};
int[] pressed = {Color.GREEN, Color.GREEN};
GradientDrawable gd = new GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM, colors);
gd.setStroke(3, Color.DKGRAY);
gd.setCornerRadius(100);
GradientDrawable gd2 = new GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM, pressed);
gd2.setStroke(3, Color.DKGRAY);
gd2.setCornerRadius(100);
Button button = new Button(getContext());
button.setText("Run Forge..");
button.setTypeface(Typeface.DEFAULT_BOLD);
StateListDrawable states = new StateListDrawable();
states.addState(new int[]{android.R.attr.state_pressed}, gd2);
states.addState(new int[]{}, gd);
button.setBackground(states);
button.setTextColor(Color.RED);
button.setOnClickListener(v -> {
button.setClickable(false);
crossfade(forgeView, TL);
});
buttonRow.addView(button);
buttonRow.setGravity(Gravity.CENTER);
TL.addView(messageRow, new TableLayout.LayoutParams(TableLayout.LayoutParams.WRAP_CONTENT, TableLayout.LayoutParams.WRAP_CONTENT));
TL.addView(checkboxRow, new TableLayout.LayoutParams(TableLayout.LayoutParams.WRAP_CONTENT, TableLayout.LayoutParams.WRAP_CONTENT));
TL.addView(buttonRow, new TableLayout.LayoutParams(TableLayout.LayoutParams.WRAP_CONTENT, TableLayout.LayoutParams.WRAP_CONTENT));
TL.setGravity(Gravity.CENTER);
crossfade(TL, forgeLogo);
}
}
}).start();
} catch (Exception e) {
e.printStackTrace();
}
}
private AnimatorSet getAnimator(Animator play, Animator with, AnimatorListenerAdapter adapter) {
AnimatorSet animatorSet = new AnimatorSet();
if (with != null)
animatorSet.play(play).with(with);
else
animatorSet.play(play);
animatorSet.addListener(adapter);
return animatorSet;
}
@Override
public void onBackPressed() {
if (Gadapter!=null)
if (Gadapter != null)
Gadapter.exit();
super.onBackPressed();
}
private boolean checkPermission() {
int pid = android.os.Process.myPid();
int uid = android.os.Process.myUid();
try {
int result = this.getBaseContext().checkPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE, pid, uid);
if (result == PackageManager.PERMISSION_GRANTED) {
return true;
} else {
return false;
}
int result = getBaseContext().checkPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE, pid, uid);
return result == PackageManager.PERMISSION_GRANTED;
} catch (NullPointerException e) {
return false;
}
}
private void requestPermission() {
//Show Information about why you need the permission
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Storage Permission Denied...");
builder.setMessage("This app needs storage permission to run properly.\n\n\n\n");
builder.setPositiveButton("Open App Details", (dialog, which) -> {
dialog.cancel();
//ActivityCompat crashes... maybe it needs the appcompat v7???
//ActivityCompat.requestPermissions(Main.this, new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
});
/*builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
}
});*/
builder.show();
}
private void initForge(AndroidAdapter adapter, boolean permissiongranted, int totalRAM, boolean isTabletDevice) {
AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
config.useAccelerometer = false;
config.useCompass = false;
config.useGyroscope = false;
config.useRotationVectorSensor = false;
config.useImmersiveMode = false;
config.nativeLoader = () -> ReLinker.loadLibrary(getContext(), "gdx");
private void initForge(AndroidAdapter adapter, boolean permissiongranted, int totalRAM, boolean isTabletDevice, int AndroidAPI, String AndroidRelease){
if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
//fake init for error message
//set current orientation
String message = getDeviceName()+"\n"+"Android "+AndroidRelease+"\n"+"RAM "+ totalRAM+"MB" +"\n"+"LibGDX "+ Version.VERSION+"\n"+"Can't access external storage";
Sentry.addBreadcrumb(new Breadcrumb(message));
String message = getDeviceName() + "\n" + "Android " + Build.VERSION.RELEASE + "\n" + "RAM " + totalRAM + "MB" + "\n" + "LibGDX " + Version.VERSION + "\n" + "Can't access external storage";
Main.this.setRequestedOrientation(Main.this.getResources().getConfiguration().orientation);
initialize(Forge.getApp(getAndroidClipboard(), adapter, "", false, true, totalRAM, isTabletDevice, AndroidAPI, AndroidRelease, getDeviceName()));
displayMessage(adapter, true, message);
loadGame("", "", false, adapter, permissiongranted, totalRAM, isTabletDevice, config, true, message);
return;
}
String obbforge = Environment.getExternalStorageDirectory() + "/obbforge";
//if obbforge file exists in Phone Storage, Forge uses app-specific Obb directory as path, Android 11+ is mandatory even without obbforge
String assetsDir = (FileUtil.doesFileExist(obbforge) || Build.VERSION.SDK_INT > 29) ? getContext().getObbDir()+"/Forge/" : Environment.getExternalStorageDirectory()+"/Forge/";
if (!FileUtil.ensureDirectoryExists(assetsDir)) {
//fake init for error message
//set current orientation
String message = getDeviceName()+"\n"+"Android "+AndroidRelease+"\n"+"RAM "+ totalRAM+"MB" +"\n"+"LibGDX "+ Version.VERSION+"\n"+"Can't access external storage\nPath: " + assetsDir;
Sentry.addBreadcrumb(new Breadcrumb(message));
ASSETS_DIR = Build.VERSION.SDK_INT > 29 ? getContext().getObbDir() + "/Forge/" : Environment.getExternalStorageDirectory() + "/Forge/";
if (!FileUtil.ensureDirectoryExists(ASSETS_DIR)) {
String message = getDeviceName() + "\n" + "Android " + Build.VERSION.RELEASE + "\n" + "RAM " + totalRAM + "MB" + "\n" + "LibGDX " + Version.VERSION + "\n" + "Can't access external storage\nPath: " + ASSETS_DIR;
Main.this.setRequestedOrientation(Main.this.getResources().getConfiguration().orientation);
initialize(Forge.getApp(getAndroidClipboard(), adapter, "", false, true, totalRAM, isTabletDevice, AndroidAPI, AndroidRelease, getDeviceName()));
displayMessage(adapter, true, message);
loadGame("", "", false, adapter, permissiongranted, totalRAM, isTabletDevice, config, true, message);
return;
}
boolean isPortrait;
if (permissiongranted) {
//ensure .nomedia file exists in Forge directory so its images
//and other media files don't appear in Gallery or other apps
String noMediaFile = assetsDir + ".nomedia";
if (!FileUtil.doesFileExist(noMediaFile)) {
//ensure .nomedia file exists in Forge directory so its images
//and other media files don't appear in Gallery or other apps
String noMediaFile = ASSETS_DIR + ".nomedia";
if (!FileUtil.doesFileExist(noMediaFile)) {
try {
FileUtil.writeFile(noMediaFile, "");
} catch (Exception e) {
String message = getDeviceName() + "\n" + "Android " + Build.VERSION.RELEASE + "\n" + "RAM " + totalRAM + "MB" + "\n" + "LibGDX " + Version.VERSION + "\n" + "Can't read/write to storage";
Main.this.setRequestedOrientation(Main.this.getResources().getConfiguration().orientation);
loadGame("", "", false, adapter, permissiongranted, totalRAM, isTabletDevice, config, true, message);
return;
}
//enforce orientation based on whether device is a tablet and user preference
adapter.switchOrientationFile = assetsDir + "switch_orientation.ini";
boolean landscapeMode = adapter.isTablet == !FileUtil.doesFileExist(adapter.switchOrientationFile);
ForgePreferences prefs = FModel.getPreferences();
boolean propertyConfig = prefs != null && prefs.getPrefBoolean(ForgePreferences.FPref.UI_NETPLAY_COMPAT);
if (landscapeMode) {
isPortrait = false;
Main.this.setRequestedOrientation(Build.VERSION.SDK_INT >= 26 ?
ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE : //Oreo and above has virtual back/menu buttons
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
} else {
isPortrait = true;
Main.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
initialize(Forge.getApp(getAndroidClipboard(), adapter, assetsDir, propertyConfig, isPortrait, totalRAM, isTabletDevice, AndroidAPI, AndroidRelease, getDeviceName()));
} else {
isPortrait = true;
//fake init for permission instruction
Main.this.setRequestedOrientation(Main.this.getResources().getConfiguration().orientation);
initialize(Forge.getApp(getAndroidClipboard(), adapter, "", false, isPortrait, totalRAM, isTabletDevice, AndroidAPI, AndroidRelease, getDeviceName()));
displayMessage(adapter, false, "");
}
//enforce orientation based on whether device is a tablet and user preference
adapter.switchOrientationFile = ASSETS_DIR + "switch_orientation.ini";
boolean landscapeMode = adapter.isTablet == !FileUtil.doesFileExist(adapter.switchOrientationFile);
String info = totalRAM < 3500 || Build.VERSION.SDK_INT < 29 ? "Device Specification Check\n" + getDeviceName()
+ "\n" + "Android " + Build.VERSION.RELEASE + "\n" + "RAM " + totalRAM + "MB\n\nMinimum Requirements:" : "";
String lowV = Build.VERSION.SDK_INT < 29 ? "\nAPI: Android 10 or higher" : "";
String lowM = totalRAM < 3500 ? "\nRAM: 4GB RAM or higher" : "";
if (landscapeMode && Build.VERSION.SDK_INT > 32) {
Main.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
}
loadGame(info, lowV + lowM, landscapeMode, adapter, permissiongranted, totalRAM, isTabletDevice, config, false, "");
}
@Override
protected void onDestroy() {
hasLaunched = false;
try {
final Forge forge = (Forge) Gdx.app.getApplicationListener();
if (forge != null)
forge.dispose();
} catch (Exception e) {
e.printStackTrace();
}
super.onDestroy();
//ensure app doesn't stick around
//ActivityManager am = (ActivityManager)getSystemService(Activity.ACTIVITY_SERVICE);
@@ -290,11 +459,10 @@ public class Main extends AndroidApplication {
}
@Override
protected void onPause()
{
protected void onPause() {
super.onPause();
ForgePreferences prefs = FModel.getPreferences();
/*ForgePreferences prefs = FModel.getPreferences();
boolean minimizeonScreenLock = prefs != null && prefs.getPrefBoolean(ForgePreferences.FPref.UI_ANDROID_MINIMIZE_ON_SCRLOCK);
if (minimizeonScreenLock) {
@@ -305,7 +473,7 @@ public class Main extends AndroidApplication {
this.moveTaskToBack(true);
// Minimize the app to the background...
}
}
}*/
}
//special clipboard that words on Android
@@ -317,8 +485,7 @@ public class Main extends AndroidApplication {
if (cm.getPrimaryClip().getItemCount() > 0) {
try {
return cm.getPrimaryClip().getItemAt(0).coerceToText(getContext()).length() > 0;
}
catch (Exception ex) {
} catch (Exception ex) {
return false;
}
}
@@ -331,8 +498,7 @@ public class Main extends AndroidApplication {
try {
String text = cm.getPrimaryClip().getItemAt(0).coerceToText(getContext()).toString();
return Normalizer.normalize(text, Normalizer.Form.NFD);
}
catch (Exception ex) {
} catch (Exception ex) {
ex.printStackTrace();
}
}
@@ -341,7 +507,11 @@ public class Main extends AndroidApplication {
@Override
public void setContents(String contents0) {
cm.setPrimaryClip(ClipData.newPlainText("Forge", contents0));
try {
cm.setPrimaryClip(ClipData.newPlainText("Forge", contents0));
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
@@ -380,16 +550,23 @@ public class Main extends AndroidApplication {
@Override
public boolean openFile(String filename) {
try {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); //ensure this task isn't linked to this application
Uri uri = Uri.fromFile(new File(filename));
String type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
MimeTypeMap.getFileExtensionFromUrl(uri.toString()));
intent.setDataAndType(uri, type);
startActivity(intent);
return true;
}
catch (Exception e) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); //ensure this task isn't linked to this application
Uri uri = Uri.fromFile(new File(filename));
String type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
MimeTypeMap.getFileExtensionFromUrl(uri.toString()));
intent.setDataAndType(uri, type);
startActivity(intent);
return true;
} else {
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.setData(PublicFileProvider.getUriForFile(getContext(), "com.mydomain.publicfileprovider", new File(filename)));
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);
return true;
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
@@ -397,28 +574,7 @@ public class Main extends AndroidApplication {
@Override
public void restart() {
try { //solution from http://stackoverflow.com/questions/6609414/howto-programatically-restart-android-app
Context c = getApplicationContext();
PackageManager pm = c.getPackageManager();
if (pm != null) {
//create the intent with the default start activity for your application
Intent mStartActivity = pm.getLaunchIntentForPackage(c.getPackageName());
if (mStartActivity != null) {
mStartActivity.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
//create a pending intent so the application is restarted after System.exit(0) was called.
// We use an AlarmManager to call this intent in 100ms
int mPendingIntentId = 223344;
PendingIntent mPendingIntent = PendingIntent.getActivity(c, mPendingIntentId, mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager mgr = (AlarmManager) c.getSystemService(Context.ALARM_SERVICE);
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent);
//kill the application
System.exit(0);
}
}
}
catch (Exception e) {
e.printStackTrace();
}
triggerRebirth();
}
@Override
@@ -439,8 +595,7 @@ public class Main extends AndroidApplication {
//create file to indicate that portrait mode should be used for tablet or landscape should be used for phone
if (landscapeMode != isTablet) {
FileUtil.writeFile(switchOrientationFile, "1");
}
else {
} else {
FileUtil.deleteFile(switchOrientationFile);
}
}
@@ -450,14 +605,7 @@ public class Main extends AndroidApplication {
// Setting getWindow() Flags needs to run on UI thread.
// Should fix android.view.ViewRoot$CalledFromWrongThreadException:
// Only the original thread that created a view hierarchy can touch its views.
runOnUiThread(() -> {
if (preventSleep) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
else {
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
});
preventSleep(preventSleep);
}
@Override
@@ -465,6 +613,7 @@ public class Main extends AndroidApplication {
Bitmap bmp = BitmapFactory.decodeStream(input);
bmp.compress(Bitmap.CompressFormat.JPEG, 100, output);
}
@Override
public Pair<Integer, Integer> getRealScreenSize(boolean real) {
//app size
@@ -473,12 +622,12 @@ public class Main extends AndroidApplication {
Point size = new Point();
if (Build.VERSION.SDK_INT >= 17) {
// Seems it doesn't compile if using 4.1.1.4 since it's missing this method
/*if (real)
if (real)
display.getRealSize(size);
else
display.getSize(size);*/
display.getSize(size);
//remove this line below and use the method above if using Android libs higher than 4.1.1.4
return Pair.of(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); // this method don't take account the soft navigation bars taken in rendered screen
//return Pair.of(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); // this method don't take account the soft navigation bars taken in rendered screen
} else if (Build.VERSION.SDK_INT >= 14) {
try {
size.x = (Integer) Display.class.getMethod("getRawWidth").invoke(display);
@@ -499,13 +648,42 @@ public class Main extends AndroidApplication {
return gamepads;
}
}
private void preventSleep(boolean preventSleep) {
runOnUiThread(() -> {
if (preventSleep) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
} else {
if (!isMIUI)
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
});
}
private void triggerRebirth() {
try {
Context context = getApplicationContext();
PackageManager packageManager = context.getPackageManager();
Intent intent = packageManager.getLaunchIntentForPackage(context.getPackageName());
ComponentName componentName = intent.getComponent();
Intent mainIntent = Intent.makeRestartActivityTask(componentName);
// Required for API 34 and later
// Ref: https://developer.android.com/about/versions/14/behavior-changes-14#safer-intents
mainIntent.setPackage(context.getPackageName());
context.startActivity(mainIntent);
Runtime.getRuntime().exit(0);
} catch (Exception e) {
e.printStackTrace();
}
}
private ArrayList<String> getGameControllers() {
ArrayList<String> gameControllerDeviceIds = new ArrayList<String>();
ArrayList<String> gameControllerDeviceIds = new ArrayList<>();
int[] deviceIds = InputDevice.getDeviceIds();
for (int deviceId : deviceIds) {
InputDevice dev = InputDevice.getDevice(deviceId);
int sources = dev.getSources();
String devNameId = dev.getName()+"["+deviceId+"]";
String devNameId = dev.getName() + "[" + deviceId + "]";
// Verify that the device has gamepad buttons, control sticks, or both.
if (((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
@@ -522,7 +700,7 @@ public class Main extends AndroidApplication {
public String getDeviceName() {
String manufacturer = Build.MANUFACTURER;
String model = Build.MODEL;
String model = Build.BRAND + " - " + Build.MODEL;
if (model.toLowerCase().startsWith(manufacturer.toLowerCase())) {
return capitalize(model);
} else {