diff --git a/ReadMe.txt b/ReadMe.txt index e3944a1..9fb6734 100644 --- a/ReadMe.txt +++ b/ReadMe.txt @@ -17,7 +17,7 @@ QUICK START 1. From the left side, slide your finger to open the menu. 2. Touch the "New..." menu item. -3. Select a predefined faceplate (or select a custom KML script folder with Android >= 5.0). +3. Select a default calculator (or with Android >= 5.0, "[Select a Custom KML script folder...]" where you have copied the KML scripts and ROM files (Android 11 cannot use the folder Download)). 4. And the calculator should now be opened. @@ -58,7 +58,7 @@ LINKS CHANGES -Version 2.3 (2021-09-xx) +Version 2.3 (2021-10-19) - Add an experimental serial port support (via USB OTG). - Show KML log on request. @@ -66,6 +66,9 @@ Version 2.3 (2021-09-xx) - Update the embedded help file "Emu48.html" to the latest version. - Open an external web browser when you click an external links in the Help. - Add Real blue 50g faceplate based on my calculator and on the KML script from Eric Rechlin. +- Display the graphic tab of the printer without antialiasing. +- Fix a crash about the Most Recently Used state files. +- Fix an issue with "Copy Screen". Version 2.2 (2020-12-09) diff --git a/app/build.gradle b/app/build.gradle index f79b71e..ce0d7f2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -33,8 +33,8 @@ android { applicationId "org.emulator.forty.eight" minSdkVersion 19 targetSdkVersion 30 - versionCode 20 - versionName "2.2" + versionCode 22 + versionName "2.3" setProperty("archivesBaseName", "Emu48-v$versionName") testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" externalNativeBuild { diff --git a/app/src/main/assets/ReadMe.txt b/app/src/main/assets/ReadMe.txt index 03c9809..6180c83 100644 --- a/app/src/main/assets/ReadMe.txt +++ b/app/src/main/assets/ReadMe.txt @@ -17,7 +17,7 @@ QUICK START 1. From the left side, slide your finger to open the menu. 2. Touch the "New..." menu item. -3. Select a predefined faceplate (or select a custom KML script folder with Android >= 5.0). +3. Select a default calculator (or with Android >= 5.0, "[Select a Custom KML script folder...]" where you have copied the KML scripts and ROM files (Android 11 cannot use the folder Download)). 4. And the calculator should now be opened. @@ -58,7 +58,7 @@ LINKS CHANGES -Version 2.3 (2021-09-xx) +Version 2.3 (2021-10-19) - Add an experimental serial port support (via USB OTG). - Show KML log on request. @@ -66,6 +66,9 @@ Version 2.3 (2021-09-xx) - Update the embedded help file "Emu48.html" to the latest version. - Open an external web browser when you click an external links in the Help. - Add Real blue 50g faceplate based on my calculator and on the KML script from Eric Rechlin. +- Display the graphic tab of the printer without antialiasing. +- Fix a crash about the Most Recently Used state files. +- Fix an issue with "Copy Screen". Version 2.2 (2020-12-09) diff --git a/app/src/main/cpp/emu-jni.c b/app/src/main/cpp/emu-jni.c index bdd32db..d502823 100644 --- a/app/src/main/cpp/emu-jni.c +++ b/app/src/main/cpp/emu-jni.c @@ -28,6 +28,7 @@ extern AAssetManager * assetManager; static jobject mainActivity = NULL; jobject bitmapMainScreen = NULL; AndroidBitmapInfo androidBitmapInfo; +//RECT mainViewRectangleToUpdate = { 0, 0, 0, 0 }; enum DialogBoxMode currentDialogBoxMode; LPBYTE pbyRomBackup = NULL; enum ChooseKmlMode chooseCurrentKmlMode; @@ -106,7 +107,19 @@ int mainViewCallback(int type, int param1, int param2, const TCHAR * param3, con } void mainViewUpdateCallback() { - mainViewCallback(CALLBACK_TYPE_INVALIDATE, 0, 0, NULL, NULL); +// if(!IsRectEmpty(&mainViewRectangleToUpdate)) { +// int param1 = ((mainViewRectangleToUpdate.left & 0xFFFF) << 16) | (mainViewRectangleToUpdate.top & 0xFFFF); +// int param2 = ((mainViewRectangleToUpdate.right & 0xFFFF) << 16) | (mainViewRectangleToUpdate.bottom & 0xFFFF); +// mainViewCallback(CALLBACK_TYPE_INVALIDATE, +// param1, +// param2, +// NULL, NULL); +// SetRectEmpty(&mainViewRectangleToUpdate); +// } else + mainViewCallback(CALLBACK_TYPE_INVALIDATE, + 0, + 0, + NULL, NULL); } void mainViewResizeCallback(int x, int y) { @@ -975,11 +988,11 @@ JNIEXPORT void JNICALL Java_org_emulator_calculator_NativeLib_onViewCopy(JNIEnv size_t strideSource = (size_t)(4 * ((hBmp->bitmapInfoHeader->biWidth * hBmp->bitmapInfoHeader->biBitCount + 31) / 32)); size_t strideDestination = bitmapScreenInfo.stride; VOID * bitmapBitsSource = (VOID *)hBmp->bitmapBits; - VOID * bitmapBitsDestination = pixelsDestination; + VOID * bitmapBitsDestination = pixelsDestination + (hBmp->bitmapInfoHeader->biHeight - 1) * strideDestination; for(int y = 0; y < hBmp->bitmapInfoHeader->biHeight; y++) { memcpy(bitmapBitsDestination, bitmapBitsSource, strideSource); bitmapBitsSource += strideSource; - bitmapBitsDestination += strideDestination; + bitmapBitsDestination -= strideDestination; } diff --git a/app/src/main/cpp/win32-layer.c b/app/src/main/cpp/win32-layer.c index 99c02a8..fa16fef 100644 --- a/app/src/main/cpp/win32-layer.c +++ b/app/src/main/cpp/win32-layer.c @@ -60,6 +60,8 @@ static void initTimer(); #define MAX_FILE_MAPPING_HANDLE 10 static HANDLE fileMappingHandles[MAX_FILE_MAPPING_HANDLE]; +HBITMAP rootBITMAP; + void win32Init() { initTimer(); for (int i = 0; i < MAX_FILE_MAPPING_HANDLE; ++i) { @@ -70,6 +72,8 @@ void win32Init() { contentSchemeLength = _tcslen(contentScheme); documentSchemeLength = _tcslen(documentScheme); comPrefixLength = _tcslen(comPrefix); + + rootBITMAP = CreateCompatibleBitmap(NULL, 0, 0); } int abs (int i) { @@ -1832,6 +1836,8 @@ BOOL DeleteObject(HGDIOBJ ho) { } case HGDIOBJ_TYPE_BITMAP: { PAINT_LOGD("PAINT DeleteObject() HGDIOBJ_TYPE_BITMAP"); + if(ho == rootBITMAP) + return FALSE; ho->handleType = HGDIOBJ_TYPE_INVALID; if(ho->bitmapInfo) free((void *) ho->bitmapInfo); @@ -1903,6 +1909,7 @@ HDC CreateCompatibleDC(HDC hdc) { memset(handle, 0, sizeof(struct _HDC)); handle->handleType = HDC_TYPE_DC; handle->hdcCompatible = hdc; + handle->selectedBitmap = rootBITMAP; return handle; } HDC GetDC(HWND hWnd) { diff --git a/app/src/main/java/org/emulator/calculator/MainScreenView.java b/app/src/main/java/org/emulator/calculator/MainScreenView.java index 6d38955..c38333b 100644 --- a/app/src/main/java/org/emulator/calculator/MainScreenView.java +++ b/app/src/main/java/org/emulator/calculator/MainScreenView.java @@ -63,6 +63,7 @@ public class MainScreenView extends PanAndScaleView { public float defaultViewPanOffsetX = 0.0f; public float defaultViewPanOffsetY = 0.0f; +// private Rect invalidateRectangle = new Rect(); public MainScreenView(Context context) { super(context); @@ -406,6 +407,16 @@ public class MainScreenView extends PanAndScaleView { // Copy the full calculator with antialiasing canvas.drawBitmap(bitmapMainScreen, 0, 0, paintFullCalc); +// synchronized (invalidateRectangle) { +// if (invalidateRectangle.isEmpty()) { +// canvas.drawColor(getBackgroundColor()); +// canvas.drawBitmap(bitmapMainScreen, 0, 0, paintFullCalc); +// } else { +// canvas.drawBitmap(bitmapMainScreen, invalidateRectangle, invalidateRectangle, paintFullCalc); +// invalidateRectangle.setEmpty(); +// } +// } + if(usePixelBorders) { // Copy the LCD part only without antialiasing int x = NativeLib.getScreenPositionX(); @@ -457,36 +468,51 @@ public class MainScreenView extends PanAndScaleView { } public void updateCallback(int type, int param1, int param2, String param3, String param4) { - switch (type) { - case NativeLib.CALLBACK_TYPE_INVALIDATE: - if (debug) Log.d(TAG, "updateCallback() CALLBACK_TYPE_INVALIDATE postInvalidate()"); - postInvalidate(); - if(this.onUpdateDisplayListener != null) - this.onUpdateDisplayListener.run(); - break; - case NativeLib.CALLBACK_TYPE_WINDOW_RESIZE: - if (debug) Log.d(TAG, "updateCallback() CALLBACK_TYPE_WINDOW_RESIZE()"); - // New Bitmap size - if(bitmapMainScreen == null || bitmapMainScreen.getWidth() != param1 || bitmapMainScreen.getHeight() != param2) { - if(debug) Log.d(TAG, "updateCallback() Bitmap.createBitmap(x: " + Math.max(1, param1) + ", y: " + Math.max(1, param2) + ")"); - Bitmap oldBitmapMainScreen = bitmapMainScreen; - bitmapMainScreen = Bitmap.createBitmap(Math.max(1, param1), Math.max(1, param2), Bitmap.Config.ARGB_8888); - int globalColor = NativeLib.getGlobalColor(); - kmlBackgroundColor = Color.argb(255, (globalColor & 0x00FF0000) >> 16, (globalColor & 0x0000FF00) >> 8, globalColor & 0x000000FF); + try { + switch (type) { + case NativeLib.CALLBACK_TYPE_INVALIDATE: + // int left = param1 >> 16; + // int top = param1 & 0xFFFF; + // int right = param2 >> 16; + // int bottom = param2 & 0xFFFF; + // if (debug) Log.d(TAG, "updateCallback() CALLBACK_TYPE_INVALIDATE postInvalidate() left: " + left + ", top: " + top + ", right: " + right + ", bottom: " + bottom); + // synchronized (invalidateRectangle) { + // invalidateRectangle.union(left, top, right, bottom); + // } + if (debug) + Log.d(TAG, "updateCallback() CALLBACK_TYPE_INVALIDATE postInvalidate()"); + postInvalidate(); + if (this.onUpdateDisplayListener != null) + this.onUpdateDisplayListener.run(); + break; + case NativeLib.CALLBACK_TYPE_WINDOW_RESIZE: + if (debug) Log.d(TAG, "updateCallback() CALLBACK_TYPE_WINDOW_RESIZE()"); + // New Bitmap size + if (bitmapMainScreen == null || bitmapMainScreen.getWidth() != param1 || bitmapMainScreen.getHeight() != param2) { + if (debug) + Log.d(TAG, "updateCallback() Bitmap.createBitmap(x: " + Math.max(1, param1) + ", y: " + Math.max(1, param2) + ")"); + Bitmap oldBitmapMainScreen = bitmapMainScreen; + bitmapMainScreen = Bitmap.createBitmap(Math.max(1, param1), Math.max(1, param2), Bitmap.Config.ARGB_8888); + int globalColor = NativeLib.getGlobalColor(); + kmlBackgroundColor = Color.argb(255, (globalColor & 0x00FF0000) >> 16, (globalColor & 0x0000FF00) >> 8, globalColor & 0x000000FF); - bitmapMainScreen.eraseColor(getBackgroundColor()); - NativeLib.changeBitmap(bitmapMainScreen); + bitmapMainScreen.eraseColor(getBackgroundColor()); + NativeLib.changeBitmap(bitmapMainScreen); - if(oldBitmapMainScreen != null) { - oldBitmapMainScreen.recycle(); - } - firstTime = true; - setVirtualSize(bitmapMainScreen.getWidth(), bitmapMainScreen.getHeight()); - if(viewSized) - updateLayout(); - } - break; - } + if (oldBitmapMainScreen != null) { + oldBitmapMainScreen.recycle(); + } + firstTime = true; + setVirtualSize(bitmapMainScreen.getWidth(), bitmapMainScreen.getHeight()); + if (viewSized) + updateLayout(); + } + break; + } + } catch (Exception ex) { + if (debug) + Log.d(TAG, "updateCallback() Exception: " + ex.toString()); + } } public void setRotationMode(int rotationMode, boolean isDynamic) { diff --git a/app/src/main/java/org/emulator/calculator/PrinterSimulatorFragment.java b/app/src/main/java/org/emulator/calculator/PrinterSimulatorFragment.java index 1bcc573..96074df 100644 --- a/app/src/main/java/org/emulator/calculator/PrinterSimulatorFragment.java +++ b/app/src/main/java/org/emulator/calculator/PrinterSimulatorFragment.java @@ -22,9 +22,7 @@ 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; @@ -113,6 +111,8 @@ public class PrinterSimulatorFragment extends AppCompatDialogFragment { Bitmap croppedPaperBitmap = Bitmap.createBitmap(paperBitmap.getWidth(), printerSimulator.getPaperHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(croppedPaperBitmap); Paint paint = new Paint(); + paint.setAntiAlias(false); + paint.setFilterBitmap(false); canvas.drawBitmap(paperBitmap, 0, 0, paint); Activity activity = getActivity(); @@ -122,13 +122,16 @@ public class PrinterSimulatorFragment extends AppCompatDialogFragment { FileOutputStream fileOutputStream = new FileOutputStream(imageFile); croppedPaperBitmap.compress(Bitmap.CompressFormat.PNG, 90, fileOutputStream); fileOutputStream.close(); - String mimeType = "application/png"; - Intent intent = new Intent(Intent.ACTION_SEND); - intent.setType(mimeType); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.putExtra(Intent.EXTRA_SUBJECT, Utils.resId(PrinterSimulatorFragment.this, "string", "message_printer_share_graphic")); - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - intent.putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(getActivity(), getActivity().getPackageName() + ".provider", imageFile)); + + String subject = getString(Utils.resId(PrinterSimulatorFragment.this, "string", "message_printer_share_graphic")); + Intent intent = new Intent(android.content.Intent.ACTION_SEND); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(Intent.EXTRA_SUBJECT, subject); + intent.putExtra(Intent.EXTRA_TITLE, subject); + intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(getActivity(), getActivity().getPackageName() + ".provider", imageFile)); + intent.setType("image/png"); +// intent.setDataAndType(FileProvider.getUriForFile(getActivity(), getActivity().getPackageName() + ".provider", imageFile), "image/png"); startActivity(Intent.createChooser(intent, getString(Utils.resId(PrinterSimulatorFragment.this, "string", "message_printer_share_graphic")))); } } catch (Exception e) { @@ -243,6 +246,7 @@ public class PrinterSimulatorFragment extends AppCompatDialogFragment { scaleThumbnailColor = Color.GRAY; paintBitmap.setAntiAlias(false); + paintBitmap.setFilterBitmap(false); } public void setBitmap(Bitmap bitmap) { 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 5691e91..7b8673f 100644 --- a/app/src/main/java/org/emulator/forty/eight/MainActivity.java +++ b/app/src/main/java/org/emulator/forty/eight/MainActivity.java @@ -145,6 +145,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On return size() > MAX_MRU; } }; + private HashMap mruByMenuId = new HashMap<>(); private final PrinterSimulator printerSimulator = new PrinterSimulator(); private final PrinterSimulatorFragment fragmentPrinterSimulator = new PrinterSimulatorFragment(); @@ -261,6 +262,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On if (recentsSubMenu != null) recentsSubMenu.clear(); } + mruByMenuId.clear(); if (recentsSubMenu != null) { Set mruLinkedHashMapKeySet = mruLinkedHashMap.keySet(); String[] mrus = mruLinkedHashMapKeySet.toArray(new String[0]); @@ -271,8 +273,11 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On // We should remove this file because it seems impossible to get the display name of this Most Recently Used state file. // It might be deleted or the permissions does not allow to reach it anymore. mruLinkedHashMap.remove(mostRecentlyUsedFile); - } else - recentsSubMenu.add(Menu.NONE, MRU_ID_START + i, Menu.NONE, displayName); + } else { + int menuId = MRU_ID_START + i; + mruByMenuId.put(menuId, mostRecentlyUsedFile); + recentsSubMenu.add(Menu.NONE, menuId, Menu.NONE, displayName); + } } } } @@ -397,20 +402,16 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On } else if (id == R.id.nav_about) { OnAbout(); } else if(id >= MRU_ID_START && id < MRU_ID_START + MAX_MRU) { + String url = mruByMenuId.get(id); + if(url != null) { + // Increase the file usage count + mruLinkedHashMap.get(url); - Set mruLinkedHashMapKeySet = mruLinkedHashMap.keySet(); - int mruLength = mruLinkedHashMapKeySet.size(); - String[] mrus = mruLinkedHashMapKeySet.toArray(new String[mruLength]); - - int mruClickedIndex = id - MRU_ID_START; - String url = mrus[mruClickedIndex]; - mruLinkedHashMap.get(url); - - ensureDocumentSaved(() -> { - // FileOpen from MRU. - onFileOpen(url, null, null); - }); - + ensureDocumentSaved(() -> { + // FileOpen from MRU. + onFileOpen(url, null, null); + }); + } } drawer.closeDrawer(GravityCompat.START); @@ -862,14 +863,17 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On FileOutputStream fileOutputStream = new FileOutputStream(imageFile); bitmapScreen.compress(Bitmap.CompressFormat.PNG, 90, fileOutputStream); fileOutputStream.close(); - String mimeType = "application/png"; - Intent intent = new Intent(android.content.Intent.ACTION_SEND); - intent.setType(mimeType); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.putExtra(Intent.EXTRA_SUBJECT, R.string.message_screenshot); - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - intent.putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(this,this.getPackageName() + ".provider", imageFile)); - startActivity(Intent.createChooser(intent, getString(R.string.message_share_screenshot))); + + String subject = getString(R.string.message_screenshot); + Intent intent = new Intent(android.content.Intent.ACTION_SEND); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(Intent.EXTRA_SUBJECT, subject); + intent.putExtra(Intent.EXTRA_TITLE, subject); + intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(this,this.getPackageName() + ".provider", imageFile)); + intent.setType("image/png"); +// intent.setDataAndType(FileProvider.getUriForFile(this,this.getPackageName() + ".provider", imageFile), "image/png"); + startActivity(Intent.createChooser(intent, getString(R.string.message_share_screenshot))); } catch (Exception e) { e.printStackTrace(); showAlert(e.getMessage()); @@ -890,14 +894,17 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On FileOutputStream fileOutputStream = new FileOutputStream(imageFile); bitmapScreen.compress(Bitmap.CompressFormat.PNG, 90, fileOutputStream); fileOutputStream.close(); - String mimeType = "application/png"; - Intent intent = new Intent(android.content.Intent.ACTION_SEND); - intent.setType(mimeType); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.putExtra(Intent.EXTRA_SUBJECT, R.string.message_screenshot); - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - intent.putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(this,this.getPackageName() + ".provider", imageFile)); - startActivity(Intent.createChooser(intent, getString(R.string.message_share_screenshot))); + + String subject = getString(R.string.message_screenshot); + Intent intent = new Intent(android.content.Intent.ACTION_SEND); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(Intent.EXTRA_SUBJECT, subject); + intent.putExtra(Intent.EXTRA_TITLE, subject); + intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(this,this.getPackageName() + ".provider", imageFile)); + intent.setType("image/png"); +// intent.setDataAndType(FileProvider.getUriForFile(this,this.getPackageName() + ".provider", imageFile), "image/png"); + startActivity(Intent.createChooser(intent, getString(R.string.message_share_screenshot))); } catch (Exception e) { e.printStackTrace(); showAlert(e.getMessage()); @@ -961,7 +968,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On } else kmlScriptsForCurrentModel = kmlScripts; - boolean showDefaultKMLScriptFolderItem = !kmlFolderUseDefault && getPackageName().contains("org.emulator.forty.eight"); + boolean showDefaultKMLScriptFolderItem = !kmlFolderUseDefault && (getPackageName().contains("org.emulator.forty.eight") || getPackageName().contains("org.emulator.forty.two")); int lastIndex = kmlScriptsForCurrentModel.size(); String[] kmlScriptTitles = new String[lastIndex + (showDefaultKMLScriptFolderItem ? 2 : 1)]; for (int i = 0; i < kmlScriptsForCurrentModel.size(); i++) @@ -970,7 +977,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On if(showDefaultKMLScriptFolderItem) kmlScriptTitles[lastIndex + 1] = getResources().getString(R.string.load_default_kml); new AlertDialog.Builder(MainActivity.this) - .setTitle(getResources().getString(R.string.pick_calculator)) + .setTitle(getResources().getString(kmlFolderUseDefault ? R.string.pick_default_calculator : R.string.pick_custom_calculator)) .setItems(kmlScriptTitles, (dialog, which) -> { if(which == lastIndex) { // [Select a Custom KML script folder...] diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a945abd..ba53ae0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -61,7 +61,8 @@ [Select a Custom KML script folder...] [Default KML script folder] - Pick a calculator + Pick a default calculator + Pick a custom calculator Printer Simulator Text