Watch face and OauthFlows

Former Member
Former Member

Hello,

I want to create a watchface that shows your next calendar events.

I don't want to build a companion app, so I just created an Oauth flow that asks for google calendar read-only events permissions. The have created also a small endpoint that reduces the amount of data send by the google API.

Now I'm looking for proper examples of implementing the Oauth flow for my watch face and I can't find any proper reference on the developers guide. Where is the complete flow example? The only example I found is the strava api, and for my understanding, it makes some unnecessary extra steps like creating a behavior delegate that does nothing (why do they do that) and show a login screen. I don't want to sow a login screen, If the user is not logged, then I will show nothing and that's enough for my project.

Can I just register directly for oauth on app initialization? Should I provide a delegate ven if I don't need it?

Thanks for your help

  • Former Member
    Former Member over 5 years ago in reply to jim_m_58

    Thanks for the link . I can't see any source on the link you posted.

    But maybe you can help me because documentation is not very clear. I come to the conclusion that I have to add a method to my background class called registerForOAuthResponseEvent, the code I put there is to just return the method that should handle the oauth response. Here is a snippet of what I'm trying to do:

    (:background)
    class BackgroundService extends Sys.ServiceDelegate {
    
    	(:background_method)
    	function initialize() {
    		Sys.ServiceDelegate.initialize();
    		triggerOAuth();
    	}
    	
    	(:background_method)
    	function registerForOAuthResponseEvent(){
    		System.print("Registered for an oauth event"); 
    		return method(:onOAuthMessage);
    	}
    
        function triggerOAuth() {
            // makeOAuthRequest triggers login prompt on mobile device
            Comms.makeOAuthRequest(
                "https://connectiq-apps.firebaseapp.com/auth",
                {},
                "https://connectiq-apps.firebaseapp.com/token",
                Comms.HTTP_RESPONSE_CONTENT_TYPE_JSON,
                {"token" => "OAUTH_CODE", "error" => "OAUTH_ERROR"}
            );
        }
    
        function onOAuthMessage(message) {
            if (message.data != null) {
                var code = message.data["OAUTH_CODE"];
                var error = message.data["OAUTH_ERROR"];
                System.println(message.data);
    
            } else {
                // return an error
            }
        }

    The oauth request is initialized and completed, but the method I tried to register is not triggering. Any idea why?

  • Former Member
    Former Member over 5 years ago

    Well, I'm starting to make things work, but they don't feel correct, they feel hacky. And the lack of documentation on how to do things properly is not helping either.

    Here is how my Background service looks now:

    class BackgroundService extends Sys.ServiceDelegate {
    	
    	var token;
    	
    	(:background_method)
    	function initialize() {
    		Sys.ServiceDelegate.initialize();
    		Sys.println("Background initialize");
    		token = App.getApp().getProperty("token");
    		if(token == null) { 
    			triggerOAuth();
    			Comms.registerForOAuthMessages(method(:onOAuthMessage));
    		}
    	}
    
        function triggerOAuth() {
            // makeOAuthRequest triggers login prompt on mobile device
            Comms.makeOAuthRequest(
                "https://connectiq-apps.firebaseapp.com/auth",
                {},
                "https://connectiq-apps.firebaseapp.com/token",
                Comms.HTTP_RESPONSE_CONTENT_TYPE_JSON,
                {"token" => "OAUTH_CODE", "error" => "OAUTH_ERROR"}
            );
        }
    
        function onOAuthMessage(message) {
            if (message.data != null) {
                var code = message.data["OAUTH_CODE"];
                var error = message.data["OAUTH_ERROR"];
                System.println(message.data);
    			Bg.exit({"token"=>code});
            } else {
                // return an error
            }
        }

    And here is how I'm triggering it from the app:

    class garminhelloworldApp extends Application.AppBase {
    
        function initialize() {
            AppBase.initialize();
            Bg.registerForOAuthResponseEvent();
        }
    
        (:background_method)
    	function getServiceDelegate() {
    		return [new BackgroundService()];
    	}
    	
    	function onBackgroundData(data){
    		Sys.println("Got bg data");
    		Sys.println(data);
    		setProperty("token",data["token"]);
    	}

    There are some confusing things that I don't know how to explain. For example, when I was not checking for the token existence on the BG process (just for testing) the initialize method of the BG method was being called continuously, when an oauth flow was completed the background service was initialized again starting another oauth flow which in turn triggered another BG initialization... Why is that? Why calling `registerForOAuthResponseEvent` just starts a BG method instead of calling some particular method? is this the intended flow? To just start a BG process and let it handle the entire flow if necessary?

    Something weird is that the BG initialize method was called again two minutes after the first flow completed. Thankfully the saved token stopped the complete flow to start again, but I don't know what triggered that execution.

    This is all very confusing at least and also it has some frustrating parts. I would love to get some guidance.

  • Former Member
    Former Member over 5 years ago in reply to Former Member

    Ok, I think I start to understand what is happening here...

    Every time any part of the application calls App.getApp() a new instance of the main App is instantiated. I empirically checked that by putting print logs on every method.

    That's why on first instance I was falling on a loop: My app was triggering the BG process on it's initialize method, and the BG process was calling App.getApp() on its initialize method, creating the loop.

    To answer my other question, why the BG method was triggered twice the first time, I think it is because I was calling App.getApp() also on the watchface, so when the login flow finished and the watchface started there was another oauth request in-flight that triggered that.

    Now I see that the proper place to register for a oauth flow is not the app initialize method. Probably the best place will be on the getInitialView method, which is called just once per watchface initialization.

    I still don't see the usfullness of registerForOAuthResponseEvent. It just seems to initialize the BG process, I can't see any special behavior associated with it.

  • You can see the background running twice when an app starts because of onAppInstall() and onAppUpdate().  App.getApp() doesn't cause a new instance.

    But each time the background run, that does.  And this is something to look at in AppBase.

    Things like initialize(), onStart() and onStart() run when the main app starts, or the background runs,  Things like getInitialView() only run for the main app, while getServiceDelegate(), only when the background runs.

  • In the thread I linked to, there was a really simple example of a WF with a background process, with a bunch of println calls, but it may have gotten lost when the forums changed in April.  Here's the same app (but with some changes), that will probably help you see when things happen in the main app and background, what gets called in the main app vs the background, etc.

    In the sim, those things that are done in the background start with "background:"

    1222.vsbgwf.zip

  • Former Member
    Former Member over 5 years ago in reply to jim_m_58

    Thank you very much , your explanations helped me understanding better how does this work.

    Have you considered uploading your watchface to github? That way it will not get lost and it may gain in discoverability.

    Do you know what theregisterForOAuthResponseEvent is for? I can't find any reference other than the basic description on the API reference.

  • I know many people disagree with me, but I find github more of a pain than it's worth.  Believe me, many people have tried to change my mind over the years.  Posting a code block in the forum or a zip of the project works fine for me.

    The zip was lost because the whole forum changed, and let's hope that doesn't happen again for a long time.

  • Jim = Old Dog, no new tricks!  Dog

    And a Bit Bucket is the container that's used to catch the bits of the card that get punched out when you write code on a keypunch machine! Wink