How can i integrate QR-reader in Weex App? - qr-code

React.Native has plugins like react-native-qrcode-scanner.
How can I do that on Weex?

Here comes a solution named EMAS. like weex-market, there are a lot of components and modules. One of these is the scan component which meets your requirement.
But the portal website is only in the Chinese language now.
Here is an android demo using ZXing to do qrcode. Copy this and register as a scan component. Have a look at this Demo.
public class WXQRCodeScannerComponent extends WXComponent<ZXingScannerView> implements ZXingScannerView.ResultHandler{
private static final String VF_WIDTH = "vfWidth";
private static final String VF_HEIGHT = "vfHeight";
private static final String VF_TOP_MARGIN = "vfTopMargin";
private static final String AUTO_STOP = "autoStop";
private static final String ON_SCAN = "scan";
private static final String TAG = "scan";
private boolean isAutoStop = false;
private boolean hasScanned = false;
private Handler mHandler = new Handler(Looper.getMainLooper());
private static final int CAMERA_PERMISSION_REQUEST_CODE = 0x1001;
private OnRequestPermissionCallback mPermissionCallback = new OnRequestPermissionCallback() {
#Override
public void onPermissionGranted() {
ZXingScannerView hostView = getHostView();
if(hostView != null) {
hostView.startCamera();
}
}
#Override
public void onPermissionDenied() {
// nope
}
};
public WXQRCodeScannerComponent(WXSDKInstance instance, WXDomObject dom, WXVContainer parent) {
super(instance, dom, parent);
}
public WXQRCodeScannerComponent(WXSDKInstance instance, WXDomObject dom, WXVContainer parent, int type) {
super(instance, dom, parent, type);
}
#Override
protected ZXingScannerView initComponentHostView(#NonNull Context context) {
requestPermissionIfNeeded();
ZXingScannerView qrCodeView = new ZXingScannerView(context);
qrCodeView.setSquareViewFinder(true);
qrCodeView.setResultHandler(this); // Register ourselves as a handler for scan results.
return qrCodeView;
}
private void requestPermissionIfNeeded() {
Context c = getContext();
if(c != null && c instanceof Activity) {
if(ContextCompat.checkSelfPermission(c, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale((Activity) c, Manifest.permission.CAMERA)) {
Toast.makeText(c, "请授予相机权限以用于扫码", Toast.LENGTH_LONG).show();
}
LocalBroadcastManager.getInstance(c)
.registerReceiver(new InnerReceiver(mPermissionCallback), new IntentFilter(WXModule.ACTION_REQUEST_PERMISSIONS_RESULT));
ActivityCompat.requestPermissions((Activity) c,
new String[]{Manifest.permission.CAMERA}, CAMERA_PERMISSION_REQUEST_CODE);
} else {
//Nope
}
}
}
static class InnerReceiver extends BroadcastReceiver{
private OnRequestPermissionCallback mCallback;
InnerReceiver(OnRequestPermissionCallback callback) {
this.mCallback = callback;
}
#Override
public void onReceive(Context context, Intent intent) {
int code = intent.getIntExtra(WXModule.REQUEST_CODE, 0);
int[] grantResults = intent.getIntArrayExtra(WXModule.GRANT_RESULTS);
String[] permissions = intent.getStringArrayExtra(WXModule.PERMISSIONS);
if(code == CAMERA_PERMISSION_REQUEST_CODE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 权限申请成功
if(mCallback != null) {
mCallback.onPermissionGranted();
}
} else {
Toast.makeText(context, "相机权限申请失败!将无法使用扫码功能!", Toast.LENGTH_SHORT).show();
if(mCallback != null) {
mCallback.onPermissionDenied();
}
}
}
LocalBroadcastManager.getInstance(context).unregisterReceiver(this);
}
}
interface OnRequestPermissionCallback {
void onPermissionGranted();
void onPermissionDenied();
}
#Override
protected boolean setProperty(String key, Object param) {
switch (key) {
case VF_WIDTH:
Float width = WXUtils.getFloat(param,null);
if(width != null) {
setVfWidth(width);
}
return true;
case VF_HEIGHT:
Float height = WXUtils.getFloat(param,null);
if(height != null) {
setVfHeight(height);
}
return true;
case VF_TOP_MARGIN:
Float topMargin = WXUtils.getFloat(param,null);
if(topMargin != null) {
setVfTopMargin(topMargin);
}
return true;
case AUTO_STOP:
Boolean autoStop = WXUtils.getBoolean(param, false);
if(autoStop != null) {
setAutoStop(autoStop);
}
return true;
}
return super.setProperty(key, param);
}
#Override
protected void onHostViewInitialized(ZXingScannerView host) {
super.onHostViewInitialized(host);
ZXingScannerView hostView = getHostView();
if(hostView != null) {
hostView.startCamera();
}
}
#WXComponentProp(name = VF_WIDTH)
public void setVfWidth(float width) {
float finalWidth = WXViewUtils.getRealSubPxByWidth(width,getInstance().getInstanceViewPortWidth());
if(getHostView() != null) {
// ScanBoxView scanBoxView = getHostView().getScanBox();
// if(scanBoxView != null) {
// scanBoxView.setRectWidth((int) finalWidth);
// }
}
}
#WXComponentProp(name = VF_HEIGHT)
public void setVfHeight(float height) {
float finalHeight = WXViewUtils.getRealSubPxByWidth(height,getInstance().getInstanceViewPortWidth());
if(getHostView() != null) {
// ScanBoxView scanBoxView = getHostView().getScanBox();
// if(scanBoxView != null) {
// scanBoxView.setRectHeight((int) finalHeight);
// }
}
}
#WXComponentProp(name = VF_TOP_MARGIN)
public void setVfTopMargin(float topMargin) {
float finalTopMargin = WXViewUtils.getRealSubPxByWidth(topMargin,getInstance().getInstanceViewPortWidth());
if(getHostView() != null) {
// ScanBoxView scanBoxView = getHostView().getScanBox();
// if(scanBoxView != null) {
// scanBoxView.setTopOffset((int) finalTopMargin);
// }
}
}
#WXComponentProp(name = AUTO_STOP)
public void setAutoStop(boolean autoStop) {
this.isAutoStop = autoStop;
}
#Override
public void onActivityResume() {
super.onActivityResume();
ZXingScannerView hostView = getHostView();
if(hostView != null) {
hostView.startCamera();
}
}
#Override
public void onActivityPause() {
super.onActivityPause();
ZXingScannerView hostView = getHostView();
if(hostView != null) {
hostView.stopCamera(); // Stop camera on pause
}
}
#Override
public void onActivityDestroy() {
if(mHandler != null) {
ZXingScannerView hostView = getHostView();
if(hostView != null) {
hostView.stopCamera();
}
mHandler.removeCallbacksAndMessages(null);
}
}
#Override
public void handleResult(Result result) {
if(isAutoStop && hasScanned) {
resumeCameraPreviewDelayed();
return;
}
fireEventByResult(result);
resumeCameraPreviewDelayed();
hasScanned = true;
}
private void fireEventByResult(Result result) {
if(result == null || TextUtils.isEmpty(result.getText())) {
Map<String,Object> callback = new HashMap<>(4);
callback.put("result","failed");
fireEvent(ON_SCAN,callback);
if(WXEnvironment.isApkDebugable()) {
WXLogUtils.d(TAG, "scan failed");
}
} else {
Map<String,String> data = new HashMap<>(4);
data.put("timestamp", System.currentTimeMillis()+"");
data.put("code", result.getText());
Map<String,Object> callback = new HashMap<>(4);
callback.put("result","success");
callback.put("data", data);
fireEvent(ON_SCAN,callback);
if(WXEnvironment.isApkDebugable()) {
WXLogUtils.d(TAG, "scan success: " + result.getText());
}
}
}
private void resumeCameraPreviewDelayed() {
if(mHandler == null) {
return;
}
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
ZXingScannerView hostView = getHostView();
if(hostView != null) {
hostView.resumeCameraPreview(WXQRCodeScannerComponent.this);
}
}
}, 1000);
}
}

