applicationOpen event not firing

41 replies [Last post]
j735
User offline. Last seen 47 weeks 3 days ago. Offline
Joined: 9 Mar 2011

I am currently implementing Facebook deep linking in my app using a URL scheme. I've gotten everything to work from a cold start (i.e. not from suspended state): I click on open graph object in my Facebook news feed and it takes me directly to the object in the app.

However, I can't get this functionality working when the app resumes from a suspended state. The link still opens my app, but only an applicationResume event fires, not an applicationOpen event; and since there's no corresponding event.url for the applicationResume event, there's no data for me to parse and direct the user to the requested page.

Any advice would be appreciated.

Replies

Tom
User is online Online
Staff
Joined: 13 Jul 2010

I'm not sure what you're expecting but there is no applicationOpen event. There is an applicationSuspend and applicationResume "type" that is part of the "system" event. http://developer.coronalabs.com/reference/index/eventtype-0

j735
User offline. Last seen 47 weeks 3 days ago. Offline
Joined: 9 Mar 2011
ignis075
User offline. Last seen 1 year 18 hours ago. Offline
Joined: 4 Oct 2010

This probably isn't ideal, but could you force the app to NEVER suspend, using the config and build settings, thus it quits every time and always starts "cold"? You could then use the simulated save-state methods discussed in a recent blog post to make the app appear as if it was suspended by iOS, even though it wasn't. This would provide your "open" URL parameters every time. Again, I know it's not ideal, but it might be your only option in this case. :(

Brent

j735
User offline. Last seen 47 weeks 3 days ago. Offline
Joined: 9 Mar 2011

@Brent

Thanks for the response. That was my first thought too, but like you said, it's definitely not ideal. My specific concern is how such an approach would affect my Facebook single sign on (SSO) implementation.

haakon
User offline. Last seen 2 weeks 1 day ago. Offline
Joined: 19 Jan 2011

Hi,

I have the exact same problem. And a cold start is not an option. You have documented in the above mentioned blog posts that it should be done like this:

local function onSystemEvent( event )
if event.type == "applicationOpen" and event.url then
printURL( event.url )
end
end

Runtime:addEventListener( "system", onSystemEvent )

We have spent a huge amount of time implementing Facebook InviteRequests and having the server autmatically set up 2-player games when two users connect, and then in the end it turns out it will only work on cold start.

This must be fixed immediately in the next daily build.

Tom
User is online Online
Staff
Joined: 13 Jul 2010

Has anyone filed a bug report with a project showing the problem? Without a bug case with code to demonstrate the problem, it won't get fixed.

The forum is great for exchanging ideas and helping to determine if a bug really exists, but it's not the same as submitting a bug to be fixed. Our bug reporting system is the only way we can handle and track all the bugs that we receive.

haakon
User offline. Last seen 2 weeks 1 day ago. Offline
Joined: 19 Jan 2011

The only way you guys fix anything is by having your users pay for PREMIUM support, so no one, except from those new to Corona, bothers to file bug reports anymore.

ignis075
User offline. Last seen 1 year 18 hours ago. Offline
Joined: 4 Oct 2010

Hi @haakon,

Can you please describe in more detail how your app is designed? Regardless of a "cold" or "warm" start, what happens when your app is started? Does it immediately try to connect to another (networked) user, before a menu screen or anything is shown?

If it's a "warm" start, do you try to re-initiate a networked session between the 2 players that were previously playing? Is this a turn-by-turn game like "Words With Friends" where one player might not log in for hours (or days) before making his/her turn?

I'm just curious how your app is designed, and if a workaround might be coded to deal with the issue you're having...

Thanks,
Brent

haakon
User offline. Last seen 2 weeks 1 day ago. Offline
Joined: 19 Jan 2011

Hi,

it is a turn-based game, but with a server between, not device-to-device.

I doubt there is a work-around, other than exit on suspend, which is not an option, since that will kill some other functionality like the possibility to use the photo album or camer app to upload user avatars.

The only way to deal with this is that you fix your SDK to work like you document. We need the facebook url scheme to work on warm start, not only cold start. We use the url scheme to collect info about the facebook invite request clicked (we parse out the request_id from the URL scheme) and call our server with the request_id to create a game between the two players.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
-- Resume game
local function onSystemEvent(event)
  if event.type == "applicationOpen" and event.url then
    AutomaticFaceGame:Setup(event.url)
  end
end
 
-- Automatic Face Game class
 
AutomaticFaceGame = {}
 
function AutomaticFaceGame:Setup(fburl)
        local function decode(s)
                local function unescape(s)
                        s = string.gsub(s, "+", " ")
                        s = string.gsub(s, "%%(%x%x)", function (h)
                                return string.char(tonumber(h, 16))
                        end)
                        return s
                end
                
                local target_url = nil
                for name, value in string.gfind(s, "([^&=]+)=([^&=]+)") do
                        if name == "target_url" then
                                target_url = unescape(value)
                                break
                        end
                end
                if target_url then
                        local request_ids = nil
                        for name, value in string.gfind(target_url, "([^&=]+)=([^&=]+)") do
                                if string.find(name, "request_ids") then
                                        request_ids = value
                                        break
                                end
                        end
                        return request_ids
                end
                return nil
        end
        
        local request_id = decode(fburl)
        
        if request_id then
                local function requestListener(event)
                        if not event.isError then
                                result = json.decode(event.response)
                                if result then
                                        if result.success then
                                                ui.newNotification(result.message)
                                                _G.doManualPoll()
                                        elseif result.error then
                                                ui.newNotification(result.error, "error")
                                        end
                                else
                                        native.showAlert("Error", event.response, {"OK"})
                                end
                        end
                end
                
                local post_data = "user_id=" .. url.escape(_G.user.id)
                post_data = post_data .. "&request_id=" .. request_id
                
                network.request(_G.server .. "facebook/setup_game", "POST", requestListener, {body = post_data})
        else
                print("No request id...")
        end
end
Runtime:addEventListener("system", onSystemEvent)

ignis075
User offline. Last seen 1 year 18 hours ago. Offline
Joined: 4 Oct 2010

I see your point and the associated issue. I'll file a bug report on your behalf. Bug reports do, in fact, get put before the engineering team... if they didn't, Corona would have devolved into an unusable SDK long, long ago.

Brent Sorrentino

j735
User offline. Last seen 47 weeks 3 days ago. Offline
Joined: 9 Mar 2011

Please let us know the bug number so we can keep an eye out in the daily builds section. Thanks!

haakon
User offline. Last seen 2 weeks 1 day ago. Offline
Joined: 19 Jan 2011

Yeah, once again I have pulled up my Credit Card to PAY Corona Labs for fixing their own bugs, so expect to see this fixed in a daily build in a couple of days.

I HATE the way Corona Labs treat their customers (us), but unfortunately we're stuck with your SDK for a while and just have to take what we get. We're not your best ambassador, though.

DavidRangel
User offline. Last seen 20 min 9 sec ago. Offline
Staff
Joined: 27 Oct 2010

Hey guys - just want to post here and answer Haakon publicly as well. I just wrote an email to him personally as well answering an email he sent me.

We do try to fix everything possible and we do fix *many* things. It is inevitable that all our developers think that whatever bug/issue they are facing is the most important thing at that point in time. For us that is rarely the case - but we still make an effort to fix as many things as possible. But we have to juggle the needs of thousands of developers, that's just the way it is.

