Android: Instagram Authentication + URL Scheme + Appcelerator’s Titanium

The Problem

This right here was a pain in my side. One of the best features to implement yet so muddled when trying to find out how to execute…but I got it. This post covers custom url schemes for Android using the Titanium platform. It also uses Instagram explicit authentication in the example.

Using custom URL app schemes on iOS and Titanium is pretty straightforward. A few lines of code in the tiapp.xml and you’re off to the races going from app-to-browser and back or app-to-app depending on your setup. When looking for Android best practices on this….forget it. Stack Overflow? Nope. Appcelerator community Q&A? Nope. What I found were bread crumbs to keep me on the path but nothing to take me home.

Here’s what I wanted to do:

  1. Login via Instagram
  2. Take user to Instagram authentication link to sign in
  3. Once successfully authenticated, take user back to redirect uri which would open my app and pass in data with the url scheme I setup. In this case the data would come in with an intent on Android.
  4. Parse the variables and save to device for use later on (userid, username, etc)

The trouble I was having was when I would redirect back to the app. It would either:

  1. Not open my app and fail as a bad url in the device browser
  2. Open my app and crash it
  3. Or open my app and with no intent data with it (took me quite a while to get to this very exciting step)

 

The Solution

Below I am including the code that works for me (literally last night). After employing various snippets of code and not having any luck, I’ve finally got it functioning properly.

tiapp.xml

The first step is create the scheme in your tiapp.xml file. This was the most difficult I guess because everyone had a combination of this but not in its entirety. Or there would be vague fill-in variables in which I wouldn’t know what to fill in. So here it is with actual information.

      

      
           
                
                     
                          
                          
                     
                     
                          
                          
                          
                          
                     
                
           
      
 

Let’s look at the most important block in the above code:

                     
                
                     
                          
                          
                     
                     
                          
                          
                          
                          
                     
                
  1. We set the android:label to the app name. Not sure if the capital letter matters. I will try do some testing later to find out what breaks what.
  2. Set the android:name attribute to “.AppnameActivity”
  3. Set the url attribute to the file handling the external launch when your app is called.
  4. The last two attributes in the activity node I recently came across and it seems like they sealed deal so include them until I find otherwise. Most of this was trial and error as I was on a deadline.
  5. Now we get to the two intent-filters. The first one is there for launching purposes and being able to handle that. Basically you create the action of MAIN which corresponds to the home screen of the app and the category makes the it a top-level app to interact with. It was recommended to create two intent-filters: one for launching and the other for handling the url scheme. I’m still learning about this stuff since I prefer iOS so please feel free to correct in the comments.
  6. In the second intent-filter, is where you define your app url scheme. And placing the data node before the action and category nodes makes a difference.
  7. In the android:scheme attribute, this is going to be your_url://. So in my case when I want to open my app, I am redirecting the user from the browser using collectiv://ig=data1&data2&data3 in my PHP file. Some people recommended putting the android:host attribute, but I had no luck with that at all.
  8. Next, we add the action of VIEW and the two categories are: BROWSABLE – ability to open from a browser and DEFAULT – according to Android documentation it hides any activities from the user when performing the action.

Again, the above code worked for me. But I’d love to hear from some hardcore Android devs who have done this. You can also find this code on GitHub here.

Push Notifications: iOS 8 + Pushwoosh + Appcelerator’s Titanium

With Apple introducing a new method affecting push notifications, this led to Appcelerator introducing a new API call to handle this. Which in turn…you guessed it, rendered some of my code useless when interfacing with Pushwoosh. Essentially I could not send or receive device-to-device push notifications because my device was not registered.

Another thing to note, the way I use device-to-device push notifications with Pushwoosh is to send the device ID to my database under a unique userid for the app. Then when a user interacts with another user, I hit the device table to send the device id to pushwoosh, and so on. I couldn’t even get the id to save it. Huge problem.

