Click here to Skip to main content
13,834,709 members
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

30.3K views
614 downloads
35 bookmarked
Posted 5 Oct 2014
Licenced CPOL

Article 12 - Beginner's Guide to Publishing And Monetizing Your Android App

, 5 Oct 2014
Rate this:
Please Sign up or sign in to vote.
A Step by Step Guide for Getting Your Android App into the App store and Monetize it

Download the App from Google Play Store

                                                                                     (Read the readme file carefully before running the app)

Contents

1. Background

2. Ideation

3. App Devlopment and Publishing
   3.1 Core Concept for Soocial Sentiment app 
          3.1.1 Preparing Application, Logo, Icon
          3.1.2 App Polishing Checklist

    3.2 Publishing the App
           3.2.1  Creating Signed Apk 
           3.2.2 Completing Store Listing
           3.2.3 Uploading Apk  and Publishing
           3.3 Updating App

4. App Rating

5. Monetizing  App Using Google AdMob
    5.1 Getting AdMob Account and Preparing Eclipse for AdMob Integration
    5.2 Creating Ad Unit in AdMob Console 

6. Tracking App Statistics Using Google Analytics

7. In App Billing
    7.1 Enabling In App Billing for the App
    7.2 Setting Up Eclipse Project for In-App-Billing

8. Conclusion

1. Background

Quite often or not an app developer starts his journey with a dream, to become one of the successfull app publishers like Rovio and possibly to become an "Appreneur".  The list of dreamers includes even me! But thankfully I have been able to taste decent success with one of my Windows app called Walk Mania which has crossed over 7000 downloads and recently working on my Android App called Dream Curtains which in Google Play store has crossed about 500 downloads. These are by no stretch of imagination any significant numbers considering that some of the apps crosses thousands of downloads very thick and fast. So one might be bit speculative about reading through an app publishing guide from some one who himself isn't a "Million Download" publisher. So I would try not to exagarate you by begining the article with a claim that it would be  your Android app publishing bible. Rather I would share my knowledge and experience of app publishing and marketing and the things that comes with App publishing. I would also push an app in the course of this article to show you what exactly it takes for an App to be in App store. 

I would then incorporate certain features which are so important for any app's success and which are commonly being adopted by universially by all app publishers across different platforms and app stores. So let's start with our topic and see if we can really make it from here!

2. Ideation

The first step towards making an app is an idea. Well when you first conceive an idea you may feel that the idea is out of the world. You might also get a feeling that this idea is going to truely change your life. To tell you the truth, you have to first research on the idea before starting with your coding. The first thing that is part of the research is Niche A Niche is a segment or category under which you would make your App. For example if your app is about tracking the distance you wwalked, it would easily come under 'Weight Loss" Niche. If your app deals with helping a user deal with his accounts, then it would come under 'Financial" niche. Game is a common Niche that everyone is aware of.

So if your app is health app, you should Google for Health app, you should check out some of the Health apps in the store, more advisable to download and play with few of them. Whichever Niche your app idea falls under deserves a good look around. With your research you would know which are high selling or high download apps in that niche, what they are offering and whether your idea offers anything significant or not.

The second part of the research deals with the probability of people spending for the app. So if you are making an image retouching app, would there by many who would pay? Who is your target audience and what is his spending probability?

I feel it is just about the right moment to disclose what i am planning to release! I am planning to make a Twitter Opinion Polarity Mining App, an app that will tell about the sentiments for searches and topics.

Figure 2.1: App Idea research

At this moment I am comfortably confident that this would first of it's kind in Google Play atleast. You may not always be as lucky and may find many apps already lying there on your idea. Nothing to be get worried about if you find that out. All you have to do is download them one by one. Work with them. Find out if there is anything lagging in them which you may badly want as a user. Is your idea going to offer something which no other app in that similar niche is offering? Or probably is there any falut in the UI that you may not really want as a user? Is there a scope for intuitive/efficient and effective workflow alternative for the subject of your idea? 

If so, then you can still make an impact out of every other possibilities. If not then it is better to take up another subject or idea, no matter however promising your idea might have seemed to you. Remember, never be economical in research or you will pay a bad price for your time in development and marketing schedule.

Once your research votes for your idea, as it has in my case here, you need to find answers for following cases:

1) Who will use my app? In our case here, I feel before purchasing a product, a customer might be interested to know what is the positivity of a brand is? A promotion manager might one to check out how positive is the campaign going on! A politician might want to check mood of the people in his party/constituency or in general. So yes, I feel I know there are people who might try our app.

2) Is the app offering solution to a problem or a value addition?: Say if you are making an app which counts the number of staircases a person raises every day and turns that into some kind of a challenge where he knows how much calorie he has lost , then such an app would be called a solution app. If your app is a simple torch that utilizes flash light, then that is a solution. If your app can speak out the SMSs, then that's a value addition. Solutions are better poised to be successfull then value additions. Though games are all value addition, we are largely keeping games out of our current discussion.  In our case, I feel at this moment that yes, my app might provide a distinct solution for opinion analysis of products beside adding value to a twitter search. ( I am being too baised here, because I have to show that I am rational in my decision to go ahead with the app). Be honest and practical with your answers.

3) Is there enough people who are searching for similar solution? Well answer to this question can not come from your thinking. It needs research. How would you know if people are really looking for what you intend to provide? For that we use a concept known as Keyword Research.  You can search for your idea in Google Keyword Research Tool

You should look for high search volume and high competition. High competition means advertisers bids higher for these keywords. Higher search volume means more users are interested in your app. Have a look at my research before I go ahead with my app publishing.

Figure 2.2: Keyword Research

So my research says that look for terms like "Sentiment" is quite decent interms of both search volume as well as advertisers bid. Though you will not be using Google's Adsense model for your revenue for app, it provides a felly decent idea about the potential of your app. Taking a clue from this I have decided to name my app:

4) Primary Plans about App Monetization:  If you have a great product or service which you are sure that people would "need" and use irrespective of their prices and if you are certain that the value that your app adds to the concept is immense, then you can go for an outrightly paid app with price suitably fixed. It always makes sense to keep the price at lowest at the begining and then increase if you observe that number of app seekers are increasing by every day.

However to keep the app at a higher price and then reducing is considered a cheating on customers who buys at higher price and then finds out that the app is now available at a much lower price. So low to high is a preferred model.

However even at $.99, you will find it really hard to find buyers. When you see last one years of App store trends of both Play and iTunes store, you will see that the number of free apps has significantly increased.

So why a free app and how does it do business?

This model is known as Freemium  model. You make an app and publish a version with enough features to be helpful for the user and keep some version turned off or restrict some of the features. As an when user uses your app and finds the lock features extremely important on his perspective, he can unlock the features from within the app. This phenomenon is called in app purchase. The in app purchase product can be a simple unlocking code, some extra features or subscription ( a purchase which needs to be repeated after certain period like monthly, yearly etc).

Free version of the app may come with advertisement which might be removed for full version. This is the most popular model of app monetization. Therefore in this tutorial we will focus on app moonetization exactly on Fremium model supported by Advertisement.

Social Sentiment

A Social Network sentiment analysis with text mining for tweeter! 

4) What kind of Platform and Device my App will be suitable to?

  

Figure 2.3 Top Purchasing Devices in Google Play ( Image Source: mobilephonedevelopment.com)

Well, this isn't a rocket science to know that more high end devices tend to purchase more apps in comparision to low end phones. So your research should be really to know if your target audience really posses more high end models or not. For instance if you are making a medical app which is helpful for the doctors, then you be rest assured that most of the doctors would have a high end devices so chances of sales should be higher. If your app is fetching facebook stream, then be rest assured that most of the users will be college students who may not hold high end devices. So even if your app's category "Facebook" is quite heavy on search and competition, it will less likely to generate much revenue for you.

In my case I assume that mostly urban users uses Twitter. They range from students to professions. So I am pretty certain that I will not be targetting "users with mainly low end devices." Well it's tough to survive without such positive enthusiasm. Isn't it?

 

Having convinced with the potential of the idea and having conceived the idea, it's finally is the turn to shift towards development.

3. App Devlopment and Publishing

We already have finer details of our app. So in this particular tutorial, I am not going to cover technicalities of the app concept itself, but we shall discuss the features that would be added for the app to be published.

Before we go more into our favourite part, that is coding, one important thing that we will do is analyze in general what features should an app have to be able to make it big? Look at figure 3.1 for understanding what concepts are we looking at.

Figure 3.1: General Architecture of an Android App

Ok, so the above diagram pretty much sums up what we are looking for. So let's take our app's example and see how we can achieve our App publishing?