I will take a look at this and see if I can have one of the team take a look as well. I cannot guarantee how soon we will be able to address and how long it will take to fix the issue (assuming there is something to be fixed - I am coming at the issue cold and don't know what it is).

David

@RSCdev
User offline. Last seen 1 year 15 weeks ago. Offline
Joined: 6 Sep 2011

Hi @haakon,

sorry to hear all your "drama" with the SDK as am following it here BUT I would like to ask you if I understood correct what you said above:

-- Did you have to pay for Corona for them to "repair" a bug quickly and so it will be on the next daily build as you said?

If it is like this I sincerely did not know about this: if you pay you got and if not you keep yourself with the bug. (and our annual payment does not count?) o.O

Strange things going on...

DavidRangel
User offline. Last seen 20 min 9 sec ago. Offline
Staff
Joined: 27 Oct 2010

@RSCDev - I want to be clear about this. Haakon took the initiative and decided to "pay us so that we would solve the bug". I was very clear in my post above that we will look at this and see if we can solve it based on all the other work we have right now on our plate. I will then settle with Haakon whether he has to pay for this or not. I have yet to understand if this is a bug, what the real problem is and what the fix will entail.

Again, I want to be very clear: we (Corona Labs) are NOT requesting that developers pay us to fix bugs. Like any other platform, we have a prioritized list of bugs and are working to address them. Each developer has specific bug that are important to them. We, fortunately, have to juggle the needs of thousands of developers and we therefore have to act accordingly and prioritize.

Thanks guys.

@RSCdev
User offline. Last seen 1 year 15 weeks ago. Offline
Joined: 6 Sep 2011

Mr.David Rangel,

I "personally" thank you for being so clear about the @haakon`s issue right now. I understood what you meant.

BTW, this typed sentence of you below told me what I was looking forward to know:

Again, I want to be very clear: we (Corona Labs) are NOT requesting that developers pay us to fix bugs.

So, that was exactly what I always had in mind (since becoming a Corona`s dev more than a year ago by now) and if it did not change by any way, well, it is good to hear about.

PS: I`ve posted above asking @hakoom before you and so I did not notice your post of course. The time of the post means nothing as am at different FUSO than you and so my machine`s time get into anyway. (am actually 5 hours or so in "front of you") :)

Best regards,
Rodrigo Costa.

j735
User offline. Last seen 47 weeks 3 days ago. Offline
Joined: 9 Mar 2011

I can attest to the fact that Ansca (sorry...Corona Labs) does in fact work on verified submitted bugs; however, the timeframe isn't ideal unless you pay for premium support (which I can only guess at the quality of since I haven't springed for it). It's particularly frustrating if you've already paid $350 as a Pro Developer, but the sheer magnitude of the Corona SDK developer community I'm sure makes it near impossible to respond to every service request within the desired amount of time.

That being said, I would like to reiterate what I've read from other developers in other threads on this site: it would be nice if a little more emphasis was placed on the non-gaming features of Corona (i.e. the widget API). I've noticed in the recent Daily Build summaries that a lot of work is being done in this regard for Android, but the widget API is not what I would exactly call production level for iOS yet though. Just to mention a few of the bugs I've been wrestling with (which have already been identified in other threads): storyboard API transitions (i.e. slide), tableViews (in general, it's feel as a "native" object is less than ideal, but more specifically as it relates to storyboard slide transition), and webviews' lack of high level interactIon with Lua. I feel there's a significant community of developers and potential developers who would be greatly benefitted by such attention from the Corona folks.

walter
User offline. Last seen 13 hours 23 min ago. Offline
Staff
Joined: 22 Jun 2009

We're not able to reproduce the warm start issue; it's working as expected.

Based on this simple test case (https://gist.github.com/3759412), we see the following:

(a) On a warm start, we launch the app directly (tap on the icon from the home screen). We get the "applicationOpen" event as expected.
(b) On a warm start, we launch the app by typing "haakon://" into Safari. We get the "applicationOpen" event as expected.

Now specifically, for Facebook's "fbXXXXX" urls, you will *not* see "applicationOpen" events b/c that's how Facebook's iOS library wants --- they want the library to handle the urls under the hood, so that event does not get propagated.

haakon
User offline. Last seen 2 weeks 1 day ago. Offline
Joined: 19 Jan 2011

So, Walter, how can deep linking from Facebook be achieved in Corona then? The whole idea of apprequests is that it deep links back to the app. It works fine on cold start, but no event.url is found on warm start. If what you write was true, the cold start should not have a facebook deep link url either...

walter
User offline. Last seen 13 hours 23 min ago. Offline
Staff
Joined: 22 Jun 2009

Actually, in my test project, it's the opposite. There is no event.url on cold start. In the test project above, if you do a cold launch via "haakon://" in Safari, you do not get an "applicationOpen" event --- same would go for fb urls.

haakon
User offline. Last seen 2 weeks 1 day ago. Offline
Joined: 19 Jan 2011

Well, I can be a bit more precise with 2 use cases:

Corona App is not running (cold start)
===================
1) Click AppRequest in Facebook App.
2) It opens up our Corona built app
3) The fbXXXX url scheme is available in the launchArgs
=> Success

