I have an app setup and I have been testing it with Android pretty successfully but now I am trying to add in iOS and it doesn't seem to work at all.
I have a MasterDetailPage that I mostly copied from the mobileCRM project which works fine for me on Adroid and my buildhost but not in my ported over version.
public class RootPage : MasterDetailPage
{
OptionItem _previousItem;
public RootPage()
{
var optionsPage = new MenuPage { Icon = "Icon.png", Title = "menu" };
optionsPage.Menu.ItemSelected += (sender, e) => NavigateTo(e.SelectedItem as OptionItem);
Master = optionsPage;
NavigateTo(optionsPage.Menu.ItemsSource.Cast<OptionItem>().First());
}
void NavigateTo(OptionItem option)
{
if (_previousItem != null)
_previousItem.Selected = false;
option.Selected = true;
_previousItem = option;
var displayPage = PageForOption(option);
Detail = new NavigationPage(displayPage)
{
BarBackgroundColor = Helpers.Color.Orange.ToFormsColor()
};
Detail.SetValue(TitleProperty, option.Title);
Detail.SetValue(IconProperty, option.Icon);
IsPresented = false;
}
static Page PageForOption(OptionItem option)
{
switch (option.Title)
{
case "Home":
return new HomePage();
case "Circles":
return new CirclesPage();
case "Settings":
return new SettingsPage ();
default:
throw new NotImplementedException("Unknown menu option: " + option.Title);
}
}
}
This looks like a bug in Xamarin.Forms, and I've reported it.
I was able to reproduce your issue, but also able to workaround it by replacing the ListView in OptionPage by a StackLayout of Buttons :
public MenuPage (MasterDetailPage mdpage)
{
Content = new StackLayout {
Children = {
new Button { Text = "Home", Command = new Command (() => {mdpage.Detail = new HomePage ();mdpage.IsPresented = false;}) },
new Button { Text = "Circles", Command = new Command (() => {mdpage.Detail = new CirclesPage ();mdpage.IsPresented = false;}) },
new Button { Text = "Settings", Command = new Command (() => {mdpage.Detail = new SettingsPage ();mdpage.IsPresented = false;}) },
}
};
}
In one of my projects, I'm using a TableView in the Master, but just like the ListView here, it triggers the issue.
Related
I'm trying also to generate from a database list the Shell Flyout elements. But I am not sure that implementing directly in AppShell.xaml.cs is a good idea. Is it possible to do the same thing but in
xaml?
I saw something similarly in Define FlyoutItem appearance. But I still can't figure it out.
Also, is it a good idea to implement code directly into AppShell.xaml.cs or App.xaml.cs?
LocationLevelViewModel diudu = new LocationLevelViewModel();
var lists = diudu.Items;
foreach (var list in lists)
{
ShellSection shell_section = new ShellSection();
shell_section.Title = list.LocationName;
shell_section.Icon = "icon_feed.png";
shell_section.Items.Add(new ShellContent() { Content = new LocationLevelPage() });
this.Items.Add(shell_section);
}
The code you provided, you could create a new class named AppShellCS and use teh code in a new AppShellCS.cs file like below. It works well. I made a simple example to test instead of database.
public class AppShellCS : Shell
{
public AppShellCS()
{
LocationLevelViewModel diudu = new LocationLevelViewModel();
//var lists = diudu.Items;
var lists = new List<LocationLevelViewModel>
{
new LocationLevelViewModel() { LocationName="A"},
new LocationLevelViewModel() { LocationName="B"},
new LocationLevelViewModel() { LocationName="C"}
};
foreach (var list in lists)
{
ShellSection shell_section = new ShellSection();
shell_section.Title = list.LocationName;
shell_section.Icon = "tab_feed.png";
shell_section.Items.Add(new ShellContent() { Content = new LocationLevelPage() });
this.Items.Add(shell_section);
}
}
}
public class LocationLevelViewModel
{
public string LocationName { get; set; }
}
App.xaml.cs
public App()
{
InitializeComponent();
DependencyService.Register<MockDataStore>();
MainPage = new AppShellCS();
}
OutPut:
If you want to add this in a exist Shell project, you could try the code below.
AppShell.xaml.cs
public partial class AppShell : Xamarin.Forms.Shell
{
public AppShell()
{
InitializeComponent();
//Routing.RegisterRoute("step", typeof(Page2));
LocationLevelViewModel diudu = new LocationLevelViewModel();
//var lists = diudu.Items;
var lists = new List<LocationLevelViewModel>
{
new LocationLevelViewModel() { LocationName="A"},
new LocationLevelViewModel() { LocationName="B"},
new LocationLevelViewModel() { LocationName="C"}
};
foreach (var list in lists)
{
ShellSection shell_section = new ShellSection();
shell_section.Title = list.LocationName;
shell_section.Icon = "tab_feed.png";
shell_section.Items.Add(new ShellContent() { Content = new LocationLevelPage() });
this.Items.Add(shell_section);
}
}
}
Output:
I am fairly new at working with the Android code in Visual Studio 2019. The ultimate result desired here was to be able to display the various wifi Access Points near the phone. I have gotten the wifi scan to work and I can see the results of the scan in the "WifiScans" collection but the listWifiScan ListView does not show the results on the display. I can see lines on the display for the number of items that should be displayed. If I touch one of the items I can see it turn to a solid color (Orange) but the information is not displayed. Can anyone tell me what I am doing wrong? Thanks for any help you can give.
using Android;
using Android.Content.PM;
using Android.Support.V4.App;
using Android.Support.V4.Content;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Text;
using Xamarin.Forms;
namespace MobileWifi
{
public partial class MainPage : ContentPage
{
ObservableCollection<WifiScan> WifiScans { get; set; }
Label lblTitle;
ListView listWifiScan;
readonly Button btnWifiScan;
public static bool response = false;
public MainPage()
{
// add code to handle the components here
WifiScans = new ObservableCollection<WifiScan>();
this.Padding = new Thickness(20,20,20,20); // wall padding for page
// Listview data template
var scanDataTemplate = new DataTemplate(() =>
{
var grid = new Grid();
var bssidLabel = new Label { FontAttributes = FontAttributes.Bold };
var ssidLabel = new Label();
var levelLabel = new Label { HorizontalTextAlignment = TextAlignment.End };
bssidLabel.SetBinding(Label.TextProperty, "BSSID");
ssidLabel.SetBinding(Label.TextProperty, "SSID");
levelLabel.SetBinding(Label.TextProperty, "Level");
grid.Children.Add(bssidLabel);
grid.Children.Add(ssidLabel, 1, 0);
grid.Children.Add(levelLabel, 2, 0);
return new ViewCell { View = grid };
});
// setup StackLayout for controls and set spacing of controls within the layout
StackLayout panel = new StackLayout
{
Spacing = 15,
Margin = new Thickness(20),
};
panel.Children.Add(lblTitle = new Label
{
Text = "Mobile Wifi Scanner",
HorizontalTextAlignment = TextAlignment.Center,
FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
FontAttributes = FontAttributes.Bold,
});
panel.Children.Add(btnWifiScan = new Button
{
Text = "Start Scan"
});
panel.Children.Add(listWifiScan = new ListView
{
ItemsSource = WifiScans,
HasUnevenRows = true,
ItemTemplate = scanDataTemplate,
Margin = new Thickness(0, 20, 0, 0),
});
btnWifiScan.Clicked += OnBtnWifiScanClicked;
this.Content = panel;
}
private void OnBtnWifiScanClicked(object sender, EventArgs e)
{
btnWifiScan.Text = "Scanning...";
WifiScans.Clear(); // clear out results from previous scan
try
{
IWifiScan service = DependencyService.Get<IWifiScan>().GetObj();
service.Start();
service.Finished += WifiScanDone;
}
catch (Exception ex)
{
btnWifiScan.Text = "Start Scan";
DisplayAlert("Alert", ex.Message, "OK");
}
}
public void WifiScanDone(object sender,EventArgs e)
{
IWifiScan service = DependencyService.Get<IWifiScan>();
List<string> WifiBSSID = service.GetBSSID();
List<string> WifiSSID = service.GetSSID();
List<int> WifiLevel = service.GetLevel();
int count = WifiBSSID.Count;
// add logic here to display the data from the scan
// add results of the scan
if(count > 0)
{
for (int i = 0; i < count; i++)
{
WifiScans.Add(new WifiScan { WifiBSSID = WifiBSSID[i], WifiSSID = WifiSSID[i], WifiLevel = WifiLevel[i].ToString()});
}
}else
{
WifiScans.Add(new WifiScan { WifiBSSID = "None Found", WifiSSID = "", WifiLevel = "0"});
}
btnWifiScan.Text = "Start Scan";
// finalize the scan, etc.
service.Done();
}
}
}
I finally found the problem. It turns out I did not specify the correct property in the binding. The correct code for the binding in the data template should have been:
bssidLabel.SetBinding(Label.TextProperty, "WifiBSSID");
ssidLabel.SetBinding(Label.TextProperty, "WifiSSID");
levelLabel.SetBinding(Label.TextProperty, "WifiLevel");
I'm not sure where I got the original values but after studying the data binding properties I realized they were wrong. Also I did not include the source in the original question that would have helped identify the problem so I am adding it here in case it is helpful to someone else that may have a similar problem.
public class WifiScan
{
public string WifiSSID { get; set; }
public string WifiBSSID { get; set; }
public string WifiLevel { get; set; }
}
I am currently trying to use a REST service inside a xamarin.forms app.
To perform the authentication I use this code:
string consumerKey = "consumer_key";
string consumerSecret = "consumer_secret";
var requestTokenUrl = new Uri("https://service/oauth/request_token");
var authorizeUrl = new Uri("https://dservice/oauth/authorize");
var accessTokenUrl = new Uri("https://service/oauth/access_token");
var callbackUrl = new Uri("customprot://oauth1redirect");
authenticator = new Xamarin.Auth.OAuth1Authenticator(consumerKey, consumerSecret, requestTokenUrl, authorizeUrl, accessTokenUrl, callbackUrl, null, true);
authenticator.ShowErrors = true;
authenticator.Completed += Aut_Completed;
var presenter = new Xamarin.Auth.Presenters.OAuthLoginPresenter();
presenter.Completed += Presenter_Completed;
authenticator.Error += Authenticator_Error;
presenter.Login(authenticator);
Now, after authenticating the user will be redirected to customprot://oauth1redirect. To catch this redirection I added a new IntentFilter (for Android) like this:
[Activity(Label = "OAuthLoginUrlSchemeInterceptorActivity", NoHistory = true, LaunchMode = LaunchMode.SingleTop)]
[IntentFilter(
new[] { Intent.ActionView },
Categories = new[] { Intent.CategoryDefault, Intent.CategoryBrowsable },
DataSchemes = new[] { "customprot"},
DataPathPrefix = "/oauth1redirect")]
public class OAuthLoginUrlSchemeInterceptorActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Convert Android.Net.Url to Uri
var uri = new Uri(Intent.Data.ToString());
// Load redirectUrl page
Core.Controller.authenticator.OnPageLoading(uri);
Core.Controller.authenticator.OnPageLoaded(uri);
Finish();
}
}
As far as I understood the documentation of xamarin.auth this will trigger the OAuth1Authenticator to parse the resulting url to get the authenticated user's credentials, and ultimatley triggering the Completed or Error event. But suprisingly nothing happens: no event is called or error raised. As this makes debugging harder, I do not really know how to solve this issue. Therefore, I am looking for suggestings about the cause of the issue and possible solutions, too.
Edit: Just to make this clearer: The OnCreate method of the intent is called, but executing the OnPageLoading method does not raise the Completed nor the Error event of the authenticator.
Edit2: here is the code of my callbacks (I created a breakpoint inside each of them, and the debugger does not break at them or raise an exception, so I am quite sure, that the callbacks are not called at all).
private static void Presenter_Completed(object sender, Xamarin.Auth.AuthenticatorCompletedEventArgs e)
{
throw new NotImplementedException();
}
private static void Aut_Completed(object sender, Xamarin.Auth.AuthenticatorCompletedEventArgs e)
{
throw new NotImplementedException();
}
This may only help future people (like me) that stumble on this question but perhaps not answer your particular issue. I was experiencing the same symptoms using the OAuth2Authenticator. I was capturing the redirect, calling OnPageLoading(), but then neither my completed or error events were firing.
The key for me was that it was only happening the 2nd time I called the Authenticator.
After digging through the Xamarin.Auth source, I realized that if HasCompleted is true when the authenticator calls OnSucceeded(), it simply returns without raising any events:
From Authenticator.cs
public void OnSucceeded(Account account)
{
string msg = null;
#if DEBUG
string d = string.Join(" ; ", account.Properties.Select(x => x.Key + "=" + x.Value));
msg = String.Format("Authenticator.OnSucceded {0}", d);
System.Diagnostics.Debug.WriteLine(msg);
#endif
if (HasCompleted)
{
return;
}
HasCompleted = true;
etc...
So, my issue was that I was keeping my authenticator instance around. Since HasCompleted is a private set property, I had to create a new authenticator instance and now it all works as expected.
Maybe I should have posted a new question and answered it. I'm sure the community will let me know.
I have also run into this issue but after managed to get this part working
I create my OAuth2Authenticatoras follows:
App.OAuth2Authenticator = new OAuth2Authenticator(
clientId: OAuthConstants.CLIENT_ID,
clientSecret: null,
scope: OAuthConstants.SCOPE,
authorizeUrl: new Uri(OAuthConstants.AUTHORIZE_URL),
accessTokenUrl: new Uri(OAuthConstants.ACCESS_TOKEN_URL),
redirectUrl: new Uri(OAuthConstants.REDIRECT_URL), //"com.something.myapp:/oauth2redirect" -- note I only have one /
getUsernameAsync: null,
isUsingNativeUI: true);
then in my Interceptor activity:
[Activity(Label = "GoogleAuthInterceptor")]
[IntentFilter
(
actions: new[] { Intent.ActionView },
Categories = new[]
{
Intent.CategoryDefault,
Intent.CategoryBrowsable
},
DataSchemes = new[]
{
// First part of the redirect url (Package name)
"com.something.myapp"
},
DataPaths = new[]
{
// Second part of the redirect url (Path)
"/oauth2redirect"
}
)]
public class GoogleAuthInterceptor: Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Create your application here
Android.Net.Uri uri_android = Intent.Data;
// Convert Android Url to C#/netxf/BCL System.Uri
Uri uri_netfx = new Uri(uri_android.ToString());
// Send the URI to the Authenticator for continuation
App.OAuth2Authenticator?.OnPageLoading(uri_netfx);
// remove your OnPageLoaded it results in an invalid_grant exception for me
Finish();
}
}
You can try changing your DataPathPrefix = "/oauth1redirect")] to
DataPaths = new[]
{
// Second part of the redirect url (Path)
"/oauth1redirect"
}
This successfully trigger the Completed event on the OAuth2Authenticator and then after that the one on the presenter
private async void OAuth2Authenticator_Completed(object sender, AuthenticatorCompletedEventArgs e)
{
try
{
// UI presented, so it's up to us to dimiss it on Android
// dismiss Activity with WebView or CustomTabs
if(e.IsAuthenticated)
{
App.Account = e.Account;
var oAuthUser = await GetUserDetails();
// Add account to store
AccountStore.Create().Save(App.Account, App.APP_NAME_KEY);
}
else
{
// The user is not authenticated
// Show Alert user not found... or do new signup?
await App.Notify("Invalid user. Please try again");
}
}
catch(Exception ex)
{
throw;
}
}
At this stage I am redirected to the App.
I am currently trying to solve an issue where the presenter is not closed. It runs in the background even though the app is in the foreground and the user already authenticated. But this should hopefully help you solve your issue.
I'm making a very simple software with a TCP Listener that (until now) only receives a message from a TCP Client codified in ASCII and I'll have to do something with the UI that I still don't know, but by now, I'm just trying to show an AlertDialog with this message on Samsung Galaxy Tab.
The problem is that, I believe that for some reason the setContentView is not working. I have one .axml (layout) file with an AbsoluteLayout, and I'm calling this AbsoluteLayout on code, changing its color, and trying to show this AbsoluteLayout (with its color changed) on the screen, but the problem is that I just see the regular black screen.
I started debugging the code, and I can see the all the Console.Writeline commands on the output of the MS VS 2010, even the message sent from the client. But I can't see the layout and the AlertDialog.
Can anybody help me? Thanks in advance.
using System;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Android.Graphics.Drawables;
using System.Drawing;
namespace Gafisa.Automacao.VideoWall.Listener
{
[Activity(Label = "Listener", MainLauncher = true, Icon = "#drawable/icon")]
public class Activity1 : Activity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
AbsoluteLayout abs = FindViewById<AbsoluteLayout>(Resource.Id.abslayout);
abs.SetBackgroundColor(new Android.Graphics.Color(125,125,125,125));
//ImageButton btn = new ImageButton(this);
//var lp = new AbsoluteLayout.LayoutParams(50, 50, 200, 200);
//btn.LayoutParameters = lp;
//BitmapDrawable dd = new BitmapDrawable("/mnt/sdcard/1.png");
//btn.SetBackgroundDrawable(dd);
//abs.AddView(btn);
System.Net.Sockets.TcpListener listener = null;
byte[] rcvBuffer = new byte[40];
int bytesRcvd;
try
{
listener = new System.Net.Sockets.TcpListener(IPAddress.Any, 13000);
listener.Start();
Console.WriteLine("Listener iniciado");
}
catch (SocketException se)
{
Console.WriteLine("Erro ao iniciar o listener: " + se.Message);
}
for (;;)
{
TcpClient client = null;
NetworkStream netStream = null;
try
{
client = listener.AcceptTcpClient();
netStream = client.GetStream();
int totalBytesEchoed = 0;
while ((bytesRcvd = netStream.Read(rcvBuffer, 0, rcvBuffer.Length)) > 0)
{
netStream.Write(rcvBuffer, 0, bytesRcvd);
totalBytesEchoed += bytesRcvd;
}
string recebido = System.Text.Encoding.ASCII.GetString(rcvBuffer);
Console.WriteLine(recebido);
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.SetMessage(recebido);
alert.SetTitle("Mensagem Recebida");
alert.Show();
Console.WriteLine("echoed {0} bytes.", totalBytesEchoed);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Console.WriteLine("Erro no LOOP");
}
finally
{
netStream.Close();
client.Close();
}
}
}
}
}
By running an infinite loop in this OnCreate function, you prevents the UI framework from finishing rendering. That's why what you see is only the black screen.
You are supposed to run non-UI code asynchronously (in a separate thread).
Change it to
Task.Factory.StartNew(() =>
{
for (;;)
{
TcpClient client = null;
NetworkStream netStream = null;
try
{
client = listener.AcceptTcpClient();
netStream = client.GetStream();
int totalBytesEchoed = 0;
while ((bytesRcvd = netStream.Read(rcvBuffer, 0, rcvBuffer.Length)) > 0)
{
netStream.Write(rcvBuffer, 0, bytesRcvd);
totalBytesEchoed += bytesRcvd;
}
string recebido = System.Text.Encoding.ASCII.GetString(rcvBuffer);
Console.WriteLine(recebido);
RunOnUiThread(() =>
{
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.SetMessage(recebido);
alert.SetTitle("Mensagem Recebida");
alert.Show();
}
Console.WriteLine("echoed {0} bytes.", totalBytesEchoed);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Console.WriteLine("Erro no LOOP");
}
finally
{
netStream.Close();
client.Close();
}
}
}
Also with Mono for Android (Xamarin.Android) you should use Log.Info(string tag, string message) where tag is the name of calling class. Don't use Console.WriteLine(string). You can also use Log.Warn(string, string) and Log.Error(string, string)
I have a doubt
I created an application that takes pictures, sends it to a database and then print on some page.
When I go to put the application to function, the camera appears correctly. But it only on the desktop. When I turn the fla in an apk and try running on android, facing the camera appears in the horizontal.
How can I solve this problem?
The code is here:
import com.adobe.images.JPEGEncoder;
var video:Video;
var camera:Camera;
var imgBA:ByteArray;
var imgBD:BitmapData;
var imgBitmap:Bitmap;
var phpPath:String;
var jpgEncoder:JPEGEncoder;
var sendHeader:URLRequestHeader;
var sendReq:URLRequest;
var sendLoader:URLLoader;
var imagePath:String;
setupCamera(430,500);
var inicializado:Boolean=false;
status_txt.text="esperando coordenadas";
var geolocationmarcelinho:MarcelinhoGeolocation=new MarcelinhoGeolocation(geoUpdate);
function geoUpdate(lat:Number,lon:Number)
{
if(inicializado)
{
lat_txt.text=lat+"";
lon_txt.text=lon+"";
}
else
{
setupApplication();
lat_txt.text=lat+"";
lon_txt.text=lon+"";
if(lat==-1)
{
status_txt.text="sem geolocation";
}
else
{
status_txt.text="pode usar!";
}
inicializado=true;
}
}
//setupApplication();
function setupCamera(w:int,h:int):void {
try {
camera = Camera.getCamera();
} catch(e:Error) {
status_txt.text="camera não conectada!";
trace("No Camera detected!");
}
camera.addEventListener(StatusEvent.STATUS, camStatusHandler);
camera.setMode(w,h,stage.frameRate);
video = new Video(w,h);
video.attachCamera(camera);
addChild(video);
}
function camStatusHandler(event:StatusEvent):void {
// Camera.Muted or Camera.Unmuted -> User's security
trace(event.code);
}
function setupApplication():void {
shotBtn.addEventListener(MouseEvent.CLICK, createSnapshot);
removeBtn.addEventListener(MouseEvent.CLICK, removeSnapshot);
sendBtn.addEventListener(MouseEvent.CLICK, sendImage);
jpgEncoder = new JPEGEncoder(90);
}
function createSnapshot(event:MouseEvent):void {
imgBD = new BitmapData(video.width,video.height);
imgBD.draw(video);
imgBitmap = new Bitmap(imgBD);
addChild(imgBitmap);
shotBtn.removeEventListener(MouseEvent.CLICK, createSnapshot);
}
function removeSnapshot(event:MouseEvent):void {
removeChild(imgBitmap);
comentario_txt.text="";
status_txt.text="pode usar!";
shotBtn.addEventListener(MouseEvent.CLICK, createSnapshot);
}
function sendImage(event:MouseEvent):void {
phpPath = "http://celoweb.com.br/app/img_aplicativo/upload.php?comentario=qualquercoisa&latitude=34&longitude=-45"
+comentario_txt.text+"&latitude="+lat_txt.text+"&longitude="+lon_txt.text;
sendHeader = new URLRequestHeader("Content-type","application/octet-stream");
sendReq = new URLRequest(phpPath);
sendReq.requestHeaders.push(sendHeader);
sendReq.method = URLRequestMethod.POST;
sendLoader = new URLLoader();
sendLoader.addEventListener(Event.COMPLETE,imageSentHandler);
imgBA = jpgEncoder.encode(imgBD);
sendReq.data = imgBA;
sendLoader.load(sendReq);
}
function imageSentHandler(event:Event):void {
var dataStr:String = event.currentTarget.data.toString();
var resultVars:URLVariables = new URLVariables();
resultVars.decode(dataStr);
imagePath = "http://" + resultVars.base + "/" + resultVars.filename;
status_txt.text="Uploaded to: " + imagePath;
trace("Uploaded to: " + imagePath);
sendLoader.removeEventListener(Event.COMPLETE,imageSentHandler);
}
Agreed your question is a little unclear but at a guess...
To rotate your image you would use: imgBitmap.rotation = 90;
or to flip:
imgBitmap.scaleX = -1;