Android: Multiple improvements
- Save/resume state when closing/starting. - Error reporting - hopefully can figure out why it's not starting on some devices. - Reduce startup latency. - Add more options and descriptions to settings.
This commit is contained in:
@ -4,37 +4,57 @@ import android.content.Context;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
import android.view.Surface;
|
||||
import android.widget.Toast;
|
||||
|
||||
public class AndroidHostInterface
|
||||
{
|
||||
private long nativePointer;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
|
||||
public class AndroidHostInterface {
|
||||
private long mNativePointer;
|
||||
private Context mContext;
|
||||
|
||||
static public native AndroidHostInterface create(Context context, String userDirectory);
|
||||
|
||||
public AndroidHostInterface(long nativePointer)
|
||||
{
|
||||
this.nativePointer = nativePointer;
|
||||
public AndroidHostInterface(Context context) {
|
||||
this.mContext = context;
|
||||
}
|
||||
|
||||
public void reportError(String message) {
|
||||
Toast.makeText(mContext, message, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
public void reportMessage(String message) {
|
||||
Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
public native boolean isEmulationThreadRunning();
|
||||
public native boolean startEmulationThread(Surface surface, String filename, String state_filename);
|
||||
|
||||
public native boolean startEmulationThread(EmulationActivity emulationActivity, Surface surface, String filename, boolean resumeState, String state_filename);
|
||||
|
||||
public native void stopEmulationThread();
|
||||
|
||||
public native void surfaceChanged(Surface surface, int format, int width, int height);
|
||||
|
||||
// TODO: Find a better place for this.
|
||||
public native void setControllerType(int index, String typeName);
|
||||
|
||||
public native void setControllerButtonState(int index, int buttonCode, boolean pressed);
|
||||
|
||||
public native void setControllerAxisState(int index, int axisCode, float value);
|
||||
|
||||
public static native int getControllerButtonCode(String controllerType, String buttonName);
|
||||
|
||||
public static native int getControllerAxisCode(String controllerType, String axisName);
|
||||
|
||||
public native void refreshGameList(boolean invalidateCache, boolean invalidateDatabase);
|
||||
|
||||
public native GameListEntry[] getGameListEntries();
|
||||
|
||||
public native void resetSystem();
|
||||
|
||||
public native void loadState(boolean global, int slot);
|
||||
|
||||
public native void saveState(boolean global, int slot);
|
||||
|
||||
public native void applySettings();
|
||||
|
||||
static {
|
||||
@ -42,6 +62,7 @@ public class AndroidHostInterface
|
||||
}
|
||||
|
||||
static private AndroidHostInterface mInstance;
|
||||
|
||||
static public boolean createInstance(Context context) {
|
||||
// Set user path.
|
||||
String externalStorageDirectory = Environment.getExternalStorageDirectory().getAbsolutePath();
|
||||
@ -57,6 +78,7 @@ public class AndroidHostInterface
|
||||
static public boolean hasInstance() {
|
||||
return mInstance != null;
|
||||
}
|
||||
|
||||
static public AndroidHostInterface getInstance() {
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ package com.github.stenzek.duckstation;
|
||||
import android.annotation.SuppressLint;
|
||||
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import android.content.Intent;
|
||||
@ -11,14 +12,12 @@ import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.core.app.NavUtils;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
/**
|
||||
@ -30,77 +29,69 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||
* Settings interfaces.
|
||||
*/
|
||||
SharedPreferences mPreferences;
|
||||
|
||||
private boolean getBooleanSetting(String key, boolean defaultValue) {
|
||||
return mPreferences.getBoolean(key, defaultValue);
|
||||
}
|
||||
|
||||
private void setBooleanSetting(String key, boolean value) {
|
||||
SharedPreferences.Editor editor = mPreferences.edit();
|
||||
editor.putBoolean(key, value);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
private String getStringSetting(String key, String defaultValue) {
|
||||
return mPreferences.getString(key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Touchscreen controller overlay
|
||||
*/
|
||||
TouchscreenControllerView mTouchscreenController;
|
||||
private boolean mTouchscreenControllerVisible = true;
|
||||
public void reportError(String message) {
|
||||
Log.e("EmulationActivity", message);
|
||||
|
||||
/**
|
||||
* Whether or not the system UI should be auto-hidden after
|
||||
* {@link #AUTO_HIDE_DELAY_MILLIS} milliseconds.
|
||||
*/
|
||||
private static final boolean AUTO_HIDE = true;
|
||||
Object lock = new Object();
|
||||
runOnUiThread(() -> {
|
||||
// Toast.makeText(this, message, Toast.LENGTH_LONG);
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle("Error")
|
||||
.setMessage(message)
|
||||
.setPositiveButton("OK", (dialog, button) -> {
|
||||
dialog.dismiss();
|
||||
synchronized (lock) {
|
||||
lock.notify();
|
||||
}
|
||||
})
|
||||
.create()
|
||||
.show();
|
||||
});
|
||||
|
||||
/**
|
||||
* If {@link #AUTO_HIDE} is set, the number of milliseconds to wait after
|
||||
* user interaction before hiding the system UI.
|
||||
*/
|
||||
private static final int AUTO_HIDE_DELAY_MILLIS = 3000;
|
||||
|
||||
/**
|
||||
* Some older devices needs a small delay between UI widget updates
|
||||
* and a change of the status and navigation bar.
|
||||
*/
|
||||
private static final int UI_ANIMATION_DELAY = 300;
|
||||
private final Handler mHideHandler = new Handler();
|
||||
private EmulationSurfaceView mContentView;
|
||||
private final Runnable mHidePart2Runnable = new Runnable() {
|
||||
@SuppressLint("InlinedApi")
|
||||
@Override
|
||||
public void run() {
|
||||
// Delayed removal of status and navigation bar
|
||||
|
||||
// Note that some of these constants are new as of API 16 (Jelly Bean)
|
||||
// and API 19 (KitKat). It is safe to use them, as they are inlined
|
||||
// at compile-time and do nothing on earlier devices.
|
||||
mContentView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE
|
||||
| View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
|
||||
}
|
||||
};
|
||||
private final Runnable mShowPart2Runnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Delayed display of UI elements
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if (actionBar != null) {
|
||||
actionBar.show();
|
||||
synchronized (lock) {
|
||||
try {
|
||||
lock.wait();
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
};
|
||||
private boolean mVisible;
|
||||
private final Runnable mHideRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
hide();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void reportMessage(String message) {
|
||||
Log.i("EmulationActivity", message);
|
||||
runOnUiThread(() -> {
|
||||
Toast.makeText(this, message, Toast.LENGTH_SHORT);
|
||||
});
|
||||
}
|
||||
|
||||
public void onEmulationStarted() {
|
||||
}
|
||||
|
||||
public void onEmulationStopped() {
|
||||
runOnUiThread(() -> {
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
||||
public void onGameTitleChanged(String title) {
|
||||
runOnUiThread(() -> {
|
||||
setTitle(title);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
@ -114,16 +105,11 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||
return;
|
||||
}
|
||||
|
||||
String bootPath = getIntent().getStringExtra("bootPath");
|
||||
String bootSaveStatePath = getIntent().getStringExtra("bootSaveStatePath");
|
||||
boolean resumeState = getIntent().getBooleanExtra("resumeState", false);
|
||||
final String bootPath = getIntent().getStringExtra("bootPath");
|
||||
final boolean resumeState = getIntent().getBooleanExtra("resumeState", false);
|
||||
final String bootSaveStatePath = getIntent().getStringExtra("saveStatePath");
|
||||
|
||||
if (!AndroidHostInterface.getInstance()
|
||||
.startEmulationThread(holder.getSurface(), bootPath, bootSaveStatePath)) {
|
||||
Log.e("EmulationActivity", "Failed to start emulation thread");
|
||||
finishActivity(0);
|
||||
return;
|
||||
}
|
||||
AndroidHostInterface.getInstance().startEmulationThread(this, holder.getSurface(), bootPath, resumeState, bootSaveStatePath);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -146,14 +132,14 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
mVisible = true;
|
||||
mSystemUIVisible = true;
|
||||
mContentView = findViewById(R.id.fullscreen_content);
|
||||
mContentView.getHolder().addCallback(this);
|
||||
mContentView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (mVisible)
|
||||
hide();
|
||||
if (mSystemUIVisible)
|
||||
hideSystemUI();
|
||||
}
|
||||
});
|
||||
|
||||
@ -173,11 +159,16 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||
@Override
|
||||
protected void onPostCreate(Bundle savedInstanceState) {
|
||||
super.onPostCreate(savedInstanceState);
|
||||
hideSystemUI();
|
||||
}
|
||||
|
||||
// Trigger the initial hide() shortly after the activity has been
|
||||
// created, to briefly hint to the user that UI controls
|
||||
// are available.
|
||||
delayedHide(100);
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
|
||||
if (AndroidHostInterface.getInstance().isEmulationThreadRunning()) {
|
||||
AndroidHostInterface.getInstance().stopEmulationThread();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -228,37 +219,79 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (mVisible) {
|
||||
if (mSystemUIVisible) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
show();
|
||||
showSystemUI();
|
||||
}
|
||||
|
||||
private void hide() {
|
||||
/**
|
||||
* Some older devices needs a small delay between UI widget updates
|
||||
* and a change of the status and navigation bar.
|
||||
*/
|
||||
private static final int UI_ANIMATION_DELAY = 300;
|
||||
private final Handler mSystemUIHideHandler = new Handler();
|
||||
private EmulationSurfaceView mContentView;
|
||||
private final Runnable mHidePart2Runnable = new Runnable() {
|
||||
@SuppressLint("InlinedApi")
|
||||
@Override
|
||||
public void run() {
|
||||
// Delayed removal of status and navigation bar
|
||||
|
||||
// Note that some of these constants are new as of API 16 (Jelly Bean)
|
||||
// and API 19 (KitKat). It is safe to use them, as they are inlined
|
||||
// at compile-time and do nothing on earlier devices.
|
||||
mContentView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE
|
||||
| View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
|
||||
}
|
||||
};
|
||||
private final Runnable mShowPart2Runnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Delayed display of UI elements
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if (actionBar != null) {
|
||||
actionBar.show();
|
||||
}
|
||||
}
|
||||
};
|
||||
private boolean mSystemUIVisible;
|
||||
private final Runnable mHideRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
hideSystemUI();
|
||||
}
|
||||
};
|
||||
|
||||
private void hideSystemUI() {
|
||||
// Hide UI first
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if (actionBar != null) {
|
||||
actionBar.hide();
|
||||
}
|
||||
mVisible = false;
|
||||
mSystemUIVisible = false;
|
||||
|
||||
// Schedule a runnable to remove the status and navigation bar after a delay
|
||||
mHideHandler.removeCallbacks(mShowPart2Runnable);
|
||||
mHideHandler.postDelayed(mHidePart2Runnable, UI_ANIMATION_DELAY);
|
||||
mSystemUIHideHandler.removeCallbacks(mShowPart2Runnable);
|
||||
mSystemUIHideHandler.postDelayed(mHidePart2Runnable, UI_ANIMATION_DELAY);
|
||||
}
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
private void show() {
|
||||
private void showSystemUI() {
|
||||
// Show the system bar
|
||||
mContentView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
|
||||
mVisible = true;
|
||||
mSystemUIVisible = true;
|
||||
|
||||
// Schedule a runnable to display UI elements after a delay
|
||||
mHideHandler.removeCallbacks(mHidePart2Runnable);
|
||||
mHideHandler.postDelayed(mShowPart2Runnable, UI_ANIMATION_DELAY);
|
||||
mSystemUIHideHandler.removeCallbacks(mHidePart2Runnable);
|
||||
mSystemUIHideHandler.postDelayed(mShowPart2Runnable, UI_ANIMATION_DELAY);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -266,10 +299,16 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||
* previously scheduled calls.
|
||||
*/
|
||||
private void delayedHide(int delayMillis) {
|
||||
mHideHandler.removeCallbacks(mHideRunnable);
|
||||
mHideHandler.postDelayed(mHideRunnable, delayMillis);
|
||||
mSystemUIHideHandler.removeCallbacks(mHideRunnable);
|
||||
mSystemUIHideHandler.postDelayed(mHideRunnable, delayMillis);
|
||||
}
|
||||
|
||||
/**
|
||||
* Touchscreen controller overlay
|
||||
*/
|
||||
TouchscreenControllerView mTouchscreenController;
|
||||
private boolean mTouchscreenControllerVisible = true;
|
||||
|
||||
private void setTouchscreenControllerVisibility(boolean visible) {
|
||||
mTouchscreenControllerVisible = visible;
|
||||
mTouchscreenController.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
|
||||
|
||||
@ -17,13 +17,13 @@ import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public final class FileUtil {
|
||||
static String TAG="TAG";
|
||||
static String TAG = "TAG";
|
||||
private static final String PRIMARY_VOLUME_NAME = "primary";
|
||||
|
||||
@Nullable
|
||||
public static String getFullPathFromTreeUri(@Nullable final Uri treeUri, Context con) {
|
||||
if (treeUri == null) return null;
|
||||
String volumePath = getVolumePath(getVolumeIdFromTreeUri(treeUri),con);
|
||||
String volumePath = getVolumePath(getVolumeIdFromTreeUri(treeUri), con);
|
||||
if (volumePath == null) return File.separator;
|
||||
if (volumePath.endsWith(File.separator))
|
||||
volumePath = volumePath.substring(0, volumePath.length() - 1);
|
||||
@ -37,8 +37,7 @@ public final class FileUtil {
|
||||
return volumePath + documentPath;
|
||||
else
|
||||
return volumePath + File.separator + documentPath;
|
||||
}
|
||||
else return volumePath;
|
||||
} else return volumePath;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -7,14 +7,12 @@ import android.widget.TextView;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
public class GameListEntry {
|
||||
public enum EntryType
|
||||
{
|
||||
public enum EntryType {
|
||||
Disc,
|
||||
PSExe
|
||||
}
|
||||
|
||||
public enum CompatibilityRating
|
||||
{
|
||||
public enum CompatibilityRating {
|
||||
Unknown,
|
||||
DoesntBoot,
|
||||
CrashesInIntro,
|
||||
@ -72,15 +70,21 @@ public class GameListEntry {
|
||||
return mTitle;
|
||||
}
|
||||
|
||||
public String getModifiedTime() { return mModifiedTime; }
|
||||
public String getModifiedTime() {
|
||||
return mModifiedTime;
|
||||
}
|
||||
|
||||
public DiscRegion getRegion() {
|
||||
return mRegion;
|
||||
}
|
||||
|
||||
public EntryType getType() { return mType; }
|
||||
public EntryType getType() {
|
||||
return mType;
|
||||
}
|
||||
|
||||
public CompatibilityRating getCompatibilityRating() { return mCompatibilityRating; }
|
||||
public CompatibilityRating getCompatibilityRating() {
|
||||
return mCompatibilityRating;
|
||||
}
|
||||
|
||||
public void fillView(View view) {
|
||||
((TextView) view.findViewById(R.id.game_list_view_entry_title)).setText(mTitle);
|
||||
|
||||
@ -28,27 +28,26 @@ import android.view.MenuItem;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.PopupMenu;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.prefs.Preferences;
|
||||
|
||||
import static com.google.android.material.snackbar.Snackbar.make;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
private static final int REQUEST_EXTERNAL_STORAGE_PERMISSIONS = 1;
|
||||
private static final int REQUEST_ADD_DIRECTORY_TO_GAME_LIST = 2;
|
||||
|
||||
private GameList mGameList;
|
||||
private ListView mGameListView;
|
||||
private boolean mHasExternalStoragePermissions = false;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (!AndroidHostInterface.hasInstance() && !AndroidHostInterface.createInstance(this)) {
|
||||
Log.i("MainActivity", "Failed to create host interface");
|
||||
throw new RuntimeException("Failed to create host interface");
|
||||
}
|
||||
|
||||
setContentView(R.layout.activity_main);
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
@ -93,6 +92,18 @@ public class MainActivity extends AppCompatActivity {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
mHasExternalStoragePermissions = checkForExternalStoragePermissions();
|
||||
if (mHasExternalStoragePermissions)
|
||||
completeStartup();
|
||||
}
|
||||
|
||||
private void completeStartup() {
|
||||
if (!AndroidHostInterface.hasInstance() && !AndroidHostInterface.createInstance(this)) {
|
||||
Log.i("MainActivity", "Failed to create host interface");
|
||||
throw new RuntimeException("Failed to create host interface");
|
||||
}
|
||||
|
||||
mGameList.refresh(false, false);
|
||||
}
|
||||
|
||||
@ -122,12 +133,17 @@ public class MainActivity extends AppCompatActivity {
|
||||
int id = item.getItemId();
|
||||
|
||||
//noinspection SimplifiableIfStatement
|
||||
if (id == R.id.action_add_game_directory) {
|
||||
if (id == R.id.action_resume) {
|
||||
startEmulation(null, true);
|
||||
} else if (id == R.id.action_start_bios) {
|
||||
startEmulation(null, false);
|
||||
} else if (id == R.id.action_add_game_directory) {
|
||||
startAddGameDirectory();
|
||||
} else if (id == R.id.action_scan_for_new_games) {
|
||||
mGameList.refresh(false, false);
|
||||
} if (id == R.id.action_rescan_all_games) {
|
||||
mGameList.refresh(true, false);
|
||||
}
|
||||
if (id == R.id.action_rescan_all_games) {
|
||||
mGameList.refresh(true, true);
|
||||
}
|
||||
if (id == R.id.action_settings) {
|
||||
Intent intent = new Intent(this, SettingsActivity.class);
|
||||
@ -190,19 +206,21 @@ public class MainActivity extends AppCompatActivity {
|
||||
int[] grantResults) {
|
||||
// check that all were successful
|
||||
for (int i = 0; i < grantResults.length; i++) {
|
||||
if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
|
||||
Snackbar.make(mGameListView,
|
||||
"External storage permissions are required to start emulation.",
|
||||
Snackbar.LENGTH_LONG);
|
||||
if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
|
||||
if (!mHasExternalStoragePermissions) {
|
||||
mHasExternalStoragePermissions = true;
|
||||
completeStartup();
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(this,
|
||||
"External storage permissions are required to use DuckStation.",
|
||||
Toast.LENGTH_LONG);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean startEmulation(String bootPath, boolean resumeState) {
|
||||
if (!checkForExternalStoragePermissions()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(this, EmulationActivity.class);
|
||||
intent.putExtra("bootPath", bootPath);
|
||||
intent.putExtra("resumeState", resumeState);
|
||||
|
||||
@ -19,25 +19,24 @@ public class TouchscreenControllerButtonView extends View {
|
||||
private String mButtonName = "";
|
||||
private ButtonStateChangedListener mListener;
|
||||
|
||||
public interface ButtonStateChangedListener
|
||||
{
|
||||
public interface ButtonStateChangedListener {
|
||||
void onButtonStateChanged(TouchscreenControllerButtonView view, boolean pressed);
|
||||
}
|
||||
|
||||
|
||||
public TouchscreenControllerButtonView(Context context) {
|
||||
super(context);
|
||||
init(context,null, 0);
|
||||
init(context, null, 0);
|
||||
}
|
||||
|
||||
public TouchscreenControllerButtonView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context,attrs, 0);
|
||||
init(context, attrs, 0);
|
||||
}
|
||||
|
||||
public TouchscreenControllerButtonView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
init(context,attrs, defStyle);
|
||||
init(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
private void init(Context context, AttributeSet attrs, int defStyle) {
|
||||
@ -80,15 +79,12 @@ public class TouchscreenControllerButtonView extends View {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event)
|
||||
{
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
final boolean oldState = mPressed;
|
||||
|
||||
switch (event.getAction())
|
||||
{
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
case MotionEvent.ACTION_POINTER_DOWN:
|
||||
{
|
||||
case MotionEvent.ACTION_POINTER_DOWN: {
|
||||
mPressed = true;
|
||||
invalidate();
|
||||
|
||||
@ -99,8 +95,7 @@ public class TouchscreenControllerButtonView extends View {
|
||||
}
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
{
|
||||
case MotionEvent.ACTION_POINTER_UP: {
|
||||
mPressed = false;
|
||||
invalidate();
|
||||
|
||||
@ -114,8 +109,7 @@ public class TouchscreenControllerButtonView extends View {
|
||||
return super.onTouchEvent(event);
|
||||
}
|
||||
|
||||
public boolean isPressed()
|
||||
{
|
||||
public boolean isPressed() {
|
||||
return mPressed;
|
||||
}
|
||||
|
||||
@ -127,13 +121,11 @@ public class TouchscreenControllerButtonView extends View {
|
||||
mButtonName = buttonName;
|
||||
}
|
||||
|
||||
public int getButtonCode()
|
||||
{
|
||||
public int getButtonCode() {
|
||||
return mButtonCode;
|
||||
}
|
||||
|
||||
public void setButtonCode(int code)
|
||||
{
|
||||
public void setButtonCode(int code) {
|
||||
mButtonCode = code;
|
||||
}
|
||||
|
||||
@ -153,8 +145,7 @@ public class TouchscreenControllerButtonView extends View {
|
||||
mUnpressedDrawable = unpressedDrawable;
|
||||
}
|
||||
|
||||
public void setButtonStateChangedListener(ButtonStateChangedListener listener)
|
||||
{
|
||||
public void setButtonStateChangedListener(ButtonStateChangedListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,9 +50,8 @@ public class TouchscreenControllerView extends FrameLayout implements Touchscree
|
||||
linkButton(view, R.id.controller_button_r2, "R2");
|
||||
}
|
||||
|
||||
private void linkButton(View view, int id, String buttonName)
|
||||
{
|
||||
TouchscreenControllerButtonView buttonView = (TouchscreenControllerButtonView)view.findViewById(id);
|
||||
private void linkButton(View view, int id, String buttonName) {
|
||||
TouchscreenControllerButtonView buttonView = (TouchscreenControllerButtonView) view.findViewById(id);
|
||||
buttonView.setButtonName(buttonName);
|
||||
buttonView.setButtonStateChangedListener(this);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user