223 lines
7.4 KiB
Dart
223 lines
7.4 KiB
Dart
import 'dart:convert';
|
|
import 'dart:io';
|
|
|
|
import 'package:firebase_remote_config/firebase_remote_config.dart';
|
|
import 'package:guru_app/ads/core/ads_config.dart';
|
|
import 'package:guru_app/analytics/data/analytics_model.dart';
|
|
import 'package:guru_app/firebase/firebase.dart';
|
|
import 'package:guru_app/firebase/remoteconfig/reserved_remote_config_models.dart';
|
|
import 'package:guru_app/guru_app.dart';
|
|
import 'package:guru_utils/http/http_model.dart';
|
|
import 'package:guru_utils/log/log.dart';
|
|
import 'package:rxdart/rxdart.dart';
|
|
import 'package:guru_utils/remote/remote_config.dart';
|
|
|
|
part "remote_config_interface.dart";
|
|
|
|
/// Created by Haoyi on 2020/4/22
|
|
///
|
|
part "remote_config_reserved_constants.dart";
|
|
|
|
class RemoteConfigManager extends IRemoteConfig {
|
|
final BehaviorSubject<FirebaseRemoteConfigWrapper?> _subject = BehaviorSubject.seeded(null);
|
|
static RemoteConfigManager? _instance;
|
|
|
|
static RemoteConfigManager _getInstance() {
|
|
_instance ??= RemoteConfigManager._internal();
|
|
return _instance!;
|
|
}
|
|
|
|
factory RemoteConfigManager() => _getInstance();
|
|
|
|
static RemoteConfigManager get instance => _getInstance();
|
|
|
|
static final RegExp invalidABKeyRegExp = RegExp('[^a-zA-Z0-9_-]');
|
|
|
|
RemoteConfigManager._internal();
|
|
|
|
Future<void> init(Map<String, dynamic> defaultConfigs) async {
|
|
final remoteConfig = FirebaseRemoteConfig.instance;
|
|
await remoteConfig.setConfigSettings(RemoteConfigSettings(
|
|
fetchTimeout: const Duration(seconds: 15),
|
|
minimumFetchInterval: const Duration(hours: 2),
|
|
));
|
|
|
|
_subject.add(FirebaseRemoteConfigWrapper._(remoteConfig));
|
|
|
|
try {
|
|
await remoteConfig.setDefaults(defaultConfigs);
|
|
await remoteConfig.activate();
|
|
} catch (exception) {
|
|
Log.d("Unable to fetch remote config. Cached or default values will be used!",
|
|
error: exception);
|
|
} finally {
|
|
_subject.add(FirebaseRemoteConfigWrapper._(remoteConfig));
|
|
}
|
|
}
|
|
|
|
Future fetchAndActivate() async {
|
|
final remoteConfig = FirebaseRemoteConfig.instance;
|
|
try {
|
|
await remoteConfig.fetchAndActivate();
|
|
} catch (exception) {
|
|
Log.d("Unable to fetch remote config. Cached or default values will be used!",
|
|
error: exception);
|
|
} finally {
|
|
_subject.add(FirebaseRemoteConfigWrapper._(remoteConfig));
|
|
}
|
|
}
|
|
|
|
Future<String> dumpString() async {
|
|
final config = FirebaseRemoteConfig.instance;
|
|
final data = config.getAll();
|
|
String result = "";
|
|
for (var entry in data.entries) {
|
|
result += "[${entry.key}] ==> (${entry.value.asString()})\n";
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static String valueSourceToString(ValueSource source) {
|
|
switch (source) {
|
|
case ValueSource.valueRemote:
|
|
return "Remote";
|
|
case ValueSource.valueStatic:
|
|
return "Static";
|
|
default:
|
|
return "Default";
|
|
}
|
|
}
|
|
|
|
Map<String, String> allData() {
|
|
final config = FirebaseRemoteConfig.instance;
|
|
final data = config.getAll();
|
|
final result = {
|
|
for (var entry in data.entries)
|
|
"${entry.key} [${valueSourceToString(entry.value.source)}]": entry.value.asString()
|
|
};
|
|
result["last_fetch_remote_config_time"] = config.lastFetchTime.toString();
|
|
result["last_fetch_remote_config_status"] = config.lastFetchStatus.toString();
|
|
return result;
|
|
}
|
|
|
|
Future forceFetch({bool debug = false}) async {
|
|
final remoteConfig = FirebaseRemoteConfig.instance;
|
|
try {
|
|
await remoteConfig.setConfigSettings(RemoteConfigSettings(
|
|
fetchTimeout: const Duration(seconds: 15),
|
|
minimumFetchInterval: const Duration(seconds: 0),
|
|
));
|
|
await remoteConfig.fetchAndActivate();
|
|
} catch (exception) {
|
|
Log.d("Unable to fetch remote config. Cached or default values will be used $exception",
|
|
error: exception);
|
|
if (debug) {
|
|
rethrow;
|
|
}
|
|
} finally {
|
|
_subject.add(FirebaseRemoteConfigWrapper._(remoteConfig));
|
|
await remoteConfig.setConfigSettings(RemoteConfigSettings(
|
|
fetchTimeout: const Duration(seconds: 15),
|
|
minimumFetchInterval: const Duration(hours: 2),
|
|
));
|
|
}
|
|
}
|
|
|
|
Map<String, String> getABProperties() {
|
|
final config = FirebaseRemoteConfig.instance;
|
|
final data = config.getAll();
|
|
final result = <String, String>{};
|
|
final invalidABKeys = <String>{};
|
|
dynamic cause;
|
|
for (var entry in data.entries) {
|
|
final valueStr = entry.value.asString();
|
|
if (valueStr.contains("guru_ab_")) {
|
|
try {
|
|
final jsonValue = json.decode(valueStr);
|
|
if (jsonValue is Map<String, dynamic>) {
|
|
for (var jsonEntry in jsonValue.entries) {
|
|
if (jsonEntry.key.contains("guru_ab_")) {
|
|
String abName = jsonEntry.key.replaceFirst("guru_ab_", "");
|
|
if (abName.contains(invalidABKeyRegExp)) {
|
|
Log.w("abName($abName) length is invalid! $abName");
|
|
invalidABKeys.add(abName);
|
|
} else {
|
|
if (abName.length > 20) {
|
|
invalidABKeys.add(abName);
|
|
abName = abName.substring(0, 20);
|
|
}
|
|
result[GuruAnalytics.buildVariantKey(abName)] = jsonEntry.value.toString();
|
|
Log.i("abName:ab_$abName value:${jsonEntry.value}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} catch (error, stacktrace) {
|
|
Log.w("decode json error! $error");
|
|
cause = error;
|
|
}
|
|
}
|
|
}
|
|
if (invalidABKeys.isNotEmpty) {
|
|
GuruAnalytics.instance
|
|
.logException(InvalidABPropertyKeysException(invalidABKeys, cause: cause));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
@override
|
|
bool? getBool(String name, {bool? defaultValue}) => _subject.value?.getBool(name) ?? defaultValue;
|
|
|
|
@override
|
|
String? getString(String name, {String? defaultValue}) =>
|
|
_subject.value?.getString(name) ??
|
|
defaultValue ??
|
|
RemoteConfigReservedConstants.getDefaultConfigString(name);
|
|
|
|
@override
|
|
double? getDouble(String name, {double? defaultValue}) =>
|
|
_subject.value?.getDouble(name) ?? defaultValue;
|
|
|
|
@override
|
|
int? getInt(String name, {int? defaultValue}) => _subject.value?.getInt(name) ?? defaultValue;
|
|
|
|
Stream<FirebaseRemoteConfigWrapper> observeConfig() => _subject.stream
|
|
.map((config) => config ?? FirebaseRemoteConfigWrapper._(FirebaseRemoteConfig.instance));
|
|
|
|
@override
|
|
Stream<bool?> observeBool(String name, {bool? defaultValue}) =>
|
|
observeConfig().map((config) => config.getBool(name));
|
|
|
|
@override
|
|
Stream<String?> observeString(String name, {String? defaultValue}) =>
|
|
observeConfig().map((config) => config.getString(name));
|
|
|
|
@override
|
|
Stream<double?> observeDouble(String name, {double? defaultValue}) =>
|
|
observeConfig().map((config) => config.getDouble(name));
|
|
|
|
@override
|
|
Stream<int?> observeInt(String name, {int? defaultValue}) =>
|
|
observeConfig().map((config) => config.getInt(name));
|
|
}
|
|
|
|
class FirebaseRemoteConfigWrapper {
|
|
final FirebaseRemoteConfig config;
|
|
|
|
const FirebaseRemoteConfigWrapper._(this.config);
|
|
|
|
DateTime get lastFetchTime => config.lastFetchTime;
|
|
|
|
RemoteConfigFetchStatus get lastFetchStatus => config.lastFetchStatus;
|
|
|
|
static String _key(String key) => GuruApp.instance.getRemoteConfigKey(key);
|
|
|
|
String getString(String key) => config.getString(_key(key));
|
|
|
|
bool getBool(String key) => config.getBool(_key(key));
|
|
|
|
double getDouble(String key) => config.getDouble(_key(key));
|
|
|
|
int getInt(String key) => config.getInt(_key(key));
|
|
}
|