diff --git a/ReadMe.txt b/ReadMe.txt
new file mode 100644
index 0000000..9e8f6aa
--- /dev/null
+++ b/ReadMe.txt
@@ -0,0 +1,21 @@
+ABOUT
+This project ports the Windows application Emu48 written in C to Android.
+It uses the Android NDK. The former Emu48 source code remains untouch because a thin win32 emulation layer above Linux/NDK!
+This win32 layer will allow to update easily from the original source code.
+
+NOT WORKING
+- Sound
+- Disassembler
+- Debugger
+- Macro
+- Infrared Printer
+- Serial Ports (Wire or Ir)
+
+TODO
+- Bitmap corruption when touching the buttons
+- Android UI Settings
+- Sound
+
+DONE
+- Bug Red and Blue seems inverted.
+- Multitouch
diff --git a/app/build.gradle b/app/build.gradle
index c1709b6..7ccb2de 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -8,7 +8,7 @@ android {
targetSdkVersion 28
versionCode 1
versionName "1.0"
- testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags ""
@@ -30,9 +30,9 @@ android {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
- implementation 'com.android.support:appcompat-v7:28.0.0'
- implementation 'com.android.support.constraint:constraint-layout:1.1.3'
- implementation 'com.android.support:design:28.0.0'
+ implementation 'androidx.appcompat:appcompat:1.1.0-alpha01'
+ implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha2'
+ implementation 'com.google.android.material:material:1.1.0-alpha01'
// testImplementation 'junit:junit:4.12'
// androidTestImplementation 'com.android.support.test:runner:1.0.2'
// androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index bb97248..6bff32e 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -10,10 +10,12 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
+ android:resizeableActivity="true"
android:theme="@style/AppTheme">
@@ -21,6 +23,24 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/cpp/pch.h b/app/src/main/cpp/pch.h
index c613914..32a0944 100644
--- a/app/src/main/cpp/pch.h
+++ b/app/src/main/cpp/pch.h
@@ -2,7 +2,7 @@
// PCH.H
//
-#define DEBUG_DISPLAY 1
+//#define DEBUG_DISPLAY 1
#define _WIN32_IE 0x0200
#define _CRT_SECURE_NO_DEPRECATE
diff --git a/app/src/main/cpp/win32-layer.c b/app/src/main/cpp/win32-layer.c
index 8157133..ed955fb 100644
--- a/app/src/main/cpp/win32-layer.c
+++ b/app/src/main/cpp/win32-layer.c
@@ -821,8 +821,6 @@ BOOL StretchBlt(HDC hdcDest, int xDest, int yDest, int wDest, int hDest, HDC hdc
int destinationHeight = androidBitmapInfo.height;
//https://softwareengineering.stackexchange.com/questions/148123/what-is-the-algorithm-to-copy-a-region-of-one-bitmap-into-a-region-in-another
-// float src_dx = wDest / wSrc;
-// float src_dy = hDest / hSrc;
float src_dx = (float)wSrc / (float)wDest;
float src_dy = (float)hSrc / (float)hDest;
float src_maxx = xSrc + wSrc;
@@ -855,9 +853,9 @@ BOOL StretchBlt(HDC hdcDest, int xDest, int yDest, int wDest, int hDest, HDC hdc
case 1:
if(palPalEntry) {
BYTE colorIndex = sourcePixel[0];
- destinationPixel[0] = palPalEntry[colorIndex].peRed;
+ destinationPixel[0] = palPalEntry[colorIndex].peBlue;
destinationPixel[1] = palPalEntry[colorIndex].peGreen;
- destinationPixel[2] = palPalEntry[colorIndex].peBlue;
+ destinationPixel[2] = palPalEntry[colorIndex].peRed;
destinationPixel[3] = 255;
} else {
destinationPixel[0] = sourcePixel[0];
@@ -867,9 +865,9 @@ BOOL StretchBlt(HDC hdcDest, int xDest, int yDest, int wDest, int hDest, HDC hdc
}
break;
case 3:
- destinationPixel[0] = sourcePixel[0];
+ destinationPixel[0] = sourcePixel[2];
destinationPixel[1] = sourcePixel[1];
- destinationPixel[2] = sourcePixel[2];
+ destinationPixel[2] = sourcePixel[0];
destinationPixel[3] = 255;
break;
case 4:
diff --git a/app/src/main/java/com/regis/cosnier/emu48/AppCompatPreferenceActivity.java b/app/src/main/java/com/regis/cosnier/emu48/AppCompatPreferenceActivity.java
new file mode 100644
index 0000000..e997e3f
--- /dev/null
+++ b/app/src/main/java/com/regis/cosnier/emu48/AppCompatPreferenceActivity.java
@@ -0,0 +1,110 @@
+package com.regis.cosnier.emu48;
+
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+import android.view.MenuInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.LayoutRes;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AppCompatDelegate;
+import androidx.appcompat.widget.Toolbar;
+
+/**
+ * A {@link PreferenceActivity} which implements and proxies the necessary calls
+ * to be used with AppCompat.
+ */
+public abstract class AppCompatPreferenceActivity extends PreferenceActivity {
+
+ private AppCompatDelegate mDelegate;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ getDelegate().installViewFactory();
+ getDelegate().onCreate(savedInstanceState);
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState) {
+ super.onPostCreate(savedInstanceState);
+ getDelegate().onPostCreate(savedInstanceState);
+ }
+
+ public ActionBar getSupportActionBar() {
+ return getDelegate().getSupportActionBar();
+ }
+
+ public void setSupportActionBar(@Nullable Toolbar toolbar) {
+ getDelegate().setSupportActionBar(toolbar);
+ }
+
+ @Override
+ public MenuInflater getMenuInflater() {
+ return getDelegate().getMenuInflater();
+ }
+
+ @Override
+ public void setContentView(@LayoutRes int layoutResID) {
+ getDelegate().setContentView(layoutResID);
+ }
+
+ @Override
+ public void setContentView(View view) {
+ getDelegate().setContentView(view);
+ }
+
+ @Override
+ public void setContentView(View view, ViewGroup.LayoutParams params) {
+ getDelegate().setContentView(view, params);
+ }
+
+ @Override
+ public void addContentView(View view, ViewGroup.LayoutParams params) {
+ getDelegate().addContentView(view, params);
+ }
+
+ @Override
+ protected void onPostResume() {
+ super.onPostResume();
+ getDelegate().onPostResume();
+ }
+
+ @Override
+ protected void onTitleChanged(CharSequence title, int color) {
+ super.onTitleChanged(title, color);
+ getDelegate().setTitle(title);
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ getDelegate().onConfigurationChanged(newConfig);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ getDelegate().onStop();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ getDelegate().onDestroy();
+ }
+
+ public void invalidateOptionsMenu() {
+ getDelegate().invalidateOptionsMenu();
+ }
+
+ private AppCompatDelegate getDelegate() {
+ if (mDelegate == null) {
+ mDelegate = AppCompatDelegate.create(this, null);
+ }
+ return mDelegate;
+ }
+}
diff --git a/app/src/main/java/com/regis/cosnier/emu48/MainActivity.java b/app/src/main/java/com/regis/cosnier/emu48/MainActivity.java
index 5b0b9cf..d258210 100644
--- a/app/src/main/java/com/regis/cosnier/emu48/MainActivity.java
+++ b/app/src/main/java/com/regis/cosnier/emu48/MainActivity.java
@@ -1,20 +1,26 @@
package com.regis.cosnier.emu48;
-import android.content.res.AssetManager;
-import android.graphics.Bitmap;
+import android.content.Intent;
import android.os.Bundle;
-import android.util.DisplayMetrics;
import android.view.ViewGroup;
-import android.support.design.widget.FloatingActionButton;
-import android.support.design.widget.Snackbar;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
+import com.google.android.material.floatingactionbutton.FloatingActionButton;
+import com.google.android.material.navigation.NavigationView;
+import com.google.android.material.snackbar.Snackbar;
+
+import androidx.appcompat.app.ActionBarDrawerToggle;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+import androidx.core.view.GravityCompat;
+import androidx.drawerlayout.widget.DrawerLayout;
+
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
-public class MainActivity extends AppCompatActivity {
+public class MainActivity extends AppCompatActivity
+ implements NavigationView.OnNavigationItemSelectedListener {
+ private static final int INTENT_SETTINGS = 1;
private MainScreenView mainScreenView;
@Override
@@ -33,11 +39,32 @@ public class MainActivity extends AppCompatActivity {
}
});
+ DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
+ ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
+ this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
+ drawer.addDrawerListener(toggle);
+ toggle.syncState();
+
+ NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
+ navigationView.setNavigationItemSelectedListener(this);
+
+
+
mainScreenView = new MainScreenView(this); //, currentProject);
ViewGroup mainScreenContainer = (ViewGroup)findViewById(R.id.main_screen_container);
mainScreenContainer.addView(mainScreenView, 0);
}
+ @Override
+ public void onBackPressed() {
+ DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
+ if (drawer.isDrawerOpen(GravityCompat.START)) {
+ drawer.closeDrawer(GravityCompat.START);
+ } else {
+ super.onBackPressed();
+ }
+ }
+
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
@@ -54,12 +81,38 @@ public class MainActivity extends AppCompatActivity {
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
+ startActivityForResult(new Intent(this, SettingsActivity.class), INTENT_SETTINGS);
return true;
}
return super.onOptionsItemSelected(item);
}
+ @SuppressWarnings("StatementWithEmptyBody")
+ @Override
+ public boolean onNavigationItemSelected(MenuItem item) {
+ // Handle navigation view item clicks here.
+ int id = item.getItemId();
+
+ if (id == R.id.nav_camera) {
+ // Handle the camera action
+ } else if (id == R.id.nav_gallery) {
+
+ } else if (id == R.id.nav_slideshow) {
+
+ } else if (id == R.id.nav_manage) {
+
+ } else if (id == R.id.nav_share) {
+
+ } else if (id == R.id.nav_send) {
+
+ }
+
+ DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
+ drawer.closeDrawer(GravityCompat.START);
+ return true;
+ }
+
@Override
protected void onDestroy() {
diff --git a/app/src/main/java/com/regis/cosnier/emu48/MainScreenView.java b/app/src/main/java/com/regis/cosnier/emu48/MainScreenView.java
index 065edda..d7917b2 100644
--- a/app/src/main/java/com/regis/cosnier/emu48/MainScreenView.java
+++ b/app/src/main/java/com/regis/cosnier/emu48/MainScreenView.java
@@ -7,22 +7,15 @@ import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.drawable.Drawable;
-import android.support.v4.view.ViewCompat;
+
+import androidx.core.view.ViewCompat;
import android.util.DisplayMetrics;
import android.util.Log;
-import android.view.GestureDetector;
-import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
-import android.view.ScaleGestureDetector;
import android.view.SurfaceView;
-import android.view.View;
-import android.widget.OverScroller;
import java.util.HashMap;
-import java.util.Map;
public class MainScreenView extends SurfaceView {
@@ -140,33 +133,29 @@ public class MainScreenView extends SurfaceView {
@SuppressLint("ClickableViewAccessibility")
public boolean onTouchEvent(MotionEvent event) {
- // Let the ScaleGestureDetector inspect all events.
-// boolean retValScaleDetector = mScaleDetector.onTouchEvent(event);
-// boolean retValGestureDetector = mGestureDetector.onTouchEvent(event);
- boolean retVal = false; //retValGestureDetector || retValScaleDetector;
-
int touchCount = event.getPointerCount();
- if(touchCount == 1) {
- int action = event.getAction();
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- //Log.d(TAG, "ACTION_DOWN count: " + touchCount + ", PanPrevious: " + mPanPrevious.toString());
- NativeLib.buttonDown((int)((event.getX(0) - screenOffsetX) / screenScale), (int)((event.getY(0) - screenOffsetY) / screenScale));
- break;
+ int actionIndex = event.getActionIndex();
+ int action = event.getActionMasked();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_POINTER_DOWN:
+ //Log.d(TAG, "ACTION_DOWN/ACTION_POINTER_DOWN count: " + touchCount + ", actionIndex: " + actionIndex);
+ NativeLib.buttonDown((int)((event.getX(actionIndex) - screenOffsetX) / screenScale), (int)((event.getY(actionIndex) - screenOffsetY) / screenScale));
+ break;
// case MotionEvent.ACTION_MOVE:
// break;
- case MotionEvent.ACTION_UP:
- NativeLib.buttonUp((int)((event.getX(0) - screenOffsetX) / screenScale), (int)((event.getY(0) - screenOffsetY) / screenScale));
- break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_POINTER_UP:
+ //Log.d(TAG, "ACTION_UP/ACTION_POINTER_UP count: " + touchCount + ", actionIndex: " + actionIndex);
+ NativeLib.buttonUp((int)((event.getX(actionIndex) - screenOffsetX) / screenScale), (int)((event.getY(actionIndex) - screenOffsetY) / screenScale));
+ break;
// case MotionEvent.ACTION_CANCEL:
// break;
// case MotionEvent.ACTION_OUTSIDE:
// break;
- default:
- }
- return true;
+ default:
}
- return retVal || super.onTouchEvent(event);
+ return true;
}
// @Override
@@ -216,7 +205,7 @@ public class MainScreenView extends SurfaceView {
float imageSizeY = bitmapMainScreen.getHeight();
if(imageSizeX > 0 && imageSizeY > 0 && viewWidth > 0.0f && viewHeight > 0.0f) {
- // Find the scale factor and the translate offset to fit and to center the vectors in the view bounds.
+ // Find the scale factor and the translate offset to fit and to center the image in the view bounds.
float translateX, translateY, scale;
float viewRatio = (float)viewHeight / (float)viewWidth;
float imageRatio = imageSizeY / imageSizeX;
@@ -235,13 +224,12 @@ public class MainScreenView extends SurfaceView {
screenOffsetY = translateY;
}
- NativeLib.resize(viewWidth, viewHeight);
+ //NativeLib.resize(viewWidth, viewHeight);
}
@Override
protected void onDraw(Canvas canvas) {
//Log.d(TAG, "onDraw() mIsScaling: " + mIsScaling + ", mIsPanning: " + mIsPanning + ", mIsFlinging: " + mIsFlinging);
- //NativeLib.draw();
canvas.save();
canvas.translate(screenOffsetX, screenOffsetY);
canvas.scale(screenScale, screenScale);
diff --git a/app/src/main/java/com/regis/cosnier/emu48/NativeLib.java b/app/src/main/java/com/regis/cosnier/emu48/NativeLib.java
index 2571cd1..45b058a 100644
--- a/app/src/main/java/com/regis/cosnier/emu48/NativeLib.java
+++ b/app/src/main/java/com/regis/cosnier/emu48/NativeLib.java
@@ -12,7 +12,7 @@ public class NativeLib {
public static native void start(AssetManager mgr, Bitmap bitmapMainScreen, MainScreenView view);
public static native void stop();
- public static native void resize(int width, int height);
+ //public static native void resize(int width, int height);
public static native void draw();
public static native void buttonDown(int x, int y);
public static native void buttonUp(int x, int y);
diff --git a/app/src/main/java/com/regis/cosnier/emu48/SeekBarPreference.java b/app/src/main/java/com/regis/cosnier/emu48/SeekBarPreference.java
new file mode 100644
index 0000000..6ab5ab0
--- /dev/null
+++ b/app/src/main/java/com/regis/cosnier/emu48/SeekBarPreference.java
@@ -0,0 +1,167 @@
+package com.regis.cosnier.emu48;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.preference.DialogPreference;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+public class SeekBarPreference extends DialogPreference implements SeekBar.OnSeekBarChangeListener
+{
+ private static final String androidns="http://schemas.android.com/apk/res/android";
+ private static final String appdns="http://schemas.android.com/apk/res-auto";
+
+ private SeekBar mSeekBar;
+ private TextView mSplashText,mValueText;
+ private Context mContext;
+
+ private String mDialogMessage, mSuffix, mSuffixes;
+ private int mDefault, mMin, mMax, mValue = 0;
+ private float mDisplayScale = 1.0f;
+ private String mDisplayFormat;
+
+ public SeekBarPreference(Context context, AttributeSet attrs) {
+ super(context,attrs);
+ mContext = context;
+
+ mDialogMessage = getAttributeValue(attrs, androidns, "dialogMessage");
+ mValue = mDefault = attrs.getAttributeIntValue(androidns, "defaultValue", 0);
+ mDisplayScale = attrs.getAttributeFloatValue(appdns, "displayScale", 1.0f);
+ mSuffix = getAttributeValue(attrs, appdns, "suffix");
+ mSuffixes = getAttributeValue(attrs, appdns, "suffixes");
+ mDisplayFormat = attrs.getAttributeValue(appdns, "displayFormat");
+ mMin = attrs.getAttributeIntValue(appdns, "min", 0);
+ mMax = attrs.getAttributeIntValue(androidns, "max", 100);
+ }
+
+ private String getAttributeValue(AttributeSet attrs, String namespace, String name) {
+ String attributeValue = attrs.getAttributeValue(namespace, name);
+ String result = attributeValue;
+ if(attributeValue != null && attributeValue.length() > 1 && attributeValue.charAt(0) == '@') {
+ try {
+ int id = Integer.parseInt(attributeValue.substring(1));
+ result = mContext.getString(id);
+ } catch (NumberFormatException e) {}
+ }
+ return result;
+ }
+
+ @Override
+ protected View onCreateDialogView() {
+ LinearLayout layout = new LinearLayout(mContext);
+ layout.setOrientation(LinearLayout.VERTICAL);
+ layout.setPadding(6, 6, 6, 6);
+
+ mSplashText = new TextView(mContext);
+ if (mDialogMessage != null)
+ mSplashText.setText(mDialogMessage);
+ layout.addView(mSplashText);
+
+ mValueText = new TextView(mContext);
+ mValueText.setGravity(Gravity.CENTER_HORIZONTAL);
+ mValueText.setTextSize(32);
+ layout.addView(mValueText, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
+
+ mSeekBar = new SeekBar(mContext);
+ mSeekBar.setOnSeekBarChangeListener(this);
+ layout.addView(mSeekBar, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
+
+ if (shouldPersist())
+ mValue = getPersistedInt(mDefault);
+
+ mSeekBar.setMax(mMax - mMin);
+ mSeekBar.setProgress(mValue - mMin);
+ return layout;
+ }
+ @Override
+ protected void onBindDialogView(View v) {
+ super.onBindDialogView(v);
+ mSeekBar.setMax(mMax - mMin);
+ mSeekBar.setProgress(mValue - mMin);
+ }
+ @Override
+ protected void onSetInitialValue(boolean restore, Object defaultValue)
+ {
+ super.onSetInitialValue(restore, defaultValue);
+ if (restore)
+ mValue = shouldPersist() ? getPersistedInt(mDefault) : 0;
+ else
+ mValue = (Integer)defaultValue;
+ }
+
+ public void setValueSummary()
+ {
+ setSummary(getValueSummary());
+ }
+
+ public String getValueSummary()
+ {
+ return getValueSummary(mValue);
+ }
+
+ public String getValueSummary(int value)
+ {
+ String textValue;
+ if(mDisplayFormat != null)
+ textValue = String.format(mDisplayFormat, value * mDisplayScale);
+ else if(mDisplayScale != 1.0f)
+ textValue = String.valueOf(value * mDisplayScale);
+ else
+ textValue = String.valueOf(value);
+
+ if(value * mDisplayScale > 1.0f) {
+ if(mSuffixes != null)
+ textValue += " " + mSuffixes;
+ else if(mSuffix != null)
+ textValue += " " + mSuffix;
+ } else if(mSuffix != null)
+ textValue += " " + mSuffix;
+
+ return textValue;
+ }
+
+ public void onProgressChanged(SeekBar seek, int value, boolean fromTouch)
+ {
+ int theValue = mMin + value;
+ String textValue = getValueSummary(theValue);
+ mValueText.setText(textValue);
+
+ if (shouldPersist())
+ persistInt(theValue);
+
+ //mValue = theValue;
+ //callChangeListener(Integer.valueOf(mMin + value));
+ }
+ public void onStartTrackingTouch(SeekBar seek) {}
+ public void onStopTrackingTouch(SeekBar seek) {}
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ switch (which) {
+ case DialogInterface.BUTTON_POSITIVE:
+ // save your new password here
+ mValue = mMin + mSeekBar.getProgress();
+ callChangeListener(Integer.valueOf(mValue));
+ break;
+ default:
+ // do something else...
+ break;
+ }
+ }
+
+ public void setMin(int min) { mMin = min; }
+ public int getMin() { return mMin; }
+ public void setMax(int max) { mMax = max; }
+ public int getMax() { return mMax; }
+
+ public void setProgress(int progress) {
+ mValue = progress;
+ if (mSeekBar != null)
+ mSeekBar.setProgress(progress - mMin);
+ }
+ public int getProgress() { return mValue; }
+}
diff --git a/app/src/main/java/com/regis/cosnier/emu48/SettingsActivity.java b/app/src/main/java/com/regis/cosnier/emu48/SettingsActivity.java
new file mode 100644
index 0000000..8b40742
--- /dev/null
+++ b/app/src/main/java/com/regis/cosnier/emu48/SettingsActivity.java
@@ -0,0 +1,203 @@
+package com.regis.cosnier.emu48;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.res.Configuration;
+import android.os.Build;
+import android.os.Bundle;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceFragment;
+import android.preference.PreferenceManager;
+import android.view.MenuItem;
+
+import androidx.appcompat.app.ActionBar;
+import androidx.core.app.NavUtils;
+
+/**
+ * A {@link PreferenceActivity} that presents a set of application settings. On
+ * handset devices, settings are presented as a single list. On tablets,
+ * settings are split by category, with category headers shown to the left of
+ * the list of settings.
+ *
+ * See
+ * Android Design: Settings for design guidelines and the Settings
+ * API Guide for more information on developing a Settings UI.
+ */
+public class SettingsActivity extends AppCompatPreferenceActivity {
+
+ /**
+ * A preference value change listener that updates the preference's summary
+ * to reflect its new value.
+ */
+ private static Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = new Preference.OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object value) {
+ String stringValue = value.toString();
+
+ if (preference instanceof ListPreference) {
+ // For list preferences, look up the correct display value in
+ // the preference's 'entries' list.
+ ListPreference listPreference = (ListPreference) preference;
+ int index = listPreference.findIndexOfValue(stringValue);
+
+ // Set the summary to reflect the new value.
+ preference.setSummary(
+ index >= 0
+ ? listPreference.getEntries()[index]
+ : null);
+
+ } else {
+ // For all other preferences, set the summary to the value's
+ // simple string representation.
+ preference.setSummary(stringValue);
+ }
+ return true;
+ }
+ };
+
+ /**
+ * Helper method to determine if the device has an extra-large screen. For
+ * example, 10" tablets are extra-large.
+ */
+ private static boolean isXLargeTablet(Context context) {
+ return (context.getResources().getConfiguration().screenLayout
+ & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_XLARGE;
+ }
+
+ /**
+ * Binds a preference's summary to its value. More specifically, when the
+ * preference's value is changed, its summary (line of text below the
+ * preference title) is updated to reflect the value. The summary is also
+ * immediately updated upon calling this method. The exact display format is
+ * dependent on the type of preference.
+ *
+ * @see #sBindPreferenceSummaryToValueListener
+ */
+ private static void bindPreferenceSummaryToStringValue(Preference preference) {
+ // Set the listener to watch for value changes.
+ preference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener);
+
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(preference.getContext());
+
+ // Trigger the listener immediately with the preference's
+ // current value.
+ sBindPreferenceSummaryToValueListener.onPreferenceChange(preference, sharedPreferences.getString(preference.getKey(), ""));
+ }
+
+ /**
+ * Binds a preference's summary to its value. More specifically, when the
+ * preference's value is changed, its summary (line of text below the
+ * preference title) is updated to reflect the value. The summary is also
+ * immediately updated upon calling this method. The exact display format is
+ * dependent on the type of preference.
+ *
+ * @see #sBindPreferenceSummaryToValueListener
+ */
+ private static void bindPreferenceSummaryToIntValue(Preference preference) {
+ // Set the listener to watch for value changes.
+ preference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener);
+
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(preference.getContext());
+
+ // Trigger the listener immediately with the preference's
+ // current value.
+ sBindPreferenceSummaryToValueListener.onPreferenceChange(preference, sharedPreferences.getInt(preference.getKey(), 0));
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ // Show the Up button in the action bar.
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ }
+
+ getFragmentManager().beginTransaction().replace(android.R.id.content, new GeneralPreferenceFragment()).commit();
+
+ }
+
+ @Override
+ public boolean onMenuItemSelected(int featureId, MenuItem item) {
+ int id = item.getItemId();
+ if (id == android.R.id.home) {
+ if (!super.onMenuItemSelected(featureId, item)) {
+ NavUtils.navigateUpFromSameTask(this);
+ }
+ return true;
+ }
+ return super.onMenuItemSelected(featureId, item);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onIsMultiPane() {
+ return isXLargeTablet(this);
+ }
+
+// /**
+// * {@inheritDoc}
+// */
+// @Override
+// @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+// public void onBuildHeaders(List target) {
+// loadHeadersFromResource(R.xml.pref_headers, target);
+// }
+
+ /**
+ * This method stops fragment injection in malicious applications.
+ * Make sure to deny any unknown fragments here.
+ */
+ protected boolean isValidFragment(String fragmentName) {
+ return PreferenceFragment.class.getName().equals(fragmentName)
+ || GeneralPreferenceFragment.class.getName().equals(fragmentName)
+// || DataSyncPreferenceFragment.class.getName().equals(fragmentName)
+// || NotificationPreferenceFragment.class.getName().equals(fragmentName)
+ ;
+ }
+
+ /**
+ * This fragment shows general preferences only. It is used when the
+ * activity is showing a two-pane settings UI.
+ */
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public static class GeneralPreferenceFragment extends PreferenceFragment {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.pref_general);
+ setHasOptionsMenu(true);
+
+ // Bind the summaries of EditText/List/Dialog/Ringtone preferences
+ // to their values. When their values change, their summaries are
+ // updated to reflect the new value, per the Android Design
+ // guidelines.
+// bindPreferenceSummaryToStringValue(findPreference("settings_input"));
+// bindPreferenceSummaryToIntValue(findPreference("settings_audio_input"));
+// bindPreferenceSummaryToStringValue(findPreference("settings_sample_rate"));
+// bindPreferenceSummaryToStringValue(findPreference("settings_fft_size"));
+// bindPreferenceSummaryToIntValue(findPreference("settings_volume_scale"));
+ bindPreferenceSummaryToIntValue(findPreference("settings_tempo"));
+// bindPreferenceSummaryToIntValue(findPreference("settings_intensity_minimum"));
+// bindPreferenceSummaryToIntValue(findPreference("settings_translation"));
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ int id = item.getItemId();
+ if (id == android.R.id.home) {
+ startActivity(new Intent(getActivity(), SettingsActivity.class));
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+ }
+}
diff --git a/app/src/main/res/drawable/ic_menu_camera.xml b/app/src/main/res/drawable/ic_menu_camera.xml
new file mode 100644
index 0000000..634fe92
--- /dev/null
+++ b/app/src/main/res/drawable/ic_menu_camera.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_menu_gallery.xml b/app/src/main/res/drawable/ic_menu_gallery.xml
new file mode 100644
index 0000000..03c7709
--- /dev/null
+++ b/app/src/main/res/drawable/ic_menu_gallery.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_menu_manage.xml b/app/src/main/res/drawable/ic_menu_manage.xml
new file mode 100644
index 0000000..aeb047d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_menu_manage.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_menu_send.xml b/app/src/main/res/drawable/ic_menu_send.xml
new file mode 100644
index 0000000..fdf1c90
--- /dev/null
+++ b/app/src/main/res/drawable/ic_menu_send.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_menu_share.xml b/app/src/main/res/drawable/ic_menu_share.xml
new file mode 100644
index 0000000..338d95a
--- /dev/null
+++ b/app/src/main/res/drawable/ic_menu_share.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_menu_slideshow.xml b/app/src/main/res/drawable/ic_menu_slideshow.xml
new file mode 100644
index 0000000..5e9e163
--- /dev/null
+++ b/app/src/main/res/drawable/ic_menu_slideshow.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/side_nav_bar.xml b/app/src/main/res/drawable/side_nav_bar.xml
new file mode 100644
index 0000000..6d81870
--- /dev/null
+++ b/app/src/main/res/drawable/side_nav_bar.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index c59318f..fd5480f 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -1,34 +1,26 @@
-
+ tools:context=".MainActivity"
+ android:fitsSystemWindows="true"
+ tools:openDrawer="start">
-
+ android:layout_height="match_parent" />
-
-
-
-
-
-
-
+ android:layout_height="match_parent"
+ android:layout_gravity="start"
+ android:fitsSystemWindows="true"
+ app:headerLayout="@layout/nav_header_main"
+ app:menu="@menu/activity_main_drawer" />
-
\ No newline at end of file
+
diff --git a/app/src/main/res/layout/app_bar_main.xml b/app/src/main/res/layout/app_bar_main.xml
new file mode 100644
index 0000000..2aa1a4f
--- /dev/null
+++ b/app/src/main/res/layout/app_bar_main.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml
index e389dcc..196c71a 100644
--- a/app/src/main/res/layout/content_main.xml
+++ b/app/src/main/res/layout/content_main.xml
@@ -1,5 +1,5 @@
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/nav_header_main.xml b/app/src/main/res/layout/nav_header_main.xml
new file mode 100644
index 0000000..c8943ea
--- /dev/null
+++ b/app/src/main/res/layout/nav_header_main.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/menu/activity_main_drawer.xml b/app/src/main/res/menu/activity_main_drawer.xml
new file mode 100644
index 0000000..27db2c0
--- /dev/null
+++ b/app/src/main/res/menu/activity_main_drawer.xml
@@ -0,0 +1,38 @@
+
+
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
new file mode 100644
index 0000000..b83c366
--- /dev/null
+++ b/app/src/main/res/values/attrs.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 2c00107..d1da4b2 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -1,3 +1,8 @@
+
+ 16dp
+ 16dp
+ 8dp
+ 176dp
16dp
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 61d234e..762c11d 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,4 +1,10 @@
Emu48
+ Open navigation drawer
+ Close navigation drawer
+ Android Studio
+ android.studio@android.com
+ Navigation header
Settings
+ Settings
diff --git a/app/src/main/res/xml/pref_general.xml b/app/src/main/res/xml/pref_general.xml
new file mode 100644
index 0000000..552dace
--- /dev/null
+++ b/app/src/main/res/xml/pref_general.xml
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/xml/provider_paths.xml b/app/src/main/res/xml/provider_paths.xml
new file mode 100644
index 0000000..74a5cde
--- /dev/null
+++ b/app/src/main/res/xml/provider_paths.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index 9f85f38..461e8f6 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -6,6 +6,8 @@
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
+android.enableJetifier=true
+android.useAndroidX=true
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit