added firebase analytics
This commit is contained in:
parent
41e7d889b6
commit
4a5919a979
9 changed files with 87 additions and 25 deletions
|
@ -8,6 +8,8 @@ import 'package:aves/widgets/common/icons.dart';
|
|||
import 'package:aves/widgets/common/routes.dart';
|
||||
import 'package:aves/widgets/home_page.dart';
|
||||
import 'package:aves/widgets/welcome_page.dart';
|
||||
import 'package:firebase_analytics/firebase_analytics.dart';
|
||||
import 'package:firebase_analytics/observer.dart';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
@ -41,7 +43,9 @@ class AvesApp extends StatefulWidget {
|
|||
|
||||
class _AvesAppState extends State<AvesApp> {
|
||||
Future<void> _appSetup;
|
||||
final NavigatorObserver _routeTracker = CrashlyticsRouteTracker();
|
||||
// observers are not registered when using the same list object with different items
|
||||
// the list itself needs to be reassigned
|
||||
List<NavigatorObserver> _navigatorObservers = [];
|
||||
final _newIntentChannel = EventChannel('deckers.thibault/aves/intent');
|
||||
final _navigatorKey = GlobalKey<NavigatorState>();
|
||||
|
||||
|
@ -93,11 +97,12 @@ class _AvesAppState extends State<AvesApp> {
|
|||
|
||||
Future<void> _setup() async {
|
||||
await Firebase.initializeApp().then((app) {
|
||||
FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterError;
|
||||
FirebaseCrashlytics.instance.setCustomKey('locales', window.locales.join(', '));
|
||||
final crashlytics = FirebaseCrashlytics.instance;
|
||||
FlutterError.onError = crashlytics.recordFlutterError;
|
||||
crashlytics.setCustomKey('locales', window.locales.join(', '));
|
||||
final now = DateTime.now();
|
||||
FirebaseCrashlytics.instance.setCustomKey('timezone', '${now.timeZoneName} (${now.timeZoneOffset})');
|
||||
FirebaseCrashlytics.instance.setCustomKey(
|
||||
crashlytics.setCustomKey('timezone', '${now.timeZoneName} (${now.timeZoneOffset})');
|
||||
crashlytics.setCustomKey(
|
||||
'build_mode',
|
||||
kReleaseMode
|
||||
? 'release'
|
||||
|
@ -106,7 +111,11 @@ class _AvesAppState extends State<AvesApp> {
|
|||
: 'debug');
|
||||
});
|
||||
await settings.init();
|
||||
await settings.initCrashlytics();
|
||||
await settings.initFirebase();
|
||||
_navigatorObservers = [
|
||||
FirebaseAnalyticsObserver(analytics: FirebaseAnalytics()),
|
||||
CrashlyticsRouteTracker(),
|
||||
];
|
||||
}
|
||||
|
||||
void _onNewIntent(Map intentData) {
|
||||
|
@ -126,28 +135,20 @@ class _AvesAppState extends State<AvesApp> {
|
|||
Widget build(BuildContext context) {
|
||||
// place the settings provider above `MaterialApp`
|
||||
// so it can be used during navigation transitions
|
||||
final home = FutureBuilder<void>(
|
||||
future: _appSetup,
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasError && snapshot.connectionState == ConnectionState.done) {
|
||||
return getFirstPage();
|
||||
}
|
||||
return Scaffold(
|
||||
body: snapshot.hasError ? _buildError(snapshot.error) : SizedBox.shrink(),
|
||||
);
|
||||
},
|
||||
);
|
||||
return SettingsProvider(
|
||||
child: OverlaySupport(
|
||||
child: FutureBuilder<void>(
|
||||
future: _appSetup,
|
||||
builder: (context, snapshot) {
|
||||
final home = (!snapshot.hasError && snapshot.connectionState == ConnectionState.done)
|
||||
? getFirstPage()
|
||||
: Scaffold(
|
||||
body: snapshot.hasError ? _buildError(snapshot.error) : SizedBox.shrink(),
|
||||
);
|
||||
return MaterialApp(
|
||||
navigatorKey: _navigatorKey,
|
||||
home: home,
|
||||
navigatorObservers: [
|
||||
if (!snapshot.hasError && snapshot.connectionState == ConnectionState.done) _routeTracker,
|
||||
],
|
||||
navigatorObservers: _navigatorObservers,
|
||||
title: 'Aves',
|
||||
darkTheme: darkTheme,
|
||||
themeMode: ThemeMode.dark,
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'package:aves/model/settings/coordinate_format.dart';
|
|||
import 'package:aves/model/settings/home_page.dart';
|
||||
import 'package:aves/model/settings/screen_on.dart';
|
||||
import 'package:aves/widgets/fullscreen/info/location_section.dart';
|
||||
import 'package:firebase_analytics/firebase_analytics.dart';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
@ -57,9 +58,14 @@ class Settings extends ChangeNotifier {
|
|||
|
||||
// Crashlytics initialization is separated from the main settings initialization
|
||||
// to allow settings customization without Firebase context (e.g. before a Flutter Driver test)
|
||||
Future<void> initCrashlytics() async {
|
||||
Future<void> initFirebase() async {
|
||||
await Firebase.app().setAutomaticDataCollectionEnabled(isCrashlyticsEnabled);
|
||||
await FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(isCrashlyticsEnabled);
|
||||
await FirebaseAnalytics().setAnalyticsCollectionEnabled(isCrashlyticsEnabled);
|
||||
// enable analytics debug mode:
|
||||
// # %ANDROID_SDK%/platform-tools/adb shell setprop debug.firebase.analytics.app deckers.thibault.aves.debug
|
||||
// disable analytics debug mode:
|
||||
// # %ANDROID_SDK%/platform-tools/adb shell setprop debug.firebase.analytics.app .none.
|
||||
}
|
||||
|
||||
Future<void> reset() {
|
||||
|
@ -76,7 +82,7 @@ class Settings extends ChangeNotifier {
|
|||
|
||||
set isCrashlyticsEnabled(bool newValue) {
|
||||
setAndNotify(isCrashlyticsEnabledKey, newValue);
|
||||
unawaited(initCrashlytics());
|
||||
unawaited(initFirebase());
|
||||
}
|
||||
|
||||
bool get mustBackTwiceToExit => getBoolOrDefault(mustBackTwiceToExitKey, true);
|
||||
|
|
|
@ -13,6 +13,7 @@ import 'package:aves/utils/file_utils.dart';
|
|||
import 'package:aves/widgets/common/data_providers/media_query_data_provider.dart';
|
||||
import 'package:aves/widgets/common/icons.dart';
|
||||
import 'package:aves/widgets/fullscreen/info/info_page.dart';
|
||||
import 'package:firebase_analytics/firebase_analytics.dart';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -109,6 +110,21 @@ class AppDebugPageState extends State<AppDebugPage> {
|
|||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text('Analytics'),
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
ElevatedButton(
|
||||
onPressed: () => FirebaseAnalytics().logEvent(
|
||||
name: 'debug_test',
|
||||
parameters: {'time': DateTime.now().toIso8601String()},
|
||||
),
|
||||
child: Text('Send event'),
|
||||
),
|
||||
],
|
||||
),
|
||||
Text('Firebase data collection: ${Firebase.app().isAutomaticDataCollectionEnabled ? 'enabled' : 'disabled'}'),
|
||||
Text('Crashlytics collection: ${FirebaseCrashlytics.instance.isCrashlyticsCollectionEnabled ? 'enabled' : 'disabled'}'),
|
||||
Divider(),
|
||||
|
|
|
@ -6,8 +6,10 @@ import 'package:aves/model/metadata_db.dart';
|
|||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:aves/model/source/collection_source.dart';
|
||||
import 'package:aves/services/image_file_service.dart';
|
||||
import 'package:firebase_analytics/firebase_analytics.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_native_timezone/flutter_native_timezone.dart';
|
||||
import 'package:pedantic/pedantic.dart';
|
||||
|
||||
class MediaStoreSource extends CollectionSource {
|
||||
Future<void> init() async {
|
||||
|
@ -68,16 +70,31 @@ class MediaStoreSource extends CollectionSource {
|
|||
onDone: () async {
|
||||
addPendingEntries();
|
||||
debugPrint('$runtimeType refresh loaded ${allNewEntries.length} new entries, elapsed=${stopwatch.elapsed}');
|
||||
|
||||
await metadataDb.saveEntries(allNewEntries); // 700ms for 5500 entries
|
||||
updateAlbums();
|
||||
final analytics = FirebaseAnalytics();
|
||||
unawaited(analytics.setUserProperty(name: 'local_item_count', value: (ceilBy(rawEntries.length, 3)).toString()));
|
||||
unawaited(analytics.setUserProperty(name: 'album_count', value: (ceilBy(sortedAlbums.length, 1)).toString()));
|
||||
|
||||
stateNotifier.value = SourceState.cataloguing;
|
||||
await catalogEntries();
|
||||
unawaited(analytics.setUserProperty(name: 'tag_count', value: (ceilBy(sortedTags.length, 1)).toString()));
|
||||
|
||||
stateNotifier.value = SourceState.locating;
|
||||
await locateEntries();
|
||||
unawaited(analytics.setUserProperty(name: 'country_count', value: (ceilBy(sortedCountries.length, 1)).toString()));
|
||||
|
||||
stateNotifier.value = SourceState.ready;
|
||||
debugPrint('$runtimeType refresh done, elapsed=${stopwatch.elapsed}');
|
||||
},
|
||||
onError: (error) => debugPrint('$runtimeType stream error=$error'),
|
||||
);
|
||||
}
|
||||
|
||||
// e.g. x=12345, precision=3 should return 13000
|
||||
int ceilBy(num x, int precision) {
|
||||
final factor = pow(10, precision);
|
||||
return (x / factor).ceil() * factor;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ abstract class ChipSetActionDelegate {
|
|||
options: {
|
||||
ChipSortFactor.date: 'By date',
|
||||
ChipSortFactor.name: 'By name',
|
||||
ChipSortFactor.count: 'By entry count',
|
||||
ChipSortFactor.count: 'By item count',
|
||||
},
|
||||
title: 'Sort',
|
||||
),
|
||||
|
|
|
@ -115,7 +115,7 @@ class SettingsPage extends StatelessWidget {
|
|||
SwitchListTile(
|
||||
value: settings.isCrashlyticsEnabled,
|
||||
onChanged: (v) => settings.isCrashlyticsEnabled = v,
|
||||
title: Text('Allow anonymous crash reporting'),
|
||||
title: Text('Allow anonymous analytics and crash reporting'),
|
||||
),
|
||||
GrantedDirectories(),
|
||||
],
|
||||
|
|
|
@ -99,7 +99,7 @@ class _WelcomePageState extends State<WelcomePage> {
|
|||
LabeledCheckbox(
|
||||
value: settings.isCrashlyticsEnabled,
|
||||
onChanged: (v) => setState(() => settings.isCrashlyticsEnabled = v),
|
||||
text: 'Allow anonymous crash reporting',
|
||||
text: 'Allow anonymous analytics and crash reporting',
|
||||
),
|
||||
LabeledCheckbox(
|
||||
key: Key('agree-checkbox'),
|
||||
|
|
21
pubspec.lock
21
pubspec.lock
|
@ -201,6 +201,27 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "7.3.2"
|
||||
firebase_analytics:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: firebase_analytics
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "6.0.2"
|
||||
firebase_analytics_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_analytics_platform_interface
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.3"
|
||||
firebase_analytics_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_analytics_web
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.1"
|
||||
firebase_core:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
|
@ -49,6 +49,7 @@ dependencies:
|
|||
git:
|
||||
url: git://github.com/deckerst/expansion_tile_card.git
|
||||
firebase_core:
|
||||
firebase_analytics:
|
||||
firebase_crashlytics:
|
||||
flushbar:
|
||||
flutter_ijkplayer:
|
||||
|
|
Loading…
Reference in a new issue