Before going through your development, I encourage your to obtain a Google Play Developer Account

Quote: Android.com
  1. Visit the Google Play Developer Console.
  2. Enter basic information about your developer identity — name, email address, and so on. You can modify this information later.
  3. Read and accept the Developer Distribution Agreement for your country or region. Note that apps and store listings that you publish on Google Play must comply with the Developer Program Policies and US export law.
  4. Pay a $25 USD registration fee using Google Wallet. If you don't have a Google Wallet account, you can quickly set one up during the process.
  5. When your registration is verified, you’ll be notified at the email address you entered during registration.

Once your account is created, you can log into developer console which will have 'Add new Application'  button through which you can submit your application.

We will see the app submission through our Social Sentiment app. As we have already covered the tweeter login and accessing tweets through our social integration tutorial and Opinion Mining through our Android Data handling tutorial, we will skip explaining the core logic of the app. However we will discuss every other programming as well as non programming aspects for testing the app. 

3.1 Core Concept for Social Sentiment app 

3.1.1 Preparing Application, Logo, Icon

Create a new Android app with the same name as the name you reserved in your App store with  package name containing publisher name. I have said this before, and would say it again. Do not use com.example  namespace. It is reserved and play store wouldn't let you publish with this name.
Figure 3.2: Creating New Android Project
 

Once you create your app, it's time to change the icon and logo. I prefer both of them to be same, but you can design different icon and logo for your app. I created a simple icon cum logo for our Social Sentiment App. According to your app demands and your design choice, you can opt for filled design or trnaparent design. Being conventianally a windows developer, 

Figure 3.3: Our App Logo

Having designed your logo, you can now upload select new icon/logo into your drawable-xhdpi directory and select the file as logo/icon option in android manifest. Remember : Uppercase letters, Special characters (other than UNDERSCORE ) and spaces not allowed in icon/logo file name.

Figure 3.4: Selecting Icon and Logo through Manifest

Once you build and run the application, test how it looks as installed icon as well as for logo in your activity form. You can iterate your design process till you are not satisfied with their look and feel in actual device.

Look at the following figure to understand where does the logo and icon sits.

Figure 3.5: App Logo and Icon 

3.1.2 App Polishing Checklist

Once your app is ready for publishing, do not jump into publishing it. You need to go through some standard check list before publishing. Well though these are not substantial, but important for releasing a stable app into the store. We will discuss the main points of general app checklist and how we have gone about the checklist.

 

1) See if Entire Workflow is functioning or not:  Social Sentiment apps logic is:

a)Shared Preferences, Login, Authentication and Multiple Home Screen Once it is launched for the first time, the app must create some shared preferences which are app specific data including twitter access token, version of the app etc. All the tokens must be blank. The first screen must have only one option: i.e. the option to login to twitter.

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.twitter_login);

        btnLogin=(Button)findViewById(R.id.btnLogin);
        btnLogin.setOnClickListener(new OnClickListener() 
        {
            
            @Override
            public void onClick(View v)
            {
              // Twitter login Needs to be called from here               
            }
        });

///////////////////// After Authorizing app, browser will relaunch app with access token/////////
        Uri uri = getIntent().getData();
        
        ////////////////////////////So Initialize Shared Preferences////////////////////
        mSharedPreferences = getApplicationContext().getSharedPreferences(
                "socialSentimentPref", 0);
if(mSharedPreferences.contains("ACCESS_TOKEN")) // Means app is already authorized
{
    try{
        formNo=2;  
        LoadForm2(); // Launch home screen which has search option

    return;
    }catch(Exception ex)
    {
        Log.e("Error!",ex.getMessage());
        
    }
}
if(twitter==null)
      twitter = new TwitterFactory().getInstance();
        
        //////This part is called when MainActivity is relaunched  with access token//////////////////////////
        if(uri!=null && uri.toString().startsWith(TWITTER_CALLBACK_URL)) 
        {

            uri = getIntent().getData();

        //URL_TWITTER_OAUTH_TOKEN
   final       String verifier = uri.getQueryParameter(URL_TWITTER_OAUTH_VERIFIER);
   final       String token = uri.getQueryParameter(URL_TWITTER_OAUTH_TOKEN);

            try {
                
                

          //    requestToken=new RequestToken(token, verifier);
                            // Get the access token
                MainActivity.this.accessToken = twitter.getOAuthAccessToken(requestToken, verifier);
                long userID = accessToken.getUserId();

                User user = twitter.showUser(userID);
                uname = user.getName();
                screenName=accessToken.getScreenName();

            /**************************** Store all details in Shared Preferences*******************/
                Editor ed=mSharedPreferences.edit();
                ed.putString("ACCESS_TOKEN", MainActivity.this.accessToken.toString());
                ed.putString("TOKEN", accessToken.getToken());
                ed.putString("TOKEN_SECRET", accessToken.getTokenSecret());
                ed.putString("UID", ""+userID);
                ed.putString("SCREEN_NAME", ""+screenName);
                
                ed.commit();
                
               // After storing all details in Shared Preferences, Home Screen will be launched///////////////////
                LoadForm2();
                showToast("Twitter Login Successfull.. "+screenName);
                /////////////////////////////////////////////////////
                
                
               }
            catch(Exception ex1)
            {
                
                Log.i("Fetching Access token Error",ex1.getMessage());
            }
        }
     

        ///////////////////////////////////////////////////////
    }

So we have isolated the logic of presenting home screen using LoadForm2(). So if app is already authorized, Form2 which is home form will be launched else Login form will be launched.

void LoadForm2()
    {
        formNo=2;

       ///////////////////////// Load the Layout Corresponding to Home Form//////////
        parent=LayoutInflater.from(this).inflate(R.layout.activity_main, null);;
        setContentView(parent);

      ////////////////////////////////////////////////////////////////////////////////


////////////// Initialize all other Controls of the home form////////////////////
        tvMessage=(TextView)findViewById(R.id.tvMessage);
        tvResult=(TextView)findViewById(R.id.tvResult);
        tvResult.setText("");
        btnSearch=(Button)findViewById(R.id.btnSearch);
        listView1=(ListView)findViewById(R.id.listView1);
        edSearch=(EditText)findViewById(R.id.edSearch);
        btnSearch.setOnClickListener(new OnClickListener()
        {
            
            @Override
            public void onClick(View v) 
            {
                // Searching logic must be implemented here

            }
                  
                
            
        });
        tvMessage.setText("Welcome "+uname+"("+screenName+")");
        invalidateOptionsMenu();
        
       
    }

So we can logically present different screens based on different state of the app. Also observe the use of invalidateOptionMenu(). A call to this method calls onCreateOptionsMenu(). Note that we want to present the user with a menu option only when he is logged in. Therefore we shall inflate the menu only is the displayed screen is form 2.

@Override
public boolean onCreateOptionsMenu(Menu menu) {

    // Inflate the menu; this adds items to the action bar if it is present.
    if(formNo==2)
    {
     getMenuInflater().inflate(R.menu.main, menu);

    }
    return true;
}

b)Browser Callback Once user presses the button, browser must be opened with authorizing url where user needs to authorize the app. Upon authorization a new access token will be available and browser will redirect to App's second form. Shared preference must store this token.

Having setup the basic structure of the app, we just need to call MyTwitterLogin() method we developed in our social integration tutorial to setup the login process. We will reemphesize that for twitter to be able to present you with your app after successful authorization, you need to specify callback URL like bellow.

static final String TWITTER_CALLBACK_URL = "x-oauthflow-twitter://callback";

Also you need to specify through manifest that your MainActivity class and hence the layout main is a browsable intent ( one that can be triggered from browser) using an intent filter in your activity tag. So your activity looks like bellow:

 <activity
            android:name="com.integratedideas.socialsentiment.MainActivity"
            android:configChanges="keyboardHidden|orientation|screenSize"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
              <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="x-oauthflow-twitter" android:host="callback" />
            </intent-filter>
        </activity>

Bold and Underlined part is the specific part which allows the app to be reloaded by browser after successfull authorization. 

c)Search User should be able to search twitter through the text box. Every tweet in the search result must be subjected to opinion analysis and finally the overall opinion of the search must be displayed.

Remember from our social Integration part that we used a simple String adapter to assign the result from a search to a list box. However when you are planning to do a commercial app, I advise you to go for a custom list view which you can customize as par your requirement.

Unlike the social integration tutorial, here I would want to display the search result, user details, retweets-favourites as well as opinion data of the app. So we will design a custom layout for one row of the list box which needs to display twitter results:

