flutter icon indicating copy to clipboard operation
flutter copied to clipboard

PluginRegistry.ActivityResultListener: not triggered

Open faspadaro opened this issue 6 years ago • 22 comments

Steps to Reproduce

  1. Create a Flutter package in java
  2. In plugin class add a Activity result listener
  3. In onMethodCall call startActivityForResult
  4. In Activity called, at a certain point, call setResult and finish

Issue is the `onActivityResult' plugin is not triggered when Activity child call finish.

here's the code of parent MyBarcodeScannerPlugin ...

    public class MyBarcodeScannerPlugin implements MethodCallHandler, PluginRegistry.ActivityResultListener {
      private final Activity activity;
      private Result result;

      public static void registerWith(Registrar registrar) {
        final MethodChannel channel = new MethodChannel(registrar.messenger(),    "my_barcode_scanner");

        final MyBarcodeScannerPlugin plugin = new MyBarcodeScannerPlugin(registrar.activity());

        channel.setMethodCallHandler(plugin);
        registrar.addActivityResultListener(plugin);
      }

      private MyBarcodeScannerPlugin(Activity activity) {
        this.activity = activity;
      }


      @Override
      public void onMethodCall(MethodCall call, Result result) {
        if (call.method.equals("scan")) {
          this.result = result;
          showBarcodeView(activity);
        } else {
          result.notImplemented();
        }
      }

      private void showBarcodeView(Activity activity) {
        Intent intent = new Intent(activity, MyBarcodeScannerActivity.class);
        activity.startActivityForResult(intent, 100);
      }

      @Override
      public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
        // IT DOES NOT FIRE !
        if (requestCode == 100) {
          if (resultCode == Activity.RESULT_OK) {
            this.result.success("");
          } else {
            String errorCode = data.getStringExtra("ERROR_CODE");
            this.result.error(errorCode, null, null);
          }
          return true;
        }
        return false;
      }
    }

And here's the child activity that finish when user deny permission...

    public class MyBarcodeScannerActivity extends AppCompatActivity {
        ...
        ...
        private void finishWithError(String errorCode) {
            Intent intent = new Intent();
            intent.putExtra("ERROR_CODE", errorCode);
            setResult(Activity.RESULT_CANCELED, intent);
            finish();
        }
        ...
        public void onRequestPermissionsResult(int requestCode, String  permissions[], int[] grantResults) {
            switch (requestCode) {
                case REQUEST_CAMERA:
                    if (grantResults.length > 0) {
                        boolean cameraAccepted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
                        if (cameraAccepted){
                        }else {
                            finishWithError("PERMISSION_NOT_GRANTED");
                        }
                    }
                    break;
            }
        }
    }
flutter doctor -v
[√] Flutter (Channel stable, v1.0.0, on Microsoft Windows [Versione 10.0.16299.904], locale it-IT)
    • Flutter version 1.0.0 at C:\dati\Flutter\flutter
    • Framework revision 5391447fae (8 weeks ago), 2018-11-29 19:41:26 -0800
    • Engine revision 7375a0f414
    • Dart version 2.1.0 (build 2.1.0-dev.9.4 f9ebf21297)

[√] Android toolchain - develop for Android devices (Android SDK 28.0.3)
    • Android SDK at C:/Users/...
    • Android NDK location not configured (optional; useful for native profiling support)
    • Platform android-28, build-tools 28.0.3
    • ANDROID_HOME = C:/Users/...
    • Java binary at: C:\dati\Android\Android Studio\jre\bin\java
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1024-b02)

[√] Android Studio (version 3.1)
    • Android Studio at C:\dati\Android\Android Studio
    • Flutter plugin version 29.0.1
    • Dart plugin version 173.4700
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1024-b02)

[√] IntelliJ IDEA Community Edition (version 2018.1)
    • IntelliJ at C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2018.1.4
    • Flutter plugin version 25.0.2
    • Dart plugin version 181.4892.1

[√] VS Code, 64-bit edition (version 1.23.1)
    • VS Code at C:\Program Files\Microsoft VS Code
    • Flutter extension version 2.13.2

[√] Connected device (1 available)
    • Redmi Note 5 • 8614d893 • android-arm64 • Android 8.1.0 (API 27)

faspadaro avatar Jan 23 '19 08:01 faspadaro

The impage_picker and google_sign_in plugin seem to use that as well. Did you check how they do it?

  • https://github.com/flutter/plugins/blob/9616a32faf6d1066c8f633a30f0bd7dca6fe7e9e/packages/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java
  • https://github.com/flutter/plugins/blob/d89cae7289ec4f7a51b6e7a17e3f0bb9498d0b19/packages/google_sign_in/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java

zoechi avatar Jan 23 '19 09:01 zoechi

Please consider asking support questions in one of the other channels listed at http://flutter.io/support .

zoechi avatar Jan 23 '19 09:01 zoechi

@zoechi

I checked barcode_scan plugin wirtten in kotlin, it is similar to my package

    class BarcodeScanPlugin(val activity: Activity): MethodCallHandler,
        PluginRegistry.ActivityResultListener {
      var result : Result? = null
      companion object {
        @JvmStatic
        fun registerWith(registrar: Registrar): Unit {
          val channel = MethodChannel(registrar.messenger(), "com.apptreesoftware.barcode_scan")
          val plugin = BarcodeScanPlugin(registrar.activity())
          channel.setMethodCallHandler(plugin)
          registrar.addActivityResultListener(plugin)
        }
      }

      override fun onMethodCall(call: MethodCall, result: Result): Unit {
        if (call.method.equals("scan")) {
          this.result = result
          showBarcodeView()
        } else {
          result.notImplemented()
        }
      }

      private fun showBarcodeView() {
        val intent = Intent(activity, BarcodeScannerActivity::class.java)
        activity.startActivityForResult(intent, 100)
      }

      override fun onActivityResult(code: Int, resultCode: Int, data: Intent?): Boolean {
        if (code == 100) {
          if (resultCode == Activity.RESULT_OK) {
            val barcode = data?.getStringExtra("SCAN_RESULT")
            barcode?.let { this.result?.success(barcode) }
          } else {
            val errorCode = data?.getStringExtra("ERROR_CODE")
            this.result?.error(errorCode, null, null)
          }
          return true
        }
        return false
      }
    }


    class BarcodeScannerActivity : Activity(), ZXingScannerView.ResultHandler {
        ...
        fun finishWithError(errorCode: String) {
                val intent = Intent()
                intent.putExtra("ERROR_CODE", errorCode)
                setResult(Activity.RESULT_CANCELED, intent)
                finish()
            }
        ...
    }

faspadaro avatar Jan 23 '19 10:01 faspadaro

Is there any update on this issue?

faspadaro avatar Apr 11 '19 09:04 faspadaro

I have the same problem

yxwandroid avatar May 05 '19 10:05 yxwandroid

google_sign_in 4.0.1+3 has the same problem

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel dev, v1.5.8, on Linux, locale en_US.UTF-8)
 
[✓] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
[✓] Android Studio (version 3.4)
[!] VS Code (version 1.33.1)
    ✗ Flutter extension not installed; install from
      https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter
[✓] Connected device (1 available)

function1983 avatar May 13 '19 10:05 function1983

I landed here searching a plugin design guideline to deal with duplicate Request Codes across android plugins. It happened to me (and possibly @faspadaro 's as well) having barcode_scan in the project and designing a plugin that uses the same intent request code = 100.

They conflict! And barcode_scan is taking the lead in the delegation queue.

Changing the Request Code solved my issue.

Now this brings me to another topic. How to guarantee that plugins in pub.dev will not have identical request codes? (without randomly typing the num keyboard)

My suggestion would be to recommend every plugin to use android's intents to register an xml id and using it via R.id.mypluginrequestcode. The sdk handles uniqueness when integrating each plugin to the same project. No overhead.

@zoechi , sounds reasonable?

Even better: the flutter plugin template creating the id resource from the beginning.

rmarau avatar Jun 18 '19 21:06 rmarau

I landed here searching a plugin design guideline to deal with duplicate Request Codes across android plugins. It happened to me (and possibly @faspadaro 's as well) having barcode_scan in the project and designing a plugin that uses the same intent request code = 100.

They conflict! And barcode_scan is taking the lead in the delegation queue.

Changing the Request Code solved my issue.

Now this brings me to another topic. How to guarantee that plugins in pub.dev will not have identical request codes? (without randomly typing the num keyboard)

My suggestion would be to recommend every plugin to use android's intents to register an xml id and using it via R.id.mypluginrequestcode. The sdk handles uniqueness when integrating each plugin to the same project. No overhead.

@zoechi , sounds reasonable?

Even better: the flutter plugin template creating the id resource from the beginning.

Very Good !!!!! Solve my question

guzishiwo avatar Jul 24 '19 07:07 guzishiwo

faced the same problem. there is no solution so far.

longdw avatar Aug 29 '19 08:08 longdw

I am facing same issue, I found even after we registrar.addActivityResultListener(this) on the plugin, still if you go to the MainActivity of the project which uses our plugin and add an override for onActivityResult, you will find the result reaching there , instead of listener in the plugin class object. Need some help here.

muthufmass avatar Sep 24 '19 12:09 muthufmass

Trying thousands of examples, I managed to make it work and I realized that the requestCode 100 did not return anything to me (caused by flutter - android requestCode conversion), try another number (I did it with 666) ... I leave the code of my plugin in case it is useful.

`package com.example.barcode_vision;

