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
12 changes: 5 additions & 7 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import org.gradle.api.JavaVersion.VERSION_11
import org.gradle.api.JavaVersion.VERSION_17
import org.jetbrains.kotlin.gradle.dsl.JvmTarget

plugins {
Expand Down Expand Up @@ -49,11 +47,11 @@ android {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
kotlin {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_21)
kotlin {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_21)
}
}
}
buildFeatures {
compose = true
buildConfig = true
Expand Down Expand Up @@ -86,7 +84,7 @@ dependencies {
// Android 12+ SplashScreen API with backward compatibility attributes
implementation("androidx.core:core-splashscreen:1.0.1")

implementation ("androidx.compose.material3:material3:1.5.0-alpha10")
implementation("androidx.compose.material3:material3:1.5.0-alpha10")
implementation("androidx.compose.material:material-icons-core:1.7.8")
implementation("androidx.compose.material:material-icons-extended:1.7.8")

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package com.sameerasw.airsync

import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4

import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith

import org.junit.Assert.*

/**
* Instrumented test, which will execute on an Android device.
*
Expand Down
44 changes: 24 additions & 20 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,16 @@
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-feature android:name="android.hardware.bluetooth_le" android:required="false" />

<uses-feature
android:name="android.hardware.bluetooth_le"
android:required="false" />

<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_WALLPAPER_INTERNAL" />



<!-- Permission for downloading updates -->
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />

Expand Down Expand Up @@ -57,21 +59,23 @@
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.AirSync"
android:networkSecurityConfig="@xml/network_security_config"
tools:targetApi="36">

<!-- Disable Sentry auto-init as it's handled manually in AirSyncApp -->
<meta-data android:name="io.sentry.auto-init" android:value="false" />
<meta-data
android:name="io.sentry.auto-init"
android:value="false" />

<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.AirSync.Splash"
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden">
android:theme="@style/Theme.AirSync.Splash">
<!-- Main launcher intent filter -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand All @@ -87,29 +91,30 @@
<!-- Deep link intent filter for QR codes -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data android:scheme="airsync" />
</intent-filter>

<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
</intent-filter>

<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcuts" />
</activity>



<activity
android:name=".presentation.ui.activities.ClipboardActionActivity"
android:theme="@style/Theme.AirSync.Transparent"
android:exported="true"
android:excludeFromRecents="true"
android:exported="true"
android:noHistory="true"
android:taskAffinity="">
android:taskAffinity=""
android:theme="@style/Theme.AirSync.Transparent">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
Expand All @@ -131,8 +136,8 @@
android:name=".presentation.ui.activities.QRScannerActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.AirSync"
android:screenOrientation="portrait">
android:screenOrientation="portrait"
android:theme="@style/Theme.AirSync">
<!-- Allow external apps to launch the scanner -->
<intent-filter>
<action android:name="com.sameerasw.airsync.SCAN_QR" />
Expand All @@ -151,8 +156,7 @@
<service
android:name=".service.MacMediaPlayerService"
android:exported="false"
android:foregroundServiceType="connectedDevice">
</service>
android:foregroundServiceType="connectedDevice"></service>

<!-- Quick Settings Tile Service -->
<service
Expand All @@ -179,7 +183,6 @@
</service>



<!-- Wake-up Service for receiving reconnection requests from Mac -->
<service
android:name=".service.WakeupService"
Expand Down Expand Up @@ -234,8 +237,8 @@
<provider
android:name=".smartspacer.AirSyncDeviceTarget"
android:authorities="${applicationId}.target.airsync_device"
android:permission="com.kieronquinn.app.smartspacer.permission.ACCESS_SMARTSPACER_TARGETS"
android:exported="true">
android:exported="true"
android:permission="com.kieronquinn.app.smartspacer.permission.ACCESS_SMARTSPACER_TARGETS">
<intent-filter>
<action android:name="com.kieronquinn.app.smartspacer.TARGET" />
</intent-filter>
Expand Down Expand Up @@ -270,8 +273,9 @@
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent>
<package android:name="com.google.ar.lens" />
<package android:name="com.sameerasw.essentials" />

<package android:name="com.google.ar.lens" />
<package android:name="com.sameerasw.essentials" />
</queries>

</manifest>
10 changes: 7 additions & 3 deletions app/src/main/java/com/sameerasw/airsync/AirSyncApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,29 @@ class AirSyncApp : Application() {
companion object {
private var instance: AirSyncApp? = null
fun isAppForeground(): Boolean = instance?.isForeground() ?: false
fun getBleConnectionManager(): com.sameerasw.airsync.data.ble.BleConnectionManager? = instance?.bleConnectionManager
fun getBleConnectionManager(): com.sameerasw.airsync.data.ble.BleConnectionManager? =
instance?.bleConnectionManager
}

override fun onCreate() {
super.onCreate()
instance = this
initSentry()

bleConnectionManager = com.sameerasw.airsync.data.ble.BleConnectionManager(this)
bleConnectionManager.start()
registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}
override fun onActivityStarted(activity: Activity) {
activityCount++
}

override fun onActivityResumed(activity: Activity) {}
override fun onActivityPaused(activity: Activity) {}
override fun onActivityStopped(activity: Activity) {
activityCount--
}

override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
override fun onActivityDestroyed(activity: Activity) {}
})
Expand All @@ -49,7 +52,8 @@ class AirSyncApp : Application() {
if (!isEnabled) return

SentryAndroid.init(this) { options ->
options.dsn = "https://cb9b0ead9e88e0818269e773cb662141@o4510996760887296.ingest.de.sentry.io/4511002261389392"
options.dsn =
"https://cb9b0ead9e88e0818269e773cb662141@o4510996760887296.ingest.de.sentry.io/4511002261389392"
options.isEnabled = true
}
}
Expand Down
68 changes: 25 additions & 43 deletions app/src/main/java/com/sameerasw/airsync/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,37 +12,15 @@ import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.material.icons.rounded.HelpOutline
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.Surface
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.core.animation.doOnEnd
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.navigation.compose.NavHost
Expand All @@ -52,7 +30,6 @@ import com.sameerasw.airsync.data.local.DataStoreManager
import com.sameerasw.airsync.presentation.ui.activities.QRScannerActivity
import com.sameerasw.airsync.presentation.ui.screens.AirSyncMainScreen
import com.sameerasw.airsync.ui.theme.AirSyncTheme
import com.sameerasw.airsync.presentation.viewmodel.AirSyncViewModel
import com.sameerasw.airsync.utils.AdbMdnsDiscovery
import com.sameerasw.airsync.utils.ContentCaptureManager
import com.sameerasw.airsync.utils.DevicePreviewResolver
Expand Down Expand Up @@ -246,7 +223,8 @@ class MainActivity : ComponentActivity() {
this@MainActivity,
R.color.material_primary
)
splashIcon.imageTintList = android.content.res.ColorStateList.valueOf(colorPrimary)
splashIcon.imageTintList =
android.content.res.ColorStateList.valueOf(colorPrimary)
Log.d("MainActivity", "Switched to device icon with primary tint")

// Fade in the new device icon
Expand Down Expand Up @@ -299,28 +277,28 @@ class MainActivity : ComponentActivity() {
fadeOutIcon.start()
} else {
// No device icon found, or splashIcon is null/not ImageView (OEM device compatibility)
// Proceed directly to outro after a brief hold
try {
splashScreenView.postDelayed({
startOutroAnimation(
splashScreenView,
splashIcon,
splashScreenViewProvider
)
}, 500)
} catch (e: Exception) {
Log.e(
"MainActivity",
"Error scheduling outro with no icon: ${e.message}",
e
)
// Fallback: start outro immediately
// Proceed directly to outro after a brief hold
try {
splashScreenView.postDelayed({
startOutroAnimation(
splashScreenView,
splashIcon,
splashScreenViewProvider
)
}
}, 500)
} catch (e: Exception) {
Log.e(
"MainActivity",
"Error scheduling outro with no icon: ${e.message}",
e
)
// Fallback: start outro immediately
startOutroAnimation(
splashScreenView,
splashIcon,
splashScreenViewProvider
)
}
}
} catch (e: Exception) {
// Fallback for any unexpected exceptions during animation
Expand All @@ -344,7 +322,10 @@ class MainActivity : ComponentActivity() {
AdbDiscoveryHolder.initialize(this)
Log.d("MainActivity", "Started persistent ADB discovery at app startup")
} else {
Log.d("MainActivity", "Skipping persistent ADB discovery at startup: ACCESS_LOCAL_NETWORK permission not granted")
Log.d(
"MainActivity",
"Skipping persistent ADB discovery at startup: ACCESS_LOCAL_NETWORK permission not granted"
)
}

// Check if this is a QS tile long-press intent and device is not connected
Expand Down Expand Up @@ -412,7 +393,8 @@ class MainActivity : ComponentActivity() {
modifier = Modifier.padding(innerPadding)
) {
composable("main") {
val initialPage = if (intent?.action == ShortcutUtil.DASH_ACTION_REMOTE) 1 else 0
val initialPage =
if (intent?.action == ShortcutUtil.DASH_ACTION_REMOTE) 1 else 0
AirSyncMainScreen(
initialIp = ip,
initialPort = port,
Expand Down
Loading