Corona App is suspended (warm start)
===================
1) Click AppRequest in Facebook App.
2) It opens up our Corona built app
3) There is no fbXXXX in the applicationOpen event.url - there is no applicationOpen event firing at all
=> Failure

I just asked the engineers over at Facebook if they have a limitation like you describe above, and they tell me there's no such limitation. You are allowed to forward the deep link also on warm start.

walter
User offline. Last seen 13 hours 23 min ago. Offline
Staff
Joined: 22 Jun 2009

Ah, okay. Why didn't you say it like that earlier? :)

It's much clearer the way you explain it now --- and incidentally, this is why we ask for code because that is always more precise and eliminates ambiguity.

Let me get back to you.

j735
User offline. Last seen 47 weeks 3 days ago. Offline
Joined: 9 Mar 2011

@walter

While I didn't provide sample code when I started this thread, the problem was described very clearly. Maybe you should read the entire thread before responding.

walter
User offline. Last seen 13 hours 23 min ago. Offline
Staff
Joined: 22 Jun 2009

@j735, in retrospect, I agree, but hindsight is 20-20. The reality is the test code the team made and that was passed around wasn't testing the right thing. @haakon's precise description helps us refine it properly.

However, practically speaking, we still prefer to have the code. It saves everyone time, and more importantly, we can verify we fixed the actual issue.

j735
User offline. Last seen 47 weeks 3 days ago. Offline
Joined: 9 Mar 2011

@walter

I hear you. You're probably picking up on a bit of frustration I've been feeling over the fact that I started this thread over a month ago and the only response I received from a Corona staff member (Tom) before your post this week was not only dismissive in tone, but completely and utterly WRONG -- I expect a staff member to at least check his employer's documentation before chiming in on a developer discussion.

BTW, is there an ID number for this bug yet?

walter
User offline. Last seen 13 hours 23 min ago. Offline
Staff
Joined: 22 Jun 2009

In today's daily build (922), we have added a fix so that when an app is launched via url, "applicationOpen" system events now open on *both* initial launch and on a resume.

[Update: In addition, all fbXXX url's are propagated as "applicationOpen" events.]

We plan to deprecate the use of the launchArgs facility for grabbing the url on an initial (cold) launch, as we think it's more consistent to have all url-based launches go through the "applicationOpen" system event.

This fix addresses our own internal test project, so hopefully it fixes it for you all as well!

j735
User offline. Last seen 47 weeks 3 days ago. Offline
Joined: 9 Mar 2011

I downloaded Build 922 last night and used it for an iOS build of the app which was the original subject of this thread. At the end of the build process, I get a Corona error indicating there's an entitlement issue (something about an improper signature). However, I know this isn't the case and the issue went away as soon as I reverted back to Build 894. Something was broken in the course of this fix.

walter
User offline. Last seen 13 hours 23 min ago. Offline
Staff
Joined: 22 Jun 2009

Have you upgraded to XCode 4.5 GM?

Starting in daily build 919, we require XCode 4.5: http://www.coronalabs.com/blog/2012/09/19/corona-ios-6-and-xcode-4-5-are-gm/

j735
User offline. Last seen 47 weeks 3 days ago. Offline
Joined: 9 Mar 2011

@walter

Thanks for that -- that seems to have fixed the problem. At first glance, everything seems to be working the way I expected in the new build :)

haakon
User offline. Last seen 2 weeks 1 day ago. Offline
Joined: 19 Jan 2011

Hi, yes, we can confirm that it works, and we've also removed the launchArgs implementation and rely completely on applicationOpen for both cold and warm starts. Works like it should.

How come this is not implemented for Android? I mean, some work in regards to deep linking from Facebook must have been done already, since SSO works. It's a shame apprequests will only actually work for the iOS-users, and not the Android users.

Thanks.

antonyburrows
User offline. Last seen 46 weeks 5 days ago. Offline
Joined: 6 Aug 2012

Hi,
Sorry to hijack but I have a mildly off topic question regarding FB deep linking in in Corona. I can't for the life of me get it to work (on the facebook side).

