Showing posts with label beginner. Show all posts
Showing posts with label beginner. Show all posts

Friday, October 23, 2015

Push notifications for Android using Parse.com

I'm writing this mainly because I spent a few hours last night trying to make this work, and the guides found at Parse.com were incomplete so things didn't work as smooth as they should've.

What this tutorial will accomplish

The final product will be a simple push notification sent from the web using Parse.com as the platform, which will make the phone get a notification of the message arrived. When the user touches the notification, the app will open.
We will do this by setting up the Parse push SDK in the application.



If you rather watch a step-by-step video of this tutorial, click on this link.

What you need for this tutorial


An account on Parse.com. A free account will work for this.
A Parse project. Any project in Parse.com will do (if this gets confusing, send me an email and I'll add these steps)
Android Studio
An android device or emulator

Get to work!

This tutorial will be done using the Parse.com guide of using an existing android project instead of a brand new project. This is because this way the tutorial will work both situations.

1. Get Parse project information
Once you create the project on Parse you should see something like this:


First of all, we'll be using Gradle to install the library so you don't need to download the .zip file. You can ignore this step.
However, since we'll be using Gradle to build our application we do care about the dependencies listed here. We will add this in the android project in a minute.

The portion below is also important since it has your Parse Application Id and Client Key.
You should get your own unique Application Id and Client Key for this Parse project, as shown here (I hid mine):

For now, just leave the web page of your Parse project open with this information. We will use it in the Android application.

2. Create an Android project, or open an existing project
If you're creating a new project, any blank project will do. I would suggest to support Android OS 4.1 and higher, but its up to you.

For this tutorial I created a new project named "Test_ParsePush", and the package name for this project is "com.eduardoflores.test_parsepush".
It doesn't matter what yours is, but we will need it in the manifest.

3. Modify your build.gradle (Module:app) file
Right now your project structure in Android Studio should look something like this:

Open the build.gradle file from your module (if you only have 1 module it'll be named "app").
At the bottom of this file, under "dependencies" we will be adding the dependencies given to you by Parse to get the Parse SDK:
    compile 'com.parse.bolts:bolts-android:1.+'
    compile 'com.parse:parse-android:1.+'
Your dependencies should look like this (you may have more or less default dependencies):
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.0.1'
    compile 'com.android.support:design:23.0.1'
    compile 'com.parse.bolts:bolts-android:1.+'
    compile 'com.parse:parse-android:1.+'
}

Now add the maven repository where you will be getting the Parse SDK tools. (note: this is not mentioned in the Parse tutorials):
buildscript {
    repositories {
        mavenCentral()
        maven { url 'https://maven.parse.com/repo' }
    }
    dependencies {
        classpath 'com.parse.tools:gradle:1.+'
    }
}
And finally add the parse plugin at the top of your build.gradle file, like this:
apply plugin: 'com.parse'
So now your entire build.gradle file should look something like this:
apply plugin: 'com.android.application'
apply plugin: 'com.parse'

buildscript {
    repositories {
        mavenCentral()
        maven { url 'https://maven.parse.com/repo' }
    }
    dependencies {
        classpath 'com.parse.tools:gradle:1.+'
    }
}

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.1"

    defaultConfig {
        applicationId "eduardoflores.com.test_parsepush"
        minSdkVersion 16
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.0.1'
    compile 'com.android.support:design:23.0.1'
    compile 'com.parse.bolts:bolts-android:1.+'
    compile 'com.parse:parse-android:1.+'
}
 
You're done with the build.gradle file.

4. Modify your manifest file
Open the manifest file of your Android application.
- In your manifest file, before your <application tag, you need add 2 things.
The first one, goes just like this:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
The second thing requires you to change the package name to the package name of your Android application:
<permission android:protectionLevel="signature"
                android:name="YOUR_PACKAGE_NAME.permission.C2D_MESSAGE" />
<uses-permission android:name="YOUR_PACKAGE_NAME.permission.C2D_MESSAGE" />
In here, you need to make sure you're changing the text "YOUR_PACKAGE_NAME" with the name of your application's package name. In my case, as mentioned on step 2, the package name is "com.eduardoflores.test_parsepush"

- Now in the same manifest file, between the last </activity> and </application> tags, you need to enter this, for GCM to receive your message:
<service android:name="com.parse.PushService" />
<receiver android:name="com.parse.ParsePushBroadcastReceiver"
          android:exported="false">
    <intent-filter>
        <action android:name="com.parse.push.intent.RECEIVE" />
        <action android:name="com.parse.push.intent.DELETE" />
        <action android:name="com.parse.push.intent.OPEN" />
    </intent-filter>
</receiver>
<receiver android:name="com.parse.GcmBroadcastReceiver"
          android:permission="com.google.android.c2dm.permission.SEND">
    <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
        <category android:name="YOUR_PACKAGE_NAME" />
    </intent-filter>
</receiver>
Notice how you must also enter your application's package name in the last category's name.
So with that, and the package names replaced, your entire manifest file should look something like this:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="eduardoflores.com.test_parsepush" >

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
    <permission android:protectionLevel="signature"
                android:name="eduardoflores.com.test_parsepush.permission.C2D_MESSAGE" />
    <uses-permission android:name="eduardoflores.com.test_parsepush.permission.C2D_MESSAGE" />

    <application
        android:name=".StarterClass"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service android:name="com.parse.PushService" />
        <receiver android:name="com.parse.ParsePushBroadcastReceiver"
                  android:exported="false">
            <intent-filter>
                <action android:name="com.parse.push.intent.RECEIVE" />
                <action android:name="com.parse.push.intent.DELETE" />
                <action android:name="com.parse.push.intent.OPEN" />
            </intent-filter>
        </receiver>
        <receiver android:name="com.parse.GcmBroadcastReceiver"
                  android:permission="com.google.android.c2dm.permission.SEND">
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
                <category android:name="eduardoflores.com.test_parsepush" />
            </intent-filter>
        </receiver>
    </application>
</manifest>
You're done with the manifest file (the name=.StarterClass will be explained later).

5. Write the Android application's java code
In your initial activity you must initialize the Parse SDK with the application id and the client key, and then you tell Parse to save that in a background thread.

This is very important: YOUR APP WILL CRASH IF YOU INITIALIZE PARSE MORE THAN ONCE IN YOUR APPLICATION.

What does this mean? If you initialize Parse on the onCreate() method of your MainActivity, the application will run and you will be able to receive notifications, but only if the app is running.
Once a notification arrives the app will start, the onCreate() method will get called again and Parse will be initialized again and the app will crash with a beautiful "Unable to create service com.parse.PushService: java.lang.NullPointerException"
To avoid this, we will create a simple starter activity that will just initialize the Parse library.


Create a starter class

Create a new java file called "StarterClass.java" and in this class extend Activity.

In the StarterClass class, add ann onCreate() method, and after the super() code, add this:
Parse.initialize(this, APPLICATION_ID, CLIENT_KEY);
ParseInstallation.getCurrentInstallation().saveInBackground();

So with that, your entire StarterClass class (StarterClass.java file) will look like this:
package eduardoflores.com.test_parsepush;

import com.parse.Parse;
import com.parse.ParseInstallation;

import android.app.Application;

/**
 * @author Eduardo Flores
 */
public class StarterClass extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        // Setup Parse
        Parse.initialize(this, "application_id_from_parse", "client_key_from_parse");
        ParseInstallation.getCurrentInstallation().saveInBackground();
    }
}
This StarterClass we just created is what we defined in the Manifest file under name=".StarterClass"for the application. This way this class only gets called once, and therefore, Parse only gets initialized once.

