Add sample js and android plugin
Summary:
The start of an example plugin.
My intention is for this to be a place that we keep up to date with the current best practice for doing things.
For example, with the introduction on persistedStateReducer, there are two ways to receive incoming messages, but only one of them works in the background. This should act as a guideline.
For this reason, don't hold back on reviewing it. I want it to be 👌
Reviewed By: priteshrnandgaonkar
Differential Revision: D10448592
fbshipit-source-id: d5fa978c14e47a7fa3c9a29d0929d5a6109267af
This commit is contained in:
committed by
Facebook Github Bot
parent
881d066369
commit
6cc7f60cde
@@ -1,11 +1,17 @@
|
|||||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
/*
|
||||||
|
* Copyright (c) 2004-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
|
* file in the root directory of this source tree.
|
||||||
|
*
|
||||||
|
*/
|
||||||
package com.facebook.flipper.sample;
|
package com.facebook.flipper.sample;
|
||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import com.facebook.flipper.android.AndroidFlipperClient;
|
import com.facebook.flipper.android.AndroidFlipperClient;
|
||||||
import com.facebook.flipper.core.FlipperClient;
|
import com.facebook.flipper.core.FlipperClient;
|
||||||
|
import com.facebook.flipper.plugins.example.ExampleFlipperPlugin;
|
||||||
import com.facebook.flipper.plugins.inspector.DescriptorMapping;
|
import com.facebook.flipper.plugins.inspector.DescriptorMapping;
|
||||||
import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
|
import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
|
||||||
import com.facebook.flipper.plugins.leakcanary.LeakCanaryFlipperPlugin;
|
import com.facebook.flipper.plugins.leakcanary.LeakCanaryFlipperPlugin;
|
||||||
@@ -56,6 +62,7 @@ public class FlipperSampleApplication extends Application {
|
|||||||
new SharedPreferencesDescriptor("sample", Context.MODE_PRIVATE),
|
new SharedPreferencesDescriptor("sample", Context.MODE_PRIVATE),
|
||||||
new SharedPreferencesDescriptor("other_sample", Context.MODE_PRIVATE))));
|
new SharedPreferencesDescriptor("other_sample", Context.MODE_PRIVATE))));
|
||||||
client.addPlugin(new LeakCanaryFlipperPlugin());
|
client.addPlugin(new LeakCanaryFlipperPlugin());
|
||||||
|
client.addPlugin(new ExampleFlipperPlugin());
|
||||||
client.start();
|
client.start();
|
||||||
|
|
||||||
getSharedPreferences("sample", Context.MODE_PRIVATE).edit().putString("Hello", "world").apply();
|
getSharedPreferences("sample", Context.MODE_PRIVATE).edit().putString("Hello", "world").apply();
|
||||||
|
|||||||
@@ -1,9 +1,18 @@
|
|||||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
/*
|
||||||
|
* Copyright (c) 2004-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
|
* file in the root directory of this source tree.
|
||||||
|
*
|
||||||
|
*/
|
||||||
package com.facebook.flipper.sample;
|
package com.facebook.flipper.sample;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import com.facebook.flipper.android.AndroidFlipperClient;
|
||||||
|
import com.facebook.flipper.core.FlipperClient;
|
||||||
|
import com.facebook.flipper.core.FlipperPlugin;
|
||||||
|
import com.facebook.flipper.plugins.example.ExampleFlipperPlugin;
|
||||||
import com.facebook.litho.ComponentContext;
|
import com.facebook.litho.ComponentContext;
|
||||||
import com.facebook.litho.LithoView;
|
import com.facebook.litho.LithoView;
|
||||||
|
|
||||||
@@ -14,5 +23,13 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
final ComponentContext c = new ComponentContext(this);
|
final ComponentContext c = new ComponentContext(this);
|
||||||
setContentView(LithoView.create(c, RootComponent.create(c).build()));
|
setContentView(LithoView.create(c, RootComponent.create(c).build()));
|
||||||
|
|
||||||
|
FlipperClient client = AndroidFlipperClient.getInstanceIfInitialized();
|
||||||
|
if (client != null) {
|
||||||
|
FlipperPlugin samplePlugin = client.getPlugin(ExampleFlipperPlugin.ID);
|
||||||
|
if (samplePlugin instanceof ExampleFlipperPlugin) {
|
||||||
|
((ExampleFlipperPlugin) samplePlugin).setActivity(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,19 @@
|
|||||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
/*
|
||||||
|
* Copyright (c) 2004-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
|
* file in the root directory of this source tree.
|
||||||
|
*
|
||||||
|
*/
|
||||||
package com.facebook.flipper.sample;
|
package com.facebook.flipper.sample;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import com.facebook.flipper.android.AndroidFlipperClient;
|
||||||
import com.facebook.flipper.android.diagnostics.FlipperDiagnosticActivity;
|
import com.facebook.flipper.android.diagnostics.FlipperDiagnosticActivity;
|
||||||
|
import com.facebook.flipper.core.FlipperClient;
|
||||||
|
import com.facebook.flipper.core.FlipperPlugin;
|
||||||
|
import com.facebook.flipper.plugins.example.ExampleFlipperPlugin;
|
||||||
import com.facebook.litho.ClickEvent;
|
import com.facebook.litho.ClickEvent;
|
||||||
import com.facebook.litho.Column;
|
import com.facebook.litho.Column;
|
||||||
import com.facebook.litho.Component;
|
import com.facebook.litho.Component;
|
||||||
@@ -39,6 +48,12 @@ public class RootComponentSpec {
|
|||||||
.key("2")
|
.key("2")
|
||||||
.textSizeSp(20)
|
.textSizeSp(20)
|
||||||
.clickHandler(RootComponent.hitPostRequest(c)))
|
.clickHandler(RootComponent.hitPostRequest(c)))
|
||||||
|
.child(
|
||||||
|
Text.create(c)
|
||||||
|
.text("Trigger Notification")
|
||||||
|
.key("3")
|
||||||
|
.textSizeSp(20)
|
||||||
|
.clickHandler(RootComponent.triggerNotification(c)))
|
||||||
.child(
|
.child(
|
||||||
Text.create(c)
|
Text.create(c)
|
||||||
.text("Diagnose connection issues")
|
.text("Diagnose connection issues")
|
||||||
@@ -107,9 +122,21 @@ public class RootComponentSpec {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OnEvent(ClickEvent.class)
|
||||||
|
static void triggerNotification(final ComponentContext c) {
|
||||||
|
FlipperClient client = AndroidFlipperClient.getInstanceIfInitialized();
|
||||||
|
if (client != null) {
|
||||||
|
FlipperPlugin plugin = client.getPlugin(ExampleFlipperPlugin.ID);
|
||||||
|
if (plugin instanceof ExampleFlipperPlugin) {
|
||||||
|
((ExampleFlipperPlugin) plugin).triggerNotification();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@OnEvent(ClickEvent.class)
|
@OnEvent(ClickEvent.class)
|
||||||
static void openDiagnostics(final ComponentContext c) {
|
static void openDiagnostics(final ComponentContext c) {
|
||||||
Intent intent = new Intent(c, FlipperDiagnosticActivity.class);
|
Intent intent = new Intent(c, FlipperDiagnosticActivity.class);
|
||||||
c.startActivity(intent);
|
c.startActivity(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
|
* file in the root directory of this source tree.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.facebook.flipper.plugins.example;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.widget.Toast;
|
||||||
|
import com.facebook.flipper.core.FlipperConnection;
|
||||||
|
import com.facebook.flipper.core.FlipperObject;
|
||||||
|
import com.facebook.flipper.core.FlipperPlugin;
|
||||||
|
import com.facebook.flipper.core.FlipperReceiver;
|
||||||
|
import com.facebook.flipper.core.FlipperResponder;
|
||||||
|
|
||||||
|
public class ExampleFlipperPlugin implements FlipperPlugin {
|
||||||
|
|
||||||
|
public static final String ID = "Example";
|
||||||
|
|
||||||
|
private Activity mActivity;
|
||||||
|
private FlipperConnection mConnection;
|
||||||
|
|
||||||
|
private int mNotificationsSent = 0;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Activity to be used to display incoming messages
|
||||||
|
*/
|
||||||
|
public void setActivity(Activity activity) {
|
||||||
|
mActivity = activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConnect(FlipperConnection connection) throws Exception {
|
||||||
|
mConnection = connection;
|
||||||
|
connection.receive(
|
||||||
|
"displayMessage",
|
||||||
|
new FlipperReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(final FlipperObject params, FlipperResponder responder)
|
||||||
|
throws Exception {
|
||||||
|
if (mActivity != null) {
|
||||||
|
mActivity.runOnUiThread(
|
||||||
|
new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Toast.makeText(mActivity, params.getString("message"), Toast.LENGTH_SHORT)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
responder.success(new FlipperObject.Builder().put("greeting", "Hello").build());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void triggerNotification() {
|
||||||
|
if (mConnection != null) {
|
||||||
|
mConnection.send(
|
||||||
|
"triggerNotification", new FlipperObject.Builder().put("id", mNotificationsSent).build());
|
||||||
|
mNotificationsSent++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisconnect() throws Exception {
|
||||||
|
mConnection = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean runInBackground() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the LICENSE
|
||||||
|
* file in the root directory of this source tree.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.facebook.flipper.plugins.example;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.hasItem;
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
|
||||||
|
import com.facebook.flipper.core.FlipperObject;
|
||||||
|
import com.facebook.flipper.testing.FlipperConnectionMock;
|
||||||
|
import com.facebook.flipper.testing.FlipperResponderMock;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
public class ExampleFlipperPluginTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void greetingTest() throws Exception {
|
||||||
|
final ExampleFlipperPlugin plugin = new ExampleFlipperPlugin();
|
||||||
|
final FlipperConnectionMock connection = new FlipperConnectionMock();
|
||||||
|
final FlipperResponderMock responder = new FlipperResponderMock();
|
||||||
|
|
||||||
|
plugin.onConnect(connection);
|
||||||
|
connection
|
||||||
|
.receivers
|
||||||
|
.get("displayMessage")
|
||||||
|
.onReceive(new FlipperObject.Builder().put("message", "test").build(), responder);
|
||||||
|
|
||||||
|
assertThat(
|
||||||
|
responder.successes, hasItem(new FlipperObject.Builder().put("greeting", "Hello").build()));
|
||||||
|
}
|
||||||
|
}
|
||||||
97
src/plugins/example/index.js
Normal file
97
src/plugins/example/index.js
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2018-present Facebook.
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
* @format
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Button, Input, FlipperPlugin, FlexColumn, styled, Text} from 'flipper';
|
||||||
|
|
||||||
|
type DisplayMessageResponse = {
|
||||||
|
greeting: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
type State = {
|
||||||
|
prompt: string,
|
||||||
|
message: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
type PersistedState = {
|
||||||
|
currentNotificationId: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Container = styled(FlexColumn)({
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-around',
|
||||||
|
padding: 20,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default class extends FlipperPlugin<*, State, PersistedState> {
|
||||||
|
static title = 'Example';
|
||||||
|
static id = 'Example';
|
||||||
|
static icon = 'apps';
|
||||||
|
|
||||||
|
state = {
|
||||||
|
prompt: 'Type a message below to see it displayed on the mobile app',
|
||||||
|
message: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reducer to process incoming "send" messages from the mobile counterpart.
|
||||||
|
*/
|
||||||
|
static persistedStateReducer = (
|
||||||
|
persistedState: PersistedState,
|
||||||
|
method: string,
|
||||||
|
payload: Object,
|
||||||
|
): PersistedState => {
|
||||||
|
if (method === 'triggerNotification') {
|
||||||
|
return {
|
||||||
|
currentNotificationId: payload.id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return persistedState || {};
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Callback to provide the currently active notifications.
|
||||||
|
*/
|
||||||
|
static getActiveNotifications = (persistedState: PersistedState) => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: 'test-notification:' + persistedState.currentNotificationId,
|
||||||
|
message: 'Example Notification',
|
||||||
|
severity: 'warning',
|
||||||
|
title: 'Notification: ' + persistedState.currentNotificationId,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Call a method of the mobile counterpart, to display a message.
|
||||||
|
*/
|
||||||
|
sendMessage() {
|
||||||
|
this.client
|
||||||
|
.call('displayMessage', {message: this.state.message || 'Weeeee!'})
|
||||||
|
.then((params: DisplayMessageResponse) => {
|
||||||
|
this.setState({
|
||||||
|
prompt: 'Nice',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<Text>{this.state.prompt}</Text>,
|
||||||
|
<Input
|
||||||
|
placeholder="Message"
|
||||||
|
onChange={event => {
|
||||||
|
this.setState({message: event.target.value});
|
||||||
|
}}
|
||||||
|
/>,
|
||||||
|
<Button onClick={this.sendMessage.bind(this)}>Send</Button>,
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
6
src/plugins/example/package.json
Normal file
6
src/plugins/example/package.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "flipper-plugin-sample",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "index.js",
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
||||||
4
src/plugins/example/yarn.lock
Normal file
4
src/plugins/example/yarn.lock
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user