diff --git a/app/build.gradle.kts b/app/build.gradle.kts index dddd746d..e420967d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -14,7 +14,7 @@ android { defaultConfig { applicationId = "com.sameerasw.airsync" minSdk = 30 - versionCode = 31 + versionCode = 32 versionName = "4.0.1" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" @@ -22,17 +22,16 @@ android { buildTypes { // optimized dev build -// debug { -// isMinifyEnabled = true -// isShrinkResources = true -// isDebuggable = false -// -// proguardFiles( -// getDefaultProguardFile("proguard-android-optimize.txt"), -// "proguard-rules.pro" -// ) -// } -// end +// debug { +// isMinifyEnabled = true +// isShrinkResources = true +// isDebuggable = false +// proguardFiles( +// getDefaultProguardFile("proguard-android-optimize.txt"), +// "proguard-rules.pro" +// ) +// } +// end release { isMinifyEnabled = true diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index eaf486fd..dd1d5d7c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -133,7 +133,6 @@ android:name=".presentation.ui.activities.QRScannerActivity" android:exported="true" android:label="@string/app_name" - android:screenOrientation="portrait" android:theme="@style/Theme.AirSync"> diff --git a/app/src/main/java/com/sameerasw/airsync/MainActivity.kt b/app/src/main/java/com/sameerasw/airsync/MainActivity.kt index ea656f2f..c8a335fc 100644 --- a/app/src/main/java/com/sameerasw/airsync/MainActivity.kt +++ b/app/src/main/java/com/sameerasw/airsync/MainActivity.kt @@ -11,6 +11,7 @@ import android.widget.ImageView import android.widget.Toast import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding @@ -173,8 +174,8 @@ class MainActivity : ComponentActivity() { // Install and configure the splash screen before any UI rendering val splashScreen = installSplashScreen() - // Make activity draw behind system bars - let the theme handle the colors - androidx.core.view.WindowCompat.setDecorFitsSystemWindows(window, false) + // Enable edge-to-edge for Android 15+ and backward compatibility + enableEdgeToEdge() super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/sameerasw/airsync/data/ble/BleConnectionManager.kt b/app/src/main/java/com/sameerasw/airsync/data/ble/BleConnectionManager.kt index f7b3515c..1d256c8a 100644 --- a/app/src/main/java/com/sameerasw/airsync/data/ble/BleConnectionManager.kt +++ b/app/src/main/java/com/sameerasw/airsync/data/ble/BleConnectionManager.kt @@ -56,8 +56,8 @@ class BleConnectionManager(private val context: Context) { } private fun updateBleState(regularConnectionActive: Boolean, manuallyDisconnected: Boolean) { - if (!isBleEnabled || manuallyDisconnected) { - Log.d(TAG, "BLE disabled or user manually disconnected, stopping/pausing server") + if (!isBleEnabled) { + Log.d(TAG, "BLE disabled in settings, stopping/pausing server") bleServer?.pauseAdvertising() return } @@ -67,7 +67,7 @@ class BleConnectionManager(private val context: Context) { Log.d(TAG, "Regular connection active — pausing BLE advertising") bleServer?.pauseAdvertising() } else { - // No regular connection — ensure server is started and advertising. + // No regular connection — ensure server is started and advertising so devices can discover and connect Log.d(TAG, "No regular connection — resuming BLE advertising") bleServer?.start() bleServer?.resumeAdvertising() diff --git a/app/src/main/java/com/sameerasw/airsync/data/ble/BleGattServer.kt b/app/src/main/java/com/sameerasw/airsync/data/ble/BleGattServer.kt index a82ae4d2..06c72f3e 100644 --- a/app/src/main/java/com/sameerasw/airsync/data/ble/BleGattServer.kt +++ b/app/src/main/java/com/sameerasw/airsync/data/ble/BleGattServer.kt @@ -709,6 +709,11 @@ class BleGattServer(private val context: Context) { } isAuthenticated = false authenticatedFlag.set(false) - _connectionState.value = BleConnectionState.DISCONNECTED + stopHeartbeat() + _connectionState.value = if (gattServer != null) BleConnectionState.ADVERTISING else BleConnectionState.DISCONNECTED + isAdvertisingPaused = false + if (gattServer != null && isBleSyncEnabled) { + startAdvertising() + } } } diff --git a/app/src/main/java/com/sameerasw/airsync/utils/ContactPhotoUtil.kt b/app/src/main/java/com/sameerasw/airsync/utils/ContactPhotoUtil.kt index ebf089eb..5f5c2fbd 100644 --- a/app/src/main/java/com/sameerasw/airsync/utils/ContactPhotoUtil.kt +++ b/app/src/main/java/com/sameerasw/airsync/utils/ContactPhotoUtil.kt @@ -145,13 +145,30 @@ object ContactPhotoUtil { */ private fun loadBitmap(context: Context, uri: Uri): Bitmap? { return try { - context.contentResolver.openInputStream(uri).use { inputStream -> - if (inputStream != null) { - BitmapFactory.decodeStream(inputStream) - } else { - null + val targetSize = 256 + val boundsOptions = BitmapFactory.Options().apply { + inJustDecodeBounds = true + } + context.contentResolver.openInputStream(uri)?.use { inputStream -> + BitmapFactory.decodeStream(inputStream, null, boundsOptions) + } + + var inSampleSize = 1 + if (boundsOptions.outHeight > targetSize || boundsOptions.outWidth > targetSize) { + val halfHeight = boundsOptions.outHeight / 2 + val halfWidth = boundsOptions.outWidth / 2 + while ((halfHeight / inSampleSize) >= targetSize && (halfWidth / inSampleSize) >= targetSize) { + inSampleSize *= 2 } } + + val decodeOptions = BitmapFactory.Options().apply { + this.inSampleSize = inSampleSize + } + + context.contentResolver.openInputStream(uri)?.use { inputStream -> + BitmapFactory.decodeStream(inputStream, null, decodeOptions) + } } catch (e: Exception) { Log.e(TAG, "Error loading bitmap: ${e.message}") null diff --git a/app/src/main/java/com/sameerasw/airsync/utils/MacDeviceStatusManager.kt b/app/src/main/java/com/sameerasw/airsync/utils/MacDeviceStatusManager.kt index e873edf9..0d07d9bd 100644 --- a/app/src/main/java/com/sameerasw/airsync/utils/MacDeviceStatusManager.kt +++ b/app/src/main/java/com/sameerasw/airsync/utils/MacDeviceStatusManager.kt @@ -209,7 +209,25 @@ object MacDeviceStatusManager { return try { if (base64String.isNotEmpty()) { val decodedBytes = Base64.decode(base64String, Base64.DEFAULT) - BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.size) + val boundsOptions = BitmapFactory.Options().apply { + inJustDecodeBounds = true + } + BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.size, boundsOptions) + + val targetSize = 512 + var inSampleSize = 1 + if (boundsOptions.outHeight > targetSize || boundsOptions.outWidth > targetSize) { + val halfHeight = boundsOptions.outHeight / 2 + val halfWidth = boundsOptions.outWidth / 2 + while ((halfHeight / inSampleSize) >= targetSize && (halfWidth / inSampleSize) >= targetSize) { + inSampleSize *= 2 + } + } + + val decodeOptions = BitmapFactory.Options().apply { + this.inSampleSize = inSampleSize + } + BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.size, decodeOptions) } else { null } diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml index e936f554..2681b821 100644 --- a/app/src/main/res/values-night/themes.xml +++ b/app/src/main/res/values-night/themes.xml @@ -11,10 +11,6 @@ true false - - @android:color/transparent - @android:color/transparent - false false diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index bbcdb83b..a215f460 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -12,10 +12,6 @@ true false - - @android:color/transparent - @android:color/transparent - true true @@ -30,8 +26,5 @@ false @android:style/Animation - - @android:color/transparent - @android:color/transparent \ No newline at end of file