Skip to content

Commit 8bb2cef

Browse files
authored
Merge pull request #50 from sameerasw/scanner
Scanner
2 parents 77147a4 + 5d9da9d commit 8bb2cef

7 files changed

Lines changed: 593 additions & 67 deletions

File tree

app/build.gradle.kts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ android {
1515
applicationId = "com.sameerasw.airsync"
1616
minSdk = 30
1717
targetSdk = 36
18-
versionCode = 11
19-
versionName = "2.1.4"
18+
versionCode = 12
19+
versionName = "2.1.5"
2020

2121
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
2222
}
@@ -89,6 +89,16 @@ dependencies {
8989
implementation(libs.ui.graphics)
9090
implementation(libs.androidx.foundation)
9191

92+
// CameraX for QR scanning
93+
implementation("androidx.camera:camera-core:1.4.0")
94+
implementation("androidx.camera:camera-camera2:1.4.0")
95+
implementation("androidx.camera:camera-lifecycle:1.4.0")
96+
implementation("androidx.camera:camera-view:1.4.0")
97+
implementation("androidx.camera:camera-mlkit-vision:1.4.0")
98+
99+
// ML Kit barcode scanner (QR code only)
100+
implementation("com.google.mlkit:barcode-scanning:17.3.0")
101+
92102
testImplementation(libs.junit)
93103
androidTestImplementation(libs.androidx.junit)
94104
androidTestImplementation(libs.androidx.espresso.core)

app/src/main/AndroidManifest.xml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
2323
<uses-permission android:name="android.permission.WAKE_LOCK" />
2424

25+
<!-- Camera permission for QR code scanner -->
26+
<uses-permission android:name="android.permission.CAMERA" />
27+
2528
<application
2629
android:allowBackup="true"
2730
android:dataExtractionRules="@xml/data_extraction_rules"
@@ -93,6 +96,20 @@
9396
</intent-filter>
9497
</service>
9598

99+
<!-- QR Code Scanner Activity -->
100+
<activity
101+
android:name=".presentation.ui.activities.QRScannerActivity"
102+
android:exported="true"
103+
android:label="@string/app_name"
104+
android:theme="@style/Theme.AirSync"
105+
android:screenOrientation="portrait">
106+
<!-- Allow external apps to launch the scanner -->
107+
<intent-filter>
108+
<action android:name="com.sameerasw.airsync.SCAN_QR" />
109+
<category android:name="android.intent.category.DEFAULT" />
110+
</intent-filter>
111+
</activity>
112+
96113
<!-- Mac Media Player Service - creates native Android media player UI -->
97114
<service
98115
android:name=".service.MacMediaPlayerService"

app/src/main/java/com/sameerasw/airsync/MainActivity.kt

Lines changed: 95 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.sameerasw.airsync
33
import android.Manifest
44
import android.os.Build
55
import android.os.Bundle
6+
import android.util.Log
67
import androidx.activity.ComponentActivity
78
import androidx.activity.compose.setContent
89
import androidx.activity.enableEdgeToEdge
@@ -27,10 +28,9 @@ import androidx.compose.ui.graphics.ColorFilter
2728
import androidx.core.net.toUri
2829
import androidx.compose.ui.input.nestedscroll.nestedScroll
2930
import com.sameerasw.airsync.data.local.DataStoreManager
30-
import kotlinx.coroutines.flow.collect
31-
import kotlinx.coroutines.flow.flowOf
32-
import kotlinx.coroutines.flow.map
33-
import kotlinx.coroutines.flow.Flow
31+
import android.content.Intent
32+
import com.sameerasw.airsync.presentation.ui.activities.QRScannerActivity
33+
import com.sameerasw.airsync.utils.WebSocketUtil
3434

3535
class MainActivity : ComponentActivity() {
3636

@@ -40,9 +40,35 @@ class MainActivity : ComponentActivity() {
4040
) { isGranted ->
4141
}
4242

43+
private val qrScannerLauncher = registerForActivityResult(
44+
ActivityResultContracts.StartActivityForResult()
45+
) { result ->
46+
if (result.resultCode == RESULT_OK) {
47+
val qrCode = result.data?.getStringExtra("QR_CODE")
48+
if (qrCode != null) {
49+
// Parse and connect with the QR code data
50+
handleQRCodeResult(qrCode)
51+
}
52+
} else {
53+
// User cancelled or invalid QR, do nothing - app remains open
54+
}
55+
}
56+
4357
@OptIn(ExperimentalMaterial3Api::class)
4458
override fun onCreate(savedInstanceState: Bundle?) {
4559
super.onCreate(savedInstanceState)
60+
61+
// Check if this is a QS tile long-press intent and device is not connected
62+
if (intent?.action == "android.service.quicksettings.action.QS_TILE_PREFERENCES") {
63+
if (!WebSocketUtil.isConnected()) {
64+
// Not connected, open QR scanner instead
65+
val qrScannerIntent = Intent(this, QRScannerActivity::class.java)
66+
qrScannerLauncher.launch(qrScannerIntent)
67+
return
68+
}
69+
}
70+
71+
// ...existing code...
4672
// Enable full edge-to-edge drawing for both status and navigation bars
4773
enableEdgeToEdge(
4874
statusBarStyle = SystemBarStyle.auto(
@@ -181,4 +207,69 @@ class MainActivity : ComponentActivity() {
181207
}
182208
}
183209
}
210+
211+
private fun handleQRCodeResult(qrCode: String) {
212+
try {
213+
val uri = android.net.Uri.parse(qrCode)
214+
215+
// Validate QR code format: must be airsync scheme with host and port
216+
if (uri.scheme != "airsync") {
217+
Log.w("MainActivity", "Invalid QR code scheme: ${uri.scheme}")
218+
android.widget.Toast.makeText(
219+
this,
220+
"Invalid QR code format",
221+
android.widget.Toast.LENGTH_SHORT
222+
).show()
223+
finish()
224+
return
225+
}
226+
227+
val host = uri.host
228+
val port = uri.port
229+
230+
if (host.isNullOrEmpty() || port == -1) {
231+
Log.w("MainActivity", "Invalid QR code: missing host or port")
232+
android.widget.Toast.makeText(
233+
this,
234+
"Invalid QR code format",
235+
android.widget.Toast.LENGTH_SHORT
236+
).show()
237+
finish()
238+
return
239+
}
240+
241+
// Valid QR code, proceed with connection
242+
val mainIntent = Intent(this, MainActivity::class.java).apply {
243+
data = uri
244+
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
245+
}
246+
startActivity(mainIntent)
247+
finish()
248+
} catch (e: Exception) {
249+
Log.e("MainActivity", "Error handling QR code result: ${e.message}", e)
250+
android.widget.Toast.makeText(
251+
this,
252+
"Invalid QR code format",
253+
android.widget.Toast.LENGTH_SHORT
254+
).show()
255+
finish()
256+
}
257+
}
258+
259+
override fun onNewIntent(intent: Intent?) {
260+
super.onNewIntent(intent)
261+
262+
// Check if this is a QS tile long-press intent
263+
if (intent?.action == "android.service.quicksettings.action.QS_TILE_PREFERENCES") {
264+
// Check if device is connected
265+
if (!WebSocketUtil.isConnected()) {
266+
// Not connected, open QR scanner
267+
val qrScannerIntent = Intent(this, QRScannerActivity::class.java).apply {
268+
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
269+
}
270+
startActivity(qrScannerIntent)
271+
finish()
272+
}
273+
}
274+
}
184275
}

0 commit comments

Comments
 (0)