Hide navigation bar in Xamarin Forms - xamarin

I need to hide navigation bar in Xamarin Forms application permanently. Application is destinated to be installed on Android device. I mean, device with will be open to public (like restaurant's kiosk). I want to start my application only and prevent user from going to the system.
I read this: https://developer.android.com/training/system-ui/navigation
I tried this immersive sticky mode. At start status bar and navigation bar are hidden. When I swipe at top or at bottom, the navigation bar appears. How to prevent or disable it?
I read about rooting device and uninstall UISystem.apk and boot indicated app but I think the warranty for device will be lost. Is there another way?
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
HideSoftwareMenuBars();
LoadApplication(new App());
}
public override void OnWindowFocusChanged(bool hasFocus)
{
base.OnWindowFocusChanged(hasFocus);
if (hasFocus)
HideSoftwareMenuBars();
}
protected override void OnResume()
{
base.OnResume();
HideSoftwareMenuBars();
}
private void HideSoftwareMenuBars()
{
int uiOptions = (int)Window.DecorView.SystemUiVisibility;
uiOptions |= (int)SystemUiFlags.LayoutStable;
uiOptions |= (int)SystemUiFlags.LayoutHideNavigation;
uiOptions |= (int)SystemUiFlags.LayoutFullscreen;
uiOptions |= (int)SystemUiFlags.LowProfile;
uiOptions |= (int)SystemUiFlags.HideNavigation;
uiOptions |= (int)SystemUiFlags.Fullscreen;
uiOptions |= (int)SystemUiFlags.ImmersiveSticky;
this.Window.DecorView.SystemUiVisibility = (StatusBarVisibility)uiOptions;
}

You can implement it by using MessagingCenter
in ContentPage
For example , It will hide or show the status bar when click a button in the page . You can invoke it when the page scroll to top or bottom .
bool isHaveBar = false;
public MainPage()
{
InitializeComponent();
NavigationPage.SetHasNavigationBar(this, false);
}
private void Button_Clicked(object sender, EventArgs e)
{
isHaveBar = !isHaveBar;
NavigationPage.SetHasNavigationBar(this, isHaveBar);
MessagingCenter.Send<Object,bool>(this,"Hide",isHaveBar);
}
Don't forget to set the MainPage of App as NavigationPage
MainPage = new NavigationPage(new YourContentPage());
in Android MainActivity
protected override void OnCreate(Bundle savedInstanceState)
{
//...
base.OnCreate(savedInstanceState);
MessagingCenter.Subscribe<Object, bool>(this, "Hide", (arg,isHaveBar) => {
if(isHaveBar)
{
Window.ClearFlags(WindowManagerFlags.Fullscreen);
Window.AddFlags(WindowManagerFlags.ForceNotFullscreen);
}
else
{
Window.AddFlags(WindowManagerFlags.Fullscreen);
Window.ClearFlags(WindowManagerFlags.ForceNotFullscreen);
}
});
//...
}

Related

Xamarin Forms - Android - initialize libraries in splash activity (before main activity)

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

In Xamarin Forms how to call MainActivity's method or get MainActivity's 'Window' from another class?

