- Add the KML Icon if present in the navigation menu header (only support PNG or 32bits BMP in the ICO file).

- Allow an optional overlapping LCD part stuck to the screen when swiping the 2 calc parts (not yet working).
This commit is contained in:
dgis 2019-08-08 22:50:56 +02:00
parent b8b8f92964
commit 62da22c1d0
13 changed files with 849 additions and 162 deletions

View file

@ -25,9 +25,8 @@
#include "win32-layer.h"
extern AAssetManager * assetManager;
static jobject viewToUpdate = NULL;
static jobject mainActivity = NULL;
jobject bitmapMainScreen;
jobject bitmapMainScreen = NULL;
AndroidBitmapInfo androidBitmapInfo;
enum DialogBoxMode currentDialogBoxMode;
enum ChooseKmlMode chooseCurrentKmlMode;
@ -86,16 +85,16 @@ enum CALLBACK_TYPE {
// https://stackoverflow.com/questions/9630134/jni-how-to-callback-from-c-or-c-to-java
int mainViewCallback(int type, int param1, int param2, const TCHAR * param3, const TCHAR * param4) {
if (viewToUpdate) {
if (mainActivity) {
JNIEnv *jniEnv = getJNIEnvironment();
if(jniEnv) {
jclass viewToUpdateClass = (*jniEnv)->GetObjectClass(jniEnv, viewToUpdate);
if(viewToUpdateClass) {
jmethodID midStr = (*jniEnv)->GetMethodID(jniEnv, viewToUpdateClass, "updateCallback", "(IIILjava/lang/String;Ljava/lang/String;)I");
jclass mainActivityClass = (*jniEnv)->GetObjectClass(jniEnv, mainActivity);
if(mainActivityClass) {
jmethodID midStr = (*jniEnv)->GetMethodID(jniEnv, mainActivityClass, "updateCallback", "(IIILjava/lang/String;Ljava/lang/String;)I");
jstring utfParam3 = (*jniEnv)->NewStringUTF(jniEnv, param3);
jstring utfParam4 = (*jniEnv)->NewStringUTF(jniEnv, param4);
int result = (*jniEnv)->CallIntMethod(jniEnv, viewToUpdate, midStr, type, param1, param2, utfParam3, utfParam4);
(*jniEnv)->DeleteLocalRef(jniEnv, viewToUpdateClass);
int result = (*jniEnv)->CallIntMethod(jniEnv, mainActivity, midStr, type, param1, param2, utfParam3, utfParam4);
(*jniEnv)->DeleteLocalRef(jniEnv, mainActivityClass);
//if(needDetach) ret = (*java_machine)->DetachCurrentThread(java_machine);
return result;
}
@ -283,20 +282,30 @@ void sendByteUdp(unsigned char byteSent) {
}
}
JNIEXPORT void JNICALL Java_org_emulator_calculator_NativeLib_start(JNIEnv *env, jobject thisz, jobject assetMgr, jobject bitmapMainScreen0, jobject activity, jobject view) {
void setKMLIcon(int imageWidth, int imageHeight, LPBYTE buffer, int bufferSize) {
JNIEnv *jniEnv = getJNIEnvironment();
if(jniEnv) {
jclass mainActivityClass = (*jniEnv)->GetObjectClass(jniEnv, mainActivity);
if(mainActivityClass) {
jmethodID midStr = (*jniEnv)->GetMethodID(jniEnv, mainActivityClass, "setKMLIcon", "(II[B)V");
jbyteArray pixels = NULL;
if(buffer) {
pixels = (*jniEnv)->NewByteArray(jniEnv, bufferSize);
(*jniEnv)->SetByteArrayRegion(jniEnv, pixels, 0, bufferSize, (jbyte *) buffer);
}
(*jniEnv)->CallVoidMethod(jniEnv, mainActivity, midStr, imageWidth, imageHeight, pixels);
(*jniEnv)->DeleteLocalRef(jniEnv, mainActivityClass);
}
}
}
JNIEXPORT void JNICALL Java_org_emulator_calculator_NativeLib_start(JNIEnv *env, jobject thisz, jobject assetMgr, jobject activity) {
chooseCurrentKmlMode = ChooseKmlMode_UNKNOWN;
szChosenCurrentKml[0] = '\0';
bitmapMainScreen = (*env)->NewGlobalRef(env, bitmapMainScreen0);
mainActivity = (*env)->NewGlobalRef(env, activity);
viewToUpdate = (*env)->NewGlobalRef(env, view);
int ret = AndroidBitmap_getInfo(env, bitmapMainScreen, &androidBitmapInfo);
if (ret < 0) {
LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret);
}
assetManager = AAssetManager_fromJava(env, assetMgr);
@ -394,10 +403,6 @@ JNIEXPORT void JNICALL Java_org_emulator_calculator_NativeLib_stop(JNIEnv *env,
SoundClose(); // close waveform-audio output device
soundEnabled = FALSE;
if (viewToUpdate) {
(*env)->DeleteGlobalRef(env, viewToUpdate);
viewToUpdate = NULL;
}
if(bitmapMainScreen) {
(*env)->DeleteGlobalRef(env, bitmapMainScreen);
bitmapMainScreen = NULL;
@ -419,6 +424,74 @@ JNIEXPORT void JNICALL Java_org_emulator_calculator_NativeLib_changeBitmap(JNIEn
}
}
JNIEXPORT jboolean JNICALL Java_org_emulator_calculator_NativeLib_copyLCD(JNIEnv *env, jobject thisz, jobject bitmapLCD) {
if(!bitmapLCD)
return JNI_FALSE;
AndroidBitmapInfo bitmapLCDInfo;
int ret = AndroidBitmap_getInfo(env, bitmapLCD, &bitmapLCDInfo);
if (ret < 0) {
LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret);
return JNI_FALSE;
}
// INT nxSize, nySize;
// GetSizeLcdBitmap(&nxSize,&nySize); // get LCD size
HDC hSrcDC = hLcdDC; // use display HDC as source
if(!hSrcDC)
return JNI_FALSE;
HBITMAP hBmp = hSrcDC->selectedBitmap;
if (hBmp && hBmp->bitmapInfoHeader && hBmp->bitmapInfoHeader->biWidth == bitmapLCDInfo.width &&
abs(hBmp->bitmapInfoHeader->biHeight) == bitmapLCDInfo.height) {
INT nxO = 0, nyO = 0; // origin in HDC
void *pixelsDestination;
if ((ret = AndroidBitmap_lockPixels(env, bitmapLCD, &pixelsDestination)) < 0) {
LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
return JNI_FALSE;
}
// if (nCurrentHardware == HDW_SACA)
// {
// TCHAR cBuffer[32]; // temp. buffer for text
// MSG msg;
//
// // calculate bitmap origins from hWindowDC
// nxO = nLcdX; if (nxO > 1) { nxO -= 2; nxSize += 4; };
// nyO = nLcdY; if (nyO > 1) { nyO -= 2; nySize += 4; };
//
// hSrcDC = hWindowDC; // use output HDC as source
// }
EnterCriticalSection(&csGDILock); // solving NT GDI problems
// copy display area
//BitBlt(hBmpDC,0,0,nxSize,nySize,hSrcDC,nxO,nyO,SRCCOPY);
size_t strideSource = ((unsigned int) (4 * ((hBmp->bitmapInfoHeader->biWidth *
hBmp->bitmapInfoHeader->biBitCount + 31) /
32)));
size_t strideDestination = bitmapLCDInfo.stride;
VOID *bitmapBitsSource = (VOID *) hBmp->bitmapBits;
VOID *bitmapBitsDestination = pixelsDestination;
int biHeight = abs(hBmp->bitmapInfoHeader->biHeight);
for (int y = 0; y < biHeight; y++) {
memcpy(bitmapBitsDestination, bitmapBitsSource, strideSource);
bitmapBitsSource += strideSource;
bitmapBitsDestination += strideDestination;
}
LeaveCriticalSection(&csGDILock);
AndroidBitmap_unlockPixels(env, bitmapLCD);
return JNI_TRUE;
}
return JNI_FALSE;
}
JNIEXPORT void JNICALL Java_org_emulator_calculator_NativeLib_draw(JNIEnv *env, jobject thisz) {
draw();
@ -1117,6 +1190,12 @@ JNIEXPORT jint JNICALL Java_org_emulator_calculator_NativeLib_getState(JNIEnv *e
return nState;
}
JNIEXPORT jint JNICALL Java_org_emulator_calculator_NativeLib_getScreenPositionX(JNIEnv *env, jobject thisz) {
return nLcdX;
}
JNIEXPORT jint JNICALL Java_org_emulator_calculator_NativeLib_getScreenPositionY(JNIEnv *env, jobject thisz) {
return nLcdY;
}
JNIEXPORT jint JNICALL Java_org_emulator_calculator_NativeLib_getScreenWidth(JNIEnv *env, jobject thisz) {
return 131*nLcdZoom*nGdiXZoom;
}

View file

@ -23,6 +23,8 @@
#include "core/resource.h"
#include "win32-layer.h"
#include "emu.h"
#include "core/lodepng.h"
extern JavaVM *java_machine;
extern jobject bitmapMainScreen;
@ -837,9 +839,189 @@ BOOL GetSaveFileName(LPOPENFILENAME openFilename) {
return FALSE;
}
// Almost the same function as the private core function DibNumColors()
static __inline WORD DibNumColors(BITMAPINFOHEADER CONST *lpbi)
{
if (lpbi->biClrUsed != 0) return (WORD) lpbi->biClrUsed;
/* a 24 bitcount DIB has no color table */
return (lpbi->biBitCount <= 8) ? (1 << lpbi->biBitCount) : 0;
}
static HBITMAP DecodeBMPIcon(LPBYTE imageBuffer, size_t imageSize) {
// size of bitmap header information
DWORD dwFileSize = sizeof(BITMAPINFOHEADER);
if (imageSize < dwFileSize)
return NULL;
LPBITMAPINFO pBmi = (LPBITMAPINFO)imageBuffer;
// size with color table
if (pBmi->bmiHeader.biCompression == BI_BITFIELDS)
dwFileSize += 3 * sizeof(DWORD);
else
dwFileSize += DibNumColors(&pBmi->bmiHeader) * sizeof(RGBQUAD);
pBmi->bmiHeader.biHeight /= 2;
DWORD stride = (((pBmi->bmiHeader.biWidth * (DWORD)pBmi->bmiHeader.biBitCount) + 31) / 32 * 4);
DWORD height = (DWORD) abs(pBmi->bmiHeader.biHeight);
// size with bitmap data
if (pBmi->bmiHeader.biCompression != BI_RGB)
dwFileSize += pBmi->bmiHeader.biSizeImage;
else {
pBmi->bmiHeader.biSizeImage = stride * height;
dwFileSize += pBmi->bmiHeader.biSizeImage;
}
if (imageSize < dwFileSize)
return NULL;
HBITMAP hBitmap = CreateDIBitmap(hWindowDC, &pBmi->bmiHeader, CBM_INIT, imageBuffer, pBmi, DIB_RGB_COLORS);
if(hBitmap) {
// Inverse the height
BYTE *source = imageBuffer + dwFileSize - stride;
BYTE *destination = hBitmap->bitmapBits;
for (int i = 0; i < height; ++i) {
memcpy(destination, source, stride);
source -= stride;
destination += stride;
}
}
// Only support 32bits RGBA BMP for now.
return hBitmap;
}
static HBITMAP DecodePNGIcon(LPBYTE imageBuffer, size_t imageSize) {
HBITMAP hBitmap = NULL;
UINT uWidth,uHeight;
LPBYTE pbyImage = NULL;
UINT uError = lodepng_decode_memory(&pbyImage, &uWidth, &uHeight, imageBuffer, imageSize, LCT_RGBA, 8);
if (uError == 0) {
BITMAPINFO bmi;
ZeroMemory(&bmi, sizeof(bmi)); // init bitmap info
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = (LONG) uWidth;
bmi.bmiHeader.biHeight = (LONG) uHeight;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32; // create a true color DIB
bmi.bmiHeader.biCompression = BI_RGB;
// bitmap dimensions
LONG lBytesPerLine = (((bmi.bmiHeader.biWidth * bmi.bmiHeader.biBitCount) + 31) / 32 * 4);
bmi.bmiHeader.biSizeImage = (DWORD) (lBytesPerLine * bmi.bmiHeader.biHeight);
// allocate buffer for pixels
LPBYTE pbyPixels; // BMP buffer
hBitmap = CreateDIBSection(hWindowDC, &bmi, DIB_RGB_COLORS, (VOID **)&pbyPixels, NULL, 0);
if (hBitmap)
memcpy(pbyPixels, pbyImage, bmi.bmiHeader.biSizeImage);
}
if (pbyImage != NULL)
free(pbyImage);
return hBitmap;
}
// ICO (file format) https://en.wikipedia.org/wiki/ICO_(file_format)
// https://docs.microsoft.com/en-us/previous-versions/ms997538(v=msdn.10)
typedef struct {
WORD idReserved;
WORD idType;
WORD idCount;
} ICONDIR, *LPICONDIR;
typedef struct ICONDIRENTRY {
BYTE bWidth; // Specifies image width in pixels. Can be any number between 0 and 255. Value 0 means image width is 256 pixels.
BYTE bHeight; // Specifies image height in pixels. Can be any number between 0 and 255. Value 0 means image height is 256 pixels.
BYTE bColorCount; // Specifies number of colors in the color palette. Should be 0 if the image does not use a color palette.
BYTE bReserved; // Reserved. Should be 0.
WORD wPlanes; // In ICO format: Specifies color planes. Should be 0 or 1.
WORD wBitCount; // In ICO format: Specifies bits per pixel.
DWORD dwBytesInRes; // Specifies the size of the image's data in bytes
DWORD dwImageOffset; // Specifies the offset of BMP or PNG data from the beginning of the ICO/CUR file
} ICONDIRENTRY, *LPICONDIRENTRY;
typedef struct {
BITMAPINFOHEADER icHeader; // DIB header
RGBQUAD icColors[1]; // Color table
BYTE icXOR[1]; // DIB bits for XOR mask
BYTE icAND[1]; // DIB bits for AND mask
} ICONIMAGE, *LPICONIMAGE;
HANDLE LoadImage(HINSTANCE hInst, LPCSTR name, UINT type, int cx, int cy, UINT fuLoad) {
//TODO
return NULL;
HANDLE hIconFile = CreateFile(name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hIconFile == INVALID_HANDLE_VALUE)
return NULL;
ICONDIR fileHeader;
DWORD nNumberOfBytesToRead = 0;
if(ReadFile(hIconFile, &fileHeader, sizeof(ICONDIR), &nNumberOfBytesToRead, NULL) == FALSE || sizeof(ICONDIR) != nNumberOfBytesToRead) {
CloseHandle(hIconFile);
return NULL;
}
size_t iconsBufferSize = fileHeader.idCount * sizeof(ICONDIRENTRY);
ICONDIRENTRY * iconHeaderArray = malloc(iconsBufferSize);
nNumberOfBytesToRead = 0;
if(ReadFile(hIconFile, iconHeaderArray, iconsBufferSize, &nNumberOfBytesToRead, NULL) == FALSE || iconsBufferSize != nNumberOfBytesToRead) {
CloseHandle(hIconFile);
return NULL;
}
int maxWidth = -1;
int maxHeight = -1;
int maxBitPerPixel = -1;
int maxIconIndex = -1;
for (int i = 0; i < fileHeader.idCount; ++i) {
ICONDIRENTRY * iconHeader = &(iconHeaderArray[i]);
int width = iconHeader->bWidth == 0 ? 256 : iconHeader->bWidth;
int height = iconHeader->bHeight == 0 ? 256 : iconHeader->bHeight;
if(width >= maxWidth && height >= maxHeight && iconHeader->wBitCount > maxBitPerPixel) {
maxWidth = width;
maxHeight = height;
maxBitPerPixel = iconHeader->wBitCount;
maxIconIndex = i;
}
}
maxIconIndex = 1; // To test BMP
if(maxIconIndex == -1) {
CloseHandle(hIconFile);
return NULL;
}
DWORD dwBytesInRes = iconHeaderArray[maxIconIndex].dwBytesInRes;
LPBYTE iconBuffer = malloc(dwBytesInRes);
SetFilePointer(hIconFile, iconHeaderArray[maxIconIndex].dwImageOffset, 0, FILE_BEGIN);
if(ReadFile(hIconFile, iconBuffer, dwBytesInRes, &nNumberOfBytesToRead, NULL) == FALSE || dwBytesInRes != nNumberOfBytesToRead) {
CloseHandle(hIconFile);
free(iconHeaderArray);
free(iconBuffer);
return NULL;
}
CloseHandle(hIconFile);
HBITMAP icon = NULL;
if (dwBytesInRes >= 8 && memcmp(iconBuffer, "\x89PNG\r\n\x1a\n", 8) == 0)
// It is a PNG image
icon = DecodePNGIcon(iconBuffer, dwBytesInRes);
else
// It is a BMP image
icon = DecodeBMPIcon(iconBuffer, dwBytesInRes);
free(iconHeaderArray);
free(iconBuffer);
if(!icon)
return NULL;
HANDLE handle = malloc(sizeof(struct _HANDLE));
memset(handle, 0, sizeof(struct _HANDLE));
handle->handleType = HANDLE_TYPE_ICON;
handle->icon = icon;
return handle;
}
@ -871,8 +1053,17 @@ LRESULT SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
return selItemDataIndex[wParam];
}
}
if(Msg == WM_SETICON) {
if(Msg == WM_SETICON && wParam == ICON_BIG) {
if(lParam != NULL) {
HANDLE hIcon = (HANDLE)lParam;
if(hIcon->handleType == HANDLE_TYPE_ICON && hIcon->icon != NULL) {
HBITMAP icon = hIcon->icon;
if(icon && icon->bitmapInfoHeader && icon->bitmapBits)
setKMLIcon(icon->bitmapInfoHeader->biWidth, icon->bitmapInfoHeader->biHeight, icon->bitmapBits, icon->bitmapInfoHeader->biSizeImage);
}
} else {
setKMLIcon(0, 0, NULL, 0);
}
}
return NULL;
}
@ -1964,7 +2155,7 @@ BOOL StretchBlt(HDC hdcDest, int xDest, int yDest, int wDest, int hDest, HDC hdc
}
}
if(jniEnv && (ret = AndroidBitmap_unlockPixels(jniEnv, bitmapMainScreen)) < 0) {
if(jniEnv && hdcDest->hdcCompatible == NULL && (ret = AndroidBitmap_unlockPixels(jniEnv, bitmapMainScreen)) < 0) {
LOGD("AndroidBitmap_unlockPixels() failed ! error=%d", ret);
return FALSE;
}
@ -1996,8 +2187,7 @@ HBITMAP CreateBitmap( int nWidth, int nHeight, UINT nPlanes, UINT nBitCount, CON
BITMAPINFO * newBitmapInfo = malloc(sizeof(BITMAPINFO));
memset(newBitmapInfo, 0, sizeof(BITMAPINFO));
//newBitmapInfo->bmiHeader.biBitCount = 32; //TODO should be nBitCount
newBitmapInfo->bmiHeader.biBitCount = nBitCount; //TODO should be nBitCount
newBitmapInfo->bmiHeader.biBitCount = (WORD) nBitCount;
newBitmapInfo->bmiHeader.biClrUsed = 0;
newBitmapInfo->bmiHeader.biWidth = nWidth;
newBitmapInfo->bmiHeader.biHeight = -nHeight;

View file

@ -507,20 +507,24 @@ enum HANDLE_TYPE {
HANDLE_TYPE_EVENT,
HANDLE_TYPE_THREAD,
HANDLE_TYPE_WINDOW,
HANDLE_TYPE_ICON,
};
struct _HANDLE {
enum HANDLE_TYPE handleType;
// HANDLE_TYPE_FILE*
int fileDescriptor;
BOOL fileOpenFileFromContentResolver;
AAsset* fileAsset;
// HANDLE_TYPE_FILE_MAPPING*
off_t fileMappingOffset;
size_t fileMappingSize;
void* fileMappingAddress;
DWORD fileMappingProtect;
// HANDLE_TYPE_THREAD
pthread_t threadId;
DWORD (*threadStartAddress)(LPVOID);
LPVOID threadParameter;
@ -528,12 +532,17 @@ struct _HANDLE {
struct tagMSG threadMessage;
int threadIndex;
// HANDLE_TYPE_EVENT
pthread_cond_t eventCVariable;
pthread_mutex_t eventMutex;
BOOL eventAutoReset;
BOOL eventState;
HDC windowDC;
// HANDLE_TYPE_WINDOW
HDC windowDC;
// HANDLE_TYPE_ICON
HBITMAP icon;
};
typedef struct _HANDLE * HANDLE;
@ -1227,6 +1236,7 @@ void clipboardCopyText(const TCHAR * text);
const TCHAR * clipboardPasteText();
void performHapticFeedback();
void sendByteUdp(unsigned char byteSent);
void setKMLIcon(int imageWidth, int imageHeight, LPBYTE buffer, int bufferSize);
typedef int SOCKET;

View file

@ -0,0 +1,316 @@
package org.emulator.calculator;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.preference.PreferenceManager;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
public class LCDOverlappingView extends View {
protected static final String TAG = "LCDOverlappingView";
protected final boolean debug = false;
private SharedPreferences sharedPreferences;
private Paint paint = new Paint();
private Bitmap bitmapLCD;
private float bitmapRatio = -1;
private float minViewSize = 200.0f;
private int overlappingLCDMode = 1;
private MainScreenView mainScreenView;
public LCDOverlappingView(Context context, MainScreenView mainScreenView) {
super(context);
this.mainScreenView = mainScreenView;
this.mainScreenView.setOnUpdateLayoutListener(() -> this.updateLayout(this.mainScreenView.viewPanOffsetX, this.mainScreenView.viewPanOffsetY, this.mainScreenView.viewScaleFactorX, this.mainScreenView.viewScaleFactorY));
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext());
paint.setFilterBitmap(true);
paint.setAntiAlias(true);
DisplayMetrics displayMetrics = new DisplayMetrics();
((Activity)context).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
bitmapLCD = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
bitmapLCD.eraseColor(Color.BLACK);
this.setFocusable(true);
this.setFocusableInTouchMode(true);
}
private float previousX0 = -1.0f, previousY0 = -1.0f, previousX1 = -1.0f, previousY1 = -1.0f;
private float previousDownX0 = 0, previousDownY0 = 0;
private float distanceBetweenTwoPoints(float fromPointX, float fromPointY, float toPointX, float toPointY) {
float x = toPointX - fromPointX;
float y = toPointY - fromPointY;
return (float)Math.sqrt(x * x + y * y);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if(debug) Log.d(TAG, "onTouchEvent() getAction(): " + event.getAction() + ", getPointerCount(): " + event.getPointerCount());
int touchCount = event.getPointerCount();
float currentX0 = 0f, currentY0 = 0f, currentX1 = 0f, currentY1 = 0f;
if(touchCount > 0) {
currentX0 = event.getX(0);
currentY0 = event.getY(0);
}
if(touchCount > 1) {
currentX1 = event.getX(1);
currentY1 = event.getY(1);
}
if(debug) {
Log.d(TAG, "onTouchEvent() currentX0: " + currentX0 + ", currentY0: " + currentY0);
Log.d(TAG, "onTouchEvent() currentX1: " + currentX1 + ", currentY1: " + currentY1);
Log.d(TAG, "onTouchEvent() touchCount: " + touchCount);
}
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
if(debug) Log.d(TAG, "onTouchEvent() touchesBegan count: " + touchCount);
previousDownX0 = currentX0;
previousDownY0 = currentY0;
break;
case MotionEvent.ACTION_MOVE:
//if(debug) Log.d(TAG, "touchesMoved count: " + touchCount);
if (touchCount == 1) {
FrameLayout.LayoutParams viewFlowLayout = (FrameLayout.LayoutParams)getLayoutParams();
viewFlowLayout.leftMargin += currentX0 - previousDownX0;
viewFlowLayout.topMargin += currentY0 - previousDownY0;
setLayoutParams(viewFlowLayout);
changeOverlappingLCDMode();
} else if (touchCount == 2) {
if(previousX0 != -1.0f) {
FrameLayout.LayoutParams viewFlowLayout = (FrameLayout.LayoutParams)getLayoutParams();
float scaleFactor = distanceBetweenTwoPoints(currentX0, currentY0, currentX1, currentY1) / distanceBetweenTwoPoints(previousX0, previousY0, previousX1, previousY1);
float scaledViewWidth = (float)viewFlowLayout.width * scaleFactor;
float scaledViewHeight = (float)viewFlowLayout.height * scaleFactor;
float deltaFactor = (scaleFactor - 1.0f) / 2.0f;
int deltaW = (int) ((float) viewFlowLayout.width * deltaFactor);
int deltaH = (int) ((float) viewFlowLayout.height * deltaFactor);
if (debug)
Log.d(TAG, "onTouchEvent() scaleFactor: " + scaleFactor + ", currentWidth: " + viewFlowLayout.width + ", currentHeight: " + viewFlowLayout.height
+ ", deltaW: " + deltaW + ", deltaH: " + deltaH);
if (debug)
Log.d(TAG, "onTouchEvent() BEFORE leftMargin: " + viewFlowLayout.leftMargin + ", topMargin: " + viewFlowLayout.topMargin
+ ", width: " + viewFlowLayout.width + ", height: " + viewFlowLayout.height);
viewFlowLayout.leftMargin -= deltaW;
viewFlowLayout.topMargin -= deltaH;
int newViewWidth, newViewHeight;
if(bitmapRatio > 0.0f) {
if(bitmapRatio < 1.0f) {
newViewWidth = (int) scaledViewWidth;
newViewHeight = (int) (newViewWidth * bitmapRatio);
} else {
newViewHeight = (int) scaledViewHeight;
newViewWidth = (int) (newViewHeight / bitmapRatio);
}
} else {
newViewWidth = (int) scaledViewWidth;
newViewHeight = (int) scaledViewHeight;
}
if(newViewWidth >= minViewSize && newViewWidth >= minViewSize) {
viewFlowLayout.width = newViewWidth;
viewFlowLayout.height = newViewHeight;
}
if (debug)
Log.d(TAG, "onTouchEvent() AFTER leftMargin: " + viewFlowLayout.leftMargin + ", topMargin: " + viewFlowLayout.topMargin
+ ", width: " + viewFlowLayout.width + ", height: " + viewFlowLayout.height);
setLayoutParams(viewFlowLayout);
changeOverlappingLCDMode();
}
previousX0 = currentX0;
previousY0 = currentY0;
previousX1 = currentX1;
previousY1 = currentY1;
}
break;
case MotionEvent.ACTION_UP:
previousX0 = -1.0f;
previousY0 = -1.0f;
previousX1 = -1.0f;
previousY1 = -1.0f;
break;
case MotionEvent.ACTION_CANCEL:
break;
case MotionEvent.ACTION_OUTSIDE:
break;
default:
}
return true; // processed
}
@Override
protected void onDraw(Canvas canvas) {
//if(debug) Log.d(TAG, "onDraw()");
canvas.drawColor(Color.RED);
if(this.overlappingLCDMode > 0 && bitmapLCD != null) {
canvas.save();
if (bitmapLCD.getWidth() > 0 && bitmapLCD.getHeight() > 0)
canvas.scale((float) getWidth() / (float) bitmapLCD.getWidth(), (float) getHeight() / (float) bitmapLCD.getHeight());
canvas.drawBitmap(bitmapLCD, 0, 0, paint);
canvas.restore();
}
}
public int updateCallback(int type, int param1, int param2, String param3, String param4) {
if(this.overlappingLCDMode == 0)
return -1;
switch (type) {
case NativeLib.CALLBACK_TYPE_INVALIDATE:
//if(debug) Log.d(TAG, "PAINT updateCallback() postInvalidate()");
if(debug) Log.d(TAG, "updateCallback() CALLBACK_TYPE_INVALIDATE");
NativeLib.copyLCD(bitmapLCD);
postInvalidate();
break;
case NativeLib.CALLBACK_TYPE_WINDOW_RESIZE:
// New Bitmap size
int newLCDWidth = NativeLib.getScreenWidth();
int newLCDHeight = NativeLib.getScreenHeight();
if(bitmapLCD == null || bitmapLCD.getWidth() != newLCDWidth || bitmapLCD.getHeight() != newLCDHeight) {
int newWidth = Math.max(1, newLCDWidth);
int newHeight = Math.max(1, newLCDHeight);
if(debug) Log.d(TAG, "updateCallback() Bitmap.createBitmap(x: " + newWidth + ", y: " + newHeight + ")");
Bitmap oldBitmapLCD = bitmapLCD;
bitmapLCD = Bitmap.createBitmap(newWidth, newHeight, Bitmap.Config.ARGB_8888);
bitmapRatio = (float)newHeight / (float)newWidth;
if(oldBitmapLCD != null)
oldBitmapLCD.recycle();
if(this.overlappingLCDMode != 1) {
setVisibility(View.VISIBLE);
float scale = sharedPreferences.getFloat("settings_lcd_overlapping_scale", 1.0f);
if (scale < 0.01f)
scale = 0.01f;
int viewWidth = (int) (newWidth * scale);
int viewHeight = (int) (newHeight * scale);
if (viewWidth < minViewSize && viewHeight < minViewSize) {
if (bitmapRatio > 0.0f) {
if (bitmapRatio < 1.0f) {
viewWidth = (int) minViewSize;
viewHeight = (int) (viewWidth * bitmapRatio);
} else {
viewHeight = (int) minViewSize;
viewWidth = (int) (viewHeight / bitmapRatio);
}
} else {
viewWidth = (int) minViewSize;
viewHeight = (int) minViewSize;
}
}
FrameLayout.LayoutParams viewFlowLayout = new FrameLayout.LayoutParams(viewWidth, viewHeight);
viewFlowLayout.leftMargin = sharedPreferences.getInt("settings_lcd_overlapping_x", 20);
viewFlowLayout.topMargin = sharedPreferences.getInt("settings_lcd_overlapping_y", 80);
if (viewFlowLayout.leftMargin + viewWidth < 0)
viewFlowLayout.leftMargin = 0;
if (viewFlowLayout.topMargin + viewHeight < 0)
viewFlowLayout.topMargin = 0;
setLayoutParams(viewFlowLayout);
}
}
break;
}
return -1;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if(debug) Log.d(TAG, "onSizeChanged() width: " + w + ", height: " + h);
}
public void updateLayout(float viewPanOffsetX, float viewPanOffsetY, float viewScaleFactorX, float viewScaleFactorY) {
if(debug) Log.d(TAG, "updateLayout()");
if(this.overlappingLCDMode == 0) // No Overlapping LCD
return;
if(this.overlappingLCDMode == 1) { // Auto
int newLCDWidth = NativeLib.getScreenWidth();
int newLCDHeight = NativeLib.getScreenHeight();
int newWidth = Math.max(1, newLCDWidth);
int newHeight = Math.max(1, newLCDHeight);
post(() -> {
FrameLayout.LayoutParams viewFlowLayout = new FrameLayout.LayoutParams((int) (newWidth * viewScaleFactorX), (int) (newHeight * viewScaleFactorY));
viewFlowLayout.leftMargin = (int)(viewScaleFactorX * NativeLib.getScreenPositionX() + viewPanOffsetX);
viewFlowLayout.topMargin = (int)(viewScaleFactorY * NativeLib.getScreenPositionY() + viewPanOffsetY);
if(debug) Log.d(TAG, "updateLayout() leftMargin: " + viewFlowLayout.leftMargin + ", topMargin: " + viewFlowLayout.topMargin
+ ", width: " + viewFlowLayout.width + ", height: " + viewFlowLayout.height);
setLayoutParams(viewFlowLayout);
setVisibility(View.VISIBLE);
});
}
}
public void saveViewLayout() {
if(this.overlappingLCDMode > 0)
return;
FrameLayout.LayoutParams viewFlowLayout = (FrameLayout.LayoutParams)getLayoutParams();
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("settings_lcd_overlapping_mode", Integer.toString(this.overlappingLCDMode));
editor.putInt("settings_lcd_overlapping_x", viewFlowLayout.leftMargin);
editor.putInt("settings_lcd_overlapping_y", viewFlowLayout.topMargin);
editor.putFloat("settings_lcd_overlapping_scale", bitmapLCD != null && bitmapLCD.getWidth() > 0 ? (float)viewFlowLayout.width / (float)bitmapLCD.getWidth() : 1.0f);
editor.apply();
}
private void changeOverlappingLCDMode() {
if(this.overlappingLCDMode == 1) { // Mode Auto
this.overlappingLCDMode = 2; // We change the mode to Manual
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("settings_lcd_overlapping_mode", Integer.toString(this.overlappingLCDMode));
editor.apply();
Context context = getContext();
if(context != null)
Utils.showAlert(context, context.getString(Utils.resId(context, "string", "message_change_overlapping_lcd_mode_to_manual")));
}
}
public void setOverlappingLCDMode(int overlappingLCDMode, boolean isDynamic) {
if(debug) Log.d(TAG, "setOverlappingLCDMode(" + overlappingLCDMode + ")");
int previousOverlappingLCDMode = this.overlappingLCDMode;
this.overlappingLCDMode = overlappingLCDMode;
if(previousOverlappingLCDMode == 0) { // Off 0
if(overlappingLCDMode == 1) // Auto 1
this.updateLayout(this.mainScreenView.viewPanOffsetX, this.mainScreenView.viewPanOffsetY, this.mainScreenView.viewScaleFactorX, this.mainScreenView.viewScaleFactorY);
else if(overlappingLCDMode == 2) // Manual 2
setVisibility(VISIBLE);
} else if(previousOverlappingLCDMode == 1) { // Auto 1
if(overlappingLCDMode == 0) // Off 0
setVisibility(GONE);
} else if(previousOverlappingLCDMode == 2) { // Manual 2
if(overlappingLCDMode == 0) // Off 0
setVisibility(GONE);
else if(overlappingLCDMode == 1) // Auto 1
this.updateLayout(this.mainScreenView.viewPanOffsetX, this.mainScreenView.viewPanOffsetY, this.mainScreenView.viewScaleFactorX, this.mainScreenView.viewScaleFactorY);
}
}
}

View file

@ -61,7 +61,7 @@ public class MainScreenView extends PanAndScaleView {
DisplayMetrics displayMetrics = new DisplayMetrics();
((Activity)context).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
bitmapMainScreen = Bitmap.createBitmap(displayMetrics.widthPixels, displayMetrics.heightPixels, Bitmap.Config.ARGB_8888);
bitmapMainScreen = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
bitmapMainScreen.eraseColor(Color.BLACK);
enableZoomKeyboard = false;
@ -153,10 +153,6 @@ public class MainScreenView extends PanAndScaleView {
this.setFocusable(true);
this.setFocusableInTouchMode(true);
// This call is necessary, or else the
// draw method will not be called.
setWillNotDraw(false);
}
// Prevent accidental scroll when taping a calc button
@ -285,6 +281,8 @@ public class MainScreenView extends PanAndScaleView {
viewPanOffsetY = translateY;
constrainPan();
if(this.onUpdateLayoutListener != null)
this.onUpdateLayoutListener.run();
return;
}
}
@ -292,13 +290,19 @@ public class MainScreenView extends PanAndScaleView {
}
// Else, the screens orientations are the same, so we set the calculator in fullscreen
resetViewport();
if(this.onUpdateLayoutListener != null)
this.onUpdateLayoutListener.run();
}
}
/**
* Draw the score.
* @param canvas The canvas to draw to coming from the View.onDraw() method.
*/
private Runnable onUpdateLayoutListener = null;
public void setOnUpdateLayoutListener(Runnable onUpdateLayoutListener) {
this.onUpdateLayoutListener = onUpdateLayoutListener;
}
@Override
protected void onCustomDraw(Canvas canvas) {
//Log.d(TAG, "onCustomDraw()");
@ -307,17 +311,13 @@ public class MainScreenView extends PanAndScaleView {
canvas.drawBitmap(bitmapMainScreen, 0, 0, paint);
}
final int CALLBACK_TYPE_INVALIDATE = 0;
final int CALLBACK_TYPE_WINDOW_RESIZE = 1;
@SuppressWarnings("unused")
public int updateCallback(int type, int param1, int param2, String param3, String param4) {
switch (type) {
case CALLBACK_TYPE_INVALIDATE:
case NativeLib.CALLBACK_TYPE_INVALIDATE:
//Log.d(TAG, "PAINT updateCallback() postInvalidate()");
postInvalidate();
break;
case CALLBACK_TYPE_WINDOW_RESIZE:
case NativeLib.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) + ")");
@ -337,16 +337,11 @@ public class MainScreenView extends PanAndScaleView {
if(viewSized)
updateLayout();
}
//postInvalidate();
break;
}
return -1;
}
public Bitmap getBitmapMainScreen() {
return bitmapMainScreen;
}
public void setRotationMode(int rotationMode, boolean isDynamic) {
this.rotationMode = rotationMode;
if(isDynamic) {

View file

@ -24,9 +24,13 @@ public class NativeLib {
System.loadLibrary("native-lib");
}
public static native void start(AssetManager mgr, Bitmap bitmapMainScreen, Activity activity, MainScreenView view);
public static final int CALLBACK_TYPE_INVALIDATE = 0;
public static final int CALLBACK_TYPE_WINDOW_RESIZE = 1;
public static native void start(AssetManager mgr, Activity activity);
public static native void stop();
public static native void changeBitmap(Bitmap bitmapMainScreen);
public static native boolean copyLCD(Bitmap bitmapLCD);
public static native void draw();
public static native boolean buttonDown(int x, int y);
public static native void buttonUp(int x, int y);
@ -71,6 +75,8 @@ public class NativeLib {
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();
public static native int getScreenPositionX();
public static native int getScreenPositionY();
public static native int getScreenWidth();
public static native int getScreenHeight();
}

View file

@ -528,13 +528,13 @@ public class PanAndScaleView extends View {
// Keep the panning limits and the image centered.
float viewWidth = viewSizeWidth;
float viewHeight = viewSizeHeight;
if(viewWidth == 0.0f){
if(viewWidth == 0.0f) {
viewWidth = 1.0f;
viewHeight = 1.0f;
}
float virtualWidth = virtualSizeWidth;
float virtualHeight = virtualSizeHeight;
if(virtualWidth == 0.0f){
if(virtualWidth == 0.0f) {
virtualWidth = 1.0f;
virtualHeight = 1.0f;
}

View file

@ -37,7 +37,9 @@ import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
@ -56,6 +58,7 @@ import com.google.android.material.navigation.NavigationView;
import org.emulator.calculator.InfoActivity;
import org.emulator.calculator.InfoWebActivity;
import org.emulator.calculator.LCDOverlappingView;
import org.emulator.calculator.MainScreenView;
import org.emulator.calculator.NativeLib;
import org.emulator.calculator.PrinterSimulator;
@ -69,6 +72,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@ -92,6 +96,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
private NavigationView navigationView;
private DrawerLayout drawer;
private MainScreenView mainScreenView;
private LCDOverlappingView lcdOverlappingView;
private ImageButton imageButtonMenu;
public static final int INTENT_GETOPENFILENAME = 1;
@ -127,6 +132,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
private PrinterSimulator printerSimulator = new PrinterSimulator();
private PrinterSimulatorFragment fragmentPrinterSimulator = new PrinterSimulatorFragment();
private Bitmap bitmapIcon;
@Override
@ -151,23 +157,23 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
mainScreenView = new MainScreenView(this);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
mainScreenView.setStatusBarColor(getWindow().getStatusBarColor());
mainScreenView.setLayoutParams(new ViewGroup.LayoutParams(
mainScreenContainer.addView(mainScreenView, 0, new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
mainScreenContainer.addView(mainScreenView, 0);
lcdOverlappingView = new LCDOverlappingView(this, mainScreenView);
lcdOverlappingView.setVisibility(View.GONE);
mainScreenContainer.addView(lcdOverlappingView, 1, new FrameLayout.LayoutParams(0, 0));
imageButtonMenu = findViewById(R.id.button_menu);
imageButtonMenu.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(drawer != null)
drawer.openDrawer(GravityCompat.START);
}
imageButtonMenu.setOnClickListener(v -> {
if(drawer != null)
drawer.openDrawer(GravityCompat.START);
});
showCalculatorView(false);
AssetManager assetManager = getResources().getAssets();
NativeLib.start(assetManager, mainScreenView.getBitmapMainScreen(), this, mainScreenView);
NativeLib.start(assetManager, this);
// By default Port1 is set
setPort1Settings(true, true);
@ -278,6 +284,9 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
editor.putStringSet("MRU", mruLinkedHashMap.keySet());
editor.apply();
if(lcdOverlappingView != null)
lcdOverlappingView.saveViewLayout();
super.onStop();
}
@ -842,9 +851,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
new AlertDialog.Builder(MainActivity.this)
.setTitle(getString(R.string.message_kml_folder_selection_need_api_lollipop))
.setMessage(getString(R.string.message_kml_folder_selection_need_api_lollipop_description))
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
}
.setPositiveButton(android.R.string.ok, (dialog1, which1) -> {
}).show();
} else {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
@ -880,47 +887,44 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
String[] stringArrayRAMCards = getResources().getStringArray(R.array.ram_cards);
new AlertDialog.Builder(MainActivity.this)
.setTitle(getResources().getString(R.string.create_ram_card_title))
.setItems(stringArrayRAMCards, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
String sizeTitle = "2mb";
selectedRAMSize = -1;
switch (which) {
case 0: // 32kb (1 port: 2)
sizeTitle = "32kb";
selectedRAMSize = 32;
break;
case 1: // 128kb (1 port: 2)
sizeTitle = "128kb";
selectedRAMSize = 128;
break;
case 2: // 256kb (2 ports: 2,3)
sizeTitle = "256kb";
selectedRAMSize = 256;
break;
case 3: // 512kb (4 ports: 2 through 5)
sizeTitle = "512kb";
selectedRAMSize = 512;
break;
case 4: // 1mb (8 ports: 2 through 9)
sizeTitle = "1mb";
selectedRAMSize = 1024;
break;
case 5: // 2mb (16 ports: 2 through 17)
sizeTitle = "2mb";
selectedRAMSize = 2048;
break;
case 6: // 4mb (32 ports: 2 through 33)
sizeTitle = "4mb";
selectedRAMSize = 4096;
break;
}
intent.putExtra(Intent.EXTRA_TITLE, "shared-" + sizeTitle + ".bin");
startActivityForResult(intent, INTENT_CREATE_RAM_CARD);
.setItems(stringArrayRAMCards, (dialog, which) -> {
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
String sizeTitle = "2mb";
selectedRAMSize = -1;
switch (which) {
case 0: // 32kb (1 port: 2)
sizeTitle = "32kb";
selectedRAMSize = 32;
break;
case 1: // 128kb (1 port: 2)
sizeTitle = "128kb";
selectedRAMSize = 128;
break;
case 2: // 256kb (2 ports: 2,3)
sizeTitle = "256kb";
selectedRAMSize = 256;
break;
case 3: // 512kb (4 ports: 2 through 5)
sizeTitle = "512kb";
selectedRAMSize = 512;
break;
case 4: // 1mb (8 ports: 2 through 9)
sizeTitle = "1mb";
selectedRAMSize = 1024;
break;
case 5: // 2mb (16 ports: 2 through 17)
sizeTitle = "2mb";
selectedRAMSize = 2048;
break;
case 6: // 4mb (32 ports: 2 through 33)
sizeTitle = "4mb";
selectedRAMSize = 4096;
break;
}
intent.putExtra(Intent.EXTRA_TITLE, "shared-" + sizeTitle + ".bin");
startActivityForResult(intent, INTENT_CREATE_RAM_CARD);
}).show();
}
@ -941,12 +945,9 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
}
private void OnMacroStop() {
runOnUiThread(new Runnable() {
@Override
public void run() {
NativeLib.onToolMacroStop();
updateNavigationDrawerItems();
}
runOnUiThread(() -> {
NativeLib.onToolMacroStop();
updateNavigationDrawerItems();
});
}
@ -1003,11 +1004,9 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
new AlertDialog.Builder(this)
.setTitle(getString(R.string.message_open_security))
.setMessage(getString(R.string.message_open_security_description))
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, INTENT_PICK_KML_FOLDER_FOR_SECURITY);
}
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, INTENT_PICK_KML_FOLDER_FOR_SECURITY);
}).show();
}
break;
@ -1059,9 +1058,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
new AlertDialog.Builder(this)
.setTitle(getString(R.string.message_open_security_retry))
.setMessage(getString(R.string.message_open_security_retry_description))
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
}
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
}).show();
break;
}
@ -1122,11 +1119,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
if(url != null && !url.isEmpty())
mruLinkedHashMap.put(url, null);
navigationView.post(new Runnable() {
public void run() {
updateMRU();
}
});
navigationView.post(this::updateMRU);
}
private void makeUriPersistable(Intent data, Uri uri) {
@ -1167,10 +1160,12 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
private void displayFilename(String url) {
String displayName = getFilenameFromURL(url);
View header = displayKMLTitle();
TextView textViewSubtitle = header.findViewById(R.id.nav_header_subtitle);
if(textViewSubtitle != null)
textViewSubtitle.setText(displayName);
View headerView = displayKMLTitle();
if(headerView != null) {
TextView textViewSubtitle = headerView.findViewById(R.id.nav_header_subtitle);
if (textViewSubtitle != null)
textViewSubtitle.setText(displayName);
}
}
private String getFilenameFromURL(String url) {
@ -1184,12 +1179,32 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
}
private View displayKMLTitle() {
View headerView = null;
NavigationView navigationView = findViewById(R.id.nav_view);
View header = navigationView.getHeaderView(0);
TextView textViewTitle = header.findViewById(R.id.nav_header_title);
if(textViewTitle != null)
textViewTitle.setText(NativeLib.getKMLTitle());
return header;
if (navigationView != null) {
headerView = navigationView.getHeaderView(0);
if (headerView != null) {
TextView textViewTitle = headerView.findViewById(R.id.nav_header_title);
if (textViewTitle != null)
textViewTitle.setText(NativeLib.getKMLTitle());
changeHeaderIcon();
}
}
return headerView;
}
private void changeHeaderIcon() {
NavigationView navigationView = findViewById(R.id.nav_view);
View headerView = navigationView.getHeaderView(0);
if (headerView != null) {
ImageView imageViewIcon = headerView.findViewById(R.id.nav_header_icon);
if (imageViewIcon != null) {
if (bitmapIcon != null)
imageViewIcon.setImageBitmap(bitmapIcon);
else
imageViewIcon.setImageDrawable(null);
}
}
}
private void showKMLLog() {
@ -1203,14 +1218,21 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
new AlertDialog.Builder(this)
.setTitle(getString(R.string.message_kml_script_compilation_result))
.setMessage(kmlLog)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
}
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
}).show();
}
// Method used from JNI!
@SuppressWarnings("unused")
public int updateCallback(int type, int param1, int param2, String param3, String param4) {
mainScreenView.updateCallback(type, param1, param2, param3, param4);
lcdOverlappingView.updateCallback(type, param1, param2, param3, param4);
return -1;
}
final int GENERIC_READ = 1;
final int GENERIC_WRITE = 2;
SparseArray<ParcelFileDescriptor> parcelFileDescriptorPerFd = null;
@ -1421,6 +1443,25 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
printerSimulator.write(byteSent);
}
@SuppressWarnings("unused")
public synchronized void setKMLIcon(int imageWidth, int imageHeight, byte[] pixels) {
if(imageWidth > 0 && imageHeight > 0 && pixels != null) {
try {
bitmapIcon = Bitmap.createBitmap(imageWidth, imageHeight, Bitmap.Config.ARGB_8888);
ByteBuffer buffer = ByteBuffer.wrap(pixels);
bitmapIcon.copyPixelsFromBuffer(buffer);
} catch (Exception ex) {
// Cannot load the icon
bitmapIcon.recycle();
bitmapIcon = null;
}
} else if(bitmapIcon != null) {
bitmapIcon.recycle();
bitmapIcon = null;
}
changeHeaderIcon();
}
private void setPort1Settings(boolean port1Plugged, boolean port1Writable) {
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putBoolean("settings_port1en", port1Plugged);
@ -1433,7 +1474,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
int isDynamicValue = isDynamic ? 1 : 0;
if(key == null) {
String[] settingKeys = {
"settings_realspeed", "settings_grayscale", "settings_rotation", "settings_auto_layout", "settings_allow_pinch_zoom",
"settings_realspeed", "settings_grayscale", "settings_rotation", "settings_auto_layout", "settings_allow_pinch_zoom", "settings_lcd_overlapping_mode",
"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",
@ -1471,6 +1512,15 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
case "settings_allow_pinch_zoom":
mainScreenView.setAllowPinchZoom(sharedPreferences.getBoolean("settings_allow_pinch_zoom", true));
break;
case "settings_lcd_overlapping_mode":
int overlappingLCDMode = 0;
try {
overlappingLCDMode = Integer.parseInt(sharedPreferences.getString("settings_lcd_overlapping_mode", "0"));
} catch (NumberFormatException ex) {
// Catch bad number format
}
lcdOverlappingView.setOverlappingLCDMode(overlappingLCDMode, isDynamic);
break;
case "settings_hide_bar":
case "settings_hide_bar_status":
case "settings_hide_bar_nav":

View file

@ -1,14 +1,9 @@
<?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"
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main_screen_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#00808080"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:showIn="@layout/activity_main">
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#00808080">
<ImageButton
android:id="@+id/button_menu"
@ -18,7 +13,5 @@
android:layout_marginTop="16dp"
android:background="@android:color/transparent"
android:src="@drawable/ic_more_vert_black_24dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:contentDescription="@string/button_main_menu_content_description" />
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>

View file

@ -1,29 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<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:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="90dp"
android:layout_height="wrap_content"
android:background="@drawable/side_nav_bar"
android:gravity="bottom"
android:orientation="vertical"
android:padding="8dp"
android:theme="@style/ThemeOverlay.AppCompat.Dark">
<TextView
android:id="@+id/nav_header_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
<ImageView
android:id="@+id/nav_header_icon"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:contentDescription="@string/nav_header_desc"
android:paddingTop="@dimen/nav_header_vertical_spacing"
android:text=""
android:textAppearance="@style/TextAppearance.AppCompat.Body1" />
app:layout_constraintBottom_toTopOf="@+id/nav_header_title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" />
<TextView
android:id="@+id/nav_header_subtitle"
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:paddingTop="@dimen/nav_header_vertical_spacing"
android:text=""
android:textAppearance="@style/TextAppearance.AppCompat.Body1" />
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
app:layout_constraintBottom_toBottomOf="@+id/nav_header_icon"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/nav_header_icon" />
</LinearLayout>
<TextView
android:id="@+id/nav_header_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:paddingTop="@dimen/nav_header_vertical_spacing"
android:text=""
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -36,6 +36,17 @@
<item>2</item>
</string-array>
<string-array name="settings_lcd_overlapping_mode_item">
<item>No Overlapping LCD</item>
<item>Auto</item>
<item>Manual</item>
</string-array>
<string-array name="settings_lcd_overlapping_mode_value">
<item>0</item>
<item>1</item>
<item>2</item>
</string-array>
<string-array name="ram_cards">
<item>32kb (1 port: 2)</item>
<item>128kb (1 port: 2)</item>

View file

@ -20,6 +20,7 @@
<string name="action_settings">Settings</string>
<string name="navigation_drawer_open">Open navigation drawer</string>
<string name="navigation_drawer_close">Close navigation drawer</string>
<string name="nav_header_desc">Navigation header</string>
<string name="button_main_menu_content_description">Open the main menu</string>
<string name="nav_new">New...</string>
<string name="nav_open">Open...</string>
@ -84,7 +85,7 @@
<string name="message_printer_share_text">Save printer paper as text</string>
<string name="message_printer_share_graphic">Save printer paper as image</string>
<string name="message_printer_out_of_paper">Out of paper error for the graphic printer (max line: %d, max pixel line: %d).</string>
<string name="message_change_overlapping_lcd_mode_to_manual">The overlapping LCD mode has been changed to "Manual".</string>
<string name="settings_category_general_title">General</string>
@ -102,6 +103,8 @@
<string name="settings_rotation_summary">Allow to rotate, or force Portrait or Landscape orientation</string>
<string name="settings_allow_pinch_zoom_title">Allow to pinch to zoom</string>
<string name="settings_lcd_overlapping_mode_title">Overlapping LCD mode</string>
<string name="settings_lcd_overlapping_mode_summary">Select the overlapping LCD mode. A pan or pinch gesture will change the mode to manual.</string>
<string name="settings_hide_bar_status">Hide the status bar</string>
<string name="settings_hide_bar_nav">Hide the navigation bar</string>

View file

@ -48,6 +48,17 @@
android:key="settings_allow_pinch_zoom"
android:title="@string/settings_allow_pinch_zoom_title"
android:defaultValue="true" />
<ListPreference
android:key="settings_lcd_overlapping_mode"
android:title="@string/settings_lcd_overlapping_mode_title"
android:dialogTitle="@string/settings_lcd_overlapping_mode_title"
android:summary="@string/settings_lcd_overlapping_mode_summary"
android:entries="@array/settings_lcd_overlapping_mode_item"
android:entryValues="@array/settings_lcd_overlapping_mode_value"
android:defaultValue="0"
/>
<SwitchPreference
android:key="settings_hide_bar_status"
android:title="@string/settings_hide_bar_status"