Skip to content

Commit

Permalink
add android connetivity feature
Browse files Browse the repository at this point in the history
  • Loading branch information
alexandre_poichet committed Jan 23, 2025
1 parent 5d5c7cc commit 8a873ed
Show file tree
Hide file tree
Showing 30 changed files with 1,233 additions and 551 deletions.
2 changes: 1 addition & 1 deletion .fvmrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"flutter": "3.19.3",
"flutter": "3.24.5",
"flavors": {}
}
6 changes: 5 additions & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ jobs:

steps:
- uses: actions/checkout@v2
- uses: hrishikesh-kadam/setup-lcov@v1

# Note: This workflow uses the latest stable version of the Dart SDK.
# You can specify other versions if desired, see documentation here:
Expand All @@ -39,7 +40,10 @@ jobs:
working-directory: lib

- name: Run tests
run: flutter test --coverage --update-goldens
run: flutter test --coverage

- name: Exclude coverage
run: lcov --remove coverage/lcov.info 'lib/flutter_eco_mode_platform_interface.dart' 'lib/messages.g.dart' 'lib/streams/*' -o coverage/lcov.info

- name: Upload coverage reports to Codecov
uses: codecov/[email protected]
Expand Down
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,9 @@ migrate_working_dir/
**/doc/api/
.dart_tool/
build/
.fvm

# FVM Version Cache
.fvm/

# Coverage
coverage/
39 changes: 39 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ also to offer a less energy-consuming app.
| Battery Level | Yes | Yes | X | X |
| Battery In Low Power Mode | Yes | Yes | X | X |
| <span style="color: #3CB371">**Battery Eco Mode**</span> | <span style="color: #3CB371">Yes</span> | <span style="color: #3CB371">Yes</span> | <span style="color: #3CB371">X</span> | <span style="color: #3CB371">X</span> |
| <span style="color: #007FFF">**Connectivity**</span> | <span style="color: #007FFF">Yes</span> | <span style="color: #007FFF">No</span> | <span style="color: #007FFF">X</span> | <span style="color: #007FFF">X</span> |

## Eco Mode