I want to hide software buttons when enter text to entry is done. When user put finger outside keyboard, this method is invoked:
public override void OnUserInteraction()
{
base.OnUserInteraction();
HideSoftwareMenuBars();
}
That works perfectly:
private void HideSoftwareMenuBars()
{
try
{
int uiOptions = (int)Window.DecorView.SystemUiVisibility;
uiOptions |= (int)SystemUiFlags.LayoutStable;
uiOptions |= (int)SystemUiFlags.LayoutHideNavigation;
uiOptions |= (int)SystemUiFlags.LayoutFullscreen;
uiOptions |= (int)SystemUiFlags.LowProfile;
uiOptions |= (int)SystemUiFlags.HideNavigation;
uiOptions |= (int)SystemUiFlags.Fullscreen;
uiOptions |= (int)SystemUiFlags.ImmersiveSticky;
this.Window.DecorView.SystemUiVisibility = (StatusBarVisibility)uiOptions;
}
catch (System.Exception Ex)
{
}
}
But there is no method in MainActivity.cs to override that is being called when user press "enter" key on keyboard. I have overridden more than 200 methods to check it. Then in Xamarin.Forms in code behind I have event:
private void Entry_Unfocused(object sender, FocusEventArgs e)
{
DependencyService.Get<ISoftwareButtons>().HideSoftwareMenuBars();
}
Then by calling dependency interface, here I have 2 errors:
[assembly: Dependency(typeof(SoftwareButtonsHandler))]
namespace Channel.Droid.DependencyHandlers
{
public class SoftwareButtonsHandler
{
private void HideSoftwareMenuBars()
{
try
{
int uiOptions = (int) Window.DecorView.SystemUiVisibility;
uiOptions |= (int)SystemUiFlags.LayoutStable;
uiOptions |= (int)SystemUiFlags.LayoutHideNavigation;
uiOptions |= (int)SystemUiFlags.LayoutFullscreen;
uiOptions |= (int)SystemUiFlags.LowProfile;
uiOptions |= (int)SystemUiFlags.HideNavigation;
uiOptions |= (int)SystemUiFlags.Fullscreen;
uiOptions |= (int)SystemUiFlags.ImmersiveSticky;
this.Window.DecorView.SystemUiVisibility = (StatusBarVisibility)uiOptions;
}
catch (System.Exception Ex)
{
}
}
}
}
The problem is that "Window.Devorview" has errors:
"Abstract base class for a top-level window look and behaviour policy.
An object reference is required for the non-static field, method, or
property 'Window.DecorView;"
and "this.Window"
"SoftwareButtonsHandler' does not contain definition for 'Window' and
no accessible extension method 'Window' accepting a first argument of
type 'SoftwareButtonsHandler' could be found (are you missing a using
directive or an assembly reference?"
Is there a way to call MainActivity.cs's method from another class or get necessary "Window" in dependency handler (that is accessible only in MainActivity.cs) ?
You can create a MainActivityInstance in MainActivity and set it like MainActivityInstance = this in OnCreate:
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
public static MainActivity MainActivityInstance { get; private set; }
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
MainActivityInstance = this;
LoadApplication(new App());
}
}
Then you can get the MainActivity in dependency interface:
public class SoftwareButtonsHandler : ISoftwareButtons
{
void ISoftwareButtons.HideSoftwareMenuBars()
{
try
{
int uiOptions = (int)MainActivity.MainActivityInstance.Window.DecorView.SystemUiVisibility;
uiOptions |= (int)SystemUiFlags.LayoutStable;
uiOptions |= (int)SystemUiFlags.LayoutHideNavigation;
uiOptions |= (int)SystemUiFlags.LayoutFullscreen;
uiOptions |= (int)SystemUiFlags.LowProfile;
uiOptions |= (int)SystemUiFlags.HideNavigation;
uiOptions |= (int)SystemUiFlags.Fullscreen;
uiOptions |= (int)SystemUiFlags.ImmersiveSticky;
MainActivity.MainActivityInstance.Window.DecorView.SystemUiVisibility = (StatusBarVisibility)uiOptions;
}
catch (System.Exception Ex)
{
}
}
}
You can make a static reference to MainActivity that will be accessible everywhere, or you may use CurrentActivity plugin.

How can we change the ringer mode in xamarin form

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();
}

Xamarin, how to clear intentExtra after use

My app showing notification on specific time. Notification has got pending intent with note id. When user tapped on notification, this note id is use to launch app and load note with id that come from notification.
Basically it works fine but problem starts when user leave app using "back button". On next application start that note id is still in memory and app takes user to that note again.
How can I clear that intent from memory after first use.
MainActivity.cs
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
public static Context AndroidContext { get; private set; }
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
//noteId is a id of Note thet notification was dispaly and tapped by user
//Id is pass to xamarin PCL and proper note basic on this id is displayed
int noteId = Intent.GetIntExtra("noteId", 0);
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
CalligraphyConfig.InitDefault(new CalligraphyConfig.Builder()
.SetDefaultFontPath("Dosis-Regular.ttf")
.SetFontAttrId(Resource.Attribute.fontPath)
.Build()
);
AndroidContext = this;
global::Xamarin.Forms.Forms.Init(this, bundle);
Xamarin.FormsGoogleMaps.Init(this, bundle);
LoadApplication(new App(noteId));
}
protected override void AttachBaseContext(Context context)
{
base.AttachBaseContext(CalligraphyContextWrapper.Wrap(context));
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
BroadcastReciver
class AlamReciver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
int noteId = intent.GetIntExtra("id",0);
var noteTitle = intent.GetStringExtra("title");
var noteContent = intent.GetStringExtra("content");
var resultIntent = new Intent(context, typeof(MainActivity));
resultIntent.PutExtra("noteId", noteId);
resultIntent.SetFlags(ActivityFlags.ClearTop | ActivityFlags.ClearTask);
var PI = PendingIntent.GetActivity(context, noteId, resultIntent, PendingIntentFlags.OneShot);
var builder = new Notification.Builder(context)
.SetContentTitle(noteTitle)
.SetContentText(noteContent)
.SetSmallIcon(Resource.Drawable.ic_gnote_logo)
.SetPriority(1)
.SetAutoCancel(true)
.SetDefaults(NotificationDefaults.All)
.SetContentIntent(PI);
var notification = builder.Build();
var manager = NotificationManager.FromContext(context);
manager.Notify(noteId, notification);
}
}
Thank you.

Xamarin BLE scanning only sometimes

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);
}
}

Resources