Minimal Android Flavor (#682)

* Initial rip out of most of the Googley stuff.

* Lokalise plugin and dependencies removed.
They would be more useful if we had a large app with many translations.  However, with most of our UI being a PWA the usefulness of it is very limited.

* Both apps now build.... Yay

* Formatting.

* Tests and checks pass.

* Make sure Github Actions are up to date.

* Fix more unit tests.

* ktLint

* Use config class for gradle dependencies.
This commit is contained in:
Justin Bassett 2020-07-31 07:46:54 -04:00 committed by GitHub
parent f468ecefdc
commit bf29174a4a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 401 additions and 296 deletions

View file

@ -31,7 +31,33 @@
"current_key": "current_key"
}
]
},
{
"client_info": {
"mobilesdk_app_id": "mobilesdk_app_id",
"android_client_info": {
"package_name": "io.homeassistant.companion.android.minimal"
}
},
"api_key": [
{
"current_key": "current_key"
}
]
},
{
"client_info": {
"mobilesdk_app_id": "mobilesdk_app_id",
"android_client_info": {
"package_name": "io.homeassistant.companion.android.minimal.debug"
}
},
"api_key": [
{
"current_key": "current_key"
}
]
}
],
"configuration_version": "1"
}
}

View file

@ -83,14 +83,19 @@ jobs:
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEYSTORE_ALIAS: ${{ secrets.KEYSTORE_ALIAS }}
KEYSTORE_ALIAS_PASSWORD: ${{ secrets.KEYSTORE_ALIAS_PASSWORD }}
run: ./gradlew appDistributionUploadRelease
run: ./gradlew appDistributionUploadFullRelease
- name: Deploy to Playstore Beta
env:
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEYSTORE_ALIAS: ${{ secrets.KEYSTORE_ALIAS }}
KEYSTORE_ALIAS_PASSWORD: ${{ secrets.KEYSTORE_ALIAS_PASSWORD }}
run: ./gradlew publishReleaseBundle
run: ./gradlew publishFullReleaseBundle
- name: Archive Release Build
uses: kittaakos/upload-artifact-as-is@v0
with:
path: ./app/build/outputs/apk/*/release/*-release.apk
release_draft:
name: Update Release Draft

View file

@ -40,6 +40,6 @@ jobs:
run: ./gradlew assembleDebug
- name: Archive Debug Build
uses: actions/upload-artifact@v2
uses: kittaakos/upload-artifact-as-is@v0
with:
path: ./app/build/outputs/apk/debug/app-debug.apk
path: ./app/build/outputs/apk/*/debug/*-debug.apk

View file

@ -97,3 +97,11 @@ jobs:
KEYSTORE_ALIAS: ${{ secrets.KEYSTORE_ALIAS }}
KEYSTORE_ALIAS_PASSWORD: ${{ secrets.KEYSTORE_ALIAS_PASSWORD }}
run: ./gradlew promoteArtifact --from-track beta --promote-track production
- name: Upload files to a GitHub release page
uses: svenstaro/upload-release-action@2.0.0
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ./app/build/outputs/apk/*/release/*-release.apk
file_glob: true
tag: ${{ github.ref }}

View file

@ -72,6 +72,17 @@ android {
signingConfig = signingConfigs.getByName("release")
}
}
flavorDimensions("version")
productFlavors {
create("minimal") {
applicationIdSuffix = ".minimal"
versionNameSuffix = "-minimal"
}
create("full") {
applicationIdSuffix = ""
versionNameSuffix = "-full"
}
}
testOptions {
unitTests.apply { isReturnDefaultValues = true }
@ -118,18 +129,17 @@ dependencies {
implementation(Config.Dependency.AndroidX.roomKtx)
kapt(Config.Dependency.AndroidX.roomCompiler)
implementation(Config.Dependency.Misc.threeTenAbp) {
exclude(group = "org.threeten")
}
implementation(Config.Dependency.Misc.lokalize)
implementation(Config.Dependency.Misc.jackson)
implementation(Config.Dependency.Square.okhttp)
implementation(Config.Dependency.Play.location)
implementation(Config.Dependency.Firebase.core)
implementation(Config.Dependency.Firebase.iid)
implementation(Config.Dependency.Firebase.messaging)
implementation(Config.Dependency.Firebase.crashlytics)
// "fullImplementation"(Config.Dependency.Misc.lokalize)
"fullImplementation"(Config.Dependency.Play.location)
"fullImplementation"(Config.Dependency.Firebase.core)
"fullImplementation"(Config.Dependency.Firebase.iid)
"fullImplementation"(Config.Dependency.Firebase.messaging)
"fullImplementation"(Config.Dependency.Firebase.crashlytics)
"fullImplementation"(Config.Dependency.Kotlin.coroutinesPlayServices)
implementation(Config.Dependency.AndroidX.workManager)
implementation(Config.Dependency.AndroidX.biometric)

View file

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="io.homeassistant.companion.android">
<application
android:name=".HomeAssistantApplication"
>
<receiver
android:name=".notifications.NotificationActionReceiver"
android:enabled="true"
android:exported="true" />
<receiver
android:name=".notifications.NotificationDeleteReceiver"
android:enabled="true"
android:exported="true" />
<receiver
android:name=".notifications.NotificationContentReceiver"
android:enabled="true"
android:exported="true" />
<service
android:name=".notifications.MessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/ic_stat_ic_notification" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="@color/colorPrimary" />
</application>
</manifest>

View file

@ -1,7 +1,6 @@
package io.homeassistant.companion.android.background
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.location.Location
@ -14,64 +13,14 @@ import com.google.android.gms.location.LocationCallback
import com.google.android.gms.location.LocationRequest
import com.google.android.gms.location.LocationResult
import com.google.android.gms.location.LocationServices
import io.homeassistant.companion.android.common.dagger.GraphComponentAccessor
import io.homeassistant.companion.android.domain.integration.IntegrationUseCase
import io.homeassistant.companion.android.domain.integration.UpdateLocation
import io.homeassistant.companion.android.util.PermissionManager
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
class LocationBroadcastReceiver : BroadcastReceiver() {
class LocationBroadcastReceiver : LocationBroadcastReceiverBase() {
companion object {
const val MINIMUM_ACCURACY = 200
const val ACTION_REQUEST_LOCATION_UPDATES =
"io.homeassistant.companion.android.background.REQUEST_UPDATES"
const val ACTION_REQUEST_ACCURATE_LOCATION_UPDATE =
"io.homeassistant.companion.android.background.REQUEST_ACCURATE_UPDATE"
const val ACTION_PROCESS_LOCATION =
"io.homeassistant.companion.android.background.PROCESS_UPDATES"
const val ACTION_PROCESS_GEO =
"io.homeassistant.companion.android.background.PROCESS_GEOFENCE"
private const val TAG = "LocBroadcastReceiver"
}
@Inject
lateinit var integrationUseCase: IntegrationUseCase
private val mainScope: CoroutineScope = CoroutineScope(Dispatchers.Main + Job())
override fun onReceive(context: Context, intent: Intent) {
ensureInjected(context)
when (intent.action) {
Intent.ACTION_BOOT_COMPLETED,
ACTION_REQUEST_LOCATION_UPDATES -> setupLocationTracking(context)
ACTION_PROCESS_LOCATION -> handleLocationUpdate(intent)
ACTION_PROCESS_GEO -> handleGeoUpdate(context, intent)
ACTION_REQUEST_ACCURATE_LOCATION_UPDATE -> requestSingleAccurateLocation(context)
else -> Log.w(TAG, "Unknown intent action: ${intent.action}!")
}
}
private fun ensureInjected(context: Context) {
if (context.applicationContext is GraphComponentAccessor) {
DaggerReceiverComponent.builder()
.appComponent((context.applicationContext as GraphComponentAccessor).appComponent)
.build()
.inject(this)
} else {
throw Exception("Application Context passed is not of our application!")
}
}
private fun setupLocationTracking(context: Context) {
override fun setupLocationTracking(context: Context) {
if (!PermissionManager.checkLocationPermission(context)) {
Log.w(TAG, "Not starting location reporting because of permissions.")
return
@ -140,7 +89,7 @@ class LocationBroadcastReceiver : BroadcastReceiver() {
}
}
private fun handleLocationUpdate(intent: Intent) {
override fun handleLocationUpdate(intent: Intent) {
Log.d(TAG, "Received location update.")
LocationResult.extractResult(intent)?.lastLocation?.let {
if (it.accuracy > MINIMUM_ACCURACY) {
@ -151,7 +100,7 @@ class LocationBroadcastReceiver : BroadcastReceiver() {
}
}
private fun handleGeoUpdate(context: Context, intent: Intent) {
override fun handleGeoUpdate(context: Context, intent: Intent) {
Log.d(TAG, "Received geofence update.")
val geofencingEvent = GeofencingEvent.fromIntent(intent)
if (geofencingEvent.hasError()) {
@ -197,7 +146,7 @@ class LocationBroadcastReceiver : BroadcastReceiver() {
}
private fun getLocationUpdateIntent(context: Context, isGeofence: Boolean): PendingIntent {
val intent = Intent(context, LocationBroadcastReceiver::class.java)
val intent = Intent(context, LocationBroadcastReceiverBase::class.java)
intent.action = if (isGeofence) ACTION_PROCESS_GEO else ACTION_PROCESS_LOCATION
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
}
@ -233,7 +182,7 @@ class LocationBroadcastReceiver : BroadcastReceiver() {
return geofencingRequestBuilder.build()
}
private fun requestSingleAccurateLocation(context: Context) {
override fun requestSingleAccurateLocation(context: Context) {
if (!PermissionManager.checkLocationPermission(context)) {
Log.w(TAG, "Not getting single accurate location because of permissions.")
return

View file

@ -0,0 +1,35 @@
package io.homeassistant.companion.android.launch
import android.os.Build
import android.util.Log
import com.google.firebase.iid.FirebaseInstanceId
import io.homeassistant.companion.android.BuildConfig
import io.homeassistant.companion.android.domain.authentication.AuthenticationUseCase
import io.homeassistant.companion.android.domain.integration.IntegrationUseCase
import java.lang.Exception
import javax.inject.Inject
import kotlinx.coroutines.launch
import kotlinx.coroutines.tasks.await
class LaunchPresenterImpl @Inject constructor(
view: LaunchView,
authenticationUseCase: AuthenticationUseCase,
integrationUseCase: IntegrationUseCase
) : LaunchPresenterBase(view, authenticationUseCase, integrationUseCase) {
override fun resyncRegistration() {
mainScope.launch {
try {
integrationUseCase.updateRegistration(
"${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})",
null,
Build.MANUFACTURER ?: "UNKNOWN",
Build.MODEL ?: "UNKNOWN",
Build.VERSION.SDK_INT.toString(),
pushToken = FirebaseInstanceId.getInstance().instanceId.await().token
)
} catch (e: Exception) {
Log.e(TAG, "Issue updating Registration", e)
}
}
}
}

View file

@ -18,7 +18,7 @@ import androidx.core.text.HtmlCompat
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import io.homeassistant.companion.android.R
import io.homeassistant.companion.android.background.LocationBroadcastReceiver
import io.homeassistant.companion.android.background.LocationBroadcastReceiverBase
import io.homeassistant.companion.android.common.dagger.GraphComponentAccessor
import io.homeassistant.companion.android.domain.authentication.AuthenticationUseCase
import io.homeassistant.companion.android.domain.authentication.SessionState
@ -106,8 +106,8 @@ class MessagingService : FirebaseMessagingService() {
}
private fun requestAccurateLocationUpdate() {
val intent = Intent(this, LocationBroadcastReceiver::class.java)
intent.action = LocationBroadcastReceiver.ACTION_REQUEST_ACCURATE_LOCATION_UPDATE
val intent = Intent(this, LocationBroadcastReceiverBase::class.java)
intent.action = LocationBroadcastReceiverBase.ACTION_REQUEST_ACCURATE_LOCATION_UPDATE
sendBroadcast(intent)
}

View file

@ -0,0 +1,31 @@
package io.homeassistant.companion.android.onboarding.integration
import android.util.Log
import com.google.firebase.iid.FirebaseInstanceId
import io.homeassistant.companion.android.domain.integration.DeviceRegistration
import io.homeassistant.companion.android.domain.integration.IntegrationUseCase
import javax.inject.Inject
import kotlinx.coroutines.tasks.await
class MobileAppIntegrationPresenterImpl @Inject constructor(
view: MobileAppIntegrationView,
integrationUseCase: IntegrationUseCase
) : MobileAppIntegrationPresenterBase(
view, integrationUseCase
) {
override suspend fun createRegistration(simple: Boolean): DeviceRegistration {
val registration = super.createRegistration(simple)
if (!simple) {
try {
val instanceId = FirebaseInstanceId.getInstance().instanceId.await()
registration.pushToken = instanceId.token
} catch (e: Exception) {
Log.e(TAG, "Unable to get firebase token.", e)
throw e
}
}
return registration
}
}

View file

@ -0,0 +1,25 @@
package io.homeassistant.companion.android.sensors
import android.content.Context
import io.homeassistant.companion.android.domain.integration.IntegrationUseCase
import io.homeassistant.companion.android.util.PermissionManager
class AllSensorsUpdaterImpl(
integrationUseCase: IntegrationUseCase,
appContext: Context
) :
AllSensorsUpdater(integrationUseCase, appContext) {
override suspend fun getManagers(): List<SensorManager> {
val sensorManagers = mutableListOf(
BatterySensorManager(),
NetworkSensorManager()
)
if (integrationUseCase.isBackgroundTrackingEnabled() && PermissionManager.checkLocationPermission(appContext)) {
sensorManagers.add(GeocodeSensorManager())
}
return sensorManagers
}
}

View file

@ -5,7 +5,7 @@ import android.location.Geocoder
import android.util.Log
import com.google.android.gms.location.LocationServices
import com.google.android.gms.tasks.Tasks
import io.homeassistant.companion.android.background.LocationBroadcastReceiver
import io.homeassistant.companion.android.background.LocationBroadcastReceiverBase
import io.homeassistant.companion.android.domain.integration.Sensor
import io.homeassistant.companion.android.domain.integration.SensorRegistration
import io.homeassistant.companion.android.util.PermissionManager
@ -47,7 +47,7 @@ class GeocodeSensorManager : SensorManager {
try {
val locApi = LocationServices.getFusedLocationProviderClient(context)
Tasks.await(locApi.lastLocation)?.let {
if (it.accuracy > LocationBroadcastReceiver.MINIMUM_ACCURACY)
if (it.accuracy > LocationBroadcastReceiverBase.MINIMUM_ACCURACY)
return null
Geocoder(context)

View file

@ -71,21 +71,6 @@
</intent-filter>
</receiver>
<receiver
android:name=".notifications.NotificationActionReceiver"
android:enabled="true"
android:exported="true" />
<receiver
android:name=".notifications.NotificationDeleteReceiver"
android:enabled="true"
android:exported="true" />
<receiver
android:name=".notifications.NotificationContentReceiver"
android:enabled="true"
android:exported="true" />
<activity android:name=".launch.LaunchActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@ -111,20 +96,6 @@
android:name=".settings.SettingsActivity"
android:parentActivityName=".webview.WebViewActivity" />
<service
android:name=".notifications.MessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/ic_stat_ic_notification" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="@color/colorPrimary" />
</application>
</manifest>

View file

@ -3,24 +3,18 @@ package io.homeassistant.companion.android
import android.app.Application
import android.content.Intent
import android.content.IntentFilter
import com.jakewharton.threetenabp.AndroidThreeTen
import com.lokalise.sdk.Lokalise
import io.homeassistant.companion.android.common.dagger.AppComponent
import io.homeassistant.companion.android.common.dagger.Graph
import io.homeassistant.companion.android.common.dagger.GraphComponentAccessor
import io.homeassistant.companion.android.sensors.ChargingBroadcastReceiver
class HomeAssistantApplication : Application(), GraphComponentAccessor {
open class HomeAssistantApplication : Application(), GraphComponentAccessor {
lateinit var graph: Graph
override fun onCreate() {
super.onCreate()
Lokalise.init(this, "16ff9dee3da7a3cba0d998a4e58fa99e92ba089d", "145814835dd655bc5ab0d0.36753359")
Lokalise.updateTranslations()
AndroidThreeTen.init(this)
graph = Graph(this, 0)
// This will cause the sensor to be updated every time the OS broadcasts that a cable was plugged/unplugged.

View file

@ -0,0 +1,64 @@
package io.homeassistant.companion.android.background
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
import io.homeassistant.companion.android.common.dagger.GraphComponentAccessor
import io.homeassistant.companion.android.domain.integration.IntegrationUseCase
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
abstract class LocationBroadcastReceiverBase : BroadcastReceiver() {
companion object {
const val MINIMUM_ACCURACY = 200
const val ACTION_REQUEST_LOCATION_UPDATES =
"io.homeassistant.companion.android.background.REQUEST_UPDATES"
const val ACTION_REQUEST_ACCURATE_LOCATION_UPDATE =
"io.homeassistant.companion.android.background.REQUEST_ACCURATE_UPDATE"
const val ACTION_PROCESS_LOCATION =
"io.homeassistant.companion.android.background.PROCESS_UPDATES"
const val ACTION_PROCESS_GEO =
"io.homeassistant.companion.android.background.PROCESS_GEOFENCE"
internal const val TAG = "LocBroadcastReceiver"
}
@Inject
lateinit var integrationUseCase: IntegrationUseCase
internal val mainScope: CoroutineScope = CoroutineScope(Dispatchers.Main + Job())
override fun onReceive(context: Context, intent: Intent) {
ensureInjected(context)
when (intent.action) {
Intent.ACTION_BOOT_COMPLETED,
ACTION_REQUEST_LOCATION_UPDATES -> setupLocationTracking(context)
ACTION_PROCESS_LOCATION -> handleLocationUpdate(intent)
ACTION_PROCESS_GEO -> handleGeoUpdate(context, intent)
ACTION_REQUEST_ACCURATE_LOCATION_UPDATE -> requestSingleAccurateLocation(context)
else -> Log.w(TAG, "Unknown intent action: ${intent.action}!")
}
}
private fun ensureInjected(context: Context) {
if (context.applicationContext is GraphComponentAccessor) {
DaggerReceiverComponent.builder()
.appComponent((context.applicationContext as GraphComponentAccessor).appComponent)
.build()
.inject(this)
} else {
throw Exception("Application Context passed is not of our application!")
}
}
internal abstract fun setupLocationTracking(context: Context)
internal abstract fun handleLocationUpdate(intent: Intent)
internal abstract fun handleGeoUpdate(context: Context, intent: Intent)
internal abstract fun requestSingleAccurateLocation(context: Context)
}

View file

@ -6,5 +6,5 @@ import io.homeassistant.companion.android.common.dagger.AppComponent
@Component(dependencies = [AppComponent::class])
interface ReceiverComponent {
fun inject(receiver: LocationBroadcastReceiver)
fun inject(receiver: LocationBroadcastReceiverBase)
}

View file

@ -1,11 +1,7 @@
package io.homeassistant.companion.android.launch
import android.content.Context
import android.os.Bundle
import android.view.MenuInflater
import androidx.appcompat.app.AppCompatActivity
import com.lokalise.sdk.LokaliseContextWrapper
import com.lokalise.sdk.menu_inflater.LokaliseMenuInflater
import io.homeassistant.companion.android.DaggerPresenterComponent
import io.homeassistant.companion.android.PresenterModule
import io.homeassistant.companion.android.common.dagger.GraphComponentAccessor
@ -15,7 +11,8 @@ import javax.inject.Inject
class LaunchActivity : AppCompatActivity(), LaunchView {
@Inject lateinit var presenter: LaunchPresenter
@Inject
lateinit var presenter: LaunchPresenter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -48,12 +45,4 @@ class LaunchActivity : AppCompatActivity(), LaunchView {
presenter.onFinish()
super.onDestroy()
}
override fun attachBaseContext(newBase: Context) {
super.attachBaseContext(LokaliseContextWrapper.wrap(newBase))
}
override fun getMenuInflater(): MenuInflater {
return LokaliseMenuInflater(this)
}
}

View file

@ -1,31 +1,25 @@
package io.homeassistant.companion.android.launch
import android.os.Build
import android.util.Log
import com.google.firebase.iid.FirebaseInstanceId
import io.homeassistant.companion.android.BuildConfig
import io.homeassistant.companion.android.domain.authentication.AuthenticationUseCase
import io.homeassistant.companion.android.domain.authentication.SessionState
import io.homeassistant.companion.android.domain.integration.IntegrationUseCase
import java.lang.Exception
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
class LaunchPresenterImpl @Inject constructor(
abstract class LaunchPresenterBase(
private val view: LaunchView,
private val authenticationUseCase: AuthenticationUseCase,
private val integrationUseCase: IntegrationUseCase
internal val integrationUseCase: IntegrationUseCase
) : LaunchPresenter {
companion object {
const val TAG = "LaunchPresenter"
}
private val mainScope: CoroutineScope = CoroutineScope(Dispatchers.Main + Job())
internal val mainScope: CoroutineScope = CoroutineScope(Dispatchers.Main + Job())
override fun onViewReady() {
mainScope.launch {
@ -50,22 +44,5 @@ class LaunchPresenterImpl @Inject constructor(
}
// TODO: This should probably go in settings?
private fun resyncRegistration() {
FirebaseInstanceId.getInstance().instanceId.addOnSuccessListener {
mainScope.launch {
try {
integrationUseCase.updateRegistration(
"${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})",
null,
Build.MANUFACTURER ?: "UNKNOWN",
Build.MODEL ?: "UNKNOWN",
Build.VERSION.SDK_INT.toString(),
pushToken = it.token
)
} catch (e: Exception) {
Log.e(TAG, "Issue updating Registration", e)
}
}
}
}
internal abstract fun resyncRegistration()
}

View file

@ -3,10 +3,7 @@ package io.homeassistant.companion.android.onboarding
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.MenuInflater
import androidx.appcompat.app.AppCompatActivity
import com.lokalise.sdk.LokaliseContextWrapper
import com.lokalise.sdk.menu_inflater.LokaliseMenuInflater
import io.homeassistant.companion.android.R
import io.homeassistant.companion.android.onboarding.authentication.AuthenticationFragment
import io.homeassistant.companion.android.onboarding.authentication.AuthenticationListener
@ -104,14 +101,6 @@ class OnboardingActivity : AppCompatActivity(), DiscoveryListener, ManualSetupLi
startWebView()
}
override fun attachBaseContext(newBase: Context) {
super.attachBaseContext(LokaliseContextWrapper.wrap(newBase))
}
override fun getMenuInflater(): MenuInflater {
return LokaliseMenuInflater(this)
}
private fun startWebView() {
startActivity(WebViewActivity.newInstance(this))
finish()

View file

@ -67,7 +67,7 @@ class MobileAppIntegrationFragment : Fragment(), MobileAppIntegrationView {
return inflater.inflate(R.layout.fragment_mobile_app_integration, container, false).apply {
viewFlipper = this.findViewById(R.id.view_flipper)
findViewById<Button>(R.id.retry).setOnClickListener {
presenter.onRegistrationAttempt()
presenter.onRegistrationAttempt(false)
}
findViewById<AppCompatButton>(R.id.location_perms).apply {
@ -107,16 +107,16 @@ class MobileAppIntegrationFragment : Fragment(), MobileAppIntegrationView {
.setTitle(R.string.firebase_error_title)
.setMessage(R.string.firebase_error_message)
.setPositiveButton(R.string.skip) { _, _ ->
presenter.onRegistrationAttempt(false)
presenter.onRegistrationAttempt(true)
}
.setNegativeButton(R.string.retry) { _, _ ->
presenter.onRegistrationAttempt(true)
presenter.onRegistrationAttempt(false)
}
.create()
.show()
}
presenter.onRegistrationAttempt()
presenter.onRegistrationAttempt(false)
}
}

View file

@ -1,7 +1,7 @@
package io.homeassistant.companion.android.onboarding.integration
interface MobileAppIntegrationPresenter {
fun onRegistrationAttempt(includeFirebase: Boolean = true)
fun onRegistrationAttempt(simple: Boolean)
fun onToggleZoneTracking(enabled: Boolean)
fun onToggleBackgroundTracking(enabled: Boolean)
fun onFinish()

View file

@ -2,61 +2,52 @@ package io.homeassistant.companion.android.onboarding.integration
import android.os.Build
import android.util.Log
import com.google.firebase.iid.FirebaseInstanceId
import io.homeassistant.companion.android.BuildConfig
import io.homeassistant.companion.android.domain.integration.DeviceRegistration
import io.homeassistant.companion.android.domain.integration.IntegrationUseCase
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
class MobileAppIntegrationPresenterImpl @Inject constructor(
open class MobileAppIntegrationPresenterBase constructor(
private val view: MobileAppIntegrationView,
private val integrationUseCase: IntegrationUseCase
) : MobileAppIntegrationPresenter {
companion object {
private const val TAG = "IntegrationPresenter"
internal const val TAG = "IntegrationPresenter"
}
private val mainScope: CoroutineScope = CoroutineScope(Dispatchers.Main + Job())
override fun onRegistrationAttempt(includeFirebase: Boolean) {
view.showLoading()
val deviceRegistration = DeviceRegistration(
internal open suspend fun createRegistration(simple: Boolean): DeviceRegistration {
return DeviceRegistration(
"${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})",
Build.MODEL ?: "UNKNOWN"
)
if (includeFirebase) {
val instanceId = FirebaseInstanceId.getInstance().instanceId
instanceId.addOnSuccessListener {
deviceRegistration.pushToken = it.token
register(deviceRegistration)
}
instanceId.addOnFailureListener {
Log.e(TAG, "Couldn't get FirebaseInstanceId", it)
view.showError(true)
}
} else {
register(deviceRegistration)
}
}
private fun register(deviceRegistration: DeviceRegistration) {
override fun onRegistrationAttempt(simple: Boolean) {
view.showLoading()
mainScope.launch {
val deviceRegistration: DeviceRegistration
try {
deviceRegistration = createRegistration(simple)
} catch (e: Exception) {
Log.e(TAG, "Unable to create registration.", e)
view.showError(true)
return@launch
}
try {
integrationUseCase.registerDevice(deviceRegistration)
// TODO: Get the name of the instance to display
view.deviceRegistered()
} catch (e: Exception) {
Log.e(TAG, "Error with registering application", e)
view.showError()
Log.e(TAG, "Unable to register with Home Assistant", e)
view.showError(false)
return@launch
}
view.deviceRegistered()
}
}

View file

@ -4,25 +4,19 @@ import android.content.Context
import android.util.Log
import io.homeassistant.companion.android.SensorUpdater
import io.homeassistant.companion.android.domain.integration.IntegrationUseCase
import io.homeassistant.companion.android.util.PermissionManager
class AllSensorsUpdaterImpl(
private val integrationUseCase: IntegrationUseCase,
private val appContext: Context
abstract class AllSensorsUpdater(
internal val integrationUseCase: IntegrationUseCase,
internal val appContext: Context
) : SensorUpdater {
companion object {
private const val TAG = "AllSensorsUpdaterImpl"
internal const val TAG = "AllSensorsUpdaterImpl"
}
override suspend fun updateSensors() {
val sensorManagers = mutableListOf(
BatterySensorManager(),
NetworkSensorManager()
)
abstract suspend fun getManagers(): List<SensorManager>
if (integrationUseCase.isBackgroundTrackingEnabled() && PermissionManager.checkLocationPermission(appContext)) {
sensorManagers.add(GeocodeSensorManager())
}
override suspend fun updateSensors() {
val sensorManagers = getManagers()
registerSensors(sensorManagers)

View file

@ -3,12 +3,9 @@ package io.homeassistant.companion.android.settings
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.MenuInflater
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import com.lokalise.sdk.LokaliseContextWrapper
import com.lokalise.sdk.menu_inflater.LokaliseMenuInflater
import io.homeassistant.companion.android.BuildConfig
import io.homeassistant.companion.android.R
import io.homeassistant.companion.android.settings.ssid.SsidDialogFragment
@ -45,12 +42,4 @@ class SettingsActivity : AppCompatActivity(),
}
return false
}
override fun attachBaseContext(newBase: Context) {
super.attachBaseContext(LokaliseContextWrapper.wrap(newBase))
}
override fun getMenuInflater(): MenuInflater {
return LokaliseMenuInflater(this)
}
}

View file

@ -8,6 +8,7 @@ import android.os.Build
import androidx.core.app.ActivityCompat
import androidx.fragment.app.Fragment
import io.homeassistant.companion.android.background.LocationBroadcastReceiver
import io.homeassistant.companion.android.background.LocationBroadcastReceiverBase
class PermissionManager {
@ -65,7 +66,7 @@ class PermissionManager {
fun restartLocationTracking(context: Context) {
val intent = Intent(context, LocationBroadcastReceiver::class.java)
intent.action = LocationBroadcastReceiver.ACTION_REQUEST_LOCATION_UPDATES
intent.action = LocationBroadcastReceiverBase.ACTION_REQUEST_LOCATION_UPDATES
context.sendBroadcast(intent)
}

View file

@ -17,7 +17,6 @@ import android.text.method.HideReturnsTransformationMethod
import android.text.method.PasswordTransformationMethod
import android.util.Log
import android.util.Rational
import android.view.MenuInflater
import android.view.View
import android.webkit.CookieManager
import android.webkit.HttpAuthHandler
@ -38,15 +37,13 @@ import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.room.Room
import com.lokalise.sdk.LokaliseContextWrapper
import com.lokalise.sdk.menu_inflater.LokaliseMenuInflater
import eightbitlab.com.blurview.RenderScriptBlur
import io.homeassistant.companion.android.BuildConfig
import io.homeassistant.companion.android.DaggerPresenterComponent
import io.homeassistant.companion.android.PresenterModule
import io.homeassistant.companion.android.R
import io.homeassistant.companion.android.authenticator.Authenticator
import io.homeassistant.companion.android.background.LocationBroadcastReceiver
import io.homeassistant.companion.android.background.LocationBroadcastReceiverBase
import io.homeassistant.companion.android.common.dagger.GraphComponentAccessor
import io.homeassistant.companion.android.database.AppDataBase
import io.homeassistant.companion.android.database.AuthenticationList
@ -107,8 +104,8 @@ class WebViewActivity : AppCompatActivity(), io.homeassistant.companion.android.
// Start the sensor worker if they start the app. The only other place we start this ia Boot BroadcastReceiver
SensorWorker.start(this)
val intent = Intent(this, LocationBroadcastReceiver::class.java)
intent.action = LocationBroadcastReceiver.ACTION_REQUEST_LOCATION_UPDATES
val intent = Intent(this, LocationBroadcastReceiverBase::class.java)
intent.action = LocationBroadcastReceiverBase.ACTION_REQUEST_LOCATION_UPDATES
sendBroadcast(intent)
if (BuildConfig.DEBUG) {
@ -440,14 +437,6 @@ class WebViewActivity : AppCompatActivity(), io.homeassistant.companion.android.
}
}
override fun attachBaseContext(newBase: Context) {
super.attachBaseContext(LokaliseContextWrapper.wrap(newBase))
}
override fun getMenuInflater(): MenuInflater {
return LokaliseMenuInflater(this)
}
override fun openOnBoarding() {
finish()
startActivity(Intent(this, OnboardingActivity::class.java))

View file

@ -0,0 +1,22 @@
package io.homeassistant.companion.android.background
import android.content.Context
import android.content.Intent
class LocationBroadcastReceiver : LocationBroadcastReceiverBase() {
override fun setupLocationTracking(context: Context) {
// No op
}
override fun handleLocationUpdate(intent: Intent) {
// No op
}
override fun handleGeoUpdate(context: Context, intent: Intent) {
// No op
}
override fun requestSingleAccurateLocation(context: Context) {
// No op
}
}

View file

@ -0,0 +1,31 @@
package io.homeassistant.companion.android.launch
import android.os.Build
import android.util.Log
import io.homeassistant.companion.android.BuildConfig
import io.homeassistant.companion.android.domain.authentication.AuthenticationUseCase
import io.homeassistant.companion.android.domain.integration.IntegrationUseCase
import javax.inject.Inject
import kotlinx.coroutines.launch
class LaunchPresenterImpl @Inject constructor(
view: LaunchView,
authenticationUseCase: AuthenticationUseCase,
integrationUseCase: IntegrationUseCase
) : LaunchPresenterBase(view, authenticationUseCase, integrationUseCase) {
override fun resyncRegistration() {
mainScope.launch {
try {
integrationUseCase.updateRegistration(
"${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})",
null,
Build.MANUFACTURER ?: "UNKNOWN",
Build.MODEL ?: "UNKNOWN",
Build.VERSION.SDK_INT.toString()
)
} catch (e: Exception) {
Log.e(TAG, "Issue updating Registration", e)
}
}
}
}

View file

@ -0,0 +1,9 @@
package io.homeassistant.companion.android.onboarding.integration
import io.homeassistant.companion.android.domain.integration.IntegrationUseCase
import javax.inject.Inject
class MobileAppIntegrationPresenterImpl @Inject constructor(
view: MobileAppIntegrationView,
integrationUseCase: IntegrationUseCase
) : MobileAppIntegrationPresenterBase(view, integrationUseCase)

View file

@ -0,0 +1,19 @@
package io.homeassistant.companion.android.sensors
import android.content.Context
import io.homeassistant.companion.android.domain.integration.IntegrationUseCase
class AllSensorsUpdaterImpl(
integrationUseCase: IntegrationUseCase,
appContext: Context
) : AllSensorsUpdater(integrationUseCase, appContext) {
override suspend fun getManagers(): List<SensorManager> {
val sensorManagers = mutableListOf(
BatterySensorManager(),
NetworkSensorManager()
)
return sensorManagers
}
}

View file

@ -1,16 +1,10 @@
package io.homeassistant.companion.android.launch
import com.google.android.gms.tasks.OnSuccessListener
import com.google.firebase.iid.FirebaseInstanceId
import com.google.firebase.iid.InstanceIdResult
import io.homeassistant.companion.android.domain.authentication.AuthenticationUseCase
import io.homeassistant.companion.android.domain.authentication.SessionState
import io.homeassistant.companion.android.domain.integration.IntegrationUseCase
import io.mockk.coEvery
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.slot
import io.mockk.verify
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.test.resetMain
@ -22,27 +16,6 @@ object LaunchPresenterImplSpec : Spek({
beforeEachTest {
Dispatchers.setMain(Dispatchers.Unconfined)
val onSuccessListener = slot<OnSuccessListener<InstanceIdResult>>()
val mockResults = mockk<InstanceIdResult> {
every { token } returns "ABC123"
}
mockkStatic(FirebaseInstanceId::class)
every { FirebaseInstanceId.getInstance() } returns mockk {
every { instanceId } returns mockk {
every { addOnSuccessListener(capture(onSuccessListener)) } answers {
onSuccessListener.captured.onSuccess(mockResults)
mockk {
every { result } returns mockResults
}
}
every { addOnFailureListener(any()) } returns mockk {
every { exception } returns Exception()
}
}
}
}
afterEachTest {

View file

@ -1,21 +1,15 @@
package io.homeassistant.companion.android.onboarding.integration
import android.os.Build
import com.google.android.gms.tasks.OnSuccessListener
import com.google.firebase.iid.FirebaseInstanceId
import com.google.firebase.iid.InstanceIdResult
import io.homeassistant.companion.android.BuildConfig
import io.homeassistant.companion.android.domain.integration.DeviceRegistration
import io.homeassistant.companion.android.domain.integration.IntegrationUseCase
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.coVerifyAll
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.runs
import io.mockk.slot
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.setMain
@ -26,27 +20,6 @@ object MobileAppIntegrationPresenterImplSpec : Spek({
beforeEachTest {
Dispatchers.setMain(Dispatchers.Unconfined)
val onSuccessListener = slot<OnSuccessListener<InstanceIdResult>>()
val mockResults = mockk<InstanceIdResult> {
every { token } returns "ABC123"
}
mockkStatic(FirebaseInstanceId::class)
every { FirebaseInstanceId.getInstance() } returns mockk {
every { instanceId } returns mockk {
every { addOnSuccessListener(capture(onSuccessListener)) } answers {
onSuccessListener.captured.onSuccess(mockResults)
mockk {
every { result } returns mockResults
}
}
every { addOnFailureListener(any()) } returns mockk {
every { exception } returns Exception()
}
}
}
}
afterEachTest {
@ -56,20 +29,19 @@ object MobileAppIntegrationPresenterImplSpec : Spek({
describe("presenter") {
val integrationUseCase by memoized { mockk<IntegrationUseCase>(relaxUnitFun = true) }
val view by memoized { mockk<MobileAppIntegrationView>(relaxUnitFun = true) }
val presenter by memoized { MobileAppIntegrationPresenterImpl(view, integrationUseCase) }
val presenter by memoized { MobileAppIntegrationPresenterBase(view, integrationUseCase) }
describe("on registration success") {
val deviceRegistration = DeviceRegistration(
"${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})",
Build.MODEL ?: "UNKNOWN",
"ABC123"
Build.MODEL ?: "UNKNOWN"
)
beforeEachTest {
coEvery { integrationUseCase.registerDevice(deviceRegistration) } just runs
}
describe("register") {
beforeEachTest {
presenter.onRegistrationAttempt()
presenter.onRegistrationAttempt(true)
}
it("should register successfully") {
coVerify {
@ -87,7 +59,7 @@ object MobileAppIntegrationPresenterImplSpec : Spek({
}
describe("register") {
beforeEachTest {
presenter.onRegistrationAttempt()
presenter.onRegistrationAttempt(false)
}
it("should fail") {
coVerifyAll {

View file

@ -1,7 +1,7 @@
object Config {
object Plugin {
const val android = "com.android.tools.build:gradle:4.0.0"
const val android = "com.android.tools.build:gradle:4.0.1"
const val kotlin = "org.jetbrains.kotlin:kotlin-gradle-plugin:${Dependency.Kotlin.version}"
const val google = "com.google.gms:google-services:4.3.3"
const val appDistribution = "com.google.firebase:firebase-appdistribution-gradle:1.4.0"
@ -30,6 +30,7 @@ object Config {
private const val coroutinesVersion = "1.3.3"
const val coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-core:${coroutinesVersion}"
const val coroutinesPlayServices = "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.3.8"
const val coroutinesAndroid = "org.jetbrains.kotlinx:kotlinx-coroutines-android:${coroutinesVersion}"
const val coroutinesTest = "org.jetbrains.kotlinx:kotlinx-coroutines-test:${coroutinesVersion}"
}