Expand Down Expand Up @@ -82,6 +83,44 @@ It will return a boolean.
]).map((event) => event.every((element) => element)).asBroadcastStream();
```

## Connectivity

### /!\ Only available for Android at the moment

This feature can help you to observe the network, know if the device is connected to the internet,
or just want to adapt your app to the network state.

We have created a class **_Connectivity_** which contains basic information about the network.

And you can use directly the methode **_hasEnoughNetwork_** which follows these rules in the code

```
extension on Connectivity {
bool? get isEnough => type == ConnectivityType.unknown
? null
: (_isMobileEnoughNetwork || _isWifiEnoughNetwork || type == ConnectivityType.ethernet);
bool get _isMobileEnoughNetwork =>
[ConnectivityType.mobile5g, ConnectivityType.mobile4g, ConnectivityType.mobile3g].contains(type);
bool get _isWifiEnoughNetwork =>
ConnectivityType.wifi == type && wifiSignalStrength != null ? wifiSignalStrength! >= minWifiSignalStrength : false;
}
```

### How does it work ?

* First, we retrieve the type of network via native access.
* Then, if we have Wifi identified we catch the signal strength.
* And finally, we build and return the object Connectivity.

At this moment, you can ask your self. Is it really reliable ? Is there a better way ?

Probably the better thing to do is to make your own speed test in your app.
You're right, it's more precise, and you can directly define what is a good network fo your purposes.
But you need to ping a server, and it's not really eco-friendly.
Here we just use the native access, trust directly your device and OS.

## Example

See the `example` directory for a complete sample app using flutter_eco_mode.
Expand Down
5 changes: 5 additions & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ version '1.0-SNAPSHOT'

buildscript {
ext.kotlin_version = '1.7.10'

repositories {
google()
mavenCentral()
Expand All @@ -12,9 +13,11 @@ buildscript {
classpath 'com.android.tools.build:gradle:8.3.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}

}

allprojects {

repositories {
google()
mavenCentral()
Expand All @@ -25,6 +28,7 @@ apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'

android {

if (project.android.hasProperty("namespace")) {
namespace 'sncf.connect.tech.flutter_eco_mode'
}
Expand Down Expand Up @@ -56,6 +60,7 @@ android {
testImplementation 'org.powermock:powermock-module-junit4-rule-agent:1.6.2'
testImplementation 'org.powermock:powermock-module-junit4-rule:1.6.2'
testImplementation 'org.powermock:powermock-module-junit4:1.6.2'
implementation('com.google.code.gson:gson:2.10.1')
}

tasks.withType(Test).configureEach {
Expand Down
4 changes: 4 additions & 0 deletions android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="sncf.connect.tech.flutter_eco_mode">

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_BASIC_PHONE_STATE" />
</manifest>
132 changes: 132 additions & 0 deletions android/src/main/kotlin/listener/BatteryListener.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package listener

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.Intent.ACTION_BATTERY_CHANGED
import android.content.IntentFilter
import android.os.BatteryManager
import android.os.BatteryManager.BATTERY_STATUS_CHARGING
import android.os.BatteryManager.BATTERY_STATUS_DISCHARGING
import android.os.BatteryManager.BATTERY_STATUS_FULL
import android.os.BatteryManager.BATTERY_STATUS_NOT_CHARGING
import android.os.Build
import android.os.PowerManager
import androidx.annotation.RequiresApi
import io.flutter.plugin.common.EventChannel
import sncf.connect.tech.flutter_eco_mode.BatteryState.CHARGING
import sncf.connect.tech.flutter_eco_mode.BatteryState.DISCHARGING
import sncf.connect.tech.flutter_eco_mode.BatteryState.FULL
import sncf.connect.tech.flutter_eco_mode.BatteryState.UNKNOWN

class PowerModeStreamHandler(private val context: Context) : EventChannel.StreamHandler {

private var lowPowerModeEventSink: EventChannel.EventSink? = null
private var powerSavingReceiver: BroadcastReceiver? = null

override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
lowPowerModeEventSink = events

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setupPowerSavingReceiver()
}
}

override fun onCancel(p0: Any?) {
context.unregisterReceiver(powerSavingReceiver)
}

@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private fun setupPowerSavingReceiver() {
powerSavingReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent?) {
val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
lowPowerModeEventSink?.success(powerManager.isPowerSaveMode)
}
}
val filter = IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)
context.registerReceiver(powerSavingReceiver, filter)
}

}

class BatteryStateStreamHandler(private val context: Context) : EventChannel.StreamHandler {

private var batteryStateEventSink: EventChannel.EventSink? = null
private var batteryStateReceiver: BroadcastReceiver? = null

override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
batteryStateEventSink = events

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
setupBatteryStateReceiver()
}
}

override fun onCancel(p0: Any?) {
context.unregisterReceiver(batteryStateReceiver)
}

@RequiresApi(Build.VERSION_CODES.M)
private fun setupBatteryStateReceiver() {
batteryStateReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent?) {
val event = when (intent?.action) {
ACTION_BATTERY_CHANGED ->
when (intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1)) {
BATTERY_STATUS_CHARGING -> CHARGING.name
BATTERY_STATUS_FULL -> FULL.name
BATTERY_STATUS_DISCHARGING, BATTERY_STATUS_NOT_CHARGING -> DISCHARGING.name
else -> UNKNOWN.name
}

else -> DISCHARGING.name
}
batteryStateEventSink?.success(event)
}
}
val filterBatteryState = IntentFilter()
filterBatteryState.addAction(ACTION_BATTERY_CHANGED)
context.registerReceiver(batteryStateReceiver, filterBatteryState)
}

}

class BatteryLevelStreamHandler(private val context: Context) : EventChannel.StreamHandler {

private var batteryLevelEventSink: EventChannel.EventSink? = null
private var batteryLevelReceiver: BroadcastReceiver? = null

override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
batteryLevelEventSink = events

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setupBatteryLevelReceiver()
}
}

override fun onCancel(p0: Any?) {
context.unregisterReceiver(batteryLevelReceiver)
}


@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private fun setupBatteryLevelReceiver() {

batteryLevelReceiver = object : BroadcastReceiver() {

override fun onReceive(context: Context, intent: Intent?) {
val batteryPct = intent?.let { i ->
val level: Int = i.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
val scale: Int = i.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
level * 100 / scale.toFloat()
}
batteryLevelEventSink?.success(batteryPct?.toDouble())
}
}
val filter = IntentFilter(ACTION_BATTERY_CHANGED)
context.registerReceiver(batteryLevelReceiver, filter)

}

}
Loading

0 comments on commit 8a873ed

Please sign in to comment.