And that folks would be all you need to setup your app for notifications using Parse.
Now let's run the app and send a message to see how this puppy works!


 

And finally, you can download the source code of this project from this link.

Eduardo Flores.

Monday, March 23, 2015

Android Studio Rendering problems

A quick note:

On Android Studio version 1.1.0 for Mac (and maybe other versions) a brand new blank application throws an error dialog in the UI editor that says:

Rendering problems. The following classes could not be found:
- android.support.v7.internal.widget.ActionBarOverlayLayout

The issue seems to be in the version of the UI editor Android Studio is using.
On a blank default application Android studio is using level 22. Change the level in the UI editor to level 21 (or another other), and the error should go away:



Eduardo

Thursday, March 19, 2015

In-App purchases on iOS

The purpose of this tutorial is to teach how to create an extremely simple and basic iOS application, but enabling it to purchase non-consumable items from iTunes using Apple's in-app purchase system.
I have found a large number of tutorials online about this subject, but most of them use large or existing applications to show this subject, making it harder to understand.
This app is targeted for intermediate level programmers, but it is written in a way that even a beginner programmer can do it.

What you need for this tutorial
  • A mac with Xcode
  • A physical iOS device (iPhone/iPad/iPod Touch)
  • A paid iOS developer account on developer.apple.com. You should also have access to create new apps, products and sandbox users in iTunesConnect (this is enabled by default with paid iOS developer accounts)
  • Basic understanding of Objective-C

