From d27d777153fa4d640e55e0edf56c8765cc215570 Mon Sep 17 00:00:00 2001 From: dgis Date: Thu, 3 Mar 2022 08:11:19 +0100 Subject: [PATCH] Version 2.5 (2022-03-03) - Allow to load RLE4, RLE8 and monochrome BMP images. - Optimize the number of draw calls when displaying the LCD pixel borders. --- ReadMe.txt | 6 + app/build.gradle | 18 +- app/src/main/AndroidManifest.xml | 2 +- app/src/main/assets/ReadMe.txt | 6 + app/src/main/cpp/emu-jni.c | 2 +- app/src/main/cpp/win32-layer.c | 247 ++++++++++++++++-- app/src/main/cpp/win32-layer.h | 1 + .../emulator/calculator/MainScreenView.java | 24 +- .../emulator/forty/eight/MainActivity.java | 44 +++- app/src/main/res/values/ids.xml | 4 + build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 12 files changed, 310 insertions(+), 48 deletions(-) create mode 100644 app/src/main/res/values/ids.xml diff --git a/ReadMe.txt b/ReadMe.txt index f35abc8..55b7098 100644 --- a/ReadMe.txt +++ b/ReadMe.txt @@ -58,6 +58,12 @@ LINKS CHANGES +Version 2.5 (2022-03-03) + +- Allow to load RLE4, RLE8 and monochrome BMP images. +- Optimize the number of draw calls when displaying the LCD pixel borders. + + Version 2.4 (2021-12-08) - Updated source code from Eric Rechlin's Emu48 version 1.63+ that was merged from Christoph Gießelink's Emu48 version 1.64. diff --git a/app/build.gradle b/app/build.gradle index 96db432..aed1951 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -28,13 +28,13 @@ if (keystorePropertiesFile.exists()) { } android { - compileSdkVersion 30 + compileSdkVersion 31 defaultConfig { applicationId "org.emulator.forty.eight" minSdkVersion 19 - targetSdkVersion 30 - versionCode 23 - versionName "2.4" + targetSdkVersion 31 + versionCode 24 + versionName "2.5" setProperty("archivesBaseName", "Emu48-v$versionName") testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" externalNativeBuild { @@ -84,12 +84,12 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'androidx.appcompat:appcompat:1.3.1' - implementation 'androidx.constraintlayout:constraintlayout:2.1.1' - implementation 'androidx.preference:preference:1.1.1' - implementation 'com.google.android.material:material:1.4.0' + implementation 'androidx.appcompat:appcompat:1.4.1' + implementation 'androidx.constraintlayout:constraintlayout:2.1.3' + implementation 'androidx.preference:preference:1.2.0' + implementation 'com.google.android.material:material:1.5.0' implementation 'androidx.documentfile:documentfile:1.0.1' - testImplementation 'junit:junit:4.13.1' + testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test:runner:1.4.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2ec4e10..d260787 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -21,7 +21,7 @@ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout" android:launchMode="singleTop" android:theme="@style/AppTheme.NoActionBar" - > + android:exported="true"> diff --git a/app/src/main/assets/ReadMe.txt b/app/src/main/assets/ReadMe.txt index 324d1dd..76db633 100644 --- a/app/src/main/assets/ReadMe.txt +++ b/app/src/main/assets/ReadMe.txt @@ -58,6 +58,12 @@ LINKS CHANGES +Version 2.5 (2022-03-03) + +- Allow to load RLE4, RLE8 and monochrome BMP images. +- Optimize the number of draw calls when displaying the LCD pixel borders. + + Version 2.4 (2021-12-08) - Updated source code from Eric Rechlin's Emu48 version 1.63+ that was merged from Christoph Gießelink's Emu48 version 1.64. diff --git a/app/src/main/cpp/emu-jni.c b/app/src/main/cpp/emu-jni.c index d502823..0c2a677 100644 --- a/app/src/main/cpp/emu-jni.c +++ b/app/src/main/cpp/emu-jni.c @@ -725,7 +725,7 @@ JNIEXPORT jint JNICALL Java_org_emulator_calculator_NativeLib_onFileOpen(JNIEnv pbyRomBackup = pbyRom; pbyRom = NULL; } - BOOL result = OpenDocument(szBufferFilename); + BOOL result = OpenDocument(szBufferFilename); if(pbyRomBackup) pbyRomBackup = NULL; chooseCurrentKmlMode = ChooseKmlMode_UNKNOWN; mainViewResizeCallback(nBackgroundW, nBackgroundH); diff --git a/app/src/main/cpp/win32-layer.c b/app/src/main/cpp/win32-layer.c index fa16fef..1739c82 100644 --- a/app/src/main/cpp/win32-layer.c +++ b/app/src/main/cpp/win32-layer.c @@ -2237,7 +2237,7 @@ void StretchBltInternal(int xDest, int yDest, int wDest, int hDest, // and the color white in the source turns into the destination’s background // color. BYTE * sourcePixel = sourcePixelBase + ((UINT)src_curx >> (UINT)3); - UINT bitNumber = (UINT) (src_curx % 8); + UINT bitNumber = (UINT) (7 - (src_curx % 8)); if(*sourcePixel & ((UINT)1 << bitNumber)) { // Monochrome 1=White sourceColorPointer[0] = 255; @@ -2311,7 +2311,7 @@ void StretchBltInternal(int xDest, int yDest, int wDest, int hDest, // In other words, GDI considers a monochrome bitmap to be // black pixels on a white background. BYTE * destinationPixel = destinationPixelBase + (x >> 3); - UINT bitNumber = x % 8; + UINT bitNumber = (UINT) (7 - (x % 8)); if(backgroundColor == sourceColor) { *destinationPixel |= (1 << bitNumber); // 1 White } else { @@ -2397,29 +2397,237 @@ HBITMAP CreateBitmap( int nWidth, int nHeight, UINT nPlanes, UINT nBitCount, CON newHBITMAP->bitmapBits = bitmapBits; return newHBITMAP; } + +// RLE decode from Christoph Giesselink in FILES.C from Emu48forPocketPC v125 +#define WIDTHBYTES(bits) ((((bits) + 31) / 32) * 4) + +typedef struct _BmpFile +{ + DWORD dwPos; // actual reading pos + DWORD dwFileSize; // file size + LPBYTE pbyFile; // buffer +} BMPFILE, FAR *LPBMPFILE, *PBMPFILE; + +static BOOL ReadRleBmpByte(LPBMPFILE pBmp, BYTE *n) +{ + // outside BMP file + if (pBmp->dwPos >= pBmp->dwFileSize) + return TRUE; + + *n = pBmp->pbyFile[pBmp->dwPos++]; + return FALSE; +} + +static BOOL DecodeRleBmp(LPBYTE ppvBits,BITMAPINFOHEADER CONST *lpbi, LPBMPFILE pBmp) +{ + BYTE byLength,byColorIndex; + DWORD dwPos,dwRow,dwSizeImage,dwPixelPerLine; + BOOL bDecoding; + + _ASSERT(ppvBits != NULL); // destination + _ASSERT(lpbi != NULL); // BITMAPINFOHEADER + _ASSERT(pBmp != NULL); // bitmap data + + // valid bit count for RLE bitmaps + _ASSERT(lpbi->biBitCount == 4 || lpbi->biBitCount == 8); + + // wrong bit count for compressed bitmap or top-down bitmap + if ((lpbi->biBitCount != 4 && lpbi->biBitCount != 8) || lpbi->biHeight < 0) + return TRUE; + + bDecoding = TRUE; // RLE decoder running + dwPos = dwRow = 0; // reset absolute position and row counter + + // image size + _ASSERT(lpbi->biHeight >= 0); + dwSizeImage = WIDTHBYTES((DWORD)lpbi->biWidth * lpbi->biBitCount) * lpbi->biHeight; + + ZeroMemory(ppvBits,dwSizeImage); // clear bitmap + + // image size in pixel + dwSizeImage *= (8 / lpbi->biBitCount); + + // no. of pixels per line + dwPixelPerLine = dwSizeImage / lpbi->biHeight; + + do + { + // length information is WORD aligned + _ASSERT(((DWORD) &pBmp->pbyFile[pBmp->dwPos] % sizeof(WORD)) == 0); + if (ReadRleBmpByte(pBmp,&byLength)) return TRUE; + if (ReadRleBmpByte(pBmp,&byColorIndex)) return TRUE; + + if (byLength) // length information + { + // check for buffer overflow + if (dwPos + byLength > dwSizeImage) + { + // write rest of data until buffer full + byLength = (dwPos > dwSizeImage) ? 0 : (BYTE) (dwSizeImage - dwPos); + bDecoding = FALSE; // abort + } + + if (lpbi->biBitCount == 4) // RLE4 + { + BYTE byColor[2]; + UINT s,d; + + // split into upper/lower nibble + byColor[0] = byColorIndex >> 4; + byColor[1] = byColorIndex & 0x0F; + + s = 0; // source nibble selector [0/1] + d = (~dwPos & 1) * 4; // destination shift [0/4] + + while (byLength-- > 0) + { + // write nibble to memory + _ASSERT((byColor[s] & 0xF0) == 0); + ppvBits[dwPos++/2] |= (byColor[s] << d); + s ^= 1; // next source nibble + d ^= 4; // next destination shift + } + } + else // RLE8 + { + while (byLength-- > 0) + ppvBits[dwPos++] = byColorIndex; + } + } + else // escape sequence + { + switch (byColorIndex) + { + case 0: // End of Line + dwPos = ++dwRow * dwPixelPerLine; + break; + case 1: // End of Bitmap + bDecoding = FALSE; + break; + case 2: // Delta + // column offset + if (ReadRleBmpByte(pBmp,&byColorIndex)) return TRUE; + dwPos += byColorIndex; + // row offset + if (ReadRleBmpByte(pBmp,&byColorIndex)) return TRUE; + dwRow += byColorIndex; + dwPos += dwPixelPerLine * byColorIndex; + break; + default: // absolute mode + // check for buffer overflow + if (dwPos + byColorIndex > dwSizeImage) + { + // write rest of data until buffer full + byColorIndex = (dwPos > dwSizeImage) ? 0 : (BYTE) (dwSizeImage - dwPos); + bDecoding = FALSE; // abort + } + + if (lpbi->biBitCount == 4) // RLE4 + { + BYTE byColor,byColorPair; + UINT s,d; + + d = (~dwPos & 1) * 4; // destination shift [0/4] + + for (s = 0; s < byColorIndex; ++s) + { + if ((s & 1) == 0) // upper nibble + { + // fetch color pair + if (ReadRleBmpByte(pBmp,&byColorPair)) return TRUE; + + // get upper nibble + byColor = (byColorPair >> 4); + } + else // lower nibble + { + // get lower nibble + byColor = (byColorPair & 0x0F); + } + + // write nibble to memory + _ASSERT((byColor & 0xF0) == 0); + ppvBits[dwPos++/2] |= (byColor << d); + d ^= 4; // next destination shift + } + + // for odd byte length detection + byColorIndex = (++byColorIndex) >> 1; + } + else // RLE8 + { + if (pBmp->dwPos + byColorIndex > pBmp->dwFileSize) return TRUE; + CopyMemory(ppvBits+dwPos,&pBmp->pbyFile[pBmp->dwPos],byColorIndex); + dwPos += byColorIndex; + pBmp->dwPos += byColorIndex; + } + // word align on odd byte length + if (byColorIndex & 1) ++pBmp->dwPos; + break; + } + } + } + while (bDecoding); + return FALSE; +} + HBITMAP CreateDIBitmap( HDC hdc, CONST BITMAPINFOHEADER *pbmih, DWORD flInit, CONST VOID *pjBits, CONST BITMAPINFO *pbmi, UINT iUsage) { PAINT_LOGD("PAINT CreateDIBitmap()"); - HGDIOBJ newHBITMAP = (HGDIOBJ)malloc(sizeof(_HGDIOBJ)); - memset(newHBITMAP, 0, sizeof(_HGDIOBJ)); - newHBITMAP->handleType = HGDIOBJ_TYPE_BITMAP; + HGDIOBJ newHBITMAP = (HGDIOBJ)malloc(sizeof(_HGDIOBJ)); + memset(newHBITMAP, 0, sizeof(_HGDIOBJ)); + newHBITMAP->handleType = HGDIOBJ_TYPE_BITMAP; - BITMAPINFO * newBitmapInfo = malloc(sizeof(BITMAPINFO)); - memcpy(newBitmapInfo, pbmi, sizeof(BITMAPINFO)); - newHBITMAP->bitmapInfo = newBitmapInfo; - newHBITMAP->bitmapInfoHeader = (BITMAPINFOHEADER *)newBitmapInfo; + BITMAPINFO * newBitmapInfo = malloc(sizeof(BITMAPINFO)); + memcpy(newBitmapInfo, pbmi, sizeof(BITMAPINFO)); + newHBITMAP->bitmapInfo = newBitmapInfo; + newHBITMAP->bitmapInfoHeader = (BITMAPINFOHEADER *)newBitmapInfo; - size_t stride = (size_t)(4 * ((newBitmapInfo->bmiHeader.biWidth * newBitmapInfo->bmiHeader.biBitCount + 31) / 32)); - size_t size = newBitmapInfo->bmiHeader.biSizeImage ? - newBitmapInfo->bmiHeader.biSizeImage : - abs(newBitmapInfo->bmiHeader.biHeight) * stride; - VOID * bitmapBits = malloc(size); - memcpy(bitmapBits, pjBits, size); - newHBITMAP->bitmapBits = bitmapBits; - return newHBITMAP; + if(flInit == CBM_INIT && pjBits) { + VOID * bitmapBits = NULL; + if(iUsage == DIB_RGB_COLORS) { + switch (pbmi->bmiHeader.biCompression) { + case BI_RLE4: + case BI_RLE8: { + + // Destination + BOOL bErr; + newBitmapInfo->bmiHeader.biCompression = BI_RGB; + size_t stride = (size_t)(4 * ((newBitmapInfo->bmiHeader.biWidth * newBitmapInfo->bmiHeader.biBitCount + 31) / 32)); + size_t size = abs(newBitmapInfo->bmiHeader.biHeight) * stride; + bitmapBits = malloc(size); + BMPFILE Bmp; + int bitOffset = sizeof(BITMAPINFOHEADER) + (pbmi->bmiHeader.biCompression == BI_BITFIELDS ? 3 * sizeof(DWORD) : DibNumColors(&pbmi->bmiHeader) * sizeof(RGBQUAD)); + + Bmp.pbyFile = ((LPBYTE)&pbmi->bmiHeader) + bitOffset; + Bmp.dwPos = 0; + Bmp.dwFileSize = -1; //size; + + bErr = DecodeRleBmp( + /* Destination */ bitmapBits, + /* Destination header */ &newBitmapInfo->bmiHeader, + /* Source */ &Bmp); + break; + } + case BI_RGB: + case BI_BITFIELDS: { + // We consider the source and destination dib with the same format + size_t stride = (size_t)(4 * ((newBitmapInfo->bmiHeader.biWidth * newBitmapInfo->bmiHeader.biBitCount + 31) / 32)); + size_t size = newBitmapInfo->bmiHeader.biSizeImage ? + newBitmapInfo->bmiHeader.biSizeImage : + abs(newBitmapInfo->bmiHeader.biHeight) * stride; + bitmapBits = malloc(size); + memcpy(bitmapBits, pjBits, size); + break; + } + } + } + newHBITMAP->bitmapBits = bitmapBits; + } + return newHBITMAP; } HBITMAP CreateDIBSection(HDC hdc, CONST BITMAPINFO *pbmi, UINT usage, VOID **ppvBits, HANDLE hSection, DWORD offset) { - PAINT_LOGD("PAINT CreateDIBitmap()"); + PAINT_LOGD("PAINT CreateDIBSection()"); HGDIOBJ newHBITMAP = (HGDIOBJ)malloc(sizeof(_HGDIOBJ)); memset(newHBITMAP, 0, sizeof(_HGDIOBJ)); @@ -2445,7 +2653,7 @@ HBITMAP CreateDIBSection(HDC hdc, CONST BITMAPINFO *pbmi, UINT usage, VOID **ppv return newHBITMAP; } HBITMAP CreateCompatibleBitmap( HDC hdc, int cx, int cy) { - PAINT_LOGD("PAINT CreateDIBitmap()"); + PAINT_LOGD("PAINT CreateCompatibleBitmap()"); HGDIOBJ newHBITMAP = (HGDIOBJ)malloc(sizeof(_HGDIOBJ)); memset(newHBITMAP, 0, sizeof(_HGDIOBJ)); @@ -2469,6 +2677,7 @@ HBITMAP CreateCompatibleBitmap( HDC hdc, int cx, int cy) { return newHBITMAP; } int GetDIBits(HDC hdc, HBITMAP hbm, UINT start, UINT cLines, LPVOID lpvBits, LPBITMAPINFO lpbmi, UINT usage) { + PAINT_LOGD("PAINT GetDIBits()"); //TODO Not sure at all for this function if(hbm && lpbmi) { CONST BITMAPINFO *pbmi = hbm->bitmapInfo; diff --git a/app/src/main/cpp/win32-layer.h b/app/src/main/cpp/win32-layer.h index ea187ef..66ecc19 100644 --- a/app/src/main/cpp/win32-layer.h +++ b/app/src/main/cpp/win32-layer.h @@ -811,6 +811,7 @@ typedef struct _RGNDATA { char Buffer[1]; } RGNDATA, *PRGNDATA, NEAR *NPRGNDATA, FAR *LPRGNDATA; extern int GetDIBits(HDC hdc, HBITMAP hbm, UINT start, UINT cLines, LPVOID lpvBits, LPBITMAPINFO lpbmi, UINT usage); +extern int SetDIBits(HDC hdc, HBITMAP hbm, UINT start, UINT cLines, const VOID *lpBits, const BITMAPINFO *lpbmi, UINT ColorUse); extern COLORREF GetPixel(HDC hdc, int x ,int y); extern HPALETTE SelectPalette(HDC hdc, HPALETTE hPal, BOOL bForceBkgd); extern UINT RealizePalette(HDC hdc); diff --git a/app/src/main/java/org/emulator/calculator/MainScreenView.java b/app/src/main/java/org/emulator/calculator/MainScreenView.java index c38333b..0e2804e 100644 --- a/app/src/main/java/org/emulator/calculator/MainScreenView.java +++ b/app/src/main/java/org/emulator/calculator/MainScreenView.java @@ -256,7 +256,7 @@ public class MainScreenView extends PanAndScaleView { @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if((event.getFlags() & KeyEvent.FLAG_VIRTUAL_HARD_KEY) == 0 - && (event.getSource() & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD) { + && ((event.getSource() & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD) || event.getSource() == 0) { if(!event.isNumLockOn() && numpadKey.indexOf(keyCode) != -1) return false; char pressedKey = (char) event.getUnicodeChar(); @@ -277,7 +277,7 @@ public class MainScreenView extends PanAndScaleView { @Override public boolean onKeyUp(int keyCode, KeyEvent event) { if((event.getFlags() & KeyEvent.FLAG_VIRTUAL_HARD_KEY) == 0 - && (event.getSource() & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD) { + && ((event.getSource() & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD) || event.getSource() == 0) { if(!event.isNumLockOn() && numpadKey.indexOf(keyCode) != -1) return false; char pressedKey = (char) event.getUnicodeChar(); @@ -451,20 +451,36 @@ public class MainScreenView extends PanAndScaleView { } } + + static float[] pointsBuffer = new float[0]; static void drawPixelBorder(Canvas canvas, int lcdWidthNative, int lcdHeightNative, float screenPositionX, float screenPositionY, float screenWidth, float screenHeight, Paint paintLCD) { // Draw the LCD grid lines without antialiasing to emulate the genuine pixels borders int lcdBackgroundColor = 0xFF000000 | NativeLib.getLCDBackgroundColor(); paintLCD.setColor(lcdBackgroundColor); float stepX = screenWidth / lcdWidthNative; + + // Optimized drawcalls + int pointBufferLength = (lcdWidthNative + lcdHeightNative) << 2; + if(pointsBuffer.length != pointBufferLength) + pointsBuffer = new float[pointBufferLength]; // Adjust the buffer of points + + int pointsIndex = 0; for (int x = 0; x < lcdWidthNative; x++) { float screenX = screenPositionX + x * stepX; - canvas.drawLine(screenX, screenPositionY, screenX, screenPositionY + screenHeight, paintLCD); + pointsBuffer[pointsIndex++] = screenX; + pointsBuffer[pointsIndex++] = screenPositionY; + pointsBuffer[pointsIndex++] = screenX; + pointsBuffer[pointsIndex++] = screenPositionY + screenHeight; } float stepY = screenHeight / lcdHeightNative; for (int y = 0; y < lcdHeightNative; y++) { float screenY = screenPositionY + y * stepY; - canvas.drawLine(screenPositionX, screenY, screenPositionX + screenWidth, screenY, paintLCD); + pointsBuffer[pointsIndex++] = screenPositionX; + pointsBuffer[pointsIndex++] = screenY; + pointsBuffer[pointsIndex++] = screenPositionX + screenWidth; + pointsBuffer[pointsIndex++] = screenY; } + canvas.drawLines(pointsBuffer, paintLCD); } public void updateCallback(int type, int param1, int param2, String param3, String param4) { diff --git a/app/src/main/java/org/emulator/forty/eight/MainActivity.java b/app/src/main/java/org/emulator/forty/eight/MainActivity.java index 7b8673f..aa55230 100644 --- a/app/src/main/java/org/emulator/forty/eight/MainActivity.java +++ b/app/src/main/java/org/emulator/forty/eight/MainActivity.java @@ -154,7 +154,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On @Override protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); drawer = findViewById(R.id.drawer_layout); @@ -169,6 +169,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On ViewGroup mainScreenContainer = findViewById(R.id.main_screen_container); mainScreenView = new MainScreenView(this); + mainScreenView.setId(R.id.main_screen_view); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) mainScreenView.setStatusBarColor(getWindow().getStatusBarColor()); mainScreenContainer.addView(mainScreenView, 0, new ViewGroup.LayoutParams( @@ -437,6 +438,8 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On boolean bStackCEnable = bObjectEnable; boolean bStackPEnable = bObjectEnable; + menu.findItem(R.id.nav_manage_flash_rom).setEnabled(uRun && (cCurrentRomType == 'X' || cCurrentRomType == 'Q')); + menu.findItem(R.id.nav_save).setEnabled(uRun); menu.findItem(R.id.nav_save_as).setEnabled(uRun); menu.findItem(R.id.nav_close).setEnabled(uRun); @@ -451,7 +454,6 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On 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_show_kml_script_compilation_result).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 */); @@ -844,7 +846,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On } else openDocument(); } - private void OnObjectSave() { + private void SaveObject() { Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("*/*"); @@ -852,6 +854,23 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On saveWhenLaunchingActivity = false; startActivityForResult(intent, INTENT_OBJECT_SAVE); } + private void OnObjectSave() { + int model = NativeLib.getCurrentModel(); + if(model == 'L' // HP32S # Leonardo + || model == 'N' // HP32SII # Nardo + || model == 'D') { // HP42S # Davinci + final String[] objectsToSave = NativeLib.getObjectsToSave(); + objectsToSaveItemChecked = new boolean[objectsToSave.length]; + new AlertDialog.Builder(MainActivity.this) + .setTitle(getResources().getString(R.string.message_object_save_program)) + .setMultiChoiceItems(objectsToSave, null, (dialog, which, isChecked) -> objectsToSaveItemChecked[which] = isChecked).setPositiveButton("OK", (dialog, id) -> { + //NativeLib.onObjectSave(url); + SaveObject(); + }).setNegativeButton("Cancel", (dialog, id) -> objectsToSaveItemChecked = null).show(); + } else + SaveObject(); // Others calculators + } + private void OnViewCopyFullscreen() { Bitmap bitmapScreen = mainScreenView.getBitmap(); if(bitmapScreen == null) @@ -1147,7 +1166,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On } } - private void OnMacroRecord() { + private void OnMacroRecord() { Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("*/*"); @@ -1220,11 +1239,12 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On NativeLib.onObjectLoad(url); break; } - case INTENT_OBJECT_SAVE: { - if(debug) Log.d(TAG, "onActivityResult INTENT_OBJECT_SAVE " + url); - NativeLib.onObjectSave(url, null); - break; - } + case INTENT_OBJECT_SAVE: { + if(debug) Log.d(TAG, "onActivityResult INTENT_OBJECT_SAVE " + url); + NativeLib.onObjectSave(url, objectsToSaveItemChecked); + objectsToSaveItemChecked = null; + break; + } case INTENT_PORT2LOAD: { if(debug) Log.d(TAG, "onActivityResult INTENT_PORT2LOAD " + url); //TODO Duplicate code in SettingsFragment! @@ -1544,7 +1564,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On } } - if(settings.getBoolean("settings_port2en", false)) { + if(getPackageName().contains("org.emulator.forty.eight") && settings.getBoolean("settings_port2en", false)) { // Check if the access to the port2 shared file is still possible. String port2Url = settings.getString("settings_port2load", ""); if(port2Url != null && port2Url.length() > 0) { @@ -2201,9 +2221,9 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On mainScreenView.setRotationMode(rotationMode, isDynamic); break; case "settings_auto_layout": - int autoLayoutMode = 1; + int autoLayoutMode = getPackageName().contains("org.emulator.forty.eight") ? 1 : 2; try { - autoLayoutMode = Integer.parseInt(settings.getString("settings_auto_layout", "1")); + autoLayoutMode = Integer.parseInt(settings.getString("settings_auto_layout", String.valueOf(autoLayoutMode))); } catch (NumberFormatException ex) { // Catch bad number format } diff --git a/app/src/main/res/values/ids.xml b/app/src/main/res/values/ids.xml new file mode 100644 index 0000000..be3153a --- /dev/null +++ b/app/src/main/res/values/ids.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/build.gradle b/build.gradle index 81ef344..9df810f 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:7.0.4' + classpath 'com.android.tools.build:gradle:7.1.2' // NOTE: Do not place your application dependencies here; they belong diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0a26fdb..68fcb18 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip