Merge remote-tracking branch 'upstream/master' into settings

pull/233/head
Vadim Furman 2021-02-05 21:30:40 -08:00
commit 26d7ff9578
13 zmienionych plików z 137 dodań i 62 usunięć

Wyświetl plik

@ -3,6 +3,7 @@
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$/app/src/main/proto" vcs="Git" />
<mapping directory="$PROJECT_DIR$/design" vcs="Git" />
<mapping directory="$PROJECT_DIR$/geeksville-androidlib" vcs="Git" />
<mapping directory="$PROJECT_DIR$/mesh_shared/src/main/proto" vcs="Git" />
</component>

Wyświetl plik

@ -31,8 +31,8 @@ android {
applicationId "com.geeksville.mesh"
minSdkVersion 21 // The oldest emulator image I have tried is 22 (though 21 probably works)
targetSdkVersion 29
versionCode 20139 // format is Mmmss (where M is 1+the numeric major number
versionName "1.1.39"
versionCode 20142 // format is Mmmss (where M is 1+the numeric major number
versionName "1.1.42"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
// per https://developer.android.com/studio/write/vector-asset-studio

Wyświetl plik

@ -73,7 +73,9 @@ class MeshService : Service(), Logging {
class IdNotFoundException(id: String) : Exception("ID not found $id")
class NodeNumNotFoundException(id: Int) : Exception("NodeNum not found $id")
class IsUpdatingException() : Exception("Operation prohibited during firmware update")
/** We treat software update as similar to loss of comms to the regular bluetooth service (so things like sendPosition for background GPS ignores the problem */
class IsUpdatingException() : RadioNotConnectedException("Operation prohibited during firmware update")
/**
* Talk to our running service and try to set a new device address. And then immediately
@ -342,6 +344,9 @@ class MeshService : Service(), Logging {
radio.close()
saveSettings()
stopForeground(true) // Make sure we aren't using the notification first
serviceNotifications.close()
super.onDestroy()
serviceJob.cancel()
}
@ -1085,6 +1090,9 @@ class MeshService : Service(), Logging {
setFirmwareUpdateFilename(myInfo)
val a = RadioInterfaceService.getBondedDeviceAddress(this)
val isBluetoothInterface = a != null && a.startsWith("x")
val mi = with(myInfo) {
MyNodeInfo(
myNodeNum,
@ -1093,7 +1101,7 @@ class MeshService : Service(), Logging {
hwModel,
firmwareVersion,
firmwareUpdateFilename != null,
SoftwareUpdateService.shouldUpdate(
isBluetoothInterface && SoftwareUpdateService.shouldUpdate(
this@MeshService,
DeviceVersion(firmwareVersion)
),

Wyświetl plik

@ -6,27 +6,40 @@ import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Color
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.DrawableCompat
import com.geeksville.mesh.DataPacket
import com.geeksville.mesh.MainActivity
import com.geeksville.mesh.R
import com.geeksville.mesh.android.notificationManager
import com.geeksville.mesh.utf8
import java.io.Closeable
class MeshServiceNotifications(
private val context: Context
) {
) : Closeable
{
private val notificationManager: NotificationManager get() = context.notificationManager
val notifyId = 101
private var largeIcon: Bitmap? = null
@RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel(): String {
val channelId = "my_service"
val channelName = context.getString(R.string.meshtastic_service_notifications)
val channel = NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH).apply {
val channel = NotificationChannel(
channelId,
channelName,
NotificationManager.IMPORTANCE_HIGH
).apply {
lightColor = Color.BLUE
importance = NotificationManager.IMPORTANCE_NONE
lockscreenVisibility = Notification.VISIBILITY_PRIVATE
@ -61,6 +74,25 @@ class MeshServiceNotifications(
PendingIntent.getActivity(context, 0, Intent(context, MainActivity::class.java), 0)
}
/**
* Generate a bitmap from a vector drawable (even on old builds)
* https://stackoverflow.com/questions/33696488/getting-bitmap-from-vector-drawable
*/
fun getBitmapFromVectorDrawable(drawableId: Int): Bitmap {
var drawable = ContextCompat.getDrawable(context, drawableId)!!
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
drawable = DrawableCompat.wrap(drawable).mutate()
}
val bitmap = Bitmap.createBitmap(
drawable.intrinsicWidth,
drawable.intrinsicHeight, Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
return bitmap
}
/**
* Generate a new version of our notification - reflecting current app state
*/
@ -69,11 +101,16 @@ class MeshServiceNotifications(
summaryString: String,
senderName: String
): Notification {
// We delay making this bitmap until we know we need it
if(largeIcon == null)
largeIcon = getBitmapFromVectorDrawable(R.mipmap.ic_launcher2)
val category = if (recentReceivedText != null) Notification.CATEGORY_SERVICE else Notification.CATEGORY_MESSAGE
val builder = NotificationCompat.Builder(context, channelId).setOngoing(true)
.setPriority(NotificationCompat.PRIORITY_MIN)
.setCategory(category)
.setSmallIcon(R.drawable.app_icon)
.setSmallIcon(if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) R.drawable.app_icon_novect else R.drawable.app_icon) // vector form icons don't work reliably on older androids
.setLargeIcon(largeIcon) // we must include a large icon because of a bug in cyanogenmod https://github.com/open-keychain/open-keychain/issues/1356#issue-89493995
.setContentTitle(summaryString) // leave this off for now so our notification looks smaller
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setContentIntent(openAppIntent)
@ -93,4 +130,9 @@ class MeshServiceNotifications(
return builder.build()
}
override fun close() {
largeIcon?.recycle()
largeIcon = null
}
}

Wyświetl plik

@ -22,7 +22,7 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
class RadioNotConnectedException(message: String = "Not connected to radio") :
open class RadioNotConnectedException(message: String = "Not connected to radio") :
BLEException(message)

Wyświetl plik

@ -136,7 +136,7 @@ class MapFragment : ScreenFragment("Map"), Logging {
* Mapbox native code can crash painfully if you ever call a mapbox view function while the view is not actively being show
*/
private val isViewVisible: Boolean
get() = view != null && isResumed
get() = view != null && isResumed && (mapView?.isDestroyed != false)
override fun onViewCreated(viewIn: View, savedInstanceState: Bundle?) {
super.onViewCreated(viewIn, savedInstanceState)
@ -145,9 +145,9 @@ class MapFragment : ScreenFragment("Map"), Logging {
if ((requireContext().applicationContext as GeeksvilleApplication).isAnalyticsAllowed) {
val vIn = viewIn.findViewById<MapView>(R.id.mapView)
mapView = vIn
vIn.onCreate(savedInstanceState)
mapView?.let { v ->
v.onCreate(savedInstanceState)
// Each time the pane is shown start fetching new map info (we do this here instead of
// onCreate because getMapAsync can die in native code if the view goes away)
v.getMapAsync { map ->
@ -205,14 +205,25 @@ class MapFragment : ScreenFragment("Map"), Logging {
}
override fun onDestroyView() {
mapView?.onDestroy()
super.onDestroyView()
mapView?.onDestroy()
}
override fun onSaveInstanceState(outState: Bundle) {
mapView?.onSaveInstanceState(outState)
mapView?.let {
if (!it.isDestroyed)
it.onSaveInstanceState(outState)
}
super.onSaveInstanceState(outState)
}
override fun onLowMemory() {
mapView?.let {
if (!it.isDestroyed)
it.onLowMemory()
}
super.onLowMemory()
}
}

Wyświetl plik

@ -698,6 +698,56 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
binding.scanProgressBar.visibility = visible
binding.deviceRadioGroup.visibility = visible
}
private fun updateDevicesButtons( devices: MutableMap<String, BTScanModel.DeviceListEntry>?) {
// Remove the old radio buttons and repopulate
binding.deviceRadioGroup.removeAllViews()
if(devices == null) return
val adapter = scanModel.bluetoothAdapter
var hasShownOurDevice = false
devices.values.forEach { device ->
if (device.address == scanModel.selectedNotNull)
hasShownOurDevice = true
addDeviceButton(device, true)
}
// The selected device is not in the scan; it is either offline, or it doesn't advertise
// itself (most BLE devices don't advertise when connected).
// Show it in the list, greyed out based on connection status.
if (!hasShownOurDevice) {
// Note: we pull this into a tempvar, because otherwise some other thread can change selectedAddress after our null check
// and before use
val bleAddr = scanModel.selectedBluetooth
if (bleAddr != null && adapter != null && adapter.isEnabled) {
val bDevice =
adapter.getRemoteDevice(bleAddr)
if (bDevice.name != null) { // ignore nodes that node have a name, that means we've lost them since they appeared
val curDevice = BTScanModel.DeviceListEntry(
bDevice.name,
scanModel.selectedAddress!!,
bDevice.bondState == BOND_BONDED
)
addDeviceButton(curDevice, model.isConnected.value == MeshService.ConnectionState.CONNECTED)
}
} else if (scanModel.selectedUSB != null) {
// Must be a USB device, show a placeholder disabled entry
val curDevice = BTScanModel.DeviceListEntry(
scanModel.selectedUSB!!,
scanModel.selectedAddress!!,
false
)
addDeviceButton(curDevice, false)
}
}
val hasBonded =
RadioInterfaceService.getBondedDeviceAddress(requireContext()) != null
// get rid of the warning text once at least one device is paired
binding.warningNotPaired.visibility = if (hasBonded) View.GONE else View.VISIBLE
}
/// Setup the GUI to do a classic (pre SDK 26 BLE scan)
private fun initClassicScan() {
@ -719,54 +769,13 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
}
})
scanModel.devices.observe(viewLifecycleOwner, Observer { devices ->
// Remove the old radio buttons and repopulate
binding.deviceRadioGroup.removeAllViews()
scanModel.devices.observe(
viewLifecycleOwner,
Observer { devices -> updateDevicesButtons(devices) })
val adapter = scanModel.bluetoothAdapter
var hasShownOurDevice = false
devices.values.forEach { device ->
if (device.address == scanModel.selectedNotNull)
hasShownOurDevice = true
addDeviceButton(device, true)
}
// The device the user is already paired with is offline currently, still show it
// it in the list, but greyed out
if (!hasShownOurDevice) {
// Note: we pull this into a tempvar, because otherwise some other thread can change selectedAddress after our null check
// and before use
val bleAddr = scanModel.selectedBluetooth
if (bleAddr != null && adapter != null && adapter.isEnabled) {
val bDevice =
adapter.getRemoteDevice(bleAddr)
if (bDevice.name != null) { // ignore nodes that node have a name, that means we've lost them since they appeared
val curDevice = BTScanModel.DeviceListEntry(
bDevice.name,
scanModel.selectedAddress!!,
bDevice.bondState == BOND_BONDED
)
addDeviceButton(curDevice, false)
}
} else if (scanModel.selectedUSB != null) {
// Must be a USB device, show a placeholder disabled entry
val curDevice = BTScanModel.DeviceListEntry(
scanModel.selectedUSB!!,
scanModel.selectedAddress!!,
false
)
addDeviceButton(curDevice, false)
}
}
val hasBonded =
RadioInterfaceService.getBondedDeviceAddress(requireContext()) != null
// get rid of the warning text once at least one device is paired
binding.warningNotPaired.visibility = if (hasBonded) View.GONE else View.VISIBLE
})
model.isConnected.observe(
viewLifecycleOwner,
{ updateDevicesButtons(scanModel.devices.value) })
}
/// Start running the modern scan, once it has one result we enable the

Wyświetl plik

@ -0,0 +1 @@
app_icon.png

Wyświetl plik

@ -0,0 +1 @@
app_icon.png

Wyświetl plik

@ -0,0 +1 @@
app_icon.png

Wyświetl plik

@ -0,0 +1 @@
app_icon.png

Wyświetl plik

@ -1,7 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.4.21'
ext.kotlin_version = '1.4.30'
ext.coroutines_version = "1.3.9"
repositories {

@ -1 +1 @@
Subproject commit f3812d8484c571f62c72d1509a1e02357fda5b8e
Subproject commit d7c3fa8ab6a47169e5dc8761d03d24588c3dd845