Is there some specific format the facebook post needs to be to get the deep linking activated? Does it need to be a specified base URL? My test below, using a google url, always opens up the link in facebook app embedded browser, instead of launching my app.

1
2
3
4
     local path = "me/feed"
     local message = "You have bee sent a Message"
     local params = {message = message , link="http://www.google.com"}
     libFacebook.request( path, "POST", params, onFbPost )

My app is set up to use SSO and deep linking, SSO works a treat. When I click the app name in post the app will launch, but clicking the actual link just opens the web page?

Is there a special format you are meant to use? I think I am missing something really stupid here, but just can't seem to get it right. Any advice?

Thanks

antonyburrows
User offline. Last seen 46 weeks 5 days ago. Offline
Joined: 6 Aug 2012

Never mind problem solved. As suspected I was being dumb, it will not deep link any old URL, as far as I can tell it will only deep link your namesapce so the code above needs to have the google link changed to something link "https://apps.facebook.com/appNameSpace/" for it to work

Joshua Quick
User offline. Last seen 38 min 56 sec ago. Offline
Staff
Joined: 31 Jan 2011

Everyone,

We've added support for launch arguments and system event "applicationOpen" to Android as of daily build #942. This allows you to support facebook deep linking on Android.

Just be sure to set up the following settings on your facebook developer page as follows:
- package name: The package name that you've entered into Corona's build window.
- class name: com.ansca.corona.CoronaActivity

Facebook on Android will launch your app via an Android "Intent" using the package name and class name that you've entered on the developer page. The package name is your app's unique string ID and tells the Android system which app to launch the activity from. This way there won't be a conflict if more than one Corona app is installed on the same device.

haakon
User offline. Last seen 2 weeks 1 day ago. Offline
Joined: 19 Jan 2011

Hi,

this is not working on Android as of build 984.

Walter wrote: In today's daily build (922), we have added a fix so that when an app is launched via url, "applicationOpen" system events now open on *both* initial launch and on a resume.

1) The applicationOpen event does NOT fire on Android on cold start, only on warm start. However, that's not such a big deal, since it is just a matter of using the launchArgs instead. But, check out the next bug:

Joshua wrote: We've added support for launch arguments and system event "applicationOpen" to Android as of daily build #942. This allows you to support facebook deep linking on Android.

2) The event.url and launchArgs.url does not contain the correct URL from Facebook. When clicking an invite request in my Android Facebook app, I'm correctly redirected to our game, but the url param contains a link to the Facebook app center: http://www.facebook.com/appcenter/mindfeud?fb_source=notification. It should have contained the request_id from the facebook invite request the user tapped on.

EDIT:

I tested on iOS as well, and #1 is not happening there either. But even worse is the fact that you have broken #2 as well on iOS, it's no longer getting the request_id from facebook.

I suspect that this is a consequence of you not having updated to the latest Facebook SDK... Hurray!

Joshua Quick
User offline. Last seen 38 min 56 sec ago. Offline
Staff
Joined: 31 Jan 2011

Haakon,

Regarding #1, we'll have a look at the "applicationOpen" event this coming Monday. We've implemented it on Android to match the iOS behavior, but if the iOS behavior that we've implemented in build #922 was lost, then we'll need to re-look into it. Although a quick test by Walter today demonstrated that applicationOpen was working from a cold start. In any case, we'll look into it this Monday and make both platforms do the right thing.

Regarding #2 about the received facebook URL, that's not a bug. The URL you are receiving is exactly how the facebook app is giving it to you via its Android intent. The facebook SDK that we are using is *not* a factor because the intent is sent from the facebook app and receiving by our Corona activity... meaning that the facebook SDK that we use never touches it. Just so you know, all of the useful information is within the Intent's "extras" bundle, which we provide via the "androidIntent" property in the launch arguments and applicationOpen. Within that intent's extras, you will find properties such as "app_id", "access_token", "expires_in", and "extra_launch_uri".

If you want to find out everything that is within your Android intent, trying the below code at the bottom of your main.lua file...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
local function printTable(table, stringPrefix)
    if not stringPrefix then
        stringPrefix = "### "
    end
    if type(table) == "table" then
        for key, value in pairs(table) do
            if type(value) == "table" then
                print(stringPrefix .. tostring(key))
                print(stringPrefix .. "{")
                printTable(value, stringPrefix .. "   ")
                print(stringPrefix .. "}")
            else
                print(stringPrefix .. tostring(key) .. ": " .. tostring(value))
            end
        end
    end
