For the HP49/50 port 2, it is now possible to load a new Flash ROM file (It fully replaces the ROM).

This commit is contained in:
dgis 2020-10-27 00:17:08 +01:00
parent c7608bbbed
commit f674c90993
14 changed files with 581 additions and 112 deletions

View file

@ -65,8 +65,9 @@ CHANGES
Version 1.91 (2020-10-XX)
- Fix transparency issue (RGB -> NGR).
- Fix transparency issue (RGB -> BGR).
- Fix a printer issue from Christoph Gießelink's HP82240B Printer Simulator version 1.12.
- For the HP49/50 port 2, it is now possible to load a new Flash ROM file (It fully replaces the ROM).
Version 1.9 (2020-09-07)
@ -212,6 +213,7 @@ The Eric's Real scripts ("real*.kml" and "real*.bmp/png") are embedded in this a
TODO
- Manage the HP 48 port 2 with the same kind of interface for the memory card.
- The render pixels are very nice. A solution to obtain uniform pixel size could be a preset (a multiplier, auto) so the user could decide and upscale/downscale (Michael P).
- Bug: In Xiaomi mi A3 under Android10, the haptic feedback does not work (add an intensity setting).
- Somehow LEFT (Shift on the keyboard) + 7 activates the DIVIDE-key (z-Key)..., but with the NUM-Key it can make it work without problems...

View file

@ -63,6 +63,13 @@ LINKS
CHANGES
Version 1.91 (2020-10-XX)
- Fix transparency issue (RGB -> BGR).
- Fix a printer issue from Christoph Gießelink's HP82240B Printer Simulator version 1.12.
- For the HP49/50 port 2, it is now possible to load a new Flash ROM file (It fully replaces the ROM).
Version 1.9 (2020-09-07)
- If the KML folder does not exist (like the first time), prompt the user to choose a new KML folder.

View file