Related

iOS web view renderer doesn't update its value

I have implemented WebViewRenderer it works well on Android but it doesn't work on iOS. It displays the value, but then user clicks next button and the value doesn't update. I have tried the the example on ms website, but that displayed no text, I don't have the values from URL. I have also tried several post here.
[assembly: ExportRenderer(typeof(ExtendedWebView), typeof(ExtendedWebViewRenderer))]
namespace MyApp.iOS.Renderers
{
public class ExtendedWebViewRenderer : ViewRenderer<ExtendedWebView, WKWebView>
{
WKWebView _wkWebView;
static ExtendedWebView _xwebView = null;
protected override void OnElementChanged(ElementChangedEventArgs<ExtendedWebView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
WKWebViewConfiguration config = new WKWebViewConfiguration();
_wkWebView = new WKWebView(Frame, config);
_wkWebView.NavigationDelegate = new CustomWKNavigationDelegate(this);
SetNativeControl(_wkWebView);
}
if (e.NewElement != null)
{
HtmlWebViewSource source = (Xamarin.Forms.HtmlWebViewSource)Element.Source;
string html = source.Html;
_wkWebView.LoadHtmlString(html, baseUrl: null);
_wkWebView.ScrollView.ScrollEnabled = false;
_wkWebView.SizeToFit();
}
}
}
public class CustomWKNavigationDelegate : WKNavigationDelegate
{
ExtendedWebViewRenderer _webViewRenderer;
public CustomWKNavigationDelegate(ExtendedWebViewRenderer webViewRenderer)
{
_webViewRenderer = webViewRenderer;
}
public override async void DidFinishNavigation(WKWebView webView, WKNavigation navigation)
{
var wv = _webViewRenderer.Element as ExtendedWebView;
if (wv != null)
{
await System.Threading.Tasks.Task.Delay(100); // wait here till content is rendered
wv.HeightRequest = (double)webView.ScrollView.ContentSize.Height;
}
}
}
}
[assembly: ExportRenderer(typeof(ExtendedWebView), typeof(ExtendedWebViewRenderer))]
namespace MyApp.Droid.Renderers
{
public class ExtendedWebViewRenderer : WebViewRenderer
{
public static int _webViewHeight;
static ExtendedWebView _xwebView = null;
public WebView _webView;
bool isScroll;
[Obsolete]
public ExtendedWebViewRenderer(Context context) : base(context)
{
}
class ExtendedWebViewClient : WebViewClient
{
WebView _webView;
public async override void OnPageFinished(WebView view, string url)
{
try
{
_webView = view;
if (_xwebView != null)
{
view.Settings.JavaScriptEnabled = true;
await Task.Delay(100);
string result = await _xwebView.EvaluateJavaScriptAsync("(function(){return document.body.scrollHeight;})()");
_xwebView.HeightRequest = Convert.ToDouble(result);
}
base.OnPageFinished(view, url);
}
catch (Exception ex)
{
Console.WriteLine($"{ex.Message}");
}
}
public override bool ShouldOverrideUrlLoading(Android.Webkit.WebView view, IWebResourceRequest request)
{
return true;
}
}
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
{
base.OnElementChanged(e);
_xwebView = e.NewElement as ExtendedWebView;
_webView = Control;
if (e.OldElement == null)
{
_webView.SetWebViewClient(new ExtendedWebViewClient());
}
}
}
}
public class ExtendedWebView : WebView
{
public ExtendedWebView()
{
}
}
XAML
<controls:ExtendedWebView
VerticalOptions="FillAndExpand" Grid.RowSpan="2"
x:Name="enView"
Opacity="1"
Source="{Binding CZ, Mode=TwoWay}"/>
private static HtmlWebViewSource htmlSourceExplanation = new HtmlWebViewSource();
public HtmlWebViewSource Cz
{
get { return _cz; }
set
{
_cz = value;
NotifyPropertyChanged(nameof(Cz));
}
}
private void SetExplanationDetail(string text, string text2)
{
htmlSourceExplanation.Html = _style + "<div class=\"text\"><div>" + text + "</div><div>" + text2 + "</div></div>";
_cz = htmlSourceExplanation;
NotifyPropertyChanged("Cz");
}
I expect the value to change once to user cliks the button. `

SelectedDate event is not trigger when the current date selected in Nullable date picker Custom Control in xamarin.Fomrs

I have implemented the nullable date picker to show the placeholder to date field as well as the null date in it, You can see the code below
Now the issue here is when I click on the current date then the DateSelected event is not getting trigger, it triggers when you select the previous date or next dates but not getting selected for the current date.
Thanks in advance.
NDateControl.cs
public class NDatePicker : DatePicker
{
public NDatePicker()
{
Format = "MM'/'dd'/'yyyy";
}
public string _originalFormat = null;
public static readonly BindableProperty PlaceHolderProperty =
BindableProperty.Create(nameof(PlaceHolder), typeof(string), typeof(c), "'MM/DD/YYYY'");
public string PlaceHolder
{
get { return (string)GetValue(PlaceHolderProperty); }
set { SetValue(PlaceHolderProperty, value); }
}
public static readonly BindableProperty NullableDateProperty =
BindableProperty.Create(nameof(NullableDate), typeof(DateTime?), typeof(NDatePicker), null, defaultBindingMode: BindingMode.TwoWay);
public DateTime? NullableDate
{
get { return (DateTime?)GetValue(NullableDateProperty); }
set
{
SetValue(NullableDateProperty, value);
UpdateDate();
}
}
private void UpdateDate()
{
if (NullableDate != null)
{
if (_originalFormat != null)
{
Format = _originalFormat;
}
}
else
{
Format = PlaceHolder;
}
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
if (BindingContext != null)
{
_originalFormat = Format;
UpdateDate();
}
}
protected override void OnPropertyChanged(string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName == DateProperty.PropertyName || (propertyName == IsFocusedProperty.PropertyName
&& !IsFocused && (Date.ToString(Constant.DATE_FORMAT) == DateTime.Now.ToString(Constant.DATE_FORMAT))))
{
AssignValue();
}
if (propertyName == NullableDateProperty.PropertyName && NullableDate.HasValue)
{
Date = NullableDate.Value;
if (Date.ToString(_originalFormat) == DateTime.Now.ToString(_originalFormat))
{
//this code was done because when date selected is the actual date the"DateProperty" does not raise
UpdateDate();
}
}
}
public void AssignValue()
{
NullableDate = Date;
UpdateDate();
}
}
NDatePickerDroid.cs
public class NDatePickerDroid : ViewRenderer<NDatePicker, EditText>
{
DatePickerDialog _dialog;
public NDatePickerDroid(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<NDatePicker> e)
{
base.OnElementChanged(e);
SetNativeControl(new EditText(Context));
if (Control == null || e.NewElement == null)
return;
Control.TextSize = 14;
Control.Click += OnPickerClick;
Control.Text = Element.Date.ToString(Element.Format);
Control.KeyListener = null;
Control.FocusChange += OnPickerFocusChange;
Control.Enabled = Element.IsEnabled;
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == Xamarin.Forms.DatePicker.DateProperty.PropertyName || e.PropertyName == Xamarin.Forms.DatePicker.FormatProperty.PropertyName)
SetDate(Element.Date);
}
void OnPickerFocusChange(object sender, Android.Views.View.FocusChangeEventArgs e)
{
if (e.HasFocus)
{
ShowDatePicker();
}
}
protected override void Dispose(bool disposing)
{
if (Control != null)
{
Control.Click -= OnPickerClick;
Control.FocusChange -= OnPickerFocusChange;
if (_dialog != null)
{
_dialog.Hide();
_dialog.Dispose();
_dialog = null;
}
}
base.Dispose(disposing);
}
void OnPickerClick(object sender, EventArgs e)
{
ShowDatePicker();
}
void SetDate(DateTime date)
{
Control.Text = date.ToString(Element.Format);
Element.Date = date;
}
private void ShowDatePicker()
{
CreateDatePickerDialog(Element.Date.Year, Element.Date.Month - 1, Element.Date.Day);
_dialog.Show();
}
void CreateDatePickerDialog(int year, int month, int day)
{
NDatePicker view = Element;
_dialog = new DatePickerDialog(Context, (o, e) =>
{
view.Date = e.Date;
((IElementController)view).SetValueFromRenderer(VisualElement.IsFocusedProperty, false);
Control.ClearFocus();
_dialog = null;
}, year, month, day);
_dialog.SetButton(Constant.DONE, (sender, e) =>
{
SetDate(_dialog.DatePicker.DateTime);
Element.Format = Element._originalFormat;
Element.AssignValue();
});
}
}
NDatePickeriOS.cs
public class NDatePickeriOS : DatePickerRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<DatePicker> e)
{
base.OnElementChanged(e);
if (e.NewElement != null && Control != null)
{
AddClearButton();
Control.Font = Control.Font.WithSize(14);
Control.BorderStyle = UITextBorderStyle.Line;
Control.Layer.BorderColor = UIColor.LightGray.CGColor;
Control.Layer.BorderWidth = 1;
if (Device.Idiom == TargetIdiom.Tablet)
{
Control.Font = UIFont.SystemFontOfSize(25);
}
}
}
private void AddClearButton()
{
UIToolbar originalToolbar = Control.InputAccessoryView as UIToolbar;
if (originalToolbar != null && originalToolbar.Items.Length <= 2)
{
var clearButton = new UIBarButtonItem(Constant.CLEAR, UIBarButtonItemStyle.Plain, ((sender, ev) =>
{
NDatePicker baseDatePicker = Element as NDatePicker;
Element.Unfocus();
Element.Date = DateTime.Now;
//baseDatePicker.CleanDate();
}));
var newItems = new List<UIBarButtonItem>();
foreach (var item in originalToolbar.Items)
{
newItems.Add(item);
}
newItems.Insert(0, clearButton);
originalToolbar.Items = newItems.ToArray();
originalToolbar.SetNeedsDisplay();
}
}
}
The XAML is
<StackLayout Orientation="Vertical" HorizontalOptions="FillAndExpand" Margin="0,2" HeightRequest="60">
<control:NDatePicker NullableDate="{Binding DateAnswer}" DateSelected="DateChanged" HorizontalOptions="FillAndExpand" VerticalOptions="Center"/>
</StackLayout>
You could use MessagingCenter to send a message to your page when you select the current date.
_dialog.SetButton(Constant.DONE, (sender, e) =>
{
SetDate(_dialog.DatePicker.DateTime);
Element.Format = Element._originalFormat;
Element.AssignValue();
if (_dialog.DatePicker.DateTime == DateTime.Today)
{
MessagingCenter.Send<Object,DateTime>(this, "SameDate", view.Date);
}
});
in your page.xaml.cs:
public YourPage()
{
InitializeComponent();
MessagingCenter.Subscribe<Object,DateTime>(this, "SameDate", (sender,args) =>
{
DateTime dateTime = args; //triggered here
});
}

After phone restart 'unfortunately app has stopped' error

I get "unfortunately app has stopped" error after phone restart. My Phone is Oreo 8 but another phone with Nougat 7.1.1 also having the same error after phone restart. I use James Montemagnos' MyStepCounter bound services codes from GitHub with some changes in VS 2017 (Xamarin Cross-Platform App). The app is working fine if once started after phone restart with this error, else it is not counting steps and not working on the background. If I check the app in Phone Settings-General-Battery-Power saving exclusions then it works with no problems after phone restart and it is counting steps on the background without starting the app on Oreo phone. But there are no settings for battery power saving exclusions on the other phone
BootReceiver.cs
[BroadcastReceiver(Enabled = true, Exported = true, DirectBootAware = true)]
[IntentFilter(new string[] { Intent.ActionBootCompleted, Intent.ActionLockedBootCompleted, "android.intent.action.QUICKBOOT_POWERON", "com.htc.intent.action.QUICKBOOT_POWERON" })]
public class BootReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
var stepServiceIntent = new Intent(context, typeof(StepService));
context.StartService(stepServiceIntent);
}
}
StepServiceBinder.cs
public class StepServiceBinder : Binder
{
StepService stepService;
public StepServiceBinder(StepService service)
{
this.stepService = service;
}
public StepService StepService
{
get { return stepService; }
}
}
StepServiceConnection.cs
public class StepServiceConnection : Java.Lang.Object, IServiceConnection
{
MainActivity activity;
public StepServiceConnection(MainActivity activity)
{
this.activity = activity;
}
public void OnServiceConnected(ComponentName name, IBinder service)
{
var serviceBinder = service as StepServiceBinder;
if (serviceBinder != null)
{
activity.Binder = serviceBinder;
activity.IsBound = true;
}
}
public void OnServiceDisconnected(ComponentName name)
{
activity.IsBound = false;
}
}
StepService.cs
public class StepServiceConnection : Java.Lang.Object, IServiceConnection
{
MainActivity activity;
public StepServiceConnection(MainActivity activity)
{
this.activity = activity;
}
public void OnServiceConnected(ComponentName name, IBinder service)
{
var serviceBinder = service as StepServiceBinder;
if (serviceBinder != null)
{
activity.Binder = serviceBinder;
activity.IsBound = true;
}
}
public void OnServiceDisconnected(ComponentName name)
{
activity.IsBound = false;
}
}
StepService.cs
[Service(Enabled = true)]
[IntentFilter(new String[] { "com.PedometerApp.StepService" })]
public class StepService : Service, ISensorEventListener, INotifyPropertyChanged
{
private SensorManager sManager;
private bool isRunning;
private long stepsToday = 0;
public bool WarningState
{
get;
set;
}
public long StepsToday
{
get { return stepsToday; }
set
{
if (stepsToday == value)
return;
stepsToday = value;
OnPropertyChanged("StepsToday");
Settings.CurrentDaySteps = value;
MessagingCenter.Send<object, long>(this, "Steps", Settings.CurrentDaySteps);
}
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
var alarmManager = ((AlarmManager)ApplicationContext.GetSystemService(AlarmService));
var intent2 = new Intent(this, typeof(StepService));
intent2.PutExtra("warning", WarningState);
var stepIntent = PendingIntent.GetService(ApplicationContext, 200, intent2, PendingIntentFlags.UpdateCurrent);
alarmManager.Set(AlarmType.Rtc, Java.Lang.JavaSystem
.CurrentTimeMillis() + 1000 * 60 * 60, stepIntent);
var warning = false;
if (intent != null)
warning = intent.GetBooleanExtra("warning", false);
Startup();
return StartCommandResult.Sticky;
}
public override void OnTaskRemoved(Intent rootIntent)
{
base.OnTaskRemoved(rootIntent);
UnregisterListeners();
var intent = new Intent(this, typeof(StepService));
intent.PutExtra("warning", WarningState);
((AlarmManager)GetSystemService(AlarmService)).Set(AlarmType.Rtc, Java.Lang.JavaSystem
.CurrentTimeMillis() + 500,
PendingIntent.GetService(this, 201, intent, 0));
}
private void Startup(bool warning = false)
{
CrunchDates(true);
if (!isRunning)
{
RegisterListeners();
WarningState = warning;
}
isRunning = true;
}
public override void OnDestroy()
{
base.OnDestroy();
UnregisterListeners();
isRunning = false;
CrunchDates();
}
void RegisterListeners()
{
sManager = GetSystemService(SensorService) as SensorManager;
sManager.RegisterListener(this, sManager.GetDefaultSensor(SensorType.StepCounter), SensorDelay.Ui);
}
void UnregisterListeners()
{
if (!isRunning)
return;
try
{
var sensorManager = (SensorManager)GetSystemService(Context.SensorService);
sensorManager.UnregisterListener(this);
isRunning = false;
}
catch (Exception ex)
{
}
}
StepServiceBinder binder;
public override Android.OS.IBinder OnBind(Android.Content.Intent intent)
{
binder = new StepServiceBinder(this);
return binder;
}
public void OnAccuracyChanged(Sensor sensor, SensorStatus accuracy)
{
//do nothing here
}
public void AddSteps(long count)
{
if (lastSteps == 0)
{
lastSteps = count;
}
newSteps = count - lastSteps;
if (newSteps < 0)
newSteps = 1;
else if (newSteps > 100)
newSteps = 1;
lastSteps = count;
CrunchDates();
Settings.TotalSteps += newSteps;
StepsToday = Settings.TotalSteps - Settings.StepsBeforeToday;
}
long newSteps = 0;
long lastSteps = 0;
public void OnSensorChanged(SensorEvent e)
{
if (lastSteps < 0)
lastSteps = 0;
var count = (long)e.Values[0];
WarningState = false;
AddSteps(count);
}
private void CrunchDates(bool startup = false)
{
if (!Utils.IsSameDay)
{
var yesterday = Settings.CurrentDay;
var dayEntry = StepEntryManager.GetStepEntry(yesterday);
if (dayEntry == null || dayEntry.Date.DayOfYear != yesterday.DayOfYear)
{
dayEntry = new StepEntry();
}
dayEntry.Date = yesterday;
dayEntry.Steps = Settings.CurrentDaySteps;
Settings.CurrentDay = DateTime.Today;
Settings.CurrentDaySteps = 0;
Settings.StepsBeforeToday = Settings.TotalSteps;
StepsToday = 0;
try
{
StepEntryManager.SaveStepEntry(dayEntry);
}
catch (Exception ex)
{
Console.WriteLine("Error {0}", ex.Message);
}
}
else if (startup)
{
StepsToday = Settings.TotalSteps - Settings.StepsBeforeToday;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string name)
{
if (PropertyChanged == null)
return;
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}

Xamarin Forms pedometer application foreground service problem on android oreo 8.1 and android pie 9.0

I am using foreground service if OS version >= 8.0. On android 8.1 and 9.0 when my pedometer app is open or on the background it is counting steps, but not when the app is closed. Also after closing the app notification icon disappearing from the phone screen. Tested on Oreo 8.0 using foreground service, it is working fine and counting steps when the app is closed. Tested also on android Nougat 7.0 with background service and working with no problems.
BootReceiver.cs
[BroadcastReceiver(Enabled = true, Exported = true, DirectBootAware = true)]
[IntentFilter(new string[] { Intent.ActionBootCompleted, Intent.ActionLockedBootCompleted, "android.intent.action.QUICKBOOT_POWERON", "com.htc.intent.action.QUICKBOOT_POWERON" })]
public class BootReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
var stepServiceIntent = new Intent(context, typeof(StepService));
if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.O)
{
context.StartForegroundService(stepServiceIntent);
}
else
{
context.StartService(stepServiceIntent);
}
}
}
StepServiceBinder.cs
public class StepServiceBinder : Binder
{
StepService stepService;
public StepServiceBinder(StepService service)
{
this.stepService = service;
}
public StepService StepService
{
get { return stepService; }
}
}
StepServiceConnection.cs
public class StepServiceConnection : Java.Lang.Object, IServiceConnection
{
MainActivity activity;
public StepServiceConnection(MainActivity activity)
{
this.activity = activity;
}
public void OnServiceConnected(ComponentName name, IBinder service)
{
var serviceBinder = service as StepServiceBinder;
if (serviceBinder != null)
{
activity.Binder = serviceBinder;
activity.IsBound = true;
}
}
public void OnServiceDisconnected(ComponentName name)
{
activity.IsBound = false;
}
}
*Manifest.xml *
<manifest android:versionName="1.1" package="com.PedometerApp" android:installLocation="internalOnly" android:versionCode="11">
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="27" />
<application android:label="My Pedometer" android:icon="#drawable/logo"> </application>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
</manifest>
StepService.cs
[Service(Enabled = true)]
[IntentFilter(new String[] { "com.PedometerApp.StepService" })]
public class StepService : Service, ISensorEventListener, INotifyPropertyChanged
{
private SensorManager sManager;
private bool isRunning;
private long stepsToday = 0;
public bool WarningState
{
get;
set;
}
public long StepsToday
{
get { return stepsToday; }
set
{
if (stepsToday == value)
return;
stepsToday = value;
OnPropertyChanged("StepsToday");
Settings.CurrentDaySteps = value;
MessagingCenter.Send<object, long>(this, "Steps", Settings.CurrentDaySteps);
}
}
public const string PRIMARY_NOTIF_CHANNEL = "exampleChannel";
public const int SERVICE_RUNNING_NOTIFICATION_ID = 10000;
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
RegisterForService();
var warning = false;
if (intent != null)
warning = intent.GetBooleanExtra("warning", false);
Startup();
return StartCommandResult.Sticky;
}
private void RegisterForService()
{
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
var channel = new NotificationChannel(PRIMARY_NOTIF_CHANNEL, "Pedometer Service Channel", NotificationImportance.Low)
{
Description = "Foreground Service Channel"
};
var notificationManager = (NotificationManager)GetSystemService(NotificationService);
notificationManager.CreateNotificationChannel(channel);
var notification = new Notification.Builder(this, PRIMARY_NOTIF_CHANNEL)
.SetContentTitle("Service")
.SetContentText("Running")
.SetSmallIcon(Resource.Drawable.ic_stat_name)
.SetContentIntent(BuildIntentToShowMainActivity())
.SetOngoing(true)
.Build();
StartForeground(SERVICE_RUNNING_NOTIFICATION_ID, notification);
}
else
{
BuildIntentToShowMainActivity();
}
}
PendingIntent BuildIntentToShowMainActivity()
{
var alarmManager = ((AlarmManager)ApplicationContext.GetSystemService(AlarmService));
var intent2 = new Intent(this, typeof(StepService));
intent2.PutExtra("warning", WarningState);
var stepIntent = PendingIntent.GetService(ApplicationContext, 200, intent2, PendingIntentFlags.UpdateCurrent);
alarmManager.Set(AlarmType.Rtc, Java.Lang.JavaSystem
.CurrentTimeMillis() + 1000 * 60 * 60, stepIntent);
return stepIntent;
}
public override void OnTaskRemoved(Intent rootIntent)
{
base.OnTaskRemoved(rootIntent);
UnregisterListeners();
var intent = new Intent(this, typeof(StepService));
intent.PutExtra("warning", WarningState);
((AlarmManager)GetSystemService(AlarmService)).Set(AlarmType.Rtc, Java.Lang.JavaSystem
.CurrentTimeMillis() + 500,
PendingIntent.GetService(this, 201, intent, 0));
}
private void Startup(bool warning = false)
{
CrunchDates(true);
if (!isRunning)
{
RegisterListeners();
WarningState = warning;
}
isRunning = true;
}
public override void OnDestroy()
{
base.OnDestroy();
UnregisterListeners();
isRunning = false;
CrunchDates();
}
void RegisterListeners()
{
sManager = GetSystemService(SensorService) as SensorManager;
sManager.RegisterListener(this, sManager.GetDefaultSensor(SensorType.StepCounter), SensorDelay.Ui);
}
void UnregisterListeners()
{
if (!isRunning)
return;
try
{
var sensorManager = (SensorManager)GetSystemService(Context.SensorService);
sensorManager.UnregisterListener(this);
isRunning = false;
}
catch (Exception ex)
{
}
}
StepServiceBinder binder;
public override Android.OS.IBinder OnBind(Android.Content.Intent intent)
{
binder = new StepServiceBinder(this);
return binder;
}
public void OnAccuracyChanged(Sensor sensor, SensorStatus accuracy)
{
//do nothing here
}
public void AddSteps(long count)
{
if (lastSteps == 0)
{
lastSteps = count;
}
newSteps = count - lastSteps;
if (newSteps < 0)
newSteps = 1;
else if (newSteps > 100)
newSteps = 1;
lastSteps = count;
CrunchDates();
Settings.TotalSteps += newSteps;
StepsToday = Settings.TotalSteps - Settings.StepsBeforeToday;
}
long newSteps = 0;
long lastSteps = 0;
public void OnSensorChanged(SensorEvent e)
{
if (lastSteps < 0)
lastSteps = 0;
var count = (long)e.Values[0];
WarningState = false;
AddSteps(count);
}
private void CrunchDates(bool startup = false)
{
if (!Utils.IsSameDay)
{
var yesterday = Settings.CurrentDay;
var dayEntry = StepEntryManager.GetStepEntry(yesterday);
if (dayEntry == null || dayEntry.Date.DayOfYear != yesterday.DayOfYear)
{
dayEntry = new StepEntry();
}
dayEntry.Date = yesterday;
dayEntry.Steps = Settings.CurrentDaySteps;
Settings.CurrentDay = DateTime.Today;
Settings.CurrentDaySteps = 0;
Settings.StepsBeforeToday = Settings.TotalSteps;
StepsToday = 0;
try
{
StepEntryManager.SaveStepEntry(dayEntry);
}
catch (Exception ex)
{
Console.WriteLine("Error {0}", ex.Message);
}
}
else if (startup)
{
StepsToday = Settings.TotalSteps - Settings.StepsBeforeToday;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string name)
{
if (PropertyChanged == null)
return;
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
MainActivity.cs
[Activity(Label = "My Pedometer", Icon = "#mipmap/icon", Theme = "#style/MainTheme", LaunchMode = LaunchMode.SingleTask, MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
public bool IsBound { get; set; }
private StepServiceBinder binder;
private bool registered;
private Handler handler;
private bool firstRun = true;
private StepServiceConnection serviceConnection;
public StepServiceBinder Binder
{
get { return binder; }
set
{
binder = value;
if (binder == null)
return;
HandlePropertyChanged(null, new System.ComponentModel.PropertyChangedEventArgs("StepsToday"));
if (registered)
binder.StepService.PropertyChanged -= HandlePropertyChanged;
binder.StepService.PropertyChanged += HandlePropertyChanged;
registered = true;
}
}
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
Xamarin.Forms.Forms.Init(this, savedInstanceState);
StartStepService();
handler = new Handler();
handler.PostDelayed(() => UpdateUI(), 500);
LoadApplication(new App());
}
private void StartStepService()
{
try
{
var service = new Intent(this, typeof(StepService));
if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.O)
{
StartForegroundService(service);
}
else
{
StartService(service);
}
}
catch (Exception ex)
{
Console.WriteLine("Exception {0}.", ex.Message);
}
}
protected override void OnStop()
{
base.OnStop();
if (IsBound)
{
UnbindService(serviceConnection);
IsBound = false;
}
}
protected override void OnDestroy()
{
base.OnDestroy();
if (IsBound)
{
UnbindService(serviceConnection);
IsBound = false;
}
}
protected override void OnStart()
{
base.OnStart();
if (!firstRun)
StartStepService();
if (IsBound)
return;
var serviceIntent = new Intent(this, typeof(StepService));
serviceConnection = new StepServiceConnection(this);
BindService(serviceIntent, serviceConnection, Bind.AutoCreate);
}
protected override void OnPause()
{
base.OnPause();
if (registered && binder != null)
{
binder.StepService.PropertyChanged -= HandlePropertyChanged;
registered = false;
}
}
protected override void OnResume()
{
base.OnResume();
if (!firstRun)
{
if (handler == null)
handler = new Handler();
handler.PostDelayed(() => UpdateUI(), 500);
}
firstRun = false;
if (!registered && binder != null)
{
binder.StepService.PropertyChanged += HandlePropertyChanged;
registered = true;
}
}
void HandlePropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName != "StepsToday")
return;
UpdateUI();
}
private void UpdateUI()
{
RunOnUiThread(() =>
{
long steps = 0;
var showWaring = false;
if (Binder == null)
{
if (Utils.IsSameDay)
steps = Settings.CurrentDaySteps;
}
else
{
steps = Binder.StepService.StepsToday;
showWaring = binder.StepService.WarningState;
}
Settings.CurrentDaySteps = steps;
});
}
}

Android implement CursorLoader to SQLite database loader

I want to have two ListView in one Activity. I am trying to use Fragments. Data will loaded to ListViews from Sqlite database. I implement AsyncTaskLoader for working with SQllite used source code of cursorloader, but method doInBackground() issue mistake. This is my code:
public class DatabaseLoader extends AsyncTaskLoader<Cursor> {
final ForceLoadContentObserver mObserver;
SQLiteDatabase mDb;
String mTable;
String[] mColumns;
String mSelection;
String[] mSelectionArgs;
String mSortOrder;
Cursor mCursor;
/* Runs on a worker thread */
#Override
public Cursor loadInBackground() {
Cursor cursor = mDb.query(mTable, mColumns, mSelection,
mSelectionArgs, null, null, mSortOrder, null);
if (cursor != null) {
cursor.getCount();
registerContentObserver(cursor, mObserver);
}
return cursor;
}
void registerContentObserver(Cursor cursor, ContentObserver observer) {
cursor.registerContentObserver(mObserver);
}
/* Runs on the UI thread */
#Override
public void deliverResult(Cursor cursor) {
if (isReset()) {
// An async query came in while the loader is stopped
if (cursor != null) {
cursor.close();
}
return;
}
Cursor oldCursor = mCursor;
mCursor = cursor;
if (isStarted()) {
super.deliverResult(cursor);
}
if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {
oldCursor.close();
}
}
public DatabaseLoader(Context context) {
super(context);
mObserver = new ForceLoadContentObserver();
}
public DatabaseLoader(Context context, String table, String[] columns, String selection,
String[] selectionArgs, String sortOrder) {
super(context);
mObserver = new ForceLoadContentObserver();
mTable = table;
mColumns = columns;
mSelection = selection;
mSelectionArgs = selectionArgs;
mSortOrder = sortOrder;
}
#Override
protected void onStartLoading() {
if (mCursor != null) {
deliverResult(mCursor);
}
if (takeContentChanged() || mCursor == null) {
forceLoad();
}
}
/**
* Must be called from the UI thread
*/
#Override
protected void onStopLoading() {
// Attempt to cancel the current load task if possible.
cancelLoad();
}
#Override
public void onCanceled(Cursor cursor) {
if (cursor != null && !cursor.isClosed()) {
cursor.close();
}
}
#Override
protected void onReset() {
super.onReset();
// Ensure the loader is stopped
onStopLoading();
if (mCursor != null && !mCursor.isClosed()) {
mCursor.close();
}
mCursor = null;
}
public String getTable() {
return mTable;
}
public void setTable(String table) {
mTable = table;
}
public String[] getColumns() {
return mColumns;
}
public void setColumns(String[] columns) {
mColumns = columns;
}
public String getSelection() {
return mSelection;
}
public void setSelection(String selection) {
mSelection = selection;
}
public String[] getSelectionArgs() {
return mSelectionArgs;
}
public void setSelectionArgs(String[] selectionArgs) {
mSelectionArgs = selectionArgs;
}
public String getSortOrder() {
return mSortOrder;
}
public void setSortOrder(String sortOrder) {
mSortOrder = sortOrder;
}
#Override
public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
super.dump(prefix, fd, writer, args);
// writer.print(prefix); writer.print("mUri="); writer.println(mUri);
writer.print(prefix); writer.print("mTable="); writer.println(mTable);
writer.print(prefix); writer.print("mColumns="); writer.println(Arrays.toString(mColumns));
writer.print(prefix); writer.print("mSelection="); writer.println(mSelection);
writer.print(prefix); writer.print("mSelectionArgs="); writer.println(Arrays.toString(mSelectionArgs));
writer.print(prefix); writer.print("mSortOrder="); writer.println(mSortOrder);
writer.print(prefix); writer.print("mCursor="); writer.println(mCursor);
// writer.print(prefix); writer.print("mContentChanged=");
writer.println(mContentChanged);
}
}
Can help me somebody. Why method doInBackground() don't work?
You have to open the Database mDb before performing query on it.

Resources