The final result
Let's begin by showing you what the app is going to do. Remember, this is a dummy app and the Level 2 doesn't do anything. I will also be using an iPad for this demo, but will work on all iOS devices.

The app has a single view with 2 buttons and 1 label. The single label shows which items you have already purchased:

When you click on the "Buy level 2" item you will be prompt to login (with your sandbox account), and you will be asked to confirm your purchase:


The app will then display a "Thank you for your purchase" type of message, and the second button ("Go to level 2") will be enabled:
That's all. Super simple but it works to illustrate the point of in-app purchases.
So, let's get started.


Setup iTunesConnect

Before start writing a single line of code, or even open Xcode, we will setup iTunesConnect first for our application. This tutorial was created on March 2015, so iTunesConnect might look different for whenever you read this. The concept should remain the same.
Note: creating the sandbox tester account turned out to be a lot more difficult than what it should be. iTunesConnect doesn't seem to like to use your own email address or any email address that has already been used on iTunesConnect. My best luck was to use a new email address that was not associated or used to log into iCloud, so when the "confirm email address" came, I just logged into the iCloud page with the password created for the Sandbox tester.

After the sandbox tester is created and the account has been verified, you need to log into your physical iOS device with this sandbox login you created. You need to do this in the "Settings -> iTunes & App Store" section of the iOS device.

Start creating the app on Xcode

While we still need to create the app and in-app purchase items in iTunesConnect, begin the creation of the app on Xcode, and running it on a device, helps to get the bundle id setup automatically on iTunesConnect.
  • Start Xcode
  • Create a new single view project
What really matters here in this view is the "bundle identifier." You can modify it later if you want to, but for this demo we will just use something generic that should be available.


Note: iTunesConnect does not like dashes, so avoid them if you can.

Save the app and you will now have a "clean" (only boiler plate code) new project.
We will leave this code "as-is" for now, and we will continue setting up things on iTunesConnect and the iOS developer portal with the information we used on Xcode for our application.

Create the Bundle ID

The first thing we need to do for our application to  be added in iTunesConnect is create the application's bundle id in the developer portal. So:
At this point, you should be looking at something like this:

On the top right corner, click on the "+" button to create a new iOS app ID.
The name is what iTunesConnect will display, and the App ID Prefix is what Apple assigned for your Team ID. What really matters here is what you enter for App ID Suffix.
You could select a wildcard option, but in my experience that makes things get messy once you have multiple apps.

Select "Explicit App ID" and enter the bundle ID, created in Xcode as bundle identifier, exactly the same way it was entered in Xcode.


Scroll down and make sure In-App purchase is selected (it is by default at this time), and click on Continue. Review the information and click on Submit.

Create the App in iTunesConnect

With the bundle ID created, we are done with the developer portal (so you can close it) and we will be going back to iTunesConnect.

In the next window, we will enter the name of the app, a language, the new Bundle ID we just created, the version of the app and a fake SKU. For real applications you might want to get a better SKU.
Note: iTunesConnect will treat this as a real app that you intend to release on the App Store. Because of this iTunesConnect will actually check to see if the bundle id is valid and if the name has been taken. If the name has been already taken, just change the name of the app to something else.

