Skip to content

Commit

Permalink
Merge pull request #19 from MSzalek-Mobile/voice_search
Browse files Browse the repository at this point in the history
Voice search
  • Loading branch information
MarcinusX authored Dec 26, 2017
2 parents 406b2ce + a14b374 commit 1d5d87d
Show file tree
Hide file tree
Showing 11 changed files with 245 additions and 18 deletions.
5 changes: 5 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<action android:name="com.google.android.gms.actions.CREATE_NOTE"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
</activity>
</application>
</manifest>
Original file line number Diff line number Diff line change
@@ -1,13 +1,47 @@
package com.mszalek.weight_tracker;

import android.content.Intent;
import android.os.Bundle;
import com.google.android.gms.actions.NoteIntents;

import java.nio.ByteBuffer;

import io.flutter.app.FlutterActivity;
import io.flutter.plugin.common.ActivityLifecycleListener;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugins.GeneratedPluginRegistrant;

public class MainActivity extends FlutterActivity {
String savedNote;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
Intent intent = getIntent();
String action = intent.getAction();
String type = intent.getType();

if (NoteIntents.ACTION_CREATE_NOTE.equals(action) && type != null) {
if ("text/plain".equals(type)) {
handleSendText(intent);
}
}

new MethodChannel(getFlutterView(), "app.channel.shared.data").setMethodCallHandler(new MethodChannel.MethodCallHandler() {
@Override
public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
if (methodCall.method.contentEquals("getSavedNote")) {
result.success(savedNote);
savedNote = null;
}
}
});
}


void handleSendText(Intent intent) {
savedNote = intent.getStringExtra(Intent.EXTRA_TEXT);
}
}
}
12 changes: 12 additions & 0 deletions lib/logic/actions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,18 @@ class AddDatabaseReferenceAction {
AddDatabaseReferenceAction(this.databaseReference);
}

class GetSavedWeightNote {
}

class AddWeightFromNotes {
final double weight;

AddWeightFromNotes(this.weight);
}

class ConsumeWeightFromNotes {
}

