Merge branch 'develop' ... latest Linux, Android, and Mac variant

This commit is contained in:
Aaron Culliney 2015-11-14 11:10:11 -08:00
commit b445c2dcf1
1088 changed files with 528661 additions and 9130 deletions

26
.gitignore vendored
View File

@ -43,16 +43,19 @@ ylwrap
apple2ix*.tar.gz
test-driver
# GDB
.gdb_history
# generated sources
src/rom.c
src/x86/glue.S
src/meta/debug.c
src/arm/glue.S
# sub{tree,module}
src/rom
# generated binaries
apple2ix
/apple2ix
genfont
genrom
@ -64,3 +67,22 @@ man6
xcuserdata
.DS_Store
xcshareddata
# Android CLI builds
Android/bin
Android/gen
Android/libs
# Android.mk is tEh dynamicz!
Android/jni/Android.mk
*.apk
# Android Studio
.gradle
Android/local.properties
Android/.idea/workspace.xml
Android/.idea/libraries
Android/.idea/dictionaries
Android/build
Android/jni/obj
Android/obj

9
Android/.classpath Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
<classpathentry kind="output" path="bin/classes"/>
</classpath>

1
Android/.idea/.name Normal file
View File

@ -0,0 +1 @@
Android

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<resourceExtensions />
<wildcardResourcePatterns>
<entry name="!?*.java" />
<entry name="!?*.form" />
<entry name="!?*.class" />
<entry name="!?*.groovy" />
<entry name="!?*.scala" />
<entry name="!?*.flex" />
<entry name="!?*.kt" />
<entry name="!?*.clj" />
<entry name="!?*.aj" />
</wildcardResourcePatterns>
<annotationProcessing>
<profile default="true" name="Default" enabled="false">
<processorPath useClasspath="true" />
</profile>
</annotationProcessing>
</component>
</project>

View File

@ -0,0 +1,9 @@
<component name="CopyrightManager">
<copyright>
<option name="notice" value="Copyright (c) deadc0de.org" />
<option name="keyword" value="Copyright" />
<option name="allowReplaceKeyword" value="" />
<option name="myName" value="deadc0de.org" />
<option name="myLocal" value="true" />
</copyright>
</component>

View File

@ -0,0 +1,3 @@
<component name="CopyrightManager">
<settings default="" />
</component>

19
Android/.idea/gradle.xml Normal file
View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="distributionType" value="LOCAL" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleHome" value="$APPLICATION_HOME_DIR$/gradle/gradle-2.4" />
<option name="gradleJvm" value="1.7" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>

46
Android/.idea/misc.xml Normal file
View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EntryPointsManager">
<entry_points version="2.0" />
</component>
<component name="NullableNotNullManager">
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
<option name="myNullables">
<value>
<list size="4">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
</list>
</value>
</option>
<option name="myNotNulls">
<value>
<list size="4">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
</list>
</value>
</option>
</component>
<component name="ProjectLevelVcsManager" settingsEditedManually="false">
<OptionsSetting value="true" id="Add" />
<OptionsSetting value="true" id="Remove" />
<OptionsSetting value="true" id="Checkout" />
<OptionsSetting value="true" id="Update" />
<OptionsSetting value="true" id="Status" />
<OptionsSetting value="true" id="Edit" />
<ConfirmationsSetting value="0" id="Add" />
<ConfirmationsSetting value="0" id="Remove" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.7" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/Android.iml" filepath="$PROJECT_DIR$/Android.iml" />
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
</modules>
</component>
</project>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
</set>
</option>
</component>
</project>

6
Android/.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="" />
</component>
</project>

19
Android/Android.iml Normal file
View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="Android" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="java-gradle" name="Java-Gradle">
<configuration>
<option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" />
<option name="BUILDABLE" value="false" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.gradle" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

1
Android/AndroidManifest.xml Symbolic link
View File

@ -0,0 +1 @@
app/build/intermediates/manifests/full/debug/AndroidManifest.xml

1
Android/app/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

97
Android/app/app.iml Normal file
View File