Thankfully Appcelerator’s documentation is thorough and I found the solution:

        // Check if the device is running iOS 8 or later
	if (Ti.Platform.name == "iPhone OS" && parseInt(Ti.Platform.version.split(".")[0]) >= 8) {
	    function registerForPush() {
	        Ti.Network.registerForPushNotifications({
	            success: deviceTokenSuccess,
	            error: deviceTokenError,
	            callback: receivePush
	        });
	        // Remove event listener once registered for push notifications
	        Ti.App.iOS.removeEventListener('usernotificationsettings', registerForPush); 
	    };
	 
            // Wait for user settings to be registered before registering for push notifications
	    Ti.App.iOS.addEventListener('usernotificationsettings', registerForPush);
	 
	    // Register notification types to use
	    Ti.App.iOS.registerUserNotificationSettings({
		    types: [
	            Ti.App.iOS.USER_NOTIFICATION_TYPE_ALERT,
	            Ti.App.iOS.USER_NOTIFICATION_TYPE_SOUND,
	            Ti.App.iOS.USER_NOTIFICATION_TYPE_BADGE
	        ]
	    });
	
	} else {
	    // For iOS 7 and earlier
	    Ti.Network.registerForPushNotifications({
	        // Specifies which notifications to receive
	        types: [
	            Ti.Network.NOTIFICATION_TYPE_BADGE,
	            Ti.Network.NOTIFICATION_TYPE_ALERT,
	            Ti.Network.NOTIFICATION_TYPE_SOUND
	        ],
	        success:deviceTokenSuccess,
	        error: deviceTokenError,
	        callback: receivePush
	    });
	}
	
	function deviceTokenError(e) {
	    alert('Failed to register for push notifications! ' + e.error);
	};
	
	function deviceTokenSuccess(e){
                //store deviceToken in variable
		app.DEVICE_ID = e.deviceToken;
    	        //this is where I store my device id in the database too
                //show the deviceToken for testing
    	        //alert(e.deviceToken);

                //Pushwoosh register() function goes here in the success function
    	        PushWoosh.register(function(data) {
                     Ti.API.info("PushWoosh register success: " + JSON.stringify(data));
                },function(e)
                {
      	             Ti.API.info("Couldn't register with PushWoosh: " + JSON.stringify(e));
                });
	};
	
	function receivePush(e){
	     //do something with the data from Pushwoosh
             var parsedContent = JSON.parse(e.data.u);
	};

This seems to be working for me. Official Appcelerator documentation is here. I just added in Pushwoosh stuff. You still need the Pushwoosh file in your Resources folder but the code just changes a little. Happy coding.

Open SMS View in iOS Using Appcelerator’s Titanium Platform

I was struggling with this for a while. I saw a few different syntaxes that were used but only one worked for me:
**UPDATE: added code to check if user has authorized app to access contacts

if(Ti.Contacts.contactsAuthorization == Ti.Contacts.AUTHORIZATION_AUTHORIZED){ 
     Ti.Contacts.showContacts({
          animated:true,
          selectedProperty:function(e){
               Ti.API.info(JSON.stringify(e));
	       var tempNum = e.value.replace(/[^0-9.]/g, "");
	       Ti.API.info('new number: ' + tempNum);
               Ti.Platform.openURL('sms://' + tempNum + '&body=' + encodeURIComponent('Check out my app! http://soundrapp.com'));
          },
          fields:["phone"]
     });
}else if(Ti.Contacts.contactsAuthorization == Ti.Contacts.AUTHORIZATION_UNKNOWN){
     Ti.Contacts.requestAuthorization(function(e){
          if(e.success){
               Ti.Contacts.showContacts({
                    animated:true,
                    selectedProperty:function(e){
                         Ti.API.info(JSON.stringify(e));
	                 var tempNum = e.value.replace(/[^0-9.]/g, "");
	                 Ti.API.info('new number: ' + tempNum);
                         Ti.Platform.openURL('sms://' + tempNum + '&body=' + encodeURIComponent('Check out my app! http://soundrapp.com'));
                    },
                    fields:["phone"]
               });
    
          }else
          {
               alert('authorization error message');
          }
     });
}

What I’m doing is allowing a user to share an app via text message. I even tried to put the above code in a function to call when the phone number was selected, but it always failed. I put in a regex cleanser for the number just to be sure it’s a valid phone number.

Tested on: Appcelerator Titanium SDK 3.4.0 and using iOS 8

Calendar Module Working In Titanium

I’ve been searching all over for a working module but none actually requested access before implementing so none worked. Found a great and simple calendar module and I just implemented the explicit code of prompting a user to allow calendar access. Works like a charm. I’m using it for an app where I need to store events silently without opening the calendar app.

Module link:
https://github.com/alanleard/Titanium-Calendar

Request code for access to Calendar:

if(Ti.Calendar.eventsAuthorization == Ti.Calendar.AUTHORIZATION_AUTHORIZED) {
   // performCalendarReadFunctions();
} else {
    Ti.Calendar.requestEventsAuthorization(function(e){
        if (e.success) {
           alert('You have granted access to the calendar');
            //performCalendarReadFunctions();
        } else {
            alert('Access to calendar is not allowed');
        }
    });
 
}