Create a new layout by name list_row.xml  and dump the following xml code

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="fill_parent"

    android:layout_height="wrap_content"

    android:background="@color/BLACK"

    android:orientation="horizontal"

    android:padding="5dip" >

    <!-- ListRow Left sied Thumbnail image -->

    <LinearLayout

        android:id="@+id/thumbnail"

        android:layout_width="50dp"

        android:layout_height="50dp"

        android:layout_alignParentLeft="true"

        android:layout_marginRight="5dip"

        android:padding="3dip" >

        <com.android.volley.toolbox.NetworkImageView

            android:id="@+id/list_image"

            style="@dimen/activity_horizontal_margin"

            android:layout_width="50dp"

            android:layout_height="50dp"

            android:layout_gravity="top"

            android:contentDescription="@string/app_name"

            android:maxHeight="80dp"

            android:maxWidth="120dp" />
    </LinearLayout>

    <TextView

        android:id="@+id/tvTweet"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_centerVertical="true"

        android:layout_toRightOf="@+id/thumbnail"

        android:paddingTop="16dip"

        android:paddingBottom="8dip"

        android:text="tweet"

        android:textColor="@color/WHITE"

        android:textSize="15sp"

        android:textStyle="bold" />

    <TextView

        android:id="@+id/tvUserStat"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_alignBaseline="@+id/tvNameScreenName"

        android:layout_toRightOf="@+id/tvNameScreenName"

        android:layout_marginLeft="15dp"

        

        android:textColor="#00fffa"

         android:textSize="15sp"

        android:text="TextView" />

    <TextView

        android:id="@+id/tvNameScreenName"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_alignLeft="@+id/tvTweet"

        android:layout_alignTop="@+id/thumbnail"

        android:paddingTop="5dip"

        android:text="name"

        android:textColor="@color/WHITE"

        android:textSize="15sp" />

    <TextView

        android:id="@+id/tvTweetStat"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_alignLeft="@+id/tvTweet"

        android:layout_below="@+id/tvTweet"

        android:paddingTop="8dip"

        android:text="Tweet Stat"

        android:textColor="#fb0aff"

        android:textSize="15sp" />

    <TextView

        android:id="@+id/tvPolarity"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_alignBaseline="@+id/tvTweetStat"

        android:layout_alignBottom="@+id/tvTweetStat"

        android:layout_toRightOf="@+id/tvTweetStat"

         android:paddingLeft="16dip"

        android:textSize="15sp"

        android:textColor="#ff0a24"

        android:text="TextView" />

</RelativeLayout>

You can see tvPolarity which is to hold polarity data, tvTweetStat to hold statistics about retweets and favourites, tvNameScreenName to hold user's data specific to the tweet, tvUserStat to hold number of followers of the user and tvTweet to hold the tweets.

But most interesting is an item called 

com.android.volley.toolbox.NetworkImageView

 So what is it all about? Android provides a great library called Volly  to help you access ( transmit and receive) network data in more effectieve way. You can download Volly from here

Build the project and copy Volly.jar. Place it into your project's lib folder and rebuild your project, you are ready to work with volly. The library provides several great tools to work with network related operations.

As you have understood that we want to display the images of the tweet posters ( commonly known as DP in tweeter slang) However fetching the images for every search is really very very resource intenssive and you need some kind of cache to smoothen up the process. So we will use LruBitmapCache.java to perform bitmap image caching.

public class LruBitmapCache extends LruCache<String, Bitmap> implements
        ImageCache {
    public static int getDefaultLruCacheSize() {
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        final int cacheSize = maxMemory / 8;
 
        return cacheSize;
    }
 
    public LruBitmapCache() {
        this(getDefaultLruCacheSize());
    }
 
    public LruBitmapCache(int sizeInKiloBytes) {
        super(sizeInKiloBytes);
    }
 
    @Override
    protected int sizeOf(String key, Bitmap value) {
        return value.getRowBytes() * value.getHeight() / 1024;
    }
 
    @Override
    public Bitmap getBitmap(String url) {
        return get(url);
    }
 
    @Override
    public void putBitmap(String url, Bitmap bitmap) {
        put(url, bitmap);
    }
} 

Now finally we need an adopter class to hold the data for List items.

The class called TweetAdapter must extend BaseAdapter and should have a List of twitter4j.Status . when a view is requested, it should format one element from this list into one row of the list box.

public class TweetAdapter extends BaseAdapter 
{
    public ArrayList<twitter4j.Status> tweetData;
    Context context;
    LayoutInflater inflater;
    int sumScore=0;
    ImageLoader imageLoader = AppController.getInstance().getImageLoader();
    public TweetAdapter()
    {
        
    }
    public TweetAdapter(Context context,ArrayList<twitter4j.Status>searchResult)
    {
        this.tweetData=searchResult;
        inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
     this.context=context;
        
    }
    
    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        
        return tweetData.size();
    }

    @Override
    public Object getItem(int arg0) {
        // TODO Auto-generated method stub
        return tweetData.get(arg0);
    }

    @Override
    public long getItemId(int arg0) {
        // TODO Auto-generated method stub
        return 0;
    }
       ViewHolder holder;
       
     
         @Override
    public View getView(int position, View convertView, ViewGroup parent) 
    {
             if(position==0)
             {
                 sumScore=0;
             }
        View vi=convertView;
        if(convertView==null)
        {
            
         Log.i("Tracking view", "-----Scrolling---------");
        vi = inflater.inflate(R.layout.list_row, null);
        
        holder = new ViewHolder();
        
        holder.tvName = (TextView)vi.findViewById(R.id.tvNameScreenName); // city name
        holder.tvUserStat = (TextView)vi.findViewById(R.id.tvUserStat); // city name
          holder.tvTweet = (TextView)vi.findViewById(R.id.tvTweet); // city weather overview
          holder.tvPolarity = (TextView)vi.findViewById(R.id.tvPolarity);
          holder.tvTweetStat = (TextView)vi.findViewById(R.id.tvTweetStat); // city weather overview
         
          holder.tvImage =(ImageView)vi.findViewById(R.id.list_image); // thumb image
          holder.thumbNail =(NetworkImageView)vi.findViewById(R.id.list_image); // thumb image
     
          
          vi.setTag(holder);
          
        }
     else{
            
            holder = (ViewHolder)vi.getTag();
        }
        try
        {
            
            String s=tweetData.get(position).getText();
            

            //////////////////////////////////////
             holder.thumbNail.setImageUrl(tweetData.get(position).getUser().getBiggerProfileImageURL(), imageLoader);
            holder.tvName.setText("@"+tweetData.get(position).getUser().getName()+"["+tweetData.get(position).getUser().getScreenName()+"]");
            holder.tvTweet.setText(s);
            int followers=tweetData.get(position).getUser().getFollowersCount();
            int retweets=tweetData.get(position).getRetweetCount();
            holder.tvUserStat.setText("Followers:"+followers+" Follows:"+tweetData.get(position).getUser().getFollowersCount());
            holder.tvTweetStat.setText("Retweets:"+retweets+" Favourites:"+tweetData.get(position).getFavoriteCount());
            ////////////////////////////////////////////////////////////////////////////
         

               // Opinion polarity logic for  variable s
              int score=OpnionLogic(s);// here opinion logic is just some method which needs to be replaced by our 

                                                      // Actual opinion polarity method
            /////////////////////////Change the color of Text view based on Opinion////////////////
            
            if(score>0)
            {
                holder.tvPolarity.setText("[Positive "+score+" ]");
                holder.tvPolarity.setTextColor(Color.GREEN);
                
            }
            else
            {
                if(score<0)
                   {
                    holder.tvPolarity.setText("[Negative "+score+" ]");
                    holder.tvPolarity.setTextColor(Color.RED);
                       
                   }
                else
                {
                    holder.tvPolarity.setText("[Neutral "+score+" ]");
                    holder.tvPolarity.setTextColor(Color.BLUE);
                    
                    
                }
            }
            ///////////////// Download DP image asynchronously////////////////////////////////////////////////////////
           ImageDownloader(imvs[position]).execute(tweetData.get(position).getUser().getBiggerProfileImageURLHttps());

        }
        catch(Exception ex)
        {
            
        }
        // TODO Auto-generated method stub
       
        return vi;
    }

static class ViewHolder{
    NetworkImageView thumbNail;
        TextView tvName;
        TextView tvTweet;
        TextView tvUserStat;
        ImageView tvImage;
        TextView tvTweetStat;
        TextView tvPolarity;
    }