@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id=":app" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="Android" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="android-gradle" name="Android-Gradle">
<configuration>
<option name="GRADLE_PROJECT_PATH" value=":app" />
</configuration>
</facet>
<facet type="android" name="Android">
<configuration>
<option name="SELECTED_BUILD_VARIANT" value="debug" />
<option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
<option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugAndroidTest" />
<option name="COMPILE_JAVA_TEST_TASK_NAME" value="compileDebugAndroidTestSources" />
<afterSyncTasks>
<task>generateDebugAndroidTestSources</task>
<task>generateDebugSources</task>
</afterSyncTasks>
<option name="ALLOW_USER_CONFIGURATION" value="false" />
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
<option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
<output-test url="file://$MODULE_DIR$/build/intermediates/classes/androidTest/debug" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/coverage-instrumented-classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/23.1.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/23.1.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/libs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndk" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
<excludeFolder url="file://$MODULE_DIR$/build/tmp" />
</content>
<orderEntry type="jdk" jdkName="Android API 23 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" exported="" name="support-annotations-23.1.0" level="project" />
<orderEntry type="library" exported="" name="appcompat-v7-23.1.0" level="project" />
<orderEntry type="library" exported="" name="support-v4-23.1.0" level="project" />
</component>
</module>

41
Android/app/build.gradle Normal file
View File

