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).
- Wrap the table of content in the former help documentation.
- 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)
@ -192,6 +194,8 @@ The Eric's Real scripts ("real*.kml" and "real*.bmp/png") are embedded in this a
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".
- The clock seems unsynchronized sometimes.
- Retain a key by right clicking if it is from a mouse.

View file

@ -28,11 +28,11 @@ if (keystorePropertiesFile.exists()) {
}
android {
compileSdkVersion 28
compileSdkVersion 29
defaultConfig {
applicationId "org.emulator.forty.eight"
minSdkVersion 19
targetSdkVersion 28
targetSdkVersion 29
versionCode 13
versionName "1.8beta2"
setProperty("archivesBaseName", "Emu48-v$versionName")
@ -80,13 +80,12 @@ android {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta3'
implementation 'androidx.preference:preference:1.1.0'
implementation 'com.google.android.material:material:1.2.0-alpha02'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.viewpager:viewpager:1.0.0'
implementation 'androidx.appcompat:appcompat:1.2.0-beta01'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.preference:preference:1.1.1'
implementation 'com.google.android.material:material:1.1.0'
implementation 'androidx.documentfile:documentfile:1.0.1'
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'
}

View file

@ -16,7 +16,8 @@
android:label="@string/app_name"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
android:launchMode="singleTop"
android:theme="@style/AppTheme.NoActionBar">
android:theme="@style/AppTheme.NoActionBar"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
@ -44,31 +45,6 @@
android:host="*"/>
</intent-filter>
</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
android:name="androidx.core.content.FileProvider"
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).
- Wrap the table of content in the former help documentation.
- 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)

View file

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

View file