class AddEntryAction {
final WeightEntry weightEntry;

Expand Down
2 changes: 2 additions & 0 deletions lib/logic/constants.dart
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
const double KG_LBS_RATIO = 2.2;
const int MAX_KG_VALUE = 200;
const int MIN_KG_VALUE = 5;
65 changes: 61 additions & 4 deletions lib/logic/middleware.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ import 'dart:async';

import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/services.dart';
import 'package:redux/redux.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:weight_tracker/logic/actions.dart';
import 'package:weight_tracker/logic/constants.dart';
import 'package:weight_tracker/logic/redux_state.dart';
import 'package:weight_tracker/model/weight_entry.dart';

middleware(Store<ReduxState> store, action, NextDispatcher next) {
//print(action.runtimeType);
print(action.runtimeType);
if (action is InitAction) {
_handleInitAction(store);
} else if (action is AddEntryAction) {
Expand All @@ -22,12 +24,65 @@ middleware(Store<ReduxState> store, action, NextDispatcher next) {
_handleUndoRemovalAction(store);
} else if (action is SetUnitAction) {
_handleSetUnitAction(action, store);
} else if (action is GetSavedWeightNote) {
_handleGetSavedWeightNote(store);
} else if (action is AddWeightFromNotes) {
_handleAddWeightFromNotes(store, action);
}
next(action);
if (action is UserLoadedAction) {
_handleUserLoadedAction(store);
} else if (action is AddDatabaseReferenceAction) {
_handleAddedDatabaseReference(store);
}
}

_handleAddWeightFromNotes(Store<ReduxState> store, AddWeightFromNotes action) {
if (store.state.firebaseState?.mainReference != null) {
WeightEntry weightEntry =
new WeightEntry(new DateTime.now(), action.weight, null);
store.dispatch(new AddEntryAction(weightEntry));
action = new AddWeightFromNotes(null);
}
}

_handleGetSavedWeightNote(Store<ReduxState> store) async {
double savedWeight = await _getSavedWeightNote();
if (savedWeight != null) {
store.dispatch(new AddWeightFromNotes(savedWeight));
}
}

Future<double> _getSavedWeightNote() async {
String sharedData = await const MethodChannel('app.channel.shared.data')
.invokeMethod("getSavedNote");
if (sharedData != null) {
int firstIndex = sharedData.indexOf(new RegExp("[0-9]"));
int lastIndex = sharedData.lastIndexOf(new RegExp("[0-9]"));
if (firstIndex != -1) {
String number = sharedData.substring(firstIndex, lastIndex + 1);
double num = double.parse(number, (error) => null);
return num;
}
}
return null;
}

_handleAddedDatabaseReference(Store<ReduxState> store) {
double weight = store.state.weightFromNotes;
if (weight != null) {
if (store.state.unit == 'lbs') {
weight = weight / KG_LBS_RATIO;
}
if (weight >= MIN_KG_VALUE && weight <= MAX_KG_VALUE) {
WeightEntry weightEntry =
new WeightEntry(new DateTime.now(), weight, null);
store.dispatch(new AddEntryAction(weightEntry));
store.dispatch(new ConsumeWeightFromNotes());
}
}
}

_handleUserLoadedAction(Store<ReduxState> store) {
store.dispatch(new AddDatabaseReferenceAction(FirebaseDatabase.instance
.reference()
Expand All @@ -54,7 +109,8 @@ _handleUndoRemovalAction(Store<ReduxState> store) {
}

_handleRemoveEntryAction(Store<ReduxState> store, RemoveEntryAction action) {
store.state.firebaseState.mainReference.child(action.weightEntry.key)
store.state.firebaseState.mainReference
.child(action.weightEntry.key)
.remove();
}

Expand All @@ -65,8 +121,9 @@ _handleEditEntryAction(Store<ReduxState> store, EditEntryAction action) {
}

_handleAddEntryAction(Store<ReduxState> store, AddEntryAction action) {
store.state.firebaseState.mainReference.push().set(
action.weightEntry.toJson());
store.state.firebaseState.mainReference
.push()
.set(action.weightEntry.toJson());
}

_handleInitAction(Store<ReduxState> store) {
Expand Down
28 changes: 21 additions & 7 deletions lib/logic/reducer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,28 @@ ReduxState reduce(ReduxState state, action) {
FirebaseState firebaseState = _reduceFirebaseState(state, action);
MainPageReduxState mainPageState = _reduceMainPageState(state, action);
ProgressChartState progressChartState = _reduceChartState(state, action);
double weightFromNotes = _reduceWeightFromNotes(state, action);

return new ReduxState(
entries: entries,
unit: unit,
removedEntryState: removedEntryState,
weightEntryDialogState: weightEntryDialogState,
firebaseState: firebaseState,
mainPageState: mainPageState,
progressChartState: progressChartState);
entries: entries,
unit: unit,
removedEntryState: removedEntryState,
weightEntryDialogState: weightEntryDialogState,
firebaseState: firebaseState,
mainPageState: mainPageState,
progressChartState: progressChartState,
weightFromNotes: weightFromNotes,
);
}

double _reduceWeightFromNotes(ReduxState state, action) {
double weight = state.weightFromNotes;
if (action is AddWeightFromNotes) {
weight = action.weight;
} else if (action is ConsumeWeightFromNotes) {
weight = null;
}
return weight;
}

String _reduceUnit(ReduxState reduxState, action) {
Expand Down
2 changes: 2 additions & 0 deletions lib/logic/redux_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class ReduxState {
final FirebaseState firebaseState;
final MainPageReduxState mainPageState;
final ProgressChartState progressChartState;
final double weightFromNotes;

const ReduxState({
this.firebaseState = const FirebaseState(),
Expand All @@ -21,6 +22,7 @@ class ReduxState {
this.removedEntryState = const RemovedEntryState(),
this.weightEntryDialogState = const WeightEntryDialogReduxState(),
this.progressChartState = const ProgressChartState(),
this.weightFromNotes,
});

ReduxState copyWith({
Expand Down
11 changes: 7 additions & 4 deletions lib/screens/main_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ class MainPageViewModel {
final double defaultWeight;
final bool hasEntryBeenAdded;
final String unit;
final Function() addEntryFunction;
final Function() openAddEntryDialog;
final Function() acceptEntryAddedCallback;

MainPageViewModel({
this.addEntryFunction,
this.openAddEntryDialog,
this.defaultWeight,
this.hasEntryBeenAdded,
this.acceptEntryAddedCallback,
Expand Down Expand Up @@ -63,7 +63,7 @@ class MainPageState extends State<MainPage>
hasEntryBeenAdded: store.state.mainPageState.hasEntryBeenAdded,
acceptEntryAddedCallback: () =>
store.dispatch(new AcceptEntryAddedAction()),
addEntryFunction: () {
openAddEntryDialog: () {
store.dispatch(new OpenAddEntryDialog());
Navigator.of(context).push(new MaterialPageRoute(
builder: (BuildContext context) {
Expand All @@ -75,6 +75,9 @@ class MainPageState extends State<MainPage>
unit: store.state.unit,
);
},
onInit: (store) {
store.dispatch(new GetSavedWeightNote());
},
builder: (context, viewModel) {
if (viewModel.hasEntryBeenAdded) {
_scrollToTop();
Expand Down Expand Up @@ -123,7 +126,7 @@ class MainPageState extends State<MainPage>
),
),
floatingActionButton: new FloatingActionButton(
onPressed: () => viewModel.addEntryFunction(),
onPressed: () => viewModel.openAddEntryDialog(),
tooltip: 'Add new weight entry',
child: new Icon(Icons.add),
),
Expand Down
8 changes: 6 additions & 2 deletions lib/screens/weight_entry_dialog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,12 @@ class WeightEntryDialogState extends State<WeightEntryDialog> {
showDialog(
context: context,
child: new NumberPickerDialog.decimal(
minValue: viewModel.unit == "kg" ? 5 : (5 * KG_LBS_RATIO).toInt(),
maxValue: viewModel.unit == "kg" ? 200 : (200 * KG_LBS_RATIO).toInt(),
minValue: viewModel
.unit == "kg" ? MIN_KG_VALUE : (MIN_KG_VALUE * KG_LBS_RATIO)
.toInt(),
maxValue: viewModel
.unit == "kg" ? MAX_KG_VALUE : (MAX_KG_VALUE * KG_LBS_RATIO)
.toInt(),
initialDoubleValue: viewModel.weightToDisplay,
title: new Text("Enter your weight"),
),
Expand Down
69 changes: 69 additions & 0 deletions test/unit_tests/middleware_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,73 @@ void main() {
verify(firebaseMock.child(weightEntry.key)).called(1);
verify(firebaseMock.set(weightEntry.toJson())).called(1);
});

test("Added database calls add entry when weight is saved", () {
//given
bool wasAddEntryCalled = false;
var reducer = (ReduxState state, action) {
if (action is AddEntryAction) {
wasAddEntryCalled = true;
}
return state;
};
DatabaseReferenceMock databaseReferenceMock = new DatabaseReferenceMock();
when(databaseReferenceMock.child(any)).thenReturn(databaseReferenceMock);
when(databaseReferenceMock.push()).thenReturn(databaseReferenceMock);
ReduxState state = new ReduxState(
weightFromNotes: 70.0,
firebaseState: new FirebaseState(mainReference: databaseReferenceMock),
);
Store<ReduxState> store = new Store(reducer,
initialState: state, middleware: [middleware].toList());
//when
store.dispatch(new AddDatabaseReferenceAction(databaseReferenceMock));
//then
expect(wasAddEntryCalled, true);
});

test("Added database calls consume saved weight when weight is saved", () {
//given
bool wasConsumeSavedWeightCalled = false;
var reducer = (ReduxState state, action) {
if (action is ConsumeWeightFromNotes) {
wasConsumeSavedWeightCalled = true;
}
return state;
};
DatabaseReferenceMock databaseReferenceMock = new DatabaseReferenceMock();
when(databaseReferenceMock.child(any)).thenReturn(databaseReferenceMock);
when(databaseReferenceMock.push()).thenReturn(databaseReferenceMock);
ReduxState state = new ReduxState(
weightFromNotes: 70.0,
firebaseState: new FirebaseState(mainReference: databaseReferenceMock),
);
Store<ReduxState> store = new Store(reducer,
initialState: state, middleware: [middleware].toList());
//when
store.dispatch(new AddDatabaseReferenceAction(databaseReferenceMock));
//then
expect(wasConsumeSavedWeightCalled, true);
});

test("Added database doesnt call consume/add when saved weight is null", () {
//given
bool wasConsumeOrAddCalled = false;
var reducer = (ReduxState state, action) {
if (action is ConsumeWeightFromNotes || action is AddEntryAction) {
wasConsumeOrAddCalled = true;
}
return state;
};
DatabaseReferenceMock databaseReferenceMock = new DatabaseReferenceMock();
ReduxState state = new ReduxState(
weightFromNotes: null,
);
Store<ReduxState> store = new Store(reducer,
initialState: state, middleware: [middleware].toList());
//when
store.dispatch(new AddDatabaseReferenceAction(databaseReferenceMock));
//then
expect(wasConsumeOrAddCalled, false);
});
}
Loading

0 comments on commit 1d5d87d

Please sign in to comment.