Since we're not going to submit this app to the App Store, we won't worry about screenshots, description, keyword or anything like that.


Setup your In-App products

Still not a single line of code!
In iTunesConnect, under your app go to "In-App purchases" and "Create a new" in-app purchase (note how I had to change the name of my app since DemoInApp was already taken)



Word on the street is that "Non-Consumable" is the most popular type of in-app purchase, since that's what the cool kids use, we will use it as well.

Once we select the Non-Consumable type, we need to enter some information for our in-app product.
  • Reference name: Something internal that only you will see. Could be anything
  • Product ID: this is how we will refer to our in-app product from our code. Make it something relevant to your app, like adding the bundle prefix and the reference name
  • Price Tier: Could be anything, but selected Free since this is just a demo

  • Language: You are required to enter at least 1 language.
  • Display name and description: These will be a localized name and description of your in-app product and it will be visible to the end-user so make something appropriate.
  • Hosting with Apple: No. We're not uploading content to apple at this time as the purpose of this simple demo is to enable things that already exist within the app but are hidden or disabled.

Save your in-app purchase product, and you should have something like this:

We are now finally and officially done with iTunesConnect, although we will need the "Product ID" to be called from our code.
Let's move to Xcode now!

Create the UI for the application

The UI will be super simple. 2 buttons and 1 label. Organize them anywhere you want.
  • Button_buyLevel2: This button is what triggers the purchase of the in-app purchase. This button is visible and enabled
  • Button_goToLevel2: This button uses the in-app purchase. In practice this button will open a new window or access the new material the user just purchased. This button is hidden and disabled until the level 2 gets purchased.
  • Label_status: This is just a status line to tell you if the level 2 has been purchased

Notice how the Button_goToLevel2 is declared as both an IBAction and an IBOutlet. The reason for this is because we want to reference the Button_goToLevel2 and be able to hide it and disable it at runtime.

With the UI done, it's time to setup the library we will use, and the code we will write.

Add StoreKit to the application

The application will use an Apple provided library called StoreKit to handle all in-app related transactions. You can add the StoreKit as part of the Capabilities of the app, or manually.
I will add it manually.
  • Select your application project in the Navigator
  • Go to "Build Phases"
  • Expand "Link Binary with Libraries"
  • Click the "+" to add a library
  • Select "StoreKit.framework" and add it


Congratulations. You just added StoreKit to your application.
Now it's time to finally write some code!

Write the code for in-app purchases

When we created the project in Xcode, Xcode generated several files for us. We're only going to work with 2 files. ViewController.h and ViewController.m.

In the header file (ViewController.h) we need to:
  • Import the StoreKit framework we just linked
  • Declare that our code will implement the SKPaymentTransactionObserver and the SKProductRequestDelegate delegates
  • Create a variable for the SKPaymentQueue
  • Create a variable for the SKProduct we will purchase
After this, your ViewController.h file should look like this:
//
//  ViewController.h
//  DemoInApp
//
//  Created by Eduardo Flores on 3/19/15.
//  Copyright (c) 2015 Eduardo Flores. All rights reserved.
//

#import 
#import 

@interface ViewController : UIViewController <SKPaymentTransactionObserver, SKProductsRequestDelegate>
{
    SKPaymentQueue *defaultQueue;
    SKProduct *product;
}

@property (weak, nonatomic) IBOutlet UILabel *Label_status;
@property (weak, nonatomic) IBOutlet UIButton *Button_goToLevel2;

- (IBAction)Button_buyLevel2:(id)sender;
- (IBAction)Button_goToLevel2:(id)sender;

@end

We're done with our header file. We will now work on our implementation file (ViewController.m)
The first thing I always like to do is synthesize the IBOutlets I have. This is no longer necessary, but I still prefer this method over the self.variable option.

In viewDidLoad, we will:
  • Initialize the SKPaymentQueue variable
  • Make the notify the defaultQueue that we will be the observer for transactions
  • Disable and hide the "Go to level 2" button since it hasn't been purchased yet 
  • Call our method "getProductInfo" which will be created in a minute.
Our viewDidLoad looks like this now:
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    defaultQueue = [SKPaymentQueue defaultQueue];
    [defaultQueue addTransactionObserver:self];
    
    Button_goToLevel2.enabled = NO;
    Button_goToLevel2.hidden = YES;
    
    [self getProductInfo];
} 


We will then create our "getProductInfo" method.
This method first checks if the application's SKPaymentQueue is able to make payments. If it is, it creates a product ID with an NSSet of 1 single object using the Product ID we created in iTunesConnect for the in-app purchase product.
After that it creates a request, makes the delegate of the request look at this class for callbacks, and starts the request.
Our getInfoProduct method would then look like this:
- (void) getProductInfo
{
    if ([SKPaymentQueue canMakePayments])
    {
        // This object comes from iTunesConnect
        // This is the in-app purchase item created
        NSSet *productID = [NSSet setWithObject:@"com.eduardoflores.level2"];   // From iTunesConnect in-app product
        SKProductsRequest *request = [[SKProductsRequest alloc]initWithProductIdentifiers:productID];
        request.delegate = self;
        [request start];
    }
}
Since we're the delegate for the StoreKit transactionObserver and productRequest, we need to implement 2 methods to get the information from the StoreKit.
These methods are "paymentQueue:updatedTransactions" and "productsRequest:didReceiveResponse"

Here's what the "paymentQueue:updatedTransactions" would look like:
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
    for (SKPaymentTransaction *transaction in transactions)
    {
        switch (transaction.transactionState)
        {
            case SKPaymentTransactionStatePurchased:
                [self openLevel2];
                [defaultQueue finishTransaction:transaction];
                break;
            case SKPaymentTransactionStateFailed:
                [defaultQueue finishTransaction:transaction];
                break;
            case SKPaymentTransactionStateRestored:
                [defaultQueue restoreCompletedTransactions];
                break;
            default:
                break;
        }
    }
}

In here the paymentQueue:updatedTransactions we're looping for a the transactions we're getting from the paymentQueue. For each transaction we're doing a switch based on the state of the transaction. If the SKPaymentTransactionState is "Purchased" we will then call a new method (which we will create in a minute) to open or enable the level 2 that has been purchased, and finish the transaction. If the transaction failed, finish the transaction. And finally, if the transaction was restored, let the queue restore the completed transactions.

Now here's the "productsRequest:didReceiveResponse":
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
    NSArray *products = response.products;
    if ([products count] != 0)
    {
        product = [products objectAtIndex:0];
        NSLog(@"product Title = %@", product.localizedTitle);
        NSLog(@"product Description = %@", product.localizedDescription);
        
        Label_status.text = [NSString stringWithFormat:@"%@\n%@", product.localizedTitle, product.localizedDescription];
    }
    
}

This one is super simple, and gets called at startup and gets all of the products that exist in our in-app inventory in iTunesConnect.
The delegate receives an array of responses, which we then call it products for simplicity.
For each product, we get the localized name and localized description which we added manually in iTunesConnect (remember, something appropriate in English only for now). Then we set these elements in the label to display it to the user, and in this case I'm also outputting them to the log just for fun.

There's one more method mentioned in this tutorial, which is to open the level 2. In my case, all this method does is enable the button to use the level 2 and make it visible, like this:
- (void) openLevel2
{
    NSLog(@"in openLevel2");
    
    Button_goToLevel2.enabled = YES;
    Button_goToLevel2.hidden = NO;
}

Finally, pushing the "buy level 2" button triggers the purchase:
- (IBAction)Button_buyLevel2:(id)sender
{
    NSLog(@"in button_buyLevel2" );
    
    if (product)
    {
        SKPayment *payment = [SKPayment paymentWithProduct:product];
        [defaultQueue addPayment:payment];
    }
    else
        NSLog(@"Can't handle payment. Product is null");
    
}


I hope this tutorial helps!


Lastly, the source code for this project can be found here: Source Code