Skip to content

Commit

Permalink
Merge pull request #48 from boschglobal/feature-20
Browse files Browse the repository at this point in the history
feature: Optimize Views for Landscape
  • Loading branch information
Chrylo authored Feb 12, 2024
2 parents d2a3cd8 + 4fbc622 commit e852135
Show file tree
Hide file tree
Showing 42 changed files with 2,131 additions and 772 deletions.
1 change: 1 addition & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ ktlint_standard_annotation = disabled # false positive with @JvmOverloads and de
ktlint_function_naming_ignore_when_annotated_with = Composable
ktlint_standard_multiline-expression-wrapping = disabled
ktlint_standard_string-template-indent = disabled
ktlint_standard_property-naming = disabled
4 changes: 4 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ dependencies {
implementation(libs.kotlinx.serialization.json)
implementation(libs.androidx.lifecycle.runtime.compose)

implementation(libs.material)

implementation(libs.ramses.aar)

testImplementation(libs.junit)
Expand All @@ -137,6 +139,8 @@ dependencies {

implementation(libs.hilt.android)
kapt(libs.hilt.android.compiler)

implementation(libs.androidx.material3.window.size.clazz)
}

// Allow references to generated code
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ package org.eclipse.kuksa.companion

import android.app.Application
import dagger.hilt.android.HiltAndroidApp
import org.eclipse.kuksa.DataBrokerConnection

const val PREVIEW_WIDTH_DP = 400
const val PREVIEW_HEIGHT_DP = 900

const val SHEET_EXPANDED_HEIGHT = 350
const val SHEET_COLLAPSED_HEIGHT = 50

@HiltAndroidApp
class CompanionApplication : Application()
class CompanionApplication : Application() {
var dataBrokerConnection: DataBrokerConnection? = null
}
70 changes: 40 additions & 30 deletions app/src/main/kotlin/org/eclipse/kuksa/companion/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass
import androidx.lifecycle.lifecycleScope
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.rememberNavController
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
Expand All @@ -45,12 +45,9 @@ import org.eclipse.kuksa.companion.feature.door.viewModel.DoorControlViewModel.C
import org.eclipse.kuksa.companion.feature.door.viewModel.DoorControlViewModel.Companion.DOOR_ALL_OPEN
import org.eclipse.kuksa.companion.feature.door.viewModel.DoorControlViewModel.Companion.TRUNK_CLOSED
import org.eclipse.kuksa.companion.feature.door.viewModel.DoorControlViewModel.Companion.TRUNK_OPEN
import org.eclipse.kuksa.companion.feature.home.view.HOME_SCREEN
import org.eclipse.kuksa.companion.feature.home.view.RamsesView
import org.eclipse.kuksa.companion.feature.home.view.homeScreen
import org.eclipse.kuksa.companion.feature.home.view.AdaptiveAppScreen
import org.eclipse.kuksa.companion.feature.light.viewmodel.LightControlViewModel
import org.eclipse.kuksa.companion.feature.settings.navigation.navigateToSettingsScreen
import org.eclipse.kuksa.companion.feature.settings.navigation.settingsScreen
import org.eclipse.kuksa.companion.feature.navigation.viewmodel.NavigationViewModel
import org.eclipse.kuksa.companion.feature.settings.viewModel.SettingsViewModel
import org.eclipse.kuksa.companion.feature.temperature.viewmodel.TemperatureViewModel
import org.eclipse.kuksa.companion.feature.wheel.pressure.viewmodel.WheelPressureViewModel
Expand All @@ -71,16 +68,21 @@ import javax.inject.Inject
@AndroidEntryPoint
@VssDefinition("vss_rel_4.0.yaml")
class MainActivity : ComponentActivity() {
private val companionApplication
get() = applicationContext as CompanionApplication

@Inject
lateinit var connectionInfoRepository: ConnectionInfoRepository

private lateinit var doorVehicleScene: DoorVehicleScene

private val disconnectListener = DisconnectListener {
dataBrokerConnection = null
connectionStatusViewModel.connectionState = ConnectionState.DISCONNECTED
}

private val connectionStatusViewModel: ConnectionStatusViewModel by viewModels()
private val navigationViewModel: NavigationViewModel by viewModels()

private val doorControlViewModel: DoorControlViewModel by viewModels()
private val temperatureViewModel: TemperatureViewModel by viewModels()
Expand All @@ -89,7 +91,13 @@ class MainActivity : ComponentActivity() {

private val settingsViewModel: SettingsViewModel by viewModels()

private var dataBrokerConnection: DataBrokerConnection? = null
// storing the connection in the Application keeps the Connection alive on orientation changes
private var dataBrokerConnection: DataBrokerConnection?
get() = companionApplication.dataBrokerConnection
set(value) {
companionApplication.dataBrokerConnection = value
}

private val dataBrokerConnectorFactory = DataBrokerConnectorFactory()

private val doorVehicleSurface = DoorVehicleSurface()
Expand Down Expand Up @@ -142,32 +150,26 @@ class MainActivity : ComponentActivity() {
}

// region: Lifecycle
@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

doorVehicleScene = doorVehicleSurface.loadScene(doorControlViewModel)

setContent {
val windowSizeClass = calculateWindowSizeClass(activity = this@MainActivity)
KuksaCompanionTheme {
val navController = rememberNavController()
RamsesView(callback = doorVehicleSurface)
NavHost(navController, startDestination = HOME_SCREEN) {
homeScreen(
connectionStatusViewModel = connectionStatusViewModel,
doorControlViewModel = doorControlViewModel,
temperatureViewModel = temperatureViewModel,
lightControlViewModel = lightControlViewModel,
wheelPressureViewModel = wheelPressureViewModel,
onNavigateToSettingsScreen = { navController.navigateToSettingsScreen() },
)

settingsScreen(
settingsViewModel = settingsViewModel,
onNavigateBack = {
navController.navigateUp()
},
)
}
AdaptiveAppScreen(
callback = doorVehicleSurface,
connectionStatusViewModel = connectionStatusViewModel,
navigationViewModel = navigationViewModel,
doorControlViewModel = doorControlViewModel,
temperatureViewModel = temperatureViewModel,
lightControlViewModel = lightControlViewModel,
wheelPressureViewModel = wheelPressureViewModel,
settingsViewModel = settingsViewModel,
windowSizeClass = windowSizeClass,
)
}
}
}
Expand Down Expand Up @@ -263,6 +265,8 @@ class MainActivity : ComponentActivity() {

private fun subscribe() {
dataBrokerConnection?.apply {
disconnectListeners.register(disconnectListener)

subscribe(VssDoor(), listener = vssDoorListener)
subscribe(VssTrunk(), listener = vssTrunkListener)
subscribe(VssHvac(), listener = vssTemperatureListener)
Expand All @@ -273,6 +277,8 @@ class MainActivity : ComponentActivity() {

private fun unsubscribe() {
dataBrokerConnection?.apply {
disconnectListeners.unregister(disconnectListener)

unsubscribe(VssDoor(), listener = vssDoorListener)
unsubscribe(VssTrunk(), listener = vssTrunkListener)
unsubscribe(VssHvac(), listener = vssTemperatureListener)
Expand All @@ -297,6 +303,12 @@ class MainActivity : ComponentActivity() {
}

private fun connectToDataBroker(onConnected: () -> Unit = {}) {
// dataBrokerConnection is already established e.g. after an orientation change
if (dataBrokerConnection != null) {
onConnected()
return
}

lifecycleScope.launch {
val connectionInfo = connectionInfoRepository.connectionInfoFlow.first()

Expand All @@ -306,9 +318,7 @@ class MainActivity : ComponentActivity() {
connectionStatusViewModel.connectionState = ConnectionState.CONNECTING
val context = this@MainActivity
val dataBrokerConnector = dataBrokerConnectorFactory.create(context, connectionInfo)
dataBrokerConnection = dataBrokerConnector.connect().apply {
disconnectListeners.register(disconnectListener)
}
dataBrokerConnection = dataBrokerConnector.connect()
connectionStatusViewModel.connectionState = ConnectionState.CONNECTED
onConnected()
} catch (e: DataBrokerException) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2023 Contributors to the Eclipse Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*
*/

package org.eclipse.kuksa.companion.extension

import android.content.res.Configuration
import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
import androidx.compose.material3.windowsizeclass.WindowSizeClass
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp

@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
val Configuration.windowSizeClass: WindowSizeClass
get() {
val screenWidth = screenWidthDp
val screenHeight = screenHeightDp
val size = DpSize(screenWidth.dp, screenHeight.dp)
return WindowSizeClass.calculateFromSize(size)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2023 Contributors to the Eclipse Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*
*/

package org.eclipse.kuksa.companion.extension

import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha

fun Modifier.isVisible(isVisible: Boolean): Modifier {
val alpha = if (isVisible) 1F else 0F

return then(Modifier.alpha(alpha))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright (c) 2023 Contributors to the Eclipse Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*
*/

package org.eclipse.kuksa.companion.extension

fun String.toVertical(): String {
if (isEmpty()) return this

val charArray = toCharArray()
val builder = StringBuilder()
charArray.forEach {
builder.appendLine(it)
}

return builder.toString().dropLast(1)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright (c) 2023 Contributors to the Eclipse Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*
*/

package org.eclipse.kuksa.companion.feature.connection.view

import androidx.compose.foundation.layout.Box
import androidx.compose.material3.windowsizeclass.WindowSizeClass
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.tooling.preview.Preview
import org.eclipse.kuksa.companion.PREVIEW_HEIGHT_DP
import org.eclipse.kuksa.companion.PREVIEW_WIDTH_DP
import org.eclipse.kuksa.companion.extension.windowSizeClass
import org.eclipse.kuksa.companion.feature.connection.viewModel.ConnectionStatusViewModel

@Composable
fun AdaptiveConnectionStatusView(
viewModel: ConnectionStatusViewModel,
windowSizeClass: WindowSizeClass,
modifier: Modifier = Modifier,
) {
if (windowSizeClass.widthSizeClass == WindowWidthSizeClass.Compact) {
HorizontalConnectionStatusView(viewModel, modifier)
} else {
VerticalConnectionStatusView(viewModel, modifier)
}
}

@Preview(widthDp = PREVIEW_WIDTH_DP, heightDp = PREVIEW_HEIGHT_DP)
@Preview(widthDp = PREVIEW_HEIGHT_DP, heightDp = PREVIEW_WIDTH_DP)
@Composable
private fun AdaptiveDisconnectedPreview() {
val viewModel = ConnectionStatusViewModel()
viewModel.connectionState = ConnectionStatusViewModel.ConnectionState.DISCONNECTED

val windowSizeClass = LocalConfiguration.current.windowSizeClass
Box {
AdaptiveConnectionStatusView(viewModel = viewModel, windowSizeClass)
}
}

@Preview(widthDp = PREVIEW_WIDTH_DP, heightDp = PREVIEW_HEIGHT_DP)
@Preview(widthDp = PREVIEW_HEIGHT_DP, heightDp = PREVIEW_WIDTH_DP)
@Composable
private fun AdaptiveConnectingPreview() {
val viewModel = ConnectionStatusViewModel()
viewModel.connectionState = ConnectionStatusViewModel.ConnectionState.CONNECTING

val windowSizeClass = LocalConfiguration.current.windowSizeClass
Box {
AdaptiveConnectionStatusView(viewModel = viewModel, windowSizeClass)
}
}

@Preview(widthDp = PREVIEW_WIDTH_DP, heightDp = PREVIEW_HEIGHT_DP)
@Preview(widthDp = PREVIEW_HEIGHT_DP, heightDp = PREVIEW_WIDTH_DP)
@Composable
private fun AdaptiveConnectedStatusPreview() {
val viewModel = ConnectionStatusViewModel()
viewModel.connectionState = ConnectionStatusViewModel.ConnectionState.CONNECTED

val windowSizeClass = LocalConfiguration.current.windowSizeClass
Box {
AdaptiveConnectionStatusView(viewModel = viewModel, windowSizeClass)
}
}
Loading

0 comments on commit e852135

Please sign in to comment.