@ -29,6 +29,7 @@ static jobject mainActivity = NULL;
jobject bitmapMainScreen = NULL;
AndroidBitmapInfo androidBitmapInfo;
enum DialogBoxMode currentDialogBoxMode;
LPBYTE pbyRomBackup;
enum ChooseKmlMode chooseCurrentKmlMode;
TCHAR szChosenCurrentKml[MAX_PATH];
TCHAR szKmlLog[10240];
@ -582,7 +583,14 @@ JNIEXPORT jint JNICALL Java_org_emulator_calculator_NativeLib_onFileOpen(JNIEnv
kmlFileNotFound = FALSE;
lastKMLFilename[0] = '\0';
// It is to open a new document, so we will trick the next KillKML() to prevent to UnmapROM()!!!
// pbyRom is then restored in the unused win32 GetKeyboardLayoutName(), just after KillKML() but before the final InitKML(). Crazy!
if (pbyRom) {
pbyRomBackup = pbyRom;
pbyRom = NULL;
}
BOOL result = OpenDocument(szBufferFilename);
if(pbyRomBackup) pbyRomBackup = NULL;
if (result) {
if(hLcdDC && hLcdDC->selectedBitmap) {
hLcdDC->selectedBitmap->bitmapInfoHeader->biHeight = -abs(hLcdDC->selectedBitmap->bitmapInfoHeader->biHeight);
@ -911,7 +919,14 @@ JNIEXPORT jint JNICALL Java_org_emulator_calculator_NativeLib_onViewScript(JNIEn
chooseCurrentKmlMode = ChooseKmlMode_CHANGE_KML;
// It is to open a new document, so we will trick the next KillKML() to prevent to UnmapROM()!!!
// pbyRom is then restored in the unused win32 GetKeyboardLayoutName(), just after KillKML() but before the final InitKML(). Crazy!
if (pbyRom) {
pbyRomBackup = pbyRom;
pbyRom = NULL;
}
BOOL bSucc = InitKML(szCurrentKml, FALSE);
if(pbyRomBackup) pbyRomBackup = NULL;
if(!bSucc) {
// restore KML script file name
@ -995,6 +1010,39 @@ JNIEXPORT void JNICALL Java_org_emulator_calculator_NativeLib_onToolMacroStop(JN
}
JNIEXPORT jboolean JNICALL Java_org_emulator_calculator_NativeLib_onLoadFlashROM(JNIEnv *env, jobject thisz, jstring filename) {
const char *filenameUTF8 = (*env)->GetStringUTFChars(env, filename , NULL);
if (bDocumentAvail)
SwitchToState(SM_INVALID);
UnmapRom();
BYTE cCurrentRomTypeBackup = cCurrentRomType;
cCurrentRomType = 'Q'; // Fake the model to allow to load the ROM in RW!
BOOL result = MapRom(filenameUTF8);
cCurrentRomType = cCurrentRomTypeBackup;
if(result) {
UpdatePatches(true); // Apply the patch again if needed (not tested!)
if (!CrcRom(&wRomCrc)) // build patched ROM fingerprint and check for unpacked data
result = FALSE;
if (result && bDocumentAvail) {
if (Chipset.wRomCrc != wRomCrc) // ROM changed
{
CpuReset();
Chipset.Shutdn = FALSE; // automatic restart
Chipset.wRomCrc = wRomCrc; // update current ROM fingerprint
}
if (pbyRom)
SwitchToState(SM_RUN); // continue emulation
}
}
// If failed to load,
// either it is when opening a new document, so it will fall back to the default Flash ROM of the KML script,
// or it is when loading a Flash ROM, so we have to manually load the default Flash ROM by resetting (loading the same KML script (OnViewScript))
// or it is when saving the Flash ROM, so we have to manually load the default Flash ROM by resetting (loading the same KML script (OnViewScript))
(*env)->ReleaseStringUTFChars(env, filename, filenameUTF8);
return result ? JNI_TRUE : JNI_FALSE;
}
JNIEXPORT void JNICALL Java_org_emulator_calculator_NativeLib_setConfiguration(JNIEnv *env, jobject thisz, jstring key, jint isDynamic, jint intValue1, jint intValue2, jstring stringValue) {
const char *configKey = (*env)->GetStringUTFChars(env, key, NULL) ;
const char *configStringValue = stringValue ? (*env)->GetStringUTFChars(env, stringValue, NULL) : NULL;

View file

@ -2948,6 +2948,9 @@ int MulDiv(int nNumber, int nNumerator, int nDenominator) {
BOOL GetKeyboardLayoutName(LPSTR pwszKLID) {
//TODO
//Trick to bypass UnmapROM in KillKML
if(pbyRom == NULL && pbyRomBackup)
pbyRom = pbyRomBackup;
return 0;
}

View file

@ -1218,6 +1218,7 @@ extern int showAlert(const TCHAR * messageText, int flags);
extern void sendMenuItemCommand(int menuItem);
extern TCHAR szCurrentKml[MAX_PATH];
extern TCHAR szChosenCurrentKml[MAX_PATH];
extern LPBYTE pbyRomBackup;
enum ChooseKmlMode {
ChooseKmlMode_UNKNOWN,
ChooseKmlMode_FILE_NEW,

View file

@ -78,6 +78,8 @@ public class NativeLib {
public static native void onToolMacroPlay(String filename);
public static native void onToolMacroStop();
public static native boolean onLoadFlashROM(String filename);
public static native void setConfiguration(String key, int isDynamic, int intValue1, int intValue2, String stringValue);
public static native boolean isPortExtensionPossible();
public static native int getState();

View file

@ -32,11 +32,14 @@ import android.os.ParcelFileDescriptor;
import android.util.Log;
import android.util.SparseArray;
import android.view.HapticFeedbackConstants;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
@ -51,7 +54,6 @@ import androidx.core.content.FileProvider;
import androidx.core.view.GravityCompat;
import androidx.documentfile.provider.DocumentFile;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.fragment.app.Fragment;
import com.google.android.material.navigation.NavigationView;
@ -68,10 +70,12 @@ import org.emulator.calculator.Utils;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
@ -93,7 +97,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 final boolean debug = false;
private Settings settings;
private NavigationView navigationView;
private DrawerLayout drawer;
@ -113,13 +117,15 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
public static final int INTENT_CREATE_RAM_CARD = 12;
public static final int INTENT_MACRO_LOAD = 13;
public static final int INTENT_MACRO_SAVE = 14;
public static final int INTENT_CREATE_FLASH_ROM = 15;
public static final int INTENT_LOAD_FLASH_ROM = 16;
public static String intentPickKmlFolderForUrlToOpen;
public String urlToOpenInIntentPort2Load;
public String kmlScriptFolderInIntentPort2Load;
private String kmlMimeType = "application/vnd.google-earth.kml+xml";
private final String kmlMimeType = "application/vnd.google-earth.kml+xml";
private boolean kmlFolderUseDefault = true;
private String kmlFolderURL = "";
private boolean kmlFolderChange = true;
@ -130,8 +136,8 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
private boolean[] objectsToSaveItemChecked = null;
// Most Recently Used state files
private int MRU_ID_START = 10000;
private int MAX_MRU = 5;
private final int MRU_ID_START = 10000;
private final int MAX_MRU = 5;
private LinkedHashMap<String, String> mruLinkedHashMap = new LinkedHashMap<String, String>(5, 1.0f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
@ -139,8 +145,8 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
}
};
private PrinterSimulator printerSimulator = new PrinterSimulator();
private PrinterSimulatorFragment fragmentPrinterSimulator = new PrinterSimulatorFragment();
private final PrinterSimulator printerSimulator = new PrinterSimulator();
private final PrinterSimulatorFragment fragmentPrinterSimulator = new PrinterSimulatorFragment();
private Bitmap bitmapIcon;
@ -207,7 +213,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
String documentToOpenUrl = settings.getString("lastDocument", "");
Uri documentToOpenUri = null;
Uri documentToOpenUri;
Intent intent = getIntent();
if(intent != null) {
String action = intent.getAction();
@ -235,7 +241,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
// FileOpen auto-open.
onFileOpen(documentToOpenUrl, intent, null);
} catch (Exception e) {
if(debug) Log.e(TAG, e.getMessage());
if(debug && e.getMessage() != null) Log.e(TAG, e.getMessage());
}
else if(drawer != null)
drawer.openDrawer(GravityCompat.START);
@ -378,6 +384,8 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
OnViewPrinter();
} else if (id == R.id.nav_create_ram_card) {
OnCreateRAMCard();
} else if (id == R.id.nav_manage_flash_rom) {
OnManageFlashROM();
} else if (id == R.id.nav_macro_record) {
OnMacroRecord();
} else if (id == R.id.nav_macro_play) {
@ -441,6 +449,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
menu.findItem(R.id.nav_backup_restore).setEnabled(uRun && isBackup);
menu.findItem(R.id.nav_backup_delete).setEnabled(uRun && isBackup);
menu.findItem(R.id.nav_change_kml_script).setEnabled(uRun);
menu.findItem(R.id.nav_manage_flash_rom).setEnabled(uRun && (cCurrentRomType == 'X' || cCurrentRomType == 'Q'));
menu.findItem(R.id.nav_macro_record).setEnabled(uRun && nMacroState == 0 /* MACRO_OFF */);
menu.findItem(R.id.nav_macro_play).setEnabled(uRun && nMacroState == 0 /* MACRO_OFF */);
menu.findItem(R.id.nav_macro_stop).setEnabled(uRun && nMacroState != 0 /* MACRO_OFF */);
@ -502,9 +511,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
Matcher m;
for (String calculatorFilename : calculatorsAssetFilenames) {
if (calculatorFilename.toLowerCase().lastIndexOf(".kml") != -1) {
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(assetManager.open("calculators/" + calculatorFilename), StandardCharsets.UTF_8));
try (BufferedReader reader = new BufferedReader(new InputStreamReader(assetManager.open("calculators/" + calculatorFilename), StandardCharsets.UTF_8))) {
// do reading, usually loop until end of file reading
String mLine;
boolean inGlobal = false;
@ -542,14 +549,6 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
} catch (IOException e) {
//log the exception
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
//log the exception
}
}
}
}
}
@ -996,6 +995,8 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
// We choose a calculator name from the list.
KMLScriptItem scriptItem = kmlScriptsForCurrentModel.get(which);
if(changeKML) {
// Optionally load the flash ROM for HP49
updateFromPreferences("settings_flash_port2", true);
// We only change the KML script here.
int result = NativeLib.onViewScript(scriptItem.filename, scriptItem.folder);
if(result > 0) {
@ -1057,6 +1058,86 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
}).show();
}
private void OnManageFlashROM() {
//TODO Before loading a new flash, save the current flash ROM by closing the current Document and then opening it again!
// -> IT IS POSSIBLE WITH UNMAPROM/MAPROM
// -> Already done for Create because an unmap is done before (in KillKML)
// -> Already done for Load because an unmap is done before (in KillKML)
// -> Already done for Load because an unmap is done before (in KillKML)
// For Close OK
// For Save / Save As... ?
String currentFlashPort2Url = settings.getString("settings_flash_port2", null);
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(MainActivity.this);
LayoutInflater inflater = this.getLayoutInflater();
View dialogView = inflater.inflate(R.layout.alert_manage_flash_rom, null);
Button buttonFlashROMCreate = dialogView.findViewById(R.id.buttonFlashROMCreate);
Button buttonFlashROMLoad = dialogView.findViewById(R.id.buttonFlashROMLoad);
Button buttonFlashROMReset = dialogView.findViewById(R.id.buttonFlashROMReset);
Button buttonFlashROMCancel = dialogView.findViewById(R.id.buttonFlashROMCancel);
dialogBuilder.setView(dialogView);
dialogBuilder.setTitle(getResources().getString(R.string.nav_manage_flash_rom));
AlertDialog alertDialog = dialogBuilder.show();
buttonFlashROMCreate.setOnClickListener(v -> {
// Save the current Flash ROM
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
intent.putExtra(Intent.EXTRA_TITLE, "FlashROM.49g");
saveWhenLaunchingActivity = false;
startActivityForResult(intent, INTENT_CREATE_FLASH_ROM);
alertDialog.dismiss();
});
buttonFlashROMLoad.setOnClickListener(v -> {
Runnable flashROMLoad = () -> {
// Load a Flash ROM
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
intent.putExtra(Intent.EXTRA_TITLE, "rom.49g");
saveWhenLaunchingActivity = false;
startActivityForResult(intent, INTENT_LOAD_FLASH_ROM);
};
if(currentFlashPort2Url != null && currentFlashPort2Url.length() > 0)
new AlertDialog.Builder(this)
.setTitle(getString(R.string.alert_losing_flash_rom_title))
.setMessage(getString(R.string.alert_losing_flash_rom_message))
.setPositiveButton(android.R.string.yes, (dialog, which) -> flashROMLoad.run())
.setNegativeButton(android.R.string.no, (dialog, which) -> {})
.show();
else
flashROMLoad.run();
alertDialog.dismiss();
});
buttonFlashROMReset.setOnClickListener(v -> {
resetToDefaultKMLFlashROM();
alertDialog.dismiss();
});
buttonFlashROMCancel.setOnClickListener(v -> {
// Cancel the alert dialog.
alertDialog.dismiss();
});
}
private void resetToDefaultKMLFlashROM() {
// Reset to default Flash ROM from the KML file
settings.putString("settings_flash_port2", null);
String kmlFilename = NativeLib.getCurrentKml();
if(kmlFilename.length() > 0) {
String kmlFolder = kmlFolderURL == null || kmlFolderURL.length() == 0 ? null : kmlFolderURL;
// Reset the flashROM
NativeLib.onLoadFlashROM("");
// Load the KML file again. TODO If it goes wrong, we are lost.
int result = NativeLib.onViewScript(kmlFilename, kmlFolder);
if (result > 0) {
displayKMLTitle();
displayFilename(NativeLib.getCurrentFilename());
//showKMLLog();
} else
showKMLLogForce();
updateNavigationDrawerItems();
}
}
private void OnMacroRecord() {
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
@ -1108,10 +1189,18 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
if(debug) Log.d(TAG, "onActivityResult INTENT_GETSAVEFILENAME " + url);
if (NativeLib.onFileSaveAs(url) != 0) {
settings.saveInStateFile(this, url);
// Save and reload the flash ROM!
updateFromPreferences("settings_flash_port2", true);
displayFilename(url);
String settingsFlashPort2Url = settings.getString("settings_flash_port2", null);
if(settingsFlashPort2Url != null && settingsFlashPort2Url.length() > 0)
showAlert(String.format(Locale.US, getString(R.string.message_state_and_flash_saved), getFilenameFromURL(url), getFilenameFromURL(settingsFlashPort2Url)));
else
showAlert(String.format(Locale.US, getString(R.string.message_state_saved), getFilenameFromURL(url)));
saveLastDocument(url);
Utils.makeUriPersistable(this, data, uri);
displayFilename(url);
showAlert(String.format(Locale.US, getString(R.string.message_state_saved), getFilenameFromURL(url)));
if (fileSaveAsCallback != null)
fileSaveAsCallback.run();
}
@ -1205,6 +1294,31 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
NativeLib.onToolMacroNew(url);
updateNavigationDrawerItems();
break;
}
case INTENT_CREATE_FLASH_ROM: {
if(debug) Log.d(TAG, "onActivityResult INTENT_CREATE_FLASH_ROM " + url);
// Not possible to save the current FlashROM :-( [NativeLib.onSaveFlashROM(url);]
// So, we are going to create a new one from the ROM loaded by the current KML script!
String kmlFilename = NativeLib.getCurrentKml();
if(kmlFilename != null && kmlFilename.length() > 0) {
if(kmlFolderURL != null && kmlFolderURL.length() > 0)
copyROMFromFolder(kmlFilename, uri);
else
copyROMFromAsset(kmlFilename, uri);
}
new AlertDialog.Builder(this)
.setTitle(getString(R.string.alert_load_new_flash_rom_title))
.setPositiveButton(R.string.message_yes, (dialog, which) ->
loadFlashROM(uri, data))
.setNegativeButton(R.string.message_no, (dialog, which) -> {})
.show();
break;
}
case INTENT_LOAD_FLASH_ROM: {
if(debug) Log.d(TAG, "onActivityResult INTENT_LOAD_FLASH_ROM " + url);
loadFlashROM(uri, data);
break;
}
default:
break;
@ -1220,6 +1334,131 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
super.onActivityResult(requestCode, resultCode, data);
}
private void loadFlashROM(Uri uri, Intent intent) {
String url = uri != null ? uri.toString() : "";
if(NativeLib.onLoadFlashROM(url)) {
if (intent != null)
Utils.makeUriPersistable(this, intent, uri);
settings.putString("settings_flash_port2", url);
} else {
// The load failed, reset to the default Flash ROM
new AlertDialog.Builder(this)
.setTitle(getString(R.string.alert_load_flash_rom_error_title))
.setMessage(getString(R.string.alert_load_flash_rom_error_message))
.setPositiveButton(android.R.string.ok, (dialog, which) -> {})
.show();
resetToDefaultKMLFlashROM();
}
displayFilename(NativeLib.getCurrentFilename());
}
private String extractROMFilename(InputStream inputStream) {
String romFilename = null;
if(inputStream != null) {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
// do reading, usually loop until end of file reading
Pattern patternGlobalROM = Pattern.compile("\\s*Rom\\s+\"(.*)\"");
String mLine;
boolean inGlobal = false;
while ((mLine = reader.readLine()) != null) {
//process line
if (mLine.indexOf("Global") == 0) {
inGlobal = true;
romFilename = null;
continue;
}
if (inGlobal) {
if (mLine.indexOf("End") == 0) {
break;
}
Matcher m = patternGlobalROM.matcher(mLine);
if (m.find()) {
romFilename = m.group(1);
break;
}
}
}
} catch (IOException e) {
//log the exception
e.printStackTrace();
}
//log the exception
}
return romFilename;
}
private void copyROMFromFolder(String kmlFilename, Uri uri) {
Uri kmlFolderUri = Uri.parse(kmlFolderURL);
DocumentFile kmlFolderDocumentFile = DocumentFile.fromTreeUri(this, kmlFolderUri);
if(kmlFolderDocumentFile != null) {
String romFilename = null;
for (DocumentFile file : kmlFolderDocumentFile.listFiles()) {
String name = file.getName();
if (name != null && name.compareTo(kmlFilename) == 0) {
try {
DocumentFile documentFile = DocumentFile.fromSingleUri(this, file.getUri());
if (documentFile != null) {
Uri fileUri = documentFile.getUri();
romFilename = extractROMFilename(getContentResolver().openInputStream(fileUri));
}
} catch (IOException e) {
//log the exception
e.printStackTrace();
}
}
}
if(romFilename != null && romFilename.length() > 0) {
ParcelFileDescriptor pfdROM = openFileInFolderFromContentResolverPFD(romFilename, kmlFolderURL, GENERIC_READ);
if(pfdROM != null) {
try {
InputStream romInputStream = new FileInputStream(pfdROM.getFileDescriptor());
copyROMtoFlashROM(romInputStream, uri);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
pfdROM.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}
private void copyROMFromAsset(String kmlFilename, Uri uri) {
AssetManager assetManager = getAssets();
InputStream kmlInputStream;
try {
kmlInputStream = assetManager.open("calculators/" + kmlFilename);
String romFilename = extractROMFilename(kmlInputStream);
if(romFilename != null && romFilename.length() > 0) {
InputStream romInputStream = assetManager.open("calculators/" + romFilename);
copyROMtoFlashROM(romInputStream, uri);
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void copyROMtoFlashROM(InputStream romInputStream, Uri uri) throws IOException {
ParcelFileDescriptor pfd = getContentResolver().openFileDescriptor(uri, "w");
if (pfd != null) {
OutputStream flashROMOutputStream = new FileOutputStream(pfd.getFileDescriptor());
byte[] buffer = new byte[1024 * 1024];
int length;
while ((length = romInputStream.read(buffer)) > 0) {
flashROMOutputStream.write(buffer, 0, length);
}
flashROMOutputStream.flush();
flashROMOutputStream.close();
romInputStream.close();
pfd.close();
}
}
private void saveLastDocument(String url) {
settings.putString("lastDocument", url);
@ -1259,7 +1498,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
// Make this file persistable to allow a next access to this same file.
Utils.makeUriPersistable(this, intent, Uri.parse(url));
String kmlScriptFolder = null;
String kmlScriptFolder;
String embeddedKMLScriptFolder = settings.getString("settings_kml_folder_embedded", null);
if(openWithKMLScriptFolder != null)
@ -1282,16 +1521,14 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
Uri port2Uri = Uri.parse(port2Url);
DocumentFile port2DocumentFile = DocumentFile.fromSingleUri(this, port2Uri);
if (port2DocumentFile == null || !port2DocumentFile.exists()) {
//showAlert("Cannot access to the port 2 file!");
String port2Filename = getFilenameFromURL(port2Url);
String finalKmlScriptFolder = kmlScriptFolder;
new AlertDialog.Builder(this)
.setTitle(getString(R.string.message_open_port2_file_not_found))
.setMessage(String.format(Locale.US, getString(R.string.message_open_port2_file_not_found_description), port2Filename))
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
urlToOpenInIntentPort2Load = url;
kmlScriptFolderInIntentPort2Load = finalKmlScriptFolder;
kmlScriptFolderInIntentPort2Load = kmlScriptFolder;
Intent intentPort2 = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intentPort2.addCategory(Intent.CATEGORY_OPENABLE);
@ -1302,7 +1539,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
}).setNegativeButton(android.R.string.cancel, (dialog, which) -> {
// Deactivate the port2 because it is not reachable.
settings.putBoolean("settings_port2en", false);
onFileOpenNative(url, finalKmlScriptFolder);
onFileOpenNative(url, kmlScriptFolder);
}).show();
return;
}
@ -1362,9 +1599,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
intentPickKmlFolderForUrlToOpen = url;
saveWhenLaunchingActivity = false;
startActivityForResult(new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE), INTENT_PICK_KML_FOLDER_FOR_SECURITY);
}).setNegativeButton(android.R.string.cancel, (dialog, which) -> {
removeMRU(url);
}).show();
}).setNegativeButton(android.R.string.cancel, (dialog, which) -> removeMRU(url)).show();
else if(result == -3 || kmlScriptFolder == null) {
// KML file (or a compatible KML file) has not been found, you must select the folder where are the KML and ROM files and then, reopen this file!
String currentKml = NativeLib.getCurrentKml();
@ -1383,9 +1618,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
intentPickKmlFolderForUrlToOpen = url;
saveWhenLaunchingActivity = false;
startActivityForResult(new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE), INTENT_PICK_KML_FOLDER_FOR_KML_NOT_FOUND);
}).setNegativeButton(android.R.string.cancel, (dialog, which) -> {
removeMRU(url);
}).show();
}).setNegativeButton(android.R.string.cancel, (dialog, which) -> removeMRU(url)).show();
} else
removeMRU(url);
} else {
@ -1401,13 +1634,23 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
if (NativeLib.onFileSave() == 1) {
String currentFilenameUrl = NativeLib.getCurrentFilename();
settings.saveInStateFile(this, currentFilenameUrl);
String currentFilename = getFilenameFromURL(currentFilenameUrl);
showAlert(String.format(Locale.US, getString(R.string.message_state_saved), currentFilename));
// Save and reload the flash ROM!
updateFromPreferences("settings_flash_port2", true);
String settingsFlashPort2Url = settings.getString("settings_flash_port2", null);
if(settingsFlashPort2Url != null && settingsFlashPort2Url.length() > 0)
showAlert(String.format(Locale.US, getString(R.string.message_state_and_flash_saved), getFilenameFromURL(currentFilenameUrl), getFilenameFromURL(settingsFlashPort2Url)));
else
showAlert(String.format(Locale.US, getString(R.string.message_state_saved), getFilenameFromURL(currentFilenameUrl)));
}
}
private void displayFilename(String url) {
String displayName = getFilenameFromURL(url);
private void displayFilename(String stateFileURL) {
String displayName = getFilenameFromURL(stateFileURL == null ? "" : stateFileURL);
String port2FileURL = settings.getString("settings_flash_port2", null);
if(port2FileURL != null && port2FileURL.length() > 0)
displayName += " " + getFilenameFromURL(port2FileURL);
View headerView = displayKMLTitle();
if(headerView != null) {
TextView textViewSubtitle = headerView.findViewById(R.id.nav_header_subtitle);
@ -1472,7 +1715,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
// Method used from JNI!
@SuppressWarnings("unused")
@SuppressWarnings("UnusedDeclaration")
public int updateCallback(int type, int param1, int param2, String param3, String param4) {
mainScreenView.updateCallback(type, param1, param2, param3, param4);
@ -1518,7 +1761,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
folderCache.clear();
}
@SuppressWarnings("unused")
@SuppressWarnings("UnusedDeclaration")
public int openFileInFolderFromContentResolver(String filename, String folderURL, int writeAccess) {
if(filename != null) {
if(filename.startsWith("content://"))
@ -1539,7 +1782,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
return -1;
}
@SuppressWarnings("unused")
@SuppressWarnings("UnusedDeclaration")
public int closeFileFromContentResolver(int fd) {
if(parcelFileDescriptorPerFd != null) {
ParcelFileDescriptor filePfd = parcelFileDescriptorPerFd.get(fd);
@ -1556,6 +1799,43 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
return -1;
}
public ParcelFileDescriptor openFileFromContentResolverPFD(String fileURL, int writeAccess) {
//https://stackoverflow.com/a/31677287
Uri uri = Uri.parse(fileURL);
ParcelFileDescriptor filePfd;
try {
String mode = "";
if((writeAccess & GENERIC_READ) == GENERIC_READ)
mode += "r";
if((writeAccess & GENERIC_WRITE) == GENERIC_WRITE)
mode += "w";
filePfd = getContentResolver().openFileDescriptor(uri, mode);
} catch (Exception e) {
e.printStackTrace();
return null;
}
return filePfd;
}
public ParcelFileDescriptor openFileInFolderFromContentResolverPFD(String filename, String folderURL, int writeAccess) {
if(filename != null) {
if(filename.startsWith("content://"))
return openFileFromContentResolverPFD(filename, writeAccess);
if (folderURLCached == null || !folderURLCached.equals(folderURL)) {
folderURLCached = folderURL;
folderCache.clear();
Uri folderURI = Uri.parse(folderURL);
DocumentFile folderDocumentFile = DocumentFile.fromTreeUri(this, folderURI);
if (folderDocumentFile != null)
for (DocumentFile file : folderDocumentFile.listFiles())
folderCache.put(file.getName(), file.getUri().toString());
}
String filenameUrl = folderCache.get(filename);
if (filenameUrl != null)
return openFileFromContentResolverPFD(filenameUrl, writeAccess);
}
return null;
}
public void showAlert(String text) {
showAlert(text, false);
}
@ -1564,7 +1844,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
Utils.showAlert(this, text, lengthLong);
}
@SuppressWarnings("unused")
@SuppressWarnings("UnusedDeclaration")
public void sendMenuItemCommand(int menuItem) {
switch (menuItem) {
case 1: // FILE_NEW
@ -1643,7 +1923,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
}
}
@SuppressWarnings("unused")
@SuppressWarnings("UnusedDeclaration")
public int getFirstKMLFilenameForType(char chipsetType) {
if(!kmlFolderUseDefault) {
extractKMLScripts();
@ -1678,7 +1958,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
return 0;
}
@SuppressWarnings("unused")
@SuppressWarnings("UnusedDeclaration")
public void clipboardCopyText(String text) {
// Gets a handle to the clipboard service.
ClipboardManager clipboard = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);
@ -1688,7 +1968,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
clipboard.setPrimaryClip(clip);
}
}
@SuppressWarnings("unused")
@SuppressWarnings("UnusedDeclaration")
public String clipboardPasteText() {
ClipboardManager clipboard = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);
if (clipboard != null && clipboard.hasPrimaryClip()) {
@ -1704,18 +1984,18 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
return "";
}
@SuppressWarnings("unused")
@SuppressWarnings("UnusedDeclaration")
public void performHapticFeedback() {
if(settings.getBoolean("settings_haptic_feedback", true))
mainScreenView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
}
@SuppressWarnings("unused")
@SuppressWarnings("UnusedDeclaration")
public void sendByteUdp(int byteSent) {
printerSimulator.write(byteSent);
}
@SuppressWarnings("unused")
@SuppressWarnings("UnusedDeclaration")
public synchronized void setKMLIcon(int imageWidth, int imageHeight, byte[] pixels) {
if(imageWidth > 0 && imageHeight > 0 && pixels != null) {
try {
@ -1765,14 +2045,13 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
"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_macro",
"settings_kml", "settings_port1", "settings_port2" };
"settings_kml", "settings_port1", "settings_port2",
"settings_flash_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;
@ -1883,6 +2162,11 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
settings.getBoolean("settings_port2wr", false) ? 1 : 0,
settings.getString("settings_port2load", ""));
break;
case "settings_flash_port2":
String settingsFlashPort2Url = settings.getString("settings_flash_port2", null);
if(settingsFlashPort2Url != null)
loadFlashROM(Uri.parse(settingsFlashPort2Url), null);
break;
}
}
}

View file

@ -59,8 +59,8 @@ public class SettingsFragment extends AppCompatDialogFragment {
protected final boolean debug = false;
private static Settings settings;
private HashSet<String> settingsKeyChanged = new HashSet<>();
private Settings.OnOneKeyChangedListener sharedPreferenceChangeListener = (key) -> settingsKeyChanged.add(key);
private final HashSet<String> settingsKeyChanged = new HashSet<>();
private final Settings.OnOneKeyChangedListener sharedPreferenceChangeListener = settingsKeyChanged::add;
private GeneralPreferenceFragment generalPreferenceFragment;
public interface OnSettingsKeyChangedListener {
@ -307,6 +307,32 @@ public class SettingsFragment extends AppCompatDialogFragment {
return true;
});
}
// Port 2 flash (HP49 / HP50)
Preference preferenceFlashPort2 = findPreference("settings_flash_port2");
if(preferenceFlashPort2 != null) {
String flashPort2Filename = settings.getString(preferenceFlashPort2.getKey(), "");
String displayName = flashPort2Filename;
try {
displayName = Utils.getFileName(getActivity(), flashPort2Filename);
} catch (Exception e) {
// Do nothing
}
preferenceFlashPort2.setSummary(displayName);
//TODO offer the possibility to manage the Flash from here too.
// preferenceFlashPort2.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) {

View file

@ -0,0 +1,72 @@
<?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">
<TextView
android:id="@+id/textViewDescription"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:ems="10"
android:maxLines="10"
android:text="@string/alert_manage_flash_rom_description"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/buttonFlashROMCreate"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:text="@string/alert_manage_flash_rom_button_create"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textViewDescription" />
<Button
android:id="@+id/buttonFlashROMLoad"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:text="@string/alert_manage_flash_rom_button_load"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/buttonFlashROMCreate" />
<Button
android:id="@+id/buttonFlashROMReset"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:text="@string/alert_manage_flash_rom_button_reset"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/buttonFlashROMLoad" />
<Button
android:id="@+id/buttonFlashROMCancel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:text="@string/alert_manage_flash_rom_button_cancel"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/buttonFlashROMReset" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -104,6 +104,10 @@
android:id="@+id/nav_create_ram_card"
android:icon="@drawable/ic_memory_black_24dp"
android:title="@string/nav_create_ram_card" />
<item
android:id="@+id/nav_manage_flash_rom"
android:icon="@drawable/ic_description_black_24dp"
android:title="@string/nav_manage_flash_rom" />
<item
android:id="@+id/nav_macro_record"
android:icon="@drawable/ic_fiber_manual_record_black_24dp"

View file

@ -48,6 +48,7 @@
<string name="nav_show_printer">Show the Printer Simulator...</string>
<string name="nav_tool">Tool</string>
<string name="nav_create_ram_card">Create RAM Card...</string>
<string name="nav_manage_flash_rom">Manage Flash ROM (HP49)...</string>
<string name="nav_macro_record">Record Macro...</string>
<string name="nav_macro_play">Play Macro...</string>
<string name="nav_macro_stop">Macro Stop</string>
@ -76,6 +77,18 @@
<string name="ram_cards_item_4">1mb (8 ports: 2 through 9)</string>
<string name="ram_cards_item_5">2mb (16 ports: 2 through 17)</string>
<string name="ram_cards_item_6">4mb (32 ports: 2 through 33)</string>
<string name="alert_manage_flash_rom_description">The following actions may reset the calculator without losing the memory.</string>
<string name="alert_manage_flash_rom_button_create">Create Flash ROM file from current KML...</string>
<string name="alert_manage_flash_rom_button_load">Load Flash ROM file...</string>
<string name="alert_manage_flash_rom_button_reset">Reset Flash ROM to default KML settings</string>
<string name="alert_manage_flash_rom_button_cancel">Cancel</string>
<string name="alert_losing_flash_rom_title">Are you sure?</string>
<string name="alert_losing_flash_rom_message">You already have a loaded flash ROM file. Are you sure to replace its?</string>
<string name="alert_load_new_flash_rom_title">Load New Flash ROM?</string>
<string name="alert_load_flash_rom_error_title">Error when Loading Flash ROM</string>
<string name="alert_load_flash_rom_error_message">Cannot load the Flash ROM. Reset to the default Flash ROM (from the current KML script). Please, try to load it again from the menu.</string>
<string name="message_open_security">Permission Denied</string>
<string name="message_open_security_description">For security reason, you must select the folder where are the KML and ROM files, please.</string>
<string name="message_open_security_retry">Please, open again</string>
@ -87,11 +100,12 @@
<string name="message_open_port2_file_not_found">Port 2 File not Found</string>
<string name="message_open_port2_file_not_found_description">The port 2 file "%s" cannot be found. You should select it again, please.</string>
<string name="message_state_saved">State saved in %s</string>
<string name="message_state_and_flash_saved">State %s and flash %s saved</string>
<string name="message_do_you_want_to_save">Do you want to save changes?\n(BACK to cancel)</string>
<string name="message_save_new_file">Do you want to save this new state file?\n(To avoid losing the state of the machine)</string>
<string name="message_yes">Yes</string>
<string name="message_no">No</string>
<string name="message_ok">Ok</string>
<string name="message_ok">OK</string>
<string name="message_cancel">Cancel</string>
<string name="message_object_load">Warning: Trying to load an object while the emulator is busy will certainly result in a memory lost. Before loading an object you should be sure that the calculator is in idle state. Do you want to see this warning next time you try to load an object?</string>
<string name="message_object_save_program">Select Program</string>
@ -165,6 +179,7 @@
<string name="settings_port2en_title">Port 2 is Plugged</string>
<string name="settings_port2wr_title">Port 2 is Writable</string>
<string name="settings_port2load_title">Port 2 File</string>
<string name="settings_flash_port2_title">Port 2 Flash File</string>
<string name="settings_category_printer_title">Printer Simulator</string>
<string name="settings_printer_model_title">Printer Model</string>

View file

@ -170,6 +170,11 @@
android:title="@string/settings_port2load_title"
android:summary=""
/>
<Preference
android:key="settings_flash_port2"
android:title="@string/settings_flash_port2_title"
android:summary=""
/>
</PreferenceCategory>
<!-- Peripheral -->

View file

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

View file

@ -1,6 +1,6 @@
#Mon Jun 01 16:14:55 CEST 2020
#Tue Oct 13 08:57:24 CEST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip