Skip to content
This repository has been archived by the owner on Dec 18, 2017. It is now read-only.

PictureChooser does not return #28

Open
petero-dk opened this issue Nov 11, 2015 · 7 comments
Open

PictureChooser does not return #28

petero-dk opened this issue Nov 11, 2015 · 7 comments

Comments

@petero-dk
Copy link

The current implementation of the MvxFormsApplicationActivity does not implement IMvxAndroidView nor IMvxEventSourceActivity and because of this the MvvmCross picture chooser plugin (when correctly implemented as a service) cannot return with an image to the viewmodel.

I have proposed a solution in the xamarin forum. But if needed I could probably find time to do a pull-request.

https://forums.xamarin.com/discussion/51135/mvvmcross-picture-chooser-not-working-with-the-take-picture-option

@petero-dk
Copy link
Author

Posted directly since it is awaiting moderation in the Xamarin Forums

If you have Xamarin Forms and MvvmCross, then the most likely cause of PictureChooserTask not returning is because the current ApplicationActivity dues not implement IMvxAndroidView and does not implement IMvxEventSourceActivity.

Basically if your source looks like this:

       public class MvxFormsApplicationActivity
         : FormsApplicationActivity {

        protected override void OnCreate(Bundle bundle) {
            base.OnCreate(bundle);

            Xamarin.Forms.Forms.Init(this, bundle);
            Acr.UserDialogs.UserDialogs.Init(this);

            Mvx.RegisterSingleton<Acr.UserDialogs.IUserDialogs>(Acr.UserDialogs.UserDialogs.Instance);
            Mvx.RegisterSingleton<Acr.Settings.ISettings>(Acr.Settings.Settings.CreateInstance("droid"));
            Mvx.RegisterSingleton<IWaterMarkingService>(() => new ZueblinNM.Droid.Services.WaterMarkingService());


            var mvxFormsApp = new MvxFormsApp();
            LoadApplication(mvxFormsApp);

            var presenter = Mvx.Resolve<IMvxViewPresenter>() as MvxFormsDroidPagePresenter;
            presenter.MvxFormsApp = mvxFormsApp;

            Mvx.Resolve<IMvxAppStart>().Start();
        }

    }

Then you should create a class like this:

    using Android.App;
    using Android.Content;
    using Android.OS;
    using Cirrious.CrossCore.Core;
    using Cirrious.CrossCore.Droid.Views;
    using Cirrious.MvvmCross.Droid.Views;
    using Cirrious.MvvmCross.ViewModels;
    using System;
    using Xamarin.Forms.Platform.Android;

    namespace EcoMerc.Xamarin.Droid {
        public class MvxFormsAndroidEventApplicationActivity : FormsApplicationActivity, IMvxAndroidView, IMvxEventSourceActivity {

            protected override void OnCreate(Bundle bundle) {
                CreateWillBeCalled.Raise(this, bundle);
                base.OnCreate(bundle);
                CreateCalled.Raise(this, bundle);

                this.AddEventListeners();

            }


            #region IMvxAndroidView (properties not implemented)

            public void MvxInternalStartActivityForResult(Android.Content.Intent intent, int requestCode) {
                StartActivityForResultCalled.Raise(this, new MvxStartActivityForResultParameters(intent, requestCode));
                base.StartActivityForResult(intent, requestCode);
            }

            public IMvxViewModel ViewModel { get; set; }

            public object DataContext { get; set; }

            public Cirrious.MvvmCross.Binding.BindingContext.IMvxBindingContext BindingContext { get; set; }

            #endregion


            #region Overrides for Event Raising for IMvxEventSourceActivity

            public override void OnConfigurationChanged(Android.Content.Res.Configuration newConfig) {
                base.OnConfigurationChanged(newConfig);
            }

            protected override void OnDestroy() {
                DestroyCalled.Raise(this);
                base.OnDestroy();
            }

            protected override void OnNewIntent(Intent intent) {
                base.OnNewIntent(intent);
                NewIntentCalled.Raise(this, intent);
            }

            protected override void OnResume() {
                base.OnResume();
                ResumeCalled.Raise(this);
            }

            protected override void OnPause() {
                PauseCalled.Raise(this);
                base.OnPause();
            }

            protected override void OnStart() {
                base.OnStart();
                StartCalled.Raise(this);
            }

            protected override void OnRestart() {
                base.OnRestart();
                RestartCalled.Raise(this);
            }

            protected override void OnStop() {
                StopCalled.Raise(this);
                base.OnStop();
            }

            public override void StartActivityForResult(Intent intent, int requestCode) {
                StartActivityForResultCalled.Raise(this, new MvxStartActivityForResultParameters(intent, requestCode));
                base.StartActivityForResult(intent, requestCode);
            }

            public override void StartActivityForResult(Intent intent, int requestCode, Bundle options) {
                StartActivityForResultCalled.Raise(this, new MvxStartActivityForResultParameters(intent, requestCode));
                base.StartActivityForResult(intent, requestCode, options);
            }

            protected override void OnActivityResult(int requestCode, Result resultCode, Intent data) {
                ActivityResultCalled.Raise(this, new MvxActivityResultParameters(requestCode, resultCode, data));
                base.OnActivityResult(requestCode, resultCode, data);
            }

            protected override void OnSaveInstanceState(Bundle outState) {
                SaveInstanceStateCalled.Raise(this, outState);
                base.OnSaveInstanceState(outState);
            }

            protected override void Dispose(bool disposing) {
                if (disposing) {
                    DisposeCalled.Raise(this);
                }
                base.Dispose(disposing);
            }



            #endregion  

            #region Events

            public event EventHandler<MvxValueEventArgs<Bundle>> CreateWillBeCalled;
            public event EventHandler<MvxValueEventArgs<Bundle>> CreateCalled;
            public event EventHandler DestroyCalled;
            public event EventHandler<MvxValueEventArgs<Intent>> NewIntentCalled;
            public event EventHandler ResumeCalled;
            public event EventHandler PauseCalled;
            public event EventHandler StartCalled;
            public event EventHandler RestartCalled;
            public event EventHandler StopCalled;
            public event EventHandler<MvxValueEventArgs<Bundle>> SaveInstanceStateCalled;
            public event EventHandler<MvxValueEventArgs<MvxStartActivityForResultParameters>> StartActivityForResultCalled;
            public event EventHandler<MvxValueEventArgs<MvxActivityResultParameters>> ActivityResultCalled;
            public event EventHandler DisposeCalled;

            #endregion


        }
    }

@martijn00
Copy link
Contributor

Hi, thanks for pointing this out! Can you make a pull request, so it is easier to merge in?

@PeterBurke
Copy link
Contributor

I ill be looking at this shortly. It may be more appropriate to make it an example with perhaps some additions to the presenters.

@petero-dk
Copy link
Author

@PeterBurke maybe you are right,
And @martijn00 Martijn: I am unsure if there is a better (or already existing class) to derive from, which is why I didn't start with a pull request

However I feel it must be a bug that Mvx does not know the correct current activity. In the Mvvmcross-Forms sample I think (through som debugging) that it for the most part the current IMvxAndroidView is the splashscreen (even when inside active ViewModels).

@petero-dk
Copy link
Author

After a few hours of testing, if I turn on the kill Activities when they are not in focus (in developer mode) then there is an exception in the above code: In the OnDestroy() Even if I check if there is registered an eventhandler there seems to be a null reference exception somewhere inside MvvmCross. To tired to investigate more now.

Additionally there should be some communicating with the ViewModels. Because they maybe closed and resumed and this class would have the capability of notifying them. Or maybe not and people needing it can implement it themselves?

@petero-dk
Copy link
Author

Just a small update quite a long time from discovery. The exception that is thrown from MvvmCross is a null reference exception

02-22 11:11:44.989 W/mvvmcross(26695): System.NullReferenceException: Object reference not set to an instance of an object
02-22 11:11:44.989 W/mvvmcross(26695):   at MvvmCross.Droid.Views.MvxBindingActivityAdapter.EventSourceOnDestroyCalled (System.Object sender, System.EventArgs eventArgs) [0x00006] in <filename unknown>:0 
02-22 11:11:44.989 W/mvvmcross(26695):   at (wrapper delegate-invoke) <Module>:invoke_void_object_EventArgs (object,System.EventArgs)
02-22 11:11:44.989 W/mvvmcross(26695):   at MvvmCross.Platform.Core.MvxDelegateExtensionMethods.Raise (System.EventHandler eventHandler, System.Object sender) [0x00000] in V:\Xamarin\MvvmCross\MvvmCross\Platform\Platform\Core\MvxDelegateExtensionMethods.cs:16 
02-22 11:11:44.989 W/mvvmcross(26695):   at EcoMerc.Xamarin.Droid.MvxFormsAndroidEventApplicationActivity.OnDestroy () [0x00006] in C:\Users\Peter\Projects\...\MvxFormsAndroidEventApplicationActivity.cs:50 

For what I can see everything is null checked so I cannot really see why this should fail. However wrapping everything in a try catch (bad bad developer) is does not die horribly:

  protected override void OnDestroy() {
            try {
                base.OnDestroy();
                DestroyCalled?.Raise(this);
            } catch (Exception ex) {
                Android.Util.Log.Warn("mvvmcross", ex.ToString());
            }
        }

@johnslaby
Copy link

This worked for me. Thanks!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

No branches or pull requests

4 participants