PopToRoot while retap active tab in TabbedPage in MAUI Android - android

How can I setup a PopToRoot action while tapping active tab on Android devices. On iOS it works fine but on android nothing happened if i clicked active tab again.
In Xamarin using shell i use a code :
[assembly: ExportRenderer(typeof(Shell), typeof(JknShellRenderer))]
namespace MyApp.Droid.Renderers
{
/// <summary>
/// The JknShellRenderer is necessary in order to replace the ShellItemRenderer with your own.
/// </summary>
class JknShellRenderer : ShellRenderer
{
public JknShellRenderer(Context context) : base(context)
{
}
protected override IShellItemRenderer CreateShellItemRenderer(ShellItem shellItem)
{
return new JknShellItemRenderer(this);
}
}
/// <summary>
/// This renderer is necessary to allow us to handle the TabReselected (current tab clicked) event as it is not implemented by default on Android
/// and the only way is to go through a renderer.
/// </summary>
public class JknShellItemRenderer : ShellItemRenderer
{
public JknShellItemRenderer(IShellContext shellContext) : base(shellContext)
{
}
/// <summary>
/// Pops to root when the selected tab is pressed.
/// </summary>
/// <param name="shellSection"></param>
protected override void OnTabReselected(ShellSection shellSection)
{
Xamarin.Forms.Device.BeginInvokeOnMainThread(async () =>
{
await shellSection?.Navigation.PopToRootAsync();
});
}
}
}
but on MAUI I'm not using a Shell...
What the way to make it in MAUI TabbedPage ?

Related

Pop to root on tabclick FreshTabbedNavigationContainer on android

Consider a tabbar with "home" and "profile" buttons, when i click on either i switch between two pages, on the "home" page the user can navigate multiple times up in the navigationstack still having the focus on the "home" tab indicating that this is where the user came from.
Now, on iOS whenever the user clicks on "home" from high up in the navigationstack the user is popped to root and all is well, this is not the case on android however, on android the user has to pop one page at a time by clicking on the backbutton to get to the root.
Is this intended behavior, am i doing something wrong, does someone have a clue as to what i can do to get the desired behavior?
This is the intended behavior between iOS and Android .
If you need to make the Android has the same effect with iOS, you need to custom TabbedPageRenderer to achieve that. And the bottom tab bar effect can custom a FreshTabbedNavigationContainer . Last, we will use MessagingCenter to send message to Forms to pop to Root Page.
For example, CustomFreshTabbedNavigationContainer class:
public class CustomFreshTabbedNavigationContainer : FreshTabbedNavigationContainer
{
public CustomFreshTabbedNavigationContainer()
{
On<Android>().SetToolbarPlacement(ToolbarPlacement.Bottom);
MessagingCenter.Subscribe<object>(this, "Hi", (sender) =>
{
// Do something whenever the "Hi" message is received
PopToRoot(true);
});
}
}
Used in App.xaml.cs:
public App()
{
InitializeComponent();
var container = new CustomFreshTabbedNavigationContainer();
container.AddTab<FirstPageModel>("Home", default);
container.AddTab<ProfilePageModel>("Profile", default);
MainPage = container;
}
Now we will create a CustomTabbedPageRenderer in Android:
public class CustomTabbedPageRenderer : TabbedPageRenderer, BottomNavigationView.IOnNavigationItemSelectedListener
{
public CustomTabbedPageRenderer(Context context) : base(context)
{
}
int previousItemId = 0;
bool BottomNavigationView.IOnNavigationItemSelectedListener.OnNavigationItemSelected(IMenuItem item)
{
base.OnNavigationItemSelected(item);
if (item.IsChecked)
{
if (previousItemId != item.ItemId)
{
previousItemId = item.ItemId;
}
else
{
Console.WriteLine("ok");
MessagingCenter.Send<object>(this, "Hi");
}
}
return true;
}
}
The effect:
Note: If need to have the same effect with the top Tabbar in Android, there is different code in CustomTabbedPageRenderer. You can have a look at this discussion.

React Native handle on java side addListener