class ImageDownloader extends AsyncTask<String, Void, Bitmap> {
      ImageView bmImage;

      public ImageDownloader(ImageView bmImage) {
          this.bmImage = bmImage;
      }

      protected Bitmap doInBackground(String... urls) {
          String url = urls[0];
          Bitmap mIcon = null;
          try {
            InputStream in = new java.net.URL(url).openStream();
            mIcon = BitmapFactory.decodeStream(in);
          } catch (Exception e) {
              Log.e("Error", e.getMessage());
          }
          return mIcon;
      }

      protected void onPostExecute(Bitmap result) {
          bmImage.setImageBitmap(result);
      }
    }
}

d) Opinion Mining on Search Result:  Look list box view is temporary. Only the part of screen you are seeing is created and presented. You want to display the polarity of the tweet in each row of the list box for that tweet. Therefore the polarity must be calculated in the adapter class while view is getting created. Hence as you scroll down or scoll up, it must be updated. Score must be reset to 0 when first row is getting created ( i.e. position=0) and final score must be interpreted when all rows are displayed.

Now replace

int score=OpnionLogic(s)

with following code:

if(position==0)
{
sumScore=0;
}
int score=((MainActivity)context).mp.SimpleMine(s);
            score=score*(retweets+1);
            followers=followers/1000;
        sumScore=sumScore+score; 
if(position>=tweetData.size())
{// Put the final score interpretation


((MainActivity)context).tvResult.setText("Result Score of Opinion mining is "+sumScore);
}

You can again change the last line to suit the display you want.

e)Share User must be able to share the search result, in which case it must be converted into an image, saved temporarily and then allow the user to share the image.

Remember when user logs in successfully, menu will be inflated which has "Share" option. Once share is clicked, contents of the form should be converted into a bitmap and must be saved with a file name generated from time stamp to avoid ambiguity.

Once the image is created a share intent must be triggered with the file name in the extra field of the intent.

case R.id.menuShare:
            
///////////////////////////////////////////////////// Generate File name///////////////////
            String filePath=Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getPath()+"/";
             String fileName = new SimpleDateFormat("yyyyMMddhhmm'.jpg'").format(new Date());
             String fname=filePath+fileName;
/////////////////////////////////////////////////////////////////////////////////////////////             
             // Generate bitmap from View//////////////
             Bitmap b=loadBitmapFromView(parent);
             //////////////////////////////////////////
             try
                {

                     // Save bitmap into the file location with name as obtained above///////
                     OutputStream fOut = null;
                     File file = new File(fname);
                     fOut = new FileOutputStream(file);
                     b.compress(Bitmap.CompressFormat.JPEG, 100, fOut);
                     fOut.flush();
                     fOut.close();
                     //////////// If saving was success, trigger share menu//////////
                     
                     try{
                        Log.d("Ok this is pressed","In the share menu");
                    Intent    myIntent = new Intent(Intent.ACTION_SEND);
                        myIntent.setType("image/jpeg");   
                            try{
                           myIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file));
                            }catch(Exception ex)
                            {
                                
                            }
                        
                            myIntent.putExtra(Intent.EXTRA_SUBJECT, "Social Sentiment Analysis of "+edSearch.getText().toString().trim());
                            myIntent.putExtra(Intent.EXTRA_TEXT, "Shared by Social Sentiment");
                            
                            startActivity(Intent.createChooser(myIntent, "Share Image"));
                        }catch(Exception ex)
                        {
                            
                        }
                    
                     /////////////////////////////////////////////
                }catch(Exception ex)
                {
                    
                }
             
            break;

where loadBitmapFromView() is a utility method as given bellow:

public static Bitmap loadBitmapFromView(View v) 
    {
        if (v.getMeasuredHeight() <= 0) {
            v.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        }
        Bitmap b = Bitmap.createBitmap(v.getMeasuredWidth(),v.getMeasuredHeight(), Bitmap.Config.ARGB_8888);                
        Canvas c = new Canvas(b);
      //  v.layout(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
        v.draw(c);
        return b;
    }

The method first reserves a drawing area equivalent to screen width and height and then using canvas API's draw the content of the view in the drawing are and returns that as bitmap.

Recall from LoadForm2() method that parent is the actual parent view which is inflated by the layout inflator.

parent=LayoutInflater.from(this).inflate(R.layout.activity_main, null);;

e) Sign Out User should be able to sign out and in such case app should wipe out the access token from Shared Preferences and must present the user with the first screen. User should be again able to login from the first screen.

Observe that we have not presented any monetization logic here. That is because your app needs to do what it is meant to do perfectly first. 

2) The app must launch correctly:  The app after installation must launch correctly. As you will be creating and checkinjg several shared preferences, there are chances that app fails to launch. Once your core logic is completed, copy the apk from project's bin directory into any of your phone/tablet directories like download. From setting enable "can install external apks". Uninstall any previously installed version and install the app from the apk. Launch the app from the installed icon and see if the app is launching without hassels or not. App crash duing launch a big turn off for the users. Also app launching must be fast and must not make the user wait for eternity. In case you want to fetch certain resources at the begining, present user with some suitable message about the wait.

Once your app launches and completes the entire workflow successfully, it is time to test.

3) Test for all possible conditions:  Think about the user inputs and other possible scenerios. For instance in our case after the URI was presented in the browser, what if the internet connection becomes unavailable? What if the internet connection is available but with extremely low speed? What if user clicks authorize app in browser after say abut 10 minutes when the session is destroyed? Try to create the scenerio and make the app responsive under all these test conditions.

4) UI Responsiveness: This is another critical important thing for a modern app. Many of your app options may be resource intenssive which might take a lot of time to complete. Do not block your UI for this period. Use Asynchronous execution whereever it is possible. Use progress bars, progress rings and animations to let user know that certain tasks are under way. Never ever call a resource intensive thread from the main thread. If these steps can be achieved then surely there are no reasons why user would not love your app.

 Remember we had presented our app's logic in point number 1) where we have literally left the button click events empty. I wanted to cover the topics under this subsection to show how responsiveness can be assured.

Let's first take the example for TwitterLogin. When you click the button, first an URI is created based on the request tokens and a browser tab is opened with this uri. This process takes about 1.5sec to 2 sec. However if you had called MyTwitterLogin() method directly from button click, user might have got confused with no action and might have pressed the same button several times. Hence the entire process is now embedded into an async task. in preExecute we display appropriate progress ring for the user to know that some actions have started. When the task is started, the progress ring is withdrawn and at the end user is notified about success or failure of task.

class TweetLoginAsync extends AsyncTask<Void, Void, Void> {
        
        @Override
        protected Void doInBackground(Void... params) {
            //hrd.start();
            try
            {
                
                MyTwitterLogin();
                
            }catch(Exception ex)
            {
                
            }
            return null;
        }
        
        @Override
        protected void onPostExecute(Void result) 
        {
            ringProgressDialog.dismiss();
        }
        
        @Override
        protected void onPreExecute() 
        {
            ringProgressDialog.show();
            btnLogin.setVisibility(View.INVISIBLE);
            
            
        }
        
    }

 And finally from onClick method of btnLogin which is initialized in onCreate, we invoke Twitter login Asynchronously.

btnLogin.setOnClickListener(new OnClickListener() 
        {
            
            @Override
            public void onClick(View v)
            {
                // TODO Auto-generated method stub
                
                
                ringProgressDialog = new ProgressDialog(MainActivity.this);
                ringProgressDialog.setCancelable(true);
                ringProgressDialog.setMessage("Preparing for Login :) ");
                ringProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
                ringProgressDialog.setIndeterminate(true);
                ringProgressDialog.show();
                
                
                TweetLoginAsync tla=new TweetLoginAsync();
                tla.execute();
                    
            }
        });

We use the same Async task logic even for searching:

class TweetSearchAsync extends AsyncTask<Void, Void, Void> {
        
        @Override
        protected Void doInBackground(Void... params) {
            //hrd.start();
            try
            {
                
                  tweets=new ArrayList<twitter4j.Status>();
                  try{
                  Query query = new Query(edSearch.getText().toString().trim());
                  query.setCount(100);
                list = twitter.search(query).getTweets();
                  
                  for (int i=0;i<list.size();i++)
                  {

                      twitter4j.Status each = (twitter4j.Status) list.get(i);
                      tweets.add(each);
                
                  }
                  
                  adapter = new TweetAdapter(MainActivity.this ,tweets);
                  listView1.setAdapter(adapter);
                
    
                  }catch(Exception ex){}
                
            }catch(Exception ex)
            {
                
            }
            return null;
        }

So in the background we are generating the query and collecting the result in a list which is passed to adapter constroctor to initiate the adapter and then load the adapter as list adapter which displays the result in the list view.

For performing Opinion Mining, we need data from our server. But we will not fetch opinion table for every search. Rather we shall download the data into a local table and will trigger search only after this data is available. Because search result has to utilize opinion mining. 

So we call Search option from another async task from postExecute, after data is available.

class OpinionDatabaseFetchAsync extends AsyncTask<Void, Void, Void> {
        
