diff --git a/app/build.gradle b/app/build.gradle index f34418e..c7583c6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -32,6 +32,7 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.3.1' implementation 'androidx.core:core:1.6.0' implementation 'org.java-websocket:Java-WebSocket:1.5.4' + implementation 'com.google.zxing:core:3.3.3' testImplementation 'junit:junit:4.13.2' } diff --git a/app/src/main/java/com/m5dev/librecast/MainActivity.java b/app/src/main/java/com/m5dev/librecast/MainActivity.java index ed95d3d..8f6fd1e 100644 --- a/app/src/main/java/com/m5dev/librecast/MainActivity.java +++ b/app/src/main/java/com/m5dev/librecast/MainActivity.java @@ -6,6 +6,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; +import android.graphics.Bitmap; import android.media.MediaFormat; import android.os.Bundle; import android.os.IBinder; @@ -14,6 +15,7 @@ import android.view.SurfaceView; import android.view.View; import android.view.WindowManager; +import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; @@ -26,6 +28,7 @@ public class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal private SurfaceView surfaceView; private LinearLayout layoutStatus; + private ImageView imageQrCode; private TextView textServerStatus; private TextView textConnectionStatus; @@ -75,6 +78,7 @@ protected void onCreate(Bundle savedInstanceState) { surfaceView = findViewById(R.id.surface_view); layoutStatus = findViewById(R.id.layout_status); + imageQrCode = findViewById(R.id.image_qr_code); textServerStatus = findViewById(R.id.text_server_status); textConnectionStatus = findViewById(R.id.text_connection_status); @@ -138,8 +142,20 @@ private void updateUI(Intent intent) { serverIp = NetworkUtils.getLocalIpAddress(this); } textServerStatus.setText(getString(R.string.server_ready_format, serverIp, serverPort)); + + if (serverIp != null && !serverIp.isEmpty() && !"0.0.0.0".equals(serverIp)) { + String wsUri = "ws://" + serverIp + ":" + serverPort; + Bitmap qrBitmap = QRCodeUtils.generateQRCode(wsUri, 250, 250); + if (qrBitmap != null && imageQrCode != null) { + imageQrCode.setImageBitmap(qrBitmap); + imageQrCode.setVisibility(View.VISIBLE); + } + } } else { textServerStatus.setText(getString(R.string.server_stopped)); + if (imageQrCode != null) { + imageQrCode.setVisibility(View.GONE); + } } if (isClientConnected) { diff --git a/app/src/main/java/com/m5dev/librecast/QRCodeUtils.java b/app/src/main/java/com/m5dev/librecast/QRCodeUtils.java new file mode 100644 index 0000000..d939054 --- /dev/null +++ b/app/src/main/java/com/m5dev/librecast/QRCodeUtils.java @@ -0,0 +1,60 @@ +package com.m5dev.librecast; + +import android.graphics.Bitmap; +import android.util.Log; + +import com.google.zxing.BarcodeFormat; +import com.google.zxing.EncodeHintType; +import com.google.zxing.WriterException; +import com.google.zxing.common.BitMatrix; +import com.google.zxing.qrcode.QRCodeWriter; + +import java.util.HashMap; +import java.util.Map; + +public class QRCodeUtils { + + private static final String TAG = "QRCodeUtils"; + + /** + * Generates an Android Bitmap containing a QR code for the specified text. + * + * @param text The content string to encode in the QR code (e.g. "ws://192.168.1.100:8080") + * @param width Desired bitmap width in pixels + * @param height Desired bitmap height in pixels + * @return Bitmap containing the QR code, or null if generation fails or input is empty. + */ + public static Bitmap generateQRCode(String text, int width, int height) { + if (text == null || text.trim().isEmpty() || width <= 0 || height <= 0) { + return null; + } + + try { + QRCodeWriter writer = new QRCodeWriter(); + Map hints = new HashMap<>(); + hints.put(EncodeHintType.MARGIN, 1); + + BitMatrix bitMatrix = writer.encode(text, BarcodeFormat.QR_CODE, width, height, hints); + int matrixWidth = bitMatrix.getWidth(); + int matrixHeight = bitMatrix.getHeight(); + + int[] pixels = new int[matrixWidth * matrixHeight]; + for (int y = 0; y < matrixHeight; y++) { + int offset = y * matrixWidth; + for (int x = 0; x < matrixWidth; x++) { + pixels[offset + x] = bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF; + } + } + + Bitmap bitmap = Bitmap.createBitmap(matrixWidth, matrixHeight, Bitmap.Config.ARGB_8888); + bitmap.setPixels(pixels, 0, matrixWidth, 0, 0, matrixWidth, matrixHeight); + return bitmap; + } catch (WriterException e) { + Log.e(TAG, "Failed to encode QR code", e); + return null; + } catch (Exception e) { + Log.e(TAG, "Unexpected error generating QR code", e); + return null; + } + } +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 878ea9a..8f0096f 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -27,6 +27,14 @@ android:textSize="36sp" android:textStyle="bold" /> + + hints = new HashMap<>(); + hints.put(EncodeHintType.MARGIN, 1); + + BitMatrix bitMatrix = writer.encode(testUrl, BarcodeFormat.QR_CODE, 250, 250, hints); + assertNotNull("BitMatrix should not be null", bitMatrix); + assertTrue("Width should be positive", bitMatrix.getWidth() > 0); + assertTrue("Height should be positive", bitMatrix.getHeight() > 0); + } + + @Test + public void testQRCodeUtilsNullOrEmptyHandling() { + assertNull("Null text should return null", QRCodeUtils.generateQRCode(null, 250, 250)); + assertNull("Empty text should return null", QRCodeUtils.generateQRCode("", 250, 250)); + assertNull("Whitespace text should return null", QRCodeUtils.generateQRCode(" ", 250, 250)); + assertNull("Invalid width should return null", QRCodeUtils.generateQRCode("ws://192.168.1.1:8080", 0, 250)); + assertNull("Invalid height should return null", QRCodeUtils.generateQRCode("ws://192.168.1.1:8080", 250, -1)); + } +}