From fca84b7f23aea3aacf2245ee4e6c557a9d7549cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sylwester=20Zieli=C5=84ski?= Date: Tue, 4 Jan 2022 15:07:57 +0100 Subject: [PATCH] Add DFU module --- profile_dfu/build.gradle | 30 +++++++++++ .../nordicsemi/dfu/ExampleInstrumentedTest.kt | 24 +++++++++ profile_dfu/src/main/AndroidManifest.xml | 5 ++ .../java/no/nordicsemi/dfu/data/DFUData.kt | 4 ++ .../no/nordicsemi/dfu/data/DFUDataHolder.kt | 15 ++++++ .../nordicsemi/dfu/repository/DFUService.kt | 51 ++++++++++++++++++ .../no/nordicsemi/dfu/view/DFUContentView.kt | 8 +++ .../no/nordicsemi/dfu/view/DFUErrorView.kt | 7 +++ .../nordicsemi/dfu/view/DFUInstallingView.kt | 7 +++ .../java/no/nordicsemi/dfu/view/DFUScreen.kt | 54 +++++++++++++++++++ .../nordicsemi/dfu/view/DFUSelectFileView.kt | 7 +++ .../no/nordicsemi/dfu/view/DFUSuccessView.kt | 7 +++ .../no/nordicsemi/dfu/view/DFUViewEvent.kt | 5 ++ .../dfu/view/NotificationActivity.kt | 49 +++++++++++++++++ .../nordicsemi/dfu/viewmodel/DFUViewModel.kt | 19 +++++++ profile_dfu/src/main/res/values/strings.xml | 4 ++ .../java/no/nordicsemi/dfu/ExampleUnitTest.kt | 17 ++++++ settings.gradle | 3 +- 18 files changed, 315 insertions(+), 1 deletion(-) create mode 100644 profile_dfu/build.gradle create mode 100644 profile_dfu/src/androidTest/java/no/nordicsemi/dfu/ExampleInstrumentedTest.kt create mode 100644 profile_dfu/src/main/AndroidManifest.xml create mode 100644 profile_dfu/src/main/java/no/nordicsemi/dfu/data/DFUData.kt create mode 100644 profile_dfu/src/main/java/no/nordicsemi/dfu/data/DFUDataHolder.kt create mode 100644 profile_dfu/src/main/java/no/nordicsemi/dfu/repository/DFUService.kt create mode 100644 profile_dfu/src/main/java/no/nordicsemi/dfu/view/DFUContentView.kt create mode 100644 profile_dfu/src/main/java/no/nordicsemi/dfu/view/DFUErrorView.kt create mode 100644 profile_dfu/src/main/java/no/nordicsemi/dfu/view/DFUInstallingView.kt create mode 100644 profile_dfu/src/main/java/no/nordicsemi/dfu/view/DFUScreen.kt create mode 100644 profile_dfu/src/main/java/no/nordicsemi/dfu/view/DFUSelectFileView.kt create mode 100644 profile_dfu/src/main/java/no/nordicsemi/dfu/view/DFUSuccessView.kt create mode 100644 profile_dfu/src/main/java/no/nordicsemi/dfu/view/DFUViewEvent.kt create mode 100644 profile_dfu/src/main/java/no/nordicsemi/dfu/view/NotificationActivity.kt create mode 100644 profile_dfu/src/main/java/no/nordicsemi/dfu/viewmodel/DFUViewModel.kt create mode 100644 profile_dfu/src/main/res/values/strings.xml create mode 100644 profile_dfu/src/test/java/no/nordicsemi/dfu/ExampleUnitTest.kt diff --git a/profile_dfu/build.gradle b/profile_dfu/build.gradle new file mode 100644 index 00000000..1e223cb2 --- /dev/null +++ b/profile_dfu/build.gradle @@ -0,0 +1,30 @@ +apply from: rootProject.file("library.gradle") +apply plugin: 'kotlin-parcelize' + +dependencies { + implementation project(":lib_service") + implementation project(":lib_theme") + implementation project(":lib_utils") + + implementation libs.chart + + implementation libs.nordic.ble.common + implementation libs.nordic.theme + + implementation libs.nordic.log + implementation libs.nordic.dfu + + implementation libs.bundles.compose + implementation libs.androidx.core + implementation libs.material + implementation libs.lifecycle.activity + implementation libs.lifecycle.service + implementation libs.compose.lifecycle + implementation libs.compose.activity + + testImplementation libs.test.junit + androidTestImplementation libs.android.test.junit + androidTestImplementation libs.android.test.espresso + androidTestImplementation libs.android.test.compose.ui + debugImplementation libs.android.test.compose.tooling +} diff --git a/profile_dfu/src/androidTest/java/no/nordicsemi/dfu/ExampleInstrumentedTest.kt b/profile_dfu/src/androidTest/java/no/nordicsemi/dfu/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..9e50f754 --- /dev/null +++ b/profile_dfu/src/androidTest/java/no/nordicsemi/dfu/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package no.nordicsemi.dfu + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("no.nordicsemi.dfu.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/profile_dfu/src/main/AndroidManifest.xml b/profile_dfu/src/main/AndroidManifest.xml new file mode 100644 index 00000000..47115554 --- /dev/null +++ b/profile_dfu/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/profile_dfu/src/main/java/no/nordicsemi/dfu/data/DFUData.kt b/profile_dfu/src/main/java/no/nordicsemi/dfu/data/DFUData.kt new file mode 100644 index 00000000..eb8ba2a3 --- /dev/null +++ b/profile_dfu/src/main/java/no/nordicsemi/dfu/data/DFUData.kt @@ -0,0 +1,4 @@ +package no.nordicsemi.dfu.data + +class DFUData { +} \ No newline at end of file diff --git a/profile_dfu/src/main/java/no/nordicsemi/dfu/data/DFUDataHolder.kt b/profile_dfu/src/main/java/no/nordicsemi/dfu/data/DFUDataHolder.kt new file mode 100644 index 00000000..cbe95f47 --- /dev/null +++ b/profile_dfu/src/main/java/no/nordicsemi/dfu/data/DFUDataHolder.kt @@ -0,0 +1,15 @@ +package no.nordicsemi.dfu.data + +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +internal class DFUDataHolder @Inject constructor() { + + private val _data = MutableStateFlow(DFUData()) + val data: StateFlow = _data + + +} diff --git a/profile_dfu/src/main/java/no/nordicsemi/dfu/repository/DFUService.kt b/profile_dfu/src/main/java/no/nordicsemi/dfu/repository/DFUService.kt new file mode 100644 index 00000000..634c8a73 --- /dev/null +++ b/profile_dfu/src/main/java/no/nordicsemi/dfu/repository/DFUService.kt @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2015, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package no.nordicsemi.dfu.repository + +import android.app.Activity +import no.nordicsemi.android.dfu.DfuBaseService +import no.nordicsemi.dfu.view.NotificationActivity + +class DFUService : DfuBaseService() { + + override fun getNotificationTarget(): Class? { + /* + * As a target activity the NotificationActivity is returned, not the MainActivity. This is because the notification must create a new task: + * + * intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + * + * when user press it. Using NotificationActivity we can check whether the new activity is a root activity (that means no other activity was open before) + * or that there is other activity already open. In the later case the notificationActivity will just be closed. System will restore the previous activity. + * However if the application has been closed during upload and user click the notification a NotificationActivity will be launched as a root activity. + * It will create and start the main activity and terminate itself. + * + * This method may be used to restore the target activity in case the application was closed or is open. It may also be used to recreate an activity + * history (see NotificationActivity). + */ + return NotificationActivity::class.java + } + + override fun isDebug(): Boolean { + // return BuildConfig.DEBUG; + return true + } +} \ No newline at end of file diff --git a/profile_dfu/src/main/java/no/nordicsemi/dfu/view/DFUContentView.kt b/profile_dfu/src/main/java/no/nordicsemi/dfu/view/DFUContentView.kt new file mode 100644 index 00000000..b0d0df2f --- /dev/null +++ b/profile_dfu/src/main/java/no/nordicsemi/dfu/view/DFUContentView.kt @@ -0,0 +1,8 @@ +package no.nordicsemi.dfu.view + +import androidx.compose.runtime.Composable +import no.nordicsemi.dfu.data.DFUData + +@Composable +internal fun DFUContentView(state: DFUData, onEvent: (DFUViewEvent) -> Unit) { +} diff --git a/profile_dfu/src/main/java/no/nordicsemi/dfu/view/DFUErrorView.kt b/profile_dfu/src/main/java/no/nordicsemi/dfu/view/DFUErrorView.kt new file mode 100644 index 00000000..911c5167 --- /dev/null +++ b/profile_dfu/src/main/java/no/nordicsemi/dfu/view/DFUErrorView.kt @@ -0,0 +1,7 @@ +package no.nordicsemi.dfu.view + +import androidx.compose.runtime.Composable + +@Composable +fun DFUErrorView() { +} diff --git a/profile_dfu/src/main/java/no/nordicsemi/dfu/view/DFUInstallingView.kt b/profile_dfu/src/main/java/no/nordicsemi/dfu/view/DFUInstallingView.kt new file mode 100644 index 00000000..c3161e33 --- /dev/null +++ b/profile_dfu/src/main/java/no/nordicsemi/dfu/view/DFUInstallingView.kt @@ -0,0 +1,7 @@ +package no.nordicsemi.dfu.view + +import androidx.compose.runtime.Composable + +@Composable +fun DFUInstallingView() { +} diff --git a/profile_dfu/src/main/java/no/nordicsemi/dfu/view/DFUScreen.kt b/profile_dfu/src/main/java/no/nordicsemi/dfu/view/DFUScreen.kt new file mode 100644 index 00000000..f657d6c8 --- /dev/null +++ b/profile_dfu/src/main/java/no/nordicsemi/dfu/view/DFUScreen.kt @@ -0,0 +1,54 @@ +package no.nordicsemi.dfu.view + +import android.content.Intent +import androidx.compose.foundation.layout.Column +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.hilt.navigation.compose.hiltViewModel +import no.nordicsemi.android.theme.view.BackIconAppBar +import no.nordicsemi.android.utils.isServiceRunning +import no.nordicsemi.dfu.R +import no.nordicsemi.dfu.data.DFUData +import no.nordicsemi.dfu.repository.DFUService +import no.nordicsemi.dfu.viewmodel.DFUViewModel + +@Composable +fun DFUScreen(finishAction: () -> Unit) { + val viewModel: DFUViewModel = hiltViewModel() + val state = viewModel.state.collectAsState().value + val isScreenActive = viewModel.isActive.collectAsState().value + + val context = LocalContext.current + LaunchedEffect(isScreenActive) { + if (!isScreenActive) { + finishAction() + } + if (context.isServiceRunning(DFUService::class.java.name)) { + val intent = Intent(context, DFUService::class.java) + context.stopService(intent) + } + } + + LaunchedEffect("start-service") { + if (!context.isServiceRunning(DFUService::class.java.name)) { + val intent = Intent(context, DFUService::class.java) + context.startService(intent) + } + } + + DFUView(state) { viewModel.onEvent(it) } +} + +@Composable +private fun DFUView(state: DFUData, onEvent: (DFUViewEvent) -> Unit) { + Column { + BackIconAppBar(stringResource(id = R.string.dfu_title)) { + onEvent(OnDisconnectButtonClick) + } + + DFUContentView(state) { onEvent(it) } + } +} diff --git a/profile_dfu/src/main/java/no/nordicsemi/dfu/view/DFUSelectFileView.kt b/profile_dfu/src/main/java/no/nordicsemi/dfu/view/DFUSelectFileView.kt new file mode 100644 index 00000000..6992f962 --- /dev/null +++ b/profile_dfu/src/main/java/no/nordicsemi/dfu/view/DFUSelectFileView.kt @@ -0,0 +1,7 @@ +package no.nordicsemi.dfu.view + +import androidx.compose.runtime.Composable + +@Composable +fun DFUSelectFileView() { +} diff --git a/profile_dfu/src/main/java/no/nordicsemi/dfu/view/DFUSuccessView.kt b/profile_dfu/src/main/java/no/nordicsemi/dfu/view/DFUSuccessView.kt new file mode 100644 index 00000000..d8277efe --- /dev/null +++ b/profile_dfu/src/main/java/no/nordicsemi/dfu/view/DFUSuccessView.kt @@ -0,0 +1,7 @@ +package no.nordicsemi.dfu.view + +import androidx.compose.runtime.Composable + +@Composable +fun DFUSuccessView() { +} diff --git a/profile_dfu/src/main/java/no/nordicsemi/dfu/view/DFUViewEvent.kt b/profile_dfu/src/main/java/no/nordicsemi/dfu/view/DFUViewEvent.kt new file mode 100644 index 00000000..56cb09e4 --- /dev/null +++ b/profile_dfu/src/main/java/no/nordicsemi/dfu/view/DFUViewEvent.kt @@ -0,0 +1,5 @@ +package no.nordicsemi.dfu.view + +internal sealed class DFUViewEvent + +internal object OnDisconnectButtonClick : DFUViewEvent() diff --git a/profile_dfu/src/main/java/no/nordicsemi/dfu/view/NotificationActivity.kt b/profile_dfu/src/main/java/no/nordicsemi/dfu/view/NotificationActivity.kt new file mode 100644 index 00000000..207d4370 --- /dev/null +++ b/profile_dfu/src/main/java/no/nordicsemi/dfu/view/NotificationActivity.kt @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2015, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package no.nordicsemi.dfu.view + +import android.app.Activity +import android.os.Bundle + +class NotificationActivity : Activity() { + + protected override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + // If this activity is the root activity of the task, the app is not running + if (isTaskRoot()) { + // Start the app before finishing + //TODO +// val parentIntent = Intent(this, FeaturesActivity::class.java) +// parentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) +// val startAppIntent = Intent(this, DfuActivity::class.java) +// if (getIntent() != null && getIntent().getExtras() != null) startAppIntent.putExtras( +// getIntent().getExtras() +// ) +// startActivities(arrayOf(parentIntent, startAppIntent)) + } + + // Now finish, which will drop the user in to the activity that was at the top + // of the task stack + finish() + } +} \ No newline at end of file diff --git a/profile_dfu/src/main/java/no/nordicsemi/dfu/viewmodel/DFUViewModel.kt b/profile_dfu/src/main/java/no/nordicsemi/dfu/viewmodel/DFUViewModel.kt new file mode 100644 index 00000000..03491672 --- /dev/null +++ b/profile_dfu/src/main/java/no/nordicsemi/dfu/viewmodel/DFUViewModel.kt @@ -0,0 +1,19 @@ +package no.nordicsemi.dfu.viewmodel + +import dagger.hilt.android.lifecycle.HiltViewModel +import no.nordicsemi.android.theme.viewmodel.CloseableViewModel +import no.nordicsemi.dfu.data.DFUDataHolder +import no.nordicsemi.dfu.view.DFUViewEvent +import javax.inject.Inject + +@HiltViewModel +internal class DFUViewModel @Inject constructor( + private val dataHolder: DFUDataHolder +) : CloseableViewModel() { + + val state = dataHolder.data + + fun onEvent(event: DFUViewEvent) { + + } +} diff --git a/profile_dfu/src/main/res/values/strings.xml b/profile_dfu/src/main/res/values/strings.xml new file mode 100644 index 00000000..287bafd3 --- /dev/null +++ b/profile_dfu/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + + DFU + \ No newline at end of file diff --git a/profile_dfu/src/test/java/no/nordicsemi/dfu/ExampleUnitTest.kt b/profile_dfu/src/test/java/no/nordicsemi/dfu/ExampleUnitTest.kt new file mode 100644 index 00000000..d48fc46b --- /dev/null +++ b/profile_dfu/src/test/java/no/nordicsemi/dfu/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package no.nordicsemi.dfu + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index df157373..aff91c5a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -7,7 +7,6 @@ dependencyResolutionManagement { maven { url 'https://jitpack.io' } mavenCentral() mavenLocal() - jcenter() // Warning: this repository is going to shut down soon } versionCatalogs { @@ -17,6 +16,7 @@ dependencyResolutionManagement { alias('nordic-log').to('no.nordicsemi.android:log:2.3.0') alias('nordic-scanner').to('no.nordicsemi.android.support.v18:scanner:1.5.0') alias('nordic-ui-scanner').to('no.nordicsemi.android.common:scanner:1.0.0') + alias('nordic-dfu').to('no.nordicsemi.android:dfu:1.12.1-beta01') alias('localbroadcastmanager').to('androidx.localbroadcastmanager:localbroadcastmanager:1.0.0') alias('material').to('com.google.android.material:material:1.5.0-beta01') @@ -95,3 +95,4 @@ if (file('../Android-Common-Libraries').exists()) { // includeBuild('../Android-Scanner-Compat-Library') //} include ':profile_uart' +include ':profile_dfu'