@ -14,34 +14,65 @@
package org.emulator.calculator;
import android.app.Dialog;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.method.ScrollingMovementMethod;
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 androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatDialogFragment;
import androidx.appcompat.widget.Toolbar;
import java.io.IOException;
import java.io.InputStream;
import androidx.appcompat.app.AppCompatActivity;
public class InfoActivity extends AppCompatActivity {
private int homeId;
public class InfoFragment extends AppCompatDialogFragment {
@Override
protected void onCreate(Bundle savedInstanceState) {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStyle(AppCompatDialogFragment.STYLE_NO_FRAME, Utils.resId(this, "style", "AppTheme"));
}
setContentView(Utils.resId(this, "layout", "activity_info"));
String filepath = getString(Utils.resId(this, "string", "info_readme"));
homeId = Utils.resId(this, "id", "home");
@NonNull
@Override
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
// text view. Note that the text we are loading is ASCII, so we
// need to convert it to UTF-16.
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
// 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);
// 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.setText(text);
Linkify.addLinks(textViewInfo, Linkify.ALL);
} catch (IOException e) {
// Should never happen!
//throw new RuntimeException(e);
} catch (IOException ignored) { }
return view;
}
}
@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.MotionEvent;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class MainScreenView extends PanAndScaleView {
protected static final String TAG = "MainScreenView";
protected final boolean debug = false;
protected final boolean debug = true;
private Paint paintFullCalc = new Paint();
private Paint paintLCD = new Paint();
@ -45,6 +47,7 @@ public class MainScreenView extends PanAndScaleView {
private Rect srcBitmapCopy = new Rect();
private SparseIntArray vkmap;
private HashMap<Character, Integer> charmap;
private List<Integer> numpadKey;
private int kmlBackgroundColor = Color.BLACK;
private boolean useKmlBackgroundColor = false;
private int fallbackBackgroundColorType = 0;
@ -102,6 +105,10 @@ public class MainScreenView extends PanAndScaleView {
// vkmap.put(KeyEvent.KEYCODE_CTRL_RIGHT, 0x11); // VK_CONTROL
vkmap.put(KeyEvent.KEYCODE_ESCAPE, 0x1B); // VK_ESCAPE
vkmap.put(KeyEvent.KEYCODE_SPACE, 0x20); // VK_SPACE
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_RIGHT, 0x27); // VK_RIGHT
@ -166,6 +173,21 @@ public class MainScreenView extends PanAndScaleView {
vkmap.put(KeyEvent.KEYCODE_APOSTROPHE, 0xDE); // VK_OEM_7 ( »)
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.setFocusableInTouchMode(true);
}
@ -213,6 +235,8 @@ public class MainScreenView extends PanAndScaleView {
public boolean onKeyDown(int keyCode, KeyEvent event) {
if((event.getFlags() & KeyEvent.FLAG_VIRTUAL_HARD_KEY) == 0
&& (event.getSource() & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD) {
if(!event.isNumLockOn() && numpadKey.indexOf(keyCode) != -1)
return false;
char pressedKey = (char) event.getUnicodeChar();
Integer windowsKeycode = charmap.get(pressedKey);
if(windowsKeycode == null)
@ -232,6 +256,8 @@ public class MainScreenView extends PanAndScaleView {
public boolean onKeyUp(int keyCode, KeyEvent event) {
if((event.getFlags() & KeyEvent.FLAG_VIRTUAL_HARD_KEY) == 0
&& (event.getSource() & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD) {
if(!event.isNumLockOn() && numpadKey.indexOf(keyCode) != -1)
return false;
char pressedKey = (char) event.getUnicodeChar();
Integer windowsKeycode = charmap.get(pressedKey);
if(windowsKeycode == null)

View file

@ -70,14 +70,6 @@ public class PrinterSimulator {
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.
@ -252,8 +244,6 @@ public class PrinterSimulator {
// Text Printer
private boolean preventLineWrap = false;
/**
* ROMAN8 Unicode table
*/
@ -303,7 +293,7 @@ public class PrinterSimulator {
private void addTextData(int byData) {
do {
// special LF and LF characters
if (/*!preventLineWrap &&*/ byData == 0x04 || byData == 0x0A) {
if (byData == 0x04 || byData == 0x0A) {
textUpdate.append('\r');
textUpdate.append('\n');
if(debug) Log.d(TAG, "addTextData(" + byData + ")");

View file

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

View file

@ -14,6 +14,7 @@
package org.emulator.calculator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.net.Uri;
@ -33,226 +34,82 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Settings extends PreferenceDataStore {
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 SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener;
private boolean isCommonSettings = true;
public interface OnOneKeyChangedListener {
void onOneKeyChanged(String keyChanged);
}
private OnOneKeyChangedListener oneKeyChangedListener;
private static String magic = "MYHP";
private boolean isDefaultSettings = true;
public Settings(@NonNull SharedPreferences sharedPreferences) {
if(debug) Log.d(TAG, "Settings()");
this.defaultSettings = sharedPreferences;
androidSettings = sharedPreferences;
loadApplicationSettings();
}
public void registerOnSharedPreferenceChangeListener(SharedPreferences.OnSharedPreferenceChangeListener listener) {
if(debug) Log.d(TAG, "registerOnSharedPreferenceChangeListener()");
sharedPreferenceChangeListener = listener;
public void registerOnOneKeyChangeListener(OnOneKeyChangedListener listener) {
if(debug) Log.d(TAG, "registerOnOneKeyChangeListener()");
oneKeyChangedListener = listener;
}
public void unregisterOnSharedPreferenceChangeListener(SharedPreferences.OnSharedPreferenceChangeListener listener) {
if(debug) Log.d(TAG, "unregisterOnSharedPreferenceChangeListener()");
sharedPreferenceChangeListener = null;
public void unregisterOnOneKeyChangeListener() {
if(debug) Log.d(TAG, "unregisterOnOneKeyChangeListener()");
oneKeyChangedListener = null;
}
@Override
public void putString(String key, @Nullable String value) {
putString(key, value, false);
}
public void putString(String key, @Nullable String value, boolean forceDefault) {
if(debug) Log.d(TAG, (isDefaultSettings ? "DEFAULT" : "LOCAL") + " putString(key: '" + key + "' value: '" + value + "')");
if(isDefaultSettings || forceDefault) {
defaultSettings.edit().putString(key, value).apply();
if(sharedPreferenceChangeListener != null)
sharedPreferenceChangeListener.onSharedPreferenceChanged(defaultSettings, key);
} else {
private void putValue(String key, @Nullable Object value) {
if(applicationSettingKeys.indexOf(key) != -1)
applicationSettings.put(key, value);
else if(isCommonSettings)
commonSettings.put(key, value);
else
embeddedStateSettings.put(key, value);
if(sharedPreferenceChangeListener != null)
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, key);
}
if(oneKeyChangedListener != null)
oneKeyChangedListener.onOneKeyChanged(key);
}
@Override
public void putStringSet(String key, @Nullable Set<String> value) {
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);
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 void putInt(String key, int value) {
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);
}
public boolean getIsDefaultSettings() {
return isCommonSettings;
}
@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;
public void setIsDefaultSettings(boolean isDefaultSettings) {
this.isCommonSettings = isDefaultSettings;
}
private static String toJSON(Collection<String> array) {
@ -340,14 +197,23 @@ public class Settings extends PreferenceDataStore {
public void saveInStateFile(Context context, String url) {
if(debug) Log.d(TAG, "saveInStateFile(url: '" + url + "')");
// Consolidate common and embedded settings but without the app only settings
Map<String, Object> stateSettings = new HashMap<>();
stateSettings.putAll(commonSettings);
stateSettings.putAll(embeddedStateSettings);
String json = toJSON(stateSettings);
byte[] jsonBytes = json.getBytes(StandardCharsets.UTF_8);
int jsonLength = jsonBytes.length;
if(jsonLength < 65536) {
Uri uri = Uri.parse(url);
try {
ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(uri, "wa");
if(pfd != null) {
if (pfd != null) {
FileOutputStream fileOutputStream = new FileOutputStream(pfd.getFileDescriptor());
String json = toJSON(embeddedStateSettings);
byte[] jsonBytes = json.getBytes(StandardCharsets.UTF_8);
int jsonLength = jsonBytes.length;
fileOutputStream.write(jsonBytes);
// The JSON text should not be more than 64KB
fileOutputStream.write((jsonLength >> 8) & 0xFF);
@ -362,11 +228,18 @@ public class Settings extends PreferenceDataStore {
e.printStackTrace();
}
}
}
public void clearEmbeddedStateSettings() {
embeddedStateSettings.clear();
}
@SuppressLint("ApplySharedPref")
public void clearCommonDefaultSettings() {
embeddedStateSettings.clear();
commonSettings.clear();
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void loadFromStateFile(Context context, String url) {
if(debug) Log.d(TAG, "loadFromStateFile(url: '" + url + "')");
@ -383,7 +256,7 @@ public class Settings extends PreferenceDataStore {
fileInputStream.read(lastChunk, 0, lastChunk.length);
} else {
int lastChunkOffset = lastChunk.length - (int)fileSize;
fileInputStream.read(lastChunk, lastChunkOffset, lastChunk.length);
fileInputStream.read(lastChunk, lastChunkOffset, (int)fileSize);
}
fileInputStream.close();
@ -408,4 +281,154 @@ public class Settings extends PreferenceDataStore {
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.Intent;
import android.content.res.Resources;
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.os.Build;
import android.provider.OpenableColumns;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.Toast;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import javax.microedition.khronos.egl.EGL10;
@ -43,28 +50,48 @@ public class Utils {
toast.show();
}
static int resId(Context context, String resourcename, String variableName)
{
static int resId(Context context, String resourceName, String variableName) {
try {
return context.getResources().getIdentifier(variableName, resourcename, context.getApplicationContext().getPackageName());
return context.getResources().getIdentifier(variableName, resourceName, context.getApplicationContext().getPackageName());
} catch (Exception e) {
e.printStackTrace();
return -1;
}
}
static int resId(Fragment fragment, String resourcename, String variableName)
{
public static int resId(Fragment fragment, String resourceName, String variableName) {
try {
Context context = fragment.getContext();
if(context != null)
return fragment.getResources().getIdentifier(variableName, resourcename, context.getApplicationContext().getPackageName());
return fragment.getResources().getIdentifier(variableName, resourceName, context.getApplicationContext().getPackageName());
} catch (Exception e) {
e.printStackTrace();
}
return -1;
}
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);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)

View file

@ -21,7 +21,6 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
@ -32,7 +31,6 @@ import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.HapticFeedbackConstants;
import android.view.Menu;
import android.view.MenuItem;
@ -47,10 +45,8 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.FileProvider;
import androidx.core.view.GravityCompat;
import androidx.documentfile.provider.DocumentFile;
@ -59,8 +55,8 @@ import androidx.drawerlayout.widget.DrawerLayout;
import com.google.android.material.navigation.NavigationView;
import org.emulator.calculator.EmuApplication;
import org.emulator.calculator.InfoActivity;
import org.emulator.calculator.InfoWebActivity;
import org.emulator.calculator.InfoFragment;
import org.emulator.calculator.InfoWebFragment;
import org.emulator.calculator.LCDOverlappingView;
import org.emulator.calculator.MainScreenView;
import org.emulator.calculator.NativeLib;
@ -96,6 +92,7 @@ import java.util.regex.Pattern;
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
private static final String TAG = "MainActivity";
private boolean debug = false;
private Settings settings;
private NavigationView navigationView;
private DrawerLayout drawer;
@ -107,7 +104,6 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
public static final int INTENT_GETSAVEFILENAME = 2;
public static final int INTENT_OBJECT_LOAD = 3;
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_PICK_KML_FOLDER_FOR_NEW_FILE = 7;
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) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
toolbar.setVisibility(View.GONE);
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.setNavigationItemSelectedListener(this);
settings = EmuApplication.getSettings();
settings.setDefaultSettings(true);
settings.setIsDefaultSettings(true);
ViewGroup mainScreenContainer = findViewById(R.id.main_screen_container);
@ -193,7 +183,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
}
}
updateMRU();
updateFromPreferences(null);
updateFromPreferences(null, false);
updateNavigationDrawerItems();
@ -240,7 +230,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
Utils.makeUriPersistable(this, intent, documentToOpenUri);
}
} catch (Exception e) {
Log.e(TAG, e.getMessage());
if(debug) Log.e(TAG, e.getMessage());
}
else if(drawer != null)
drawer.openDrawer(GravityCompat.START);
@ -285,7 +275,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
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!
// -> Maybe by settings some flags when loading/saving
settings.putStringSet("MRU", mruLinkedHashMap.keySet(), true);
settings.putStringSet("MRU", mruLinkedHashMap.keySet());
if(lcdOverlappingView != null)
lcdOverlappingView.saveViewLayout();
@ -295,6 +285,8 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
onFileSave();
}
settings.saveApplicationSettings();
clearFolderCache();
super.onStop();
@ -534,7 +526,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
String url = file.getUri().toString();
String name = file.getName();
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)) {
calculatorsAssetFilenames.add(url);
}
@ -662,7 +654,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
private void newFileFromKML(String kmlScriptFilename) {
int result = NativeLib.onFileNew(kmlScriptFilename);
if(result > 0) {
settings.setDefaultSettings(false);
settings.setIsDefaultSettings(false);
settings.clearEmbeddedStateSettings();
showCalculatorView(true);
displayFilename("");
@ -731,7 +723,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
private void OnFileClose() {
ensureDocumentSaved(() -> {
NativeLib.onFileClose();
settings.setDefaultSettings(true);
settings.setIsDefaultSettings(true);
settings.clearEmbeddedStateSettings();
showCalculatorView(false);
saveLastDocument("");
@ -744,7 +736,32 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
}
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() {
@ -911,7 +928,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
}
} else if(which == lastIndex + 1) {
// Reset to default KML folder
settings.putBoolean("settings_kml_default", true, true);
settings.putBoolean("settings_kml_default", true);
updateFromPreferences("settings_kml", true);
if(changeKML)
OnViewScript();
@ -1002,41 +1019,15 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
}
private void OnTopics() {
startActivity(new Intent(this, InfoWebActivity.class));
new InfoWebFragment().show(getSupportFragmentManager(), "InfoWebFragment");
}
private void OnAbout() {
startActivity(new Intent(this, InfoActivity.class));
new InfoFragment().show(getSupportFragmentManager(), "InfoFragment");
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if(resultCode == Activity.RESULT_OK && data != null) {
if(requestCode == INTENT_SETTINGS) {
String[] changedKeys = data.getStringArrayExtra("changedKeys");
if(changedKeys != null) {
HashSet<String> changedKeysCleaned = new HashSet<>();
for (String key : changedKeys) {
//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);
}
}
} else {
Uri uri = data.getData();
String url = null;
if (uri != null)
@ -1044,7 +1035,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
if (url != null) {
switch (requestCode) {
case INTENT_GETOPENFILENAME: {
//Log.d(TAG, "onActivityResult INTENT_GETOPENFILENAME " + url);
if(debug) Log.d(TAG, "onActivityResult INTENT_GETOPENFILENAME " + url);
int openResult = onFileOpen(url);
if (openResult > 0) {
saveLastDocument(url);
@ -1062,7 +1053,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
break;
}
case INTENT_GETSAVEFILENAME: {
//Log.d(TAG, "onActivityResult INTENT_GETSAVEFILENAME " + url);
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);
@ -1075,12 +1066,12 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
break;
}
case INTENT_OBJECT_LOAD: {
//Log.d(TAG, "onActivityResult INTENT_OBJECT_LOAD " + url);
if(debug) Log.d(TAG, "onActivityResult INTENT_OBJECT_LOAD " + url);
NativeLib.onObjectLoad(url);
break;
}
case INTENT_OBJECT_SAVE: {
//Log.d(TAG, "onActivityResult INTENT_OBJECT_SAVE " + url);
if(debug) Log.d(TAG, "onActivityResult INTENT_OBJECT_SAVE " + url);
NativeLib.onObjectSave(url, null);
break;
}
@ -1088,9 +1079,9 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
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);
if(debug) Log.d(TAG, "onActivityResult INTENT_PICK_KML_FOLDER " + url);
settings.putBoolean("settings_kml_default", false);
settings.putString("settings_kml_folder", url);
updateFromPreferences("settings_kml", true);
Utils.makeUriPersistableReadOnly(this, data, uri);
@ -1114,7 +1105,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
break;
}
case INTENT_CREATE_RAM_CARD: {
//Log.d(TAG, "onActivityResult INTENT_CREATE_RAM_CARD " + url);
if(debug) Log.d(TAG, "onActivityResult INTENT_CREATE_RAM_CARD " + url);
if(selectedRAMSize > 0) {
int size = 2 * selectedRAMSize;
FileOutputStream fileOutputStream;
@ -1138,13 +1129,13 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
break;
}
case INTENT_MACRO_LOAD: {
//Log.d(TAG, "onActivityResult INTENT_MACRO_LOAD " + url);
if(debug) 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);
if(debug) Log.d(TAG, "onActivityResult INTENT_MACRO_SAVE " + url);
NativeLib.onToolMacroNew(url);
updateNavigationDrawerItems();
break;
@ -1154,13 +1145,12 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
}
}
}
}
fileSaveAsCallback = null;
super.onActivityResult(requestCode, resultCode, data);
}
private void saveLastDocument(String url) {
settings.putString("lastDocument", url, true);
settings.putString("lastDocument", url);
if(url != null && !url.isEmpty())
mruLinkedHashMap.put(url, null);
@ -1177,14 +1167,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
} else {
mainScreenView.setEnablePanAndScale(false);
mainScreenView.setVisibility(View.GONE);
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);
}
imageButtonMenu.setColorFilter(Utils.getThemedColor(this, android.R.attr.colorForeground));
}
}
@ -1192,18 +1175,18 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
// Eventually, close the previous state file
NativeLib.onFileClose();
settings.setDefaultSettings(true);
settings.setIsDefaultSettings(true);
showCalculatorView(false);
displayFilename("");
// Pre-Load the embedded settings from the end of the classic state file
settings.setDefaultSettings(false);
settings.setIsDefaultSettings(false);
settings.clearEmbeddedStateSettings();
settings.loadFromStateFile(this, url);
// Update the Emu VM with the new settings
updateFromPreferences(null);
updateFromPreferences(null, false);
// Load the genuine state file
int result = NativeLib.onFileOpen(url);
@ -1214,7 +1197,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
showKMLLog();
} else {
// Because it failed to load the state file, we switch to the default settings
settings.setDefaultSettings(true);
settings.setIsDefaultSettings(true);
settings.clearEmbeddedStateSettings();
showKMLLogForce();
}
@ -1564,23 +1547,18 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
updateFromPreferences("settings_port1", true);
}
private void updateFromPreferences(Set<String> keys) {
if(keys != null) {
for (String settingKey : keys)
updateFromPreferences(settingKey, false);
} else {
private void updateFromPreferences(String key, boolean isDynamic) {
int isDynamicValue = isDynamic ? 1 : 0;
if(key == null) {
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_printer_model", "settings_macro",
"settings_kml", "settings_port1", "settings_port2" };
for (String settingKey : settingKeys)
updateFromPreferences(settingKey, false);
}
}
private void updateFromPreferences(String key, boolean isDynamic) {
int isDynamicValue = isDynamic ? 1 : 0;
} else {
switch (key) {
case "settings_realspeed":
NativeLib.setConfiguration(key, isDynamicValue, settings.getBoolean(key, false) ? 1 : 0, 0, null);
@ -1627,7 +1605,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
case "settings_hide_bar":
case "settings_hide_bar_status":
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))
hideSystemUI();
else
@ -1663,15 +1641,12 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
// Catch bad number format
}
break;
case "settings_printer_prevent_line_wrap":
printerSimulator.setPreventLineWrap(settings.getBoolean("settings_printer_prevent_line_wrap", false));
break;
case "settings_kml":
case "settings_kml_default":
case "settings_kml_folder":
kmlFolderUseDefault = settings.getBoolean("settings_kml_default", true);
if(!kmlFolderUseDefault) {
if (!kmlFolderUseDefault) {
kmlFolderURL = settings.getString("settings_kml_folder", "");
// 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
@ -1707,6 +1682,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
break;
}
}
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {

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_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" />
</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>
<!-- InfoActivity -->
<!-- InfoFragment -->
<string name="title_activity_info">About</string>
<string name="title_fragment_info">About</string>
<string name="info_readme">ReadMe.txt</string>
<!-- InfoWebActivity -->
<string name="title_web_activity_info">Help</string>
<!-- InfoWebFragment -->
<string name="title_web_fragment_info">Help</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 -->
@ -129,7 +133,7 @@
<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_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_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_1">HP82240B (default)</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_macro_real_speed_title">Use Real Replay Speed</string>

View file

@ -121,12 +121,6 @@
android:entryValues="@array/settings_printer_model_value"
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>

View file

@ -7,7 +7,7 @@ buildscript {
jcenter()
}
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