Add Firebase Crashlytics

This commit is contained in:
Ming Ming 2021-04-17 00:54:02 +08:00
parent e1bfca432e
commit 5473ff5599
12 changed files with 178 additions and 0 deletions

View file

@ -128,3 +128,6 @@ dependencies {
implementation 'com.nkming.nc_photos.np_android_core:np_android_core'
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:2.0.3"
}
apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.google.firebase.crashlytics'

View file

@ -62,5 +62,8 @@
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="" />
<meta-data
android:name="firebase_crashlytics_collection_enabled"
android:value="false" />
</application>
</manifest>

View file

@ -8,6 +8,8 @@ buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:7.4.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.google.gms:google-services:4.3.14'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
}
}

View file

@ -1,5 +1,9 @@
import 'dart:io';
import 'package:equatable/equatable.dart';
import 'package:event_bus/event_bus.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter/foundation.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:kiwi/kiwi.dart';
@ -42,6 +46,7 @@ import 'package:nc_photos/entity/tagged_file/data_source.dart';
import 'package:nc_photos/k.dart' as k;
import 'package:nc_photos/mobile/android/android_info.dart';
import 'package:nc_photos/mobile/self_signed_cert_manager.dart';
import 'package:nc_photos/object_extension.dart';
import 'package:nc_photos/platform/features.dart' as features;
import 'package:nc_photos/session_storage.dart';
import 'package:nc_photos/touch_manager.dart';
@ -85,6 +90,7 @@ Future<void> init(InitIsolateType isolateType) async {
// init session storage
SessionStorage();
await _initFirebase();
await _initAds();
_hasInitedInThisIsolate = true;
@ -98,6 +104,16 @@ void initLog() {
np_log.initLog(
isDebugMode: np_log.isDevMode,
print: (log) => debugPrint(log, wrapWidth: 1024),
onLog: (record) {
if (_shouldReportCrashlytics(record)) {
FirebaseCrashlytics.instance.recordError(
record.error,
record.stackTrace,
reason: record.message,
printDetails: false,
);
}
},
);
}
@ -236,5 +252,39 @@ Future<InitializationStatus> _initAds() {
return MobileAds.instance.initialize();
}
Future<void> _initFirebase() async {
await Firebase.initializeApp();
// Crashlytics
if (features.isSupportCrashlytics) {
if (kDebugMode) {
await FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(false);
await FirebaseCrashlytics.instance.deleteUnsentReports();
}
}
}
bool _shouldReportCrashlytics(LogRecord record) {
if (kDebugMode ||
!features.isSupportCrashlytics ||
record.level < Level.SHOUT) {
return false;
}
final e = record.error;
// We ignore these SocketExceptions as they are likely caused by an unstable
// internet connection
// 7: No address associated with hostname
// 101: Network is unreachable
// 103: Software caused connection abort
// 104: Connection reset by peer
// 110: Connection timed out
// 113: No route to host
if (e is SocketException &&
e.osError?.errorCode.isIn([7, 101, 103, 104, 110, 113]) == true) {
return false;
}
return true;
}
final _log = Logger("app_init");
var _hasInitedInThisIsolate = false;

View file

@ -7,6 +7,8 @@
"settingsPrivacyTitle": "Privacy",
"settingsPrivacyDescription": "Privacy-related settings",
"settingsPrivacyPageTitle": "Privacy settings",
"settingsAnalyticsTitle": "Analytics",
"settingsAnalyticsSubtitle": "Collect analytics after an error to help developers better diagnose the issue",
"settingsPrivacyPolicyTitle": "Privacy policy",
"setupPrivacyAgreeStatement": "Please read carefully the above privacy policy. By continuing, you agree to our privacy policy",
"photosTabLabel": "Photos",

View file

@ -3,4 +3,7 @@ import 'package:np_common/object_util.dart';
extension ObjectExtension<T> on T {
/// Deprecated, use [let]
U run<U>(U Function(T obj) fn) => let(fn);
/// Return if this is contained inside [iterable]
bool isIn(Iterable<T> iterable) => iterable.contains(this);
}

View file

@ -6,3 +6,4 @@ final isSupportSelfSignedCert = getRawPlatform() == NpPlatform.android;
final isSupportEnhancement = getRawPlatform() == NpPlatform.android;
final isSupportAds = getRawPlatform() != NpPlatform.web;
final isSupportCrashlytics = getRawPlatform() != NpPlatform.web;

View file

@ -1,5 +1,6 @@
import 'dart:async';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:logging/logging.dart';
@ -272,6 +273,15 @@ class _PrivacySettings extends StatefulWidget {
}
class _PrivacySettingsState extends State<_PrivacySettings> {
@override
initState() {
super.initState();
if (features.isSupportCrashlytics) {
_isEnableAnalytics =
FirebaseCrashlytics.instance.isCrashlyticsCollectionEnabled;
}
}
@override
build(BuildContext context) {
return Scaffold(
@ -291,6 +301,13 @@ class _PrivacySettingsState extends State<_PrivacySettings> {
SliverList(
delegate: SliverChildListDelegate(
[
if (features.isSupportCrashlytics)
SwitchListTile(
title: Text(L10n.global().settingsAnalyticsTitle),
subtitle: Text(L10n.global().settingsAnalyticsSubtitle),
value: _isEnableAnalytics,
onChanged: (value) => _onAnalyticsChanged(value),
),
ListTile(
title: Text(L10n.global().settingsPrivacyPolicyTitle),
onTap: () {
@ -303,4 +320,13 @@ class _PrivacySettingsState extends State<_PrivacySettings> {
],
);
}
void _onAnalyticsChanged(bool value) {
setState(() {
_isEnableAnalytics = value;
});
FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(value);
}
late bool _isEnableAnalytics;
}

View file

@ -1,9 +1,12 @@
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:logging/logging.dart';
import 'package:nc_photos/app_localizations.dart';
import 'package:nc_photos/controller/pref_controller.dart';
import 'package:nc_photos/entity/pref.dart';
import 'package:nc_photos/k.dart' as k;
import 'package:nc_photos/platform/features.dart' as features;
import 'package:nc_photos/url_launcher_util.dart';
import 'package:nc_photos/widget/home.dart';
import 'package:nc_photos/widget/sign_in.dart';
@ -250,6 +253,17 @@ class _PrivacyState extends State<_Privacy> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SwitchListTile(
title: Text(L10n.global().settingsAnalyticsTitle),
value: _isEnableAnalytics,
onChanged: _onAnalyticsValueChanged,
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(L10n.global().settingsAnalyticsSubtitle),
),
const SizedBox(height: 16),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: InkWell(
@ -286,4 +300,25 @@ class _PrivacyState extends State<_Privacy> {
),
);
}
@override
dispose() {
super.dispose();
// persist user's choice
_log.info("[dispose] Analytics: $_isEnableAnalytics");
if (features.isSupportCrashlytics) {
FirebaseCrashlytics.instance
.setCrashlyticsCollectionEnabled(_isEnableAnalytics);
}
}
void _onAnalyticsValueChanged(bool value) {
setState(() {
_isEnableAnalytics = value;
});
}
bool _isEnableAnalytics = true;
static final _log = Logger("widget.setup._PrivacyState");
}

View file

@ -9,6 +9,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "67.0.0"
_flutterfire_internals:
dependency: transitive
description:
name: _flutterfire_internals
sha256: "4eec93681221723a686ad580c2e7d960e1017cf1a4e0a263c2573c2c6b0bf5cd"
url: "https://pub.dev"
source: hosted
version: "1.3.25"
analyzer:
dependency: transitive
description:
@ -438,6 +446,46 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.0.0"
firebase_core:
dependency: "direct main"
description:
name: firebase_core
sha256: "53316975310c8af75a96e365f9fccb67d1c544ef0acdbf0d88bbe30eedd1c4f9"
url: "https://pub.dev"
source: hosted
version: "2.27.0"
firebase_core_platform_interface:
dependency: transitive
description:
name: firebase_core_platform_interface
sha256: c437ae5d17e6b5cc7981cf6fd458a5db4d12979905f9aafd1fea930428a9fe63
url: "https://pub.dev"
source: hosted
version: "5.0.0"
firebase_core_web:
dependency: transitive
description:
name: firebase_core_web
sha256: c8e1d59385eee98de63c92f961d2a7062c5d9a65e7f45bdc7f1b0b205aab2492
url: "https://pub.dev"
source: hosted
version: "2.11.5"
firebase_crashlytics:
dependency: "direct main"
description:
name: firebase_crashlytics
sha256: c4f1b723d417bc9c4774810e774ff91df8fb0032d33fb2888b2c887e865581b8
url: "https://pub.dev"
source: hosted
version: "3.4.18"
firebase_crashlytics_platform_interface:
dependency: transitive
description:
name: firebase_crashlytics_platform_interface
sha256: c5a11fca3df76a98e3fa68fde8b10a08aacb9a7639f619fbfd4dad6c67a08643
url: "https://pub.dev"
source: hosted
version: "3.6.25"
fixnum:
dependency: transitive
description:

View file

@ -163,6 +163,8 @@ dependencies:
woozy_search: ^2.0.3
# android/ios only
google_mobile_ads: 5.1.0
firebase_core:
firebase_crashlytics: 3.4.18
dependency_overrides:
video_player:

View file

@ -5,6 +5,7 @@ import 'package:logging/logging.dart';
void initLog({
required bool isDebugMode,
void Function(String) print = print,
void Function(LogRecord record)? onLog,
}) {
Logger.root.level = !isDebugMode ? Level.WARNING : Level.ALL;
Logger.root.onRecord.listen((record) {
@ -35,6 +36,8 @@ void initLog({
}
print(msg);
LogStream().add(msg);
onLog?.call(record);
});
}