diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000000..e94930a509b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +indent_size = 4 +indent_style = space + +[*.{kt,kts}] +ktlint_code_style = android_studio +# Disable wildcard imports +ij_kotlin_name_count_to_use_star_import = 9999 +ij_kotlin_name_count_to_use_star_import_for_members = 9999 +# ktlint overrides +ktlint_function_naming_ignore_when_annotated_with=Composable +max_line_length = off diff --git a/Gemfile.lock b/Gemfile.lock index 0e0e0c9fdc9..0825be63372 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,25 +3,25 @@ GEM specs: CFPropertyList (3.0.6) rexml - addressable (2.8.5) + addressable (2.8.6) public_suffix (>= 2.0.2, < 6.0) artifactory (3.0.15) atomos (0.1.3) - aws-eventstream (1.2.0) - aws-partitions (1.824.0) - aws-sdk-core (3.181.1) - aws-eventstream (~> 1, >= 1.0.2) + aws-eventstream (1.3.0) + aws-partitions (1.887.0) + aws-sdk-core (3.191.0) + aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.651.0) - aws-sigv4 (~> 1.5) + aws-sigv4 (~> 1.8) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.71.0) - aws-sdk-core (~> 3, >= 3.177.0) + aws-sdk-kms (1.77.0) + aws-sdk-core (~> 3, >= 3.191.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.134.0) - aws-sdk-core (~> 3, >= 3.181.0) + aws-sdk-s3 (1.143.0) + aws-sdk-core (~> 3, >= 3.191.0) aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.6) - aws-sigv4 (1.6.0) + aws-sigv4 (~> 1.8) + aws-sigv4 (1.8.0) aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) claide (1.1.0) @@ -32,11 +32,10 @@ GEM declarative (0.0.20) digest-crc (0.6.5) rake (>= 12.0.0, < 14.0.0) - domain_name (0.5.20190701) - unf (>= 0.0.5, < 1.0.0) + domain_name (0.6.20240107) dotenv (2.8.1) emoji_regex (3.2.3) - excon (0.103.0) + excon (0.109.0) faraday (1.10.3) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) @@ -65,8 +64,8 @@ GEM faraday-retry (1.0.3) faraday_middleware (1.2.0) faraday (~> 1.0) - fastimage (2.2.7) - fastlane (2.215.1) + fastimage (2.3.0) + fastlane (2.219.0) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) @@ -85,6 +84,7 @@ GEM gh_inspector (>= 1.1.2, < 2.0.0) google-apis-androidpublisher_v3 (~> 0.3) google-apis-playcustomapp_v1 (~> 0.1) + google-cloud-env (>= 1.6.0, < 2.0.0) google-cloud-storage (~> 1.31) highline (~> 2.0) http-cookie (~> 1.0.5) @@ -93,7 +93,7 @@ GEM mini_magick (>= 4.9.4, < 5.0.0) multipart-post (>= 2.0.0, < 3.0.0) naturally (~> 2.2) - optparse (~> 0.1.1) + optparse (>= 0.1.1) plist (>= 3.1.0, < 4.0.0) rubyzip (>= 2.0.0, < 3.0.0) security (= 0.1.3) @@ -108,9 +108,9 @@ GEM xcpretty-travis-formatter (>= 0.0.3) fastlane-plugin-amazon_app_submission (0.4.0) gh_inspector (1.1.3) - google-apis-androidpublisher_v3 (0.49.0) + google-apis-androidpublisher_v3 (0.54.0) google-apis-core (>= 0.11.0, < 2.a) - google-apis-core (0.11.1) + google-apis-core (0.11.3) addressable (~> 2.5, >= 2.5.1) googleauth (>= 0.16.2, < 2.a) httpclient (>= 2.8.1, < 3.a) @@ -118,28 +118,27 @@ GEM representable (~> 3.0) retriable (>= 2.0, < 4.a) rexml - webrick google-apis-iamcredentials_v1 (0.17.0) google-apis-core (>= 0.11.0, < 2.a) google-apis-playcustomapp_v1 (0.13.0) google-apis-core (>= 0.11.0, < 2.a) - google-apis-storage_v1 (0.19.0) - google-apis-core (>= 0.9.0, < 2.a) - google-cloud-core (1.6.0) - google-cloud-env (~> 1.0) + google-apis-storage_v1 (0.31.0) + google-apis-core (>= 0.11.0, < 2.a) + google-cloud-core (1.6.1) + google-cloud-env (>= 1.0, < 3.a) google-cloud-errors (~> 1.0) google-cloud-env (1.6.0) faraday (>= 0.17.3, < 3.0) google-cloud-errors (1.3.1) - google-cloud-storage (1.44.0) + google-cloud-storage (1.47.0) addressable (~> 2.8) digest-crc (~> 0.4) google-apis-iamcredentials_v1 (~> 0.1) - google-apis-storage_v1 (~> 0.19.0) + google-apis-storage_v1 (~> 0.31.0) google-cloud-core (~> 1.6) googleauth (>= 0.16.2, < 2.a) mini_mime (~> 1.0) - googleauth (1.8.0) + googleauth (1.8.1) faraday (>= 0.17.3, < 3.a) jwt (>= 1.4, < 3.0) multi_json (~> 1.11) @@ -150,7 +149,7 @@ GEM domain_name (~> 0.5) httpclient (2.8.3) jmespath (1.6.2) - json (2.6.3) + json (2.7.1) jwt (2.7.1) mini_magick (4.12.0) mini_mime (1.1.5) @@ -158,11 +157,11 @@ GEM multipart-post (2.3.0) nanaimo (0.3.0) naturally (2.2.1) - optparse (0.1.1) + optparse (0.4.0) os (1.1.4) - plist (3.7.0) - public_suffix (5.0.3) - rake (13.0.6) + plist (3.7.1) + public_suffix (5.0.4) + rake (13.1.0) representable (3.2.0) declarative (< 0.1.0) trailblazer-option (>= 0.1.1, < 0.2.0) @@ -186,17 +185,13 @@ GEM unicode-display_width (>= 1.1.1, < 3) trailblazer-option (0.1.2) tty-cursor (0.7.1) - tty-screen (0.8.1) + tty-screen (0.8.2) tty-spinner (0.9.3) tty-cursor (~> 0.7) uber (0.1.0) - unf (0.1.4) - unf_ext - unf_ext (0.0.8.2) - unicode-display_width (2.4.2) - webrick (1.8.1) + unicode-display_width (2.5.0) word_wrap (1.0.0) - xcodeproj (1.22.0) + xcodeproj (1.24.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 8660f46013d..cf18f8d4fd2 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -201,7 +201,6 @@ dependencies { implementation(libs.activity.compose) implementation(libs.navigation.compose) implementation(libs.accompanist.systemuicontroller) - implementation(libs.accompanist.themeadapter.material) implementation(libs.iconics.core) implementation(libs.iconics.compose) diff --git a/app/src/full/java/io/homeassistant/companion/android/CrashHandling.kt b/app/src/full/java/io/homeassistant/companion/android/CrashHandling.kt index 0753db14023..afcece3ffe9 100644 --- a/app/src/full/java/io/homeassistant/companion/android/CrashHandling.kt +++ b/app/src/full/java/io/homeassistant/companion/android/CrashHandling.kt @@ -4,8 +4,6 @@ import android.content.Context import android.util.Log import io.sentry.SentryOptions import io.sentry.android.core.SentryAndroid -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext import java.io.File import java.io.PrintWriter import java.net.ConnectException @@ -18,6 +16,8 @@ import javax.net.ssl.SSLException import javax.net.ssl.SSLHandshakeException import javax.net.ssl.SSLPeerUnverifiedException import javax.net.ssl.SSLProtocolException +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext private const val FATAL_CRASH_FILE = "/fatalcrash/last_crash" diff --git a/app/src/full/java/io/homeassistant/companion/android/launch/LaunchPresenterImpl.kt b/app/src/full/java/io/homeassistant/companion/android/launch/LaunchPresenterImpl.kt index ba4dbe9a6c0..7d741964f67 100644 --- a/app/src/full/java/io/homeassistant/companion/android/launch/LaunchPresenterImpl.kt +++ b/app/src/full/java/io/homeassistant/companion/android/launch/LaunchPresenterImpl.kt @@ -8,8 +8,8 @@ import io.homeassistant.companion.android.BuildConfig import io.homeassistant.companion.android.common.data.integration.DeviceRegistration import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.onboarding.getMessagingToken -import kotlinx.coroutines.launch import javax.inject.Inject +import kotlinx.coroutines.launch @ActivityScoped class LaunchPresenterImpl @Inject constructor( diff --git a/app/src/full/java/io/homeassistant/companion/android/location/HighAccuracyLocationService.kt b/app/src/full/java/io/homeassistant/companion/android/location/HighAccuracyLocationService.kt index aa499780ec8..06ad47fc25c 100644 --- a/app/src/full/java/io/homeassistant/companion/android/location/HighAccuracyLocationService.kt +++ b/app/src/full/java/io/homeassistant/companion/android/location/HighAccuracyLocationService.kt @@ -19,12 +19,12 @@ import com.google.android.gms.location.FusedLocationProviderClient import com.google.android.gms.location.LocationRequest import com.google.android.gms.location.LocationServices import com.google.android.gms.location.Priority -import io.homeassistant.companion.android.common.util.highAccuracyChannel +import io.homeassistant.companion.android.common.R as commonR +import io.homeassistant.companion.android.common.util.CHANNEL_HIGH_ACCURACY import io.homeassistant.companion.android.sensors.LocationSensorManager import io.homeassistant.companion.android.util.ForegroundServiceLauncher import kotlin.math.abs import kotlin.math.roundToInt -import io.homeassistant.companion.android.common.R as commonR class HighAccuracyLocationService : Service() { @@ -112,7 +112,7 @@ class HighAccuracyLocationService : Service() { private fun createNotificationBuilder(context: Context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val channel = NotificationChannel(highAccuracyChannel, context.getString(commonR.string.high_accuracy_mode_channel_name), NotificationManager.IMPORTANCE_DEFAULT) + val channel = NotificationChannel(CHANNEL_HIGH_ACCURACY, context.getString(commonR.string.high_accuracy_mode_channel_name), NotificationManager.IMPORTANCE_DEFAULT) notificationManagerCompat.createNotificationChannel(channel) } @@ -123,7 +123,7 @@ class HighAccuracyLocationService : Service() { val disablePendingIntent = PendingIntent.getBroadcast(context, 0, disableIntent, PendingIntent.FLAG_MUTABLE) - notificationBuilder = NotificationCompat.Builder(context, highAccuracyChannel) + notificationBuilder = NotificationCompat.Builder(context, CHANNEL_HIGH_ACCURACY) .setSmallIcon(commonR.drawable.ic_stat_ic_notification) .setColor(Color.GRAY) .setOngoing(true) diff --git a/app/src/full/java/io/homeassistant/companion/android/matter/MatterCommissioningActivity.kt b/app/src/full/java/io/homeassistant/companion/android/matter/MatterCommissioningActivity.kt index 64b2fc8d362..0d481b88d24 100644 --- a/app/src/full/java/io/homeassistant/companion/android/matter/MatterCommissioningActivity.kt +++ b/app/src/full/java/io/homeassistant/companion/android/matter/MatterCommissioningActivity.kt @@ -12,16 +12,16 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.lifecycleScope -import com.google.accompanist.themeadapter.material.MdcTheme import com.google.android.gms.home.matter.Matter import com.google.android.gms.home.matter.commissioning.SharedDeviceData import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.database.server.Server import io.homeassistant.companion.android.matter.views.MatterCommissioningView +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme import io.homeassistant.companion.android.webview.WebViewActivity -import kotlinx.coroutines.launch import javax.inject.Inject +import kotlinx.coroutines.launch @AndroidEntryPoint class MatterCommissioningActivity : AppCompatActivity() { @@ -47,7 +47,7 @@ class MatterCommissioningActivity : AppCompatActivity() { super.onCreate(savedInstanceState) setContent { - MdcTheme { + HomeAssistantAppTheme { MatterCommissioningView( step = viewModel.step, deviceName = deviceName, diff --git a/app/src/full/java/io/homeassistant/companion/android/matter/MatterCommissioningService.kt b/app/src/full/java/io/homeassistant/companion/android/matter/MatterCommissioningService.kt index 33cdcc0e809..b8f34d3acf9 100644 --- a/app/src/full/java/io/homeassistant/companion/android/matter/MatterCommissioningService.kt +++ b/app/src/full/java/io/homeassistant/companion/android/matter/MatterCommissioningService.kt @@ -10,12 +10,12 @@ import com.google.android.gms.home.matter.commissioning.CommissioningService import com.google.android.gms.home.matter.commissioning.CommissioningService.CommissioningError import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.common.data.servers.ServerManager +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.cancel import kotlinx.coroutines.launch -import javax.inject.Inject @AndroidEntryPoint class MatterCommissioningService : Service(), CommissioningService.Callback { @@ -58,7 +58,7 @@ class MatterCommissioningService : Service(), CommissioningService.Callback { commissioningServiceDelegate.sendCommissioningError(CommissioningError.OTHER) return@launch } - val result = matterManager.commissionOnNetworkDevice(metadata.passcode, serverId) + val result = matterManager.commissionOnNetworkDevice(metadata.passcode, metadata.networkLocation.formattedIpAddress, serverId) Log.d(TAG, "Server commissioning was ${if (result?.success == true) "successful" else "not successful (${result?.errorCode})"}") if (result?.success == true) { diff --git a/app/src/full/java/io/homeassistant/companion/android/matter/MatterCommissioningViewModel.kt b/app/src/full/java/io/homeassistant/companion/android/matter/MatterCommissioningViewModel.kt index e522a259ce2..b4df665a5f5 100644 --- a/app/src/full/java/io/homeassistant/companion/android/matter/MatterCommissioningViewModel.kt +++ b/app/src/full/java/io/homeassistant/companion/android/matter/MatterCommissioningViewModel.kt @@ -2,7 +2,6 @@ package io.homeassistant.companion.android.matter import android.app.Application import android.content.IntentSender -import android.util.Log import androidx.activity.result.ActivityResult import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -12,8 +11,8 @@ import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.thread.ThreadManager -import kotlinx.coroutines.launch import javax.inject.Inject +import kotlinx.coroutines.launch @HiltViewModel class MatterCommissioningViewModel @Inject constructor( @@ -91,23 +90,12 @@ class MatterCommissioningViewModel @Inject constructor( } } - suspend fun syncThreadIfNecessary(): IntentSender? { + fun syncThreadIfNecessary(): IntentSender? { step = CommissioningFlowStep.Working - return try { - val result = threadManager.syncPreferredDataset( - getApplication().applicationContext, - serverId, - viewModelScope - ) - when (result) { - is ThreadManager.SyncResult.OnlyOnDevice -> result.exportIntent - is ThreadManager.SyncResult.AllHaveCredentials -> result.exportIntent - else -> null - } - } catch (e: Exception) { - Log.w(TAG, "Unable to sync preferred Thread dataset, continuing", e) - null - } + // The app used to sync Thread credentials here until commit 26a472a, but it was + // (temporarily?) removed due to slowing down the Matter commissioning flow for the user + // and limited usefulness of the result (because of API limitations) + return null } fun onThreadPermissionResult(result: ActivityResult, code: String) { diff --git a/app/src/full/java/io/homeassistant/companion/android/matter/MatterManagerImpl.kt b/app/src/full/java/io/homeassistant/companion/android/matter/MatterManagerImpl.kt index aa221818e85..7e47d8b1eee 100644 --- a/app/src/full/java/io/homeassistant/companion/android/matter/MatterManagerImpl.kt +++ b/app/src/full/java/io/homeassistant/companion/android/matter/MatterManagerImpl.kt @@ -63,9 +63,9 @@ class MatterManagerImpl @Inject constructor( } } - override suspend fun commissionOnNetworkDevice(pin: Long, serverId: Int): MatterCommissionResponse? { + override suspend fun commissionOnNetworkDevice(pin: Long, ip: String, serverId: Int): MatterCommissionResponse? { return try { - serverManager.webSocketRepository(serverId).commissionMatterDeviceOnNetwork(pin) + serverManager.webSocketRepository(serverId).commissionMatterDeviceOnNetwork(pin, ip) } catch (e: Exception) { Log.e(TAG, "Error while executing server commissioning request", e) null diff --git a/app/src/full/java/io/homeassistant/companion/android/matter/views/MatterCommissioningView.kt b/app/src/full/java/io/homeassistant/companion/android/matter/views/MatterCommissioningView.kt index 2f448608f79..06ce9d61497 100644 --- a/app/src/full/java/io/homeassistant/companion/android/matter/views/MatterCommissioningView.kt +++ b/app/src/full/java/io/homeassistant/companion/android/matter/views/MatterCommissioningView.kt @@ -32,15 +32,15 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import com.google.accompanist.themeadapter.material.MdcTheme import io.homeassistant.companion.android.R +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.database.server.Server import io.homeassistant.companion.android.database.server.ServerConnectionInfo import io.homeassistant.companion.android.database.server.ServerSessionInfo import io.homeassistant.companion.android.database.server.ServerUserInfo import io.homeassistant.companion.android.matter.MatterCommissioningViewModel.CommissioningFlowStep +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme import kotlin.math.min -import io.homeassistant.companion.android.common.R as commonR @Composable fun MatterCommissioningView( @@ -217,7 +217,7 @@ fun MatterCommissioningViewHeader() { fun PreviewMatterCommissioningView( @PreviewParameter(MatterCommissioningViewPreviewStates::class) step: CommissioningFlowStep ) { - MdcTheme { + HomeAssistantAppTheme { MatterCommissioningView( step = step, deviceName = "Manufacturer Matter Light", diff --git a/app/src/full/java/io/homeassistant/companion/android/notifications/FirebaseCloudMessagingService.kt b/app/src/full/java/io/homeassistant/companion/android/notifications/FirebaseCloudMessagingService.kt index 1c01ff42a57..9065d8cf1f8 100644 --- a/app/src/full/java/io/homeassistant/companion/android/notifications/FirebaseCloudMessagingService.kt +++ b/app/src/full/java/io/homeassistant/companion/android/notifications/FirebaseCloudMessagingService.kt @@ -6,11 +6,11 @@ import com.google.firebase.messaging.RemoteMessage import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.common.data.integration.DeviceRegistration import io.homeassistant.companion.android.common.data.servers.ServerManager +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch -import javax.inject.Inject @AndroidEntryPoint class FirebaseCloudMessagingService : FirebaseMessagingService() { diff --git a/app/src/full/java/io/homeassistant/companion/android/onboarding/WearOnboardingListener.kt b/app/src/full/java/io/homeassistant/companion/android/onboarding/WearOnboardingListener.kt index 079d2bb66ed..cf1474befd3 100644 --- a/app/src/full/java/io/homeassistant/companion/android/onboarding/WearOnboardingListener.kt +++ b/app/src/full/java/io/homeassistant/companion/android/onboarding/WearOnboardingListener.kt @@ -9,8 +9,8 @@ import com.google.android.gms.wearable.Wearable import com.google.android.gms.wearable.WearableListenerService import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.common.data.servers.ServerManager -import kotlinx.coroutines.runBlocking import javax.inject.Inject +import kotlinx.coroutines.runBlocking @AndroidEntryPoint @SuppressLint("VisibleForTests") // https://issuetracker.google.com/issues/239451111 diff --git a/app/src/full/java/io/homeassistant/companion/android/sensors/ActivitySensorManager.kt b/app/src/full/java/io/homeassistant/companion/android/sensors/ActivitySensorManager.kt index 43c4846e696..c07094f9a81 100644 --- a/app/src/full/java/io/homeassistant/companion/android/sensors/ActivitySensorManager.kt +++ b/app/src/full/java/io/homeassistant/companion/android/sensors/ActivitySensorManager.kt @@ -15,11 +15,11 @@ import com.google.android.gms.location.SleepClassifyEvent import com.google.android.gms.location.SleepSegmentEvent import com.google.android.gms.location.SleepSegmentRequest import dagger.hilt.android.AndroidEntryPoint +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.sensors.SensorManager import io.homeassistant.companion.android.common.sensors.SensorReceiverBase import io.homeassistant.companion.android.common.util.STATE_UNKNOWN import java.util.concurrent.TimeUnit -import io.homeassistant.companion.android.common.R as commonR @AndroidEntryPoint class ActivitySensorManager : BroadcastReceiver(), SensorManager { @@ -234,11 +234,11 @@ class ActivitySensorManager : BroadcastReceiver(), SensorManager { } } if (( - isEnabled(context, sleepConfidence) || isEnabled( + isEnabled(context, sleepConfidence) || isEnabled( context, sleepSegment ) - ) && !sleepRegistration + ) && !sleepRegistration ) { val pendingIntent = getSleepPendingIntent(context) Log.d(TAG, "Registering for sleep updates") diff --git a/app/src/full/java/io/homeassistant/companion/android/sensors/AndroidAutoSensorManager.kt b/app/src/full/java/io/homeassistant/companion/android/sensors/AndroidAutoSensorManager.kt index 2d899c334bb..f94f3b888e6 100644 --- a/app/src/full/java/io/homeassistant/companion/android/sensors/AndroidAutoSensorManager.kt +++ b/app/src/full/java/io/homeassistant/companion/android/sensors/AndroidAutoSensorManager.kt @@ -5,12 +5,12 @@ import android.os.Build import android.util.Log import androidx.car.app.connection.CarConnection import androidx.lifecycle.Observer +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.sensors.SensorManager import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch -import io.homeassistant.companion.android.common.R as commonR class AndroidAutoSensorManager : SensorManager, Observer { diff --git a/app/src/full/java/io/homeassistant/companion/android/sensors/GeocodeSensorManager.kt b/app/src/full/java/io/homeassistant/companion/android/sensors/GeocodeSensorManager.kt index a400f52088e..7739f438230 100644 --- a/app/src/full/java/io/homeassistant/companion/android/sensors/GeocodeSensorManager.kt +++ b/app/src/full/java/io/homeassistant/companion/android/sensors/GeocodeSensorManager.kt @@ -8,19 +8,19 @@ import android.os.Build import android.os.Build.VERSION.SDK_INT import android.util.Log import com.google.android.gms.location.LocationServices +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.sensors.SensorManager import io.homeassistant.companion.android.common.util.STATE_UNKNOWN import io.homeassistant.companion.android.database.AppDatabase import io.homeassistant.companion.android.database.sensor.SensorSetting import io.homeassistant.companion.android.database.sensor.SensorSettingType import io.homeassistant.companion.android.location.HighAccuracyLocationService -import kotlinx.coroutines.MainScope -import kotlinx.coroutines.launch -import kotlinx.coroutines.tasks.await import kotlin.coroutines.resume import kotlin.coroutines.resumeWithException import kotlin.coroutines.suspendCoroutine -import io.homeassistant.companion.android.common.R as commonR +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.tasks.await class GeocodeSensorManager : SensorManager { diff --git a/app/src/full/java/io/homeassistant/companion/android/settings/wear/SettingsWearActivity.kt b/app/src/full/java/io/homeassistant/companion/android/settings/wear/SettingsWearActivity.kt index 4bc961d0b52..d15d42d5564 100644 --- a/app/src/full/java/io/homeassistant/companion/android/settings/wear/SettingsWearActivity.kt +++ b/app/src/full/java/io/homeassistant/companion/android/settings/wear/SettingsWearActivity.kt @@ -19,6 +19,7 @@ import com.google.android.gms.wearable.CapabilityInfo import com.google.android.gms.wearable.Node import com.google.android.gms.wearable.NodeClient import com.google.android.gms.wearable.Wearable +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.databinding.ActivitySettingsWearBinding import io.homeassistant.companion.android.settings.HelpMenuProvider import io.homeassistant.companion.android.settings.wear.views.SettingsWearMainView @@ -28,7 +29,6 @@ import kotlinx.coroutines.guava.await import kotlinx.coroutines.launch import kotlinx.coroutines.tasks.await import kotlinx.coroutines.withContext -import io.homeassistant.companion.android.common.R as commonR class SettingsWearActivity : AppCompatActivity(), CapabilityClient.OnCapabilityChangedListener { diff --git a/app/src/full/java/io/homeassistant/companion/android/settings/wear/SettingsWearViewModel.kt b/app/src/full/java/io/homeassistant/companion/android/settings/wear/SettingsWearViewModel.kt index fabab805ec8..5b5217891d0 100644 --- a/app/src/full/java/io/homeassistant/companion/android/settings/wear/SettingsWearViewModel.kt +++ b/app/src/full/java/io/homeassistant/companion/android/settings/wear/SettingsWearViewModel.kt @@ -5,7 +5,6 @@ import android.app.Application import android.util.Log import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateMapOf -import androidx.compose.runtime.mutableStateOf import androidx.core.net.toUri import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope @@ -22,7 +21,9 @@ import com.google.android.gms.wearable.PutDataMapRequest import com.google.android.gms.wearable.Wearable import dagger.hilt.android.lifecycle.HiltViewModel import io.homeassistant.companion.android.HomeAssistantApplication +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.Entity +import io.homeassistant.companion.android.common.data.prefs.impl.entities.TemplateTileConfig import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.common.util.WearDataMessages import io.homeassistant.companion.android.database.server.Server @@ -30,6 +31,8 @@ import io.homeassistant.companion.android.database.server.ServerConnectionInfo import io.homeassistant.companion.android.database.server.ServerSessionInfo import io.homeassistant.companion.android.database.server.ServerType import io.homeassistant.companion.android.database.server.ServerUserInfo +import java.util.UUID +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -40,9 +43,6 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch import kotlinx.coroutines.tasks.await import org.burnoutcrew.reorderable.ItemPosition -import java.util.UUID -import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR @HiltViewModel @SuppressLint("VisibleForTests") // https://issuetracker.google.com/issues/239451111 @@ -74,11 +74,9 @@ class SettingsWearViewModel @Inject constructor( private set var favoriteEntityIds = mutableStateListOf() private set - var templateTileContent = mutableStateOf("") - private set - var templateTileContentRendered = mutableStateOf("") + var templateTiles = mutableStateMapOf() private set - var templateTileRefreshInterval = mutableStateOf(0) + var templateTilesRenderedTemplates = mutableStateMapOf() private set private val _resultSnackbar = MutableSharedFlow() @@ -144,17 +142,42 @@ class SettingsWearViewModel @Inject constructor( } } - fun setTemplateContent(template: String) { - templateTileContent.value = template + fun setTemplateTileContent(tileId: Int, updatedTemplateTileContent: String) { + val templateTileConfig = templateTiles[tileId] + templateTileConfig?.let { + templateTiles[tileId] = it.copy(template = updatedTemplateTileContent) + renderTemplate(tileId, updatedTemplateTileContent) + } + } + + fun setTemplateTileRefreshInterval(tileId: Int, refreshInterval: Int) { + val templateTileConfig = templateTiles[tileId] + templateTileConfig?.let { + templateTiles[tileId] = it.copy(refreshInterval = refreshInterval) + } + } + + private fun setTemplateTiles(newTemplateTiles: Map) { + templateTiles.clear() + templateTilesRenderedTemplates.clear() + + templateTiles.putAll(newTemplateTiles) + templateTiles.forEach { + renderTemplate(it.key, it.value.template) + } + } + + private fun renderTemplate(tileId: Int, template: String) { if (template.isNotEmpty() && serverId != 0) { viewModelScope.launch { try { - templateTileContentRendered.value = - serverManager.integrationRepository(serverId).renderTemplate(template, mapOf()).toString() + templateTilesRenderedTemplates[tileId] = serverManager + .integrationRepository(serverId) + .renderTemplate(template, mapOf()).toString() } catch (e: Exception) { - Log.e(TAG, "Exception while rendering template", e) + Log.e(TAG, "Exception while rendering template for tile ID $tileId", e) // JsonMappingException suggests that template is not a String (= error) - templateTileContentRendered.value = getApplication().getString( + templateTilesRenderedTemplates[tileId] = getApplication().getString( if (e.cause is JsonMappingException) { commonR.string.template_error } else { @@ -164,7 +187,7 @@ class SettingsWearViewModel @Inject constructor( } } } else { - templateTileContentRendered.value = "" + templateTilesRenderedTemplates[tileId] = "" } } @@ -254,9 +277,8 @@ class SettingsWearViewModel @Inject constructor( } fun sendTemplateTileInfo() { - val putDataRequest = PutDataMapRequest.create("/updateTemplateTile").run { - dataMap.putString(WearDataMessages.CONFIG_TEMPLATE_TILE, templateTileContent.value) - dataMap.putInt(WearDataMessages.CONFIG_TEMPLATE_TILE_REFRESH_INTERVAL, templateTileRefreshInterval.value) + val putDataRequest = PutDataMapRequest.create("/updateTemplateTiles").run { + dataMap.putString(WearDataMessages.CONFIG_TEMPLATE_TILES, objectMapper.writeValueAsString(templateTiles)) setUrgent() asPutDataRequest() } @@ -308,8 +330,14 @@ class SettingsWearViewModel @Inject constructor( favoriteEntityIdList.forEach { entityId -> favoriteEntityIds.add(entityId) } - setTemplateContent(data.getString(WearDataMessages.CONFIG_TEMPLATE_TILE, "")) - templateTileRefreshInterval.value = data.getInt(WearDataMessages.CONFIG_TEMPLATE_TILE_REFRESH_INTERVAL, 0) + + val templateTilesFromWear: Map = objectMapper.readValue( + data.getString( + WearDataMessages.CONFIG_TEMPLATE_TILES, + "{}" + ) + ) + setTemplateTiles(templateTilesFromWear) _hasData.value = true } diff --git a/app/src/full/java/io/homeassistant/companion/android/settings/wear/views/SettingsWearFavoritesView.kt b/app/src/full/java/io/homeassistant/companion/android/settings/wear/views/SettingsWearFavoritesView.kt index cb5a8ff8274..de4869fed60 100644 --- a/app/src/full/java/io/homeassistant/companion/android/settings/wear/views/SettingsWearFavoritesView.kt +++ b/app/src/full/java/io/homeassistant/companion/android/settings/wear/views/SettingsWearFavoritesView.kt @@ -17,6 +17,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.integration.friendlyName import io.homeassistant.companion.android.settings.wear.SettingsWearViewModel @@ -30,7 +31,6 @@ import kotlinx.coroutines.withContext import org.burnoutcrew.reorderable.ReorderableItem import org.burnoutcrew.reorderable.rememberReorderableLazyListState import org.burnoutcrew.reorderable.reorderable -import io.homeassistant.companion.android.common.R as commonR @Composable fun LoadWearFavoritesSettings( diff --git a/app/src/full/java/io/homeassistant/companion/android/settings/wear/views/SettingsWearHomeView.kt b/app/src/full/java/io/homeassistant/companion/android/settings/wear/views/SettingsWearHomeView.kt index c4bce7605b9..326fb70e24e 100644 --- a/app/src/full/java/io/homeassistant/companion/android/settings/wear/views/SettingsWearHomeView.kt +++ b/app/src/full/java/io/homeassistant/companion/android/settings/wear/views/SettingsWearHomeView.kt @@ -11,14 +11,16 @@ import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.stringResource +import androidx.navigation.NavType import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController -import com.google.accompanist.themeadapter.material.MdcTheme +import androidx.navigation.navArgument import com.mikepenz.iconics.compose.Image import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial -import io.homeassistant.companion.android.settings.wear.SettingsWearViewModel import io.homeassistant.companion.android.common.R as commonR +import io.homeassistant.companion.android.settings.wear.SettingsWearViewModel +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme const val WEAR_DOCS_LINK = "https://companion.home-assistant.io/docs/wear-os/" @@ -29,7 +31,7 @@ fun LoadSettingsHomeView( loginWearOs: () -> Unit, onStartBackClicked: () -> Unit ) { - MdcTheme { + HomeAssistantAppTheme { val navController = rememberNavController() NavHost(navController = navController, startDestination = SettingsWearMainView.LANDING) { composable(SettingsWearMainView.FAVORITES) { @@ -48,30 +50,50 @@ fun LoadSettingsHomeView( hasData = hasData, isAuthed = isAuthenticated, navigateFavorites = { navController.navigate(SettingsWearMainView.FAVORITES) }, - navigateTemplateTile = { navController.navigate(SettingsWearMainView.TEMPLATE) }, + navigateTemplateTile = { navController.navigate(SettingsWearMainView.TEMPLATES) }, loginWearOs = loginWearOs, onBackClicked = onStartBackClicked, events = settingsWearViewModel.resultSnackbar ) } - composable(SettingsWearMainView.TEMPLATE) { - SettingsWearTemplateTile( - template = settingsWearViewModel.templateTileContent.value, - renderedTemplate = settingsWearViewModel.templateTileContentRendered.value, - refreshInterval = settingsWearViewModel.templateTileRefreshInterval.value, - onContentChanged = { - settingsWearViewModel.setTemplateContent(it) - settingsWearViewModel.sendTemplateTileInfo() - }, - onRefreshIntervalChanged = { - settingsWearViewModel.templateTileRefreshInterval.value = it - settingsWearViewModel.sendTemplateTileInfo() + composable(SettingsWearMainView.TEMPLATES) { + SettingsWearTemplateTileList( + templateTiles = settingsWearViewModel.templateTiles, + onTemplateTileClicked = { tileId -> + navController.navigate(SettingsWearMainView.TEMPLATE_TILE.format(tileId)) }, onBackClicked = { navController.navigateUp() } ) } + composable( + route = SettingsWearMainView.TEMPLATE_TILE.format("{tileId}"), + arguments = listOf(navArgument("tileId") { type = NavType.IntType }) + ) { backStackEntry -> + val tileId = backStackEntry.arguments?.getInt("tileId") + val templateTile = settingsWearViewModel.templateTiles[tileId] + val renderedTemplate = settingsWearViewModel.templateTilesRenderedTemplates[tileId] + + templateTile?.let { + SettingsWearTemplateTile( + template = it.template, + renderedTemplate = renderedTemplate ?: "", + refreshInterval = it.refreshInterval, + onContentChanged = { templateContent -> + settingsWearViewModel.setTemplateTileContent(tileId!!, templateContent) + settingsWearViewModel.sendTemplateTileInfo() + }, + onRefreshIntervalChanged = { refreshInterval -> + settingsWearViewModel.setTemplateTileRefreshInterval(tileId!!, refreshInterval) + settingsWearViewModel.sendTemplateTileInfo() + }, + onBackClicked = { + navController.navigateUp() + } + ) + } + } } } } diff --git a/app/src/full/java/io/homeassistant/companion/android/settings/wear/views/SettingsWearLandingView.kt b/app/src/full/java/io/homeassistant/companion/android/settings/wear/views/SettingsWearLandingView.kt index e8bc4ce47f4..42b4b9d3dfd 100644 --- a/app/src/full/java/io/homeassistant/companion/android/settings/wear/views/SettingsWearLandingView.kt +++ b/app/src/full/java/io/homeassistant/companion/android/settings/wear/views/SettingsWearLandingView.kt @@ -20,13 +20,13 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.settings.views.SettingsRow import io.homeassistant.companion.android.util.wearDeviceName import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import io.homeassistant.companion.android.common.R as commonR @Composable fun SettingWearLandingView( @@ -87,7 +87,7 @@ fun SettingWearLandingView( onClicked = navigateFavorites ) SettingsRow( - primaryText = stringResource(commonR.string.template_tile), + primaryText = stringResource(commonR.string.template_tiles), secondaryText = stringResource(commonR.string.template_tile_set_on_watch), mdiIcon = CommunityMaterial.Icon3.cmd_text_box, enabled = true, diff --git a/app/src/full/java/io/homeassistant/companion/android/settings/wear/views/SettingsWearMainView.kt b/app/src/full/java/io/homeassistant/companion/android/settings/wear/views/SettingsWearMainView.kt index 864829a3713..dea533cc7eb 100644 --- a/app/src/full/java/io/homeassistant/companion/android/settings/wear/views/SettingsWearMainView.kt +++ b/app/src/full/java/io/homeassistant/companion/android/settings/wear/views/SettingsWearMainView.kt @@ -32,7 +32,8 @@ class SettingsWearMainView : AppCompatActivity() { private var registerUrl: String? = null const val LANDING = "Landing" const val FAVORITES = "Favorites" - const val TEMPLATE = "Template" + const val TEMPLATES = "Templates" + const val TEMPLATE_TILE = "Template/%s" fun newInstance(context: Context, wearNodes: Set, url: String?): Intent { currentNodes = wearNodes @@ -70,11 +71,12 @@ class SettingsWearMainView : AppCompatActivity() { url = registerUrl, defaultDeviceName = currentNodes.firstOrNull()?.displayName ?: "unknown", locationTrackingPossible = false, + // While notifications are technically possible, the app can't handle this for the Wear device notificationsPossible = false, isWatch = true, discoveryOptions = OnboardApp.DiscoveryOptions.ADD_EXISTING_EXTERNAL, mayRequireTlsClientCertificate = (application as HomeAssistantApplication).keyChainRepository.getPrivateKey() != null - ) // While notifications are technically possible, the app can't handle this for the Wear device + ) ) } diff --git a/app/src/full/java/io/homeassistant/companion/android/settings/wear/views/SettingsWearTemplateTile.kt b/app/src/full/java/io/homeassistant/companion/android/settings/wear/views/SettingsWearTemplateTile.kt index 0e5ff2e8c61..e2a901d635e 100644 --- a/app/src/full/java/io/homeassistant/companion/android/settings/wear/views/SettingsWearTemplateTile.kt +++ b/app/src/full/java/io/homeassistant/companion/android/settings/wear/views/SettingsWearTemplateTile.kt @@ -36,14 +36,15 @@ import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.core.text.HtmlCompat.FROM_HTML_MODE_LEGACY import androidx.core.text.HtmlCompat.fromHtml import com.mikepenz.iconics.compose.Image import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial -import io.homeassistant.companion.android.util.intervalToString import io.homeassistant.companion.android.common.R as commonR +import io.homeassistant.companion.android.util.intervalToString @Composable fun SettingsWearTemplateTile( @@ -87,7 +88,7 @@ fun SettingsWearTemplateTile( expanded = dropdownExpanded, onDismissRequest = { dropdownExpanded = false } ) { - val options = listOf(0, 60, 2 * 60, 5 * 60, 10 * 60, 15 * 60, 30 * 60, 60 * 60, 2 * 60 * 60, 5 * 60 * 60, 10 * 60 * 60, 24 * 60 * 60) + val options = listOf(0, 1, 60, 2 * 60, 5 * 60, 10 * 60, 15 * 60, 30 * 60, 60 * 60, 2 * 60 * 60, 5 * 60 * 60, 10 * 60 * 60, 24 * 60 * 60) for (option in options) { DropdownMenuItem(onClick = { onRefreshIntervalChanged(option) @@ -141,3 +142,16 @@ private fun parseHtml(renderedText: String) = buildAnnotatedString { } } } + +@Preview +@Composable +private fun PreviewSettingsWearTemplateTile() { + SettingsWearTemplateTile( + template = "Example entity: {{ states('sensor.example_entity') }}", + renderedTemplate = "Example entity: Lorem ipsum", + refreshInterval = 300, + onContentChanged = {}, + onRefreshIntervalChanged = {}, + onBackClicked = {} + ) +} diff --git a/app/src/full/java/io/homeassistant/companion/android/settings/wear/views/SettingsWearTemplateTileList.kt b/app/src/full/java/io/homeassistant/companion/android/settings/wear/views/SettingsWearTemplateTileList.kt new file mode 100644 index 00000000000..699d2f3c96b --- /dev/null +++ b/app/src/full/java/io/homeassistant/companion/android/settings/wear/views/SettingsWearTemplateTileList.kt @@ -0,0 +1,117 @@ +package io.homeassistant.companion.android.settings.wear.views + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Scaffold +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial +import io.homeassistant.companion.android.common.R as commonR +import io.homeassistant.companion.android.common.data.prefs.impl.entities.TemplateTileConfig +import io.homeassistant.companion.android.settings.views.SettingsRow + +@Composable +fun SettingsWearTemplateTileList( + templateTiles: Map, + onTemplateTileClicked: (tileId: Int) -> Unit, + onBackClicked: () -> Unit +) { + Scaffold( + topBar = { + SettingsWearTopAppBar( + title = { Text(stringResource(commonR.string.template_tiles)) }, + onBackClicked = onBackClicked, + docsLink = WEAR_DOCS_LINK + ) + } + ) { padding -> + Column( + Modifier + .verticalScroll(rememberScrollState()) + .padding(padding) + ) { + if (templateTiles.entries.isEmpty()) { + Text( + text = stringResource(commonR.string.template_tile_no_tiles_yet), + modifier = Modifier + .padding(all = 16.dp) + ) + } else { + Row( + modifier = Modifier + .height(48.dp) + .padding(start = 72.dp, end = 16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = stringResource(id = commonR.string.template_tile_configure), + style = MaterialTheme.typography.body2, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colors.primary + ) + } + + var index = 1 + for (templateTileEntry in templateTiles.entries) { + val template = templateTileEntry.value.template + SettingsRow( + primaryText = stringResource(commonR.string.template_tile_n, index++), + secondaryText = when { + template.length <= 100 -> template + else -> "${template.take(100)}…" + }, + mdiIcon = CommunityMaterial.Icon3.cmd_text_box, + enabled = true, + onClicked = { onTemplateTileClicked(templateTileEntry.key) } + ) + } + } + } + } +} + +@Preview +@Composable +private fun PreviewSettingsWearTemplateTileList() { + SettingsWearTemplateTileList( + templateTiles = mapOf( + 123 to TemplateTileConfig("Example entity 1: {{ states('sensor.example_entity_1') }}", 300), + 51468 to TemplateTileConfig("Example entity 2: {{ states('sensor.example_entity_2') }}", 0) + ), + onTemplateTileClicked = {}, + onBackClicked = {} + ) +} + +@Preview +@Composable +private fun PreviewSettingsWearTemplateSingleLegacyTile() { + SettingsWearTemplateTileList( + templateTiles = mapOf( + -1 to TemplateTileConfig("Example entity 1: {{ states('sensor.example_entity_1') }}", 300) + ), + onTemplateTileClicked = {}, + onBackClicked = {} + ) +} + +@Preview +@Composable +private fun PreviewSettingsWearTemplateTileListEmpty() { + SettingsWearTemplateTileList( + templateTiles = mapOf(), + onTemplateTileClicked = {}, + onBackClicked = {} + ) +} diff --git a/app/src/full/java/io/homeassistant/companion/android/thread/ThreadManagerImpl.kt b/app/src/full/java/io/homeassistant/companion/android/thread/ThreadManagerImpl.kt index 88b3ca55dac..1dce4401195 100644 --- a/app/src/full/java/io/homeassistant/companion/android/thread/ThreadManagerImpl.kt +++ b/app/src/full/java/io/homeassistant/companion/android/thread/ThreadManagerImpl.kt @@ -7,19 +7,21 @@ import android.content.pm.PackageManager import android.os.Build import android.util.Log import androidx.activity.result.ActivityResult +import com.google.android.gms.common.api.ApiException import com.google.android.gms.threadnetwork.IsPreferredCredentialsResult import com.google.android.gms.threadnetwork.ThreadBorderAgent import com.google.android.gms.threadnetwork.ThreadNetwork import com.google.android.gms.threadnetwork.ThreadNetworkCredentials +import com.google.android.gms.threadnetwork.ThreadNetworkStatusCodes import io.homeassistant.companion.android.common.data.HomeAssistantVersion import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.common.data.websocket.impl.entities.ThreadDatasetResponse -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.async import javax.inject.Inject import kotlin.coroutines.resume import kotlin.coroutines.resumeWithException import kotlin.coroutines.suspendCoroutine +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.async class ThreadManagerImpl @Inject constructor( private val serverManager: ServerManager, @@ -49,11 +51,52 @@ class ThreadManagerImpl @Inject constructor( override suspend fun syncPreferredDataset( context: Context, serverId: Int, + exportOnly: Boolean, scope: CoroutineScope ): ThreadManager.SyncResult { if (!appSupportsThread()) return ThreadManager.SyncResult.AppUnsupported if (!coreSupportsThread(serverId)) return ThreadManager.SyncResult.ServerUnsupported + return if (exportOnly) { // Limited sync, only export non-app dataset + exportSyncPreferredDataset(context) + } else { // Full sync + fullSyncPreferredDataset(context, serverId, scope) + } + } + + private suspend fun exportSyncPreferredDataset( + context: Context + ): ThreadManager.SyncResult { + val getDeviceDataset = try { + getPreferredDatasetFromDevice(context) + } catch (e: ApiException) { + Log.e(TAG, "Thread: export cannot be started", e) + if (e.statusCode == ThreadNetworkStatusCodes.LOCAL_NETWORK_NOT_CONNECTED) { + return ThreadManager.SyncResult.NotConnected + } else { + throw e + } + } + + return if (getDeviceDataset == null) { + ThreadManager.SyncResult.NoneHaveCredentials + } else { + val appIsDevicePreferred = appAddedIsPreferredCredentials(context) + Log.d(TAG, "Thread: device ${if (appIsDevicePreferred) "prefers" else "doesn't prefer" } dataset from app") + + return if (appIsDevicePreferred) { + ThreadManager.SyncResult.OnlyOnServer(imported = false) + } else { + ThreadManager.SyncResult.OnlyOnDevice(exportIntent = getDeviceDataset) + } + } + } + + private suspend fun fullSyncPreferredDataset( + context: Context, + serverId: Int, + scope: CoroutineScope + ): ThreadManager.SyncResult { deleteOrphanedThreadCredentials(context, serverId) val getDeviceDataset = scope.async { getPreferredDatasetFromDevice(context) } diff --git a/app/src/main/java/io/homeassistant/companion/android/HomeAssistantApplication.kt b/app/src/main/java/io/homeassistant/companion/android/HomeAssistantApplication.kt index e7963e95882..5e6f894a004 100644 --- a/app/src/main/java/io/homeassistant/companion/android/HomeAssistantApplication.kt +++ b/app/src/main/java/io/homeassistant/companion/android/HomeAssistantApplication.kt @@ -19,6 +19,7 @@ import android.telephony.TelephonyManager import dagger.hilt.android.HiltAndroidApp import io.homeassistant.companion.android.common.data.keychain.KeyChainRepository import io.homeassistant.companion.android.common.data.prefs.PrefsRepository +import io.homeassistant.companion.android.common.sensors.AudioSensorManager import io.homeassistant.companion.android.common.sensors.LastUpdateManager import io.homeassistant.companion.android.database.AppDatabase import io.homeassistant.companion.android.database.settings.SensorUpdateFrequencySetting @@ -30,13 +31,13 @@ import io.homeassistant.companion.android.widgets.button.ButtonWidget import io.homeassistant.companion.android.widgets.entity.EntityWidget import io.homeassistant.companion.android.widgets.mediaplayer.MediaPlayerControlsWidget import io.homeassistant.companion.android.widgets.template.TemplateWidget +import javax.inject.Inject +import javax.inject.Named import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch import java.util.* -import javax.inject.Inject -import javax.inject.Named @HiltAndroidApp open class HomeAssistantApplication : Application() { @@ -164,6 +165,7 @@ open class HomeAssistantApplication : Application() { addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY) addAction(AudioManager.ACTION_HEADSET_PLUG) addAction(AudioManager.RINGER_MODE_CHANGED_ACTION) + addAction(AudioSensorManager.VOLUME_CHANGED_ACTION) } ) diff --git a/app/src/main/java/io/homeassistant/companion/android/assist/AssistActivity.kt b/app/src/main/java/io/homeassistant/companion/android/assist/AssistActivity.kt index 85a5e2e5797..b15f9d952ad 100644 --- a/app/src/main/java/io/homeassistant/companion/android/assist/AssistActivity.kt +++ b/app/src/main/java/io/homeassistant/companion/android/assist/AssistActivity.kt @@ -18,12 +18,12 @@ import androidx.core.content.ContextCompat import androidx.core.content.getSystemService import androidx.core.view.WindowCompat import com.google.accompanist.systemuicontroller.rememberSystemUiController -import com.google.accompanist.themeadapter.material.MdcTheme import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.BaseActivity import io.homeassistant.companion.android.assist.ui.AssistSheetView import io.homeassistant.companion.android.common.assist.AssistViewModelBase import io.homeassistant.companion.android.common.data.servers.ServerManager +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme import io.homeassistant.companion.android.webview.WebViewActivity @AndroidEntryPoint @@ -97,7 +97,7 @@ class AssistActivity : BaseActivity() { WindowCompat.setDecorFitsSystemWindows(window, false) setContent { - MdcTheme { + HomeAssistantAppTheme { val systemUiController = rememberSystemUiController() val useDarkIcons = MaterialTheme.colors.isLight SideEffect { diff --git a/app/src/main/java/io/homeassistant/companion/android/assist/AssistViewModel.kt b/app/src/main/java/io/homeassistant/companion/android/assist/AssistViewModel.kt index 39715e94f08..6918af2a2b7 100644 --- a/app/src/main/java/io/homeassistant/companion/android/assist/AssistViewModel.kt +++ b/app/src/main/java/io/homeassistant/companion/android/assist/AssistViewModel.kt @@ -11,14 +11,14 @@ import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import io.homeassistant.companion.android.assist.ui.AssistMessage import io.homeassistant.companion.android.assist.ui.AssistUiPipeline +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.assist.AssistViewModelBase import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.common.data.websocket.impl.entities.AssistPipelineResponse import io.homeassistant.companion.android.common.util.AudioRecorder import io.homeassistant.companion.android.common.util.AudioUrlPlayer -import kotlinx.coroutines.launch import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR +import kotlinx.coroutines.launch @HiltViewModel class AssistViewModel @Inject constructor( diff --git a/app/src/main/java/io/homeassistant/companion/android/assist/ui/AssistSheetView.kt b/app/src/main/java/io/homeassistant/companion/android/assist/ui/AssistSheetView.kt index 5630261b92b..f2ad7bfd721 100644 --- a/app/src/main/java/io/homeassistant/companion/android/assist/ui/AssistSheetView.kt +++ b/app/src/main/java/io/homeassistant/companion/android/assist/ui/AssistSheetView.kt @@ -79,9 +79,9 @@ import androidx.compose.ui.unit.sp import com.mikepenz.iconics.compose.Image import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial import io.homeassistant.companion.android.R +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.assist.AssistViewModelBase import kotlinx.coroutines.launch -import io.homeassistant.companion.android.common.R as commonR @OptIn(ExperimentalMaterialApi::class) @Composable diff --git a/app/src/main/java/io/homeassistant/companion/android/controls/CameraControl.kt b/app/src/main/java/io/homeassistant/companion/android/controls/CameraControl.kt index aa1569a294e..6e72d95d08b 100644 --- a/app/src/main/java/io/homeassistant/companion/android/controls/CameraControl.kt +++ b/app/src/main/java/io/homeassistant/companion/android/controls/CameraControl.kt @@ -12,17 +12,16 @@ import android.service.controls.templates.ThumbnailTemplate import android.util.Log import androidx.annotation.RequiresApi import io.homeassistant.companion.android.R +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.integration.IntegrationRepository -import io.homeassistant.companion.android.common.data.websocket.impl.entities.AreaRegistryResponse import io.homeassistant.companion.android.common.util.STATE_UNAVAILABLE +import java.net.URL +import java.util.concurrent.TimeUnit import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeoutOrNull -import java.net.URL -import java.util.concurrent.TimeUnit -import io.homeassistant.companion.android.common.R as commonR @RequiresApi(Build.VERSION_CODES.S) object CameraControl : HaControl { @@ -32,11 +31,10 @@ object CameraControl : HaControl { context: Context, control: Control.StatefulBuilder, entity: Entity>, - area: AreaRegistryResponse?, - baseUrl: String? + info: HaControlInfo ): Control.StatefulBuilder { - val image = if (baseUrl != null && (entity.attributes["entity_picture"] as? String)?.isNotBlank() == true) { - getThumbnail(baseUrl + entity.attributes["entity_picture"] as String) + val image = if (info.baseUrl != null && (entity.attributes["entity_picture"] as? String)?.isNotBlank() == true) { + getThumbnail(info.baseUrl + entity.attributes["entity_picture"] as String) } else { null } diff --git a/app/src/main/java/io/homeassistant/companion/android/controls/ClimateControl.kt b/app/src/main/java/io/homeassistant/companion/android/controls/ClimateControl.kt index 3116f75d617..7badd42f3ed 100644 --- a/app/src/main/java/io/homeassistant/companion/android/controls/ClimateControl.kt +++ b/app/src/main/java/io/homeassistant/companion/android/controls/ClimateControl.kt @@ -4,19 +4,22 @@ import android.content.Context import android.os.Build import android.service.controls.Control import android.service.controls.DeviceTypes +import android.service.controls.actions.BooleanAction import android.service.controls.actions.ControlAction import android.service.controls.actions.FloatAction import android.service.controls.actions.ModeAction import android.service.controls.templates.RangeTemplate import android.service.controls.templates.TemperatureControlTemplate +import android.service.controls.templates.ToggleRangeTemplate import androidx.annotation.RequiresApi +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.integration.IntegrationRepository -import io.homeassistant.companion.android.common.data.websocket.impl.entities.AreaRegistryResponse -import io.homeassistant.companion.android.common.R as commonR @RequiresApi(Build.VERSION_CODES.R) object ClimateControl : HaControl { + private data class ClimateState(val currentMode: String, val supportedModes: ArrayList) + private const val SUPPORT_TARGET_TEMPERATURE = 1 private const val SUPPORT_TARGET_TEMPERATURE_RANGE = 2 private val temperatureControlModes = mapOf( @@ -31,13 +34,13 @@ object ClimateControl : HaControl { "heat_cool" to TemperatureControlTemplate.FLAG_MODE_HEAT_COOL, "off" to TemperatureControlTemplate.FLAG_MODE_OFF ) + private val climateStates = HashMap() override fun provideControlFeatures( context: Context, control: Control.StatefulBuilder, entity: Entity>, - area: AreaRegistryResponse?, - baseUrl: String? + info: HaControlInfo ): Control.StatefulBuilder { val minValue = (entity.attributes["min_temp"] as? Number)?.toFloat() ?: 0f val maxValue = (entity.attributes["max_temp"] as? Number)?.toFloat() ?: 100f @@ -60,7 +63,7 @@ object ClimateControl : HaControl { } val temperatureFormatSize = if (temperatureStepSize < 1f) "1" else "0" val rangeTemplate = RangeTemplate( - entity.entityId, + info.systemId, minValue, maxValue, currentValue, @@ -68,14 +71,24 @@ object ClimateControl : HaControl { "%.${temperatureFormatSize}f $temperatureUnit" ) if (entityShouldBePresentedAsThermostat(entity)) { + val state = ClimateState(entity.state, ArrayList()) + val toggleRangeTemplate = ToggleRangeTemplate( + info.systemId + "_range", + // Set checked to true to always show the temperature indicator, regardless of climate mode + true, + context.getString(commonR.string.widget_tap_action_toggle), + rangeTemplate + ) var modesFlag = 0 (entity.attributes["hvac_modes"] as? List)?.forEach { modesFlag = modesFlag or temperatureControlModeFlags[it]!! + state.supportedModes.add(it) } + this.climateStates[info.systemId] = state control.setControlTemplate( TemperatureControlTemplate( - entity.entityId, - rangeTemplate, + info.systemId, + toggleRangeTemplate, temperatureControlModes[entity.state]!!, temperatureControlModes[entity.state]!!, modesFlag @@ -102,13 +115,18 @@ object ClimateControl : HaControl { integrationRepository: IntegrationRepository, action: ControlAction ): Boolean { + val entityStr: String = if (action.templateId.split(".").size > 2) { + action.templateId.split(".", limit = 2)[1] + } else { + action.templateId + } return when (action) { is FloatAction -> { integrationRepository.callService( - action.templateId.split(".")[0], + entityStr.split(".")[0], "set_temperature", hashMapOf( - "entity_id" to action.templateId, + "entity_id" to entityStr, "temperature" to (action as? FloatAction)?.newValue.toString() ) ) @@ -119,7 +137,7 @@ object ClimateControl : HaControl { action.templateId.split(".")[0], "set_hvac_mode", hashMapOf( - "entity_id" to action.templateId, + "entity_id" to entityStr, "hvac_mode" to ( temperatureControlModes.entries.find { it.value == ((action as? ModeAction)?.newMode ?: -1) @@ -129,6 +147,23 @@ object ClimateControl : HaControl { ) true } + is BooleanAction -> { + if (this.climateStates[action.templateId] == null) { + return false + } + val supportedModes = this.climateStates[action.templateId]!!.supportedModes + val currentMode = this.climateStates[action.templateId]!!.currentMode + val nextMode = (supportedModes.indexOf(currentMode) + 1) % supportedModes.count() + integrationRepository.callService( + entityStr.split(".")[0], + "set_hvac_mode", + hashMapOf( + "entity_id" to entityStr, + "hvac_mode" to supportedModes[nextMode] + ) + ) + true + } else -> { false } diff --git a/app/src/main/java/io/homeassistant/companion/android/controls/CoverControl.kt b/app/src/main/java/io/homeassistant/companion/android/controls/CoverControl.kt index 52ea1289bb2..e15906033fb 100644 --- a/app/src/main/java/io/homeassistant/companion/android/controls/CoverControl.kt +++ b/app/src/main/java/io/homeassistant/companion/android/controls/CoverControl.kt @@ -12,12 +12,11 @@ import android.service.controls.templates.RangeTemplate import android.service.controls.templates.ToggleRangeTemplate import android.service.controls.templates.ToggleTemplate import androidx.annotation.RequiresApi +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.integration.IntegrationRepository import io.homeassistant.companion.android.common.data.integration.getCoverPosition import io.homeassistant.companion.android.common.data.integration.isActive -import io.homeassistant.companion.android.common.data.websocket.impl.entities.AreaRegistryResponse -import io.homeassistant.companion.android.common.R as commonR @RequiresApi(Build.VERSION_CODES.R) object CoverControl : HaControl { @@ -26,8 +25,7 @@ object CoverControl : HaControl { context: Context, control: Control.StatefulBuilder, entity: Entity>, - area: AreaRegistryResponse?, - baseUrl: String? + info: HaControlInfo ): Control.StatefulBuilder { val position = entity.getCoverPosition() control.setControlTemplate( diff --git a/app/src/main/java/io/homeassistant/companion/android/controls/DefaultButtonControl.kt b/app/src/main/java/io/homeassistant/companion/android/controls/DefaultButtonControl.kt index e2ee174deb9..d81a16bf232 100644 --- a/app/src/main/java/io/homeassistant/companion/android/controls/DefaultButtonControl.kt +++ b/app/src/main/java/io/homeassistant/companion/android/controls/DefaultButtonControl.kt @@ -7,13 +7,12 @@ import android.service.controls.DeviceTypes import android.service.controls.actions.ControlAction import android.service.controls.templates.StatelessTemplate import androidx.annotation.RequiresApi +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.integration.IntegrationRepository import io.homeassistant.companion.android.common.data.integration.domain -import io.homeassistant.companion.android.common.data.websocket.impl.entities.AreaRegistryResponse import io.homeassistant.companion.android.common.util.capitalize import java.util.Locale -import io.homeassistant.companion.android.common.R as commonR @RequiresApi(Build.VERSION_CODES.R) object DefaultButtonControl : HaControl { @@ -21,8 +20,7 @@ object DefaultButtonControl : HaControl { context: Context, control: Control.StatefulBuilder, entity: Entity>, - area: AreaRegistryResponse?, - baseUrl: String? + info: HaControlInfo ): Control.StatefulBuilder { control.setStatusText("") control.setControlTemplate( diff --git a/app/src/main/java/io/homeassistant/companion/android/controls/DefaultSliderControl.kt b/app/src/main/java/io/homeassistant/companion/android/controls/DefaultSliderControl.kt index f8d859d72ba..bf8e04df030 100644 --- a/app/src/main/java/io/homeassistant/companion/android/controls/DefaultSliderControl.kt +++ b/app/src/main/java/io/homeassistant/companion/android/controls/DefaultSliderControl.kt @@ -8,11 +8,10 @@ import android.service.controls.actions.ControlAction import android.service.controls.actions.FloatAction import android.service.controls.templates.RangeTemplate import androidx.annotation.RequiresApi +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.integration.IntegrationRepository -import io.homeassistant.companion.android.common.data.websocket.impl.entities.AreaRegistryResponse import kotlinx.coroutines.runBlocking -import io.homeassistant.companion.android.common.R as commonR @RequiresApi(Build.VERSION_CODES.R) object DefaultSliderControl : HaControl { @@ -20,8 +19,7 @@ object DefaultSliderControl : HaControl { context: Context, control: Control.StatefulBuilder, entity: Entity>, - area: AreaRegistryResponse?, - baseUrl: String? + info: HaControlInfo ): Control.StatefulBuilder { control.setStatusText("") control.setControlTemplate( diff --git a/app/src/main/java/io/homeassistant/companion/android/controls/DefaultSwitchControl.kt b/app/src/main/java/io/homeassistant/companion/android/controls/DefaultSwitchControl.kt index a4873828c5d..381c1b60e04 100644 --- a/app/src/main/java/io/homeassistant/companion/android/controls/DefaultSwitchControl.kt +++ b/app/src/main/java/io/homeassistant/companion/android/controls/DefaultSwitchControl.kt @@ -9,14 +9,13 @@ import android.service.controls.actions.ControlAction import android.service.controls.templates.ControlButton import android.service.controls.templates.ToggleTemplate import androidx.annotation.RequiresApi +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.integration.IntegrationRepository import io.homeassistant.companion.android.common.data.integration.domain import io.homeassistant.companion.android.common.data.integration.isActive -import io.homeassistant.companion.android.common.data.websocket.impl.entities.AreaRegistryResponse import io.homeassistant.companion.android.common.util.capitalize import java.util.Locale -import io.homeassistant.companion.android.common.R as commonR @RequiresApi(Build.VERSION_CODES.R) object DefaultSwitchControl : HaControl { @@ -24,8 +23,7 @@ object DefaultSwitchControl : HaControl { context: Context, control: Control.StatefulBuilder, entity: Entity>, - area: AreaRegistryResponse?, - baseUrl: String? + info: HaControlInfo ): Control.StatefulBuilder { control.setControlTemplate( ToggleTemplate( diff --git a/app/src/main/java/io/homeassistant/companion/android/controls/FanControl.kt b/app/src/main/java/io/homeassistant/companion/android/controls/FanControl.kt index a7821a972eb..81ea93d70c0 100644 --- a/app/src/main/java/io/homeassistant/companion/android/controls/FanControl.kt +++ b/app/src/main/java/io/homeassistant/companion/android/controls/FanControl.kt @@ -12,13 +12,12 @@ import android.service.controls.templates.RangeTemplate import android.service.controls.templates.ToggleRangeTemplate import android.service.controls.templates.ToggleTemplate import androidx.annotation.RequiresApi +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.integration.IntegrationRepository import io.homeassistant.companion.android.common.data.integration.getFanSpeed import io.homeassistant.companion.android.common.data.integration.isActive import io.homeassistant.companion.android.common.data.integration.supportsFanSetSpeed -import io.homeassistant.companion.android.common.data.websocket.impl.entities.AreaRegistryResponse -import io.homeassistant.companion.android.common.R as commonR @RequiresApi(Build.VERSION_CODES.R) object FanControl : HaControl { @@ -26,8 +25,7 @@ object FanControl : HaControl { context: Context, control: Control.StatefulBuilder, entity: Entity>, - area: AreaRegistryResponse?, - baseUrl: String? + info: HaControlInfo ): Control.StatefulBuilder { if (entity.supportsFanSetSpeed()) { val position = entity.getFanSpeed() diff --git a/app/src/main/java/io/homeassistant/companion/android/controls/HaControl.kt b/app/src/main/java/io/homeassistant/companion/android/controls/HaControl.kt index eee7394a999..41e53730f6d 100644 --- a/app/src/main/java/io/homeassistant/companion/android/controls/HaControl.kt +++ b/app/src/main/java/io/homeassistant/companion/android/controls/HaControl.kt @@ -17,7 +17,6 @@ import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.integration.IntegrationRepository import io.homeassistant.companion.android.common.data.integration.domain import io.homeassistant.companion.android.common.data.integration.friendlyState -import io.homeassistant.companion.android.common.data.websocket.impl.entities.AreaRegistryResponse import io.homeassistant.companion.android.webview.WebViewActivity @RequiresApi(Build.VERSION_CODES.R) @@ -77,15 +76,14 @@ interface HaControl { } } - return provideControlFeatures(context, control, entity, info.area, info.baseUrl).build() + return provideControlFeatures(context, control, entity, info).build() } fun provideControlFeatures( context: Context, control: Control.StatefulBuilder, entity: Entity>, - area: AreaRegistryResponse?, - baseUrl: String? + info: HaControlInfo ): Control.StatefulBuilder fun getDeviceType(entity: Entity>): Int diff --git a/app/src/main/java/io/homeassistant/companion/android/controls/HaControlsPanelActivity.kt b/app/src/main/java/io/homeassistant/companion/android/controls/HaControlsPanelActivity.kt index 339ab343b0a..a0bd550be54 100644 --- a/app/src/main/java/io/homeassistant/companion/android/controls/HaControlsPanelActivity.kt +++ b/app/src/main/java/io/homeassistant/companion/android/controls/HaControlsPanelActivity.kt @@ -23,14 +23,14 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.core.content.getSystemService import androidx.lifecycle.lifecycleScope -import com.google.accompanist.themeadapter.material.MdcTheme import dagger.hilt.android.AndroidEntryPoint +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.prefs.PrefsRepository import io.homeassistant.companion.android.common.data.servers.ServerManager +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme import io.homeassistant.companion.android.webview.WebViewActivity -import kotlinx.coroutines.launch import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR +import kotlinx.coroutines.launch @AndroidEntryPoint class HaControlsPanelActivity : AppCompatActivity() { @@ -90,7 +90,7 @@ class HaControlsPanelActivity : AppCompatActivity() { @Composable fun LockedPanelView() { - MdcTheme { + HomeAssistantAppTheme { Column( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, diff --git a/app/src/main/java/io/homeassistant/companion/android/controls/HaControlsProviderService.kt b/app/src/main/java/io/homeassistant/companion/android/controls/HaControlsProviderService.kt index 9373a660d2c..a3806af2dd5 100644 --- a/app/src/main/java/io/homeassistant/companion/android/controls/HaControlsProviderService.kt +++ b/app/src/main/java/io/homeassistant/companion/android/controls/HaControlsProviderService.kt @@ -17,6 +17,10 @@ import io.homeassistant.companion.android.common.data.websocket.impl.entities.Ar import io.homeassistant.companion.android.common.data.websocket.impl.entities.DeviceRegistryResponse import io.homeassistant.companion.android.common.data.websocket.impl.entities.EntityRegistryResponse import io.homeassistant.companion.android.util.RegistriesDataHandler +import java.util.Calendar +import java.util.concurrent.Flow +import java.util.function.Consumer +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async @@ -28,10 +32,6 @@ import okhttp3.ResponseBody import okhttp3.ResponseBody.Companion.toResponseBody import retrofit2.HttpException import retrofit2.Response -import java.util.Calendar -import java.util.concurrent.Flow -import java.util.function.Consumer -import javax.inject.Inject @RequiresApi(Build.VERSION_CODES.R) @AndroidEntryPoint diff --git a/app/src/main/java/io/homeassistant/companion/android/controls/HaFailedControl.kt b/app/src/main/java/io/homeassistant/companion/android/controls/HaFailedControl.kt index 33b79b81474..e833f02645b 100644 --- a/app/src/main/java/io/homeassistant/companion/android/controls/HaFailedControl.kt +++ b/app/src/main/java/io/homeassistant/companion/android/controls/HaFailedControl.kt @@ -10,7 +10,6 @@ import androidx.annotation.RequiresApi import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.integration.IntegrationRepository import io.homeassistant.companion.android.common.data.integration.domain -import io.homeassistant.companion.android.common.data.websocket.impl.entities.AreaRegistryResponse import io.homeassistant.companion.android.common.util.capitalize import java.util.Locale @@ -20,8 +19,7 @@ object HaFailedControl : HaControl { context: Context, control: Control.StatefulBuilder, entity: Entity>, - area: AreaRegistryResponse?, - baseUrl: String? + info: HaControlInfo ): Control.StatefulBuilder { control.setStatus(if (entity.state == "notfound") Control.STATUS_NOT_FOUND else Control.STATUS_ERROR) control.setStatusText("") diff --git a/app/src/main/java/io/homeassistant/companion/android/controls/LightControl.kt b/app/src/main/java/io/homeassistant/companion/android/controls/LightControl.kt index 9202a5416e6..7162fd45a00 100644 --- a/app/src/main/java/io/homeassistant/companion/android/controls/LightControl.kt +++ b/app/src/main/java/io/homeassistant/companion/android/controls/LightControl.kt @@ -12,13 +12,12 @@ import android.service.controls.templates.RangeTemplate import android.service.controls.templates.ToggleRangeTemplate import android.service.controls.templates.ToggleTemplate import androidx.annotation.RequiresApi +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.integration.IntegrationRepository import io.homeassistant.companion.android.common.data.integration.getLightBrightness import io.homeassistant.companion.android.common.data.integration.isActive import io.homeassistant.companion.android.common.data.integration.supportsLightBrightness -import io.homeassistant.companion.android.common.data.websocket.impl.entities.AreaRegistryResponse -import io.homeassistant.companion.android.common.R as commonR @RequiresApi(Build.VERSION_CODES.R) object LightControl : HaControl { @@ -26,8 +25,7 @@ object LightControl : HaControl { context: Context, control: Control.StatefulBuilder, entity: Entity>, - area: AreaRegistryResponse?, - baseUrl: String? + info: HaControlInfo ): Control.StatefulBuilder { val position = entity.getLightBrightness() control.setControlTemplate( diff --git a/app/src/main/java/io/homeassistant/companion/android/controls/LockControl.kt b/app/src/main/java/io/homeassistant/companion/android/controls/LockControl.kt index c33058ad124..b1319eeed71 100644 --- a/app/src/main/java/io/homeassistant/companion/android/controls/LockControl.kt +++ b/app/src/main/java/io/homeassistant/companion/android/controls/LockControl.kt @@ -9,11 +9,10 @@ import android.service.controls.actions.ControlAction import android.service.controls.templates.ControlButton import android.service.controls.templates.ToggleTemplate import androidx.annotation.RequiresApi +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.integration.IntegrationRepository import io.homeassistant.companion.android.common.data.integration.isActive -import io.homeassistant.companion.android.common.data.websocket.impl.entities.AreaRegistryResponse -import io.homeassistant.companion.android.common.R as commonR @RequiresApi(Build.VERSION_CODES.R) object LockControl : HaControl { @@ -21,8 +20,7 @@ object LockControl : HaControl { context: Context, control: Control.StatefulBuilder, entity: Entity>, - area: AreaRegistryResponse?, - baseUrl: String? + info: HaControlInfo ): Control.StatefulBuilder { control.setControlTemplate( ToggleTemplate( diff --git a/app/src/main/java/io/homeassistant/companion/android/controls/VacuumControl.kt b/app/src/main/java/io/homeassistant/companion/android/controls/VacuumControl.kt index 582bb2a5373..bf29c9a456f 100644 --- a/app/src/main/java/io/homeassistant/companion/android/controls/VacuumControl.kt +++ b/app/src/main/java/io/homeassistant/companion/android/controls/VacuumControl.kt @@ -9,11 +9,10 @@ import android.service.controls.actions.ControlAction import android.service.controls.templates.ControlButton import android.service.controls.templates.ToggleTemplate import androidx.annotation.RequiresApi +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.integration.IntegrationRepository import io.homeassistant.companion.android.common.data.integration.isActive -import io.homeassistant.companion.android.common.data.websocket.impl.entities.AreaRegistryResponse -import io.homeassistant.companion.android.common.R as commonR @RequiresApi(Build.VERSION_CODES.R) object VacuumControl : HaControl { @@ -24,8 +23,7 @@ object VacuumControl : HaControl { context: Context, control: Control.StatefulBuilder, entity: Entity>, - area: AreaRegistryResponse?, - baseUrl: String? + info: HaControlInfo ): Control.StatefulBuilder { entitySupportedFeatures = entity.attributes["supported_features"] as Int control.setControlTemplate( diff --git a/app/src/main/java/io/homeassistant/companion/android/launch/LaunchActivity.kt b/app/src/main/java/io/homeassistant/companion/android/launch/LaunchActivity.kt index 0f2d2f3515e..c7ba57da81f 100644 --- a/app/src/main/java/io/homeassistant/companion/android/launch/LaunchActivity.kt +++ b/app/src/main/java/io/homeassistant/companion/android/launch/LaunchActivity.kt @@ -13,9 +13,9 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material.CircularProgressIndicator import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import com.google.accompanist.themeadapter.material.MdcTheme import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.BuildConfig +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.DeviceRegistration import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.database.sensor.SensorDao @@ -31,16 +31,16 @@ import io.homeassistant.companion.android.sensors.LocationSensorManager import io.homeassistant.companion.android.settings.SettingViewModel import io.homeassistant.companion.android.settings.server.ServerChooserFragment import io.homeassistant.companion.android.util.UrlUtil +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme import io.homeassistant.companion.android.webview.WebViewActivity +import javax.inject.Inject +import javax.net.ssl.SSLException +import javax.net.ssl.SSLHandshakeException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch import retrofit2.HttpException -import javax.inject.Inject -import javax.net.ssl.SSLException -import javax.net.ssl.SSLHandshakeException -import io.homeassistant.companion.android.common.R as commonR @AndroidEntryPoint class LaunchActivity : AppCompatActivity(), LaunchView { @@ -71,7 +71,7 @@ class LaunchActivity : AppCompatActivity(), LaunchView { super.onCreate(savedInstanceState) setContent { Box(modifier = Modifier.fillMaxSize()) { - MdcTheme { + HomeAssistantAppTheme { CircularProgressIndicator( modifier = Modifier.align(Alignment.Center) ) diff --git a/app/src/main/java/io/homeassistant/companion/android/matter/MatterFrontendCommissioningStatus.kt b/app/src/main/java/io/homeassistant/companion/android/matter/MatterFrontendCommissioningStatus.kt deleted file mode 100644 index 8f7699e1c93..00000000000 --- a/app/src/main/java/io/homeassistant/companion/android/matter/MatterFrontendCommissioningStatus.kt +++ /dev/null @@ -1,9 +0,0 @@ -package io.homeassistant.companion.android.matter - -enum class MatterFrontendCommissioningStatus { - NOT_STARTED, - REQUESTED, - THREAD_EXPORT_TO_SERVER, - IN_PROGRESS, - ERROR -} diff --git a/app/src/main/java/io/homeassistant/companion/android/matter/MatterManager.kt b/app/src/main/java/io/homeassistant/companion/android/matter/MatterManager.kt index cc8ae49b366..6eb9d2256f3 100644 --- a/app/src/main/java/io/homeassistant/companion/android/matter/MatterManager.kt +++ b/app/src/main/java/io/homeassistant/companion/android/matter/MatterManager.kt @@ -42,5 +42,5 @@ interface MatterManager { * Send a request to the server to commission an "on network" Matter device * @return [MatterCommissionResponse], or `null` if it wasn't possible to complete the request */ - suspend fun commissionOnNetworkDevice(pin: Long, serverId: Int): MatterCommissionResponse? + suspend fun commissionOnNetworkDevice(pin: Long, ip: String, serverId: Int): MatterCommissionResponse? } diff --git a/app/src/main/java/io/homeassistant/companion/android/nfc/NfcSetupActivity.kt b/app/src/main/java/io/homeassistant/companion/android/nfc/NfcSetupActivity.kt index ba9441bf438..9bacc1dd7f9 100644 --- a/app/src/main/java/io/homeassistant/companion/android/nfc/NfcSetupActivity.kt +++ b/app/src/main/java/io/homeassistant/companion/android/nfc/NfcSetupActivity.kt @@ -11,13 +11,13 @@ import android.widget.Toast import androidx.activity.compose.setContent import androidx.activity.viewModels import androidx.lifecycle.lifecycleScope -import com.google.accompanist.themeadapter.material.MdcTheme import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.BaseActivity +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.nfc.views.LoadNfcView import io.homeassistant.companion.android.util.UrlUtil +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme import kotlinx.coroutines.launch -import io.homeassistant.companion.android.common.R as commonR @AndroidEntryPoint class NfcSetupActivity : BaseActivity() { @@ -62,7 +62,7 @@ class NfcSetupActivity : BaseActivity() { } setContent { - MdcTheme { + HomeAssistantAppTheme { LoadNfcView( viewModel = viewModel, startDestination = if (simpleWrite) NAV_WRITE else NAV_WELCOME, diff --git a/app/src/main/java/io/homeassistant/companion/android/nfc/NfcViewModel.kt b/app/src/main/java/io/homeassistant/companion/android/nfc/NfcViewModel.kt index f4983da5301..80073a4ba29 100644 --- a/app/src/main/java/io/homeassistant/companion/android/nfc/NfcViewModel.kt +++ b/app/src/main/java/io/homeassistant/companion/android/nfc/NfcViewModel.kt @@ -9,16 +9,16 @@ import androidx.compose.runtime.setValue import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.util.Navigator +import java.util.UUID +import javax.inject.Inject import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.launch -import java.util.UUID -import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR @HiltViewModel class NfcViewModel @Inject constructor( diff --git a/app/src/main/java/io/homeassistant/companion/android/nfc/TagReaderActivity.kt b/app/src/main/java/io/homeassistant/companion/android/nfc/TagReaderActivity.kt index 51574023952..6326e0e376c 100644 --- a/app/src/main/java/io/homeassistant/companion/android/nfc/TagReaderActivity.kt +++ b/app/src/main/java/io/homeassistant/companion/android/nfc/TagReaderActivity.kt @@ -8,17 +8,17 @@ import android.util.Log import android.widget.Toast import androidx.activity.compose.setContent import androidx.lifecycle.lifecycleScope -import com.google.accompanist.themeadapter.material.MdcTheme import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.BaseActivity +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.nfc.views.TagReaderView import io.homeassistant.companion.android.util.UrlUtil +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme +import javax.inject.Inject import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.launch -import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR @AndroidEntryPoint class TagReaderActivity : BaseActivity() { @@ -34,7 +34,7 @@ class TagReaderActivity : BaseActivity() { super.onCreate(savedInstanceState) setContent { - MdcTheme { + HomeAssistantAppTheme { TagReaderView() } } diff --git a/app/src/main/java/io/homeassistant/companion/android/nfc/views/NfcNavigationView.kt b/app/src/main/java/io/homeassistant/companion/android/nfc/views/NfcNavigationView.kt index f8bc179b8ff..3a9c06f997e 100644 --- a/app/src/main/java/io/homeassistant/companion/android/nfc/views/NfcNavigationView.kt +++ b/app/src/main/java/io/homeassistant/companion/android/nfc/views/NfcNavigationView.kt @@ -9,8 +9,8 @@ import androidx.compose.material.Scaffold import androidx.compose.material.Text import androidx.compose.material.TopAppBar import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.ArrowBack -import androidx.compose.material.icons.outlined.HelpOutline +import androidx.compose.material.icons.automirrored.outlined.ArrowBack +import androidx.compose.material.icons.automirrored.outlined.HelpOutline import androidx.compose.material.rememberScaffoldState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -23,11 +23,11 @@ import androidx.compose.ui.res.stringResource import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.nfc.NfcSetupActivity import io.homeassistant.companion.android.nfc.NfcViewModel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import io.homeassistant.companion.android.common.R as commonR @Composable fun LoadNfcView( @@ -76,7 +76,7 @@ fun LoadNfcView( } }) { Icon( - imageVector = Icons.Outlined.ArrowBack, + imageVector = Icons.AutoMirrored.Outlined.ArrowBack, contentDescription = stringResource(commonR.string.navigate_up) ) } @@ -87,7 +87,7 @@ fun LoadNfcView( context.startActivity(intent) }) { Icon( - imageVector = Icons.Outlined.HelpOutline, + imageVector = Icons.AutoMirrored.Outlined.HelpOutline, contentDescription = stringResource(commonR.string.get_help), tint = colorResource(commonR.color.colorOnBackground) ) diff --git a/app/src/main/java/io/homeassistant/companion/android/nfc/views/NfcWriteView.kt b/app/src/main/java/io/homeassistant/companion/android/nfc/views/NfcWriteView.kt index 901d4807c63..2197ea426b0 100644 --- a/app/src/main/java/io/homeassistant/companion/android/nfc/views/NfcWriteView.kt +++ b/app/src/main/java/io/homeassistant/companion/android/nfc/views/NfcWriteView.kt @@ -27,8 +27,8 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import com.mikepenz.iconics.compose.Image import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial -import io.homeassistant.companion.android.util.compose.MdcAlertDialog import io.homeassistant.companion.android.common.R as commonR +import io.homeassistant.companion.android.util.compose.MdcAlertDialog @Composable fun NfcWriteView( diff --git a/app/src/main/java/io/homeassistant/companion/android/notifications/MessagingManager.kt b/app/src/main/java/io/homeassistant/companion/android/notifications/MessagingManager.kt index fdca74d9a03..c4f065861ff 100644 --- a/app/src/main/java/io/homeassistant/companion/android/notifications/MessagingManager.kt +++ b/app/src/main/java/io/homeassistant/companion/android/notifications/MessagingManager.kt @@ -45,6 +45,7 @@ import com.fasterxml.jackson.module.kotlin.readValue import dagger.hilt.android.qualifiers.ApplicationContext import io.homeassistant.companion.android.R import io.homeassistant.companion.android.authenticator.Authenticator +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.prefs.PrefsRepository import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.common.notifications.DeviceCommandData @@ -80,6 +81,11 @@ import io.homeassistant.companion.android.util.UrlUtil import io.homeassistant.companion.android.vehicle.HaCarAppService import io.homeassistant.companion.android.websocket.WebsocketManager import io.homeassistant.companion.android.webview.WebViewActivity +import java.io.File +import java.io.FileOutputStream +import java.net.URL +import java.net.URLDecoder +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers @@ -92,12 +98,6 @@ import kotlinx.coroutines.withContext import okhttp3.OkHttpClient import okhttp3.Request import org.json.JSONObject -import java.io.File -import java.io.FileOutputStream -import java.net.URL -import java.net.URLDecoder -import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR class MessagingManager @Inject constructor( @ApplicationContext val context: Context, @@ -728,13 +728,13 @@ class MessagingManager @Inject constructor( PowerManager.ON_AFTER_RELEASE, "HomeAssistant::NotificationScreenOnWakeLock" ) - wakeLock?.acquire(1 * 30 * 1000L /*30 seconds */) + wakeLock?.acquire(1 * 30 * 1000L) // 30 seconds wakeLock?.release() } COMMAND_MEDIA -> { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { if (!NotificationManagerCompat.getEnabledListenerPackages(context) - .contains(context.packageName) + .contains(context.packageName) ) { notifyMissingPermission(message.toString(), serverId) } else { @@ -1878,7 +1878,7 @@ class MessagingManager @Inject constructor( } navigateAppDetails() } - COMMAND_SCREEN_BRIGHTNESS_LEVEL, COMMAND_AUTO_SCREEN_BRIGHTNESS -> { + COMMAND_SCREEN_BRIGHTNESS_LEVEL, COMMAND_AUTO_SCREEN_BRIGHTNESS, COMMAND_SCREEN_OFF_TIMEOUT -> { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { requestWriteSystemPermission() } diff --git a/app/src/main/java/io/homeassistant/companion/android/notifications/NotificationActionReceiver.kt b/app/src/main/java/io/homeassistant/companion/android/notifications/NotificationActionReceiver.kt index 04a1b4a0b9d..6e29b8c6a66 100644 --- a/app/src/main/java/io/homeassistant/companion/android/notifications/NotificationActionReceiver.kt +++ b/app/src/main/java/io/homeassistant/companion/android/notifications/NotificationActionReceiver.kt @@ -10,16 +10,16 @@ import android.widget.Toast import androidx.core.app.NotificationManagerCompat import androidx.core.app.RemoteInput import dagger.hilt.android.AndroidEntryPoint +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.common.util.cancel import io.homeassistant.companion.android.database.notification.NotificationDao import io.homeassistant.companion.android.notifications.MessagingManager.Companion.KEY_TEXT_REPLY +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch -import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR @AndroidEntryPoint class NotificationActionReceiver : BroadcastReceiver() { diff --git a/app/src/main/java/io/homeassistant/companion/android/onboarding/authentication/AuthenticationFragment.kt b/app/src/main/java/io/homeassistant/companion/android/onboarding/authentication/AuthenticationFragment.kt index 5ee2c243aee..0260666c928 100644 --- a/app/src/main/java/io/homeassistant/companion/android/onboarding/authentication/AuthenticationFragment.kt +++ b/app/src/main/java/io/homeassistant/companion/android/onboarding/authentication/AuthenticationFragment.kt @@ -22,9 +22,9 @@ import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.viewinterop.AndroidView import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import com.google.accompanist.themeadapter.material.MdcTheme import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.R +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.HomeAssistantApis import io.homeassistant.companion.android.common.data.authentication.impl.AuthenticationService import io.homeassistant.companion.android.common.data.keychain.KeyChainRepository @@ -32,12 +32,12 @@ import io.homeassistant.companion.android.onboarding.OnboardingViewModel import io.homeassistant.companion.android.onboarding.integration.MobileAppIntegrationFragment import io.homeassistant.companion.android.themes.ThemesManager import io.homeassistant.companion.android.util.TLSWebViewClient +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme import io.homeassistant.companion.android.util.isStarted -import okhttp3.HttpUrl -import okhttp3.HttpUrl.Companion.toHttpUrl import javax.inject.Inject import javax.inject.Named -import io.homeassistant.companion.android.common.R as commonR +import okhttp3.HttpUrl +import okhttp3.HttpUrl.Companion.toHttpUrl @AndroidEntryPoint class AuthenticationFragment : Fragment() { @@ -66,7 +66,7 @@ class AuthenticationFragment : Fragment() { ): View { return ComposeView(requireContext()).apply { setContent { - MdcTheme { + HomeAssistantAppTheme { AndroidView({ WebView(requireContext()).apply { themesManager.setThemeForWebView(requireContext(), settings) diff --git a/app/src/main/java/io/homeassistant/companion/android/onboarding/discovery/DiscoveryFragment.kt b/app/src/main/java/io/homeassistant/companion/android/onboarding/discovery/DiscoveryFragment.kt index d7b6b851180..e02744b30d5 100644 --- a/app/src/main/java/io/homeassistant/companion/android/onboarding/discovery/DiscoveryFragment.kt +++ b/app/src/main/java/io/homeassistant/companion/android/onboarding/discovery/DiscoveryFragment.kt @@ -10,14 +10,14 @@ import androidx.fragment.app.activityViewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle -import com.google.accompanist.themeadapter.material.MdcTheme import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.R import io.homeassistant.companion.android.onboarding.OnboardingViewModel import io.homeassistant.companion.android.onboarding.authentication.AuthenticationFragment import io.homeassistant.companion.android.onboarding.manual.ManualSetupFragment -import kotlinx.coroutines.launch +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme import javax.inject.Inject +import kotlinx.coroutines.launch @AndroidEntryPoint class DiscoveryFragment @Inject constructor() : Fragment() { @@ -42,7 +42,7 @@ class DiscoveryFragment @Inject constructor() : Fragment() { return ComposeView(requireContext()).apply { setContent { - MdcTheme { + HomeAssistantAppTheme { DiscoveryView( onboardingViewModel = viewModel, manualSetupClicked = { navigateToManualSetup() }, diff --git a/app/src/main/java/io/homeassistant/companion/android/onboarding/discovery/DiscoveryView.kt b/app/src/main/java/io/homeassistant/companion/android/onboarding/discovery/DiscoveryView.kt index cc6d82df9a1..2c1d7a94d4f 100644 --- a/app/src/main/java/io/homeassistant/companion/android/onboarding/discovery/DiscoveryView.kt +++ b/app/src/main/java/io/homeassistant/companion/android/onboarding/discovery/DiscoveryView.kt @@ -26,9 +26,9 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial import io.homeassistant.companion.android.R +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.onboarding.OnboardingHeaderView import io.homeassistant.companion.android.onboarding.OnboardingViewModel -import io.homeassistant.companion.android.common.R as commonR @Composable fun DiscoveryView( diff --git a/app/src/main/java/io/homeassistant/companion/android/onboarding/discovery/HomeAssistantSearcher.kt b/app/src/main/java/io/homeassistant/companion/android/onboarding/discovery/HomeAssistantSearcher.kt index ab0ef1d5c55..88c7bbd74fb 100644 --- a/app/src/main/java/io/homeassistant/companion/android/onboarding/discovery/HomeAssistantSearcher.kt +++ b/app/src/main/java/io/homeassistant/companion/android/onboarding/discovery/HomeAssistantSearcher.kt @@ -7,10 +7,10 @@ import android.util.Log import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import io.homeassistant.companion.android.common.data.HomeAssistantVersion -import okio.internal.commonToUtf8String import java.net.MalformedURLException import java.net.URL import java.util.concurrent.locks.ReentrantLock +import okio.internal.commonToUtf8String class HomeAssistantSearcher constructor( private val nsdManager: NsdManager, diff --git a/app/src/main/java/io/homeassistant/companion/android/onboarding/integration/MobileAppIntegrationFragment.kt b/app/src/main/java/io/homeassistant/companion/android/onboarding/integration/MobileAppIntegrationFragment.kt index e0e50d50196..4b604420326 100644 --- a/app/src/main/java/io/homeassistant/companion/android/onboarding/integration/MobileAppIntegrationFragment.kt +++ b/app/src/main/java/io/homeassistant/companion/android/onboarding/integration/MobileAppIntegrationFragment.kt @@ -20,17 +20,17 @@ import androidx.core.content.getSystemService import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.lifecycle.lifecycleScope -import com.google.accompanist.themeadapter.material.MdcTheme import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.R +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.util.DisabledLocationHandler import io.homeassistant.companion.android.onboarding.OnboardingViewModel import io.homeassistant.companion.android.onboarding.notifications.NotificationPermissionFragment import io.homeassistant.companion.android.sensors.LocationSensorManager -import kotlinx.coroutines.launch +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme import java.io.IOException import java.security.KeyStore -import io.homeassistant.companion.android.common.R as commonR +import kotlinx.coroutines.launch @AndroidEntryPoint class MobileAppIntegrationFragment : Fragment() { @@ -52,7 +52,7 @@ class MobileAppIntegrationFragment : Fragment() { ): View { return ComposeView(requireContext()).apply { setContent { - MdcTheme { + HomeAssistantAppTheme { MobileAppIntegrationView( onboardingViewModel = viewModel, openPrivacyPolicy = this@MobileAppIntegrationFragment::openPrivacyPolicy, diff --git a/app/src/main/java/io/homeassistant/companion/android/onboarding/integration/MobileAppIntegrationView.kt b/app/src/main/java/io/homeassistant/companion/android/onboarding/integration/MobileAppIntegrationView.kt index e7d11d13810..88cd678409c 100644 --- a/app/src/main/java/io/homeassistant/companion/android/onboarding/integration/MobileAppIntegrationView.kt +++ b/app/src/main/java/io/homeassistant/companion/android/onboarding/integration/MobileAppIntegrationView.kt @@ -37,9 +37,9 @@ import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.unit.dp import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.onboarding.OnboardingHeaderView import io.homeassistant.companion.android.onboarding.OnboardingViewModel -import io.homeassistant.companion.android.common.R as commonR @OptIn(ExperimentalComposeUiApi::class) @Composable diff --git a/app/src/main/java/io/homeassistant/companion/android/onboarding/manual/ManualSetupFragment.kt b/app/src/main/java/io/homeassistant/companion/android/onboarding/manual/ManualSetupFragment.kt index d5c2b8ba899..850fda04ef4 100644 --- a/app/src/main/java/io/homeassistant/companion/android/onboarding/manual/ManualSetupFragment.kt +++ b/app/src/main/java/io/homeassistant/companion/android/onboarding/manual/ManualSetupFragment.kt @@ -7,11 +7,11 @@ import android.view.ViewGroup import androidx.compose.ui.platform.ComposeView import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import com.google.accompanist.themeadapter.material.MdcTheme import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.R import io.homeassistant.companion.android.onboarding.OnboardingViewModel import io.homeassistant.companion.android.onboarding.authentication.AuthenticationFragment +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme @AndroidEntryPoint class ManualSetupFragment : Fragment() { @@ -25,7 +25,7 @@ class ManualSetupFragment : Fragment() { ): View { return ComposeView(requireContext()).apply { setContent { - MdcTheme { + HomeAssistantAppTheme { ManualSetupView( onboardingViewModel = viewModel, connectedClicked = { connectClicked() } diff --git a/app/src/main/java/io/homeassistant/companion/android/onboarding/manual/ManualSetupView.kt b/app/src/main/java/io/homeassistant/companion/android/onboarding/manual/ManualSetupView.kt index 23e2d414884..663f884e913 100644 --- a/app/src/main/java/io/homeassistant/companion/android/onboarding/manual/ManualSetupView.kt +++ b/app/src/main/java/io/homeassistant/companion/android/onboarding/manual/ManualSetupView.kt @@ -21,9 +21,9 @@ import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.onboarding.OnboardingHeaderView import io.homeassistant.companion.android.onboarding.OnboardingViewModel -import io.homeassistant.companion.android.common.R as commonR @OptIn(ExperimentalComposeUiApi::class) @Composable diff --git a/app/src/main/java/io/homeassistant/companion/android/onboarding/notifications/NotificationPermissionFragment.kt b/app/src/main/java/io/homeassistant/companion/android/onboarding/notifications/NotificationPermissionFragment.kt index 5f66a99283c..a9093cad89e 100644 --- a/app/src/main/java/io/homeassistant/companion/android/onboarding/notifications/NotificationPermissionFragment.kt +++ b/app/src/main/java/io/homeassistant/companion/android/onboarding/notifications/NotificationPermissionFragment.kt @@ -14,9 +14,9 @@ import androidx.compose.ui.platform.ComposeView import androidx.core.app.NotificationManagerCompat import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import com.google.accompanist.themeadapter.material.MdcTheme -import io.homeassistant.companion.android.onboarding.OnboardingViewModel import io.homeassistant.companion.android.common.R as commonR +import io.homeassistant.companion.android.onboarding.OnboardingViewModel +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme class NotificationPermissionFragment : Fragment() { @@ -44,7 +44,7 @@ class NotificationPermissionFragment : Fragment() { ): View { return ComposeView(requireContext()).apply { setContent { - MdcTheme { + HomeAssistantAppTheme { NotificationPermissionView( onSetNotificationsEnabled = ::setNotifications ) diff --git a/app/src/main/java/io/homeassistant/companion/android/onboarding/notifications/NotificationPermissionView.kt b/app/src/main/java/io/homeassistant/companion/android/onboarding/notifications/NotificationPermissionView.kt index bcd4a8a210e..b590218f205 100644 --- a/app/src/main/java/io/homeassistant/companion/android/onboarding/notifications/NotificationPermissionView.kt +++ b/app/src/main/java/io/homeassistant/companion/android/onboarding/notifications/NotificationPermissionView.kt @@ -20,12 +20,12 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import com.google.accompanist.themeadapter.material.MdcTheme import com.mikepenz.iconics.compose.Image import com.mikepenz.iconics.typeface.IIcon import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial -import io.homeassistant.companion.android.onboarding.OnboardingHeaderView import io.homeassistant.companion.android.common.R as commonR +import io.homeassistant.companion.android.onboarding.OnboardingHeaderView +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme @Composable fun NotificationPermissionView( @@ -102,7 +102,7 @@ fun NotificationPermissionBullet( @Preview(showSystemUi = true) @Composable fun NotificationPermissionViewPreview() { - MdcTheme { + HomeAssistantAppTheme { NotificationPermissionView( onSetNotificationsEnabled = {} ) diff --git a/app/src/main/java/io/homeassistant/companion/android/onboarding/welcome/WelcomeFragment.kt b/app/src/main/java/io/homeassistant/companion/android/onboarding/welcome/WelcomeFragment.kt index 290d4c8b785..cfa29ce043f 100644 --- a/app/src/main/java/io/homeassistant/companion/android/onboarding/welcome/WelcomeFragment.kt +++ b/app/src/main/java/io/homeassistant/companion/android/onboarding/welcome/WelcomeFragment.kt @@ -7,10 +7,10 @@ import android.view.View import android.view.ViewGroup import androidx.compose.ui.platform.ComposeView import androidx.fragment.app.Fragment -import com.google.accompanist.themeadapter.material.MdcTheme import io.homeassistant.companion.android.R import io.homeassistant.companion.android.onboarding.discovery.DiscoveryFragment import io.homeassistant.companion.android.onboarding.manual.ManualSetupFragment +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme class WelcomeFragment : Fragment() { @@ -21,7 +21,7 @@ class WelcomeFragment : Fragment() { ): View { return ComposeView(requireContext()).apply { setContent { - MdcTheme { + HomeAssistantAppTheme { WelcomeView( onContinue = { welcomeNavigation() } ) diff --git a/app/src/main/java/io/homeassistant/companion/android/onboarding/welcome/WelcomeView.kt b/app/src/main/java/io/homeassistant/companion/android/onboarding/welcome/WelcomeView.kt index b27076e0e04..62d2d44f7d2 100644 --- a/app/src/main/java/io/homeassistant/companion/android/onboarding/welcome/WelcomeView.kt +++ b/app/src/main/java/io/homeassistant/companion/android/onboarding/welcome/WelcomeView.kt @@ -27,9 +27,9 @@ import androidx.compose.ui.text.withStyle import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.google.accompanist.themeadapter.material.MdcTheme import io.homeassistant.companion.android.R import io.homeassistant.companion.android.common.R as commonR +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme @Composable fun WelcomeView( @@ -103,7 +103,7 @@ fun WelcomeView( @Preview(showSystemUi = true) @Preview(showSystemUi = true, uiMode = UI_MODE_NIGHT_YES) private fun PreviewWelcome() { - MdcTheme { + HomeAssistantAppTheme { WelcomeView(onContinue = {}) } } diff --git a/app/src/main/java/io/homeassistant/companion/android/qs/TileExtensions.kt b/app/src/main/java/io/homeassistant/companion/android/qs/TileExtensions.kt index 658585e1c77..79969d988dd 100755 --- a/app/src/main/java/io/homeassistant/companion/android/qs/TileExtensions.kt +++ b/app/src/main/java/io/homeassistant/companion/android/qs/TileExtensions.kt @@ -21,6 +21,7 @@ import dagger.hilt.InstallIn import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.EntryPointAccessors import dagger.hilt.components.SingletonComponent +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.integration.EntityExt import io.homeassistant.companion.android.common.data.integration.getIcon @@ -35,6 +36,7 @@ import io.homeassistant.companion.android.database.qs.numberedId import io.homeassistant.companion.android.settings.SettingsActivity import io.homeassistant.companion.android.settings.qs.updateActiveTileServices import io.homeassistant.companion.android.util.icondialog.getIconByMdiName +import javax.inject.Inject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.MainScope @@ -42,8 +44,6 @@ import kotlinx.coroutines.cancel import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext -import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR @RequiresApi(Build.VERSION_CODES.N) @AndroidEntryPoint diff --git a/app/src/main/java/io/homeassistant/companion/android/sensors/CarSensorManager.kt b/app/src/main/java/io/homeassistant/companion/android/sensors/CarSensorManager.kt index c64ffa2e6ee..4493b79a121 100644 --- a/app/src/main/java/io/homeassistant/companion/android/sensors/CarSensorManager.kt +++ b/app/src/main/java/io/homeassistant/companion/android/sensors/CarSensorManager.kt @@ -25,78 +25,122 @@ class CarSensorManager : SensorManager, DefaultLifecycleObserver { + data class CarSensor( + val sensor: SensorManager.BasicSensor, + val autoEnabled: Boolean = true, + val automotiveEnabled: Boolean = true, + val autoPermissions: List = emptyList(), + /** + * Permissions can be checked here: + * [PropertyUtils.java](https://github.com/androidx/androidx/blob/androidx-main/car/app/app-automotive/src/main/java/androidx/car/app/hardware/common/PropertyUtils.java) + */ + val automotivePermissions: List = emptyList() + ) + companion object { internal const val TAG = "CarSM" - private val fuelLevel = SensorManager.BasicSensor( - "car_fuel", - "sensor", - R.string.basic_sensor_name_car_fuel, - R.string.sensor_description_car_fuel, - "mdi:barrel", - unitOfMeasurement = "%", - stateClass = SensorManager.STATE_CLASS_MEASUREMENT, - deviceClass = "battery" + private val fuelLevel = CarSensor( + SensorManager.BasicSensor( + "car_fuel", + "sensor", + R.string.basic_sensor_name_car_fuel, + R.string.sensor_description_car_fuel, + "mdi:barrel", + unitOfMeasurement = "%", + stateClass = SensorManager.STATE_CLASS_MEASUREMENT, + deviceClass = "battery" + ), + autoPermissions = listOf("com.google.android.gms.permission.CAR_FUEL"), + automotivePermissions = listOf( + "android.car.permission.CAR_ENERGY", + "android.car.permission.CAR_ENERGY_PORTS", + "android.car.permission.READ_CAR_DISPLAY_UNITS" + ) ) - private val batteryLevel = SensorManager.BasicSensor( - "car_battery", - "sensor", - R.string.basic_sensor_name_car_battery, - R.string.sensor_description_car_battery, - "mdi:car-battery", - unitOfMeasurement = "%", - stateClass = SensorManager.STATE_CLASS_MEASUREMENT, - deviceClass = "battery", - entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC + private val batteryLevel = CarSensor( + SensorManager.BasicSensor( + "car_battery", + "sensor", + R.string.basic_sensor_name_car_battery, + R.string.sensor_description_car_battery, + "mdi:car-battery", + unitOfMeasurement = "%", + stateClass = SensorManager.STATE_CLASS_MEASUREMENT, + deviceClass = "battery", + entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC + ), + autoPermissions = listOf("com.google.android.gms.permission.CAR_FUEL"), + automotivePermissions = listOf( + "android.car.permission.CAR_ENERGY", + "android.car.permission.CAR_ENERGY_PORTS", + "android.car.permission.READ_CAR_DISPLAY_UNITS" + ) ) - private val carName = SensorManager.BasicSensor( - "car_name", - "sensor", - R.string.basic_sensor_name_car_name, - R.string.sensor_description_car_name, - "mdi:car-info" + private val carName = CarSensor( + SensorManager.BasicSensor( + "car_name", + "sensor", + R.string.basic_sensor_name_car_name, + R.string.sensor_description_car_name, + "mdi:car-info" + ), + automotivePermissions = listOf("android.car.permission.CAR_INFO") ) - private val carStatus = SensorManager.BasicSensor( - "car_charging_status", - "sensor", - R.string.basic_sensor_name_car_charging_status, - R.string.sensor_description_car_charging_status, - "mdi:ev-station", - deviceClass = "plug" + private val carChargingStatus = CarSensor( + SensorManager.BasicSensor( + "car_charging_status", + "sensor", + R.string.basic_sensor_name_car_charging_status, + R.string.sensor_description_car_charging_status, + "mdi:ev-station", + deviceClass = "plug" + ), + automotivePermissions = listOf("android.car.permission.CAR_ENERGY_PORTS") ) - private val odometerValue = SensorManager.BasicSensor( - "car_odometer", - "sensor", - R.string.basic_sensor_name_car_odometer, - R.string.sensor_description_car_odometer, - "mdi:map-marker-distance", - unitOfMeasurement = "m", - stateClass = SensorManager.STATE_CLASS_TOTAL_INCREASING, - deviceClass = "distance" + private val odometerValue = CarSensor( + SensorManager.BasicSensor( + "car_odometer", + "sensor", + R.string.basic_sensor_name_car_odometer, + R.string.sensor_description_car_odometer, + "mdi:map-marker-distance", + unitOfMeasurement = "m", + stateClass = SensorManager.STATE_CLASS_TOTAL_INCREASING, + deviceClass = "distance" + ), + automotiveEnabled = false, + autoPermissions = listOf("com.google.android.gms.permission.CAR_MILEAGE") ) - - private val fuelType = SensorManager.BasicSensor( - "car_fuel_type", - "sensor", - R.string.basic_sensor_name_car_fuel_type, - R.string.sensor_description_car_fuel_type, - "mdi:gas-station", - entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC + private val fuelType = CarSensor( + SensorManager.BasicSensor( + "car_fuel_type", + "sensor", + R.string.basic_sensor_name_car_fuel_type, + R.string.sensor_description_car_fuel_type, + "mdi:gas-station", + entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC + ), + autoPermissions = listOf("com.google.android.gms.permission.CAR_FUEL"), + automotivePermissions = listOf("android.car.permission.CAR_INFO") ) - - private val evConnector = SensorManager.BasicSensor( - "car_ev_connector", - "sensor", - R.string.basic_sensor_name_car_ev_connector_type, - R.string.sensor_description_car_ev_connector_type, - "mdi:car-electric", - entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC + private val evConnector = CarSensor( + SensorManager.BasicSensor( + "car_ev_connector", + "sensor", + R.string.basic_sensor_name_car_ev_connector_type, + R.string.sensor_description_car_ev_connector_type, + "mdi:car-electric", + entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC + ), + autoPermissions = listOf("com.google.android.gms.permission.CAR_FUEL"), + automotivePermissions = listOf("android.car.permission.CAR_INFO") ) - private val sensorsList = listOf( + private val allSensorsList = listOf( batteryLevel, carName, - carStatus, + carChargingStatus, evConnector, fuelLevel, fuelType, @@ -104,13 +148,17 @@ class CarSensorManager : ) private enum class Listener { - ENERGY, MODEL, MILEAGE, STATUS, PROFILE + ENERGY, + MODEL, + MILEAGE, + STATUS, + PROFILE } private val listenerSensors = mapOf( Listener.ENERGY to listOf(batteryLevel, fuelLevel), Listener.MODEL to listOf(carName), - Listener.STATUS to listOf(carStatus), + Listener.STATUS to listOf(carChargingStatus), Listener.MILEAGE to listOf(odometerValue), Listener.PROFILE to listOf(evConnector, fuelType) ) @@ -123,10 +171,23 @@ class CarSensorManager : ) } + private lateinit var latestContext: Context + + private val isAutomotive get() = latestContext.packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) + + private val carSensorsList get() = allSensorsList.filter { (isAutomotive && it.automotiveEnabled) || (!isAutomotive && it.autoEnabled) } + private val sensorsList get() = carSensorsList.map { it.sensor } + + private fun allDisabled(): Boolean = sensorsList.none { isEnabled(latestContext, it) } + + private fun connected(): Boolean = HaCarAppService.carInfo != null + override val name: Int get() = R.string.sensor_name_car override suspend fun getAvailableSensors(context: Context): List { + this.latestContext = context.applicationContext + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { sensorsList } else { @@ -135,33 +196,39 @@ class CarSensorManager : } override fun hasSensor(context: Context): Boolean { - // TODO: show sensors for automotive (except odometer) once - // we can ask for special automotive permissions in requiredPermissions - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && - !context.packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) && - BuildConfig.FLAVOR == "full" + this.latestContext = context.applicationContext + + return if (isAutomotive) { + BuildConfig.FLAVOR == "minimal" + } else { + Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && + BuildConfig.FLAVOR == "full" + } } override fun requiredPermissions(sensorId: String): Array { - return when { - (sensorId == fuelLevel.id || sensorId == batteryLevel.id || sensorId == fuelType.id || sensorId == evConnector.id) -> { - arrayOf("com.google.android.gms.permission.CAR_FUEL") - } - sensorId == odometerValue.id -> { - arrayOf("com.google.android.gms.permission.CAR_MILEAGE") + return carSensorsList.firstOrNull { it.sensor.id == sensorId }?.let { + if (isAutomotive) { + it.automotivePermissions.toTypedArray() + } else { + it.autoPermissions.toTypedArray() } - else -> emptyArray() - } + } ?: emptyArray() } - private lateinit var context: Context + fun isEnabled(context: Context, carSensor: CarSensor): Boolean { + this.latestContext = context.applicationContext - private fun allDisabled(): Boolean = sensorsList.none { isEnabled(context, it) } + if ((isAutomotive && !carSensor.automotiveEnabled) || (!isAutomotive && !carSensor.autoEnabled)) { + return false + } - private fun connected(): Boolean = HaCarAppService.carInfo != null + return super.isEnabled(context, carSensor.sensor) + } override fun requestSensorUpdate(context: Context) { - this.context = context.applicationContext + this.latestContext = context.applicationContext + if (allDisabled()) { return } @@ -170,13 +237,13 @@ class CarSensorManager : if (connected()) { updateCarInfo() } else { - sensorsList.forEach { + carSensorsList.forEach { if (isEnabled(context, it)) { onSensorUpdated( context, - it, + it.sensor, STATE_UNAVAILABLE, - it.statelessIcon, + it.sensor.statelessIcon, mapOf() ) } @@ -195,7 +262,7 @@ class CarSensorManager : Log.d(TAG, "unregistering CarInfo $l listener") } - val executor = ContextCompat.getMainExecutor(context) + val executor = ContextCompat.getMainExecutor(latestContext) when (l) { Listener.ENERGY -> { if (enable) { @@ -239,7 +306,7 @@ class CarSensorManager : private fun updateCarInfo() { listenerSensors.forEach { (listener, sensors) -> - if (sensors.any { isEnabled(context, it) }) { + if (sensors.any { isEnabled(latestContext, it) }) { if (listenerLastRegistered[listener] != -1L && listenerLastRegistered[listener]!! + SensorManager.SENSOR_LISTENER_TIMEOUT < System.currentTimeMillis()) { Log.d(TAG, "Re-registering CarInfo $listener listener as it appears to be stuck") setListener(listener, false) @@ -255,12 +322,12 @@ class CarSensorManager : private fun onEnergyAvailable(data: EnergyLevel) { val fuelStatus = carValueStatus(data.fuelPercent.status) Log.d(TAG, "Received Energy level: $data") - if (isEnabled(context, fuelLevel)) { + if (isEnabled(latestContext, fuelLevel)) { onSensorUpdated( - context, - fuelLevel, + latestContext, + fuelLevel.sensor, if (fuelStatus == "success") data.fuelPercent.value!! else STATE_UNKNOWN, - fuelLevel.statelessIcon, + fuelLevel.sensor.statelessIcon, mapOf( "status" to fuelStatus ), @@ -268,12 +335,12 @@ class CarSensorManager : ) } val batteryStatus = carValueStatus(data.batteryPercent.status) - if (isEnabled(context, batteryLevel)) { + if (isEnabled(latestContext, batteryLevel)) { onSensorUpdated( - context, - batteryLevel, + latestContext, + batteryLevel.sensor, if (batteryStatus == "success") data.batteryPercent.value!! else STATE_UNKNOWN, - batteryLevel.statelessIcon, + batteryLevel.sensor.statelessIcon, mapOf( "status" to batteryStatus ), @@ -286,12 +353,12 @@ class CarSensorManager : private fun onModelAvailable(data: Model) { val status = carValueStatus(data.name.status) Log.d(TAG, "Received model information: $data") - if (isEnabled(context, carName)) { + if (isEnabled(latestContext, carName)) { onSensorUpdated( - context, - carName, + latestContext, + carName.sensor, if (status == "success") data.name.value!! else STATE_UNKNOWN, - carName.statelessIcon, + carName.sensor.statelessIcon, mapOf( "car_manufacturer" to data.manufacturer.value, "car_manufactured_year" to data.year.value, @@ -307,12 +374,12 @@ class CarSensorManager : fun onStatusAvailable(data: EvStatus) { val status = carValueStatus(data.evChargePortConnected.status) Log.d(TAG, "Received status available: $data") - if (isEnabled(context, carStatus)) { + if (isEnabled(latestContext, carChargingStatus)) { onSensorUpdated( - context, - carStatus, + latestContext, + carChargingStatus.sensor, if (status == "success") (data.evChargePortConnected.value == true) else STATE_UNKNOWN, - carStatus.statelessIcon, + carChargingStatus.sensor.statelessIcon, mapOf( "car_charge_port_open" to (data.evChargePortOpen.value == true), "status" to status @@ -327,12 +394,12 @@ class CarSensorManager : fun onMileageAvailable(data: Mileage) { val status = carValueStatus(data.odometerMeters.status) Log.d(TAG, "Received mileage: $data") - if (isEnabled(context, odometerValue)) { + if (isEnabled(latestContext, odometerValue)) { onSensorUpdated( - context, - odometerValue, + latestContext, + odometerValue.sensor, if (status == "success") data.odometerMeters.value!! else STATE_UNKNOWN, - odometerValue.statelessIcon, + odometerValue.sensor.statelessIcon, mapOf( "status" to status ), @@ -346,24 +413,24 @@ class CarSensorManager : val fuelTypeStatus = carValueStatus(data.fuelTypes.status) val evConnectorTypeStatus = carValueStatus(data.evConnectorTypes.status) Log.d(TAG, "Received energy profile: $data") - if (isEnabled(context, fuelType)) { + if (isEnabled(latestContext, fuelType)) { onSensorUpdated( - context, - fuelType, + latestContext, + fuelType.sensor, if (fuelTypeStatus == "success") getFuelType(data.fuelTypes.value!!) else STATE_UNKNOWN, - fuelType.statelessIcon, + fuelType.sensor.statelessIcon, mapOf( "status" to fuelTypeStatus ), forceUpdate = true ) } - if (isEnabled(context, evConnector)) { + if (isEnabled(latestContext, evConnector)) { onSensorUpdated( - context, - evConnector, + latestContext, + evConnector.sensor, if (evConnectorTypeStatus == "success") getEvConnectorType(data.evConnectorTypes.value!!) else STATE_UNKNOWN, - evConnector.statelessIcon, + evConnector.sensor.statelessIcon, mapOf( "status" to evConnectorTypeStatus ), diff --git a/app/src/main/java/io/homeassistant/companion/android/sensors/DynamicColorSensorManager.kt b/app/src/main/java/io/homeassistant/companion/android/sensors/DynamicColorSensorManager.kt index 9c63da70d3e..fc541211bbd 100755 --- a/app/src/main/java/io/homeassistant/companion/android/sensors/DynamicColorSensorManager.kt +++ b/app/src/main/java/io/homeassistant/companion/android/sensors/DynamicColorSensorManager.kt @@ -5,8 +5,8 @@ import androidx.core.graphics.blue import androidx.core.graphics.green import androidx.core.graphics.red import com.google.android.material.color.DynamicColors -import io.homeassistant.companion.android.common.sensors.SensorManager import io.homeassistant.companion.android.common.R as commonR +import io.homeassistant.companion.android.common.sensors.SensorManager class DynamicColorSensorManager : SensorManager { companion object { @@ -51,7 +51,7 @@ class DynamicColorSensorManager : SensorManager { val dynamicColorContext = DynamicColors.wrapContextIfAvailable(context) val attrsToResolve = intArrayOf( - android.R.attr.colorAccent // 0 + android.R.attr.colorAccent ) val test = dynamicColorContext.obtainStyledAttributes(attrsToResolve) val accent = test.getColor(0, 0) diff --git a/app/src/main/java/io/homeassistant/companion/android/sensors/LastAppSensorManager.kt b/app/src/main/java/io/homeassistant/companion/android/sensors/LastAppSensorManager.kt index 25dc60af22b..5c628a305c2 100755 --- a/app/src/main/java/io/homeassistant/companion/android/sensors/LastAppSensorManager.kt +++ b/app/src/main/java/io/homeassistant/companion/android/sensors/LastAppSensorManager.kt @@ -8,9 +8,9 @@ import android.os.Build import android.util.Log import androidx.annotation.RequiresApi import androidx.core.content.getSystemService +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.sensors.SensorManager import io.homeassistant.companion.android.common.util.STATE_UNKNOWN -import io.homeassistant.companion.android.common.R as commonR class LastAppSensorManager : SensorManager { companion object { diff --git a/app/src/main/java/io/homeassistant/companion/android/sensors/NotificationSensorManager.kt b/app/src/main/java/io/homeassistant/companion/android/sensors/NotificationSensorManager.kt index 90be7da956a..a3029863809 100644 --- a/app/src/main/java/io/homeassistant/companion/android/sensors/NotificationSensorManager.kt +++ b/app/src/main/java/io/homeassistant/companion/android/sensors/NotificationSensorManager.kt @@ -16,11 +16,11 @@ import android.service.notification.StatusBarNotification import android.util.Log import androidx.core.app.NotificationManagerCompat import androidx.core.content.getSystemService +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.sensors.SensorManager import io.homeassistant.companion.android.common.util.STATE_UNAVAILABLE import io.homeassistant.companion.android.common.util.STATE_UNKNOWN import io.homeassistant.companion.android.database.sensor.SensorSettingType -import io.homeassistant.companion.android.common.R as commonR class NotificationSensorManager : NotificationListenerService(), SensorManager { companion object { diff --git a/app/src/main/java/io/homeassistant/companion/android/sensors/QuestSensorManager.kt b/app/src/main/java/io/homeassistant/companion/android/sensors/QuestSensorManager.kt index 11312f047d3..831a457763a 100644 --- a/app/src/main/java/io/homeassistant/companion/android/sensors/QuestSensorManager.kt +++ b/app/src/main/java/io/homeassistant/companion/android/sensors/QuestSensorManager.kt @@ -4,8 +4,8 @@ import android.content.Context import android.content.Intent import android.content.IntentFilter import android.os.Build -import io.homeassistant.companion.android.common.sensors.SensorManager import io.homeassistant.companion.android.common.R as commonR +import io.homeassistant.companion.android.common.sensors.SensorManager class QuestSensorManager : SensorManager { companion object { diff --git a/app/src/main/java/io/homeassistant/companion/android/sensors/SensorReceiver.kt b/app/src/main/java/io/homeassistant/companion/android/sensors/SensorReceiver.kt index 8bce08fdeb2..038c952204a 100644 --- a/app/src/main/java/io/homeassistant/companion/android/sensors/SensorReceiver.kt +++ b/app/src/main/java/io/homeassistant/companion/android/sensors/SensorReceiver.kt @@ -104,24 +104,34 @@ class SensorReceiver : SensorReceiverBase() { // Suppress Lint because we only register for the receiver if the android version matches the intent @SuppressLint("InlinedApi") override val skippableActions = mapOf( - "android.app.action.NEXT_ALARM_CLOCK_CHANGED" to NextAlarmManager.nextAlarm.id, - "android.bluetooth.device.action.ACL_CONNECTED" to BluetoothSensorManager.bluetoothConnection.id, - "android.bluetooth.device.action.ACL_DISCONNECTED" to BluetoothSensorManager.bluetoothConnection.id, - "com.oculus.intent.action.MOUNT_STATE_CHANGED" to QuestSensorManager.headsetMounted.id, - "android.net.wifi.WIFI_AP_STATE_CHANGED" to NetworkSensorManager.hotspotState.id, - BluetoothAdapter.ACTION_STATE_CHANGED to BluetoothSensorManager.bluetoothState.id, - Intent.ACTION_SCREEN_OFF to PowerSensorManager.interactiveDevice.id, - Intent.ACTION_SCREEN_ON to PowerSensorManager.interactiveDevice.id, - PowerManager.ACTION_POWER_SAVE_MODE_CHANGED to PowerSensorManager.powerSave.id, - PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED to PowerSensorManager.doze.id, - NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED to DNDSensorManager.dndSensor.id, - AudioManager.ACTION_MICROPHONE_MUTE_CHANGED to AudioSensorManager.micMuted.id, - AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED to AudioSensorManager.speakerphoneState.id, - AudioManager.RINGER_MODE_CHANGED_ACTION to AudioSensorManager.audioSensor.id, - Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE to DevicePolicyManager.isWorkProfile.id, - Intent.ACTION_MANAGED_PROFILE_AVAILABLE to DevicePolicyManager.isWorkProfile.id, - WifiManager.WIFI_STATE_CHANGED_ACTION to NetworkSensorManager.wifiState.id, - NfcAdapter.ACTION_ADAPTER_STATE_CHANGED to NfcSensorManager.nfcStateSensor.id + "android.app.action.NEXT_ALARM_CLOCK_CHANGED" to listOf(NextAlarmManager.nextAlarm.id), + "android.bluetooth.device.action.ACL_CONNECTED" to listOf(BluetoothSensorManager.bluetoothConnection.id), + "android.bluetooth.device.action.ACL_DISCONNECTED" to listOf(BluetoothSensorManager.bluetoothConnection.id), + "com.oculus.intent.action.MOUNT_STATE_CHANGED" to listOf(QuestSensorManager.headsetMounted.id), + "android.net.wifi.WIFI_AP_STATE_CHANGED" to listOf(NetworkSensorManager.hotspotState.id), + BluetoothAdapter.ACTION_STATE_CHANGED to listOf(BluetoothSensorManager.bluetoothState.id), + Intent.ACTION_SCREEN_OFF to listOf(PowerSensorManager.interactiveDevice.id), + Intent.ACTION_SCREEN_ON to listOf(PowerSensorManager.interactiveDevice.id), + PowerManager.ACTION_POWER_SAVE_MODE_CHANGED to listOf(PowerSensorManager.powerSave.id), + PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED to listOf(PowerSensorManager.doze.id), + NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED to listOf(DNDSensorManager.dndSensor.id), + AudioManager.ACTION_MICROPHONE_MUTE_CHANGED to listOf(AudioSensorManager.micMuted.id), + AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED to listOf(AudioSensorManager.speakerphoneState.id), + AudioManager.RINGER_MODE_CHANGED_ACTION to listOf(AudioSensorManager.audioSensor.id), + AudioSensorManager.VOLUME_CHANGED_ACTION to listOf( + AudioSensorManager.volAccessibility.id, + AudioSensorManager.volAlarm.id, + AudioSensorManager.volCall.id, + AudioSensorManager.volDTMF.id, + AudioSensorManager.volNotification.id, + AudioSensorManager.volMusic.id, + AudioSensorManager.volRing.id, + AudioSensorManager.volSystem.id + ), + Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE to listOf(DevicePolicyManager.isWorkProfile.id), + Intent.ACTION_MANAGED_PROFILE_AVAILABLE to listOf(DevicePolicyManager.isWorkProfile.id), + WifiManager.WIFI_STATE_CHANGED_ACTION to listOf(NetworkSensorManager.wifiState.id), + NfcAdapter.ACTION_ADAPTER_STATE_CHANGED to listOf(NfcSensorManager.nfcStateSensor.id) ) override fun getSensorSettingsIntent( diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/SettingViewModel.kt b/app/src/main/java/io/homeassistant/companion/android/settings/SettingViewModel.kt index 41d1eb2a6cf..bdaf70e85f0 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/SettingViewModel.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/SettingViewModel.kt @@ -9,8 +9,8 @@ import io.homeassistant.companion.android.database.settings.Setting import io.homeassistant.companion.android.database.settings.SettingsDao import io.homeassistant.companion.android.database.settings.WebsocketSetting import io.homeassistant.companion.android.websocket.WebsocketManager -import kotlinx.coroutines.flow.Flow import javax.inject.Inject +import kotlinx.coroutines.flow.Flow @HiltViewModel class SettingViewModel @Inject constructor( diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/SettingsActivity.kt b/app/src/main/java/io/homeassistant/companion/android/settings/SettingsActivity.kt index 7efd051d4d9..9e8567b14e7 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/SettingsActivity.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/SettingsActivity.kt @@ -14,19 +14,18 @@ import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.EntryPointAccessors import dagger.hilt.android.components.ActivityComponent import eightbitlab.com.blurview.BlurView -import eightbitlab.com.blurview.RenderScriptBlur import io.homeassistant.companion.android.BaseActivity import io.homeassistant.companion.android.R import io.homeassistant.companion.android.authenticator.Authenticator +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.settings.notification.NotificationHistoryFragment import io.homeassistant.companion.android.settings.qs.ManageTilesFragment import io.homeassistant.companion.android.settings.sensor.SensorDetailFragment import io.homeassistant.companion.android.settings.server.ServerSettingsFragment import io.homeassistant.companion.android.settings.websocket.WebsocketSettingFragment -import kotlinx.coroutines.runBlocking import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR +import kotlinx.coroutines.runBlocking @AndroidEntryPoint class SettingsActivity : BaseActivity() { @@ -60,10 +59,7 @@ class SettingsActivity : BaseActivity() { blurView = findViewById(R.id.blurView) blurView.setupWith(window.decorView.rootView as ViewGroup) - .setBlurAlgorithm(RenderScriptBlur(this)) - .setBlurAutoUpdate(true) .setBlurRadius(8f) - .setHasFixedTransformationMatrix(false) .setBlurEnabled(false) authenticator = Authenticator(this, this, ::settingsActivityAuthenticationResult) diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/SettingsFragment.kt b/app/src/main/java/io/homeassistant/companion/android/settings/SettingsFragment.kt index 3324cd594be..f230403f413 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/SettingsFragment.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/SettingsFragment.kt @@ -35,6 +35,7 @@ import com.google.android.material.snackbar.Snackbar import io.homeassistant.companion.android.BuildConfig import io.homeassistant.companion.android.R import io.homeassistant.companion.android.authenticator.Authenticator +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.database.server.Server import io.homeassistant.companion.android.nfc.NfcSetupActivity import io.homeassistant.companion.android.onboarding.OnboardApp @@ -53,15 +54,14 @@ import io.homeassistant.companion.android.settings.wear.SettingsWearActivity import io.homeassistant.companion.android.settings.wear.SettingsWearDetection import io.homeassistant.companion.android.settings.widgets.ManageWidgetsSettingsFragment import io.homeassistant.companion.android.webview.WebViewActivity -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock import java.time.Instant import java.time.ZoneId import java.time.format.DateTimeFormatter import java.time.format.FormatStyle -import io.homeassistant.companion.android.common.R as commonR +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock class SettingsFragment( private val presenter: SettingsPresenter, @@ -145,7 +145,8 @@ class SettingsFragment( it.setOnPreferenceClickListener { requestOnboardingResult.launch( OnboardApp.Input( - url = "", // Empty url skips the 'Welcome' screen + // Empty url skips the 'Welcome' screen + url = "", discoveryOptions = OnboardApp.DiscoveryOptions.HIDE_EXISTING ) ) diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/SettingsPresenterImpl.kt b/app/src/main/java/io/homeassistant/companion/android/settings/SettingsPresenterImpl.kt index 08a2dc8cccc..26c9e81fff8 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/SettingsPresenterImpl.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/SettingsPresenterImpl.kt @@ -10,6 +10,7 @@ import androidx.core.content.getSystemService import androidx.preference.PreferenceDataStore import io.homeassistant.companion.android.BuildConfig import io.homeassistant.companion.android.R +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.DeviceRegistration import io.homeassistant.companion.android.common.data.integration.impl.entities.RateLimitResponse import io.homeassistant.companion.android.common.data.prefs.PrefsRepository @@ -31,6 +32,7 @@ import io.homeassistant.companion.android.settings.language.LanguagesManager import io.homeassistant.companion.android.themes.ThemesManager import io.homeassistant.companion.android.util.ChangeLog import io.homeassistant.companion.android.util.UrlUtil +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -40,8 +42,6 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext -import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR class SettingsPresenterImpl @Inject constructor( private val serverManager: ServerManager, diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/controls/ManageControlsSettingsFragment.kt b/app/src/main/java/io/homeassistant/companion/android/settings/controls/ManageControlsSettingsFragment.kt index 829759267ee..badda352eb5 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/controls/ManageControlsSettingsFragment.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/controls/ManageControlsSettingsFragment.kt @@ -9,14 +9,14 @@ import androidx.annotation.RequiresApi import androidx.compose.ui.platform.ComposeView import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels -import com.google.accompanist.themeadapter.material.MdcTheme import dagger.hilt.android.AndroidEntryPoint +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.ControlsAuthRequiredSetting import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.settings.addHelpMenuProvider import io.homeassistant.companion.android.settings.controls.views.ManageControlsView +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR @RequiresApi(Build.VERSION_CODES.TIRAMISU) @AndroidEntryPoint @@ -34,7 +34,7 @@ class ManageControlsSettingsFragment : Fragment() { ): View { return ComposeView(requireContext()).apply { setContent { - MdcTheme { + HomeAssistantAppTheme { ManageControlsView( panelEnabled = viewModel.panelEnabled, authSetting = viewModel.authRequired, diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/controls/ManageControlsViewModel.kt b/app/src/main/java/io/homeassistant/companion/android/settings/controls/ManageControlsViewModel.kt index 687ef4ec5d1..8aa5998f74c 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/controls/ManageControlsViewModel.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/controls/ManageControlsViewModel.kt @@ -20,10 +20,10 @@ import io.homeassistant.companion.android.common.data.prefs.PrefsRepository import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.controls.HaControlsPanelActivity import io.homeassistant.companion.android.controls.HaControlsProviderService +import javax.inject.Inject import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.launch -import javax.inject.Inject @RequiresApi(Build.VERSION_CODES.TIRAMISU) @HiltViewModel @@ -54,8 +54,8 @@ class ManageControlsViewModel @Inject constructor( if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { panelEnabled = application.packageManager.getComponentEnabledSetting( - ComponentName(application, HaControlsPanelActivity::class.java) - ) == PackageManager.COMPONENT_ENABLED_STATE_ENABLED + ComponentName(application, HaControlsPanelActivity::class.java) + ) == PackageManager.COMPONENT_ENABLED_STATE_ENABLED val panelServer = prefsRepository.getControlsPanelServer() val panelPath = prefsRepository.getControlsPanelPath() diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/controls/views/ManageControlsView.kt b/app/src/main/java/io/homeassistant/companion/android/settings/controls/views/ManageControlsView.kt index d7d469ce07b..26b77d85145 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/controls/views/ManageControlsView.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/controls/views/ManageControlsView.kt @@ -51,6 +51,7 @@ import androidx.compose.ui.unit.sp import com.mikepenz.iconics.compose.Image import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial import io.homeassistant.companion.android.common.R +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.ControlsAuthRequiredSetting import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.integration.domain @@ -59,7 +60,6 @@ import io.homeassistant.companion.android.database.server.Server import io.homeassistant.companion.android.util.compose.HaAlertWarning import io.homeassistant.companion.android.util.compose.ServerExposedDropdownMenu import io.homeassistant.companion.android.util.compose.getEntityDomainString -import io.homeassistant.companion.android.common.R as commonR @Composable fun ManageControlsView( @@ -275,7 +275,8 @@ fun ManageControlsEntity( Checkbox( checked = selected, modifier = Modifier.padding(end = 16.dp), - onCheckedChange = null // Handled by parent Row clickable modifier + // Handled by parent Row clickable modifier + onCheckedChange = null ) Column( modifier = Modifier.weight(1f) @@ -324,14 +325,16 @@ fun ManageControlsModeButton( modifier = Modifier.fillMaxWidth() ) Text( - text = "${stringResource(if (isPanel) commonR.string.controls_setting_mode_panel_info else commonR.string.controls_setting_mode_builtin_info)}\n", // Newline for spacing + // Add newline at the end for spacing + text = "${stringResource(if (isPanel) commonR.string.controls_setting_mode_panel_info else commonR.string.controls_setting_mode_builtin_info)}\n", fontSize = 14.sp, textAlign = TextAlign.Center, modifier = Modifier.fillMaxWidth() ) RadioButton( selected = selected, - onClick = null // Handled by parent + // Handled by parent + onClick = null ) } } diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/developer/DeveloperSettingsFragment.kt b/app/src/main/java/io/homeassistant/companion/android/settings/developer/DeveloperSettingsFragment.kt index 35d279b9616..c582856da6b 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/developer/DeveloperSettingsFragment.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/developer/DeveloperSettingsFragment.kt @@ -11,12 +11,12 @@ import androidx.preference.PreferenceFragmentCompat import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.BuildConfig import io.homeassistant.companion.android.R +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.settings.developer.location.LocationTrackingFragment import io.homeassistant.companion.android.settings.log.LogFragment import io.homeassistant.companion.android.settings.server.ServerChooserFragment import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR @AndroidEntryPoint class DeveloperSettingsFragment : DeveloperSettingsView, PreferenceFragmentCompat() { diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/developer/DeveloperSettingsPresenterImpl.kt b/app/src/main/java/io/homeassistant/companion/android/settings/developer/DeveloperSettingsPresenterImpl.kt index 45ee7558af9..7eae79d1507 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/developer/DeveloperSettingsPresenterImpl.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/developer/DeveloperSettingsPresenterImpl.kt @@ -4,9 +4,11 @@ import android.content.Context import android.util.Log import androidx.activity.result.ActivityResult import androidx.preference.PreferenceDataStore +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.prefs.PrefsRepository import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.thread.ThreadManager +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -14,8 +16,6 @@ import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking -import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR class DeveloperSettingsPresenterImpl @Inject constructor( private val prefsRepository: PrefsRepository, @@ -63,7 +63,7 @@ class DeveloperSettingsPresenterImpl @Inject constructor( override fun runThreadDebug(context: Context, serverId: Int) { mainScope.launch { try { - when (val syncResult = threadManager.syncPreferredDataset(context, serverId, CoroutineScope(coroutineContext + SupervisorJob()))) { + when (val syncResult = threadManager.syncPreferredDataset(context, serverId, false, CoroutineScope(coroutineContext + SupervisorJob()))) { is ThreadManager.SyncResult.ServerUnsupported -> view.onThreadDebugResult(context.getString(commonR.string.thread_debug_result_unsupported_server), false) is ThreadManager.SyncResult.OnlyOnServer -> { diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/developer/location/LocationTrackingFragment.kt b/app/src/main/java/io/homeassistant/companion/android/settings/developer/location/LocationTrackingFragment.kt index fa2d63afac8..15913a70ef8 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/developer/location/LocationTrackingFragment.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/developer/location/LocationTrackingFragment.kt @@ -15,13 +15,13 @@ import androidx.core.view.MenuProvider import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle -import com.google.accompanist.themeadapter.material.MdcTheme import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.R +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.settings.developer.location.views.LocationTrackingView +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR @AndroidEntryPoint class LocationTrackingFragment : Fragment() { @@ -38,7 +38,7 @@ class LocationTrackingFragment : Fragment() { ): View { return ComposeView(requireContext()).apply { setContent { - MdcTheme { + HomeAssistantAppTheme { LocationTrackingView( useHistory = viewModel.historyEnabled, onSetHistory = viewModel::enableHistory, diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/developer/location/LocationTrackingViewModel.kt b/app/src/main/java/io/homeassistant/companion/android/settings/developer/location/LocationTrackingViewModel.kt index bb9bd741859..eed66811ba0 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/developer/location/LocationTrackingViewModel.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/developer/location/LocationTrackingViewModel.kt @@ -15,11 +15,11 @@ import io.homeassistant.companion.android.R import io.homeassistant.companion.android.common.data.prefs.PrefsRepository import io.homeassistant.companion.android.database.location.LocationHistoryDao import io.homeassistant.companion.android.database.location.LocationHistoryItemResult +import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.launch -import javax.inject.Inject @HiltViewModel class LocationTrackingViewModel @Inject constructor( diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/developer/location/views/LocationTrackingView.kt b/app/src/main/java/io/homeassistant/companion/android/settings/developer/location/views/LocationTrackingView.kt index ea747601e92..c799ae0cc30 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/developer/location/views/LocationTrackingView.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/developer/location/views/LocationTrackingView.kt @@ -53,15 +53,15 @@ import androidx.paging.compose.collectAsLazyPagingItems import androidx.paging.compose.itemKey import com.mikepenz.iconics.compose.Image import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.database.location.LocationHistoryItem import io.homeassistant.companion.android.database.location.LocationHistoryItemResult import io.homeassistant.companion.android.database.location.LocationHistoryItemTrigger import io.homeassistant.companion.android.database.server.Server import io.homeassistant.companion.android.settings.views.EmptyState -import kotlinx.coroutines.flow.Flow import java.text.DateFormat import java.util.TimeZone -import io.homeassistant.companion.android.common.R as commonR +import kotlinx.coroutines.flow.Flow @Composable fun LocationTrackingView( @@ -91,7 +91,8 @@ fun LocationTrackingView( ) Switch( checked = useHistory, - onCheckedChange = null, // Handled by row + // Handled by row + onCheckedChange = null, modifier = Modifier.padding(start = 16.dp), colors = SwitchDefaults.colors(uncheckedThumbColor = colorResource(commonR.color.colorSwitchUncheckedThumb)) ) diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/language/LanguagesManager.kt b/app/src/main/java/io/homeassistant/companion/android/settings/language/LanguagesManager.kt index e8bbaf67aa4..ab8420126bf 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/language/LanguagesManager.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/language/LanguagesManager.kt @@ -5,11 +5,11 @@ import android.os.Build import android.util.Log import androidx.appcompat.app.AppCompatDelegate import androidx.core.os.LocaleListCompat +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.prefs.PrefsRepository +import javax.inject.Inject import kotlinx.coroutines.runBlocking import org.xmlpull.v1.XmlPullParser -import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR class LanguagesManager @Inject constructor( private var prefs: PrefsRepository diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/language/LanguagesProvider.kt b/app/src/main/java/io/homeassistant/companion/android/settings/language/LanguagesProvider.kt index d8005967675..08c083cba57 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/language/LanguagesProvider.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/language/LanguagesProvider.kt @@ -1,10 +1,10 @@ package io.homeassistant.companion.android.settings.language import android.content.Context +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.util.capitalize import java.util.Locale import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR class LanguagesProvider @Inject constructor( private var langManager: LanguagesManager diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/log/LogFragment.kt b/app/src/main/java/io/homeassistant/companion/android/settings/log/LogFragment.kt index 77d808eeead..270230eaf62 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/log/LogFragment.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/log/LogFragment.kt @@ -29,15 +29,15 @@ import androidx.lifecycle.lifecycleScope import com.google.android.material.tabs.TabLayout import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.R +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.prefs.PrefsRepository import io.homeassistant.companion.android.getLatestFatalCrash import io.homeassistant.companion.android.util.LogcatReader -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch import java.io.File import java.util.Calendar import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch @AndroidEntryPoint class LogFragment : Fragment() { diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/notification/NotificationChannelFragment.kt b/app/src/main/java/io/homeassistant/companion/android/settings/notification/NotificationChannelFragment.kt index c2fd51925e5..4218e744f1a 100755 --- a/app/src/main/java/io/homeassistant/companion/android/settings/notification/NotificationChannelFragment.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/notification/NotificationChannelFragment.kt @@ -9,11 +9,11 @@ import androidx.annotation.RequiresApi import androidx.compose.ui.platform.ComposeView import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels -import com.google.accompanist.themeadapter.material.MdcTheme import dagger.hilt.android.AndroidEntryPoint +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.settings.addHelpMenuProvider import io.homeassistant.companion.android.settings.notification.views.NotificationChannelView -import io.homeassistant.companion.android.common.R as commonR +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme @AndroidEntryPoint class NotificationChannelFragment : Fragment() { @@ -28,7 +28,7 @@ class NotificationChannelFragment : Fragment() { ): View { return ComposeView(requireContext()).apply { setContent { - MdcTheme { + HomeAssistantAppTheme { NotificationChannelView(notificationViewModel = viewModel) } } diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/notification/NotificationDetailFragment.kt b/app/src/main/java/io/homeassistant/companion/android/settings/notification/NotificationDetailFragment.kt index ac3d50e0834..176dc8b0a15 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/notification/NotificationDetailFragment.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/notification/NotificationDetailFragment.kt @@ -12,15 +12,15 @@ import androidx.core.view.MenuHost import androidx.fragment.app.Fragment import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope -import com.google.accompanist.themeadapter.material.MdcTheme import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.R +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.database.notification.NotificationDao import io.homeassistant.companion.android.database.notification.NotificationItem import io.homeassistant.companion.android.settings.notification.views.LoadNotification -import kotlinx.coroutines.launch +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR +import kotlinx.coroutines.launch @AndroidEntryPoint class NotificationDetailFragment : Fragment() { @@ -51,7 +51,7 @@ class NotificationDetailFragment : Fragment() { ): View { return ComposeView(requireContext()).apply { setContent { - MdcTheme { + HomeAssistantAppTheme { LoadNotification(notification) } } diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/notification/NotificationHistoryFragment.kt b/app/src/main/java/io/homeassistant/companion/android/settings/notification/NotificationHistoryFragment.kt index c9c06226547..e060651a544 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/notification/NotificationHistoryFragment.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/notification/NotificationHistoryFragment.kt @@ -16,13 +16,13 @@ import androidx.preference.PreferenceCategory import androidx.preference.PreferenceFragmentCompat import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.R +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.database.notification.NotificationDao import io.homeassistant.companion.android.database.notification.NotificationItem -import kotlinx.coroutines.launch import java.util.Calendar import java.util.GregorianCalendar import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR +import kotlinx.coroutines.launch @AndroidEntryPoint class NotificationHistoryFragment : PreferenceFragmentCompat() { diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/notification/views/NotificationDetailView.kt b/app/src/main/java/io/homeassistant/companion/android/settings/notification/views/NotificationDetailView.kt index e5053757ede..a72868609f1 100755 --- a/app/src/main/java/io/homeassistant/companion/android/settings/notification/views/NotificationDetailView.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/notification/views/NotificationDetailView.kt @@ -18,11 +18,11 @@ import androidx.compose.ui.viewinterop.AndroidView import androidx.core.text.HtmlCompat import com.fasterxml.jackson.core.JsonParser import com.fasterxml.jackson.databind.ObjectMapper +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.database.notification.NotificationItem import io.homeassistant.companion.android.util.notificationItem import java.util.Calendar import java.util.GregorianCalendar -import io.homeassistant.companion.android.common.R as commonR @Composable fun LoadNotification(notification: NotificationItem) { diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/qs/ManageTilesFragment.kt b/app/src/main/java/io/homeassistant/companion/android/settings/qs/ManageTilesFragment.kt index 8b365a511a8..10d3a7b3ac5 100755 --- a/app/src/main/java/io/homeassistant/companion/android/settings/qs/ManageTilesFragment.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/qs/ManageTilesFragment.kt @@ -12,14 +12,14 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.platform.ComposeView import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels -import com.google.accompanist.themeadapter.material.MdcTheme import com.mikepenz.iconics.typeface.IIcon import dagger.hilt.android.AndroidEntryPoint +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.EntityExt import io.homeassistant.companion.android.settings.addHelpMenuProvider import io.homeassistant.companion.android.settings.qs.views.ManageTilesView +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme import io.homeassistant.companion.android.util.icondialog.IconDialog -import io.homeassistant.companion.android.common.R as commonR @AndroidEntryPoint class ManageTilesFragment : Fragment() { @@ -38,7 +38,7 @@ class ManageTilesFragment : Fragment() { ): View { return ComposeView(requireContext()).apply { setContent { - MdcTheme { + HomeAssistantAppTheme { var showingDialog by remember { mutableStateOf(false) } if (showingDialog) { diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/qs/ManageTilesViewModel.kt b/app/src/main/java/io/homeassistant/companion/android/settings/qs/ManageTilesViewModel.kt index 26696969a29..5891104909f 100755 --- a/app/src/main/java/io/homeassistant/companion/android/settings/qs/ManageTilesViewModel.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/qs/ManageTilesViewModel.kt @@ -18,6 +18,7 @@ import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.typeface.IIcon import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial import dagger.hilt.android.lifecycle.HiltViewModel +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.integration.domain import io.homeassistant.companion.android.common.data.integration.getIcon @@ -69,6 +70,8 @@ import io.homeassistant.companion.android.qs.Tile8Service import io.homeassistant.companion.android.qs.Tile9Service import io.homeassistant.companion.android.util.icondialog.getIconByMdiName import io.homeassistant.companion.android.util.icondialog.mdiName +import java.util.concurrent.Executors +import javax.inject.Inject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll @@ -76,9 +79,6 @@ import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import java.util.concurrent.Executors -import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR @HiltViewModel class ManageTilesViewModel @Inject constructor( diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/qs/TileSlot.kt b/app/src/main/java/io/homeassistant/companion/android/settings/qs/TileSlot.kt index 487ce571c68..dfbc6689ef5 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/qs/TileSlot.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/qs/TileSlot.kt @@ -4,8 +4,8 @@ import android.content.ComponentName import android.content.Context import android.content.pm.PackageManager import android.content.res.Resources -import kotlin.math.min import io.homeassistant.companion.android.R as commonR +import kotlin.math.min data class TileSlot( val id: String, diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/sensor/SensorDetailFragment.kt b/app/src/main/java/io/homeassistant/companion/android/settings/sensor/SensorDetailFragment.kt index b21b912cab8..5b3d65add8c 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/sensor/SensorDetailFragment.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/sensor/SensorDetailFragment.kt @@ -21,12 +21,12 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle -import com.google.accompanist.themeadapter.material.MdcTheme import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.R import io.homeassistant.companion.android.common.util.DisabledLocationHandler import io.homeassistant.companion.android.common.util.LocationPermissionInfoHandler import io.homeassistant.companion.android.settings.sensor.views.SensorDetailView +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme import kotlinx.coroutines.launch @AndroidEntryPoint @@ -71,7 +71,7 @@ class SensorDetailFragment : Fragment() { ): View { return ComposeView(requireContext()).apply { setContent { - MdcTheme { + HomeAssistantAppTheme { SensorDetailView( viewModel = viewModel, onSetEnabled = { enable, serverId -> viewModel.setEnabled(enable, serverId) }, diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/sensor/SensorDetailViewModel.kt b/app/src/main/java/io/homeassistant/companion/android/settings/sensor/SensorDetailViewModel.kt index 1e1e6a0a0f0..a9e947b6e94 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/sensor/SensorDetailViewModel.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/sensor/SensorDetailViewModel.kt @@ -17,6 +17,7 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.bluetooth.BluetoothUtils import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.common.sensors.BluetoothSensorManager @@ -32,6 +33,7 @@ import io.homeassistant.companion.android.database.settings.SensorUpdateFrequenc import io.homeassistant.companion.android.database.settings.SettingsDao import io.homeassistant.companion.android.sensors.LastAppSensorManager import io.homeassistant.companion.android.sensors.SensorReceiver +import javax.inject.Inject import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.flow.Flow @@ -41,8 +43,6 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking -import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR @HiltViewModel class SensorDetailViewModel @Inject constructor( diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/sensor/SensorSettingsFragment.kt b/app/src/main/java/io/homeassistant/companion/android/settings/sensor/SensorSettingsFragment.kt index c54b2105e9a..54ce284456f 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/sensor/SensorSettingsFragment.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/sensor/SensorSettingsFragment.kt @@ -17,11 +17,11 @@ import androidx.core.view.MenuProvider import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle -import com.google.accompanist.themeadapter.material.MdcTheme import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.R -import io.homeassistant.companion.android.settings.sensor.views.SensorListView import io.homeassistant.companion.android.common.R as commonR +import io.homeassistant.companion.android.settings.sensor.views.SensorListView +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme @AndroidEntryPoint class SensorSettingsFragment : Fragment() { @@ -35,7 +35,7 @@ class SensorSettingsFragment : Fragment() { ): View { return ComposeView(requireContext()).apply { setContent { - MdcTheme { + HomeAssistantAppTheme { SensorListView( viewModel = viewModel, onSensorClicked = { sensor -> diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/sensor/SensorSettingsViewModel.kt b/app/src/main/java/io/homeassistant/companion/android/settings/sensor/SensorSettingsViewModel.kt index aa98ebe0cbb..4586a25570f 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/sensor/SensorSettingsViewModel.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/sensor/SensorSettingsViewModel.kt @@ -13,8 +13,8 @@ import io.homeassistant.companion.android.common.sensors.SensorManager import io.homeassistant.companion.android.database.sensor.Sensor import io.homeassistant.companion.android.database.sensor.SensorDao import io.homeassistant.companion.android.sensors.SensorReceiver -import kotlinx.coroutines.launch import javax.inject.Inject +import kotlinx.coroutines.launch @HiltViewModel class SensorSettingsViewModel @Inject constructor( diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/sensor/SensorUpdateFrequencyFragment.kt b/app/src/main/java/io/homeassistant/companion/android/settings/sensor/SensorUpdateFrequencyFragment.kt index 9d0804a2b88..bfb59afad0a 100755 --- a/app/src/main/java/io/homeassistant/companion/android/settings/sensor/SensorUpdateFrequencyFragment.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/sensor/SensorUpdateFrequencyFragment.kt @@ -8,12 +8,12 @@ import androidx.compose.runtime.collectAsState import androidx.compose.ui.platform.ComposeView import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels -import com.google.accompanist.themeadapter.material.MdcTheme import dagger.hilt.android.AndroidEntryPoint +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.settings.SettingViewModel import io.homeassistant.companion.android.settings.addHelpMenuProvider import io.homeassistant.companion.android.settings.sensor.views.SensorUpdateFrequencyView -import io.homeassistant.companion.android.common.R as commonR +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme @AndroidEntryPoint class SensorUpdateFrequencyFragment : Fragment() { @@ -27,7 +27,7 @@ class SensorUpdateFrequencyFragment : Fragment() { ): View { return ComposeView(requireContext()).apply { setContent { - MdcTheme { + HomeAssistantAppTheme { val settings = viewModel.getSettingFlow(0) .collectAsState(initial = viewModel.getSetting(0)) SensorUpdateFrequencyView( diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/sensor/views/SensorDetailView.kt b/app/src/main/java/io/homeassistant/companion/android/settings/sensor/views/SensorDetailView.kt index 7c3fd25cc00..af710a18397 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/sensor/views/SensorDetailView.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/sensor/views/SensorDetailView.kt @@ -69,6 +69,7 @@ import com.fasterxml.jackson.module.kotlin.readValue import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.compose.Image import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.sensors.SensorManager import io.homeassistant.companion.android.database.sensor.SensorSetting import io.homeassistant.companion.android.database.sensor.SensorSettingType @@ -79,7 +80,6 @@ import io.homeassistant.companion.android.util.compose.MdcAlertDialog import io.homeassistant.companion.android.util.compose.TransparentChip import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import io.homeassistant.companion.android.common.R as commonR @Composable fun SensorDetailView( @@ -281,7 +281,9 @@ fun SensorDetailTopPanel( } val mdiIcon = try { IconicsDrawable(context, "cmd-${iconToUse.split(":")[1]}").icon - } catch (e: Exception) { null } + } catch (e: Exception) { + null + } if (mdiIcon != null) { Image( @@ -536,9 +538,9 @@ fun SensorDetailSettingDialog( } onSubmit(state.copy().apply { setting.value = inputValue.value }) } - } else { + } else { // list is saved when selecting a value null - }, // list is saved when selecting a value + }, contentPadding = if (listSettingDialog) PaddingValues(all = 0.dp) else PaddingValues(horizontal = 24.dp) ) } diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/sensor/views/SensorListView.kt b/app/src/main/java/io/homeassistant/companion/android/settings/sensor/views/SensorListView.kt index 9fa6f2ea259..18f841af5be 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/sensor/views/SensorListView.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/sensor/views/SensorListView.kt @@ -20,12 +20,12 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import com.mikepenz.iconics.IconicsDrawable +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.sensors.SensorManager import io.homeassistant.companion.android.common.sensors.id import io.homeassistant.companion.android.database.sensor.Sensor import io.homeassistant.companion.android.settings.sensor.SensorSettingsViewModel import io.homeassistant.companion.android.settings.views.SettingsRow -import io.homeassistant.companion.android.common.R as commonR @OptIn(ExperimentalFoundationApi::class) @Composable @@ -92,7 +92,9 @@ fun SensorRow( } val mdiIcon = try { IconicsDrawable(context, "cmd-${iconToUse.split(":")[1]}").icon - } catch (e: Exception) { null } + } catch (e: Exception) { + null + } SettingsRow( primaryText = stringResource(basicSensor.name), diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/sensor/views/SensorUpdateFrequencyView.kt b/app/src/main/java/io/homeassistant/companion/android/settings/sensor/views/SensorUpdateFrequencyView.kt index 9278aef1eca..e4be101e3ae 100755 --- a/app/src/main/java/io/homeassistant/companion/android/settings/sensor/views/SensorUpdateFrequencyView.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/sensor/views/SensorUpdateFrequencyView.kt @@ -13,7 +13,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import io.homeassistant.companion.android.common.R -import io.homeassistant.companion.android.common.util.sensorWorkerChannel +import io.homeassistant.companion.android.common.util.CHANNEL_SENSOR_WORKER import io.homeassistant.companion.android.database.settings.SensorUpdateFrequencySetting import io.homeassistant.companion.android.util.compose.InfoNotification import io.homeassistant.companion.android.util.compose.RadioButtonRow @@ -50,7 +50,7 @@ fun SensorUpdateFrequencyView( if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { InfoNotification( infoString = R.string.sensor_update_notification, - channelId = sensorWorkerChannel, + channelId = CHANNEL_SENSOR_WORKER, buttonString = R.string.sensor_worker_notification_channel ) } diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/server/ServerChooserFragment.kt b/app/src/main/java/io/homeassistant/companion/android/settings/server/ServerChooserFragment.kt index ae07ba2959e..e5147d12812 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/server/ServerChooserFragment.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/server/ServerChooserFragment.kt @@ -8,13 +8,13 @@ import android.widget.FrameLayout import androidx.compose.ui.platform.ComposeView import androidx.core.os.bundleOf import androidx.fragment.app.setFragmentResult -import com.google.accompanist.themeadapter.material.MdcTheme import com.google.android.material.R import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialogFragment import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.common.data.servers.ServerManager +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme import javax.inject.Inject @AndroidEntryPoint @@ -37,7 +37,7 @@ class ServerChooserFragment : BottomSheetDialogFragment() { ): View { return ComposeView(requireContext()).apply { setContent { - MdcTheme { + HomeAssistantAppTheme { ServerChooserView( servers = serverManager.defaultServers, onServerSelected = { serverId -> diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/server/ServerChooserView.kt b/app/src/main/java/io/homeassistant/companion/android/settings/server/ServerChooserView.kt index a10366e5366..86624e2665e 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/server/ServerChooserView.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/server/ServerChooserView.kt @@ -14,9 +14,9 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.database.server.Server import io.homeassistant.companion.android.util.compose.ModalBottomSheet -import io.homeassistant.companion.android.common.R as commonR @Composable fun ServerChooserView( diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/server/ServerSettingsFragment.kt b/app/src/main/java/io/homeassistant/companion/android/settings/server/ServerSettingsFragment.kt index 9787c09016d..26326aef48e 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/server/ServerSettingsFragment.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/server/ServerSettingsFragment.kt @@ -25,6 +25,7 @@ import androidx.preference.SwitchPreference import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.R import io.homeassistant.companion.android.authenticator.Authenticator +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.util.DisabledLocationHandler import io.homeassistant.companion.android.common.util.LocationPermissionInfoHandler import io.homeassistant.companion.android.launch.LaunchActivity @@ -33,10 +34,9 @@ import io.homeassistant.companion.android.settings.ssid.SsidFragment import io.homeassistant.companion.android.settings.url.ExternalUrlFragment import io.homeassistant.companion.android.settings.websocket.WebsocketSettingFragment import io.homeassistant.companion.android.webview.WebViewActivity +import javax.inject.Inject import kotlinx.coroutines.launch import okhttp3.HttpUrl.Companion.toHttpUrlOrNull -import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR @AndroidEntryPoint class ServerSettingsFragment : ServerSettingsView, PreferenceFragmentCompat() { diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/server/ServerSettingsPresenterImpl.kt b/app/src/main/java/io/homeassistant/companion/android/settings/server/ServerSettingsPresenterImpl.kt index 50b2818aa6e..459d9930624 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/server/ServerSettingsPresenterImpl.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/server/ServerSettingsPresenterImpl.kt @@ -4,13 +4,13 @@ import android.util.Log import androidx.preference.PreferenceDataStore import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.common.data.wifi.WifiHelper +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.cancel import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking -import javax.inject.Inject class ServerSettingsPresenterImpl @Inject constructor( private val serverManager: ServerManager, diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/shortcuts/ManageShortcutsSettingsFragment.kt b/app/src/main/java/io/homeassistant/companion/android/settings/shortcuts/ManageShortcutsSettingsFragment.kt index 52b25054fd6..37a98d1a920 100755 --- a/app/src/main/java/io/homeassistant/companion/android/settings/shortcuts/ManageShortcutsSettingsFragment.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/shortcuts/ManageShortcutsSettingsFragment.kt @@ -14,13 +14,13 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.platform.ComposeView import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels -import com.google.accompanist.themeadapter.material.MdcTheme import com.mikepenz.iconics.typeface.IIcon import dagger.hilt.android.AndroidEntryPoint +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.settings.addHelpMenuProvider import io.homeassistant.companion.android.settings.shortcuts.views.ManageShortcutsView +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme import io.homeassistant.companion.android.util.icondialog.IconDialog -import io.homeassistant.companion.android.common.R as commonR @RequiresApi(Build.VERSION_CODES.N_MR1) @AndroidEntryPoint @@ -41,7 +41,7 @@ class ManageShortcutsSettingsFragment : Fragment() { ): View { return ComposeView(requireContext()).apply { setContent { - MdcTheme { + HomeAssistantAppTheme { var showingTag by remember { mutableStateOf(null) } showingTag?.let { tag -> IconDialog( diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/shortcuts/ManageShortcutsViewModel.kt b/app/src/main/java/io/homeassistant/companion/android/settings/shortcuts/ManageShortcutsViewModel.kt index 219cc1ce98b..ff1b79a5ace 100755 --- a/app/src/main/java/io/homeassistant/companion/android/settings/shortcuts/ManageShortcutsViewModel.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/shortcuts/ManageShortcutsViewModel.kt @@ -36,10 +36,10 @@ import io.homeassistant.companion.android.util.icondialog.getIconByMdiName import io.homeassistant.companion.android.util.icondialog.mdiName import io.homeassistant.companion.android.webview.WebViewActivity import io.homeassistant.companion.android.widgets.assist.AssistShortcutActivity +import javax.inject.Inject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import javax.inject.Inject @RequiresApi(Build.VERSION_CODES.N_MR1) @HiltViewModel diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/ssid/SsidFragment.kt b/app/src/main/java/io/homeassistant/companion/android/settings/ssid/SsidFragment.kt index 62b0c4ebaa2..98d03a94017 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/ssid/SsidFragment.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/ssid/SsidFragment.kt @@ -7,11 +7,11 @@ import android.view.ViewGroup import androidx.compose.ui.platform.ComposeView import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels -import com.google.accompanist.themeadapter.material.MdcTheme import dagger.hilt.android.AndroidEntryPoint +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.settings.addHelpMenuProvider import io.homeassistant.companion.android.settings.ssid.views.SsidView -import io.homeassistant.companion.android.common.R as commonR +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme @AndroidEntryPoint class SsidFragment : Fragment() { @@ -29,7 +29,7 @@ class SsidFragment : Fragment() { ): View { return ComposeView(requireContext()).apply { setContent { - MdcTheme { + HomeAssistantAppTheme { SsidView( wifiSsids = viewModel.wifiSsids, prioritizeInternal = viewModel.prioritizeInternal, diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/ssid/SsidViewModel.kt b/app/src/main/java/io/homeassistant/companion/android/settings/ssid/SsidViewModel.kt index a9ccfd18288..1f847e597f9 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/ssid/SsidViewModel.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/ssid/SsidViewModel.kt @@ -11,8 +11,8 @@ import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.common.data.wifi.WifiHelper -import kotlinx.coroutines.launch import javax.inject.Inject +import kotlinx.coroutines.launch @HiltViewModel class SsidViewModel @Inject constructor( diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/ssid/views/SsidView.kt b/app/src/main/java/io/homeassistant/companion/android/settings/ssid/views/SsidView.kt index 592f004933d..8b65df89a41 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/ssid/views/SsidView.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/ssid/views/SsidView.kt @@ -51,8 +51,8 @@ import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import io.homeassistant.companion.android.common.data.wifi.WifiHelper import io.homeassistant.companion.android.common.R as commonR +import io.homeassistant.companion.android.common.data.wifi.WifiHelper @OptIn( ExperimentalComposeUiApi::class, diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/url/ExternalUrlFragment.kt b/app/src/main/java/io/homeassistant/companion/android/settings/url/ExternalUrlFragment.kt index 2a05b53579b..0fc003f1909 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/url/ExternalUrlFragment.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/url/ExternalUrlFragment.kt @@ -7,10 +7,10 @@ import android.view.ViewGroup import androidx.compose.ui.platform.ComposeView import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels -import com.google.accompanist.themeadapter.material.MdcTheme import dagger.hilt.android.AndroidEntryPoint -import io.homeassistant.companion.android.settings.url.views.ExternalUrlView import io.homeassistant.companion.android.common.R as commonR +import io.homeassistant.companion.android.settings.url.views.ExternalUrlView +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme @AndroidEntryPoint class ExternalUrlFragment : Fragment() { @@ -28,7 +28,7 @@ class ExternalUrlFragment : Fragment() { ): View { return ComposeView(requireContext()).apply { setContent { - MdcTheme { + HomeAssistantAppTheme { ExternalUrlView( canUseCloud = viewModel.canUseCloud, useCloud = viewModel.useCloud, diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/url/ExternalUrlViewModel.kt b/app/src/main/java/io/homeassistant/companion/android/settings/url/ExternalUrlViewModel.kt index b1b5149182b..31ddbfef3ac 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/url/ExternalUrlViewModel.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/url/ExternalUrlViewModel.kt @@ -12,8 +12,8 @@ import dagger.hilt.android.lifecycle.HiltViewModel import io.homeassistant.companion.android.common.data.MalformedHttpUrlException import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.util.UrlUtil -import kotlinx.coroutines.launch import javax.inject.Inject +import kotlinx.coroutines.launch @HiltViewModel class ExternalUrlViewModel @Inject constructor( diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/url/views/ExternalUrlInputView.kt b/app/src/main/java/io/homeassistant/companion/android/settings/url/views/ExternalUrlInputView.kt index 6f89d158d83..a6baff339ec 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/url/views/ExternalUrlInputView.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/url/views/ExternalUrlInputView.kt @@ -28,8 +28,8 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp -import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import io.homeassistant.companion.android.common.R as commonR +import okhttp3.HttpUrl.Companion.toHttpUrlOrNull @OptIn(ExperimentalComposeUiApi::class) @Composable diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/vehicle/ManageAndroidAutoSettingsFragment.kt b/app/src/main/java/io/homeassistant/companion/android/settings/vehicle/ManageAndroidAutoSettingsFragment.kt index 37a840dd8b5..5bbe2364319 100755 --- a/app/src/main/java/io/homeassistant/companion/android/settings/vehicle/ManageAndroidAutoSettingsFragment.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/vehicle/ManageAndroidAutoSettingsFragment.kt @@ -10,13 +10,13 @@ import androidx.annotation.RequiresApi import androidx.compose.ui.platform.ComposeView import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels -import com.google.accompanist.themeadapter.material.MdcTheme import dagger.hilt.android.AndroidEntryPoint +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.settings.addHelpMenuProvider import io.homeassistant.companion.android.settings.vehicle.views.AndroidAutoFavoritesSettings +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR @AndroidEntryPoint class ManageAndroidAutoSettingsFragment : Fragment() { @@ -34,7 +34,7 @@ class ManageAndroidAutoSettingsFragment : Fragment() { ): View { return ComposeView(requireContext()).apply { setContent { - MdcTheme { + HomeAssistantAppTheme { AndroidAutoFavoritesSettings( androidAutoViewModel = viewModel, serversList = serverManager.defaultServers, diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/vehicle/ManageAndroidAutoViewModel.kt b/app/src/main/java/io/homeassistant/companion/android/settings/vehicle/ManageAndroidAutoViewModel.kt index f2b798d3483..58d99e9880c 100755 --- a/app/src/main/java/io/homeassistant/companion/android/settings/vehicle/ManageAndroidAutoViewModel.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/vehicle/ManageAndroidAutoViewModel.kt @@ -15,11 +15,11 @@ import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.prefs.PrefsRepository import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.util.vehicle.isVehicleDomain +import javax.inject.Inject import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.launch import org.burnoutcrew.reorderable.ItemPosition -import javax.inject.Inject @RequiresApi(Build.VERSION_CODES.O) @HiltViewModel diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/vehicle/views/AndroidAutoFavoritesView.kt b/app/src/main/java/io/homeassistant/companion/android/settings/vehicle/views/AndroidAutoFavoritesView.kt index 642b4ce3c1a..c2a07c0d4ab 100755 --- a/app/src/main/java/io/homeassistant/companion/android/settings/vehicle/views/AndroidAutoFavoritesView.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/vehicle/views/AndroidAutoFavoritesView.kt @@ -17,6 +17,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.integration.friendlyName import io.homeassistant.companion.android.database.server.Server @@ -30,7 +31,6 @@ import kotlinx.coroutines.withContext import org.burnoutcrew.reorderable.ReorderableItem import org.burnoutcrew.reorderable.rememberReorderableLazyListState import org.burnoutcrew.reorderable.reorderable -import io.homeassistant.companion.android.common.R as commonR @RequiresApi(Build.VERSION_CODES.O) @Composable diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/websocket/WebsocketSettingFragment.kt b/app/src/main/java/io/homeassistant/companion/android/settings/websocket/WebsocketSettingFragment.kt index e005adaaeaf..ae0168e45d0 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/websocket/WebsocketSettingFragment.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/websocket/WebsocketSettingFragment.kt @@ -18,14 +18,14 @@ import androidx.compose.ui.platform.ComposeView import androidx.core.content.getSystemService import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels -import com.google.accompanist.themeadapter.material.MdcTheme import dagger.hilt.android.AndroidEntryPoint +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.wifi.WifiHelper import io.homeassistant.companion.android.settings.SettingViewModel import io.homeassistant.companion.android.settings.addHelpMenuProvider import io.homeassistant.companion.android.settings.websocket.views.WebsocketSettingView +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR @AndroidEntryPoint class WebsocketSettingFragment : Fragment() { @@ -61,7 +61,7 @@ class WebsocketSettingFragment : Fragment() { ): View { return ComposeView(requireContext()).apply { setContent { - MdcTheme { + HomeAssistantAppTheme { val settings = viewModel.getSettingFlow(serverId) .collectAsState(initial = viewModel.getSetting(serverId)) WebsocketSettingView( diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/websocket/views/WebsocketSettingView.kt b/app/src/main/java/io/homeassistant/companion/android/settings/websocket/views/WebsocketSettingView.kt index e95263fed1e..4e3c030d09a 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/websocket/views/WebsocketSettingView.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/websocket/views/WebsocketSettingView.kt @@ -20,7 +20,7 @@ import androidx.compose.ui.unit.dp import androidx.core.content.getSystemService import io.homeassistant.companion.android.BuildConfig import io.homeassistant.companion.android.common.R -import io.homeassistant.companion.android.common.util.websocketChannel +import io.homeassistant.companion.android.common.util.CHANNEL_WEBSOCKET import io.homeassistant.companion.android.database.settings.WebsocketSetting import io.homeassistant.companion.android.util.compose.HaAlertWarning import io.homeassistant.companion.android.util.compose.InfoNotification @@ -77,7 +77,7 @@ fun WebsocketSettingView( if (websocketSetting != WebsocketSetting.NEVER && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && uiManager?.currentModeType != Configuration.UI_MODE_TYPE_TELEVISION) { InfoNotification( infoString = R.string.websocket_persistent_notification, - channelId = websocketChannel, + channelId = CHANNEL_WEBSOCKET, buttonString = R.string.websocket_notification_channel ) } diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/widgets/ManageWidgetsSettingsFragment.kt b/app/src/main/java/io/homeassistant/companion/android/settings/widgets/ManageWidgetsSettingsFragment.kt index 761b3be343f..dd0b1e442f2 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/widgets/ManageWidgetsSettingsFragment.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/widgets/ManageWidgetsSettingsFragment.kt @@ -7,11 +7,11 @@ import android.view.ViewGroup import androidx.compose.ui.platform.ComposeView import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels -import com.google.accompanist.themeadapter.material.MdcTheme import dagger.hilt.android.AndroidEntryPoint +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.settings.addHelpMenuProvider import io.homeassistant.companion.android.settings.widgets.views.ManageWidgetsView -import io.homeassistant.companion.android.common.R as commonR +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme @AndroidEntryPoint class ManageWidgetsSettingsFragment : Fragment() { @@ -25,7 +25,7 @@ class ManageWidgetsSettingsFragment : Fragment() { ): View { return ComposeView(requireContext()).apply { setContent { - MdcTheme { + HomeAssistantAppTheme { ManageWidgetsView(viewModel = viewModel) } } diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/widgets/ManageWidgetsViewModel.kt b/app/src/main/java/io/homeassistant/companion/android/settings/widgets/ManageWidgetsViewModel.kt index 1495af237c6..87917e40d88 100755 --- a/app/src/main/java/io/homeassistant/companion/android/settings/widgets/ManageWidgetsViewModel.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/widgets/ManageWidgetsViewModel.kt @@ -16,9 +16,9 @@ import io.homeassistant.companion.android.database.widget.CameraWidgetDao import io.homeassistant.companion.android.database.widget.MediaPlayerControlsWidgetDao import io.homeassistant.companion.android.database.widget.StaticWidgetDao import io.homeassistant.companion.android.database.widget.TemplateWidgetDao +import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.launch -import javax.inject.Inject @HiltViewModel class ManageWidgetsViewModel @Inject constructor( diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/widgets/views/ManageWidgetsView.kt b/app/src/main/java/io/homeassistant/companion/android/settings/widgets/views/ManageWidgetsView.kt index e309d496b79..d1c6575d4b9 100755 --- a/app/src/main/java/io/homeassistant/companion/android/settings/widgets/views/ManageWidgetsView.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/widgets/views/ManageWidgetsView.kt @@ -47,7 +47,7 @@ import io.homeassistant.companion.android.widgets.template.TemplateWidgetConfigu enum class WidgetType(val widgetIcon: IIcon) { BUTTON(CommunityMaterial.Icon2.cmd_gesture_tap), - CAMERA(CommunityMaterial.Icon.cmd_camera), + CAMERA(CommunityMaterial.Icon.cmd_camera_image), STATE(CommunityMaterial.Icon3.cmd_shape), MEDIA(CommunityMaterial.Icon3.cmd_play_box_multiple), TEMPLATE(CommunityMaterial.Icon.cmd_code_braces); diff --git a/app/src/main/java/io/homeassistant/companion/android/share/ShareActivity.kt b/app/src/main/java/io/homeassistant/companion/android/share/ShareActivity.kt index 38f97abde79..f2a47b03dda 100644 --- a/app/src/main/java/io/homeassistant/companion/android/share/ShareActivity.kt +++ b/app/src/main/java/io/homeassistant/companion/android/share/ShareActivity.kt @@ -7,11 +7,11 @@ import android.widget.Toast import androidx.core.app.ActivityCompat import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.BaseActivity +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.servers.ServerManager +import javax.inject.Inject import kotlinx.coroutines.runBlocking import okhttp3.HttpUrl.Companion.toHttpUrlOrNull -import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR @AndroidEntryPoint class ShareActivity : BaseActivity() { diff --git a/app/src/main/java/io/homeassistant/companion/android/themes/ThemesManager.kt b/app/src/main/java/io/homeassistant/companion/android/themes/ThemesManager.kt index 3220caf510f..e616aeb058e 100644 --- a/app/src/main/java/io/homeassistant/companion/android/themes/ThemesManager.kt +++ b/app/src/main/java/io/homeassistant/companion/android/themes/ThemesManager.kt @@ -8,8 +8,8 @@ import androidx.appcompat.app.AppCompatDelegate import androidx.webkit.WebSettingsCompat import androidx.webkit.WebViewFeature import io.homeassistant.companion.android.common.data.prefs.PrefsRepository -import kotlinx.coroutines.runBlocking import javax.inject.Inject +import kotlinx.coroutines.runBlocking class ThemesManager @Inject constructor( private val themesUseCase: PrefsRepository diff --git a/app/src/main/java/io/homeassistant/companion/android/thread/ThreadManager.kt b/app/src/main/java/io/homeassistant/companion/android/thread/ThreadManager.kt index 7976e890c45..23d4496dfd3 100644 --- a/app/src/main/java/io/homeassistant/companion/android/thread/ThreadManager.kt +++ b/app/src/main/java/io/homeassistant/companion/android/thread/ThreadManager.kt @@ -11,6 +11,7 @@ interface ThreadManager { sealed class SyncResult { object AppUnsupported : SyncResult() object ServerUnsupported : SyncResult() + object NotConnected : SyncResult() class OnlyOnServer(val imported: Boolean) : SyncResult() class OnlyOnDevice(val exportIntent: IntentSender?) : SyncResult() class AllHaveCredentials(val matches: Boolean?, val fromApp: Boolean?, val updated: Boolean?, val exportIntent: IntentSender?) : SyncResult() @@ -28,15 +29,21 @@ interface ThreadManager { suspend fun coreSupportsThread(serverId: Int): Boolean /** - * Try to sync the preferred Thread dataset with the device and server. If one has a preferred - * dataset while the other one doesn't, it will sync. If both have preferred datasets, it will - * send updated data to the server if needed. If neither has a preferred dataset, skip syncing. + * Try to sync the preferred Thread dataset. + * @param exportOnly Controls the synchronization direction. + * - If set to `true`, only get the device preferred dataset and sync to the server if it + * wasn't added by the app. + * - If set to `false`, try to get the device and server in sync. This will clean up old/stale + * app datasets. If one has a preferred dataset while the other one doesn't, it will sync to + * the other. If both have preferred datasets, it will send updated data to the server if + * needed. If neither has a preferred dataset, skip syncing. * @return [SyncResult] with details of the sync operation, which may include an [IntentSender] * if permission is required to import the device dataset */ suspend fun syncPreferredDataset( context: Context, serverId: Int, + exportOnly: Boolean, scope: CoroutineScope ): SyncResult diff --git a/app/src/main/java/io/homeassistant/companion/android/util/DataUriDownloadManager.kt b/app/src/main/java/io/homeassistant/companion/android/util/DataUriDownloadManager.kt index df632686399..78253ba65ca 100644 --- a/app/src/main/java/io/homeassistant/companion/android/util/DataUriDownloadManager.kt +++ b/app/src/main/java/io/homeassistant/companion/android/util/DataUriDownloadManager.kt @@ -16,9 +16,8 @@ import android.util.Log import android.webkit.MimeTypeMap import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat -import io.homeassistant.companion.android.common.util.downloadsChannel -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext +import io.homeassistant.companion.android.common.R as commonR +import io.homeassistant.companion.android.common.util.CHANNEL_DOWNLOADS import java.io.File import java.io.FileOutputStream import java.text.SimpleDateFormat @@ -26,7 +25,8 @@ import java.util.Date import java.util.Locale import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine -import io.homeassistant.companion.android.common.R as commonR +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext object DataUriDownloadManager { private const val TAG = "DataUriDownloadMgr" @@ -44,7 +44,7 @@ object DataUriDownloadManager { val result = writeDataUriToFile(context, url, mime) createNotificationChannel(context) - val notification = NotificationCompat.Builder(context, downloadsChannel) + val notification = NotificationCompat.Builder(context, CHANNEL_DOWNLOADS) .setContentTitle(context.getString(commonR.string.downloads_unnamed_file)) .setSmallIcon(android.R.drawable.stat_sys_download_done) .setAutoCancel(true) @@ -145,7 +145,7 @@ object DataUriDownloadManager { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val notificationManager = NotificationManagerCompat.from(context) val channel = NotificationChannel( - downloadsChannel, + CHANNEL_DOWNLOADS, context.getString(commonR.string.downloads), NotificationManager.IMPORTANCE_DEFAULT ) diff --git a/app/src/main/java/io/homeassistant/companion/android/util/LogcatReader.kt b/app/src/main/java/io/homeassistant/companion/android/util/LogcatReader.kt index a02debb57bf..68c383a8ce4 100644 --- a/app/src/main/java/io/homeassistant/companion/android/util/LogcatReader.kt +++ b/app/src/main/java/io/homeassistant/companion/android/util/LogcatReader.kt @@ -1,10 +1,10 @@ package io.homeassistant.companion.android.util import android.util.Log -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext import java.io.BufferedReader import java.io.InputStreamReader +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext object LogcatReader { const val TAG = "LogcatReader" diff --git a/app/src/main/java/io/homeassistant/companion/android/util/Navigator.kt b/app/src/main/java/io/homeassistant/companion/android/util/Navigator.kt index 0ca08642436..9174c6c5f7a 100644 --- a/app/src/main/java/io/homeassistant/companion/android/util/Navigator.kt +++ b/app/src/main/java/io/homeassistant/companion/android/util/Navigator.kt @@ -4,15 +4,15 @@ import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow class Navigator { - private val _sharedFlow = MutableSharedFlow(extraBufferCapacity = 1) - val flow = _sharedFlow.asSharedFlow() + private val mutableFlow = MutableSharedFlow(extraBufferCapacity = 1) + val flow = mutableFlow.asSharedFlow() fun navigateTo(navTarget: String) { - _sharedFlow.tryEmit(NavigatorItem(navTarget)) + mutableFlow.tryEmit(NavigatorItem(navTarget)) } fun navigateTo(navItem: NavigatorItem) { - _sharedFlow.tryEmit(navItem) + mutableFlow.tryEmit(navItem) } data class NavigatorItem( diff --git a/app/src/main/java/io/homeassistant/companion/android/util/OnSwipeListener.kt b/app/src/main/java/io/homeassistant/companion/android/util/OnSwipeListener.kt index bc2e8111276..9e40c6cbb14 100644 --- a/app/src/main/java/io/homeassistant/companion/android/util/OnSwipeListener.kt +++ b/app/src/main/java/io/homeassistant/companion/android/util/OnSwipeListener.kt @@ -157,7 +157,10 @@ abstract class OnSwipeListener(context: Context?) : View.OnTouchListener { ): Boolean enum class SwipeDirection { - UP, DOWN, LEFT, RIGHT; + UP, + DOWN, + LEFT, + RIGHT; companion object { fun fromVelocity(velocityX: Float, velocityY: Float): SwipeDirection { diff --git a/app/src/main/java/io/homeassistant/companion/android/util/TLSWebViewClient.kt b/app/src/main/java/io/homeassistant/companion/android/util/TLSWebViewClient.kt index 706e895b2a2..4e0f3d7cb9e 100644 --- a/app/src/main/java/io/homeassistant/companion/android/util/TLSWebViewClient.kt +++ b/app/src/main/java/io/homeassistant/companion/android/util/TLSWebViewClient.kt @@ -13,13 +13,13 @@ import androidx.annotation.RequiresApi import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.lifecycleScope import io.homeassistant.companion.android.common.data.keychain.KeyChainRepository -import kotlinx.coroutines.launch import java.security.Principal import java.security.PrivateKey import java.security.cert.CertificateException import java.security.cert.X509Certificate import javax.inject.Inject import javax.inject.Named +import kotlinx.coroutines.launch open class TLSWebViewClient @Inject constructor(@Named("keyChainRepository") private var keyChainRepository: KeyChainRepository) : WebViewClient() { diff --git a/app/src/main/java/io/homeassistant/companion/android/util/compose/ExposedDropdownMenu.kt b/app/src/main/java/io/homeassistant/companion/android/util/compose/ExposedDropdownMenu.kt index 9c78d5dbb7c..41fa7ddefb6 100644 --- a/app/src/main/java/io/homeassistant/companion/android/util/compose/ExposedDropdownMenu.kt +++ b/app/src/main/java/io/homeassistant/companion/android/util/compose/ExposedDropdownMenu.kt @@ -16,8 +16,8 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource -import io.homeassistant.companion.android.database.server.Server import io.homeassistant.companion.android.common.R as commonR +import io.homeassistant.companion.android.database.server.Server @OptIn(ExperimentalMaterialApi::class) @Composable diff --git a/app/src/main/java/io/homeassistant/companion/android/util/compose/SingleEntityPicker.kt b/app/src/main/java/io/homeassistant/companion/android/util/compose/SingleEntityPicker.kt index b0a174532fe..580f449235b 100644 --- a/app/src/main/java/io/homeassistant/companion/android/util/compose/SingleEntityPicker.kt +++ b/app/src/main/java/io/homeassistant/companion/android/util/compose/SingleEntityPicker.kt @@ -31,11 +31,11 @@ import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.integration.friendlyName import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -import io.homeassistant.companion.android.common.R as commonR @OptIn(ExperimentalMaterialApi::class) @Composable diff --git a/app/src/main/java/io/homeassistant/companion/android/util/compose/Theme.kt b/app/src/main/java/io/homeassistant/companion/android/util/compose/Theme.kt new file mode 100644 index 00000000000..9032361e910 --- /dev/null +++ b/app/src/main/java/io/homeassistant/companion/android/util/compose/Theme.kt @@ -0,0 +1,51 @@ +package io.homeassistant.companion.android.util.compose + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material.LocalContentColor +import androidx.compose.material.MaterialTheme +import androidx.compose.material.darkColors +import androidx.compose.material.lightColors +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.graphics.Color + +val colorPrimary = Color(0xFF03A9F4) +val colorPrimaryDark = Color(0xFF0288D1) +val darkColorBackground = Color(0xFF1C1C1C) + +private val haLightColors = lightColors( + primary = colorPrimary, + primaryVariant = colorPrimaryDark, + secondary = colorPrimary, + secondaryVariant = colorPrimary, + onPrimary = Color.White, + onSecondary = Color.White +) +private val haDarkColors = darkColors( + primary = colorPrimary, + primaryVariant = colorPrimaryDark, + secondary = colorPrimary, + secondaryVariant = colorPrimary, + background = darkColorBackground, + onPrimary = Color.White, + onSecondary = Color.White +) + +/** + * A Compose [MaterialTheme] version of the app's XML theme. This achieves the same goal as the + * (now deprecated) [com.google.accompanist.themeadapter.material.MdcTheme]. + */ +@Composable +fun HomeAssistantAppTheme( + content: @Composable () -> Unit +) { + MaterialTheme( + colors = if (isSystemInDarkTheme()) haDarkColors else haLightColors + ) { + // Copied from MdcTheme: + CompositionLocalProvider( + LocalContentColor provides MaterialTheme.colors.onBackground, + content = content + ) + } +} diff --git a/app/src/main/java/io/homeassistant/companion/android/util/icondialog/IconDialog.kt b/app/src/main/java/io/homeassistant/companion/android/util/icondialog/IconDialog.kt index 4cf18123f27..dd03cc4692b 100644 --- a/app/src/main/java/io/homeassistant/companion/android/util/icondialog/IconDialog.kt +++ b/app/src/main/java/io/homeassistant/companion/android/util/icondialog/IconDialog.kt @@ -14,9 +14,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog -import com.google.accompanist.themeadapter.material.MdcTheme import com.mikepenz.iconics.typeface.IIcon import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme @Composable fun IconDialogContent( @@ -56,7 +56,7 @@ fun IconDialog( @Preview @Composable private fun IconDialogPreview() { - MdcTheme { + HomeAssistantAppTheme { Surface( modifier = Modifier .width(480.dp) diff --git a/app/src/main/java/io/homeassistant/companion/android/util/icondialog/IconDialogFragment.kt b/app/src/main/java/io/homeassistant/companion/android/util/icondialog/IconDialogFragment.kt index fbd8e7fc27a..f1835db10ef 100644 --- a/app/src/main/java/io/homeassistant/companion/android/util/icondialog/IconDialogFragment.kt +++ b/app/src/main/java/io/homeassistant/companion/android/util/icondialog/IconDialogFragment.kt @@ -7,8 +7,8 @@ import android.view.View import android.view.ViewGroup import androidx.compose.ui.platform.ComposeView import androidx.fragment.app.DialogFragment -import com.google.accompanist.themeadapter.material.MdcTheme import com.mikepenz.iconics.typeface.IIcon +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme import kotlin.math.min class IconDialogFragment(callback: (IIcon) -> Unit) : DialogFragment() { @@ -33,7 +33,7 @@ class IconDialogFragment(callback: (IIcon) -> Unit) : DialogFragment() { super.onViewCreated(view, savedInstanceState) (view as ComposeView).setContent { - MdcTheme { + HomeAssistantAppTheme { IconDialogContent( onSelect = onSelect ) diff --git a/app/src/main/java/io/homeassistant/companion/android/util/icondialog/IconDialogGrid.kt b/app/src/main/java/io/homeassistant/companion/android/util/icondialog/IconDialogGrid.kt index 5d5feb53061..e8b0c4cf83d 100644 --- a/app/src/main/java/io/homeassistant/companion/android/util/icondialog/IconDialogGrid.kt +++ b/app/src/main/java/io/homeassistant/companion/android/util/icondialog/IconDialogGrid.kt @@ -20,11 +20,11 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import com.google.accompanist.themeadapter.material.MdcTheme import com.mikepenz.iconics.compose.Image import com.mikepenz.iconics.typeface.IIcon import com.mikepenz.iconics.typeface.ITypeface import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -82,7 +82,7 @@ fun IconDialogGrid( @Preview @Composable private fun IconDialogGridPreview() { - MdcTheme { + HomeAssistantAppTheme { Surface( modifier = Modifier .width(480.dp) diff --git a/app/src/main/java/io/homeassistant/companion/android/util/icondialog/IconDialogSearch.kt b/app/src/main/java/io/homeassistant/companion/android/util/icondialog/IconDialogSearch.kt index 3851987fafa..14cda4675ea 100644 --- a/app/src/main/java/io/homeassistant/companion/android/util/icondialog/IconDialogSearch.kt +++ b/app/src/main/java/io/homeassistant/companion/android/util/icondialog/IconDialogSearch.kt @@ -17,8 +17,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.intl.Locale import androidx.compose.ui.tooling.preview.Preview -import com.google.accompanist.themeadapter.material.MdcTheme import io.homeassistant.companion.android.common.R +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme @Composable fun IconDialogSearch( @@ -52,7 +52,7 @@ fun IconDialogSearch( @Preview @Composable private fun IconDialogSearchPreview() { - MdcTheme { + HomeAssistantAppTheme { Surface { IconDialogSearch(value = "account", onValueChange = {}) } diff --git a/app/src/main/java/io/homeassistant/companion/android/util/vehicle/GridItems.kt b/app/src/main/java/io/homeassistant/companion/android/util/vehicle/GridItems.kt index d5a9803b979..11286ff94b5 100755 --- a/app/src/main/java/io/homeassistant/companion/android/util/vehicle/GridItems.kt +++ b/app/src/main/java/io/homeassistant/companion/android/util/vehicle/GridItems.kt @@ -28,12 +28,12 @@ import io.homeassistant.companion.android.vehicle.ChangeServerScreen import io.homeassistant.companion.android.vehicle.DomainListScreen import io.homeassistant.companion.android.vehicle.EntityGridVehicleScreen import io.homeassistant.companion.android.vehicle.MapVehicleScreen +import java.util.Calendar +import java.util.Locale import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch -import java.util.Calendar -import java.util.Locale private const val TAG = "GridItems" diff --git a/app/src/main/java/io/homeassistant/companion/android/vehicle/ChangeServerScreen.kt b/app/src/main/java/io/homeassistant/companion/android/vehicle/ChangeServerScreen.kt index 5cb197fb7a9..e450fcf4a8d 100644 --- a/app/src/main/java/io/homeassistant/companion/android/vehicle/ChangeServerScreen.kt +++ b/app/src/main/java/io/homeassistant/companion/android/vehicle/ChangeServerScreen.kt @@ -7,9 +7,9 @@ import androidx.car.app.model.ItemList import androidx.car.app.model.ListTemplate import androidx.car.app.model.Row import androidx.car.app.model.Template +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.servers.ServerManager import kotlinx.coroutines.flow.StateFlow -import io.homeassistant.companion.android.common.R as commonR class ChangeServerScreen( carContext: CarContext, diff --git a/app/src/main/java/io/homeassistant/companion/android/vehicle/HaCarAppService.kt b/app/src/main/java/io/homeassistant/companion/android/vehicle/HaCarAppService.kt index 7c3a5cc24a4..d45eec0af4b 100644 --- a/app/src/main/java/io/homeassistant/companion/android/vehicle/HaCarAppService.kt +++ b/app/src/main/java/io/homeassistant/companion/android/vehicle/HaCarAppService.kt @@ -19,6 +19,7 @@ import io.homeassistant.companion.android.R import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.prefs.PrefsRepository import io.homeassistant.companion.android.common.data.servers.ServerManager +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow @@ -27,7 +28,6 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.launch import okhttp3.internal.toImmutableMap -import javax.inject.Inject @RequiresApi(Build.VERSION_CODES.O) @AndroidEntryPoint diff --git a/app/src/main/java/io/homeassistant/companion/android/vehicle/LoginScreen.kt b/app/src/main/java/io/homeassistant/companion/android/vehicle/LoginScreen.kt index 4dd383e53ce..d5c0d39ef3e 100644 --- a/app/src/main/java/io/homeassistant/companion/android/vehicle/LoginScreen.kt +++ b/app/src/main/java/io/homeassistant/companion/android/vehicle/LoginScreen.kt @@ -27,13 +27,13 @@ class LoginScreen(context: CarContext, val serverManager: ServerManager) : Scree lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { isLoggedIn = serverManager.isRegistered() && serverManager.authenticationRepository() - .getSessionState() == SessionState.CONNECTED + .getSessionState() == SessionState.CONNECTED invalidate() while (isLoggedIn != true) { delay(1000) isLoggedIn = serverManager.isRegistered() && serverManager.authenticationRepository() - .getSessionState() == SessionState.CONNECTED + .getSessionState() == SessionState.CONNECTED } if (isLoggedIn == true) { screenManager.pop() diff --git a/app/src/main/java/io/homeassistant/companion/android/vehicle/MainVehicleScreen.kt b/app/src/main/java/io/homeassistant/companion/android/vehicle/MainVehicleScreen.kt index 760f7910e8e..9ca84ca1289 100644 --- a/app/src/main/java/io/homeassistant/companion/android/vehicle/MainVehicleScreen.kt +++ b/app/src/main/java/io/homeassistant/companion/android/vehicle/MainVehicleScreen.kt @@ -12,6 +12,7 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import io.homeassistant.companion.android.BuildConfig +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.authentication.SessionState import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.integration.domain @@ -30,7 +31,6 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.launch -import io.homeassistant.companion.android.common.R as commonR @RequiresApi(Build.VERSION_CODES.O) class MainVehicleScreen( @@ -63,13 +63,13 @@ class MainVehicleScreen( favoritesList = prefsRepository.getAutoFavorites() isLoggedIn = serverManager.isRegistered() && serverManager.authenticationRepository() - .getSessionState() == SessionState.CONNECTED + .getSessionState() == SessionState.CONNECTED invalidate() while (isLoggedIn != true) { delay(1000) isLoggedIn = serverManager.isRegistered() && serverManager.authenticationRepository() - .getSessionState() == SessionState.CONNECTED + .getSessionState() == SessionState.CONNECTED invalidate() } serverId.collect { server -> diff --git a/app/src/main/java/io/homeassistant/companion/android/vehicle/MapVehicleScreen.kt b/app/src/main/java/io/homeassistant/companion/android/vehicle/MapVehicleScreen.kt index f5f459bea10..c3170df2d08 100644 --- a/app/src/main/java/io/homeassistant/companion/android/vehicle/MapVehicleScreen.kt +++ b/app/src/main/java/io/homeassistant/companion/android/vehicle/MapVehicleScreen.kt @@ -22,6 +22,7 @@ import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.utils.sizeDp import com.mikepenz.iconics.utils.toAndroidIconCompat import io.homeassistant.companion.android.common.R +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.integration.EntityExt import io.homeassistant.companion.android.common.data.integration.IntegrationRepository @@ -32,7 +33,6 @@ import io.homeassistant.companion.android.common.data.integration.getIcon import io.homeassistant.companion.android.common.data.integration.isActive import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.launch -import io.homeassistant.companion.android.common.R as commonR @RequiresApi(Build.VERSION_CODES.O) class MapVehicleScreen( diff --git a/app/src/main/java/io/homeassistant/companion/android/websocket/WebsocketManager.kt b/app/src/main/java/io/homeassistant/companion/android/websocket/WebsocketManager.kt index a8d9008ea9a..a6951d97b33 100644 --- a/app/src/main/java/io/homeassistant/companion/android/websocket/WebsocketManager.kt +++ b/app/src/main/java/io/homeassistant/companion/android/websocket/WebsocketManager.kt @@ -24,14 +24,16 @@ import dagger.hilt.components.SingletonComponent import io.homeassistant.companion.android.BuildConfig import io.homeassistant.companion.android.common.R import io.homeassistant.companion.android.common.data.servers.ServerManager -import io.homeassistant.companion.android.common.util.websocketChannel -import io.homeassistant.companion.android.common.util.websocketIssuesChannel +import io.homeassistant.companion.android.common.util.CHANNEL_WEBSOCKET +import io.homeassistant.companion.android.common.util.CHANNEL_WEBSOCKET_ISSUES import io.homeassistant.companion.android.database.settings.SettingsDao import io.homeassistant.companion.android.database.settings.WebsocketSetting import io.homeassistant.companion.android.notifications.MessagingManager import io.homeassistant.companion.android.settings.SettingsActivity import io.homeassistant.companion.android.util.hasActiveConnection import io.homeassistant.companion.android.webview.WebViewActivity +import java.lang.IllegalStateException +import java.util.concurrent.TimeUnit import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -40,8 +42,6 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import java.lang.IllegalStateException -import java.util.concurrent.TimeUnit class WebsocketManager( appContext: Context, @@ -214,10 +214,10 @@ class WebsocketManager( private suspend fun createNotification(): Boolean { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { var notificationChannel = - notificationManager.getNotificationChannel(websocketChannel) + notificationManager.getNotificationChannel(CHANNEL_WEBSOCKET) if (notificationChannel == null) { notificationChannel = NotificationChannel( - websocketChannel, + CHANNEL_WEBSOCKET, applicationContext.getString(R.string.websocket_setting_name), NotificationManager.IMPORTANCE_LOW ) @@ -247,13 +247,13 @@ class WebsocketManager( settingIntent, PendingIntent.FLAG_IMMUTABLE ) - val notification = NotificationCompat.Builder(applicationContext, websocketChannel) + val notification = NotificationCompat.Builder(applicationContext, CHANNEL_WEBSOCKET) .setSmallIcon(R.drawable.ic_stat_ic_notification) .setContentTitle(applicationContext.getString(R.string.websocket_listening)) .setContentIntent(pendingIntent) .setPriority(NotificationCompat.PRIORITY_LOW) .setOngoing(true) - .setGroup(websocketChannel) + .setGroup(CHANNEL_WEBSOCKET) .addAction( io.homeassistant.companion.android.R.drawable.ic_websocket, applicationContext.getString(R.string.settings), @@ -268,16 +268,16 @@ class WebsocketManager( Log.e(TAG, "Unable to setForeground due to restrictions", e) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - if (notificationManager.getNotificationChannel(websocketIssuesChannel) == null) { + if (notificationManager.getNotificationChannel(CHANNEL_WEBSOCKET_ISSUES) == null) { val restrictedNotificationChannel = NotificationChannel( - websocketIssuesChannel, + CHANNEL_WEBSOCKET_ISSUES, applicationContext.getString(R.string.websocket_notification_issues), NotificationManager.IMPORTANCE_DEFAULT ) notificationManager.createNotificationChannel(restrictedNotificationChannel) } } - val restrictedNotification = NotificationCompat.Builder(applicationContext, websocketIssuesChannel) + val restrictedNotification = NotificationCompat.Builder(applicationContext, CHANNEL_WEBSOCKET_ISSUES) .setSmallIcon(R.drawable.ic_stat_ic_notification) .setContentTitle(applicationContext.getString(R.string.websocket_restricted_title)) .setContentText(applicationContext.getString(R.string.websocket_restricted_fix)) diff --git a/app/src/main/java/io/homeassistant/companion/android/webview/MatterThreadStep.kt b/app/src/main/java/io/homeassistant/companion/android/webview/MatterThreadStep.kt new file mode 100644 index 00000000000..808a66effcf --- /dev/null +++ b/app/src/main/java/io/homeassistant/companion/android/webview/MatterThreadStep.kt @@ -0,0 +1,14 @@ +package io.homeassistant.companion.android.webview + +enum class MatterThreadStep { + NOT_STARTED, + REQUESTED, + THREAD_EXPORT_TO_SERVER_MATTER, + THREAD_EXPORT_TO_SERVER_ONLY, + MATTER_IN_PROGRESS, + THREAD_SENT, + THREAD_NONE, + ERROR_MATTER, + ERROR_THREAD_LOCAL_NETWORK, + ERROR_THREAD_OTHER +} diff --git a/app/src/main/java/io/homeassistant/companion/android/webview/WebViewActivity.kt b/app/src/main/java/io/homeassistant/companion/android/webview/WebViewActivity.kt index 0928a40891a..86022d13ec0 100644 --- a/app/src/main/java/io/homeassistant/companion/android/webview/WebViewActivity.kt +++ b/app/src/main/java/io/homeassistant/companion/android/webview/WebViewActivity.kt @@ -72,12 +72,12 @@ import androidx.media3.ui.PlayerView import androidx.webkit.WebViewCompat import androidx.webkit.WebViewFeature import dagger.hilt.android.AndroidEntryPoint -import eightbitlab.com.blurview.RenderScriptBlur import io.homeassistant.companion.android.BaseActivity import io.homeassistant.companion.android.BuildConfig import io.homeassistant.companion.android.R import io.homeassistant.companion.android.assist.AssistActivity import io.homeassistant.companion.android.authenticator.Authenticator +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.HomeAssistantApis import io.homeassistant.companion.android.common.data.keychain.KeyChainRepository import io.homeassistant.companion.android.common.data.servers.ServerManager @@ -87,7 +87,6 @@ import io.homeassistant.companion.android.database.authentication.Authentication import io.homeassistant.companion.android.databinding.ActivityWebviewBinding import io.homeassistant.companion.android.databinding.DialogAuthenticationBinding import io.homeassistant.companion.android.launch.LaunchActivity -import io.homeassistant.companion.android.matter.MatterFrontendCommissioningStatus import io.homeassistant.companion.android.nfc.WriteNfcTag import io.homeassistant.companion.android.sensors.SensorReceiver import io.homeassistant.companion.android.sensors.SensorWorker @@ -103,6 +102,9 @@ import io.homeassistant.companion.android.util.TLSWebViewClient import io.homeassistant.companion.android.util.isStarted import io.homeassistant.companion.android.websocket.WebsocketManager import io.homeassistant.companion.android.webview.WebView.ErrorType +import java.util.concurrent.Executors +import javax.inject.Inject +import javax.inject.Named import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope @@ -116,10 +118,6 @@ import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.OkHttpClient import org.chromium.net.CronetEngine import org.json.JSONObject -import java.util.concurrent.Executors -import javax.inject.Inject -import javax.inject.Named -import io.homeassistant.companion.android.common.R as commonR @OptIn(androidx.media3.common.util.UnstableApi::class) @AndroidEntryPoint @@ -173,7 +171,7 @@ class WebViewActivity : BaseActivity(), io.homeassistant.companion.android.webvi mFilePathCallback = null } private val commissionMatterDevice = registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { result -> - presenter.onMatterCommissioningIntentResult(this, result) + presenter.onMatterThreadIntentResult(this, result) } @Inject @@ -264,9 +262,7 @@ class WebViewActivity : BaseActivity(), io.homeassistant.companion.android.webvi setStatusBarAndNavigationBarColor(colorLaunchScreenBackground, colorLaunchScreenBackground) binding.blurView.setupWith(binding.root) - .setBlurAlgorithm(RenderScriptBlur(this)) .setBlurRadius(8f) - .setHasFixedTransformationMatrix(false) exoPlayerView = binding.exoplayerView exoPlayerView.visibility = View.GONE @@ -497,9 +493,9 @@ class WebViewActivity : BaseActivity(), io.homeassistant.companion.android.webvi if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q || ActivityCompat.checkSelfPermission( - context, - android.Manifest.permission.WRITE_EXTERNAL_STORAGE - ) == PackageManager.PERMISSION_GRANTED + context, + android.Manifest.permission.WRITE_EXTERNAL_STORAGE + ) == PackageManager.PERMISSION_GRANTED ) { downloadFile(url, contentDisposition, mimetype) } else { @@ -637,18 +633,45 @@ class WebViewActivity : BaseActivity(), io.homeassistant.companion.android.webvi lifecycleScope.launch { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { - presenter.getMatterCommissioningStatusFlow().collect { - Log.d(TAG, "Matter commissioning status changed to $it") + presenter.getMatterThreadStepFlow().collect { + Log.d(TAG, "Matter/Thread step changed to $it") when (it) { - MatterFrontendCommissioningStatus.THREAD_EXPORT_TO_SERVER, - MatterFrontendCommissioningStatus.IN_PROGRESS -> { - presenter.getMatterCommissioningIntent()?.let { intentSender -> + MatterThreadStep.THREAD_EXPORT_TO_SERVER_MATTER, + MatterThreadStep.THREAD_EXPORT_TO_SERVER_ONLY, + MatterThreadStep.MATTER_IN_PROGRESS -> { + presenter.getMatterThreadIntent()?.let { intentSender -> commissionMatterDevice.launch(IntentSenderRequest.Builder(intentSender).build()) } } - MatterFrontendCommissioningStatus.ERROR -> { + MatterThreadStep.THREAD_NONE -> { + alertDialog?.cancel() + AlertDialog.Builder(this@WebViewActivity) + .setMessage(commonR.string.thread_export_none) + .setPositiveButton(commonR.string.ok, null) + .show() + presenter.finishMatterThreadFlow() + } + MatterThreadStep.THREAD_SENT -> { + Toast.makeText(this@WebViewActivity, commonR.string.thread_export_success, Toast.LENGTH_SHORT).show() + alertDialog?.cancel() + presenter.finishMatterThreadFlow() + } + MatterThreadStep.ERROR_MATTER -> { Toast.makeText(this@WebViewActivity, commonR.string.matter_commissioning_unavailable, Toast.LENGTH_SHORT).show() - presenter.confirmMatterCommissioningError() + presenter.finishMatterThreadFlow() + } + MatterThreadStep.ERROR_THREAD_LOCAL_NETWORK -> { + alertDialog?.cancel() + AlertDialog.Builder(this@WebViewActivity) + .setMessage(commonR.string.thread_export_not_connected) + .setPositiveButton(commonR.string.ok, null) + .show() + presenter.finishMatterThreadFlow() + } + MatterThreadStep.ERROR_THREAD_OTHER -> { + Toast.makeText(this@WebViewActivity, commonR.string.thread_export_unavailable, Toast.LENGTH_SHORT).show() + alertDialog?.cancel() + presenter.finishMatterThreadFlow() } else -> { } // Do nothing } @@ -708,6 +731,7 @@ class WebViewActivity : BaseActivity(), io.homeassistant.companion.android.webvi val pm: PackageManager = context.packageManager val hasNfc = pm.hasSystemFeature(PackageManager.FEATURE_NFC) val canCommissionMatter = presenter.appCanCommissionMatterDevice() + val canExportThread = presenter.appCanExportThreadCredentials() webView.externalBus( id = JSONObject(message).get("id"), type = "result", @@ -718,6 +742,7 @@ class WebViewActivity : BaseActivity(), io.homeassistant.companion.android.webvi "canWriteTag" to hasNfc, "hasExoPlayer" to true, "canCommissionMatter" to canCommissionMatter, + "canImportThreadCredentials" to canExportThread, "hasAssist" to true ) ) @@ -761,6 +786,14 @@ class WebViewActivity : BaseActivity(), io.homeassistant.companion.android.webvi ) ) "matter/commission" -> presenter.startCommissioningMatterDevice(this@WebViewActivity) + "thread/import_credentials" -> { + presenter.exportThreadCredentials(this@WebViewActivity) + + alertDialog = AlertDialog.Builder(this@WebViewActivity) + .setMessage(commonR.string.thread_debug_active) + .create() + alertDialog?.show() + } "exoplayer/play_hls" -> exoPlayHls(json) "exoplayer/stop" -> exoStopHls() "exoplayer/resize" -> exoResizeHls(json) diff --git a/app/src/main/java/io/homeassistant/companion/android/webview/WebViewPresenter.kt b/app/src/main/java/io/homeassistant/companion/android/webview/WebViewPresenter.kt index 313ce866bfa..21baaedce40 100644 --- a/app/src/main/java/io/homeassistant/companion/android/webview/WebViewPresenter.kt +++ b/app/src/main/java/io/homeassistant/companion/android/webview/WebViewPresenter.kt @@ -3,7 +3,6 @@ package io.homeassistant.companion.android.webview import android.content.Context import android.content.IntentSender import androidx.activity.result.ActivityResult -import io.homeassistant.companion.android.matter.MatterFrontendCommissioningStatus import kotlinx.coroutines.flow.Flow interface WebViewPresenter { @@ -54,8 +53,12 @@ interface WebViewPresenter { fun appCanCommissionMatterDevice(): Boolean fun startCommissioningMatterDevice(context: Context) - fun getMatterCommissioningStatusFlow(): Flow - fun getMatterCommissioningIntent(): IntentSender? - fun onMatterCommissioningIntentResult(context: Context, result: ActivityResult) - fun confirmMatterCommissioningError() + + /** @return `true` if the app can send this device's preferred Thread credential to the server */ + fun appCanExportThreadCredentials(): Boolean + fun exportThreadCredentials(context: Context) + fun getMatterThreadStepFlow(): Flow + fun getMatterThreadIntent(): IntentSender? + fun onMatterThreadIntentResult(context: Context, result: ActivityResult) + fun finishMatterThreadFlow() } diff --git a/app/src/main/java/io/homeassistant/companion/android/webview/WebViewPresenterImpl.kt b/app/src/main/java/io/homeassistant/companion/android/webview/WebViewPresenterImpl.kt index 4a490299087..20eb6212bd5 100644 --- a/app/src/main/java/io/homeassistant/companion/android/webview/WebViewPresenterImpl.kt +++ b/app/src/main/java/io/homeassistant/companion/android/webview/WebViewPresenterImpl.kt @@ -8,15 +8,22 @@ import android.net.Uri import android.util.Log import androidx.activity.result.ActivityResult import dagger.hilt.android.qualifiers.ActivityContext +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.authentication.SessionState import io.homeassistant.companion.android.common.data.prefs.PrefsRepository import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.common.util.DisabledLocationHandler -import io.homeassistant.companion.android.matter.MatterFrontendCommissioningStatus import io.homeassistant.companion.android.matter.MatterManager import io.homeassistant.companion.android.thread.ThreadManager import io.homeassistant.companion.android.util.UrlUtil import io.homeassistant.companion.android.util.UrlUtil.baseIsEqual +import java.net.SocketTimeoutException +import java.net.URL +import java.util.regex.Matcher +import java.util.regex.Pattern +import javax.inject.Inject +import javax.net.ssl.SSLException +import javax.net.ssl.SSLHandshakeException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -28,14 +35,6 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext -import java.net.SocketTimeoutException -import java.net.URL -import java.util.regex.Matcher -import java.util.regex.Pattern -import javax.inject.Inject -import javax.net.ssl.SSLException -import javax.net.ssl.SSLHandshakeException -import io.homeassistant.companion.android.common.R as commonR class WebViewPresenterImpl @Inject constructor( @ActivityContext context: Context, @@ -58,9 +57,9 @@ class WebViewPresenterImpl @Inject constructor( private var url: URL? = null private var urlForServer: Int? = null - private val _matterCommissioningStatus = MutableStateFlow(MatterFrontendCommissioningStatus.NOT_STARTED) + private val mutableMatterThreadStep = MutableStateFlow(MatterThreadStep.NOT_STARTED) - private var matterCommissioningIntentSender: IntentSender? = null + private var matterThreadIntentSender: IntentSender? = null init { updateActiveServer() @@ -343,27 +342,14 @@ class WebViewPresenterImpl @Inject constructor( override fun appCanCommissionMatterDevice(): Boolean = matterUseCase.appSupportsCommissioning() override fun startCommissioningMatterDevice(context: Context) { - if (_matterCommissioningStatus.value != MatterFrontendCommissioningStatus.REQUESTED) { - _matterCommissioningStatus.tryEmit(MatterFrontendCommissioningStatus.REQUESTED) + if (mutableMatterThreadStep.value != MatterThreadStep.REQUESTED) { + mutableMatterThreadStep.tryEmit(MatterThreadStep.REQUESTED) - mainScope.launch { - val deviceThreadIntent = try { - when (val result = threadUseCase.syncPreferredDataset(context, serverId, CoroutineScope(coroutineContext + SupervisorJob()))) { - is ThreadManager.SyncResult.OnlyOnDevice -> result.exportIntent - is ThreadManager.SyncResult.AllHaveCredentials -> result.exportIntent - else -> null - } - } catch (e: Exception) { - Log.w(TAG, "Unable to sync preferred Thread dataset, continuing", e) - null - } - if (deviceThreadIntent != null) { - matterCommissioningIntentSender = deviceThreadIntent - _matterCommissioningStatus.tryEmit(MatterFrontendCommissioningStatus.THREAD_EXPORT_TO_SERVER) - } else { - startMatterCommissioningFlow(context) - } - } + // The app used to sync Thread credentials here until commit 26a472a, but it was + // (temporarily?) removed due to slowing down the Matter commissioning flow for the user + // and limited usefulness of the result (because of API limitations) + + startMatterCommissioningFlow(context) } // else already waiting for a result, don't send another request } @@ -372,33 +358,79 @@ class WebViewPresenterImpl @Inject constructor( context, { intentSender -> Log.d(TAG, "Matter commissioning is ready") - matterCommissioningIntentSender = intentSender - _matterCommissioningStatus.tryEmit(MatterFrontendCommissioningStatus.IN_PROGRESS) + matterThreadIntentSender = intentSender + mutableMatterThreadStep.tryEmit(MatterThreadStep.MATTER_IN_PROGRESS) }, { e -> Log.e(TAG, "Matter commissioning couldn't be prepared", e) - _matterCommissioningStatus.tryEmit(MatterFrontendCommissioningStatus.ERROR) + mutableMatterThreadStep.tryEmit(MatterThreadStep.ERROR_MATTER) } ) } - override fun getMatterCommissioningStatusFlow(): Flow = - _matterCommissioningStatus.asStateFlow() + override fun appCanExportThreadCredentials(): Boolean = threadUseCase.appSupportsThread() + + override fun exportThreadCredentials(context: Context) { + if (mutableMatterThreadStep.value != MatterThreadStep.REQUESTED) { + mutableMatterThreadStep.tryEmit(MatterThreadStep.REQUESTED) + + mainScope.launch { + try { + val result = threadUseCase.syncPreferredDataset(context, serverId, true, CoroutineScope(coroutineContext + SupervisorJob())) + Log.d(TAG, "Export preferred Thread dataset returned $result") + + when (result) { + is ThreadManager.SyncResult.OnlyOnDevice -> { + matterThreadIntentSender = result.exportIntent + mutableMatterThreadStep.tryEmit(MatterThreadStep.THREAD_EXPORT_TO_SERVER_ONLY) + } + is ThreadManager.SyncResult.NoneHaveCredentials, + is ThreadManager.SyncResult.OnlyOnServer -> { + mutableMatterThreadStep.tryEmit(MatterThreadStep.THREAD_NONE) + } + is ThreadManager.SyncResult.NotConnected -> { + mutableMatterThreadStep.tryEmit(MatterThreadStep.ERROR_THREAD_LOCAL_NETWORK) + } + else -> { + mutableMatterThreadStep.tryEmit(MatterThreadStep.ERROR_THREAD_OTHER) + } + } + } catch (e: Exception) { + Log.w(TAG, "Unable to export preferred Thread dataset", e) + mutableMatterThreadStep.tryEmit(MatterThreadStep.ERROR_THREAD_OTHER) + } + } + } // else already waiting for a result, don't send another request + } + + override fun getMatterThreadStepFlow(): Flow = + mutableMatterThreadStep.asStateFlow() - override fun getMatterCommissioningIntent(): IntentSender? { - val intent = matterCommissioningIntentSender - matterCommissioningIntentSender = null + override fun getMatterThreadIntent(): IntentSender? { + val intent = matterThreadIntentSender + matterThreadIntentSender = null return intent } - override fun onMatterCommissioningIntentResult(context: Context, result: ActivityResult) { - when (_matterCommissioningStatus.value) { - MatterFrontendCommissioningStatus.THREAD_EXPORT_TO_SERVER -> { + override fun onMatterThreadIntentResult(context: Context, result: ActivityResult) { + when (mutableMatterThreadStep.value) { + MatterThreadStep.THREAD_EXPORT_TO_SERVER_MATTER -> { mainScope.launch { threadUseCase.sendThreadDatasetExportResult(result, serverId) startMatterCommissioningFlow(context) } } + MatterThreadStep.THREAD_EXPORT_TO_SERVER_ONLY -> { + mainScope.launch { + val sent = threadUseCase.sendThreadDatasetExportResult(result, serverId) + Log.d(TAG, "Thread ${if (!sent.isNullOrBlank()) "sent credential for $sent" else "did not send credential"}") + if (sent.isNullOrBlank()) { + mutableMatterThreadStep.tryEmit(MatterThreadStep.THREAD_NONE) + } else { + mutableMatterThreadStep.tryEmit(MatterThreadStep.THREAD_SENT) + } + } + } else -> { // Any errors will have been shown in the UI provided by Play Services if (result.resultCode == Activity.RESULT_OK) { @@ -410,7 +442,7 @@ class WebViewPresenterImpl @Inject constructor( } } - override fun confirmMatterCommissioningError() { - _matterCommissioningStatus.tryEmit(MatterFrontendCommissioningStatus.NOT_STARTED) + override fun finishMatterThreadFlow() { + mutableMatterThreadStep.tryEmit(MatterThreadStep.NOT_STARTED) } } diff --git a/app/src/main/java/io/homeassistant/companion/android/widgets/BaseWidgetConfigureActivity.kt b/app/src/main/java/io/homeassistant/companion/android/widgets/BaseWidgetConfigureActivity.kt index 318f9c0e353..e232ffd653f 100644 --- a/app/src/main/java/io/homeassistant/companion/android/widgets/BaseWidgetConfigureActivity.kt +++ b/app/src/main/java/io/homeassistant/companion/android/widgets/BaseWidgetConfigureActivity.kt @@ -53,7 +53,9 @@ abstract class BaseWidgetConfigureActivity : BaseActivity() { val newId = serverManager.defaultServers.getOrNull(position)?.id val isDifferent = selectedServerId != newId selectedServerId = newId - if (isDifferent && newId != null) { onServerSelected(newId) } + if (isDifferent && newId != null) { + onServerSelected(newId) + } } override fun onNothingSelected(parent: AdapterView<*>?) { diff --git a/app/src/main/java/io/homeassistant/companion/android/widgets/BaseWidgetProvider.kt b/app/src/main/java/io/homeassistant/companion/android/widgets/BaseWidgetProvider.kt index e193b261ad9..662ca074dc9 100644 --- a/app/src/main/java/io/homeassistant/companion/android/widgets/BaseWidgetProvider.kt +++ b/app/src/main/java/io/homeassistant/companion/android/widgets/BaseWidgetProvider.kt @@ -11,13 +11,13 @@ import android.util.Log import android.widget.RemoteViews import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.servers.ServerManager +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.cancel import kotlinx.coroutines.isActive import kotlinx.coroutines.launch -import javax.inject.Inject /** * A widget provider class for widgets that update based on entity state changes. diff --git a/app/src/main/java/io/homeassistant/companion/android/widgets/assist/AssistShortcutActivity.kt b/app/src/main/java/io/homeassistant/companion/android/widgets/assist/AssistShortcutActivity.kt index 33df1e01c08..e7618fc6a40 100644 --- a/app/src/main/java/io/homeassistant/companion/android/widgets/assist/AssistShortcutActivity.kt +++ b/app/src/main/java/io/homeassistant/companion/android/widgets/assist/AssistShortcutActivity.kt @@ -8,11 +8,11 @@ import androidx.activity.viewModels import androidx.core.content.pm.ShortcutInfoCompat import androidx.core.content.pm.ShortcutManagerCompat import androidx.core.graphics.drawable.IconCompat -import com.google.accompanist.themeadapter.material.MdcTheme import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.BaseActivity import io.homeassistant.companion.android.R import io.homeassistant.companion.android.assist.AssistActivity +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme import java.util.UUID @AndroidEntryPoint @@ -28,7 +28,7 @@ class AssistShortcutActivity : BaseActivity() { super.onCreate(savedInstanceState) setContent { - MdcTheme { + HomeAssistantAppTheme { AssistShortcutView( selectedServerId = viewModel.serverId, servers = viewModel.servers, diff --git a/app/src/main/java/io/homeassistant/companion/android/widgets/assist/AssistShortcutView.kt b/app/src/main/java/io/homeassistant/companion/android/widgets/assist/AssistShortcutView.kt index 34eb7d0b1e8..bf7e85f72cc 100644 --- a/app/src/main/java/io/homeassistant/companion/android/widgets/assist/AssistShortcutView.kt +++ b/app/src/main/java/io/homeassistant/companion/android/widgets/assist/AssistShortcutView.kt @@ -26,12 +26,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.assist.AssistViewModelBase import io.homeassistant.companion.android.common.data.websocket.impl.entities.AssistPipelineListResponse import io.homeassistant.companion.android.database.server.Server import io.homeassistant.companion.android.util.compose.ExposedDropdownMenu import io.homeassistant.companion.android.util.compose.ServerExposedDropdownMenu -import io.homeassistant.companion.android.common.R as commonR @Composable fun AssistShortcutView( diff --git a/app/src/main/java/io/homeassistant/companion/android/widgets/assist/AssistShortcutViewModel.kt b/app/src/main/java/io/homeassistant/companion/android/widgets/assist/AssistShortcutViewModel.kt index 8039f392fe0..05331b42012 100644 --- a/app/src/main/java/io/homeassistant/companion/android/widgets/assist/AssistShortcutViewModel.kt +++ b/app/src/main/java/io/homeassistant/companion/android/widgets/assist/AssistShortcutViewModel.kt @@ -9,8 +9,8 @@ import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.common.data.websocket.impl.entities.AssistPipelineListResponse -import kotlinx.coroutines.launch import javax.inject.Inject +import kotlinx.coroutines.launch @HiltViewModel class AssistShortcutViewModel @Inject constructor( diff --git a/app/src/main/java/io/homeassistant/companion/android/widgets/button/ButtonWidget.kt b/app/src/main/java/io/homeassistant/companion/android/widgets/button/ButtonWidget.kt index 4d40cf7b4fa..06f95973aaf 100644 --- a/app/src/main/java/io/homeassistant/companion/android/widgets/button/ButtonWidget.kt +++ b/app/src/main/java/io/homeassistant/companion/android/widgets/button/ButtonWidget.kt @@ -29,6 +29,7 @@ import com.mikepenz.iconics.utils.padding import com.mikepenz.iconics.utils.size import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.R +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.database.widget.ButtonWidgetDao import io.homeassistant.companion.android.database.widget.ButtonWidgetEntity @@ -36,13 +37,12 @@ import io.homeassistant.companion.android.database.widget.WidgetBackgroundType import io.homeassistant.companion.android.util.getAttribute import io.homeassistant.companion.android.util.icondialog.getIconByMdiName import io.homeassistant.companion.android.widgets.common.WidgetAuthenticationActivity +import java.util.regex.Pattern +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch -import java.util.regex.Pattern -import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR @AndroidEntryPoint class ButtonWidget : AppWidgetProvider() { diff --git a/app/src/main/java/io/homeassistant/companion/android/widgets/button/ButtonWidgetConfigureActivity.kt b/app/src/main/java/io/homeassistant/companion/android/widgets/button/ButtonWidgetConfigureActivity.kt index 3de9da9ed61..690c8052d26 100644 --- a/app/src/main/java/io/homeassistant/companion/android/widgets/button/ButtonWidgetConfigureActivity.kt +++ b/app/src/main/java/io/homeassistant/companion/android/widgets/button/ButtonWidgetConfigureActivity.kt @@ -34,6 +34,7 @@ import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.typeface.IIcon import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial import dagger.hilt.android.AndroidEntryPoint +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.integration.Service import io.homeassistant.companion.android.database.widget.ButtonWidgetDao @@ -48,9 +49,8 @@ import io.homeassistant.companion.android.widgets.BaseWidgetConfigureActivity import io.homeassistant.companion.android.widgets.common.ServiceFieldBinder import io.homeassistant.companion.android.widgets.common.SingleItemArrayAdapter import io.homeassistant.companion.android.widgets.common.WidgetDynamicFieldAdapter -import kotlinx.coroutines.launch import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR +import kotlinx.coroutines.launch @AndroidEntryPoint class ButtonWidgetConfigureActivity : BaseWidgetConfigureActivity() { diff --git a/app/src/main/java/io/homeassistant/companion/android/widgets/camera/CameraWidget.kt b/app/src/main/java/io/homeassistant/companion/android/widgets/camera/CameraWidget.kt index a0f0cc0eaaf..b67c5d24be5 100755 --- a/app/src/main/java/io/homeassistant/companion/android/widgets/camera/CameraWidget.kt +++ b/app/src/main/java/io/homeassistant/companion/android/widgets/camera/CameraWidget.kt @@ -21,11 +21,11 @@ import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.database.widget.CameraWidgetDao import io.homeassistant.companion.android.database.widget.CameraWidgetEntity import io.homeassistant.companion.android.util.hasActiveConnection +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch -import javax.inject.Inject @AndroidEntryPoint class CameraWidget : AppWidgetProvider() { diff --git a/app/src/main/java/io/homeassistant/companion/android/widgets/camera/CameraWidgetConfigureActivity.kt b/app/src/main/java/io/homeassistant/companion/android/widgets/camera/CameraWidgetConfigureActivity.kt index c518c63be47..a6f02fdecbb 100755 --- a/app/src/main/java/io/homeassistant/companion/android/widgets/camera/CameraWidgetConfigureActivity.kt +++ b/app/src/main/java/io/homeassistant/companion/android/widgets/camera/CameraWidgetConfigureActivity.kt @@ -15,6 +15,7 @@ import android.widget.Toast import androidx.core.content.getSystemService import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.integration.domain import io.homeassistant.companion.android.database.widget.CameraWidgetDao @@ -22,10 +23,9 @@ import io.homeassistant.companion.android.databinding.WidgetCameraConfigureBindi import io.homeassistant.companion.android.settings.widgets.ManageWidgetsViewModel import io.homeassistant.companion.android.widgets.BaseWidgetConfigureActivity import io.homeassistant.companion.android.widgets.common.SingleItemArrayAdapter +import javax.inject.Inject import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking -import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR @AndroidEntryPoint class CameraWidgetConfigureActivity : BaseWidgetConfigureActivity() { @@ -136,7 +136,7 @@ class CameraWidgetConfigureActivity : BaseWidgetConfigureActivity() { lifecycleScope.launch { try { val fetchedEntities = serverManager.integrationRepository(server.id).getEntities().orEmpty() - .filter { it.domain == "camera" } + .filter { it.domain == "camera" || it.domain == "image" } entities[server.id] = fetchedEntities if (server.id == selectedServerId) setAdapterEntities(server.id) } catch (e: Exception) { diff --git a/app/src/main/java/io/homeassistant/companion/android/widgets/entity/EntityWidget.kt b/app/src/main/java/io/homeassistant/companion/android/widgets/entity/EntityWidget.kt index 7d94668e9f6..49564bc47bb 100644 --- a/app/src/main/java/io/homeassistant/companion/android/widgets/entity/EntityWidget.kt +++ b/app/src/main/java/io/homeassistant/companion/android/widgets/entity/EntityWidget.kt @@ -18,6 +18,7 @@ import androidx.core.graphics.toColorInt import com.google.android.material.color.DynamicColors import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.R +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.integration.canSupportPrecision import io.homeassistant.companion.android.common.data.integration.friendlyState @@ -28,9 +29,8 @@ import io.homeassistant.companion.android.database.widget.WidgetBackgroundType import io.homeassistant.companion.android.database.widget.WidgetTapAction import io.homeassistant.companion.android.util.getAttribute import io.homeassistant.companion.android.widgets.BaseWidgetProvider -import kotlinx.coroutines.launch import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR +import kotlinx.coroutines.launch @AndroidEntryPoint class EntityWidget : BaseWidgetProvider() { diff --git a/app/src/main/java/io/homeassistant/companion/android/widgets/entity/EntityWidgetConfigureActivity.kt b/app/src/main/java/io/homeassistant/companion/android/widgets/entity/EntityWidgetConfigureActivity.kt index 139cd5c6cda..00bc6c2b025 100644 --- a/app/src/main/java/io/homeassistant/companion/android/widgets/entity/EntityWidgetConfigureActivity.kt +++ b/app/src/main/java/io/homeassistant/companion/android/widgets/entity/EntityWidgetConfigureActivity.kt @@ -22,6 +22,7 @@ import androidx.core.view.isVisible import androidx.lifecycle.lifecycleScope import com.google.android.material.color.DynamicColors import dagger.hilt.android.AndroidEntryPoint +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.integration.EntityExt import io.homeassistant.companion.android.common.data.integration.domain @@ -34,10 +35,9 @@ import io.homeassistant.companion.android.util.getHexForColor import io.homeassistant.companion.android.widgets.BaseWidgetConfigureActivity import io.homeassistant.companion.android.widgets.BaseWidgetProvider import io.homeassistant.companion.android.widgets.common.SingleItemArrayAdapter +import javax.inject.Inject import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking -import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR @AndroidEntryPoint class EntityWidgetConfigureActivity : BaseWidgetConfigureActivity() { diff --git a/app/src/main/java/io/homeassistant/companion/android/widgets/mediaplayer/MediaPlayerControlsWidget.kt b/app/src/main/java/io/homeassistant/companion/android/widgets/mediaplayer/MediaPlayerControlsWidget.kt index 7c9171d57cc..719f8569678 100644 --- a/app/src/main/java/io/homeassistant/companion/android/widgets/mediaplayer/MediaPlayerControlsWidget.kt +++ b/app/src/main/java/io/homeassistant/companion/android/widgets/mediaplayer/MediaPlayerControlsWidget.kt @@ -20,17 +20,17 @@ import com.squareup.picasso.Picasso import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.BuildConfig import io.homeassistant.companion.android.R +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.database.widget.MediaPlayerControlsWidgetDao import io.homeassistant.companion.android.database.widget.MediaPlayerControlsWidgetEntity import io.homeassistant.companion.android.database.widget.WidgetBackgroundType import io.homeassistant.companion.android.util.hasActiveConnection import io.homeassistant.companion.android.widgets.BaseWidgetProvider -import kotlinx.coroutines.launch import java.util.LinkedList import javax.inject.Inject import kotlin.collections.HashMap -import io.homeassistant.companion.android.common.R as commonR +import kotlinx.coroutines.launch @AndroidEntryPoint class MediaPlayerControlsWidget : BaseWidgetProvider() { diff --git a/app/src/main/java/io/homeassistant/companion/android/widgets/mediaplayer/MediaPlayerControlsWidgetConfigureActivity.kt b/app/src/main/java/io/homeassistant/companion/android/widgets/mediaplayer/MediaPlayerControlsWidgetConfigureActivity.kt index baa318596b1..240afe276f6 100644 --- a/app/src/main/java/io/homeassistant/companion/android/widgets/mediaplayer/MediaPlayerControlsWidgetConfigureActivity.kt +++ b/app/src/main/java/io/homeassistant/companion/android/widgets/mediaplayer/MediaPlayerControlsWidgetConfigureActivity.kt @@ -17,6 +17,7 @@ import androidx.core.content.getSystemService import androidx.lifecycle.lifecycleScope import com.google.android.material.color.DynamicColors import dagger.hilt.android.AndroidEntryPoint +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.integration.domain import io.homeassistant.companion.android.database.widget.MediaPlayerControlsWidgetDao @@ -25,11 +26,10 @@ import io.homeassistant.companion.android.databinding.WidgetMediaControlsConfigu import io.homeassistant.companion.android.settings.widgets.ManageWidgetsViewModel import io.homeassistant.companion.android.widgets.BaseWidgetConfigureActivity import io.homeassistant.companion.android.widgets.common.SingleItemArrayAdapter -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking import java.util.LinkedList import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking @AndroidEntryPoint class MediaPlayerControlsWidgetConfigureActivity : BaseWidgetConfigureActivity() { diff --git a/app/src/main/java/io/homeassistant/companion/android/widgets/template/TemplateWidget.kt b/app/src/main/java/io/homeassistant/companion/android/widgets/template/TemplateWidget.kt index ad68573692d..12881a57115 100644 --- a/app/src/main/java/io/homeassistant/companion/android/widgets/template/TemplateWidget.kt +++ b/app/src/main/java/io/homeassistant/companion/android/widgets/template/TemplateWidget.kt @@ -20,19 +20,19 @@ import androidx.core.text.HtmlCompat import com.google.android.material.color.DynamicColors import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.R +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.database.widget.TemplateWidgetDao import io.homeassistant.companion.android.database.widget.TemplateWidgetEntity import io.homeassistant.companion.android.database.widget.WidgetBackgroundType import io.homeassistant.companion.android.util.getAttribute +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.cancel import kotlinx.coroutines.isActive import kotlinx.coroutines.launch -import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR @AndroidEntryPoint class TemplateWidget : AppWidgetProvider() { diff --git a/app/src/main/java/io/homeassistant/companion/android/widgets/template/TemplateWidgetConfigureActivity.kt b/app/src/main/java/io/homeassistant/companion/android/widgets/template/TemplateWidgetConfigureActivity.kt index 38b5fc2078a..f7b17a518c0 100644 --- a/app/src/main/java/io/homeassistant/companion/android/widgets/template/TemplateWidgetConfigureActivity.kt +++ b/app/src/main/java/io/homeassistant/companion/android/widgets/template/TemplateWidgetConfigureActivity.kt @@ -21,17 +21,17 @@ import androidx.lifecycle.lifecycleScope import com.fasterxml.jackson.databind.JsonMappingException import com.google.android.material.color.DynamicColors import dagger.hilt.android.AndroidEntryPoint +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.database.widget.TemplateWidgetDao import io.homeassistant.companion.android.database.widget.WidgetBackgroundType import io.homeassistant.companion.android.databinding.WidgetTemplateConfigureBinding import io.homeassistant.companion.android.settings.widgets.ManageWidgetsViewModel import io.homeassistant.companion.android.util.getHexForColor import io.homeassistant.companion.android.widgets.BaseWidgetConfigureActivity +import javax.inject.Inject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR @AndroidEntryPoint class TemplateWidgetConfigureActivity : BaseWidgetConfigureActivity() { diff --git a/app/src/main/res/xml/button_widget_info.xml b/app/src/main/res/xml/button_widget_info.xml index d3e123708cb..238a7a447f9 100644 --- a/app/src/main/res/xml/button_widget_info.xml +++ b/app/src/main/res/xml/button_widget_info.xml @@ -7,6 +7,8 @@ android:minHeight="40dp" android:minResizeWidth="40dp" android:minResizeHeight="40dp" + android:targetCellWidth="2" + android:targetCellHeight="1" android:resizeMode="vertical|horizontal" android:updatePeriodMillis="86400000" android:widgetFeatures="reconfigurable" diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index e2b9e79a2bf..ba40a8af8de 100755 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,23 +1,21 @@ - - Update icon - Add hotspot state sensor - Updated default value for BLE transmitter Minor ID for upcoming HA core changes to iBeacon integration for better app detection + + Add instant updates for all Volume sensors + Add Network sensor for IPv6 addresses + Allow Climate entities to cycle through HVAC modes in device controls Bug fixes and dependency updates - - Update icon - Notifications: Add support for clear_notification command and mobile_app_notification_cleared event - Add support for TLS client certificate authentication - Updated default value for BLE transmitter Minor ID for upcoming HA core changes to iBeacon integration for better app detection - Bug fixes, design improvements and dependency updates + + Add instant updates for all Volume sensors + Add Network sensor for IPv6 addresses + Add option to update Camera and Template tiles when the tile is in view + Bug fixes, design tweaks and dependency updates - - Update icon - Add hotspot state sensor - Updated default value for BLE transmitter Minor ID for upcoming HA core changes to iBeacon integration for better app detection + + Add instant updates for all Volume sensors + Add Network sensor for IPv6 addresses Bug fixes and dependency updates \ No newline at end of file diff --git a/app/src/minimal/java/io/homeassistant/companion/android/launch/LaunchPresenterImpl.kt b/app/src/minimal/java/io/homeassistant/companion/android/launch/LaunchPresenterImpl.kt index bcfd9b1ca11..698729e5a5d 100644 --- a/app/src/minimal/java/io/homeassistant/companion/android/launch/LaunchPresenterImpl.kt +++ b/app/src/minimal/java/io/homeassistant/companion/android/launch/LaunchPresenterImpl.kt @@ -4,8 +4,8 @@ import android.util.Log import io.homeassistant.companion.android.BuildConfig import io.homeassistant.companion.android.common.data.integration.DeviceRegistration import io.homeassistant.companion.android.common.data.servers.ServerManager -import kotlinx.coroutines.launch import javax.inject.Inject +import kotlinx.coroutines.launch class LaunchPresenterImpl @Inject constructor( view: LaunchView, diff --git a/app/src/minimal/java/io/homeassistant/companion/android/matter/MatterManagerImpl.kt b/app/src/minimal/java/io/homeassistant/companion/android/matter/MatterManagerImpl.kt index ac7f60e8c7c..97a406d1919 100644 --- a/app/src/minimal/java/io/homeassistant/companion/android/matter/MatterManagerImpl.kt +++ b/app/src/minimal/java/io/homeassistant/companion/android/matter/MatterManagerImpl.kt @@ -28,5 +28,5 @@ class MatterManagerImpl @Inject constructor() : MatterManager { override suspend fun commissionDevice(code: String, serverId: Int): MatterCommissionResponse? = null - override suspend fun commissionOnNetworkDevice(pin: Long, serverId: Int): MatterCommissionResponse? = null + override suspend fun commissionOnNetworkDevice(pin: Long, ip: String, serverId: Int): MatterCommissionResponse? = null } diff --git a/app/src/minimal/java/io/homeassistant/companion/android/sensors/ActivitySensorManager.kt b/app/src/minimal/java/io/homeassistant/companion/android/sensors/ActivitySensorManager.kt index c6828e2c02e..66dd1b26a70 100644 --- a/app/src/minimal/java/io/homeassistant/companion/android/sensors/ActivitySensorManager.kt +++ b/app/src/minimal/java/io/homeassistant/companion/android/sensors/ActivitySensorManager.kt @@ -3,8 +3,8 @@ package io.homeassistant.companion.android.sensors import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import io.homeassistant.companion.android.common.sensors.SensorManager import io.homeassistant.companion.android.common.R as commonR +import io.homeassistant.companion.android.common.sensors.SensorManager class ActivitySensorManager : BroadcastReceiver(), SensorManager { diff --git a/app/src/minimal/java/io/homeassistant/companion/android/sensors/AndroidAutoSensorManager.kt b/app/src/minimal/java/io/homeassistant/companion/android/sensors/AndroidAutoSensorManager.kt index 7e9b64cfd47..0dd600c34a7 100644 --- a/app/src/minimal/java/io/homeassistant/companion/android/sensors/AndroidAutoSensorManager.kt +++ b/app/src/minimal/java/io/homeassistant/companion/android/sensors/AndroidAutoSensorManager.kt @@ -1,8 +1,8 @@ package io.homeassistant.companion.android.sensors import android.content.Context -import io.homeassistant.companion.android.common.sensors.SensorManager import io.homeassistant.companion.android.common.R as commonR +import io.homeassistant.companion.android.common.sensors.SensorManager class AndroidAutoSensorManager : SensorManager { diff --git a/app/src/minimal/java/io/homeassistant/companion/android/sensors/GeocodeSensorManager.kt b/app/src/minimal/java/io/homeassistant/companion/android/sensors/GeocodeSensorManager.kt index 8147f338e56..1373ce70429 100644 --- a/app/src/minimal/java/io/homeassistant/companion/android/sensors/GeocodeSensorManager.kt +++ b/app/src/minimal/java/io/homeassistant/companion/android/sensors/GeocodeSensorManager.kt @@ -1,8 +1,8 @@ package io.homeassistant.companion.android.sensors import android.content.Context -import io.homeassistant.companion.android.common.sensors.SensorManager import io.homeassistant.companion.android.common.R as commonR +import io.homeassistant.companion.android.common.sensors.SensorManager class GeocodeSensorManager : SensorManager { diff --git a/app/src/minimal/java/io/homeassistant/companion/android/sensors/LocationSensorManager.kt b/app/src/minimal/java/io/homeassistant/companion/android/sensors/LocationSensorManager.kt index b94a84e39ff..0b1f48337b9 100644 --- a/app/src/minimal/java/io/homeassistant/companion/android/sensors/LocationSensorManager.kt +++ b/app/src/minimal/java/io/homeassistant/companion/android/sensors/LocationSensorManager.kt @@ -3,8 +3,8 @@ package io.homeassistant.companion.android.sensors import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import io.homeassistant.companion.android.common.sensors.SensorManager import io.homeassistant.companion.android.common.R as commonR +import io.homeassistant.companion.android.common.sensors.SensorManager class LocationSensorManager : BroadcastReceiver(), SensorManager { diff --git a/app/src/minimal/java/io/homeassistant/companion/android/thread/ThreadManagerImpl.kt b/app/src/minimal/java/io/homeassistant/companion/android/thread/ThreadManagerImpl.kt index 875ea499bfe..25987179c60 100644 --- a/app/src/minimal/java/io/homeassistant/companion/android/thread/ThreadManagerImpl.kt +++ b/app/src/minimal/java/io/homeassistant/companion/android/thread/ThreadManagerImpl.kt @@ -4,8 +4,8 @@ import android.content.Context import android.content.IntentSender import androidx.activity.result.ActivityResult import io.homeassistant.companion.android.common.data.websocket.impl.entities.ThreadDatasetResponse -import kotlinx.coroutines.CoroutineScope import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope class ThreadManagerImpl @Inject constructor() : ThreadManager { @@ -19,6 +19,7 @@ class ThreadManagerImpl @Inject constructor() : ThreadManager { override suspend fun syncPreferredDataset( context: Context, serverId: Int, + exportOnly: Boolean, scope: CoroutineScope ): ThreadManager.SyncResult = ThreadManager.SyncResult.AppUnsupported diff --git a/automotive/build.gradle.kts b/automotive/build.gradle.kts index 11eb77cb54b..13c94eaef40 100644 --- a/automotive/build.gradle.kts +++ b/automotive/build.gradle.kts @@ -209,7 +209,6 @@ dependencies { implementation(libs.activity.compose) implementation(libs.navigation.compose) implementation(libs.accompanist.systemuicontroller) - implementation(libs.accompanist.themeadapter.material) implementation(libs.iconics.core) implementation(libs.iconics.compose) diff --git a/automotive/src/main/AndroidManifest.xml b/automotive/src/main/AndroidManifest.xml index 747b851bbd1..7d9e53f659b 100644 --- a/automotive/src/main/AndroidManifest.xml +++ b/automotive/src/main/AndroidManifest.xml @@ -44,6 +44,10 @@ + + + + diff --git a/common/src/main/java/io/homeassistant/companion/android/common/bluetooth/ble/IBeaconMonitor.kt b/common/src/main/java/io/homeassistant/companion/android/common/bluetooth/ble/IBeaconMonitor.kt index a557d11d295..28b06391d1a 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/bluetooth/ble/IBeaconMonitor.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/bluetooth/ble/IBeaconMonitor.kt @@ -3,9 +3,9 @@ package io.homeassistant.companion.android.common.bluetooth.ble import android.content.Context import io.homeassistant.companion.android.common.sensors.BluetoothSensorManager import io.homeassistant.companion.android.common.sensors.SensorUpdateReceiver -import org.altbeacon.beacon.Beacon import kotlin.math.abs import kotlin.math.round +import org.altbeacon.beacon.Beacon const val MAX_SKIPPED_UPDATED = 10 diff --git a/common/src/main/java/io/homeassistant/companion/android/common/bluetooth/ble/KalmanFilter.kt b/common/src/main/java/io/homeassistant/companion/android/common/bluetooth/ble/KalmanFilter.kt index 27b57b8713e..80a163b7790 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/bluetooth/ble/KalmanFilter.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/bluetooth/ble/KalmanFilter.kt @@ -1,7 +1,7 @@ package io.homeassistant.companion.android.common.bluetooth.ble -import org.altbeacon.beacon.service.RssiFilter import kotlin.math.pow +import org.altbeacon.beacon.service.RssiFilter class KalmanFilter : RssiFilter { companion object { diff --git a/common/src/main/java/io/homeassistant/companion/android/common/bluetooth/ble/MonitoringManager.kt b/common/src/main/java/io/homeassistant/companion/android/common/bluetooth/ble/MonitoringManager.kt index ce1fcfaa5bc..0127322227c 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/bluetooth/ble/MonitoringManager.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/bluetooth/ble/MonitoringManager.kt @@ -12,7 +12,7 @@ import io.homeassistant.companion.android.common.BuildConfig import io.homeassistant.companion.android.common.R import io.homeassistant.companion.android.common.sensors.SensorReceiverBase import io.homeassistant.companion.android.common.sensors.SensorUpdateReceiver -import io.homeassistant.companion.android.common.util.beaconMonitorChannel +import io.homeassistant.companion.android.common.util.CHANNEL_BEACON_MONITOR import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -76,11 +76,11 @@ class MonitoringManager { } } - val builder = NotificationCompat.Builder(context, beaconMonitorChannel) + val builder = NotificationCompat.Builder(context, CHANNEL_BEACON_MONITOR) builder.setSmallIcon(R.drawable.ic_stat_ic_notification) builder.setContentTitle(context.getString(R.string.beacon_scanning)) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val channel = NotificationChannel(beaconMonitorChannel, context.getString(R.string.beacon_scanning), NotificationManager.IMPORTANCE_LOW) + val channel = NotificationChannel(CHANNEL_BEACON_MONITOR, context.getString(R.string.beacon_scanning), NotificationManager.IMPORTANCE_LOW) val notifManager = context.getSystemService()!! notifManager.createNotificationChannel(channel) } diff --git a/common/src/main/java/io/homeassistant/companion/android/common/bluetooth/ble/TransmitterManager.kt b/common/src/main/java/io/homeassistant/companion/android/common/bluetooth/ble/TransmitterManager.kt index 1ccba16cc41..64630c0e75d 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/bluetooth/ble/TransmitterManager.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/bluetooth/ble/TransmitterManager.kt @@ -6,10 +6,10 @@ import android.bluetooth.le.AdvertiseSettings import android.content.Context import androidx.core.content.getSystemService import io.homeassistant.companion.android.common.sensors.BluetoothSensorManager +import java.util.UUID import org.altbeacon.beacon.Beacon import org.altbeacon.beacon.BeaconParser import org.altbeacon.beacon.BeaconTransmitter -import java.util.UUID object TransmitterManager { private lateinit var physicalTransmitter: BeaconTransmitter diff --git a/common/src/main/java/io/homeassistant/companion/android/common/data/DataModule.kt b/common/src/main/java/io/homeassistant/companion/android/common/data/DataModule.kt index f5f813892ca..2514b62a18b 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/data/DataModule.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/data/DataModule.kt @@ -27,11 +27,11 @@ import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.common.data.servers.ServerManagerImpl import io.homeassistant.companion.android.common.data.wifi.WifiHelper import io.homeassistant.companion.android.common.data.wifi.WifiHelperImpl -import kotlinx.coroutines.runBlocking -import okhttp3.OkHttpClient import java.util.UUID import javax.inject.Named import javax.inject.Singleton +import kotlinx.coroutines.runBlocking +import okhttp3.OkHttpClient @Module @InstallIn(SingletonComponent::class) diff --git a/common/src/main/java/io/homeassistant/companion/android/common/data/HomeAssistantApis.kt b/common/src/main/java/io/homeassistant/companion/android/common/data/HomeAssistantApis.kt index ac6df8e14b5..446a03c3f8f 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/data/HomeAssistantApis.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/data/HomeAssistantApis.kt @@ -10,12 +10,12 @@ import com.fasterxml.jackson.databind.PropertyNamingStrategies import com.fasterxml.jackson.module.kotlin.registerKotlinModule import dagger.hilt.android.qualifiers.ApplicationContext import io.homeassistant.companion.android.common.BuildConfig +import java.util.concurrent.TimeUnit +import javax.inject.Inject import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit import retrofit2.converter.jackson.JacksonConverterFactory -import java.util.concurrent.TimeUnit -import javax.inject.Inject class HomeAssistantApis @Inject constructor( private val tlsHelper: TLSHelper, diff --git a/common/src/main/java/io/homeassistant/companion/android/common/data/TLSHelper.kt b/common/src/main/java/io/homeassistant/companion/android/common/data/TLSHelper.kt index 34ff2588e84..3846dbca0c3 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/data/TLSHelper.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/data/TLSHelper.kt @@ -1,8 +1,6 @@ package io.homeassistant.companion.android.common.data import io.homeassistant.companion.android.common.data.keychain.KeyChainRepository -import kotlinx.coroutines.runBlocking -import okhttp3.OkHttpClient import java.net.Socket import java.security.KeyStore import java.security.Principal @@ -14,6 +12,8 @@ import javax.net.ssl.SSLContext import javax.net.ssl.TrustManagerFactory import javax.net.ssl.X509ExtendedKeyManager import javax.net.ssl.X509TrustManager +import kotlinx.coroutines.runBlocking +import okhttp3.OkHttpClient class TLSHelper @Inject constructor( @Named("keyChainRepository") private val keyChainRepository: KeyChainRepository, diff --git a/common/src/main/java/io/homeassistant/companion/android/common/data/authentication/impl/AuthenticationRepositoryImpl.kt b/common/src/main/java/io/homeassistant/companion/android/common/data/authentication/impl/AuthenticationRepositoryImpl.kt index fa0d301dbe6..0631c06097d 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/data/authentication/impl/AuthenticationRepositoryImpl.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/data/authentication/impl/AuthenticationRepositoryImpl.kt @@ -10,8 +10,8 @@ import io.homeassistant.companion.android.common.data.authentication.Authorizati import io.homeassistant.companion.android.common.data.authentication.SessionState import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.database.server.ServerSessionInfo -import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import javax.inject.Named +import okhttp3.HttpUrl.Companion.toHttpUrlOrNull class AuthenticationRepositoryImpl @AssistedInject constructor( private val authenticationService: AuthenticationService, diff --git a/common/src/main/java/io/homeassistant/companion/android/common/data/integration/Entity.kt b/common/src/main/java/io/homeassistant/companion/android/common/data/integration/Entity.kt index 42d4e70958a..f9a6d86ee14 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/data/integration/Entity.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/data/integration/Entity.kt @@ -8,6 +8,7 @@ import android.util.Log import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.typeface.IIcon import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.websocket.impl.entities.CompressedStateDiff import io.homeassistant.companion.android.common.data.websocket.impl.entities.EntityRegistryOptions import java.time.ZonedDateTime @@ -16,7 +17,6 @@ import java.time.format.DateTimeParseException import java.util.Calendar import java.util.Locale import kotlin.math.round -import io.homeassistant.companion.android.common.R as commonR data class Entity( val entityId: String, diff --git a/common/src/main/java/io/homeassistant/companion/android/common/data/integration/impl/IntegrationRepositoryImpl.kt b/common/src/main/java/io/homeassistant/companion/android/common/data/integration/impl/IntegrationRepositoryImpl.kt index 0e444884d41..2262564ffea 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/data/integration/impl/IntegrationRepositoryImpl.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/data/integration/impl/IntegrationRepositoryImpl.kt @@ -30,13 +30,13 @@ import io.homeassistant.companion.android.common.data.websocket.impl.entities.As import io.homeassistant.companion.android.common.data.websocket.impl.entities.AssistPipelineEventType import io.homeassistant.companion.android.common.data.websocket.impl.entities.AssistPipelineIntentEnd import io.homeassistant.companion.android.common.data.websocket.impl.entities.GetConfigResponse +import java.util.concurrent.TimeUnit +import javax.inject.Named import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map import okhttp3.HttpUrl.Companion.toHttpUrlOrNull -import java.util.concurrent.TimeUnit -import javax.inject.Named class IntegrationRepositoryImpl @AssistedInject constructor( private val integrationService: IntegrationService, diff --git a/common/src/main/java/io/homeassistant/companion/android/common/data/integration/impl/entities/SensorRequest.kt b/common/src/main/java/io/homeassistant/companion/android/common/data/integration/impl/entities/SensorRequest.kt index e32001c157e..055387f8ac6 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/data/integration/impl/entities/SensorRequest.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/data/integration/impl/entities/SensorRequest.kt @@ -11,11 +11,13 @@ data class SensorRegistrationRequest( val icon: String, val attributes: Map, val name: String? = null, - @JsonInclude(JsonInclude.Include.ALWAYS) // Always to override incorrect value from old registration + // Always to override incorrect value from old registration + @JsonInclude(JsonInclude.Include.ALWAYS) val deviceClass: String? = null, val unitOfMeasurement: String? = null, val stateClass: String? = null, - @JsonInclude(JsonInclude.Include.ALWAYS) // Always to override incorrect value from old registration + // Always to override incorrect value from old registration + @JsonInclude(JsonInclude.Include.ALWAYS) val entityCategory: String? = null, val disabled: Boolean? = null ) { diff --git a/common/src/main/java/io/homeassistant/companion/android/common/data/keychain/KeyChainRepositoryImpl.kt b/common/src/main/java/io/homeassistant/companion/android/common/data/keychain/KeyChainRepositoryImpl.kt index 8dad643d6dc..29099b64dcc 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/data/keychain/KeyChainRepositoryImpl.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/data/keychain/KeyChainRepositoryImpl.kt @@ -4,12 +4,12 @@ import android.content.Context import android.security.KeyChain import android.util.Log import io.homeassistant.companion.android.common.data.prefs.PrefsRepository -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext import java.lang.UnsupportedOperationException import java.security.PrivateKey import java.security.cert.X509Certificate import javax.inject.Inject +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext class KeyChainRepositoryImpl @Inject constructor( private val prefsRepository: PrefsRepository diff --git a/common/src/main/java/io/homeassistant/companion/android/common/data/keychain/KeyStoreRepositoryImpl.kt b/common/src/main/java/io/homeassistant/companion/android/common/data/keychain/KeyStoreRepositoryImpl.kt index a55f7316f2c..700cf0ef68f 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/data/keychain/KeyStoreRepositoryImpl.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/data/keychain/KeyStoreRepositoryImpl.kt @@ -2,13 +2,13 @@ package io.homeassistant.companion.android.common.data.keychain import android.content.Context import android.util.Log -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext import java.security.KeyStore import java.security.KeyStore.PrivateKeyEntry import java.security.PrivateKey import java.security.cert.X509Certificate import javax.inject.Inject +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext class KeyStoreRepositoryImpl @Inject constructor() : KeyChainRepository { companion object { diff --git a/common/src/main/java/io/homeassistant/companion/android/common/data/prefs/PrefsRepositoryImpl.kt b/common/src/main/java/io/homeassistant/companion/android/common/data/prefs/PrefsRepositoryImpl.kt index e92e3a70be6..002789ac79a 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/data/prefs/PrefsRepositoryImpl.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/data/prefs/PrefsRepositoryImpl.kt @@ -2,9 +2,9 @@ package io.homeassistant.companion.android.common.data.prefs import io.homeassistant.companion.android.common.data.LocalStorage import io.homeassistant.companion.android.common.data.integration.ControlsAuthRequiredSetting -import kotlinx.coroutines.runBlocking import javax.inject.Inject import javax.inject.Named +import kotlinx.coroutines.runBlocking class PrefsRepositoryImpl @Inject constructor( @Named("themes") private val localStorage: LocalStorage, diff --git a/common/src/main/java/io/homeassistant/companion/android/common/data/prefs/WearPrefsRepository.kt b/common/src/main/java/io/homeassistant/companion/android/common/data/prefs/WearPrefsRepository.kt index b2beb6b8409..b038baac12f 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/data/prefs/WearPrefsRepository.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/data/prefs/WearPrefsRepository.kt @@ -1,5 +1,7 @@ package io.homeassistant.companion.android.common.data.prefs +import io.homeassistant.companion.android.common.data.prefs.impl.entities.TemplateTileConfig + interface WearPrefsRepository { suspend fun getAllTileShortcuts(): Map> suspend fun getTileShortcutsAndSaveTileId(tileId: Int): List @@ -7,10 +9,11 @@ interface WearPrefsRepository { suspend fun removeTileShortcuts(tileId: Int?): List? suspend fun getShowShortcutText(): Boolean suspend fun setShowShortcutTextEnabled(enabled: Boolean) - suspend fun getTemplateTileContent(): String - suspend fun setTemplateTileContent(content: String) - suspend fun getTemplateTileRefreshInterval(): Int - suspend fun setTemplateTileRefreshInterval(interval: Int) + suspend fun getAllTemplateTiles(): Map + suspend fun getTemplateTileAndSaveTileId(tileId: Int): TemplateTileConfig + suspend fun setAllTemplateTiles(templateTiles: Map) + suspend fun setTemplateTile(tileId: Int, content: String, refreshInterval: Int): TemplateTileConfig + suspend fun removeTemplateTile(tileId: Int): TemplateTileConfig? suspend fun getWearHapticFeedback(): Boolean suspend fun setWearHapticFeedback(enabled: Boolean) suspend fun getWearToastConfirmation(): Boolean diff --git a/common/src/main/java/io/homeassistant/companion/android/common/data/prefs/WearPrefsRepositoryImpl.kt b/common/src/main/java/io/homeassistant/companion/android/common/data/prefs/WearPrefsRepositoryImpl.kt index 7f0bf459565..61cc6343024 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/data/prefs/WearPrefsRepositoryImpl.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/data/prefs/WearPrefsRepositoryImpl.kt @@ -1,12 +1,13 @@ package io.homeassistant.companion.android.common.data.prefs import io.homeassistant.companion.android.common.data.LocalStorage +import io.homeassistant.companion.android.common.data.prefs.impl.entities.TemplateTileConfig import io.homeassistant.companion.android.common.util.toStringList +import javax.inject.Inject +import javax.inject.Named import kotlinx.coroutines.runBlocking import org.json.JSONArray import org.json.JSONObject -import javax.inject.Inject -import javax.inject.Named class WearPrefsRepositoryImpl @Inject constructor( @Named("wear") private val localStorage: LocalStorage, @@ -15,19 +16,23 @@ class WearPrefsRepositoryImpl @Inject constructor( companion object { private const val MIGRATION_PREF = "migration" - private const val MIGRATION_VERSION = 1 + private const val MIGRATION_VERSION = 2 private const val PREF_TILE_SHORTCUTS = "tile_shortcuts_list" private const val PREF_SHOW_TILE_SHORTCUTS_TEXT = "show_tile_shortcuts_text" - private const val PREF_TILE_TEMPLATE = "tile_template" - private const val PREF_TILE_TEMPLATE_REFRESH_INTERVAL = "tile_template_refresh_interval" + private const val PREF_TILE_TEMPLATES = "tile_templates" private const val PREF_WEAR_HAPTIC_FEEDBACK = "wear_haptic_feedback" private const val PREF_WEAR_TOAST_CONFIRMATION = "wear_toast_confirmation" private const val PREF_WEAR_FAVORITES_ONLY = "wear_favorites_only" + + private const val UNKNOWN_TEMPLATE_TILE_ID = -1 } init { runBlocking { + val legacyPrefTileTemplate = "tile_template" + val legacyPrefTileTemplateRefreshInterval = "tile_template_refresh_interval" + val currentVersion = localStorage.getInt(MIGRATION_PREF) if (currentVersion == null || currentVersion < 1) { integrationStorage.getString(PREF_TILE_SHORTCUTS)?.let { @@ -36,11 +41,11 @@ class WearPrefsRepositoryImpl @Inject constructor( integrationStorage.getBooleanOrNull(PREF_SHOW_TILE_SHORTCUTS_TEXT)?.let { localStorage.putBoolean(PREF_SHOW_TILE_SHORTCUTS_TEXT, it) } - integrationStorage.getString(PREF_TILE_TEMPLATE)?.let { - localStorage.putString(PREF_TILE_TEMPLATE, it) + integrationStorage.getString(legacyPrefTileTemplate)?.let { + localStorage.putString(legacyPrefTileTemplate, it) } - integrationStorage.getInt(PREF_TILE_TEMPLATE_REFRESH_INTERVAL)?.let { - localStorage.putInt(PREF_TILE_TEMPLATE_REFRESH_INTERVAL, it) + integrationStorage.getInt(legacyPrefTileTemplateRefreshInterval)?.let { + localStorage.putInt(legacyPrefTileTemplateRefreshInterval, it) } integrationStorage.getBooleanOrNull(PREF_WEAR_HAPTIC_FEEDBACK)?.let { localStorage.putBoolean(PREF_WEAR_HAPTIC_FEEDBACK, it) @@ -51,6 +56,26 @@ class WearPrefsRepositoryImpl @Inject constructor( localStorage.putInt(MIGRATION_PREF, MIGRATION_VERSION) } + + if (currentVersion == null || currentVersion < 2) { + val template = localStorage.getString(legacyPrefTileTemplate) + val templateRefreshInterval = localStorage.getInt( + legacyPrefTileTemplateRefreshInterval + ) + + if (template != null && templateRefreshInterval != null) { + val templates = mapOf( + UNKNOWN_TEMPLATE_TILE_ID.toString() to TemplateTileConfig(template, templateRefreshInterval).toJSONObject() + ) + + localStorage.putString(PREF_TILE_TEMPLATES, JSONObject(templates).toString()) + } + + localStorage.remove(legacyPrefTileTemplate) + localStorage.remove(legacyPrefTileTemplateRefreshInterval) + + localStorage.putInt(MIGRATION_PREF, MIGRATION_VERSION) + } } } @@ -91,7 +116,8 @@ class WearPrefsRepositoryImpl @Inject constructor( val jsonArray = JSONArray(jsonStr) val entities = jsonArray.toStringList() mapOf( - null to entities // the key is null since we don't (yet) have the tileId + // the key is null since we don't (yet) have the tileId + null to entities ) } ) @@ -118,10 +144,6 @@ class WearPrefsRepositoryImpl @Inject constructor( return entities } - override suspend fun getTemplateTileContent(): String { - return localStorage.getString(PREF_TILE_TEMPLATE) ?: "" - } - override suspend fun getShowShortcutText(): Boolean { return localStorage.getBoolean(PREF_SHOW_TILE_SHORTCUTS_TEXT) } @@ -130,16 +152,56 @@ class WearPrefsRepositoryImpl @Inject constructor( localStorage.putBoolean(PREF_SHOW_TILE_SHORTCUTS_TEXT, enabled) } - override suspend fun setTemplateTileContent(content: String) { - localStorage.putString(PREF_TILE_TEMPLATE, content) + override suspend fun getAllTemplateTiles(): Map { + return localStorage.getString(PREF_TILE_TEMPLATES)?.let { jsonStr -> + val jsonObject = JSONObject(jsonStr) + buildMap { + jsonObject.keys().forEach { tileId -> + val id = tileId.toInt() + val templateTileConfig = TemplateTileConfig(jsonObject.getJSONObject(tileId)) + put(id, templateTileConfig) + } + } + } ?: emptyMap() + } + + override suspend fun getTemplateTileAndSaveTileId(tileId: Int): TemplateTileConfig { + val tileIdToTemplatesMap = getAllTemplateTiles() + return if (UNKNOWN_TEMPLATE_TILE_ID in tileIdToTemplatesMap && tileId !in tileIdToTemplatesMap) { + // if there are Templates with an unknown (-1) tileId key from a previous installation, + // and the tileId parameter is not already present in the map, associate it with that Template + val templateData = removeTemplateTile(UNKNOWN_TEMPLATE_TILE_ID)!! + setTemplateTile(tileId, templateData.template, templateData.refreshInterval) + templateData + } else { + var templateData = tileIdToTemplatesMap[tileId] + if (templateData == null) { + templateData = setTemplateTile(tileId, "", 0) + } + templateData + } + } + + override suspend fun setAllTemplateTiles(templateTiles: Map) { + val templateTilesJson = templateTiles.map { (tileId, templateTileConfig) -> + tileId.toString() to templateTileConfig.toJSONObject() + }.toMap() + val jsonStr = JSONObject(templateTilesJson).toString() + localStorage.putString(PREF_TILE_TEMPLATES, jsonStr) } - override suspend fun getTemplateTileRefreshInterval(): Int { - return localStorage.getInt(PREF_TILE_TEMPLATE_REFRESH_INTERVAL) ?: 0 + override suspend fun setTemplateTile(tileId: Int, content: String, refreshInterval: Int): TemplateTileConfig { + val templateTileConfig = TemplateTileConfig(content, refreshInterval) + val map = getAllTemplateTiles() + mapOf(tileId to templateTileConfig) + setAllTemplateTiles(map) + return templateTileConfig } - override suspend fun setTemplateTileRefreshInterval(interval: Int) { - localStorage.putInt(PREF_TILE_TEMPLATE_REFRESH_INTERVAL, interval) + override suspend fun removeTemplateTile(tileId: Int): TemplateTileConfig? { + val templateTilesMap = getAllTemplateTiles().toMutableMap() + val templateTile = templateTilesMap.remove(tileId) + setAllTemplateTiles(templateTilesMap) + return templateTile } override suspend fun getWearHapticFeedback(): Boolean { diff --git a/common/src/main/java/io/homeassistant/companion/android/common/data/prefs/impl/entities/TemplateTileConfig.kt b/common/src/main/java/io/homeassistant/companion/android/common/data/prefs/impl/entities/TemplateTileConfig.kt new file mode 100644 index 00000000000..b49ed84a189 --- /dev/null +++ b/common/src/main/java/io/homeassistant/companion/android/common/data/prefs/impl/entities/TemplateTileConfig.kt @@ -0,0 +1,28 @@ +package io.homeassistant.companion.android.common.data.prefs.impl.entities + +import com.fasterxml.jackson.annotation.JsonProperty +import org.json.JSONObject + +const val FIELD_TEMPLATE = "template" +const val FIELD_REFRESH_INTERVAL = "refresh_interval" + +data class TemplateTileConfig( + @JsonProperty(value = FIELD_TEMPLATE) + val template: String, + @JsonProperty(value = FIELD_REFRESH_INTERVAL) + val refreshInterval: Int +) { + constructor(jsonObject: JSONObject) : this( + jsonObject.getString(FIELD_TEMPLATE), + jsonObject.getInt(FIELD_REFRESH_INTERVAL) + ) + + fun toJSONObject(): JSONObject { + return JSONObject( + mapOf( + FIELD_TEMPLATE to template, + FIELD_REFRESH_INTERVAL to refreshInterval + ) + ) + } +} diff --git a/common/src/main/java/io/homeassistant/companion/android/common/data/servers/ServerManagerImpl.kt b/common/src/main/java/io/homeassistant/companion/android/common/data/servers/ServerManagerImpl.kt index 709575665f0..c3eb8316d5f 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/data/servers/ServerManagerImpl.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/data/servers/ServerManagerImpl.kt @@ -15,6 +15,9 @@ import io.homeassistant.companion.android.database.server.Server import io.homeassistant.companion.android.database.server.ServerDao import io.homeassistant.companion.android.database.server.ServerType import io.homeassistant.companion.android.database.settings.SettingsDao +import javax.inject.Inject +import javax.inject.Named +import kotlin.math.min import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -23,9 +26,6 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking -import javax.inject.Inject -import javax.inject.Named -import kotlin.math.min class ServerManagerImpl @Inject constructor( private val authenticationRepositoryFactory: AuthenticationRepositoryFactory, @@ -41,8 +41,8 @@ class ServerManagerImpl @Inject constructor( private val ioScope = CoroutineScope(Dispatchers.IO + Job()) - private val _servers = mutableMapOf() - private val _defaultServersFlow = MutableStateFlow>(emptyList()) + private val mutableServers = mutableMapOf() + private val mutableDefaultServersFlow = MutableStateFlow>(emptyList()) private val authenticationRepos = mutableMapOf() private val integrationRepos = mutableMapOf() @@ -53,22 +53,22 @@ class ServerManagerImpl @Inject constructor( } override val defaultServers: List - get() = _servers.values.filter { it.type == ServerType.DEFAULT }.toList() + get() = mutableServers.values.filter { it.type == ServerType.DEFAULT }.toList() override val defaultServersFlow: StateFlow> - get() = _defaultServersFlow.asStateFlow() + get() = mutableDefaultServersFlow.asStateFlow() init { // Initial (blocking) load serverDao.getAll().forEach { - _servers[it.id] = it.apply { connection.wifiHelper = wifiHelper } + mutableServers[it.id] = it.apply { connection.wifiHelper = wifiHelper } } // Listen for updates and update flow ioScope.launch { - _defaultServersFlow.emit(defaultServers) + mutableDefaultServersFlow.emit(defaultServers) serverDao.getAllFlow().collect { servers -> - _servers + mutableServers .filter { it.value.type == ServerType.DEFAULT && it.key !in servers.map { server -> server.id } @@ -77,27 +77,27 @@ class ServerManagerImpl @Inject constructor( removeServerFromManager(it.key) } servers.forEach { - _servers[it.id] = it.apply { connection.wifiHelper = wifiHelper } + mutableServers[it.id] = it.apply { connection.wifiHelper = wifiHelper } } - _defaultServersFlow.emit(defaultServers) + mutableDefaultServersFlow.emit(defaultServers) } } } override fun isRegistered(): Boolean = - _servers.values.any { it.type == ServerType.DEFAULT && it.connection.isRegistered() } + mutableServers.values.any { it.type == ServerType.DEFAULT && it.connection.isRegistered() } override suspend fun addServer(server: Server): Int { val newServer = server.copy( id = when (server.type) { - ServerType.TEMPORARY -> min(-2, (_servers.keys.minOrNull() ?: 0) - 1) + ServerType.TEMPORARY -> min(-2, (mutableServers.keys.minOrNull() ?: 0) - 1) else -> 0 // Use autogenerated ID } ) return if (server.type == ServerType.DEFAULT) { serverDao.add(newServer).toInt() } else { - _servers[newServer.id] = newServer.apply { connection.wifiHelper = wifiHelper } + mutableServers[newServer.id] = newServer.apply { connection.wifiHelper = wifiHelper } newServer.id } } @@ -105,36 +105,36 @@ class ServerManagerImpl @Inject constructor( private fun activeServerId(): Int? = runBlocking { val pref = localStorage.getInt(PREF_ACTIVE_SERVER) - if (pref != null && _servers[pref] != null) { + if (pref != null && mutableServers[pref] != null) { pref } else { - _servers.keys.maxOfOrNull { it } + mutableServers.keys.maxOfOrNull { it } } } override fun getServer(id: Int): Server? { val serverId = if (id == SERVER_ID_ACTIVE) activeServerId() else id - return serverId?.let { _servers[serverId] ?: serverDao.get(serverId) } + return serverId?.let { mutableServers[serverId] ?: serverDao.get(serverId) } } override fun getServer(webhookId: String): Server? = - _servers.values.firstOrNull { it.connection.webhookId == webhookId } ?: serverDao.get(webhookId) + mutableServers.values.firstOrNull { it.connection.webhookId == webhookId } ?: serverDao.get(webhookId) override fun activateServer(id: Int) { - if (id != SERVER_ID_ACTIVE && _servers[id] != null && _servers[id]?.type == ServerType.DEFAULT) { + if (id != SERVER_ID_ACTIVE && mutableServers[id] != null && mutableServers[id]?.type == ServerType.DEFAULT) { ioScope.launch { localStorage.putInt(PREF_ACTIVE_SERVER, id) } } } override fun updateServer(server: Server) { - _servers[server.id] = server.apply { connection.wifiHelper = wifiHelper } + mutableServers[server.id] = server.apply { connection.wifiHelper = wifiHelper } if (server.type == ServerType.DEFAULT) { ioScope.launch { serverDao.update(server) } } } override suspend fun convertTemporaryServer(id: Int): Int? { - return _servers[id]?.let { server -> + return mutableServers[id]?.let { server -> if (server.type != ServerType.TEMPORARY) return null val newServer = server.copy(id = 0) // Use autogenerated ID @@ -155,7 +155,7 @@ class ServerManagerImpl @Inject constructor( } private suspend fun removeServerFromManager(id: Int) { - if (_servers[id]?.type == ServerType.TEMPORARY) { + if (mutableServers[id]?.type == ServerType.TEMPORARY) { authenticationRepository(id).deletePreferences() integrationRepository(id).deletePreferences() prefsRepository.removeServer(id) @@ -164,13 +164,13 @@ class ServerManagerImpl @Inject constructor( integrationRepos.remove(id) webSocketRepos[id]?.shutdown() webSocketRepos.remove(id) - _servers.remove(id) + mutableServers.remove(id) } override fun authenticationRepository(serverId: Int): AuthenticationRepository { val id = if (serverId == SERVER_ID_ACTIVE) activeServerId() else serverId return authenticationRepos[id] ?: run { - if (id == null || _servers[id] == null) throw IllegalArgumentException("No server for ID") + if (id == null || mutableServers[id] == null) throw IllegalArgumentException("No server for ID") authenticationRepos[id] = authenticationRepositoryFactory.create(id) authenticationRepos[id]!! } @@ -179,7 +179,7 @@ class ServerManagerImpl @Inject constructor( override fun integrationRepository(serverId: Int): IntegrationRepository { val id = if (serverId == SERVER_ID_ACTIVE) activeServerId() else serverId return integrationRepos[id] ?: run { - if (id == null || _servers[id] == null) throw IllegalArgumentException("No server for ID") + if (id == null || mutableServers[id] == null) throw IllegalArgumentException("No server for ID") integrationRepos[id] = integrationRepositoryFactory.create(id) integrationRepos[id]!! } @@ -188,7 +188,7 @@ class ServerManagerImpl @Inject constructor( override fun webSocketRepository(serverId: Int): WebSocketRepository { val id = if (serverId == SERVER_ID_ACTIVE) activeServerId() else serverId return webSocketRepos[id] ?: run { - if (id == null || _servers[id] == null) throw IllegalArgumentException("No server for ID") + if (id == null || mutableServers[id] == null) throw IllegalArgumentException("No server for ID") webSocketRepos[id] = webSocketRepositoryFactory.create(id) webSocketRepos[id]!! } diff --git a/common/src/main/java/io/homeassistant/companion/android/common/data/websocket/WebSocketRepository.kt b/common/src/main/java/io/homeassistant/companion/android/common/data/websocket/WebSocketRepository.kt index 2fd3e659056..b7ed40a294d 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/data/websocket/WebSocketRepository.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/data/websocket/WebSocketRepository.kt @@ -60,7 +60,7 @@ interface WebSocketRepository { * @return [MatterCommissionResponse] detailing the server's response, or `null` if the server * did not return a response. */ - suspend fun commissionMatterDeviceOnNetwork(pin: Long): MatterCommissionResponse? + suspend fun commissionMatterDeviceOnNetwork(pin: Long, ip: String): MatterCommissionResponse? /** * Return a list of all Thread datasets known to the server. diff --git a/common/src/main/java/io/homeassistant/companion/android/common/data/websocket/WebSocketState.kt b/common/src/main/java/io/homeassistant/companion/android/common/data/websocket/WebSocketState.kt index 1d8c578e45f..f94637467f3 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/data/websocket/WebSocketState.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/data/websocket/WebSocketState.kt @@ -1,5 +1,8 @@ package io.homeassistant.companion.android.common.data.websocket enum class WebSocketState { - AUTHENTICATING, ACTIVE, CLOSED_AUTH, CLOSED_OTHER + AUTHENTICATING, + ACTIVE, + CLOSED_AUTH, + CLOSED_OTHER } diff --git a/common/src/main/java/io/homeassistant/companion/android/common/data/websocket/impl/WebSocketRepositoryImpl.kt b/common/src/main/java/io/homeassistant/companion/android/common/data/websocket/impl/WebSocketRepositoryImpl.kt index 6529d3738a0..8221876ce16 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/data/websocket/impl/WebSocketRepositoryImpl.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/data/websocket/impl/WebSocketRepositoryImpl.kt @@ -52,6 +52,10 @@ import io.homeassistant.companion.android.common.data.websocket.impl.entities.Th import io.homeassistant.companion.android.common.data.websocket.impl.entities.TriggerEvent import io.homeassistant.companion.android.common.util.toHexString import io.homeassistant.companion.android.database.server.ServerUserInfo +import java.io.IOException +import java.util.Collections +import java.util.concurrent.atomic.AtomicLong +import kotlin.coroutines.resumeWithException import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineStart @@ -80,10 +84,6 @@ import okhttp3.WebSocket import okhttp3.WebSocketListener import okio.ByteString import okio.ByteString.Companion.toByteString -import java.io.IOException -import java.util.Collections -import java.util.concurrent.atomic.AtomicLong -import kotlin.coroutines.resumeWithException class WebSocketRepositoryImpl @AssistedInject constructor( private val okHttpClient: OkHttpClient, @@ -455,7 +455,8 @@ class WebSocketRepositoryImpl @AssistedInject constructor( "type" to "matter/commission", "code" to code ), - timeout = 120000L // Matter commissioning takes at least 60 seconds + interview + // Matter commissioning takes at least 60 seconds + interview + timeout = 120000L ) ) @@ -473,14 +474,16 @@ class WebSocketRepositoryImpl @AssistedInject constructor( } } - override suspend fun commissionMatterDeviceOnNetwork(pin: Long): MatterCommissionResponse? { + override suspend fun commissionMatterDeviceOnNetwork(pin: Long, ip: String): MatterCommissionResponse? { + val data = mapOf( + "type" to "matter/commission_on_network", + "pin" to pin + ) val response = sendMessage( WebSocketRequest( - message = mapOf( - "type" to "matter/commission_on_network", - "pin" to pin - ), - timeout = 120000L // Matter commissioning takes at least 60 seconds + interview + message = if (server?.version?.isAtLeast(2024, 1) == true) data.plus("ip_addr" to ip) else data, + // Matter commissioning takes at least 60 seconds + interview + timeout = 120000L ) ) diff --git a/common/src/main/java/io/homeassistant/companion/android/common/data/websocket/impl/entities/GetConfigResponse.kt b/common/src/main/java/io/homeassistant/companion/android/common/data/websocket/impl/entities/GetConfigResponse.kt index b638efa7fb2..af691017afd 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/data/websocket/impl/entities/GetConfigResponse.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/data/websocket/impl/entities/GetConfigResponse.kt @@ -12,7 +12,10 @@ data class GetConfigResponse( val timeZone: String, val components: List, val version: String, - val cloudhookUrl: String?, // only when using webhook - val remoteUiUrl: String?, // only when using webhook - val entities: Map>? // only on core >= 2022.6 when using webhook + // only when using webhook + val cloudhookUrl: String?, + // only when using webhook + val remoteUiUrl: String?, + // only on core >= 2022.6 when using webhook + val entities: Map>? ) diff --git a/common/src/main/java/io/homeassistant/companion/android/common/data/websocket/impl/entities/ThreadDatasetResponse.kt b/common/src/main/java/io/homeassistant/companion/android/common/data/websocket/impl/entities/ThreadDatasetResponse.kt index 8abc5ea02d0..9d9ee9c57e8 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/data/websocket/impl/entities/ThreadDatasetResponse.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/data/websocket/impl/entities/ThreadDatasetResponse.kt @@ -9,6 +9,7 @@ data class ThreadDatasetResponse( val networkName: String, val panId: String, val preferred: Boolean, - val preferredBorderAgentId: String?, // only on core >= 2023.9, may still be null + // only on core >= 2023.9, may still be null + val preferredBorderAgentId: String?, val source: String ) diff --git a/common/src/main/java/io/homeassistant/companion/android/common/notifications/DeviceCommands.kt b/common/src/main/java/io/homeassistant/companion/android/common/notifications/DeviceCommands.kt index 864b32435d4..a21dc2505f7 100755 --- a/common/src/main/java/io/homeassistant/companion/android/common/notifications/DeviceCommands.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/notifications/DeviceCommands.kt @@ -5,9 +5,9 @@ import android.util.Log import io.homeassistant.companion.android.common.sensors.BluetoothSensorManager import io.homeassistant.companion.android.common.sensors.SensorUpdateReceiver import io.homeassistant.companion.android.database.sensor.SensorDao +import java.util.UUID import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch -import java.util.UUID object DeviceCommandData { diff --git a/common/src/main/java/io/homeassistant/companion/android/common/notifications/NotificationDeleteReceiver.kt b/common/src/main/java/io/homeassistant/companion/android/common/notifications/NotificationDeleteReceiver.kt index f27f629bd05..16e54ea6129 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/notifications/NotificationDeleteReceiver.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/notifications/NotificationDeleteReceiver.kt @@ -10,11 +10,11 @@ import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.common.util.cancelGroupIfNeeded import io.homeassistant.companion.android.database.notification.NotificationDao +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch -import javax.inject.Inject @AndroidEntryPoint class NotificationDeleteReceiver : BroadcastReceiver() { diff --git a/common/src/main/java/io/homeassistant/companion/android/common/notifications/NotificationFunctions.kt b/common/src/main/java/io/homeassistant/companion/android/common/notifications/NotificationFunctions.kt index 4ed54bb1ef5..774f615d5c0 100755 --- a/common/src/main/java/io/homeassistant/companion/android/common/notifications/NotificationFunctions.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/notifications/NotificationFunctions.kt @@ -24,8 +24,8 @@ import com.mikepenz.iconics.utils.colorFilter import com.mikepenz.iconics.utils.toAndroidIconCompat import com.vdurmont.emoji.EmojiParser import io.homeassistant.companion.android.common.R +import io.homeassistant.companion.android.common.util.CHANNEL_GENERAL import io.homeassistant.companion.android.common.util.cancel -import io.homeassistant.companion.android.common.util.generalChannel import java.util.Locale object NotificationData { @@ -74,7 +74,7 @@ fun handleChannel( data: Map ): String { // Define some values for a default channel - var channelID = generalChannel + var channelID = CHANNEL_GENERAL var channelName = "General" if (!data[NotificationData.CHANNEL].isNullOrEmpty()) { diff --git a/common/src/main/java/io/homeassistant/companion/android/common/sensors/AndroidOsSensorManager.kt b/common/src/main/java/io/homeassistant/companion/android/common/sensors/AndroidOsSensorManager.kt index 98b9b86cc40..7324f08e3a8 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/sensors/AndroidOsSensorManager.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/sensors/AndroidOsSensorManager.kt @@ -2,8 +2,8 @@ package io.homeassistant.companion.android.common.sensors import android.content.Context import android.os.Build -import io.homeassistant.companion.android.common.util.STATE_UNKNOWN import io.homeassistant.companion.android.common.R as commonR +import io.homeassistant.companion.android.common.util.STATE_UNKNOWN class AndroidOsSensorManager : SensorManager { diff --git a/common/src/main/java/io/homeassistant/companion/android/common/sensors/AppSensorManagerBase.kt b/common/src/main/java/io/homeassistant/companion/android/common/sensors/AppSensorManagerBase.kt index e213fa34c8b..27ff18a70ca 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/sensors/AppSensorManagerBase.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/sensors/AppSensorManagerBase.kt @@ -9,8 +9,8 @@ import android.os.Process import android.util.Log import androidx.annotation.RequiresApi import androidx.core.content.getSystemService -import java.math.RoundingMode import io.homeassistant.companion.android.common.R as commonR +import java.math.RoundingMode abstract class AppSensorManagerBase : SensorManager { companion object { diff --git a/common/src/main/java/io/homeassistant/companion/android/common/sensors/AudioSensorManager.kt b/common/src/main/java/io/homeassistant/companion/android/common/sensors/AudioSensorManager.kt index 35623af8f13..14105581817 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/sensors/AudioSensorManager.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/sensors/AudioSensorManager.kt @@ -6,12 +6,13 @@ import android.media.AudioManager import android.os.Build import androidx.annotation.RequiresApi import androidx.core.content.getSystemService -import io.homeassistant.companion.android.common.util.STATE_UNKNOWN import io.homeassistant.companion.android.common.R as commonR +import io.homeassistant.companion.android.common.util.STATE_UNKNOWN class AudioSensorManager : SensorManager { companion object { private const val TAG = "AudioSensor" + const val VOLUME_CHANGED_ACTION = "android.media.VOLUME_CHANGED_ACTION" val audioSensor = SensorManager.BasicSensor( "audio_sensor", @@ -69,69 +70,77 @@ class AudioSensorManager : SensorManager { SensorManager.BasicSensor.UpdateType.WORKER } ) - private val volAlarm = SensorManager.BasicSensor( + val volAlarm = SensorManager.BasicSensor( "volume_alarm", "sensor", commonR.string.sensor_name_volume_alarm, commonR.string.sensor_description_volume_alarm, "mdi:alarm", - entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC + entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC, + updateType = SensorManager.BasicSensor.UpdateType.INTENT ) - private val volCall = SensorManager.BasicSensor( + val volCall = SensorManager.BasicSensor( "volume_call", "sensor", commonR.string.sensor_name_volume_call, commonR.string.sensor_description_volume_call, "mdi:phone", - entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC + entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC, + updateType = SensorManager.BasicSensor.UpdateType.INTENT ) - private val volMusic = SensorManager.BasicSensor( + val volMusic = SensorManager.BasicSensor( "volume_music", "sensor", commonR.string.sensor_name_volume_music, commonR.string.sensor_description_volume_music, "mdi:music", - entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC + entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC, + updateType = SensorManager.BasicSensor.UpdateType.INTENT ) - private val volRing = SensorManager.BasicSensor( + val volRing = SensorManager.BasicSensor( "volume_ring", "sensor", commonR.string.sensor_name_volume_ring, commonR.string.sensor_description_volume_ring, "mdi:phone-ring", - entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC + entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC, + updateType = SensorManager.BasicSensor.UpdateType.INTENT ) - private val volNotification = SensorManager.BasicSensor( + val volNotification = SensorManager.BasicSensor( "volume_notification", "sensor", commonR.string.sensor_name_volume_notification, commonR.string.sensor_description_volume_notification, "mdi:bell-ring", - entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC + entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC, + updateType = SensorManager.BasicSensor.UpdateType.INTENT ) - private val volSystem = SensorManager.BasicSensor( + val volSystem = SensorManager.BasicSensor( "volume_system", "sensor", commonR.string.sensor_name_volume_system, commonR.string.sensor_description_volume_system, "mdi:cellphone-sound", - entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC + entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC, + updateType = SensorManager.BasicSensor.UpdateType.INTENT ) - private val volAccessibility = SensorManager.BasicSensor( + val volAccessibility = SensorManager.BasicSensor( "volume_accessibility", "sensor", commonR.string.sensor_name_volume_accessibility, commonR.string.sensor_description_volume_accessibility, "mdi:human", - entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC + entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC, + updateType = SensorManager.BasicSensor.UpdateType.INTENT ) - private val volDTMF = SensorManager.BasicSensor( + val volDTMF = SensorManager.BasicSensor( "volume_dtmf", "sensor", commonR.string.sensor_name_volume_dtmf, commonR.string.sensor_description_volume_dtmf, "mdi:volume-high", - entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC + entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC, + updateType = SensorManager.BasicSensor.UpdateType.INTENT ) } diff --git a/common/src/main/java/io/homeassistant/companion/android/common/sensors/BatterySensorManager.kt b/common/src/main/java/io/homeassistant/companion/android/common/sensors/BatterySensorManager.kt index aeeda0a78b6..55e24266ccb 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/sensors/BatterySensorManager.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/sensors/BatterySensorManager.kt @@ -4,9 +4,9 @@ import android.content.Context import android.content.Intent import android.content.IntentFilter import android.os.BatteryManager +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.util.STATE_UNKNOWN import java.math.RoundingMode -import io.homeassistant.companion.android.common.R as commonR class BatterySensorManager : SensorManager { diff --git a/common/src/main/java/io/homeassistant/companion/android/common/sensors/BluetoothSensorManager.kt b/common/src/main/java/io/homeassistant/companion/android/common/sensors/BluetoothSensorManager.kt index acedadaa0cf..585c99dc85a 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/sensors/BluetoothSensorManager.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/sensors/BluetoothSensorManager.kt @@ -4,6 +4,7 @@ import android.Manifest import android.annotation.SuppressLint import android.content.Context import android.os.Build +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.bluetooth.BluetoothDevice import io.homeassistant.companion.android.common.bluetooth.BluetoothUtils import io.homeassistant.companion.android.common.bluetooth.BluetoothUtils.supportsTransmitter @@ -18,11 +19,10 @@ import io.homeassistant.companion.android.common.util.STATE_UNKNOWN import io.homeassistant.companion.android.database.AppDatabase import io.homeassistant.companion.android.database.sensor.SensorSetting import io.homeassistant.companion.android.database.sensor.SensorSettingType +import java.util.UUID import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import java.util.UUID -import io.homeassistant.companion.android.common.R as commonR public class BluetoothSensorManager : SensorManager { companion object { diff --git a/common/src/main/java/io/homeassistant/companion/android/common/sensors/DNDSensorManager.kt b/common/src/main/java/io/homeassistant/companion/android/common/sensors/DNDSensorManager.kt index ed18daa7fb6..3685a95cd87 100755 --- a/common/src/main/java/io/homeassistant/companion/android/common/sensors/DNDSensorManager.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/sensors/DNDSensorManager.kt @@ -7,8 +7,8 @@ import android.os.Build import androidx.annotation.ChecksSdkIntAtLeast import androidx.annotation.RequiresApi import androidx.core.content.getSystemService -import io.homeassistant.companion.android.common.util.STATE_UNKNOWN import io.homeassistant.companion.android.common.R as commonR +import io.homeassistant.companion.android.common.util.STATE_UNKNOWN @RequiresApi(Build.VERSION_CODES.M) class DNDSensorManager : SensorManager { diff --git a/common/src/main/java/io/homeassistant/companion/android/common/sensors/LastRebootSensorManager.kt b/common/src/main/java/io/homeassistant/companion/android/common/sensors/LastRebootSensorManager.kt index d5563d62782..bdcd893cb7c 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/sensors/LastRebootSensorManager.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/sensors/LastRebootSensorManager.kt @@ -4,6 +4,7 @@ import android.annotation.SuppressLint import android.content.Context import android.os.SystemClock import android.util.Log +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.util.STATE_UNAVAILABLE import io.homeassistant.companion.android.database.AppDatabase import io.homeassistant.companion.android.database.sensor.SensorSetting @@ -15,7 +16,6 @@ import java.util.Date import java.util.GregorianCalendar import java.util.TimeZone import kotlin.math.absoluteValue -import io.homeassistant.companion.android.common.R as commonR class LastRebootSensorManager : SensorManager { companion object { diff --git a/common/src/main/java/io/homeassistant/companion/android/common/sensors/LastUpdateManager.kt b/common/src/main/java/io/homeassistant/companion/android/common/sensors/LastUpdateManager.kt index 0cf6fc5b672..fa6dce35a71 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/sensors/LastUpdateManager.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/sensors/LastUpdateManager.kt @@ -2,10 +2,10 @@ package io.homeassistant.companion.android.common.sensors import android.content.Context import android.util.Log +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.database.AppDatabase import io.homeassistant.companion.android.database.sensor.SensorSetting import io.homeassistant.companion.android.database.sensor.SensorSettingType -import io.homeassistant.companion.android.common.R as commonR class LastUpdateManager : SensorManager { companion object { diff --git a/common/src/main/java/io/homeassistant/companion/android/common/sensors/LightSensorManager.kt b/common/src/main/java/io/homeassistant/companion/android/common/sensors/LightSensorManager.kt index 5bfc631b355..4fb4aea6013 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/sensors/LightSensorManager.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/sensors/LightSensorManager.kt @@ -8,8 +8,8 @@ import android.hardware.SensorEventListener import android.hardware.SensorManager.SENSOR_DELAY_NORMAL import android.util.Log import androidx.core.content.getSystemService -import kotlin.math.roundToInt import io.homeassistant.companion.android.common.R as commonR +import kotlin.math.roundToInt class LightSensorManager : SensorManager, SensorEventListener { companion object { diff --git a/common/src/main/java/io/homeassistant/companion/android/common/sensors/NetworkSensorManager.kt b/common/src/main/java/io/homeassistant/companion/android/common/sensors/NetworkSensorManager.kt index 4a3aed5a447..349dfc275eb 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/sensors/NetworkSensorManager.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/sensors/NetworkSensorManager.kt @@ -12,11 +12,14 @@ import android.os.Build import android.util.Log import androidx.annotation.RequiresApi import androidx.core.content.getSystemService +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.util.STATE_UNAVAILABLE import io.homeassistant.companion.android.common.util.STATE_UNKNOWN import io.homeassistant.companion.android.database.AppDatabase import io.homeassistant.companion.android.database.sensor.SensorSetting import io.homeassistant.companion.android.database.sensor.SensorSettingType +import java.lang.reflect.Method +import java.net.Inet6Address import okhttp3.Call import okhttp3.Callback import okhttp3.OkHttpClient @@ -25,8 +28,6 @@ import okhttp3.Response import okio.IOException import org.json.JSONException import org.json.JSONObject -import java.lang.reflect.Method -import io.homeassistant.companion.android.common.R as commonR class NetworkSensorManager : SensorManager { companion object { @@ -119,6 +120,17 @@ class NetworkSensorManager : SensorManager { docsLink = "https://companion.home-assistant.io/docs/core/sensors#public-ip-sensor", entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC ) + val ip6Addresses = SensorManager.BasicSensor( + "ip6_addresses", + "sensor", + commonR.string.basic_sensor_name_ip6_addresses, + commonR.string.sensor_description_ip6_addresses, + "mdi:ip", + unitOfMeasurement = "address(es)", + stateClass = SensorManager.STATE_CLASS_MEASUREMENT, + entityCategory = SensorManager.ENTITY_CATEGORY_DIAGNOSTIC, + updateType = SensorManager.BasicSensor.UpdateType.INTENT + ) val networkType = SensorManager.BasicSensor( "network_type", "sensor", @@ -158,7 +170,7 @@ class NetworkSensorManager : SensorManager { listOf(publicIp) } return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - list.plus(networkType) + list.plus(networkType).plus(ip6Addresses) } else { list } @@ -195,6 +207,7 @@ class NetworkSensorManager : SensorManager { updatePublicIpSensor(context) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { updateNetworkType(context) + updateIP6Sensor(context) } } @@ -356,6 +369,37 @@ class NetworkSensorManager : SensorManager { ) } + @RequiresApi(Build.VERSION_CODES.M) + private fun updateIP6Sensor(context: Context) { + if (!isEnabled(context, ip6Addresses)) { + return + } + var ipAddressList: List = ArrayList() + var totalAddresses = 0 + + if (checkPermission(context, ip6Addresses.id)) { + val connectivityManager = context.applicationContext.getSystemService() + val activeNetwork = connectivityManager?.activeNetwork + val ipAddresses = connectivityManager?.getLinkProperties(activeNetwork)?.linkAddresses + if (!ipAddresses.isNullOrEmpty()) { + val ip6Addresses = ipAddresses.filter { linkAddress -> linkAddress.address is Inet6Address } + if (ip6Addresses.isNotEmpty()) { + ipAddressList = ipAddressList.plus(elements = ip6Addresses.map { linkAddress -> linkAddress.toString() }) + totalAddresses += ip6Addresses.size + } + } + } + onSensorUpdated( + context, + ip6Addresses, + totalAddresses, + ip6Addresses.statelessIcon, + mapOf( + "ip6_addresses" to ipAddressList + ) + ) + } + private fun updateWifiLinkSpeedSensor(context: Context) { if (!isEnabled(context, wifiLinkSpeed) || !hasWifi(context)) { return diff --git a/common/src/main/java/io/homeassistant/companion/android/common/sensors/NextAlarmManager.kt b/common/src/main/java/io/homeassistant/companion/android/common/sensors/NextAlarmManager.kt index 48a2edc4f82..584e73131f8 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/sensors/NextAlarmManager.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/sensors/NextAlarmManager.kt @@ -6,6 +6,7 @@ import android.content.pm.PackageManager import android.os.Build import android.util.Log import androidx.core.content.getSystemService +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.util.STATE_UNAVAILABLE import io.homeassistant.companion.android.common.util.STATE_UNKNOWN import io.homeassistant.companion.android.database.AppDatabase @@ -17,7 +18,6 @@ import java.util.Date import java.util.GregorianCalendar import java.util.Locale import java.util.TimeZone -import io.homeassistant.companion.android.common.R as commonR class NextAlarmManager : SensorManager { companion object { diff --git a/common/src/main/java/io/homeassistant/companion/android/common/sensors/PhoneStateSensorManager.kt b/common/src/main/java/io/homeassistant/companion/android/common/sensors/PhoneStateSensorManager.kt index 1ef032c9b9e..d064b278b59 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/sensors/PhoneStateSensorManager.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/sensors/PhoneStateSensorManager.kt @@ -10,9 +10,9 @@ import android.telephony.SubscriptionManager import android.telephony.TelephonyManager import android.util.Log import androidx.core.content.getSystemService +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.util.STATE_UNAVAILABLE import io.homeassistant.companion.android.common.util.STATE_UNKNOWN -import io.homeassistant.companion.android.common.R as commonR class PhoneStateSensorManager : SensorManager { diff --git a/common/src/main/java/io/homeassistant/companion/android/common/sensors/PowerSensorManager.kt b/common/src/main/java/io/homeassistant/companion/android/common/sensors/PowerSensorManager.kt index 9f9c0f3951a..fdb995ee0e7 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/sensors/PowerSensorManager.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/sensors/PowerSensorManager.kt @@ -10,7 +10,7 @@ import io.homeassistant.companion.android.common.R as commonR class PowerSensorManager : SensorManager { companion object { private const val TAG = "PowerSensors" - private const val packageName = "io.homeassistant.companion.android" + private const val PACKAGE_NAME = "io.homeassistant.companion.android" val interactiveDevice = SensorManager.BasicSensor( "is_interactive", @@ -103,7 +103,7 @@ class PowerSensorManager : SensorManager { icon, mapOf( "ignoring_battery_optimizations" to powerManager.isIgnoringBatteryOptimizations( - packageName + PACKAGE_NAME ) ) ) diff --git a/common/src/main/java/io/homeassistant/companion/android/common/sensors/PressureSensorManager.kt b/common/src/main/java/io/homeassistant/companion/android/common/sensors/PressureSensorManager.kt index 910f6577e81..ea90761e27e 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/sensors/PressureSensorManager.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/sensors/PressureSensorManager.kt @@ -8,8 +8,8 @@ import android.hardware.SensorEventListener import android.hardware.SensorManager.SENSOR_DELAY_NORMAL import android.util.Log import androidx.core.content.getSystemService -import java.math.RoundingMode import io.homeassistant.companion.android.common.R as commonR +import java.math.RoundingMode class PressureSensorManager : SensorManager, SensorEventListener { companion object { diff --git a/common/src/main/java/io/homeassistant/companion/android/common/sensors/ProximitySensorManager.kt b/common/src/main/java/io/homeassistant/companion/android/common/sensors/ProximitySensorManager.kt index da9ea28c55e..6f374edd79d 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/sensors/ProximitySensorManager.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/sensors/ProximitySensorManager.kt @@ -8,8 +8,8 @@ import android.hardware.SensorEventListener import android.hardware.SensorManager.SENSOR_DELAY_NORMAL import android.util.Log import androidx.core.content.getSystemService -import kotlin.math.roundToInt import io.homeassistant.companion.android.common.R as commonR +import kotlin.math.roundToInt class ProximitySensorManager : SensorManager, SensorEventListener { companion object { diff --git a/common/src/main/java/io/homeassistant/companion/android/common/sensors/SensorManager.kt b/common/src/main/java/io/homeassistant/companion/android/common/sensors/SensorManager.kt index 75eccc2447a..e64edda0bf6 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/sensors/SensorManager.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/sensors/SensorManager.kt @@ -13,13 +13,13 @@ import dagger.hilt.EntryPoint import dagger.hilt.InstallIn import dagger.hilt.android.EntryPointAccessors import dagger.hilt.components.SingletonComponent +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.database.AppDatabase import io.homeassistant.companion.android.database.sensor.Attribute import io.homeassistant.companion.android.database.sensor.SensorSetting import io.homeassistant.companion.android.database.sensor.SensorSettingType import java.util.Locale -import io.homeassistant.companion.android.common.R as commonR interface SensorManager { @@ -49,7 +49,10 @@ interface SensorManager { val enabledByDefault: Boolean = false ) { enum class UpdateType { - INTENT, WORKER, LOCATION, CUSTOM + INTENT, + WORKER, + LOCATION, + CUSTOM } } diff --git a/common/src/main/java/io/homeassistant/companion/android/common/sensors/SensorReceiverBase.kt b/common/src/main/java/io/homeassistant/companion/android/common/sensors/SensorReceiverBase.kt index 9c7516a9ca7..3aeec8be617 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/sensors/SensorReceiverBase.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/sensors/SensorReceiverBase.kt @@ -16,7 +16,7 @@ import io.homeassistant.companion.android.common.R import io.homeassistant.companion.android.common.data.integration.IntegrationException import io.homeassistant.companion.android.common.data.integration.SensorRegistration import io.homeassistant.companion.android.common.data.servers.ServerManager -import io.homeassistant.companion.android.common.util.sensorCoreSyncChannel +import io.homeassistant.companion.android.common.util.CHANNEL_SENSOR_SYNC import io.homeassistant.companion.android.database.AppDatabase import io.homeassistant.companion.android.database.sensor.SensorDao import io.homeassistant.companion.android.database.sensor.SensorWithAttributes @@ -24,6 +24,11 @@ import io.homeassistant.companion.android.database.sensor.toSensorWithAttributes import io.homeassistant.companion.android.database.sensor.toSensorsWithAttributes import io.homeassistant.companion.android.database.server.Server import io.homeassistant.companion.android.database.settings.SensorUpdateFrequencySetting +import java.io.IOException +import java.net.ConnectException +import java.net.SocketTimeoutException +import java.util.Locale +import javax.inject.Inject import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -32,11 +37,6 @@ import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.delay import kotlinx.coroutines.launch -import java.io.IOException -import java.net.ConnectException -import java.net.SocketTimeoutException -import java.util.Locale -import javax.inject.Inject abstract class SensorReceiverBase : BroadcastReceiver() { companion object { @@ -78,7 +78,7 @@ abstract class SensorReceiverBase : BroadcastReceiver() { Intent.ACTION_POWER_DISCONNECTED ) - protected abstract val skippableActions: Map + protected abstract val skippableActions: Map> protected abstract fun getSensorSettingsIntent( context: Context, @@ -89,14 +89,16 @@ abstract class SensorReceiverBase : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { Log.d(tag, "Received intent: ${intent.action}") - if (skippableActions.containsKey(intent.action)) { - val sensor = skippableActions[intent.action] - if (!isSensorEnabled(sensor!!)) { + skippableActions[intent.action]?.let { sensors -> + val noSensorsEnabled = sensors.none { + isSensorEnabled(it) + } + if (noSensorsEnabled) { Log.d( tag, String.format( - "Sensor %s corresponding to received event %s is disabled, skipping sensors update", - sensor, + "Sensor(s) %s corresponding to received event %s are disabled, skipping sensors update", + sensors.toString(), intent.action ) ) @@ -283,9 +285,9 @@ abstract class SensorReceiverBase : BroadcastReceiver() { context.getSystemService()?.let { notificationManager -> createNotificationChannel(context) - val notificationId = "$sensorCoreSyncChannel-${basicSensor.id}".hashCode() + val notificationId = "$CHANNEL_SENSOR_SYNC-${basicSensor.id}".hashCode() val notificationIntent = getSensorSettingsIntent(context, basicSensor.id, manager.id(), notificationId) - val notification = NotificationCompat.Builder(context, sensorCoreSyncChannel) + val notification = NotificationCompat.Builder(context, CHANNEL_SENSOR_SYNC) .setSmallIcon(R.drawable.ic_stat_ic_notification) .setContentTitle(context.getString(basicSensor.name)) .setContentText(context.getString(R.string.sensor_worker_sync_missing_permissions)) @@ -425,11 +427,11 @@ abstract class SensorReceiverBase : BroadcastReceiver() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val notificationManager = context.getSystemService() ?: return var notificationChannel = - notificationManager.getNotificationChannel(sensorCoreSyncChannel) + notificationManager.getNotificationChannel(CHANNEL_SENSOR_SYNC) if (notificationChannel == null) { notificationChannel = NotificationChannel( - sensorCoreSyncChannel, - sensorCoreSyncChannel, + CHANNEL_SENSOR_SYNC, + CHANNEL_SENSOR_SYNC, NotificationManager.IMPORTANCE_DEFAULT ) notificationManager.createNotificationChannel(notificationChannel) diff --git a/common/src/main/java/io/homeassistant/companion/android/common/sensors/SensorUpdateReceiver.kt b/common/src/main/java/io/homeassistant/companion/android/common/sensors/SensorUpdateReceiver.kt index 22d1c9f41b9..a01b8ac22ea 100755 --- a/common/src/main/java/io/homeassistant/companion/android/common/sensors/SensorUpdateReceiver.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/sensors/SensorUpdateReceiver.kt @@ -26,7 +26,7 @@ class SensorUpdateReceiver : SensorReceiverBase() { override val managers: List get() = listOf(BluetoothSensorManager()) - override val skippableActions: Map + override val skippableActions: Map> get() = emptyMap() override fun getSensorSettingsIntent( diff --git a/common/src/main/java/io/homeassistant/companion/android/common/sensors/SensorWorkerBase.kt b/common/src/main/java/io/homeassistant/companion/android/common/sensors/SensorWorkerBase.kt index 69117eedc5d..0874f9ef7aa 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/sensors/SensorWorkerBase.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/sensors/SensorWorkerBase.kt @@ -10,13 +10,13 @@ import androidx.core.content.getSystemService import androidx.work.CoroutineWorker import androidx.work.ForegroundInfo import androidx.work.WorkerParameters +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.servers.ServerManager -import io.homeassistant.companion.android.common.util.sensorWorkerChannel +import io.homeassistant.companion.android.common.util.CHANNEL_SENSOR_WORKER import io.homeassistant.companion.android.database.AppDatabase +import java.lang.IllegalStateException import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -import java.lang.IllegalStateException -import io.homeassistant.companion.android.common.R as commonR abstract class SensorWorkerBase( val appContext: Context, @@ -43,7 +43,7 @@ abstract class SensorWorkerBase( } ) { createNotificationChannel() - val notification = NotificationCompat.Builder(applicationContext, sensorWorkerChannel) + val notification = NotificationCompat.Builder(applicationContext, CHANNEL_SENSOR_WORKER) .setSmallIcon(commonR.drawable.ic_stat_ic_notification) .setContentTitle(appContext.getString(commonR.string.updating_sensors)) .setPriority(NotificationCompat.PRIORITY_LOW) @@ -72,10 +72,10 @@ abstract class SensorWorkerBase( protected fun createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { var notificationChannel = - notificationManager.getNotificationChannel(sensorWorkerChannel) + notificationManager.getNotificationChannel(CHANNEL_SENSOR_WORKER) if (notificationChannel == null) { notificationChannel = NotificationChannel( - sensorWorkerChannel, + CHANNEL_SENSOR_WORKER, TAG, NotificationManager.IMPORTANCE_LOW ) diff --git a/common/src/main/java/io/homeassistant/companion/android/common/sensors/StepsSensorManager.kt b/common/src/main/java/io/homeassistant/companion/android/common/sensors/StepsSensorManager.kt index 68b55f936c9..d4d45e2e9b9 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/sensors/StepsSensorManager.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/sensors/StepsSensorManager.kt @@ -10,8 +10,8 @@ import android.hardware.SensorManager.SENSOR_DELAY_NORMAL import android.os.Build import android.util.Log import androidx.core.content.getSystemService -import kotlin.math.roundToInt import io.homeassistant.companion.android.common.R as commonR +import kotlin.math.roundToInt class StepsSensorManager : SensorManager, SensorEventListener { companion object { diff --git a/common/src/main/java/io/homeassistant/companion/android/common/sensors/StorageSensorManager.kt b/common/src/main/java/io/homeassistant/companion/android/common/sensors/StorageSensorManager.kt index fa3979bec08..8b0665ac3b6 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/sensors/StorageSensorManager.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/sensors/StorageSensorManager.kt @@ -4,9 +4,9 @@ import android.content.Context import android.os.Environment import android.os.StatFs import android.util.Log +import io.homeassistant.companion.android.common.R as commonR import java.io.File import kotlin.math.roundToInt -import io.homeassistant.companion.android.common.R as commonR class StorageSensorManager : SensorManager { companion object { diff --git a/common/src/main/java/io/homeassistant/companion/android/common/sensors/TimeZoneManager.kt b/common/src/main/java/io/homeassistant/companion/android/common/sensors/TimeZoneManager.kt index eed641d5024..f3aba6730cb 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/sensors/TimeZoneManager.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/sensors/TimeZoneManager.kt @@ -1,10 +1,10 @@ package io.homeassistant.companion.android.common.sensors import android.content.Context +import io.homeassistant.companion.android.common.R as commonR import java.util.Date import java.util.Locale import java.util.TimeZone -import io.homeassistant.companion.android.common.R as commonR class TimeZoneManager : SensorManager { companion object { diff --git a/common/src/main/java/io/homeassistant/companion/android/common/sensors/TrafficStatsManager.kt b/common/src/main/java/io/homeassistant/companion/android/common/sensors/TrafficStatsManager.kt index f127330796b..5b01ad97824 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/sensors/TrafficStatsManager.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/sensors/TrafficStatsManager.kt @@ -6,9 +6,9 @@ import android.net.NetworkCapabilities import android.net.TrafficStats import android.util.Log import androidx.core.content.getSystemService +import io.homeassistant.companion.android.common.R as commonR import java.math.RoundingMode import kotlin.math.absoluteValue -import io.homeassistant.companion.android.common.R as commonR class TrafficStatsManager : SensorManager { companion object { diff --git a/common/src/main/java/io/homeassistant/companion/android/common/util/AppNotifChannels.kt b/common/src/main/java/io/homeassistant/companion/android/common/util/AppNotifChannels.kt index 1120a4b367c..cf37b19830b 100755 --- a/common/src/main/java/io/homeassistant/companion/android/common/util/AppNotifChannels.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/util/AppNotifChannels.kt @@ -1,25 +1,25 @@ package io.homeassistant.companion.android.common.util -const val sensorWorkerChannel = "Sensor Worker" -const val sensorCoreSyncChannel = "Sensor Sync" -const val websocketChannel = "Websocket" -const val websocketIssuesChannel = "Websocket Issues" -const val highAccuracyChannel = "High accuracy location" -const val databaseChannel = "App Database" -const val locationDisabledChannel = "Location disabled" -const val downloadsChannel = "downloads" -const val generalChannel = "general" -const val beaconMonitorChannel = "beacon" +const val CHANNEL_SENSOR_WORKER = "Sensor Worker" +const val CHANNEL_SENSOR_SYNC = "Sensor Sync" +const val CHANNEL_WEBSOCKET = "Websocket" +const val CHANNEL_WEBSOCKET_ISSUES = "Websocket Issues" +const val CHANNEL_HIGH_ACCURACY = "High accuracy location" +const val CHANNEL_DATABASE = "App Database" +const val CHANNEL_LOCATION_DISABLED = "Location disabled" +const val CHANNEL_DOWNLOADS = "downloads" +const val CHANNEL_GENERAL = "general" +const val CHANNEL_BEACON_MONITOR = "beacon" val appCreatedChannels = listOf( - sensorWorkerChannel, - sensorCoreSyncChannel, - websocketChannel, - websocketIssuesChannel, - highAccuracyChannel, - databaseChannel, - locationDisabledChannel, - downloadsChannel, - generalChannel, - beaconMonitorChannel + CHANNEL_SENSOR_WORKER, + CHANNEL_SENSOR_SYNC, + CHANNEL_WEBSOCKET, + CHANNEL_WEBSOCKET_ISSUES, + CHANNEL_HIGH_ACCURACY, + CHANNEL_DATABASE, + CHANNEL_LOCATION_DISABLED, + CHANNEL_DOWNLOADS, + CHANNEL_GENERAL, + CHANNEL_BEACON_MONITOR ) diff --git a/common/src/main/java/io/homeassistant/companion/android/common/util/AudioUrlPlayer.kt b/common/src/main/java/io/homeassistant/companion/android/common/util/AudioUrlPlayer.kt index 9b883b2e1f8..af43fb52449 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/util/AudioUrlPlayer.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/util/AudioUrlPlayer.kt @@ -4,11 +4,11 @@ import android.media.AudioAttributes import android.media.MediaPlayer import android.os.Build import android.util.Log +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.isActive import kotlinx.coroutines.withContext -import kotlin.coroutines.resume -import kotlin.coroutines.suspendCoroutine /** * Simple interface for playing short streaming audio (from URLs). diff --git a/common/src/main/java/io/homeassistant/companion/android/common/util/DisabledLocationHandler.kt b/common/src/main/java/io/homeassistant/companion/android/common/util/DisabledLocationHandler.kt index c020ec660ba..47e3b64dc99 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/util/DisabledLocationHandler.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/util/DisabledLocationHandler.kt @@ -86,7 +86,7 @@ object DisabledLocationHandler { val notificationManager = NotificationManagerCompat.from(context) if (notificationManager.getActiveNotification(DISABLED_LOCATION_WARN_ID, DISABLED_LOCATION_WARN_ID.hashCode()) == null) { if (VERSION.SDK_INT >= VERSION_CODES.O) { - val channel = NotificationChannel(locationDisabledChannel, context.applicationContext.getString(commonR.string.location_warn_channel), NotificationManager.IMPORTANCE_DEFAULT) + val channel = NotificationChannel(CHANNEL_LOCATION_DISABLED, context.applicationContext.getString(commonR.string.location_warn_channel), NotificationManager.IMPORTANCE_DEFAULT) notificationManager.createNotificationChannel(channel) } @@ -97,7 +97,7 @@ object DisabledLocationHandler { PendingIntent.FLAG_IMMUTABLE ) - val notificationBuilder = NotificationCompat.Builder(context, locationDisabledChannel) + val notificationBuilder = NotificationCompat.Builder(context, CHANNEL_LOCATION_DISABLED) .setSmallIcon(commonR.drawable.ic_stat_ic_notification) .setColor(Color.RED) .setOngoing(true) diff --git a/common/src/main/java/io/homeassistant/companion/android/common/util/LocationPermissionInfoHandler.kt b/common/src/main/java/io/homeassistant/companion/android/common/util/LocationPermissionInfoHandler.kt index 2a1351a1bcb..2826a5eb099 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/util/LocationPermissionInfoHandler.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/util/LocationPermissionInfoHandler.kt @@ -9,8 +9,8 @@ object LocationPermissionInfoHandler { fun showLocationPermInfoDialogIfNeeded(context: Context, permissions: Array, continueYesCallback: () -> Unit, continueNoCallback: (() -> Unit)? = null) { if (permissions.any { - it == Manifest.permission.ACCESS_FINE_LOCATION || it == Manifest.permission.ACCESS_BACKGROUND_LOCATION - } + it == Manifest.permission.ACCESS_FINE_LOCATION || it == Manifest.permission.ACCESS_BACKGROUND_LOCATION + } ) { AlertDialog.Builder(context) .setTitle(commonR.string.location_perm_info_title) diff --git a/common/src/main/java/io/homeassistant/companion/android/common/util/TextUtil.kt b/common/src/main/java/io/homeassistant/companion/android/common/util/TextUtil.kt index 9fef2a161d8..5d1d5f99800 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/util/TextUtil.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/util/TextUtil.kt @@ -1,7 +1,7 @@ package io.homeassistant.companion.android.common.util -import okhttp3.internal.and import java.util.Locale +import okhttp3.internal.and private val HEX_ARRAY = "0123456789ABCDEF".toCharArray() diff --git a/common/src/main/java/io/homeassistant/companion/android/common/util/WearDataMessages.kt b/common/src/main/java/io/homeassistant/companion/android/common/util/WearDataMessages.kt index 278c8ee7a3c..2f065f1cc5b 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/util/WearDataMessages.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/util/WearDataMessages.kt @@ -17,8 +17,7 @@ object WearDataMessages { const val CONFIG_SERVER_REFRESH_TOKEN = "serverRefreshToken" const val CONFIG_SUPPORTED_DOMAINS = "supportedDomains" const val CONFIG_FAVORITES = "favorites" - const val CONFIG_TEMPLATE_TILE = "templateTile" - const val CONFIG_TEMPLATE_TILE_REFRESH_INTERVAL = "templateTileRefreshInterval" + const val CONFIG_TEMPLATE_TILES = "templateTiles" const val LOGIN_RESULT_EXCEPTION = "exception" } diff --git a/common/src/main/java/io/homeassistant/companion/android/database/AppDatabase.kt b/common/src/main/java/io/homeassistant/companion/android/database/AppDatabase.kt index f4089a77486..5f71eeb8eea 100644 --- a/common/src/main/java/io/homeassistant/companion/android/database/AppDatabase.kt +++ b/common/src/main/java/io/homeassistant/companion/android/database/AppDatabase.kt @@ -30,8 +30,9 @@ import androidx.room.migration.AutoMigrationSpec import androidx.room.migration.Migration import androidx.sqlite.db.SupportSQLiteDatabase import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.IntegrationRepository -import io.homeassistant.companion.android.common.util.databaseChannel +import io.homeassistant.companion.android.common.util.CHANNEL_DATABASE import io.homeassistant.companion.android.database.authentication.Authentication import io.homeassistant.companion.android.database.authentication.AuthenticationDao import io.homeassistant.companion.android.database.location.LocationHistoryDao @@ -72,9 +73,8 @@ import io.homeassistant.companion.android.database.widget.TemplateWidgetDao import io.homeassistant.companion.android.database.widget.TemplateWidgetEntity import io.homeassistant.companion.android.database.widget.WidgetBackgroundTypeConverter import io.homeassistant.companion.android.database.widget.WidgetTapActionConverter -import kotlinx.coroutines.runBlocking import java.util.UUID -import io.homeassistant.companion.android.common.R as commonR +import kotlinx.coroutines.runBlocking @Database( entities = [ @@ -212,38 +212,38 @@ abstract class AppDatabase : RoomDatabase() { } private val MIGRATION_1_2 = object : Migration(1, 2) { - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL( + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL( "CREATE TABLE IF NOT EXISTS `sensors` (`unique_id` TEXT NOT NULL, `enabled` INTEGER NOT NULL, `registered` INTEGER NOT NULL, `state` TEXT NOT NULL, PRIMARY KEY(`unique_id`))" ) } } private val MIGRATION_2_3 = object : Migration(2, 3) { - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("CREATE TABLE IF NOT EXISTS `button_widgets` (`id` INTEGER NOT NULL, `icon_id` INTEGER NOT NULL, `domain` TEXT NOT NULL, `service` TEXT NOT NULL, `service_data` TEXT NOT NULL, `label` TEXT, PRIMARY KEY(`id`))") - database.execSQL("CREATE TABLE IF NOT EXISTS `static_widget` (`id` INTEGER NOT NULL, `entity_id` TEXT NOT NULL, `attribute_id` TEXT, `label` TEXT, PRIMARY KEY(`id`))") + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL("CREATE TABLE IF NOT EXISTS `button_widgets` (`id` INTEGER NOT NULL, `icon_id` INTEGER NOT NULL, `domain` TEXT NOT NULL, `service` TEXT NOT NULL, `service_data` TEXT NOT NULL, `label` TEXT, PRIMARY KEY(`id`))") + db.execSQL("CREATE TABLE IF NOT EXISTS `static_widget` (`id` INTEGER NOT NULL, `entity_id` TEXT NOT NULL, `attribute_id` TEXT, `label` TEXT, PRIMARY KEY(`id`))") } } private val MIGRATION_3_4 = object : Migration(3, 4) { - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("ALTER TABLE `static_widget` ADD `text_size` FLOAT NOT NULL DEFAULT '30'") - database.execSQL("ALTER TABLE `static_widget` ADD `separator` TEXT NOT NULL DEFAULT ' '") + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL("ALTER TABLE `static_widget` ADD `text_size` FLOAT NOT NULL DEFAULT '30'") + db.execSQL("ALTER TABLE `static_widget` ADD `separator` TEXT NOT NULL DEFAULT ' '") } } private val MIGRATION_4_5 = object : Migration(4, 5) { - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("CREATE TABLE IF NOT EXISTS `template_widgets` (`id` INTEGER NOT NULL, `template` TEXT NOT NULL, PRIMARY KEY(`id`))") + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL("CREATE TABLE IF NOT EXISTS `template_widgets` (`id` INTEGER NOT NULL, `template` TEXT NOT NULL, PRIMARY KEY(`id`))") } } private val MIGRATION_5_6 = object : Migration(5, 6) { @SuppressLint("Range") - override fun migrate(database: SupportSQLiteDatabase) { + override fun migrate(db: SupportSQLiteDatabase) { try { - val widgets = database.query("SELECT * FROM `static_widget`") + val widgets = db.query("SELECT * FROM `static_widget`") widgets.use { if (widgets.count > 0) { val contentValues = widgets.map { widgets -> @@ -257,14 +257,14 @@ abstract class AppDatabase : RoomDatabase() { put("attribute_separator", " ") } } - database.execSQL("DROP TABLE IF EXISTS `static_widget`") - database.execSQL("CREATE TABLE IF NOT EXISTS `static_widget` (`id` INTEGER NOT NULL, `entity_id` TEXT NOT NULL, `attribute_ids` TEXT, `label` TEXT, `text_size` FLOAT NOT NULL DEFAULT '30', `state_separator` TEXT NOT NULL DEFAULT '', `attribute_separator` TEXT NOT NULL DEFAULT '', PRIMARY KEY(`id`))") + db.execSQL("DROP TABLE IF EXISTS `static_widget`") + db.execSQL("CREATE TABLE IF NOT EXISTS `static_widget` (`id` INTEGER NOT NULL, `entity_id` TEXT NOT NULL, `attribute_ids` TEXT, `label` TEXT, `text_size` FLOAT NOT NULL DEFAULT '30', `state_separator` TEXT NOT NULL DEFAULT '', `attribute_separator` TEXT NOT NULL DEFAULT '', PRIMARY KEY(`id`))") for (cv in contentValues) { - database.insert("static_widget", 0, cv) + db.insert("static_widget", 0, cv) } } else { - database.execSQL("DROP TABLE IF EXISTS `static_widget`") - database.execSQL("CREATE TABLE IF NOT EXISTS `static_widget` (`id` INTEGER NOT NULL, `entity_id` TEXT NOT NULL, `attribute_ids` TEXT, `label` TEXT, `text_size` FLOAT NOT NULL DEFAULT '30', `state_separator` TEXT NOT NULL DEFAULT '', `attribute_separator` TEXT NOT NULL DEFAULT '', PRIMARY KEY(`id`))") + db.execSQL("DROP TABLE IF EXISTS `static_widget`") + db.execSQL("CREATE TABLE IF NOT EXISTS `static_widget` (`id` INTEGER NOT NULL, `entity_id` TEXT NOT NULL, `attribute_ids` TEXT, `label` TEXT, `text_size` FLOAT NOT NULL DEFAULT '30', `state_separator` TEXT NOT NULL DEFAULT '', `attribute_separator` TEXT NOT NULL DEFAULT '', PRIMARY KEY(`id`))") } } widgets.close() @@ -276,10 +276,10 @@ abstract class AppDatabase : RoomDatabase() { private val MIGRATION_6_7 = object : Migration(6, 7) { @SuppressLint("Range") - override fun migrate(database: SupportSQLiteDatabase) { + override fun migrate(db: SupportSQLiteDatabase) { var migrationFailed = false val sensors = try { - database.query("SELECT * FROM sensors").use { cursor -> + db.query("SELECT * FROM sensors").use { cursor -> cursor.map { ContentValues().also { it.put("id", cursor.getString(cursor.getColumnIndex("unique_id"))) @@ -302,38 +302,38 @@ abstract class AppDatabase : RoomDatabase() { Log.e(TAG, "Unable to migrate, proceeding with recreating the table", e) null } - database.execSQL("DROP TABLE IF EXISTS `sensors`") - database.execSQL("CREATE TABLE IF NOT EXISTS `sensors` (`id` TEXT NOT NULL, `enabled` INTEGER NOT NULL, `registered` INTEGER NOT NULL, `state` TEXT NOT NULL, `state_type` TEXT NOT NULL, `type` TEXT NOT NULL, `icon` TEXT NOT NULL, `name` TEXT NOT NULL, `device_class` TEXT, `unit_of_measurement` TEXT, PRIMARY KEY(`id`))") + db.execSQL("DROP TABLE IF EXISTS `sensors`") + db.execSQL("CREATE TABLE IF NOT EXISTS `sensors` (`id` TEXT NOT NULL, `enabled` INTEGER NOT NULL, `registered` INTEGER NOT NULL, `state` TEXT NOT NULL, `state_type` TEXT NOT NULL, `type` TEXT NOT NULL, `icon` TEXT NOT NULL, `name` TEXT NOT NULL, `device_class` TEXT, `unit_of_measurement` TEXT, PRIMARY KEY(`id`))") sensors?.forEach { - database.insert("sensors", OnConflictStrategy.REPLACE, it) + db.insert("sensors", OnConflictStrategy.REPLACE, it) } if (migrationFailed) { notifyMigrationFailed() } - database.execSQL("CREATE TABLE IF NOT EXISTS `sensor_attributes` (`sensor_id` TEXT NOT NULL, `name` TEXT NOT NULL, `value` TEXT NOT NULL, PRIMARY KEY(`sensor_id`, `name`))") + db.execSQL("CREATE TABLE IF NOT EXISTS `sensor_attributes` (`sensor_id` TEXT NOT NULL, `name` TEXT NOT NULL, `value` TEXT NOT NULL, PRIMARY KEY(`sensor_id`, `name`))") } } private val MIGRATION_7_8 = object : Migration(7, 8) { - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("ALTER TABLE `sensor_attributes` ADD `value_type` TEXT NOT NULL DEFAULT 'string'") + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL("ALTER TABLE `sensor_attributes` ADD `value_type` TEXT NOT NULL DEFAULT 'string'") } } private val MIGRATION_8_9 = object : Migration(8, 9) { - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("ALTER TABLE `sensors` ADD `state_changed` INTEGER NOT NULL DEFAULT ''") + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL("ALTER TABLE `sensors` ADD `state_changed` INTEGER NOT NULL DEFAULT ''") } } private val MIGRATION_9_10 = object : Migration(9, 10) { @SuppressLint("Range") - override fun migrate(database: SupportSQLiteDatabase) { + override fun migrate(db: SupportSQLiteDatabase) { var migrationFailed = false val sensors = try { - database.query("SELECT * FROM sensors").use { cursor -> + db.query("SELECT * FROM sensors").use { cursor -> cursor.map { ContentValues().also { it.put("id", cursor.getString(cursor.getColumnIndex("id"))) @@ -356,11 +356,11 @@ abstract class AppDatabase : RoomDatabase() { Log.e(TAG, "Unable to migrate, proceeding with recreating the table", e) null } - database.execSQL("DROP TABLE IF EXISTS `sensors`") - database.execSQL("CREATE TABLE IF NOT EXISTS `sensors` (`id` TEXT NOT NULL, `enabled` INTEGER NOT NULL, `registered` INTEGER NOT NULL, `state` TEXT NOT NULL, `last_sent_state` TEXT NOT NULL, `state_type` TEXT NOT NULL, `type` TEXT NOT NULL, `icon` TEXT NOT NULL, `name` TEXT NOT NULL, `device_class` TEXT, `unit_of_measurement` TEXT, PRIMARY KEY(`id`))") + db.execSQL("DROP TABLE IF EXISTS `sensors`") + db.execSQL("CREATE TABLE IF NOT EXISTS `sensors` (`id` TEXT NOT NULL, `enabled` INTEGER NOT NULL, `registered` INTEGER NOT NULL, `state` TEXT NOT NULL, `last_sent_state` TEXT NOT NULL, `state_type` TEXT NOT NULL, `type` TEXT NOT NULL, `icon` TEXT NOT NULL, `name` TEXT NOT NULL, `device_class` TEXT, `unit_of_measurement` TEXT, PRIMARY KEY(`id`))") sensors?.forEach { - database.insert("sensors", OnConflictStrategy.REPLACE, it) + db.insert("sensors", OnConflictStrategy.REPLACE, it) } if (migrationFailed) { notifyMigrationFailed() @@ -369,46 +369,46 @@ abstract class AppDatabase : RoomDatabase() { } private val MIGRATION_10_11 = object : Migration(10, 11) { - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("CREATE TABLE IF NOT EXISTS `sensor_settings` (`sensor_id` TEXT NOT NULL, `name` TEXT NOT NULL, `value` TEXT NOT NULL, `value_type` TEXT NOT NULL DEFAULT 'string', PRIMARY KEY(`sensor_id`, `name`))") + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL("CREATE TABLE IF NOT EXISTS `sensor_settings` (`sensor_id` TEXT NOT NULL, `name` TEXT NOT NULL, `value` TEXT NOT NULL, `value_type` TEXT NOT NULL DEFAULT 'string', PRIMARY KEY(`sensor_id`, `name`))") } } private val MIGRATION_11_12 = object : Migration(11, 12) { - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("CREATE TABLE IF NOT EXISTS `mediaplayctrls_widgets` (`id` INTEGER NOT NULL, `entityId` TEXT NOT NULL, `label` TEXT, `showSkip` INTEGER NOT NULL, `showSeek` INTEGER NOT NULL, PRIMARY KEY(`id`))") + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL("CREATE TABLE IF NOT EXISTS `mediaplayctrls_widgets` (`id` INTEGER NOT NULL, `entityId` TEXT NOT NULL, `label` TEXT, `showSkip` INTEGER NOT NULL, `showSeek` INTEGER NOT NULL, PRIMARY KEY(`id`))") } } private val MIGRATION_12_13 = object : Migration(12, 13) { - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("CREATE TABLE IF NOT EXISTS `notification_history` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `received` INTEGER NOT NULL, `message` TEXT NOT NULL, `data` TEXT NOT NULL)") + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL("CREATE TABLE IF NOT EXISTS `notification_history` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `received` INTEGER NOT NULL, `message` TEXT NOT NULL, `data` TEXT NOT NULL)") } } private val MIGRATION_13_14 = object : Migration(13, 14) { - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("ALTER TABLE `static_widget` ADD `last_update` TEXT NOT NULL DEFAULT ''") - database.execSQL("ALTER TABLE `template_widgets` ADD `last_update` TEXT NOT NULL DEFAULT ''") + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL("ALTER TABLE `static_widget` ADD `last_update` TEXT NOT NULL DEFAULT ''") + db.execSQL("ALTER TABLE `template_widgets` ADD `last_update` TEXT NOT NULL DEFAULT ''") } } private val MIGRATION_14_15 = object : Migration(14, 15) { - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("ALTER TABLE `sensor_settings` ADD `enabled` INTEGER NOT NULL DEFAULT '1'") + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL("ALTER TABLE `sensor_settings` ADD `enabled` INTEGER NOT NULL DEFAULT '1'") } } private val MIGRATION_15_16 = object : Migration(15, 16) { - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("CREATE TABLE IF NOT EXISTS `camera_widgets` (`id` INTEGER NOT NULL, `entityId` TEXT NOT NULL, PRIMARY KEY(`id`))") + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL("CREATE TABLE IF NOT EXISTS `camera_widgets` (`id` INTEGER NOT NULL, `entityId` TEXT NOT NULL, PRIMARY KEY(`id`))") } } private val MIGRATION_16_17 = object : Migration(16, 17) { @SuppressLint("Range") - override fun migrate(database: SupportSQLiteDatabase) { - val cursor = database.query("SELECT * FROM sensor_settings") + override fun migrate(db: SupportSQLiteDatabase) { + val cursor = db.query("SELECT * FROM sensor_settings") val sensorSettings = mutableListOf() var migrationFailed = false try { @@ -420,20 +420,14 @@ abstract class AppDatabase : RoomDatabase() { val currentSensorSettingName = cursor.getString(cursor.getColumnIndex("name")) var entries = "" var newSensorSettingName = currentSensorSettingName - // Alarm + if (currentSensorId == "next_alarm" && currentSensorSettingName == "Allow List") { newSensorSettingName = "nextalarm_allow_list" - } - // Notification - else if ((currentSensorId == "last_removed_notification" || currentSensorId == "last_notification") && currentSensorSettingName == "Allow List") { + } else if ((currentSensorId == "last_removed_notification" || currentSensorId == "last_notification") && currentSensorSettingName == "Allow List") { newSensorSettingName = "notification_allow_list" - } - // Geocode - else if (currentSensorId == "geocoded_location" && currentSensorSettingName == "Minimum Accuracy") { + } else if (currentSensorId == "geocoded_location" && currentSensorSettingName == "Minimum Accuracy") { newSensorSettingName = "geocode_minimum_accuracy" - } - // Location - else if ((currentSensorId == "zone_background" || currentSensorId == "accurate_location" || currentSensorId == "location_background") && currentSensorSettingName == "Minimum Accuracy") { + } else if ((currentSensorId == "zone_background" || currentSensorId == "accurate_location" || currentSensorId == "location_background") && currentSensorSettingName == "Minimum Accuracy") { newSensorSettingName = "location_minimum_accuracy" } else if (currentSensorId == "accurate_location" && currentSensorSettingName == "Minimum time between updates") { newSensorSettingName = "location_minimum_time_updates" @@ -449,9 +443,7 @@ abstract class AppDatabase : RoomDatabase() { newSensorSettingName = "location_ham_only_enter_zone" } else if (currentSensorId == "location_background" && currentSensorSettingName == "High accuracy mode trigger range for zone (meters)") { newSensorSettingName = "location_ham_trigger_range" - } - // Bluetooth - else if (currentSensorId == "ble_emitter" && currentSensorSettingName == "UUID") { + } else if (currentSensorId == "ble_emitter" && currentSensorSettingName == "UUID") { newSensorSettingName = "ble_uuid" } else if (currentSensorId == "ble_emitter" && currentSensorSettingName == "Major") { newSensorSettingName = "ble_major" @@ -464,19 +456,13 @@ abstract class AppDatabase : RoomDatabase() { newSensorSettingName = "ble_transmit_enabled" } else if (currentSensorId == "ble_emitter" && currentSensorSettingName == "Include when enabling all sensors") { newSensorSettingName = "ble_enable_toggle_all" - } - // Last reboot - else if (currentSensorId == "last_reboot" && currentSensorSettingName == "deadband") { + } else if (currentSensorId == "last_reboot" && currentSensorSettingName == "deadband") { newSensorSettingName = "lastreboot_deadband" - } - // Last update - else if (currentSensorId == "last_update" && currentSensorSettingName == "Add New Intent") { + } else if (currentSensorId == "last_update" && currentSensorSettingName == "Add New Intent") { newSensorSettingName = "lastupdate_add_new_intent" } else if (currentSensorId == "last_update" && currentSensorSettingName.startsWith("intent")) { newSensorSettingName = "lastupdate_intent_var1:" + currentSensorSettingName.substringAfter("intent") + ":" - } - // Network - else if (currentSensorId == "wifi_bssid" && currentSensorSettingName == "get_current_bssid") { + } else if (currentSensorId == "wifi_bssid" && currentSensorSettingName == "get_current_bssid") { newSensorSettingName = "network_get_current_bssid" } else if (currentSensorId == "wifi_bssid" && currentSensorSettingName.startsWith("replace_")) { newSensorSettingName = "network_replace_mac_var1:" + currentSensorSettingName.substringAfter("replace_") + ":" @@ -495,11 +481,11 @@ abstract class AppDatabase : RoomDatabase() { migrationFailed = true Log.e(TAG, "Unable to migrate, proceeding with recreating the table", e) } - database.execSQL("DROP TABLE IF EXISTS `sensor_settings`") - database.execSQL("CREATE TABLE IF NOT EXISTS `sensor_settings` (`sensor_id` TEXT NOT NULL, `name` TEXT NOT NULL, `value` TEXT NOT NULL, `value_type` TEXT NOT NULL DEFAULT 'string', `entries` TEXT NOT NULL, `enabled` INTEGER NOT NULL DEFAULT '1', PRIMARY KEY(`sensor_id`, `name`))") + db.execSQL("DROP TABLE IF EXISTS `sensor_settings`") + db.execSQL("CREATE TABLE IF NOT EXISTS `sensor_settings` (`sensor_id` TEXT NOT NULL, `name` TEXT NOT NULL, `value` TEXT NOT NULL, `value_type` TEXT NOT NULL DEFAULT 'string', `entries` TEXT NOT NULL, `enabled` INTEGER NOT NULL DEFAULT '1', PRIMARY KEY(`sensor_id`, `name`))") sensorSettings.forEach { - database.insert("sensor_settings", OnConflictStrategy.REPLACE, it) + db.insert("sensor_settings", OnConflictStrategy.REPLACE, it) } if (migrationFailed) { notifyMigrationFailed() @@ -508,47 +494,47 @@ abstract class AppDatabase : RoomDatabase() { } private val MIGRATION_17_18 = object : Migration(17, 18) { - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("CREATE TABLE IF NOT EXISTS `qs_tiles` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `tileId` TEXT NOT NULL, `icon_id` INTEGER, `entityId` TEXT NOT NULL, `label` TEXT NOT NULL, `subtitle` TEXT)") + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL("CREATE TABLE IF NOT EXISTS `qs_tiles` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `tileId` TEXT NOT NULL, `icon_id` INTEGER, `entityId` TEXT NOT NULL, `label` TEXT NOT NULL, `subtitle` TEXT)") } } private val MIGRATION_18_19 = object : Migration(18, 19) { - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("ALTER TABLE `sensors` ADD `state_class` TEXT") - database.execSQL("ALTER TABLE `sensors` ADD `entity_category` TEXT") - database.execSQL("ALTER TABLE `sensors` ADD `core_registration` TEXT") - database.execSQL("ALTER TABLE `sensors` ADD `app_registration` TEXT") + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL("ALTER TABLE `sensors` ADD `state_class` TEXT") + db.execSQL("ALTER TABLE `sensors` ADD `entity_category` TEXT") + db.execSQL("ALTER TABLE `sensors` ADD `core_registration` TEXT") + db.execSQL("ALTER TABLE `sensors` ADD `app_registration` TEXT") } } private val MIGRATION_19_20 = object : Migration(19, 20) { - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("CREATE TABLE IF NOT EXISTS `favorites` (`id` TEXT PRIMARY KEY NOT NULL, `position` INTEGER)") + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL("CREATE TABLE IF NOT EXISTS `favorites` (`id` TEXT PRIMARY KEY NOT NULL, `position` INTEGER)") } } private val MIGRATION_20_21 = object : Migration(20, 21) { - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("CREATE TABLE IF NOT EXISTS `settings` (`id` INTEGER NOT NULL, `websocketSetting` TEXT NOT NULL, PRIMARY KEY(`id`))") + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL("CREATE TABLE IF NOT EXISTS `settings` (`id` INTEGER NOT NULL, `websocketSetting` TEXT NOT NULL, PRIMARY KEY(`id`))") } } private val MIGRATION_21_22 = object : Migration(21, 22) { - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("ALTER TABLE `notification_history` ADD `source` TEXT NOT NULL DEFAULT 'FCM'") + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL("ALTER TABLE `notification_history` ADD `source` TEXT NOT NULL DEFAULT 'FCM'") } } private val MIGRATION_22_23 = object : Migration(22, 23) { - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("ALTER TABLE `mediaplayctrls_widgets` ADD `showVolume` INTEGER NOT NULL DEFAULT '0'") + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL("ALTER TABLE `mediaplayctrls_widgets` ADD `showVolume` INTEGER NOT NULL DEFAULT '0'") } } private val MIGRATION_23_24 = object : Migration(23, 24) { - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("ALTER TABLE `settings` ADD `sensorUpdateFrequency` TEXT NOT NULL DEFAULT 'NORMAL'") + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL("ALTER TABLE `settings` ADD `sensorUpdateFrequency` TEXT NOT NULL DEFAULT 'NORMAL'") } } @@ -835,10 +821,10 @@ abstract class AppDatabase : RoomDatabase() { } @SuppressLint("Range") - override fun migrate(database: SupportSQLiteDatabase) { + override fun migrate(db: SupportSQLiteDatabase) { var migrationFailed = false val widgets = try { - database.query("SELECT * FROM `button_widgets`").use { cursor -> + db.query("SELECT * FROM `button_widgets`").use { cursor -> cursor.map { ContentValues().apply { put("id", cursor.getString(cursor.getColumnIndex("id"))) @@ -860,15 +846,15 @@ abstract class AppDatabase : RoomDatabase() { Log.e(TAG, "Unable to migrate, proceeding with recreating the table", e) null } - database.execSQL("DROP TABLE IF EXISTS `button_widgets`") - database.execSQL("CREATE TABLE IF NOT EXISTS `button_widgets` (`id` INTEGER NOT NULL, `server_id` INTEGER NOT NULL DEFAULT 0, `icon_name` TEXT NOT NULL, `domain` TEXT NOT NULL, `service` TEXT NOT NULL, `service_data` TEXT NOT NULL, `label` TEXT, `background_type` TEXT NOT NULL DEFAULT 'DAYNIGHT', `text_color` TEXT, `require_authentication` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(`id`))") + db.execSQL("DROP TABLE IF EXISTS `button_widgets`") + db.execSQL("CREATE TABLE IF NOT EXISTS `button_widgets` (`id` INTEGER NOT NULL, `server_id` INTEGER NOT NULL DEFAULT 0, `icon_name` TEXT NOT NULL, `domain` TEXT NOT NULL, `service` TEXT NOT NULL, `service_data` TEXT NOT NULL, `label` TEXT, `background_type` TEXT NOT NULL DEFAULT 'DAYNIGHT', `text_color` TEXT, `require_authentication` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(`id`))") widgets?.forEach { - database.insert("button_widgets", OnConflictStrategy.REPLACE, it) + db.insert("button_widgets", OnConflictStrategy.REPLACE, it) } Log.d(TAG, "Migrated ${widgets?.size ?: "no"} button widgets to MDI icon names") val tiles = try { - database.query("SELECT * FROM `qs_tiles`").use { cursor -> + db.query("SELECT * FROM `qs_tiles`").use { cursor -> cursor.map { ContentValues().apply { put("id", cursor.getString(cursor.getColumnIndex("id"))) @@ -893,10 +879,10 @@ abstract class AppDatabase : RoomDatabase() { Log.e(TAG, "Unable to migrate, proceeding with recreating the table", e) null } - database.execSQL("DROP TABLE IF EXISTS `qs_tiles`") - database.execSQL("CREATE TABLE IF NOT EXISTS `qs_tiles` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `tile_id` TEXT NOT NULL, `added` INTEGER NOT NULL DEFAULT 1, `server_id` INTEGER NOT NULL DEFAULT 0, `icon_name` TEXT, `entity_id` TEXT NOT NULL, `label` TEXT NOT NULL, `subtitle` TEXT, `should_vibrate` INTEGER NOT NULL DEFAULT 0, `auth_required` INTEGER NOT NULL DEFAULT 0)") + db.execSQL("DROP TABLE IF EXISTS `qs_tiles`") + db.execSQL("CREATE TABLE IF NOT EXISTS `qs_tiles` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `tile_id` TEXT NOT NULL, `added` INTEGER NOT NULL DEFAULT 1, `server_id` INTEGER NOT NULL DEFAULT 0, `icon_name` TEXT, `entity_id` TEXT NOT NULL, `label` TEXT NOT NULL, `subtitle` TEXT, `should_vibrate` INTEGER NOT NULL DEFAULT 0, `auth_required` INTEGER NOT NULL DEFAULT 0)") tiles?.forEach { - database.insert("qs_tiles", OnConflictStrategy.REPLACE, it) + db.insert("qs_tiles", OnConflictStrategy.REPLACE, it) } Log.d(TAG, "Migrated ${tiles?.size ?: "no"} QS tiles to MDI icon names") @@ -911,10 +897,10 @@ abstract class AppDatabase : RoomDatabase() { val notificationManager = appContext.getSystemService()!! var notificationChannel = - notificationManager.getNotificationChannel(databaseChannel) + notificationManager.getNotificationChannel(CHANNEL_DATABASE) if (notificationChannel == null) { notificationChannel = NotificationChannel( - databaseChannel, + CHANNEL_DATABASE, TAG, NotificationManager.IMPORTANCE_HIGH ) @@ -925,7 +911,7 @@ abstract class AppDatabase : RoomDatabase() { private fun notifyMigrationFailed() { createNotificationChannel() - val notification = NotificationCompat.Builder(appContext, databaseChannel) + val notification = NotificationCompat.Builder(appContext, CHANNEL_DATABASE) .setSmallIcon(commonR.drawable.ic_stat_ic_notification) .setContentTitle(appContext.getString(commonR.string.database_migration_failed)) .setPriority(NotificationCompat.PRIORITY_HIGH) diff --git a/common/src/main/java/io/homeassistant/companion/android/database/IconDialogCompat.kt b/common/src/main/java/io/homeassistant/companion/android/database/IconDialogCompat.kt index 87e06fd1abc..b0e85ee2da6 100644 --- a/common/src/main/java/io/homeassistant/companion/android/database/IconDialogCompat.kt +++ b/common/src/main/java/io/homeassistant/companion/android/database/IconDialogCompat.kt @@ -4,11 +4,11 @@ import android.content.res.AssetManager import android.util.JsonReader import android.util.NoSuchPropertyException import androidx.annotation.WorkerThread +import javax.inject.Inject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.withContext -import javax.inject.Inject /** * Translation layer for IDs used by the old icondialog package to material icon names. diff --git a/common/src/main/java/io/homeassistant/companion/android/database/server/Server.kt b/common/src/main/java/io/homeassistant/companion/android/database/server/Server.kt index 2e4bb2d109c..9014a2b8269 100644 --- a/common/src/main/java/io/homeassistant/companion/android/database/server/Server.kt +++ b/common/src/main/java/io/homeassistant/companion/android/database/server/Server.kt @@ -40,5 +40,6 @@ data class Server( } enum class ServerType { - TEMPORARY, DEFAULT + TEMPORARY, + DEFAULT } diff --git a/common/src/main/java/io/homeassistant/companion/android/database/server/ServerConnectionInfo.kt b/common/src/main/java/io/homeassistant/companion/android/database/server/ServerConnectionInfo.kt index 1db2732c287..83e90ca39fa 100644 --- a/common/src/main/java/io/homeassistant/companion/android/database/server/ServerConnectionInfo.kt +++ b/common/src/main/java/io/homeassistant/companion/android/database/server/ServerConnectionInfo.kt @@ -8,8 +8,8 @@ import com.fasterxml.jackson.core.JsonProcessingException import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.fasterxml.jackson.module.kotlin.readValue import io.homeassistant.companion.android.common.data.wifi.WifiHelper -import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import java.net.URL +import okhttp3.HttpUrl.Companion.toHttpUrlOrNull data class ServerConnectionInfo( @ColumnInfo(name = "external_url") diff --git a/common/src/main/java/io/homeassistant/companion/android/database/widget/ThemeableWidgetEntity.kt b/common/src/main/java/io/homeassistant/companion/android/database/widget/ThemeableWidgetEntity.kt index d640b2eb89a..d738edec22a 100644 --- a/common/src/main/java/io/homeassistant/companion/android/database/widget/ThemeableWidgetEntity.kt +++ b/common/src/main/java/io/homeassistant/companion/android/database/widget/ThemeableWidgetEntity.kt @@ -8,7 +8,9 @@ interface ThemeableWidgetEntity { } enum class WidgetBackgroundType { - DYNAMICCOLOR, DAYNIGHT, TRANSPARENT + DYNAMICCOLOR, + DAYNIGHT, + TRANSPARENT } class WidgetBackgroundTypeConverter { diff --git a/common/src/main/java/io/homeassistant/companion/android/database/widget/WidgetTapAction.kt b/common/src/main/java/io/homeassistant/companion/android/database/widget/WidgetTapAction.kt index c06aa3a7b87..a5187b764b0 100644 --- a/common/src/main/java/io/homeassistant/companion/android/database/widget/WidgetTapAction.kt +++ b/common/src/main/java/io/homeassistant/companion/android/database/widget/WidgetTapAction.kt @@ -3,7 +3,8 @@ package io.homeassistant.companion.android.database.widget import androidx.room.TypeConverter enum class WidgetTapAction { - REFRESH, TOGGLE + REFRESH, + TOGGLE } class WidgetTapActionConverter { diff --git a/common/src/main/java/io/homeassistant/companion/android/util/IntervalToString.kt b/common/src/main/java/io/homeassistant/companion/android/util/IntervalToString.kt index 74ed1a59d7b..e3e3b2e840b 100644 --- a/common/src/main/java/io/homeassistant/companion/android/util/IntervalToString.kt +++ b/common/src/main/java/io/homeassistant/companion/android/util/IntervalToString.kt @@ -6,6 +6,7 @@ import io.homeassistant.companion.android.common.R fun intervalToString(context: Context, interval: Int): String { return when { interval == 0 -> context.getString(R.string.interval_manual) + interval == 1 -> context.getString(R.string.interval_in_view) interval >= 60 * 60 -> context.resources.getQuantityString(R.plurals.interval_hours, interval / 60 / 60, interval / 60 / 60) interval >= 60 -> context.resources.getQuantityString(R.plurals.interval_minutes, interval / 60, interval / 60) else -> context.resources.getQuantityString(R.plurals.interval_seconds, interval, interval) diff --git a/common/src/main/java/io/homeassistant/companion/android/util/UrlUtil.kt b/common/src/main/java/io/homeassistant/companion/android/util/UrlUtil.kt index a2d55519d81..7e4c6bb5e31 100644 --- a/common/src/main/java/io/homeassistant/companion/android/util/UrlUtil.kt +++ b/common/src/main/java/io/homeassistant/companion/android/util/UrlUtil.kt @@ -4,11 +4,11 @@ import android.net.Uri import android.util.Log import io.homeassistant.companion.android.common.data.MalformedHttpUrlException import io.homeassistant.companion.android.common.data.authentication.impl.AuthenticationService +import java.net.URI +import java.net.URL import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrlOrNull -import java.net.URI -import java.net.URL object UrlUtil { fun formattedUrlString(url: String): String { diff --git a/common/src/main/res/values/strings.xml b/common/src/main/res/values/strings.xml index 95179839823..17244f84f2b 100644 --- a/common/src/main/res/values/strings.xml +++ b/common/src/main/res/values/strings.xml @@ -82,6 +82,7 @@ High accuracy mode Interactive Internal storage + IPv6 addresses Keyguard locked Keyguard secure Last notification @@ -132,8 +133,8 @@ Edit the tile settings and select a camera to show Camera tile #%d Camera tiles - Camera widgets - Displays the latest image from the camera + Picture widgets + Displays the latest picture from a camera or image entity Cancel View full change log Checking with Home Assistant @@ -248,7 +249,7 @@ Unable to connect to %1$s Error loading entities Unable to connect to Home Assistant. - Unable to communicate with Home Assistant because of a SSL error. Please ensure your certificate is valid. + Unable to communicate with Home Assistant because of a SSL error. Please ensure your certificate is valid. The \'Mobile App\' integration is required to use the app, but it is not available on your Home Assistant server. Please check your server configuration and try again. Errors There was an error loading Home Assistant. Please review the connection settings and try again.\n\nError Code: %1$d \nDescription: %2$s @@ -317,6 +318,7 @@ %d second %d seconds + In view Do not lock screen when dashboard is active Keep screen on Allow pinch-to-zoom gesture to zoom app window @@ -389,10 +391,10 @@ Log Logbook Logged in - Login - Login on your phone - Login Wear OS device - Logout + Log in + Log in on your phone + Log in Wear OS device + Log out Dashboard view or Dashboard Dashboard Mailbox @@ -406,10 +408,10 @@ The internal connection URL will be used when connected to one of the listed SSIDs. Manage SSID(s) Manage this notification - Setup and manage quick settings tiles here. They will not function until you set them up here. + Set up and manage quick settings tiles here. They will not function until you set them up here. Manage tiles Manage device %1$s - Edit your widgets, adding/deleting can only be done from the home screen + Edit your widgets. Adding/deleting can only be done from the home screen. Manage widgets Enter the URL of your Home Assistant server. Make sure it includes the protocol and port. For example:\n\nhttp://homeassistant.local:8123 or \nhttps://example.duckdns.org. Enter address manually @@ -452,7 +454,7 @@ This tag does not contain Home Assistant data Error while processing NFC tag Hold your device near an NFC tag - Setup NFC tags + Set up NFC tags Tag identifier NFC tags NFC tags @@ -597,11 +599,12 @@ Whether the WIFI hotspot is enabled Whether the device is in an interactive state Information about the total and available storage space internally + The IPv6 addresses bound to the currently active network interface Whether the keyguard is currently locked Whether the keyguard is secured by a PIN, pattern or password or a SIM card is currently locked - The details of the last notification. You must setup an allow list or explicitly allow all notifications to be sent.\n\nNote: Sending all notification data will result in heavy battery usage. + The details of the last notification. You must set up an allow list or explicitly allow all notifications to be sent.\n\nNote: Sending all notification data will result in heavy battery usage. The date and time of the devices last reboot. The setting below will allow you to adjust the deadband in milliseconds, if you still find the value to jump incorrectly. The default value is 60000 (1 minute). - The details of the last removed notification. This can be any notification either cleared by the user or removed by an application. You must setup an allow list or explicitly allow all notifications to be sent.\n\nNote: Sending all notification data will result in heavy battery usage. + The details of the last removed notification. This can be any notification either cleared by the user or removed by an application. You must set up an allow list or explicitly allow all notifications to be sent.\n\nNote: Sending all notification data will result in heavy battery usage. The intent action for the last update that was sent, periodic updates will show as \"SensorWorker\".\n\nEnabling the \"Add New Intent\" toggle will create 1 setting to allow you to register for a intent action. The toggle will switch back to off once a new setting is created so you will need to turn it back on to save more intent actions. You can also clear out the setting value to remove the setting in the next update.\n\nIf you are not receiving all intents then you will need to add categories that the intent expects. To do this you will need to add each category after the intent separated by a \",\" repeating until there are no more categories. For example an intent with 2 categories will be: \"intent,category1,category2\" as the setting value.\n\nYou must restart the application after making changes to these settings to take effect. The current level of illuminance Allow Home Assistant to send a notification to request an accurate location along with the application periodically requesting an accurate location. The Minimum Accuracy setting will allow you to decide how accurate the device location (in meters) has to be in order to send to Home Assistant. The Minimum time between updates (in milliseconds) keeps the device from sending the accurate location too often. The Include in sensor update setting will make a location request with each sensor update. @@ -846,7 +849,12 @@ Processing tag Template Template tile - Set template to display on Wear OS tile + Template tile #%d + Template tiles + Configure Template tiles + Select Template tile to manage + There are no Template tiles added yet - add one from the watch face to set it up + Configure templates to use as Wear OS tiles Change template in phone settings Template tile content Renders and displays a template @@ -969,8 +977,8 @@ Dynamic color Transparent Service button - Camera widget - Camera image + Picture widget + Image A custom component is preventing service data from loading. Unable to create widget. Unable to fetch data for configured entity. @@ -1145,6 +1153,10 @@ The Home Assistant server does not support Thread Updated network from Home Assistant on this device Manually update device and server Thread credentials and verify results + Imported credential + You don\'t have any credentials to import. + You are not connected to a local network. Connect to Wi-Fi or ethernet to import Thread credentials. + Thread is currently unavailable Vibrate when clicked Requires unlocked device No results yet diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 69036feffab..3c451377cd4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,9 +1,9 @@ [versions] -accompanist = "0.32.0" +accompanist = "0.34.0" activity-compose = "1.8.2" androidBeaconLibrary = "2.20" androidJunit5 = "1.9.3.0" -androidPlugin = "8.2.0" +androidPlugin = "8.2.2" androidSdk-compile = "34" androidSdk-min = "21" androidSdk-target = "33" @@ -12,62 +12,63 @@ androidSdk-wear-min = "26" androidSdk-wear-target = "32" appcompat = "1.6.1" biometric = "1.1.0" -blurView = "version-1.6.6" +blurView = "version-2.0.4" car-versions = "1.4.0-rc02" -changeLog = "3.4" +changeLog = "3.5" community-material-typeface = "7.0.96.0-kotlin" -compose-bom = "2023.10.01" -compose-compiler = "1.5.6" +compose-bom = "2024.02.01" +compose-compiler = "1.5.10" constraintlayout = "2.1.4" converterJackson = "2.9.0" coreKtx = "1.12.0" core-splashscreen = "1.1.0-alpha02" -cronet-embedded = "113.5672.61" +cronet-embedded = "119.6045.31" emojiJava = "5.1.1" -firebase-bom = "32.7.0" -firebaseAppdistributionGradle = "4.0.1" +firebase-bom = "32.7.2" +firebaseAppdistributionGradle = "4.1.0" fragment-ktx = "1.6.2" -googleServices = "4.4.0" -guava = "32.1.1-android" +googleServices = "4.4.1" +guava = "33.0.0-android" healthServicesClient = "1.0.0-rc01" -hilt = "2.49" +hilt = "2.50" iconics = "5.4.0" jackson-module-kotlin = "2.13.5" javaVersion = "11" -kotlinx-coroutines = "1.7.3" -kotlin = "1.9.21" -ksp = "1.9.21-1.0.15" -ktlint = "11.6.1" -lifecycle = "2.6.2" -material = "1.10.0" -media3 = "1.2.0" -navigation-compose = "2.7.6" +kotlinx-coroutines = "1.8.0" +kotlin = "1.9.22" +ksp = "1.9.22-1.0.17" +ktlint = "12.1.0" +lifecycle = "2.7.0" +material = "1.11.0" +media3 = "1.2.1" +navigation-compose = "2.7.7" okhttp = "4.12.0" paging = "3.2.1" picasso = "2.8" play-services-threadnetwork = "16.0.0" play-services-home = "16.0.0" -play-services-location = "21.0.1" +play-services-location = "21.1.0" play-services-wearable = "18.1.0" preference-ktx = "1.2.1" recyclerview = "1.3.2" reorderable = "0.9.6" retrofit = "2.9.0" room = "2.6.1" -sentry-android = "7.0.0" -watchfaceComplicationsDataSourceKtx = "1.2.0" +sentry-android = "7.4.0" +tools-desugar-jdk-libs = "2.0.4" +watchfaceComplicationsDataSourceKtx = "1.2.1" wear = "1.3.0" -wear-compose-foundation = "1.2.1" +wear-compose-foundation = "1.3.0" wear-compose-material = "1.0.0-alpha15" -wear-protolayout = "1.0.0" -wear-tiles = "1.2.0" +wear-protolayout = "1.1.0" +wear-tiles = "1.3.0" wear-tooling = "1.0.0" wearPhoneInteractions = "1.0.1" wearInput = "1.2.0-alpha02" -webkit = "1.9.0" +webkit = "1.10.0" wear-remote-interactions = "1.0.0" workRuntimeKtx = "2.9.0" -horologist = "0.5.14" +horologist = "0.5.22" firebase="2.9.6" @@ -85,7 +86,6 @@ ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" } firebase-crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebase" } [libraries] -accompanist-themeadapter-material = { module = "com.google.accompanist:accompanist-themeadapter-material", version.ref = "accompanist" } accompanist-systemuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanist" } activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activity-compose" } activity-ktx = { module = "androidx.activity:activity-ktx", version.ref = "activity-compose" } @@ -160,6 +160,7 @@ recyclerview = { module = "androidx.recyclerview:recyclerview", version.ref = "r reorderable = { module = "org.burnoutcrew.composereorderable:reorderable", version.ref = "reorderable" } retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" } sentry-android = { module = "io.sentry:sentry-android", version.ref = "sentry-android" } +tools-desugar-jdk = { module = "com.android.tools:desugar_jdk_libs", version.ref = "tools-desugar-jdk-libs" } wear = { module = "androidx.wear:wear", version.ref = "wear" } wear-compose-foundation = { module = "androidx.wear.compose:compose-foundation", version.ref = "wear-compose-foundation" } wear-compose-material = { module = "androidx.wear.compose:compose-material3", version.ref = "wear-compose-material" } diff --git a/wear/build.gradle.kts b/wear/build.gradle.kts index 0441954c636..9994046c71c 100644 --- a/wear/build.gradle.kts +++ b/wear/build.gradle.kts @@ -58,6 +58,7 @@ android { } compileOptions { + isCoreLibraryDesugaringEnabled = true sourceCompatibility(libs.versions.javaVersion.get()) targetCompatibility(libs.versions.javaVersion.get()) } @@ -74,6 +75,8 @@ android { dependencies { implementation(project(":common")) + coreLibraryDesugaring(libs.tools.desugar.jdk) + implementation(libs.kotlin.stdlib) implementation(libs.kotlinx.coroutines.android) implementation(libs.kotlinx.coroutines.guava) @@ -125,10 +128,3 @@ dependencies { implementation(platform(libs.firebase.bom)) implementation(libs.firebase.messaging) } - -// https://github.com/google/guava/releases/tag/v32.1.0: Reporting dependencies that overlap with Guava -configurations.all { - resolutionStrategy.capabilitiesResolution.withCapability("com.google.guava:listenablefuture") { - select("com.google.guava:guava:0") - } -} diff --git a/wear/src/main/AndroidManifest.xml b/wear/src/main/AndroidManifest.xml index 19dc629b31b..b16e17708d8 100644 --- a/wear/src/main/AndroidManifest.xml +++ b/wear/src/main/AndroidManifest.xml @@ -97,6 +97,11 @@ + + + + + @@ -160,6 +165,14 @@ + + + + + android:path="/updateTemplateTiles" /> diff --git a/wear/src/main/java/io/homeassistant/companion/android/HomeAssistantApplication.kt b/wear/src/main/java/io/homeassistant/companion/android/HomeAssistantApplication.kt index 31df51a82da..1021c24de3f 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/HomeAssistantApplication.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/HomeAssistantApplication.kt @@ -13,14 +13,15 @@ import android.os.PowerManager import dagger.hilt.android.HiltAndroidApp import io.homeassistant.companion.android.common.data.keychain.KeyChainRepository import io.homeassistant.companion.android.common.data.keychain.KeyStoreRepositoryImpl +import io.homeassistant.companion.android.common.sensors.AudioSensorManager import io.homeassistant.companion.android.complications.ComplicationReceiver import io.homeassistant.companion.android.sensors.SensorReceiver +import javax.inject.Inject +import javax.inject.Named import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch -import javax.inject.Inject -import javax.inject.Named @HiltAndroidApp open class HomeAssistantApplication : Application() { @@ -73,6 +74,7 @@ open class HomeAssistantApplication : Application() { addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY) addAction(AudioManager.ACTION_HEADSET_PLUG) addAction(AudioManager.RINGER_MODE_CHANGED_ACTION) + addAction(AudioSensorManager.VOLUME_CHANGED_ACTION) } ) diff --git a/wear/src/main/java/io/homeassistant/companion/android/complications/ComplicationConfigViewModel.kt b/wear/src/main/java/io/homeassistant/companion/android/complications/ComplicationConfigViewModel.kt index 621ab488c5c..ae0bcd5980e 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/complications/ComplicationConfigViewModel.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/complications/ComplicationConfigViewModel.kt @@ -23,9 +23,9 @@ import io.homeassistant.companion.android.database.wear.EntityStateComplications import io.homeassistant.companion.android.database.wear.EntityStateComplicationsDao import io.homeassistant.companion.android.database.wear.FavoritesDao import io.homeassistant.companion.android.database.wear.getAllFlow +import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.launch -import javax.inject.Inject @HiltViewModel class ComplicationConfigViewModel @Inject constructor( @@ -39,7 +39,9 @@ class ComplicationConfigViewModel @Inject constructor( } enum class LoadingState { - LOADING, READY, ERROR + LOADING, + READY, + ERROR } val app = getApplication() diff --git a/wear/src/main/java/io/homeassistant/companion/android/complications/ComplicationReceiver.kt b/wear/src/main/java/io/homeassistant/companion/android/complications/ComplicationReceiver.kt index 8aa137d30a3..69570c2345d 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/complications/ComplicationReceiver.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/complications/ComplicationReceiver.kt @@ -10,11 +10,11 @@ import androidx.wear.watchface.complications.datasource.ComplicationDataSourceUp import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.conversation.ConversationActivity +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch -import javax.inject.Inject @AndroidEntryPoint class ComplicationReceiver : BroadcastReceiver() { diff --git a/wear/src/main/java/io/homeassistant/companion/android/complications/EntityStateDataSourceService.kt b/wear/src/main/java/io/homeassistant/companion/android/complications/EntityStateDataSourceService.kt index dc7a8b974d3..fdab1cf5209 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/complications/EntityStateDataSourceService.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/complications/EntityStateDataSourceService.kt @@ -22,8 +22,8 @@ import io.homeassistant.companion.android.common.data.integration.friendlyState import io.homeassistant.companion.android.common.data.integration.getIcon import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.database.wear.EntityStateComplicationsDao -import retrofit2.HttpException import javax.inject.Inject +import retrofit2.HttpException @AndroidEntryPoint class EntityStateDataSourceService : SuspendingComplicationDataSourceService() { @@ -163,7 +163,11 @@ class EntityStateDataSourceService : SuspendingComplicationDataSourceService() { setTapAction: Boolean = false ): ComplicationData { val text = PlainComplicationText.Builder( - if (setTapAction) { "+" } else { getText(textRes) } + if (setTapAction) { + "+" + } else { + getText(textRes) + } ).build() val contentDescription = PlainComplicationText.Builder(getText(R.string.complication_entity_state_content_description)).build() val tapAction = if (setTapAction) { diff --git a/wear/src/main/java/io/homeassistant/companion/android/conversation/ConversationActivity.kt b/wear/src/main/java/io/homeassistant/companion/android/conversation/ConversationActivity.kt index 6b5898ad0ec..b84943f12b3 100755 --- a/wear/src/main/java/io/homeassistant/companion/android/conversation/ConversationActivity.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/conversation/ConversationActivity.kt @@ -17,6 +17,7 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.conversation.views.LoadAssistView +import io.homeassistant.companion.android.home.HomeActivity import kotlinx.coroutines.launch @AndroidEntryPoint @@ -53,6 +54,9 @@ class ConversationActivity : ComponentActivity() { val launchIntent = conversationViewModel.onCreate(hasRecordingPermission()) if (launchIntent) { launchVoiceInputIntent() + } else if (!conversationViewModel.isRegistered()) { + startActivity(HomeActivity.newInstance(this@ConversationActivity)) + finish() } } diff --git a/wear/src/main/java/io/homeassistant/companion/android/conversation/ConversationViewModel.kt b/wear/src/main/java/io/homeassistant/companion/android/conversation/ConversationViewModel.kt index 46e6812c222..d669134a526 100755 --- a/wear/src/main/java/io/homeassistant/companion/android/conversation/ConversationViewModel.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/conversation/ConversationViewModel.kt @@ -9,6 +9,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.assist.AssistViewModelBase import io.homeassistant.companion.android.common.data.prefs.WearPrefsRepository import io.homeassistant.companion.android.common.data.servers.ServerManager @@ -16,9 +17,8 @@ import io.homeassistant.companion.android.common.data.websocket.impl.entities.As import io.homeassistant.companion.android.common.util.AudioRecorder import io.homeassistant.companion.android.common.util.AudioUrlPlayer import io.homeassistant.companion.android.conversation.views.AssistMessage -import kotlinx.coroutines.launch import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR +import kotlinx.coroutines.launch @HiltViewModel class ConversationViewModel @Inject constructor( @@ -129,6 +129,8 @@ class ConversationViewModel @Inject constructor( return false } + fun isRegistered(): Boolean = serverManager.isRegistered() + override fun getInput(): AssistInputMode = inputMode override fun setInput(inputMode: AssistInputMode) { diff --git a/wear/src/main/java/io/homeassistant/companion/android/conversation/views/ConversationView.kt b/wear/src/main/java/io/homeassistant/companion/android/conversation/views/ConversationView.kt index 456d3cc3336..fc1e83c6a30 100755 --- a/wear/src/main/java/io/homeassistant/companion/android/conversation/views/ConversationView.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/conversation/views/ConversationView.kt @@ -121,7 +121,7 @@ fun ConversationResultView( val scrollState = rememberScalingLazyListState() LaunchedEffect(conversation.size) { scrollState.scrollToItem( - if (inputMode != AssistViewModelBase.AssistInputMode.BLOCKED) conversation.size else (conversation.size - 1) + if (inputMode != AssistViewModelBase.AssistInputMode.BLOCKED) (conversation.size + 1) else conversation.size ) } if (hapticFeedback) { @@ -135,6 +135,9 @@ fun ConversationResultView( } ThemeLazyColumn(state = scrollState) { + item { + Spacer(Modifier.size(1.dp)) // This exists to allow the next item to be centered + } item { if (currentPipeline != null) { val textColor = LocalContentColor.current.copy(alpha = 0.38f) // disabled/hint alpha @@ -144,7 +147,8 @@ fun ConversationResultView( onClick = { onChangePipeline() }, onClickLabel = stringResource(R.string.assist_change_pipeline) ) - .padding(bottom = 4.dp) + .padding(bottom = 4.dp), + verticalAlignment = Alignment.CenterVertically ) { Text( text = currentPipeline.name, diff --git a/wear/src/main/java/io/homeassistant/companion/android/home/HomeActivity.kt b/wear/src/main/java/io/homeassistant/companion/android/home/HomeActivity.kt index c9ad3f8a01f..3f823f5b2d5 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/home/HomeActivity.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/home/HomeActivity.kt @@ -13,13 +13,14 @@ import androidx.lifecycle.repeatOnLifecycle import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.home.views.DEEPLINK_PREFIX_SET_CAMERA_TILE import io.homeassistant.companion.android.home.views.DEEPLINK_PREFIX_SET_SHORTCUT_TILE +import io.homeassistant.companion.android.home.views.DEEPLINK_PREFIX_SET_TEMPLATE_TILE import io.homeassistant.companion.android.home.views.LoadHomePage import io.homeassistant.companion.android.onboarding.OnboardingActivity import io.homeassistant.companion.android.sensors.SensorReceiver import io.homeassistant.companion.android.sensors.SensorWorker +import javax.inject.Inject import kotlinx.coroutines.Job import kotlinx.coroutines.launch -import javax.inject.Inject @AndroidEntryPoint class HomeActivity : ComponentActivity(), HomeView { @@ -57,6 +58,16 @@ class HomeActivity : ComponentActivity(), HomeView { context, HomeActivity::class.java ) + + fun getTemplateTileSettingsIntent( + context: Context, + tileId: Int + ) = Intent( + Intent.ACTION_VIEW, + "$DEEPLINK_PREFIX_SET_TEMPLATE_TILE/$tileId".toUri(), + context, + HomeActivity::class.java + ) } override fun onCreate(savedInstanceState: Bundle?) { diff --git a/wear/src/main/java/io/homeassistant/companion/android/home/HomePresenter.kt b/wear/src/main/java/io/homeassistant/companion/android/home/HomePresenter.kt index a99de612eb2..a39105c498c 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/home/HomePresenter.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/home/HomePresenter.kt @@ -1,6 +1,7 @@ package io.homeassistant.companion.android.home import io.homeassistant.companion.android.common.data.integration.Entity +import io.homeassistant.companion.android.common.data.prefs.impl.entities.TemplateTileConfig import io.homeassistant.companion.android.common.data.websocket.WebSocketState import io.homeassistant.companion.android.common.data.websocket.impl.entities.AreaRegistryResponse import io.homeassistant.companion.android.common.data.websocket.impl.entities.AreaRegistryUpdatedEvent @@ -48,9 +49,9 @@ interface HomePresenter { suspend fun setWearToastConfirmation(enabled: Boolean) suspend fun getShowShortcutText(): Boolean suspend fun setShowShortcutTextEnabled(enabled: Boolean) - suspend fun getTemplateTileContent(): String - suspend fun getTemplateTileRefreshInterval(): Int - suspend fun setTemplateTileRefreshInterval(interval: Int) + suspend fun getAllTemplateTiles(): Map + suspend fun setTemplateTileRefreshInterval(tileId: Int, interval: Int) + suspend fun getWearFavoritesOnly(): Boolean suspend fun setWearFavoritesOnly(enabled: Boolean) } diff --git a/wear/src/main/java/io/homeassistant/companion/android/home/HomePresenterImpl.kt b/wear/src/main/java/io/homeassistant/companion/android/home/HomePresenterImpl.kt index 257ee915b28..8c55b664c56 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/home/HomePresenterImpl.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/home/HomePresenterImpl.kt @@ -2,11 +2,13 @@ package io.homeassistant.companion.android.home import android.util.Log import io.homeassistant.companion.android.BuildConfig +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.authentication.SessionState import io.homeassistant.companion.android.common.data.integration.DeviceRegistration import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.integration.EntityExt import io.homeassistant.companion.android.common.data.prefs.WearPrefsRepository +import io.homeassistant.companion.android.common.data.prefs.impl.entities.TemplateTileConfig import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.common.data.websocket.WebSocketState import io.homeassistant.companion.android.common.data.websocket.impl.entities.AreaRegistryResponse @@ -17,14 +19,13 @@ import io.homeassistant.companion.android.common.data.websocket.impl.entities.En import io.homeassistant.companion.android.common.data.websocket.impl.entities.EntityRegistryUpdatedEvent import io.homeassistant.companion.android.data.SimplifiedEntity import io.homeassistant.companion.android.onboarding.getMessagingToken +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.launch -import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR class HomePresenterImpl @Inject constructor( private val serverManager: ServerManager, @@ -270,16 +271,14 @@ class HomePresenterImpl @Inject constructor( wearPrefsRepository.setShowShortcutTextEnabled(enabled) } - override suspend fun getTemplateTileContent(): String { - return wearPrefsRepository.getTemplateTileContent() + override suspend fun getAllTemplateTiles(): Map { + return wearPrefsRepository.getAllTemplateTiles() } - override suspend fun getTemplateTileRefreshInterval(): Int { - return wearPrefsRepository.getTemplateTileRefreshInterval() - } - - override suspend fun setTemplateTileRefreshInterval(interval: Int) { - wearPrefsRepository.setTemplateTileRefreshInterval(interval) + override suspend fun setTemplateTileRefreshInterval(tileId: Int, interval: Int) { + getAllTemplateTiles()[tileId]?.let { + wearPrefsRepository.setTemplateTile(tileId, it.template, interval) + } } override suspend fun getWearFavoritesOnly(): Boolean { diff --git a/wear/src/main/java/io/homeassistant/companion/android/home/MainViewModel.kt b/wear/src/main/java/io/homeassistant/companion/android/home/MainViewModel.kt index ed7cfd07160..5b49a7f4d04 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/home/MainViewModel.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/home/MainViewModel.kt @@ -19,6 +19,7 @@ import io.homeassistant.companion.android.BuildConfig import io.homeassistant.companion.android.HomeAssistantApplication import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.integration.domain +import io.homeassistant.companion.android.common.data.prefs.impl.entities.TemplateTileConfig import io.homeassistant.companion.android.common.data.websocket.WebSocketState import io.homeassistant.companion.android.common.data.websocket.impl.entities.AreaRegistryResponse import io.homeassistant.companion.android.common.data.websocket.impl.entities.DeviceRegistryResponse @@ -36,6 +37,7 @@ import io.homeassistant.companion.android.database.wear.getAllFlow import io.homeassistant.companion.android.sensors.SensorReceiver import io.homeassistant.companion.android.util.RegistriesDataHandler import io.homeassistant.companion.android.util.throttleLatest +import javax.inject.Inject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.flow.Flow @@ -43,7 +45,6 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import javax.inject.Inject @HiltViewModel class MainViewModel @Inject constructor( @@ -59,7 +60,9 @@ class MainViewModel @Inject constructor( } enum class LoadingState { - LOADING, READY, ERROR + LOADING, + READY, + ERROR } private val app = application @@ -121,9 +124,7 @@ class MainViewModel @Inject constructor( private set var isShowShortcutTextEnabled = mutableStateOf(false) private set - var templateTileContent = mutableStateOf("") - private set - var templateTileRefreshInterval = mutableStateOf(0) + var templateTiles = mutableStateMapOf() private set var isFavoritesOnly by mutableStateOf(false) private set @@ -148,8 +149,8 @@ class MainViewModel @Inject constructor( isHapticEnabled.value = homePresenter.getWearHapticFeedback() isToastEnabled.value = homePresenter.getWearToastConfirmation() isShowShortcutTextEnabled.value = homePresenter.getShowShortcutText() - templateTileContent.value = homePresenter.getTemplateTileContent() - templateTileRefreshInterval.value = homePresenter.getTemplateTileRefreshInterval() + templateTiles.clear() + templateTiles.putAll(homePresenter.getAllTemplateTiles()) isFavoritesOnly = homePresenter.getWearFavoritesOnly() val assistantAppComponent = ComponentName( @@ -171,6 +172,13 @@ class MainViewModel @Inject constructor( } } + fun loadTemplateTiles() { + viewModelScope.launch { + templateTiles.clear() + templateTiles.putAll(homePresenter.getAllTemplateTiles()) + } + } + fun loadEntities() { if (!homePresenter.isConnected()) return viewModelScope.launch { @@ -485,10 +493,12 @@ class MainViewModel @Inject constructor( } } - fun setTemplateTileRefreshInterval(interval: Int) { + fun setTemplateTileRefreshInterval(tileId: Int, interval: Int) { viewModelScope.launch { - homePresenter.setTemplateTileRefreshInterval(interval) - templateTileRefreshInterval.value = interval + homePresenter.setTemplateTileRefreshInterval(tileId, interval) + templateTiles[tileId]?.let { + templateTiles[tileId] = it.copy(refreshInterval = interval) + } } } diff --git a/wear/src/main/java/io/homeassistant/companion/android/home/views/EntityListView.kt b/wear/src/main/java/io/homeassistant/companion/android/home/views/EntityListView.kt index feb09904625..6738b8f58e7 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/home/views/EntityListView.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/home/views/EntityListView.kt @@ -9,6 +9,7 @@ import androidx.wear.compose.foundation.lazy.items import androidx.wear.compose.material3.Button import androidx.wear.compose.material3.Text import androidx.wear.tooling.preview.devices.WearDevices +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.theme.WearAppTheme import io.homeassistant.companion.android.theme.getPrimaryButtonColors @@ -21,7 +22,6 @@ import io.homeassistant.companion.android.views.ExpandableListHeader import io.homeassistant.companion.android.views.ListHeader import io.homeassistant.companion.android.views.ThemeLazyColumn import io.homeassistant.companion.android.views.rememberExpandedStates -import io.homeassistant.companion.android.common.R as commonR @Composable fun EntityViewList( diff --git a/wear/src/main/java/io/homeassistant/companion/android/home/views/HomeView.kt b/wear/src/main/java/io/homeassistant/companion/android/home/views/HomeView.kt index 6f790e8731e..1ffd48b7836 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/home/views/HomeView.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/home/views/HomeView.kt @@ -24,6 +24,7 @@ private const val ARG_SCREEN_SENSOR_MANAGER_ID = "sensorManagerId" private const val ARG_SCREEN_CAMERA_TILE_ID = "cameraTileId" private const val ARG_SCREEN_SHORTCUTS_TILE_ID = "shortcutsTileId" private const val ARG_SCREEN_SHORTCUTS_TILE_ENTITY_INDEX = "shortcutsTileEntityIndex" +private const val ARG_SCREEN_TEMPLATE_TILE_ID = "templateTileId" private const val SCREEN_LANDING = "landing" private const val SCREEN_ENTITY_DETAIL = "entity_detail" @@ -38,7 +39,9 @@ private const val SCREEN_SET_CAMERA_TILE = "set_camera_tile" private const val SCREEN_SET_CAMERA_TILE_ENTITY = "entity" private const val SCREEN_SET_CAMERA_TILE_REFRESH_INTERVAL = "refresh_interval" private const val ROUTE_SHORTCUTS_TILE = "shortcuts_tile" +private const val ROUTE_TEMPLATE_TILE = "template_tile" private const val SCREEN_SELECT_SHORTCUTS_TILE = "select_shortcuts_tile" +private const val SCREEN_SELECT_TEMPLATE_TILE = "select_template_tile" private const val SCREEN_SET_SHORTCUTS_TILE = "set_shortcuts_tile" private const val SCREEN_SHORTCUTS_TILE_CHOOSE_ENTITY = "shortcuts_tile_choose_entity" private const val SCREEN_SET_TILE_TEMPLATE = "set_tile_template" @@ -47,6 +50,7 @@ private const val SCREEN_SET_TILE_TEMPLATE_REFRESH_INTERVAL = "set_tile_template const val DEEPLINK_SENSOR_MANAGER = "ha_wear://$SCREEN_SINGLE_SENSOR_MANAGER" const val DEEPLINK_PREFIX_SET_CAMERA_TILE = "ha_wear://$SCREEN_SET_CAMERA_TILE" const val DEEPLINK_PREFIX_SET_SHORTCUT_TILE = "ha_wear://$SCREEN_SET_SHORTCUTS_TILE" +const val DEEPLINK_PREFIX_SET_TEMPLATE_TILE = "ha_wear://$SCREEN_SET_TILE_TEMPLATE" @Composable fun LoadHomePage( @@ -161,7 +165,10 @@ fun LoadHomePage( onClickCameraTile = { swipeDismissableNavController.navigate("$ROUTE_CAMERA_TILE/$SCREEN_SELECT_CAMERA_TILE") }, - onClickTemplateTile = { swipeDismissableNavController.navigate(SCREEN_SET_TILE_TEMPLATE) }, + onClickTemplateTiles = { + mainViewModel.loadTemplateTiles() + swipeDismissableNavController.navigate("$ROUTE_TEMPLATE_TILE/$SCREEN_SELECT_TEMPLATE_TILE") + }, onAssistantAppAllowed = mainViewModel::setAssistantApp ) } @@ -317,21 +324,49 @@ fun LoadHomePage( } ) } - composable(SCREEN_SET_TILE_TEMPLATE) { + composable("$ROUTE_TEMPLATE_TILE/$SCREEN_SELECT_TEMPLATE_TILE") { + SelectTemplateTileView( + templateTiles = mainViewModel.templateTiles, + onSelectTemplateTile = { tileId -> + swipeDismissableNavController.navigate("$ROUTE_TEMPLATE_TILE/$tileId/$SCREEN_SET_TILE_TEMPLATE") + } + ) + } + composable( + route = "$ROUTE_TEMPLATE_TILE/{$ARG_SCREEN_TEMPLATE_TILE_ID}/$SCREEN_SET_TILE_TEMPLATE", + arguments = listOf( + navArgument(name = ARG_SCREEN_TEMPLATE_TILE_ID) { + type = NavType.StringType + } + ), + deepLinks = listOf( + navDeepLink { uriPattern = "$DEEPLINK_PREFIX_SET_TEMPLATE_TILE/{$ARG_SCREEN_TEMPLATE_TILE_ID}" } + ) + ) { backStackEntry -> + val tileId = backStackEntry.arguments!!.getString(ARG_SCREEN_TEMPLATE_TILE_ID)!!.toIntOrNull() + TemplateTileSettingsView( - templateContent = mainViewModel.templateTileContent.value, - refreshInterval = mainViewModel.templateTileRefreshInterval.value + templateContent = mainViewModel.templateTiles[tileId]?.template ?: "", + refreshInterval = mainViewModel.templateTiles[tileId]?.refreshInterval ?: 0 ) { swipeDismissableNavController.navigate( - SCREEN_SET_TILE_TEMPLATE_REFRESH_INTERVAL + "$ROUTE_TEMPLATE_TILE/$tileId/$SCREEN_SET_TILE_TEMPLATE_REFRESH_INTERVAL" ) } } - composable(SCREEN_SET_TILE_TEMPLATE_REFRESH_INTERVAL) { + composable( + route = "$ROUTE_TEMPLATE_TILE/{$ARG_SCREEN_TEMPLATE_TILE_ID}/$SCREEN_SET_TILE_TEMPLATE_REFRESH_INTERVAL", + arguments = listOf( + navArgument(name = ARG_SCREEN_TEMPLATE_TILE_ID) { + type = NavType.StringType + } + ) + ) { backStackEntry -> + val tileId = backStackEntry.arguments!!.getString(ARG_SCREEN_TEMPLATE_TILE_ID)!!.toInt() RefreshIntervalPickerView( - currentInterval = mainViewModel.templateTileRefreshInterval.value + currentInterval = mainViewModel.templateTiles[tileId]?.refreshInterval ?: 0 ) { - mainViewModel.setTemplateTileRefreshInterval(it) + mainViewModel.setTemplateTileRefreshInterval(tileId, it) TileService.getUpdater(context).requestUpdate(TemplateTile::class.java) swipeDismissableNavController.navigateUp() } diff --git a/wear/src/main/java/io/homeassistant/companion/android/home/views/MainView.kt b/wear/src/main/java/io/homeassistant/companion/android/home/views/MainView.kt index a23345b763f..f8ed4a3dc6b 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/home/views/MainView.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/home/views/MainView.kt @@ -31,6 +31,7 @@ import androidx.wear.compose.material3.MaterialTheme import androidx.wear.compose.material3.Text import com.mikepenz.iconics.compose.Image import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.util.STATE_UNKNOWN import io.homeassistant.companion.android.home.MainViewModel @@ -43,7 +44,6 @@ import io.homeassistant.companion.android.util.onEntityClickedFeedback import io.homeassistant.companion.android.views.ExpandableListHeader import io.homeassistant.companion.android.views.ListHeader import io.homeassistant.companion.android.views.ThemeLazyColumn -import io.homeassistant.companion.android.common.R as commonR @Composable fun MainView( @@ -224,8 +224,8 @@ fun MainView( ) { mainViewModel.getCategoryForEntity(it.entityId) == null && mainViewModel.getHiddenByForEntity( - it.entityId - ) == null + it.entityId + ) == null } }, colors = getPrimaryButtonColors() diff --git a/wear/src/main/java/io/homeassistant/companion/android/home/views/RefreshIntervalPickerView.kt b/wear/src/main/java/io/homeassistant/companion/android/home/views/RefreshIntervalPickerView.kt index dd5abb956fb..dfe88f38ef2 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/home/views/RefreshIntervalPickerView.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/home/views/RefreshIntervalPickerView.kt @@ -2,6 +2,7 @@ package io.homeassistant.companion.android.home.views import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Check @@ -9,12 +10,17 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.wear.compose.material.ListHeader +import androidx.wear.compose.material.LocalContentColor import androidx.wear.compose.material.Picker import androidx.wear.compose.material.rememberPickerState import androidx.wear.compose.material3.FilledIconButton @@ -27,10 +33,11 @@ import androidx.wear.tooling.preview.devices.WearDevices import com.google.android.horologist.annotations.ExperimentalHorologistApi import com.google.android.horologist.composables.picker.toRotaryScrollAdapter import com.google.android.horologist.compose.rotaryinput.rotaryWithSnap +import com.mikepenz.iconics.compose.Image +import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial +import io.homeassistant.companion.android.common.R as R import io.homeassistant.companion.android.theme.wearColorScheme import io.homeassistant.companion.android.util.intervalToString -import io.homeassistant.companion.android.views.ListHeader -import io.homeassistant.companion.android.common.R as R @OptIn(ExperimentalHorologistApi::class) @Composable @@ -38,7 +45,8 @@ fun RefreshIntervalPickerView( currentInterval: Int, onSelectInterval: (Int) -> Unit ) { - val options = listOf(0, 60, 2 * 60, 5 * 60, 10 * 60, 15 * 60, 30 * 60, 60 * 60, 2 * 60 * 60, 5 * 60 * 60, 10 * 60 * 60, 24 * 60 * 60) + // Refresh interval options: never, when viewed, every x time + val options = listOf(0, 1, 60, 2 * 60, 5 * 60, 10 * 60, 15 * 60, 30 * 60, 60 * 60, 2 * 60 * 60, 5 * 60 * 60, 10 * 60 * 60, 24 * 60 * 60) val initialIndex = options.indexOf(currentInterval) val state = rememberPickerState( initialNumberOfOptions = options.size, @@ -46,10 +54,16 @@ fun RefreshIntervalPickerView( ) Column( - modifier = Modifier.fillMaxWidth(), + modifier = Modifier.padding(horizontal = 12.dp).fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally ) { - ListHeader(R.string.refresh_interval) + ListHeader { + Image( + asset = CommunityMaterial.Icon3.cmd_timer_cog, + contentDescription = stringResource(R.string.refresh_interval), + colorFilter = ColorFilter.tint(LocalContentColor.current) + ) + } Picker( state = state, contentDescription = stringResource(R.string.refresh_interval), @@ -62,10 +76,15 @@ fun RefreshIntervalPickerView( style = with(LocalDensity.current) { MaterialTheme.typography.displayMedium.copy( fontWeight = FontWeight.Medium, - fontSize = MaterialTheme.typography.displayMedium.fontSize.value.dp.toSp() // Ignore text scaling + // Ignore text scaling + fontSize = MaterialTheme.typography.displayMedium.fontSize.value.dp.toSp() ) }, - color = wearColorScheme.primary + color = wearColorScheme.primary, + // In case of overflow, minimize weird layout behavior + textAlign = TextAlign.Center, + overflow = TextOverflow.Ellipsis, + maxLines = 2 ) } FilledIconButton( diff --git a/wear/src/main/java/io/homeassistant/companion/android/home/views/SelectCameraTileView.kt b/wear/src/main/java/io/homeassistant/companion/android/home/views/SelectCameraTileView.kt index d9d3ab6efd2..adf79ba64c5 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/home/views/SelectCameraTileView.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/home/views/SelectCameraTileView.kt @@ -10,12 +10,12 @@ import androidx.wear.compose.foundation.lazy.itemsIndexed import androidx.wear.compose.material3.Button import androidx.wear.compose.material3.Text import androidx.wear.tooling.preview.devices.WearDevices +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.database.wear.CameraTile import io.homeassistant.companion.android.theme.WearAppTheme import io.homeassistant.companion.android.theme.getFilledTonalButtonColors import io.homeassistant.companion.android.views.ListHeader import io.homeassistant.companion.android.views.ThemeLazyColumn -import io.homeassistant.companion.android.common.R as commonR @Composable fun SelectCameraTileView( diff --git a/wear/src/main/java/io/homeassistant/companion/android/home/views/SelectShortcutsTileView.kt b/wear/src/main/java/io/homeassistant/companion/android/home/views/SelectShortcutsTileView.kt index 4026df9e6bd..0ab805524f6 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/home/views/SelectShortcutsTileView.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/home/views/SelectShortcutsTileView.kt @@ -14,6 +14,7 @@ import androidx.wear.compose.material3.ToggleButton import androidx.wear.tooling.preview.devices.WearDevices import com.mikepenz.iconics.compose.Image import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.theme.WearAppTheme import io.homeassistant.companion.android.theme.getFilledTonalButtonColors import io.homeassistant.companion.android.theme.getToggleButtonColors @@ -21,7 +22,6 @@ import io.homeassistant.companion.android.theme.wearColorScheme import io.homeassistant.companion.android.util.ToggleCheckbox import io.homeassistant.companion.android.views.ListHeader import io.homeassistant.companion.android.views.ThemeLazyColumn -import io.homeassistant.companion.android.common.R as commonR @Composable fun SelectShortcutsTileView( diff --git a/wear/src/main/java/io/homeassistant/companion/android/home/views/SelectTemplateTileView.kt b/wear/src/main/java/io/homeassistant/companion/android/home/views/SelectTemplateTileView.kt new file mode 100644 index 00000000000..db0d558e9fe --- /dev/null +++ b/wear/src/main/java/io/homeassistant/companion/android/home/views/SelectTemplateTileView.kt @@ -0,0 +1,74 @@ +package io.homeassistant.companion.android.home.views + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.wear.compose.foundation.lazy.itemsIndexed +import androidx.wear.compose.material3.Button +import androidx.wear.compose.material3.Text +import androidx.wear.tooling.preview.devices.WearDevices +import io.homeassistant.companion.android.common.R as commonR +import io.homeassistant.companion.android.common.data.prefs.impl.entities.TemplateTileConfig +import io.homeassistant.companion.android.theme.WearAppTheme +import io.homeassistant.companion.android.theme.getFilledTonalButtonColors +import io.homeassistant.companion.android.views.ListHeader +import io.homeassistant.companion.android.views.ThemeLazyColumn + +@Composable +fun SelectTemplateTileView( + templateTiles: Map, + onSelectTemplateTile: (tileId: Int) -> Unit +) { + WearAppTheme { + ThemeLazyColumn { + item { + ListHeader(id = commonR.string.template_tiles) + } + if (templateTiles.isEmpty()) { + item { + Text( + text = stringResource(commonR.string.template_tile_no_tiles_yet), + textAlign = TextAlign.Center + ) + } + } else { + itemsIndexed(templateTiles.keys.toList()) { index, templateTileId -> + Button( + modifier = Modifier + .fillMaxWidth(), + label = { + Text(stringResource(commonR.string.template_tile_n, index + 1)) + }, + onClick = { onSelectTemplateTile(templateTileId) }, + colors = getFilledTonalButtonColors() + ) + } + } + } + } +} + +@Preview(device = WearDevices.LARGE_ROUND) +@Composable +private fun PreviewSelectTemplateTileView() { + SelectTemplateTileView( + templateTiles = mapOf( + -1 to TemplateTileConfig("Old template", 0), + 1111 to TemplateTileConfig("New template #1", 10), + 2222 to TemplateTileConfig("New template #2", 20) + ), + onSelectTemplateTile = {} + ) +} + +@Preview(device = WearDevices.LARGE_ROUND) +@Composable +private fun PreviewSelectTemplateTileEmptyView() { + SelectTemplateTileView( + templateTiles = mapOf(), + onSelectTemplateTile = {} + ) +} diff --git a/wear/src/main/java/io/homeassistant/companion/android/home/views/SensorsView.kt b/wear/src/main/java/io/homeassistant/companion/android/home/views/SensorsView.kt index f3e30d3bfad..be638e115a3 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/home/views/SensorsView.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/home/views/SensorsView.kt @@ -11,13 +11,13 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.wear.compose.material3.Button import androidx.wear.compose.material3.Text import androidx.wear.tooling.preview.devices.WearDevices +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.sensors.SensorManager import io.homeassistant.companion.android.sensors.SensorReceiver import io.homeassistant.companion.android.theme.WearAppTheme import io.homeassistant.companion.android.theme.getFilledTonalButtonColors import io.homeassistant.companion.android.views.ListHeader import io.homeassistant.companion.android.views.ThemeLazyColumn -import io.homeassistant.companion.android.common.R as commonR @Composable fun SensorsView( diff --git a/wear/src/main/java/io/homeassistant/companion/android/home/views/SetCameraTileView.kt b/wear/src/main/java/io/homeassistant/companion/android/home/views/SetCameraTileView.kt index a69521c1bee..c7e070f8f65 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/home/views/SetCameraTileView.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/home/views/SetCameraTileView.kt @@ -11,6 +11,7 @@ import androidx.wear.compose.material3.Text import com.mikepenz.iconics.compose.Image import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial import io.homeassistant.companion.android.common.R +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.integration.friendlyName import io.homeassistant.companion.android.common.data.integration.getIcon @@ -22,7 +23,6 @@ import io.homeassistant.companion.android.tiles.CameraTile.Companion.DEFAULT_REF import io.homeassistant.companion.android.util.intervalToString import io.homeassistant.companion.android.views.ListHeader import io.homeassistant.companion.android.views.ThemeLazyColumn -import io.homeassistant.companion.android.common.R as commonR @Composable fun SetCameraTileView( diff --git a/wear/src/main/java/io/homeassistant/companion/android/home/views/SetFavoriteView.kt b/wear/src/main/java/io/homeassistant/companion/android/home/views/SetFavoriteView.kt index e307be18313..b634a02c6a2 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/home/views/SetFavoriteView.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/home/views/SetFavoriteView.kt @@ -10,6 +10,7 @@ import androidx.wear.compose.foundation.lazy.items import androidx.wear.compose.material3.Text import androidx.wear.compose.material3.ToggleButton import com.mikepenz.iconics.compose.Image +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.integration.getIcon import io.homeassistant.companion.android.home.MainViewModel @@ -21,7 +22,6 @@ import io.homeassistant.companion.android.views.ExpandableListHeader import io.homeassistant.companion.android.views.ListHeader import io.homeassistant.companion.android.views.ThemeLazyColumn import io.homeassistant.companion.android.views.rememberExpandedStates -import io.homeassistant.companion.android.common.R as commonR @Composable fun SetFavoritesView( diff --git a/wear/src/main/java/io/homeassistant/companion/android/home/views/SetShortcutsTileView.kt b/wear/src/main/java/io/homeassistant/companion/android/home/views/SetShortcutsTileView.kt index a29ad51525c..bff99ba525b 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/home/views/SetShortcutsTileView.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/home/views/SetShortcutsTileView.kt @@ -22,6 +22,7 @@ import androidx.wear.compose.material3.Text import androidx.wear.compose.material3.touchTargetAwareSize import androidx.wear.tooling.preview.devices.WearDevices import com.mikepenz.iconics.compose.Image +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.data.SimplifiedEntity import io.homeassistant.companion.android.theme.WearAppTheme import io.homeassistant.companion.android.theme.getFilledTonalButtonColors @@ -29,7 +30,6 @@ import io.homeassistant.companion.android.util.getIcon import io.homeassistant.companion.android.util.simplifiedEntity import io.homeassistant.companion.android.views.ListHeader import io.homeassistant.companion.android.views.ThemeLazyColumn -import io.homeassistant.companion.android.common.R as commonR @Composable fun SetShortcutsTileView( diff --git a/wear/src/main/java/io/homeassistant/companion/android/home/views/SettingsView.kt b/wear/src/main/java/io/homeassistant/companion/android/home/views/SettingsView.kt index 4fdea148a81..43137e4c5bb 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/home/views/SettingsView.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/home/views/SettingsView.kt @@ -18,6 +18,7 @@ import com.mikepenz.iconics.compose.Image import com.mikepenz.iconics.typeface.IIcon import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial import io.homeassistant.companion.android.common.BuildConfig +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.home.MainViewModel import io.homeassistant.companion.android.theme.WearAppTheme import io.homeassistant.companion.android.theme.getFilledTonalButtonColors @@ -27,7 +28,6 @@ import io.homeassistant.companion.android.util.ToggleSwitch import io.homeassistant.companion.android.util.previewFavoritesList import io.homeassistant.companion.android.views.ListHeader import io.homeassistant.companion.android.views.ThemeLazyColumn -import io.homeassistant.companion.android.common.R as commonR @Composable fun SecondarySettingsChip( @@ -72,7 +72,7 @@ fun SettingsView( onToastEnabled: (Boolean) -> Unit, setFavoritesOnly: (Boolean) -> Unit, onClickCameraTile: () -> Unit, - onClickTemplateTile: () -> Unit, + onClickTemplateTiles: () -> Unit, onAssistantAppAllowed: (Boolean) -> Unit ) { WearAppTheme { @@ -187,8 +187,8 @@ fun SettingsView( item { SecondarySettingsChip( icon = CommunityMaterial.Icon3.cmd_text_box, - label = stringResource(commonR.string.template_tile), - onClick = onClickTemplateTile + label = stringResource(commonR.string.template_tiles), + onClick = onClickTemplateTiles ) } item { @@ -272,7 +272,7 @@ private fun PreviewSettingsView() { onToastEnabled = {}, setFavoritesOnly = {}, onClickCameraTile = {}, - onClickTemplateTile = {}, + onClickTemplateTiles = {}, onAssistantAppAllowed = {} ) } diff --git a/wear/src/main/java/io/homeassistant/companion/android/notifications/FirebaseCloudMessagingService.kt b/wear/src/main/java/io/homeassistant/companion/android/notifications/FirebaseCloudMessagingService.kt index e886caaf3dc..1e4d016af22 100755 --- a/wear/src/main/java/io/homeassistant/companion/android/notifications/FirebaseCloudMessagingService.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/notifications/FirebaseCloudMessagingService.kt @@ -6,11 +6,11 @@ import com.google.firebase.messaging.RemoteMessage import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.common.data.integration.DeviceRegistration import io.homeassistant.companion.android.common.data.servers.ServerManager +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch -import javax.inject.Inject @AndroidEntryPoint class FirebaseCloudMessagingService : FirebaseMessagingService() { diff --git a/wear/src/main/java/io/homeassistant/companion/android/notifications/MessagingManager.kt b/wear/src/main/java/io/homeassistant/companion/android/notifications/MessagingManager.kt index 76cd38f90d9..328057a8c7b 100755 --- a/wear/src/main/java/io/homeassistant/companion/android/notifications/MessagingManager.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/notifications/MessagingManager.kt @@ -26,12 +26,12 @@ import io.homeassistant.companion.android.database.AppDatabase import io.homeassistant.companion.android.database.notification.NotificationItem import io.homeassistant.companion.android.database.sensor.SensorDao import io.homeassistant.companion.android.sensors.SensorReceiver +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch import org.json.JSONObject -import javax.inject.Inject class MessagingManager @Inject constructor( @ApplicationContext val context: Context, diff --git a/wear/src/main/java/io/homeassistant/companion/android/onboarding/OnboardingActivity.kt b/wear/src/main/java/io/homeassistant/companion/android/onboarding/OnboardingActivity.kt index e413e1ba895..42a5d6e3982 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/onboarding/OnboardingActivity.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/onboarding/OnboardingActivity.kt @@ -21,13 +21,13 @@ import com.google.android.gms.wearable.DataMapItem import com.google.android.gms.wearable.Wearable import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.R +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.onboarding.integration.MobileAppIntegrationActivity import io.homeassistant.companion.android.onboarding.phoneinstall.PhoneInstallActivity import io.homeassistant.companion.android.util.LoadingView +import javax.inject.Inject import kotlinx.coroutines.guava.await import kotlinx.coroutines.launch -import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR @AndroidEntryPoint @SuppressLint("VisibleForTests") // https://issuetracker.google.com/issues/239451111 @@ -123,7 +123,8 @@ class OnboardingActivity : AppCompatActivity(), OnboardingView { addCategory(Intent.CATEGORY_BROWSABLE) data = Uri.parse(url) }, - null // a Wear device only has one companion device so this is not needed + // A Wear device only has one companion device so this is not needed + null ).await() showContinueOnPhone() } catch (e: Exception) { diff --git a/wear/src/main/java/io/homeassistant/companion/android/onboarding/OnboardingPresenterImpl.kt b/wear/src/main/java/io/homeassistant/companion/android/onboarding/OnboardingPresenterImpl.kt index 5294b1a894e..ebd08da47ed 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/onboarding/OnboardingPresenterImpl.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/onboarding/OnboardingPresenterImpl.kt @@ -14,6 +14,7 @@ import com.google.android.gms.wearable.DataEventBuffer import com.google.android.gms.wearable.DataMap import com.google.android.gms.wearable.DataMapItem import dagger.hilt.android.qualifiers.ActivityContext +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.database.server.Server import io.homeassistant.companion.android.database.server.ServerConnectionInfo @@ -21,15 +22,14 @@ import io.homeassistant.companion.android.database.server.ServerSessionInfo import io.homeassistant.companion.android.database.server.ServerType import io.homeassistant.companion.android.database.server.ServerUserInfo import io.homeassistant.companion.android.util.UrlUtil +import java.net.URL +import java.util.concurrent.Executors +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.cancel import kotlinx.coroutines.launch -import java.net.URL -import java.util.concurrent.Executors -import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR @SuppressLint("VisibleForTests") // https://issuetracker.google.com/issues/239451111 class OnboardingPresenterImpl @Inject constructor( diff --git a/wear/src/main/java/io/homeassistant/companion/android/onboarding/ServerListAdapter.kt b/wear/src/main/java/io/homeassistant/companion/android/onboarding/ServerListAdapter.kt index 0455bed166b..5ce58aec2ba 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/onboarding/ServerListAdapter.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/onboarding/ServerListAdapter.kt @@ -5,12 +5,12 @@ import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.ViewHolder import io.homeassistant.companion.android.R +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.viewHolders.HeaderViewHolder import io.homeassistant.companion.android.viewHolders.InstanceViewHolder import io.homeassistant.companion.android.viewHolders.LoadingViewHolder import io.homeassistant.companion.android.viewHolders.ManualSetupViewHolder import kotlin.math.min -import io.homeassistant.companion.android.common.R as commonR class ServerListAdapter( val servers: ArrayList diff --git a/wear/src/main/java/io/homeassistant/companion/android/onboarding/integration/MobileAppIntegrationActivity.kt b/wear/src/main/java/io/homeassistant/companion/android/onboarding/integration/MobileAppIntegrationActivity.kt index bcb791c0fcf..3c5a0078f8c 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/onboarding/integration/MobileAppIntegrationActivity.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/onboarding/integration/MobileAppIntegrationActivity.kt @@ -8,11 +8,11 @@ import android.view.View import androidx.appcompat.app.AppCompatActivity import androidx.wear.activity.ConfirmationActivity import dagger.hilt.android.AndroidEntryPoint +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.databinding.ActivityIntegrationBinding import io.homeassistant.companion.android.home.HomeActivity import io.homeassistant.companion.android.util.adjustInset import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR @AndroidEntryPoint class MobileAppIntegrationActivity : AppCompatActivity(), MobileAppIntegrationView { diff --git a/wear/src/main/java/io/homeassistant/companion/android/onboarding/integration/MobileAppIntegrationPresenterImpl.kt b/wear/src/main/java/io/homeassistant/companion/android/onboarding/integration/MobileAppIntegrationPresenterImpl.kt index bce5de1b1a1..8e1a5cb456e 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/onboarding/integration/MobileAppIntegrationPresenterImpl.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/onboarding/integration/MobileAppIntegrationPresenterImpl.kt @@ -12,12 +12,12 @@ import io.homeassistant.companion.android.tiles.CameraTile import io.homeassistant.companion.android.tiles.ConversationTile import io.homeassistant.companion.android.tiles.ShortcutsTile import io.homeassistant.companion.android.tiles.TemplateTile +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.cancel import kotlinx.coroutines.launch -import javax.inject.Inject class MobileAppIntegrationPresenterImpl @Inject constructor( @ActivityContext context: Context, diff --git a/wear/src/main/java/io/homeassistant/companion/android/onboarding/manual/ManualSetupActivity.kt b/wear/src/main/java/io/homeassistant/companion/android/onboarding/manual/ManualSetupActivity.kt index 292b76c2439..6dd929addfe 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/onboarding/manual/ManualSetupActivity.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/onboarding/manual/ManualSetupActivity.kt @@ -10,11 +10,11 @@ import androidx.appcompat.app.AppCompatActivity import androidx.wear.activity.ConfirmationActivity import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.R +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.databinding.ActivityManualSetupBinding import io.homeassistant.companion.android.onboarding.integration.MobileAppIntegrationActivity import io.homeassistant.companion.android.util.adjustInset import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR @AndroidEntryPoint class ManualSetupActivity : AppCompatActivity(), ManualSetupView { diff --git a/wear/src/main/java/io/homeassistant/companion/android/onboarding/manual/ManualSetupPresenterImpl.kt b/wear/src/main/java/io/homeassistant/companion/android/onboarding/manual/ManualSetupPresenterImpl.kt index 6827b7bdfe9..8c858ebea26 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/onboarding/manual/ManualSetupPresenterImpl.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/onboarding/manual/ManualSetupPresenterImpl.kt @@ -9,6 +9,7 @@ import androidx.wear.phone.interactions.authentication.OAuthRequest import androidx.wear.phone.interactions.authentication.OAuthResponse import androidx.wear.phone.interactions.authentication.RemoteAuthClient import dagger.hilt.android.qualifiers.ActivityContext +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.database.server.Server import io.homeassistant.companion.android.database.server.ServerConnectionInfo @@ -16,14 +17,13 @@ import io.homeassistant.companion.android.database.server.ServerSessionInfo import io.homeassistant.companion.android.database.server.ServerType import io.homeassistant.companion.android.database.server.ServerUserInfo import io.homeassistant.companion.android.util.UrlUtil +import java.util.concurrent.Executors +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.cancel import kotlinx.coroutines.launch -import java.util.concurrent.Executors -import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR class ManualSetupPresenterImpl @Inject constructor( @ActivityContext context: Context, diff --git a/wear/src/main/java/io/homeassistant/companion/android/onboarding/phoneinstall/PhoneInstallActivity.kt b/wear/src/main/java/io/homeassistant/companion/android/onboarding/phoneinstall/PhoneInstallActivity.kt index cc61407fd46..b7e18a57efb 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/onboarding/phoneinstall/PhoneInstallActivity.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/onboarding/phoneinstall/PhoneInstallActivity.kt @@ -11,11 +11,11 @@ import androidx.lifecycle.lifecycleScope import androidx.wear.activity.ConfirmationActivity import androidx.wear.remote.interactions.RemoteActivityHelper import io.homeassistant.companion.android.BuildConfig +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.onboarding.manual.ManualSetupActivity import io.homeassistant.companion.android.theme.WearAppTheme import kotlinx.coroutines.guava.await import kotlinx.coroutines.launch -import io.homeassistant.companion.android.common.R as commonR class PhoneInstallActivity : AppCompatActivity() { companion object { @@ -59,7 +59,8 @@ class PhoneInstallActivity : AppCompatActivity() { addCategory(Intent.CATEGORY_BROWSABLE) data = Uri.parse("https://play.google.com/store/apps/details?id=${BuildConfig.APPLICATION_ID}") }, - null // a Wear device only has one companion device so this is not needed + // A Wear device only has one companion device so this is not needed + null ).await() } catch (e: Exception) { Log.e(TAG, "Unable to open remote activity", e) @@ -69,14 +70,24 @@ class PhoneInstallActivity : AppCompatActivity() { Intent(this@PhoneInstallActivity, ConfirmationActivity::class.java).apply { putExtra( ConfirmationActivity.EXTRA_ANIMATION_TYPE, - if (success) { ConfirmationActivity.OPEN_ON_PHONE_ANIMATION } else { ConfirmationActivity.FAILURE_ANIMATION } + if (success) { + ConfirmationActivity.OPEN_ON_PHONE_ANIMATION + } else { + ConfirmationActivity.FAILURE_ANIMATION + } ) if (success) { putExtra(ConfirmationActivity.EXTRA_ANIMATION_DURATION_MILLIS, 2000) } putExtra( ConfirmationActivity.EXTRA_MESSAGE, - getString(if (success) { commonR.string.continue_on_phone } else { commonR.string.failed_phone_connection }) + getString( + if (success) { + commonR.string.continue_on_phone + } else { + commonR.string.failed_phone_connection + } + ) ) } startActivity(confirmation) diff --git a/wear/src/main/java/io/homeassistant/companion/android/onboarding/phoneinstall/PhoneInstallView.kt b/wear/src/main/java/io/homeassistant/companion/android/onboarding/phoneinstall/PhoneInstallView.kt index 20b3722285a..0fc317ca89c 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/onboarding/phoneinstall/PhoneInstallView.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/onboarding/phoneinstall/PhoneInstallView.kt @@ -16,9 +16,9 @@ import androidx.wear.compose.material3.MaterialTheme import androidx.wear.compose.material3.Text import androidx.wear.tooling.preview.devices.WearDevices import io.homeassistant.companion.android.R +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.theme.getFilledTonalButtonColors import io.homeassistant.companion.android.views.ThemeLazyColumn -import io.homeassistant.companion.android.common.R as commonR @Composable fun PhoneInstallView( diff --git a/wear/src/main/java/io/homeassistant/companion/android/phone/PhoneSettingsListener.kt b/wear/src/main/java/io/homeassistant/companion/android/phone/PhoneSettingsListener.kt index 17ee1c147fc..abe9f973e56 100755 --- a/wear/src/main/java/io/homeassistant/companion/android/phone/PhoneSettingsListener.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/phone/PhoneSettingsListener.kt @@ -21,6 +21,7 @@ import io.homeassistant.companion.android.common.data.integration.DeviceRegistra import io.homeassistant.companion.android.common.data.keychain.KeyChainRepository import io.homeassistant.companion.android.common.data.keychain.KeyStoreRepositoryImpl import io.homeassistant.companion.android.common.data.prefs.WearPrefsRepository +import io.homeassistant.companion.android.common.data.prefs.impl.entities.TemplateTileConfig import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.common.util.WearDataMessages import io.homeassistant.companion.android.database.server.Server @@ -39,16 +40,16 @@ import io.homeassistant.companion.android.tiles.ConversationTile import io.homeassistant.companion.android.tiles.ShortcutsTile import io.homeassistant.companion.android.tiles.TemplateTile import io.homeassistant.companion.android.util.UrlUtil -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.launch -import kotlinx.coroutines.tasks.await import java.security.KeyStore import java.security.PrivateKey import java.security.cert.X509Certificate import javax.inject.Inject import javax.inject.Named +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import kotlinx.coroutines.tasks.await @AndroidEntryPoint @SuppressLint("VisibleForTests") // https://issuetracker.google.com/issues/239451111 @@ -103,8 +104,7 @@ class PhoneSettingsListener : WearableListenerService(), DataClient.OnDataChange } dataMap.putString(WearDataMessages.CONFIG_SUPPORTED_DOMAINS, objectMapper.writeValueAsString(HomePresenterImpl.supportedDomains)) dataMap.putString(WearDataMessages.CONFIG_FAVORITES, objectMapper.writeValueAsString(currentFavorites)) - dataMap.putString(WearDataMessages.CONFIG_TEMPLATE_TILE, wearPrefsRepository.getTemplateTileContent()) - dataMap.putInt(WearDataMessages.CONFIG_TEMPLATE_TILE_REFRESH_INTERVAL, wearPrefsRepository.getTemplateTileRefreshInterval()) + dataMap.putString(WearDataMessages.CONFIG_TEMPLATE_TILES, objectMapper.writeValueAsString(wearPrefsRepository.getAllTemplateTiles())) setUrgent() asPutDataRequest() } @@ -129,8 +129,8 @@ class PhoneSettingsListener : WearableListenerService(), DataClient.OnDataChange "/updateFavorites" -> { saveFavorites(DataMapItem.fromDataItem(item).dataMap) } - "/updateTemplateTile" -> { - saveTileTemplate(DataMapItem.fromDataItem(item).dataMap) + "/updateTemplateTiles" -> { + saveTemplateTiles(DataMapItem.fromDataItem(item).dataMap) } } } @@ -246,11 +246,15 @@ class PhoneSettingsListener : WearableListenerService(), DataClient.OnDataChange } } - private fun saveTileTemplate(dataMap: DataMap) = mainScope.launch { - val content = dataMap.getString(WearDataMessages.CONFIG_TEMPLATE_TILE, "") - val interval = dataMap.getInt(WearDataMessages.CONFIG_TEMPLATE_TILE_REFRESH_INTERVAL, 0) - wearPrefsRepository.setTemplateTileContent(content) - wearPrefsRepository.setTemplateTileRefreshInterval(interval) + private fun saveTemplateTiles(dataMap: DataMap) = mainScope.launch { + val templateTilesFromPhone: Map = objectMapper.readValue( + dataMap.getString( + WearDataMessages.CONFIG_TEMPLATE_TILES, + "{}" + ) + ) + + wearPrefsRepository.setAllTemplateTiles(templateTilesFromPhone) } private fun updateTiles() = mainScope.launch { diff --git a/wear/src/main/java/io/homeassistant/companion/android/sensors/BedtimeModeSensorManager.kt b/wear/src/main/java/io/homeassistant/companion/android/sensors/BedtimeModeSensorManager.kt index b5f9ba9c444..d0a235db99e 100755 --- a/wear/src/main/java/io/homeassistant/companion/android/sensors/BedtimeModeSensorManager.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/sensors/BedtimeModeSensorManager.kt @@ -4,8 +4,8 @@ import android.content.Context import android.os.Build import android.provider.Settings import android.util.Log -import io.homeassistant.companion.android.common.sensors.SensorManager import io.homeassistant.companion.android.common.R as commonR +import io.homeassistant.companion.android.common.sensors.SensorManager class BedtimeModeSensorManager : SensorManager { companion object { diff --git a/wear/src/main/java/io/homeassistant/companion/android/sensors/HealthServicesSensorManager.kt b/wear/src/main/java/io/homeassistant/companion/android/sensors/HealthServicesSensorManager.kt index ec03ae297c7..64310caca05 100755 --- a/wear/src/main/java/io/homeassistant/companion/android/sensors/HealthServicesSensorManager.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/sensors/HealthServicesSensorManager.kt @@ -19,13 +19,13 @@ import androidx.health.services.client.data.PassiveListenerConfig import androidx.health.services.client.data.PassiveMonitoringCapabilities import androidx.health.services.client.data.UserActivityInfo import androidx.health.services.client.data.UserActivityState +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.sensors.SensorManager import io.homeassistant.companion.android.common.util.STATE_UNKNOWN import io.homeassistant.companion.android.database.AppDatabase +import java.time.Instant import kotlinx.coroutines.guava.await import kotlinx.coroutines.runBlocking -import java.time.Instant -import io.homeassistant.companion.android.common.R as commonR @RequiresApi(Build.VERSION_CODES.R) class HealthServicesSensorManager : SensorManager { diff --git a/wear/src/main/java/io/homeassistant/companion/android/sensors/HeartRateSensorManager.kt b/wear/src/main/java/io/homeassistant/companion/android/sensors/HeartRateSensorManager.kt index feef3b67acc..d8ec4993add 100755 --- a/wear/src/main/java/io/homeassistant/companion/android/sensors/HeartRateSensorManager.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/sensors/HeartRateSensorManager.kt @@ -14,10 +14,10 @@ import android.hardware.SensorManager.SENSOR_STATUS_NO_CONTACT import android.hardware.SensorManager.SENSOR_STATUS_UNRELIABLE import android.util.Log import androidx.core.content.getSystemService +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.sensors.SensorManager import io.homeassistant.companion.android.common.util.STATE_UNKNOWN import kotlin.math.roundToInt -import io.homeassistant.companion.android.common.R as commonR class HeartRateSensorManager : SensorManager, SensorEventListener { companion object { diff --git a/wear/src/main/java/io/homeassistant/companion/android/sensors/OnBodySensorManager.kt b/wear/src/main/java/io/homeassistant/companion/android/sensors/OnBodySensorManager.kt index d9a9f4b5cfc..9020605e4a4 100755 --- a/wear/src/main/java/io/homeassistant/companion/android/sensors/OnBodySensorManager.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/sensors/OnBodySensorManager.kt @@ -7,9 +7,9 @@ import android.hardware.SensorEventListener import android.hardware.SensorManager.SENSOR_DELAY_NORMAL import android.util.Log import androidx.core.content.getSystemService +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.sensors.SensorManager import kotlin.math.roundToInt -import io.homeassistant.companion.android.common.R as commonR class OnBodySensorManager : SensorManager, SensorEventListener { companion object { diff --git a/wear/src/main/java/io/homeassistant/companion/android/sensors/SensorReceiver.kt b/wear/src/main/java/io/homeassistant/companion/android/sensors/SensorReceiver.kt index 33db86a7a72..819160123d5 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/sensors/SensorReceiver.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/sensors/SensorReceiver.kt @@ -105,22 +105,32 @@ class SensorReceiver : SensorReceiverBase() { // Suppress Lint because we only register for the receiver if the android version matches the intent @SuppressLint("InlinedApi") override val skippableActions = mapOf( - WifiManager.WIFI_STATE_CHANGED_ACTION to NetworkSensorManager.wifiState.id, - "android.app.action.NEXT_ALARM_CLOCK_CHANGED" to NextAlarmManager.nextAlarm.id, - Intent.ACTION_SCREEN_OFF to PowerSensorManager.interactiveDevice.id, - Intent.ACTION_SCREEN_ON to PowerSensorManager.interactiveDevice.id, - PowerManager.ACTION_POWER_SAVE_MODE_CHANGED to PowerSensorManager.powerSave.id, - PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED to PowerSensorManager.doze.id, - NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED to DNDSensorManager.dndSensor.id, - AudioManager.ACTION_MICROPHONE_MUTE_CHANGED to AudioSensorManager.micMuted.id, - AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED to AudioSensorManager.speakerphoneState.id, - AudioManager.RINGER_MODE_CHANGED_ACTION to AudioSensorManager.audioSensor.id, - "com.google.android.clockwork.actions.WET_MODE_STARTED" to WetModeSensorManager.wetModeSensor.id, - "com.google.android.clockwork.actions.WET_MODE_ENDED" to WetModeSensorManager.wetModeSensor.id, - "android.bluetooth.device.action.ACL_CONNECTED" to BluetoothSensorManager.bluetoothConnection.id, - "android.bluetooth.device.action.ACL_DISCONNECTED" to BluetoothSensorManager.bluetoothConnection.id, - BluetoothAdapter.ACTION_STATE_CHANGED to BluetoothSensorManager.bluetoothState.id, - NfcAdapter.ACTION_ADAPTER_STATE_CHANGED to NfcSensorManager.nfcStateSensor.id + WifiManager.WIFI_STATE_CHANGED_ACTION to listOf(NetworkSensorManager.wifiState.id), + "android.app.action.NEXT_ALARM_CLOCK_CHANGED" to listOf(NextAlarmManager.nextAlarm.id), + Intent.ACTION_SCREEN_OFF to listOf(PowerSensorManager.interactiveDevice.id), + Intent.ACTION_SCREEN_ON to listOf(PowerSensorManager.interactiveDevice.id), + PowerManager.ACTION_POWER_SAVE_MODE_CHANGED to listOf(PowerSensorManager.powerSave.id), + PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED to listOf(PowerSensorManager.doze.id), + NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED to listOf(DNDSensorManager.dndSensor.id), + AudioManager.ACTION_MICROPHONE_MUTE_CHANGED to listOf(AudioSensorManager.micMuted.id), + AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED to listOf(AudioSensorManager.speakerphoneState.id), + AudioManager.RINGER_MODE_CHANGED_ACTION to listOf(AudioSensorManager.audioSensor.id), + AudioSensorManager.VOLUME_CHANGED_ACTION to listOf( + AudioSensorManager.volAccessibility.id, + AudioSensorManager.volAlarm.id, + AudioSensorManager.volCall.id, + AudioSensorManager.volDTMF.id, + AudioSensorManager.volNotification.id, + AudioSensorManager.volMusic.id, + AudioSensorManager.volRing.id, + AudioSensorManager.volSystem.id + ), + "com.google.android.clockwork.actions.WET_MODE_STARTED" to listOf(WetModeSensorManager.wetModeSensor.id), + "com.google.android.clockwork.actions.WET_MODE_ENDED" to listOf(WetModeSensorManager.wetModeSensor.id), + "android.bluetooth.device.action.ACL_CONNECTED" to listOf(BluetoothSensorManager.bluetoothConnection.id), + "android.bluetooth.device.action.ACL_DISCONNECTED" to listOf(BluetoothSensorManager.bluetoothConnection.id), + BluetoothAdapter.ACTION_STATE_CHANGED to listOf(BluetoothSensorManager.bluetoothState.id), + NfcAdapter.ACTION_ADAPTER_STATE_CHANGED to listOf(NfcSensorManager.nfcStateSensor.id) ) override fun getSensorSettingsIntent( diff --git a/wear/src/main/java/io/homeassistant/companion/android/sensors/TheaterModeSensorManager.kt b/wear/src/main/java/io/homeassistant/companion/android/sensors/TheaterModeSensorManager.kt index 701f0d83849..631e434e4cf 100755 --- a/wear/src/main/java/io/homeassistant/companion/android/sensors/TheaterModeSensorManager.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/sensors/TheaterModeSensorManager.kt @@ -4,8 +4,8 @@ import android.content.Context import android.os.Build import android.provider.Settings import android.util.Log -import io.homeassistant.companion.android.common.sensors.SensorManager import io.homeassistant.companion.android.common.R as commonR +import io.homeassistant.companion.android.common.sensors.SensorManager class TheaterModeSensorManager : SensorManager { companion object { diff --git a/wear/src/main/java/io/homeassistant/companion/android/sensors/WetModeSensorManager.kt b/wear/src/main/java/io/homeassistant/companion/android/sensors/WetModeSensorManager.kt index 0919e673d94..11560b85f0d 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/sensors/WetModeSensorManager.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/sensors/WetModeSensorManager.kt @@ -2,8 +2,8 @@ package io.homeassistant.companion.android.sensors import android.content.Context import android.content.Intent -import io.homeassistant.companion.android.common.sensors.SensorManager import io.homeassistant.companion.android.common.R as commonR +import io.homeassistant.companion.android.common.sensors.SensorManager class WetModeSensorManager : SensorManager { companion object { diff --git a/wear/src/main/java/io/homeassistant/companion/android/tiles/CameraTile.kt b/wear/src/main/java/io/homeassistant/companion/android/tiles/CameraTile.kt index 4de307d62f0..f39aedad182 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/tiles/CameraTile.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/tiles/CameraTile.kt @@ -18,24 +18,25 @@ import androidx.wear.tiles.TileService import com.google.common.util.concurrent.ListenableFuture import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.R +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.prefs.WearPrefsRepository import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.database.AppDatabase import io.homeassistant.companion.android.database.wear.CameraTile import io.homeassistant.companion.android.util.UrlUtil +import java.io.ByteArrayOutputStream +import java.util.concurrent.TimeUnit +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.cancel import kotlinx.coroutines.guava.future +import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import okhttp3.OkHttpClient import okhttp3.Request -import java.io.ByteArrayOutputStream -import java.util.concurrent.TimeUnit -import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR @AndroidEntryPoint class CameraTile : TileService() { @@ -70,12 +71,15 @@ class CameraTile : TileService() { if (requestParams.currentState.lastClickableId == MODIFIER_CLICK_REFRESH) { if (wearPrefsRepository.getWearHapticFeedback()) hapticClick(applicationContext) } + val freshness = when { + (tileConfig?.refreshInterval != null && tileConfig.refreshInterval!! <= 1) -> 0 + tileConfig?.refreshInterval != null -> tileConfig.refreshInterval!! + else -> DEFAULT_REFRESH_INTERVAL + } Tile.Builder() .setResourcesVersion("$TAG$tileId.${System.currentTimeMillis()}") - .setFreshnessIntervalMillis( - TimeUnit.SECONDS.toMillis(tileConfig?.refreshInterval ?: DEFAULT_REFRESH_INTERVAL) - ) + .setFreshnessIntervalMillis(TimeUnit.SECONDS.toMillis(freshness)) .setTileTimeline( if (serverManager.isRegistered()) { timeline( @@ -202,6 +206,25 @@ class CameraTile : TileService() { } } + override fun onTileEnterEvent(requestParams: EventBuilders.TileEnterEvent) { + serviceScope.launch { + val tileId = requestParams.tileId + val tileConfig = AppDatabase.getInstance(this@CameraTile) + .cameraTileDao() + .get(tileId) + tileConfig?.refreshInterval?.let { + if (it >= 1) { + try { + getUpdater(this@CameraTile) + .requestUpdate(io.homeassistant.companion.android.tiles.CameraTile::class.java) + } catch (e: Exception) { + Log.w(TAG, "Unable to request tile update on enter", e) + } + } + } + } + } + override fun onDestroy() { super.onDestroy() serviceScope.cancel() diff --git a/wear/src/main/java/io/homeassistant/companion/android/tiles/ConversationTile.kt b/wear/src/main/java/io/homeassistant/companion/android/tiles/ConversationTile.kt index 5c5481494b5..01c5eadd5b8 100755 --- a/wear/src/main/java/io/homeassistant/companion/android/tiles/ConversationTile.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/tiles/ConversationTile.kt @@ -19,14 +19,14 @@ import androidx.wear.tiles.TileService import com.google.common.util.concurrent.ListenableFuture import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.common.R +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.conversation.ConversationActivity +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.guava.future -import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR @AndroidEntryPoint class ConversationTile : TileService() { diff --git a/wear/src/main/java/io/homeassistant/companion/android/tiles/OpenTileSettingsActivity.kt b/wear/src/main/java/io/homeassistant/companion/android/tiles/OpenTileSettingsActivity.kt index 4ea43bbed98..a1259e0c564 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/tiles/OpenTileSettingsActivity.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/tiles/OpenTileSettingsActivity.kt @@ -6,8 +6,8 @@ import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.common.data.prefs.WearPrefsRepositoryImpl import io.homeassistant.companion.android.home.HomeActivity -import kotlinx.coroutines.launch import javax.inject.Inject +import kotlinx.coroutines.launch @AndroidEntryPoint class OpenTileSettingsActivity : AppCompatActivity() { @@ -34,6 +34,15 @@ class OpenTileSettingsActivity : AppCompatActivity() { tileId = it ) } + "ConfigTemplateTile" -> { + lifecycleScope.launch { + wearPrefsRepository.getTemplateTileAndSaveTileId(tileId) + } + HomeActivity.getTemplateTileSettingsIntent( + context = this, + tileId = it + ) + } else -> null } diff --git a/wear/src/main/java/io/homeassistant/companion/android/tiles/ShortcutsTile.kt b/wear/src/main/java/io/homeassistant/companion/android/tiles/ShortcutsTile.kt index c49a0689f4e..65ff998daf8 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/tiles/ShortcutsTile.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/tiles/ShortcutsTile.kt @@ -34,21 +34,21 @@ import com.mikepenz.iconics.utils.colorInt import com.mikepenz.iconics.utils.sizeDp import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.R +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.prefs.WearPrefsRepository import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.data.SimplifiedEntity import io.homeassistant.companion.android.util.getIcon +import java.nio.ByteBuffer +import javax.inject.Inject +import kotlin.math.min +import kotlin.math.roundToInt import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.guava.future import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext -import java.nio.ByteBuffer -import javax.inject.Inject -import kotlin.math.min -import kotlin.math.roundToInt -import io.homeassistant.companion.android.common.R as commonR // Dimensions (dp) private const val CIRCLE_SIZE = 56f diff --git a/wear/src/main/java/io/homeassistant/companion/android/tiles/TemplateTile.kt b/wear/src/main/java/io/homeassistant/companion/android/tiles/TemplateTile.kt index e53476f4961..8eabfe27be3 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/tiles/TemplateTile.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/tiles/TemplateTile.kt @@ -19,6 +19,7 @@ import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement import androidx.wear.protolayout.ResourceBuilders import androidx.wear.protolayout.ResourceBuilders.Resources import androidx.wear.protolayout.TimelineBuilders.Timeline +import androidx.wear.tiles.EventBuilders import androidx.wear.tiles.RequestBuilders.ResourcesRequest import androidx.wear.tiles.RequestBuilders.TileRequest import androidx.wear.tiles.TileBuilders.Tile @@ -27,17 +28,26 @@ import com.fasterxml.jackson.databind.JsonMappingException import com.google.common.util.concurrent.ListenableFuture import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.R +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.prefs.WearPrefsRepository +import io.homeassistant.companion.android.common.data.prefs.impl.entities.TemplateTileConfig import io.homeassistant.companion.android.common.data.servers.ServerManager +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.guava.future -import javax.inject.Inject -import io.homeassistant.companion.android.common.R as commonR +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext @AndroidEntryPoint class TemplateTile : TileService() { + + companion object { + private const val TAG = "TemplateTile" + } + private val serviceJob = Job() private val serviceScope = CoroutineScope(Dispatchers.IO + serviceJob) @@ -53,14 +63,19 @@ class TemplateTile : TileService() { if (wearPrefsRepository.getWearHapticFeedback()) hapticClick(applicationContext) } + val tileId = requestParams.tileId + val templateTileConfig = getTemplateTileConfig(tileId) + val freshness = when { + templateTileConfig.refreshInterval <= 1 -> 0 + else -> templateTileConfig.refreshInterval + } + Tile.Builder() .setResourcesVersion("1") - .setFreshnessIntervalMillis( - wearPrefsRepository.getTemplateTileRefreshInterval().toLong() * 1000 - ) + .setFreshnessIntervalMillis(freshness.toLong() * 1_000) .setTileTimeline( if (serverManager.isRegistered()) { - timeline() + timeline(templateTileConfig) } else { loggedOutTimeline( this@TemplateTile, @@ -88,17 +103,57 @@ class TemplateTile : TileService() { .build() } + override fun onTileAddEvent(requestParams: EventBuilders.TileAddEvent): Unit = runBlocking { + withContext(Dispatchers.IO) { + /** + * When the app is updated from an older version (which only supported a single Template Tile), + * and the user is adding a new Template Tile, we can't tell for sure if it's the 1st or 2nd Tile. + * Even though we may have the Template tile config stored in the prefs, it doesn't guarantee that + * the tile was actually added to the Tiles carousel. + * The [WearPrefsRepositoryImpl::getTemplateTileAndSaveTileId] method will handle both of the following cases: + * 1. There was no Tile added, but there was a Template tile config stored in the prefs. + * In this case, the stored config will be associated to the new tileId. + * 2. There was a single Tile added, and there was a Template tile config stored in the prefs. + * If there was a Tile update since updating the app, the tileId will be already + * associated to the config, because it also calls [getTemplateTileAndSaveTileId]. + * If there was no Tile update yet, the new Tile will "steal" the config from the existing Tile, + * and the old Tile will behave as it is the new Tile. This is needed because + * we don't know if it's the 1st or 2nd Tile. + */ + wearPrefsRepository.getTemplateTileAndSaveTileId(requestParams.tileId) + } + } + + override fun onTileRemoveEvent(requestParams: EventBuilders.TileRemoveEvent): Unit = runBlocking { + withContext(Dispatchers.IO) { + wearPrefsRepository.removeTemplateTile(requestParams.tileId) + } + } + + override fun onTileEnterEvent(requestParams: EventBuilders.TileEnterEvent) { + serviceScope.launch { + val tileId = requestParams.tileId + val templateTileConfig = getTemplateTileConfig(tileId) + if (templateTileConfig.refreshInterval >= 1) { + try { + getUpdater(this@TemplateTile).requestUpdate(TemplateTile::class.java) + } catch (e: Exception) { + Log.w(TAG, "Unable to request tile update on enter", e) + } + } + } + } + override fun onDestroy() { super.onDestroy() // Cleans up the coroutine serviceJob.cancel() } - private suspend fun timeline(): Timeline { - val template = wearPrefsRepository.getTemplateTileContent() + private suspend fun timeline(templateTileConfig: TemplateTileConfig): Timeline { val renderedText = try { if (serverManager.isRegistered()) { - serverManager.integrationRepository().renderTemplate(template, mapOf()).toString() + serverManager.integrationRepository().renderTemplate(templateTileConfig.template, mapOf()).toString() } else { "" } @@ -115,6 +170,10 @@ class TemplateTile : TileService() { return Timeline.fromLayoutElement(layout(renderedText)) } + private suspend fun getTemplateTileConfig(tileId: Int): TemplateTileConfig { + return wearPrefsRepository.getTemplateTileAndSaveTileId(tileId) + } + fun layout(renderedText: String): LayoutElement = Box.Builder().apply { if (renderedText.isEmpty()) { addContent( diff --git a/wear/src/main/java/io/homeassistant/companion/android/tiles/TileActionReceiver.kt b/wear/src/main/java/io/homeassistant/companion/android/tiles/TileActionReceiver.kt index 30cf8dc63d6..4250f8604fe 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/tiles/TileActionReceiver.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/tiles/TileActionReceiver.kt @@ -8,8 +8,8 @@ import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.common.data.integration.onEntityPressedWithoutState import io.homeassistant.companion.android.common.data.prefs.WearPrefsRepository import io.homeassistant.companion.android.common.data.servers.ServerManager -import kotlinx.coroutines.runBlocking import javax.inject.Inject +import kotlinx.coroutines.runBlocking @AndroidEntryPoint class TileActionReceiver : BroadcastReceiver() { diff --git a/wear/src/main/java/io/homeassistant/companion/android/tiles/TileViews.kt b/wear/src/main/java/io/homeassistant/companion/android/tiles/TileViews.kt index b05a8303c2f..b2e264ec117 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/tiles/TileViews.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/tiles/TileViews.kt @@ -22,8 +22,8 @@ import androidx.wear.protolayout.material.Typography import androidx.wear.protolayout.material.layouts.PrimaryLayout import androidx.wear.tiles.RequestBuilders import io.homeassistant.companion.android.R -import io.homeassistant.companion.android.splash.SplashActivity import io.homeassistant.companion.android.common.R as commonR +import io.homeassistant.companion.android.splash.SplashActivity const val RESOURCE_REFRESH = "refresh" const val MODIFIER_CLICK_REFRESH = "refresh" @@ -77,10 +77,12 @@ fun primaryLayoutTimeline( action: ActionBuilders.Action ): Timeline { val theme = Colors( - ContextCompat.getColor(context, commonR.color.colorPrimary), // Primary - ContextCompat.getColor(context, commonR.color.colorOnPrimary), // On primary - ContextCompat.getColor(context, R.color.colorOverlay), // Surface - ContextCompat.getColor(context, android.R.color.white) // On surface + ContextCompat.getColor(context, commonR.color.colorPrimary), + ContextCompat.getColor(context, commonR.color.colorOnPrimary), + // Surface + ContextCompat.getColor(context, R.color.colorOverlay), + // On surface + ContextCompat.getColor(context, android.R.color.white) ) val chipColors = ChipColors.primaryChipColors(theme) val chipAction = ModifiersBuilders.Clickable.Builder() diff --git a/wear/src/main/java/io/homeassistant/companion/android/util/CommonFunctions.kt b/wear/src/main/java/io/homeassistant/companion/android/util/CommonFunctions.kt index ae3ae6174cf..cfbd6f79e7d 100755 --- a/wear/src/main/java/io/homeassistant/companion/android/util/CommonFunctions.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/util/CommonFunctions.kt @@ -5,11 +5,11 @@ import android.widget.Toast import androidx.compose.ui.hapticfeedback.HapticFeedback import androidx.compose.ui.hapticfeedback.HapticFeedbackType import com.mikepenz.iconics.typeface.IIcon +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.integration.getIcon import io.homeassistant.companion.android.home.HomePresenterImpl import java.util.Calendar -import io.homeassistant.companion.android.common.R as commonR fun stringForDomain(domain: String, context: Context): String? = ( diff --git a/wear/src/main/java/io/homeassistant/companion/android/views/ChooseEntityView.kt b/wear/src/main/java/io/homeassistant/companion/android/views/ChooseEntityView.kt index 2a4e9345d61..958555f1c36 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/views/ChooseEntityView.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/views/ChooseEntityView.kt @@ -23,6 +23,7 @@ import androidx.wear.compose.material3.ButtonDefaults import androidx.wear.compose.material3.Text import com.mikepenz.iconics.compose.Image import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial +import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.common.data.integration.Entity import io.homeassistant.companion.android.common.data.integration.getIcon import io.homeassistant.companion.android.common.util.capitalize @@ -31,7 +32,6 @@ import io.homeassistant.companion.android.theme.WearAppTheme import io.homeassistant.companion.android.theme.getFilledTonalButtonColors import io.homeassistant.companion.android.util.stringForDomain import java.util.Locale -import io.homeassistant.companion.android.common.R as commonR @Composable fun ChooseEntityView( diff --git a/wear/src/main/java/io/homeassistant/companion/android/views/ListHeader.kt b/wear/src/main/java/io/homeassistant/companion/android/views/ListHeader.kt index 4c3f2a9296a..2c0d03d916a 100644 --- a/wear/src/main/java/io/homeassistant/companion/android/views/ListHeader.kt +++ b/wear/src/main/java/io/homeassistant/companion/android/views/ListHeader.kt @@ -11,8 +11,8 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.wear.compose.material3.ListHeader import androidx.wear.compose.material3.LocalTextStyle import androidx.wear.compose.material3.Text -import kotlin.math.floor import io.homeassistant.companion.android.common.R as commonR +import kotlin.math.floor @Composable fun ListHeader(@StringRes id: Int, modifier: Modifier = Modifier) {