        @Override
        protected Void doInBackground(Void... params) {
            //hrd.start();
            try
            {
                
///////////////////// If opinion data not available, call soap and get this data
                if(datas==null)
                {
                    CallSoap cs=new CallSoap();
                    webResult=cs.GetSentiData();

                }
////////////////////////////////////////////////////////////////////////
                
            }catch(Exception ex)
            {
                
            }
            return null;
        }
        
        @Override
        protected void onPostExecute(Void result) 
        {
            ringProgressDialog.dismiss();

           /////////////////////// If for first time we come here after soap, deserialize soap result//
            if(datas==null)
            {
                String [] rows=webResult.split("\n");
                String [][]datas=new String [rows.length][2];
                        for(int i=0;i<rows.length;i++)
                        {
                            datas[i]=rows[i].split("#");
                        }
                        mp=new MinePolarity(datas);
            }
            ///////// Opinion Database is With Us. Go for search///////////
            TweetSearchAsync tsa=new TweetSearchAsync();
            tsa.execute();
            //////////////////////////////////////////////////////////////
            
        }
        
        @Override
        protected void onPreExecute() 
        {
            ringProgressDialog=new ProgressDialog(MainActivity.this);
            ringProgressDialog.setTitle("Preparing Senti Analysis System");
            ringProgressDialog.show();
            btnLogin.setVisibility(View.INVISIBLE);
            
            
        }
        
    }

And finally here is how we trigger the search process by first triggering fetching of opinion data from onClickListener of the button.

btnSearch.setOnClickListener(new OnClickListener()
        {
            
            @Override
            public void onClick(View v) 
            {
           

                adapter=new TweetAdapter();
                listView1.setAdapter(null);
                
                OpinionDatabaseFetchAsync odfa=new OpinionDatabaseFetchAsync();
                odfa.execute();
            
                // TODO Auto-generated method stub

            }
                  
                
            
        });

Now what I mean by testing is what you can find out by replacing these two lines from the above code:

adapter=new TweetAdapter();
                listView1.setAdapter(null);

You will see the app gets crashed for second search without any of these two lines. That is because a new query will have result with different number of rows, hence reinitializing the adapter is important. But when you do that the adapter can present any view. So the app crashes. Hence listView adapter must be set to null.

You might also observe that after you click the button, the on screen keyboard still remains which makes it really irritating for the user as he has to manually minimize it. Add following line in btnSearch's onClickListener to minimize the keyboard soon as the button was clicked. It will be popped up again when you tap on the text box for the text search.

InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); 

inputManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(),
                           InputMethodManager.HIDE_NOT_ALWAYS);

5) Layout Independent Functioning:  Remember Android calls onCreate method whenever you you change the orientation of the device. The state of the device could be saved in a bundle and restored from the bundle. However in many apps like a youtube video viewer, or in our case the app does not only have a view to be saved, but at the same time a network state. So if you were in the middle of a search process getting your data, you would not want everything to be reset by changing the orientation. Using a simple manifest hack, you can ensure that your app runs just as fine when you change the orientation. Just change your activity tag in Manifest as follows to see this effect. Now your layout will be changed with change or orientation but data will not be destroyed.

<activity
            android:name="com.integratedideas.socialsentiment.MainActivity"
            android:configChanges="keyboardHidden|orientation|screenSize"
            android:label="@string/app_name" >

 

3.2 Publishing the App

Having done all the coding, refining and testing, you are finally ready to publish your app. However few things are to be taken care of before publishing. Let us again make a checklist and perform the task one by one.

3.2.1  Creating Signed Apk  When you are developing your project, it is configured in build mode which needs to be changed to release mode and must be signed with a key in order to prevent any tampering.

 For generating Signed Release Version of App right click on your Eclipse project, select Android Tools and opt for Export Signed Application Package  as shown in figure 3.6

Figure 3.6: Preparing for Signing and Releasing the Apk

 

Assuming that you are using this option for the first time, we will go ahead and create a new keystore. Select next on the screen of App signing and export so that you get a screen like 3.7. Here you must chose 'Create New Keystore'  option and provide a location for Keystore which you can remember. Give a strong password which you can remember.

3.7: Creating a New keystore

Provide your details with the password that you had used in the previous screen like 3.8.

Figure 3.8: Filling Details for Creating New Key Store

Please avoid using ,-#$  and any other special characters in the fields. Otherwise you may get invalid ava format.

After you have completed the steps you will see your signed apk file in the folder you specified in Figure 3.7. It's now time to upload the apk in Google Play store.

3.2.2 Completing Store Listing   Login to Google Play Developer Console . You had already  registered your app name. Now select on that app name and complete store listing first as shown in figure 3.9.

Figure 3.9: Completing Store Listing for the App

After completing text data, it is time for completing Graphics assets. For successfully completing your resources you need:

 

1) Atleast One screenshot for Phone and one for 7" Tablet. 

2) High resolution icon ( one which you have designed for your project)

3) Feature Graphic

Each of these images has their own size specification. If your image does not match the requirement, you would be prompted to upload an image with different size. You can use any photo resizing software to incorporate the changes. For generating a screen shot in Eclipse, you can click on DDMS tab and then click on the camera icon. This is how I have been taking all the screenshots for this article:

Figure 3.10: Generating App Screenshot from Eclipse

If you are sure that your app will look same in phones too, then you can upload same set of images for both phones as well as tablets. Also do not forget to rearrange the images such that they reflect the app workflow.

Feature graphics is one that will be shown in the store. You can keep one of your screenshots as feature graphics or better design a simple and effective banner for the app. 

Figure 3.11 will give you an idea what each field means for the final listing.

Figure 3.11: How Graphic and Text info is combined into Store Listing

You can now plan your graphic and text information by looking at figure 3.11

3.2.3 Uploading Apk  and Publishing We have exported our signed APK. it's finally time to upload it from Apk tab of your app in developer console. All you have to do is click on Apk under your app's name in developer console and drag and drop the signed apk into the box prompting you to upload apk for production.

Figure 3.12 Uploading APK for Production

Now you can publish the app by clicking on top right link which says aqpp is ready for publishing as shown in figure bellow.

Figure 3.13: Ready to Publish Message

If any of the information needed is not provided, this button will not be available. Instead it will tell you why you can't publish.

Once you publish your app, you will see the App details changing from "Draft" to "Published" in your home page of developer console.

Figure 3.14 App Published Confirmation in Developer Console

Once yoour app is published, it takes little time for getting reflected in Google Play. You can check for the Google Play listing by following url

https://play.google.com/store/apps/details?id=YOUR_APP_FULL_PACKAGE_NAME

Where YOUR_APP_FULL_PACKAGE_NAME here is com.integratedideas.socialsentiment

You can refresh this URl after say half an hour after publishing and your store listing will be Visible. It is always advisable to download the app from the link in your device to see if the app is installing and launching correctly or not. Before doing that, do not forget to uninstall the debug version of app that resides in your device while developing and testing the app.

3.3 Updating App

Even after taking care of the crashes and test cases, you may run into trouble after the app is published. On the other hand you might want to incorporate features that were left out from initial version. So now you will need the app to be updated. For updating, all you have to do is upload new apk. The new apk must have a different app version and version name than previously uploaded one. You can change the version from Manifest file as shown bellow.

Figure 3.15 Changing the Version of the app after modifications

This new version must also be build, tested and then exported as we did for our first version. Before exporting, do not forget to delete previously exported app from drive. Eclipse does not always overrite the exported apks.

Once you opt to publish the new apk, the older ones will be automatically unpublished and new apk will be availabe.

Figure 3.16: Updating Your App

You don't have to notify your customers explicitly after app update. Those devices which have installed app will be automatically updated which will be taken care by Google Play installed in the device.

Now understand that app updation invariably triggers downloading new version of app which means extra bandwidth. Customers are quite concerned about wastage of their data plan in downloading same data again and again through update. Therefore you must plan your apk in such a way that graphic assets which are hardly changed are downloaded from external source and are not downloaded with new apk. In this way you will be able to keep the download size restricted and your customers will indeed enjoy going for new app version.

 