I use addListener method of NativeEventEmitter to add listener from JavaScript to native side.
import {NativeModules, NativeEventEmitter, EmitterSubscription} from 'react-native';
const CoreBridge = NativeModules.Core;
class Core extends NativeEventEmitter {
constructor() {
super(CoreBridge);
}
addListener(event: CoreEvent, handler: Function): EmitterSubscription {
return super.addListener(event, handler);
}
}
export default new Core();
Can I handle addListener() on the native Java side? I have try with this but it's not called:
#ReactMethod
public void addListener(String event) {
//register native listener for event emitting
}
For iOS this can be done:
#pragma mark - RCTEventEmitter
- (NSArray<NSString > )supportedEvents {
return #[EVENT_ONE,
EVENT_TWO];
}
/// This method will be called when the first event listener is added.
- (void)startObserving {}
/// This method will be called when the last event listener is removed.
- (void)stopObserving {}
Can I have the same event listeners handling for Android?

Is there a better way to do a PopToRoot async without leaking GRefs?

According to this bug: bugzilla.xamarin.com/show_bug.cgi?id=52597 the PopToRoot of the Navigation object still leak GRefs (in fact JNI Global References)
In one application we use Listview really intensively and to avoid crash I had to implement a workaround using two methods :
/// <summary>
/// Workaround to minimize the quantity of GREFs leaked by Xamarin.Forms JMA 17.02.2017
/// </summary>
/// <param name="animate">if set to <c>true</c> [animate].</param>
public async Task PopToRoot(bool animate)
{
var nav = Application.Current.MainPage.Navigation;
if(animate == false) //simulate animate = false
for (int i = 1; i < nav.NavigationStack.Count; i++)
if(nav.NavigationStack[i] is BaseView)
{
//hide the animation
(nav.NavigationStack[i] as BaseView).IsVisible = false;
//hide the toolbar title change
(nav.NavigationStack[i] as BaseView).Title = (nav.NavigationStack[nav.NavigationStack.Count - 1] as BaseView).Title;
}
await _PopToRootInner(true);
}
/// <summary>
/// Workaround to minimize the quantity of GREFs leaked by Xamarin.Forms JMA 17.02.2017
/// </summary>
/// <param name="animate">if set to <c>true</c> [animate].</param>
private async Task _PopToRootInner(bool animate)
{
var nav = Application.Current.MainPage.Navigation;
await PopAsync(animate);
if (nav.NavigationStack.Count > 1 && nav.NavigationStack[nav.NavigationStack.Count - 1] is BaseView)
await (nav.NavigationStack[nav.NavigationStack.Count - 1] as BaseView)._PopToRootInner(animate);
}
My question is : Do you know any better method to do a PopToRoot without animation and without leaking GRefs ?
And Also, if you know how to correct the bug, I am of course interested !

iBeacon Receiver and Vuforia Autofocus not work on an Android tablet [Unity3d]

I have two problems, about autofocus on Vuforia's AR-camera and iBeacon receiver. These are not work.
However, I see these problem just on an Android tablet(me173x).
I tried with 3 Android phones and iPhone5, 5s and 6+. No problem on them.
Development Environments are:
MacBookPro-Retina15-Early2012 (FullSpec)
OSX 10.10.4 (Latest)
Unity 5.1.1p2 (Latest), Professional (but no iOS-Pro and no Android-Pro)
Vuforia 4.2.3 (Latest)
I captured screenshot of terminal running 'pidcat --min-level w' (levels above warning), because so many logs.
No other logs above warning.
Error logs about linking of class for iBeacon found.
The tablet supports bluetooth 4.0 and BLE, and some camera apps support autofocus.
I guess... these problems due to the device architecture.
Please help me!
P.S.
thank you for your response, jacob.
my code to enable autofocus is:
using UnityEngine;
using Vuforia;
using System;
public class VuforiaARCameraAutoFocusMultiSelect : MonoBehaviour {
[SerializableAttribute]
public struct FocusModes {
public CameraDevice.FocusMode secondary;
public CameraDevice.FocusMode primary;
}
public FocusModes iOSFocusModes;
public FocusModes androidFocusModes;
public FocusModes defaultFocusModes;
void Start() {
var qcar = FindObjectOfType<QCARAbstractBehaviour>();
if (qcar != null) {
qcar.RegisterQCARStartedCallback(OnQCARStarted);
qcar.RegisterOnPauseCallback(OnQCARPaused);
Debug.Log("QCARBehaviour Found in current scene");
} else
Debug.LogError("Failed to find QCARBehaviour in current scene..");
}
/// <summary>
/// Raises the QCAR started event.
/// </summary>
private void OnQCARStarted() {
Debug.Log("OnQCARStarted");
SetFocusMode();
}
/// <summary>
/// Raises the QCAR paused event.
/// </summary>
/// <param name="paused">true: paused, false: resumed</param>
private void OnQCARPaused(bool paused) {
if (paused) {
Debug.Log("OnQCARPaused");
} else {
Debug.Log("OnQCARResumed");
SetFocusMode();
}
}
private void SetFocusMode() {
FocusModes modes;
#if UNITY_IOS
modes = iOSFocusModes;
#elif UNITY_ANDROID
modes = androidFocusModes;
#else
modes = defaultFocusModes;
#endif
if (CameraDevice.Instance.SetFocusMode(modes.primary))
Debug.LogFormat("Successfully enabled autofocus mode: {0}", modes.primary);
else if (CameraDevice.Instance.SetFocusMode(modes.secondary))
Debug.LogFormat("Successfully enabled autofocus mode: {0}", modes.secondary);
else
Debug.LogError("Couldn't enabled autofocus!!");
}
}
This code attached to the 'AR Camera' gameobject in the scene.

