Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.WAKE_LOCK" />

<application
Expand Down
336 changes: 313 additions & 23 deletions app/src/main/java/org/staacks/alpharemote/camera/CameraBLE.kt

Large diffs are not rendered by default.

16 changes: 15 additions & 1 deletion app/src/main/java/org/staacks/alpharemote/camera/CameraState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,24 @@ data class CameraStateReady(
val shutter: ReportedBoolean,
val recording: ReportedBoolean,
val pressedButtons: Set<ButtonCode>,
val pressedJogs: Set<JogCode>
val pressedJogs: Set<JogCode>,
val mediaStatus: CameraMediaStatus?,
val batteryStatus: CameraBatteryStatus?
) : CameraState()

data class CameraStateError(
val exception: Exception?,
val description: String = ""
) : CameraState()

data class CameraMediaStatus(
val shotsRemaining: Int?,
val secondsRemaining: Int?,
val description: String
)

data class CameraBatteryStatus(
val percentage: Int,
val charging: Boolean,
val description: String
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,33 @@ package org.staacks.alpharemote.service
import android.Manifest
import android.annotation.SuppressLint
import android.companion.AssociationInfo
import android.companion.CompanionDeviceManager
import android.companion.CompanionDeviceService
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.ServiceInfo
import android.content.res.Configuration
import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import android.location.LocationRequest
import android.os.Bundle
import android.os.PowerManager
import android.os.SystemClock
import android.util.Log
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import org.staacks.alpharemote.MainActivity
import org.staacks.alpharemote.R
import org.staacks.alpharemote.SettingsStore
Expand All @@ -23,25 +39,12 @@ import org.staacks.alpharemote.camera.CACountdown
import org.staacks.alpharemote.camera.CAJog
import org.staacks.alpharemote.camera.CAWaitFor
import org.staacks.alpharemote.camera.CameraAction
import org.staacks.alpharemote.camera.CameraActionPreset
import org.staacks.alpharemote.camera.CameraActionStep
import org.staacks.alpharemote.camera.CameraBLE
import org.staacks.alpharemote.camera.CameraStateIdentified
import org.staacks.alpharemote.camera.CameraStateReady
import org.staacks.alpharemote.camera.WaitTarget
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import org.staacks.alpharemote.camera.CameraActionPreset
import org.staacks.alpharemote.ui.settings.CompanionDeviceHelper
import java.util.LinkedList
import java.util.Timer
import java.util.TimerTask
Expand All @@ -56,6 +59,7 @@ class AlphaRemoteService : CompanionDeviceService() {
private var timer: TimerTask? = null
private var notificationUI: NotificationUI? = null

private var locationManager: LocationManager? = null
private lateinit var pendingActionsWakeLock: PowerManager.WakeLock

companion object {
Expand Down Expand Up @@ -84,6 +88,8 @@ class AlphaRemoteService : CompanionDeviceService() {
var broadcastControl = false
}

@Suppress("DEPRECATION")
@Deprecated("Deprecated in Java")
override fun onDeviceAppeared(address: String) {
Log.d(MainActivity.TAG, "Device appeared: $address")
try {
Expand Down Expand Up @@ -159,6 +165,8 @@ class AlphaRemoteService : CompanionDeviceService() {
Log.d(MainActivity.TAG, "API33 onDeviceAppeared: $associationInfo")
}

@Suppress("DEPRECATION")
@Deprecated("Deprecated in Java")
override fun onDeviceDisappeared(address: String) {
Log.d(MainActivity.TAG, "Device disappeared: $address")
try {
Expand Down Expand Up @@ -191,10 +199,12 @@ class AlphaRemoteService : CompanionDeviceService() {
ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST
)
}
startLocationUpdates()
}

private fun onDisconnect() {
Log.d(MainActivity.TAG, "onDisconnect")
stopLocationUpdates()
_serviceState.value = ServiceStateGone()
cancelPendingActionSteps()
stopForeground(STOP_FOREGROUND_REMOVE)
Expand Down Expand Up @@ -225,6 +235,7 @@ class AlphaRemoteService : CompanionDeviceService() {
startCameraAction(cameraAction.getReleaseStepList())
}

@Suppress("DEPRECATION")
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.d(MainActivity.TAG, "onStartCommand: $intent")
when (intent?.action) {
Expand Down Expand Up @@ -404,4 +415,40 @@ class AlphaRemoteService : CompanionDeviceService() {
}
}

fun startLocationUpdates() {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_BACKGROUND_LOCATION) != PackageManager.PERMISSION_GRANTED) {
Log.w(MainActivity.TAG, "Start location updates: Location permission not granted")
return
}
Log.d(MainActivity.TAG, "Start location updates")
if (locationManager == null) {
locationManager = getSystemService(LOCATION_SERVICE) as LocationManager
}
val request = LocationRequest.Builder(10000L)
.setMinUpdateIntervalMillis(10000L)
.build()
locationManager?.requestLocationUpdates(
LocationManager.FUSED_PROVIDER,
request,
ContextCompat.getMainExecutor(this),
locationListener
)
}

fun stopLocationUpdates() {
Log.d(MainActivity.TAG, "Stop location updates")
locationManager?.removeUpdates(locationListener)
}

private val locationListener = object : LocationListener {
override fun onLocationChanged(location: Location) {
Log.d(MainActivity.TAG, "Latitude: ${location.latitude}, Longitude: ${location.longitude}")
cameraBLE?.sendLocation(location)
}
override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {}
override fun onProviderEnabled(provider: String) {}
override fun onProviderDisabled(provider: String) {}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ object CompanionDeviceHelper {
//with remote disabled it is 0x03006400453122e800...
//This would suggest that we have to check for 0x04.
//So, until we can verify this on a few different models, we do not check this bit to ensure compatibility
//For the a6700 we have to check for 0x04 in the 8th byte. (Remote disabled: 0x03006500553122bb..., Remote enabled: 0x03006500553122bf...)
)
.build()
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ class SettingsFragment : Fragment(), CustomButtonListEventReceiver, CameraAction
SettingsViewModel.SettingsUIAction.UNPAIR -> unpair()
SettingsViewModel.SettingsUIAction.REQUEST_BLUETOOTH_PERMISSION -> requestBluetoothPermission(bluetoothRequestPermissionLauncher, true)
SettingsViewModel.SettingsUIAction.REQUEST_NOTIFICATION_PERMISSION -> requestNotificationPermission(true)
SettingsViewModel.SettingsUIAction.REQUEST_LOCATION_PERMISSION -> requestLocationPermission()
SettingsViewModel.SettingsUIAction.ADD_CUSTOM_BUTTON -> addCustomButton()
SettingsViewModel.SettingsUIAction.HELP_CONNECTION ->
HelpDialogFragment().setContent(
Expand Down Expand Up @@ -263,6 +264,10 @@ class SettingsFragment : Fragment(), CustomButtonListEventReceiver, CameraAction
}
}

private fun requestLocationPermission() {
// TODO
}

private val bluetoothRequestPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
if (isGranted) {
Log.d(MainActivity.TAG, "Bluetooth permission granted.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
var cameraName: String?,
var bluetoothPermissionGranted: Boolean,
var notificationPermissionGranted: Boolean,
var locationPermissionGranted: Boolean,
var bluetoothEnabled: Boolean,
var locationServiceEnabled: Boolean,
var bleScanningEnabled: Boolean
Expand All @@ -60,12 +61,13 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
UNPAIR,
REQUEST_BLUETOOTH_PERMISSION,
REQUEST_NOTIFICATION_PERMISSION,
REQUEST_LOCATION_PERMISSION,
ADD_CUSTOM_BUTTON,
HELP_CONNECTION,
HELP_CUSTOM_BUTTONS
}

private val _uiState = MutableStateFlow(SettingsUIState(cameraState = SettingsUICameraState.OFFLINE, cameraError = null, cameraName = null, bluetoothPermissionGranted = true, notificationPermissionGranted = true, bluetoothEnabled = false, locationServiceEnabled = false, bleScanningEnabled = false))
private val _uiState = MutableStateFlow(SettingsUIState(cameraState = SettingsUICameraState.OFFLINE, cameraError = null, cameraName = null, bluetoothPermissionGranted = true, notificationPermissionGranted = true, locationPermissionGranted = true, bluetoothEnabled = false, locationServiceEnabled = false, bleScanningEnabled = false))
val uiState: StateFlow<SettingsUIState> = _uiState.asStateFlow()

private val _uiAction = MutableSharedFlow<SettingsUIAction>()
Expand Down Expand Up @@ -210,6 +212,12 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
}
}

fun requestLocationPermission() {
viewModelScope.launch {
_uiAction.emit(SettingsUIAction.REQUEST_LOCATION_PERMISSION)
}
}

fun addCustomButton() {
viewModelScope.launch {
_uiAction.emit(SettingsUIAction.ADD_CUSTOM_BUTTON)
Expand Down
32 changes: 31 additions & 1 deletion app/src/main/res/layout/fragment_camera.xml
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,11 @@
android:layout_height="match_parent"
android:layout_margin="4dp"
android:visibility="@{viewModel.uiState.connected ? View.VISIBLE : View.GONE}"
>
tools:layout_editor_absoluteX="4dp"
tools:layout_editor_absoluteY="4dp">

<Button
android:id="@+id/button11"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
Expand Down Expand Up @@ -97,6 +99,34 @@
android:maxLines="1"
/>

<TextView
android:id="@+id/status_battery"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:maxLines="1"
android:text="@{viewModel.uiState.cameraState.batteryStatus == null ? @string/settings_camera_unknown_name : viewModel.uiState.cameraState.batteryStatus.description}"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:visibility="@{viewModel.uiState.cameraState.batteryStatus == null ? View.INVISIBLE : View.VISIBLE}"
app:layout_constraintEnd_toStartOf="@+id/status_media"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/status_name"
app:layout_constraintTop_toTopOf="parent" />

<TextView
android:id="@+id/status_media"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:maxLines="1"
android:text="@{viewModel.uiState.cameraState.mediaStatus == null ? @string/settings_camera_unknown_name : viewModel.uiState.cameraState.mediaStatus.description}"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:visibility="@{viewModel.uiState.cameraState.mediaStatus == null ? View.INVISIBLE : View.VISIBLE}"
app:layout_constraintEnd_toStartOf="@+id/button11"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/status_battery"
app:layout_constraintTop_toTopOf="parent" />

<ImageView
android:id="@+id/status_focus"
android:layout_width="@dimen/activity_status_size"
Expand Down