From adb7170ccbedeb7d21b40b2984c0cd186fe24526 Mon Sep 17 00:00:00 2001 From: SugyoIn-LBSTech Date: Fri, 29 May 2020 12:28:18 +0900 Subject: [PATCH 1/5] =?UTF-8?q?[Update]=20Flutter=20side=20code=EB=A5=BC?= =?UTF-8?q?=20EventChannel=20=EC=82=AC=EC=9A=A9=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flutter side code를 기존 MethodChannel을 사용한 data streaming 방식에서 EventChannel로 변경. --- lib/lbs_pedometer.dart | 3 +- lib/src/controller.dart | 76 ++++++++++++++++++----------------------- 2 files changed, 36 insertions(+), 43 deletions(-) diff --git a/lib/lbs_pedometer.dart b/lib/lbs_pedometer.dart index 0297e35..d2e4643 100644 --- a/lib/lbs_pedometer.dart +++ b/lib/lbs_pedometer.dart @@ -13,4 +13,5 @@ part 'src/coordinate.dart'; part 'src/authorization_state.dart'; part 'src/step_data.dart'; -const String _CHANNEL_NAME = 'lbstech.net.plugin/lbs_pedoemter'; \ No newline at end of file +const String _METHOD_CHANNEL_NAME = 'lbstech.net.plugin/lbs_pedoemter/method'; +const String _EVENT_CHANNEL_NAME = 'lbstech.net.plugin/lbs_pedoemter/event'; \ No newline at end of file diff --git a/lib/src/controller.dart b/lib/src/controller.dart index 3cae449..0006f7f 100644 --- a/lib/src/controller.dart +++ b/lib/src/controller.dart @@ -1,22 +1,25 @@ part of lbs_pedometer; -class _PedometerController{ - final MethodChannel _channel = MethodChannel(_CHANNEL_NAME); +class _PedometerController { + final MethodChannel _channel = MethodChannel(_METHOD_CHANNEL_NAME); + final EventChannel _eventChannel = EventChannel(_EVENT_CHANNEL_NAME); final Pedometer pedometer; String _androidNotificationTitle = 'GPS 위치정보 추적중'; String _androidNotificationContent = '백그라운드에서 현재 위치를 지속적으로 추적하고 있습니다'; + StreamSubscription _pedometerStream; - _PedometerController(this.pedometer){ - this._channel.setMethodCallHandler((call){ - - if(call.method == _MTD_TAKE_STEPS){ - if (pedometer._onTakeStep != null){ - pedometer._onTakeStep(StepData.fromMap(call.arguments)); - } - }else if(call.method == _MTD_ANDROID_RESUME){ - if (pedometer._onAndroidResumed != null){ - pedometer._onAndroidResumed(_convertToStepData(call.arguments)); + _PedometerController(this.pedometer) { + this._channel.setMethodCallHandler((call) { + if (call.method == _MTD_ANDROID_RESUME) { + pedometer._onAndroidResumed?.call(_convertToStepData(call.arguments)); + } else if (call.method == "onCancel") { + List list = call.arguments; + if (list != null && list.isNotEmpty && pedometer._onEnd != null) { + pedometer._onEnd(_convertToStepData(list)); + } else { + print('종료 이후 이전 기록 가져오기의 결과가 비어 있습니다.'); + return null; } } @@ -24,57 +27,47 @@ class _PedometerController{ }); } - Future start() async { - if (Platform.isAndroid){ - Map arg = { - "title" : _androidNotificationTitle, - "content" : _androidNotificationContent - }; - await _channel.invokeMethod(_MTD_START, arg); - } else { - await _channel.invokeMethod(_MTD_START); - } + void start({bool storeHistory, int resultStepsUnit, int storeStepsUnit}) { + _pedometerStream = _eventChannel.receiveBroadcastStream({ + 'title': _androidNotificationTitle, + 'content': _androidNotificationContent, + }).listen((stepData) => pedometer._onTakeStep(StepData.fromMap(stepData))); } - Future stop() async{ - List list = await _channel.invokeMethod(_MTD_STOP); - if (list != null && list.isNotEmpty && pedometer._onEnd != null){ - pedometer._onEnd(_convertToStepData(list)); - }else{ - print('종료 이후 이전 기록 가져오기의 결과가 비어 있습니다.'); - return null; - } - } + void stop() => _pedometerStream?.cancel(); - Future requestPermission() async{ + Future requestPermission() async { await _channel.invokeMethod(_MTD_REQUEST_PERMISSION); } - Future getLocation() async{ + Future getLocation() async { Map arg = await _channel.invokeMethod(_MTD_GET_LOCATION); - if (arg == null){ + if (arg == null) { AssertionError('argument from nvative method call[getLocation] is null.'); return null; } return Coordinate(latitude: arg['lat'], longitude: arg['lng']); } - Future getAuthorizationState() async{ + Future getAuthorizationState() async { int state = await _channel.invokeMethod(_MTD_GET_STATE); return AuthorizationState.values[state]; } - void setAndroidNotification ({@required String title, @required String content}){ + void setAndroidNotification({ + @required String title, + @required String content, + }) { _androidNotificationTitle = title; _androidNotificationContent = content; return; } - Future> getHistory() async{ + Future> getHistory() async { List list = await _channel.invokeMethod(_MTD_GET_HISTORY); - if (list != null && list.isNotEmpty){ + if (list != null && list.isNotEmpty) { return _convertToStepData(list); - }else{ + } else { print('이전 기록 가져오기의 결과가 비어 있습니다.'); return null; } @@ -82,10 +75,9 @@ class _PedometerController{ List _convertToStepData(List list) { List result = List(); - for(Map row in list){ + for (Map row in list) { result.add(StepData.fromMap(row)); } return result; } - -} \ No newline at end of file +} From fba4640782e6038a4669d593c9d60e48b0d967ba Mon Sep 17 00:00:00 2001 From: SugyoIn-LBSTech Date: Fri, 29 May 2020 12:56:11 +0900 Subject: [PATCH 2/5] =?UTF-8?q?[Update]=20=EC=B6=94=EA=B0=80=20=EC=98=B5?= =?UTF-8?q?=EC=85=98=20=EA=B5=AC=ED=98=84.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 디바이스 데이터 베이스에 저장할지 여부를 선택하는 storeHistory 옵션 추가. - 디바이스 데이터 베이스에 저장하는 데이터의 걸음 주기를 정하는 resultStepsUnit 옵션 추가. - Flutter로 보내는 데이터의 걸음 주기를 정하는 resultStepsUnit 옵션 추가. --- lib/src/controller.dart | 3 ++ lib/src/method_name.dart | 6 --- lib/src/pedometer.dart | 99 ++++++++++++++++++++++++++-------------- 3 files changed, 67 insertions(+), 41 deletions(-) diff --git a/lib/src/controller.dart b/lib/src/controller.dart index 0006f7f..cf4a31c 100644 --- a/lib/src/controller.dart +++ b/lib/src/controller.dart @@ -31,6 +31,9 @@ class _PedometerController { _pedometerStream = _eventChannel.receiveBroadcastStream({ 'title': _androidNotificationTitle, 'content': _androidNotificationContent, + 'storeHistory': storeHistory ?? true, + 'resultStepsUnit': resultStepsUnit ?? storeStepsUnit ?? 10, + 'storeStepsUnit': storeStepsUnit ?? resultStepsUnit ?? 10, }).listen((stepData) => pedometer._onTakeStep(StepData.fromMap(stepData))); } diff --git a/lib/src/method_name.dart b/lib/src/method_name.dart index 656c0c6..a788679 100644 --- a/lib/src/method_name.dart +++ b/lib/src/method_name.dart @@ -2,12 +2,6 @@ part of lbs_pedometer; const String _MTD_REQUEST_PERMISSION = 'requestPermission'; -const String _MTD_START = 'start'; - -const String _MTD_STOP = 'stop'; - -const String _MTD_TAKE_STEPS = 'takeSteps'; - const String _MTD_GET_HISTORY = 'getHistory'; const String _MTD_ANDROID_RESUME = 'androidResume'; diff --git a/lib/src/pedometer.dart b/lib/src/pedometer.dart index c1d8108..c58791e 100644 --- a/lib/src/pedometer.dart +++ b/lib/src/pedometer.dart @@ -1,12 +1,12 @@ part of lbs_pedometer; -class Pedometer{ +class Pedometer { static final Pedometer _instance = Pedometer._internal(); static _PedometerController _controller; Pedometer._internal(); - factory Pedometer(){ + factory Pedometer() { if (_controller == null) _controller = _PedometerController(_instance); return _instance; } @@ -24,68 +24,97 @@ class Pedometer{ /// [StepData]의 List 들로 반환한다. OnEnd _onEnd; - /// ios의 pedometer 의 이벤트를 받기 시작한다. - /// 이때 CLLocationManager 를 이용한 위치 변경 이벤트도 같이 받는다. + /// 현제 위치를 가져오는 일회성 함수이다. + /// /// + /// @return type : [Coordinate] + Future get location => _controller.getLocation(); + + /// ios 의 현제 권한 상태를 가져온다. /// - /// OnTakeStep call back 으로 step의 수와 timeStemp, - /// 그리고 [Coordinate] 객체를 전달한다. /// - /// onAndroidStarted 함수는 안드로이드가 종료된 이후 다시 시작했을때 - /// 이전까지 기록된 센서 값을 반환한다. - Future start({OnTakeStep onTakeStep, OnAndroidResumed onAndroidResumed}) { + /// @return type : [AuthorizationState] + Future get authorizationState => + _controller.getAuthorizationState(); + + /// 백그라운드에서 SQLite 데이터가 기록되고 있는 중인경우 + /// 해당 함수를 호출하는 시점 이전의 모든 기록을 가져오는 함수. + Future> get history { + if (Platform.isIOS) return null; + return _controller.getHistory(); + } + + /// 걸음과 위치에 대한 정보를 받기 시작한다. + /// + /// [onTakeStep]에서 데이터에 대한 콜백 처리를한다. + /// [storeHistory]를 true로 주게되면 디바이스 데이터 베이스에 데이터를 기록하며 기본값은 `true`이다. + /// 안드로이드 한정으로 [onAndroidResumed]에서 onResume의 콜백 처리를 한다. + /// 마찬가지로 안드로이드 한정으로 [resultStepsUnit]과 [storeStepsUnit]는 각각 Flutter로 데이터를 보내는 걸음 단위와 디바이스 데이터 베이스에 저장하는 걸음 단위를 의미한다. + /// 예를 들어 [resultStepsUnit]이 3이고 [storeStepsUnit]가 10이면 매 3걸음 마다 [onTakeStep]이 불리며 10걸음마다 디바이스 데이터 베이스에 데이터를 저장한다. + /// [resultStepsUnit]와 [storeStepsUnit] 둘 중 하나의 값 많이 정의 되더라도 다른 값 또한 같은 값으로 정의된며 기본값은 둘 다 10이다. + void start({ + OnTakeStep onTakeStep, + OnAndroidResumed onAndroidResumed, + bool storeHistory = true, + int resultStepsUnit = 10, + int storeStepsUnit = 10, + }) { _onTakeStep = onTakeStep; _onAndroidResumed = onAndroidResumed; - _controller.start(); + _controller.start( + storeHistory: storeHistory, + resultStepsUnit: resultStepsUnit, + storeStepsUnit: storeStepsUnit, + ); return null; - } + } /// 현제 pedometer의 이벤트를 받고 있을 경우 해당 이벤트 스트림을 취소한다. /// /// onAndroidStop 의 경우 안드로이드에서만 호출되는 함수이며, 최종 기록들을 모두 불러온다. - Future stop({OnEnd onEnd}){ + Future stop({OnEnd onEnd}) { _onEnd = onEnd; _onTakeStep = null; _controller.stop(); return null; } - /// permission 을 요청하는 함수 - Future requestPermission() async { - return _controller.requestPermission(); + /// android 의 notification 의 title 과 content 를 설정한다. + /// + /// title default value : 'GPS 위치정보 추적중' + /// + /// content default value : '백그라운드에서 현재 위치를 지속적으로 추적하고 있습니다' + void setAndroidNotification({ + @required String title, + @required String content, + }) { + if (Platform.isIOS) return; + return _controller.setAndroidNotification(title: title, content: content); } + /// permission 을 요청하는 함수 + Future requestPermission() async => _controller.requestPermission(); + /// 현제 위치를 가져오는 일회성 함수이다. /// /// /// @return type : [Coordinate] - Future getLocation(){ - return _controller.getLocation(); - } + @Deprecated('Effective Dart문서에 따라 getter를 쓰기로합니다. 2.x.x 이후 사라집니다.') + Future getLocation() => _controller.getLocation(); /// ios 의 현제 권한 상태를 가져온다. /// /// /// @return type : [AuthorizationState] - Future getAuthorizationState() async{ - return _controller.getAuthorizationState(); - } - - /// android 의 notification 의 title 과 content 를 설정한다. - /// - /// title default value : 'GPS 위치정보 추적중' - /// - /// content default value : '백그라운드에서 현재 위치를 지속적으로 추적하고 있습니다' - void setAndroidNotification({@required String title, @required String content}){ - if (Platform.isIOS) { return; } - return _controller.setAndroidNotification(title: title, content: content); - } + @Deprecated('Effective Dart문서에 따라 getter를 쓰기로합니다. 2.x.x 이후 사라집니다.') + Future getAuthorizationState() async => + _controller.getAuthorizationState(); /// 백그라운드에서 SQLite 데이터가 기록되고 있는 중인경우 /// 해당 함수를 호출하는 시점 이전의 모든 기록을 가져오는 함수. - Future> getHistory() async{ - if (Platform.isIOS) { return null; } + @Deprecated('Effective Dart문서에 따라 getter를 쓰기로합니다. 2.x.x 이후 사라집니다.') + Future> getHistory() async { + if (Platform.isIOS) return null; return _controller.getHistory(); } - -} \ No newline at end of file +} From d2efcf8093ba5f435f1b665db6d211abfeae12b9 Mon Sep 17 00:00:00 2001 From: SugyoIn-LBSTech Date: Fri, 29 May 2020 12:56:56 +0900 Subject: [PATCH 3/5] =?UTF-8?q?[Update]=20Android=20=EC=AA=BD=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81=20=EB=B0=8F=20?= =?UTF-8?q?EventChannel=20=EC=82=AC=EC=9A=A9=EC=9C=BC=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD,=20=EC=B6=94=EA=B0=80=20=EB=8D=B0=EC=9D=B4=ED=84=B0?= =?UTF-8?q?=20=ED=95=B8=EB=93=A4=EB=A7=81.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - LbsPedometerPlugin.java의 static 변수들 member 변수로 전환. - Plugin의 성격상 MethodChannel 사용에서 EventChannel 사용으로 변경. - ServiceManager와 ServiceSensor가 사용하던 같은 상수들을 ServiceManager로 병합 및 공유. - Flutter에서 추가된 옵션 처리 부분 추가. --- .../lbs_pedometer/LbsPedometerPlugin.java | 371 ++++++++++-------- .../lbs_pedometer/LocationHandler.java | 7 +- .../lbstech/lbs_pedometer/ServiceManager.java | 78 ++-- .../lbstech/lbs_pedometer/ServiceSensor.java | 78 ++-- 4 files changed, 302 insertions(+), 232 deletions(-) diff --git a/android/src/main/java/plugin/net/lbstech/lbs_pedometer/LbsPedometerPlugin.java b/android/src/main/java/plugin/net/lbstech/lbs_pedometer/LbsPedometerPlugin.java index baa9012..eca0e68 100644 --- a/android/src/main/java/plugin/net/lbstech/lbs_pedometer/LbsPedometerPlugin.java +++ b/android/src/main/java/plugin/net/lbstech/lbs_pedometer/LbsPedometerPlugin.java @@ -9,212 +9,237 @@ import androidx.annotation.NonNull; -import java.nio.channels.Channel; import java.util.ArrayList; import java.util.HashMap; +import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.embedding.engine.plugins.activity.ActivityAware; import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; +import io.flutter.plugin.common.EventChannel; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.MethodChannel.MethodCallHandler; import io.flutter.plugin.common.MethodChannel.Result; import io.flutter.plugin.common.PluginRegistry.Registrar; -/** LbsPedometerPlugin */ +/** + * LbsPedometerPlugin + */ public class LbsPedometerPlugin implements FlutterPlugin, MethodCallHandler, + EventChannel.StreamHandler, Application.ActivityLifecycleCallbacks, ActivityAware { - private static final String CHANNEL_NAME = "lbstech.net.plugin/lbs_pedoemter"; - private static MethodChannel channel; - - private static LocationHandler locationHandler; - private static ServiceManager serviceManager; - - private static Context applicationContext; - private static Activity activity; - private boolean isServiceRunning; - - private static boolean isRegisterLifecycleCallBack = false; - private static boolean isInit = true; - private static AtomicInteger initialActivityHashCode = null; - - @Override - public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { - Log.i("PEDOLOG_PP", "onAttachedToEngine"); - applicationContext = flutterPluginBinding.getApplicationContext(); - LbsPedometerPlugin instance = new LbsPedometerPlugin(); - channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), CHANNEL_NAME); - channel.setMethodCallHandler(this); - } - - public static void registerWith(Registrar registrar) { - LbsPedometerPlugin instance = new LbsPedometerPlugin(); - applicationContext = registrar.context(); - activity = registrar.activity(); - activity.getApplication().registerActivityLifecycleCallbacks(instance); - isRegisterLifecycleCallBack = true; - channel = new MethodChannel(registrar.messenger(), CHANNEL_NAME); - channel.setMethodCallHandler(instance); - - serviceManager = new ServiceManager(applicationContext, activity, instance); - locationHandler = new LocationHandler(applicationContext, activity, instance); - } - - @Override - public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { + private static final String METHOD_CHANNEL_NAME = "lbstech.net.plugin/lbs_pedoemter/method"; + private static final String EVENT_CHANNEL_NAME = "lbstech.net.plugin/lbs_pedoemter/event"; + + private boolean isRegisterLifecycleCallBack; + private boolean isInit; + private boolean isServiceRunning; + private MethodChannel methodChannel; + private EventChannel eventChannel; + private LocationHandler locationHandler; + private ServiceManager serviceManager; + private Context applicationContext; + private AtomicInteger initialActivityHashCode = null; + + public static void registerWith(Registrar registrar) { + new LbsPedometerPlugin(registrar); + } + + public LbsPedometerPlugin() { + isRegisterLifecycleCallBack = false; + isInit = true; + } + + private LbsPedometerPlugin(Registrar registrar) { + this.serviceManager = new ServiceManager(registrar.context()); + this.locationHandler = new LocationHandler(registrar.context(), registrar.activity()); + this.methodChannel = new MethodChannel(registrar.messenger(), METHOD_CHANNEL_NAME); + this.eventChannel = new EventChannel(registrar.messenger(), EVENT_CHANNEL_NAME); + + registrar.activity().getApplication().registerActivityLifecycleCallbacks(this); + isRegisterLifecycleCallBack = true; + isInit = true; + } + + @Override + public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { + Log.i("PEDOLOG_PP", "onAttachedToEngine"); + applicationContext = flutterPluginBinding.getApplicationContext(); + methodChannel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), METHOD_CHANNEL_NAME); + eventChannel = new EventChannel(flutterPluginBinding.getBinaryMessenger(), EVENT_CHANNEL_NAME); + methodChannel.setMethodCallHandler(this); + eventChannel.setStreamHandler(this); + } + + @Override + public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { + switch (call.method) { + case "getLocation": + Log.i("PEDOLOG_PP", "getLocation method called"); + locationHandler.getLocation(result); + break; + case "getState": + Log.i("PEDOLOG_PP", "getState method called"); + result.success(locationHandler.getLocationPermissionState()); + break; + case "requestPermission": + Log.i("PEDOLOG_PP", "requestPermission method called"); + locationHandler.requestPermission(); + serviceManager.requestPermission(); + result.success(null); + break; + case "getHistory": + Log.i("PEDOLOG_PP", "getHistory method called"); + ArrayList> list = SQLController.getInstance(applicationContext).getHistory(); + result.success(list); + break; + default: + throw new NoSuchMethodError("'" + call.method + "' does not exist."); + } + } + + private boolean isServiceRunning(Class serviceClass) { + ActivityManager manager = (ActivityManager) applicationContext.getSystemService(Context.ACTIVITY_SERVICE); + for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { + if (serviceClass.getName().equals(service.service.getClassName())) { + return true; + } + } + return false; + } + + @Override + public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { + } + + + // ================================= ActivityAware ================================= - if (call.method.equals("getLocation")) - { - Log.i("PEDOLOG_PP", "getLocation method called"); - locationHandler.getLocation(result); - } - else if (call.method.equals("getState")) - { - Log.i("PEDOLOG_PP", "getState method called"); - result.success(locationHandler.getLocationPermissionState()); - } - else if (call.method.equals("start")) - { - Log.i("PEDOLOG_PP", "start method called"); - - if (!isServiceRunning(ServiceSensor.class)) { - String title = call.argument("title"); - String content = call.argument("content"); - assert content != null && title != null; - serviceManager.serviceStart(title, content); - } - result.success(null); - } - else if (call.method.equals("stop")) - { - Log.i("PEDOLOG_PP", "stop method called"); - ArrayList> list = serviceManager.serviceStop(); - result.success(list); - } - else if (call.method.equals("requestPermission")) - { - Log.i("PEDOLOG_PP", "requestPermission method called"); - locationHandler.requestPermission(); - serviceManager.requestPermission(); - result.success(null); - } - else if(call.method.equals("getHistory")) - { - Log.i("PEDOLOG_PP", "getHistory method called"); - ArrayList> list = SQLController.getInstance(applicationContext).getHistory(); - result.success(list); - } - - } - - void sendDataToFlutter(HashMap map){ - channel.invokeMethod("takeSteps", map); - } - - private boolean isServiceRunning(Class serviceClass) { - ActivityManager manager = (ActivityManager) applicationContext.getSystemService(Context.ACTIVITY_SERVICE); - for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { - if (serviceClass.getName().equals(service.service.getClassName())) { - return true; - } - } - return false; - } - - @Override - public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { } - - - // ================================= ActivityAware ================================= - - - @Override - public void onAttachedToActivity(ActivityPluginBinding binding) { - Log.i("PEDOLOG_PP", "onAttachToActivity"); - activity = binding.getActivity(); - if (isInit){ - initialActivityHashCode = new AtomicInteger(activity.hashCode()); - isInit = false; - } - if (!isRegisterLifecycleCallBack) { - activity.getApplication().registerActivityLifecycleCallbacks(this); - isRegisterLifecycleCallBack = true; - } - - if(locationHandler == null || serviceManager == null) { - locationHandler = new LocationHandler(applicationContext, activity, this); - serviceManager = new ServiceManager(applicationContext, activity, this); - } - } - @Override - public void onDetachedFromActivityForConfigChanges() { - this.onDetachedFromActivity(); - } + @Override + public void onAttachedToActivity(ActivityPluginBinding binding) { + Log.i("PEDOLOG_PP", "onAttachToActivity"); + Activity activity = binding.getActivity(); - @Override - public void onReattachedToActivityForConfigChanges(ActivityPluginBinding binding) { - binding.getActivity().getApplication().registerActivityLifecycleCallbacks(this); - } + if (isInit) { + initialActivityHashCode = new AtomicInteger(activity.hashCode()); + isInit = false; + } - @Override - public void onDetachedFromActivity() { } + if (!isRegisterLifecycleCallBack) { + activity.getApplication().registerActivityLifecycleCallbacks(this); + isRegisterLifecycleCallBack = true; + } + + if (locationHandler == null || serviceManager == null) { + locationHandler = new LocationHandler(applicationContext, activity); + serviceManager = new ServiceManager(applicationContext); + } + } + + @Override + public void onDetachedFromActivityForConfigChanges() { + this.onDetachedFromActivity(); + } + + @Override + public void onReattachedToActivityForConfigChanges(ActivityPluginBinding binding) { + binding.getActivity().getApplication().registerActivityLifecycleCallbacks(this); + } + + @Override + public void onDetachedFromActivity() { + } - // ================================= life cycle call back ====================================== + // ================================= life cycle call back ====================================== - @Override - public void onActivityCreated(Activity activity, Bundle savedInstanceState) { - // created 이후에 registerActivityLifecycleCallbacks 함수가 불리기 떄문에 - // 실제로는 호출 되지 않음. - Log.i("PEDOLOG_PP", "onCreated"); - channel.setMethodCallHandler(this); - } + @Override + public void onActivityCreated(Activity activity, Bundle savedInstanceState) { + // created 이후에 registerActivityLifecycleCallbacks 함수가 불리기 떄문에 + // 실제로는 호출 되지 않음. + Log.i("PEDOLOG_PP", "onCreated"); + methodChannel.setMethodCallHandler(this); + } - @Override - public void onActivityStarted(Activity activity) { - Log.i("PEDOLOG_PP", "onStarted"); + @Override + public void onActivityStarted(Activity activity) { + Log.i("PEDOLOG_PP", "onStarted"); - if (serviceManager == null) return; - isServiceRunning = isServiceRunning(ServiceSensor.class); - serviceManager.onStarted(isServiceRunning); - } + if (serviceManager == null) return; + isServiceRunning = isServiceRunning(ServiceSensor.class); + serviceManager.onStarted(isServiceRunning); + } - @Override - public void onActivityResumed(Activity activity) { - Log.i("PEDOLOG_PP", "onResumed"); - if(isServiceRunning){ - ArrayList> list = SQLController.getInstance(applicationContext).getHistory(); - channel.invokeMethod("androidResume", list); + @Override + public void onActivityResumed(Activity activity) { + Log.i("PEDOLOG_PP", "onResumed"); + if (isServiceRunning) { + ArrayList> list = SQLController.getInstance(applicationContext).getHistory(); + methodChannel.invokeMethod("androidResume", list); + } } - } - @Override - public void onActivityPaused(Activity activity) { - Log.i("PEDOLOG_PP", "onPaused"); - } + @Override + public void onActivityPaused(Activity activity) { + Log.i("PEDOLOG_PP", "onPaused"); + } - @Override - public void onActivityStopped(Activity activity) { - Log.i("PEDOLOG_PP", "onStopped"); - serviceManager.onStopped(); - } + @Override + public void onActivityStopped(Activity activity) { + Log.i("PEDOLOG_PP", "onStopped"); + serviceManager.onStopped(); + } - @Override - public void onActivitySaveInstanceState(Activity activity, Bundle outState) { } + @Override + public void onActivitySaveInstanceState(Activity activity, Bundle outState) { + } - @Override + @Override + public void onActivityDestroyed(Activity activity) { + Log.i("PEDOLOG_PP", "onDestroy"); + if (initialActivityHashCode != null && initialActivityHashCode.intValue() == activity.hashCode()) { + methodChannel.setMethodCallHandler(null); + activity.getApplication().unregisterActivityLifecycleCallbacks(this); + } + } - public void onActivityDestroyed(Activity activity) { - Log.i("PEDOLOG_PP", "onDestroy"); - if (initialActivityHashCode != null && initialActivityHashCode.intValue() == activity.hashCode()) { - channel.setMethodCallHandler(null); - activity.getApplication().unregisterActivityLifecycleCallbacks(this); + // ================================= StreamHandler ====================================== + + @Override + public void onListen(Object arguments, EventChannel.EventSink events) { + Log.i("PEDOLOG_PP", "Event channel is opened."); + + if (arguments instanceof Map) { + String title = (String) ((Map) arguments).get("title"); + String content = (String) ((Map) arguments).get("content"); + boolean storeHistory = (boolean) ((Map) arguments).get("storeHistory"); + int resultStepsUnit = (int) ((Map) arguments).get("resultStepsUnit"); + int storeStepsUnit = (int) ((Map) arguments).get("storeStepsUnit"); + + if (!isServiceRunning(ServiceSensor.class)) { + assert content != null && title != null; + serviceManager.serviceStart( + title, + content, + storeHistory, + resultStepsUnit, + storeStepsUnit, + events + ); + } + } } - } + @Override + public void onCancel(Object arguments) { + Log.i("PEDOLOG_PP", "Event channel is closed."); + + ArrayList> list = serviceManager.serviceStop(); + methodChannel.invokeMethod("onCancel", list); + } } diff --git a/android/src/main/java/plugin/net/lbstech/lbs_pedometer/LocationHandler.java b/android/src/main/java/plugin/net/lbstech/lbs_pedometer/LocationHandler.java index 8dcab28..0421f49 100644 --- a/android/src/main/java/plugin/net/lbstech/lbs_pedometer/LocationHandler.java +++ b/android/src/main/java/plugin/net/lbstech/lbs_pedometer/LocationHandler.java @@ -20,23 +20,20 @@ import io.flutter.plugin.common.MethodChannel; -public class LocationHandler { +class LocationHandler { private static final int RQ_CODE_PERMISSION = 0xff01; private Context context; private Activity activity; - private LbsPedometerPlugin plugin; private FusedLocationProviderClient fusedLocationProviderClient; private int locationPermissionState = 22312; private boolean isLocationEnable; private boolean isGPSEnable; - LocationHandler(Context context, Activity activity, LbsPedometerPlugin instance) { + LocationHandler(Context context, Activity activity) { this.context = context; this.activity = activity; - this.plugin = instance; -// requestPermission(); LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { diff --git a/android/src/main/java/plugin/net/lbstech/lbs_pedometer/ServiceManager.java b/android/src/main/java/plugin/net/lbstech/lbs_pedometer/ServiceManager.java index 4a1c2ec..1d8137d 100644 --- a/android/src/main/java/plugin/net/lbstech/lbs_pedometer/ServiceManager.java +++ b/android/src/main/java/plugin/net/lbstech/lbs_pedometer/ServiceManager.java @@ -19,33 +19,52 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.Map; + +import io.flutter.plugin.common.EventChannel; public class ServiceManager implements ServiceConnection { - private final int MSG_INIT = 0XAA1; - private final int MSG_SENSOR_TRIGGER = 0XAA2; - private final int MSG_LIFECYCLE_START = 0XAA3; - private final int MSG_LIFECYCLE_STOP = 0XAA4; + static final int MSG_INIT = 0XAA1; + static final int MSG_SENSOR_TRIGGER = 0XAA2; + static final int MSG_LIFECYCLE_START = 0XAA3; + static final int MSG_LIFECYCLE_STOP = 0XAA4; private Context context; - private Activity activity; - private LbsPedometerPlugin plugin; private SQLController sqlController; private Messenger serviceMessenger = null; private Messenger pluginMessenger = new Messenger(new Receiver()); - private boolean isServiceConnected() { return serviceMessenger != null; } + private boolean isServiceConnected() { + return serviceMessenger != null; + } + private boolean isRebind = false; - ServiceManager(Context context, Activity activity, LbsPedometerPlugin plugin) { + private boolean storeHistory; + private int resultStepsUnit; + private int storeStepsUnit; + private EventChannel.EventSink eventSink; + + ServiceManager(Context context) { this.context = context; - this.activity = activity; - this.plugin = plugin; sqlController = SQLController.getInstance(context); } // 서비스 시작 - void serviceStart(@NonNull String notificationTitle, @NonNull String notificationContent) { + void serviceStart( + @NonNull String notificationTitle, + @NonNull String notificationContent, + boolean storeHistory, + int resultStepsUnit, + int storeStepsUnit, + EventChannel.EventSink eventSink + ) { + this.storeHistory = storeHistory; + this.resultStepsUnit = resultStepsUnit; + this.storeStepsUnit = storeStepsUnit; + this.eventSink = eventSink; + Intent serviceIntent = new Intent(context, ServiceSensor.class); serviceIntent.putExtra("title", notificationTitle); serviceIntent.putExtra("text", notificationContent); @@ -55,26 +74,26 @@ void serviceStart(@NonNull String notificationTitle, @NonNull String notificatio } // App 이 start 된 시점에서 서비스 돌아가면 rebind 하기 위해서 분리. - void serviceBind(){ + void serviceBind() { context.bindService(new Intent(context, ServiceSensor.class), this, 0); Log.i("PEDOLOG_SM", "startForegroundSensor _ Service Binding"); } // 서비스 종료 - ArrayList> serviceStop(){ + ArrayList> serviceStop() { if (isServiceConnected()) { ArrayList> result = sqlController.getHistory(); try { context.unbindService(this); Intent serviceIntent = new Intent(context, ServiceSensor.class); context.stopService(serviceIntent); - } catch (Exception e){ + } catch (Exception e) { Log.i("ANDOIRD_SM", "Error occurred :" + e.getLocalizedMessage()); } serviceMessenger = null; isRebind = false; return result; - }else { + } else { return null; } } @@ -82,18 +101,18 @@ ArrayList> serviceStop(){ // =================================== life cycle 관련 ======================================== // 해당 라이프 사이클에 호출되는 메서드들 - void requestPermission(){ + void requestPermission() { // android Q 버전 부터 동적 퍼미션 필요. // Q's API level : 29 - if (Build.VERSION.SDK_INT >= 29){ + if (Build.VERSION.SDK_INT >= 29) { // TODO : API 29 부터 ACCESS_BACKGROUND_LOCATION 동적 퍼미션 체크. } } - void onStarted(boolean isServiceRunning){ + void onStarted(boolean isServiceRunning) { // 만약 서비스가 진짜 돌아가면 rebind. Log.i("PEDOLOG_SM", "onStart. 서비스 존재 여부 : " + isServiceRunning); - if (isServiceRunning){ + if (isServiceRunning) { isRebind = true; serviceBind(); @@ -103,7 +122,7 @@ void onStarted(boolean isServiceRunning){ serviceMessenger.send(msg); isRebind = true; Log.i("PEDOLOG_SM", "service attached"); - }catch (RemoteException e){ + } catch (RemoteException e) { Log.i("PEDOLOG_SM", "try to send, but Service doesn't exist.." + "\nmsg: MSG_INIT"); e.printStackTrace(); @@ -111,12 +130,12 @@ void onStarted(boolean isServiceRunning){ } } - void onStopped(){ - if (isServiceConnected()){ + void onStopped() { + if (isServiceConnected()) { try { Message msg = Message.obtain(null, MSG_LIFECYCLE_STOP); serviceMessenger.send(msg); - }catch (RemoteException e){ + } catch (RemoteException e) { Log.i("PEDOLOG_SM", "try to send, but Service doesn't exist.." + "\nmsg: MSG_LIFECYCLE_STOP"); e.printStackTrace(); @@ -130,13 +149,18 @@ public void onServiceConnected(ComponentName name, IBinder service) { serviceMessenger = new Messenger(service); int what = isRebind ? MSG_LIFECYCLE_START : MSG_INIT; + Map obj = new HashMap(); + obj.put("storeHistory", storeHistory); + obj.put("resultStepsUnit", resultStepsUnit); + obj.put("storeStepsUnit", storeStepsUnit); + try { - Message msg = Message.obtain(null, what); + Message msg = Message.obtain(null, what, obj); msg.replyTo = pluginMessenger; serviceMessenger.send(msg); isRebind = true; Log.i("PEDOLOG_SM", "service attached"); - }catch (RemoteException e){ + } catch (RemoteException e) { Log.i("PEDOLOG_SM", "try to send, but Service doesn't exist.." + "\nmsg: MSG_INIT"); e.printStackTrace(); @@ -158,9 +182,7 @@ public void handleMessage(Message msg) { int data = msg.what; if (data == MSG_SENSOR_TRIGGER) { Log.i("PEDOLOG_SM", "receive trigger."); - plugin.sendDataToFlutter( - sqlController.getCurrentWhereStep(ServiceSensor.currentSavedStepCnt) - ); + eventSink.success(msg.obj); } super.handleMessage(msg); } diff --git a/android/src/main/java/plugin/net/lbstech/lbs_pedometer/ServiceSensor.java b/android/src/main/java/plugin/net/lbstech/lbs_pedometer/ServiceSensor.java index daae701..a8d3670 100644 --- a/android/src/main/java/plugin/net/lbstech/lbs_pedometer/ServiceSensor.java +++ b/android/src/main/java/plugin/net/lbstech/lbs_pedometer/ServiceSensor.java @@ -30,14 +30,12 @@ import com.google.android.gms.location.LocationResult; import com.google.android.gms.location.LocationServices; +import java.util.Calendar; +import java.util.HashMap; import java.util.List; +import java.util.Map; public class ServiceSensor extends Service implements SensorEventListener { - private final int MSG_INIT = 0XAA1; - private final int MSG_SENSOR_TRIGGER = 0XAA2; - private final int MSG_LIFECYCLE_START = 0XAA3; - private final int MSG_LIFECYCLE_STOP = 0XAA4; - private FusedLocationProviderClient fusedLocationProviderClient; private LocationCallback locationCallback; private SensorManager sensorManager; @@ -46,27 +44,42 @@ public class ServiceSensor extends Service implements SensorEventListener { private Location location; private int stepCnt; + + private boolean storeHistory; + private int resultStepsUnit; + private int storeStepsUnit; + static int currentSavedStepCnt = 0; - private boolean isAppAlive() { return appMessenger != null; } + + /// 처음 몇 걸음에 좌표가 나오지 않을경우가 있어 이에 대한 보정 offset. + static final int noLocationStepOffset = 2; + + private boolean isAppAlive() { + return appMessenger != null; + } // ==================================== anonymous class ===================================== // message handler - APP 으로부터의 메세지를 다루는 익명 클래스 @SuppressLint("HandlerLeak") - private Messenger messenger = new Messenger(new Handler(){ + private Messenger messenger = new Messenger(new Handler() { @Override public void handleMessage(Message msg) { int data = msg.what; - switch (data){ - case MSG_INIT: + switch (data) { + case ServiceManager.MSG_INIT: Log.i("PEDOLOG_SS", "received msg: MSG_INIT"); appMessenger = msg.replyTo; + Map args = (Map) msg.obj; + storeHistory = (boolean) args.get("storeHistory"); + resultStepsUnit = (int) args.get("resultStepsUnit"); + storeStepsUnit = (int) args.get("storeStepsUnit"); sensorStart(); break; - case MSG_LIFECYCLE_START: + case ServiceManager.MSG_LIFECYCLE_START: Log.i("PEDOLOG_SS", "received msg: MSG_LIFECYCLE_START"); appMessenger = msg.replyTo; break; - case MSG_LIFECYCLE_STOP: + case ServiceManager.MSG_LIFECYCLE_STOP: Log.i("PEDOLOG_SS", "received msg: MSG_LIFECYCLE_STOP"); appMessenger = null; break; @@ -96,7 +109,7 @@ public IBinder onBind(Intent intent) { @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i("PEDOLOG_SS", "onStartCommand called."); - if(intent != null) { + if (intent != null) { Bundle bundle = intent.getExtras(); String title = "GPS Activating"; String text = "App is receive location update"; @@ -110,13 +123,13 @@ public int onStartCommand(Intent intent, int flags, int startId) { return START_STICKY; } - private Notification buildNotification(String title, String text){ + private Notification buildNotification(String title, String text) { NotificationCompat.Builder builder; String CHANNEL_ID = "lbstech_service_channel"; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT); - ((NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE)) + ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)) .createNotificationChannel(channel); } builder = new NotificationCompat.Builder(this, CHANNEL_ID) @@ -146,7 +159,7 @@ public boolean onUnbind(Intent intent) { // ================================== sensor 관련 ===================================== // MSG_INIT 받으면 호출 - private void sensorStart(){ + private void sensorStart() { locationStart(); if (sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR) != null) { Sensor stepSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR); @@ -172,43 +185,56 @@ private void sensorStop() { @Override public void onSensorChanged(SensorEvent event) { int type = event.sensor.getType(); - switch (type){ - case Sensor.TYPE_STEP_DETECTOR : + switch (type) { + case Sensor.TYPE_STEP_DETECTOR: ++stepCnt; Log.i("PEDOLOG_SS", "step detector. Steps : " + stepCnt); - if(stepCnt % 10 == 2 && location != null){ - currentSavedStepCnt = stepCnt; - sqlController.insert(stepCnt, location.getLatitude(), location.getLongitude()); - if(isAppAlive()){ + + if (stepCnt % resultStepsUnit == noLocationStepOffset && location != null) { + if (storeHistory && stepCnt % storeStepsUnit == noLocationStepOffset) { + currentSavedStepCnt = stepCnt; + sqlController.insert(stepCnt, location.getLatitude(), location.getLongitude()); + } + + if (isAppAlive()) { try { - Message msg = Message.obtain(null, MSG_SENSOR_TRIGGER); + Map obj = new HashMap(); + obj.put("step", stepCnt); + obj.put("latitude", location.getLatitude()); + obj.put("longitude", location.getLongitude()); + obj.put("timestamp", Calendar.getInstance().getTimeInMillis()); + + Message msg = Message.obtain(null, ServiceManager.MSG_SENSOR_TRIGGER, obj); appMessenger.send(msg); - }catch (RemoteException e){ + } catch (RemoteException e) { Log.i("PEDOLOG_SM", "try to send, but Service doesn't exist.." + "\nmsg: MSG_LIFECYCLE_STOP"); e.printStackTrace(); } } } + break; } } @Override - public void onAccuracyChanged(Sensor sensor, int accuracy) {} + public void onAccuracyChanged(Sensor sensor, int accuracy) { + } // ================================== inner class =========================================== class PedometerLocationCallback extends LocationCallback { boolean isFirst = true; + @Override public void onLocationResult(LocationResult locationResult) { if (isFirst) { Log.i("PEDOLOG_SS", "location update start."); isFirst = false; } - if (locationResult != null){ + if (locationResult != null) { List locationList = locationResult.getLocations(); location = locationList.get(locationList.size() - 1); } From fee2bdd02e9c59af25e4a41fdea85aabf69ec3dd Mon Sep 17 00:00:00 2001 From: SugyoIn-LBSTech Date: Fri, 29 May 2020 13:01:16 +0900 Subject: [PATCH 4/5] [Update] Version number, change log, pub upgrade --- CHANGELOG.md | 7 +++++++ pubspec.lock | 22 +++++++++++----------- pubspec.yaml | 2 +- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41cc7d8..2be6587 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ ## 0.0.1 * TODO: Describe initial release. + +## 1.3.0 +Flutter로 보내는 데이터와 디바이스 데이터 베이스에 저장 하는 데이터의 걸음 단위를 따로 정의 할 수 있도록 옵션 추가. + +- 디바이스 데이터 베이스에 저장할지 여부를 선택하는 storeHistory 옵션 추가. +- 디바이스 데이터 베이스에 저장하는 데이터의 걸음 주기를 정하는 resultStepsUnit 옵션 추가. +- Flutter로 보내는 데이터의 걸음 주기를 정하는 resultStepsUnit 옵션 추가. \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 217a040..4270840 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,42 +7,42 @@ packages: name: archive url: "https://pub.dartlang.org" source: hosted - version: "2.0.11" + version: "2.0.13" args: dependency: transitive description: name: args url: "https://pub.dartlang.org" source: hosted - version: "1.5.2" + version: "1.6.0" async: dependency: transitive description: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.4.0" + version: "2.4.1" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "1.0.5" + version: "2.0.0" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.1.2" + version: "1.1.3" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.14.11" + version: "1.14.12" convert: dependency: transitive description: @@ -56,7 +56,7 @@ packages: name: crypto url: "https://pub.dartlang.org" source: hosted - version: "2.1.3" + version: "2.1.4" flutter: dependency: "direct main" description: flutter @@ -73,7 +73,7 @@ packages: name: image url: "https://pub.dartlang.org" source: hosted - version: "2.1.4" + version: "2.1.12" matcher: dependency: transitive description: @@ -108,7 +108,7 @@ packages: name: quiver url: "https://pub.dartlang.org" source: hosted - version: "2.0.5" + version: "2.1.3" sky_engine: dependency: transitive description: flutter @@ -120,7 +120,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.5.5" + version: "1.7.0" stack_trace: dependency: transitive description: @@ -176,7 +176,7 @@ packages: name: xml url: "https://pub.dartlang.org" source: hosted - version: "3.5.0" + version: "3.6.1" sdks: dart: ">=2.7.0 <3.0.0" flutter: ">=1.10.0" diff --git a/pubspec.yaml b/pubspec.yaml index ba8c69f..4faefe1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: lbs_pedometer description: A new Flutter plugin. -version: 1.2.1 +version: 1.3.0 author: homepage: lbstech.net From e4680df834747cf56279972ef81665bf03260fce Mon Sep 17 00:00:00 2001 From: SugyoIn-LBSTech Date: Fri, 29 May 2020 13:01:41 +0900 Subject: [PATCH 5/5] =?UTF-8?q?[Update]=20=EB=B3=80=EA=B2=BD=EB=90=9C=20?= =?UTF-8?q?=ED=94=8C=EB=9F=AC=EA=B7=B8=EC=9D=B8=20=EB=B2=84=EC=A0=84?= =?UTF-8?q?=EC=97=90=20=EB=A7=9E=EC=B6=B0=20example=20app=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- example/lib/main.dart | 3 +++ example/pubspec.lock | 24 ++++++++++++------------ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 7628b37..b54f3b0 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -122,6 +122,9 @@ class _MyAppState extends State { _pedometer.start( onTakeStep: onTakeStep, onAndroidResumed: onAndroidResume, + resultStepsUnit: 3, + storeHistory: true, + storeStepsUnit: 10, ); } diff --git a/example/pubspec.lock b/example/pubspec.lock index 554e8f3..b2d12c4 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -7,42 +7,42 @@ packages: name: archive url: "https://pub.dartlang.org" source: hosted - version: "2.0.11" + version: "2.0.13" args: dependency: transitive description: name: args url: "https://pub.dartlang.org" source: hosted - version: "1.5.2" + version: "1.6.0" async: dependency: transitive description: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.4.0" + version: "2.4.1" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "1.0.5" + version: "2.0.0" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.1.2" + version: "1.1.3" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.14.11" + version: "1.14.12" convert: dependency: transitive description: @@ -56,7 +56,7 @@ packages: name: crypto url: "https://pub.dartlang.org" source: hosted - version: "2.1.3" + version: "2.1.4" cupertino_icons: dependency: "direct main" description: @@ -80,14 +80,14 @@ packages: name: image url: "https://pub.dartlang.org" source: hosted - version: "2.1.4" + version: "2.1.12" lbs_pedometer: dependency: "direct main" description: path: ".." relative: true source: path - version: "1.2.1" + version: "1.3.0" matcher: dependency: transitive description: @@ -122,7 +122,7 @@ packages: name: quiver url: "https://pub.dartlang.org" source: hosted - version: "2.0.5" + version: "2.1.3" sky_engine: dependency: transitive description: flutter @@ -134,7 +134,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.5.5" + version: "1.7.0" stack_trace: dependency: transitive description: @@ -190,7 +190,7 @@ packages: name: xml url: "https://pub.dartlang.org" source: hosted - version: "3.5.0" + version: "3.6.1" sdks: dart: ">=2.7.0 <3.0.0" flutter: ">=1.10.0"