Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
}
16 changes: 16 additions & 0 deletions app/src/main/java/com/m5dev/librecast/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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) {
Expand Down
60 changes: 60 additions & 0 deletions app/src/main/java/com/m5dev/librecast/QRCodeUtils.java
Original file line number Diff line number Diff line change
@@ -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<EncodeHintType, Object> 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;
}
}
}
8 changes: 8 additions & 0 deletions app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@
android:textSize="36sp"
android:textStyle="bold" />

<ImageView
android:id="@+id/image_qr_code"
android:layout_width="250dp"
android:layout_height="250dp"
android:layout_marginTop="20dp"
android:contentDescription="QR Code"
android:visibility="gone" />

<TextView
android:id="@+id/text_server_status"
android:layout_width="wrap_content"
Expand Down
41 changes: 41 additions & 0 deletions app/src/test/java/com/m5dev/librecast/QRCodeUtilsTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.m5dev.librecast;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;

import org.junit.Test;

import java.util.HashMap;
import java.util.Map;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

public class QRCodeUtilsTest {

@Test
public void testQRCodeBitMatrixEncoding() throws Exception {
String testUrl = "ws://192.168.1.100:8080";
QRCodeWriter writer = new QRCodeWriter();
Map<EncodeHintType, Object> 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));
}
}
Loading