4. App Rating

The popularity of your app depends upon the ratings and reviews in the app store. More people likes your app, the more popular it will be and will be shown ahead of your competitor's apps. Getting a user to rate your app is not easy as this involves going to store and rate it. After installing the app, hardly ever a user goes back to the store particularly to rate it ( unless and untill he is really mad about the app either in a positive sense or in a negative sense).

Therefore it is always recommended that your app has some kind of an App Rater engine that prompts the user to rate the app after certain number of usage or after certain days. This gives your user a chance to show his love for the app as well as it also provides an opportunity for your app to get popularize in the app store.

There is an exceptionally cool open source library ( or rather a class file) for rating your app written by Chris Hager which can be downloaded from here

This is quite a simple class which comes with it's own shared preferences through which it can remember the last shown time, user preference for the dialog and so on.

In my case, I wanted the Rater to be popped up after every search results. So I have put the following lines of codes in TweetAdapter. You are free to chose when and at what frequency the rating message will be displayed.

AppRater appRater = new AppRater(context);
           appRater.setDaysBeforePrompt(0);
           appRater.setLaunchesBeforePrompt(0);
           appRater.show();

If you are using it from mainActivity form, you can pass MainActivity.this  in place of context.

 setDaysBeforePrompt waits for user to use the app for few days before prompting him to rate your app. setLaunchesBeforePrompt() will set a value for number of times user must have launched the app before showing him with app rating dialog.

Following figure 4.1 shows the result.

4.1 App Rating Dialog

When user choses to rate the app, he will be taken to play store listing where he can rate as well as submit a review.

Figure 4.2: In App Rating Result

You can also keep a menu icon and display the dialog.  You might in that case display app rating menu only if user has not rated the app. So I have incorporated one more utility method in AppRater.java as bellow:

public boolean isAlreadyRated()
    {
        SharedPreferences prefs = mContext.getSharedPreferences(mPrefGroup, 0);
        try{
        return prefs.getBoolean(mPreference_dontShow, true);
        }catch(Exception ex)
        {
            return false;
        }
    }

So you can create an instance of AppRater with the context of the activity and check if the app is already rated or not, if not display the option.

5. Monetizing  App Using Google AdMob

 

In order to use virtually any ad engine, you need to sign up with the ad engine, download sdk, integrate in your app and the ads starts appearing on the app on customer's devices. There are different type of bidding for the App: for instance CPI,  based, CPA based and so on. If you had no prior knowledge about what they are, then advertisers pays either for number of views or based on action. Suppose you are organizing a conference and your educational app is presenting an advertisement on that. So the advertiser will pay if someone travelled to the conference site clicking on the ad on your device and actually registers for the conference. That is cost par action. It may also involve sales. Some apps are very heavily downloaded ( in thousands every month). There advertisers prefer to put CPI/CPM based ads where the advertiser pays par 1000 impressions.

But thumb rule to success at the end of the day is how persistent your user is with your app. At the end of the day everything will depend upon number of downloads and persistancy(devices who do not uninstalls your app).

As more or less all the ad networks and engines for mobile ads works in same way, I would go with Google Ad Engine. Frankly I have never had an app engine in any of my apps and for the first time I am going to use them. Then how I survived with my apps is  a story I will share as a separate subsection as possible alternatives.

So lets get started:

5.1 Getting AdMob Account and Preparing Eclipse for AdMob Integration

1) Create your AdMob account by going to this link here>>. Sign in with your gmail account.

2) I already have an AdSense as well as AdWords account. That makes it easier for me as my accounts are verified. So all I need to do is verify my adsense and adwords account. If you do not have an adsense account then Google is going to ask you to create one. ( I really can not say what are the informations it seeks as the programs are country dependent. So you should carefully get on with the process).

3) Review the Policies:  And do not hit "I agree" term without reading. AdSense and AdMob has some very strict policies which you must read and understand before proceeding. There are thousands of accounts banned everyday. So , spend a little time in reading.

4) For Working with mobile ads you do not need to download any separate SDK. It comes bundled with host of other services in a package called Google Play Services. In Eclipse Click on  SDK manager and ensure that Google Play Services are installed as given in figure bellow:

Figure 5.1:  Google Play Service API's in Eclipse

5. Importing Google Play Service into Your Project:

You can search for your android-sdks  in your folder. It is generally located in C:\Users\<SYSTEM_USER_NAME>\android-sdks. In my system it is C:\Users\Rupam\android-sdks. Once you locate the android folder open extras\goolgle folder, which in my system is C:\Users\Rupam\android-sdks\extras\google.

Now copy google_play_services  folder into a directory where your projects are located. You can right click on any projects in eclipse and then in properties see the directory.

Figure 5.2: Copying google-play-services to workspace directory

Now import the project google-play-services_lib  located inside google-play-services directory with File > Import, select Android > Existing Android Code into Workspace  option. Noe that do not use Existing Project Into Workspace ( see figure bellow)

Figure 5.3 Right way of Importing Google Play Services

 

Now you can reference your project to the library project by right clicking on your eclipse project->properties->Android->Library->add like following figure.

Figure 5.4 : Referencing Google Play Services from your project

6) Incresing Memory of Eclipse: Now comes the most trickey part. Unfortunately Google's play services leaks a lot of memory. When you compile a project that uses Play Service, the copilation may take eternity and finally fail with Out of Memory Exception. Therefore Eclipse settings needs to be changed by editing Eclipse.ini as shown in figure bellow. Do not forget to edit the file in Administrator mode.

Figure 5.5 Editing Eclipse.ini to help it compile apps with Google Play Services 

Take a backup of your Eclipse.ini into a different folder, close eclipse, open eclipse.ini as shown above and replace the code with following

-startup
plugins/org.eclipse.equinox.launcher_1.3.0.v20120522-1813.jar
--launcher.library
plugins/org.eclipse.equinox.launcher.win32.win32.x86_64_1.1.200.v20120913-144807
-showsplash
org.eclipse.platform
--launcher.XXMaxPermSize
1024m
--launcher.defaultAction
openFile
-vmargs
-Xms1024m
-Xmx1024m

Save the file, close it, Now restart your eclipse. It is ready to compile your Google Play enabled ads ( especially MobAds.

6) Enable API Version 7 Compatibility:  One of the other important problem of working with AdMob integration is it needs the API 7 libraries to effectively compile your app. For this just create a blank project in your Eclipse and replace the Manifest file with following AndroidManifest.xml file

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

          package="android.support.v7.appcompat">
    <uses-sdk android:minSdkVersion="7"/>
    <application />
</manifest>

Build the project and you will see android-support-v7-appcompat.jar  being created in the lib directory.

Figure 5.6:  Creating an App which is compatible to API 7

Alternatively you can Download appcompat_v7.zip, unzip and import the project in Eclipse and build it.

5.2 Creating Ad Unit in AdMob Console 

 It takes few business days for your AdMob account to get approved. Once it is approved, you will be able to login to the console. In console clik on Monetize new app, search for your app listing and select. If you have just uploaded the apk and your appo is not available through search, you can go with Manual option.

Figure 5.7 Adding New App for Monetization

Once your app is ready for monetization it will be visible under All Apps  tab in left side panel. Click on the app.

Figure 5.8 Creating New Ad Unit

You can create new ad unit by clicking the New Ad Unit button. There are basically two kinds of Ads: Banner Ads and Interstitial Ads. Interstitial Ads are full screen or splash screen ads which appears before or after certain events. Banner ads can be placed at strategic locations in your app or may be triggered programatically.

Once you Ad Unit is created, note down the Ad Unit ID which will be something like:

ca-app-pub-27************82/4++++++++8  format.

5.3 AdMob Integration in your Code 

In order to work with AdMob Apis, firstly you have to reference both google play services as well as appcompat_v7 in your project. Right click on the project ->Properties->Android then add these two projects by clicking add option in bottom right panel.

Figure 5.9 Setting Project References for AdMob Integration

We also will be using a utility class called ToastAdListener which can be found inside android-sdks\extras\google\admob_ads_sdk

You might also want to check out all the ad-mob integration samples from the same project.

Now we add the following lines in LoadForm2() method to display ads only in second screen.

