I am trying to register a Xamarin app to listen for value changes on a particular value change. I can access the value, but for some reason when I listen to the value change, it will fire once but never again until I reboot.
I am using Xamarin Studio, and FireSharp libraries. This is the API code in the C# library portion of the app. The reason I have removed the delegate was to check if it wasn't the delegate being cleaned up after the first call or something.
public class ValueAPI
{
private IFirebaseClient _client;
private ITemperatureListener _listener;
private EventStreamResponse _response;
public ValueAPI()
{
IFirebaseConfig config = new FirebaseConfig
{
AuthSecret = "...",
BasePath = "https://[value-api].firebaseio.com/"
};
_client = new FirebaseClient(config);
}
public async Task<string> getValue()
{
FirebaseResponse response = await _client.GetAsync("VALUE");
return response.Body;
}
public async Task<string> registerForUpdates(IValueListener listener)
{
_listener = listener;
_response = await _client.OnAsync("VALUE", null, this.OnValueChange, null, null);
return _response.ToString();
}
private void OnValueChange(object sender, ValueChangedEventArgs args, object context)
{
if (_listener != null)
{
_listener.OnValueUpdated(args.Data);
}
}
}
ANDROID CODE
ValueAPI api = new ValueAPI();
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
getValue();
getValueUpdates();
}
private async void getValue()
{
Task<string> task = api.getValue();
string result = await task;
TextView label = (TextView)FindViewById(Resource.Id.label_value);
label.Text = result;
}
private async void getValueUpdates()
{
Task<string> task = api.registerForUpdates(this);
await task;
}
public void OnValueUpdated(string value)
{
TextView label = (TextView)FindViewById(Resource.Id.label_value_updated);
label.Text = value;
}
Related
Opening app from another app can be done like:
public async Task<bool> LaunchUriAsync(string objectNumber)
{
var option = new LauncherOptions();
option.UI.PreferredPlacement = Placement.Right;
option.DesiredRemainingView = ViewSizePreference.UseMore;
return await Launcher.LaunchUriAsync(buildObjectAccessUri(objectNumber), option);
}
private static Uri buildObjectAccessUri(string objectId)
{
return new Uri(String.Format("{0}://{1}/{2}", "myapp", "data", objectId), UriKind.Absolute);
}
in a Command I do:
await MyLauncherService.LaunchUriAsync("whatever");
and in opened application I have a custom start
public class CustomAppStart : MvxAppStart
{
protected override async Task NavigateToFirstViewModel(object hint = null)
{
// hint here is type of `Windows.ApplicationModel.Activation.ProtocolActivatedEventArgs`
}
}
Seems that object hint is type of Windows.ApplicationModel.Activation.ProtocolActivatedEventArgs
The App.xaml.cs of UWP project:
sealed partial class App
{
public App()
{
InitializeComponent();
}
}
public abstract class UwpApp : MvxApplication<Setup<Core.App>, Core.App>
{
private ILoggerService mLoggerService;
public UwpApp()
{
UnhandledException += (sender, args) =>
{
mLoggerService = Mvx.IoCProvider.Resolve<ILoggerService>();
if (mLoggerService == null)
{
return;
}
mLoggerService.Fatal("Unhandled exception!", args.Exception);
};
DebugSettings.BindingFailed += (sender, args) => Debug.WriteLine(args.Message);
TaskScheduler.UnobservedTaskException += (sender, args) =>
{
mLoggerService = Mvx.IoCProvider.Resolve<ILoggerService>();
if (mLoggerService == null)
{
return;
}
foreach (var ex in args.Exception.Flatten().InnerExceptions)
{
mLoggerService.Error(ex.Message + " StackTrace:" + ex.StackTrace);
}
args.SetObserved();
};
Suspending += OnSuspending;
ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.Auto;
}
protected override void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
deferral.Complete();
}
}
Why I got that ?
I expected to be string ...
Where is the mistake ?
I'm using latest version of xamarin and mvvm cross
In your MvxApplication you can override GetAppStartHint to process the object coming into that method and return a string as you expect.
Something like:
public abstract class UwpApp : MvxApplication<Setup<Core.App>, Core.App>
{
protected override object GetAppStartHint(object hint = null)
{
// process hint
//return string
}
}
I am writting an application with xamarin forms for iOS and Android.
I want to pick a photo from image gallery.
I have created an android specific static helper:
var i = new Intent();
i.SetType("*/*");
Forms.Context.StartActivity(Intent.CreateChooser(i, ""));
But i have no way to get the selected picture bytes.
I have seen on android tutorials i should implement onActivityResult, but i am not on an activity, this is a specific static call...
Thanks
Via a Form's dependency service:
Create your dependency interface (IMediaPicker)
Create a Activity subclass (MediaChooserActivityProxy) that will act as your Intent.ActionPick proxy
In your Xamarin.Android implementation of the IMediaPicker, use a AutoResetEvent to convert the Android StartActivityForResult / OnActivityResult callback to an await-able synchronous flow.
Dependency Service Interace:
public interface IMediaPicker
{
Task<string> ChooseAFileAsync();
}
Android Dependency Implementation:
public class MediaPicker : IMediaPicker
{
public static string filePickedPath;
public static AutoResetEvent waitHandle;
public async Task<string> ChooseAFileAsync()
{
waitHandle = new AutoResetEvent(false);
filePickedPath = "";
Forms.Context.StartActivity(typeof(MediaChooserActivityProxy));
await Task.Run(() => waitHandle.WaitOne());
return filePickedPath;
}
}
The Proxy/Pseudo Activity to capture OnActivityResult:
public class MediaChooserActivityProxy : Activity
{
const string mimeType = "image/*";
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
var intent = new Intent(Intent.ActionPick);
intent.SetType(mimeType);
if (Build.VERSION.SdkInt >= BuildVersionCodes.Kitkat)
{
intent.PutExtra(Intent.ExtraMimeTypes, mimeType);
}
StartActivityForResult(Intent.CreateChooser(intent, "StackOverflow"), 73);
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
if (requestCode == 73)
if (resultCode == Result.Ok)
{
string[] filePathColumn = { MediaStore.Images.ImageColumns.Data };
var cursor = ContentResolver.Query(data.Data, filePathColumn, null, null, null);
cursor.MoveToFirst();
var colummIndex = cursor.GetColumnIndex(filePathColumn[0]);
MediaPicker.filePickedPath = cursor.GetString(colummIndex);
}
MediaPicker.waitHandle.Set();
Finish();
}
}
Note: This can be implemented on the MainActivity/FormsAppCompatActivity to avoid this additional Activity if desired...
Usage:
var filePath = await DependencyService.Get<IMediaPicker>().ChooseAFileAsync();
System.Diagnostics.Debug.WriteLine(filePath);
I'm creating new app using xamarin. I have already completed some part using some sample codes. I'm able to disable back buttons, volume buttons and power button.
But when trying to disable home button I'm getting error on debugging.
I'm following this link,Kiosk mode in Andriod.
But when trying to disable home button I'm getting error on debugging.
Since you didn't post your code and your error message, we don't know what happened, I just tried to create such a sample followed the blog your posted and it works fine by my side.
Here is the service:
namespace KioskModeAndroid
{
[Service]
[IntentFilter(new[] { "KioskModeAndroid.KioskService" })]
public class KioskService : Service
{
private static long INTERVAL = Java.Util.Concurrent.TimeUnit.Seconds.ToMillis(2);
private static string TAG = typeof(KioskService).Name;
private static string PREF_KIOSK_MODE = "pref_kiosk_mode";
private Thread t = null;
private Context ctx = null;
private bool running = false;
public override void OnDestroy()
{
Log.Info(TAG, "Stopping service 'KioskService'");
running = false;
base.OnDestroy();
}
[return: GeneratedEnum]
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
Log.Info(TAG, "Starting service 'KioskService'");
running = true;
ctx = this;
t = new Thread(() =>
{
while (running)
{
handleKioskMode();
Thread.Sleep(INTERVAL);
}
StopSelf();
});
t.Start();
return StartCommandResult.NotSticky;
}
private void handleKioskMode()
{
if (isKioskModeActive(ctx))
{
}
if (isInBackground())
{
restoreApp();
}
}
private bool isKioskModeActive(Context context)
{
var sp = PreferenceManager.GetDefaultSharedPreferences(context);
return sp.GetBoolean(PREF_KIOSK_MODE, false);
}
private bool isInBackground()
{
var am = ctx.GetSystemService(Context.ActivityService) as ActivityManager;
var processes = am.RunningAppProcesses;
foreach (var process in processes)
{
if (process.Importance == ActivityManager.RunningAppProcessInfo.ImportanceForeground)
{
foreach (var activeprocess in process.PkgList)
{
if (activeprocess == ctx.PackageName)
return false;
}
}
}
return true;
}
private void restoreApp()
{
Intent i = new Intent(ctx, typeof(MainActivity));
i.AddFlags(ActivityFlags.NewTask);
ctx.StartActivity(i);
}
public override IBinder OnBind(Intent intent)
{
return null;
}
}
}
I started this service in the OnCreate of MainActivity:
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
StartService(new Intent(this, typeof(KioskService)));
}
I have a requirement where i have to show the status of the download on a DisplayAlert. But with changing text on it asynchronously.
How to achieve this?
DisplayAlert("Download Info", "Downloading.....", "Ok");
I want to show status like...
Connected to server
Downloading
Download Complete
Here is a simple "Dynamic Alert" for Forms and iOS using UIAlertController and Android using a DialogFragment and a Xamarin.Forms dependency service:
Dependency Interface:
public interface IDynamicAlert
{
void Show(string title, string message);
void Update(string message);
void Dismiss();
}
iOS IDynamicAlert Dependency Implementation:
public class DynamicAlert : IDynamicAlert
{
UIAlertController alert;
public void Show(string title, string message)
{
if (alert != null) throw new Exception("DynamicAlert already showing");
alert = UIAlertController.Create(title, message, UIAlertControllerStyle.Alert);
var rootVC = UIApplication.SharedApplication.Windows[0].RootViewController;
rootVC.PresentViewController(alert, true, () =>
{
});
}
public void Update(string message)
{
if (alert == null) throw new Exception("DynamicAlert is not showing, call Show first");
alert.Message = message;
}
public void Dismiss()
{
if (alert == null) throw new Exception("DynamicAlert is not showing, call Show first");
alert.DismissViewController(true, () =>
{
alert.Dispose();
alert = null;
});
}
}
Example Usage:
var alert = DependencyService.Get<IDynamicAlert>();
if (alert != null)
{
alert.Show("StackOverflow", "Starting your request...");
await Task.Delay(2000); // Do some work...
alert.Update("Your request is processing...");
await Task.Delay(2000); // Do some work...
alert.Update("Your request is complete...");
await Task.Delay(750);
alert.Dismiss();
}
else
{
throw new Exception("IDynamicAlert Dependency not found");
}
Output:
Android Version:
The android version consists of a couple of parts, a DialogFragment subclass and the IDynamicAlert implementation that uses the custom DialogFragment.
Android DialogFragment Subclass:
public class DynamicAlertDialogFragment : DialogFragment
{
AlertDialog alertDialog;
readonly Context context;
public static DynamicAlertDialogFragment Instance(Context context, string title, string message)
{
var fragment = new DynamicAlertDialogFragment(context);
Bundle bundle = new Bundle();
bundle.PutString("title", title);
bundle.PutString("message", message);
fragment.Arguments = bundle;
return fragment;
}
public DynamicAlertDialogFragment(Context context)
{
this.context = context;
}
public override Dialog OnCreateDialog(Bundle savedInstanceState)
{
var title = Arguments.GetString("title");
var message = Arguments.GetString("message");
alertDialog = new AlertDialog.Builder(context)
.SetIcon(Android.Resource.Drawable.IcDialogInfo)
.SetTitle(title)
.SetMessage(message)
.Create();
return alertDialog;
}
public void SetMessage(string message)
{
(context as Activity).RunOnUiThread(() => { alertDialog.SetMessage(message);});
}
}
Android IDynamicAlert Dependency Implementation:
public class DynamicAlert : IDynamicAlert
{
const string FRAGMENT_TAG = "DynamicAlert_Fragment";
DynamicAlertDialogFragment fragment;
static FormsAppCompatActivity currentActivity;
public static FormsAppCompatActivity CurrentActivity { set { currentActivity = value; } }
public void Show(string title, string message)
{
if (currentActivity == null) throw new Exception("DynamicAlert.CurrentActivity needs assigned");
var fragMgr = currentActivity.FragmentManager;
var fragTransaction = fragMgr.BeginTransaction();
var previous = fragMgr.FindFragmentByTag(FRAGMENT_TAG);
if (previous != null)
{
fragTransaction.Remove(previous);
}
fragTransaction.DisallowAddToBackStack();
fragment = DynamicAlertDialogFragment.Instance(currentActivity, title, message);
fragment.Show(fragMgr, FRAGMENT_TAG);
}
public void Update(string message)
{
if (fragment == null) throw new Exception("DynamicAlert is not showing, call Show first");
fragment.SetMessage(message);
}
public void Dismiss()
{
if (fragment == null) throw new Exception("DynamicAlert is not showing, call Show first");
fragment.Dismiss();
fragment.Dispose();
fragment = null;
}
}
Android Init / Usage:
When creating the AlertDialog in the DialogFragment we need access to the current Activity and when using Xamarin.Forms, that is normally the MainActivity that is a FormsAppCompatActivity subclass. Thus you will need to initialize the DynamicAlert.CurrentActivity static property with this Activity in your MainActivity.OnCreate subclass:
Example:
protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(bundle);
////////////
DynamicAlert.CurrentActivity = this;
////////////
global::Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication(new App());
}
Android Output:
I am working on a Chat app. After the messages of a chat are loaded and the messages were visible for 5 seconds, I want to send a read confirmation to the server. This is what I've come up with so far:
public async void RefreshLocalData()
{
// some async code to load the messages
if (_selectedChat.countNewMessages > 0)
{
Device.StartTimer(TimeSpan.FromSeconds(5), SendReadConfirmation);
}
}
When RefreshLocalData() is called, I know that either another chat was selected by the user or new messages came in for the current chat. So when RefreshLocalData() is called, I have to cancel the current timer to start a new one.
Another situation where I have to cancel the timer is when I navigate to another Page. This is no problem, because the whole ViewModel is disposed when this happens.
With the code above, if RefreshLocalData() is called again but the stated TimeSpan of 5 seconds is not over yet, the method is still executing.
Is there a way to cancel the timer (if RefreshLocalData() is called again)?
I have found this answer in the Xamarin forum: https://forums.xamarin.com/discussion/comment/149877/#Comment_149877
I have changed it a little bit to meet my needs and this solution is working:
public class StoppableTimer
{
private readonly TimeSpan timespan;
private readonly Action callback;
private CancellationTokenSource cancellation;
public StoppableTimer(TimeSpan timespan, Action callback)
{
this.timespan = timespan;
this.callback = callback;
this.cancellation = new CancellationTokenSource();
}
public void Start()
{
CancellationTokenSource cts = this.cancellation; // safe copy
Device.StartTimer(this.timespan,
() => {
if (cts.IsCancellationRequested) return false;
this.callback.Invoke();
return false; // or true for periodic behavior
});
}
public void Stop()
{
Interlocked.Exchange(ref this.cancellation, new CancellationTokenSource()).Cancel();
}
public void Dispose()
{
}
}
And this is how I use it in the RefreshLocalData() method:
private StoppableTimer stoppableTimer;
public async void RefreshLocalData()
{
if (stoppableTimer != null)
{
stoppableTimer.Stop();
}
// some async code to load the messages
if (_selectedChat.countNewMessages > 0)
{
if (stoppableTimer == null)
{
stoppableTimer = new StoppableTimer(TimeSpan.FromSeconds(5), SendReadConfirmation);
stoppableTimer.Start();
}
else
{
stoppableTimer.Start();
}
}
}
You can try using this class I found, it covers some of the limits to the DeviceTimer:
public class MySystemDeviceTimer
{
private readonly TimeSpan timespan;
private readonly Action callback;
private CancellationTokenSource cancellation;
public bool running { get; private set; }
public MySystemDeviceTimer(TimeSpan timespan, Action callback)
{
this.timespan = timespan;
this.callback = callback;
this.cancellation = new CancellationTokenSource();
}
public void Start()
{
running = true;
start(true);
}
private void start(bool continuous)
{
CancellationTokenSource cts = this.cancellation; // safe copy
Device.StartTimer(this.timespan,
() =>
{
if (cts.IsCancellationRequested)
{
running = false;
return false;
}
this.callback.Invoke();
return continuous;
});
}
public void FireOnce()
{
running = true;
start(false);
running = false;
}
public void Stop()
{
Interlocked.Exchange(ref this.cancellation, new CancellationTokenSource()).Cancel();
}
}
Then for your purpose:
MySystemDeviceTimer timer;
if (timer == null)
{
timer = new MySystemDeviceTimer(TimeSpan.FromSeconds(5), SendReadConfirmation);
timer.FireOnce();
}
else if (timer.running)
timer.Stop();
Yes you can with Device.StartTimer() as long as you return true to have the function repeat. I typically handle this through a Boolean variable that I can control in my ViewModel. Something like below:
bool shouldRun = true;
public async void RefreshLocalData()
{
// some async code to load the messages
if (_selectedChat.countNewMessages > 0)
{
Device.StartTimer(TimeSpan.FromSeconds(5), async() =>
{
await SendReadConfirmationAsync()
return shouldRun;
});
}
}
public async Task SendReadConfirmationAsync()
{
//Do some stuff
if(we want to stop call)
shouldRun = false;
}