@ -0,0 +1,41 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "21.1.2"
signingConfigs {
release {
storeFile file("release2.keystore")
storePassword System.getenv("GOOGSTOREPWD")
keyPassword System.getenv("GOOGKEYPWD")
keyAlias "release"
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
debug {
debuggable true
jniDebuggable true
}
}
defaultConfig {
applicationId "org.deadc0de.apple2ix.basic"
minSdkVersion 10
targetSdkVersion 23
versionCode 7
versionName "1.0.4"
ndk {
moduleName "apple2ix"
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:23.1.0'
}

17
Android/app/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /home/asc/Android/Sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

View File

@ -0,0 +1,13 @@
package org.deadc0de.apple2ix;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
*/
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.deadc0de.apple2ix.basic" >
<uses-permission android:name="android.permission.INTERNET" />
</manifest>

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.deadc0de.apple2ix.basic" >
<uses-feature android:glEsVersion="0x00020000" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<supports-screens
android:largeScreens="true"
android:normalScreens="true"
android:resizeable="true"
android:smallScreens="false" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:name="org.deadc0de.apple2ix.Apple2Activity"
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|screenLayout|fontScale|uiMode|orientation"
android:screenOrientation="sensorLandscape"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:windowSoftInputMode="adjustResize" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" android:host="*" android:mimeType="application/x-gzip" android:pathPattern="/.*\\.nib\\.gz" />
<data android:scheme="file" android:host="*" android:mimeType="application/x-gzip" android:pathPattern="/.*\\.dsk\\.gz" />
<data android:scheme="file" android:host="*" android:mimeType="application/x-gzip" android:pathPattern="/.*\\.do\\.gz" />
<data android:scheme="file" android:host="*" android:mimeType="application/x-gzip" android:pathPattern="/.*\\.po\\.gz" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" /> <!-- catch-all since I can't get the following to work because ... Android -->
<!--
<data android:scheme="file" android:host="*" android:mimeType="application/octet-stream" android:pathPattern="/.*\\.nib" />
<data android:scheme="file" android:host="*" android:mimeType="application/octet-stream" android:pathPattern="/.*\\.dsk" />
<data android:scheme="file" android:host="*" android:mimeType="application/octet-stream" android:pathPattern="/.*\\.do" />
<data android:scheme="file" android:host="*" android:mimeType="application/octet-stream" android:pathPattern="/.*\\.po" />
-->
</intent-filter>
</activity>
</application>
</manifest>

1
Android/app/src/main/assets Symbolic link
View File

@ -0,0 +1 @@
../../../assets

View File

@ -0,0 +1,282 @@
/*
* Apple // emulator for *nix
*
* This software package is subject to the GNU General Public License
* version 3 or later (your choice) as published by the Free Software
* Foundation.
*
* Copyright 2015 Aaron Culliney
*
*/
package org.deadc0de.apple2ix;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CheckedTextView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.SeekBar;
import android.widget.TextView;
import org.deadc0de.apple2ix.basic.R;
public abstract class Apple2AbstractMenu implements Apple2MenuView {
private final static String TAG = "Apple2AbstractMenu";
protected Apple2Activity mActivity = null;
private View mSettingsView = null;
public Apple2AbstractMenu(Apple2Activity activity) {
mActivity = activity;
setup();
}
public void show() {
if (isShowing()) {
return;
}
mActivity.pushApple2View(this);
}
public void dismiss() {
mActivity.popApple2View(this);
}
public void dismissAll() {
this.dismiss();
}
public boolean isShowing() {
return mSettingsView.getParent() != null;
}
public View getView() {
return mSettingsView;
}
public boolean isCalibrating() {
return false;
}
public void onKeyTapCalibrationEvent(char ascii, int scancode) {
/* ... */
}
// ------------------------------------------------------------------------
// required overrides ...
public interface IMenuEnum {
public String getTitle(final Apple2Activity activity);
public String getSummary(final Apple2Activity activity);
public View getView(final Apple2Activity activity, View convertView);
public void handleSelection(final Apple2Activity activity, final Apple2AbstractMenu settingsMenu, boolean isChecked);
}
public abstract IMenuEnum[] allValues();
public abstract String[] allTitles();
public abstract boolean areAllItemsEnabled();
public abstract boolean isEnabled(int position);
// ------------------------------------------------------------------------
// boilerplate menu view code
protected static View _basicView(Apple2Activity activity, IMenuEnum setting, View convertView) {
TextView tv = (TextView) convertView.findViewById(R.id.a2preference_title);
if (tv == null) {
// attemping to recycle different layout ...
LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.a2preference, null, false);
tv = (TextView) convertView.findViewById(R.id.a2preference_title);
}
tv.setText(setting.getTitle(activity));
tv = (TextView) convertView.findViewById(R.id.a2preference_summary);
tv.setText(setting.getSummary(activity));
LinearLayout layout = (LinearLayout) convertView.findViewById(R.id.a2preference_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();
}
return convertView;
}
public interface IPreferenceLoadSave {
public int intValue();
public void saveInt(int value);
}
public interface IPreferenceSlider extends IPreferenceLoadSave {
public void showValue(int value, final TextView seekBarValue);
}
protected static View _sliderView(final Apple2Activity activity, final IMenuEnum setting, final int numChoices, final IPreferenceSlider iLoadSave) {
LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.a2preference_slider, null, false);
TextView tv = (TextView) view.findViewById(R.id.a2preference_slider_summary);
tv.setText(setting.getSummary(activity));
final TextView seekBarValue = (TextView) view.findViewById(R.id.a2preference_slider_seekBarValue);
SeekBar sb = (SeekBar) view.findViewById(R.id.a2preference_slider_seekBar);
sb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (!fromUser) {
return;
}
iLoadSave.showValue(progress, seekBarValue);
iLoadSave.saveInt(progress);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
sb.setMax(0); // http://stackoverflow.com/questions/10278467/seekbar-not-setting-actual-progress-setprogress-not-working-on-early-android
sb.setMax(numChoices);
int progress = iLoadSave.intValue();
sb.setProgress(progress);
iLoadSave.showValue(progress, seekBarValue);
return view;
}
protected static void _alertDialogHandleSelection(final Apple2Activity activity, final int titleId, final String[] choices, final IPreferenceLoadSave iLoadSave) {
_alertDialogHandleSelection(activity, activity.getResources().getString(titleId), choices, iLoadSave);
}
protected static void _alertDialogHandleSelection(final Apple2Activity activity, final String titleId, final String[] choices, final IPreferenceLoadSave iLoadSave) {
AlertDialog.Builder builder = new AlertDialog.Builder(activity).setIcon(R.drawable.ic_launcher).setCancelable(true).setTitle(titleId);
builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
final int checkedPosition = iLoadSave.intValue();
final ArrayAdapter<String> adapter = new ArrayAdapter<String>(activity, android.R.layout.select_dialog_singlechoice, choices) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
CheckedTextView ctv = (CheckedTextView) view.findViewById(android.R.id.text1);
ctv.setChecked(position == checkedPosition);
return view;
}
};
builder.setAdapter(adapter, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int value) {
iLoadSave.saveInt(value);
dialog.dismiss();
}
});
AlertDialog dialog = builder.create();
activity.registerAndShowDialog(dialog);
}
protected static ImageView _addPopupIcon(Apple2Activity activity, IMenuEnum setting, View convertView) {
ImageView imageView = new ImageView(activity);
Drawable drawable = activity.getResources().getDrawable(android.R.drawable.ic_menu_edit);
imageView.setImageDrawable(drawable);
LinearLayout layout = (LinearLayout) convertView.findViewById(R.id.a2preference_widget_frame);
layout.addView(imageView);
return imageView;
}
protected static CheckBox _addCheckbox(Apple2Activity activity, IMenuEnum setting, View convertView, boolean isChecked) {
CheckBox checkBox = new CheckBox(activity);
checkBox.setChecked(isChecked);
LinearLayout layout = (LinearLayout) convertView.findViewById(R.id.a2preference_widget_frame);
layout.addView(checkBox);
return checkBox;
}
private void setup() {
LayoutInflater inflater = (LayoutInflater) mActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mSettingsView = inflater.inflate(R.layout.activity_settings, null, false);
final Button cancelButton = (Button) mSettingsView.findViewById(R.id.cancelButton);
cancelButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mActivity.dismissAllMenus();
}
});
ListView settingsList = (ListView) mSettingsView.findViewById(R.id.listView_settings);
settingsList.setEnabled(true);
ArrayAdapter<?> adapter = new ArrayAdapter<String>(mActivity, R.layout.a2preference, R.id.a2preference_title, allTitles()) {
@Override
public boolean areAllItemsEnabled() {
return Apple2AbstractMenu.this.areAllItemsEnabled();
}
@Override
public boolean isEnabled(int position) {
return Apple2AbstractMenu.this.isEnabled(position);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//View view = super.getView(position, convertView, parent);
// ^^^ WHOA ... this is catching an NPE deep in AOSP code on the second time loading ... WTF?
// Methinks it is related to the hack of loading a completely different R.layout.something for certain views...
View view = convertView != null ? convertView : super.getView(position, null, parent);
IMenuEnum setting = allValues()[position];
return setting.getView(mActivity, view);
}
};
settingsList.setAdapter(adapter);
settingsList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
IMenuEnum setting = allValues()[position];
LinearLayout layout = (LinearLayout) view.findViewById(R.id.a2preference_widget_frame);
if (layout == null) {
return;
}
View childView = layout.getChildAt(0);
boolean selected = false;
if (childView != null && childView instanceof CheckBox) {
CheckBox checkBox = (CheckBox) childView;
checkBox.setChecked(!checkBox.isChecked());
selected = checkBox.isChecked();
}
setting.handleSelection(mActivity, Apple2AbstractMenu.this, selected);
}
});
}
}

