I am developing an inventory management app in xamarin forms using Zebra TC20 model. Right now I am able to retrieve barcode data using datawedge. Now I am trying to disable and enable barcode scanner using datawedge as well. I have surfed and found out we can broadcast to datawedge for enable and disable the barcode scanner. Now I am having the issue when I want to enable or disable the the scanner from a page I am retrieving an error message Java.Lang.NullPointerException: 'Attempt to invoke virtual method 'void android.content.Context.sendBroadcast(android.content.Intent)' on a null object reference . The scanner functions are implemented in Android file. I am using dependency service to call the scanner functions. I have attached the code I have done.
MainActivity.cs
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity, IKeyboardListener, IScannerConnection
{
private static string ACTION_DATAWEDGE_FROM_6_2 = "com.symbol.datawedge.api.ACTION";
private static string EXTRA_CREATE_PROFILE = "com.symbol.datawedge.api.CREATE_PROFILE";
private static string EXTRA_SET_CONFIG = "com.symbol.datawedge.api.SET_CONFIG";
private static string EXTRA_PROFILE_NAME = "Barcode Scan";
private DataWedgeReceiver _broadcastReceiver = null;
protected override void OnCreate(Bundle bundle)
{
base.Window.RequestFeature(WindowFeatures.ActionBar);
// Name of the MainActivity theme you had there before.
// Or you can use global::Android.Resource.Style.ThemeHoloLight
base.SetTheme(Resource.Style.MainTheme);
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(bundle);
DependencyService.Register<ToastNotification>(); // Register your dependency
ToastNotification.Init(this);
var container = new SimpleContainer(); // Create a SimpleCOntainer
container.Register<IGeolocator, Geolocator>(); // Register the Geolocator
container.Register<IDevice>(t => AndroidDevice.CurrentDevice); // Register the Device
Resolver.ResetResolver(); // Reset the resolver
Resolver.SetResolver(container.GetResolver()); // Resolve it
FFImageLoading.Forms.Platform.CachedImageRenderer.Init(enableFastRenderer: true);
UserDialogs.Init(this);
Rg.Plugins.Popup.Popup.Init(this, bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
global::ZXing.Net.Mobile.Forms.Android.Platform.Init();
_broadcastReceiver = new DataWedgeReceiver();
App inventoryApp = new App(OnSaveSignature);
_broadcastReceiver.scanDataReceived += (s, scanData) =>
{
MessagingCenter.Send<App, string>(inventoryApp, "ScanBarcode", scanData);
};
CreateProfile();
//DependencyService.Register<ToastNotification>();
//ToastNotification.Init(this, new PlatformOptions() { SmallIconDrawable = Android.Resource.Drawable.IcDialogInfo });
LoadApplication(inventoryApp);
}
public override void OnBackPressed()
{
if (Rg.Plugins.Popup.Popup.SendBackPressed(base.OnBackPressed))
{
// Do something if there are some pages in the `PopupStack`
}
else
{
// Do something if there are not any pages in the `PopupStack`
}
}
public override bool OnOptionsItemSelected(IMenuItem item)
{
// check if the current item id
// is equals to the back button id
if (item.ItemId == 16908332 && !RFIDStockCountViewModel.IsPosted && RFIDStockCountViewModel.IsStockCounting)
{
return false;
/*
if (Xamarin.Forms.Application.Current.MainPage.Navigation.NavigationStack.Count > 0)
{
//LIFO is the only game in town! - so send back the last page
int index = Xamarin.Forms.Application.Current.MainPage.Navigation.NavigationStack.Count - 1;
var currPage = (CustomContentPage)Xamarin.Forms.Application.Current.MainPage.Navigation.NavigationStack[index];
// check if the page has subscribed to
// the custom back button event
if (currPage?.CustomBackButtonAction != null)
{
// invoke the Custom back button action
currPage?.CustomBackButtonAction.Invoke();
// and disable the default back button action
return false;
}
}
// if its not subscribed then go ahead
// with the default back button action
return base.OnOptionsItemSelected(item);
*/
}
else
{
// since its not the back button
//click, pass the event to the base
return base.OnOptionsItemSelected(item);
}
}
private async Task<bool> OnSaveSignature(Stream bitmap, string filename)
{
var path = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryPictures).AbsolutePath;
var file = Path.Combine(path, "signature.png");
using (var dest = File.OpenWrite(file))
{
await bitmap.CopyToAsync(dest);
}
return true;
}
//public override void OnBackPressed()
//{
// if (Xamarin.Forms.Application.Current.MainPage.Navigation.NavigationStack.Count > 0)
// {
// //LIFO is the only game in town! - so send back the last page
// int index = Xamarin.Forms.Application.Current.MainPage.Navigation.NavigationStack.Count - 1;
// var currentpage = (CustomContentPage)Xamarin.Forms.Application.Current.MainPage.Navigation.NavigationStack[index];
// // check if the page has subscribed to
// // the custom back button event
// if (currentpage?.CustomBackButtonAction != null)
// {
// currentpage?.CustomBackButtonAction.Invoke();
// }
// else
// {
// base.OnBackPressed();
// }
// }
//}
public override bool OnKeyDown(Keycode keyCode, KeyEvent e)
{
if (e.KeyCode.GetHashCode() == 139 || e.KeyCode.GetHashCode() == 280)
{
MessagingCenter.Send<IKeyboardListener, string>(this, "KeyboardListener", "TRUE");
}
return base.OnKeyDown(keyCode, e);
}
public override bool OnKeyUp(Keycode keyCode, KeyEvent e)
{
if (e.KeyCode.GetHashCode() == 139 || e.KeyCode.GetHashCode() == 280)
{
MessagingCenter.Send<IKeyboardListener, string>(this, "KeyboardListener", "FALSE");
}
return base.OnKeyDown(keyCode, e);
}
protected override void OnResume()
{
base.OnResume();
if (null != _broadcastReceiver)
{
// Register the broadcast receiver
IntentFilter filter = new IntentFilter(DataWedgeReceiver.IntentAction);
filter.AddCategory(DataWedgeReceiver.IntentCategory);
Android.App.Application.Context.RegisterReceiver(_broadcastReceiver, filter);
}
}
protected override void OnPause()
{
if (null != _broadcastReceiver)
{
// Unregister the broadcast receiver
Android.App.Application.Context.UnregisterReceiver(_broadcastReceiver);
}
base.OnStop();
}
private void CreateProfile()
{
String profileName = EXTRA_PROFILE_NAME;
SendDataWedgeIntentWithExtra(ACTION_DATAWEDGE_FROM_6_2, EXTRA_CREATE_PROFILE, profileName);
// Now configure that created profile to apply to our application
Bundle profileConfig = new Bundle();
profileConfig.PutString("PROFILE_NAME", EXTRA_PROFILE_NAME);
profileConfig.PutString("PROFILE_ENABLED", "true"); // Seems these are all strings
profileConfig.PutString("CONFIG_MODE", "CREATE_IF_NOT_EXIST");
Bundle barcodeConfig = new Bundle();
barcodeConfig.PutString("PLUGIN_NAME", "BARCODE");
//barcodeConfig.PutString("RESET_CONFIG", "true"); // This is the default but never hurts to specify
Bundle barcodeProps = new Bundle();
barcodeProps.PutString("scanner_input_enabled", "true"); // This is the default but never hurts to specify
barcodeProps.PutString("scanner_selection_by_identifier", "AUTO ");
barcodeProps.PutString("scanner_selection", "auto ");
barcodeProps.PutString("aim_mode", "off ");
barcodeProps.PutString("illumination_mode", "off ");
barcodeConfig.PutBundle("PARAM_LIST", barcodeProps);
profileConfig.PutBundle("PLUGIN_CONFIG", barcodeConfig);
Bundle appConfig = new Bundle();
appConfig.PutString("PACKAGE_NAME", this.PackageName); // Associate the profile with this app
appConfig.PutStringArray("ACTIVITY_LIST", new String[] { "*" });
profileConfig.PutParcelableArray("APP_LIST", new Bundle[] { appConfig });
SendDataWedgeIntentWithExtra(ACTION_DATAWEDGE_FROM_6_2, EXTRA_SET_CONFIG, profileConfig);
// You can only configure one plugin at a time, we have done the barcode input, now do the intent output
profileConfig.Remove("PLUGIN_CONFIG");
Bundle intentConfig = new Bundle();
intentConfig.PutString("PLUGIN_NAME", "INTENT");
intentConfig.PutString("RESET_CONFIG", "true");
Bundle intentProps = new Bundle();
intentProps.PutString("intent_output_enabled", "true");
intentProps.PutString("intent_action", DataWedgeReceiver.IntentAction);
intentProps.PutString("intent_delivery", "2");
intentConfig.PutBundle("PARAM_LIST", intentProps);
profileConfig.PutBundle("PLUGIN_CONFIG", intentConfig);
SendDataWedgeIntentWithExtra(ACTION_DATAWEDGE_FROM_6_2, EXTRA_SET_CONFIG, profileConfig);
}
private void SendDataWedgeIntentWithExtra(String action, String extraKey, Bundle extras)
{
Intent dwIntent = new Intent();
dwIntent.SetAction(action);
dwIntent.PutExtra(extraKey, extras);
SendBroadcast(dwIntent);
}
private void SendDataWedgeIntentWithExtra(String action, String extraKey, String extraValue)
{
Intent dwIntent = new Intent();
dwIntent.SetAction(action);
dwIntent.PutExtra(extraKey, extraValue);
SendBroadcast(dwIntent);
}
public void SendScannerEnable()
{
SendDataWedgeIntentWithExtra(ACTION_DATAWEDGE_FROM_6_2, EXTRA_SET_CONFIG, "ENABLE_PLUGIN");
}
public void SendScannerDisable()
{
SendDataWedgeIntentWithExtra(ACTION_DATAWEDGE_FROM_6_2, EXTRA_SET_CONFIG, "DISABLE_PLUGIN");
}
}
}
Snip from Page.cs
protected override void OnAppearing()
{
DependencyService.Get<IScannerConnection>().SendScannerEnable();
MessagingCenter.Subscribe<App, string>(this, "ScanBarcode", (sender, arg) =>
{
ScanBarcode(arg);
});
base.OnAppearing();
}
I have searched through internet but I didn't get any resource on how to fix it.
Thank you.
The url below helped me to fix my problem. I had to create a new scanner class which enables and disables datawedge profile by triggering events. https://developer.zebra.com/community/home/blog/2018/07/11/xamarinforms-freshmvvm-datawedge-take-2
Related
I got an issue with my Android program when updating user data on Fragment_profile. Fragment_Profile loads User profile data and contains an update button in which the button navigates to an activity called EditProfileActivity. Users can technically update the data but, after they save the updated data, the data still can't be updated. The old data still appear. I try to refresh the fragment by using OnResume() and add OnRestart on my EditProfileActivity.cs and OnPause both in fragment and activity but, still nothing. I am using Xamarin Android and develop it with C#. For more info, you can see what happened in the GIF
My issue
So far, I've tried to code, and here's my code.
Fragment_profile.cs
public class Fragment_Profile : Android.Support.V4.App.Fragment
{
public HomePageActivity m_currentActivity;
public TextView m_tv_loginname, m_tv_username, m_tv_fullname , m_tv_dob;
public Boolean isRefreshing = false;
public override void OnCreate(Bundle aSavedInstanceState)
{
base.OnCreate(aSavedInstanceState);
}
//public static Fragment_Profile NewInstance(Model.User aCurrentUser)
public static Fragment_Profile NewInstance()
{
var _frag4 = new Fragment_Profile { Arguments = new Bundle() };
return _frag4;
}
public override View OnCreateView(LayoutInflater aInflater, ViewGroup aContainer, Bundle aSavedInstanceState)
{
var _ignored = base.OnCreateView(aInflater, aContainer, aSavedInstanceState);
var view= aInflater.Inflate(Resource.Layout.FragmentProfile, null);
m_tv_loginname = view.FindViewById<TextView>(Resource.Id.tv_loginname);
m_tv_username = view.FindViewById<TextView>(Resource.Id.tv_userEmail);
m_tv_fullname = view.FindViewById<TextView>(Resource.Id.tv_fullname);
m_tv_dob = view.FindViewById<TextView>(Resource.Id.tv_dob);
Button _updateProfile = view.FindViewById<Button>(Resource.Id.btnUpdateProfile);
_updateProfile.Click += _updateProfile_Click;
m_currentActivity = (HomePageActivity)this.Activity;
if (m_currentActivity.CurrentUser != null)
{
//string = "Welcome, " + m_currentActivity.CurrentUser.UserName;
m_tv_loginname.Text = m_currentActivity.CurrentUser.LoginName;
m_tv_fullname.Text = m_currentActivity.CurrentUser.UserName;
m_tv_username.Text = m_currentActivity.CurrentUser.UserEmail;
m_tv_dob.Text = m_currentActivity.CurrentUser.DateOfBirth;
}
else
{
Toast.MakeText(Activity, "The data is not found!", ToastLength.Short).Show();
Intent i = new Intent(Context, typeof(MainActivity));
StartActivity(i);
this.Activity.Finish();
}
return view;
}
private void _updateProfile_Click(object sender, EventArgs e)
{
Intent i = new Intent(Context, typeof(EditProfileActivity));
i.PutExtra("loginname", m_currentActivity.CurrentUser.LoginName);
i.PutExtra("fullname", m_currentActivity.CurrentUser.UserName);
i.PutExtra("useremail", m_currentActivity.CurrentUser.UserEmail);
i.PutExtra("dob", m_currentActivity.CurrentUser.DateOfBirth);
StartActivity(i);
}
public override void OnResume()
{
base.OnResume();
if (isRefreshing)
{
Fragment fragment = new Fragment_Profile();
Android.Support.V4.App.FragmentManager fragmentMg = Activity.SupportFragmentManager;
FragmentTransaction fragmentTrans = fragmentMg.BeginTransaction();
fragmentTrans.Replace(Resource.Id.content_frame, fragment);
fragmentTrans.Detach(fragment);
fragmentTrans.Attach(fragment);
fragmentTrans.Commit();
//adapter.notifyDataSetChanged();
}
}
public override void OnPause()
{
base.OnPause();
isRefreshing = true;
}
}
When user click the _updateProfile button It will reference to next activity which is EditProfileActivity. Here's my EditProfileActivity.cs
public class EditProfileActivity : Activity, IOnDateSetListener
{
public EditText m_editFullName, m_editUsername, m_dob;
public TextView m_tvEmail;
public Button m_btnUpdate;
public Boolean isRefreshing = false;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.EditProfile);
m_editFullName = FindViewById<EditText>(Resource.Id.et_editfullName);
m_editUsername = FindViewById<EditText>(Resource.Id.et_editUserName);
m_tvEmail = FindViewById<TextView>(Resource.Id.tv_Email);
m_dob = FindViewById<EditText>(Resource.Id.et_editDob);
m_btnUpdate = FindViewById<Button>(Resource.Id.btn_updateprofile);
m_btnUpdate.Click += _btnUpdate_Click;
m_dob.Click += _dob_Click;
Bundle extras = Intent.Extras;
m_editFullName.Text = extras.GetString("loginname");
m_editUsername.Text = extras.GetString("fullname");
m_tvEmail.Text = extras.GetString("useremail");
m_dob.Text = extras.GetString("dob");
}
private void _btnUpdate_Click(object sender, EventArgs e)
{
try
{
Model.User _currentUser = Model.User.CheckEmailUser(m_tvEmail.Text);
_currentUser.UserName = m_editUsername.Text;
_currentUser.LoginName = m_editFullName.Text;
_currentUser.UserEmail = m_tvEmail.Text;
_currentUser.DateOfBirth = m_dob.Text;
var _updated = DBManager.Instance.Update(_currentUser);
if (_updated > 0)
{
Toast.MakeText(this, "Your account has been succesfully updated!", ToastLength.Long).Show();
Finish();
OnResume();
}
else
{
Toast.MakeText(this, "Failed to update!", ToastLength.Long).Show();
}
}
catch(Exception ex)
{
Toast.MakeText(this, ex.ToString(), ToastLength.Short).Show();
}
}
private void _dob_Click(object sender, EventArgs e)
{
var _dateTimeNow = DateTime.Now;
DatePickerDialog _datepicker = new DatePickerDialog(this, this
, _dateTimeNow.Year, _dateTimeNow.Month, _dateTimeNow.Day);
_datepicker.Show();
}
public void OnDateSet(DatePicker view, int year, int month, int dayOfMonth)
{
m_dob.Text = new DateTime(year, month + 1, dayOfMonth).ToShortDateString();
}
protected override void OnRestart()
{
base.OnRestart();
if (isRefreshing)
{
isRefreshing = false;
Finish();
}
}
protected override void OnPause()
{
base.OnPause();
if (!isRefreshing)
isRefreshing = true;
}
}
I also have read some articles that have the same problems as me but, It still confusing me and still same. I know it's a simple thing but, It took a few days for me because I am a newby and I still don't get the solution.
Do you guys have any ideas? Would you like to help me? If you don't mind please check my source code so, I know what I am missing. Thank in advance for your help!
In you Fragment, you can override the OnResume method. If you back to the fragment from Acitivty. you should query the new data from DB, then set the new value to the controls in Fragment.
public override void OnResume()
{
base.OnResume();
PHName.Text = photoDAO.GetFirstPhotos(Id).PhotoName;
}
Here is running GIF.
You can see this lifecycle about Fragment. Every time you display the Fragment,OnResume method will be executed. You get the newest data from DB, then set it to the Fragment page.
Here is my demo.
https://drive.google.com/file/d/11dROKS7TtqAaVYkG8w6ZKpqnQJRBD87E/view
I'm trying to port an app from Swift (iOS only) to C# in Visual Studio - and it's going (slightly) well. I'm having some Android troubles though (many of them - but only one for this question!)
The page loads correctly in the webview of the Android version of the app - but the Javascript doesn't execute until after the page has rendered, the result being that the App Store advert is displayed briefly before it disappears.
My iOS app works correctly - the source code for the iOS version is here:
public class PortalViewRenderer : ViewRenderer<PortalView, WKWebView>, IWKScriptMessageHandler, IWKNavigationDelegate {
private class NavigationDelegate : WKNavigationDelegate {
private readonly WeakReference<PortalViewRenderer> _webView;
public NavigationDelegate(PortalViewRenderer webView) {
_webView = new WeakReference<PortalViewRenderer>(webView);
}
public override void DidFinishNavigation(WKWebView webView, WKNavigation navigation) {
}
public override void DidStartProvisionalNavigation(WKWebView webView, WKNavigation navigation) {
NSUrl currentURL = webView.Url;
var current = Connectivity.NetworkAccess;
if (current != NetworkAccess.Internet) {
if (!(currentURL.AbsoluteString.Contains("file://"))) {
string noConnectionPath = Path.Combine(NSBundle.MainBundle.BundlePath, "Common/NoInternet.html");
webView.LoadRequest(new NSUrlRequest(NSUrl.FromString(noConnectionPath)));
}
}
}
public override void DidFailNavigation(WKWebView webView, WKNavigation navigation, NSError error) {
}
public override void DecidePolicy(WKWebView webView, WKNavigationAction navigationAction, Action<WKNavigationActionPolicy> decisionHandler) {
NSUrl url = navigationAction.Request.Url;
if (url != null) {
if (url.Host == "<website url>" || url.AbsoluteString.Contains("file://")) {
decisionHandler(WKNavigationActionPolicy.Allow);
} else if (url.AbsoluteString.Contains("<website url>/mydavylamp/timeout")) {
decisionHandler(WKNavigationActionPolicy.Cancel);
webView.LoadRequest(new NSUrlRequest(NSUrl.FromString(Element.Uri)));
} else {
UIApplication.SharedApplication.OpenUrl(url);
}
}
}
}
WKUserContentController userController;
protected override void OnElementChanged (ElementChangedEventArgs<PortalView> e) {
base.OnElementChanged (e);
var javaScriptFunction = System.IO.File.ReadAllText("Common/HideAppStoreAds.js");
if (Control == null) {
userController = new WKUserContentController();
var script = new WKUserScript(new NSString(javaScriptFunction), WKUserScriptInjectionTime.AtDocumentStart, false);
userController.AddUserScript(script);
userController.AddScriptMessageHandler(this, "invokeAction");
var config = new WKWebViewConfiguration { UserContentController = userController };
var webView = new WKWebView(Frame, config);
webView.BackgroundColor = UIKit.UIColor.FromRGB(0x11, 0x25, 0x43);
webView.ScrollView.BackgroundColor = webView.BackgroundColor;
webView.CustomUserAgent = "headbanger.davylamp.ios";
webView.ScrollView.ScrollEnabled = true;
webView.ScrollView.Bounces = false;
webView.AllowsBackForwardNavigationGestures = false;
webView.ContentMode = UIKit.UIViewContentMode.ScaleToFill;
webView.NavigationDelegate = new NavigationDelegate(this);
SetNativeControl(webView);
}
if (e.OldElement != null) {
userController.RemoveAllUserScripts();
userController.RemoveScriptMessageHandler("invokeAction");
var portalView = e.OldElement as PortalView;
portalView.Cleanup();
}
if (e.NewElement != null) {
Control.LoadRequest(new NSUrlRequest(NSUrl.FromString(Element.Uri)));
}
}
public void DidReceiveScriptMessage (WKUserContentController userContentController, WKScriptMessage message) {
Element.InvokeAction (message.Body.ToString ());
}
}
The Android version is as follows (it doesn't do so much yet, because I haven't worked out how to set policies etc. yet) but my main concern is that I can't get the Javascript to run at the correct time (as set on the iOS version using WKUserScriptInjectionTime which doesn't seem to have an Android equivalent)
public class JavascriptWebViewClient : WebViewClient {
string _javascript;
public JavascriptWebViewClient(string javascript) {
_javascript = javascript;
}
public override void OnPageFinished(WebView view, string url) {
base.OnPageFinished(view, url);
view.EvaluateJavascript(_javascript, null);
}
}
public class PortalViewRenderer : ViewRenderer<PortalView, Android.Webkit.WebView> {
Context _context;
public PortalViewRenderer(Context context) : base(context) {
_context = context;
}
protected override void OnElementChanged(ElementChangedEventArgs<PortalView> e) {
string javascriptFunction;
Android.Content.Res.AssetManager assets = _context.Assets;
using (StreamReader sr = new StreamReader(assets.Open("Common/HideAppStoreAds.js"))) {
javascriptFunction = sr.ReadToEnd();
}
base.OnElementChanged(e);
if (Control == null) {
var webView = new Android.Webkit.WebView(_context);
webView.Settings.JavaScriptEnabled = true;
webView.SetWebViewClient(new JavascriptWebViewClient($"javascript: {javascriptFunction}"));
SetNativeControl(webView);
}
if (e.OldElement != null) {
Control.RemoveJavascriptInterface("jsBridge");
var portalView = e.OldElement as PortalView;
portalView.Cleanup();
}
if (e.NewElement != null) {
Control.LoadUrl($"{Element.Uri}");
}
}
}
The (common) Javascript is as follows:
var styleTag = document.createElement("style");
styleTag.textContent = '.mobile-apps {display:none;}';
document.documentElement.appendChild(styleTag);
I've googled for the magic spell, but I can't seem to find any guides on how to build a webview for Android in C# - and particularly not for iOS developers!
As always, any help that anyone can provide will be gratefully received.
The answer, for my use case, seems to be as follows.
The Android documentation (https://developer.android.com/reference/android/webkit/WebViewClient) for onPageCommitVisible says that:
This callback can be used to determine the point at which it is safe to make a recycled WebView visible, ensuring that no stale content is shown. It is called at the earliest point at which it can be guaranteed that WebView#onDraw will no longer draw any content from previous navigations. The next draw will display either the WebView#setBackgroundColor of the WebView, or some of the contents of the newly loaded page.
To my mind, this means that the HTML has loaded (although not necessarily any other resources) and the page might start to be rendered (although, crucially, it won't be rendered until this callback completes.)
I used the following code:
public override void OnPageCommitVisible(WebView view, string url) {
view.EvaluateJavascript(_javascript, null);
base.OnPageCommitVisible(view, url);
}
and it seems to work correctly. I hope that this helps anyone else.
You need just to override OnPageCommitVisible method:
public override void OnPageCommitVisible(AWebView view, string url)
{
//Insert javascript injection first
view.EvaluateJavascript(_javascript, null);
base.OnPageCommitVisible(view, url);
}
It fires Certainly.
Have a good code!
I am building a Xamarin.Android application for a Zebra TC-70 Android device. My main activity runs the scanner just fine the first time through. It gets the data from the scanner and then passes data to another activity just fine. If I cancel the new activity or complete my work and return to the first activity, the scanner does not re-initialize. Here is my code:
[Activity(Label = "MyApp", MainLauncher = true, Icon = "#mipmap/icon",
ScreenOrientation = ScreenOrientation.Portrait)]
public class MainActivity : AppCompatActivity, EMDKManager.IEMDKListener
{
//EMDK
private BarcodeManager _barcodeManager;
private EMDKManager _emdkManager;
private EditText _scanBarcodeEditText;
private Scanner _scanner;
void EMDKManager.IEMDKListener.OnClosed()
{
if(_emdkManager != null)
{
_emdkManager.Release();
_emdkManager = null;
}
}
void EMDKManager.IEMDKListener.OnOpened(EMDKManager p0)
{
_emdkManager = p0;
InitScanner();
}
protected override void OnResume()
{
base.OnResume();
var intent = new Intent(this, typeof(FpmsDataService));
BindService(intent, _dataServiceConnection, Bind.AutoCreate);
_scanBarcodeEditText.Text = string.Empty;
InitScanner();
}
protected override void OnPause()
{
base.OnPause();
UnbindService(_dataServiceConnection);
DeinitScanner();
if (_emdkManager != null)
{
_emdkManager.Release();
_emdkManager = null;
}
}
protected override void OnDestroy()
{
base.OnDestroy();
DeinitScanner();
if(_emdkManager != null)
{
_emdkManager.Release();
_emdkManager = null;
}
Log.Information("Destroyed FirePMS MainActivity");
Log.CloseAndFlush();
}
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Window.SetFlags(WindowManagerFlags.KeepScreenOn, WindowManagerFlags.KeepScreenOn);
SetContentView(Resource.Layout.Main);
_scanBarcodeEditText = FindViewById<EditText>(Resource.Id.MaintenanceScanBarcode_EditText);
//Initialize Scanner
var results = EMDKManager.GetEMDKManager(Application.Context, this);
}
private void InitScanner()
{
if(_emdkManager != null)
{
if(_barcodeManager == null)
{
try
{
//Get the feature object such as BarcodeManager object for accessing the feature.
_barcodeManager = (BarcodeManager)_emdkManager.GetInstance(EMDKManager.FEATURE_TYPE.Barcode);
_scanner = _barcodeManager.GetDevice(BarcodeManager.DeviceIdentifier.Default);
if (_scanner != null)
{
//Attach the Data Event handler to get the data callbacks.
_scanner.Data += Scanner_Data;
_scanner.Status += Scanner_Status;
_scanner.Enable();
}
else
{
Log.Error("Failed to enable scanner");
}
}
catch (ScannerException e)
{
Log.Error(e.Message);
}
catch (Exception ex)
{
Log.Error(ex.Message);
}
}
}
}
private void Scanner_Status(object sender, Scanner.StatusEventArgs e)
{
var state = e.P0.State;
if(state == StatusData.ScannerStates.Idle)
{
try
{
if (_scanner.IsEnabled &&
!_scanner.IsReadPending)
{
SetScannerConfig();
_scanner.Read();
}
}
catch (ScannerException e1)
{
Log.Error(e1.Message);
}
}
}
private void DeinitScanner()
{
if(_emdkManager != null)
{
if(_scanner != null)
{
try
{
_scanner.CancelRead();
_scanner.Disable();
_scanner.Data -= Scanner_Data;
_scanner.Status -= Scanner_Status;
_scanner.Release();
}
catch (ScannerException e)
{
Log.Error(e.Result.Description);
}
}
}
if (_barcodeManager != null)
{
_emdkManager.Release(EMDKManager.FEATURE_TYPE.Barcode);
}
_barcodeManager = null;
_scanner = null;
}
private void SetScannerConfig()
{
var config = _scanner.GetConfig();
config.SkipOnUnsupported = ScannerConfig.SkipOnUnSupported.None;
config.ScanParams.DecodeLEDFeedback = true;
config.ReaderParams.ReaderSpecific.ImagerSpecific.PicklistEx = ScannerConfig.PicklistEx.Hardware;
config.DecoderParams.Code39.Enabled = true;
config.DecoderParams.Code128.Enabled = false;
_scanner.SetConfig(config);
}
private void Scanner_Data(object sender, Scanner.DataEventArgs e)
{
var scanDataCollection = e.P0;
if((scanDataCollection != null) && (scanDataCollection.Result == ScannerResults.Success))
{
var scanData = scanDataCollection.GetScanData();
if (scanData[0].Data == null)
{
return;
}
RunOnUiThread(() => _scanBarcodeEditText.Text = scanData[0].Data);
RunOnUiThread(ProcessScan);
}
}
private void ProcessScan()
{
if (string.IsNullOrEmpty(_scanBarcodeEditText.Text))
{
Toast.MakeText(this, "You must scan or enter a barcode to begin", ToastLength.Long).Show();
return;
}
else
{
var intent = new Intent(this, typeof(SecondActivity));
intent.PutExtra("ScannedData",_scanBarcodeEditText.Text);
StartActivity(intent)
}
}
}
Any suggestions would be greatly appreciated. As I indicated, the process works just fine the first time, it's when I return to this activity that the scanner is no longer initialized and doesn't come back.
I suspect the EMDK is not being properly released before the activity is restarted. If you take a look at the following article, though using Java the principle is the same and the advice there is to release the EMDK in onStop(). See the note about managing the EMDK instance about half way down.
I'm developing an Android C# Application using Xamarin. It has a very simple structure: the MainActivity launches and creates instances of classes GUIHandler and Connection. Each class raises different events:
In Connection.cs:
public EventHandler<ConnectionEventArgs> ConnectionMade;
protected virtual void OnConnectionMade()
{
if (ConnectionMade != null) {
ConnectionMade (this, new ConnectionEventArgs ());
}
}
public EventHandler<ConnectionEventArgs> ConnectionFailed;
protected virtual void OnConnectionFailed()
{
if (ConnectionFailed != null) {
ConnectionFailed (this, new ConnectionEventArgs ());
}
}
public EventHandler<ConnectionEventArgs> LostConnection;
protected virtual void OnLostConnection()
{
if (LostConnection != null) {
LostConnection (this, new ConnectionEventArgs ());
}
}
public EventHandler<ConnectionEventArgs> ReceivedData;
protected virtual void OnReceivedData(byte[] _byteDataReceived, int _byteDataReceivedLength)
{
if (ReceivedData != null) {
ReceivedData (this, new ConnectionEventArgs {byteDataReceived = _byteDataReceived, byteDataReceivedLength = _byteDataReceivedLength});
}
}
public EventHandler<ConnectionEventArgs> SentData;
protected virtual void OnSentData()
{
if (SentData != null) {
SentData (this, new ConnectionEventArgs ());
}
}
In GUIHandler.cs:
public EventHandler<GUIHandlerEventArgs> SendButtonClicked;
protected virtual void OnSendButtonClicked()
{
if (SendButtonClicked != null) {
SendButtonClicked (this, new GUIHandlerEventArgs { sendTextEditData = sendEditText.Text });
}
}
The subscriptions are in the MainActivity.cs file, which gets run when the application is executed:
...
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
SetContentView (Resource.Layout.Main);
var guiHandler = new GUIHandler();
var connection = new Connection();
connection.ConnectionMade += guiHandler.OnConnectionMade;
connection.ConnectionFailed += guiHandler.OnConnectionFailed;
connection.LostConnection += guiHandler.OnLostConnection;
connection.ReceivedData += guiHandler.OnReceivedData;
connection.SentData += guiHandler.OnSentData;
// THE CODE BELOW RAISES System.ArgumentException: Value does not fall within expected range DURING RUNTIME.
guiHandler.SendButtonClicked += connection.OnSendButtonClicked;
// However, this line makes the job.
guiHandler.SendButtonClicked += (sender, args) => connection.Send (args.sendTextEditData);
}
I can't find the reason why it raises that Exception there and does not anywhere else. However, doing some debugging I noticed I can't subscribe a public method from connection, but have no idea why. Anyone has any idea?
Edit: this is the definition of OnSendButtonClicked in Connection.cs:
public void OnSendButtonClicked (object sender, GUIHandlerEventArgs args)
{
//Send data
Send(args.sendTextEditData);
}
How do I load a url from firstActivity to the webpageActivity?
I would like to be able to click a button with the url from firstActivity then pass it to the webpage activity and load the url.
Here is my code: FirstActivity
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
// Set our view from the "main" layout resource
SetContentView (Resource.Layout.Main);
// Get our button from the layout resource,
// and attach an event to it
var scisnews = FindViewById<Button> (Resource.Id.scisnewsbtn);
string scisnewsurl = "http://cis.ulster.ac.uk/news-a-events-mainmenu-70";
//labinduction.Click += (sender, e) => {
// var LabInductionI = new Intent (this, typeof(LabInduction));
// StartActivity (LabInductionI);
//};
scisnews.Click += delegate {
var ScisNewsI = new Intent (this, typeof(WebPage));
ScisNewsI.PutExtra ("scisnews", scisnewsurl);
this.StartActivity (ScisNewsI);
};
}
public class HelloWebViewClient : WebViewClient
{
public override bool ShouldOverrideUrlLoading (WebView view, string url)
{
view.LoadUrl ("http://cis.ulster.ac.uk/news-a-events-mainmenu-70");
return true;
}
}
}
Code: WebPageActivity
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
// Create your application here
SetContentView (Resource.Layout.WebPageLO);
web_view = FindViewById<WebView> (Resource.Id.webview);
web_view.Settings.JavaScriptEnabled = true;
web_view.Settings.BuiltInZoomControls = true;
web_view.SetWebViewClient (new HelloWebViewClient ());
}
}
public class HelloWebViewClient : WebViewClient
{
public override bool ShouldOverrideUrlLoading (WebView view, string url)
{
view.LoadUrl ("http://cis.ulster.ac.uk/news-a-events-mainmenu-70");
return true;
}
}
public override bool OnKeyDown (Android.Views.Keycode keyCode, Android.Views.KeyEvent e)
{
if (keyCode == Keycode.Back && web_view.CanGoBack ())
{
web_view.GoBack ();
return true;
}
return base.OnKeyDown (keyCode, e);
}
}
You generally pass information from one activity to another by using the Intent class. It looks like you are trying to, but don't have the code pulling the value out.
Here is an example:
//In your first activity
var intent = new Intent(this, typeof(WebPage));
intent.PutExtra("url", "http://cis.ulster.ac.uk/news-a-events-mainmenu-70");
StartActivity(intent);
//Then in the second activity
string url = Intent.GetStringExtra("url");
//Then you can load the page like this
web_view.LoadUrl(url);
Does this help?