import android.app.Activity; import android.app.Application; import android.content.Intent; import android.util.Log;

import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity;

import io.flutter.app.FlutterActivity; import io.flutter.embedding.engine.plugins.FlutterPlugin; 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; import io.flutter.plugin.common.PluginRegistry.Registrar;

/** BarcodeVisionPlugin */ public class BarcodeVisionPlugin implements MethodCallHandler, PluginRegistry.ActivityResultListener {

private Activity context; private Result mResult;

// This static function is optional and equivalent to onAttachedToEngine. It supports the old // pre-Flutter-1.12 Android projects. You are encouraged to continue supporting // plugin registration via this function while apps migrate to use the new Android APIs // post-flutter-1.12 via https://flutter.dev/go/android-project-migration. // // It is encouraged to share logic between onAttachedToEngine and registerWith to keep // them functionally equivalent. Only one of onAttachedToEngine or registerWith will be called // depending on the user's project. onAttachedToEngine or registerWith must both be defined // in the same class.

public static void registerWith(Registrar registrar) { final MethodChannel channel = new MethodChannel(registrar.messenger(), "barcode_vision"); channel.setMethodCallHandler(new BarcodeVisionPlugin(registrar, channel)); }

public BarcodeVisionPlugin(Registrar registrar, MethodChannel methodChannel){ this.context = registrar.activity(); methodChannel.setMethodCallHandler(this); registrar.addActivityResultListener(this); }

@Override public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { if (call.method.equals("getPlatformVersion")) { mResult = result; result.success("Android " + android.os.Build.VERSION.RELEASE); } else if (call.method.equals("scan")) { mResult = result; scanIC(call, result); } else { result.notImplemented(); } }

@Override public boolean onActivityResult(int requestCode, int resultCode, Intent data) { Log.d("BARCODE VISION", "onActivityResult"); if (requestCode == 666) { if (resultCode == Activity.RESULT_OK) { String barcode = data.getStringExtra("SCAN_RESULT"); Log.e("BARCODE VISION", barcode); mResult.success(barcode); } else { String errorCode = data.getStringExtra("ERROR_CODE"); Log.e("BARCODE VISION", errorCode); mResult.error(errorCode, "No chive", "estas bien pendejo"); } return true; } return false; }

private void scanIC(MethodCall call, Result result) { Log.d("BARCODE VISION", "INTENT"); Intent intent = new Intent(context, VisionScannerActivity.class); context.startActivityForResult(intent, 666); } } `

RobertoLayna avatar Jan 16 '20 15:01 RobertoLayna

My working code

https://bitbucket.org/prathap_kumar/mvbarcodescan/raw/7a231a8235e3eda5639b54954f3c5822199b5249/android/src/main/java/com/mv/mvbarcodescan/MvbarcodescanPlugin.java

kprathap23 avatar Jul 20 '20 20:07 kprathap23

  @Override
  public void onAttachedToActivity(@NonNull ActivityPluginBinding activityBinding) {
    activityBinding.addRequestPermissionsResultListener(this);
    activityBinding.addActivityResultListener(this);
  }
  @Override
  public void onDetachedFromActivity() {
    activityBinding.removeRequestPermissionsResultListener(this);
    activityBinding.removeActivityResultListener(this);
  }

this solved the problem!

phamconganh avatar Jun 04 '21 21:06 phamconganh

I tried all the above solutions, but not working, Any one example with solution? Thanks in advance.

msarkrish avatar Dec 26 '21 16:12 msarkrish

Now this brings me to another topic. How to guarantee that plugins in pub.dev will not have identical request codes? (without randomly typing the num keyboard)

Thats exactly what you do! You need a garantee that no other app in COULD use the same intent code. The best way to do that is use large random numbers. It's not like there's a benefit to small numbers anyway.

larssn avatar May 03 '22 08:05 larssn

Where does the activityBinding reference come from in onDetachedFromActivity()?

masterwok avatar Oct 25 '22 22:10 masterwok

@masterwok If memory serves: It comes from the other Flutter plugin callback onAttachedToActivity.

larssn avatar Oct 26 '22 12:10 larssn

please i need you guys help, i'm having the same issue now

odejinmi avatar Feb 09 '23 06:02 odejinmi

please i need you guys help, i'm having the same issue now here is my code

import android.app.Activity import android.content.Intent import android.util.Log import com.netpluspay.netpossdk.NetPosSdk import com.netpluspay.netpossdk.utils.TerminalParameters import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding import io.flutter.plugin.common.BinaryMessenger import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.PluginRegistry

const val ERROR_CODE_SDK_INITIALIZATION = "INIT_ERROR"

class MethodCallHandlerImpl( messenger: BinaryMessenger?, private val binding: ActivityPluginBinding ) : MethodChannel.MethodCallHandler, PluginRegistry.ActivityResultListener {

private var channel: MethodChannel? = null
private var result: MethodChannel.Result? = null
val RequestCode = 545667654

init {
    channel = MethodChannel(messenger!!, "flutternetpos")

    channel?.setMethodCallHandler(this)

}

override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
    this.result = result

    when (call.method) {

        "initialize" -> {
            initialise(call)
        }

        "initializePayment" -> {
            makePayment(call)
        }


        else -> result.notImplemented()
    }

}

private fun initialise(call: MethodCall) {

    if (call.arguments == null || !(call.arguments is Map<*,*>)) {
        result?.error(ERROR_CODE_SDK_INITIALIZATION, "Invalid input(s)", null)
        return
    }

    NetPosSdk.init()
    val merchantid = call.argument<String>("merchantId")!!
    val merchantname = call.argument<String>("merchantName")!!
    val terminalid = call.argument<String>("terminalId")!!
    val terminalParams = TerminalParameters()
        .apply {
            merchantId = merchantid
            merchantName = merchantname
            terminalId = terminalid
        }
    NetPosSdk.loadEmvParams(terminalParams)

    result?.success(true)

}

/**
 * It is invoked when making transaction
 * @param arg is the data that was passed in from the flutter side to make payment
 */
private fun makePayment(call: MethodCall) {

// if (call.arguments == null || !(call.arguments is Map<,>)) { // result?.error(ERROR_CODE_PAYMENT_INITIALIZATION, "Invalid input(s)", null) // return // }

    val intent = Intent(binding.activity, Showcard::class.java)

// startActivityForResult(binding.activity,intent, RequestCode, null) binding.activity.startActivityForResult(intent, RequestCode)

}

/**
 * dispose the channel when this handler detaches from the activity
 */
fun dispose() {
    channel?.setMethodCallHandler(null)
    channel = null
}

/**
 * this is the call back that is invoked when the activity result returns a value after calling
 * startActivityForResult().
 * @param requestCode if it matches with our [REQUEST_CODE] it means the result if the one we
 * asked for.
 * @param resultCode, it is okay if it equals [RESULT_OK]
 */
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean {
    Log.e("Tolu", "startcard: " + data?.getStringExtra("product"))
    if (resultCode == Activity.RESULT_OK) {
        when(requestCode) {
            RequestCode -> {
                Log.e("Tolu", "startcard: " + data?.getStringExtra("product"))
                result?.success(data?.getStringExtra("product"))
            }
            // Other result codes
            else -> {
            }
        }

    }else{
        Log.e("Tolu", "onActivityResult: error", )
    }
    return true
}

}

odejinmi avatar Feb 09 '23 06:02 odejinmi

Snippets from my code that worked:

class MyScannerPlugin : FlutterPlugin, ActivityAware, MethodCallHandler, ActivityResultListener,
    StreamHandler {

private var mActivityBinding: ActivityPluginBinding? = null

...

override fun onAttachedToActivity(binding: ActivityPluginBinding) {
        mActivityBinding = binding
        setupActivity(mActivityBinding?.activity!!)

        activity = mActivityBinding?.activity!! as FlutterActivity
    }

...

fun setupActivity(activity: Activity) {
        mActivityBinding?.addActivityResultListener(this) //without this onActivityResult will not be called
    }

...
}

rexmihaela avatar Aug 22 '23 17:08 rexmihaela

` @Override public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { channel.setMethodCallHandler(null); if (usbPrinter != null) { usbPrinter.releaseResources(); } }