end
 
local launchArgs = ...
print("### --- Launch Arguments ---")
printTable(launchArgs)
 
local function onSystemEvent(event)
    print("*** system event type = " .. event.type)
    if (event.type == "applicationOpen") then
        print("### --- Application Open ---")
        printTable(event)
    end
end
Runtime:addEventListener("system", onSystemEvent)

ed69
User offline. Last seen 15 weeks 2 days ago. Offline
Joined: 23 Dec 2012

@haakon and @j735

Could you please help to clarify how to initiating of the dialog request with my own context 'data' ?
As described here: http://developers.facebook.com/docs/tutorials/ios-sdk-games/requests/
it is possible to send invitation to the friend like this:

1
facebook.showDialog( "apprequests", {message = "invite you to play this game!", data = "cutom_data_string_here"} )

But unfortunately during receiving on the friend app there is only following URL available:

1
fb12345://authorize?expires_in=3600&access_token=ACCESSTOKEN&target_url=http%3A%2F%2Fwww.facebook.com%2Fappcenter%2F12345

so the target_url parameter is available, but the request_ids parameters is missing :(

Have you any ideas why this is not working?

haakon
User offline. Last seen 2 weeks 1 day ago. Offline
Joined: 19 Jan 2011

Sure. Just ask Ansca why they changed the API, and if you're lucky you'll get a proper answer. But most probably they'll blame something else; either you or Facebook.

What you're doing is completely right, and a few weeks back you'd get the request_id back in the Facebook event.response. Recently something has happened and now you get that useless URL back instead on iOS. On Android you get nothing, just an empty string. Or maybe it was the other way around, I don't remember. One of them returns the useless URL, the other an empty string.

We finally gave up on Coronalabs way of dealing with the Facebook API - they break it on a monthly basis it seems - and removed all such dependencies completely by leveraging our own custom Friend selector and moving most of the critical stuff to be handled by our servers.

ed69
User offline. Last seen 15 weeks 2 days ago. Offline
Joined: 23 Dec 2012

thank you for your feedback haakon - this is what I was afraid of :(

I was also looking on Game Center integration on iOS - there are few methods available there how to get GC friends information (their IDs, etc.) - but there is also no way how to invite friend to your game.

So in summary it looks like there is now way (at least for now) to create multiplayer game on Corona SDK neither with using of Facebook or Game Center :(

Joshua Quick
User offline. Last seen 38 min 56 sec ago. Offline
Staff
Joined: 31 Jan 2011

I did a quick Internet search and found the following bug report on facebook's website...
http://developers.facebook.com/bugs/465760136778889/

Apparently, the facebook app does not provide the "request_id" in the URL anymore unless you set up a "Mobile Web URL" on your facebook developer page. At least that's the posted work-around on the above web page. I'm not in the office and unable to test this for myself at the moment, but I suggest that you give it a go.

One more thing. On Android, passing information via URL arguments is uncommon and are typically sent via an Intent's extras bundle. If you can't find what you are looking for in the URL on Android, then have a look within the Intent's extras via the code snippet I've shared here...
http://developer.coronalabs.com/forum/2012/08/08/applicationopen-event-not-firing#comment-135203

ed69
User offline. Last seen 15 weeks 2 days ago. Offline
Joined: 23 Dec 2012

@Joshua Quick - thank you for catch up on this!

After defining the Mobile Web URL on the facebook app I was able to get request_is parameter as expected!

Joshua Quick
User offline. Last seen 38 min 56 sec ago. Offline
Staff
Joined: 31 Jan 2011

Glad I could help! And thanks for confirming that this setting worked.

Just so you know, we're actively writing up new documentation that provides instructions on how to set up the facebook developer page. This is because we get facebook tech-support questions multiple times per week and the vast majority of times it is due to configuration issues on the facebook developer page. We recognize that facebook does not make it obvious how to set it up, especially when you have to set fields such as mobile web url which is completely unrelated to deep linking. Hopeful our upcoming documentation will make it easier on everyone.

Viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.