From e808dd9861c620c7506a02c80cd11ab3625b261a Mon Sep 17 00:00:00 2001 From: Aaron Culliney Date: Tue, 7 Apr 2015 22:40:22 -0700 Subject: [PATCH] First cut at Android disk selection interface --- .../org/deadc0de/apple2ix/Apple2Activity.java | 26 ++- .../deadc0de/apple2ix/Apple2DisksMenu.java | 159 ++++++++++++++++++ .../org/deadc0de/apple2ix/Apple2MainMenu.java | 15 +- .../org/deadc0de/apple2ix/Apple2View.java | 7 +- Android/app/src/main/res/layout/a2disk.xml | 61 +++++++ .../src/main/res/layout/activity_disks.xml | 48 ++++++ Android/app/src/main/res/values/strings.xml | 4 + Android/jni/jnihooks.c | 20 +++ 8 files changed, 335 insertions(+), 5 deletions(-) create mode 100644 Android/app/src/main/java/org/deadc0de/apple2ix/Apple2DisksMenu.java create mode 100644 Android/app/src/main/res/layout/a2disk.xml create mode 100644 Android/app/src/main/res/layout/activity_disks.xml diff --git a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Activity.java b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Activity.java index 6fc52f24..41332d12 100644 --- a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Activity.java +++ b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Activity.java @@ -39,6 +39,8 @@ public class Apple2Activity extends Activity { private final static int SOFTKEYBOARD_THRESHOLD = 50; private final static int MAX_FINGERS = 32;// HACK ... + private String mDataDir = null; + private Apple2View mView = null; private AlertDialog mQuitDialog = null; private AlertDialog mRebootDialog = null; @@ -77,6 +79,7 @@ public class Apple2Activity extends Activity { public native void nativeEnableTouchJoystick(boolean visibility); public native void nativeSetColor(int color); + public native void nativeChooseDisk(String path, boolean driveA, boolean readOnly); // HACK NOTE 2015/02/22 : Apparently native code cannot easily access stuff in the APK ... so copy various resources @@ -152,8 +155,8 @@ public class Apple2Activity extends Activity { super.onCreate(savedInstanceState); Log.e(TAG, "onCreate()"); - String dataDir = firstTimeInitialization(); - nativeOnCreate(dataDir); + mDataDir = firstTimeInitialization(); + nativeOnCreate(mDataDir); mView = new Apple2View(this); setContentView(mView); @@ -214,9 +217,13 @@ public class Apple2Activity extends Activity { } Apple2SettingsMenu settingsMenu = mView.getSettingsMenu(); + Apple2DisksMenu disksMenu = mView.getDisksMenu(); if (settingsMenu != null) { settingsMenu.dismissWithoutResume(); } + if (disksMenu != null) { + disksMenu.dismissWithoutResume(); + } boolean someMenuShowing = mainMenu.isShowing() || (mQuitDialog != null && mQuitDialog.isShowing()) || @@ -237,9 +244,12 @@ public class Apple2Activity extends Activity { public boolean onKeyUp(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { Apple2SettingsMenu settingsMenu = mView.getSettingsMenu(); + Apple2DisksMenu disksMenu = mView.getDisksMenu(); if (settingsMenu != null) { if (settingsMenu.isShowing()) { settingsMenu.dismiss(); + } else if (disksMenu.isShowing()) { + disksMenu.dismiss(); } else { mView.showMainMenu(); } @@ -304,9 +314,13 @@ public class Apple2Activity extends Activity { } Apple2SettingsMenu settingsMenu = mainMenu.getSettingsMenu(); + Apple2DisksMenu disksMenu = mView.getDisksMenu(); if (settingsMenu != null && settingsMenu.isShowing()) { break; } + if (disksMenu != null && disksMenu.isShowing()) { + break; + } //printSamples(event); int action = event.getActionMasked(); @@ -396,6 +410,14 @@ public class Apple2Activity extends Activity { nativeGraphicsInitialized(width, height); } + public Apple2View getView() { + return mView; + } + + public String getDataDir() { + return mDataDir; + } + public int getWidth() { return mWidth; } diff --git a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2DisksMenu.java b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2DisksMenu.java new file mode 100644 index 00000000..5407a150 --- /dev/null +++ b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2DisksMenu.java @@ -0,0 +1,159 @@ +/* + * Apple // emulator for *nix + * + * This software package is subject to the GNU General Public License + * version 2 or later (your choice) as published by the Free Software + * Foundation. + * + * THERE ARE NO WARRANTIES WHATSOEVER. + * + */ + +package org.deadc0de.apple2ix; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.CheckBox; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.RadioButton; + +import java.io.File; +import java.io.FilenameFilter; +import java.util.Arrays; + +public class Apple2DisksMenu { + + private final static String TAG = "Apple2DisksMenu"; + + private Apple2Activity mActivity = null; + private Apple2View mParentView = null; + private View mDisksView = null; + + public Apple2DisksMenu(Apple2Activity activity, Apple2View parent) { + mActivity = activity; + mParentView = parent; + setup(); + } + + private void setup() { + + LayoutInflater inflater = (LayoutInflater) mActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + mDisksView = inflater.inflate(R.layout.activity_disks, null, false); + + RadioButton diskA = (RadioButton) mDisksView.findViewById(R.id.radioButton_diskA); + diskA.setChecked(true); + + CheckBox readWrite = (CheckBox) mDisksView.findViewById(R.id.checkBox_readWrite); + readWrite.setChecked(false); + } + + private void dynamicSetup() { + + final ListView disksList = (ListView)mDisksView.findViewById(R.id.listView_settings); + disksList.setEnabled(true); + + String dataDir = mActivity.getDataDir() + File.separator + "disks"; + File dir = new File(dataDir); + + final File[] files = dir.listFiles(new FilenameFilter() { + public boolean accept(File dir, String name) { + name = name.toLowerCase(); + boolean acceptable = name.endsWith(".dsk") || name.endsWith(".do") || name.endsWith(".po") || name.endsWith(".nib") || + name.endsWith(".dsk.gz") || name.endsWith(".do.gz") || name.endsWith(".po.gz") || name.endsWith(".nib.gz"); + return acceptable; + } + }); + + Arrays.sort(files); + int len = files.length; + final String[] fileNames = new String[len]; + final boolean[] isDirectory = new boolean[len]; + + for (int i=0; i adapter = new ArrayAdapter(mActivity, R.layout.a2disk, R.id.a2disk_title, fileNames) { + @Override + public boolean areAllItemsEnabled() { + return true; + } + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View view = super.getView(position, convertView, parent); + + LinearLayout layout = (LinearLayout)view.findViewById(R.id.a2disk_widget_frame); + if (layout.getChildCount() > 0) { + // layout cells appear to be reused when scrolling into view ... make sure we start with clear hierarchy + layout.removeAllViews(); + } + + if (isDirectory[position]) { + ImageView imageView = new ImageView(mActivity); + Drawable drawable = mActivity.getResources().getDrawable(android.R.drawable.ic_menu_more); + imageView.setImageDrawable(drawable); + layout.addView(imageView); + } + return view; + } + }; + + disksList.setAdapter(adapter); + disksList.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + if (isDirectory[position]) { + // TODO FIXME ... + } else { + RadioButton diskA = (RadioButton)mDisksView.findViewById(R.id.radioButton_diskA); + CheckBox readWrite = (CheckBox)mDisksView.findViewById(R.id.checkBox_readWrite); + mActivity.nativeChooseDisk(files[position].getAbsolutePath(), diskA.isChecked(), !readWrite.isChecked()); + Apple2DisksMenu.this.dismissWithoutResume(); + Apple2DisksMenu.this.mActivity.getView().showMainMenu(); + } + } + }); + } + + public void show() { + if (isShowing()) { + return; + } + dynamicSetup(); + mActivity.nativeOnPause(); + mActivity.addContentView(mDisksView, new FrameLayout.LayoutParams(mActivity.getWidth(), mActivity.getHeight())); + } + + public void dismiss() { + if (isShowing()) { + dismissWithoutResume(); + mActivity.nativeOnResume(); + } + } + + public void dismissWithoutResume() { + if (isShowing()) { + ((ViewGroup)mDisksView.getParent()).removeView(mDisksView); + // HACK FIXME TODO ... we seem to lose ability to toggle/show soft keyboard upon dismissal of mDisksView after use. + // This hack appears to get the Android UI unwedged ... =P + Apple2MainMenu androidUIFTW = mParentView.getMainMenu(); + androidUIFTW.show(); + androidUIFTW.dismiss(); + } + } + + public boolean isShowing() { + return mDisksView.isShown(); + } +} diff --git a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2MainMenu.java b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2MainMenu.java index 910eb2dd..edc77662 100644 --- a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2MainMenu.java +++ b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2MainMenu.java @@ -35,6 +35,7 @@ public class Apple2MainMenu { private Apple2View mParentView = null; private PopupWindow mMainMenuPopup = null; private Apple2SettingsMenu mSettingsMenu = null; + private Apple2DisksMenu mDisksMenu = null; public Apple2MainMenu(Apple2Activity activity, Apple2View parent) { mActivity = activity; @@ -161,7 +162,8 @@ public class Apple2MainMenu { mMainMenuPopup.setOnDismissListener(new PopupWindow.OnDismissListener() { @Override public void onDismiss() { - if ( !(getSettingsMenu().isShowing() /*|| getDisksMenu().isShowing()*/) ) { + boolean otherMenusShowing = (getSettingsMenu().isShowing() || getDisksMenu().isShowing()); + if (!otherMenusShowing) { Apple2MainMenu.this.mActivity.nativeOnResume(); } } @@ -169,7 +171,9 @@ public class Apple2MainMenu { } public void showDisksMenu() { - Log.d(TAG, "showDisksMenu..."); + Apple2DisksMenu disksMenu = getDisksMenu(); + disksMenu.show(); + mMainMenuPopup.dismiss(); } public void showSettings() { @@ -178,6 +182,13 @@ public class Apple2MainMenu { mMainMenuPopup.dismiss(); } + public synchronized Apple2DisksMenu getDisksMenu() { + if (mDisksMenu == null) { + mDisksMenu = new Apple2DisksMenu(mActivity, mParentView); + } + return mDisksMenu; + } + public synchronized Apple2SettingsMenu getSettingsMenu() { if (mSettingsMenu == null) { mSettingsMenu = new Apple2SettingsMenu(mActivity, mParentView); diff --git a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2View.java b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2View.java index 3db6f57a..0a3ad5e4 100644 --- a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2View.java +++ b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2View.java @@ -93,7 +93,8 @@ class Apple2View extends GLSurfaceView { public void showMainMenu() { if (mMainMenu != null) { Apple2SettingsMenu settingsMenu = mMainMenu.getSettingsMenu(); - if (!settingsMenu.isShowing()) { + Apple2DisksMenu disksMenu = mMainMenu.getDisksMenu(); + if (! (settingsMenu.isShowing() || disksMenu.isShowing()) ) { mMainMenu.show(); } } @@ -107,6 +108,10 @@ class Apple2View extends GLSurfaceView { return (mMainMenu == null) ? null : mMainMenu.getSettingsMenu(); } + public Apple2DisksMenu getDisksMenu() { + return (mMainMenu == null) ? null : mMainMenu.getDisksMenu(); + } + public void toggleKeyboard() { InputMethodManager inputMethodManager=(InputMethodManager)mActivity.getSystemService(Context.INPUT_METHOD_SERVICE); inputMethodManager.toggleSoftInputFromWindow(getApplicationWindowToken(), InputMethodManager.SHOW_FORCED, 0); diff --git a/Android/app/src/main/res/layout/a2disk.xml b/Android/app/src/main/res/layout/a2disk.xml new file mode 100644 index 00000000..d3119c1a --- /dev/null +++ b/Android/app/src/main/res/layout/a2disk.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Android/app/src/main/res/layout/activity_disks.xml b/Android/app/src/main/res/layout/activity_disks.xml new file mode 100644 index 00000000..9f9cc6b5 --- /dev/null +++ b/Android/app/src/main/res/layout/activity_disks.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + diff --git a/Android/app/src/main/res/values/strings.xml b/Android/app/src/main/res/values/strings.xml index 21ce972b..8c7132db 100644 --- a/Android/app/src/main/res/values/strings.xml +++ b/Android/app/src/main/res/values/strings.xml @@ -15,6 +15,10 @@ TouchJoy axis and buttons, hitboxes, etc Configure video… Color settings + Drive 1 + Drive 2 + Read/write + Insert disk: Joystick Key Joystick 1 Key Joystick 2 diff --git a/Android/jni/jnihooks.c b/Android/jni/jnihooks.c index d18e300a..f680b652 100644 --- a/Android/jni/jnihooks.c +++ b/Android/jni/jnihooks.c @@ -276,3 +276,23 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeSetColor(JNIEnv *env, jobje video_redraw(); } +void Java_org_deadc0de_apple2ix_Apple2Activity_nativeChooseDisk(JNIEnv *env, jobject obj, jstring jPath, jboolean driveA, jboolean readOnly) { + const char *path = (*env)->GetStringUTFChars(env, jPath, 0); + int drive = driveA ? 0 : 1; + int ro = readOnly ? 1 : 0; + LOG("nativeChooseDisk(%s, %s, %s)", path, driveA ? "drive A" : "drive B", readOnly ? "read only" : "read/write"); + if (c_new_diskette_6(drive, path, ro)) { + char *gzPath = NULL; + asprintf(&gzPath, "%s.gz", path); + if (c_new_diskette_6(drive, gzPath, ro)) { + // TODO FIXME : show error message disk was unreadable ... + } else { + // TODO FIXME : show an OpenGL message that the disk was chosen ... + } + FREE(gzPath); + } else { + // TODO FIXME : show an OpenGL message that the disk was chosen ... + } + (*env)->ReleaseStringUTFChars(env, jPath, path); +} +