diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..df40ccf --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,40 @@ +plugins { + alias(libs.plugins.android.application) +} + +android { + namespace 'com.example.lifeline_test2' + compileSdk 36 + + defaultConfig { + applicationId "com.example.lifeline_test2" + minSdk 31 + targetSdk 36 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } +} + +dependencies { + + implementation libs.appcompat + implementation libs.material + implementation libs.activity + implementation libs.constraintlayout + testImplementation libs.junit + androidTestImplementation libs.ext.junit + androidTestImplementation libs.espresso.core +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/src/androidTest/java/com/example/lifeline_test2/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/example/lifeline_test2/ExampleInstrumentedTest.java new file mode 100644 index 0000000..0656df0 --- /dev/null +++ b/app/src/androidTest/java/com/example/lifeline_test2/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.example.lifeline_test2; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals("com.example.lifeline_test2", appContext.getPackageName()); + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..2279569 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/assets/index.html b/app/src/main/assets/index.html new file mode 100644 index 0000000..5b4debb --- /dev/null +++ b/app/src/main/assets/index.html @@ -0,0 +1,465 @@ + + + + + + Lifeline Kiosk System - USB Printer Console + + + +
+ +
+
+

LIFELINE KIOSK SYSTEM

+

USB Thermal Printer Management Interface

+
+
+ + Scanning for printer... +
+
+ + +
+ +
+
+

+ + Print Command Centre +

+

Modify the markup below and dispatch to the thermal printer

+
+ + + +
+ +
+
+ + +
+
+

+ + System Terminal Logs +

+

Real-time USB printer communication logs

+
+ + + +
+ + +
+
+
+ + + +
+ + + + \ No newline at end of file diff --git a/app/src/main/java/com/example/lifeline_test2/ESCPOS_Printer.java b/app/src/main/java/com/example/lifeline_test2/ESCPOS_Printer.java new file mode 100644 index 0000000..97804b0 --- /dev/null +++ b/app/src/main/java/com/example/lifeline_test2/ESCPOS_Printer.java @@ -0,0 +1,175 @@ +package com.example.lifeline_test2; + + +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.hardware.usb.UsbConstants; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbDeviceConnection; +import android.hardware.usb.UsbEndpoint; +import android.hardware.usb.UsbInterface; +import android.hardware.usb.UsbManager; + +import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.Iterator; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ESCPOS_Printer { + + private final UsbEndpoint endpointOut; + private final UsbDeviceConnection connection; + + + public ESCPOS_Printer(UsbDeviceConnection connection, UsbEndpoint endpoint) { + this.connection = connection; + this.endpointOut = endpoint; + } + + public void printFormatted(String markup) throws Exception { + // Split into lines + String[] lines = markup.split("\n"); + + for (String line : lines) { + // Alignment tags + if (line.startsWith("[C]")) { + send(new byte[]{0x1B, 0x61, 0x01}); // Center + line = line.substring(3); + } else if (line.startsWith("[R]")) { + send(new byte[]{0x1B, 0x61, 0x02}); // Right + line = line.substring(3); + } else if (line.startsWith("[L]")) { + send(new byte[]{0x1B, 0x61, 0x00}); // Left + line = line.substring(3); + } + + // Bold tags + line = parseTag(line, "", () -> send(new byte[]{0x1B, 0x45, 0x01}), + "", () -> send(new byte[]{0x1B, 0x45, 0x00})); + + // Underline tags + line = parseTag(line, "", () -> send(new byte[]{0x1B, 0x2D, 0x01}), + "", () -> send(new byte[]{0x1B, 0x2D, 0x00})); + + // Font size tags + Pattern fontPattern = Pattern.compile(""); + Matcher fontMatcher = fontPattern.matcher(line); + while (fontMatcher.find()) { + String size = fontMatcher.group(1); + if ("big".equalsIgnoreCase(size)) { + send(new byte[]{0x1D, 0x21, 0x11}); + } else if ("tall".equalsIgnoreCase(size)) { + send(new byte[]{0x1D, 0x21, 0x01}); + } else if ("wide".equalsIgnoreCase(size)) { + send(new byte[]{0x1D, 0x21, 0x10}); + } else { + send(new byte[]{0x1D, 0x21, 0x00}); // normal + } + } + line = line.replaceAll("", "").replaceAll("", ""); + + // Image tags + Pattern imgPattern = Pattern.compile("(.*?)"); + Matcher imgMatcher = imgPattern.matcher(line); + while (imgMatcher.find()) { + String hex = imgMatcher.group(1); + byte[] imgBytes = hexStringToBytes(hex); + send(imgBytes); + } + line = line.replaceAll(".*?", ""); + + // Barcode tags + Pattern barcodePattern = Pattern.compile("(.*?)"); + Matcher barcodeMatcher = barcodePattern.matcher(line); + while (barcodeMatcher.find()) { + String type = barcodeMatcher.group(1); + int height = Integer.parseInt(barcodeMatcher.group(2)); + String data = barcodeMatcher.group(3); + printBarcode(type, height, data); + } + line = line.replaceAll("", ""); + + // QR code tags + Pattern qrPattern = Pattern.compile("(.*?)"); + Matcher qrMatcher = qrPattern.matcher(line); + while (qrMatcher.find()) { + int size = Integer.parseInt(qrMatcher.group(1)); + String data = qrMatcher.group(2); + printQRCode(size, data); + } + line = line.replaceAll("", ""); + + // Print remaining text + if (!line.trim().isEmpty()) { + send(line.getBytes(Charset.forName("UTF-8"))); + } + send(new byte[]{0x0A}); // New line + } + + // Reset to defaults + send(new byte[]{0x1B, 0x40}); + } + + public void feed(Integer count){ + for(var i=0; i<=count; i++){ + send(new byte[]{0x0A}); + } + } + public void cut(){ + send(new byte[]{0x1D, 0x56, 0x00}); + } + + private String parseTag(String text, String startTag, Runnable startAction, String endTag, Runnable endAction) { + while (text.contains(startTag)) { + startAction.run(); + text = text.replaceFirst(Pattern.quote(startTag), ""); + } + while (text.contains(endTag)) { + endAction.run(); + text = text.replaceFirst(Pattern.quote(endTag), ""); + } + return text; + } + + private void send(byte[] data) { + connection.bulkTransfer(endpointOut, data, data.length, 1000); + } + + private byte[] hexStringToBytes(String hex) { + int len = hex.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4) + + Character.digit(hex.charAt(i+1), 16)); + } + return data; + } + + private void printBarcode(String type, int height, String data) { + send(new byte[]{0x1D, 0x68, (byte) height}); + send(new byte[]{0x1D, 0x48, 0x02}); // Print label below + send(new byte[]{0x1D, 0x77, 0x02}); // Module width + + if ("ean13".equalsIgnoreCase(type)) { + send(new byte[]{0x1D, 0x6B, 0x02}); + } + send(data.getBytes(Charset.forName("UTF-8"))); + send(new byte[]{0x00}); + } + + private void printQRCode(int size, String data) { + // Store size + send(new byte[]{0x1D, 0x28, 0x6B, 0x03, 0x00, 0x31, 0x43, (byte) size}); + // Store data + byte[] bytes = data.getBytes(Charset.forName("UTF-8")); + int pL = (bytes.length + 3) & 0xFF; + int pH = (bytes.length + 3) >> 8; + send(new byte[]{0x1D, 0x28, 0x6B, (byte) pL, (byte) pH, 0x31, 0x50, 0x30}); + send(bytes); + // Print QR + send(new byte[]{0x1D, 0x28, 0x6B, 0x03, 0x00, 0x31, 0x51, 0x30}); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/lifeline_test2/MainActivity.java b/app/src/main/java/com/example/lifeline_test2/MainActivity.java new file mode 100644 index 0000000..bce372f --- /dev/null +++ b/app/src/main/java/com/example/lifeline_test2/MainActivity.java @@ -0,0 +1,308 @@ +package com.example.lifeline_test2; + +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.hardware.usb.UsbConstants; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbDeviceConnection; +import android.hardware.usb.UsbEndpoint; +import android.hardware.usb.UsbInterface; +import android.hardware.usb.UsbManager; +import android.os.Bundle; +import android.util.Log; +import android.webkit.JavascriptInterface; +import android.webkit.WebSettings; +import android.webkit.WebView; + +import androidx.appcompat.app.AppCompatActivity; + +import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.Iterator; + +public class MainActivity extends AppCompatActivity { + + public static final String ACTION_USB_PERMISSION = "com.example.lifeline_test2.USB_PERMISSION"; + + private UsbManager usbManager; + private UsbDevice usbDevice; + private UsbInterface usbInterface; + private UsbEndpoint usbEndpoint; + private UsbDeviceConnection usbDeviceConnection; + + // WebView and Log Queue Fields + private WebView webView; + private boolean isPageLoaded = false; + private final java.util.ArrayList logQueue = new java.util.ArrayList<>(); + private String currentStatusText = "Scanning for printer..."; + private boolean currentStatusConnected = false; + + private void log(final String message) { + Log.d("USB_PRINTER", message); + final String escaped = message.replace("\\", "\\\\").replace("'", "\\'").replace("\n", "\\n").replace("\r", ""); + runOnUiThread(new Runnable() { + @Override + public void run() { + if (isPageLoaded && webView != null) { + webView.evaluateJavascript("addLog('" + escaped + "');", null); + } else { + logQueue.add(escaped); + } + } + }); + } + + private void updateStatus(final String statusText, final boolean isConnected) { + currentStatusText = statusText; + currentStatusConnected = isConnected; + runOnUiThread(new Runnable() { + @Override + public void run() { + if (isPageLoaded && webView != null) { + webView.evaluateJavascript("updateStatus('" + statusText.replace("'", "\\'") + "', " + isConnected + ");", null); + } + } + }); + } + + private void flushLogs() { + isPageLoaded = true; + webView.evaluateJavascript("updateStatus('" + currentStatusText.replace("'", "\\'") + "', " + currentStatusConnected + ");", null); + for (String msg : logQueue) { + webView.evaluateJavascript("addLog('" + msg + "');", null); + } + logQueue.clear(); + } + + private final BroadcastReceiver usbReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (ACTION_USB_PERMISSION.equals(intent.getAction())) { + synchronized (this) { + UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); + log("USB permission broadcast received."); + if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { + if (device != null) { + log("USB permission GRANTED for device: " + device.getDeviceName()); + setupPrinterConnection(device); + } else { + log("USB permission GRANTED but device is null."); + } + } else { + log("USB permission DENIED for device: " + (device != null ? device.getDeviceName() : "unknown")); + updateStatus("Permission Denied", false); + } + } + } + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + usbManager = (UsbManager) getSystemService(Context.USB_SERVICE); + + registerReceiver(usbReceiver, new IntentFilter(ACTION_USB_PERMISSION), RECEIVER_NOT_EXPORTED); + + webView = new WebView(this); + setContentView(webView); + WebSettings webSettings = webView.getSettings(); + webSettings.setJavaScriptEnabled(true); + + webView.setWebViewClient(new android.webkit.WebViewClient() { + @Override + public void onPageFinished(WebView view, String url) { + super.onPageFinished(view, url); + flushLogs(); + } + }); + + webView.addJavascriptInterface(new WebAppInterface(this), "AndroidPrinter"); + webView.loadUrl("file:///android_asset/index.html"); + + log("System started. Initializing USB printer discovery..."); + findUsbPrinter(); + } + + private void findUsbPrinter(){ + log("Scanning for USB printers..."); + updateStatus("Scanning for printer...", false); + + HashMap deviceList = usbManager.getDeviceList(); + log("Found " + deviceList.size() + " connected USB device(s) in system."); + + Iterator deviceIterator = deviceList.values().iterator(); + boolean printerFound = false; + + while(deviceIterator.hasNext()){ + UsbDevice device = deviceIterator.next(); + log("Checking USB device: " + device.getDeviceName() + + " [VID: " + String.format("0x%04X", device.getVendorId()) + + ", PID: " + String.format("0x%04X", device.getProductId()) + + ", ID: " + device.getDeviceId() + "]"); + + for (int i = 0; i < device.getInterfaceCount(); i++) { + UsbInterface intf = device.getInterface(i); + int intfClass = intf.getInterfaceClass(); + log(" - Interface " + i + ": class = " + intfClass + " (Printer class is " + UsbConstants.USB_CLASS_PRINTER + ")"); + + if (intfClass == UsbConstants.USB_CLASS_PRINTER || intfClass == UsbConstants.USB_CLASS_VENDOR_SPEC) { + printerFound = true; + usbInterface = intf; + usbDevice = device; + + log("-> Matched USB Printer Interface! Class: " + intfClass); + log("-> Device Vendor ID: " + String.format("0x%04X", usbDevice.getVendorId())); + log("-> Device Product ID: " + String.format("0x%04X", usbDevice.getProductId())); + log("-> Device Name: " + usbDevice.getDeviceName()); + + PendingIntent permissionIntent = PendingIntent.getBroadcast( + this, 0, new Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE + ); + + if (!usbManager.hasPermission(usbDevice)) { + log("USB permission not granted. Requesting permission from user..."); + updateStatus("Awaiting USB Permission...", false); + usbManager.requestPermission(usbDevice, permissionIntent); + } else { + log("USB permission already granted. Setting up connection..."); + setupPrinterConnection(usbDevice); + } + break; + } + } + if (printerFound) { + break; + } + } + + if (!printerFound) { + log("No USB printer detected. Please check connection and try again."); + updateStatus("No Printer Found", false); + } + } + + private void setupPrinterConnection(UsbDevice device){ + log("Setting up printer connection endpoint..."); + boolean endpointFound = false; + + for (int i = 0; i < usbInterface.getEndpointCount(); i++) { + UsbEndpoint ep = usbInterface.getEndpoint(i); + log(" - Endpoint " + i + ": type = " + ep.getType() + + ", direction = " + ep.getDirection() + + " (Bulk is " + UsbConstants.USB_ENDPOINT_XFER_BULK + + ", Out is " + UsbConstants.USB_DIR_OUT + ")"); + + if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK && + ep.getDirection() == UsbConstants.USB_DIR_OUT) { + usbEndpoint = ep; + endpointFound = true; + log("-> Selected Bulk OUT endpoint: " + ep.getAddress()); + break; + } + } + + if (endpointFound) { + log("Printer connection set up successfully."); + updateStatus("Connected: " + device.getDeviceName() + " (VID: " + String.format("0x%04X", device.getVendorId()) + ")", true); + } else { + log("Error: No bulk OUT endpoint found on this printer interface."); + updateStatus("Endpoint Error", false); + } + } + + + private void print(String text) throws Exception { + log("Starting print operation..."); + if (usbInterface == null) { + log("Error: No printer interface found. Aborting print."); + return; + } + + if (usbEndpoint == null) { + log("Error: No bulk OUT endpoint found. Aborting print."); + return; + } + + log("Opening USB device connection..."); + usbDeviceConnection = usbManager.openDevice(usbDevice); + if (usbDeviceConnection == null) { + log("Error: Unable to open USB device connection."); + return; + } + + log("Claiming USB interface (exclusive access)..."); + boolean claimed = usbDeviceConnection.claimInterface(usbInterface, true); + if (!claimed) { + log("Error: Failed to claim USB interface."); + usbDeviceConnection.close(); + return; + } + log("USB interface claimed successfully."); + + try { + log("Initializing ESC/POS Printer..."); + ESCPOS_Printer printer = new ESCPOS_Printer(usbDeviceConnection, usbEndpoint); + + log("Sending formatted receipt text to printer (Length: " + text.length() + " chars)..."); + printer.printFormatted(text); + + log("Feeding paper (5 lines)..."); + printer.feed(5); + + log("Executing paper cut..."); + printer.cut(); + + log("Print job completed successfully."); + } catch (Exception e) { + log("Error during printing: " + e.getMessage()); + e.printStackTrace(); + throw e; + } finally { + log("Releasing USB interface..."); + usbDeviceConnection.releaseInterface(usbInterface); + log("Closing USB connection."); + usbDeviceConnection.close(); + } + } + + + public class WebAppInterface { + Context mContext; + WebAppInterface(Context c){ + mContext = c; + } + + @JavascriptInterface + public void printReceipt(String text) throws Exception { + log("JavaScript called printReceipt()"); + try { + print(text); + } catch (Exception e) { + log("Print failed: " + e.getMessage()); + throw e; + } + } + + @JavascriptInterface + public void scanForPrinter() { + log("JavaScript called scanForPrinter() manually."); + runOnUiThread(new Runnable() { + @Override + public void run() { + findUsbPrinter(); + } + }); + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + unregisterReceiver(usbReceiver); + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..9affce0 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi/ic_launcher.xml b/app/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000..c209e78 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000..b2dfe3d Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000..4f0f1d6 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000..948a307 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..1b9a695 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..cd4ea1f --- /dev/null +++ b/app/src/main/res/values-night/themes.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..c8524cd --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,5 @@ + + + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..0884498 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Lifeline-Test2 + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..3161d44 --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,9 @@ + + + + +