View File

@ -0,0 +1,608 @@
/*
* Apple // emulator for *nix
*
* This software package is subject to the GNU General Public License
* version 3 or later (your choice) as published by the Free Software
* Foundation.
*
* Copyright 2015 Aaron Culliney
*
*/
package org.deadc0de.apple2ix;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.StrictMode;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.Toast;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.StringTokenizer;
import java.util.concurrent.atomic.AtomicBoolean;
import org.deadc0de.apple2ix.basic.BuildConfig;
import org.deadc0de.apple2ix.basic.R;
public class Apple2Activity extends Activity {
private final static String TAG = "Apple2Activity";
private final static int MAX_FINGERS = 32;// HACK ...
private static volatile boolean DEBUG_STRICT = false;
private Apple2View mView = null;
private Runnable mGraphicsInitializedRunnable = null;
private Apple2SplashScreen mSplashScreen = null;
private Apple2MainMenu mMainMenu = null;
private Apple2SettingsMenu mSettingsMenu = null;
private Apple2DisksMenu mDisksMenu = null;
private ArrayList<Apple2MenuView> mMenuStack = new ArrayList<Apple2MenuView>();
private ArrayList<AlertDialog> mAlertDialogs = new ArrayList<AlertDialog>();
private AtomicBoolean mPausing = new AtomicBoolean(false);
private float[] mXCoords = new float[MAX_FINGERS];
private float[] mYCoords = new float[MAX_FINGERS];
// non-null if we failed to load/link the native code ... likely we are running on some bizarre 'droid variant
private static Throwable sNativeBarfedThrowable = null;
private static boolean sNativeBarfed = false;
static {
try {
System.loadLibrary("apple2ix");
} catch (Throwable barf) {
sNativeBarfed = true;
sNativeBarfedThrowable = barf;
}
}
public final static long NATIVE_TOUCH_HANDLED = (1 << 0);
public final static long NATIVE_TOUCH_REQUEST_SHOW_MENU = (1 << 1);
public final static long NATIVE_TOUCH_KEY_TAP = (1 << 4);
public final static long NATIVE_TOUCH_KBD = (1 << 5);
public final static long NATIVE_TOUCH_JOY = (1 << 6);
public final static long NATIVE_TOUCH_MENU = (1 << 7);
public final static long NATIVE_TOUCH_JOY_KPAD = (1 << 8);
public final static long NATIVE_TOUCH_INPUT_DEVICE_CHANGED = (1 << 16);
public final static long NATIVE_TOUCH_CPU_SPEED_DEC = (1 << 17);
public final static long NATIVE_TOUCH_CPU_SPEED_INC = (1 << 18);
public final static long NATIVE_TOUCH_ASCII_SCANCODE_SHIFT = 32;
public final static long NATIVE_TOUCH_ASCII_SCANCODE_MASK = 0xFFFFL;
public final static long NATIVE_TOUCH_ASCII_MASK = 0xFF00L;
public final static long NATIVE_TOUCH_SCANCODE_MASK = 0x00FFL;
private native void nativeOnCreate(String dataDir, int sampleRate, int monoBufferSize, int stereoBufferSize);
private native void nativeOnKeyDown(int keyCode, int metaState);
private native void nativeOnKeyUp(int keyCode, int metaState);
public native void nativeEmulationResume();
public native void nativeEmulationPause();
public native void nativeOnQuit();
public native long nativeOnTouch(int action, int pointerCount, int pointerIndex, float[] xCoords, float[] yCoords);
public native void nativeReboot();
public native void nativeChooseDisk(String path, boolean driveA, boolean readOnly);
public native void nativeEjectDisk(boolean driveA);
@Override
public void onCreate(Bundle savedInstanceState) {
if (Apple2Activity.DEBUG_STRICT && BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectAll()
.penaltyLog()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
/*.detectLeakedClosableObjects()*/
.penaltyLog()
.penaltyDeath()
.build());
}
super.onCreate(savedInstanceState);
Log.e(TAG, "onCreate()");
// placeholder view on initial launch
if (mView == null) {
setContentView(new View(this));
}
Apple2CrashHandler.getInstance().initializeAndSetCustomExceptionHandler(this);
if (sNativeBarfed) {
Log.e(TAG, "NATIVE BARFED...", sNativeBarfedThrowable);
return;
}
int sampleRate = DevicePropertyCalculator.getRecommendedSampleRate(this);
int monoBufferSize = DevicePropertyCalculator.getRecommendedBufferSize(this, /*isStereo:*/false);
int stereoBufferSize = DevicePropertyCalculator.getRecommendedBufferSize(this, /*isStereo:*/true);
Log.d(TAG, "Device sampleRate:" + sampleRate + " mono bufferSize:" + monoBufferSize + " stereo bufferSize:" + stereoBufferSize);
String dataDir = Apple2DisksMenu.getDataDir(this);
nativeOnCreate(dataDir, sampleRate, monoBufferSize, stereoBufferSize);
final boolean firstTime = !Apple2Preferences.FIRST_TIME_CONFIGURED.booleanValue(this);
Apple2Preferences.FIRST_TIME_CONFIGURED.saveBoolean(this, true);
showSplashScreen(!firstTime);
Apple2CrashHandler.getInstance().checkForCrashes(this);
mGraphicsInitializedRunnable = new Runnable() {
@Override
public void run() {
if (firstTime) {
Apple2Preferences.KeypadPreset.IJKM_SPACE.apply(Apple2Activity.this);
}
Apple2Preferences.loadPreferences(Apple2Activity.this);
}
};
// first-time initializations
if (firstTime) {
new Thread(new Runnable() {
@Override
public void run() {
Apple2DisksMenu.firstTime(Apple2Activity.this);
mSplashScreen.setDismissable(true);
Log.d(TAG, "Finished first time copying...");
}
}).start();
}
mSettingsMenu = new Apple2SettingsMenu(this);
mDisksMenu = new Apple2DisksMenu(this);
Intent intent = getIntent();
String path = null;
if (intent != null) {
Uri data = intent.getData();
if (data != null) {
path = data.getPath();
}
}
if (path != null && Apple2DisksMenu.hasDiskExtension(path)) {
handleInsertDiskIntent(path);
}
}
@Override
protected void onResume() {
super.onResume();
if (sNativeBarfed) {
Apple2CrashHandler.getInstance().abandonAllHope(this, sNativeBarfedThrowable);
return;
}
Log.d(TAG, "onResume()");
showSplashScreen(/*dismissable:*/true);
Apple2CrashHandler.getInstance().checkForCrashes(this); // NOTE : needs to be called again to clean-up
}
@Override
protected void onPause() {
super.onPause();
if (sNativeBarfed) {
return;
}
boolean wasPausing = mPausing.getAndSet(true);
if (wasPausing) {
return;
}
Log.d(TAG, "onPause()");
if (mView != null) {
mView.onPause();
}
// Apparently not good to leave popup/dialog windows showing when backgrounding.
// Dismiss these popups to avoid android.view.WindowLeaked issues
synchronized (this) {
dismissAllMenus();
nativeEmulationPause();
}
mPausing.set(false);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (sNativeBarfed) {
return true;
}
if ((keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) || (keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) || (keyCode == KeyEvent.KEYCODE_VOLUME_UP)) {
return false;
}
nativeOnKeyDown(keyCode, event.getMetaState());
return true;
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (sNativeBarfed) {
return true;
}
if (keyCode == KeyEvent.KEYCODE_BACK) {
Apple2MenuView apple2MenuView = peekApple2View();
if (apple2MenuView == null) {
showMainMenu();
} else {
apple2MenuView.dismiss();
}
return true;
} else if (keyCode == KeyEvent.KEYCODE_MENU) {
showMainMenu();
return true;
} else if ((keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) || (keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) || (keyCode == KeyEvent.KEYCODE_VOLUME_UP)) {
return false;
} else {
nativeOnKeyUp(keyCode, event.getMetaState());
return true;
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
do {
if (sNativeBarfed) {
break;
}
if (mMainMenu == null) {
break;
}
Apple2MenuView apple2MenuView = peekApple2View();
if ((apple2MenuView != null) && (!apple2MenuView.isCalibrating())) {
break;
}
//printSamples(event);
int action = event.getActionMasked();
int pointerIndex = event.getActionIndex();
int pointerCount = event.getPointerCount();
for (int i = 0; i < pointerCount/* && i < MAX_FINGERS */; i++) {
mXCoords[i] = event.getX(i);
mYCoords[i] = event.getY(i);
}
long nativeFlags = nativeOnTouch(action, pointerCount, pointerIndex, mXCoords, mYCoords);
if ((nativeFlags & NATIVE_TOUCH_HANDLED) == 0) {
break;
}
if ((nativeFlags & NATIVE_TOUCH_REQUEST_SHOW_MENU) != 0) {
mMainMenu.show();
}
if ((nativeFlags & NATIVE_TOUCH_KEY_TAP) != 0) {
if (Apple2Preferences.KEYBOARD_CLICK_ENABLED.booleanValue(this)) {
AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE);
if (am != null) {
am.playSoundEffect(AudioManager.FX_KEY_CLICK);
}
}
if ((apple2MenuView != null) && apple2MenuView.isCalibrating()) {
long asciiScancodeLong = nativeFlags & (NATIVE_TOUCH_ASCII_SCANCODE_MASK << NATIVE_TOUCH_ASCII_SCANCODE_SHIFT);
int asciiInt = (int) (asciiScancodeLong >> (NATIVE_TOUCH_ASCII_SCANCODE_SHIFT + 8));
int scancode = (int) ((asciiScancodeLong >> NATIVE_TOUCH_ASCII_SCANCODE_SHIFT) & 0xFFL);
char ascii = (char) asciiInt;
apple2MenuView.onKeyTapCalibrationEvent(ascii, scancode);
}
}
if ((nativeFlags & NATIVE_TOUCH_MENU) == 0) {
break;
}
// handle menu-specific actions
if ((nativeFlags & NATIVE_TOUCH_INPUT_DEVICE_CHANGED) != 0) {
Apple2Preferences.TouchDeviceVariant nextVariant;
if ((nativeFlags & NATIVE_TOUCH_KBD) != 0) {
nextVariant = Apple2Preferences.TouchDeviceVariant.KEYBOARD;
} else if ((nativeFlags & NATIVE_TOUCH_JOY) != 0) {
nextVariant = Apple2Preferences.TouchDeviceVariant.JOYSTICK;
} else if ((nativeFlags & NATIVE_TOUCH_JOY_KPAD) != 0) {
nextVariant = Apple2Preferences.TouchDeviceVariant.JOYSTICK_KEYPAD;
} else {
int touchDevice = Apple2Preferences.nativeGetCurrentTouchDevice();
nextVariant = Apple2Preferences.TouchDeviceVariant.next(touchDevice);
}
Apple2Preferences.CURRENT_TOUCH_DEVICE.saveTouchDevice(this, nextVariant);
} else if ((nativeFlags & NATIVE_TOUCH_CPU_SPEED_DEC) != 0) {
int percentSpeed = Apple2Preferences.nativeGetCPUSpeed();
if (percentSpeed > 400) { // HACK: max value from native side
percentSpeed = 375;
} else if (percentSpeed > 100) {
percentSpeed -= 25;
} else {
percentSpeed -= 5;
}
Apple2Preferences.CPU_SPEED_PERCENT.saveInt(this, percentSpeed);
} else if ((nativeFlags & NATIVE_TOUCH_CPU_SPEED_INC) != 0) {
int percentSpeed = Apple2Preferences.nativeGetCPUSpeed();
if (percentSpeed >= 100) {
percentSpeed += 25;
} else {
percentSpeed += 5;
}
Apple2Preferences.CPU_SPEED_PERCENT.saveInt(this, percentSpeed);
}
} while (false);
return super.onTouchEvent(event);
}
public void showMainMenu() {
if (mMainMenu != null) {
if (!(mSettingsMenu.isShowing() || mDisksMenu.isShowing())) {
mMainMenu.show();
}
}
}
public Apple2MainMenu getMainMenu() {
return mMainMenu;
}
public synchronized Apple2DisksMenu getDisksMenu() {
return mDisksMenu;
}
public synchronized Apple2SettingsMenu getSettingsMenu() {
return mSettingsMenu;
}
private void handleInsertDiskIntent(final String path) {
runOnUiThread(new Runnable() {
@Override
public void run() {
synchronized (Apple2Activity.this) {
if (mMainMenu == null) {
return;
}
String diskPath = path;
File diskFile = new File(diskPath);
if (!diskFile.canRead()) {
Toast.makeText(Apple2Activity.this, Apple2Activity.this.getString(R.string.disk_insert_could_not_read), Toast.LENGTH_SHORT).show();
return;
}
Apple2Preferences.CURRENT_DISK_A_RO.saveBoolean(Apple2Activity.this, true);
final int len = diskPath.length();
final String suffix = diskPath.substring(len - 3, len);
if (suffix.equalsIgnoreCase(".gz")) { // HACK FIXME TODO : small amount of code duplication of Apple2DisksMenu
diskPath = diskPath.substring(0, len - 3);
}
Apple2Preferences.CURRENT_DISK_A.saveString(Apple2Activity.this, diskPath);
while (mDisksMenu.popPathStack() != null) {
/* ... */
}
File storageDir = Apple2DisksMenu.getExternalStorageDirectory();
if (storageDir != null) {
String storagePath = storageDir.getAbsolutePath();
if (diskPath.contains(storagePath)) {
diskPath = diskPath.replace(storagePath + File.separator, "");
mDisksMenu.pushPathStack(storagePath);
}
}
StringTokenizer tokenizer = new StringTokenizer(diskPath, File.separator);
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken();
if (token.equals("")) {
continue;
}
if (Apple2DisksMenu.hasDiskExtension(token)) {
continue;
}
mDisksMenu.pushPathStack(token);
}
Toast.makeText(Apple2Activity.this, Apple2Activity.this.getString(R.string.disk_insert_toast), Toast.LENGTH_SHORT).show();
}
}
});
}
public Apple2SplashScreen getSplashScreen() {
return mSplashScreen;
}
private void showSplashScreen(boolean dismissable) {
if (mSplashScreen != null) {
return;
}
mSplashScreen = new Apple2SplashScreen(this, dismissable);
mSplashScreen.show();
}
private void setupGLView() {
boolean glViewFirstTime = false;
if (mView == null) {
glViewFirstTime = true;
mView = new Apple2View(this, mGraphicsInitializedRunnable);
mGraphicsInitializedRunnable = null;
mMainMenu = new Apple2MainMenu(this, mView);
}
if (glViewFirstTime) {
// HACK NOTE : do not blanket setContentView() ... it appears to wedge Gingerbread
setContentView(mView);
} else {
mView.onResume();
}
}
public void registerAndShowDialog(AlertDialog dialog) {
dialog.show();
mAlertDialogs.add(dialog);
}
public synchronized void pushApple2View(Apple2MenuView apple2MenuView) {
mMenuStack.add(apple2MenuView);
View menuView = apple2MenuView.getView();
nativeEmulationPause();
addContentView(menuView, new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
}
public synchronized Apple2MenuView popApple2View() {
int lastIndex = mMenuStack.size() - 1;
if (lastIndex < 0) {
return null;
}
Apple2MenuView apple2MenuView = mMenuStack.remove(lastIndex);
_disposeApple2View(apple2MenuView);
return apple2MenuView;
}
public synchronized Apple2MenuView peekApple2View() {
int lastIndex = mMenuStack.size() - 1;
if (lastIndex < 0) {
return null;
}
return mMenuStack.get(lastIndex);
}
public synchronized Apple2MenuView peekApple2View(int index) {
int lastIndex = mMenuStack.size() - 1;
if (lastIndex < 0) {
return null;
}
try {
return mMenuStack.get(index);
} catch (IndexOutOfBoundsException e) {
return null;
}
}
public void dismissAllMenus() {
if (mMainMenu != null) {
mMainMenu.dismiss();
}
for (AlertDialog dialog : mAlertDialogs) {
dialog.dismiss();
}
mAlertDialogs.clear();
// Get rid of the menu hierarchy
ArrayList<Apple2MenuView> menuHierarchy = new ArrayList<Apple2MenuView>(mMenuStack);
Collections.reverse(menuHierarchy);
for (Apple2MenuView view : menuHierarchy) {
view.dismissAll();
}
}
public synchronized Apple2MenuView popApple2View(Apple2MenuView apple2MenuView) {
boolean wasRemoved = mMenuStack.remove(apple2MenuView);
_disposeApple2View(apple2MenuView);
return wasRemoved ? apple2MenuView : null;
}
private void _disposeApple2View(Apple2MenuView apple2MenuView) {
boolean dismissedSplashScreen = false;
// Actually remove View from view hierarchy
{
View menuView = apple2MenuView.getView();
ViewGroup viewGroup = (ViewGroup) menuView.getParent();
if (viewGroup != null) {
viewGroup.removeView(menuView);
}
if (apple2MenuView instanceof Apple2SplashScreen) { // 20151101 HACK NOTE : use instanceof to avoid edge case where joystick calibration occurred (and thus the splash was already dismissed without proper mView initialization)
mSplashScreen = null;
dismissedSplashScreen = true;
}
}
// if no more views on menu stack, resume emulation
if (mMenuStack.size() == 0) {
dismissAllMenus(); // NOTE : at this point, this should not be re-entrant into mMenuStack, it should just dismiss lingering popups
if (!mPausing.get()) {
if (dismissedSplashScreen) {
setupGLView();
} else {
nativeEmulationResume();
}
}
}
}
public void maybeResumeCPU() {
if (mMenuStack.size() == 0 && !mPausing.get()) {
nativeEmulationResume();
}
}
public void maybeQuitApp() {
nativeEmulationPause();
AlertDialog quitDialog = new AlertDialog.Builder(this).setIcon(R.drawable.ic_launcher).setCancelable(true).setTitle(R.string.quit_really).setMessage(R.string.quit_warning).setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
nativeOnQuit();
Apple2Activity.this.finish();