I created simple Xamarin.Forms Bluetooth low energy scanning application. As I will use Bluetooth only on Android, I implemented scanning in MainActivity.cs of Android project:
namespace BlankAppXamlXamarinForms.Droid
{
[Activity(Label = "BlankAppXamlXamarinForms", Icon = "#drawable/icon", Theme = "#style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
private BluetoothManager _manager;
App app;
protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
app = new App();
var appContext = Application.Context;
_manager = (BluetoothManager)appContext.GetSystemService(BluetoothService);
_manager.Adapter.BluetoothLeScanner.StartScan(new MyScanCallback(app));
LoadApplication(new App());
}
}
public class MyScanCallback : ScanCallback
{
App _app;
public MyScanCallback(App app) {
_app = app;
}
public override void OnScanResult(ScanCallbackType callbackType, ScanResult result)
{
_app.newDevice(result.Device.Name + " - " + result.Device.Address);
}
}
}
The problem is that OnScanResult receives advertising packets only a moment after the application is started and when I switch the display of my phone off and then turn it on again. Meanwhile the application receives almost no advertising packets. How to receive advertising packets all the time my application is active?
You are creating two instances of App. Instead of LoadApplication(new App()); you should be passing in your field app, i.e. LoadApplication(app);
Also, instead of starting the scan in OnCreate, you might want to start the scan in OnResume and stop the scan in OnPause e.g.:
public class MainActivity : FormsAppCompatActivity
{
BluetoothManager bleManager;
App app;
ScanCallback scanCallback;
protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(bundle);
Forms.Init(this, bundle);
app = new App();
bleManager = (BluetoothManager)Application.Context.GetSystemService(BluetoothService);
scanCallback = new MyScanCallback(app);
LoadApplication(new App());
}
protected override void OnResume()
{
base.OnResume();
bleManager.Adapter.BluetoothLeScanner.StartScan(scanCallback);
}
protected override void OnPause()
{
base.OnPause();
bleManager.Adapter.BluetoothLeScanner.StopScan(scanCallback);
}
}
Related
My splash screen is Lottie animation, during 3-sec animation I am trying to initialize all I can in the background thread, so I put everything from MainActivity before LoadApplication method in this new activity, just wanted to put everything I can inside splash animation time
here is my splash activity class
[Activity(Label = "MyName", Theme = "#style/MyName.Splash", MainLauncher = true, NoHistory = true)]
public class SplashActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity, IAnimatorListener
{
private Com.Airbnb.Lottie.LottieAnimationView animationView;
public void OnAnimationCancel(Animator animation)
{
}
public void OnAnimationEnd(Animator animation)
{
//StartActivity(new Intent(Application.Context, typeof(MainActivity)));
StartActivity(new Intent(this, typeof(MainActivity)));
Finish(); //if I remove this I got 1-2 sec white screen
}
public async void OnAnimationStart(Animator animation)
{
await Task.Run(() =>
{
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
global::Xamarin.Forms.FormsMaterial.Init(this, savedInstanceState);
OneSignal.Current.StartInit(API_KEY).InFocusDisplaying(OSInFocusDisplayOption.Notification).EndInit();
FFImageLoading.Forms.Platform.CachedImageRenderer.Init(enableFastRenderer: true);
Rg.Plugins.Popup.Popup.Init(this); // ------ this became problem
MainActivity.AppInstance = new App(); //even create Xam App here but don't show it yet
});
}
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.SplashLayout);
animationView = FindViewById<Com.Airbnb.Lottie.LottieAnimationView>(Resource.Id.animation_view);
animationView.AddAnimatorListener(this);
}
}
I put init methods inside the background thread because if it is on the main thread my animation is not fluid.
After the animation ends, I start main activity and show my app
main activity code
[Activity(Label = "MyName", Icon = "#mipmap/ic_launcher", Theme = "#style/MainTheme", MainLauncher = false, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
public static App AppInstance; //Xamarin App instance created on splash activity
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
LoadApplication(AppInstance);
AppInstance.Show(); //just setting MainPage = new AppShell();
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
everything is working fine, except Rg plugin
if I move Rg.Plugins.Popup.Popup.Init(this); into main activity then it's working.
So my question is am I doing wrong to initialize platform-specific libraries in splash activity, and I why only Rg plugin doesn't work I see in init it asks for Context same as other libraries so I did initialize it with this keyword and starting main activity with this keyword
Should I do it some other way, init libraries while my animation is playing, because every sec counts when you start the application.
Context which Main activity is created is it different one from splash activity?
I even tried to replace all this with Application.Context
Is there a standard cross platform way of playing a 250 ms or so 'ding' on Xamarin Forms iOS and Android?
Mark Wardell
You can use DependencyService to play default system notification sound in each platform .
Create IPlaySoundService Interface :
public interface IPlaySoundService
{
void PlaySystemSound();
}
Implement the PlaySystemSound method in iOS as follow:
[assembly: Xamarin.Forms.Dependency(typeof(PlaySoundService))]
namespace AppCarouselViewSample.iOS
{
public class PlaySoundService : IPlaySoundService
{
public void PlaySystemSound()
{
var sound = new SystemSound(1000);
sound.PlaySystemSound();
}
}
}
Implement the PlaySystemSound method in Android as follow :
[assembly:Xamarin.Forms.Dependency(typeof(PlaySoundService))]
namespace AppCarouselViewSample.Droid
{
public class PlaySoundService : IPlaySoundService
{
public void PlaySystemSound()
{
Android.Net.Uri uri = RingtoneManager.GetDefaultUri(RingtoneType.Ringtone);
Ringtone rt = RingtoneManager.GetRingtone(MainActivity.instance.ApplicationContext, uri);
rt.Play();
}
}
}
This is the definition of instance from MainActivity :
namespace xxx.Droid
{
[Activity(Label = "AppCarouselViewSample", Icon = "#mipmap/icon", Theme = "#style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
public static MainActivity instance { set; get; }
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
instance = this;
Xamarin.Forms.Forms.SetFlags(new string[] { "CarouselView_Experimental", "SwipeView_Experimental", "IndicatorView_Experimental" });
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
}
...
}
Then it they will play the default notification Sound in each platform . You can modify SystemSoundID in iOS to fit your wants .Here is the Sound ID list .
here is my now compiling yet crashing Droid implementation
using Android.Media;
using TripCalculator.Droid.Services;
[assembly: Xamarin.Forms.Dependency(typeof(PlaySoundService))]
// Crashes
namespace TripCalculator.Droid.Services
{
public class PlaySoundService : IPlaySoundService
{
public void PlaySystemSound()
{
var currentContext = Android.App.Application.Context;
Android.Net.Uri uri = RingtoneManager.GetDefaultUri(RingtoneType.Ringtone);
Ringtone rt = RingtoneManager.GetRingtone(currentContext.ApplicationContext, uri);
rt.Play();
}
}
}
Runtime error when I try to change ringer mode to normal.i am using dependency service to call audio manager in android in xamarin form but code runs without error when the ringer mode change to silent ( maybe it's already silent)
About changing ringer mode, you can follow these steps.
1.define a interface in Xamarin.Forms PCL.
public interface IChangeRingModeService
{
void changeRingModeToNormal();
void changeRingModeToVibrate();
void changeRingModeToSilent();
}
In Android project, implement this interface in Mainactivity.cs, please don't forget register dependency
[assembly: Dependency(typeof(MainActivity))]
namespace App4.Droid
{
[Activity(Label = "App4", Icon = "#mipmap/icon", Theme = "#style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize| ConfigChanges.Orientation)]
public class MainActivity :
global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity, IChangeRingModeService
{
AudioManager am;
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
}
public void changeRingModeToVibrate()
{
am = (AudioManager)Android.App.Application.Context.GetSystemService(Context.AudioService);
am.RingerMode = RingerMode.Vibrate;
}
public void changeRingModeToNormal()
{
am = (AudioManager)Android.App.Application.Context.GetSystemService(Context.AudioService);
am.RingerMode = RingerMode.Normal;
}
public void changeRingModeToSilent()
{
am = (AudioManager)Android.App.Application.Context.GetSystemService(Context.AudioService);
am.RingerMode = RingerMode.Silent;
}}}
call this method in click event.
private void Button1licked(object sender, EventArgs e)
{
DependencyService.Get<IChangeRingModeService>().changeRingModeToVibrate();
}
private void Button2licked(object sender, EventArgs e)
{
DependencyService.Get<IChangeRingModeService>().changeRingModeToNormal();
}
private void Button3licked(object sender, EventArgs e)
{
DependencyService.Get<IChangeRingModeService>().changeRingModeToSilent();
}
A black screen with app title is showing when minimizing the app while loading splash screen , also the first screen will display after some time. Here is my splash activity and main activity classes.
[Activity(Theme = "#style/Theme.Splash", Icon = "#drawable/icon", MainLauncher = true, NoHistory = true,
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation,
ScreenOrientation = ScreenOrientation.Behind)]
public class SplashActivity : Activity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
var dpWidth = Resources.DisplayMetrics.WidthPixels/Resources.DisplayMetrics.Density;
RequestedOrientation = dpWidth > 700 ? ScreenOrientation.Unspecified : ScreenOrientation.Portrait;
ThreadPool.QueueUserWorkItem(o => LoadActivity());
}
private void LoadActivity()
{
RunOnUiThread(() => StartActivity(typeof(MainActivity)));
}
public override void OnBackPressed()
{
Environment.Exit(0);
}
}
[Activity(Label = "HACCP", ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : FormsApplicationActivity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
ActionBar.SetIcon(Android.Resource.Color.Transparent);
Forms.Init(this, bundle);
// some function //
LoadApplication(new App());
}
}
You tagged Xamarin.Forms so it should be as simple as..
class App : Application
{
public App()
{
MainPage = new MySplashPage();
}
}
class MySplashPage : ContentPage
{
public MySplashPage()
{
Task.Delay(3000); //show my pretty splash for 3 seconds
Application.Current.MainPage = new MyOtherSpiffyPage();
}
}
Not certain if the activity attribute ScreenOrientation = ScreenOrientation.Behind) is causing any issues, we don't use that in our apps.
Here is a "standard" splash activity we do use, letting Xamarin.Android take care of timing etc:
public class SplashActivity : Activity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Start main.
StartActivity(typeof(MainActivity));
}
}
You might try simplifying your app in similar fashion.
Xamarin I updated to version 4, Forms and 2.0 versions. On Android, I use AppCompat.
I had a problem. Previously, the Android keyboard caused resize view. Now this does not happen. The keyboard appears on the top view. And the desired Elements to be hiding.
I've tried:
[Activity(WindowSoftInputMode = SoftInput.AdjustResize, Label = "Title", Icon = "#drawable/icon", Theme = "#style/MyTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
ToolbarResource = Resource.Layout.toolbar;
TabLayoutResource = Resource.Layout.tabs;
LoadApplication(new App());
Window.DecorView.SetFitsSystemWindows(true);
}
}
Daylight AppCompat has been made on this lesson: https://blog.xamarin.com/material-design-for-your-xamarin-forms-android-apps/
Thank you.
I have solved the problem. Decompile class
"Forms AppCompatActivity" and watched how the method works OnCreate.
As a result, I turned out the following code:
[Activity(Label = "Title", Icon = "#drawable/icon", Theme = "#style/MyTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
Window.SetSoftInputMode(SoftInput.AdjustResize);
AndroidBug5497WorkaroundForXamarinAndroid.assistActivity(this);
ToolbarResource = Resource.Layout.toolbar;
TabLayoutResource = Resource.Layout.tabs;
LoadApplication(new App());
}
public class AndroidBug5497WorkaroundForXamarinAndroid
{
// For more information, see https://code.google.com/p/android/issues/detail?id=5497
// To use this class, simply invoke assistActivity() on an Activity that already has its content view set.
// CREDIT TO Joseph Johnson (http://stackoverflow.com/users/341631/joseph-johnson) for publishing the original Android solution on stackoverflow.com
public static void assistActivity(Activity activity)
{
new AndroidBug5497WorkaroundForXamarinAndroid(activity);
}
private Android.Views.View mChildOfContent;
private int usableHeightPrevious;
private FrameLayout.LayoutParams frameLayoutParams;
private AndroidBug5497WorkaroundForXamarinAndroid(Activity activity)
{
FrameLayout content = (FrameLayout)activity.FindViewById(Android.Resource.Id.Content);
mChildOfContent = content.GetChildAt(0);
ViewTreeObserver vto = mChildOfContent.ViewTreeObserver;
vto.GlobalLayout += (object sender, EventArgs e) => {
possiblyResizeChildOfContent();
};
frameLayoutParams = (FrameLayout.LayoutParams)mChildOfContent.LayoutParameters;
}
private void possiblyResizeChildOfContent()
{
int usableHeightNow = computeUsableHeight();
if (usableHeightNow != usableHeightPrevious)
{
int usableHeightSansKeyboard = mChildOfContent.RootView.Height;
int heightDifference = usableHeightSansKeyboard - usableHeightNow;
frameLayoutParams.Height = usableHeightSansKeyboard - heightDifference;
mChildOfContent.RequestLayout();
usableHeightPrevious = usableHeightNow;
}
}
private int computeUsableHeight()
{
Rect r = new Rect();
mChildOfContent.GetWindowVisibleDisplayFrame(r);
if (Build.VERSION.SdkInt < BuildVersionCodes.Lollipop)
{
return (r.Bottom - r.Top);
}
return r.Bottom;
}
}
}
It is important to add "Window.SetSoftInputMode(SoftInput.AdjustResize);" After calling base.OnCreate(bundle);