I wanna write a native module for to scan barcode but it too hard for me to figure out a way to handle the result without adding a method to the MainActivity. It's not a good idea to modify the MainActivity such heavily because it's no easy job for application developers who writes javascript to use the module.
For example, if I use ZXing Android Embedded: https://github.com/journeyapps/zxing-android-embedded, I have to add a method to MainActivity to handle the result.
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
IntentResult result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
if(result != null) {
if(result.getContents() == null) {
Toast.makeText(this, "Cancelled", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(this, "Scanned: " + result.getContents(), Toast.LENGTH_LONG).show();
}
} else {
// This is important, otherwise the result will not be passed to the fragment
super.onActivityResult(requestCode, resultCode, data);
}
}
That makes an issue that any one who uses this module has to modify their MainActivity, which means the module is hard to use. So, any ideas to work it out?
You can just use react-native-rn-zxing:
npm i react-native-rn-zxing
then link it :
react-native link react-native-rn-zxing
And enjoy
Related
I have two separate applications written using Xamarin.Android; for the sake of discussion, let's call them "Tristan" and "Isolde". Tristan has some state information that Isolde sometimes needs to know. Complication: Tristan may or may not be running at the moment Isolde develops the need to know his state.
I've got kludge working now where Isolde sends a special launch intent to Tristan, who then uses a broadcast intent to send information back to Isolde. (See my earlier question for details.)
"But wait!" I hear you cry, "Is this not a perfect use case for StartActivityForResult()?" Indeed it is! The code is a whole lot simpler, and everything I've read implies that this is how Android wants you to do stuff like this.
Unfortunately, I can't get it to work (despite trying many variations and reading the dozen-or-so related questions on this very site). My specific problem is that in Isolde's OnActivityResult() callback, the resultCode is always Result.Canceled and the data is always null.
Here is the code for Tristan (where commented-out bits represent variations I've tried):
using Android.App;
using Android.Content;
namespace com.example.Tristan.Android
{
[Activity(Name ="com.example.Tristan.Android.IsoldeQueryActivity")]
public class IsoldeQueryActivity : Activity
{
protected override void OnStart()
{
// base.OnStart();
var rtn = new Intent();
rtn.PutExtra("Test", "test");
//rtn.SetAction("TestAction");
SetResult(Result.Ok, rtn);
Finish();
//FinishActivity(1234);
}
}
}
And here is the relevant code from the Activity where Isolde needs to ask for Tristan's state:
private TaskCompletionSource<bool> TristanStateCompletion;
public async Task GetTristanState()
{
TristanStateCompletion = new TaskCompletionSource<bool>();
var req = new Intent("com.example.Tristan.Android.IsoldeQueryActivity");
//req.PutExtra(Intent.ExtraReturnResult, true);
StartActivityForResult(req, 1234);
var rtn = await TristanStateCompletion.Task;
if (!rtn) bomb("can't get state");
TristanStateCompletion = null;
}
protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if(requestCode == 1234) {
DoStuffWith(data);
TristanStateCompletion?.TrySetResult(true);
}
}
Diagnostics -- or rather, a specific lack of them -- leads me to believe that Tristan's IsoldeQueryActivity.OnStart() is never actually being called.
Ideas, requests for additional information and/or useful experiments to try are all welcome. (If your idea is "Put <thing> in the manifest", remember this is Xamarin.Android and I have to do that by putting <relatedThing> in the attribute decorating the Activity.)
Edited to add: In Isolde's code, DoStuffWith(data) was crashing because data was null. When I changed that method to avoid that, I found that I got a (slightly later) exception thrown in StartActivityForResult():
Android.Content.ActivityNotFoundException No Activity found to handle Intent { act=com.example.Tristan.Android.IsoldeQueryActivity }
This leads me to believe I'm not creating the Intent properly in Isolde. Do I need to be using one of the other Intent constructors? If so, how specifically?
Okay, I think I have this figured out. The code in my original question had three major problems:
I was building the Intent incorrectly in Isolde.
I didn't export the IsoldeQueryActivity in Tristan.
The call to base.OnStart() in Tristan's OnStart override is mandatory.
Here is the working version of Tristan:
using Android.App;
using Android.Content;
namespace com.example.Tristan.Android
{
[Activity(Name ="com.example.Tristan.Android.IsoldeQueryActivity", Exported=true)]
public class IsoldeQueryActivity : Activity
{
protected override void OnStart()
{
base.OnStart();
var rtn = new Intent();
rtn.PutExtra("Test", "test");
SetResult(Result.Ok, rtn);
Finish();
}
}
}
And here is the fixed code from Isolde:
private TaskCompletionSource<bool> TristanStateCompletion;
public async Task GetTristanState()
{
TristanStateCompletion = new TaskCompletionSource<bool>();
var req = new Intent();
req.SetComponent(new ComponentName("com.example.Tristan.Android", "com.example.Tristan.Android.IsoldeQueryActivity"));
StartActivityForResult(req, 1234);
var rtn = await TristanStateCompletion.Task;
if (!rtn) bomb("can't get state");
TristanStateCompletion = null;
}
protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if(requestCode == 1234) {
if(resultCode != Result.Ok) bomb("bad resultCode {0}", resultCode);
if(data == null) bomb("null data from Tristan");
DoStuffWith(data);
TristanStateCompletion?.TrySetResult(true);
}
}
I am curious to know the difference between the following two methods. Does it matter which one I use, or can they be used according to preference?
Here is the first example:
#Override public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != Activity.RESULT_OK){
return;
}
if (requestCode == REQUEST_DATA){
//Do stuff
}
}
Here is the second one:
#Override public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_DATA){
//Do stuff
}
}
Logically both statements are identical.
The second statement makes the code more readable and understandable, so I always prefer the same. But the condition statement is too complex then nested if is more preferable. Anyway it depends on the coding standard that you follow.
I have been looking for an answer all over the internet.
The thing is, i found many ways to implement a QR code scanner in my app, in an activity.
This is one of the ways:
scan_btn = (Button) view.findViewById(R.id.scan_btn);
scan_btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
IntentIntegrator integrator = new IntentIntegrator(getActivity());
integrator.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE_TYPES);
integrator.setPrompt("Scan!!");
integrator.setCameraId(0);
integrator.setBeepEnabled(false);
integrator.setBarcodeImageEnabled(false);
integrator.initiateScan();
}
});
Now i want to get it to work in a Fragment.
The problem is, it starts a new activity (the QR code reader)
Scans the QR code
But i dont get a response in my onActivityResult:
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
IntentResult result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
if (result != null) {
if (result.getContents() == null) {
System.out.println("Cancelled");
Toast.makeText(getActivity(), "You cancelled the scanning!", Toast.LENGTH_LONG).show();
} else {
System.out.println("Worked: " + result.getContents());
Toast.makeText(getActivity(), "scanned: " + result.getContents(), Toast.LENGTH_LONG).show();
}
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
But what is going wrong?
I guess it has to do with this part:
IntentIntegrator integrator = new IntentIntegrator(getActivity());
It gets the activity, but it is a fragment, instead of an activity.
How can i solve this problem?
Communicating first to my Activity which holds the fragment and then get the result ?
Please help, Thanks :)
I will presume that the implementation of the onActivityResult is on your Fragment, right?
The IntentIntegrator implementation on your Fragment is right. So, just remove your onActivityResult code from the Fragment and put it on the Activity.
I had a similar problem and this was my solution.
IntentIntegrator intentIntegrator=
IntentIntegrator.forSupportFragment(FragmentNme.this);
It worked for me to solve my problem...rest all code is same.
I have an Activity that should handle results from both the Facebook SDK, and from other custom Activities.
Where can I find the requestCodes used by the Facebook SDK, in order to not use the same for my Activities?
I should be able to tell them apart in the onActivityResult using their requestCode so they need to be unique.
Pass the request code in the sdkInitialize call
FacebookSdk.sdkInitialize(context, 1200);
Then
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (FacebookSdk.isFacebookRequestCode(requestCode)) {
//Facebook activity result
//Do your stuff here
//Further you can also check if it's login or Share etc by using
//CallbackManagerImpl as explained by rajath's answer here
if (requestCode == CallbackManagerImpl.RequestCodeOffset.Login.toRequestCode()) {
//login
}
else if (requestCode == CallbackManagerImpl.RequestCodeOffset.Share.toRequestCode()){
//share
}
}
From the docs
isFacebookRequestCode(int)
Returns true if the request code is within the range used by Facebook SDK requests. This does not include request codes that you explicitly set on the dialogs, buttons or LoginManager. The range of request codes that the SDK uses starts at the callbackRequestCodeOffset and continues for the next 100 values.
sdkInitialize(Context, int)
This function initializes the Facebook SDK, the behavior of Facebook SDK functions are undetermined if this function is not called. It should be called as early as possible.
public static synchronized void sdkInitialize(Context applicationContext, int callbackRequestCodeOffset)
applicationContext The application context
callbackRequestCodeOffset The request code offset that Facebook activities will be called with. Please do not use the range between the value you set and another 100 entries after it in your other requests.
Go to CallbackManagerImpl.RequestCodeOffset. I personally used a piece of code like this to prevent unwanted behaviour.
if (requestCode == CallbackManagerImpl.RequestCodeOffset.Login.toRequestCode()) {
callbackManager.onActivityResult(requestCode, resultCode, data);
}
Try this:
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
//Log.iClassMethod();
switch(requestCode)
{
case 1:
if (resultCode == Activity.RESULT_OK)
{
// do something ...
}
break;
case ...:
if (resultCode == Activity.RESULT_OK)
{
// do something ...
}
break;
case Session.DEFAULT_AUTHORIZE_ACTIVITY_CODE:
{
Session.getActiveSession().onActivityResult(this, requestCode, resultCode, data);
Log.i("Facebook");
}
break;
}
}
Offering an alternative if you're using FB login via LoginButton
Set request code of login button
Use the request code to differentiate activity
private LoginButton mFacebookLoginButton;
private static int RC_FB_SIGN_IN;
#Override
protected void onCreate(Bundle savedInstanceState) {
...
mFacebookLoginButton = (LoginButton) findByViewId(R.id.fb_login_button);
mFacebookLoginButton.registerCallback(...)
RC_FB_SIGN_IN = mFacebookLoginButton.getRequestCode();
...
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RC_GP_SIGN_IN) {
...
} else if (requestCode == RC_FB_SIGN_IN) {
Log.i(TAG, "Doing my facebook usual things");
mFacebookCallbackManager.onActivityResult(requestCode, resultCode, data);
}
}
You can set your own request codes to disambiguate. All the OpenRequest and NewPermissionsRequest classes take a requestCode parameter:
setRequestCode
Adding to Apetroaei Andrei's answer, there are other options available at Facebook SDK's CallbackManagerImpl class:
public enum RequestCodeOffset {
Login(0),
Share(1),
Message(2),
Like(3),
GameRequest(4),
AppGroupCreate(5),
AppGroupJoin(6),
AppInvite(7),
;
These can be accessed by the foll. code:
if (requestCode == CallbackManagerImpl.RequestCodeOffset.Share.toRequestCode()) {
callbackManager.onActivityResult(requestCode, resultCode, data);
}
in the lates sdk, (4.4.1), you can manually set the request code by modifying two files in the facebook library:
1) LoginClient.java
public static int getLoginRequestCode() {
//return CallbackManagerImpl.RequestCodeOffset.Login.toRequestCode();
return [your request code here];
}
2)LoginManager.java
public void registerCallback(
...
((CallbackManagerImpl) callbackManager).registerCallback([your request code here],
new CallbackManagerImpl.Callback() {
#Override
...
}
);
}
I'm using ZXING IntentIntegrator in order to read a URL.
I managed to launch the barcode scanner using:
IntentIntegrator integrator = new IntentIntegrator(List8.this);
dialog = integrator.initiateScan();
The barcode scanner indicated that a URL has been found and redirects me back to my application where I retrieve the information using:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (requestCode == 0) {
if (resultCode == RESULT_OK) {
String contents = intent.getStringExtra("SCAN_RESULT");
String format = intent.getStringExtra("SCAN_RESULT_FORMAT");
Toast.makeText(getApplicationContext(), contents, Toast.LENGTH_LONG).show();
// Handle successful scan
} else if (resultCode == RESULT_CANCELED) {
// Handle cancel
}
}
}
The problem is that even thought a URL has been found the requestCode is -1 and the intent has no data.
Does anyone have any idea what's the source of my problem?
P.S.
I tried implementing onActivityResultListener but got the following error:
The return type is incompatible with PreferenceManager.OnActivityResultListener.onActivityResult(int, int,
Intent)
Why do you expect the resultCode to be not -1? The resultCode is used to determine the intention, the "why" you have called the activity for result. Nothing more, nothing less... I would just remove the if (requestCode == 0) as it isn't really important.
Are you sure you aren't looking at resultCode? The value of RESULT_OK is in fact -1 (http://developer.android.com/reference/android/app/Activity.html#RESULT_OK).
If you are using the integration code, then requestCode will be 0xC0DE actually. But, you don't need to bother with these details and getting them right if you just use IntentIntegrator.parseActivityResult() from the project. See the javadoc which shows how to use this fully.