import 'package:comunic/helpers/firebase_messaging_helper.dart'; import 'package:comunic/helpers/independent_push_notifications_helper.dart'; import 'package:comunic/helpers/push_notifications_helper.dart'; import 'package:comunic/helpers/server_config_helper.dart'; import 'package:comunic/models/config.dart'; import 'package:comunic/ui/widgets/async_screen_widget.dart'; import 'package:comunic/utils/flutter_utils.dart'; import 'package:comunic/utils/intl_utils.dart'; import 'package:comunic/utils/log_utils.dart'; import 'package:comunic/utils/ui_utils.dart'; import 'package:flutter/material.dart'; /// Push notifications configuration route /// /// @author Pierre Hubert Future showInitialPushNotificationsConfiguration( BuildContext context) async { // Check if no notifications service are available if (!PushNotificationsHelper.arePushNotificationsAvailable) return; await Navigator.of(context).push( MaterialPageRoute(builder: (c) => PushNotificationsConfigurationRoute())); } class PushNotificationsConfigurationRoute extends StatefulWidget { @override _PushNotificationsConfigurationRouteState createState() => _PushNotificationsConfigurationRouteState(); } class _PushNotificationsConfigurationRouteState extends State { final _key = GlobalKey(); @override Widget build(BuildContext context) => Material( textStyle: TextStyle(color: Colors.white), color: config().splashBackgroundColor, child: Center( child: Padding( padding: const EdgeInsets.all(8.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Spacer(), Icon(Icons.notifications_none, color: Colors.white), Spacer(), PushNotificationsConfigurationWidget( key: _key, onConfigured: () => (Navigator.of(context).pop()), onChanged: () => setState(() {}), ), Spacer(), OutlinedButton( onPressed: _key?.currentState?.canSubmit ?? false ? _key.currentState.submit : null, child: Text(tr("Configure").toUpperCase()), style: OutlinedButton.styleFrom(primary: Colors.white)), Spacer(), ], ), ), ), ); } class PushNotificationsConfigurationWidget extends StatefulWidget { final Function() onConfigured; final Function() onChanged; const PushNotificationsConfigurationWidget({ Key key, @required this.onConfigured, @required this.onChanged, }) : super(key: key); @override PushNotificationsConfigurationWidgetState createState() => PushNotificationsConfigurationWidgetState(); } class PushNotificationsConfigurationWidgetState extends State { PushNotificationsStatus currStatus; bool get canSubmit => (currStatus ?? PushNotificationsStatus.UNDEFINED) != PushNotificationsStatus.UNDEFINED; Future _refresh() async { await PushNotificationsHelper.refreshLocalStatus(); currStatus = await PushNotificationsHelper.getLocalStatus(); if (currStatus == PushNotificationsStatus.UNDEFINED && srvConfig.notificationsPolicy.hasFirebase) currStatus = PushNotificationsStatus.FIREBASE; widget.onChanged(); } @override Widget build(BuildContext context) { return AsyncScreenWidget( onReload: _refresh, onBuild: _buildForm, errorMessage: tr("Failed to load push notifications settings!"), ); } Widget _buildForm() => ConstrainedBox( constraints: BoxConstraints(maxWidth: 300), child: Column( children: [ Text( tr("%app% can send you push notifications to your device.", args: { "app": config().appName, }), textAlign: TextAlign.center, ), SizedBox(height: 10), _NotificationOption( title: tr("Use Google services (recommended)"), description: tr("Save your battery life."), option: PushNotificationsStatus.FIREBASE, current: currStatus, available: srvConfig.notificationsPolicy.hasFirebase, onChanged: (s) { setState(() => currStatus = s); if (widget.onChanged != null) widget.onChanged(); }, ), SizedBox(height: 5), _NotificationOption( title: tr("Use independent notifications service"), description: tr( "Protect more your privacy, but drains battery and is less reliable."), option: PushNotificationsStatus.INDEPENDENT, current: currStatus, available: srvConfig.notificationsPolicy.hasIndependent && isAndroid, onChanged: (s) { setState(() => currStatus = s); if (widget.onChanged != null) widget.onChanged(); }, ), SizedBox(height: 5), _NotificationOption( title: tr("Do not send notification"), option: PushNotificationsStatus.DISABLED, current: currStatus, available: true, onChanged: (s) { setState(() => currStatus = s); if (widget.onChanged != null) widget.onChanged(); }, ), ], ), ); Future submit() async { try { String firebaseToken = ""; if (currStatus == await PushNotificationsHelper.getLocalStatus()) { widget.onConfigured(); return true; } switch (currStatus) { case PushNotificationsStatus.DISABLED: break; case PushNotificationsStatus.FIREBASE: await FirebaseMessagingHelper.preConfigure(); firebaseToken = await FirebaseMessagingHelper.getToken(); break; case PushNotificationsStatus.INDEPENDENT: if (await IndependentPushNotificationsHelper.needPreConfiguration()) await IndependentPushNotificationsHelper.preConfigure(context); break; default: throw new Exception("Unknown status!"); } await PushNotificationsHelper.clearLocalStatus(); await PushNotificationsHelper.setNewStatus(currStatus, firebaseToken: firebaseToken); await PushNotificationsHelper.refreshLocalStatus(); widget.onConfigured(); } catch (e, s) { logError(e, s); showAlert( context: context, message: tr("Failed to configure push notifications!")); return false; } return true; } } class _NotificationOption extends StatelessWidget { final String title; final String description; final PushNotificationsStatus option; final PushNotificationsStatus current; final bool available; final Function(PushNotificationsStatus) onChanged; const _NotificationOption({ Key key, @required this.title, this.description, @required this.option, @required this.current, @required this.available, @required this.onChanged, }) : assert(title != null), assert(option != null), assert(current != null), assert(available != null), assert(onChanged != null), super(key: key); @override Widget build(BuildContext context) => !available ? Container() : Theme( data: Theme.of(context).copyWith( splashColor: Colors.transparent, highlightColor: Colors.transparent), child: ListTile( leading: Radio( value: option, groupValue: current, onChanged: onChanged, fillColor: MaterialStateProperty.all(Colors.white), ), title: Text(title, style: TextStyle(color: Colors.white)), subtitle: description == null ? null : Text(description, style: TextStyle(color: Colors.white)), contentPadding: EdgeInsets.all(0), onTap: () => onChanged(option), ), ); }