Bug after Samsung update | Monogame | Detecting viewport.height as devices width

UPDATE
This issue have also been discussed in: https://github.com/mono/MonoGame/issues/2492
The problem is if the app is only allowed to run in Landscape orientation, not when you use Portrait or both.
Got a android game coded in monogame, where I got a GraphicsDeviceManager which are set to FullScreen=true.
I use the GraphicsDevice.Viewport.Height and the GraphicsDevice.Viewport.Width to determine the resolution of the device.
This was working very good until I got an samsung update and had to turn FastDev off.
The big mysterious problem:
When I debug with the pc, the height and width of the viewport is set correct. But when i unplug and play the app after 1-2 times the viewport.Height becomes the devices width, and the viewport.Width becomes the device height, which completely makes the game unplayable.
Its very hard to find the solution, since this is never happening when i debug and got the cable in from pc to the device.
Anyone got any ideas what it can be?
I can now confirm that it is because of the samsung update
I made worlds simplest android app to test it, just got a mainframe with a background image called "bg" and a spritefront printing out the viewport.Width and viewport.Height.
Here's the code:
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace TestRes
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
SpriteFont font;
Rectangle _mainFrame;
Texture2D _background;
string text;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.IsFullScreen = true;
//graphics.PreferredBackBufferWidth = 800;
// graphics.PreferredBackBufferHeight = 480;
graphics.SupportedOrientations = DisplayOrientation.LandscapeLeft;
}
/// <summary>
/// Allows the game to perform any initialization it needs to before starting
/// This is where it can query for any required services and load any non-
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
// TODO: Add your initialization logic here
base.Initialize();
text = "";
}
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
_background = Content.Load<Texture2D>("Bilder/bg");
_mainFrame = new Rectangle(0, 0, this.GraphicsDevice.Viewport.Width, this.GraphicsDevice.Viewport.Height);
// TODO: use this.Content to load your game content here
font = Content.Load<SpriteFont>("spriteFont1");
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
{
Exit();
}
text = "ScreenWidth: " + this.GraphicsDevice.Viewport.Width + ", screenheight: " + this.GraphicsDevice.Viewport.Height;
// TODO: Add your update logic here
base.Update(gameTime);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
spriteBatch.Draw(_background, _mainFrame, Color.White);
spriteBatch.DrawString(font, text, new Vector2(16, 1000), Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
}
}
When deploying from VS everything is good, the viewport.width is the actual device width and the viewport.height is the actual device height. But when trying to deploy it from the Samsung Galaxy S4 active (sometimes you need to try 2-3 times) then all of a sudden Viewport.Height is the device Width and the other way around, which makes the background picture just cover a bit of the screen.
Have taken pictures to show it:
Checked in version 3.6 , and the bug is fixed.

Categories

Resources