// Implement the methods of the ActivityAware interface @Override public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) { onActivity(binding); // updateBluetoothPrinterActivity(); }

@Override public void onDetachedFromActivityForConfigChanges() { onDetach(); // updateBluetoothPrinterActivity(); }

@Override public void onReattachedToActivityForConfigChanges( @NonNull ActivityPluginBinding binding ) { onActivity(binding); // updateBluetoothPrinterActivity(); }

@Override public void onDetachedFromActivity() { onDetach(); // updateBluetoothPrinterActivity(); }

private void onActivity(ActivityPluginBinding binding) { activityPluginBinding = binding; activity = activityPluginBinding.getActivity(); activityPluginBinding.addActivityResultListener(this); }

private void onDetach() { if (activityPluginBinding != null) { activityPluginBinding.removeActivityResultListener(this ); } activityPluginBinding = null; activity = null; }`

humam-alBasha avatar Sep 11 '23 20:09 humam-alBasha

I am facing same issue, I found even after we registrar.addActivityResultListener(this) on the plugin, still if you go to the MainActivity of the project which uses our plugin and add an override for onActivityResult, you will find the result reaching there , instead of listener in the plugin class object. Need some help here.

Don't forget to call super.onActivityResult(requestCode, resultCode, data) first from the main app's onActivityResult(), that's the solution. You still need to make sure there are no ID clashes.

deakjahn avatar Nov 24 '24 12:11 deakjahn