Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
## 2.0.2

* Android: Restore support for the legacy v1 embedding via `registerWith` and
guard lifecycle cleanup to avoid crashes when activities change.
* Android: Remove the strict `FlutterActivity` cast so the plugin works with
`FlutterFragmentActivity` and other activity types.

## 2.0.1

* Guard against unsupported platforms and provide clearer errors instead of
`MissingPluginException` when the plugin is invoked outside Android/iOS.
* Document platform support expectations in the README.

## 2.0.0

* Android: Fixes #137 and #132
Expand Down
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,20 @@ A plugin for Flutter apps that adds barcode scanning support on both Android and

![Demo gif](https://github.com/AmolGangadhare/MyProfileRepo/blob/master/flutter_barcode_scanning_demo.gif "Demo")

## Platform support

> **Note**
> `flutter_barcode_scanner` only provides native implementations for **Android** and
> **iOS**. Invoking the plugin on the web, macOS, Windows, Linux, or Fuchsia now
> throws a descriptive `PlatformException` instead of the previous
> `MissingPluginException`. If you see this message, run the sample on a real or
> emulated mobile device.

> **Android embedding**
> Apps that still rely on the legacy (pre-1.12) Android embedding are fully
> supported again starting with **v2.0.2**. After upgrading, run `flutter clean`
> and rebuild so Gradle picks up the refreshed plugin registration code.


## Try example
Just clone or download the repository, open the project in `Android Studio/ VS Code`, open `pubspec.yaml` and click on `Packages get`.
Expand Down
4 changes: 2 additions & 2 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ version '1.0-SNAPSHOT'
buildscript {
repositories {
google()
jcenter()
mavenCentral()
}

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
Expand All @@ -14,29 +15,31 @@
import com.google.android.gms.common.api.CommonStatusCodes;
import com.google.android.gms.vision.barcode.Barcode;

import java.lang.reflect.Method;
import java.util.Map;

import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.embedding.engine.plugins.lifecycle.FlutterLifecycleAdapter;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.EventChannel.StreamHandler;
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.ActivityResultListener;
import io.flutter.plugin.common.EventChannel.StreamHandler;
import io.flutter.embedding.engine.plugins.lifecycle.FlutterLifecycleAdapter;

/**
* FlutterBarcodeScannerPlugin
*/
public class FlutterBarcodeScannerPlugin implements MethodCallHandler, ActivityResultListener, StreamHandler, FlutterPlugin, ActivityAware {
private static final String CHANNEL = "flutter_barcode_scanner";
private static final String CHANNEL = "flutter_barcode_scanner";
private static final String LEGACY_REGISTRAR_KEY = "com.amolg.flutterbarcodescanner.FlutterBarcodeScannerPlugin";

private static FlutterActivity activity;
private static Activity activity;
private static Result pendingResult;
private Map<String, Object> arguments;

Expand All @@ -46,20 +49,57 @@ public class FlutterBarcodeScannerPlugin implements MethodCallHandler, ActivityR
public static boolean isShowFlashIcon = false;
public static boolean isContinuousScan = false;
static EventChannel.EventSink barcodeStream;
private EventChannel eventChannel;
private EventChannel eventChannel;

private MethodChannel channel;
private FlutterPluginBinding pluginBinding;
private ActivityPluginBinding activityBinding;
private Application applicationContext;
private Lifecycle lifecycle;
private LifeCycleObserver observer;
private boolean isLifecycleObserverAdded = false;
private boolean isApplicationObserverAdded = false;

public FlutterBarcodeScannerPlugin() {
}

private FlutterBarcodeScannerPlugin(Activity activity) {
FlutterBarcodeScannerPlugin.activity = (FlutterActivity) activity;
@SuppressWarnings("deprecation")
public static void registerWith(PluginRegistry registry) {
if (registry == null) {
Log.w(TAG, "FlutterBarcodeScannerPlugin registration skipped: registry is null.");
return;
}

final Object registrar = getLegacyRegistrar(registry);
if (registrar == null) {
Log.w(TAG, "FlutterBarcodeScannerPlugin registration skipped: registrar unavailable.");
return;
}

final BinaryMessenger messenger = invokeRegistrarMethod(registrar, "messenger", BinaryMessenger.class);
final Activity legacyActivity = invokeRegistrarMethod(registrar, "activity", Activity.class);
final Context legacyContext = invokeRegistrarMethod(registrar, "context", Context.class);
Application application = null;
if (legacyActivity != null) {
application = legacyActivity.getApplication();
}
if (application == null && legacyContext instanceof Application) {
application = (Application) legacyContext;
} else if (application == null && legacyContext != null) {
final Context appContext = legacyContext.getApplicationContext();
if (appContext instanceof Application) {
application = (Application) appContext;
}
}

if (messenger == null || legacyActivity == null || application == null) {
Log.w(TAG, "FlutterBarcodeScannerPlugin registration skipped: missing legacy dependencies.");
return;
}

FlutterBarcodeScannerPlugin plugin = new FlutterBarcodeScannerPlugin();
plugin.createPluginSetup(messenger, application, legacyActivity, null);
plugin.registerLegacyActivityResultListener(registrar);
}

@Override
Expand Down Expand Up @@ -98,6 +138,12 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {

private void startBarcodeScannerActivityView(String buttonText, boolean isContinuousScan) {
try {
if (activity == null) {
Log.e(TAG, "Activity reference is null. Cannot start scanner view.");
pendingResult.success("-1");
return;
}

Intent intent = new Intent(activity, BarcodeCaptureActivity.class).putExtra("cancelButtonText", buttonText);
if (isContinuousScan) {
activity.startActivity(intent);
Expand Down Expand Up @@ -152,7 +198,7 @@ public void onCancel(Object o) {

public static void onBarcodeScanReceiver(final Barcode barcode) {
try {
if (barcode != null && !barcode.displayValue.isEmpty()) {
if (barcode != null && !barcode.displayValue.isEmpty() && activity != null) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
Expand Down Expand Up @@ -185,36 +231,45 @@ public void onReattachedToActivityForConfigChanges(ActivityPluginBinding binding
onAttachedToActivity(binding);
}

private void createPluginSetup(
private void createPluginSetup(
final BinaryMessenger messenger,
final Application applicationContext,
final Activity activity,
final ActivityPluginBinding activityBinding) {

this.activity = (FlutterActivity) activity;
if (activity == null) {
Log.e(TAG, "Unable to start flutter_barcode_scanner: activity is null");
return;
}

FlutterBarcodeScannerPlugin.activity = activity;
eventChannel = new EventChannel(messenger, "flutter_barcode_scanner_receiver");
eventChannel.setStreamHandler(this);

this.applicationContext = applicationContext;
channel = new MethodChannel(messenger, CHANNEL);
channel.setMethodCallHandler(this);

observer = new LifeCycleObserver(activity);
if (activityBinding != null) {
activityBinding.addActivityResultListener(this);
lifecycle = FlutterLifecycleAdapter.getActivityLifecycle(activityBinding);
observer = new LifeCycleObserver(activity);
lifecycle.addObserver(observer);
isLifecycleObserverAdded = true;
} else if (applicationContext != null) {
applicationContext.registerActivityLifecycleCallbacks(observer);
isApplicationObserverAdded = true;
}
}

@Override
public void onAttachedToActivity(ActivityPluginBinding binding) {
activityBinding = binding;
createPluginSetup(
pluginBinding.getBinaryMessenger(),
(Application) pluginBinding.getApplicationContext(),
activityBinding.getActivity(),
activityBinding);
activityBinding = binding;
createPluginSetup(
pluginBinding.getBinaryMessenger(),
(Application) pluginBinding.getApplicationContext(),
activityBinding.getActivity(),
activityBinding);
}

@Override
Expand All @@ -224,14 +279,35 @@ public void onDetachedFromActivity() {

private void clearPluginSetup() {
activity = null;
activityBinding.removeActivityResultListener(this);
activityBinding = null;
lifecycle.removeObserver(observer);

if (activityBinding != null) {
activityBinding.removeActivityResultListener(this);
activityBinding = null;
}

if (isLifecycleObserverAdded && lifecycle != null && observer != null) {
lifecycle.removeObserver(observer);
isLifecycleObserverAdded = false;
}

if (isApplicationObserverAdded && applicationContext != null && observer != null) {
applicationContext.unregisterActivityLifecycleCallbacks(observer);
isApplicationObserverAdded = false;
}

lifecycle = null;
channel.setMethodCallHandler(null);
eventChannel.setStreamHandler(null);
channel = null;
applicationContext.unregisterActivityLifecycleCallbacks(observer);
observer = null;

if (channel != null) {
channel.setMethodCallHandler(null);
channel = null;
}

if (eventChannel != null) {
eventChannel.setStreamHandler(null);
eventChannel = null;
}

applicationContext = null;
}

Expand Down Expand Up @@ -299,4 +375,46 @@ public void onActivityDestroyed(Activity activity) {
public void onActivityStopped(Activity activity) {
}
}

@SuppressWarnings("deprecation")
private static Object getLegacyRegistrar(PluginRegistry registry) {
if (registry == null) {
return null;
}
try {
Method method = registry.getClass().getMethod("registrarFor", String.class);
return method.invoke(registry, LEGACY_REGISTRAR_KEY);
} catch (Exception e) {
Log.e(TAG, "Unable to obtain legacy registrar", e);
return null;
}
}

private static <T> T invokeRegistrarMethod(Object registrar, String methodName, Class<T> clazz) {
if (registrar == null) {
return null;
}
try {
Method method = registrar.getClass().getMethod(methodName);
Object result = method.invoke(registrar);
if (clazz.isInstance(result)) {
return clazz.cast(result);
}
} catch (Exception e) {
Log.e(TAG, "Failed to invoke legacy registrar method " + methodName, e);
}
return null;
}

private void registerLegacyActivityResultListener(Object registrar) {
if (registrar == null) {
return;
}
try {
Method method = registrar.getClass().getMethod("addActivityResultListener", ActivityResultListener.class);
method.invoke(registrar, this);
} catch (Exception e) {
Log.e(TAG, "Failed to attach legacy activity result listener", e);
}
}
}
32 changes: 32 additions & 0 deletions example/ios/Flutter/ephemeral/flutter_lldb_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#
# Generated file, do not edit.
#

import lldb

def handle_new_rx_page(frame: lldb.SBFrame, bp_loc, extra_args, intern_dict):
"""Intercept NOTIFY_DEBUGGER_ABOUT_RX_PAGES and touch the pages."""
base = frame.register["x0"].GetValueAsAddress()
page_len = frame.register["x1"].GetValueAsUnsigned()

# Note: NOTIFY_DEBUGGER_ABOUT_RX_PAGES will check contents of the
# first page to see if handled it correctly. This makes diagnosing
# misconfiguration (e.g. missing breakpoint) easier.
data = bytearray(page_len)
data[0:8] = b'IHELPED!'

error = lldb.SBError()
frame.GetThread().GetProcess().WriteMemory(base, data, error)
if not error.Success():
print(f'Failed to write into {base}[+{page_len}]', error)
return

def __lldb_init_module(debugger: lldb.SBDebugger, _):
target = debugger.GetDummyTarget()
# Caveat: must use BreakpointCreateByRegEx here and not
# BreakpointCreateByName. For some reasons callback function does not
# get carried over from dummy target for the later.
bp = target.BreakpointCreateByRegex("^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$")
bp.SetScriptCallbackFunction('{}.handle_new_rx_page'.format(__name__))
bp.SetAutoContinue(True)
print("-- LLDB integration loaded --")
5 changes: 5 additions & 0 deletions example/ios/Flutter/ephemeral/flutter_lldbinit
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#
# Generated file, do not edit.
#

command script import --relative-to-command-file flutter_lldb_helper.py
Loading