Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

Commit

Permalink
Add Firebase Remote Config support (#391)
Browse files Browse the repository at this point in the history
* Add Remote Config support
  • Loading branch information
kroikie committed Apr 30, 2018
1 parent 5df6cfa commit c0e9db5
Show file tree
Hide file tree
Showing 88 changed files with 3,110 additions and 0 deletions.
9 changes: 9 additions & 0 deletions packages/firebase_remote_config/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.DS_Store
.atom/
.idea
.packages
.pub/
build/
ios/.generated/
packages
pubspec.lock
3 changes: 3 additions & 0 deletions packages/firebase_remote_config/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## [0.0.1]

* Implement Firebase Remote Config.
26 changes: 26 additions & 0 deletions packages/firebase_remote_config/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
Copyright 2018, the Chromium project authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
76 changes: 76 additions & 0 deletions packages/firebase_remote_config/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# firebase_remote_config plugin

A Flutter plugin to use the [Firebase Remote Config API](https://firebase.google.com/products/remote-config/).

[![pub package](https://img.shields.io/pub/v/firebase_remote_config.svg)](https://pub.dartlang.org/packages/firebase_remote_config)

For Flutter plugins for other Firebase products, see [FlutterFire.md](https://github.com/flutter/plugins/blob/master/FlutterFire.md).

*Note*: This plugin is still under development, and some APIs might not be available yet. [Feedback](https://github.com/flutter/flutter/issues) and [Pull Requests](https://github.com/flutter/plugins/pulls) are most welcome!

## Usage

### Import the firebase_remote_config plugin
To use the firebase_remote_config plugin, follow the [plugin installation instructions](https://pub.dartlang.org/packages/firebase_remote_config#pub-pkg-tab-installing).

### Android integration

Enable the Google services by configuring the Gradle scripts as such.

1. Add the classpath to the `[project]/android/build.gradle` file.
```
dependencies {
// Example existing classpath
classpath 'com.android.tools.build:gradle:3.0.1'
// Add the google services classpath
classpath 'com.google.gms:google-services:3.1.2'
}
```

2. Add the apply plugin to the `[project]/android/app/build.gradle` file.
```
// ADD THIS AT THE BOTTOM
apply plugin: 'com.google.gms.google-services'
```

*Note:* If this section is not completed you will get an error like this:
```
java.lang.IllegalStateException:
Default FirebaseApp is not initialized in this process [package name].
Make sure to call FirebaseApp.initializeApp(Context) first.
```

*Note:* When you are debugging on android, use a device or AVD with Google Play services.
Otherwise you will not be able to use Firebase Remote Config.

### Use the plugin

Add the following imports to your Dart code:
```
import 'package:firebase_auth/firebase_remote_config.dart';
```

Initialize `RemoteConfig`:
```
final RemoteConfig remoteConfig = await RemoteConfig.instance;
```

You can now use the Firebase `remoteConfig` to fetch remote configurations in your Dart code, e.g.
```
final defaults = <String, dynamic>{'welcome': 'default welcome'};
await remoteConfig.setDefaults(defaults);
await remoteConfig.fetch(expiration: const Duration(hours: 5));
await remoteConfig.activate();
print('welcome message: ' + remoteConfig.getString('welcome'));
```

## Example

See the [example application](https://github.com/flutter/plugins/tree/master/packages/firebase_remote_config/example) source
for a complete sample app using the Firebase Remote Config.

## Issues and feedback

Please file [issues](https://github.com/flutter/flutter/issues/new)
to send feedback or report a bug. Thank you!
8 changes: 8 additions & 0 deletions packages/firebase_remote_config/android/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
38 changes: 38 additions & 0 deletions packages/firebase_remote_config/android/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
group 'io.flutter.plugins.firebase.firebaseremoteconfig'
version '1.0-SNAPSHOT'

buildscript {
repositories {
google()
jcenter()
}

dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
}
}

rootProject.allprojects {
repositories {
google()
jcenter()
}
}

apply plugin: 'com.android.library'

android {
compileSdkVersion 27

defaultConfig {
minSdkVersion 16
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
lintOptions {
disable 'InvalidPackage'
}
dependencies {
api 'com.google.firebase:firebase-config:[11.4.0,12.0['
api 'com.google.firebase:firebase-core:[11.4.0,12.0['
}
}
1 change: 1 addition & 0 deletions packages/firebase_remote_config/android/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.gradle.jvmargs=-Xmx1536M
1 change: 1 addition & 0 deletions packages/firebase_remote_config/android/settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = 'firebase_remote_config'
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.flutter.plugins.firebase.firebaseremoteconfig">
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
package io.flutter.plugins.firebase.firebaseremoteconfig;

import android.content.Context;
import android.content.SharedPreferences;
import android.support.annotation.NonNull;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.remoteconfig.FirebaseRemoteConfig;
import com.google.firebase.remoteconfig.FirebaseRemoteConfigFetchThrottledException;
import com.google.firebase.remoteconfig.FirebaseRemoteConfigInfo;
import com.google.firebase.remoteconfig.FirebaseRemoteConfigSettings;
import com.google.firebase.remoteconfig.FirebaseRemoteConfigValue;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.PluginRegistry.Registrar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/** FirebaseRemoteConfigPlugin */
public class FirebaseRemoteConfigPlugin implements MethodCallHandler {

public static final String TAG = "FirebaseRemoteConfigPlugin";
public static final String PREFS_NAME =
"io.flutter.plugins.firebase.firebaseremoteconfig.FirebaseRemoteConfigPlugin";
public static final String DEFAULT_PREF_KEY = "default_keys";

private static SharedPreferences sharedPreferences;
private final MethodChannel channel;

public static void registerWith(Registrar registrar) {
final MethodChannel channel =
new MethodChannel(registrar.messenger(), "plugins.flutter.io/firebase_remote_config");
channel.setMethodCallHandler(new FirebaseRemoteConfigPlugin(channel));
sharedPreferences = registrar.context().getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
}

private FirebaseRemoteConfigPlugin(MethodChannel channel) {
this.channel = channel;
}

@Override
public void onMethodCall(MethodCall call, final Result result) {
switch (call.method) {
case "RemoteConfig#instance":
{
FirebaseRemoteConfigInfo firebaseRemoteConfigInfo =
FirebaseRemoteConfig.getInstance().getInfo();

Map<String, Object> properties = new HashMap<>();
properties.put("lastFetchTime", firebaseRemoteConfigInfo.getFetchTimeMillis());
properties.put(
"lastFetchStatus", mapLastFetchStatus(firebaseRemoteConfigInfo.getLastFetchStatus()));
properties.put(
"inDebugMode", firebaseRemoteConfigInfo.getConfigSettings().isDeveloperModeEnabled());
properties.put("parameters", getConfigParameters());
result.success(properties);
break;
}
case "RemoteConfig#setConfigSettings":
{
boolean debugMode = call.argument("debugMode");
final FirebaseRemoteConfig firebaseRemoteConfig = FirebaseRemoteConfig.getInstance();
FirebaseRemoteConfigSettings settings =
new FirebaseRemoteConfigSettings.Builder().setDeveloperModeEnabled(debugMode).build();
firebaseRemoteConfig.setConfigSettings(settings);
result.success(null);
break;
}
case "RemoteConfig#fetch":
{
long expiration = ((Number) call.argument("expiration")).longValue();
final FirebaseRemoteConfig firebaseRemoteConfig = FirebaseRemoteConfig.getInstance();
firebaseRemoteConfig
.fetch(expiration)
.addOnCompleteListener(
new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
FirebaseRemoteConfigInfo firebaseRemoteConfigInfo =
firebaseRemoteConfig.getInfo();
Map<String, Object> properties = new HashMap<>();
properties.put(
"lastFetchTime", firebaseRemoteConfigInfo.getFetchTimeMillis());
properties.put(
"lastFetchStatus",
mapLastFetchStatus(firebaseRemoteConfigInfo.getLastFetchStatus()));
if (!task.isSuccessful()) {
final Exception exception = task.getException();

if (exception instanceof FirebaseRemoteConfigFetchThrottledException) {
properties.put(
"fetchThrottledEnd",
((FirebaseRemoteConfigFetchThrottledException) exception)
.getThrottleEndTimeMillis());
String errorMessage =
"Fetch has been throttled. See the error's "
+ "FETCH_THROTTLED_END field for throttle end time.";
result.error("fetchFailedThrottled", errorMessage, properties);
} else {
String errorMessage =
"Unable to complete fetch. Reason is unknown "
+ "but this could be due to lack of connectivity.";
result.error("fetchFailed", errorMessage, properties);
}
} else {
result.success(properties);
}
}
});
break;
}
case "RemoteConfig#activate":
{
boolean newConfig = FirebaseRemoteConfig.getInstance().activateFetched();
Map<String, Object> properties = new HashMap<>();
properties.put("parameters", getConfigParameters());
properties.put("newConfig", newConfig);
result.success(properties);
break;
}
case "RemoteConfig#setDefaults":
{
Map<String, Object> defaults = call.argument("defaults");
FirebaseRemoteConfig.getInstance().setDefaults(defaults);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putStringSet(DEFAULT_PREF_KEY, defaults.keySet()).apply();
result.success(null);
break;
}
default:
{
result.notImplemented();
break;
}
}
}

private Map<String, Object> getConfigParameters() {
FirebaseRemoteConfig firebaseRemoteConfig = FirebaseRemoteConfig.getInstance();
Map<String, Object> parameterMap = new HashMap<>();
Set<String> keys = firebaseRemoteConfig.getKeysByPrefix("");
for (String key : keys) {
FirebaseRemoteConfigValue remoteConfigValue = firebaseRemoteConfig.getValue(key);
parameterMap.put(key, createRemoteConfigValueMap(remoteConfigValue));
}
// Add default parameters if missing since `getKeysByPrefix` does not return default keys.
Set<String> defaultKeys =
sharedPreferences.getStringSet(DEFAULT_PREF_KEY, new HashSet<String>());
for (String defaultKey : defaultKeys) {
if (!parameterMap.containsKey(defaultKey)) {
FirebaseRemoteConfigValue remoteConfigValue = firebaseRemoteConfig.getValue(defaultKey);
parameterMap.put(defaultKey, createRemoteConfigValueMap(remoteConfigValue));
}
}
return parameterMap;
}

private Map<String, Object> createRemoteConfigValueMap(
FirebaseRemoteConfigValue remoteConfigValue) {
Map<String, Object> valueMap = new HashMap<>();
valueMap.put("value", remoteConfigValue.asByteArray());
valueMap.put("source", mapValueSource(remoteConfigValue.getSource()));
return valueMap;
}

private String mapLastFetchStatus(int status) {
switch (status) {
case FirebaseRemoteConfig.LAST_FETCH_STATUS_SUCCESS:
return "success";
case FirebaseRemoteConfig.LAST_FETCH_STATUS_FAILURE:
return "failure";
case FirebaseRemoteConfig.LAST_FETCH_STATUS_THROTTLED:
return "throttled";
case FirebaseRemoteConfig.LAST_FETCH_STATUS_NO_FETCH_YET:
return "noFetchYet";
default:
return "failure";
}
}

private String mapValueSource(int source) {
switch (source) {
case FirebaseRemoteConfig.VALUE_SOURCE_STATIC:
return "static";
case FirebaseRemoteConfig.VALUE_SOURCE_DEFAULT:
return "default";
case FirebaseRemoteConfig.VALUE_SOURCE_REMOTE:
return "remote";
default:
return "static";
}
}
}
11 changes: 11 additions & 0 deletions packages/firebase_remote_config/example/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.DS_Store
.atom/
.idea
.vscode/
.packages
.pub/
build/
ios/.generated/
packages
pubspec.lock
.flutter-plugins
8 changes: 8 additions & 0 deletions packages/firebase_remote_config/example/.metadata
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.

version:
revision: 538ba522eeeffb8a754ecb12b77eddac3452ed74
channel: master
Loading

0 comments on commit c0e9db5

Please sign in to comment.