mirror of
https://github.com/izzy2lost/2ship2harkinian-Android.git
synced 2026-06-19 01:20:08 -07:00
484 lines
20 KiB
Java
484 lines
20 KiB
Java
|
|
package com.dishii.mm;
|
|
import org.libsdl.app.SDLActivity;
|
|
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.SharedPreferences;
|
|
import android.net.Uri;
|
|
import android.os.Bundle;
|
|
import android.os.Environment;
|
|
import android.provider.Settings;
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.OutputStream;
|
|
import java.io.FileOutputStream;
|
|
import android.Manifest;
|
|
import android.content.pm.PackageManager;
|
|
import androidx.core.app.ActivityCompat;
|
|
import androidx.core.content.ContextCompat;
|
|
import android.util.Log;
|
|
|
|
import android.view.ViewGroup;
|
|
import android.widget.Button;
|
|
import android.widget.FrameLayout;
|
|
import android.view.LayoutInflater;
|
|
import android.view.MotionEvent;
|
|
import android.view.View;
|
|
import android.widget.ImageView;
|
|
|
|
//This class is the main SDLActivity and just sets up a bunch of default files and the input overlay
|
|
public class MainActivity extends SDLActivity{
|
|
|
|
private static final int STORAGE_PERMISSION_REQUEST_CODE = 1;
|
|
|
|
SharedPreferences preferences;
|
|
|
|
@Override
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
|
|
preferences = getSharedPreferences("com.dishii.mm.prefs",Context.MODE_PRIVATE);
|
|
|
|
setupControllerOverlay();
|
|
|
|
// Check if storage permissions are granted
|
|
if (hasStoragePermission()) {
|
|
doVersionCheck();
|
|
setupFiles();
|
|
} else {
|
|
requestStoragePermission();
|
|
}
|
|
attachController();
|
|
}
|
|
|
|
private void doVersionCheck(){
|
|
int currentVersion = BuildConfig.VERSION_CODE; // Use your app's version code
|
|
int storedVersion = preferences.getInt("appVersion", 1);
|
|
|
|
if (currentVersion > storedVersion) {
|
|
deleteOutdatedAssets();
|
|
preferences.edit().putInt("appVersion", currentVersion).apply();
|
|
}
|
|
}
|
|
|
|
private void deleteOutdatedAssets(){
|
|
File externalSohFile = new File(getExternalFilesDir(null), "2ship.o2r");
|
|
externalSohFile.delete();
|
|
File externalOotFile = new File(getExternalFilesDir(null), "mm.o2r");
|
|
externalOotFile.delete();
|
|
File externalAssetsFolder = new File(getExternalFilesDir(null), "assets");
|
|
deleteRecursive(externalAssetsFolder);
|
|
|
|
}
|
|
|
|
private void deleteRecursive(File fileOrDirectory) {
|
|
if (fileOrDirectory.isDirectory()) {
|
|
for (File child : fileOrDirectory.listFiles()) {
|
|
deleteRecursive(child);
|
|
}
|
|
}
|
|
fileOrDirectory.delete();
|
|
}
|
|
|
|
|
|
|
|
// Check if storage permission is granted
|
|
private boolean hasStoragePermission() {
|
|
return ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
|
|
== PackageManager.PERMISSION_GRANTED &&
|
|
ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
|
== PackageManager.PERMISSION_GRANTED;
|
|
}
|
|
|
|
// Request storage permission
|
|
private void requestStoragePermission() {
|
|
ActivityCompat.requestPermissions(this,
|
|
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE,
|
|
Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
|
STORAGE_PERMISSION_REQUEST_CODE);
|
|
}
|
|
|
|
// Handle permission request result
|
|
@Override
|
|
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
|
if (requestCode == STORAGE_PERMISSION_REQUEST_CODE) {
|
|
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
|
setupFiles();
|
|
} else {
|
|
// Permission denied, handle accordingly (e.g., show a message)
|
|
}
|
|
}
|
|
}
|
|
|
|
private void setupFiles(){
|
|
//Copy assets folder for rom extraction
|
|
File externalAssetsDir = new File(getExternalFilesDir(null), "assets");
|
|
if (!externalAssetsDir.exists()) {
|
|
try {
|
|
externalAssetsDir.mkdirs();
|
|
AssetCopyUtil.copyAssetsToExternal(this, "assets", externalAssetsDir.getAbsolutePath());
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
//Create empty mods folder
|
|
File externalModsDir = new File(getExternalFilesDir(null), "mods");
|
|
externalModsDir.mkdirs();
|
|
|
|
//Copy 2ship.o2r
|
|
File externalSohOtrFile = new File(getExternalFilesDir(null), "2ship.o2r");
|
|
if (!externalSohOtrFile.exists()) {
|
|
try {
|
|
InputStream in = getAssets().open("2ship.o2r");
|
|
OutputStream out = new FileOutputStream(externalSohOtrFile);
|
|
|
|
byte[] buffer = new byte[1024];
|
|
int read;
|
|
while ((read = in.read(buffer)) != -1) {
|
|
out.write(buffer, 0, read);
|
|
}
|
|
|
|
in.close();
|
|
out.close();
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
private native void nativeHandleSelectedFile(String filePath);
|
|
|
|
@Override
|
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
|
super.onActivityResult(requestCode, resultCode, data);
|
|
if (requestCode == 0 && resultCode == RESULT_OK) {
|
|
Uri selectedFileUri = data.getData();
|
|
String fileName = "MM.z64";
|
|
File destinationDirectory = getExternalFilesDir(null); // The second argument can specify a subdirectory, or you can pass null to use the root directory.
|
|
File destinationFile = new File(destinationDirectory, fileName);
|
|
|
|
if (destinationDirectory != null) {
|
|
try {
|
|
InputStream in = getContentResolver().openInputStream(selectedFileUri);
|
|
OutputStream out = new FileOutputStream(destinationFile);
|
|
|
|
byte[] buffer = new byte[4096];
|
|
int bytesRead;
|
|
while ((bytesRead = in.read(buffer)) != -1) {
|
|
out.write(buffer, 0, bytesRead);
|
|
}
|
|
|
|
in.close();
|
|
out.close();
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
nativeHandleSelectedFile(destinationFile.getPath());
|
|
}
|
|
}
|
|
|
|
public void openFilePicker() {
|
|
// Create an Intent to open the file picker dialog
|
|
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
|
|
intent.setType("*/*");
|
|
|
|
// Start the file picker dialog
|
|
startActivityForResult(intent, 0);
|
|
}
|
|
|
|
// Check if external storage is available and writable
|
|
private boolean isExternalStorageWritable() {
|
|
String state = Environment.getExternalStorageState();
|
|
return Environment.MEDIA_MOUNTED.equals(state);
|
|
}
|
|
|
|
|
|
public native void attachController();
|
|
public native void detachController();
|
|
// Native method for setting button state
|
|
public native void setButton(int button, boolean value);
|
|
public native void setCameraState(int axis, float value);
|
|
|
|
// Native method for setting joystick axis value
|
|
public native void setAxis(int axis, short value);
|
|
|
|
private Button button1, button2, button3, button4;
|
|
private Button buttonA, buttonB, buttonX, buttonY;
|
|
private Button buttonDpadUp, buttonDpadDown, buttonDpadLeft, buttonDpadRight;
|
|
private Button buttonLB, buttonRB, buttonZ, buttonStart, buttonBack;
|
|
private Button buttonToggle;
|
|
private FrameLayout leftJoystick;
|
|
private ImageView leftJoystickKnob;
|
|
private View overlayView;
|
|
|
|
// Function to set up the controller overlay (inflate layout and initialize buttons)
|
|
private void setupControllerOverlay() {
|
|
// Inflate the touchcontrol_overlay layout
|
|
LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
|
|
overlayView = inflater.inflate(R.layout.touchcontrol_overlay, null);
|
|
|
|
// Set layout params for overlayView to control positioning and sizing
|
|
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
|
|
FrameLayout.LayoutParams.MATCH_PARENT,
|
|
FrameLayout.LayoutParams.MATCH_PARENT
|
|
);
|
|
overlayView.setLayoutParams(layoutParams);
|
|
// Add overlay view to the main layout (you may need to add it to a container like FrameLayout)
|
|
ViewGroup view = (ViewGroup) getContentView();
|
|
view.addView(overlayView);
|
|
|
|
final ViewGroup buttonGroup = overlayView.findViewById(R.id.button_group);
|
|
|
|
buttonA = overlayView.findViewById(R.id.buttonA);
|
|
buttonB = overlayView.findViewById(R.id.buttonB);
|
|
buttonX = overlayView.findViewById(R.id.buttonX);
|
|
buttonY = overlayView.findViewById(R.id.buttonY);
|
|
|
|
buttonDpadUp = overlayView.findViewById(R.id.buttonDpadUp);
|
|
buttonDpadDown = overlayView.findViewById(R.id.buttonDpadDown);
|
|
buttonDpadLeft = overlayView.findViewById(R.id.buttonDpadLeft);
|
|
buttonDpadRight = overlayView.findViewById(R.id.buttonDpadRight);
|
|
|
|
buttonLB = overlayView.findViewById(R.id.buttonLB);
|
|
buttonRB = overlayView.findViewById(R.id.buttonRB);
|
|
buttonZ = overlayView.findViewById(R.id.buttonZ);
|
|
|
|
buttonStart = overlayView.findViewById(R.id.buttonStart);
|
|
buttonBack = overlayView.findViewById(R.id.buttonBack);
|
|
|
|
buttonToggle = overlayView.findViewById(R.id.buttonToggle);
|
|
|
|
// Initialize joysticks and joystick knobs from the inflated layout
|
|
leftJoystick = overlayView.findViewById(R.id.left_joystick);
|
|
leftJoystickKnob = overlayView.findViewById(R.id.left_joystick_knob);
|
|
|
|
FrameLayout rightScreenArea = overlayView.findViewById(R.id.right_screen_area);
|
|
|
|
// Set OnTouchListeners for the Xbox controller buttons
|
|
addTouchListener(buttonA, ControllerButtons.BUTTON_A); // SDL Button 0 (A)
|
|
addTouchListener(buttonB, ControllerButtons.BUTTON_B); // SDL Button 1 (B)
|
|
addTouchListener(buttonX, ControllerButtons.BUTTON_X); // SDL Button 2 (X)
|
|
addTouchListener(buttonY, ControllerButtons.BUTTON_Y); // SDL Button 3 (Y)
|
|
|
|
setupCButtons(buttonDpadUp, ControllerButtons.AXIS_RY, 1); // SDL Button 10 (D-Pad Up)
|
|
setupCButtons(buttonDpadDown, ControllerButtons.AXIS_RY , -1); // SDL Button 11 (D-Pad Down)
|
|
setupCButtons(buttonDpadLeft, ControllerButtons.AXIS_RX, 1); // SDL Button 12 (D-Pad Left)
|
|
setupCButtons(buttonDpadRight, ControllerButtons.AXIS_RX, -1); // SDL Button 13 (D-Pad Right)
|
|
|
|
addTouchListener(buttonLB, ControllerButtons.BUTTON_LB); // SDL Button 4 (LB)
|
|
addTouchListener(buttonRB, ControllerButtons.BUTTON_RB); // SDL Button 5 (RB)
|
|
addTouchListener(buttonZ, ControllerButtons.AXIS_RT); // SDL Button 5 (Z)
|
|
|
|
addTouchListener(buttonStart, ControllerButtons.BUTTON_START); // SDL Button 7 (Start)
|
|
addTouchListener(buttonBack, ControllerButtons.BUTTON_BACK); // SDL Button 6 (Back)
|
|
|
|
|
|
// Setup joystick movement
|
|
setupJoystick(leftJoystick, leftJoystickKnob, true); // Left joystick
|
|
|
|
setupLookAround(rightScreenArea);
|
|
|
|
setupToggleButton(buttonToggle,buttonGroup);
|
|
|
|
}
|
|
|
|
private void setupToggleButton(Button button, ViewGroup uiGroup){
|
|
boolean isHidden = preferences.getBoolean("controlsVisible", false); // Default to 'false' (visible)
|
|
uiGroup.setVisibility(isHidden ? View.INVISIBLE : View.VISIBLE); // Set the initial visibility based on the saved state
|
|
/*if(isHidden){
|
|
detachController();
|
|
}*/
|
|
button.setOnClickListener(new View.OnClickListener() {
|
|
boolean isHidden = false;
|
|
@Override
|
|
public void onClick(View v) {
|
|
if (isHidden) {
|
|
uiGroup.setVisibility(View.VISIBLE); // Show UI elements
|
|
//attachController();
|
|
} else {
|
|
uiGroup.setVisibility(View.INVISIBLE); // Hide UI elements
|
|
//detachController();
|
|
}
|
|
preferences.edit().putBoolean("controlsVisible", !isHidden).apply();
|
|
isHidden = !isHidden; // Toggle state
|
|
}
|
|
});
|
|
}
|
|
|
|
// Function to set a touch listener for each button
|
|
private void addTouchListener(Button button, int buttonNum) {
|
|
button.setOnTouchListener(new View.OnTouchListener() {
|
|
@Override
|
|
public boolean onTouch(View v, MotionEvent event) {
|
|
switch (event.getAction()) {
|
|
case MotionEvent.ACTION_DOWN:
|
|
setButton(buttonNum, true);
|
|
button.setPressed(true);
|
|
return true;
|
|
case MotionEvent.ACTION_UP:
|
|
setButton(buttonNum, false);
|
|
button.setPressed(false);
|
|
return true;
|
|
case MotionEvent.ACTION_CANCEL:
|
|
setButton(buttonNum, false);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
});
|
|
}
|
|
|
|
private void setupCButtons(Button button, int buttonNum, int direction) {
|
|
button.setOnTouchListener(new View.OnTouchListener() {
|
|
@Override
|
|
public boolean onTouch(View v, MotionEvent event) {
|
|
switch (event.getAction()) {
|
|
case MotionEvent.ACTION_DOWN:
|
|
setAxis(buttonNum, direction<0 ? Short.MAX_VALUE : Short.MIN_VALUE);
|
|
button.setPressed(true);
|
|
return true;
|
|
case MotionEvent.ACTION_UP:
|
|
setAxis(buttonNum, (short) 0);
|
|
button.setPressed(false);
|
|
return true;
|
|
case MotionEvent.ACTION_CANCEL:
|
|
setAxis(buttonNum, (short) 0);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
});
|
|
}
|
|
|
|
boolean TouchAreaEnabled = true;
|
|
|
|
void DisableTouchArea(){
|
|
TouchAreaEnabled = false;
|
|
}
|
|
void EnableTouchArea(){
|
|
TouchAreaEnabled = true;
|
|
}
|
|
|
|
private void setupLookAround(FrameLayout rightScreenArea) {
|
|
rightScreenArea.setOnTouchListener(new View.OnTouchListener() {
|
|
private float lastX = 0;
|
|
private float lastY = 0;
|
|
private boolean isTouching = false;
|
|
|
|
@Override
|
|
public boolean onTouch(View v, MotionEvent event) {
|
|
switch (event.getAction()) {
|
|
case MotionEvent.ACTION_DOWN:
|
|
// Start tracking the finger's position
|
|
lastX = event.getX();
|
|
lastY = event.getY();
|
|
isTouching = true;
|
|
break;
|
|
|
|
case MotionEvent.ACTION_MOVE:
|
|
if (isTouching) {
|
|
// Calculate the change in position (delta)
|
|
float deltaX = event.getX() - lastX;
|
|
float deltaY = event.getY() - lastY;
|
|
|
|
// Update the last position
|
|
lastX = event.getX();
|
|
lastY = event.getY();
|
|
|
|
// Increase sensitivity by using a larger multiplier
|
|
// Adjust these multipliers to suit your needs
|
|
float sensitivityMultiplier = 15; // Higher value for more sensitivity
|
|
float rx = (deltaX * sensitivityMultiplier);
|
|
float ry = (deltaY * sensitivityMultiplier);
|
|
|
|
// Send the mapped values to the joystick axes
|
|
setCameraState(0, rx); // Right stick X axis
|
|
setCameraState(1, ry); // Right stick Y axis
|
|
}
|
|
break;
|
|
|
|
case MotionEvent.ACTION_UP:
|
|
case MotionEvent.ACTION_CANCEL:
|
|
// Stop tracking the finger's position and reset joystick input
|
|
isTouching = false;
|
|
setCameraState(0, 0.0f); // Reset right stick X axis
|
|
setCameraState(1, 0.0f); // Reset right stick Y axis
|
|
break;
|
|
}
|
|
return TouchAreaEnabled; // Event full handled
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Function to set joystick movement with reset to center when not touched
|
|
private void setupJoystick(FrameLayout joystickLayout, ImageView joystickKnob, boolean isLeft) {
|
|
joystickLayout.post(() -> {
|
|
// Calculate the joystick center once, before any events
|
|
final float joystickCenterX = joystickLayout.getWidth() / 2f;
|
|
final float joystickCenterY = joystickLayout.getHeight() / 2f;
|
|
|
|
joystickLayout.setOnTouchListener(new View.OnTouchListener() {
|
|
@Override
|
|
public boolean onTouch(View v, MotionEvent event) {
|
|
switch (event.getAction()) {
|
|
case MotionEvent.ACTION_DOWN:
|
|
case MotionEvent.ACTION_MOVE:
|
|
// Calculate the joystick movement and move the knob
|
|
float deltaX = event.getX() - joystickCenterX;
|
|
float deltaY = event.getY() - joystickCenterY;
|
|
|
|
// Clamp the joystick movement to prevent it from going outside the area
|
|
float maxRadius = joystickLayout.getWidth() / 2f - joystickKnob.getWidth() / 2f;
|
|
float distance = (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
|
if (distance > maxRadius) {
|
|
float scale = maxRadius / distance;
|
|
deltaX *= scale;
|
|
deltaY *= scale;
|
|
}
|
|
|
|
joystickKnob.setX(joystickCenterX + deltaX - joystickKnob.getWidth() / 2f);
|
|
joystickKnob.setY(joystickCenterY + deltaY - joystickKnob.getHeight() / 2f);
|
|
|
|
// Send joystick values to native C code
|
|
short x = (short) (deltaX / maxRadius * Short.MAX_VALUE);
|
|
short y = (short) (deltaY / maxRadius * Short.MAX_VALUE);
|
|
|
|
// Send X-axis and Y-axis values
|
|
setAxis(isLeft ? ControllerButtons.AXIS_LX : ControllerButtons.AXIS_RX, x); // X-axis
|
|
setAxis(isLeft ? ControllerButtons.AXIS_LY : ControllerButtons.AXIS_RY, y); // Y-axis
|
|
break;
|
|
|
|
case MotionEvent.ACTION_UP:
|
|
case MotionEvent.ACTION_CANCEL:
|
|
// Reset joystick knob to the center position (ensure it's placed correctly)
|
|
joystickKnob.setX(joystickCenterX - joystickKnob.getWidth() / 2f);
|
|
joystickKnob.setY(joystickCenterY - joystickKnob.getHeight() / 2f);
|
|
|
|
// Reset joystick values to 0 when released or canceled
|
|
setAxis(isLeft ? ControllerButtons.AXIS_LX : ControllerButtons.AXIS_RX, (short) 0); // X-axis
|
|
setAxis(isLeft ? ControllerButtons.AXIS_LY : ControllerButtons.AXIS_RY, (short) 0); // Y-axis
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|