av = (AdView) findViewById(R.id.adView);
   av.setAdListener(new ToastAdListener(this));

  RelativeLayout layout = (RelativeLayout) findViewById(R.id.main_layout);

    RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
            RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT);


   String androidId = "" + android.provider.Settings.Secure.getString(this.getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);



    Log.i("AdRequest.Builder.addTestDevice",androidId);
    AdRequest adRequest=null;
    try{
    adRequest = new AdRequest.Builder()
    .addTestDevice(AdRequest.DEVICE_ID_EMULATOR)
    .addTestDevice(androidId)
    .build();
    av.loadAd(adRequest);

As you can see that an Ad is pulled using an AdRequest object. You need to authorize the object to pull ads into both emulator as well as real device. You need to pass encrypted device id to adRequest object which can be obtained from Secure.getString() method.

Once an ad is pulled, it will be displayed in the control called adView. Here is the our xml layout file holding placeholder for displaying app.

<com.google.android.gms.ads.AdView
       android:id="@+id/adView"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:layout_alignParentLeft="true"
       android:layout_below="@+id/btnSearch"
       ads:adSize="BANNER"
       ads:adUnitId="ca-app-pub-27---------------82/4----------8" >

   </com.google.android.gms.ads.AdView>

Specifying correct adSize and adUnitId are very important. Without these ads will not be fetched.

Finally here is our GUI view in design mode to give you an idea of what we are trying to do

Figure 5.10: GUI of App Social Sentiment

Even after performing all these steps successfully, you might encounter errors while building the app. That is because of inharent problems with Google Play Service. Another important factor to check before building and running the app is Java Build Path under project properties ( see figure 5.9). Refer following figure and make sure your app's setting is absolutely similar to the following figure.

While integrating the AdMob with my app, I faced lots of problems especially with eclipse memory, build orders and libraries. If you directly import your Google Play Samples after following the instructions in Android Developer site, 9999 out of 10000 times you will fail to get the integration successful.

Logically you must keep appcompat_v7 in Android_dependencies along with google-play-services.

Android private libraries must have every jar other than appcompat_v7. Java Build path should not include Google APIs version 4.0. Also your project's build target must be Google APIs( Figure 5.9). And why this weired configuration is beyond any logical explanation. Only Google can answer about the weiredness.

Figure 5.11: Java Build Path Setting for the App

However one important thing is still to be incorporated.  You need to add following meta description before activity tag in the Manifest file.

<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />

If everything goes as smooth as we hope they do, then you will see a result like figure 5.12

Figure 5.12: Screenshot of the App with Ad-Integration

6. Tracking App Statistics Using Google Analytics

As usual the first step is to generate the key for the app for tracking. For this all you have to do is click on Analyze tab in your AdMob console as shown bellow.

Figure 6.1 Creating New App for Tracking in Analytics

Once new app creation window appears, select the app as you have done in figure 5.7 or provide the exact package name: com.integratedideas.socialsentiment and complete next two steps. You will get a tracking ID like:

UA-5******0-2. Save this ID. you need to provide this ID in your code for tracking.

Figure 6.2: Completing App Tracking for New App and Obtaining Tracking ID

Before going with coding copy android-sdks\extras\google\analytics_sdk\libGoogleAnalytics.jar  into your project's lib folder.

Declare an Object of GoogleAnalyticTracker in the class.

GoogleAnalyticsTracker tracker;<span style="color: rgb(34, 34, 34); font-family: Arial, Helvetica, sans-serif; font-size: 15px; font-weight: bold; background-color: rgb(255, 255, 238);"> </span>

Initialize the tracker in onCreate method:

 tracker = GoogleAnalyticsTracker.getInstance();
// Start the tracker in manual dispatch mode...
tracker.startNewSession("UA-5****-2", this);//replace UA-5****-2 with your tracking ID

Now whichever page/activity/form/ event you want to track, invoke tracker.trackPageView("SomeName") from that section.

I have used to track the twitter login activity by incorporating the tracking code inside btnLogin onClickListener:

tracker.trackPageView("/TwitterLogin");

Also in btnSearch onClickListener:

tracker.trackPageView("/Searching");

But the tracker must be dispatch before your form is closed.  Therefore overrite the onDestroy method of the Activity class and dispatch the tracker here. Do not forget to call stopSession before dispatching.

@Override
      protected void onDestroy() {
        super.onDestroy();
        // Stop the tracker when it is no longer needed.
        try{
        tracker.stopSession();
        tracker.dispatch();
        }catch(Exception ex)
        {
            
        }
       
      }

Now build and debug your app. When you are inside your app, you will see new user in the analytic window.

Figure 6.3: Viewing the New user in Analytic Tracking

 

7. In App Billing

In App billing is perhaps the most popular monetizing tool for mobile apps across all app stores. This is one of the best business models supporting the Freemium  model. So user downloads somewhat restricted version of the app supported by Ads. He works with the app and loves the app. He wants to experience completely ad free environment, so he purchases full version of the app or he can buy some extra features which are missing in free version.

Google has somewhat complicated workflow for Inapp billing. But more problematic is not Google's complicated workflow, but it has several bugs which sometimes make it difficult to get this module function properly. But as always, we will hack into some of these drawbacks and make our app work with In App Billing.

 

Here I would like to emphesize the strategy of our Social Sentiment App. The free version of the app would allow the user to make only 10 searches every day. A shared preference would keep track of number of searches as well as the date. When user crosses the limit of the app, it presents user with an option to go for full version. Upon selecting and purchasing full version, user can perform any number of searches. There are several strategies and workflows that you can think of which can bring business to your app.

But first, let us get started with allowing our app to make in-app billing requests:

7.1 Enabling In App Billing for the App

First login to your Google Play Developer Console and select your app. From left panel select "In App Products you should ideally create a product with a packgage extension which is similar to your app's package extension( see following figure).

Figure 7.1: Creating In App Purchasable Products

From API version 3 ( current version) onwards Google Play specifically supports Managed Products. A Managed product is one which is remembered by Google Play and only one purchase transaction par user account/app is supported. Though Google play also offers unmanaged products, these are to be consumed explicitly after purchase from your code. If you have forgotten to do so, it will not let you purchase the product again.

Subscription is an item which is to be updated periodically.

While completing the product creation, do not forget to change the product "-Inactive" to "+Active"  from top right corner of product details page.

Once product creation is successfull you will see the listing as bellow

Figure 7.2: In App Billing Product Listing in App Store

All the In-App-Billing-Products of an app must be accessed through an API key.

You can obtain this by selecting Services and APIs  tab from left panel.

Figure 7.3: Obtaining Key for In-App-Billing Communications

7.2 Setting Up Eclipse Project for In-App-Billing

For billing API's we will use the a sample for In-App-Billing which is downloaded along with Google Play service. You can find this sample inside: android-sdks\extras\google\play_billing

 Now perform the following steps for setting up your project to support Billing API V3.

1) First copy contents of play_billing\samples\TrivialDrive\src\com folder ( containing two more folders:android,example) into your_project_directory\src\com folder. Now refresh your project in eclipse. You will see your Src folder structure something similar to Figure 7.4

Figure 7.4: Project Src Structure after importing Billing Sample Src

You will see IInAppBillingService.aidl  which is an Android Interface Definition Language (AIDL) file that defines the interface to the In-app Billing Version 3 service. Include com.android.vending.BILLING permission in your manifest.

Now clean and build your project. Your project is ready to implement In-App-Billing.

Firstly declare an object of IabHelper class in your MainActivity

IabHelper mHelper;

Remember IabHelper class is present in your src folder under com.example.android.trivialdrivesample.util package.

Initialize the helper in onCreate method:

mHelper = new IabHelper(this, base64EncodedPublicKey);

mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
          public void onIabSetupFinished(IabResult result) {
             if (!result.isSuccess())
             {
                // Oh noes, there was a problem.
                Log.d("Billing Problem", "Problem setting up In-app Billing: " + result);
             }
             else
             {
                 Log.d("Billing SUCCESS", "SUCCESS setting up In-app Billing: " + result);


                  }
               };

Every call in Billing API 3 is Asynchronous. mHelper is initialized with base64EncodedPublicKey as shown in figure 7.3.

startSetup calls Google Play and exchange communication.  When you build and run your application, if it shows billing success like figure bellow that means your app is ready for In-App-Billing.

But now the actual headeche starts. You should remember following few important points before implementing/testing IAB.

Prerequisites:

  1. Open Declaration AndroidManifest must include "com.android.vending.BILLING" permission.
  2. APK is built in release mode.
  3. APK is signed with the release certificate(s).
  4. APK is uploaded to alfa/beta distribution channel (previously - as a draft) to the developer console at least once. (takes some time ~2h-24h).
  5. IAB products are published.
  6. Test account(s) is added in developer console.

Testing requirements:

  1. Test APK has the same versionCode as the one uploaded to developer console.
  2. Test APK is signed with the same certificate(s) as the one uploaded to dev.console.
  3. Test account (not developer) - is the main account on the device.

