Transform child activities with dialog fragments, and fix an issue with the numpad keys which send the arrow keys and the numbers at the same time.

This commit is contained in:
dgis 2020-05-10 01:09:46 +02:00
parent 6765cc7035
commit 2b2308d93f
26 changed files with 1172 additions and 998 deletions

View file

@ -74,6 +74,8 @@ Version 1.8beta3 (2020-04-XX)
- Fix: Overlapping window source position when Background/Offset is not (0,0). - Fix: Overlapping window source position when Background/Offset is not (0,0).
- Wrap the table of content in the former help documentation. - Wrap the table of content in the former help documentation.
- Save the settings at the end of the state file. - Save the settings at the end of the state file.
- Transform all child activities with dialog fragments (to prevent unwanted state save).
- Fix an issue with the numpad keys which send the arrow keys and the numbers at the same time.
Version 1.7 (2019-12-12) Version 1.7 (2019-12-12)
@ -192,6 +194,8 @@ The Eric's Real scripts ("real*.kml" and "real*.bmp/png") are embedded in this a
TODO TODO
- Add a settings to switch between dark and light theme.
- Increase the loading speed (for Charlemagne faceplates) if possible.
- Add the name of the file in the toast "State saved". - Add the name of the file in the toast "State saved".
- The clock seems unsynchronized sometimes. - The clock seems unsynchronized sometimes.
- Retain a key by right clicking if it is from a mouse. - Retain a key by right clicking if it is from a mouse.

View file

@ -28,11 +28,11 @@ if (keystorePropertiesFile.exists()) {
} }
android { android {
compileSdkVersion 28 compileSdkVersion 29
defaultConfig { defaultConfig {
applicationId "org.emulator.forty.eight" applicationId "org.emulator.forty.eight"
minSdkVersion 19 minSdkVersion 19
targetSdkVersion 28 targetSdkVersion 29
versionCode 13 versionCode 13
versionName "1.8beta2" versionName "1.8beta2"
setProperty("archivesBaseName", "Emu48-v$versionName") setProperty("archivesBaseName", "Emu48-v$versionName")
@ -80,13 +80,12 @@ android {
dependencies { dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.appcompat:appcompat:1.2.0-beta01'
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta3' implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.preference:preference:1.1.0' implementation 'androidx.preference:preference:1.1.1'
implementation 'com.google.android.material:material:1.2.0-alpha02' implementation 'com.google.android.material:material:1.1.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.documentfile:documentfile:1.0.1'
implementation 'androidx.viewpager:viewpager:1.0.0'
testImplementation 'junit:junit:4.13-beta-3' testImplementation 'junit:junit:4.13-beta-3'
androidTestImplementation 'androidx.test:runner:1.3.0-alpha02' androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0-alpha02' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0-alpha02'
} }

View file

@ -16,7 +16,8 @@
android:label="@string/app_name" android:label="@string/app_name"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout" android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
android:launchMode="singleTop" android:launchMode="singleTop"
android:theme="@style/AppTheme.NoActionBar"> android:theme="@style/AppTheme.NoActionBar"
>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
@ -44,31 +45,6 @@
android:host="*"/> android:host="*"/>
</intent-filter> </intent-filter>
</activity> </activity>
<activity
android:name=".SettingsActivity"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
android:label="@string/title_activity_settings"
android:parentActivityName=".MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.emulator.forty.eight.MainActivity"/>
</activity>
<activity
android:name="org.emulator.calculator.InfoWebActivity"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
android:label="@string/title_web_activity_info">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.emulator.forty.eight.MainActivity" />
</activity>
<activity
android:name="org.emulator.calculator.InfoActivity"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
android:label="@string/title_activity_info">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.emulator.forty.eight.MainActivity" />
</activity>
<provider <provider
android:name="androidx.core.content.FileProvider" android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider" android:authorities="${applicationId}.provider"

View file

@ -74,6 +74,8 @@ Version 1.8beta3 (2020-04-XX)
- Fix: Overlapping window source position when Background/Offset is not (0,0). - Fix: Overlapping window source position when Background/Offset is not (0,0).
- Wrap the table of content in the former help documentation. - Wrap the table of content in the former help documentation.
- Save the settings at the end of the state file. - Save the settings at the end of the state file.
- Transform all child activities with dialog fragments (to prevent unwanted state save).
- Fix an issue with the numpad keys which send the arrow keys and the numbers at the same time.
Version 1.7 (2019-12-12) Version 1.7 (2019-12-12)

View file

@ -15,7 +15,7 @@
package org.emulator.calculator; package org.emulator.calculator;
import android.app.Application; import android.app.Application;
import android.preference.PreferenceManager; import androidx.preference.PreferenceManager;
public class EmuApplication extends Application { public class EmuApplication extends Application {

View file

@ -14,34 +14,65 @@
package org.emulator.calculator; package org.emulator.calculator;
import android.app.Dialog;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.text.method.ScrollingMovementMethod; import android.text.method.ScrollingMovementMethod;
import android.text.util.Linkify; import android.text.util.Linkify;
import android.view.MenuItem; import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatDialogFragment;
import androidx.appcompat.widget.Toolbar;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import androidx.appcompat.app.AppCompatActivity; public class InfoFragment extends AppCompatDialogFragment {
public class InfoActivity extends AppCompatActivity {
private int homeId;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setStyle(AppCompatDialogFragment.STYLE_NO_FRAME, Utils.resId(this, "style", "AppTheme"));
}
setContentView(Utils.resId(this, "layout", "activity_info")); @NonNull
String filepath = getString(Utils.resId(this, "string", "info_readme")); @Override
homeId = Utils.resId(this, "id", "home"); public Dialog onCreateDialog(Bundle savedInstanceState) {
Dialog dialog = super.onCreateDialog(savedInstanceState);
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
return dialog;
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
String title = getString(Utils.resId(this, "string", "title_fragment_info"));
Dialog dialog = getDialog();
if(dialog != null)
dialog.setTitle(title);
View view = inflater.inflate(Utils.resId(this, "layout", "fragment_info"), container, false);
// Toolbar
Toolbar toolbar = view.findViewById(Utils.resId(this, "id", "my_toolbar"));
toolbar.setTitle(title);
Utils.colorizeDrawableWithColor(requireContext(), toolbar.getNavigationIcon(), android.R.attr.colorForeground);
toolbar.setNavigationOnClickListener(v -> {
dismiss();
});
// Programmatically load text from an asset and place it into the // Programmatically load text from an asset and place it into the
// text view. Note that the text we are loading is ASCII, so we // text view. Note that the text we are loading is ASCII, so we
// need to convert it to UTF-16. // need to convert it to UTF-16.
try { try {
InputStream is = getAssets().open(filepath); InputStream is = requireContext().getAssets().open(getString(Utils.resId(this, "string", "info_readme")));
// We guarantee that the available method returns the total // We guarantee that the available method returns the total
// size of the asset... of course, this does mean that a single // size of the asset... of course, this does mean that a single
@ -58,23 +89,11 @@ public class InfoActivity extends AppCompatActivity {
String text = new String(buffer); String text = new String(buffer);
// Finally stick the string into the text view. // Finally stick the string into the text view.
TextView textViewInfo = findViewById(Utils.resId(this, "id", "textViewInfo")); TextView textViewInfo = view.findViewById(Utils.resId(this, "id", "textViewInfo"));
textViewInfo.setMovementMethod(new ScrollingMovementMethod()); textViewInfo.setMovementMethod(new ScrollingMovementMethod());
textViewInfo.setText(text); textViewInfo.setText(text);
Linkify.addLinks(textViewInfo, Linkify.ALL); Linkify.addLinks(textViewInfo, Linkify.ALL);
} catch (IOException e) { } catch (IOException ignored) { }
// Should never happen! return view;
//throw new RuntimeException(e);
}
} }
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if(item.getItemId() == homeId) {
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
} }

View file

@ -1,61 +0,0 @@
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
package org.emulator.calculator;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.view.MenuItem;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.appcompat.app.AppCompatActivity;
public class InfoWebActivity extends AppCompatActivity {
private int homeId;
@SuppressLint("SetJavaScriptEnabled")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(Utils.resId(this, "layout", "activity_web_info"));
homeId = Utils.resId(this, "id", "home");
WebView webView = findViewById(Utils.resId(this, "id", "webViewInfo"));
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
if(url != null)
// Inject a CSS style to wrap the table of content if needed
view.evaluateJavascript("javascript:(function(){var css=document.createElement(\"style\");css.type=\"text/css\";css.innerHTML=\".nav1{overflow-wrap:break-word;}\";document.head.appendChild(css);})();", null);
}
});
webView.loadUrl(getString(Utils.resId(this, "string", "help_url")));
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if(item.getItemId() == homeId) {
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
}

View file

@ -0,0 +1,85 @@
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
package org.emulator.calculator;
import android.annotation.SuppressLint;
import android.app.Dialog;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatDialogFragment;
import androidx.appcompat.widget.Toolbar;
public class InfoWebFragment extends AppCompatDialogFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStyle(AppCompatDialogFragment.STYLE_NO_FRAME, Utils.resId(this, "style", "AppTheme"));
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Dialog dialog = super.onCreateDialog(savedInstanceState);
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
return dialog;
}
@SuppressLint("SetJavaScriptEnabled")
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
String title = getString(Utils.resId(this, "string", "title_web_fragment_info"));
Dialog dialog = getDialog();
if(dialog != null)
dialog.setTitle(title);
View view = inflater.inflate(Utils.resId(this, "layout", "fragment_web_info"), container, false);
// Toolbar
Toolbar toolbar = view.findViewById(Utils.resId(this, "id", "my_toolbar"));
toolbar.setTitle(title);
Utils.colorizeDrawableWithColor(requireContext(), toolbar.getNavigationIcon(), android.R.attr.colorForeground);
toolbar.setNavigationOnClickListener(v -> {
dismiss();
});
WebView webView = view.findViewById(Utils.resId(this, "id", "webViewInfo"));
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
if(url != null)
// Inject a CSS style to wrap the table of content if needed
view.evaluateJavascript("javascript:(function(){var css=document.createElement(\"style\");css.type=\"text/css\";css.innerHTML=\".nav1{overflow-wrap:break-word;}\";document.head.appendChild(css);})();", null);
}
});
webView.loadUrl(getString(Utils.resId(this, "string", "help_url")));
return view;
}
}

View file

@ -30,14 +30,16 @@ import android.view.InputDevice;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.MotionEvent; import android.view.MotionEvent;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
public class MainScreenView extends PanAndScaleView { public class MainScreenView extends PanAndScaleView {
protected static final String TAG = "MainScreenView"; protected static final String TAG = "MainScreenView";
protected final boolean debug = false; protected final boolean debug = true;
private Paint paintFullCalc = new Paint(); private Paint paintFullCalc = new Paint();
private Paint paintLCD = new Paint(); private Paint paintLCD = new Paint();
@ -45,6 +47,7 @@ public class MainScreenView extends PanAndScaleView {
private Rect srcBitmapCopy = new Rect(); private Rect srcBitmapCopy = new Rect();
private SparseIntArray vkmap; private SparseIntArray vkmap;
private HashMap<Character, Integer> charmap; private HashMap<Character, Integer> charmap;
private List<Integer> numpadKey;
private int kmlBackgroundColor = Color.BLACK; private int kmlBackgroundColor = Color.BLACK;
private boolean useKmlBackgroundColor = false; private boolean useKmlBackgroundColor = false;
private int fallbackBackgroundColorType = 0; private int fallbackBackgroundColorType = 0;
@ -102,7 +105,11 @@ public class MainScreenView extends PanAndScaleView {
// vkmap.put(KeyEvent.KEYCODE_CTRL_RIGHT, 0x11); // VK_CONTROL // vkmap.put(KeyEvent.KEYCODE_CTRL_RIGHT, 0x11); // VK_CONTROL
vkmap.put(KeyEvent.KEYCODE_ESCAPE, 0x1B); // VK_ESCAPE vkmap.put(KeyEvent.KEYCODE_ESCAPE, 0x1B); // VK_ESCAPE
vkmap.put(KeyEvent.KEYCODE_SPACE, 0x20); // VK_SPACE vkmap.put(KeyEvent.KEYCODE_SPACE, 0x20); // VK_SPACE
vkmap.put(KeyEvent.KEYCODE_DPAD_LEFT, 0x25); // VK_LEFT vkmap.put(KeyEvent.KEYCODE_PAGE_UP, 0x21); // VK_PRIOR
vkmap.put(KeyEvent.KEYCODE_PAGE_DOWN, 0x22); // VK_NEXT
vkmap.put(KeyEvent.KEYCODE_MOVE_END, 0x23); // VK_END
vkmap.put(KeyEvent.KEYCODE_MOVE_HOME, 0x24); // VK_HOME
vkmap.put(KeyEvent.KEYCODE_DPAD_LEFT, 0x25); // VK_LEFT
vkmap.put(KeyEvent.KEYCODE_DPAD_UP, 0x26); // VK_UP vkmap.put(KeyEvent.KEYCODE_DPAD_UP, 0x26); // VK_UP
vkmap.put(KeyEvent.KEYCODE_DPAD_RIGHT, 0x27); // VK_RIGHT vkmap.put(KeyEvent.KEYCODE_DPAD_RIGHT, 0x27); // VK_RIGHT
vkmap.put(KeyEvent.KEYCODE_DPAD_DOWN, 0x28); // VK_DOWN vkmap.put(KeyEvent.KEYCODE_DPAD_DOWN, 0x28); // VK_DOWN
@ -166,6 +173,21 @@ public class MainScreenView extends PanAndScaleView {
vkmap.put(KeyEvent.KEYCODE_APOSTROPHE, 0xDE); // VK_OEM_7 ( ») vkmap.put(KeyEvent.KEYCODE_APOSTROPHE, 0xDE); // VK_OEM_7 ( »)
vkmap.put(KeyEvent.KEYCODE_BACKSLASH, 0xDC); // VK_OEM_5 (\|) vkmap.put(KeyEvent.KEYCODE_BACKSLASH, 0xDC); // VK_OEM_5 (\|)
numpadKey = Arrays.asList(
KeyEvent.KEYCODE_NUMPAD_0,
KeyEvent.KEYCODE_NUMPAD_1,
KeyEvent.KEYCODE_NUMPAD_2,
KeyEvent.KEYCODE_NUMPAD_3,
KeyEvent.KEYCODE_NUMPAD_4,
KeyEvent.KEYCODE_NUMPAD_5,
KeyEvent.KEYCODE_NUMPAD_6,
KeyEvent.KEYCODE_NUMPAD_7,
KeyEvent.KEYCODE_NUMPAD_8,
KeyEvent.KEYCODE_NUMPAD_9,
KeyEvent.KEYCODE_NUMPAD_DOT,
KeyEvent.KEYCODE_NUMPAD_COMMA
);
this.setFocusable(true); this.setFocusable(true);
this.setFocusableInTouchMode(true); this.setFocusableInTouchMode(true);
} }
@ -213,6 +235,8 @@ public class MainScreenView extends PanAndScaleView {
public boolean onKeyDown(int keyCode, KeyEvent event) { public boolean onKeyDown(int keyCode, KeyEvent event) {
if((event.getFlags() & KeyEvent.FLAG_VIRTUAL_HARD_KEY) == 0 if((event.getFlags() & KeyEvent.FLAG_VIRTUAL_HARD_KEY) == 0
&& (event.getSource() & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD) { && (event.getSource() & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD) {
if(!event.isNumLockOn() && numpadKey.indexOf(keyCode) != -1)
return false;
char pressedKey = (char) event.getUnicodeChar(); char pressedKey = (char) event.getUnicodeChar();
Integer windowsKeycode = charmap.get(pressedKey); Integer windowsKeycode = charmap.get(pressedKey);
if(windowsKeycode == null) if(windowsKeycode == null)
@ -232,6 +256,8 @@ public class MainScreenView extends PanAndScaleView {
public boolean onKeyUp(int keyCode, KeyEvent event) { public boolean onKeyUp(int keyCode, KeyEvent event) {
if((event.getFlags() & KeyEvent.FLAG_VIRTUAL_HARD_KEY) == 0 if((event.getFlags() & KeyEvent.FLAG_VIRTUAL_HARD_KEY) == 0
&& (event.getSource() & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD) { && (event.getSource() & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD) {
if(!event.isNumLockOn() && numpadKey.indexOf(keyCode) != -1)
return false;
char pressedKey = (char) event.getUnicodeChar(); char pressedKey = (char) event.getUnicodeChar();
Integer windowsKeycode = charmap.get(pressedKey); Integer windowsKeycode = charmap.get(pressedKey);
if(windowsKeycode == null) if(windowsKeycode == null)

View file

@ -70,14 +70,6 @@ public class PrinterSimulator {
m_bPrinter82240A = enable; m_bPrinter82240A = enable;
} }
/**
* true to prevent the line wrapping for the textual printer when the character '\4' is sent by the calc.
* @param preventLineWrap true to prevent the line wrapping; false otherwise.
*/
public void setPreventLineWrap(boolean preventLineWrap) {
this.preventLineWrap = preventLineWrap;
}
/** /**
* Change the paper, so we cleanup everything. * Change the paper, so we cleanup everything.
@ -252,8 +244,6 @@ public class PrinterSimulator {
// Text Printer // Text Printer
private boolean preventLineWrap = false;
/** /**
* ROMAN8 Unicode table * ROMAN8 Unicode table
*/ */
@ -303,7 +293,7 @@ public class PrinterSimulator {
private void addTextData(int byData) { private void addTextData(int byData) {
do { do {
// special LF and LF characters // special LF and LF characters
if (/*!preventLineWrap &&*/ byData == 0x04 || byData == 0x0A) { if (byData == 0x04 || byData == 0x0A) {
textUpdate.append('\r'); textUpdate.append('\r');
textUpdate.append('\n'); textUpdate.append('\n');
if(debug) Log.d(TAG, "addTextData(" + byData + ")"); if(debug) Log.d(TAG, "addTextData(" + byData + ")");

View file

@ -22,7 +22,9 @@ import android.graphics.Bitmap;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log; import android.util.Log;
@ -70,9 +72,7 @@ public class PrinterSimulatorFragment extends AppCompatDialogFragment {
@Override @Override
public Dialog onCreateDialog(Bundle savedInstanceState) { public Dialog onCreateDialog(Bundle savedInstanceState) {
Dialog dialog = super.onCreateDialog(savedInstanceState); Dialog dialog = super.onCreateDialog(savedInstanceState);
Window window = dialog.getWindow(); dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
if(window != null)
window.requestFeature(Window.FEATURE_NO_TITLE);
return dialog; return dialog;
} }
@ -94,7 +94,7 @@ public class PrinterSimulatorFragment extends AppCompatDialogFragment {
Toolbar toolbar = view.findViewById(Utils.resId(this, "id", "my_toolbar")); Toolbar toolbar = view.findViewById(Utils.resId(this, "id", "my_toolbar"));
toolbar.setTitle(title); toolbar.setTitle(title);
toolbar.setNavigationIcon(Utils.resId(this, "drawable", "ic_keyboard_backspace_white_24dp")); Utils.colorizeDrawableWithColor(requireContext(), toolbar.getNavigationIcon(), android.R.attr.colorForeground);
toolbar.setNavigationOnClickListener( toolbar.setNavigationOnClickListener(
v -> dismiss() v -> dismiss()
); );

View file

@ -14,6 +14,7 @@
package org.emulator.calculator; package org.emulator.calculator;
import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.net.Uri; import android.net.Uri;
@ -33,226 +34,82 @@ import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
public class Settings extends PreferenceDataStore { public class Settings extends PreferenceDataStore {
private static final String TAG = "Settings"; private static final String TAG = "Settings";
protected final boolean debug = true; protected final boolean debug = false;
private final SharedPreferences defaultSettings; private final SharedPreferences androidSettings;
private List<String> applicationSettingKeys = Arrays.asList("settings_kml_default", "settings_kml_folder", "lastDocument", "MRU");
private final HashMap<String, Object> applicationSettings = new HashMap<>();
private final HashMap<String, Object> commonSettings = new HashMap<>();
private final HashMap<String, Object> embeddedStateSettings = new HashMap<>(); private final HashMap<String, Object> embeddedStateSettings = new HashMap<>();
private SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener; private boolean isCommonSettings = true;
public interface OnOneKeyChangedListener {
void onOneKeyChanged(String keyChanged);
}
private OnOneKeyChangedListener oneKeyChangedListener;
private static String magic = "MYHP"; private static String magic = "MYHP";
private boolean isDefaultSettings = true;
public Settings(@NonNull SharedPreferences sharedPreferences) { public Settings(@NonNull SharedPreferences sharedPreferences) {
if(debug) Log.d(TAG, "Settings()"); if(debug) Log.d(TAG, "Settings()");
this.defaultSettings = sharedPreferences; androidSettings = sharedPreferences;
loadApplicationSettings();
} }
public void registerOnSharedPreferenceChangeListener(SharedPreferences.OnSharedPreferenceChangeListener listener) { public void registerOnOneKeyChangeListener(OnOneKeyChangedListener listener) {
if(debug) Log.d(TAG, "registerOnSharedPreferenceChangeListener()"); if(debug) Log.d(TAG, "registerOnOneKeyChangeListener()");
sharedPreferenceChangeListener = listener; oneKeyChangedListener = listener;
} }
public void unregisterOnSharedPreferenceChangeListener(SharedPreferences.OnSharedPreferenceChangeListener listener) { public void unregisterOnOneKeyChangeListener() {
if(debug) Log.d(TAG, "unregisterOnSharedPreferenceChangeListener()"); if(debug) Log.d(TAG, "unregisterOnOneKeyChangeListener()");
sharedPreferenceChangeListener = null; oneKeyChangedListener = null;
} }
@Override private void putValue(String key, @Nullable Object value) {
public void putString(String key, @Nullable String value) { if(applicationSettingKeys.indexOf(key) != -1)
putString(key, value, false); applicationSettings.put(key, value);
} else if(isCommonSettings)
public void putString(String key, @Nullable String value, boolean forceDefault) { commonSettings.put(key, value);
if(debug) Log.d(TAG, (isDefaultSettings ? "DEFAULT" : "LOCAL") + " putString(key: '" + key + "' value: '" + value + "')"); else
if(isDefaultSettings || forceDefault) {
defaultSettings.edit().putString(key, value).apply();
if(sharedPreferenceChangeListener != null)
sharedPreferenceChangeListener.onSharedPreferenceChanged(defaultSettings, key);
} else {
embeddedStateSettings.put(key, value); embeddedStateSettings.put(key, value);
if(sharedPreferenceChangeListener != null) if(oneKeyChangedListener != null)
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, key); oneKeyChangedListener.onOneKeyChanged(key);
}
private Object getValue(String key) {
Object value = null;
if(!isCommonSettings)
value = embeddedStateSettings.get(key);
if(value == null) {
if(applicationSettingKeys.indexOf(key) != -1)
value = applicationSettings.get(key);
else
value = commonSettings.get(key);
} }
return value;
} }
@Override public boolean getIsDefaultSettings() {
public void putStringSet(String key, @Nullable Set<String> value) { return isCommonSettings;
putStringSet(key, value, false);
}
public void putStringSet(String key, @Nullable Set<String> value, boolean forceDefault) {
if(debug) Log.d(TAG, (isDefaultSettings ? "DEFAULT" : "LOCAL") + " putStringSet(key: '" + key + "' value: '" + "" + "')");
if(isDefaultSettings || forceDefault) {
defaultSettings.edit().putStringSet(key, value).apply();
if(sharedPreferenceChangeListener != null)
sharedPreferenceChangeListener.onSharedPreferenceChanged(defaultSettings, key);
} else {
embeddedStateSettings.put(key, value);
if(sharedPreferenceChangeListener != null)
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, key);
}
} }
@Override public void setIsDefaultSettings(boolean isDefaultSettings) {
public void putInt(String key, int value) { this.isCommonSettings = isDefaultSettings;
putInt(key, value, false);
}
public void putInt(String key, int value, boolean forceDefault) {
if(debug) Log.d(TAG, (isDefaultSettings ? "DEFAULT" : "LOCAL") + " putInt(key: '" + key + "' value: '" + value + "')");
if(isDefaultSettings || forceDefault) {
defaultSettings.edit().putInt(key, value).apply();
if(sharedPreferenceChangeListener != null)
sharedPreferenceChangeListener.onSharedPreferenceChanged(defaultSettings, key);
} else {
embeddedStateSettings.put(key, value);
if(sharedPreferenceChangeListener != null)
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, key);
}
}
@Override
public void putLong(String key, long value){
putLong(key,value,false);
}
public void putLong(String key, long value, boolean forceDefault) {
if(debug) Log.d(TAG, (isDefaultSettings ? "DEFAULT" : "LOCAL") + " putLong(key: '" + key + "' value: '" + value + "')");
if(isDefaultSettings || forceDefault) {
defaultSettings.edit().putLong(key, value).apply();
if(sharedPreferenceChangeListener != null)
sharedPreferenceChangeListener.onSharedPreferenceChanged(defaultSettings, key);
} else {
embeddedStateSettings.put(key, value);
if(sharedPreferenceChangeListener != null)
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, key);
}
}
@Override
public void putFloat(String key, float value){
putFloat(key,value,false);
}
public void putFloat(String key, float value, boolean forceDefault) {
if(debug) Log.d(TAG, (isDefaultSettings ? "DEFAULT" : "LOCAL") + " putFloat(key: '" + key + "' value: '" + value + "')");
if(isDefaultSettings || forceDefault) {
defaultSettings.edit().putFloat(key, value).apply();
if(sharedPreferenceChangeListener != null)
sharedPreferenceChangeListener.onSharedPreferenceChanged(defaultSettings, key);
} else {
embeddedStateSettings.put(key, value);
if(sharedPreferenceChangeListener != null)
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, key);
}
}
@Override
public void putBoolean(String key, boolean value){
putBoolean(key,value,false);
}
public void putBoolean(String key, boolean value, boolean forceDefault) {
if(debug) Log.d(TAG, (isDefaultSettings ? "DEFAULT" : "LOCAL") + " putBoolean(key: '" + key + "' value: '" + value + "')");
if(isDefaultSettings || forceDefault) {
defaultSettings.edit().putBoolean(key, value).apply();
if(sharedPreferenceChangeListener != null)
sharedPreferenceChangeListener.onSharedPreferenceChanged(defaultSettings, key);
} else {
embeddedStateSettings.put(key, value);
if(sharedPreferenceChangeListener != null)
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, key);
}
}
@Nullable
@Override
public String getString(String key, @Nullable String defValue) {
if(debug) Log.d(TAG, "getString(key: '" + key + "')");
if(!isDefaultSettings) {
Object result = embeddedStateSettings.get(key);
if(result instanceof String)
return (String) result;
}
return defaultSettings.getString(key, defValue);
}
@SuppressWarnings("unchecked")
@Nullable
@Override
public Set<String> getStringSet(String key, @Nullable Set<String> defValues) {
if(debug) Log.d(TAG, "getStringSet(key: '" + key + "')");
if(!isDefaultSettings) {
Object result = embeddedStateSettings.get(key);
if(result instanceof Set<?>)
try {
return (Set<String>) result;
} catch (Exception ignored) {}
}
return defaultSettings.getStringSet(key, defValues);
}
@Override
public int getInt(String key, int defValue) {
if(debug) Log.d(TAG, "getInt(key: '" + key + "')");
if(!isDefaultSettings) {
Object result = embeddedStateSettings.get(key);
if(result != null)
try {
return (Integer) result;
} catch (Exception ignored) {}
}
return defaultSettings.getInt(key, defValue);
}
@Override
public long getLong(String key, long defValue) {
if(debug) Log.d(TAG, "getLong(key: '" + key + "')");
if(!isDefaultSettings) {
Object result = embeddedStateSettings.get(key);
if(result != null)
try {
return (Long) result;
} catch (Exception ignored) {}
}
return defaultSettings.getLong(key, defValue);
}
@Override
public float getFloat(String key, float defValue) {
if(debug) Log.d(TAG, "getFloat(key: '" + key + "')");
if(!isDefaultSettings) {
Object result = embeddedStateSettings.get(key);
if(result != null)
try {
return (Float) result;
} catch (Exception ignored) {}
}
return defaultSettings.getFloat(key, defValue);
}
@Override
public boolean getBoolean(String key, boolean defValue) {
if(debug) Log.d(TAG, "getBoolean(key: '" + key + "')");
if(!isDefaultSettings) {
Object result = embeddedStateSettings.get(key);
if(result != null)
try {
return (Boolean) result;
} catch (Exception ignored) {}
}
return defaultSettings.getBoolean(key, defValue);
}
public void setDefaultSettings(boolean defaultSettings) {
isDefaultSettings = defaultSettings;
} }
private static String toJSON(Collection<String> array) { private static String toJSON(Collection<String> array) {
@ -340,26 +197,36 @@ public class Settings extends PreferenceDataStore {
public void saveInStateFile(Context context, String url) { public void saveInStateFile(Context context, String url) {
if(debug) Log.d(TAG, "saveInStateFile(url: '" + url + "')"); if(debug) Log.d(TAG, "saveInStateFile(url: '" + url + "')");
Uri uri = Uri.parse(url);
try { // Consolidate common and embedded settings but without the app only settings
ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(uri, "wa"); Map<String, Object> stateSettings = new HashMap<>();
if(pfd != null) { stateSettings.putAll(commonSettings);
FileOutputStream fileOutputStream = new FileOutputStream(pfd.getFileDescriptor()); stateSettings.putAll(embeddedStateSettings);
String json = toJSON(embeddedStateSettings);
byte[] jsonBytes = json.getBytes(StandardCharsets.UTF_8); String json = toJSON(stateSettings);
int jsonLength = jsonBytes.length; byte[] jsonBytes = json.getBytes(StandardCharsets.UTF_8);
fileOutputStream.write(jsonBytes); int jsonLength = jsonBytes.length;
// The JSON text should not be more than 64KB
fileOutputStream.write((jsonLength >> 8) & 0xFF); if(jsonLength < 65536) {
fileOutputStream.write(jsonLength & 0xFF); Uri uri = Uri.parse(url);
for (int i = 0; i < magic.length(); i++) { try {
fileOutputStream.write(magic.charAt(i)); ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(uri, "wa");
if (pfd != null) {
FileOutputStream fileOutputStream = new FileOutputStream(pfd.getFileDescriptor());
fileOutputStream.write(jsonBytes);
// The JSON text should not be more than 64KB
fileOutputStream.write((jsonLength >> 8) & 0xFF);
fileOutputStream.write(jsonLength & 0xFF);
for (int i = 0; i < magic.length(); i++) {
fileOutputStream.write(magic.charAt(i));
}
fileOutputStream.flush();
fileOutputStream.close();
} }
fileOutputStream.flush(); } catch (IOException e) {
fileOutputStream.close(); e.printStackTrace();
} }
} catch (IOException e) {
e.printStackTrace();
} }
} }
@ -367,6 +234,12 @@ public class Settings extends PreferenceDataStore {
embeddedStateSettings.clear(); embeddedStateSettings.clear();
} }
@SuppressLint("ApplySharedPref")
public void clearCommonDefaultSettings() {
embeddedStateSettings.clear();
commonSettings.clear();
}
@SuppressWarnings("ResultOfMethodCallIgnored") @SuppressWarnings("ResultOfMethodCallIgnored")
public void loadFromStateFile(Context context, String url) { public void loadFromStateFile(Context context, String url) {
if(debug) Log.d(TAG, "loadFromStateFile(url: '" + url + "')"); if(debug) Log.d(TAG, "loadFromStateFile(url: '" + url + "')");
@ -383,7 +256,7 @@ public class Settings extends PreferenceDataStore {
fileInputStream.read(lastChunk, 0, lastChunk.length); fileInputStream.read(lastChunk, 0, lastChunk.length);
} else { } else {
int lastChunkOffset = lastChunk.length - (int)fileSize; int lastChunkOffset = lastChunk.length - (int)fileSize;
fileInputStream.read(lastChunk, lastChunkOffset, lastChunk.length); fileInputStream.read(lastChunk, lastChunkOffset, (int)fileSize);
} }
fileInputStream.close(); fileInputStream.close();
@ -408,4 +281,154 @@ public class Settings extends PreferenceDataStore {
e.printStackTrace(); e.printStackTrace();
} }
} }
private void loadApplicationSettings() {
commonSettings.clear();
Map<String, ?> keyValuePairs = androidSettings.getAll();
for (String key : keyValuePairs.keySet()) {
if (applicationSettingKeys.indexOf(key) != -1)
applicationSettings.put(key, keyValuePairs.get(key));
else
commonSettings.put(key, keyValuePairs.get(key));
}
}
public void saveApplicationSettings() {
if(debug) Log.d(TAG, "saveApplicationSettings()");
SharedPreferences.Editor settingsEditor = androidSettings.edit();
for (String key : commonSettings.keySet()) {
Object value = commonSettings.get(key);
putKeyValueInEditor(settingsEditor, key, value);
}
for (String key : applicationSettingKeys) {
Object value = applicationSettings.get(key);
putKeyValueInEditor(settingsEditor, key, value);
}
settingsEditor.apply();
}
private void putKeyValueInEditor(SharedPreferences.Editor settingsEditor, String key, Object value) {
if (value instanceof Integer)
settingsEditor.putInt(key, ((Number)value).intValue());
else if (value instanceof Long)
settingsEditor.putLong(key, ((Number)value).longValue());
else if (value instanceof Boolean)
settingsEditor.putBoolean(key, (Boolean) value);
else if (value instanceof Float || value instanceof Double)
settingsEditor.putFloat(key, ((Number)value).floatValue());
else if (value instanceof String)
settingsEditor.putString(key, (String) value);
else if (value instanceof Set<?>)
settingsEditor.putStringSet(key, (Set<String>) value);
else
settingsEditor.putString(key, null);
}
// PreferenceDataStore
@Override
public void putString(String key, @Nullable String value) {
if(debug) Log.d(TAG, (isCommonSettings ? "DEFAULT" : "LOCAL") + " putString(key: '" + key + "' value: '" + value + "')");
putValue(key, value);
}
@Override
public void putStringSet(String key, @Nullable Set<String> value) {
if(debug) Log.d(TAG, (isCommonSettings ? "DEFAULT" : "LOCAL") + " putStringSet(key: '" + key + "' value: '" + "" + "')");
putValue(key, value);
}
@Override
public void putInt(String key, int value) {
if(debug) Log.d(TAG, (isCommonSettings ? "DEFAULT" : "LOCAL") + " putInt(key: '" + key + "' value: '" + value + "')");
putValue(key, value);
}
@Override
public void putLong(String key, long value){
if(debug) Log.d(TAG, (isCommonSettings ? "DEFAULT" : "LOCAL") + " putLong(key: '" + key + "' value: '" + value + "')");
putValue(key, value);
}
@Override
public void putFloat(String key, float value){
if(debug) Log.d(TAG, (isCommonSettings ? "DEFAULT" : "LOCAL") + " putFloat(key: '" + key + "' value: '" + value + "')");
putValue(key, value);
}
@Override
public void putBoolean(String key, boolean value){
if(debug) Log.d(TAG, (isCommonSettings ? "DEFAULT" : "LOCAL") + " putBoolean(key: '" + key + "' value: '" + value + "')");
putValue(key, value);
}
@Nullable
@Override
public String getString(String key, @Nullable String defValue) {
if(debug) Log.d(TAG, "getString(key: '" + key + "')");
Object result = getValue(key);
if(result instanceof String)
return (String) result;
return defValue;
}
@SuppressWarnings("unchecked")
@Nullable
@Override
public Set<String> getStringSet(String key, @Nullable Set<String> defValues) {
if(debug) Log.d(TAG, "getStringSet(key: '" + key + "')");
Object result = getValue(key);
if(result instanceof Set<?>)
try {
return (Set<String>) result;
} catch (Exception ignored) {}
return defValues;
}
@Override
public int getInt(String key, int defValue) {
if(debug) Log.d(TAG, "getInt(key: '" + key + "')");
Object result = getValue(key);
if(result != null)
try {
return ((Number) result).intValue();
} catch (Exception ignored) {}
return defValue;
}
@Override
public long getLong(String key, long defValue) {
if(debug) Log.d(TAG, "getLong(key: '" + key + "')");
Object result = getValue(key);
if(result != null)
try {
return ((Number) result).longValue();
} catch (Exception ignored) {}
return defValue;
}
@Override
public float getFloat(String key, float defValue) {
if(debug) Log.d(TAG, "getFloat(key: '" + key + "')");
Object result = getValue(key);
if(result != null)
try {
return ((Number) result).floatValue();
} catch (Exception ignored) {}
return defValue;
}
@Override
public boolean getBoolean(String key, boolean defValue) {
if(debug) Log.d(TAG, "getBoolean(key: '" + key + "')");
Object result = getValue(key);
if(result != null)
try {
return (Boolean) result;
} catch (Exception ignored) {}
return defValue;
}
} }

View file

@ -16,17 +16,24 @@ package org.emulator.calculator;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.res.Resources;
import android.database.Cursor; import android.database.Cursor;
import android.graphics.BlendMode;
import android.graphics.BlendModeColorFilter;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.provider.OpenableColumns; import android.provider.OpenableColumns;
import android.util.Log; import android.util.Log;
import android.util.TypedValue;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ListAdapter; import android.widget.ListAdapter;
import android.widget.ListView; import android.widget.ListView;
import android.widget.Toast; import android.widget.Toast;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGL10;
@ -43,29 +50,49 @@ public class Utils {
toast.show(); toast.show();
} }
static int resId(Context context, String resourcename, String variableName) static int resId(Context context, String resourceName, String variableName) {
{
try { try {
return context.getResources().getIdentifier(variableName, resourcename, context.getApplicationContext().getPackageName()); return context.getResources().getIdentifier(variableName, resourceName, context.getApplicationContext().getPackageName());
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
return -1; return -1;
} }
} }
static int resId(Fragment fragment, String resourcename, String variableName) public static int resId(Fragment fragment, String resourceName, String variableName) {
{
try { try {
Context context = fragment.getContext(); Context context = fragment.getContext();
if(context != null) if(context != null)
return fragment.getResources().getIdentifier(variableName, resourcename, context.getApplicationContext().getPackageName()); return fragment.getResources().getIdentifier(variableName, resourceName, context.getApplicationContext().getPackageName());
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
return -1; return -1;
} }
public static void makeUriPersistable(Context context, Intent data, Uri uri) { public static int getThemedColor(Context context, int attr) {
Resources.Theme theme = context.getTheme();
if (theme != null) {
TypedValue tv = new TypedValue();
theme.resolveAttribute(attr, tv, true);
Resources resources = context.getResources();
if(resources != null)
return ContextCompat.getColor(context, tv.resourceId);
}
return 0;
}
public static void colorizeDrawableWithColor(Context context, Drawable icon, int colorAttribute) {
if(icon != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
icon.setColorFilter(new BlendModeColorFilter(Utils.getThemedColor(context, colorAttribute), BlendMode.SRC_ATOP));
else
icon.setColorFilter(Utils.getThemedColor(context, colorAttribute), PorterDuff.Mode.SRC_ATOP);
}
}
public static void makeUriPersistable(Context context, Intent data, Uri uri) {
int takeFlags = data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); int takeFlags = data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
context.getContentResolver().takePersistableUriPermission(uri, takeFlags); context.getContentResolver().takePersistableUriPermission(uri, takeFlags);

View file

@ -21,7 +21,6 @@ import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.res.AssetManager; import android.content.res.AssetManager;
import android.content.res.Resources;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.BitmapDrawable;
@ -32,7 +31,6 @@ import android.os.Bundle;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.util.Log; import android.util.Log;
import android.util.SparseArray; import android.util.SparseArray;
import android.util.TypedValue;
import android.view.HapticFeedbackConstants; import android.view.HapticFeedbackConstants;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
@ -47,10 +45,8 @@ import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.FileProvider; import androidx.core.content.FileProvider;
import androidx.core.view.GravityCompat; import androidx.core.view.GravityCompat;
import androidx.documentfile.provider.DocumentFile; import androidx.documentfile.provider.DocumentFile;
@ -59,8 +55,8 @@ import androidx.drawerlayout.widget.DrawerLayout;
import com.google.android.material.navigation.NavigationView; import com.google.android.material.navigation.NavigationView;
import org.emulator.calculator.EmuApplication; import org.emulator.calculator.EmuApplication;
import org.emulator.calculator.InfoActivity; import org.emulator.calculator.InfoFragment;
import org.emulator.calculator.InfoWebActivity; import org.emulator.calculator.InfoWebFragment;
import org.emulator.calculator.LCDOverlappingView; import org.emulator.calculator.LCDOverlappingView;
import org.emulator.calculator.MainScreenView; import org.emulator.calculator.MainScreenView;
import org.emulator.calculator.NativeLib; import org.emulator.calculator.NativeLib;
@ -96,7 +92,8 @@ import java.util.regex.Pattern;
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener { public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
private static final String TAG = "MainActivity"; private static final String TAG = "MainActivity";
private Settings settings; private boolean debug = false;
private Settings settings;
private NavigationView navigationView; private NavigationView navigationView;
private DrawerLayout drawer; private DrawerLayout drawer;
private MainScreenView mainScreenView; private MainScreenView mainScreenView;
@ -107,7 +104,6 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
public static final int INTENT_GETSAVEFILENAME = 2; public static final int INTENT_GETSAVEFILENAME = 2;
public static final int INTENT_OBJECT_LOAD = 3; public static final int INTENT_OBJECT_LOAD = 3;
public static final int INTENT_OBJECT_SAVE = 4; public static final int INTENT_OBJECT_SAVE = 4;
public static final int INTENT_SETTINGS = 5;
public static final int INTENT_PORT2LOAD = 6; public static final int INTENT_PORT2LOAD = 6;
public static final int INTENT_PICK_KML_FOLDER_FOR_NEW_FILE = 7; public static final int INTENT_PICK_KML_FOLDER_FOR_NEW_FILE = 7;
public static final int INTENT_PICK_KML_FOLDER_FOR_CHANGING = 8; public static final int INTENT_PICK_KML_FOLDER_FOR_CHANGING = 8;
@ -144,20 +140,14 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
toolbar.setVisibility(View.GONE);
drawer = findViewById(R.id.drawer_layout); drawer = 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 = findViewById(R.id.nav_view); navigationView = findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this); navigationView.setNavigationItemSelectedListener(this);
settings = EmuApplication.getSettings(); settings = EmuApplication.getSettings();
settings.setDefaultSettings(true); settings.setIsDefaultSettings(true);
ViewGroup mainScreenContainer = findViewById(R.id.main_screen_container); ViewGroup mainScreenContainer = findViewById(R.id.main_screen_container);
@ -193,7 +183,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
} }
} }
updateMRU(); updateMRU();
updateFromPreferences(null); updateFromPreferences(null, false);
updateNavigationDrawerItems(); updateNavigationDrawerItems();
@ -240,7 +230,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
Utils.makeUriPersistable(this, intent, documentToOpenUri); Utils.makeUriPersistable(this, intent, documentToOpenUri);
} }
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, e.getMessage()); if(debug) Log.e(TAG, e.getMessage());
} }
else if(drawer != null) else if(drawer != null)
drawer.openDrawer(GravityCompat.START); drawer.openDrawer(GravityCompat.START);
@ -285,7 +275,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
protected void onStop() { protected void onStop() {
//TODO We cannot make the difference between going to the settings or loading/saving a file and a real app stop/kill! //TODO We cannot make the difference between going to the settings or loading/saving a file and a real app stop/kill!
// -> Maybe by settings some flags when loading/saving // -> Maybe by settings some flags when loading/saving
settings.putStringSet("MRU", mruLinkedHashMap.keySet(), true); settings.putStringSet("MRU", mruLinkedHashMap.keySet());
if(lcdOverlappingView != null) if(lcdOverlappingView != null)
lcdOverlappingView.saveViewLayout(); lcdOverlappingView.saveViewLayout();
@ -295,6 +285,8 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
onFileSave(); onFileSave();
} }
settings.saveApplicationSettings();
clearFolderCache(); clearFolderCache();
super.onStop(); super.onStop();
@ -534,7 +526,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
String url = file.getUri().toString(); String url = file.getUri().toString();
String name = file.getName(); String name = file.getName();
String mime = file.getType(); String mime = file.getType();
Log.d(TAG, "url: " + url + ", name: " + name + ", mime: " + mime); if(debug) Log.d(TAG, "url: " + url + ", name: " + name + ", mime: " + mime);
if(kmlMimeType.equals(mime)) { if(kmlMimeType.equals(mime)) {
calculatorsAssetFilenames.add(url); calculatorsAssetFilenames.add(url);
} }
@ -662,7 +654,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
private void newFileFromKML(String kmlScriptFilename) { private void newFileFromKML(String kmlScriptFilename) {
int result = NativeLib.onFileNew(kmlScriptFilename); int result = NativeLib.onFileNew(kmlScriptFilename);
if(result > 0) { if(result > 0) {
settings.setDefaultSettings(false); settings.setIsDefaultSettings(false);
settings.clearEmbeddedStateSettings(); settings.clearEmbeddedStateSettings();
showCalculatorView(true); showCalculatorView(true);
displayFilename(""); displayFilename("");
@ -731,7 +723,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
private void OnFileClose() { private void OnFileClose() {
ensureDocumentSaved(() -> { ensureDocumentSaved(() -> {
NativeLib.onFileClose(); NativeLib.onFileClose();
settings.setDefaultSettings(true); settings.setIsDefaultSettings(true);
settings.clearEmbeddedStateSettings(); settings.clearEmbeddedStateSettings();
showCalculatorView(false); showCalculatorView(false);
saveLastDocument(""); saveLastDocument("");
@ -744,7 +736,32 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
} }
private void OnSettings() { private void OnSettings() {
startActivityForResult(new Intent(this, SettingsActivity.class), INTENT_SETTINGS); SettingsFragment settingsFragment = new SettingsFragment();
settingsFragment.registerOnSettingsKeyChangedListener(settingsKeyChanged -> {
HashSet<String> changedKeysCleaned = new HashSet<>();
for (String key : settingsKeyChanged) {
if(debug) Log.d(TAG, "ChangedKey): " + key);
switch (key) {
case "settings_port1en":
case "settings_port1wr":
changedKeysCleaned.add("settings_port1");
break;
case "settings_port2en":
case "settings_port2wr":
case "settings_port2load":
changedKeysCleaned.add("settings_port2");
break;
default:
changedKeysCleaned.add(key);
break;
}
}
for (String key : changedKeysCleaned) {
updateFromPreferences(key, true);
}
settingsFragment.unregisterOnSettingsKeyChangedListener();
});
settingsFragment.show(getSupportFragmentManager(), "SettingsFragment");
} }
private void openDocument() { private void openDocument() {
@ -911,7 +928,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
} }
} else if(which == lastIndex + 1) { } else if(which == lastIndex + 1) {
// Reset to default KML folder // Reset to default KML folder
settings.putBoolean("settings_kml_default", true, true); settings.putBoolean("settings_kml_default", true);
updateFromPreferences("settings_kml", true); updateFromPreferences("settings_kml", true);
if(changeKML) if(changeKML)
OnViewScript(); OnViewScript();
@ -1002,156 +1019,129 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
} }
private void OnTopics() { private void OnTopics() {
startActivity(new Intent(this, InfoWebActivity.class)); new InfoWebFragment().show(getSupportFragmentManager(), "InfoWebFragment");
} }
private void OnAbout() { private void OnAbout() {
startActivity(new Intent(this, InfoActivity.class)); new InfoFragment().show(getSupportFragmentManager(), "InfoFragment");
} }
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if(resultCode == Activity.RESULT_OK && data != null) { if(resultCode == Activity.RESULT_OK && data != null) {
if(requestCode == INTENT_SETTINGS) { Uri uri = data.getData();
String[] changedKeys = data.getStringArrayExtra("changedKeys"); String url = null;
if(changedKeys != null) { if (uri != null)
HashSet<String> changedKeysCleaned = new HashSet<>(); url = uri.toString();
for (String key : changedKeys) { if (url != null) {
//Log.d(TAG, "ChangedKey): " + key); switch (requestCode) {
switch (key) { case INTENT_GETOPENFILENAME: {
case "settings_port1en": if(debug) Log.d(TAG, "onActivityResult INTENT_GETOPENFILENAME " + url);
case "settings_port1wr": int openResult = onFileOpen(url);
changedKeysCleaned.add("settings_port1"); if (openResult > 0) {
break; saveLastDocument(url);
case "settings_port2en": Utils.makeUriPersistable(this, data, uri);
case "settings_port2wr": } else if(openResult == -2 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // >= API 21
case "settings_port2load": // For security reason, you must select the folder where are the KML and ROM files and then, reopen this file!
changedKeysCleaned.add("settings_port2"); new AlertDialog.Builder(this)
break; .setTitle(getString(R.string.message_open_security))
default: .setMessage(getString(R.string.message_open_security_description))
changedKeysCleaned.add(key); .setPositiveButton(android.R.string.ok, (dialog, which) -> {
break; Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, INTENT_PICK_KML_FOLDER_FOR_SECURITY);
}).show();
} }
break;
} }
for (String key : changedKeysCleaned) { case INTENT_GETSAVEFILENAME: {
updateFromPreferences(key, true); if(debug) Log.d(TAG, "onActivityResult INTENT_GETSAVEFILENAME " + url);
if (NativeLib.onFileSaveAs(url) != 0) {
showAlert(getString(R.string.message_state_saved));
settings.saveInStateFile(this, url);
saveLastDocument(url);
Utils.makeUriPersistable(this, data, uri);
displayFilename(url);
if (fileSaveAsCallback != null)
fileSaveAsCallback.run();
}
break;
} }
} case INTENT_OBJECT_LOAD: {
} else { if(debug) Log.d(TAG, "onActivityResult INTENT_OBJECT_LOAD " + url);
Uri uri = data.getData(); NativeLib.onObjectLoad(url);
String url = null; break;
if (uri != null) }
url = uri.toString(); case INTENT_OBJECT_SAVE: {
if (url != null) { if(debug) Log.d(TAG, "onActivityResult INTENT_OBJECT_SAVE " + url);
switch (requestCode) { NativeLib.onObjectSave(url, null);
case INTENT_GETOPENFILENAME: { break;
//Log.d(TAG, "onActivityResult INTENT_GETOPENFILENAME " + url); }
int openResult = onFileOpen(url); case INTENT_PICK_KML_FOLDER_FOR_NEW_FILE:
if (openResult > 0) { case INTENT_PICK_KML_FOLDER_FOR_CHANGING:
saveLastDocument(url); case INTENT_PICK_KML_FOLDER_FOR_SETTINGS:
Utils.makeUriPersistable(this, data, uri); case INTENT_PICK_KML_FOLDER_FOR_SECURITY: {
} else if(openResult == -2 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // >= API 21 if(debug) Log.d(TAG, "onActivityResult INTENT_PICK_KML_FOLDER " + url);
// For security reason, you must select the folder where are the KML and ROM files and then, reopen this file! settings.putBoolean("settings_kml_default", false);
settings.putString("settings_kml_folder", url);
updateFromPreferences("settings_kml", true);
Utils.makeUriPersistableReadOnly(this, data, uri);
switch (requestCode) {
case INTENT_PICK_KML_FOLDER_FOR_NEW_FILE:
OnFileNew();
break;
case INTENT_PICK_KML_FOLDER_FOR_CHANGING:
OnViewScript();
break;
case INTENT_PICK_KML_FOLDER_FOR_SETTINGS:
break;
case INTENT_PICK_KML_FOLDER_FOR_SECURITY:
new AlertDialog.Builder(this) new AlertDialog.Builder(this)
.setTitle(getString(R.string.message_open_security)) .setTitle(getString(R.string.message_open_security_retry))
.setMessage(getString(R.string.message_open_security_description)) .setMessage(getString(R.string.message_open_security_retry_description))
.setPositiveButton(android.R.string.ok, (dialog, which) -> { .setPositiveButton(android.R.string.ok, (dialog, which) -> {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, INTENT_PICK_KML_FOLDER_FOR_SECURITY);
}).show(); }).show();
} break;
break;
} }
case INTENT_GETSAVEFILENAME: { break;
//Log.d(TAG, "onActivityResult INTENT_GETSAVEFILENAME " + url);
if (NativeLib.onFileSaveAs(url) != 0) {
showAlert(getString(R.string.message_state_saved));
settings.saveInStateFile(this, url);
saveLastDocument(url);
Utils.makeUriPersistable(this, data, uri);
displayFilename(url);
if (fileSaveAsCallback != null)
fileSaveAsCallback.run();
}
break;
}
case INTENT_OBJECT_LOAD: {
//Log.d(TAG, "onActivityResult INTENT_OBJECT_LOAD " + url);
NativeLib.onObjectLoad(url);
break;
}
case INTENT_OBJECT_SAVE: {
//Log.d(TAG, "onActivityResult INTENT_OBJECT_SAVE " + url);
NativeLib.onObjectSave(url, null);
break;
}
case INTENT_PICK_KML_FOLDER_FOR_NEW_FILE:
case INTENT_PICK_KML_FOLDER_FOR_CHANGING:
case INTENT_PICK_KML_FOLDER_FOR_SETTINGS:
case INTENT_PICK_KML_FOLDER_FOR_SECURITY: {
//Log.d(TAG, "onActivityResult INTENT_PICK_KML_FOLDER " + url);
settings.putBoolean("settings_kml_default", false, true);
settings.putString("settings_kml_folder", url, true);
updateFromPreferences("settings_kml", true);
Utils.makeUriPersistableReadOnly(this, data, uri);
switch (requestCode) {
case INTENT_PICK_KML_FOLDER_FOR_NEW_FILE:
OnFileNew();
break;
case INTENT_PICK_KML_FOLDER_FOR_CHANGING:
OnViewScript();
break;
case INTENT_PICK_KML_FOLDER_FOR_SETTINGS:
break;
case INTENT_PICK_KML_FOLDER_FOR_SECURITY:
new AlertDialog.Builder(this)
.setTitle(getString(R.string.message_open_security_retry))
.setMessage(getString(R.string.message_open_security_retry_description))
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
}).show();
break;
}
break;
}
case INTENT_CREATE_RAM_CARD: {
//Log.d(TAG, "onActivityResult INTENT_CREATE_RAM_CARD " + url);
if(selectedRAMSize > 0) {
int size = 2 * selectedRAMSize;
FileOutputStream fileOutputStream;
try {
ParcelFileDescriptor pfd = getContentResolver().openFileDescriptor(uri, "w");
if(pfd != null) {
fileOutputStream = new FileOutputStream(pfd.getFileDescriptor());
byte[] zero = new byte[1024];
Arrays.fill(zero, (byte) 0);
for (int i = 0; i < size; i++)
fileOutputStream.write(zero);
fileOutputStream.flush();
fileOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
selectedRAMSize = -1;
}
break;
}
case INTENT_MACRO_LOAD: {
//Log.d(TAG, "onActivityResult INTENT_MACRO_LOAD " + url);
NativeLib.onToolMacroPlay(url);
updateNavigationDrawerItems();
break;
}
case INTENT_MACRO_SAVE: {
//Log.d(TAG, "onActivityResult INTENT_MACRO_SAVE " + url);
NativeLib.onToolMacroNew(url);
updateNavigationDrawerItems();
break;
}
default:
break;
} }
case INTENT_CREATE_RAM_CARD: {
if(debug) Log.d(TAG, "onActivityResult INTENT_CREATE_RAM_CARD " + url);
if(selectedRAMSize > 0) {
int size = 2 * selectedRAMSize;
FileOutputStream fileOutputStream;
try {
ParcelFileDescriptor pfd = getContentResolver().openFileDescriptor(uri, "w");
if(pfd != null) {
fileOutputStream = new FileOutputStream(pfd.getFileDescriptor());
byte[] zero = new byte[1024];
Arrays.fill(zero, (byte) 0);
for (int i = 0; i < size; i++)
fileOutputStream.write(zero);
fileOutputStream.flush();
fileOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
selectedRAMSize = -1;
}
break;
}
case INTENT_MACRO_LOAD: {
if(debug) Log.d(TAG, "onActivityResult INTENT_MACRO_LOAD " + url);
NativeLib.onToolMacroPlay(url);
updateNavigationDrawerItems();
break;
}
case INTENT_MACRO_SAVE: {
if(debug) Log.d(TAG, "onActivityResult INTENT_MACRO_SAVE " + url);
NativeLib.onToolMacroNew(url);
updateNavigationDrawerItems();
break;
}
default:
break;
} }
} }
} }
@ -1160,7 +1150,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
} }
private void saveLastDocument(String url) { private void saveLastDocument(String url) {
settings.putString("lastDocument", url, true); settings.putString("lastDocument", url);
if(url != null && !url.isEmpty()) if(url != null && !url.isEmpty())
mruLinkedHashMap.put(url, null); mruLinkedHashMap.put(url, null);
@ -1177,14 +1167,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
} else { } else {
mainScreenView.setEnablePanAndScale(false); mainScreenView.setEnablePanAndScale(false);
mainScreenView.setVisibility(View.GONE); mainScreenView.setVisibility(View.GONE);
imageButtonMenu.setColorFilter(Utils.getThemedColor(this, android.R.attr.colorForeground));
Resources.Theme theme = getTheme();
if (theme != null) {
TypedValue tv = new TypedValue();
theme.resolveAttribute(android.R.attr.colorForeground, tv, true);
int iconColor = getResources().getColor(tv.resourceId);
imageButtonMenu.setColorFilter(iconColor);
}
} }
} }
@ -1192,18 +1175,18 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
// Eventually, close the previous state file // Eventually, close the previous state file
NativeLib.onFileClose(); NativeLib.onFileClose();
settings.setDefaultSettings(true); settings.setIsDefaultSettings(true);
showCalculatorView(false); showCalculatorView(false);
displayFilename(""); displayFilename("");
// Pre-Load the embedded settings from the end of the classic state file // Pre-Load the embedded settings from the end of the classic state file
settings.setDefaultSettings(false); settings.setIsDefaultSettings(false);
settings.clearEmbeddedStateSettings(); settings.clearEmbeddedStateSettings();
settings.loadFromStateFile(this, url); settings.loadFromStateFile(this, url);
// Update the Emu VM with the new settings // Update the Emu VM with the new settings
updateFromPreferences(null); updateFromPreferences(null, false);
// Load the genuine state file // Load the genuine state file
int result = NativeLib.onFileOpen(url); int result = NativeLib.onFileOpen(url);
@ -1214,7 +1197,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
showKMLLog(); showKMLLog();
} else { } else {
// Because it failed to load the state file, we switch to the default settings // Because it failed to load the state file, we switch to the default settings
settings.setDefaultSettings(true); settings.setIsDefaultSettings(true);
settings.clearEmbeddedStateSettings(); settings.clearEmbeddedStateSettings();
showKMLLogForce(); showKMLLogForce();
} }
@ -1564,147 +1547,140 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
updateFromPreferences("settings_port1", true); updateFromPreferences("settings_port1", true);
} }
private void updateFromPreferences(Set<String> keys) {
if(keys != null) {
for (String settingKey : keys)
updateFromPreferences(settingKey, false);
} else {
String[] settingKeys = {
"settings_realspeed", "settings_grayscale", "settings_rotation", "settings_auto_layout", "settings_allow_pinch_zoom", "settings_lcd_overlapping_mode", "settings_lcd_pixel_borders",
"settings_hide_bar", "settings_hide_button_menu", "settings_sound_volume", "settings_haptic_feedback",
"settings_background_kml_color", "settings_background_fallback_color",
"settings_printer_model", "settings_printer_prevent_line_wrap", "settings_macro",
"settings_kml", "settings_port1", "settings_port2" };
for (String settingKey : settingKeys)
updateFromPreferences(settingKey, false);
}
}
private void updateFromPreferences(String key, boolean isDynamic) { private void updateFromPreferences(String key, boolean isDynamic) {
int isDynamicValue = isDynamic ? 1 : 0; int isDynamicValue = isDynamic ? 1 : 0;
switch (key) { if(key == null) {
case "settings_realspeed": String[] settingKeys = {
NativeLib.setConfiguration(key, isDynamicValue, settings.getBoolean(key, false) ? 1 : 0, 0, null); "settings_realspeed", "settings_grayscale", "settings_rotation", "settings_auto_layout", "settings_allow_pinch_zoom", "settings_lcd_overlapping_mode", "settings_lcd_pixel_borders",
break; "settings_hide_bar", "settings_hide_button_menu", "settings_sound_volume", "settings_haptic_feedback",
case "settings_grayscale": "settings_background_kml_color", "settings_background_fallback_color",
NativeLib.setConfiguration(key, isDynamicValue, settings.getBoolean(key, false) ? 1 : 0, 0, null); "settings_printer_model", "settings_macro",
break; "settings_kml", "settings_port1", "settings_port2" };
for (String settingKey : settingKeys)
updateFromPreferences(settingKey, false);
} else {
switch (key) {
case "settings_realspeed":
NativeLib.setConfiguration(key, isDynamicValue, settings.getBoolean(key, false) ? 1 : 0, 0, null);
break;
case "settings_grayscale":
NativeLib.setConfiguration(key, isDynamicValue, settings.getBoolean(key, false) ? 1 : 0, 0, null);
break;
case "settings_rotation": case "settings_rotation":
int rotationMode = 0; int rotationMode = 0;
try { try {
rotationMode = Integer.parseInt(settings.getString("settings_rotation", "0")); rotationMode = Integer.parseInt(settings.getString("settings_rotation", "0"));
} catch (NumberFormatException ex) { } catch (NumberFormatException ex) {
// Catch bad number format // Catch bad number format
} }
mainScreenView.setRotationMode(rotationMode, isDynamic); mainScreenView.setRotationMode(rotationMode, isDynamic);
break; break;
case "settings_auto_layout": case "settings_auto_layout":
int autoLayoutMode = 1; int autoLayoutMode = 1;
try { try {
autoLayoutMode = Integer.parseInt(settings.getString("settings_auto_layout", "1")); autoLayoutMode = Integer.parseInt(settings.getString("settings_auto_layout", "1"));
} catch (NumberFormatException ex) { } catch (NumberFormatException ex) {
// Catch bad number format // Catch bad number format
} }
mainScreenView.setAutoLayout(autoLayoutMode, isDynamic); mainScreenView.setAutoLayout(autoLayoutMode, isDynamic);
break; break;
case "settings_allow_pinch_zoom": case "settings_allow_pinch_zoom":
mainScreenView.setAllowPinchZoom(settings.getBoolean("settings_allow_pinch_zoom", true)); mainScreenView.setAllowPinchZoom(settings.getBoolean("settings_allow_pinch_zoom", true));
break; break;
case "settings_lcd_overlapping_mode": case "settings_lcd_overlapping_mode":
int overlappingLCDMode = LCDOverlappingView.OVERLAPPING_LCD_MODE_NONE; int overlappingLCDMode = LCDOverlappingView.OVERLAPPING_LCD_MODE_NONE;
try { try {
overlappingLCDMode = Integer.parseInt(settings.getString("settings_lcd_overlapping_mode", "0")); overlappingLCDMode = Integer.parseInt(settings.getString("settings_lcd_overlapping_mode", "0"));
} catch (NumberFormatException ex) { } catch (NumberFormatException ex) {
// Catch bad number format // Catch bad number format
} }
lcdOverlappingView.setOverlappingLCDMode(overlappingLCDMode); lcdOverlappingView.setOverlappingLCDMode(overlappingLCDMode);
break; break;
case "settings_lcd_pixel_borders": case "settings_lcd_pixel_borders":
boolean usePixelBorders = settings.getBoolean("settings_lcd_pixel_borders", false); boolean usePixelBorders = settings.getBoolean("settings_lcd_pixel_borders", false);
mainScreenView.setUsePixelBorders(usePixelBorders); mainScreenView.setUsePixelBorders(usePixelBorders);
lcdOverlappingView.setUsePixelBorders(usePixelBorders); lcdOverlappingView.setUsePixelBorders(usePixelBorders);
break; break;
case "settings_hide_bar": case "settings_hide_bar":
case "settings_hide_bar_status": case "settings_hide_bar_status":
case "settings_hide_bar_nav": case "settings_hide_bar_nav":
if(settings.getBoolean("settings_hide_bar_status", false) if (settings.getBoolean("settings_hide_bar_status", false)
|| settings.getBoolean("settings_hide_bar_nav", false)) || settings.getBoolean("settings_hide_bar_nav", false))
hideSystemUI(); hideSystemUI();
else else
showSystemUI(); showSystemUI();
break; break;
case "settings_hide_button_menu": case "settings_hide_button_menu":
imageButtonMenu.setVisibility(settings.getBoolean("settings_hide_button_menu", false) ? View.GONE : View.VISIBLE); imageButtonMenu.setVisibility(settings.getBoolean("settings_hide_button_menu", false) ? View.GONE : View.VISIBLE);
break; break;
case "settings_sound_volume": { case "settings_sound_volume": {
int volumeOption = settings.getInt("settings_sound_volume", 64); int volumeOption = settings.getInt("settings_sound_volume", 64);
NativeLib.setConfiguration("settings_sound_volume", isDynamicValue, volumeOption, 0, null); NativeLib.setConfiguration("settings_sound_volume", isDynamicValue, volumeOption, 0, null);
break; break;
} }
case "settings_haptic_feedback": case "settings_haptic_feedback":
// Nothing to do // Nothing to do
break; break;
case "settings_background_kml_color": case "settings_background_kml_color":
mainScreenView.setBackgroundKmlColor(settings.getBoolean("settings_background_kml_color", false)); mainScreenView.setBackgroundKmlColor(settings.getBoolean("settings_background_kml_color", false));
break; break;
case "settings_background_fallback_color": case "settings_background_fallback_color":
try { try {
mainScreenView.setBackgroundFallbackColor(Integer.parseInt(settings.getString("settings_background_fallback_color", "0"))); mainScreenView.setBackgroundFallbackColor(Integer.parseInt(settings.getString("settings_background_fallback_color", "0")));
} catch (NumberFormatException ex) { } catch (NumberFormatException ex) {
// Catch bad number format // Catch bad number format
} }
break; break;
case "settings_printer_model": case "settings_printer_model":
try { try {
printerSimulator.setPrinterModel82240A(Integer.parseInt(settings.getString("settings_printer_model", "1")) == 0); printerSimulator.setPrinterModel82240A(Integer.parseInt(settings.getString("settings_printer_model", "1")) == 0);
} catch (NumberFormatException ex) { } catch (NumberFormatException ex) {
// Catch bad number format // Catch bad number format
} }
break; break;
case "settings_printer_prevent_line_wrap":
printerSimulator.setPreventLineWrap(settings.getBoolean("settings_printer_prevent_line_wrap", false));
break;
case "settings_kml": case "settings_kml":
case "settings_kml_default": case "settings_kml_default":
case "settings_kml_folder": case "settings_kml_folder":
kmlFolderUseDefault = settings.getBoolean("settings_kml_default", true); kmlFolderUseDefault = settings.getBoolean("settings_kml_default", true);
if(!kmlFolderUseDefault) { if (!kmlFolderUseDefault) {
kmlFolderURL = settings.getString("settings_kml_folder", ""); kmlFolderURL = settings.getString("settings_kml_folder", "");
// https://github.com/googlesamples/android-DirectorySelection // https://github.com/googlesamples/android-DirectorySelection
// https://stackoverflow.com/questions/44185477/intent-action-open-document-tree-doesnt-seem-to-return-a-real-path-to-drive/44185706 // https://stackoverflow.com/questions/44185477/intent-action-open-document-tree-doesnt-seem-to-return-a-real-path-to-drive/44185706
// https://stackoverflow.com/questions/26744842/how-to-use-the-new-sd-card-access-api-presented-for-android-5-0-lollipop // https://stackoverflow.com/questions/26744842/how-to-use-the-new-sd-card-access-api-presented-for-android-5-0-lollipop
} }
kmlFolderChange = true; kmlFolderChange = true;
break; break;
case "settings_macro": case "settings_macro":
case "settings_macro_real_speed": case "settings_macro_real_speed":
case "settings_macro_manual_speed": case "settings_macro_manual_speed":
boolean macroRealSpeed = settings.getBoolean("settings_macro_real_speed", true); boolean macroRealSpeed = settings.getBoolean("settings_macro_real_speed", true);
int macroManualSpeed = settings.getInt("settings_macro_manual_speed", 500); int macroManualSpeed = settings.getInt("settings_macro_manual_speed", 500);
NativeLib.setConfiguration("settings_macro", isDynamicValue, macroRealSpeed ? 1 : 0, macroManualSpeed, null); NativeLib.setConfiguration("settings_macro", isDynamicValue, macroRealSpeed ? 1 : 0, macroManualSpeed, null);
break; break;
case "settings_port1": case "settings_port1":
case "settings_port1en": case "settings_port1en":
case "settings_port1wr": case "settings_port1wr":
NativeLib.setConfiguration("settings_port1", isDynamicValue, NativeLib.setConfiguration("settings_port1", isDynamicValue,
settings.getBoolean("settings_port1en", false) ? 1 : 0, settings.getBoolean("settings_port1en", false) ? 1 : 0,
settings.getBoolean("settings_port1wr", false) ? 1 : 0, settings.getBoolean("settings_port1wr", false) ? 1 : 0,
null); null);
break; break;
case "settings_port2": case "settings_port2":
case "settings_port2en": case "settings_port2en":
case "settings_port2wr": case "settings_port2wr":
case "settings_port2load": case "settings_port2load":
NativeLib.setConfiguration("settings_port2", isDynamicValue, NativeLib.setConfiguration("settings_port2", isDynamicValue,
settings.getBoolean("settings_port2en", false) ? 1 : 0, settings.getBoolean("settings_port2en", false) ? 1 : 0,
settings.getBoolean("settings_port2wr", false) ? 1 : 0, settings.getBoolean("settings_port2wr", false) ? 1 : 0,
settings.getString("settings_port2load", "")); settings.getString("settings_port2load", ""));
break; break;
}
} }
} }

View file

@ -1,281 +0,0 @@
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
package org.emulator.forty.eight;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.text.InputType;
import android.util.Log;
import android.view.MenuItem;
import android.widget.EditText;
import java.util.HashSet;
import java.util.Locale;
import java.util.Objects;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.preference.Preference;
import androidx.preference.PreferenceDataStore;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceManager;
import androidx.preference.SeekBarPreference;
import org.emulator.calculator.EmuApplication;
import org.emulator.calculator.NativeLib;
import org.emulator.calculator.Settings;
import org.emulator.calculator.Utils;
public class SettingsActivity extends AppCompatActivity {
private static final String TAG = "SettingsActivity";
protected final boolean debug = false;
private static Settings settings;
private HashSet<String> settingsKeyChanged = new HashSet<>();
private SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> settingsKeyChanged.add(key);
private GeneralPreferenceFragment generalPreferenceFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
settings = EmuApplication.getSettings();
settings.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
// Show the Up button in the action bar.
actionBar.setDisplayHomeAsUpEnabled(true);
}
generalPreferenceFragment = new GeneralPreferenceFragment();
getSupportFragmentManager().beginTransaction().replace(android.R.id.content, generalPreferenceFragment).commit();
}
@Override
protected void onDestroy() {
settings.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
super.onDestroy();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onBackPressed() {
Intent resultIntent = new Intent();
resultIntent.putExtra("changedKeys", settingsKeyChanged.toArray(new String[0]));
setResult(Activity.RESULT_OK, resultIntent);
finish();
}
/**
* This fragment shows general preferences only. It is used when the
* activity is showing a two-pane settings UI.
*/
public static class GeneralPreferenceFragment extends PreferenceFragmentCompat {
Preference preferencePort2load = null;
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
getPreferenceManager().setPreferenceDataStore(EmuApplication.getSettings());
// Load the preferences from an XML resource
setPreferencesFromResource(R.xml.pref_general, rootKey);
setHasOptionsMenu(true);
// Sound settings
SeekBarPreference preferenceSoundVolume = findPreference("settings_sound_volume");
if(preferenceSoundVolume != null) {
if(!NativeLib.getSoundEnabled()) {
preferenceSoundVolume.setSummary("Cannot initialize the sound engine.");
preferenceSoundVolume.setEnabled(false);
} else {
preferenceSoundVolume.setOnPreferenceClickListener(preference -> {
AlertDialog.Builder alert = new AlertDialog.Builder(Objects.requireNonNull(getContext()));
alert.setTitle(R.string.settings_sound_volume_dialog_title);
final EditText input = new EditText(getContext());
input.setInputType(InputType.TYPE_CLASS_NUMBER);
input.setRawInputType(Configuration.KEYBOARD_12KEY);
input.setFocusable(true);
input.setText(String.format(Locale.US,"%d", preferenceSoundVolume.getValue()));
alert.setView(input);
alert.setPositiveButton(R.string.message_ok, (dialog, whichButton) -> {
String newValueText = input.getText().toString();
try {
int newValue = Integer.parseInt(newValueText);
if(newValue >= preferenceSoundVolume.getMin() && newValue <= preferenceSoundVolume.getMax())
preferenceSoundVolume.setValue(newValue);
} catch (NumberFormatException ignored) {}
});
alert.setNegativeButton(R.string.message_cancel, (dialog, whichButton) -> {});
alert.show();
return true;
});
}
}
// Background color settings
Preference preferenceBackgroundFallbackColor = findPreference("settings_background_fallback_color");
// final ColorPickerPreferenceCompat preferenceBackgroundCustomColor = (ColorPickerPreferenceCompat)findPreference("settings_background_custom_color");
if(preferenceBackgroundFallbackColor != null /*&& preferenceBackgroundCustomColor != null*/) {
final String[] stringArrayBackgroundFallbackColor = getResources().getStringArray(R.array.settings_background_fallback_color_item);
Preference.OnPreferenceChangeListener onPreferenceChangeListenerBackgroundFallbackColor = (preference, value) -> {
if(value != null) {
String stringValue = value.toString();
int backgroundFallbackColor = -1;
try {
backgroundFallbackColor = Integer.parseInt(stringValue);
} catch (NumberFormatException ignored) {}
if(backgroundFallbackColor >= 0 && backgroundFallbackColor < stringArrayBackgroundFallbackColor.length)
preference.setSummary(stringArrayBackgroundFallbackColor[backgroundFallbackColor]);
// preferenceBackgroundCustomColor.setEnabled(backgroundFallbackColor == 2);
}
return true;
};
preferenceBackgroundFallbackColor.setOnPreferenceChangeListener(onPreferenceChangeListenerBackgroundFallbackColor);
onPreferenceChangeListenerBackgroundFallbackColor.onPreferenceChange(preferenceBackgroundFallbackColor,
settings.getString(preferenceBackgroundFallbackColor.getKey(), "0"));
//preferenceBackgroundCustomColor.setColorValue(customColor);
// Preference.OnPreferenceChangeListener onPreferenceChangeListenerBackgroundCustomColor = new Preference.OnPreferenceChangeListener() {
// @Override
// public boolean onPreferenceChange(Preference preference, Object value) {
// if(value != null) {
// int customColor = (Integer)value;
// }
// return true;
// }
// };
// preferenceBackgroundCustomColor.setOnPreferenceChangeListener(onPreferenceChangeListenerBackgroundCustomColor);
// onPreferenceChangeListenerBackgroundCustomColor.onPreferenceChange(preferenceBackgroundCustomColor, sharedPreferences.getBoolean(preferenceBackgroundCustomColor.getKey(), false));
}
// Macro
Preference preferenceMacroRealSpeed = findPreference("settings_macro_real_speed");
Preference preferenceMacroManualSpeed = findPreference("settings_macro_manual_speed");
if(preferenceMacroRealSpeed != null && preferenceMacroManualSpeed != null) {
Preference.OnPreferenceChangeListener onPreferenceChangeListenerMacroRealSpeed = (preference, value) -> {
if(value != null)
preferenceMacroManualSpeed.setEnabled(!(Boolean) value);
return true;
};
preferenceMacroRealSpeed.setOnPreferenceChangeListener(onPreferenceChangeListenerMacroRealSpeed);
onPreferenceChangeListenerMacroRealSpeed.onPreferenceChange(preferenceMacroRealSpeed, settings.getBoolean(preferenceMacroRealSpeed.getKey(), true));
}
// Ports 1 & 2 settings
Preference preferencePort1en = findPreference("settings_port1en");
Preference preferencePort1wr = findPreference("settings_port1wr");
Preference preferencePort2en = findPreference("settings_port2en");
Preference preferencePort2wr = findPreference("settings_port2wr");
preferencePort2load = findPreference("settings_port2load");
if(preferencePort1en != null && preferencePort1wr != null
&& preferencePort2en != null && preferencePort2wr != null
&& preferencePort2load != null) {
boolean enablePortPreferences = NativeLib.isPortExtensionPossible();
Preference.OnPreferenceChangeListener onPreferenceChangeListenerPort1en = (preference, value) -> {
preferencePort1en.setEnabled(enablePortPreferences);
preferencePort1wr.setEnabled(enablePortPreferences);
return true;
};
preferencePort1en.setOnPreferenceChangeListener(onPreferenceChangeListenerPort1en);
onPreferenceChangeListenerPort1en.onPreferenceChange(preferencePort1en, settings.getBoolean(preferencePort1en.getKey(), false));
Preference.OnPreferenceChangeListener onPreferenceChangeListenerPort2en = (preference, value) -> {
preferencePort2en.setEnabled(enablePortPreferences);
preferencePort2wr.setEnabled(enablePortPreferences);
preferencePort2load.setEnabled(enablePortPreferences);
return true;
};
preferencePort2en.setOnPreferenceChangeListener(onPreferenceChangeListenerPort2en);
onPreferenceChangeListenerPort2en.onPreferenceChange(preferencePort2en, settings.getBoolean(preferencePort2en.getKey(), false));
updatePort2LoadFilename(settings.getString(preferencePort2load.getKey(), ""));
preferencePort2load.setOnPreferenceClickListener(preference -> {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
intent.putExtra(Intent.EXTRA_TITLE, "shared.bin");
Activity activity = getActivity();
if (activity != null)
activity.startActivityForResult(intent, MainActivity.INTENT_PORT2LOAD);
return true;
});
}
}
void updatePort2LoadFilename(String port2Filename) {
if(preferencePort2load != null) {
String displayName = port2Filename;
try {
displayName = Utils.getFileName(getActivity(), port2Filename);
} catch (Exception e) {
// Do nothing
}
preferencePort2load.setSummary(displayName);
}
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if(resultCode == Activity.RESULT_OK && data != null) {
if(requestCode == MainActivity.INTENT_PORT2LOAD) {
Uri uri = data.getData();
String url;
if (uri != null) {
if(debug) Log.d(TAG, "onActivityResult INTENT_PORT2LOAD " + uri.toString());
url = uri.toString();
settings.putString("settings_port2load", url);
makeUriPersistable(data, uri);
if(generalPreferenceFragment != null)
generalPreferenceFragment.updatePort2LoadFilename(url);
}
}
}
super.onActivityResult(requestCode, resultCode, data);
}
private void makeUriPersistable(Intent data, Uri uri) {
int takeFlags = data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
getContentResolver().takePersistableUriPermission(uri, takeFlags);
}
}

View file

@ -0,0 +1,343 @@
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
package org.emulator.forty.eight;
import android.app.Activity;
import android.app.Dialog;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.BlendMode;
import android.graphics.BlendModeColorFilter;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.text.InputType;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.EditText;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatDialog;
import androidx.appcompat.app.AppCompatDialogFragment;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.SeekBarPreference;
import org.emulator.calculator.EmuApplication;
import org.emulator.calculator.NativeLib;
import org.emulator.calculator.Settings;
import org.emulator.calculator.Utils;
import java.util.HashSet;
import java.util.Locale;
public class SettingsFragment extends AppCompatDialogFragment {
private static final String TAG = "SettingsFragment";
protected final boolean debug = false;
private static Settings settings;
private HashSet<String> settingsKeyChanged = new HashSet<>();
private Settings.OnOneKeyChangedListener sharedPreferenceChangeListener = (key) -> settingsKeyChanged.add(key);
private GeneralPreferenceFragment generalPreferenceFragment;
public interface OnSettingsKeyChangedListener {
void onSettingsKeyChanged(HashSet<String> settingsKeyChanged);
}
private OnSettingsKeyChangedListener onSettingsKeyChangedListener;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
settings = EmuApplication.getSettings();
settings.registerOnOneKeyChangeListener(sharedPreferenceChangeListener);
setStyle(AppCompatDialogFragment.STYLE_NO_FRAME, R.style.AppTheme);
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Dialog dialog = new AppCompatDialog(getContext(), getTheme()) {
@Override
public void onBackPressed() {
dialogResult();
dismiss();
}
};
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
return dialog;
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
String title = getString(settings.getIsDefaultSettings() ? R.string.dialog_common_settings_title : R.string.dialog_state_settings_title);
Dialog dialog = getDialog();
if(dialog != null)
dialog.setTitle(title);
View view = inflater.inflate(R.layout.fragment_settings, container, false);
// Configure the toolbar
Toolbar toolbar = view.findViewById(R.id.my_toolbar);
toolbar.setTitle(title);
Utils.colorizeDrawableWithColor(requireContext(), toolbar.getNavigationIcon(), android.R.attr.colorForeground);
toolbar.setNavigationOnClickListener(v -> {
dialogResult();
dismiss();
});
toolbar.inflateMenu(R.menu.fragment_settings);
Menu menu = toolbar.getMenu();
menu.findItem(R.id.menu_settings_reset_to_default).setEnabled(settings.getIsDefaultSettings());
menu.findItem(R.id.menu_settings_reset_to_common).setEnabled(!settings.getIsDefaultSettings());
toolbar.setOnMenuItemClickListener(item -> {
int id = item.getItemId();
if(id == R.id.menu_settings_reset_to_default) {
settings.clearCommonDefaultSettings();
restartPreferenceFragment();
} else if(id == R.id.menu_settings_reset_to_common) {
settings.clearEmbeddedStateSettings();
restartPreferenceFragment();
}
return true;
});
// Insert the Preference fragment
restartPreferenceFragment();
return view;
}
/**
* Start or restart the preference fragment to take into account the new settings.
*/
private void restartPreferenceFragment() {
if(generalPreferenceFragment != null)
getChildFragmentManager().beginTransaction().remove(generalPreferenceFragment).commit();
generalPreferenceFragment = new GeneralPreferenceFragment();
getChildFragmentManager().beginTransaction().replace(R.id.settingsContent, generalPreferenceFragment).commit();
}
/**
* Common method to handle the result of the dialog.
*/
private void dialogResult() {
if (onSettingsKeyChangedListener != null)
onSettingsKeyChangedListener.onSettingsKeyChanged(settingsKeyChanged);
}
@Override
public void onDestroy() {
settings.unregisterOnOneKeyChangeListener();
super.onDestroy();
}
void registerOnSettingsKeyChangedListener(OnSettingsKeyChangedListener listener) {
if(debug) Log.d(TAG, "registerOnSettingsKeyChangedListener()");
onSettingsKeyChangedListener = listener;
}
void unregisterOnSettingsKeyChangedListener() {
if(debug) Log.d(TAG, "unregisterOnSettingsKeyChangedListener()");
onSettingsKeyChangedListener = null;
}
public static class GeneralPreferenceFragment extends PreferenceFragmentCompat {
Preference preferencePort2load = null;
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
// Register our own settings data store
getPreferenceManager().setPreferenceDataStore(EmuApplication.getSettings());
// Load the preferences from an XML resource
setPreferencesFromResource(R.xml.pref_general, rootKey);
// Sound settings
SeekBarPreference preferenceSoundVolume = findPreference("settings_sound_volume");
if(preferenceSoundVolume != null) {
if(!NativeLib.getSoundEnabled()) {
preferenceSoundVolume.setSummary("Cannot initialize the sound engine.");
preferenceSoundVolume.setEnabled(false);
} else {
preferenceSoundVolume.setOnPreferenceClickListener(preference -> {
AlertDialog.Builder alert = new AlertDialog.Builder(requireContext());
alert.setTitle(R.string.settings_sound_volume_dialog_title);
final EditText input = new EditText(getContext());
input.setInputType(InputType.TYPE_CLASS_NUMBER);
input.setRawInputType(Configuration.KEYBOARD_12KEY);
input.setFocusable(true);
input.setText(String.format(Locale.US,"%d", preferenceSoundVolume.getValue()));
alert.setView(input);
alert.setPositiveButton(R.string.message_ok, (dialog, whichButton) -> {
String newValueText = input.getText().toString();
try {
int newValue = Integer.parseInt(newValueText);
if(newValue >= preferenceSoundVolume.getMin() && newValue <= preferenceSoundVolume.getMax())
preferenceSoundVolume.setValue(newValue);
} catch (NumberFormatException ignored) {}
});
alert.setNegativeButton(R.string.message_cancel, (dialog, whichButton) -> {});
alert.show();
return true;
});
}
}
// Background color settings
Preference preferenceBackgroundFallbackColor = findPreference("settings_background_fallback_color");
// final ColorPickerPreferenceCompat preferenceBackgroundCustomColor = (ColorPickerPreferenceCompat)findPreference("settings_background_custom_color");
if(preferenceBackgroundFallbackColor != null /*&& preferenceBackgroundCustomColor != null*/) {
final String[] stringArrayBackgroundFallbackColor = getResources().getStringArray(R.array.settings_background_fallback_color_item);
Preference.OnPreferenceChangeListener onPreferenceChangeListenerBackgroundFallbackColor = (preference, value) -> {
if(value != null) {
String stringValue = value.toString();
int backgroundFallbackColor = -1;
try {
backgroundFallbackColor = Integer.parseInt(stringValue);
} catch (NumberFormatException ignored) {}
if(backgroundFallbackColor >= 0 && backgroundFallbackColor < stringArrayBackgroundFallbackColor.length)
preference.setSummary(stringArrayBackgroundFallbackColor[backgroundFallbackColor]);
// preferenceBackgroundCustomColor.setEnabled(backgroundFallbackColor == 2);
}
return true;
};
preferenceBackgroundFallbackColor.setOnPreferenceChangeListener(onPreferenceChangeListenerBackgroundFallbackColor);
onPreferenceChangeListenerBackgroundFallbackColor.onPreferenceChange(preferenceBackgroundFallbackColor,
settings.getString(preferenceBackgroundFallbackColor.getKey(), "0"));
//preferenceBackgroundCustomColor.setColorValue(customColor);
// Preference.OnPreferenceChangeListener onPreferenceChangeListenerBackgroundCustomColor = new Preference.OnPreferenceChangeListener() {
// @Override
// public boolean onPreferenceChange(Preference preference, Object value) {
// if(value != null) {
// int customColor = (Integer)value;
// }
// return true;
// }
// };
// preferenceBackgroundCustomColor.setOnPreferenceChangeListener(onPreferenceChangeListenerBackgroundCustomColor);
// onPreferenceChangeListenerBackgroundCustomColor.onPreferenceChange(preferenceBackgroundCustomColor, sharedPreferences.getBoolean(preferenceBackgroundCustomColor.getKey(), false));
}
// Macro
Preference preferenceMacroRealSpeed = findPreference("settings_macro_real_speed");
Preference preferenceMacroManualSpeed = findPreference("settings_macro_manual_speed");
if(preferenceMacroRealSpeed != null && preferenceMacroManualSpeed != null) {
Preference.OnPreferenceChangeListener onPreferenceChangeListenerMacroRealSpeed = (preference, value) -> {
if(value != null)
preferenceMacroManualSpeed.setEnabled(!(Boolean) value);
return true;
};
preferenceMacroRealSpeed.setOnPreferenceChangeListener(onPreferenceChangeListenerMacroRealSpeed);
onPreferenceChangeListenerMacroRealSpeed.onPreferenceChange(preferenceMacroRealSpeed, settings.getBoolean(preferenceMacroRealSpeed.getKey(), true));
}
// Ports 1 & 2 settings
Preference preferencePort1en = findPreference("settings_port1en");
Preference preferencePort1wr = findPreference("settings_port1wr");
Preference preferencePort2en = findPreference("settings_port2en");
Preference preferencePort2wr = findPreference("settings_port2wr");
preferencePort2load = findPreference("settings_port2load");
if(preferencePort1en != null && preferencePort1wr != null
&& preferencePort2en != null && preferencePort2wr != null
&& preferencePort2load != null) {
boolean enablePortPreferences = NativeLib.isPortExtensionPossible();
Preference.OnPreferenceChangeListener onPreferenceChangeListenerPort1en = (preference, value) -> {
preferencePort1en.setEnabled(enablePortPreferences);
preferencePort1wr.setEnabled(enablePortPreferences);
return true;
};
preferencePort1en.setOnPreferenceChangeListener(onPreferenceChangeListenerPort1en);
onPreferenceChangeListenerPort1en.onPreferenceChange(preferencePort1en, settings.getBoolean(preferencePort1en.getKey(), false));
Preference.OnPreferenceChangeListener onPreferenceChangeListenerPort2en = (preference, value) -> {
preferencePort2en.setEnabled(enablePortPreferences);
preferencePort2wr.setEnabled(enablePortPreferences);
preferencePort2load.setEnabled(enablePortPreferences);
return true;
};
preferencePort2en.setOnPreferenceChangeListener(onPreferenceChangeListenerPort2en);
onPreferenceChangeListenerPort2en.onPreferenceChange(preferencePort2en, settings.getBoolean(preferencePort2en.getKey(), false));
updatePort2LoadFilename(settings.getString(preferencePort2load.getKey(), ""));
preferencePort2load.setOnPreferenceClickListener(preference -> {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
intent.putExtra(Intent.EXTRA_TITLE, "shared.bin");
Fragment parentFragment = getParentFragment();
if (parentFragment != null)
parentFragment.startActivityForResult(intent, MainActivity.INTENT_PORT2LOAD);
return true;
});
}
}
void updatePort2LoadFilename(String port2Filename) {
if(preferencePort2load != null) {
String displayName = port2Filename;
try {
displayName = Utils.getFileName(getActivity(), port2Filename);
} catch (Exception e) {
// Do nothing
}
preferencePort2load.setSummary(displayName);
}
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if(resultCode == Activity.RESULT_OK && data != null) {
if(requestCode == MainActivity.INTENT_PORT2LOAD) {
Uri uri = data.getData();
String url;
if (uri != null) {
if(debug) Log.d(TAG, "onActivityResult INTENT_PORT2LOAD " + uri.toString());
url = uri.toString();
settings.putString("settings_port2load", url);
Utils.makeUriPersistable(requireContext(), data, uri);
if(generalPreferenceFragment != null)
generalPreferenceFragment.updatePort2LoadFilename(url);
}
}
}
super.onActivityResult(requestCode, resultCode, data);
}
}

View file

@ -1,13 +0,0 @@
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<TextView
android:id="@+id/textViewInfo"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="5dp"
android:textIsSelectable="true" />
</ScrollView>

View file

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<WebView
android:id="@+id/webViewInfo"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -5,17 +5,6 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary" />
</com.google.android.material.appbar.AppBarLayout>
<include layout="@layout/content_main" /> <include layout="@layout/content_main" />
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/my_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
app:layout_constraintTop_toTopOf="parent"
app:navigationIcon="?android:attr/homeAsUpIndicator" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/my_toolbar" >
<TextView
android:id="@+id/textViewInfo"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="5dp"
android:textIsSelectable="true" />
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/my_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
app:layout_constraintTop_toTopOf="parent"
app:navigationIcon="?android:attr/homeAsUpIndicator" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/settingsContent"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/my_toolbar" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/my_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
app:layout_constraintTop_toTopOf="parent"
app:navigationIcon="?android:attr/homeAsUpIndicator" />
<WebView
android:id="@+id/webViewInfo"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/my_toolbar" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/menu_settings_reset_to_default"
android:title="@string/menu_settings_reset_to_default" />
<item
android:id="@+id/menu_settings_reset_to_common"
android:title="@string/menu_settings_reset_to_common" />
</menu>

View file

@ -1,17 +1,21 @@
<resources> <resources>
<!-- InfoActivity --> <!-- InfoFragment -->
<string name="title_activity_info">About</string> <string name="title_fragment_info">About</string>
<string name="info_readme">ReadMe.txt</string> <string name="info_readme">ReadMe.txt</string>
<!-- InfoWebActivity --> <!-- InfoWebFragment -->
<string name="title_web_activity_info">Help</string> <string name="title_web_fragment_info">Help</string>
<string name="help_url">file:///android_asset/Emu48.htm</string> <string name="help_url">file:///android_asset/Emu48.htm</string>
<!-- SettingsActivity --> <!-- SettingsFragment -->
<string name="title_activity_settings">Settings</string> <string name="dialog_common_settings_title">Common Settings</string>
<string name="dialog_state_settings_title">Settings (Saved in state file)</string>
<string name="menu_settings_reset_to_default">Reset to Default</string>
<string name="menu_settings_reset_to_common">Reset to Common</string>
<!-- MainActivity --> <!-- MainActivity -->
@ -129,7 +133,7 @@
<string name="settings_lcd_overlapping_mode_item_2">Manual (Zoom or pan)</string> <string name="settings_lcd_overlapping_mode_item_2">Manual (Zoom or pan)</string>
<string name="settings_lcd_pixel_borders_title">Show the LCD pixel borders</string> <string name="settings_lcd_pixel_borders_title">Show the LCD pixel borders</string>
<string name="settings_lcd_pixel_borders_summary">Experimental feature which show a more realistic pixel. Note: Due to the difference in screen resolution between Android and the calculator, the pixels are not necessarily uniform in size.</string> <string name="settings_lcd_pixel_borders_summary">Experimental feature which shows a more realistic pixel. Note: Due to the difference in screen resolution between Android and the calculator, the pixels are not necessarily uniform in size.</string>
<string name="settings_hide_bar_status">Hide the status bar</string> <string name="settings_hide_bar_status">Hide the status bar</string>
<string name="settings_hide_bar_nav">Hide the navigation bar</string> <string name="settings_hide_bar_nav">Hide the navigation bar</string>
@ -162,8 +166,6 @@
<string name="settings_printer_model_item_0">HP82240A</string> <string name="settings_printer_model_item_0">HP82240A</string>
<string name="settings_printer_model_item_1">HP82240B (default)</string> <string name="settings_printer_model_item_1">HP82240B (default)</string>
<string name="settings_printer_model_item_2">Generic Serial</string> <string name="settings_printer_model_item_2">Generic Serial</string>
<string name="settings_printer_prevent_line_wrap">Prevent Line Wrapping</string>
<string name="settings_printer_prevent_line_wrap_summary">Prevent the calc to wrap the line in the textual printer simulator</string>
<string name="settings_category_macro_title">Macro</string> <string name="settings_category_macro_title">Macro</string>
<string name="settings_macro_real_speed_title">Use Real Replay Speed</string> <string name="settings_macro_real_speed_title">Use Real Replay Speed</string>

View file

@ -121,12 +121,6 @@
android:entryValues="@array/settings_printer_model_value" android:entryValues="@array/settings_printer_model_value"
android:defaultValue="1" android:defaultValue="1"
/> />
<!--<SwitchPreference-->
<!--android:defaultValue="false"-->
<!--android:key="settings_printer_prevent_line_wrap"-->
<!--android:summary="@string/settings_printer_prevent_line_wrap_summary"-->
<!--android:title="@string/settings_printer_prevent_line_wrap"-->
<!--/>-->
</PreferenceCategory> </PreferenceCategory>

View file

@ -7,7 +7,7 @@ buildscript {
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.6.2' classpath 'com.android.tools.build:gradle:3.6.3'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong