My junior and I are working on a React Native application which responds to calls made by the Android system. We have two classes which need to communicate between the two classes shown below: MyModule (the first code block), which should be notified by OtherClass (the second code block) when an event happens in the Android system.
What we're seeing that is:
We create an instance of MyModule, which extends ReactContextBaseJavaModule, where we pass the ReactApplicationContext in via its Constructor.
This creates an instance of OtherClass and passes the ReactApplicationContext into OtherClass, again via its constructor.
In OtherClass, the method onEvent is called when a certain system event is fired. When this happens, OtherClass' private variable reactContext becomes null.
So what we're trying to do is allow our ReactNative application to receive messages from an Android class when a given system event happens. We've attempted to use the "tutorial" at http://facebook.github.io/react-native/docs/native-modules-android.html but, because this is provided without full source code, we're getting a bit lost.
Any suggestions please?
Thanks!
OtherClass.java:
package com.ats;
import com.ats.SuperClass;
import com.ats.MyModule;
import android.content.Context;
import com.ats.MyModule;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import java.util.Date;
import javax.annotation.Nullable;
public class OtherClass extends SuperClass {
private ReactApplicationContext reactContext;
public OtherClass(ReactApplicationContext reactContext) {
super();
this.reactContext = reactContext;
}
public OtherClass() {
super();
}
#Override
protected void onEventTriggered(Context ctx, String number, Date start) {
WritableMap params = Arguments.createMap();
params.putInt("test", 0);
sendEvent("sendCall", params);
}
private void sendEvent(String eventName, #Nullable WritableMap params) {
System.out.println("OtherClass ReactAppContext: " + this.reactContext); //ReactContext is being lost at this point(Nullpointexeception)
this.reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName, params);
}
}
...
MyModule.java:
package com.ats;
import com.ats.OtherClass;
import android.telecom.Call;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReadableMap;
import javax.annotation.Nullable;
/**
* Provides a bridge between JavaScript and Java
*/
public class MyModule extends ReactContextBaseJavaModule {
private OtherClass OR;
/**
* Our local copy of the React Application Context
*/
/**
* Construct an instance of this class and the OtherClass Java class
*
* #param reactContext
*/
public MyModule(ReactApplicationContext reactContext) {
super(reactContext);
this.OR = new OtherClass(reactContext);
}
/**
* Define the name of this ReactNative [Native] Module
*
* #return String
*/
#Override
public String getName() {
return "Module";
}
/**
* Perform a simple println to test whether this code is reachable
*/
#ReactMethod
public void print() {
System.out.println("React Method Print");
}
}
Related
I've tried to modify minecraft by adding a new item called "uranium". Therefore I created the class "Trauma.java" in the main package and a few other classes listed below.
All packages and classes:
Package Explorer
Trauma.java
package main;
import items.ItemUranium;
import net.minecraft.item.Item;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.Mod.EventHandler;
import net.minecraftforge.fml.common.SidedProxy;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.registry.GameRegistry;
import proxy.ServerProxy;
#Mod(modid = Trauma.MODID)
public class Trauma {
public static final String MODID = "Trauma";
#SidedProxy(clientSide = "proxy.ClientProxy", serverSide = "proxy.ServerProxy")
public static ServerProxy proxy;
public static ItemUranium uranium = new ItemUranium();
#EventHandler
public void preInit(FMLPreInitializationEvent event) {
GameRegistry.register(uranium);
}
#EventHandler
public void init(FMLInitializationEvent event) {
proxy.registerClientStuff();
}
#EventHandler
public void postInit(FMLPostInitializationEvent event) {
}
}
BasicItem.java
package items;
import net.minecraft.item.Item;
public class BasicItem extends Item {
public BasicItem(String name) {
setUnlocalizedName(name);
setRegistryName(name);
}
}
ItemUranium.java
package items;
public class ItemUranium extends BasicItem {
public ItemUranium() {
super("uranium");
}
}
ClientProxy.java
package proxy;
import items.BasicItem;
import main.Trauma;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
public class ClientProxy extends ServerProxy {
#Override
public void registerClientStuff () {
registerItemModel(Trauma.uranium);
}
public static void registerItemModel(BasicItem item) {
Minecraft.getMinecraft().getRenderItem().getItemModelMesher().register(item, 0, new ModelResourceLocation(Trauma.MODID + ":" + item.getRegistryName(), "inventory"));
}
}
ServerProxy.java
package proxy;
public class ServerProxy {
public void registerClientStuff() {}
}
uranium.json
{
"parent": "item/generated",
"textures": {
"layer0": "Trauma:items/uranium"
}
}
uranium.png
ingame
Also I don't know why the item in inventory isn't called uranium...
I spent two hours on fixing the problem and it didn't help so it would be really nice if somebody of you may help me.
Thanks :)
Don't use the Model Mesher:
The model mesher is Vanilla (Mojang) code and using it correctly has always been finicky and unreliable, failing if you called it too early and failing if you called it too late. So Forge added the ModelLoader class to resolve that problem.
Replace this line:
Minecraft.getMinecraft().getRenderItem().getItemModelMesher().register(...)
With this line:
ModelLoader.setCustomModelResourceLocation(...)
The ... contents are identical.
Second, depending on what version of Minecraft you're using, you should...:
Stop using GameRegistry.Register
Instead use the RegistryEvent.Register<T> events (where <T> will be <Block> to register blocks, <Item> to register items, etc)
Register your models in the ModelRegistryEvent and no where else.
This event is #SideOnly(CLIENT) and can be subscribed to in your client proxy, avoiding the need to forward references through your proxy class. Eg. I do it like this, where lines 197-199 is the most common scenario needed, where the array is populated during the item registration event. The rest of that method handles the custom state mappers and custom mesh definitions that are used by only a handful of items/blocks and not relevant here.
Include your Mod ID in your unlocalized name. The best way to do this would be setUnlocalizedName(getRegistryName().toString());
See also the Forge documentation on events.
I am new to creating minecraft plugins, however not new to programming, I am following a tutorial very thoroughly, the video has good ratings so it is trusted, when watching the video the guy has no problems what so ever (Youtube video on developing minecraft plugins) , so I did some research into solutions but always the line through the code.
Eclipse gives me the option for: #SuppressWarnings("deprecation") which allows the code to be used still but I would rather have no need of that usage.
Basically my question is why is there the need of the line going through the code and how do I find a solution to get rid of it.
Main class:
package com.jc1;
import org.bukkit.Material;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.permissions.Permission;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
public class Core extends JavaPlugin
{
public Permission pPermission = new Permission("playerAbilities.allowed");
#Override
public void onEnable()
{
new BlockListener(this);
PluginManager pm = getServer().getPluginManager();
pm.addPermission(pPermission);
}
#Override
public void onDisable()
{
}
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args)
{
if(cmd.getName().equalsIgnoreCase("giveitems") && sender instanceof Player)
{
Player p = (Player) sender;
if(p.hasPermission("playerAbilities.allowed"))
{
p.setItemInHand(new ItemStack(Material.DIAMOND_BOOTS));
}
return true;
}
return false;
}
}
Secondary class:
package com.jc1;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockPlaceEvent;
public class BlockListener implements Listener
{
public BlockListener(Core plugin)
{
plugin.getServer().getPluginManager().registerEvents(this, plugin);
}
#EventHandler
public void onBlockPlace(BlockPlaceEvent e)
{
Player p = e.getPlayer();
if(!p.hasPermission("playerAbilities.allowed"))
{
e.setCancelled(true);
}
}
}
The method is deprecated, meaning that it is not recommended to be used anymore and is most likely replaced with another method.
Methods that are deprecated may still work as intended.
A simple search for the method reveals (this) documentation, stating:
players can duel wield now use the methods for the specific hand instead
which referes to the #see references:
getItemInMainHand() and getItemInOffHand().
Use this:
player.getInventory().getItemInMainHand()
Instead of:
player.getItemInHand()
Hope this helps! :D
This is a follow up to a question I asked here.
I have copied and pasted this code from this tutorial. When I paste it into Android Studio, the 'this' parameter of of content.getLoadManager.initLoader() is highlighted in red and shows the following error:
Wrong 3rd Argument Type. Found 'com.example.carl.loaderDemo.FooLoaderClient', requried: 'android.app.LoaderManager.LoaderCallBacks
I've ran into this previously (see first link). I was hoping this tutorial would help but I just seem to be going in endless circles!
Can anyone point me in the right direction?!
package com.example.carl.loaderdemo;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.AsyncTaskLoader;
import android.support.v4.content.Loader;
public class FooLoader extends AsyncTaskLoader {
public FooLoader(Context context, Bundle args) {
super(context);
// do some initializations here
}
public String loadInBackground() {
String result = "";
// ...
// do long running tasks here
// ...
return result;
}
}
class FooLoaderClient implements LoaderManager.LoaderCallbacks {
Activity context;
// to be used for support library:
// FragmentActivity context2;
public Loader onCreateLoader(int id, Bundle args) {
// init loader depending on id
return new FooLoader(context, args);
}
#Override
public void onLoadFinished(Loader loader, Object data) {
}
public void onLoaderReset(Loader loader) {
// ...
}
public void useLoader() {
Bundle args = new Bundle();
// ...
// fill in args
// ...
Loader loader =
context.getLoaderManager().initLoader(0, args, this);
// with support library:
// Loader loader =
// context2.getSupportLoaderManager().initLoader(0, args, this);
// call forceLoad() to start processing
loader.forceLoad();
}
}
Screenshot of error message:
There is a mismatch in your imports:
import android.support.v4.app.LoaderManager;
import android.support.v4.content.AsyncTaskLoader;
import android.support.v4.content.Loader;
But you need
import android.app.LoaderManager;
import android.content.AsyncTaskLoader;
import android.content.Loader;
You cannot mix the support library with the android framework. Alternatively you can subclass FragmentActivity and call getSupportLoaderManager() instead.
You're implementing android.support.v4.app.LoaderManager.LoaderCallbacks but the client is expecting android.app.LoaderManager.LoaderCallbacks. You need to be consistent in which loader API you're using.
I an trying to create an application thet will catch the incoming calls to the cellphone using a BroadcastReciver. From my BroadcastReciver i whould like to sent the number as an event to my JS file using this method.
I have checked that my Java code is working and is catching the calls and number but my application craches with the error that mentions that the react context is null. I am guessing that this is because the manifest (or something) is creating a new instance of the class when the event from the android system is catched and that the new instance does not have a ReactContext. Is there any way to access the ReactContext from the Java code or send a ReactContext to the BroadcastReciver through the manifest?
This is my BroadcastReciver:
package com.bridgetest;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.bridge.JavaScriptModule;
import android.widget.Toast;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.Arguments;
import javax.annotation.Nullable;
/**
* Created by Erik on 2016-04-06.
*/
public class BroadcastReceiverCustom extends BroadcastReceiver {
ReactContext reactContext;
public BroadcastReceiverCustom (){
}
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_RINGING)) {
// This code will execute when the phone has an incoming call
// get the phone number
String incomingNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
Toast.makeText(context, "Call from:" +incomingNumber, Toast.LENGTH_LONG).show();
sendCallEvent(incomingNumber);
} else if (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(
TelephonyManager.EXTRA_STATE_IDLE)
|| intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(
TelephonyManager.EXTRA_STATE_OFFHOOK)) {
// This code will execute when the call is disconnected
Toast.makeText(context, "Detected call hangup event", Toast.LENGTH_LONG).show();
}
}
public void sendCallEvent(String incomingNumber){
WritableMap params = Arguments.createMap();
params.putString("Number", incomingNumber);
sendEvent("CallRecevied", params);
}
private void sendEvent(String eventName,
#Nullable WritableMap params) {
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, params);
}
}
As Fury's solution did not work for me(got "cannot find symbol" for getApplication()) in the broadcast receiver, i tried applying a similar logic with what i knew is working. So:
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.ReactApplication;
...
// context - is the context you get from broadcastreceivers onReceive
ReactApplication rnApp = (ReactApplication) context.getApplicationContext();
rnApp.getReactNativeHost().getReactInstanceManager()
.getCurrentReactContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("test", Arguments.createMap());
This worked for me.
I faced a same problem and am using this.
((MainApplication)getApplication()).getReactNativeHost().getReactInstanceManager().getCurrentReactContext()
I solved the problem. I created a ReactContextBaseJavaModule with a singleton pattern and got access to ReactContext through it (This might be a huge "nono").
If there is another smarter way to solve this problem, please inform me.
You can access reactContext like this:
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.bridge.ReactContext;
ReactInstanceManager reactInstanceManager = getReactNativeHost().getReactInstanceManager();
ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
if(reactContext != null) {
// Use reactContext here
} else {
reactInstanceManager.addReactInstanceEventListener(new ReactInstanceManager.ReactInstanceEventListener() {
#Override
public void onReactContextInitialized(ReactContext context) {
// Use context here
reactInstanceManager.removeReactInstanceEventListener(this);
}
});
}
For the android installation of this component:
https://github.com/lwansbrough/react-native-camera
The first installation step is:
Modify the ReactInstanceManager.builder() calls chain in
android/app/main/java/.../MainActivity.java to include:
.addPackage(new RCTCameraPackage())
But my MainActivity.java file located at
android/app/src/main/java/com/.../MainActivity.java
Doesn't seem to have any reference to ReactInstanceManager.
Here it is in it's entirety:
package com.app;
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.rssignaturecapture.RSSignatureCapturePackage;
import java.util.Arrays;
import java.util.List;
public class MainActivity extends ReactActivity {
/**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
*/
#Override
protected String getMainComponentName() {
return "app";
}
/**
* Returns whether dev mode should be enabled.
* This enables e.g. the dev menu.
*/
#Override
protected boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
/**
* A list of packages used by the app. If the app uses additional views
* or modules besides the default ones, add more packages here.
*/
#Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new RSSignatureCapturePackage(this),
new MainReactPackage()
);
}
}
EDIT:
This seems to be related to changes in React 0.18
Here is a similar issue / solution on another Module:
https://github.com/marcshilling/react-native-image-picker/issues/74
I've tried applying the same changes to this Module as well with no luck.
I was able to solve the same issues with the following steps:
Installed react-native-camera from github and not with version "latest" but with github link
npm install react-native-camera#https://github.com/lwansbrough/react-native-camera.git --save
(thank you https://github.com/lwansbrough/react-native-camera/issues/164)
In file "/node_modules/react-native-camera/android/src/main/AndroidManifest.xml" I added the following permissions
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
In file "/node_modules/react-native-camera/android/src/main/java/com/lwansbrough/RCTCamera/RCTCameraViewManager.java" I modified the part
public class RCTCameraViewManager extends SimpleViewManager
to
public class RCTCameraViewManager extends ViewGroupManager
(thank you for 2. and 3. to https://github.com/lwansbrough/react-native-camera/issues/165)
In File "/android/app/src/main/java/com/myapp/MainActivity.java" (do not forget to exchange "myapp" with your correct path name) I added to the "#Override" section the line:
new RCTCameraPackage(),
in order to look like:
#Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new RCTCameraPackage(),
new MainReactPackage());
}
This is related to a change that was made in React Native 0.18:
https://github.com/facebook/react-native/commit/935cbb76c02cffd378a8f391c6e7443a3da13adc
In order to solve for it, I had to make sure to import the package at the correct location:
import com.lwansbrough.RCTCamera.RCTCameraPackage;
and reference it in the packages list:
new RCTCameraPackage(),
like so:
package com.myapp;
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.lwansbrough.RCTCamera.RCTCameraPackage; // IMPORT REACT-NATIVE-CAMERA
import java.util.Arrays;
import java.util.List;
public class MainActivity extends ReactActivity {
/**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
*/
#Override
protected String getMainComponentName() {
return "myapp";
}
/**
* Returns whether dev mode should be enabled.
* This enables e.g. the dev menu.
*/
#Override
protected boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
/**
* A list of packages used by the app. If the app uses additional views
* or modules besides the default ones, add more packages here.
*/
#Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new RCTCameraPackage(), // ADD REACT-NATIVE_CAMERA
new MainReactPackage()
);
}
}
Update: This fixed the error, but did not get the module working.