Which means, at this stage you must have another Google account( other than the account you used to log in to Google Play). Uposn completion of our In-App Testing, we need to test the apk with that account. Also after developing IAB concept, the app must be uploaded for Beta Testing. Remember Google sometimes takes about a day to reflect the new APK in Beta.

Before we go further, we also need to understand a bit of IAB logic.

1) Helper should first call queryInventory to get the list of purchasable products.

mHelper.queryInventoryAsync(false,new QueryInventoryFinishedListener() {
                    
                    @Override
                    public void onQueryInventoryFinished(IabResult result, Inventory inv) 
                    {
                        // TODO Auto-generated method stub
                        String s=result.getMessage();
                        Log.i("purchase",s);
                    }
                }) ;

2) This list of Inventory can be shown to user as a list or if you have limited products as in our case, you can initiate a purchase directly through product code called SKU. Android provides you a fake product called "android.test.purchased Which can be purchased ( fake) from any app. So this is a cool way of implementing the IAB logic. Once the logic is successfull, you can replace this SKU with your actual SKU.

3) Even a fake SKU should be consumed after purchase, if not, it can not be purchased any more. For purchasing:

mHelper.launchPurchaseFlow(this, ITEM_SKU, 10001,  mPurchaseFinishedListener, "mypurchasetoken");

4) mPurchasedFinishedListener must be initialized inside onCreate ( if IAB was successfull). After purchase we must consume the product to make the purchase item available again. Every purchase is carried out with hash signature. But What Google does not tell you is that signature of fake purchase never matches that with a real purchase. So when you are testing your app with android.test.purchased, the signature will fail, you can't consume and can't test your app with any further purchase of this fake item.

So I updated some logic, I make the logic that if purchase is not null( which means it was done) and only signature verification failed, then also consider the purchase as success.

Upon Success we change the app version to full mode. But at the same time you need to consume this purchase. That would be done by calling mHelper.consumeAsync(purchase, mConsumeFinishedListener);

 

mPurchaseFinishedListener
                   = new IabHelper.OnIabPurchaseFinishedListener() {
                   public void onIabPurchaseFinished(IabResult result,
                                   Purchase purchase)
                   {
                       boolean success=false;

                           if(result.getMessage().contains("Signature verification failed")&&purchase!=null)
                           {
                               success=true;

                                String data="";
                                     data=mSharedPreferences.getString("APP_MODE", data).trim();
                                     Editor ed=mSharedPreferences.edit();
                                     ed.putString("APP_MODE", "FULL");
                                     ed.commit();

                           }


                      if (!success)
                      {

                   //       purchase=new Purchase(base64EncodedPublicKey, base64EncodedPublicKey, base64EncodedPublicKey);
                         String purchaseToken = "inapp:"+getPackageName()+":"+ITEM_SKU;
                          try
                          {
                           int response = mHelper.mService.consumePurchase(3, getPackageName(),purchaseToken);
                           Log.i("Response",""+response);
                          }
                          catch (RemoteException e)
                          {
                           // TODO Auto-generated catch block
                           e.printStackTrace();
                          }
                         return;
                      }
                      if(success)
                      {
                          String data="";
                             data=mSharedPreferences.getString("APP_MODE", data).trim();
                             Editor ed=mSharedPreferences.edit();
                             ed.putString("APP_MODE", "FULL");
                             ed.commit();
                             invalidateOptionsMenu();

                      }
                   if (purchase.getSku().equals(ITEM_SKU))
                    {
                        mHelper.consumeAsync(purchase, mConsumeFinishedListener);
                      // buyButton.setEnabled(false);
                    }

                  }
               };

mConsumedFinishedListener is as given bellow:

mConsumeFinishedListener =
                    new IabHelper.OnConsumeFinishedListener() {
                     public void onConsumeFinished(Purchase purchase, 
                           IabResult result) {

                   if (result.isSuccess()) 
                   {                 
                       String data="";
                      data=mSharedPreferences.getString("APP_MODE", data).trim();
                      Editor ed=mSharedPreferences.edit();
                      ed.putString("APP_MODE", "FULL");
                      ed.commit();
                   }
                   else 
                   {
                           // handle error
                   }
                }
              };
              }
                 // Hooray, IAB is fully set up!  
           }
        });

Having setup our IAB, let's test it. When you click for the menu option of Buy Unlimited Searches, you will see fake purchase screen like bellow.

Figure 7.5: Fake Purchase

Upon successfull purchase you will see a purchase success screen as bellow.

Figure 7.6 Fake Purchase Success

Finally it's time to replace fake sku with our actual SKU.

Replace ITEM_SKU value with "com.integratedideas.full_version_social_sentiment"   Build and realse the signed app. You can't test it from your account. So add another google account as beta tester from Settings->User Accounts in your Developer Console. Invite. From the other account accept the invitation. Upload the new release for beta testing and promote the beta testing apk to production.

Add another gmail account in your phone by setting->Accounts->Add Account. Login to phone from new user's account. Install the app and test IAB through new account. Note for testing, you still need to enter credit card details. However transactions made from test account will not have any real financial transactions ( meaning you won't loose any money).

8. Conclusion

Coding, building and publishing the app is just about the first steps in Commercial mobile app business. If you want to make it big in App market, you need more visibility. Visibility increases as your app starts getting up the ladder in search results. For the app to appear at the top of search results for the segment or niche you are targeting you need to learn some Search Engine Optimization skills. It includes pointing to the app page from as many numbers of web pages as possible. You can do this by writing a review of the app in different websites, asking app reviewers to review your app and so on. SEO must be ever lasting effort just like coding effort. Another interesting tool for SEO is Youtube. It is relatively easier to rank higher in youtube than in blogs and text sites. So always consider making a video of your app and put it in youtube. You can also have a link from your youtube page to the app store page. I have had really about three days time to complete the app, publish it and monetize it. There are bound to be flaws and testing issues due to real lack of time for the app. However I have tried to build a realistic utility app which incorporates most important and common features that a commercial app should have. This article also provides a checklist for app publishing and polishing. The reason for using a live example is to help you understand the concepts with a live app. I am not an AppPreneur and don't make my living from App market. So there are other successfull people who are positioned better to tell you the secret of success. However the goal of this tutorial is not to guide you to become a successfull app publisher but to help you become an app publisher in Android market. Once you get an overview of what it needs to publish apps in Android market, you can use your skills to attain success. And when you finally become an inspiring app publisher, do not forget to share a tip or two here for us to take inspiration from your success. Good Luck.

 

 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Grasshopper.iics
CEO Integrated Ideas
India India
gasshopper.iics is a group of like minded programmers and learners in codeproject. The basic objective is to keep in touch and be notified while a member contributes an article, to check out with technology and share what we know. We are the "students" of codeproject.

This group is managed by Rupam Das, an active author here. Other Notable members include Ranjan who extends his helping hands to invaluable number of authors in their articles and writes some great articles himself.

Rupam Das is mentor of Grasshopper Network,founder and CEO of Integrated Ideas Consultancy Services, a research consultancy firm in India. He has been part of projects in several technologies including Matlab, C#, Android, OpenCV, Drupal, Omnet++, legacy C, vb, gcc, NS-2, Arduino, Raspberry-PI. Off late he has made peace with the fact that he loves C# more than anything else but is still struck in legacy style of coding.
Rupam loves algorithm and prefers Image processing, Artificial Intelligence and Bio-medical Engineering over other technologies.

He is frustrated with his poor writing and "grammer" skills but happy that coding polishes these frustrations.
Group type: Organisation

116 members


You may also be interested in...

Comments and Discussions

 
PraiseVery informative Pin
Nick_314159265419-Apr-17 4:20
memberNick_314159265419-Apr-17 4:20 
QuestionGreat Article! - But I Disagree with Your Approach to Marketing Pin
Bill SerGio Jr.8-Mar-17 11:09
professionalBill SerGio Jr.8-Mar-17 11:09 
AnswerRe: Great Article! - But I Disagree with Your Approach to Marketing Pin
Chris Maunder28-Mar-17 9:46
adminChris Maunder28-Mar-17 9:46 
QuestionGreat article Pin
Scott Hooper8-Oct-14 20:41
memberScott Hooper8-Oct-14 20:41 
AnswerRe: Great article Pin
Grasshopper.iics8-Oct-14 21:12
groupGrasshopper.iics8-Oct-14 21:12 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web05 | 2.8.190114.1 | Last Updated 6 Oct 2014
Article Copyright 2014 by Grasshopper.iics
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid