Skip to content

Commit

Permalink
Merge pull request #4 from mediocre9/development
Browse files Browse the repository at this point in the history
major `UI` rework, refactoring and bug fixes
  • Loading branch information
mediocre9 authored Jan 17, 2023
2 parents ff0fd29 + 9d6cdf6 commit 5384b6f
Show file tree
Hide file tree
Showing 21 changed files with 673 additions and 314 deletions.
41 changes: 39 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,40 @@
# remo_tooth
# Remo Tooth

**An application to remotely control arduino devices via bluetooth technology.**

- **Version:** *0.3a* (in-progress)
- **Development Stage:** alpha


## How it works?

**It scans for nearby beacon devices to establish connection. Then it sends on/off signals to the connected device to perform operations.**

## Development Branches:
- **development** (in-progress)

## Development Progress:
![Progress](https://progress-bar.dev/80/?title=progress)

## Task-List:
- [x] UI Rework
- [x] UI Responsiveness
- [ ] Unit Testing
- [ ] Integration Testing
- [ ] Regression Testing
- [ ] Refactoring

## Dependencies:
- firebase_core: 2.4.1
- firebase_auth: 4.2.5
- google_sign_in: 5.4.3
- flutter_bloc: 8.1.1
- bloc: 8.1.0
- equatable: 2.0.5
- flutter_bluetooth_serial: 0.4.0
- connectivity_plus: 3.0.2
- lottie: 2.2.0




Under Development.
1 change: 1 addition & 0 deletions assets/animations/radar.json

Large diffs are not rendered by default.

Binary file added assets/images/google_logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 33 additions & 0 deletions lib/config/app_colors.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// ignore_for_file: constant_identifier_names

import 'package:flutter/rendering.dart';

class AppColors {
AppColors._();

/// Widget colors....
static const Color PRIMARY_COLOR = Color(0xFFD4C5F6);
static const Color SCAFFOLD_COLOR = Color(0xFF1C1B1F);
static const Color PROGRESS_INDICATOR_COLOR = Color(0xFFD0BCFF);
static const Color APP_BAR_COLOR = Color(0xFF2A2831);
static const Color ELEVATED_BUTTON_COLOR = Color(0xFFBEA9EE);
static const Color SNACKBAR_COLOR = Color(0xFF313033);
static const Color FLOATING_BUTTON_COLOR = Color(0xFF313034);
static const Color BOTTOM_SHEET_COLOR = Color(0xFF1C1B1F);
static const Color CARD_COLOR = Color(0xFF25232A);
static const Color CARD_BORDER_COLOR = Color(0xFF49454F);
static const Color LIST_TILE_COLOR = Color(0xFF25232A);

/// Text Colors....
static const Color APP_BAR_TEXT_COLOR = Color(0xFFE2D9F8);
static const Color DISPLAY_MEDIUM_COLOR = Color(0xFFAC8EF0);
// static const Color DISPLAY_MEDIUM_COLOR = Color(0xFF63D3CC);
static const Color TITLE_SMALL_COLOR = Color(0xFFE6E1E5);
static const Color TITLE_MEDIUM_COLOR = Color(0xFFE2D9F8);
static const Color LABEL_SMALL_COLOR = Color(0xFF7A7A7A);
static const Color LABEL_MEDIUM_COLOR = Color(0xFFB7B7B7);
static const Color LABEL_LARGE_COLOR = Color(0xFFE6E1E5);
static const Color ELEVATED_BUTTON_TEXT_COLOR = Color(0xFF381E72);
static const Color SNACKBAR_CONTENT_COLOR = Color(0xFFF4EFF4);
}
3 changes: 2 additions & 1 deletion lib/config/app_routes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
class AppRoute {
AppRoute._();

static const String SIGN_UP = '/';
static const String SIGN_IN = '/';
static const String HOME = '/home';
static const String REMOTE = '/remote';
}
18 changes: 9 additions & 9 deletions lib/config/app_strings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ class AppString {

static const String APP_NAME = 'Remo Tooth';
static const String APP_VERSION = 'v0.1a';
static const String LOGO = 'assets/images/logo.jpg';
static const String APP_DESCRIPTION = 'Arduino board remote control';
static const String GOOGLE_LOGO_IMAGE = 'assets/images/google_logo.png';
static const String SUSPEND_MSG = 'Your account has been suspended.';
static const String ON_SCAN_MSG = 'Scanning for nearby devices . . .';
static const String INTERNET_MSG = 'No Internet Connection!';
static const String BLUE_TOOTH_SERVICE_MSG = 'Bluetooth service is off.';
static const String DESCRIPTION =
'An app to remotely connect to an arduino device via bluetooth.';
static const String DEVICES_NOT_FOUND =
static const String DISCOVERING_MSG = 'Discovering nearby devices . . .';
static const String NO_INTERNET_MSG = 'No Internet Connection!';
static const String DISABLED_BLUETOOTH_MSG = 'Bluetooth service is off.';
static const String UNDISCOVERED_DEVICES_MSG =
'No nearby device(s) available. Try Again!';
static const String DEVELOPER = 'IT & Robotics Engineering Team';

static const String GOOGLE_SIGN_IN_BTN = 'Sign in with Google';
static const String GOOGLE_LABEL_BUTTON = 'Sign in with Google';
static const String COPYRIGHT =
'(c) Copyright 2023 CUSIT IT & Robotics Engineering. All rights reserved.';
}
26 changes: 20 additions & 6 deletions lib/config/route_generator.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/cupertino.dart' show CupertinoPageRoute;
import 'package:firebase_auth/firebase_auth.dart' show User;
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_bloc/flutter_bloc.dart'
show MultiBlocProvider, BlocProvider;
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
import 'package:remo_tooth/screens/home/cubit/home_cubit.dart';
import 'package:remo_tooth/screens/remote/cubit/remote_cubit.dart';
import 'package:remo_tooth/screens/remote/remote_screen.dart';
import 'package:remo_tooth/screens/sign_in/cubit/sign_in_cubit.dart';
import '../screens/sign_in/sign_in_screen.dart';
import 'app_routes.dart';
Expand All @@ -13,6 +18,14 @@ class RouteGenerator {
static Route<dynamic> generate(RouteSettings routeSettings) {
var arg = routeSettings.arguments;
switch (routeSettings.name) {
case AppRoute.SIGN_IN:
return _pageTransition(
BlocProvider(
create: (_) => SignInCubit(),
child: const SignInScreen(),
),
);

case AppRoute.HOME:
return _pageTransition(
MultiBlocProvider(
Expand All @@ -24,11 +37,12 @@ class RouteGenerator {
),
);

case AppRoute.SIGN_UP:
case AppRoute.REMOTE:
return _pageTransition(
BlocProvider(
create: (_) => SignInCubit(),
child: const SignInScreen(),
lazy: false,
create: (_) => RemoteCubit(),
child: RemoteScreen(device: (arg as BluetoothDevice)),
),
);

Expand All @@ -38,7 +52,7 @@ class RouteGenerator {
}

static _pageTransition(Widget route) =>
MaterialPageRoute(builder: (_) => route);
CupertinoPageRoute(builder: (_) => route);

static _defaultRoute() {
return const Scaffold(
Expand Down
10 changes: 6 additions & 4 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import 'dart:io' show Platform;
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:remo_tooth/firebase_options.dart';
import 'package:remo_tooth/theme/theme.dart';

import 'config/app_routes.dart';
import 'config/app_strings.dart';
import '../config/route_generator.dart';
import '../config/app_routes.dart';
import 'config/route_generator.dart';

void main() async {
WidgetsFlutterBinding.ensureInitialized();
Expand All @@ -28,11 +29,12 @@ class RemoToothApp extends StatelessWidget {

@override
Widget build(BuildContext context) {
return const MaterialApp(
return MaterialApp(
title: AppString.APP_NAME,
initialRoute: AppRoute.SIGN_UP,
initialRoute: AppRoute.SIGN_IN,
onGenerateRoute: RouteGenerator.generate,
debugShowCheckedModeBanner: false,
theme: AppTheme.darkTheme(),
);
}
}
129 changes: 65 additions & 64 deletions lib/screens/home/cubit/home_cubit.dart
Original file line number Diff line number Diff line change
@@ -1,60 +1,24 @@
import 'dart:convert';

import 'package:bloc/bloc.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
import 'package:remo_tooth/config/app_strings.dart';

part 'home_state.dart';

class HomeCubit extends Cubit<HomeState> {
final FlutterBluetoothSerial _bluetooth = FlutterBluetoothSerial.instance;
final List<BluetoothDevice> _foundedDevices = [];
BluetoothDevice? _connectedDevice;
HomeCubit() : super(Initial()) {
getPairedDevices();
}

Future<bool?> get isBluetoothEnabled async => _bluetooth.isEnabled;
final List<BluetoothDevice> _foundedDevices = List.empty(growable: true);

Future establishConnectionToDevice(BluetoothDevice device) async {
if (!device.isBonded) {
emit(Loading(message: 'Connecting to ${device.name} . . .'));
await _bluetooth.bondDeviceAtAddress(device.address).then((connState) {
if (connState!) {
_connectedDevice = device;
emit(BluetoothResponse(message: 'Connected to ${device.name}!'));
emit(Connected());
} else {
emit(BluetoothResponse(
message: 'Couldn`t connect to ${device.name}!'));
emit(ShowDevices(
totalDevices: _foundedDevices.length,
devices: _foundedDevices,
));
}
});
}
HomeCubit() : super(Initial(message: 'Scan for beacon devices')) {
_getPairedDevices();
}

Future unPairDevice(BluetoothDevice device) async {
_foundedDevices.removeWhere((element) => element.address == device.address);
if (device.isBonded) {
await _bluetooth
.removeDeviceBondWithAddress(device.address)
.then((isUnPairSucessful) {
if (isUnPairSucessful!) {
emit(BluetoothResponse(
message: 'Device ${device.name} has been unpaired!'));
}
});
}
}
Future<bool?> get isBluetoothEnabled async => _bluetooth.isEnabled;

Future getPairedDevices() async {
/// retrieves all paired devices.
Future<void> _getPairedDevices() async {
_foundedDevices.clear();
if ((await isBluetoothEnabled)!) {
_foundedDevices.clear();
emit(Loading(message: 'Getting paired devices . . .'));
emit(Discovering(message: 'Getting paired devices . . .'));
await _bluetooth
.getBondedDevices()
.then((paired) => _foundedDevices.addAll(paired))
Expand All @@ -69,15 +33,22 @@ class HomeCubit extends Cubit<HomeState> {
}
});
} else {
await _bluetooth.requestEnable();
emit(BluetoothResponse(message: AppString.BLUE_TOOTH_SERVICE_MSG));
await _bluetooth.requestEnable().then((isBluetoothTurnedOn) {
if (isBluetoothTurnedOn!) {
_getPairedDevices();
} else {
// emit(BluetoothResponse(message: AppString.DISABLED_BLUETOOTH_MSG));
emit(Initial(message: 'Scan for beacon devices'));
}
});
}
}

Future discoverDevices() async {
/// finds nearby beacon bluetooth devices for pairing.
Future<void> discoverDevices() async {
_foundedDevices.clear();
if ((await isBluetoothEnabled)!) {
_foundedDevices.clear();
emit(Loading(message: AppString.ON_SCAN_MSG));
emit(Discovering(message: AppString.DISCOVERING_MSG));
_bluetooth.startDiscovery().listen((e) {
_foundedDevices.add(e.device);
}).onDone(() {
Expand All @@ -87,28 +58,58 @@ class HomeCubit extends Cubit<HomeState> {
devices: _foundedDevices,
));
} else {
emit(BluetoothResponse(message: AppString.DEVICES_NOT_FOUND));
emit(BluetoothResponse(message: AppString.UNDISCOVERED_DEVICES_MSG));
emit(Initial(message: 'Scan for beacon devices'));
}
});
} else {
await _bluetooth.requestEnable();
emit(BluetoothResponse(message: AppString.BLUE_TOOTH_SERVICE_MSG));
await _bluetooth.requestEnable().then((isBluetoothTurnedOn) {
if (isBluetoothTurnedOn!) {
_getPairedDevices();
} else {
emit(BluetoothResponse(message: AppString.DISABLED_BLUETOOTH_MSG));
emit(Initial(message: 'Scan for beacon devices'));
}
});
}
}

Future onMessage() async {
emit(Loading(message: 'Sending 1-bit status signal. . .'));
BluetoothConnection con =
await BluetoothConnection.toAddress(_connectedDevice!.address);
con.output.add(Uint8List.fromList(utf8.encode('1')));
emit(BluetoothResponse(message: '1-bit status signal!'));
/// Tries to pair with discovered unpaired device.
Future<void> pairDevice(BluetoothDevice device) async {
if (!device.isBonded) {
emit(Pairing(
message: 'Trying to pair with ${device.name}. Please be patient!'));
await _bluetooth.bondDeviceAtAddress(device.address).then((connState) {
if (connState!) {
device;
emit(BluetoothResponse(message: 'Paired to ${device.name}!'));
emit(Paired(device: device));
} else {
emit(
BluetoothResponse(message: 'Couldn`t pair with ${device.name}!'));
emit(ShowDevices(
totalDevices: _foundedDevices.length,
devices: _foundedDevices,
));
}
});
} else if (device.isBonded) {
emit(Paired(device: device));
}
}

Future offMessage() async {
emit(Loading(message: 'Sending 1-bit status signal. . .'));
BluetoothConnection con =
await BluetoothConnection.toAddress(_connectedDevice!.address);
con.output.add(Uint8List.fromList(utf8.encode('0')));
emit(BluetoothResponse(message: '0-bit status signal!'));
/// Unpairs a bonded/paired device.
Future<void> unPairDevice(BluetoothDevice device) async {
_foundedDevices.removeWhere((element) => element.address == device.address);
if (device.isBonded) {
await _bluetooth
.removeDeviceBondWithAddress(device.address)
.then((isUnPairSucessful) {
if (isUnPairSucessful!) {
emit(BluetoothResponse(
message: 'Device ${device.name} has been unpaired!'));
}
});
}
}
}
20 changes: 16 additions & 4 deletions lib/screens/home/cubit/home_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,25 @@ part of 'home_cubit.dart';

abstract class HomeState {}

class Initial extends HomeState {}
class Initial extends HomeState {
final String message;
Initial({required this.message});
}

class Connected extends HomeState {}
class Paired extends HomeState {
final BluetoothDevice device;

Paired({required this.device});
}

class Pairing extends HomeState {
final String message;
Pairing({required this.message});
}

class Loading extends HomeState {
class Discovering extends HomeState {
final String message;
Loading({required this.message});
Discovering({required this.message});
}

class ShowDevices extends HomeState {
Expand Down
Loading

0 comments on commit 5384b6f

Please sign in to comment.